code wiki / _hdl_build / nx_gamesave.nx
nx_gamesave.nx
buildroot/runtime/_hdl_build/nx_gamesave.nx
about
nx_gamesave.nx -- the SOVEREIGN SAVE/LOAD PART. nx_gamebench ranked this #1: save-load-persistence was
PARTIAL and blocked 11 of the 12 benchmarked reference titles -- the highest-demand missing capability in
the whole game ecosystem, found by demand-ranking rather than by eye.
WHAT WAS ACTUALLY MISSING: runtime/nx_save_slot.nx already signs a combined hash over two in-memory
chromatin snapshots -- but it never writes bytes to disk, never reads them back, and has no gate. A game
cannot save with it. This organ is the missing half: arbitrary game state <-> a durable file.
Built as a CERTIFIED COMPOSABLE PART per knowledge/registry/game_parts.tsv doctrine ("build each part to
exceed ONCE, then compose forever"), so every future emitted game inherits these properties for free:
1. BIT-EXACT ROUND-TRIP -- save->load->save is byte-identical. Our stack is all-integer with no float
and no hidden clock (saved_at is CALLER-SUPPLIED on purpose), so a save file is reproducible. This is
the determinism exceed carried into persistence; most engines cannot claim it.
2. ATOMIC -- write to .tmp, fsync, rename. A crash mid-save can never leave a torn save file. A reader
only ever observes a complete file.
3. CORRUPTION REFUSED LOUD -- a single flipped byte fails the checksum and load returns an error code.
It never hands back half-valid state. (The classic "corrupt save eats an 80-hour run" bug.)
4. FUTURE-VERSION REFUSED -- a newer format is rejected rather than misparsed.
5. ADDITIVE-ONLY (rule 13) -- saving over a slot banks the previous bytes as .prev. History is not
destroyed, so a bad save is always recoverable.
Self-contained (imports only nx_syscalls + the pure core) so it runs on the NAS as an MCP tool.
seq1151 (2026-07-30): the byte<->i64 and rolling-fold MECHANISM moved to nx_gamesave_core.nx --
the ONE definition shared with the wasm save-image surface (nx_wasm_craft). This file is the FILE
surface: atomicity, .prev banking, refusal codes. Format bytes UNCHANGED (gate-proven).
license_tier: ORIGINAL expect_exit: 0
dependencies 2 imports · 27 importers
diagram shows first 10 each side; +0 more imports, +17 more importers in the complete lists below.
imports: nx_syscalls.nxnx_gamesave_core.nx
imported by: nx_char_identity_gate.nxnx_coc_engine.nxnx_collide2d_gate.nxnx_drift_engine.nxnx_entity_store_gate.nxnx_frontier_engine.nxnx_game_actor_gate.nxnx_game_breed_gate.nxnx_game_companion.nxnx_gamehud_gate.nxnx_gamemusic_gate.nxnx_gamesave_gate.nxnx_genome_gate.nxnx_loottable_gate.nxnx_m2d_engine.nxnx_netsync_gate.nxnx_plotgen_gate.nxnx_rpgstats_gate.nxnx_vnsprite_gate.nxnx_voxchunk.nxnx_wardrobe_state.nxnx_wardrobe_state_gate.nxnx_wire_diablo2_gate.nxnx_wire_endlesssky_gate.nxnx_wire_nethack_gate.nxnx_wire_probe.nxnx_worldsim_gate.nx
structs
| none |
consts
| 30 | const GS_MAGIC_1469598103: i64 = 1469598103 |
| 31 | const GS_MAGIC_16777619: i64 = 16777619 |
| 33 | const GS_FMT_VER: i64 = 1 |
| 34 | const GS_HDR: i64 = 48 |
| 35 | const GS_MAXFIELDS: i64 = 65536 |
| 38 | const GS_E_OPEN: i64 = 0-1 |
| 39 | const GS_E_SHORT: i64 = 0-2 |
| 40 | const GS_E_MAGIC: i64 = 0-3 |
| 41 | const GS_E_VERSION: i64 = 0-4 |
| 42 | const GS_E_FIELDS: i64 = 0-5 |
| 43 | const GS_E_CKSUM: i64 = 0-6 |
| 44 | const GS_E_CAP: i64 = 0-7 |
| 45 | const GS_E_WRITE: i64 = 0-8 |
functions
| 47 | func gw(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } calls 1: sys_write |
| 48 | func gn(v: i64) -> i64 |
| 57 | func slen(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return n } called by 1: joinsuf |
| 59 | func joinsuf(base: *u8, suf: *u8) -> *u8 |
| 70 | func put64(b: *u8, off: i64, v: i64) -> i64 { return gsc_put64(b, off, v) } |
| 71 | func get64(b: *u8, off: i64) -> i64 { return gsc_get64(b, off) } |
| 74 | func gs_cksum(b: *u8, n: i64) -> i64 |
| 77 | func gs_exists(p: *u8) -> i64 |
| 86 | func gs_save(path: *u8, schema_id: i64, state: *i64, n: i64, saved_at: i64) -> i64 |
| 123 | func gs_load(path: *u8, out: *i64, cap: i64, meta: *i64) -> i64 |
| 153 | func gs_verify(path: *u8) -> i64 |
| 159 | func gs_errname(e: i64) -> *u8 called by 1: main |