ndspy.rom: ROMs

ndspy.rom provides a class that represents a Nintendo DS ROM file.

Note

Unlike most ndspy documentation, the attributes of NintendoDSRom aren’t arranged mostly alphabetically. Rather, they’re sorted in the same order as in ROM header data.

Examples

Change a ROM’s name and resave it:

>>> import ndspy.rom
>>> rom = ndspy.rom.NintendoDSRom.fromFile('nsmb.nds')
>>> print(rom.name)
bytearray(b'NEW MARIO')
>>> rom.name = b'Example Name'
>>> rom.saveToFile('nsmb_edited.nds')
>>>

Get a file from a ROM by file ID:

>>> import ndspy.rom
>>> rom = ndspy.rom.NintendoDSRom.fromFile('nsmb.nds')
>>> soundDataSDAT = rom.files[134]
>>> with open('sound_data.sdat', 'wb') as f:
...     f.write(soundDataSDAT)
...
4839008
>>>

Replace a file in a ROM by filename, and resave it:

>>> import ndspy.rom
>>> rom = ndspy.rom.NintendoDSRom.fromFile('nsmb.nds')
>>> with open('sound_data.sdat', 'rb') as f:
...     soundDataSDAT = f.read()
...
>>> rom.setFileByName('sound_data.sdat', soundDataSDAT)
>>> rom.saveToFile('nsmb_edited.nds')
>>>

Print the names of all NSBTX files, found by looking for their file data magics rather than file extensions:

>>> import ndspy.rom
>>> rom = ndspy.rom.NintendoDSRom.fromFile('nsmb.nds')
>>> rom.setFileByName('sound_data.sdat', soundDataSDAT)
>>> for i, file in enumerate(rom.files):
...     if file.startswith(b'BTX0'):
...         print(rom.filenames.filenameOf(i))
...
enemy/b_lift.nsbtx
enemy/d_bridge.nsbtx
enemy/I_do_hahen_l.nsbtx
enemy/I_do_hahen_r.nsbtx
enemy/I_kaiten_ami.nsbtx
[snip]
polygon_unit/wire_netting8.nsbtx
polygon_unit/wire_netting9.nsbtx
>>>

Load a ROM’s ARM9 code file and overlays:

>>> arm9 = rom.loadArm9()
>>> print(arm9)
<main-code at 0x02000000
    <code-section at 0x02000000: b'\xff\xde\xff\xe7\xff\xde\xff\xe7\xff\xde\xff\xe7\xff\xde\x15\xa3\x82\xe5\x1d\1\xfa\x11\x1c%\x8a\td\x80\x19\xaf\xdc\x8d'... implicit>
    <code-section at 0x01FF8000: b'\0\xc0\x90\xe5\40\x90\xe5H \x9f\xe5H\x10\x9f\xe5\0\xc0\x82\xe5\40\x82\xe5\x08\xc0\x90\xe5\x0c \x90\xe5'...>
    <code-section at 0x027E0000: b'`\x81\xff\1`\x81\xff\1`\x81\xff\18\x83\xff\1H\x83\xff\1X\x83\xff\1h\x83\xff\1`\x81\xff\1'...>
    <code-section at 0x02043380: b'\0\x10\x90\xe5\0\0Q\xe3\1\x10A\x12\0\x10\x80\x15\0\0\x90\xe5\x1e\xff/\xe1\xb0\x10\xd0\xe1\0\0Q\xe3'...>
    <code-section at 0x02085880: b''>
>
>>> overlays = rom.loadArm9Overlays()
>>> for id, overlay in overlays.items():
...     print(id, overlay)
...
0 <overlay at 0x020986E0 file=0 compressed verify-hash>
1 <overlay at 0x020CC2E0 file=5 compressed verify-hash>
2 <overlay at 0x020CC2E0 file=7 compressed verify-hash>
3 <overlay at 0x020CC2E0 file=9 compressed verify-hash>
4 <overlay at 0x020CC2E0 file=11 compressed verify-hash>
5 <overlay at 0x020CC2E0 file=12 compressed verify-hash>
[snip]
129 <overlay at 0x020B8920 file=27 compressed verify-hash>
130 <overlay at 0x021226E0 file=126 compressed verify-hash>
>>>

API

class ndspy.rom.NintendoDSRom([data])

A Nintendo DS ROM file (.nds).

Parameters:

data (bytes) – The data to be read as a ROM file. If not provided, the ROM will use default values.

name

The ROM’s name. This is usually a short ASCII string containing the name of the software. This can be up to 12 bytes long; longer values will be truncated when saving.

This is at offset 0x000 in the ROM header.

Type:

bytes (12-byte limit)

Default:

b''

idCode

The four-byte ID code of the software. Usually, this is ASCII, and the fourth character is a region identifier (“E” for North America, “P” for Europe, or “J” for Japan).

This is at offset 0x00C in the ROM header.

Type:

bytes (exactly 4 bytes long)

Default:

b'####'

developerCode

An identifier for the developer of the software. Usually two ASCII characters; for example, Nintendo is “01”.

This is at offset 0x010 in the ROM header.

Type:

bytes (exactly 2 bytes long)

Default:

b'\0\0'

unitCode

The systems this ROM supports:

  • 0: Nintendo DS (DSi only in compatibility mode)

  • 2: Both Nintendo DS and Nintendo DSi

  • 3: Nintendo DSi only

This is at offset 0x012 in the ROM header.

Type:

int

Default:

0

encryptionSeedSelect

The seed number to use when decrypting the ROM. The actual seed values are built into the DS’s hardware; this is only an index into a table. Valid values are 0 through 7, inclusive.

This is at offset 0x013 in the ROM header.

Type:

int

Default:

0

deviceCapacity

A number representing the storage capacity of the hardware this ROM is intended to be placed on. The formula is 2 ^ (17 + X) bytes; for example, a value of 7 means 16 MB.

This is at offset 0x014 in the ROM header.

Note

This can optionally be recalculated for you automatically upon saving the ROM. For more information about this, see the documentation for the save() function.

Type:

int

Default:

9

pad015

The value of the padding byte at 0x015 in the ROM header.

Type:

int

Default:

0

pad016

The value of the padding byte at 0x016 in the ROM header.

Type:

int

Default:

0

pad017

The value of the padding byte at 0x017 in the ROM header.

Type:

int

Default:

0

pad018

The value of the padding byte at 0x018 in the ROM header.

Type:

int

Default:

0

pad019

The value of the padding byte at 0x019 in the ROM header.

Type:

int

Default:

0

pad01A

The value of the padding byte at 0x01A in the ROM header.

Type:

int

Default:

0

pad01B

The value of the padding byte at 0x01B in the ROM header.

Type:

int

Default:

0

pad01C

The value of the padding byte at 0x01C in the ROM header.

Type:

int

Default:

0

region

The region this ROM is intended to be used in:

  • 0x00: most regions

  • 0x40: Korea

  • 0x80: China

This is at offset 0x01D in the ROM header.

Type:

int

Default:

0

version

The version number for this ROM. It’s unclear exactly what this means.

This is at offset 0x01E in the ROM header.

Type:

int

Default:

0

autostart

A value related to how the ROM should be loaded. If “autostart & 4” is set, the “Press Button” message after the Health and Safety screen will be skipped.

This is at offset 0x01F in the ROM header.

Type:

int

Default:

0

arm9EntryAddress

The RAM address that ARM9 execution should begin at, after the ARM9 code has been loaded into RAM at arm9RamAddress.

This is at offset 0x024 in the ROM header.

See also

arm9 – the code data that this entry address should reference.

Type:

int

Default:

0x02000800

arm9RamAddress

The RAM address that the ARM9 code should be loaded to.

This is at offset 0x028 in the ROM header.

See also

arm9 – the code data that will be loaded to this address.

Type:

int

Default:

0x02000000

arm7EntryAddress

The RAM address that ARM7 execution should begin at, after the ARM7 code has been loaded into RAM at arm7RamAddress.

This is at offset 0x034 in the ROM header.

See also

arm7 – the code data that this entry address should reference.

Type:

int

Default:

0x02380000

arm7RamAddress

The RAM address that the ARM7 code should be loaded to.

This is at offset 0x038 in the ROM header.

See also

arm7 – the code data that will be loaded to this address.

Type:

int

Default:

0x02380000

normalCardControlRegisterSettings

The “port 0x040001A4 setting for normal commands”. For more information, see the section about this value on GBATEK (subheader “40001A4h - NDS7/NDS9 - ROMCTRL - Gamecard Bus ROMCTRL (R/W)”).

This is at offset 0x060 in the ROM header.

Type:

int

Default:

0x00416657

secureCardControlRegisterSettings

The “port 0x040001A4 setting for KEY1 commands”. For more information, see the section about this value on GBATEK (subheader “40001A4h - NDS7/NDS9 - ROMCTRL - Gamecard Bus ROMCTRL (R/W)”).

This is at offset 0x064 in the ROM header.

Type:

int

Default:

0x081808f8

secureAreaChecksum

The checksum of the encrypted “secure area” of the ROM.

This is at offset 0x06C in the ROM header.

Todo

This should be calculated automatically when saving the ROM instead of being an attribute.

Type:

int

Default:

0x0000

secureTransferDelay

A delay value of some kind related to encryption commands. Measured in units of 130.912kHz each. For more information, see the section about this value on GBATEK (subheader “Secure Area Delay”).

This is at offset 0x06E in the ROM header.

Type:

int

Default:

0x0D7E

arm9CodeSettingsPointerAddress

The address in RAM (plus 4) of a pointer to the “code settings” structure in ARM9’s main code file. This defines things like the SDK version used to compile the ROM, whether the code is compressed or not, and the list of ARM9 code “sections” and where they should be placed in memory. If this value is 0, then either there is no code settings block in ARM9 or its location is unspecified.

This is at offset 0x070 in the ROM header.

Note

You have to subtract 4 from this value to get the actual address of the pointer to the code settings block.

Type:

int

Default:

0

arm7CodeSettingsPointerAddress

The address in RAM (plus 4) of a pointer to the “code settings” structure in ARM7’s main code file. This defines things like the SDK version used to compile the ROM, whether the code is compressed or not, and the list of ARM7 code “sections” and where they should be placed in memory. If this value is 0, then either there is no code settings block in ARM7 or its location is unspecified.

This is at offset 0x074 in the ROM header.

Note

You have to subtract 4 from this value to get the actual address of the pointer to the code settings block.

Type:

int

Default:

0

secureAreaDisable

This value disables the encrypted “secure area” of the ROM, allowing one to use that area without encryption. To do this, the value must be set to “NmMdOnly”, encrypted. This is probably impossible without Nintendo’s private keys.

This is at offset 0x078 in the ROM header.

Type:

bytes (exactly 8 bytes long)

Default:

b'\0\0\0\0\0\0\0\0'

pad088

Padding area beginning at 0x088 in the ROM header.

Type:

bytes (exactly 0x38 bytes long)

Default:

b'\0' * 0x38

A compressed image of the Nintendo logo. The DS will refuse to load the ROM if this is modified in any way.

This is at offset 0x0C0 in the ROM header.

Type:

bytes (exactly 0x9C bytes long)

Default:

(the correct value)

debugRomAddress

The address where the “debug rom” should be loaded to in RAM, if present. It’s unclear what exactly this is.

This is at offset 0x168 in the ROM header.

See also

debugRom – the data this refers to.

Type:

int

Default:

0

pad16C

Padding area beginning at 0x16C in the ROM header.

Type:

bytes (exactly 0x94 bytes long)

Default:

b'\0' * 0x94

pad200

Padding after the ROM header, beginning at 0x200.

Type:

bytes

Default:

b'\0' * 0x3E00

rsaSignature

The ROM’s RSA signature data. Not every ROM has is cryptographically signed, but for those that are, this is stored at the very end of the ROM. Since most methods of playing games from ROM files these days bypass the RSA verification step, this attribute probably isn’t very useful for most purposes.

Type:

bytes (either 0 or 0x88 bytes long)

Default:

b''

arm9

The main ARM9 executable binary to be loaded to arm9RamAddress.

See also

ndspy.code.MainCodeFile – the ndspy class you can use to load this data.

arm9RamAddress – the address this will be loaded to in RAM.

arm9EntryAddress – the address in RAM where ARM9 execution will begin.

Type:

bytes

Default:

b''

arm9PostData

A small amount of extra data immediately following arm9 in the ROM data. It is unclear what this is for.

Type:

bytes

Default:

b''

arm7

The main ARM7 executable binary to be loaded to arm7RamAddress.

See also

ndspy.code.MainCodeFile – the ndspy class you can use to load this data.

arm7RamAddress – the address this will be loaded to in RAM.

arm7EntryAddress – the address in RAM where ARM7 execution will begin.

Type:

bytes

Default:

b''

arm9OverlayTable

The table containing information about ARM9 overlays.

See also

ndspy.code.loadOverlayTable() – the ndspy function you can use to load this data.

Type:

bytes

Default:

b''

arm7OverlayTable

The table containing information about ARM7 overlays.

See also

ndspy.code.loadOverlayTable() – the ndspy function you can use to load this data.

Type:

bytes

Default:

b''

iconBanner

A structure containing the game’s icon data, and its title in multiple languages. For more information, see the section about this value on GBATEK.

The required length of this bytes object depends on its version number (which is its first two bytes), according to the following table:

Version number

iconBanner length

0x0001

0x0840

0x0002

0x0940

0x0003

0x1240

0x0103

0x23C0

An empty bytes object (b'') is also allowed, which corresponds to a null icon/banner data offset in the ROM’s header.

Type:

bytes (required length depends on the version number).

Default:

b''

debugRom

Some optional data related to debugging; it’s unclear what exactly this is.

See also

debugRomAddress – the address in RAM this will be loaded to.

Type:

bytes

Default:

b''

filenames

The root folder of the ROM’s filename table. These filenames usually do not cover all files in the ROM (for example, overlays are usually unnamed).

See also

ndspy.fnt – the ndspy module the ndspy.fnt.Folder class resides in.

files – the corresponding list of files that these filenames refer to.

Type:

ndspy.fnt.Folder

Default:

ndspy.fnt.Folder()

files

The list of files in this ROM. Indices are file IDs; that is, “files[0]” is the file with file ID 0, “files[1]” is the file with file ID 1, etc.

See also

filenames – the set of filenames for these files.

Type:

list of bytes

Default:

[]

sortedFileIds

For unknown reasons, ROMs sometimes store files in an order other than that of ascending file IDs. To preserve this order, this list contains file IDs in the order in which they should be saved in the ROM data. This is automatically populated when opening a ROM, and you should never really need to change this. (You can empty it to force files to be saved in order, though.)

If any file IDs are missing from this list, they will be placed in order of ascending file IDs after the files that are in the list. If this is empty, all files will be saved in order of ascending file IDs.

See also

files – the list of files these indices refer to.

Type:

list of int

Default:

[]

classmethod fromFile(filePath)

Load a ROM from a filesystem file. This is a convenience function.

Parameters:

filePath (str or other path-like object) – The path to the ROM file to open.

Returns:

The ROM object.

Return type:

NintendoDSRom

getFileByName(filename)

Return the data for the file with the given filename (path). This is a convenience function; the following two lines of code are exactly equivalent (apart from some error checking):

fileData = rom.getFileByName(filename)
fileData = rom.files[rom.filenames.idOf(filename)]

See also

setFileByName() – to replace the file data instead of retrieving it.

Parameters:

filename (str) – The name of the file.

Returns:

The file’s data.

Return type:

bytes

setFileByName(filename, data)

Replace the data for the file with the given filename (path) with the given data. This is a convenience function; the following two lines of code are exactly equivalent (apart from some error checking):

rom.setFileByName(filename, fileData)
rom.files[rom.filenames.idOf(filename)] = fileData

See also

getFileByName() – to retrieve the file data instead of replacing it.

Parameters:
  • filename (str) – The name of the file.

  • data (bytes) – The new data for the file.

loadArm7()

Create a ndspy.code.MainCodeFile object representing the main ARM7 code file in this ROM.

See also

arm7 – depending on what you’re trying to do, it may be more appropriate to just use this raw data attribute directly instead.

Returns:

The ARM7 code file.

Return type:

ndspy.code.MainCodeFile

loadArm7Overlays([idsToLoad])

Create a dictionary of this ROM’s ARM7 ndspy.code.Overlays.

See also

arm7OverlayTable – if you just want the raw overlay table data, you can access it from this attribute instead. This avoids the side effect of decompressing all of the overlay data (which can be slow).

Parameters:

idsToLoad (set of int) – A specific set of overlay IDs to load. You can use this to avoid loading overlays you don’t actually care about, in order to improve your application’s performance.

Returns:

A dict of overlays.

Return type:

dict: {overlayID: overlay} (where overlayID is of type int and overlay is of type Overlay)

loadArm9()

Create a ndspy.code.MainCodeFile object representing the main ARM9 code file in this ROM.

See also

arm9 – depending on what you’re trying to do, it may be more appropriate to just use this raw data attribute directly instead.

Returns:

The ARM9 code file.

Return type:

ndspy.code.MainCodeFile

loadArm9Overlays([idsToLoad])

Create a dictionary of this ROM’s ARM9 ndspy.code.Overlays.

See also

arm9OverlayTable – if you just want the raw overlay table data, you can access it from this attribute instead. This avoids the side effect of decompressing all of the overlay data (which can be slow).

Parameters:

idsToLoad (set of int) – A specific set of overlay IDs to load. You can use this to avoid loading overlays you don’t actually care about, in order to improve your application’s performance.

Returns:

A dict of overlays.

Return type:

dict: {overlayID: overlay} (where overlayID is of type int and overlay is of type Overlay)

save(*[, updateDeviceCapacity=False])

Generate file data representing this ROM.

Parameters:

updateDeviceCapacity (bool) –

If this is True, deviceCapacity will be updated based on the size of the output file data. It will be set to match the capacity of the smallest cartridge that would be able to hold the data.

default:

False

Returns:

The ROM file data.

Return type:

bytes

saveToFile(filePath, *[, updateDeviceCapacity=False])

Generate file data representing this ROM, and save it to a filesystem file. This is a convenience function.

Parameters:
  • filePath (str or other path-like object) – The path to the ROM file to save to.

  • updateDeviceCapacity (bool) –

    If this is True, deviceCapacity will be updated based on the size of the output file data. It will be set to match the capacity of the smallest cartridge that would be able to hold the data.

    default:

    False