Add 'annotate-usecode' command to import USECODE IR JSON annotations
- Introduced a new command 'annotate-usecode' to import USECODE IR JSON annotation hints as Ghidra comments on compiled anchors. - Added argument parsing for multiple IR JSON files, comment type selection, and a dry-run option. - Implemented logic to read annotation records from the provided IR files and set comments on the corresponding addresses in Ghidra. - Enhanced JSON schema to include response structure for the new command.
This commit is contained in:
parent
4d3c8cd81b
commit
daa363c3d2
39 changed files with 41450 additions and 871 deletions
|
|
@ -236,6 +236,9 @@ Current verified caller-side detail:
|
|||
- The seg127 fade-controller ownership is also one step tighter in the same lane. `transition_preentry_setup_resources` resets `0x630a` at `000c:c855`, `transition_preentry_step_script` now has a verified early gate at `000c:ca25` that yields to the fade controller whenever `0x630a` is active, and `transition_palette_fade_begin` at `000c:cdca` explicitly installs palette source/range/step state into `0x630e..0x6316`, asserts `0x630a`, and kicks one immediate fade tick.
|
||||
- Fade direction is now pinned to seg126 script-control bytes rather than the outer seg005 wrappers. Inside `transition_preentry_step_script`, control byte `0x5e` reaches `palette_fade_begin_full_down` at `000c:cb06`, while control byte `0x26` reaches `palette_fade_begin_full_up` at `000c:cd1a`; control byte `0x2a` shares the same post-fade bookkeeping path after the full-up call.
|
||||
- The upstream producer path for the remaining seg126 control bytes is now tighter too. `transition_preentry_setup_resources` composes one path from the mutable base at `0x6aa:0x6ac` plus local name buffers (`0x631c`, `0x6335`) through the seg072 slash-aware path helper `0009:3600`, opens that file through `file_handle_alloc_init_and_open`, allocates a buffer of the returned size, reads the full payload into `0x6301:0x6303`, and seeds `0x62fa/0x62fc/0x62ff/0x6305/0x630a/0x6318` before the loop starts. Current best reading is therefore `file-backed transition script/control buffer`, not locally synthesized opcodes.
|
||||
- The adjacent seg126 selector lane is now classified tightly enough for conservative renames. `transition_file_family_select_and_refresh` (`000c:afa5`) keys object field `+0x49` through values `0`, `1`, and `4`, composes three sibling filenames from the inherited base `0x6aa:0x6ac` plus shared stem `0x621c` with suffix buffers `0x6223`, `0x622d`, and `0x6237`, loads the chosen file into object `+0x520`, and then runs the same redraw/palette/input refresh path. The same helper uses `field49==2` as a direct `vtable[0x3c]` callback branch and `field49==-1` as a normalize-back-to-zero state.
|
||||
- The local wrappers around that selector now sharpen the caller model without forcing a stronger UI label. `transition_file_family_advance_on_anim_tick` (`000c:b153`) increments `+0x49` when the polled byte at `[param_2+0x14+0xa]` is clear and then re-enters the selector, while `transition_file_family_input_key_handler` (`000c:b199`) maps Left/Right and `n/N` into previous/next selector steps, uses `e/E` plus repeated `-` to force selector state `4`, and otherwise exits through `vtable[0x3c]`.
|
||||
- This closes the narrow `+0x49` question as a local three-way file-family selector lane, but it still does not justify a stronger UI label for the paired `0x8c5c/0x8c60` renderer presets or the sibling seg127 fade inputs.
|
||||
- The remaining `transition_preentry_step_script` opcodes now have stable local mechanics even though the higher-level text semantics are still open. Control byte `0x21` consumes the next script word into `SI` and advances `0x62ff` by two, which makes it the current baseline/start-position loader for later text draws. Control byte `0x40` renders one null-terminated entry from the same script buffer through renderer object `0x8c5c:0x8c5e`, while control byte `0x24` mirrors that behavior through `0x8c60:0x8c62`; both paths measure width through the renderer vtable, draw through seg088 `000a:30d7`, blit through seg080 `0009:943a`, advance `SI` by rendered width plus four, and then scan forward to the next opcode byte. Control byte `0x23` sets local completion byte `0x62fe = 1` and returns, so the outer shell exits on the next loop test instead of iterating further.
|
||||
- Secondary renderer-factory sampling keeps the `0x8c5c` / `0x8c60` split conservative. Other sampled `000a:9748` xrefs use different adjacent preset pairs such as `0x0d/0x0c` at `0007:df30/df3f` and `0x0c/0x0f` at `0008:47c9/4851`, while no sampled caller reproduced the exact `0x10/0x11` startup pair outside `transition_preentry_setup_resources`. That supports keeping these as paired preset text renderers without forcing a title/body or normal/highlight label.
|
||||
- The missing seg126 step body at `000c:ca1d` still cannot be split out safely because `create_function_by_address` collides with the existing oversized overlap namespace, so this pass preserved the recovery as a decompiler comment instead of forcing a destructive boundary repair. Current best reading is still that `000c:ca1d..cd34` is the real `transition_preentry_step_script` body and that `000c:cd35` starts the fade-tick helper.
|
||||
|
|
@ -317,6 +320,14 @@ Current best neutral conclusion from this pass: the shared `g_active_dispatch_en
|
|||
- The in-scope `0x31a2` readers are now classed cleanly by role. `0004:c24d` and `000c:e4d8` are edge waits; `000c:ca11` is the seg126 modal-break exit; `000c:e546`, `000c:e5c6`, and `000d:c0ee` are cleanup-abort exits; `000d:9304` and `000d:b6b1` are deferred dispatch/state-advance gates.
|
||||
- Two remaining `0x31a2` reads stay outside that presentation classification set. `0005:453d` is only a plain getter wrapper for the shared depth word, and `0008:5149` is a seg008 internal/accounting-side read that adds the current depth to another local count before tripping a `>= 0x10` capacity flag.
|
||||
|
||||
### Current batch: renderer preset contract and seg127 fade-input closure
|
||||
|
||||
- `transition_preentry_setup_resources` is now exact on the paired renderer setup path. Instruction window `000c:c659..c6ab` shows that `FUN_000a_9748` is called only with preset ids `0x10` and `0x11`, storing the resulting temporary renderer objects at `0x8c5c:0x8c5e` and `0x8c60:0x8c62`, then immediately drawing the same seed text buffer `DS:0x631a` at `(0x0a,0x0a)` through both. This closes the structural question as `paired preset text lanes` inside one temporary transition presentation path, but still does not justify a stronger title/body or highlight/shadow label.
|
||||
- The recovered `transition_preentry_step_script` body is also slightly tighter on the two text opcodes. `0x40` and `0x24` both measure their string through renderer vtable slot `+0x0c`, center it inside a `0x280`-wide lane, fetch rendered width through slot `+0x08`, draw through seg088 `000a:30d7`, blit through seg080 `0009:943a`, and advance `SI` by `rendered_width + 4`; only the selected preset lane differs (`0x8c5c` for `0x40`, `0x8c60` for `0x24`).
|
||||
- The seg127 fade-controller inputs are now exact rather than only role-level. `transition_palette_fade_begin` stores palette source at `0x630e:0x6310`, start index at `0x6312`, count at `0x6314`, step at `0x6316`, brightness at `0x630d`, active flag at `0x630a`, and direction/state at `0x630b`, then immediately ticks the local fade controller. `transition_palette_fade_tick` dispatches `0x630b==1` to `transition_palette_fade_out_step` and `0x630b==2` to `transition_palette_fade_in_step`.
|
||||
- The two default script-selected fade wrappers are now instruction-verified too. `palette_fade_begin_full_down` at `000c:c616` pushes direction `1`, step `4`, count `0x80`, start `0`, and palette buffer `DS:0x8c64`; `palette_fade_begin_full_up` at `000c:c600` is the same wrapper with direction `2`. Combined with the `0x5e`, `0x26`, and `0x2a` script-byte sites in `transition_preentry_step_script`, this closes the neighboring seg127 fade-input contract for the startup/display lane.
|
||||
- The late presentation-handoff family is now direct-decompile confirmed rather than only caller-window inferred. `FUN_000d_938c` creates up to two temporary runtime-state palette entries (`kind 0x3c`, then `kind 0x14`), waits for them to clear, redraws, clears `g_active_dispatch_entry_farptr[+0x40]`, and only then dispatches caller vtable `+0x08`; `entity_cleanup_resources_and_dispatch` shows the same late shared-hold clear on the `entity +0x737` branch immediately before the shared `0x2bd8` controller dispatch. That is enough to treat the startup/display major section as materially complete, with only low-impact residual ambiguity around the exact UI label of preset pair `0x10/0x11` and the optional overlap hygiene at `000c:db68`.
|
||||
|
||||
---
|
||||
|
||||
## Follow-up: `0x4588` Object-Role Evidence
|
||||
|
|
@ -330,6 +341,10 @@ The `0x4588` FAR object is a runtime-installed callback/dispatch object that par
|
|||
- **Teardown:** `000a:4a56` checks a once-flag at `0x4595`, clears `0x4588` when non-null, optionally performs a vtable `+0x0c` callback when `0x4590 != 0x458c`, then calls vtable slot `+0x04` followed by `FUN_0009_0d30()`.
|
||||
- **Callbacks:** `000a:b9e5`, `000a:ba66`, `000d:9d5e`, and `000d:a3b7` all push a two-word value pair followed by the `0x4588` FAR pointer and call vtable slot `+0x0c`. `entity_conditional_render_dispatch` calls the same vtable slot with a single literal `0x0101` argument.
|
||||
|
||||
Current batch note:
|
||||
|
||||
- `runtime_callback_object_init_once`, `runtime_callback_object_teardown_once`, and `entity_conditional_render_dispatch` now line up even more strongly as a video or presentation-state callback lane rather than a generic allocator client. The object is installed only after BIOS video-state snapshot, teardown emits a final callback only when recorded mode/state changed, and one live caller uses the literal mode-like pair `0x0101` through the same vtable `+0x0c` slot. That is enough to keep pushing the role toward `presentation/video-state callback broker`, but still not enough for a fully behavioral subsystem rename.
|
||||
|
||||
### Payload pairs from payload sync callsites
|
||||
|
||||
- `000d:9d5e` → vtable `+0x0c` payload from object fields `+0x12d/+0x12f`
|
||||
|
|
@ -362,11 +377,14 @@ The next ScummVM-guided validation step now confirms that the sampled owner-load
|
|||
|
||||
### Header and event-table shape
|
||||
|
||||
- The loader-side count field is now tighter too. The first dword in the sampled owner-loaded class header is not the total slot count; `000d:5066` uses it as the extra-slot count beyond a fixed `0x20` base table, which is why the cached table allocation is `extra_count * 6 + 0xc0` and the refcount array is `extra_count * 2 + 0x40`.
|
||||
- That reading matches the extracted class-family shapes exactly: `EVENT` keeps first dword `0x00000000`, `NPCTRIG` moves to `0x00000001`, and `ROLL_NS` to `0x00000002`, while the already-validated owner-loaded event counts remain `0x20`, `0x21`, and `0x23` respectively.
|
||||
- The sampled class records do contain a stable 4-byte header field at bytes `8..11`.
|
||||
- The observed values are small boundaries: `0x00d4`, `0x00da`, and `0x00e6` in the current sample set.
|
||||
- Treating that dword directly as the first post-event-table offset makes the layout line up cleanly: `(dword_at_8 - 20) / 6` yields valid tables of 32, 33, or 35 slots before inline payload/name data begins.
|
||||
- The region at `class + 0x14` is therefore now directly confirmed as repeated 6-byte slots with `u16 unknown_word + u32 code_or_payload_field` layout.
|
||||
- Representative low-slot examples are `JELYHACK` slot `1` = `{word=0x002a, dword=0x00000001}`, `SURCAMNS` slot `1` = `{word=0x0051, dword=0x000000d2}`, `SURCAMEW` slot `1` = `{word=0x00f7, dword=0x000000d2}`, `EVENT` slot `10` = `{word=0x1fd6, dword=0x00000001}`, and `REE_BOOT` slots `10/15/16` = `{0x034b,1}`, `{0x025c,0x034c}`, `{0x003b,0x05a8}`.
|
||||
- The runtime-side selector arithmetic is now exact as well: the owner-resource callbacks operate on `class_id + 2`, which matches the extracted `object_index` column directly. `EVENT` therefore lands on child `0x363` from class id `0x361`, and `NPCTRIG` on child `0x365` from class id `0x363`.
|
||||
- The leading event word is still not decoded semantically.
|
||||
|
||||
### What remains open
|
||||
|
|
@ -374,11 +392,50 @@ The next ScummVM-guided validation step now confirms that the sampled owner-load
|
|||
- Scanning with the previously noted ScummVM-style `(base_offset + 19) / 6` interpretation overruns into inline payload/name bytes on these owner-loaded records, so the local sample set does not support that exact event-count formula as written.
|
||||
- The best current arithmetic fit is now tighter: ScummVM's decremented `base_offset` is also used as the live code-stream base in `uc_machine.cpp`, so the local owner-loaded records fit best if bytes `8..11` are the first code-byte offset and event-count derivation is `(base_offset - 19) / 6`, which is exactly equivalent here to `(raw_u32_at_8_11 - 20) / 6`.
|
||||
- Current `000d` loader evidence does not point to a header rewrite before VM consumption. `entity_vm_runtime_init_from_path_if_configured` (`000d:44df`) only builds the external path and creates the runtime, `entity_vm_runtime_create` (`000d:4c99`) only installs the helper returned by `000d:7000`, `entity_vm_runtime_owner_resource_create` (`000d:7000`) only allocates the child owner table and fills it through helper vtable `+0x0c`, and `entity_vm_context_create_from_slot_index` (`000d:46ec`) directly reads slot-backed source data from that owner table. No local step is yet verified as rewriting the sampled class headers.
|
||||
- The slot-value miss path is now exact enough to align against the extractor rather than only against motifs. `entity_vm_slot_load_value` (`000d:51fd`) does not build the returned workspace out of owner-row fields or late interpreter scratch: on a miss it uses `000d:5066` plus the same owner-resource wrapper `000d:714c` to read a `0x14`-byte class header, then a cached `6 * (0x20 + extra_count)` subentry table, and finally the selected subentry's byte range straight into a newly allocated value-object buffer at `+0x0a/+0x0c`.
|
||||
- The final body read at `000d:53b4` now matches the extracted row arithmetic exactly. The 6-byte row contributes `word body_len` plus `dword raw_code_offset`, the class header contributes `dword code_base`, and the reader fetches `body_len` bytes from `code_base + raw_code_offset - 1` through `code_base + raw_code_offset + body_len - 2`.
|
||||
- That gives a direct owner-loaded fit for the two surviving `NPCTRIG` bodies. For class `NPCTRIG` (`class_id = 0x363`, `object_index = 0x365`), slot `0x0a` uses `{len = 0x0175, raw_code_offset = 0x00000001, code_base = 0x00da}` and therefore materializes range `0x00da..0x024e` (`373` bytes), while slot `0x20` uses `{len = 0x0159, raw_code_offset = 0x00000176, code_base = 0x00da}` and therefore materializes range `0x024f..0x03a7` (`345` bytes). `EVENT` slot `0x0a` fits the same runtime arithmetic with `{len = 0x1fd6, raw_code_offset = 0x00000001, code_base = 0x00d4}` -> `0x00d4..0x20a9`.
|
||||
- Because `000d:5066/51fd/53b4` now line up with the extracted class headers and event rows byte-for-byte, the remaining immortality blocker is no longer header math or slot-number translation. The open step is upstream class selection into this now-verified loader path: whether the live slot `0x0a` request really names `NPCTRIG`, `EVENT`, or another descriptor family sharing the same owner-loaded format.
|
||||
- `entity_vm_runtime_owner_resource_create` (`000d:7000`) still does not expose a direct binary-side class-name lookup or explicit `classid + 2` arithmetic. What it does expose is an indexed file-set loader contract: helper-owned count at `+0x14`, far-pointer table at `+0x10`, paired per-entry word table at `+0x18`, vtable `+0x04` size query, and vtable `+0x0c` materialization of the `0x0d`-stride owner records later consumed by `entity_vm_context_create_from_slot_index`. The current pass also makes the helper shape slightly more concrete: the two raw seg070 windows at `0009:67b6` and `0009:6916` are twin per-entry path/read loops with distinct format strings (`DS:3f2d` and `DS:3f40`) but the same `+0x10/+0x18` indexing and file open/read/close lane, which is better evidence for a multi-table or multi-phase external loader than for direct in-memory descriptor iteration.
|
||||
- The signed slot-offset lane used by the still-xref-dark wrappers `0005:2c35` / `0005:2c68` is also no longer confined to `entity_vm_context_create_from_slot_index` (`000d:46ec`). Inside `entity_vm_runtime_create`, the pre-entry body at `000d:4c25..4c90` reloads object fields `+0x32/+0x34` through `entity_vm_slot_load_value_plus_offset` (`000d:5572`), stores that returned pair into object fields `+0x10c/+0x10e`, and also caches the owner-source far pointer at `+0x117/+0x119`. The paired save path at `000d:49ec` then serializes `+0x10c` through seg070 `0009:2034`, which makes the slot-plus-offset pair a persisted runtime/dispatch state lane rather than a transient wrapper-only argument.
|
||||
- The signed slot-offset lane used by the still-xref-dark wrappers `0005:2c35` / `0005:2c68` is also no longer confined to `entity_vm_context_create_from_slot_index` (`000d:46ec`). Ghidra now reflects that contract in the conservative wrapper names `entity_vm_context_try_create_mask_0400_slot0a_with_offset` and `entity_vm_context_try_create_mask_0800_slot0b_with_offset`. Inside `entity_vm_runtime_create`, the pre-entry body at `000d:4c25..4c90` reloads object fields `+0x32/+0x34` through `entity_vm_slot_load_value_plus_offset` (`000d:5572`), stores the reconstructed `DX:AX` pair into object fields `+0x10c/+0x10e`, and also caches the owner-source far pointer at `+0x117/+0x119`. The paired save path at `000d:49ec` is narrower than it first looked: it serializes only the low word at `+0x10c` through seg070 `0009:2034`, while the high word is recomputed on load from the fresh `entity_vm_slot_load_value()` result plus the saved additive word.
|
||||
- Current disassembly closes the exact low-slot wrapper contracts too. `0005:2c35` sign-extends caller word `[BP+0x0a]`, then calls `entity_vm_context_try_create_masked_for_entity` with slot `0x0a` and packed mask `0x00000400`; `0005:2c68` is the same signed-additive shim for slot `0x0b` and packed mask `0x00000800`. Neither wrapper has a recovered outward code/data xref yet, so the best current provenance remains `extra-word masked materializer family member`, not a gameplay event label.
|
||||
- The newly recovered post-load consumers of `+0x10c/+0x10e` are weak and do not behave like a recovered event-dispatch selector. Predicate `FUN_0001_a772` returns true only when the pair is exactly `0000:0001`, while normalization block `FUN_0002_1860` checks `segment == 0` and clamps `offset < 0x0080` up to `0x0080`. No recovered downstream comparison or dispatch branch matches the five verified `NPCTRIG` slot `0x0a` clause starts (`0x0064/0x0093/0x00c2/0x00f1/0x0120`) or backward targets (`0x001f/0x004e/0x007d/0x00ac/0x00db`); if anything, the `0x0080` floor cuts across that family instead of confirming it.
|
||||
- The masked-create hub in front of that lane is now explicit too. Window `000d:463a..46e8` maps one gameplay entity through `entity_vm_slot_index_from_entity`, tests the owner/resource table row mask at `0x6611 -> +0x1315/+0x1317 -> (+0x10/+0x12) + 0x0d*slot`, and only then calls `entity_vm_context_create_from_slot_index`. That matters because the offset-specialized wrappers `0005:2c35` / `0005:2c68` are now instruction-verified as nothing more than sign-extended extra-word shims over this generic masked-context hub, rather than separate selector logic.
|
||||
- The upstream slot selector is now exact enough to rule out one remaining binary-side shortcut. `entity_vm_slot_index_from_entity` (`000d:45c5`) does not expose a class-family choice like `NPCTRIG` versus `EVENT`; it only chooses one of three generic category spans before the owner row is consulted: `(a)` entity ids `1..255` with class-word bit `0x0002` clear map to `entity_id + base_0x8c7e`, `(b)` class-nibble `4` objects map to `class_byte_0x7e05 + base_0x8c80`, and `(c)` everything else maps to `type_word_0x7df9 + base_0x8c7c`.
|
||||
- The runtime init path now shows where those bases come from too. After `entity_vm_runtime_create` succeeds, `entity_vm_runtime_init_from_path_if_configured` (`000d:44df`) seeds `0x8c7c/0x8c7e/0x8c80/0x8c82` as cumulative category bases by looping over four word counts at `0x6608..0x660e`. Because the compiled side only sees those category-base spans and the later owner-row mask words, it still does not reveal a direct descriptor-class discriminator before the slot body is loaded.
|
||||
- One direct non-hub consumer reinforces that read. `FUN_0005_295f` is the only currently recovered caller of `entity_vm_slot_index_from_entity` outside the masked hub; it recomputes the same slot index, directly tests owner-row bit `0x0040`, and then branches into gameplay handling before optionally calling `entity_vm_context_try_create_masked_for_entity` with mask `0x0040:0x0006`. Together with the still-empty xref results for `0005:2c35` and the stable `0005:2c35..2c57` function boundary, the safest current interpretation is that these owner-row words are generic capability masks, not explicit `NPCTRIG` / `EVENT` family tags.
|
||||
- The next immortality pass separates that owner-row path from the live control-stream path even more sharply. Inside `entity_vm_context_create_from_slot_index` (`000d:46ec`), the owner-table row still feeds only the preserved `0x39ca[slot]` mirror, while the actual `+0xd6/+0xd8` control stream handed to `entity_vm_context_setup` comes from `entity_vm_slot_load_value_plus_offset` and the caller-supplied setup/tail pointers come from the current VM frame record. That makes the immediate builder for the `000d:21ed` lane `slot-backed decoded stream plus frame-local replay`, not `owner-row decode`.
|
||||
- That is the current hard wall for the immortality frontier. The strongest verified answer remains that `NPCTRIG` slot `0x0a` is the best upstream descriptor-side fit and `EVENT` slot `0x0a` remains the generic-hub baseline, but the binary selector path now bottoms out at category spans plus row-capability bits rather than at a provable class-family discriminator.
|
||||
- The open descriptor question therefore moves one step earlier again. Current `000d` loader/runtime evidence still supports a descriptor-derived upstream workspace, but not a direct owner-row-to-opcode path for the immortality trigger. The closest verified compiled-side seeding now happens later inside the hidden dispatcher at `000c:fa2f`, where immediate literal cases can push byte/word/dword payloads straight onto the caller stream before the frame replay family re-materializes them into the child frame.
|
||||
- The seg070 twin-file-family helper is now tighter at the buffer/schema level as well. The paired loops at `0009:67b6` and `0009:6916` do not reuse one ambiguous scratch object: each loop performs its own size query/allocation sequence, builds paths from the same `+0x10/+0x18/+0x14` table trio with its own format string (`DS:3f2d` versus `DS:3f40`), feeds a dedicated temporary far buffer through the shared `file_handle_alloc_init_and_open` / `dos_file_seek` / `dos_file_close` trailer, and then frees that loop-local buffer before returning. Current safest read is therefore `two distinct temporary file-family materialization passes inside one owner-resource helper`, not one callback shard reused for both families.
|
||||
- Additional `0x39ca` consumers are now classified more cleanly. Beyond the already-known static seeds at `000d:7299 -> DS:67f2` and `000d:761c -> DS:6872`, the constructor-like windows at `000d:929a` and `000d:963c` seed rows `DS:68ec` and `DS:68f5` respectively before enabling local timer/dispatch behavior. Those writes behave like dispatch-entry-local static seed rows, not owner-table mirrors. Separately, `FUN_000d_938c` reads temporary dispatch-entry fields `+0x32/+0x34` at `000d:9449..9468` and `000d:9547..9566` only as a wait/poll condition on the scratch-palette (`kind 0x3c`) and current-palette (`kind 0x14`) entries it creates, which further separates active dispatch-entry state from the owner-backed `0x39ca[slot] = {source_off, source_seg}` rows written by `000d:46ec`.
|
||||
- Safe event-label correlation remains intentionally narrow after this pass. The sampled low slot ids are now concrete, but none of them yet have a verified binary-side behavior match strong enough to promote a ScummVM label like `look`, `use`, or `cachein`.
|
||||
|
||||
### Current batch: higher-slot masked wrapper ladder (`0x10..0x14`)
|
||||
|
||||
- The gameplay-side masked-wrapper island now extends one verified step past the older `0x0f` frontier. Raw call setup around `0005:3115..322d` shows five higher-slot entries feeding `entity_vm_context_try_create_masked_for_entity` with slot ids `0x10`, `0x11`, `0x12`, `0x13`, and `0x14`.
|
||||
- The slot `0x10` lane is not yet a clean standalone function object, but the containing body at `0005:3115..3129` is exact enough to classify its call shape: it pushes zero extra word, slot `0x10`, packed mask `0x00010000`, and the live entity pointer before the far call to `000d:463a`. The preceding guard at `0005:30f2..3113` restricts that path to one class-nibble-`4` lane.
|
||||
- Four neighboring helpers are now renamed directly in Ghidra from stable function objects:
|
||||
- `0005:313e` = `entity_vm_context_try_create_mask_00020000_slot11_with_offset`
|
||||
- `0005:3171` = `entity_vm_context_try_create_mask_00040000_slot12`
|
||||
- `0005:31da` = `entity_vm_context_try_create_mask_00080000_slot13_with_offset_if_valid_entity`
|
||||
- `0005:31a0` = `entity_vm_context_try_create_mask_00100000_slot14_with_offset`
|
||||
- Their payload shapes are now exact from disassembly, not only inferred from decompile:
|
||||
- slot `0x11` pushes one caller-supplied extra word (`MOVZX EAX,[BP+0xa] ; PUSH EAX`)
|
||||
- slot `0x12` pushes a fixed zero extra word
|
||||
- slot `0x13` pushes one sign-extended caller word after the same `0005:2686` / `0005:ffed` entity-validity gate used by the older slot-`0x01` helper
|
||||
- slot `0x14` pushes one caller-supplied extra word
|
||||
- This widens the verified owner-slot taxonomy in a USECODE-relevant way: the binary is no longer only distinguishing compact low-slot wrappers like `0x0a`/`0x0b`; it also separates a higher-slot family with mixed `no extra word` versus `signed extra word` call contracts.
|
||||
- The first outward callers in this higher-slot family are now explicit too. `entity_vm_context_try_create_mask_00040000_slot12` (`0005:3171`) is called at `0005:1776` and `0005:1945`; both callsites are currently trapped in non-function windows, but they are real direct edges into the slot-`0x12` zero-extra-word lane. By contrast, current MCP xrefs still show no direct outward callers for the slot `0x11`, `0x13`, or `0x14` wrappers and still none for the dark slot `0x0a` / `0x0b` pair.
|
||||
- The persisted-context side of the same lane is now tighter at the field level. `entity_vm_context_save` (`000d:498f`) serializes `+0x11f`, `+0x121`, the derived low word at `+0x10c`, the additive word at `+0x34`, and the `0x80`-byte local buffer at `+0x36/+0x38`; `entity_vm_context_load` (`000d:4a78`) rebuilds the frame pointers, reloads the saved low word as the additive argument to `entity_vm_slot_load_value_plus_offset`, restores `+0x10c/+0x10e`, and refreshes the owner-linked source pair at `+0x117/+0x119`. That strengthens the current read that persistence preserves `(slot, additive_word, derived_low_word)` after selector choice, not the upstream class-family selector itself.
|
||||
- The external event-name correlation can now be tightened slightly but still stays hint-level only:
|
||||
- slot `0x12` having no extra word is compatible with the external `justMoved()` zero-argument event label
|
||||
- slot `0x13` carrying one extra word is compatible with Pentagram's `AvatarStoleSomething(uword)` signature
|
||||
- slot `0x11` carrying one extra word is compatible with Pentagram's placeholder `func11(sint16)` signature and with ScummVM's unresolved `cast`-side slot only at the broad `one scalar argument` level
|
||||
- slot `0x14` currently does **not** fit Pentagram's older zero-argument `animGetHit()` signature, so that ordinal should remain slot-numbered on the binary side for now
|
||||
- Operational consequence for the current VM lane: there is now stronger binary evidence that the masked-context family is organized around slot ordinals with distinct payload shapes, not only around one low-slot trigger subset. That helps the current round-trip IR because it justifies keeping higher ordinals as slot-stable records with payload-shape metadata even when their event labels remain external hints.
|
||||
- The sequencer-side consumer model is also now preserved directly in Ghidra. Address `000d:22bc` carries a decompiler comment recording it as a sequencer-internal matrix stage: it reads two signed metadata bytes from `+0xd6/+0xd8`, consumes caller-stream words as entity/link ids, repeatedly calls `0008:7d27`, and only pushes back words without bit `0x0400` before jumping to `entity_vm_opcode_finish`.
|
||||
|
||||
### Conservative parser rule from this batch
|
||||
|
||||
- For current owner-loaded/raw EUSECODE work, keep bytes `8..11` raw and derive event count only with `(raw_u32_at_8_11 - 20) / 6` when divisibility and object-size bounds checks succeed.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue