Match native surface persistence policy

This commit is contained in:
gamer147
2026-07-24 20:28:59 -04:00
parent abda5b21c1
commit d099a073a8
14 changed files with 299 additions and 48 deletions

View File

@@ -1750,9 +1750,9 @@ diagnostic/extended-mode surface.
#### Layout-3 restore mechanics and port correspondence (2026-07-24)
The port tracks the native global banks separately at runtime, captures the marked frame chain, serializes
live surfaces and retained objects, and reloads host textures from the 20-byte surface records. Full load
first replaces each serialized mutable bank prefix while preserving initialization-authored cells beyond
its count, restores history/gfx and retained audio, unwinds the obsolete managed call chain, runs
live surfaces and retained objects, and applies the 20-byte surface records' explicit reload flags. Full
load first replaces each serialized mutable bank prefix while preserving initialization-authored cells
beyond its count, restores history/gfx and retained audio, unwinds the obsolete managed call chain, runs
`CALLBACK_LOAD.BIN` when
the mounted script provider resolves it, starts the saved root at its `0xae` rendezvous, recursively
reconstructs child frames, resumes parents after their saved T2 call sites,
@@ -1775,6 +1775,31 @@ tracks both lifecycles through `0xbf`/`0xc2` and `0xb4`/`0xb6`, serializes them,
track plus saved SFX resources. The new EngineCtx fields and function annotations are applied to the
saved `/v2` image.
The surface records have a related preservation boundary. Resource id is at `+0x00`, packed color key at
`+0x04`, `+0x08` is a reload flag rather than a general presence bit, `+0x0c` remains unknown, and
`+0x10` marks a blank/mutable created surface. `gfx_surface_record_tables_init@0x472700` zeroes both
1,000-record tables and initializes every resource id to `-1`.
`gfx_surface_create_blank@0x477370` writes resource `-1` and created flag one;
`gfx_surface_load_asset@0x477c40` writes resource/color and clears the created flag. Neither operation
changes the reload flag. The layout-3 serializer copies all 20,000 live record bytes verbatim, rather
than deriving reloadability from the presence of an asset.
The restore loop calls `gfx_surface_load_asset` only when the saved reload flag is one and resource id is
nonnegative. Otherwise the already-initialized surface survives. The optional all-surface release is
separately gated by both `set:CreateObject` and `set:AutoFreeTex`; the registered defaults at
`engine_settings_register_defaults@0x46be30` are one and zero respectively, matching Himegari's active
path. Opcode `0x259` is also a real lifecycle operation:
`op_0x259_script_entry_clear_surface_persistence_flags@0x417660` clears record `+0x08` and `+0x0c`
across both native tables at every script entry.
Installed slot 15 records resource `0x3383` with reload flag zero. SYSTEM4 loads that atlas before the
save UI, and BUNKI cuts its reusable choice-box corners, borders, and winged top ornament from it. The
port formerly released all 1,000 host surfaces unconditionally, so post-load BUNKI retained its black
backing but lost the atlas decoration. Restoration now preserves live surfaces and overlays only flagged
reload records by default, while `VmOptions.CreateObject && VmOptions.AutoFreeTextures` reproduces the
native all-release branch. Port-authored saves emit the exact resource/reload/created fields, including
`-1` for unused records, and opcode `0x259` clears the modeled reload policy without releasing textures.
The corrected native functions are renamed/commented in the saved `/v2` image.
### Opcode `0xae` continues numbered-save stack restoration (2026-07-20)
Opcode `0xae` is the load-side rendezvous paired with serialized script-frame state. Its handler,

View File

@@ -924,6 +924,11 @@ The setter get-or-creates the object and writes the complete operand. During ret
- **grounding:** source=investigation, confidence=high
- **evidence:** Ghidra /v2: op_0x24e_handler@0x425070 writes operand 1 directly to EngineCtx.gfx_animation_service_flags at +0x51b80. The mapped field is also read by op 0x243: bit 1 suppresses its force-complete/clock-reset request.
### 0x259 `script-entry` (script-entry, argc 0)
- **summary:** zero-arg script/prologue entry; clears surface-record persistence fields +0x08 and +0x0c across both 1,000-record native tables before the declaration chain continues
- **grounding:** source=investigation, confidence=high
- **evidence:** Ghidra /v2: op_0x259_script_entry_clear_surface_persistence_flags@0x417660 loops over both 20,000-byte surface-record tables (1,000 records x 20 bytes), writing zero to record +0x08 and +0x0c. SC0000 offset 0x0 and every observed script entry begins with this opcode; 0x258 continues the declaration chain. Port clears its modeled +0x08 reload policy; +0x0c remains opaque/unmodeled.
## input
### 0x86 `set-cursor-resource` (u0041B210, argc 1)
@@ -1149,11 +1154,6 @@ Port status (2026-07-24): implemented through the same profile-lifetime setting
- **summary:** 2 imm; runs in a chain right after script-entry 0x259, enumerating ids — prologue declaration/registration?
- **grounding:** source=harness, confidence=low, noop_headless=True
### 0x259 `script-entry` (u00416410, argc 0)
- **summary:** zero-arg; the first instruction of a script (offset 0), opens the decl chain that 0x258 continues — script/prologue entry marker, structural
- **grounding:** source=harness, confidence=low, noop_headless=True
- **evidence:** SC0000 offset 0x0 = op 0x259 (argc 0); 0x258's summary names it 'script-entry 0x259'; VM treats it as no-op (default stub) across all 279 CLEAN A0 scenes
## unknown
### 0x2 `exit` (exit, argc 0)

View File

@@ -3597,6 +3597,37 @@ then confirmed that CHMENU, unit management, SELSTAGE, and FORT audio populate c
visible load-specific discrepancy is the reusable choice-box frame losing its top winged flourish after
restoration; that is the next independent investigation slice.
### Persistence implementation step 9 — preserve flag-zero system surfaces (2026-07-24)
The post-load choice-box discrepancy came from the graphics half of the same ownership rule. BUNKI builds
the common modal frame from SYSTEM4's texture slot 15: resource `0x3383` supplies its corners, borders,
selection bar, and winged top ornament. The installed layout-3 record retains that resource id but stores
zero at record `+0x08`.
Native RE confirms `+0x08` is a reload flag, not general surface presence.
`save_data_deserialize_and_begin_restore@0x40fd10` calls `gfx_surface_load_asset` only for flag-one
records. Flag-zero system surfaces remain live unless a separate all-1,000 release path is enabled by
both `set:CreateObject` and `set:AutoFreeTex`. The port instead released every host surface and cleared
the complete managed surface map before applying only flag-one records. That removed slot 15, leaving
BUNKI's black backing but none of its atlas decoration.
Full load now preserves the current surface registry, overlays explicitly reloadable saved records, and
still replaces retained objects and the range transform. Regressions retain an initialized flag-zero
slot across a synthetic full load, prove that it is not redundantly reopened, and pin the installed
slot-15 resource/reload-flag oracle. The `/v2` deserializer annotation records the corrected ownership
boundary and the program is saved. Manual slot-000 acceptance confirms the stage-entry and later BUNKI
choices retain the complete winged frame.
The follow-up fidelity pass closes the two deferred policy gaps. `VmOptions` now carries the native
`CreateObject`/`AutoFreeTex` settings with Himegari's registered defaults (`true`/`false`), and enables
the all-1,000 host/model release only when both are true. Surface snapshots carry a named per-slot reload
bit; ordinary texture loads no longer manufacture it. Native encoding initializes unused resource ids to
`-1`, preserves resource/color/reload/created fields, and decoding distinguishes stored records from the
subset that must actually be reopened. Opcode `0x259`, formerly treated as a structural marker, is now
mapped and implemented as the script-entry clear of record `+0x08`/`+0x0c` (the port models `+0x08`).
Regressions cover native-default preservation, opt-in all-release, flag-for-flag port-authored output,
script-entry clearing, and the installed all-zero reload-bit oracle.
## Data-semantics sidebar: focused append EBINIT inspection (2026-07-24)
The static INIT surface now accepts a universal packed script id for focused append inspection.

View File

@@ -477,7 +477,10 @@ the `0xae` rendezvous. The real `SAVE.BIN` script is covered end to end for list
read-only installed-save gate now continues through `CALLBACK_LOAD`, reconstructs
`SYSTEM4.BIN → FORT.BIN`, and reaches FORT's `CHMENU` gameplay poll. Full restoration replaces only the
serialized mutable bank prefixes (preserving initialized unit/stage/string definitions), restores the
retained BGM/SFX state, and rebuilds graphics/history/frame state. JSON inspection/export,
retained BGM/SFX state, preserves initialized flag-zero system surfaces while overlaying explicit saved
reload records, reproduces the native configuration-gated all-surface release when explicitly enabled,
and writes native per-slot reload/created metadata rather than treating every texture as reloadable.
Opcode `0x259` supplies its real script-entry reload-policy clear. JSON inspection/export,
namespaced mod data, and migrations remain additive extended-mode work rather than 1.0 compatibility
requirements.

View File

@@ -252,11 +252,27 @@ Retained graphics then uses:
... native allocation slack
```
The 1,000 surface records preserve resource reload state; mapped fields include resource id at `+0x00`,
packed color key at `+0x04`, and presence at `+0x08`. A retained `0x2d4` record is structurally complete
but not every internal graphics field is semantically named. The native allocation is larger than the
records actually written (`0x2e1 + object_count * 0x2d8` DWORDs in the graphics sizing term), leaving
zero/slack bytes after the meaningful range record.
The 1,000 surface records preserve the native 20-byte metadata cells verbatim:
| Offset | Size | Meaning |
|---:|---:|---|
| `+0x00` | 4 | signed resource id; `-1` means no reloadable asset |
| `+0x04` | 4 | packed color key |
| `+0x08` | 4 | reload-on-restore flag |
| `+0x0c` | 4 | unknown lifecycle field; cleared with `+0x08` at script entry |
| `+0x10` | 4 | blank/mutable created-surface flag |
Initialization writes resource id `-1` to every otherwise-zero record. Ordinary asset load, blank-surface
creation, and release do not synthesize or clear the reload flag; opcode `0x259` clears `+0x08` and
`+0x0c` across both live/saved tables at script entry. During normal layout-3 restoration AGE reopens only
records whose reload flag is one and whose resource id is nonnegative. Flag-zero records describe
surfaces expected to remain live across the load. The installed file is decisive: all 1,000 reload flags
are zero, while SYSTEM4's reusable choice-frame atlas remains recorded as resource `0x3383` in slot 15.
A separate configuration-gated path can release all 1,000 surfaces before this loop, but Himegari's
registered `CreateObject=1`, `AutoFreeTex=0` defaults leave it inactive. A retained `0x2d4`
record is structurally complete but not every internal graphics field is semantically named. The native
allocation is larger than the records actually written (`0x2e1 + object_count * 0x2d8` DWORDs in the
graphics sizing term), leaving zero/slack bytes after the meaningful range record.
The installed `SAVE00.DAT` validates the complete layout-3 decode: cutoff 1, global-bank counts
`[402459,1,789,1,1,1]`, current BGM id `0x18`, retained SFX ids `0x3321` (channel 1) and