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
(likeBinaryFormat
from a file) - Output format:
NitroRom
(inherits fromNodeContainerFormat
) - 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:
- Header using
Binary2RomHeader
- Banner using
Binary2Banner
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
(likeNitroRom
or a directory) - Output format:
BinaryFormat
- Parameters: (Optional)
NitroRom2BinaryParams
OutputStream
:Stream
to write the output ROM data. If not provided, the converter will create aStream
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:
- Header using
RomHeader2Binary
- Banner using
Banner2Binary
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 bannerinfo
: (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
.
- Every node under this container will be either another container or a
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.