Document numeric HUD opcode contracts

This commit is contained in:
gamer147
2026-07-21 15:40:46 -04:00
parent 9fd94ff540
commit e2973f66b1
5 changed files with 112 additions and 46 deletions

View File

@@ -2467,6 +2467,38 @@ Focused tests cover stability, overflow, and zero count. A real-script regressio
SYSTEM4-to-SC0000 state into CHMENU and stops after `0x1db8`, proving selected slot 2 survives the first SYSTEM4-to-SC0000 state into CHMENU and stops after `0x1db8`, proving selected slot 2 survives the first
release roster sort without a fallback. No startup seed or menu-specific roster injection is involved. release roster sort without a fallback. No startup seed or menu-specific roster injection is involved.
### Numeric glyph styles/draws and half-byte string length — `0x13a` / `0x23b` / `0x1a6` (2026-07-21)
The missing values in the first working `DEBUGMAP` field HUD are not missing globals or a failed texture
load. They are generated by a dedicated decimal-glyph subsystem whose two opcodes were still skipped by
the port:
- `op_0x13a_register_numeric_glyph_style@0x421ab0` takes
`(style_index, surface_slot, atlas_x, atlas_y, digit_width, digit_height)`. It accepts style indices
`0..10` and stores the remaining five operands in the 20-byte record at
`EngineCtx+0x55180+style_index*0x14`; an invalid index raises the standard script error. The corpus has
74 registrations in 24 scripts, including eight in `DRAWCHP.BIN`.
- `op_0x23b_draw_decimal_glyphs@0x424190` takes
`(base_handle, style_index, value, x, y, digit_capacity, flags)`. It first erases the destination handle
range, splits the signed integer through division/remainder by ten, and binds one retained object per
displayed digit using horizontally adjacent cells from the selected style record. Flag bit `0x1`
includes leading zeroes, bit `0x2` centers the used digits, and bit `0x4` left-aligns them; without an
alignment flag the value is right-aligned in the requested capacity. An invalid or unregistered style
raises the script error. The corpus has 147 draws in the same 24 scripts; `DRAWCHP.BIN` has 22 and uses
them for the field HUD's turn/control/mana/level/HP/SP/FS values.
This path creates ordinary retained graphics objects, so the existing atlas decode and compositor are
already the correct backend. The missing engine model is the 11 style records plus the two VM dispatches;
it is not an immediate `GodotAdvHost.DrawTexture` raster operation.
The absent unit and weapon names are a separate layout-compute gap. `DRAWCHP.BIN` does populate both
strings and calls `draw-string` at `0x9c6`/`0x9f5`, but first centers each one with opcode `0x1a6`.
`op_0x1a6_half_byte_strlen@0x427020` resolves the NUL-terminated engine byte string and writes
`strlen(bytes) >> 1`. The script multiplies that result by 21 and subtracts it from x=257 on a 263-pixel
scratch surface. With the opcode skipped, x remains 257 and Godot correctly clips nearly all of the text.
An implementation must preserve the original encoded byte count (or reproduce it with CP932 encoding),
not use the .NET UTF-16 character count.
--- ---
## Native walls backlog (targets for this loop) ## Native walls backlog (targets for this loop)

View File

@@ -237,6 +237,13 @@ Implemented with domain-preserving addressed-array access, native signed 32-bit
- **grounding:** source=investigation, confidence=high - **grounding:** source=investigation, confidence=high
- **evidence:** Ghidra /v2 op_0x194_string_equals@0x426e20 fetches operands 2 and 3 through the string resolver, compares their byte ranges through FUN_004017a0, and writes compare_result==0 to integer operand 1. INIT2 and GAMESTART use it as a branch predicate for INPUTNAME/default-name handling; the natural Game Start diagnostic reached one GAMESTART call at 0x134c. The C# VM implements ordinal equality through the shared string resolver, covering literal, global, local, global-string-pointer, and local-string-pointer operands; the traced natural-boot regression proves the reached GAMESTART call no longer falls back. - **evidence:** Ghidra /v2 op_0x194_string_equals@0x426e20 fetches operands 2 and 3 through the string resolver, compares their byte ranges through FUN_004017a0, and writes compare_result==0 to integer operand 1. INIT2 and GAMESTART use it as a branch predicate for INPUTNAME/default-name handling; the natural Game Start diagnostic reached one GAMESTART call at 0x134c. The C# VM implements ordinal equality through the shared string resolver, covering literal, global, local, global-string-pointer, and local-string-pointer operands; the traced natural-boot regression proves the reached GAMESTART call no longer falls back.
### 0x1a6 `half-byte-string-length` (halve-strlen, argc 2)
- **summary:** Write half the resolved string's byte length, using integer truncation.
- **grounding:** source=investigation, confidence=high
- **evidence:** Ghidra /v2: op_0x1a6_half_byte_strlen@0x427020 resolves operand 2, scans to NUL, and vm_operand_write(1, strlen_bytes >> 1). DRAWCHP sites 0x9b3/0x9e2 multiply the result by 21 and subtract it from x=257 before draw-string.
Native applies strlen to the NUL-terminated engine byte string and shifts the byte count right by one. DRAWCHP uses this Shift-JIS-oriented width proxy to center unit and weapon names before rasterizing them into a scratch surface.
### 0x1b0 `copy-dwords` (copy-dwords, argc 3) ### 0x1b0 `copy-dwords` (copy-dwords, argc 3)
- **summary:** (source)(destination)(count) - copy count consecutive 32-bit cells from source to destination. - **summary:** (source)(destination)(count) - copy count consecutive 32-bit cells from source to destination.
- **grounding:** source=investigation, confidence=high - **grounding:** source=investigation, confidence=high
@@ -412,6 +419,14 @@ The handler uses an alpha step of 1 and timer interval=argument when argument <=
- **grounding:** source=investigation, confidence=high - **grounding:** source=investigation, confidence=high
- **evidence:** Ghidra /v2: op_0x131_handler@0x4295e0 calls the settings getter with `message:MesWinAlpha` and writes the result. HISTORY.BIN and the shared ADV redraw path compute (16-value)<<4 for the control-strip alpha. - **evidence:** Ghidra /v2: op_0x131_handler@0x4295e0 calls the settings getter with `message:MesWinAlpha` and writes the result. HISTORY.BIN and the shared ADV redraw path compute (16-value)<<4 for the control-strip alpha.
### 0x13a `register-numeric-glyph-style` (u0041F3A0, argc 6)
- **summary:** Register one of 11 decimal-glyph atlas styles as (surface slot, source x/y, digit width/height).
- **grounding:** source=investigation, confidence=high
- **depended on by:** 0x23b
- **evidence:** Ghidra /v2: op_0x13a_register_numeric_glyph_style@0x421ab0 writes operands 2..6 to the selected five-dword record at EngineCtx+0x55180 after enforcing style_index<11. Corpus: 74 sites in 24 scripts; DRAWCHP has eight registrations immediately before its stylized HUD-number draws.
The five-dword definition is stored at EngineCtx+0x55180+style_index*0x14. Opcode 0x23b consumes it to turn an integer into retained draw objects, one atlas cell per decimal digit. An index outside [0,10] raises the engine's script error.
### 0x1f6 `clear-retained-gfx-objects` (clear-retained-gfx-objects, argc 0) ### 0x1f6 `clear-retained-gfx-objects` (clear-retained-gfx-objects, argc 0)
- **summary:** Clear the complete retained gfx-object registry while preserving allocated surface resources. Subsequent object queries return absent until draw/geometry operations recreate records. - **summary:** Clear the complete retained gfx-object registry while preserving allocated surface resources. Subsequent object queries return absent until draw/geometry operations recreate records.
- **grounding:** source=investigation, confidence=high - **grounding:** source=investigation, confidence=high
@@ -650,6 +665,14 @@ The handler requires an existing destination texture, allocates/reuses a 0x478-b
- **grounding:** source=investigation, confidence=high - **grounding:** source=investigation, confidence=high
- **evidence:** Native /v2 worker and gfx_object_apply_transform_channels decompile. The consumer advances target_frame cells over duration after delay, preserves the existing source-rect dimensions, and commits the endpoint. - **evidence:** Native /v2 worker and gfx_object_apply_transform_channels decompile. The consumer advances target_frame cells over duration after delay, preserves the existing source-rect dimensions, and commits the endpoint.
### 0x23b `draw-decimal-glyphs` (u00422460, argc 7)
- **summary:** Draw an integer as decimal glyph objects from a style registered by opcode 0x13a.
- **grounding:** source=investigation, confidence=high
- **depends on:** 0x13a
- **evidence:** Ghidra /v2: op_0x23b_draw_decimal_glyphs@0x424190 reads the five-dword style record at EngineCtx+0x55180, erases the destination handle range, and calls gfx_object_bind_draw once per displayed digit. Corpus: 147 sites in 24 scripts; DRAWCHP contains 22 calls for the field HUD, including turn/control/mana/level/HP/SP/FS values visible as blank in the DEBUGMAP discrepancy.
First erase digit_capacity objects beginning at base_handle. Then split value by signed division/modulo 10 and bind at most digit_capacity retained objects using adjacent digit-width cells from the registered atlas. Flags bit 0 zero-pads, bit 1 centers the used digits, and bit 2 left-aligns them; with no alignment bit the value is right-aligned in the capacity. Invalid or unregistered style indices raise the engine's script error.
### 0x23d `release-transient-surfaces` (release-transient-surfaces, argc 0) ### 0x23d `release-transient-surfaces` (release-transient-surfaces, argc 0)
- **summary:** Stop movie bindings and release transient gfx surface slots 42 through 999 inclusive, preserving system-owned slots 0 through 41. - **summary:** Stop movie bindings and release transient gfx surface slots 42 through 999 inclusive, preserving system-owned slots 0 through 41.
- **grounding:** source=investigation, confidence=high - **grounding:** source=investigation, confidence=high
@@ -1075,10 +1098,6 @@ op 0x90 (u0041BEB0, argc 7): `0x90 x y w h tgt_a tgt_b tgt_c`. Kelebek left it "
- **summary:** — - **summary:** —
- **grounding:** source=kelebek, confidence=low - **grounding:** source=kelebek, confidence=low
### 0x13a `u0041F3A0` (u0041F3A0, argc 6)
- **summary:** —
- **grounding:** source=kelebek, confidence=low
### 0x13f `check-bit` (check-bit, argc 3) ### 0x13f `check-bit` (check-bit, argc 3)
- **summary:** — - **summary:** —
- **grounding:** source=kelebek, confidence=med - **grounding:** source=kelebek, confidence=med
@@ -1143,10 +1162,6 @@ op 0x90 (u0041BEB0, argc 7): `0x90 x y w h tgt_a tgt_b tgt_c`. Kelebek left it "
- **summary:** — - **summary:** —
- **grounding:** source=kelebek, confidence=med - **grounding:** source=kelebek, confidence=med
### 0x1a6 `halve-strlen` (halve-strlen, argc 2)
- **summary:** —
- **grounding:** source=kelebek, confidence=med
### 0x1a7 `comment` (comment, argc 1) ### 0x1a7 `comment` (comment, argc 1)
- **summary:** — - **summary:** —
- **grounding:** source=kelebek, confidence=med - **grounding:** source=kelebek, confidence=med
@@ -1219,10 +1234,6 @@ op 0x90 (u0041BEB0, argc 7): `0x90 x y w h tgt_a tgt_b tgt_c`. Kelebek left it "
- **summary:** — - **summary:** —
- **grounding:** source=kelebek, confidence=low - **grounding:** source=kelebek, confidence=low
### 0x23b `u00422460` (u00422460, argc 7)
- **summary:** —
- **grounding:** source=kelebek, confidence=low
### 0x23c `u004162B0` (u004162B0, argc 0) ### 0x23c `u004162B0` (u004162B0, argc 0)
- **summary:** — - **summary:** —
- **grounding:** source=kelebek, confidence=low - **grounding:** source=kelebek, confidence=low

View File

@@ -434,6 +434,23 @@ that value during synchronous movie-graph initialization and retains it per movi
slots return -1; unavailable timing metadata warns and returns -1 rather than exposing native's undefined slots return -1; unavailable timing metadata warns and returns -1 rather than exposing native's undefined
failure output. failure output.
The next visual comparison found all field-HUD numbers absent while their labels, bars, and source artwork
were present, plus apparently blank unit/weapon text. Native and script-side tracing resolves this into two
bounded opcode gaps rather than missing game state:
- `0x13a` registers one of 11 `(surface, atlas x/y, digit width/height)` styles, and `0x23b` expands a
decimal value into retained per-digit objects with zero-pad/center/left/right layout flags. `DRAWCHP.BIN`
contains eight style registrations and 22 numeric draws covering the visible turn/control/mana/level and
HP/SP/FS fields. Both opcodes are currently unimplemented, which precisely explains the blank values.
- `DRAWCHP.BIN` already reads and submits the unit and weapon strings to `0x204`, but its two preceding
`0x1a6` centering calls are unimplemented. Native `0x1a6` returns `strlen(CP932_bytes) >> 1`; skipping it
places the strings at x=257 on a 263-pixel scratch surface, so the existing compositor clips them.
The exact native contracts and addresses live in `docs/engine-re.md`; opcode-source metadata is in
`vm-map/opcodes.toml`. No runtime implementation was made in this investigation. The next field slice is
therefore to implement the shared 11-style numeric renderer and the encoded-byte-length calculation, then
recheck the same `DEBUGMAP` HUD before pursuing any state seeding.
## Later Phase B breadth ## Later Phase B breadth
Once the natural spine and first gameplay loop are trustworthy, broaden in independent tracks: Once the natural spine and first gameplay loop are trustworthy, broaden in independent tracks:

View File

@@ -41,11 +41,13 @@ INFERRED: dict[int, dict] = {
0x10b: dict(name='map-mouse-button', category='input', noop=False, confidence='high', source='investigation', summary='(button_slot)(physical_button) - map a physical mouse button to a logical button slot; polling emits the slot as logical action slot+4.'), 0x10b: dict(name='map-mouse-button', category='input', noop=False, confidence='high', source='investigation', summary='(button_slot)(physical_button) - map a physical mouse button to a logical button slot; polling emits the slot as logical action slot+4.'),
0x10c: dict(name='map-keyboard-scancode', category='input', noop=False, confidence='high', source='investigation', summary='(action)(dik_scan_code) - map a DirectInput keyboard scan code to a logical input action.'), 0x10c: dict(name='map-keyboard-scancode', category='input', noop=False, confidence='high', source='investigation', summary='(action)(dik_scan_code) - map a DirectInput keyboard scan code to a logical input action.'),
0x10d: dict(name='consume-mouse-wheel-delta', category='input', noop=False, confidence='high', source='investigation', summary='(out) - return the accumulated signed mouse-wheel delta and clear it.'), 0x10d: dict(name='consume-mouse-wheel-delta', category='input', noop=False, confidence='high', source='investigation', summary='(out) - return the accumulated signed mouse-wheel delta and clear it.'),
0x13a: dict(name='register-numeric-glyph-style', category='draw', noop=False, confidence='high', source='investigation', summary='Register one of 11 decimal-glyph atlas styles as (surface slot, source x/y, digit width/height).'),
0x140: dict(name='coroutine-label-yield', category='control', noop=False, confidence='med', source='investigation', summary="(out)(name_str)(sub_str)(in) — scene-coroutine LOOP ITERATOR / labeled yield. Handler copies name/sub strings + the int operand and calls the NATIVE video/transition service (*DAT_005c6018)(8, ctx[0x54fe8], &{name,sub,in}); writes the returned PC-like value to operand 1. In SC0000 label_462 'ループ開始' (@0x46d): `out=G[0x6be]=LABEL('J',G[0x6be])`; loop runs the intro-setup body (incl. call label_125bd = slot-table fill G[0x3239..0x324e]=4..11) and jmps back until out==G[0x6c3] (a per-scene exit-PC immediate) → mov aba5c 0 → content. The gate G[0xaba5c]==1 that opens this loop is NATIVE scene-entry state (no script sets it to 1). DAT_005c6018 is runtime-resolved (all xrefs READ) = SAME class as the DirectDraw workers we don't model. PORT = HOST-MODEL IMPLEMENTED: synthesize the ADV scene-entry gate, run the LABEL/J setup body once, then return the structurally discovered per-scene terminal; do not emulate the video service. See engine-re.md §Scene-coroutine framework."), 0x140: dict(name='coroutine-label-yield', category='control', noop=False, confidence='med', source='investigation', summary="(out)(name_str)(sub_str)(in) — scene-coroutine LOOP ITERATOR / labeled yield. Handler copies name/sub strings + the int operand and calls the NATIVE video/transition service (*DAT_005c6018)(8, ctx[0x54fe8], &{name,sub,in}); writes the returned PC-like value to operand 1. In SC0000 label_462 'ループ開始' (@0x46d): `out=G[0x6be]=LABEL('J',G[0x6be])`; loop runs the intro-setup body (incl. call label_125bd = slot-table fill G[0x3239..0x324e]=4..11) and jmps back until out==G[0x6c3] (a per-scene exit-PC immediate) → mov aba5c 0 → content. The gate G[0xaba5c]==1 that opens this loop is NATIVE scene-entry state (no script sets it to 1). DAT_005c6018 is runtime-resolved (all xrefs READ) = SAME class as the DirectDraw workers we don't model. PORT = HOST-MODEL IMPLEMENTED: synthesize the ADV scene-entry gate, run the LABEL/J setup body once, then return the structurally discovered per-scene terminal; do not emulate the video service. See engine-re.md §Scene-coroutine framework."),
0x199: dict(name='yield-adv-coroutine', category='control', noop=False, confidence='high', source='investigation', summary='Yield/re-enter the registered ADV coroutine handler. The fifth standard chrome button uses this transition to enter the HIDEWIN/window-hidden flow.'), 0x199: dict(name='yield-adv-coroutine', category='control', noop=False, confidence='high', source='investigation', summary='Yield/re-enter the registered ADV coroutine handler. The fifth standard chrome button uses this transition to enter the HIDEWIN/window-hidden flow.'),
0x19a: dict(name='get-message-skip', category='input', noop=False, confidence='high', source='investigation', summary='(out) - return the current all-message skip state set by op 0x88.'), 0x19a: dict(name='get-message-skip', category='input', noop=False, confidence='high', source='investigation', summary='(out) - return the current all-message skip state set by op 0x88.'),
0x19b: dict(name='suspend-adv-skip-service', category='input', noop=False, confidence='high', source='investigation', summary="() - suspend active ADV fast-forward while preserving the user's persistent all-message Skip toggle."), 0x19b: dict(name='suspend-adv-skip-service', category='input', noop=False, confidence='high', source='investigation', summary="() - suspend active ADV fast-forward while preserving the user's persistent all-message Skip toggle."),
0x19c: dict(name='resume-adv-skip-service', category='input', noop=False, confidence='high', source='investigation', summary='() - enable the ADV skip service and recompute active fast-forward from persistent all-message Skip or the live read-skip channel.'), 0x19c: dict(name='resume-adv-skip-service', category='input', noop=False, confidence='high', source='investigation', summary='() - enable the ADV skip service and recompute active fast-forward from persistent all-message Skip or the live read-skip channel.'),
0x1a6: dict(name='half-byte-string-length', category='compute', noop=False, confidence='high', source='investigation', summary="Write half the resolved string's byte length, using integer truncation."),
0x1a8: dict(name='instruction-marker-noop', category='marker', noop=True, confidence='high', source='investigation', summary="Zero-operand structural marker. The native shared 0xaf/0x1a8 handler only records this instruction's one-dword length and returns."), 0x1a8: dict(name='instruction-marker-noop', category='marker', noop=True, confidence='high', source='investigation', summary="Zero-operand structural marker. The native shared 0xaf/0x1a8 handler only records this instruction's one-dword length and returns."),
0x1b6: dict(name='get-auto-message', category='input', noop=False, confidence='high', source='investigation', summary='(out) - return whether automatic message advance is enabled.'), 0x1b6: dict(name='get-auto-message', category='input', noop=False, confidence='high', source='investigation', summary='(out) - return whether automatic message advance is enabled.'),
0x1b7: dict(name='set-auto-message', category='input', noop=False, confidence='high', source='investigation', summary='(enabled) - enable or disable automatic message advance.'), 0x1b7: dict(name='set-auto-message', category='input', noop=False, confidence='high', source='investigation', summary='(enabled) - enable or disable automatic message advance.'),
@@ -71,6 +73,7 @@ INFERRED: dict[int, dict] = {
0x231: dict(name='animate-gfx-srcrect-loop', category='draw', noop=False, confidence='high', source='investigation', summary="(handle)(frame_period_ms)(frame_count)(column_count) — loop row-major through the spritesheet. Every frame preserves draw-texture's source-rectangle width/height; frame=floor(elapsed/frame_period)%frame_count, src offset=(frame%columns*width, frame/columns*height). Worker gfx_worker_anim_srcrect @0x47eec0; consumer gfx_object_anim_interpolate @0x473ed0."), 0x231: dict(name='animate-gfx-srcrect-loop', category='draw', noop=False, confidence='high', source='investigation', summary="(handle)(frame_period_ms)(frame_count)(column_count) — loop row-major through the spritesheet. Every frame preserves draw-texture's source-rectangle width/height; frame=floor(elapsed/frame_period)%frame_count, src offset=(frame%columns*width, frame/columns*height). Worker gfx_worker_anim_srcrect @0x47eec0; consumer gfx_object_anim_interpolate @0x473ed0."),
0x232: dict(name='animate-gfx-color-loop', category='draw', noop=False, confidence='high', source='investigation', summary='0x232 anim-color (handle)(period)(alpha)(color): ping-pong the temporary packed ARGB passed to the normal object blit. Handler resolves negative alpha/RGB from static color obj+0x60 and clamps alpha above 255. Blend selector obj+0x30 is unchanged: mode 0 keeps default blending (animated alpha is inert; RGB is vertex modulation), while mode 1 uses sampled ARGB alpha as the SRCALPHA scale for additive composition. Fresh static color is 0xffffffff. The C# VM resolves sentinels and consumes sampled ARGB through the unchanged mode-specific path. See docs/engine-re.md §SC0000 anim cluster.'), 0x232: dict(name='animate-gfx-color-loop', category='draw', noop=False, confidence='high', source='investigation', summary='0x232 anim-color (handle)(period)(alpha)(color): ping-pong the temporary packed ARGB passed to the normal object blit. Handler resolves negative alpha/RGB from static color obj+0x60 and clamps alpha above 255. Blend selector obj+0x30 is unchanged: mode 0 keeps default blending (animated alpha is inert; RGB is vertex modulation), while mode 1 uses sampled ARGB alpha as the SRCALPHA scale for additive composition. Fresh static color is 0xffffffff. The C# VM resolves sentinels and consumes sampled ARGB through the unchanged mode-specific path. See docs/engine-re.md §SC0000 anim cluster.'),
0x239: dict(name='animate-gfx-srcrect-target', category='draw', noop=False, confidence='high', source='investigation', summary='(handle)(delay_ms)(duration_ms)(frame_count)(column_count)(target_frame) — one-shot row-major source-rectangle cell channel. Worker gfx_worker_set_srcrect_cell @0x47ed90 stores timing at obj+0x48/+0x5c, layout at +0x238/+0x23c, and target at +0x234. C# currently retains the endpoint cell immediately.'), 0x239: dict(name='animate-gfx-srcrect-target', category='draw', noop=False, confidence='high', source='investigation', summary='(handle)(delay_ms)(duration_ms)(frame_count)(column_count)(target_frame) — one-shot row-major source-rectangle cell channel. Worker gfx_worker_set_srcrect_cell @0x47ed90 stores timing at obj+0x48/+0x5c, layout at +0x238/+0x23c, and target at +0x234. C# currently retains the endpoint cell immediately.'),
0x23b: dict(name='draw-decimal-glyphs', category='draw', noop=False, confidence='high', source='investigation', summary='Draw an integer as decimal glyph objects from a style registered by opcode 0x13a.'),
0x23f: dict(name='query-surface-stop-time-ms', category='draw', noop=False, confidence='high', source='investigation', summary='(out_stop_time_ms)(surface_slot) — query the DirectShow stop position retained by a loaded movie surface, convert seconds to integer milliseconds by truncating toward zero, and write -1 when the movie slot is empty. The port retains this metadata during 0x236 graph initialization; unavailable metadata emits a warning and also returns -1.'), 0x23f: dict(name='query-surface-stop-time-ms', category='draw', noop=False, confidence='high', source='investigation', summary='(out_stop_time_ms)(surface_slot) — query the DirectShow stop position retained by a loaded movie surface, convert seconds to integer milliseconds by truncating toward zero, and write -1 when the movie slot is empty. The port retains this metadata during 0x236 graph initialization; unavailable metadata emits a warning and also returns -1.'),
0x258: dict(name='decl?', category='marker', noop=True, confidence='low', source='harness', summary='2 imm; runs in a chain right after script-entry 0x259, enumerating ids — prologue declaration/registration?'), 0x258: dict(name='decl?', category='marker', noop=True, confidence='low', source='harness', summary='2 imm; runs in a chain right after script-entry 0x259, enumerating ids — prologue declaration/registration?'),
0x259: dict(name='script-entry', category='marker', noop=True, confidence='low', source='harness', summary='zero-arg; the first instruction of a script (offset 0), opens the decl chain that 0x258 continues — script/prologue entry marker, structural'), 0x259: dict(name='script-entry', category='marker', noop=True, confidence='low', source='harness', summary='zero-arg; the first instruction of a script (offset 0), opens the decl chain that 0x258 continues — script/prologue entry marker, structural'),

View File

@@ -2814,43 +2814,44 @@ argc = 6
abi_source = "kelebek+decode-validated" abi_source = "kelebek+decode-validated"
[opcode.semantics] [opcode.semantics]
name = "u0041F3A0" name = "register-numeric-glyph-style"
category = "unknown" category = "draw"
summary = "" summary = "Register one of 11 decimal-glyph atlas styles as (surface slot, source x/y, digit width/height)."
details = "The five-dword definition is stored at EngineCtx+0x55180+style_index*0x14. Opcode 0x23b consumes it to turn an integer into retained draw objects, one atlas cell per decimal digit. An index outside [0,10] raises the engine's script error."
noop_headless = false noop_headless = false
source = "kelebek" source = "investigation"
confidence = "low" confidence = "high"
depends_on = [] depends_on = []
evidence = "" evidence = "Ghidra /v2: op_0x13a_register_numeric_glyph_style@0x421ab0 writes operands 2..6 to the selected five-dword record at EngineCtx+0x55180 after enforcing style_index<11. Corpus: 74 sites in 24 scripts; DRAWCHP has eight registrations immediately before its stylized HUD-number draws."
[[opcode.semantics.args]] [[opcode.semantics.args]]
i = 1 i = 1
role = "" role = "style_index"
observed_types = ["imm"] observed_types = ["imm"]
[[opcode.semantics.args]] [[opcode.semantics.args]]
i = 2 i = 2
role = "" role = "surface_slot"
observed_types = ["imm", "l-int"] observed_types = ["imm", "l-int"]
[[opcode.semantics.args]] [[opcode.semantics.args]]
i = 3 i = 3
role = "" role = "atlas_x"
observed_types = ["imm"] observed_types = ["imm"]
[[opcode.semantics.args]] [[opcode.semantics.args]]
i = 4 i = 4
role = "" role = "atlas_y"
observed_types = ["imm"] observed_types = ["imm"]
[[opcode.semantics.args]] [[opcode.semantics.args]]
i = 5 i = 5
role = "" role = "digit_width"
observed_types = ["imm"] observed_types = ["imm"]
[[opcode.semantics.args]] [[opcode.semantics.args]]
i = 6 i = 6
role = "" role = "digit_height"
observed_types = ["imm"] observed_types = ["imm"]
[[opcode]] [[opcode]]
@@ -3559,23 +3560,24 @@ argc = 2
abi_source = "kelebek+decode-validated" abi_source = "kelebek+decode-validated"
[opcode.semantics] [opcode.semantics]
name = "halve-strlen" name = "half-byte-string-length"
category = "unknown" category = "compute"
summary = "" summary = "Write half the resolved string's byte length, using integer truncation."
details = "Native applies strlen to the NUL-terminated engine byte string and shifts the byte count right by one. DRAWCHP uses this Shift-JIS-oriented width proxy to center unit and weapon names before rasterizing them into a scratch surface."
noop_headless = false noop_headless = false
source = "kelebek" source = "investigation"
confidence = "med" confidence = "high"
depends_on = [] depends_on = []
evidence = "" evidence = "Ghidra /v2: op_0x1a6_half_byte_strlen@0x427020 resolves operand 2, scans to NUL, and vm_operand_write(1, strlen_bytes >> 1). DRAWCHP sites 0x9b3/0x9e2 multiply the result by 21 and subtract it from x=257 before draw-string."
[[opcode.semantics.args]] [[opcode.semantics.args]]
i = 1 i = 1
role = "" role = "destination"
observed_types = ["l-int"] observed_types = ["l-int"]
[[opcode.semantics.args]] [[opcode.semantics.args]]
i = 2 i = 2
role = "" role = "string"
observed_types = ["l-str", "l-str-ptr"] observed_types = ["l-str", "l-str-ptr"]
[[opcode]] [[opcode]]
@@ -6299,48 +6301,49 @@ argc = 7
abi_source = "kelebek+decode-validated" abi_source = "kelebek+decode-validated"
[opcode.semantics] [opcode.semantics]
name = "u00422460" name = "draw-decimal-glyphs"
category = "unknown" category = "draw"
summary = "" summary = "Draw an integer as decimal glyph objects from a style registered by opcode 0x13a."
details = "First erase digit_capacity objects beginning at base_handle. Then split value by signed division/modulo 10 and bind at most digit_capacity retained objects using adjacent digit-width cells from the registered atlas. Flags bit 0 zero-pads, bit 1 centers the used digits, and bit 2 left-aligns them; with no alignment bit the value is right-aligned in the capacity. Invalid or unregistered style indices raise the engine's script error."
noop_headless = false noop_headless = false
source = "kelebek" source = "investigation"
confidence = "low" confidence = "high"
depends_on = [] depends_on = [0x13a]
evidence = "" evidence = "Ghidra /v2: op_0x23b_draw_decimal_glyphs@0x424190 reads the five-dword style record at EngineCtx+0x55180, erases the destination handle range, and calls gfx_object_bind_draw once per displayed digit. Corpus: 147 sites in 24 scripts; DRAWCHP contains 22 calls for the field HUD, including turn/control/mana/level/HP/SP/FS values visible as blank in the DEBUGMAP discrepancy."
[[opcode.semantics.args]] [[opcode.semantics.args]]
i = 1 i = 1
role = "" role = "base_handle"
observed_types = ["imm", "l-int"] observed_types = ["imm", "l-int"]
[[opcode.semantics.args]] [[opcode.semantics.args]]
i = 2 i = 2
role = "" role = "style_index"
observed_types = ["imm", "l-int"] observed_types = ["imm", "l-int"]
[[opcode.semantics.args]] [[opcode.semantics.args]]
i = 3 i = 3
role = "" role = "value"
observed_types = ["imm", "g-int", "l-int", "l-ptr"] observed_types = ["imm", "g-int", "l-int", "l-ptr"]
[[opcode.semantics.args]] [[opcode.semantics.args]]
i = 4 i = 4
role = "" role = "destination_x"
observed_types = ["imm", "l-int"] observed_types = ["imm", "l-int"]
[[opcode.semantics.args]] [[opcode.semantics.args]]
i = 5 i = 5
role = "" role = "destination_y"
observed_types = ["imm", "l-int"] observed_types = ["imm", "l-int"]
[[opcode.semantics.args]] [[opcode.semantics.args]]
i = 6 i = 6
role = "" role = "digit_capacity"
observed_types = ["imm"] observed_types = ["imm"]
[[opcode.semantics.args]] [[opcode.semantics.args]]
i = 7 i = 7
role = "" role = "flags"
observed_types = ["imm"] observed_types = ["imm"]
[[opcode]] [[opcode]]