Updated knowledge

This commit is contained in:
MaddoScientisto 2026-04-02 01:15:16 +02:00
commit 1ad746ba82
21 changed files with 882 additions and 9 deletions

View file

@ -819,4 +819,27 @@ That gets to a reversible editor sooner than waiting for a full semantic VM reco
- **Renderer Fixes:**: [src/lib/usecode-decompiler.js](k:/ghidra/crusader_map_viewer/map_renderer/src/lib/usecode-decompiler.js) now propagates enclosing exit labels through nested structured regions, lifts raw `foreach_list` / `foreach_slist` loops into structured `while (true)` bodies, and treats comment-prefixed cleanup-plus-return blocks such as `/* free_local_list */` + `return;` as real return targets for control-flow recovery.
- **Readability Impact:**: The remorse cache file [TRIGGER/slot_20_slot_20.txt](k:/ghidra/crusader_map_viewer/map_renderer/.cache/usecode/remorse/EUSECODE/pseudocode/TRIGGER/slot_20_slot_20.txt) now renders as one structured function: the initial phase/setup lane is straight-line `if/else`, the middle search fan-out is structured nested conditionals, the nearby `0x04B1` scan is a real `for item in nearby_items(...)` loop, and the follow-up low-priority trigger worklist is a structured fixed-point `while (1)` loop rather than detached `block_XXXX` labels.
- **Regression Coverage:**: [scripts/test-usecode-structuring.mjs](k:/ghidra/crusader_map_viewer/map_renderer/scripts/test-usecode-structuring.mjs) now covers three additional generic structuring cases and one real-data regression that decodes `STATIC/EUSECODE.FLX`, rebuilds the live `TRIGGER.slot_20` IR, and asserts that the rendered pseudocode no longer falls back to block labels or `goto block_...` jumps.
- **Binary / Ghidra Impact:**: This pass tightened renderer-side control-flow recovery only. It did not add a new compiled-side VM decode, so no new Ghidra rename or comment was applied in `CRUSADER.EXE` during this batch.
- **Binary / Ghidra Impact:**: This pass tightened renderer-side control-flow recovery only. It did not add a new compiled-side VM decode, so no new Ghidra rename or comment was applied in `CRUSADER.EXE` during this batch.
- **Additional Root Cause Closed:**: `BLASTPAC.slot_01` still kept loose blocks after the earlier trigger pass because the full structurer treated `goto` edges that jumped exactly to the current region end label as unstructured rather than as normal join exits. That blocked both the `nearby_items(shape=0x053A, origin=global[0x003C])` selector loop body and the later target/crouch join chain from collapsing.
- **Additional VM Evidence:**: ScummVM's Crusader VM remains the strongest external semantics anchor for this lane: [uc_machine.cpp](k:/misc/scummvm/engines/ultima/ultima8/usecode/uc_machine.cpp) shows opcode `0x51` as a relative branch on false, opcode `0x73` as `loopnext` pushing a loop-valid flag and freeing the temporary list when exhausted, and opcodes `0x75` / `0x76` as real foreach iterators that keep the loop frame live until completion and then pop it before jumping to the exit target.
- **Additional Renderer Fix:**: [src/lib/usecode-decompiler.js](k:/ghidra/crusader_map_viewer/map_renderer/src/lib/usecode-decompiler.js) now also treats jumps to the current structured-region end label as exits, which lets selector-loop bodies and nested join-heavy `if/else` regions close cleanly without falling back to raw block labels.
- **Additional Readability Impact:**: The remorse cache file [BLASTPAC/slot_01_use.txt](k:/ghidra/crusader_map_viewer/map_renderer/.cache/usecode/remorse/EUSECODE/pseudocode/BLASTPAC/slot_01_use.txt) now renders as straight structured pseudocode: the `shape 0x053A` search is a real `for item in nearby_items(...)` loop, the inner retry lane stays a structured counted loop, and the later `target` / `InCrouch` path is one nested `if/else` tree rather than detached `block_0415`, `block_046e`, `block_05c5`, and `block_061d` islands.
- **Additional Regression Coverage:**: [scripts/test-usecode-structuring.mjs](k:/ghidra/crusader_map_viewer/map_renderer/scripts/test-usecode-structuring.mjs) now adds one focused synthetic `region-end goto` regression plus one real-data `BLASTPAC.slot_01` regression, and the current focused suite passes after regenerating the cache.
- **Current Binary / Ghidra State:**: The compiled-side anchor is still the existing `000d:ebe3` sequencer note, and this batch still did not recover a new compiled opcode handler. A matching live decompiler comment was added at `000d:ebe3` to record the ScummVM-backed loop/branch contract used by the current BLASTPAC/TRIGGER selector-loop recovery (`0x51` false-branch, `0x73` loopnext validity/free behavior, `0x75/0x76` foreach iteration contract).
## **Recent Renderer Work (2026-04-01, list + selector follow-up)**
- **List Opcode Evidence Closed:**: ScummVM's live Crusader VM in [uc_machine.cpp](k:/misc/scummvm/engines/ultima/ultima8/usecode/uc_machine.cpp) confirms opcode `0x0E` builds a new list from `count` stack values of `element_size`, and opcode `0x17` concatenates two list ids by appending the top list into the next list and pushing the combined result.
- **Renderer Fixes:**: [src/lib/usecode-decompiler.js](k:/ghidra/crusader_map_viewer/map_renderer/src/lib/usecode-decompiler.js) now lifts `create_list` into list literals such as `[item]` and `append_list` into list concatenation expressions instead of leaving raw comment placeholders. That closes the common temporary-worklist patterns in bridge/trigger/free scripts where the old output showed `/* create_list */` and `/* append_list */` immediately before an assignment.
- **False-Branch Fix:**: The same renderer pass now treats compound boolean expressions conservatively when inverting Crusader's `0x51` false-branch. For simple comparisons it still flips the operator directly, but for composed `&&` / `||` expressions it now emits a whole-expression negation rather than corrupting the leftmost compare. This fixes the broken `BRO_BOOT.slot_0F` entry test that previously rendered as `global[0x001f] != 2 || global[0x001f] == 3 ...` even though the bytecode is a plain OR-chain of equality compares.
- **Selector Readability:**: Long same-selector equality ladders that share one join target now render as `switch (...)` blocks when every branch is a simple equality case. The immediate real-data win is [BRO_BOOT/slot_0A_equip.txt](k:/ghidra/crusader_map_viewer/map_renderer/.cache/usecode/remorse/EUSECODE/pseudocode/BRO_BOOT/slot_0A_equip.txt), whose repeated `global[0x001f] == N` movie dispatch chain now decompiles as a switch instead of six `else if` arms.
- **BRO_BOOT Structuring Impact:**: With the compound-condition fix in place, [BRO_BOOT/slot_0F_enterFastArea.txt](k:/ghidra/crusader_map_viewer/map_renderer/.cache/usecode/remorse/EUSECODE/pseudocode/BRO_BOOT/slot_0F_enterFastArea.txt) is expected to collapse into one structured `if/else` around the two `SPANEL` scans plus the trailing infinite animation loop, instead of keeping `entry:` / `block_0454`-style fallbacks.
- **Regression Coverage:**: [scripts/test-usecode-structuring.mjs](k:/ghidra/crusader_map_viewer/map_renderer/scripts/test-usecode-structuring.mjs) now adds synthetic regressions for list-literal lifting, compound false-branch negation, and switch rendering, plus real-data regressions for `BRO_BOOT.slot_0A` and `BRO_BOOT.slot_0F`.
## **Recent Renderer Work (2026-04-02, CHANGER selector close)**
- **Root Cause Closed:**: `CHANGER.slot_07` in both Remorse and Regret was still rendering as `while (condition)` because the JS loop-selector decoder only recognized the older field-match selectors such as `nearby_items(shape=...)` and the opaque `selector_0x42(...)` fallback. The CHANGER bodies use a different selector family: `loopscr 0x24` plus `loopscr 0x4c`, with a hardcoded shape whitelist left on the stack, a computed search distance (`100 * 32`), and the egg item as origin. Because that selector family was not decoded, the renderer could not surface the roof-target scan clearly enough to decompile or visualize.
- **Renderer Fix:**: [src/lib/usecode-decompiler.js](k:/ghidra/crusader_map_viewer/map_renderer/src/lib/usecode-decompiler.js) now recognizes that stacked-shape whitelist selector and emits readable loops such as `for roof in nearby_items(shapes=[...], distance=(100 * 32), origin=arg_06)` instead of collapsing back to `while (condition)`.
- **Readability Impact:**: The cached Remorse and Regret `CHANGER.slot_07` pseudocode bodies now expose the actual nearby-roof selector inputs directly: the hardcoded roof-shape whitelist, the recovered `3200`-unit range, and the egg-origin scan. That makes the later `Item.getQLo(...) == eggId` destroy branch legible without a raw-byte fallback pass.
- **Editor Impact:**: The same selector close justified promoting Regret `QLo 8 -> CHANGER` from tooltip-only metadata into the map editor overlay. The viewer can now expose the same local roof-target lane for Regret that was already proven for Remorse, using the recovered Regret whitelist and the same `3200`-unit scan distance.
- **Regression Coverage:**: [scripts/test-usecode-structuring.mjs](k:/ghidra/crusader_map_viewer/map_renderer/scripts/test-usecode-structuring.mjs) now adds one synthetic regression for the `loopscr 0x24/0x4c` stacked-shape selector and one real-data regression for Regret `CHANGER.slot_07`, so future renderer changes fail if this selector family falls back to opaque loop output again.