Table of Contents

Cartridge / ROM converter and format

Games and software in general that run in a DS or DSi device have the same binary format. It is the same format for content dumped from a physical cartridge, digital downloads (DSiWare) or digital content (firmware apps like the menu launcher).

The converters Binary2NitroRom and NitroRom2Binary support DS and DSi software.

You can find the technical details of the format specification here.

Reading a ROM

The format has information about the software and contains a list of files. This format can be converted / unpacked into a tree nodes with the converter Binary2NitroRom.

Note
  • Input format: IBinary (like BinaryFormat from a file)
  • Output format: NitroRom (inherits from NodeContainerFormat)
  • Parameters: (Optional) DsiKeyStore
// Create node from file with binary format.
Node game = NodeFactory.FromFile(gameFilePath, FileOpenMode.Read);

// Use the `Binary2NitroRom` converter to convert the binary format
// into node containers (virtual file system tree).
game.TransformWith<Binary2NitroRom>();

// And it's done!
// Now we can access to every game file. For instance, we can export one file
Node gameFile = game.Children["data"].Children["Items.dat"];
gameFile.Stream.WriteTo("dump/items.dat");

The converter will use the following converters to transform some binary data into the specific format:

If the converter parameter is present and it contains the required keys, then converter will also verify the hashes (HMAC) and signature of the binary cartridge. If it is missing, it will skip the verification.

Warning

The verification of the hashes may take some extra time. In DSi games there are hashes that verify the full content of the cartridge. The time it is proportional to the size of the binary ROM.

Some DSi games may have modcrypt encryption. In that case the converter will also decrypt the files. This operation does not require keys.

The data nodes contains two tags that are used when writing back the ROM:

  • scenegate.ekona.id: Index in the file system. Some times used by software to access files.
  • scenegate.ekona.physical_id: Index of the file in the data block.

Writing a ROM

Any container node (NodeContainerFormat) that follows the structure defined by the cartridge can be converter (packed) into the ROM / cartridge binary format using the converter NitroRom2Binary.

Note
  • Input format: NodeContainerFormat (like NitroRom or a directory)
  • Output format: BinaryFormat
  • Parameters: (Optional) NitroRom2BinaryParams
    • OutputStream: Stream to write the output ROM data. If not provided, the converter will create a Stream in memory (caution).
    • KeyStore: keys to use to re-generate the HMACs from the header and digest hashes. If not provided, the hashes will be not be generated. They may be zero bytes or the ones from the original ROM if already set.
    • DecompressedProgram: set it if the provided arm9 program file is decompressed with BLZ compression. In that case, the converter will update the compressed length in the arm9 program with zero. Otherwise it will set the current arm9 program length.
game.TransformWith<NitroRom2Binary>();
game.Stream.WriteTo("output/new_game.nds");

The converter will use the following converters to transform the program and banner information into binary data:

Warning

If you are generating a big ROM, pass the parameter with an OutputStream from a disk file. Otherwise the output data may take a lot of RAM memory.

The nodes from the container given as a input must have the formats and names specified in NitroRom tree. This means every game file must be already in IBinary (BinaryFormat).

Note

When the KeyStore parameter is set, the hashes are re-generated. This operation may take some extra time and it is not needed. Even if the hashes won't be valid anymore, emulators and custom firmwares like unlaunch skip the verification. In any case, the signature is not possible to generate (unknown private key) so the re-generated ROM won't be valid even if the keys are provided.

The file IDs are assigned by following a depth-first approach. If every node contains the tag scenegate.ekona.id then its value is used instead.

The file data is written following the order of the file IDs. If every node contains the tag scenegate.ekona.physical_id, then this order is used.

Tip

To reduce the size of patches, ensure that the nodes contains the scenegate.ekona.physical_id tags, so the data is written in the same order. You can do that by always applying changes to the file system read from a cartridge, instead of creating it from scratch.

NitroRom tree

The NitroRom format inherits from NodeContainerFormat and just provide helper properties to access to some nodes easily. You can access to the data and files via its properties or navigating the nodes.

The structure of the tree nodes is defined and required by some converters. It follows this structure from the root node (returned node by converter):

  • system: (container) ROM information and program files.
    • info: (ProgramInfo) Program information.
    • copyright_logo: (BinaryFormat) Header copyright logo.
    • banner: (container) Program banner
      • info: (Banner) Program banner content (titles and checksums).
      • icon: (IndexedPaletteImage) Program icon.
      • animated: (container) Animated icons on DSi software.
        • bitmap{0-7}: (IndexedImage) Bitmaps for the animated icon frames.
        • palettes: (PaletteCollection) Palettes for the animated icon frames.
        • animation: (IconAnimationSequence) Animation information for the icon.
    • arm9: (BinaryFormat) Executable for the ARM9 processor.
    • overlays9: (container) Overlay directory for ARM9 processor.
      • overlay_{i}: (BinaryFormat) Library overlay for ARM9 processor.
    • arm7: (BinaryFormat) Executable for the ARM7 processor.
    • overlays7: (container) Overlay directory for ARM7 processor.
      • overlay_{i}: (BinaryFormat) Library overlay for ARM7 processor.
  • data: (container) Root directory for program data files
    • Every node under this container will be either another container or a BinaryFormat.

Secure area encryption (KEY1)

The converters does not decrypt or encrypt the secure area of the ARM9 program automatically. They will present the ARM9 as it is inside the cartridge. But they will do encrypt or decrypt the ARM9 with modcrypt if the header specifies so.

You can decrypt or encrypt the file later by using the class NitroKey1Encryption

var programInfo = Navigator.SearchNode(rom, "system/info").GetFormatAs<ProgramInfo>();
var key1Encryption = new NitroKey1Encryption(programInfo.GameCode, keyStore);

DataStream arm9 = Navigator.SearchNode(rom, "system/arm9").Stream!;
bool isEncrypted = key1Encryption.HasEncryptedArm9(arm9);
if (isEncrypted) {
    DataStream decryptedArm9 = key1Encryption.DecryptArm9(arm9);
} else {
    DataStream encryptedArm9 = key1Encryption.EncryptArm9(arm9);
}

Animated icon

DSi software brings an animated icon. You can export this icon into standard GIF format by also using the Texim library. You can use the property SupportAnimatedIcon to know if the banner contains the animated icon.

The converter IconAnimation2AnimatedImage can convert the animated node from the banner into the AnimatedFullImage type from Texim. You can use later its converters to convert to GIF format.

Note
  • Input format: NodeContainerFormat
  • Output format: AnimatedFullImage
  • Parameters: none
NodeContainerFormat animated = Navigator.SearchNode(rom, "system/banner/animated")
    .GetFormatAs<NodeContainerFormat>();

AnimatedFullImage animatedImage = new IconAnimation2AnimatedImage().Convert(animated);
using BinaryFormat gifData = new AnimatedFullImage2Gif().Convert(animatedImage);

gifData.Stream.WriteTo("icon.gif");
Note

It is not possible to import the icon from a GIF file, only export. If you want to modify the animated icon, edit each frame and information. For instance, export each bitmap with a palette into PNG format and the animation information as a JSON/YAML file.