diff --git a/docs/engine-re.md b/docs/engine-re.md index e228f4c..fba3042 100644 --- a/docs/engine-re.md +++ b/docs/engine-re.md @@ -2763,6 +2763,14 @@ record with `gfx_object_bind_draw`. Native SC0000 records `ctx+0x14e9c = 50 ms`: at `0x834`, then publishes them one at a time before reaching wait `0x83c`. A click during reveal completes the remaining records and is consumed; the next click releases the stable wait. +The record layout is `{publication-chain flag, x, y, width, height}` as five dwords. The publisher binds +handle `layout+0x68 + reveal_index` from source surface `layout_slot+0x14`, uses `(x,y,width,height)` as +the source rectangle, and places it at `(layout_origin_x+x, layout_origin_y+y)`. A set first dword makes the +service publish the following record in the same tick; otherwise the configured glyph delay controls the +next publication. This gives the port a direct path to ordinary `GfxState` objects: rasterize the complete +layout surface first, then materialize these exact bounded records in handle order rather than adding a +second overlay compositor. + The port retains the SC0000-visible contract without exposing thousands of individual host glyph objects: surface strings remain associated with blank surface slots for later retained-object binding, while ADV lines retain cursor/origin, start time, visible-glyph count, and completion state. The compositor renders diff --git a/docs/platform-portability.md b/docs/platform-portability.md index 788462d..901d3e9 100644 --- a/docs/platform-portability.md +++ b/docs/platform-portability.md @@ -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 | Opcode `0x1a5` reaches presentation; `godot/Main.cs` loads Windows `MS 明朝`/`MS ゴシック` from their known TTC files when available, otherwise uses the existing Japanese-font/default fallback. AGE actually uses a display information DC, weight-700 `LOGFONTA`, `GGO_GRAY4_BITMAP`, and its own integer glyph/outline compositor; the current `Label` backend substitutes FreeType embolden and a Godot outline | On the reference Windows install, regular advances and direct SC0000 placement/bounds agree, but the behaviorally different provisional bold path has 45% fewer bright pixels and much softer edge coverage. Other platforms normally lack the proprietary faces, so substitute metrics remain profile-dependent | Deferred until gameplay settles: implement the Phase-E decoded mask/metrics/compositor backlog in `docs/remake-architecture-and-roadmap.md`, with a shipped GDI reference backend on Windows and an explicitly defined portable rasterizer/substitution policy | +| ADV font discovery/raster fidelity | Opcode `0x1a5` reaches presentation; `godot/Main.cs` loads Windows `MS 明朝`/`MS ゴシック` from their known TTC files when available, otherwise uses the existing Japanese-font/default fallback. AGE actually uses a display information DC, weight-700 `LOGFONTA`, `GGO_GRAY4_BITMAP`, and its own integer glyph/outline compositor; the current `Label` backend substitutes FreeType embolden and a Godot outline | On the reference Windows install, regular advances and direct SC0000 placement/bounds agree, but the behaviorally different provisional bold path has 45% fewer bright pixels and much softer edge coverage. Other platforms normally lack the proprietary faces, so substitute metrics remain profile-dependent | The Phase-E retained glyph-mask polish slice is now planned in `docs/remake-architecture-and-roadmap.md`: keep the GDI reference adapter outside the platform-neutral VM project, select it explicitly on Windows, and complete an explicitly non-identical portable rasterizer/substitution policy before removing the temporary Label fallback | | 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 | diff --git a/docs/remake-architecture-and-roadmap.md b/docs/remake-architecture-and-roadmap.md index bf4a2cd..2c948eb 100644 --- a/docs/remake-architecture-and-roadmap.md +++ b/docs/remake-architecture-and-roadmap.md @@ -522,14 +522,17 @@ requirements. - Enhancements the VM unlocks: higher/wide resolution, faster text, QoL, save-anywhere, new-content mods. Modding docs + tools. Save/UX polish. -#### Deferred backlog — AGE-exact glyph-mask text renderer +#### Planned polish slice — AGE-exact retained glyph-mask text renderer -**Scheduling:** defer this effort until the natural gameplay spine, first dungeon loop, and combat/system -presentation are settled. The current Godot `Label` backend is readable, respects the script-requested -Mincho/Gothic family when available, and matches SC0000's placement and effective line width. Its remaining -weight, hinting, and outline-pixel differences do not block gameplay work. Resume this backlog early only if -the approximation causes a legibility, layout, input, or gameplay-presentation defect rather than a cosmetic -native-parity difference. +**Scheduling (2026-07-30):** resume this as the next bounded presentation-polish subsystem, while still +allowing urgent playthrough blockers to preempt it. The original deferral gate has been met: the natural +gameplay spine and first-dungeon loop are playable, numbered save/load is provisionally accepted, the shipped +corpus has no remaining decoded effectful-opcode gaps, and representative system/menu presentation is live. +More importantly, the detached `Label` representation now causes behavioral defects rather than only +cosmetic native-parity differences: labels sit above the complete retained canvas, surface-drawn text does +not inherit its object's alpha/tint, and modal/publication paths need explicit visibility and replacement +exceptions. The current backend remains the migration fallback, not a target for further screenshot-driven +font tuning. **Behavioral source:** implement the native contract decoded in `docs/engine-re.md`; screenshots are integration/regression evidence, not tuning inputs. AGE selects an exact `LOGFONTA` into a @@ -557,17 +560,66 @@ primitive, which cannot be made equivalent by choosing another embolden constant through one cached glyph service. Retain the existing `Label` path as a temporary portable fallback until that integration is complete, then remove synthetic embolden/glyph-spacing constants from the exact path. +**Execution sequence:** + +1. **Close the retained-record contract and pin baselines.** Before changing presentation, record the + current semantic coordinates and native-backed checks for SC0000 voiced/unvoiced pages, STUDY/MAMES, + HISTORY, BUNKI, and save/load restoration. Confirm the remaining implementation-sensitive native details: + overflow/kinsoku behavior, object-range exhaustion, layout reset/republication, and restoration of live + layout surfaces. The known 20-byte glyph record is `{publication-chain flag, x, y, width, height}`; + publication binds `layout_first_handle + reveal_index` from surface `layout_slot + 0x14`, using `(x,y,w,h)` + as the source rectangle and `layout_origin + (x,y)` as the destination. +2. **Land the backend-neutral mask and compositor core.** Add request/result records and an + `IGlyphMaskRasterizer`-style seam outside the VM. Keep CP932 byte identity available for native-compatible + requests while permitting an explicit Unicode-capable portable policy for mods. Implement AGE's coverage + conversion, modes 0–3, clipping, alpha/RGB integer blend, metrics, wrapping, final-cursor result, and + bounded font/glyph caches in managed code. Synthetic masks, not OS fonts, are the unit-test oracle. +3. **Add the independent Windows GDI reference backend.** Keep the P/Invoke adapter outside the + platform-neutral `Age.Engine` project so the VM continues to call no OS APIs. Reproduce the decoded display + IC, LOGFONT, measurement, and `GGO_GRAY4_BITMAP` request, and compare masks/`GLYPHMETRICS`/advances directly + on Windows. Backend selection must be explicit and diagnostic; an unavailable exact backend falls back + rather than silently claiming parity. +4. **Move immediate surface strings first.** Make ops `0x204`/`0x205` rasterize directly into the numbered + RGBA surface instead of appending `SurfaceTextDraw` metadata. Remove the separate surface-label projection + only after GPU and software paths prove that ordinary handle order, object alpha/tint, affine transforms, + source clipping, offscreen `0x222` captures, fills/copies, and backbuffer preservation all consume the same + pixels. This is the smallest integration slice and directly fixes the reported surface-fade defect. +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. +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, + STUDY/MAMES, HIDEWIN, and BUNKI pass, delete the game-text Label pools and the modal/raw-callback visibility + exceptions whose only purpose was compensating for detached overlays. +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. + **Acceptance gates:** - Fixed synthetic-mask tests byte-match AGE's decoded 16-/32-bit compositor, including overlapping mode-3 neighbors, clipping, alpha, and RGB integer rounding. - On Windows, representative CP932 glyph masks, `GLYPHMETRICS`, and advances match a direct invocation of the decoded GDI request; tests compare returned data, not screenshot histograms. -- SC0000 and menu/history checks retain current placement, wrapping, line advance, reveal timing, colors, - effects, and retained-object lifetime after the backend swap. +- Surface-string tests prove text is occluded by later handles and inherits the bound object's alpha, tint, + affine transform, source clipping, offscreen capture, and transition behavior in both render backends. +- Retained-layout tests prove per-glyph handle order, capacity bounds, partial/full erase, reset/republication, + final cursor, reveal/Skip click consumption, and save/load reconstruction without Label visibility shims. +- SC0000, STUDY/MAMES, HISTORY, HIDEWIN, BUNKI, and save/load checks retain placement, wrapping, line advance, + reveal timing, colors, effects, publication behavior, and native-backed lifetime after the backend swap. - The portable backend starts without Windows fonts or GDI, reports/uses its selected substitution policy, and passes layout/legibility tests without being labeled pixel-identical to native AGE. -- Glyph/font caches remain bounded and reveal-time rendering does not introduce visible frame stalls. +- Glyph/font caches and retained node/surface pools remain bounded. A complete line is rasterized once before + reveal, so revealing another glyph does not rerasterize or upload the whole text surface and introduces no + visible frame stalls. ---