engine: materialize live ADV glyphs
This commit is contained in:
@@ -105,7 +105,8 @@ S:\Game Hacking\Eushully\Himegari\ ← workspace root (three siblings)
|
||||
│ ALF asset store, script provider, AGF/LZSS and Windows CUR decoders,
|
||||
│ and resource facade
|
||||
│ ├── Age.Engine/Text/ backend-neutral glyph-mask requests/results, deterministic AGE
|
||||
│ text compositor/layout, and bounded font/glyph cache primitives
|
||||
│ compositor/layout, retained live-glyph publication, and bounded
|
||||
│ font/glyph cache primitives
|
||||
│ ├── Age.Engine/Persistence/ native S3SD/S4SD + S3RT codecs, shared payload/ReadTextDB,
|
||||
│ layout-3 numbered state/history/gfx, DAT/STH pair + BMP codec,
|
||||
│ and profile-owned state
|
||||
|
||||
@@ -2982,8 +2982,8 @@ and returns the five-dword edge records plus final cursor, observed overflow, wr
|
||||
state. It preserves native horizontal precedence: vertical-only overflow stops before compositing, while a
|
||||
simultaneous horizontal overflow follows the wrap/kinsoku path. A reusable bounded LRU provides eviction
|
||||
callbacks for future native font handles; `CachedGlyphMaskRasterizer` applies the same bound to masks. This
|
||||
core is not yet connected to live ADV or History; those paths still use Labels pending their retained-glyph
|
||||
materialization steps.
|
||||
core now drives immediate surface strings and live ADV; retained History remains on its Label pending the
|
||||
next migration step.
|
||||
|
||||
The independent Windows reference backend landed alongside, but outside, the neutral engine as
|
||||
`Age.Engine.Text.Windows.WindowsGdiGlyphMaskRasterizer`. It holds one `CreateICA("DISPLAY")` information
|
||||
@@ -3014,8 +3014,31 @@ offscreen capture, fill/copy, and preservation behavior apply without a detached
|
||||
If the exact backend is unavailable, string conversion fails, or the target surface cannot be resolved,
|
||||
the host records one explicit diagnostic and retains the old metadata/Label projection for that draw. The
|
||||
fallback is constructed before surface publication, so it cannot mix a partial new raster with the Label.
|
||||
Live ADV, History, and the wait indicator remain on their existing presentation paths for the later staged
|
||||
migration.
|
||||
History and the wait indicator remain on their existing presentation paths for the later staged migration.
|
||||
|
||||
Live ADV now uses the same exact surface pixels through ordinary retained objects.
|
||||
`AdvTextLayoutPresentationBinding` is transient engine state: it carries the layout surface (`slot+0x14`),
|
||||
first object handle, capacity, wait-indicator handle, and reset cursor without changing the persisted
|
||||
History record layout. The VM passes that binding and its `GfxState` to the host for each `show-text` run,
|
||||
then writes the returned GDI-measured cursor into the canonical live layout before processing another
|
||||
literal/dynamic run.
|
||||
|
||||
`RetainedAdvTextLayoutPresentation` stores each five-dword record together with the layout origin active
|
||||
when that run was built. Publication binds `first_handle+index` from the record's source edges to
|
||||
`run_origin+(left,top)`, so MAMES-style origin changes do not move earlier glyphs. The complete run is
|
||||
rasterized once into the layout RGBA surface; timed reveal only adds another `GfxState` binding and never
|
||||
rerasterizes or reuploads the complete line. The configured capacity bounds timed publication. Click or
|
||||
Skip sets the existing force-complete flag, binds every remaining publishable record, synchronizes that
|
||||
endpoint, and returns without releasing the subsequent stable input wait.
|
||||
|
||||
Because these are normal handles, a partial `0x1f7` removes only the selected glyph. Op `0x20a` walks the
|
||||
retained records through the current reveal count and recreates missing bindings. HIDEWIN suspension erases
|
||||
the active page's published range and restore republishes it; layout reset erases the configured interval,
|
||||
drops transient records, and zeroes the source surface. Whole scene reset/release drops the transient layout
|
||||
state. Numbered History serialization still owns none of these pixels or records: the saved-frame script
|
||||
path reconstructs them, and a reconstruction test proves identical handle/source/destination bindings.
|
||||
Exact runs are omitted from `SnapshotLiveAdvText`, eliminating the detached always-on-top Label while the
|
||||
unavailable-backend fallback continues to use that snapshot.
|
||||
|
||||
#### ADV wait indicator -- ops `0x73` / `0x72` (2026-07-11)
|
||||
|
||||
|
||||
@@ -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 now selects the separate `Age.Engine.Text.Windows` backend for immediate `0x204`/`0x205` strings when Windows ACP 932 is available; exact masks are cached and composited into numbered RGBA surfaces. Live ADV and History still use the existing `Label` path, which loads Windows `MS 明朝`/`MS ゴシック` when available and otherwise chooses the Japanese/default fallback with FreeType embolden and a Godot outline | Three Mincho/Gothic regular/bold samples byte-match an independent Unicode GDI oracle. Immediate surface text now obeys retained ordering, alpha/tint/fade, transforms, clipping, capture, and surface mutation in both render paths. Non-Windows/non-932 runs report why exact selection failed and atomically retain the old surface-label projection; they do not claim GDI parity | Add an explicitly non-identical portable backend and face-substitution policy, then migrate live ADV/History and remove Label 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. Godot selects the separate `Age.Engine.Text.Windows` backend for immediate `0x204`/`0x205` strings and live ADV when Windows ACP 932 is available; exact masks are cached, composited into layout/numbered RGBA surfaces, and live glyphs become ordinary retained handles. History still uses the existing `Label` path. The fallback loads Windows `MS 明朝`/`MS ゴシック` 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 and live text now obey retained ordering, alpha/tint/fade, transforms, clipping, capture, HIDEWIN suspension, partial erase/republication, and reset. Non-Windows/non-932 runs report why exact 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 migrate History/wait-indicator presentation and remove the remaining Label compensations. Keep the GDI project out of non-Windows deliverables rather than hiding failed exact selection |
|
||||
| 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 |
|
||||
|
||||
@@ -612,13 +612,22 @@ primitive, which cannot be made equivalent by choosing another embolden constant
|
||||
dynamic GPU upload, and surface mutation path. Backend absence, an unrepresentable glyph, or an unresolved
|
||||
surface falls back atomically to the prior metadata/Label projection with an explicit diagnostic; that
|
||||
projection remains only for this temporary unavailable-backend case until the portable backend lands.
|
||||
5. **Materialize live ADV glyphs as ordinary retained objects.** Extend the transient layout-presentation
|
||||
binding with its source surface, first handle, and capacity without changing the persisted History record
|
||||
ABI. Build the complete line before reveal, update the canonical live cursor from measured metrics, then
|
||||
bind one normal `GfxState` object per revealed record. Click/Skip publishes the remaining records and keeps
|
||||
the existing consumed-click boundary. Layout reset erases its range and clears its surface; partial
|
||||
`0x1f7` erases work per handle naturally; `0x20a` republishes from retained records. Consecutive
|
||||
literal/dynamic runs advance by returned metrics rather than relying on Label concatenation.
|
||||
5. **Materialize live ADV glyphs as ordinary retained objects.** Completed 2026-07-30 for the exact Windows
|
||||
path. The VM passes `GfxState` plus the transient layout binding into presentation and accepts the measured
|
||||
final cursor in return; the persisted History ABI is unchanged. `RetainedAdvTextLayoutPresentation`
|
||||
retains each native edge record with the layout origin active for that run, builds the complete line into
|
||||
the layout surface once, and binds `first_handle + glyph_index` in reveal order. Consecutive
|
||||
literal/dynamic runs therefore begin at GDI's returned cursor instead of relying on Label concatenation.
|
||||
Timed reveal stops at capacity, while click/Skip publishes the remaining available handles through the
|
||||
existing consumed-click branch. Partial `0x1f7` erases ordinary handles; `0x20a` recreates all records
|
||||
through the current reveal count. HIDEWIN suspension erases that range and restoration republishes it.
|
||||
Layout reset erases the interval, clears its RGBA surface and transient records, and saved-frame-style
|
||||
reconstruction deterministically rebuilds the same bindings. Exact runs are excluded from the live Label
|
||||
snapshot; unavailable-backend runs retain it unchanged. Focused tests cover handle order/capacity,
|
||||
per-run origins, partial erase/republication, suspension, measured cursor handoff, reset, and
|
||||
reconstruction. The synthesized Godot VM self-test exercises timed exact reveal and reports
|
||||
`live-adv-text=retained-glyphs`; real GPU captures of SC0000 pages 1 and 7 confirm ordinary and voiced
|
||||
dialogue placement/wrapping alongside the immediate speaker-name surface.
|
||||
6. **Migrate History and the ADV wait indicator through the same retained path.** Op `0x1d1` rasterizes and
|
||||
binds into its target layout instead of creating a layout-keyed Label. Bind the configured wait atlas at
|
||||
the op-`0x212` handle so it follows native z-order and source-cell animation. Once SC0000, HISTORY,
|
||||
|
||||
Reference in New Issue
Block a user