ndspy.bmg: BMG (messages)

The ndspy.bmg module provides support for loading and saving BMG files.

In games that use them, BMG files generally contain most or all of the text that can be displayed to the player (apart from text embedded into images). Games contain one or more BMG files for each language they support, and will load the appropriate one depending on the console’s language setting. Each “message” is referenced by index.

Some BMG files – namely, those used in the DS Zelda games – can contain scripts that control game progression in addition to text. ndspy can read and save the file blocks that contain these scripts (FLW1 and FLI1), but decoding and encoding the instructions themselves is very game-specific and therefore out of its scope.

Examples

Load a BMG from a ROM and inspect its messages and scripts:

>>> import ndspy.rom, ndspy.bmg
>>> rom = ndspy.rom.NintendoDSRom.fromFile('Zelda - Spirit Tracks.nds')
>>> bmgData = rom.getFileByName('English/Message/castle_town.bmg')
>>> bmg = ndspy.bmg.BMG(bmgData)
>>> print(bmg)
<bmg id=12 (159 messages, 26 scripts)>
>>> print(bmg.messages[2])
What took you so long,
[254:0000]?

Did you keep me waiting
just so you could change
clothes?
>>> bmg.messages[2].stringParts
['What took you so long,\n', Escape(254, bytearray(b'\x00\x00')), '?\n\nDid you keep me waiting\njust so you could change\nclothes?']
>>> bmg.scripts[:5]
[(6553601, 9), (6553602, 140), (6553604, 117), (6553605, 124), (6553609, 183)]
>>> bmg.labels[:5]
[(12, 28), (-1, -1), (12, 0), (12, 68), (12, 73)]
>>> bmg.instructions[:5]
[bytearray(b'\x033\x00\x00e\x00\x00\x00'), bytearray(b'\x03\n\x01\x00\n\x00\r\x00'), bytearray(b'\x033\x02\x00\x03\x00\x00\x00'), bytearray(b'\x033\x03\x00\x02\x00\x00\x00'), bytearray(b'\x033\x04\x00\x04\x00\x00\x00')]
>>>

Load a BMG from a file, edit a message, and save it back into a ROM:

>>> import ndspy.bmg, ndspy.rom
>>> bmg = ndspy.bmg.BMG.fromFile('course.bmg')
>>> print(bmg.messages[15])
Welcome to the secret
Challenge mode. Think you can
reach the goal? If you get
stuck, press START and choose
Return to Map.
>>> bmg.messages[15].stringParts = ["Welcome to the secret\nChallenge mode where it's\nvery easy to softlock."]
>>> print(bmg.messages[15])
Welcome to the secret
Challenge mode where it's
very easy to softlock.
>>> rom = ndspy.rom.NintendoDSRom.fromFile('nsmb.nds')
>>> rom.setFileByName('script/course.bmg', bmg.save())
>>> rom.saveToFile('nsmb_edited.nds')
>>>

Create a new BMG using the cp1252 encoding, and save it to a file:

>>> import ndspy.bmg
>>> message1 = ndspy.bmg.Message(b'', ['Want to save your game?'])
>>> message2 = ndspy.bmg.Message(b'', ["Sure!\nNo thanks."])
>>> bmg = ndspy.bmg.BMG.fromMessages([message1, message2])
>>> bmg.encoding = 'cp1252'
>>> bmg.saveToFile('savegame-en-us.bmg')
>>>

API

class ndspy.bmg.BMG([data, ]*[, id=0])

A BMG file.

Parameters:
  • data (bytes) – The data to be read as a BMG file. If this is not provided, the BMG object will initially be empty.

  • id – The initial value for the id attribute. The BMG data itself might optionally specify its own ID; if it does, that value takes precedence and this parameter is ignored.

encoding

The encoding that should be used for storing strings in the BMG. Choosing an encoding is a trade-off between space efficiency, time efficiency, and the amount and choice of characters that can be encoded.

Valid encodings are cp1252, utf-16, shift-jis, and utf-8.

See also

fullEncoding – a read-only mirror of this property that includes endianness information, intended for use with str.encode() and bytes.decode().

Type:

str

Default:

'utf-16'

fullEncoding

A mirror property for encoding that takes endianness into account. This can be used with str.encode() or bytes.decode(), if for some reason you need to encode or decode raw string data matching this BMG’s encoding.

The value of this attribute will always be the same as that of encoding, unless that attribute has the value utf-16. In that case, this property will be either utf-16le or utf-16be, depending on endianness.

This attribute is read-only.

See also

encoding – a writable property you can use to modify the BMG’s encoding.

Type:

str

Default:

'utf-16le'

endianness

Whether values in the BMG should be stored using big- or little-endian byte order. Since the Nintendo DS is by default a little-endian console, almost every game uses little-endian BMG files. An exception to this is Super Princess Peach.

'<' and '>' (representing little-endian and big-endian, respectively) are the only values this attribute is allowed to take.

Type:

str

Default:

'<'

id

This BMG’s ID number. In at least some games, every BMG has a unique ID. This makes it possible to refer to specific messages by specifying the desired BMG ID and the message index within that BMG.

Type:

int

Default:

0

instructions

The script instructions in this BMG, if it has a FLW1 block. Instructions will be bytes objects by default, but when saving, any object that implements a .save() -> bytes method is acceptable in place of bytes. (This is to let you implement custom classes for instructions if you want to.)

Type:

list of bytes or of objects implementing .save() -> bytes

Default:

[]

labels

The script instruction labels in this BMG, if it has a FLW1 block.

Type:

list of (bmgID, instructionIndex) (both ints)

Default:

[]

messages

The list of Messages in this BMG.

Type:

list of Message

Default:

[]

scripts

The starting instruction indices for each script ID defined in this BMG, if it has a FLI1 block.

See also

ndspy.indexInNamedList(), ndspy.findInNamedList(), ndspy.setInNamedList() – helper functions you can use to find and replace values in this list.

Type:

list of (scriptID, instructionIndex) (both ints)

Default:

[]

unk14

Unknown header value at 0x14.

Type:

int

Default:

0

unk18

Unknown header value at 0x18.

Type:

int

Default:

0

unk1C

Unknown header value at 0x1C.

Type:

int

Default:

0

classmethod fromMessages(messages, [instructions, [labels, [scripts, ]]]*[, id=0])

Create a BMG from a list of messages.

Parameters:
  • messages – The initial value for the messages attribute.

  • instructions – The initial value for the instructions attribute.

  • labels – The initial value for the labels attribute.

  • scripts – The initial value for the scripts attribute.

  • id – The initial value for the id attribute.

Returns:

The BMG object.

Return type:

BMG

classmethod fromFile(filePath[, ...])

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

Parameters:

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

Further parameters are the same as those of the default constructor.

Returns:

The BMG object.

Return type:

BMG

save()

Generate file data representing this BMG.

FLW1 and FLI1 sections will be created only if any script instructions or scripts exist, respectively.

Returns:

The BMG file data.

Return type:

bytes

saveToFile(filePath)

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

FLW1 and FLI1 sections will be created only if any script instructions or scripts exist, respectively.

Parameters:

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

class ndspy.bmg.Message([info[, stringParts[, isNull]]])

A single message in a BMG file.

BMG messages are more than simple strings; they contain escape sequences that can specify font formatting and allow text to be inserted at runtime. For this reason, the message data is represented as a list of strings and Escapes instead of as a string.

Parameters:
  • info – The initial value for the info attribute.

  • stringParts – The initial value of the stringParts attribute. If you pass a bare string for this parameter, it will be automatically wrapped in a list for you.

  • isNull – The initial value for the isNull attribute.

info

A value containing message metadata, which comes from the BMG’s INF1 block.

The meaning of this value is completely game-dependent, and some games just leave this empty and don’t use it at all.

Warning

While the amount of metadata per message varies from game to game, it’s always required that all messages in a BMG have the same amount of metadata. If you violate this, you’ll experience errors when trying to save!

Type:

bytes

Default:

b''

isNull

This is True if the message is null; that is, if its data offset value in INF1 is 0. A null message should have an empty stringParts list.

Note

Messages with this attribute set to True are used to represent empty messages instead of None because empty messages can still have non-empty info values.

Type:

bool

Default:

False

stringParts

A list of strings and escape sequences that together form the message. Empty strings are allowed but discouraged.

Type:

list of str and of Escape

Default:

[]

save(encoding)

Generate binary data representing this message.

Parameters:

encoding (str) – The encoding to use for the string data in the message (i.e. 'utf-16', 'ascii', etc).

Returns:

The message data.

Return type:

bytes

class Message.Escape([type[, data]])

An escape sequence within a BMG message.

Escape sequences have a type and optional parameter data. Currently, the parameter data is left raw and unparsed; this may change in the future.

Parameters:
  • type – The initial value for the type attribute.

  • data – The initial value of the data attribute.

data

The raw data contained in this escape sequence.

Type:

bytes

Default:

b''

type

The type ID of this escape sequence.

Type:

int

Default:

0

save(encoding)

Generate binary data representing this escape sequence.

Parameters:

encoding (str) – The encoding that should be assumed when building the binary data for the escape sequence (i.e. 'utf-16', 'ascii', etc). This is used to properly encode the escape character itself, U+001A.

Returns:

The escape sequence data.

Return type:

bytes