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])[source]¶ A BMG file.
Parameters: -
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, andutf-8.See also
fullEncoding– a read-only mirror of this property that includes endianness information, intended for use withstr.encode()andbytes.decode().Type: strDefault: 'utf-16'
-
fullEncoding¶ A mirror property for
encodingthat takesendiannessinto account. This can be used withstr.encode()orbytes.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 valueutf-16. In that case, this property will be eitherutf-16leorutf-16be, depending onendianness.This attribute is read-only.
See also
encoding– a writable property you can use to modify the BMG’s encoding.Type: strDefault: '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: strDefault: '<'
-
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: intDefault: 0
-
instructions¶ The script instructions in this BMG, if it has a FLW1 block. Instructions will be
bytesobjects by default, but when saving, any object that implements a.save() -> bytesmethod is acceptable in place ofbytes. (This is to let you implement custom classes for instructions if you want to.)Type: listofbytesor of objects implementing.save() -> bytesDefault: []
-
labels¶ The script instruction labels in this BMG, if it has a FLW1 block.
Type: listof(bmgID, instructionIndex)(bothints)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: listof(scriptID, instructionIndex)(bothints)Default: []
-
classmethod
fromMessages(messages, [instructions, [labels, [scripts, ]]]*[, id=0])[source]¶ Create a BMG from a list of messages.
Parameters: - messages – The initial value for the
messagesattribute. - instructions – The initial value for the
instructionsattribute. - labels – The initial value for the
labelsattribute. - scripts – The initial value for the
scriptsattribute. - id – The initial value for the
idattribute.
Returns: The BMG object.
Return type: - messages – The initial value for the
-
classmethod
fromFile(filePath[, ...])[source]¶ Load a BMG from a filesystem file. This is a convenience function.
Parameters: filePath ( stror 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()[source]¶ 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)[source]¶ 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 ( stror other path-like object) – The path to the BMG file to save to.
-
-
class
ndspy.bmg.Message([info[, stringParts[, isNull]]])[source]¶ 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
infoattribute. - stringParts – The initial value of the
stringPartsattribute. 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
isNullattribute.
-
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: bytesDefault: b''
-
isNull¶ This is
Trueif the message is null; that is, if its data offset value in INF1 is 0. A null message should have an emptystringPartslist.Note
Messages with this attribute set toTrueare used to represent empty messages instead ofNonebecause empty messages can still have non-emptyinfovalues.Type: boolDefault: False
- info – The initial value for the
-
class
Message.Escape([type[, data]])[source]¶ 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: -
save(encoding)[source]¶ 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
-