diff --git a/docs/engine-re.md b/docs/engine-re.md index 69717f3..273ae18 100644 --- a/docs/engine-re.md +++ b/docs/engine-re.md @@ -164,6 +164,76 @@ binary for every resolution. `display:ScreenMode`, `display:FullScreenWidth/Heig related registered settings govern presentation/fullscreen selection separately; they do not redefine the authored logical canvas. The serialized trailer location is recorded in `sys4-format-notes.md`. +### SYS4INI startup-settings catalog (resolved 2026-07-28) + +The settings trailer is a real per-game startup profile, not a miscellaneous string appendix. The complete +36-pair serialized inventory and physical offsets are canonical in `sys4-format-notes.md`; this section owns +their native behavior and current port relevance. + +The startup order is: + +1. `engine_settings_register_defaults@0x46be30` creates AGE's generic registry. +2. `engine_load_sys4ini_and_mount_append_catalogs@0x4099f0` parses the asset directory and VM-bank metadata, + then passes the remaining length-delimited settings record to + `EngineCtx::engine_apply_sys4ini_settings@0x4056d0`. +3. `engine_settings_import_sys4ini_pairs@0x46da80` reads the pair count followed by NUL-terminated CP932 + key/value strings, matches external names case-insensitively, and inserts their canonical typed keys. + Unknown external names take the loader error path. +4. `engine_apply_sys4ini_settings` copies `set:WinX`, `set:WinY`, and `set:AntiFontVersion` into context + fields. `engine_initialize_subsystems_from_settings@0x415390` then initializes text, graphics, and audio + from the completed registry before script execution. + +Most entries are direct integer/string mappings, but three transforms matter: + +- `SAVEVERSION=310` becomes `set:SaveVersion1=3` and `set:SaveVersion2=10` by quotient/remainder division + by 100. It is the source of Himegari's persistence layout and append-catalog compatibility version. +- `NOSETMUSIC=3` stores `set:NoSetMusic=3` and also seeds `sound:Music=2` + (`NOSETMUSIC - 1`). `sound_route_is_enabled@0x405420` treats nonnegative music states as enabled; + `sound_set_music_route_enabled@0x407f80` moves the state between enabled/disabled bands separated by + three. The `set:NoSetMusic` copy itself has no later direct reader in this image. +- `WHEELKEYUP`/`WHEELKEYDOWN` each populate both `set:WheelKey*` and + `message:WheelKey*OnTW`. A nonempty `VERREGPOS` also invokes + `registration_read_class00_version@0x46ae80`; its Windows `CLASS00` registry result replaces + `set:GameVersion`. + +The meaningful consumer catalog is: + +| Setting group | Native Himegari effect | Classification | +|---|---|---| +| `CREATEOBJECT`, `DRAWMODE` | `CreateObject=2` selects only the retained D3D/object backend; `DrawMode=1` selects its matching draw/text path. Startup verifies that bit `1 << DrawMode` exists in the backend mask. | Active renderer selection | +| `SCREENX`, `SCREENY` | Replaces the generic `640x480` fallback with the `800x600` logical canvas before window/surface creation. | Active core profile | +| `FONT`, `ENABLEANTIFONT`, `ANTIFONTVERSION` | Seeds MS Mincho, enables `message:UseAntiFont`, selects the grayscale glyph-outline path, and records antialias version 3. | Active text profile | +| `DEPENDMOVIESOUND` | Supplies the default movie-audio dependency/routing policy when movie opcode flags do not force another route. | Active media policy | +| `FULLSCREENBIT`, `ALWAYSBACKUPSURFACE` | These are read only inside the legacy `CreateObject & 1` DirectDraw branch of `engine_initialize_graphics_from_settings@0x406480`. Himegari selects only bit 2, so its 32-bit/zero values do not drive the active renderer. | Valid compatibility settings, inert for this profile | +| `SCREENWARNING` | Imported as `set:ScreenWarning`; no reader beyond default registration/import was found in this EXE image. | Loaded, no observed consumer | +| `NOSETMUSIC` | Seeds the live music-route state as described above; the route starts enabled. | Active audio initialization plus otherwise-unused policy copy | +| all seven `MENU_*` keys | Imported as `set:Menu_*`. No direct non-loader reader of these exact registry keys was found; they appear to be shared AGE native-menu capability metadata rather than Himegari script-menu state. `MENU_USEANTIFONT` does **not** disable the independently enabled `message:UseAntiFont`. | Loaded compatibility/menu metadata | +| `ENABLEMEMFLIP` | Gates `op_0x6d`, which swaps VM-bank pointer pairs only when enabled. Himegari disables it and its corpus does not use that opcode. | Compatibility capability gate | +| `CLICKONUP` | Imported as `set:ClickOnUp`, but no direct runtime reader was found in this image. | Loaded, no observed consumer | +| `CANCELMESSKIPONCLICK` | Himegari overrides the zero fallback with 2, enabling `adv_interpreter_tick`'s press/release click-cancel state machine for persistent message Skip. | Active ADV input behavior; currently missing from port | +| `CONTROLDISIBLECURSOR` | The spelling is native. Value 1 suppresses AGE's ordinary cursor-restore call when a bound ADV hotspot action or hover callback is selected. | Active ADV cursor behavior; currently missing from port | +| `COEXISTMESSKIP` | Value 1 lets Auto and all-message Skip remain simultaneously enabled. With zero, `adv_toggle_auto_mode@0x406b70` clears Skip and `adv_toggle_skip_mode@0x406c20` clears Auto. | Active ADV toggle behavior; currently missing from port | +| `REDRAWTEXTONKEY` | Value 0 disables the wheel/key path that traverses and republishes the current text-history view through `CALLBACK_TEXT.BIN`. | Active ADV history-input policy; port uses its script callback path instead | +| `WHEELKEYUP`, `WHEELKEYDOWN` | Rebinds the registry action bits from generic defaults 3/1 to 8/9. `adv_input_service_poll@0x411230` uses them for ADV wheel-key/history handling; raw `WM_MOUSEWHEEL` accumulation for op `0x10d` remains a separate channel. | Active ADV input binding; port does not source these values | +| `USEAPPDATAFOLDER`, `SAVEPATH` | Selects `%LOCALAPPDATA%\Eushully\姫狩りダンジョンマイスター\SAVE` as the native save root. | Active native path policy; intentionally redirected by port | +| `REGFILEPATH` | Supplies the Eushully/product-relative Windows registration-file path used by the native registration integration. | Windows integration only | +| `SAVEVERSION` | Selects numbered-save layout 3.10 and enables the matching append-catalog persistence structures. | Active persistence ABI | +| `GAMEVERSION`, `VERREGPOS` | Seeds display/registration version `1.00`, then permits the GUID-selected Windows registry `CLASS00` value to replace it. | Windows version/registration metadata | +| `REGKEY` | A nonzero value activates `registration_validate_key_file@0x46fc80`, which opens `SYS4RK.BIN`, validates its header/transformed payload and both CRC variants, and publishes the registration result. | Native registration/key validation | +| `COPYRIGHT`, `RCVERSION` | Both are imported; no later direct consumer of the exact registry keys was found in this image. | Loaded product metadata | + +This is intentionally a native-behavior catalog, not a mandate to reproduce every Win32-era switch. The +current port already preserves the shipped result of several settings through profile-specific constants: +the `800x600` canvas, requested Mincho/Gothic faces, save version 3.10, and movie/audio behavior. It +deliberately redirects the native AppData path to Godot `user://SAVE`, and it should not reproduce legacy +DirectDraw selection or native registration/key validation. + +The actionable generic-profile gap is that `Sys4AssetCatalog` still parses only the directory and the frontend +hard-codes the important results. A future configuration/profile slice should expose the typed trailer values +once and let the runtime apply only cross-platform semantic settings. In particular, click-cancel, Auto/Skip +coexistence, cursor policy, redraw policy, and wheel action ids must be decided from this evidence instead of +AGE's compiled defaults. No runtime behavior changes were made as part of this investigation. + ### ops `0x1a2`/`0x1a3` store and restore shared `SAVE.DAT` integer cells (resolved 2026-07-20) The SCJUMP slice assumed `u00428010` resolved a decision value to a scene. **That premise is wrong**, @@ -1632,9 +1702,11 @@ The state queries are deliberately different: All 301 ordinary ADV button callbacks call `0x88(1)`. `CALLBACK_LOAD.BIN` contains the corpus's only `0x88(0)` reset. The engine supports an optional click-cancel state machine, but -`engine_settings_register_defaults@0x46be30` registers `set:CancelMesSkipOnClick=0`; a nonzero loaded engine -configuration is required to enable its press/release cancellation path. The port preserves that default -instead of unconditionally inventing click-to-cancel. +`engine_settings_register_defaults@0x46be30` registers `set:CancelMesSkipOnClick=0` only as the generic +fallback. Himegari's SYS4INI trailer overrides it with `CANCELMESSKIPONCLICK=2`, enabling the +press/release cancellation path. The port currently preserves the fallback instead because it does not yet +apply the trailer's ADV policy settings; that is now a cataloged parity gap rather than evidence that +Himegari intended click-cancel to remain disabled. Voice playback also has a native Skip queue. `op_0xc4_handler@0x420610` plays immediately while the skip bit is clear. While it is set, the handler replaces `ctx+0x6dbf4/+0x6dbf8` with the latest requested voice instead diff --git a/docs/phase-a-slice-plan.md b/docs/phase-a-slice-plan.md index ed92c58..bfad9d9 100644 --- a/docs/phase-a-slice-plan.md +++ b/docs/phase-a-slice-plan.md @@ -3329,6 +3329,35 @@ shared-profile registration. `G[0x3ebe]` (21 references across CALCREVISE, CHMENU, DRAWTIP, GAMECLEAR, GAMESTART, IMPROVE, and TUNE). +## SYS4INI startup-settings investigation (2026-07-28) + +The post-directory SYS4INI data is now structurally and behaviorally cataloged. Himegari's decompressed +tail contains a `0x90`-byte VM-metadata block followed by a `0x2bb`-byte, 36-pair CP932 startup-settings +record. Native AGE first registers generic defaults, imports those pairs case-insensitively, applies three +special transforms (`SAVEVERSION`, `NOSETMUSIC`, and wheel-key duplication), then initializes the logical +canvas, text, graphics, audio, ADV input policy, persistence, and Windows registration integration from +the completed registry. + +The investigation distinguishes active Himegari behavior from loaded compatibility metadata. Active core +results include retained renderer/draw mode, `800x600`, Mincho/antialias profile, movie-audio policy, +save layout 3.10, and AppData save-root selection. The important newly exposed ADV overrides are +`CANCELMESSKIPONCLICK=2`, `CONTROLDISIBLECURSOR=1`, `COEXISTMESSKIP=1`, +`REDRAWTEXTONKEY=0`, and wheel actions 8/9. The port does not currently parse/apply those ADV policy +values; in particular, the earlier click-cancel conclusion accidentally preserved AGE's zero fallback +instead of Himegari's override. + +Legacy DirectDraw-only values, unused native-menu flags, and Windows registration/key metadata are now +classified separately so a later generic profile loader does not mistake every serialized setting for a +portable runtime requirement. No gameplay/runtime behavior changed in this investigation. The exact +serialized inventory is canonical in `sys4-format-notes.md`, native consumers and port relevance in +`engine-re.md`, multi-profile direction in `remake-architecture-and-roadmap.md`, and portability impact in +`platform-portability.md`. The `/v2` Ghidra image names/comments the importer and principal graphics, +ADV-toggle, audio, and registration consumers. + +**Next:** keep the settings parser/application as a bounded generic-profile/configuration slice. Resume +the Phase-B gameplay spine unless one of the cataloged ADV policy differences becomes a visible gameplay +or input defect. + ## ADV requested-face selection implemented (2026-07-28) Opcode `0x1a5` is no longer a presentation no-op. The VM retains its requested face in diff --git a/docs/platform-portability.md b/docs/platform-portability.md index a6e3b9e..8abd28e 100644 --- a/docs/platform-portability.md +++ b/docs/platform-portability.md @@ -27,6 +27,7 @@ or replaced before claiming portable exports. | Area | Current dependency | Runtime impact | Portability status / future action | |---|---|---|---| +| SYS4INI per-game startup profile | `Sys4AssetCatalog` currently parses only the asset directory; the frontend hard-codes Himegari's important results. Native SYS4INI also carries canvas, text, ADV input/skip, save ABI/path, audio, legacy renderer, and Windows registration settings | The port has the correct `800x600`, requested faces, save 3.10, and redirected `user://SAVE` result, but does not source `CancelMesSkipOnClick`, `CoexistMesSkip`, cursor/redraw policy, or wheel action ids from the trailer | Add one typed profile/settings parser. Apply cross-platform semantic keys explicitly; translate save roots through the host; classify DirectDraw/fullscreen-bit and registration/key settings as native compatibility metadata rather than OS-neutral requirements. Canonical inventory and consumers: `sys4-format-notes.md` and `engine-re.md` | | Retained graphics presentation | Backend-neutral `GfxState`; accepted default Godot `Sprite2D` GPU stage plus the retained software pixel oracle, using runtime `ImageTexture`, canvas transforms/materials, and no native graphics API | GPU backend caches static/color-key variants, updates dynamic surfaces, handles retained range transitions, and falls back whole-frame for the legacy host screen-transition path | Godot owns D3D/Vulkan/Metal/OpenGL selection; validate shader/blend/filter behavior per target rather than adding a platform renderer | | AGE movie decode (`0x236` scene movies; `0x20f` modal LOGO/OP/ED) | `FfmpegMovieDecoder` is the sole factory over the project-owned `native/age_movie_ffmpeg` ABI | Windows-x64 passes the complete 213-payload installed video/audio corpus gate plus audible LOGO/OP/CHAPTER playback | Add target-specific native builds and export packaging | | Movie integration | Each surface owns a unique playback-instance id; `MovieRuntime` owns `IMovieDecoder` from an injected factory; video-only streams use monotonic pacing while audio-bearing streams use the Godot output clock | Concurrent/restarted uses of one asset have independent frame/audio/completion/teardown state; managed code is no longer Windows-annotated, while only the win-x64 native bundle exists today | Add Linux/macOS native builds and smoke gates | diff --git a/docs/remake-architecture-and-roadmap.md b/docs/remake-architecture-and-roadmap.md index b5983da..3ab0eb3 100644 --- a/docs/remake-architecture-and-roadmap.md +++ b/docs/remake-architecture-and-roadmap.md @@ -677,9 +677,16 @@ from that game's corpus. The probe also exposed a separate profile/presentation concern: Kamidori creates a `1024x576` render target while the current Himegari frontend assumes an `800x600` presentation. Logical canvas geometry, -scaling, and any game-specific defaults therefore belong in the selected game profile or script-driven -surface state rather than in a forked frontend. This experiment was diagnostic only; no Kamidori support -or `0x1be` semantics were implemented. +scaling, and other game-specific defaults therefore belong in the selected game profile or script-driven +surface state rather than in a forked frontend. The source is now known: after its asset directory and +VM-bank metadata, SYS4INI carries a typed per-game startup-settings record. Himegari uses it for the +canvas, text face/raster mode, ADV input/skip policy, save ABI/path, audio initialization, and native +Windows compatibility metadata. A future generic profile loader should parse that record once, preserve +unknown/profile-specific data for diagnostics, apply cross-platform semantic settings through explicit +runtime seams, and classify legacy renderer/registration switches rather than blindly emulating them. +The current frontend's scattered Himegari constants are acceptable while gameplay is the priority, but +they are not the multi-profile architecture. This experiment was diagnostic only; no Kamidori support or +`0x1be` semantics were implemented. ### Other engine versions (SYS3 / SYS5) — one app, not many Versions differ in: header (SYS4 `0x3C` vs SYS5 `0x44`), string codec (SYS4 cp932^0xFF vs SYS5 diff --git a/docs/sys4-format-notes.md b/docs/sys4-format-notes.md index eca7316..e629a3b 100644 --- a/docs/sys4-format-notes.md +++ b/docs/sys4-format-notes.md @@ -23,15 +23,72 @@ per format. NUL-delimited per-game engine-settings trailer. `tools/parse_sys4ini.py` currently consumes only the directory prefix. -The Himegari trailer contains `SCREENX=800` and `SCREENY=600`; Kamidori's independent SYS4INI contains -`SCREENX=1024` and `SCREENY=576`. This establishes the authored logical canvas as per-game data. Native -initialization and the executable's `640x480` fallback are documented in `engine-re.md`; -fullscreen/display-mode settings are a separate concern from these logical dimensions. +After the directory prefix, Himegari's decompressed stream has this tail: -The same Himegari trailer also contains `USEAPPDATAFOLDER=1` and -`SAVEPATH=Eushully\姫狩りダンジョンマイスター\SAVE`. AGE's native save-root resolver consumes those -settings to select `%LOCALAPPDATA%` plus that relative path on modern Windows. Filename patterns remain -native policy rather than trailer or script strings; see `engine-re.md` under "Save-root resolution." +```text +decompressed offset field +0x102488 vm_metadata_bytes:u32 = 0x90 +0x10248c vm_metadata[0x90] +0x10251c settings_string_bytes:u32 = 0x2bb +0x102520 setting_pair_count:u32 = 36 +0x102524 repeated { key:cstring, value:cstring }[36] +0x1027df end of decompressed stream +``` + +The string-byte count covers only the concatenated key/value strings, not the preceding pair count. +Keys are ASCII, values are CP932, and every key and value is individually NUL-terminated. Native +`engine_settings_import_sys4ini_pairs@0x46da80` matches keys case-insensitively and rejects an +unrecognized key rather than silently retaining it. + +Himegari's complete serialized list, in file order, is: + +| # | Key | Value | +|---:|---|---| +| 1 | `CREATEOBJECT` | `2` | +| 2 | `DRAWMODE` | `1` | +| 3 | `DEPENDMOVIESOUND` | `1` | +| 4 | `SCREENX` | `800` | +| 5 | `SCREENY` | `600` | +| 6 | `FONT` | `MS 明朝` | +| 7 | `ENABLEANTIFONT` | `1` | +| 8 | `ANTIFONTVERSION` | `3` | +| 9 | `FULLSCREENBIT` | `32` | +| 10 | `SCREENWARNING` | `1` | +| 11 | `NOSETMUSIC` | `3` | +| 12 | `MENU_SAVE` | `0` | +| 13 | `MENU_MESWINA` | `0` | +| 14 | `MENU_RCLICK` | `0` | +| 15 | `MENU_MESSPEED` | `0` | +| 16 | `MENU_USEANTIFONT` | `0` | +| 17 | `MENU_MESSAGE` | `1` | +| 18 | `MENU_SOUNDONOFF` | `0` | +| 19 | `ENABLEMEMFLIP` | `0` | +| 20 | `CLICKONUP` | `1` | +| 21 | `ALWAYSBACKUPSURFACE` | `0` | +| 22 | `CANCELMESSKIPONCLICK` | `2` | +| 23 | `CONTROLDISIBLECURSOR` | `1` | +| 24 | `COEXISTMESSKIP` | `1` | +| 25 | `REDRAWTEXTONKEY` | `0` | +| 26 | `WHEELKEYUP` | `8` | +| 27 | `WHEELKEYDOWN` | `9` | +| 28 | `USEAPPDATAFOLDER` | `1` | +| 29 | `SAVEPATH` | `Eushully\姫狩りダンジョンマイスター\SAVE` | +| 30 | `REGFILEPATH` | `Eushully\姫狩りダンジョンマイスター` | +| 31 | `COPYRIGHT` | `Copyright (c) Eushully 2009` | +| 32 | `SAVEVERSION` | `310` | +| 33 | `GAMEVERSION` | `1.00` | +| 34 | `RCVERSION` | `23` | +| 35 | `VERREGPOS` | `70724B91-F673-40FB-9EC4-5DE21EA68367` | +| 36 | `REGKEY` | `1886538641` | + +This list is the canonical serialized-data inventory. Native consumers, compiled fallbacks, settings +that are inert under Himegari's selected renderer, and current port coverage are cataloged in +`engine-re.md` under "SYS4INI startup-settings catalog." + +`SCREENX=800` and `SCREENY=600` are independently corroborated by Kamidori's `1024x576` pair, proving +that the authored logical canvas is per-game data. Fullscreen/display-mode settings are separate from +these logical dimensions. `USEAPPDATAFOLDER` and `SAVEPATH` select the native Windows save root; fixed +save filenames remain AGE policy rather than trailer or script strings. ## Native persistence files — Himegari 3.10