goldbox.yaml_io¶
Export a save’s party to YAML and import it back.
The YAML codec, one of four around the record goldbox/neutral.py describes
and not the thing the others convert through. entry_for reads a
NeutralCharacter and never a CharacterRecord, so a C64 party and a DOS one
render through one code path without either being converted to the other
first; import_into is the other direction and writes a C64 save.
The design goal is a lossless round-trip: exporting a save and importing it unchanged must reproduce the original file byte for byte. That is what makes the tool safe to use on a real save, and it is asserted by the tests.
Only fields we actually understand are editable. Everything else — the ~82% of
each record still unidentified, the party header, and everything in
SAVEDGAME1 past its first page — is carried through untouched, because an edit
must never destroy bytes whose meaning we do not know.
Pool of Radiance’s save is two files, and both are written. SAVEDGAME0 holds
the character records; SAVEDGAME1 opens with eight roster blocks holding the
values the game derives — armour class, THAC0, current hit points, movement
and the damage bonus. Those appear under combat:, together with the three
bytes at +0x03–+0x05 whose meaning is not established, and are the only part
of SAVEDGAME1 this module touches. Curse of the Azure Bonds and the four
titles after it write one file with the same roster as its last page; which
shape a disk has is goldbox/games.py’s business, not this module’s.
The document records the title it came from, as game:, and an import into
a different title’s disk is refused. The container geometry differs between them
and the race and class tables differ again, so a silent cross-title import would
write a plausible-looking corrupt save.
Two fields the game stores twice are kept in step, because writing one without
the other leaves a record no save has ever been seen in: the class code at
0x073 follows the bitmask at 0x0EB, and the character level at 0x0A0
follows the per-class level array.
Items keep their raw bytes in the YAML alongside friendly values. On import the
raw bytes are the starting point and the friendly fields are applied over them,
so an item survives a round-trip exactly while readied and quantity remain
editable. Item names are indices into the game’s own table and are exported
for reference only.
Module Attributes
Pool of Radiance's four, for a caller with no |
|
Donald's wording, approved verbatim (#145) -- no slot, no name, no second sentence. |
Functions
|
The single class code matching a class bitmask. |
|
|
|
One |
|
Read a save disk and return the whole party as plain data. |
|
Apply a parsed YAML document to a save disk, writing to |
|
Class name -> the record field holding that class's level, per title. |
|
|
|
The same document without its presentation-only keys. |
|
Emit the document with field comments and in-game ordering. |
Exceptions
Raised with a message a person can act on. |
- goldbox.yaml_io.LEVEL_FIELDS = {'cleric': 'level_cleric', 'fighter': 'level_fighter', 'magic-user': 'level_magic_user', 'thief': 'level_thief'}¶
Pool of Radiance’s four, for a caller with no
Gamein hand.
- goldbox.yaml_io.NAME_CHANGE_REFUSED = 'ERROR: Name field cannot be changed.'¶
Donald’s wording, approved verbatim (#145) – no slot, no name, no second sentence. Kept as one constant so it only needs changing in one place.
- exception goldbox.yaml_io.ValueError_[source]¶
Bases:
ValueErrorRaised with a message a person can act on.
- goldbox.yaml_io.class_code_for(bits, game=None)[source]¶
The single class code matching a class bitmask.
Three combinations have no code in the game’s table – magic-user/cleric/ thief, cleric/thief/fighter, and all four at once. Refuse them rather than write a code that means something else.
Delegates to
goldbox.classcode.code_for(#310), which is the mask-only case of the same rulegoldbox.dos.writeandgoldbox.c64_codec.readboth repair a stale code with.gamepicks the title’s own table –Nonemeans Pool of Radiance’s, the table this function has always used.- Parameters:
bits (int)
game (goldbox.games.Game | None)
- Return type:
- goldbox.yaml_io.comments_for(game)[source]¶
FIELD_COMMENTSwith the race and class lists this title actually has.The lists differ per title, and a comment naming Pool of Radiance’s races on a Silver Blades export would be an instruction to write a wrong number. An unknown list says so instead of listing nothing.
- Parameters:
game (goldbox.games.Game | None)
- Return type:
- goldbox.yaml_io.entry_for(char, slot_index, items, icon, game=None, names=None, types=None, spell_names=None, block=None)[source]¶
One
goldbox.neutral.NeutralCharacteras plain data.The YAML writer: a codec beside the C64, DOS and Amiga ones rather than the thing they convert through. It reads neutral field names and never a
CharacterRecord, so a C64 party and a DOS one render through one code path without either of them being converted to the other first.items,iconandblockcome in beside the character because they are not the record: a save keeps items on their own page, icons in a table of their own, and the derived combat numbers in the roster.- Parameters:
slot_index (int)
game (goldbox.games.Game | None)
- Return type:
- goldbox.yaml_io.export_save(path, game_disk=None, game=None)[source]¶
Read a save disk and return the whole party as plain data.
The title is identified from the disk unless one is named.
- goldbox.yaml_io.import_into(save_path, data, out_path, game_disk=None, game=None)[source]¶
Apply a parsed YAML document to a save disk, writing to
out_path.Returns a human-readable list of the changes made. The input file is never modified; the caller chooses the destination.
- goldbox.yaml_io.level_fields(game)[source]¶
Class name -> the record field holding that class’s level, per title.
One entry per class the title has, because the level array has one slot per class bit. A class we can name but have no field for is left out rather than pointed at the wrong byte.
- Parameters:
game (goldbox.games.Game | None)
- Return type:
- goldbox.yaml_io.names_to_classes(value, game=None)[source]¶
- Parameters:
game (goldbox.games.Game | None)
- Return type:
- goldbox.yaml_io.strip_annotations(data)[source]¶
The same document without its presentation-only keys.
Keys beginning with
_carry text we render as a YAML comment rather than as data – currently just the item-type summary. They are derived from the game disk, not from the save, so they must not survive a round-trip.
- goldbox.yaml_io.to_yaml(data)[source]¶
Emit the document with field comments and in-game ordering.
Hand-rolled because PyYAML cannot write comments.
yaml.safe_loadreads it back identically – asserted in the tests – and every scalar is rendered by PyYAML itself, so quoting and escaping stay correct.