Persist shared profile on clean shutdown

This commit is contained in:
gamer147
2026-07-24 23:55:06 -04:00
parent 0a4e200876
commit 1828701872
9 changed files with 370 additions and 13 deletions

View File

@@ -1929,8 +1929,11 @@ Persistence is shared across numbered save slots. `shared_profile_save@0x40c950`
`SAVE.DAT`, then serializes `ReadTextDB` through `$$RT.DAT` to `RT.DAT`, with `RT.BAK` handling.
`shared_profile_load@0x40ccd0` loads `SAVE.DAT` (falling back to `SAVE.BAK`) and then independently loads
`RT.DAT` when present. Numbered saves use the separate `SAVE%2.2d.DAT` pattern. A successful context/slot
save calls the shared-profile writer, and shutdown also calls it unless `set:NoSaveDat` suppresses shared
data writes.
save calls the shared-profile writer directly. On accepted `WM_CLOSE`, `age_main_window_proc@0x486320`
queries `set:NoSaveDat` and calls the same writer only when that value is zero; forced and confirmed close
share that post-acceptance path. `engine_settings_register_defaults@0x46be30` registers `NoSaveDat=0`.
Thus the switch suppresses only the shutdown write, not the shared flush following a successful numbered
save.
The selected integer-cell portion of shared `SAVE.DAT` is the `0x1a2` store / `0x1a3` restore service
documented above. It is independent of the `RT.DAT` read-message database even though the shared-profile
@@ -1951,6 +1954,13 @@ pointer residue, and writes zero in that ignored field. `DirectoryNativeDatStore
`$$RT.DAT` → `RT.DAT` / `RT.BAK` replacement; load follows native behavior and reads `RT.DAT` directly
rather than treating `RT.BAK` as a fallback. `SharedProfile.ReadText` owns records and the pending queue
across fresh VMs, and shared-profile Save/Load updates SAVE.DAT and RT.DAT as separate native domains.
Godot now requests a VM stop, releases the blocking host, waits for the VM worker to leave its opcode
boundary, and performs one shared-profile flush before frontend teardown. Repeated exit notifications do
not rotate backups twice; expected I/O failures are reported without crashing teardown; self-test uses
`NoSaveDat`; and the loaded shared header's accumulated-playtime value seeds the new process baseline.
Focused restart coverage proves selected integer/string cells and committed read flags survive a clean
exit without any numbered slot write. `NoSaveDat` suppresses that exit write while a successful `0x19e`
still writes both shared files.
Scripts now retain their packed resource id and decoded F7/T1 table. Opcode `0x71` commits pending tuples
and snapshots its T1 coordinate; `0x6e`/`0x71`/`0x72` refresh read eligibility; wait completion queues
@@ -1958,8 +1968,8 @@ and snapshots its T1 coordinate; `0x6e`/`0x71`/`0x72` refresh read eligibility;
`message:ReadTextSkip` setting, while `0x1cc` reports the current message state. This implements native
read-message behavior without scene-offset special cases and leaves JSON/export tooling as extended mode.
The `/v2` Ghidra image now names/comments the lookup, queue, commit, mark, file read/write, and shared-profile
save/load chain and corrects the relevant function prototypes; saved 2026-07-18.
The `/v2` Ghidra image now names/comments the lookup, queue, commit, mark, file read/write, shared-profile
save/load chain, exact `WM_CLOSE` gate, and `NoSaveDat=0` default; saved/refined through 2026-07-24.
### Remaining ADV control-strip actions and implementation cost (2026-07-18)

View File

@@ -3730,8 +3730,34 @@ the numbered-load reload policy; bit 1 remains identified but has no known consu
Validation: engine **393/393**, clean opcode lint/tooling, zero-warning Godot build, threaded
`SELFTEST OK`, and exact slot-006 software replay with zoom 80 and a nonblack dungeon raster.
Acceptance: rebuild and interactively load existing slot 006. The expected result is the restored
dungeon map and controls without a re-save.
Manual acceptance passed: the unchanged slot 006 restores its dungeon map and controls without a re-save.
### Persistence implementation step 14 — shared-profile shutdown lifecycle closeout (2026-07-24)
Shared `SAVE.DAT` and `RT.DAT` already loaded at Godot startup and flushed after successful numbered
opcode `0x19e`, but closing the port only disposed frontend resources. Profile-selected cells and
committed ReadTextDB flags could therefore be lost if the user exited without making a numbered save.
Native `age_main_window_proc@0x486320` resolves the policy exactly. Once `WM_CLOSE` is forced or accepted
by the normal confirmation path, it queries `set:NoSaveDat`; zero calls `shared_profile_save@0x40c950`
before teardown and nonzero skips it. `engine_settings_register_defaults@0x46be30` registers zero.
Successful numbered/context save calls the shared writer directly, so `NoSaveDat` gates shutdown only.
Both native functions are annotated in the saved `/v2` image.
Godot now requests a clean VM stop, releases the blocking host, waits up to five seconds for the worker
to leave its opcode boundary, and only then flushes the shared profile. This avoids serializing while the
VM can still mutate selected cells or read flags. The shutdown operation is idempotent across repeated
frontend notifications, reports expected I/O failures without throwing through teardown, and skips the
write rather than racing if the worker cannot stop. Self-test uses the native `NoSaveDat` seam and remains
filesystem-isolated.
`SharedProfile` now retains the accumulated-playtime value from the loaded `SAVE.DAT` header; a new VM
adds its elapsed process time to that baseline instead of resetting the shared header to the current
launch. Six focused regressions prove restart continuity without any numbered save, `NoSaveDat`
suppression, numbered-save independence from that switch, single-write teardown, playtime preservation,
and graceful I/O failure.
Validation: engine **399/399**, zero-warning Godot build, and threaded `SELFTEST OK`.
## Data-semantics sidebar: focused append EBINIT inspection (2026-07-24)

View File

@@ -361,8 +361,9 @@ In scope:
Deferred to bounded follow-ups unless the happy path requires them:
- Full configuration UI and every setting.
- Full load/save opcode and logical-payload implementation. Native format reversal and the common DAT
codec/store foundation landed on 2026-07-24.
- Remaining configuration/save UI branches beyond the native compatibility floor. Shared and numbered
codecs, real SAVE.BIN listing/load, port-authored round trips, and clean-shutdown profile persistence
landed on 2026-07-24.
- Extras, galleries, replay modes, and unrelated submenus.
- Menu visual polish that does not obstruct correct selection or state production.

View File

@@ -491,7 +491,10 @@ the later SYSTEM4 unwind that otherwise re-entered the Eushully intro. Slot 005
base-load and stage-launch round trip. Dungeon-authored slot 006 exposed one further native-ordering
requirement: restored scripts must begin at their ordinary entry, run frame-local prologues, and reach
`0xae` themselves. Matching that order restores FIELD's 80% zoom table entry and produces the dungeon
map from the unchanged slot; interactive slot-006 confirmation is the remaining visual gate.
map from the unchanged slot; interactive slot-006 confirmation passed. The shared-profile lifecycle is
also closed: accepted frontend shutdown stops and joins the VM worker before writing `SAVE.DAT` plus
`RT.DAT`, honors native `NoSaveDat=0` shutdown policy without suppressing numbered-save flushes, preserves
loaded accumulated playtime, and handles repeated teardown and I/O failure safely.
JSON inspection/export,
namespaced mod data, and migrations remain additive extended-mode work rather than 1.0 compatibility
requirements.