engine: complete portable glyph text backend

This commit is contained in:
gamer147
2026-07-30 19:47:21 -04:00
parent ef19ab79e0
commit d9f24c69ef
12 changed files with 619 additions and 840 deletions

View File

@@ -118,7 +118,8 @@ S:\Game Hacking\Eushully\Himegari\ ← workspace root (three siblings)
├── tools/frida/ runtime-capture + engine-dump scripts (see tools/frida/README.md)
└── godot/ DELIVERABLE — the Godot/C# ADV front-end (references Age.Engine
plus the optional exact Windows text adapter), including the
TITLE-only F4 debug scene launcher
portable TextServer glyph adapter, per-profile font-substitution
configuration, and TITLE-only F4 debug scene launcher
```
The disposable `build/page-map-<SCENE>.jsonl` files are produced by normal Godot runs and map runtime ADV

View File

@@ -3650,6 +3650,30 @@ The required fidelity follow-up is implementation of that decoded mask contract;
validation evidence, not the source of rendering behavior. That work is deferred until gameplay settles
and is scoped in `docs/remake-architecture-and-roadmap.md`; it is not the next Phase A/B slice.
## ADV portable glyph backend and Label retirement (2026-07-30)
Godot TextServer is now the portable glyph-mask source. It exposes rasterized atlas coverage plus glyph
offset and horizontal advance without adding a second native font library; AGE's existing deterministic
compositor still owns effects, integer blending, layout, surfaces, and retained publication. Portable
requests use Unicode directly and carry CP932 only when representable so the existing Japanese wrap rules
remain available. TextServer's second advance component is intentionally ignored because it describes
vertical-layout advance rather than GDI's horizontal `gmCellIncY`.
`godot/config/himegari-text-rendering.json` owns the policy id, bounded font/glyph cache capacities, and
ordered Mincho/Gothic/default system-family substitutions. The backend reports itself as explicitly
non-GDI-exact. Startup option `--text-backend auto|gdi|portable` retains the acceptance seam: `auto` chooses
the exact ACP-932 GDI path when available and otherwise the portable policy, while forced `gdi` fails
clearly instead of changing fidelity silently.
After forced portable and forced exact modes both passed immediate-surface, live-layout, History,
complete-line, bounded-cache, retained publication/reset, GPU-upload, surface-copy, and clear gates, the
gameplay `Label` pools were deleted. Immediate, live, and History text no longer has a top-level overlay or
metadata projection path; mask/backend failures are explicit, and all gameplay text inherits the bound
surface's z-order, alpha, tint, fade, transform, clipping, capture, and lifetime.
Validation: engine **580/580**, zero-warning Godot build, and threaded `SELFTEST OK` in both forced
`portable-godot-textserver` and forced `windows-gdi-gray4` modes.
## Persistence native-format reconnaissance complete (2026-07-24)
The deferred save/profile ownership question now has a compatibility-mode answer. The remaining native

View File

@@ -33,7 +33,7 @@ or replaced before claiming portable exports.
| AGE movie decode (`0x236` scene movies; `0x20f` modal LOGO/OP/ED; `0x24d` movie masks) | `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 and real TEST.AGF green-mask decode | 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. Opcode `0x24d` redirects decoded green bytes through the platform-neutral retained rasterizer and exact managed packed-alpha mask helper | Concurrent/restarted uses of one asset have independent frame/audio/completion/teardown state. Ordinary and mask movies share VFS/FFmpeg ownership; the mask result is a backend-neutral dynamic RGBA surface consumed by either Godot renderer. Managed code is no longer Windows-annotated, while only the win-x64 native bundle exists today | Add Linux/macOS native builds and smoke gates |
| Movie audio | ABI v2 returns timestamped stereo float PCM; bounded managed buffering feeds a per-playback Godot `AudioStreamGenerator` and routes native movie flags to engine buses | All 29 installed audio-bearing streams decode with signal; synchronized LOGO/OP/CHAPTER playback is audibly accepted | Treat absent, distorted, or unsynchronized audio from an audio-bearing movie as a runtime bug |
| ADV font discovery/raster fidelity | `Age.Engine.Text` remains OS-neutral. Godot selects the separate `Age.Engine.Text.Windows` backend for immediate `0x204`/`0x205` strings, live ADV, and retained History when Windows ACP 932 is available; exact masks are cached, composited into layout/numbered RGBA surfaces, and published through ordinary retained handles. The wait atlas is likewise bound at its script-configured handle and no longer uses a detached Godot node. The fallback loads Windows ` 明朝`/` ゴシック` when available and otherwise chooses the Japanese/default font with FreeType embolden and a Godot outline | Three Mincho/Gothic regular/bold samples byte-match an independent Unicode GDI oracle. Immediate, live, History, and wait-marker presentation now obey retained ordering, alpha/tint/fade, transforms, clipping, capture, HIDEWIN suspension, partial erase/republication, reset, and History teardown. Non-Windows/non-932 runs report why exact text selection failed and atomically retain the old Label presentations; they do not claim GDI parity | Add an explicitly non-identical portable backend and face-substitution policy, then remove the unavailable-rasterizer Label fallback and its remaining overlay compensations. Keep the GDI project out of non-Windows deliverables rather than hiding failed exact selection |
| ADV font discovery/raster fidelity | `Age.Engine.Text` remains OS-neutral. On compatible ACP-932 Windows, `auto` selects the separate exact `Age.Engine.Text.Windows` GDI gray-4 backend. Otherwise Godot TextServer supplies Unicode masks and metrics from ordered system-font substitutions in `godot/config/himegari-text-rendering.json`; this policy is explicitly not GDI pixel-exact. Immediate `0x204`/`0x205`, live ADV, retained History, and the wait atlas all enter ordinary RGBA surfaces/retained handles | Three Mincho/Gothic regular/bold samples byte-match an independent Unicode GDI oracle. Forced exact and forced portable self-tests cover immediate, live, and History rasterization, bounded caching, complete-line materialization, retained ordering, alpha/tint/fade, transforms, clipping, capture, HIDEWIN suspension, partial erase/republication, reset, and teardown. Gameplay `Label` pools and overlay projection are gone | Produce and smoke-test Linux/macOS exports, confirm installed family availability/fallback selection there, and keep the GDI project out of non-Windows deliverables |
| Filesystem semantics | Several filename and containment comparisons use `OrdinalIgnoreCase`; installed assets are conventionally uppercase | Needs validation on case-sensitive filesystems; may hide casing or containment mistakes | Add Linux/macOS tests with mixed-case synthetic roots and use filesystem-appropriate containment rules |
| Save/profile/settings storage | `Sys4PersistencePaths` models AGE's independent `SAVEPATH` and `REGFILEPATH` resolutions. Godot replaces Himegari's related profile directory with `user://`, yielding `user://SAVE` for native S3SD/S4SD/S3RT files and thumbnails plus `user://SYS4REG.INI` for the BOM-less CP932 options file; the preserving writer changes only its nine `[sound]` keys | Save payloads and engine options are isolated together without changing either native format. Native/drop-in resolution remains available through `USEAPPDATAFOLDER` plus both SYS4INI paths. A single-root override is rejected when `SAVEPATH` is not beneath `REGFILEPATH`, preventing cross-profile guesses | Expose explicit profile/native selection through the future launcher and allow independent overrides for profiles whose two native paths are unrelated. Validate CP932 availability, replace/flush, case, permissions, and interrupted-write behavior on each export target |
| Game-install and repository discovery | `GameRootSelection` accepts `--game-root`, then probes the executable directory and current working directory for `SYS4INI.BIN`; on Unix the frontend prefers inherited shell `PWD` because Godot may change the process directory during project startup. Godot injects the selected root into its catalog and loose-first ALF store. `Paths.cs` remains the development/test locator for generated opcode data, diagnostics, and CLI conveniences | Installed game data no longer depends on the workspace sibling layout, enabling an executable beside `AGE.EXE`, a terminal launch from the install, or a launcher-supplied absolute profile root. A packaged export still needs its generated runtime metadata bundled independently of repository discovery | Add export packaging for the opcode/profile artifacts, let the future profile launcher pass `--game-root`, and run executable-directory/CWD plus case/permission smoke gates on Linux and macOS |

View File

@@ -635,16 +635,17 @@ primitive, which cannot be made equivalent by choosing another embolden constant
the persisted backlog. The configured wait atlas now binds at op `0x212`'s handle, advances source cells
only on the configured frame boundary, participates in op-`0x20a` republication and HIDEWIN suspension,
and disappears when the wait service stops. The standalone Godot wait `TextureRect` and its raw-callback
visibility exception are gone. Exact History no longer enters the layout-keyed Label pool; that code and
the live/immediate Label pools remain solely as the explicit unavailable-rasterizer fallback until step 7
supplies the portable glyph backend. Focused lifecycle tests, all 579 engine tests, the exact History
visibility exception are gone. At this checkpoint exact History no longer entered the layout-keyed Label
pool; step 7 subsequently supplied the portable backend and removed every remaining gameplay Label pool.
Focused lifecycle tests, all 579 engine tests, the exact History
Godot self-test, and a real SC0000 Vulkan capture cover the retained path.
7. **Complete and select the portable backend.** Spike the smallest maintainable way to obtain portable
font masks/metrics (Godot/TextServer offscreen extraction versus a bounded FreeType-compatible adapter)
before choosing a dependency. Define face substitution and raster policy as profile/configuration data,
keep its output explicitly non-identical to GDI, and run the same layout/compositor tests. Retain a
development-only backend switch through acceptance; remove the old Label fallback only when both exact
Windows and portable startup/legibility gates pass.
7. **Complete and select the portable backend — completed 2026-07-30.** Godot's TextServer atlas is the
portable mask/metrics source, avoiding another native dependency. Profile data in
`godot/config/himegari-text-rendering.json` owns ordered Mincho/Gothic substitutions and bounded cache
sizes. `auto` selects exact GDI when its ACP-932 gate passes and otherwise selects the explicitly
non-pixel-exact Unicode backend; development runs can force either with `--text-backend`. Both modes pass
the same immediate/live/History retained-glyph self-test. Gameplay `Label` pools and surface-text overlay
projection are removed, so text always participates in the ordinary surface compositor.
**Acceptance gates:**

View File

@@ -346,6 +346,11 @@ use the last `input-wait` event in `timeline.jsonl` as the authoritative manual
are current-working-directory-relative. This is the stable handoff for future game-profile launchers.
- `--scene <NAME>` — override the default `SYSTEM4` root with a direct diagnostic scene, e.g. `--scene SC0240` (executes 29 nested subroutines).
- `--selftest` — headless; runs a **synthesized** scene through the thread/suspend/`CallDeferred` plumbing and asserts it matches a live headless run (full handling; no vm0/frozen golden). Exits.
- `--text-backend auto|gdi|portable` — select gameplay glyph-mask rasterization. `auto` (default) uses the
exact GDI gray-4 backend only when the Windows ACP-932 gate passes, otherwise the explicitly non-identical
Godot TextServer Unicode policy. `gdi` fails startup when exact selection is unavailable; `portable`
never initializes GDI and is the cross-platform acceptance/debug mode. The selected backend and portable
policy id are printed at startup.
- `--window-width <pixels>` / `--window-height <pixels>` — override only the physical windowed client
dimensions; each omitted dimension defaults independently to the SYS4INI logical canvas. Godot keeps
that logical canvas and aspect-preserving letterboxing, so these options do not change VM coordinates,