Implement AGE text line spacing

This commit is contained in:
gamer147
2026-07-19 23:32:38 -04:00
parent 7c110d762b
commit 96e4448796
11 changed files with 90 additions and 17 deletions

View File

@@ -16,6 +16,7 @@ Struct `EngineCtx`, size `0xa1000`. Applied to the Ghidra `/v2` image (dispatch-
| `0xb558` | `gfx_dirty_a` | `int` | gfx dirty flag (anim set raises) |
| `0xb560` | `gfx_dirty_b` | `int` | gfx dirty flag |
| `0x14d54` | `gfx_obj_ptr_table` | `void*` | per-object pointer table (ops 0x212/0x213 write obj+0x64/0x68/0x6c) |
| `0x14ea0` | `text_line_spacing` | `int` | extra pixel leading between text lines; defaults to 6, op 0x8b writes it |
| `0x14f45` | `script_frame_index` | `int` | call-script frame index (0x1e-dword frames) |
| `0x46d14` | `query_table_46d14` | `void*` | stride-0x14 table read by op 0x216 |
| `0x51b64` | `frame_timer` | `int` | frame timer (present updates 0x51b64/0x51b68) |

View File

@@ -1700,6 +1700,31 @@ inventing unsigned behavior. `/v2` names/comments this mount/load/open chain and
---
### ADV text line spacing -- opcode `0x8b` (2026-07-19)
`op_0x8b_set_text_line_spacing@0x41f270` stores its single operand at ADV text-manager offset
`+0x560`, or `EngineCtx.text_line_spacing` (`ctx+0x14ea0`). This is pixel leading, not a font face,
weight, or effect selector. `adv_text_manager_initialize@0x456800` gives it a native default of 6.
The horizontal line-break path (`adv_text_append_line_break_horizontal@0x456fd0`) and retained History
renderer (`text_history_render_records@0x452970`) pass `manager+0x560 - manager+0x4cc` as the line
advance. Offset `+0x4cc` is the primary `LOGFONT.lfHeight`, which AGE stores as a negative pixel height,
so the effective pitch is `font pixel height + text_line_spacing`. The corresponding vertical-writing
path uses the same pitch to move to the next column. Corpus values support that interpretation: scripts
pair 8 pixels with 22/24-pixel Mincho text and 9 pixels with 16-pixel Gothic text.
History records do not snapshot the `+0x560` field. `text_history_append_text_record@0x456000` retains
font, color, effect, geometry, flags, and string state, while `text_history_render_records` reads the
manager's current line spacing when it encounters a retained line-break marker. The port therefore keeps
line spacing in the current ADV style manager and overrides a History render batch with that current value;
the individual record continues to supply its retained font/color/effect fields.
The `/v2` image names/comments the handler, manager initializer, horizontal/vertical line-break paths, and
their low-level cursor-advance helpers. The regenerated 58-field `EngineCtx` is applied and the program is
saved.
---
## Native walls backlog (targets for this loop)
- ~~**call-script dispatch**~~ — **SOLVED** (above): `call-script <id>` = raw SYS4INI file index.

View File

@@ -55,6 +55,11 @@
- **grounding:** source=investigation, confidence=high
- **evidence:** Ghidra /v2: op_0x85_handler@0x4163d0 calls text_history_clear@0x455d70 on the text manager. The worker destroys the 0x48-byte record vector and clears the 8-byte logical index vector. Corpus: two sites in each of 143 normal ADV scripts bracket the retained backlog lifetime.
### 0x8b `set-text-line-spacing` (u0041B3D0, argc 1)
- **summary:** (pixels) - set the extra pixel spacing added between text lines.
- **grounding:** source=investigation, confidence=high
- **evidence:** Ghidra /v2: op_0x8b_set_text_line_spacing@0x41f270 stores operand 1 at text manager +0x560 (ctx+0x14ea0). adv_text_manager_initialize@0x456800 defaults it to 6. Horizontal newline/history paths at 0x456fd0/0x452970 advance y by (+0x560 - primary LOGFONT.lfHeight); lfHeight is negative, so this is font pixel height plus configured leading. Corpus arguments are 8 for 22/24px Mincho blocks and 9 for 16px Gothic blocks.
### 0x197 `set-ruby-font-size` (set-ruby-font-size, argc 1)
- **summary:** (pixels) - set the secondary/ruby text font height and rebuild its native rasterization state.
- **grounding:** source=investigation, confidence=high
@@ -815,10 +820,6 @@ op 0x90 (u0041BEB0, argc 7): `0x90 x y w h tgt_a tgt_b tgt_c`. Kelebek left it "
- **summary:** —
- **grounding:** source=kelebek, confidence=low
### 0x8b `u0041B3D0` (u0041B3D0, argc 1)
- **summary:** —
- **grounding:** source=kelebek, confidence=low
### 0x8c `jmp` (jmp, argc 1)
- **summary:** —
- **grounding:** source=kelebek, confidence=med

View File

@@ -1899,3 +1899,30 @@ gap. Keep `0x1cb` deferred, and treat `0x1ce`/`0x20a` together as their own spri
Validation: all 205 engine tests pass, opcode and EngineCtx tests/lints are clean, vm0 RECOVER passes, the
Godot build has zero warnings, and threaded `SELFTEST OK`.
### ADV History text line spacing implemented (2026-07-19)
Native RE resolves the two remaining `0x8b` calls as text-manager line leading. The handler writes its
single pixel count to manager offset `+0x560` (`EngineCtx+0x14ea0`), whose initializer default is 6.
Horizontal newline and History rendering add that value to the primary font's positive pixel height; the
script's concrete style blocks use 8 pixels with their 22/24-pixel Mincho fonts and 9 with a 16-pixel
Gothic font.
The VM now retains that current style property, Godot applies it as the Label `line_spacing` theme
constant, and History rendering matches native ownership: line spacing comes from the text manager's
current state rather than from the retained record. A focused regression distinguishes a record retained
with spacing 9 from the current History spacing 8, and the real HISTORY route requires all non-empty rows
to render with its setup value of 8.
The canonical opcode and EngineCtx sources are regenerated. The `/v2` handler and all supporting
initializer/line-advance functions are named and commented, the expanded 58-field `EngineCtx` is reapplied,
and the program is saved. HISTORY is now 75/78 distinct opcodes and 851/854 instructions handled or
safe-noop. The remaining instructions are paired sprite-animation service ops `0x1ce`/`0x20a` and the
deferred Read-message Skip setting getter `0x1cb`.
**Next:** investigate and implement `0x1ce`/`0x20a` together as the final non-persistence History support
slice. Keep `0x1cb` deferred until the global save/profile storage boundary is chosen.
Validation: all 205 engine tests pass, including four focused History presentation cases; opcode and
EngineCtx tests/lints are clean, vm0 RECOVER passes, the Godot build has zero warnings, and threaded
`SELFTEST OK`.