Files
dusk/.claude/save.md
T
2026-06-18 14:59:21 -05:00

3.0 KiB

Save & Locale


Save system (src/dusk/save/)

Slot-based persistent storage. Currently disabled in engine init (commented out in engine.c) — the system is fully implemented but not yet wired up.

Slots

Up to SAVE_FILE_COUNT_MAX (3) save slots. The global SAVE holds all slots:

saveInit();
saveExists(slot);          // bool_t — check before load
saveLoad(slot);            // read from platform storage → SAVE.files[slot]
saveWrite(slot);           // write SAVE.files[slot] → platform storage
saveDelete(slot);
savefile_t *f = saveGet(slot);
saveDispose();

Save file format

savefile_t is the serialized struct stored per slot. Currently minimal:

typedef struct {
  char_t header[3];    // "DSK"
  uint32_t version;    // SAVE_FILE_VERSION = 1
  bool_t exists;
} savefile_t;

Extend this struct to add game-specific save data (player position, story flags, etc.).

Stream serialization (save/savestream.h)

savestream_t is a cursor used to read/write a save slot's bytes. It CRC32-checksums all data written through it and verifies the checksum on read.

Write a save:

savestream_t stream;
// (platform opens stream for slot)
saveFileWriteHeader(&stream, SAVE_FILE_HEADER);
saveFileWriteVersion(&stream, SAVE_FILE_VERSION);
saveFileWriteBool(&stream, myFlag);
saveFileWriteInt32(&stream, myInt);
saveFileWriteString(&stream, myString, sizeof(myString));
saveStreamFinalizeWriteImpl(&stream);   // writes CRC

Read a save:

saveFileReadHeader(&stream, headerBuf);
saveFileReadVersion(&stream, &version);
saveFileReadBool(&stream, &myFlag);
saveFileReadInt32(&stream, &myInt);
saveFileReadString(&stream, myString, sizeof(myString));
saveStreamVerifyChecksumImpl(&stream, slot);   // returns error if CRC mismatch

All multi-byte values are stored in little-endian byte order. The saveFile* macros are thin wrappers over the *Impl functions that integrate errorChain — always use the macros.

Platform backends

Each src/dusk{platform}/save/ provides saveplatform_t (e.g. a file path on Linux, a memory-card handle on GameCube). The stream implementations (savestream{platform}.c) do the actual I/O.


Locale / i18n (src/dusk/locale/)

Translations are stored as GNU .po files in assets/locale/. Only en_US.po currently exists.

Loading

localemanager_t LOCALE tracks the active locale and its in-progress asset entry:

localeManagerInit();                         // loads en_US by default
localeManagerSetLocale(&LOCALE_EN_US);       // switch locale (async load)
localeManagerDispose();

LOCALE_EN_US is a predefined localeinfo_t constant (name = "en-US", file = "locale/en_US.po").

Looking up strings

char_t buf[128];
localeManagerGetText("my.key", buf, sizeof(buf), 1, /* format args */ );

The macro handles plural forms and printf-style format arguments. Pass plural 1 for singular, any other value for plural.

assetlocaleloader.c parses the .po format (msgid / msgstr pairs) into a key→string table during the async asset load phase.