Pseudocode and stuff
This commit is contained in:
parent
7310c4fe96
commit
ee33f94b4b
466 changed files with 27770 additions and 276 deletions
|
|
@ -91,6 +91,28 @@ Current unsafe use:
|
|||
- direct rename authority for modern Ghidra functions
|
||||
- assuming one game's intrinsic numbering matches another without local confirmation
|
||||
|
||||
### Validated follow-up on `misc_crusader_notes.txt`
|
||||
|
||||
Several of the old scratchpad items are now tight enough to record as verified follow-up rather than open leads.
|
||||
|
||||
- `STEAM2` (`shape 1297`) is no longer just a handwritten event hint. Local extracted data now confirms non-empty `STEAM2::enterFastArea` (`event 0x0f`) and `STEAM2::gotHit` (`event 0x06`) bodies in `USECODE/EUSECODE_extracted/class_event_index.tsv`, which matches the old note's `event 15 (enter fast area)` and `Got Hit: spawn process` direction.
|
||||
- The old standalone labels `FUN_1130_0896`, `FUN_1130_32af`, `FUN_1020_0000`, and `FUN_1128_026b` are now closed in the live NE database as `Key_HandleOptionKeys`, `NPC_ResetToStartOfAnim`, `Game_Start`, and `Item_ReceiveHit`. The historical notes were directionally useful, but the live names now capture their real scope: `NPC_ResetToStartOfAnim` resets an NPC/item frame to the first frame of a chosen animation and updates the stored last-anim state, while `Item_ReceiveHit` is the main damage/death handler rather than a loose "various deaths" helper.
|
||||
- The `Item_ReceiveHit` decomp also closes several handwritten shape hints directly from code. It uses `0x576` for the burning human replacement path (`flaming guy running around`), `0x5a9` and `0x52b` for shield-zap sprites, and still routes controlled-NPC death through `Target_PutTargetingReticleOnItem(0)`, which keeps the old `0x59a` reticle note on the right subsystem.
|
||||
- The old `ItemNPC_AnotherCreate` area-search TODO is now directly closed in the live NE program: `10e8:2710` (now renamed `NPC_CreateIfAreaSearchValid`) allocates/initializes an `AreaSearch` struct, checks `AreaSearch_IsValidPositionPt`, and only then runs `NPC_Create` plus `Item_PopToCoords`. So the practical role is `create NPC only if the requested point passes area-search placement validation`, not a second independent spawn path.
|
||||
- The handwritten `Kernel_11d0_2491` gloss was incomplete. The live function is not merely "prints kernel info"; it serializes the process table, timer/keyboard/mouse process-id lists, process sizes, and per-process state through file-like writer callbacks, then restores timer/interrupt state afterward. Current safest read is `kernel/process snapshot writer` rather than a simple printf-style diagnostics helper.
|
||||
- `FREE::ordinal3C` is still corpus-side evidence rather than live-NE proof, but the old disassembly now constrains it much better than the original note implied. It clears the alert state, checks avatar stasis, rolls random thresholds, and spawns `FREE::ordinal21` with a small set of ordinals (`0x0e`, `0x0f`, `0x00b6`, `0x00d2`). That makes `random global FREE event chooser after alert clear` a safer description than the narrower `random voices when alarm is disabled` guess.
|
||||
- The old `high priority unknown intrinsic: 01E - fire` note is now effectively closed as a hint. The old Remorse intrinsic tables map `Int01E` to `Actor::I_maybeFire(...)`, and the current live export map ties `Int01E` to `1128:11da`. That is still hint-level naming until the compiled body is analyzed locally, but it is no longer an unidentified ordinal.
|
||||
- The `combat.dat` scratch note is also better grounded now. The external corpus already showed the extracted tactic files are identical between Remorse and Regret, and the live `CRUSADER.EXE` export map now has NPC fields such as `combatDatTacticPtr`, `combatDatTacticCurOffset`, `combatDatBlockNo`, and `tacticNo`. So the named tactic strings are good portable data labels, but they should still be attached to tactic records and NPC state fields, not promoted directly into compiled function names.
|
||||
|
||||
Two shape hints also have light compiled-side support now:
|
||||
|
||||
- `0x59a` is passed into `SpriteProcess::I_createSprite` from the cursor/targeting lane in segment `1130`, which fits the old `targetting reticle` note.
|
||||
- `0x5a3` is passed into `SpriteProcess::I_createSprite` from the `13e8` gameplay/UI lane and stored at `0x6054`, which fits the old `use item crosshairs` note.
|
||||
|
||||
Remaining caution:
|
||||
|
||||
- these follow-ups make the scratchpad much more reusable, but `misc_crusader_notes.txt` is still an auxiliary corpus. The verified parts above should be cited with their live NE addresses or extracted-data files when reused elsewhere in the repo.
|
||||
|
||||
### Combat data note
|
||||
|
||||
`combat_dat/readme.txt` records that the extracted `combat.dat` tactic files are identical between No Remorse and No Regret.
|
||||
|
|
|
|||
|
|
@ -55,13 +55,15 @@ Evidence used here:
|
|||
### Latest verified NE pass
|
||||
|
||||
- `0005:295f` is now the only recovered non-hub consumer of `entity_vm_slot_index_from_entity` in the open NE session. It recomputes the slot index, tests owner-row bit `0x0040` directly, exposes that result to the caller, and only then optionally calls `entity_vm_context_try_create_masked_for_entity` with slot `0x06` and mask `0x0040`.
|
||||
- The direct NE callers currently recovered for `0005:295f` are `0006:43c3`, `0006:c5f0`, and `0007:3584`. That materially narrows the selector frontier to three gameplay-side caller families instead of the older wider `find any caller of 000d:45c5` search.
|
||||
- The direct NE callers currently recovered for `0005:295f` are still only `0006:43c3`, `0006:c5f0`, and `0007:3584`, but they are now all structurally classified in the live NE session instead of only enumerated as open caller families.
|
||||
- The first anchored caller family is now structurally classified in the live NE session. Repaired wrapper `0006:4379` is a seg031 dispatch-entry subtype gate over objects built by `0006:42d9`: event type `0x236`, source type `8`, subtype/tag at `+0x3c`, dword payload/source far pointer at `+0x32`, and aux words at `+0x36/+0x38`.
|
||||
- In that wrapper, subtype `0x20c` at `0006:43c3` routes into `0005:295f`, while nearby sibling subtype `0x20b` at `0006:43e5` routes into `0005:2918` (`slot 0x05` / `mask 0x0020`) using the same `+0x36/+0x38` aux pair. The split is therefore local to one dispatch-entry family, not a direct compiled `NPCTRIG` / `EVENT` class-family selector by itself.
|
||||
- The `0006:43c3` lane now shows where the owner-row bit-`0x0040` probe is consumed locally: inside a subtype-`0x20c` dispatch-entry object rather than at a generic descriptor-choice site. That improves caller provenance for `0005:295f`, but it still does not prove which owner-loaded class family seeded the later VM data.
|
||||
- The second direct caller family is now closed too. Old `0006:c5f0` lands at live call site `1128:0ff0` inside `Item_ReceiveHit`, where the non-NPC item path calls `Item_GetDamaged` with hitter sentinel `0x4000`, packed damage `(damagetype << 8) | damage_lo`, and a local flag-out byte that records the returned owner-row bit-`0x0040` capability before the local destruction / impact follow-up.
|
||||
- The third direct caller family is now closed too. Old `0007:3584` lands at live call site `1138:1384` inside `SuperSprite_HitAndFinish`, where the non-NPC collision lane probes `Item_GetDamaged` with hitter sentinel `0x4000` and packed damage `(firetype << 8) | damage`; only when that flag-out byte stays clear and the target is not fixed does the lane fall through into the local `Item_ReceiveHit` knockback path.
|
||||
- `0005:2c35` remains outward-dark in the current NE session: instruction search still shows no recovered code or data xrefs, and its proven local role is still only `sign-extended additive word -> slot 0x0a / mask 0x0400 -> generic masked hub`.
|
||||
- The first live `CRUSADER.EXE` integration batch is now applied for this lane. Comment-backed anchors were added at `1420:0dc5` (`Item_GetUsecodeClassId`), `1420:0e3a` (`Usecode_ItemCallEvent`), `10a0:2718` (`Item_Hit`), `10a0:275f` (`Item_GetDamaged`), `10f0:02d9` (`StorageDataProcess_Create`), and `10f0:0379` (`StorageDataProcess_Run`), with branch comments at `10f0:03c3` and `10f0:03e5` preserving the verified `0x20c` / `0x20b` split in the open NE program.
|
||||
- Result of this pass: the compiled-side selector evidence still bottoms out at category spans plus owner-row capability bits, not a concrete `NPCTRIG` / `EVENT` class-family choice. The next defensible NE step is to classify the three `0005:295f` caller families or recover an earlier producer that feeds the later owner-loaded class lane.
|
||||
- The live `CRUSADER.EXE` integration batch is now extended for this lane. Comment-backed anchors were already present at `1420:0dc5` (`Item_GetUsecodeClassId`), `1420:0e3a` (`Usecode_ItemCallEvent`), `10a0:2718` (`Item_Hit`), `10a0:275f` (`Item_GetDamaged`), `10f0:02d9` (`StorageDataProcess_Create`), and `10f0:0379` (`StorageDataProcess_Run`), with branch comments at `10f0:03c3` and `10f0:03e5` preserving the verified `0x20c` / `0x20b` split; new live comments now also anchor the remaining direct caller sites at `1128:0ff0` and `1138:1384`.
|
||||
- Result of this pass: all currently recovered direct `0005:295f` caller families are now closed, but the compiled-side selector evidence still bottoms out at subtype-gated dispatch or generic gameplay damage consumers plus owner-row capability bits, not a concrete `NPCTRIG` / `EVENT` class-family choice. The next defensible NE step is therefore an earlier producer that assigns subtype `0x20b/0x20c` into field `+0x3c` or otherwise chooses the owner-loaded class family before these generic damage consumers run.
|
||||
|
||||
## Priority 2: Rendering / Camera / Tile-Visibility / Watch-Controller Lane
|
||||
|
||||
|
|
|
|||
|
|
@ -320,6 +320,81 @@ What `-laurie` appears to enable by itself:
|
|||
- It is also enough to permit the separate event-`0x7e` runtime toggle path, whose entire job is to flip `0x6045` later and post the `0x6087` / `0x6091` notifications.
|
||||
- It is **not** enough for low-level keyboard-only cheat branches that check `0x6045` directly. That is why the user can see the `0x844`-gated debug-box behavior while plain F10 still behaves as if full keyboard cheats are off.
|
||||
|
||||
### Keyboard folklore correction pass (`CRUSADER.EXE` live NE)
|
||||
|
||||
- `~` is real in this build. In `World_HandleKeyboardInput_13e8_14b4`, the branch at `13e8:202d..203d` requires `0x844 != 0`, flips the live keyboard-cheat latch `0x6045`, and posts the paired on/off messages at `0x6087` / `0x6091`.
|
||||
- `Ctrl+C` is **not** the current-location popup in this build. The actual location-display branch is `Ctrl+L` (`0x426`), which formats the avatar's X/Y/Z into the `1478:610c` string and displays it at `13e8:255e..25ac`.
|
||||
- The third F7-family overlay is also real. The three cheat-gated overlay toggles live at `13e8:1a7c` (`0x141`), `13e8:1a50` (`0x241`), and `13e8:1a20` (`0x441`), writing `1478:2bca`, `1478:2bc9`, and `1478:0ee0` respectively before forcing a camera refresh through `[g_cameraProcess+0x2c]`.
|
||||
- The unresolved online entry is therefore best corrected as `Ctrl+F7 exists but is cheat-gated`; the obviously bogus list item is `Ctrl+C`, which should be `Ctrl+L` for the coordinate popup.
|
||||
|
||||
### `~` versus `jassica16`
|
||||
|
||||
- These are **not** the same mechanism. `jassica16` is handled earlier in `Key_CheckCheatToggle` as a raw scan-code matcher over `1478:2833`, while `~` is handled later in `World_HandleKeyboardInput_13e8_14b4` as a translated logical key value (`0x7e`).
|
||||
- That means Shift is probably normal behavior here, not a DOSBox-only artifact. On a standard US DOS keyboard layout, plain grave is `` ` `` (`0x60`) and Shift+grave is `~` (`0x7e`); the live handler we recovered is the `0x7e` branch, and no separate backtick/`0x60` hotkey path was recovered in this handler.
|
||||
- DOSBox can still affect host-to-DOS layout mapping, but the recovered game logic is asking for the translated `~` character value, not the raw grave-key scancode. So on a US layout the expected in-game gesture is Shift+grave.
|
||||
- `jassica16` is the stronger unlock path. On success it sets `0x8c52 = 1`, toggles both `0x844` and `0x6045`, emits the `0x103` voice/helper side effect, and shows the `1478:287b` / `1478:2892` cheat-state modals.
|
||||
- Plain `~` is narrower. It only works after the broader `0x844` gate is already on, and then it flips just the live keyboard-cheat latch `0x6045` while showing the `0x6087` / `0x6091` on/off messages.
|
||||
- Practically: `jassica16` can bootstrap cheats from a cold state because it toggles the master gate and the low-level latch together; `~` cannot bootstrap that state by itself because its own branch first requires `0x844 != 0`.
|
||||
- The extra `0x8c52` write also means `jassica16` is not just a different UI for the same toggle. It leaves a second latch behind that later keyboard-side cheat/debug branches can test, while plain `~` does not touch that latch.
|
||||
|
||||
### What `Ctrl+F7` is supposed to show
|
||||
|
||||
- `Ctrl+F7` (`13e8:1a20` -> `1478:0ee0`) is not just a third generic grid. In the camera redraw path, `Camera_1180_15ef` treats `0x0ee0` differently from the simpler `0x2bca` grid flag.
|
||||
- Plain `F7` (`1478:2bca`) draws a coarse `3 x 3` isometric world-cell grid around the camera by stepping `0x200` world units and drawing diamond tile boundaries.
|
||||
- `Alt+F7` (`1478:2bc9`) calls `Snap_1058_0814`, which walks the snap-process egg list and draws per-entry diamonds from packed egg range data. That is closer to a snap/trigger coverage overlay than to a camera-aligned background grid.
|
||||
- `Ctrl+F7` (`1478:0ee0`) walks `EggHatcherProcess` objects and calls `EggHatcher_1090_0921`, which draws diamond trigger ranges using `Egg_GetXRange`, `Egg_GetYRange`, and the shared diamond helper at `1180:1ce5`.
|
||||
- So the intended visual for `Ctrl+F7` is an **egg / hatch trigger-range overlay**. It should highlight live egg-hatcher or monster-egg regions, not fill the screen with a regular map grid.
|
||||
- That also explains why it can appear to do nothing. If the current map has no live `EggHatcherProcess` objects, or a non-monster-egg trigger has already hatched while normal gameplay input is active, the draw loop has nothing eligible to outline even though the toggle flag itself changed.
|
||||
|
||||
### Egg-Hatcher Runtime Notes
|
||||
|
||||
- The live NE runtime has a dedicated egg-hatcher process type: `EggHatcher_CreateProcess` builds process type `0x20f`, stores one target item number in the process, and names it `EggHatcher`.
|
||||
- The corresponding runner `EggHatcherProcess_Run` reads the avatar footprint and compares it against the egg item's center, `Egg_GetXRange`, `Egg_GetYRange`, and a vertical window of about `+/- 0x30` Z units.
|
||||
- For non-monster egg families, entering that range sets the process `ishatched` byte and calls `Item_CallHatchEvent`; leaving the range clears `ishatched` and calls `Item_CallUnhatchEvent`.
|
||||
- The overlay helper `EggHatcher_1090_0921` uses the same range values to draw the visible diamond outlines for `Ctrl+F7`. In other words, the visualizer is showing the same trigger footprint the runtime is testing against.
|
||||
- Current safest gameplay-facing read: these "eggs" are hidden trigger items that fire enter/leave behaviors when the avatar crosses their footprint. Some are likely classic trap/trigger/controller eggs rather than literal spawn objects.
|
||||
- The wider workspace evidence agrees with that interpretation. The extracted USECODE corpus contains egg-like labels such as `TRIGEGG`, `ONCEEGG`, `DOOREGG`, `LAZEREGG`, `STEAMEGG`, `MISS1EGG`, `GRENEGG`, and `REB_EGG`, while the older `crusader-disasm` notes explicitly call out `SnapEgg` as a fast-area participant.
|
||||
|
||||
### How To Find One In Practice
|
||||
|
||||
- The most reliable bootstrap is now: start with `-laurie`, then press `Shift+~` to flip the live keyboard cheat latch on, then use the F7-family overlays.
|
||||
- Use `Ctrl+F7` when you want true egg-hatcher ranges. If nothing appears, switch to `Alt+F7` too; that overlay walks the snap-process egg list and can reveal related trigger coverage that is not currently present in the live egg-hatcher process list.
|
||||
- Search in gameplay spaces that are likely controlled by hidden trigger items rather than by obvious UI state: door thresholds, ambush spawn zones, laser or steam hazards, missile/grenade trap corridors, teleporter pads, and one-shot scripted encounter rooms.
|
||||
- If a region only reacts once, walk out of the area and re-enter it. The live runner explicitly tracks enter/leave transitions and only calls the hatch/unhatch events when the avatar crosses the trigger boundary.
|
||||
- Monster-egg handling still looks special-cased, so a blank `Ctrl+F7` result does not prove a map has no egg-related logic at all; it only proves there are no eligible live `EggHatcherProcess` outlines being drawn at that moment.
|
||||
|
||||
### Cheat / Debug Key Matrix
|
||||
|
||||
Cheat-state legend used below:
|
||||
|
||||
- `master gate (0x844)`: broader Laurie/debug permission state. `-laurie` sets this directly.
|
||||
- `keyboard latch (0x6045)`: full keyboard-cheat latch used by the low-level F10 path and some other direct keyboard branches.
|
||||
- `sequence latch (0x8c52)`: extra post-sequence latch left behind by `jassica16`; plain `~` does not set it.
|
||||
- `input-active gate (0x85f)`: broader gameplay-input state. This is not a cheat bit, but it still suppresses some option-key behavior when the game is in modal/non-gameplay states.
|
||||
|
||||
| Input | State Needed | Effect | Evidence / Notes |
|
||||
|------|--------------|--------|------------------|
|
||||
| `-laurie` | none at startup | Sets the `master gate (0x844)` only | This is the easiest way to unlock the broader debug/event family without entering the hidden key sequence. |
|
||||
| `jassica16` | none | Toggles `0x844` and `0x6045`, sets `0x8c52`, emits `0x103`, shows cheat active/inactive modal | Raw scan-code matcher, not a translated text string. Can bootstrap full cheat state from cold. |
|
||||
| `Shift+~` / `~` | `0x844` already on | Toggles only `0x6045`; shows the on/off cheat-latch modal | On a US layout the game is checking logical `0x7e`, so Shift is the expected normal gesture. With `-laurie`, this becomes a practical way to enable full keyboard cheats. |
|
||||
| `F10` | `0x85f` and `0x6045` | Refill/loadout branch: restore/refill, credits, items, ammo, weapon set | This is the plain full-keyboard-cheat branch in `Key_HandleOptionKeys`. |
|
||||
| `Ctrl+F10` | `0x85f` and `0x6045`; current NPC alive | Toggles the current controlled NPC's immortal flag | Same F10 branch, but only the modifier-gated immortality sub-path. |
|
||||
| `Ctrl+L` | no cheat gate recovered | Shows current avatar X/Y/Z popup | Online `Ctrl+C` folklore is wrong for this build. |
|
||||
| `Ctrl+C` | no live branch recovered | No confirmed effect in this build | Best current correction is `Ctrl+L`. |
|
||||
| `Ctrl+V` | no cheat gate recovered | Displays internal stats / version / memory info | This branch appears to be available without the cheat gates. |
|
||||
| `Ctrl+Q` | `0x844` | Toggles CD transfer display state (`0x604f`) | Distinct from immortality. Uses the broader master gate, not the full keyboard latch. |
|
||||
| `F7` | `0x844` | Draws coarse `3 x 3` camera-aligned world-cell grid | Simple isometric background grid. |
|
||||
| `Alt+F7` | `0x844` | Draws snap-process egg diamonds | Better read as snap/trigger coverage overlay than generic grid. |
|
||||
| `Ctrl+F7` | `0x844` | Draws egg-hatcher trigger diamonds | Visualizes live `EggHatcherProcess` ranges; can look blank on maps without eligible live processes. |
|
||||
| `F` | `0x844` | Toggles object/framework paint overlay | Laurie/debug-side visual lane, not a full keyboard-cheat-latch feature. |
|
||||
| `H` | `0x844` and `0x8c52` | Toggles Hack Mover | Strongest current read is that this needs the broader Laurie/debug gate plus the extra post-`jassica16` sequence latch. |
|
||||
|
||||
Practical state recipes:
|
||||
|
||||
- `-laurie` by itself gives you the broader Laurie/debug event family (`0x844`) but not full keyboard cheats.
|
||||
- `-laurie` plus `Shift+~` is enough to reach the full keyboard-cheat state in live play, because `-laurie` supplies `0x844` and `~` then flips `0x6045`.
|
||||
- `jassica16` is still the most complete single-step unlock, because it toggles both cheat bytes together and also leaves the extra `0x8c52` sequence latch behind for later branches such as Hack Mover.
|
||||
|
||||
### Cheat-related string table (seg014 / `000e:xxxx`)
|
||||
|
||||
| Address | String | Notes |
|
||||
|
|
@ -411,30 +486,48 @@ Current strongest read:
|
|||
|
||||
**Secondary handler (000b:b62c):**
|
||||
|
||||
`cheat_event_listener_handle_event` (`000b:b62c`) receives event 0x410 through the registration installed by `cheat_event_listener_create` at `000b:b3b1`. When event 0x410 arrives, it writes state code `0xe` (decimal 14) into the event object's field `+0x6` and passes it to `000b:b7f3` for processing. This is a parallel state-machine path that runs alongside the 000c toggle; likely it drives an associated USECODE process or animation object into state 14.
|
||||
`usecode_debugger_handle_event` (`000b:b62c`, live `13a0:1df3`) receives event 0x410 through the registration installed by `usecode_debugger_gump_create` at `000b:b3b1` (`13a0:19b1`). When event 0x410 arrives, it writes state code `0xe` (decimal 14) into the event object's field `+0x6` and passes it to the shared debugger tail. This is a parallel state-machine path that runs alongside the 000c toggle; current best read is that it drives the hidden usecode-debugger state machine rather than a standalone cheat-only listener.
|
||||
|
||||
| Address | Symbol | Role |
|
||||
|-------------|-------------------------------|------|
|
||||
| `000b:b3b1` | `cheat_event_listener_create` | Allocates one seg109 listener object and subscribes it to the shared cheat/control event bundle that includes `0x410`. |
|
||||
| `000b:b62c` | `cheat_event_listener_handle_event` | Subscriber-side event mapper: rewrites incoming `0x410` to local state `0x0e` before entering the shared listener state machine. |
|
||||
| `000b:b3b1` | `usecode_debugger_gump_create` | Allocates the seg109 debugger gump object, initializes its panes/watch state, and subscribes it to the shared debugger/control event bundle that includes `0x410`. |
|
||||
| `000b:b62c` | `usecode_debugger_handle_event` | Debugger-side event mapper: rewrites incoming `0x410` to local state `0x0e` before entering the shared debugger state machine. |
|
||||
| `DS:0x604f` | `g_cdTransferDisplayActive` | Set/cleared by event `0x410`. |
|
||||
| `DS:0x60d2` | CD-transfer-on notification ptr | Near pointer in DS; resolves to far ptr → "CD TRANSFER DISPLAY ACTIVE." |
|
||||
| `DS:0x60ee` | CD-transfer-off notification ptr | Near pointer in DS; resolves to far ptr → "CD TRANSFER DISPLAY INACTIVE." |
|
||||
| `000a:b988` | `video_bios_state_snapshot` | Called after notification display in the 0x410 toggle to refresh screen state. |
|
||||
|
||||
### Hidden cheat menu investigation (seg109 UI lane)
|
||||
### Hidden usecode debugger investigation (seg109 UI lane)
|
||||
|
||||
New compiled-side evidence shows that the old "hidden cheat menu" label is misleading. The seg109 UI path is much closer to a hidden **usecode debugger / unit inspector** than to a retail cheat list:
|
||||
New compiled-side evidence shows that the older "hidden cheat menu" label was misleading. The recovered path is much closer to a hidden **usecode debugger / unit inspector** than to a retail cheat list.
|
||||
|
||||
| Address | Symbol | Role |
|
||||
|-------------|-------------------------------------|------|
|
||||
| `000b:9a86` | `usecode_debugger_open_for_current_unit` | Builds the seg109 debugger gump in current-unit mode, derives a usecode path from the live runtime context (`0x659c/0x659e`), loads that file, centers the current line, and then runs a modal UI loop. |
|
||||
| `000b:9c0d` | `usecode_debugger_open_modal` | Smaller generic modal wrapper that opens the same debugger UI without first preloading a current unit/file. |
|
||||
| `000b:b3b1` | `usecode_debugger_gump_create` | Constructor for the debugger object. Builds the panes/menu bar, initializes watch state, resolves the base `usecode` path, and registers the shared control-event bundle including `0x23f`, `0x410`, `0x411`, and `0x441`. |
|
||||
| `000b:b62c` | `usecode_debugger_handle_event` | Debugger event mapper; recovered cases are debugger-style commands (open unit/file, go to line, watch, inspect, clear watches, change global, find/search again, break to debugger). Event `0x23f` is reused as a local debugger-state command; event `0x410` remaps to local state `0x0e` before entering the shared tail. |
|
||||
| `000b:2882` | `usecode_debugger_build_menubar` | Builds the top-level debugger menus: File, Run, Breakpoints, Search, and Data. Recovered entries include `Open Unit`, `View File`, `Run to cursor`, `Trace into`, `Step over`, `Run until return`, `Toggle F2`, `Break to TDP`, `Find`, `Search again`, `Go to line`, `Watch`, `Inspect`, `Change Global`, and `Quit`. |
|
||||
The two address families that looked inconsistent in older notes are actually two connected layers of the same subsystem, not competing identifications:
|
||||
|
||||
This better explains the long-running negative result about the "infamous scrollable cheat menu": the hidden UI we can actually recover is not a plain scrollable cheat list at all. It is a modal debugger/unit-inspector front-end that expects valid usecode file context and developer-style command routing.
|
||||
- the `000b:*` / live `13a0:*` functions build and drive the modal debugger UI,
|
||||
- the `1408:*` helpers hold breakpoint/callstack state,
|
||||
- and the interpreter hook at `1418:04aa..04b5` ties the runtime back into that debugger state.
|
||||
|
||||
Combined debugger component table:
|
||||
|
||||
| Layer | Raw/reference anchor | Live NE Ghidra | Symbol | Verified role |
|
||||
|-------|----------------------|----------------|--------|---------------|
|
||||
| UI wrapper | `000b:9a86` | `13a0:0086` | `usecode_debugger_open_for_current_unit` | Builds the debugger gump in current-unit mode, resolves the active unit name from the live debugger state at `0x659c/0x659e`, loads the corresponding usecode file, centers on the current line, and enters the modal debugger UI. |
|
||||
| UI wrapper | `000b:9c0d` | `13a0:020d` | `usecode_debugger_open_modal` | Smaller generic modal wrapper for the same debugger gump; it skips current-unit preload and simply opens the modal UI. |
|
||||
| UI constructor | `000b:b3b1` | `13a0:19b1` | `usecode_debugger_gump_create` | Allocates the root debugger gump, builds the menu bar and panes, initializes watch state, resolves the base `usecode` path, and registers the shared debugger/control event bundle including `0x23f`, `0x410`, `0x411`, and `0x441`. |
|
||||
| UI dispatcher | `000b:b62c` | `13a0:1df3` | `usecode_debugger_handle_event` | Main debugger event dispatcher. Recovered cases are debugger-style commands: open unit/file, go to line, watch, inspect, clear watches, change global, find/search again, and break to TDP. Incoming event `0x410` is remapped to local state `0x0e`. |
|
||||
| UI menu builder | `000b:2882` | `13a0:2882` | `usecode_debugger_build_menubar` | Builds the hidden debugger menu bar with File, Run, Breakpoints, Search, and Data menus plus entries such as `Open Unit`, `View File`, `Run to cursor`, `Trace into`, `Step over`, `Run until return`, `Toggle F2`, `Break to TDP`, `Find`, `Search again`, `Go to line`, `Watch`, `Inspect`, `Change Global`, and `Quit`. |
|
||||
| Break-state constructor | n/a | `1408:0000` | `usecode_debugger_break_state_create` | Allocates and initializes the seg1408 debugger-state object: vtable, breakpoint table, current-entry stack, and run/step flags. This is the object the rest of the breakpoint lane appears to expect at `0x659c/0x659e`. |
|
||||
| Breakpoint gate | n/a | `1408:0053` | `usecode_debugger_maybe_break_on_current_line` | Stores the current interpreted line, resolves the active unit name through `1408:0444`, checks file+line breakpoints through `1408:029e`, evaluates run/step flags, and callbacks through the object's vtable when a break condition is met. |
|
||||
| Breakpoint helper | n/a | `1408:00dd` | `usecode_debugger_breakpoint_insert_sorted` | Inserts `(file,line)` breakpoint entries into the seg1408 table in sorted order. |
|
||||
| Breakpoint helper | n/a | `1408:029e` | `usecode_debugger_has_breakpoint` | Exact `(file,line)` membership test over the seg1408 breakpoint table. |
|
||||
| Callstack helper | n/a | `1408:03b0` | `usecode_debugger_callstack_push_entry` | Pushes the current unit/line debugger entry onto the seg1408 callstack. |
|
||||
| Callstack helper | n/a | `1408:03f7` | `usecode_debugger_callstack_pop_entry` | Pops one debugger callstack entry and asserts on underflow. |
|
||||
| Step-state helper | n/a | `1408:0419` | `usecode_debugger_enable_single_step` | Arms the single-step mode flags in the debugger-state object. |
|
||||
| Step-state helper | n/a | `1408:0432` | `usecode_debugger_clear_step_state` | Clears the break/step flags in the debugger-state object. |
|
||||
| Current-entry helper | n/a | `1408:0444` | `usecode_debugger_current_entry_get_unit_name` | Returns the active unit/file name pointer from the current debugger entry stack. |
|
||||
| Interpreter hook | n/a | `1418:04aa..04b5` | interpreter-side break callback site | Checks whether `0x659c/0x659e` is non-null, pushes the current interpreted line, then calls `1408:0053`. This is the concrete runtime handoff that links usecode execution to the hidden debugger state. |
|
||||
|
||||
This better explains the long-running negative result about the supposed scrollable cheat menu: the hidden UI we can actually recover is not a plain scrollable cheat list at all. It is a modal debugger/unit-inspector front-end that expects valid usecode file context and developer-style command routing.
|
||||
|
||||
#### Reachability status in retail binary
|
||||
|
||||
|
|
@ -443,27 +536,13 @@ This better explains the long-running negative result about the "infamous scroll
|
|||
- The cheat-code matcher `cheat_code_check` (`0007:0d0a`) toggles `0x844/0x6045` and emits event `0x103`; it does **not** call these menu wrappers directly.
|
||||
- The 000c handler for `0x103` (`000c:99dd`) executes a status/refresh lane and notification path; no direct call to `usecode_debugger_gump_create` appears there.
|
||||
|
||||
Current best read: this debugger path is compiled and functional at object level, but likely orphaned/hidden in final gameplay flow (possibly dev-only entry removed, or only reachable through non-recovered data-driven callback wiring). That orphaned status is a better fit for the missing retail cheat menu than assuming a still-live player-facing scrollable cheat list.
|
||||
Current best read: this debugger path is compiled and functional at object level, but likely orphaned/hidden in final gameplay flow (possibly dev-only entry removed, or only reachable through non-recovered data-driven callback wiring). That orphaned status is a better fit for the missing retail debugger entry than assuming a still-live player-facing scrollable cheat list.
|
||||
|
||||
#### Breakpoint callback lane (new strongest orphan candidate)
|
||||
|
||||
The next live NE pass shifts the strongest likely entry point away from the cheat-toggle helper and toward a surviving **usecode breakpoint callback lane** in seg1408/seg1418.
|
||||
The strongest likely original entry point is no longer the cheat-toggle helper. It is the surviving **usecode breakpoint callback lane** summarized in the combined table above.
|
||||
|
||||
New live renames in this lane:
|
||||
|
||||
| Live NE | Name | Role |
|
||||
|---------|------|------|
|
||||
| `1408:0000` | `usecode_debugger_break_state_create` | Allocates and initializes the seg1408 debugger-state object: breakpoint table, current-entry stack, and run-mode flags. |
|
||||
| `1408:0053` | `usecode_debugger_maybe_break_on_current_line` | Runtime breakpoint gate. Stores the current line into the debugger state, resolves the current unit/file name through `1408:0444`, checks the breakpoint table through `1408:029e`, and callbacks through the object's vtable when a break condition is met. |
|
||||
| `1408:00dd` | `usecode_debugger_breakpoint_insert_sorted` | Inserts `(file,line)` breakpoint entries into the seg1408 table in sorted order. |
|
||||
| `1408:029e` | `usecode_debugger_has_breakpoint` | Exact `(file,line)` membership test over the seg1408 breakpoint table. |
|
||||
| `1408:03b0` | `usecode_debugger_callstack_push_entry` | Pushes one current-unit/current-line debugger entry into the seg1408 stack. |
|
||||
| `1408:03f7` | `usecode_debugger_callstack_pop_entry` | Pops one debugger callstack/current-entry record. |
|
||||
| `1408:0419` | `usecode_debugger_enable_single_step` | Arms the step/run-state flags that make the next interpreter lane callback eligible. |
|
||||
| `1408:0432` | `usecode_debugger_clear_step_state` | Clears the step/run flags. |
|
||||
| `1408:0444` | `usecode_debugger_current_entry_get_unit_name` | Returns the active unit/file name pointer from the current debugger entry stack. |
|
||||
|
||||
This matters because `1418:04aa..04b5` is now comment-backed as a concrete interpreter-side handoff into `usecode_debugger_maybe_break_on_current_line`:
|
||||
The key live proof is the interpreter-side handoff at `1418:04aa..04b5`:
|
||||
|
||||
- it first checks whether `0x659c/0x659e` is non-null,
|
||||
- then pushes the current interpreted line,
|
||||
|
|
@ -521,8 +600,8 @@ Verified retail anchor points:
|
|||
| `0x70d75` | `0007:0d75` | cheat matcher emits event `0x103` | retail bytes = `68 03 01 9A FF FF 00 00 83 C4 02`; NE fixup source = `0007:0d79` -> `seg092:0476` |
|
||||
| `0x71d68` | fixup entry for `0007:0d79` | seg039 relocation record | exact retail entry: addr_type `0x03`, rel_type `0x00`, chain_off `0x2b79`, target `seg092:0476` |
|
||||
| `0xc99dd` | `000c:99dd` | later controller-side handler that also executes `push 0x103 / call 000a:5276` | retail fixup source = `000c:99e0` -> `seg092:0476`; this is the first materially safer deferred hook candidate after the direct matcher path failed |
|
||||
| `0xb9a8d` | `000b:9a8d` | arg setup inside `cheat_menu_open_from_current_slot` | original wrapper uses caller stack words `[BP+8]` and `[BP+6]` plus local armed flag `1` |
|
||||
| `0xb9c48` | `000b:9c48` | modal wrapper prologue; the inherited caller-word patch subsite is `000b:9c4e` / live `13a0:024a` | original wrapper still feeds caller stack words `[BP+8]` and `[BP+6]` into `cheat_event_listener_create`, but starts with local defaults `-1`, `-1`, `0` |
|
||||
| `0xb9a8d` | `000b:9a8d` | arg setup inside `usecode_debugger_open_for_current_unit` | wrapper-local constructor setup uses caller stack words `[BP+8]` and `[BP+6]` plus the current-unit mode flag `1` before calling `usecode_debugger_gump_create` |
|
||||
| `0xb9c48` | `000b:9c48` | modal wrapper prologue; the inherited caller-word patch subsite is `000b:9c4e` / live `13a0:024a` | wrapper-local setup still feeds caller stack words `[BP+8]` and `[BP+6]` into `usecode_debugger_gump_create`, but starts with local defaults `-1`, `-1`, `0` |
|
||||
|
||||
#### Live NE `CRUSADER.EXE` mapping in Ghidra
|
||||
|
||||
|
|
@ -565,7 +644,7 @@ One remaining function-hygiene caveat:
|
|||
What failed and why:
|
||||
|
||||
- Direct retarget of `0007:0d79` to `000b:9a86` crashed at startup when the NE relocation table was patched incorrectly as a raw far pointer. That was a file-format problem, not a semantic proof.
|
||||
- After the patcher was made NE-fixup-aware, direct retarget to `000b:9a86` no longer broke startup, but the game hung when the cheat actually fired. Disassembly shows why: `cheat_menu_open_from_current_slot` consumes caller-supplied words at `[BP+8]` and `[BP+6]`, so the cheat matcher context is the wrong stack shape.
|
||||
- After the patcher was made NE-fixup-aware, direct retarget to `000b:9a86` no longer broke startup, but the game hung when the cheat actually fired. Disassembly shows why: `usecode_debugger_open_for_current_unit` consumes caller-supplied words at `[BP+8]` and `[BP+6]`, so the cheat matcher context is the wrong stack shape.
|
||||
- Retargeting the same early cheat-matcher call to `000b:9c0d` got farther: the mouse pointer appeared, proving the hidden menu/display path was being entered. But it still hung with looping music, which points to **timing/context**, not a bad target address. The modal path appears unsafe when entered directly from the keyboard matcher even after the constructor args are forced to zero.
|
||||
- The narrower direct current-slot patch was then runtime-tested on `/Writable/CRUSADER-PATCHED.EXE` with bytes verified as `1130:2b78 = 9A 86 00 A0 13` and `13a0:008d = 6A 01 6A 00 6A 00 90 90`. User test result: the normal cheat-toggle path still appeared, but no hidden menu appeared. That closes the direct current-slot route as a practical candidate, not just a theoretical one.
|
||||
|
||||
|
|
@ -573,13 +652,13 @@ Current best patch rationale:
|
|||
|
||||
- `0007:0d75` remains the verified cheat-sequence success site, but the direct `1130:2b78 -> 13a0:0086` retarget is no longer the best live patch because it has now failed both analytically and at runtime.
|
||||
- The first materially safer deferred hook remains the controller-side `000c:99dd` lane, where the live call opcode begins at `13e8:25e0`. That path preserves the real `0x103` event context instead of substituting `0x42f`, which is the strongest evidence-backed difference from the rejected deferred experiment.
|
||||
- The chosen writable patch therefore restores `1130:2b78` to `CALLF 12d8:0476`, restores `13a0:008d` to the original current-slot wrapper bytes, retargets `13e8:25e0` to `13a0:020d` (`cheat_menu_open_modal`), and zeros only the inherited caller-word pushes at `13a0:024a` while preserving the modal wrapper's leading local defaults (`PUSH -1`, `PUSH -1`, `PUSH 0`).
|
||||
- The chosen writable patch therefore restores `1130:2b78` to `CALLF 12d8:0476`, restores `13a0:008d` to the original current-slot wrapper bytes, retargets `13e8:25e0` to `13a0:020d` (`usecode_debugger_open_modal`), and zeros only the inherited caller-word pushes at `13a0:024a` while preserving the modal wrapper's leading local defaults (`PUSH -1`, `PUSH -1`, `PUSH 0`).
|
||||
- The deferred `0x42f` branch remains negative evidence only: it proved the modal wrapper can enter the hidden UI path, but it also proved that substituting the event id or landing in the wrong deferred context trips the retail `FLEX.C` failure.
|
||||
|
||||
Rejected follow-up patch design:
|
||||
|
||||
- Site 1 tried changing `0007:0d75` from `push 0x103` to `push 0x42f`, keeping the original event-dispatch helper call intact.
|
||||
- Site 2 retargeted the `000c:99e1` relocation so the `0x42f` handler's internal `push 0x103 / call 000a:5276` sequence called `cheat_menu_open_modal` instead.
|
||||
- Site 2 retargeted the `000c:99e1` relocation so the `0x42f` handler's internal `push 0x103 / call 000a:5276` sequence called `usecode_debugger_open_modal` instead.
|
||||
- Site 3 patched `000b:9c48` from `6A 00 FF 76 08 FF 76 06` to `6A 00 6A 00 6A 00 90 90`.
|
||||
|
||||
Observed result on retail test build:
|
||||
|
|
@ -592,8 +671,8 @@ Current Ghidra-side patch plan for a copy:
|
|||
|
||||
1. Open the writable `/Writable/CRUSADER-PATCHED.EXE` program in Ghidra or PyGhidra, not the raw full-EXE database.
|
||||
2. Restore the disproven direct-hook sites: `1130:2b78` back to `9A 76 04 D8 12` (`CALLF 12d8:0476`) and `13a0:008d` back to `6A 01 FF 76 08 FF 76 06`.
|
||||
3. Navigate to the later controller-side `0x103` lane at `13e8:25e0` and retarget that `CALLF 12d8:0476` operand to `13a0:020d` (`cheat_menu_open_modal`), yielding bytes `9A 0D 02 A0 13`.
|
||||
4. Navigate to `13a0:024a` inside `cheat_menu_open_modal`. Replace only the inherited caller-frame pushes with `PUSH 0` / `PUSH 0` (`6A 00 6A 00 90 90`) and leave the leading `PUSH -1`, `PUSH -1`, `PUSH 0` defaults intact.
|
||||
3. Navigate to the later controller-side `0x103` lane at `13e8:25e0` and retarget that `CALLF 12d8:0476` operand to `13a0:020d` (`usecode_debugger_open_modal`), yielding bytes `9A 0D 02 A0 13`.
|
||||
4. Navigate to `13a0:024a` inside `usecode_debugger_open_modal`. Replace only the inherited caller-frame pushes with `PUSH 0` / `PUSH 0` (`6A 00 6A 00 90 90`) and leave the leading `PUSH -1`, `PUSH -1`, `PUSH 0` defaults intact.
|
||||
5. Do not reintroduce the `0x42f` substitution or the direct `13a0:0086` current-slot hook in the same test build. They are now negative evidence, not live candidates.
|
||||
|
||||
These edits are now applied and byte-verified on `/Writable/CRUSADER-PATCHED.EXE`. The live NE `CRUSADER.EXE` analysis database remains documentation-only for this batch.
|
||||
|
|
@ -601,7 +680,7 @@ These edits are now applied and byte-verified on `/Writable/CRUSADER-PATCHED.EXE
|
|||
Rationale for the revised wrapper patch:
|
||||
|
||||
- Earlier direct-hook attempts proved that inheriting the two caller-frame words at `000b:9a8f/9a92` is unsafe from the cheat matcher context.
|
||||
- But later decompilation of `cheat_event_listener_create` showed that the leading `push 0x1` at `000b:9a8d` is a distinct mode byte used by the constructor path, so zeroing all three pushed values was too aggressive.
|
||||
- But later decompilation of `usecode_debugger_gump_create` showed that the leading `push 0x1` at `000b:9a8d` is a distinct current-unit mode byte used by the constructor path, so zeroing all three pushed values was too aggressive.
|
||||
- The current patch therefore preserves the leading `1` and only forces the two ambiguous 16-bit parameters to zero.
|
||||
|
||||
Risk notes:
|
||||
|
|
|
|||
|
|
@ -134,15 +134,17 @@ Use comments on the compiled runtime functions that already consume or materiali
|
|||
|
||||
Best current anchors:
|
||||
|
||||
- `000d:51fd` = slot value load path
|
||||
- `000d:5572` = slot value plus additive word
|
||||
- `000d:46ec` = context create from slot index
|
||||
- `000d:0988` = referent-chain mutation family (`0x18..0x1b`)
|
||||
- `000d:208b` = materialize-or-forward value lane
|
||||
- `000d:21ed` = inline payload prepend stage
|
||||
- `000d:22bc` = decoded matrix/pushback consumer
|
||||
- `000d:2104` = mixed immediate/object finalize-to-outptr stage
|
||||
- `000d:ebe3` = opcode sequence runner
|
||||
|
||||
Comment payload should stay short and evidence-heavy, for example:
|
||||
|
||||
`POC USECODE body anchor: NPCTRIG slot 0x0A -> body 0x00DA..0x024F, raw word 0x013E, payload shape unresolved, parsed via tools/poc_crusader_usecode_parser.py`
|
||||
`POC USECODE body anchor: NPCTRIG slot 0x0A -> body 0x00DA..0x024F, raw word 0x013E, 5 local/debug rows after ret, parsed via tools/poc_crusader_usecode_parser.py`
|
||||
|
||||
### 3. Optional comment bundles per runtime family
|
||||
|
||||
|
|
|
|||
|
|
@ -113,6 +113,28 @@ The safe reading is:
|
|||
|
||||
The first script IR should preserve exact recompilation inputs before it tries to look pretty.
|
||||
|
||||
## Current Parser Views
|
||||
|
||||
The current proof-of-concept parser now emits three complementary views for a single class/slot body:
|
||||
|
||||
- JSON IR: the authoritative machine-facing output for tooling and any future assembler.
|
||||
- Flat text listing: a byte-faithful decode with offsets, raw bytes, and trailer sections.
|
||||
- Script view: a more readable block-labeled decompilation with locals, labels, and stack-VM statements.
|
||||
- Pseudocode view: a higher-level decompilation that tries to collapse common compare ladders and stack expressions into programming-language-like control flow.
|
||||
|
||||
The script and pseudocode views are intentionally descriptive rather than authoritative. They are meant to help read bodies like `NPCTRIG 0x0A` or `EVENT 0x0A` without losing the exact JSON IR that a round-trip compiler will need.
|
||||
|
||||
## Deferred Readability Follow-Ups
|
||||
|
||||
Keep these parser-facing readability tasks for later while the current focus stays on broad pseudocode export and class-family understanding:
|
||||
|
||||
1. Replace unresolved `class_XXXX_slot_YY` call labels with behavior-backed names where the compiled/runtime evidence is strong enough.
|
||||
2. Replace placeholder argument names such as `arg_06` with semantic names inferred from stable usage patterns.
|
||||
3. Detect more control-flow shapes beyond compare ladders, especially simple loops and early-return guards.
|
||||
4. Collapse common spawn/setup idioms into more domain-specific statements when the stack pattern is consistent.
|
||||
5. Run the pseudocode renderer across larger families like `EVENT`, `_BOOT`, and `SURCAM*` and tighten the heuristics where they still leak VM structure.
|
||||
6. Add small behavior-level comments only where they help explain gameplay meaning rather than VM mechanics.
|
||||
|
||||
### Unit of decompilation
|
||||
|
||||
The IR should be organized as:
|
||||
|
|
@ -219,6 +241,7 @@ The compiler side will need more than pretty script text. At minimum it must pre
|
|||
- Width/sign information for immediates
|
||||
- Inline versus indirect payload form
|
||||
- String payload encoding and terminators
|
||||
- Post-`ret` debug/local symbol trailers, including the local count byte and each per-local metadata row
|
||||
- Any unknown opcode byte sequences verbatim
|
||||
|
||||
If any of those are dropped, a source-level editor can still be readable, but it will stop being a trustworthy recompilation format.
|
||||
|
|
@ -396,9 +419,20 @@ event:
|
|||
derived_body_length: 373
|
||||
repeated_template_status: ""
|
||||
body:
|
||||
end_reason: end_opcode
|
||||
end_reason: debug_symbols_then_end
|
||||
raw_body_sha1: <digest>
|
||||
unknown_trailing_bytes: ""
|
||||
debug_symbol_offset: 0x0143
|
||||
debug_symbol_count: 5
|
||||
debug_symbols:
|
||||
- index: 0x00
|
||||
type_id: 0x69
|
||||
bp_repr: [BP+00h]
|
||||
name: referent
|
||||
- index: 0x01
|
||||
type_id: 0x69
|
||||
bp_repr: [BP+0Ah]
|
||||
name: event
|
||||
ops:
|
||||
- offset: 0x0000
|
||||
absolute_body_offset: 0x00da
|
||||
|
|
@ -417,9 +451,12 @@ ops:
|
|||
annotation_hints:
|
||||
runtime_family: slot-backed-owner-loaded-body
|
||||
compiled_anchors:
|
||||
- 000d:51fd
|
||||
- 000d:5572
|
||||
- 000d:46ec
|
||||
- 000d:0988
|
||||
- 000d:208b
|
||||
- 000d:21ed
|
||||
- 000d:22bc
|
||||
- 000d:2104
|
||||
- 000d:ebe3
|
||||
```
|
||||
|
||||
|
|
@ -431,7 +468,7 @@ annotation_hints:
|
|||
|
||||
`event` keeps the exact six-byte row meaningfully split into authoritative fields plus the derived body window.
|
||||
|
||||
`body` records how far the parser got and whether any bytes remain undecoded or trailing.
|
||||
`body` records how far the parser got, whether the body terminated at a real `0x7a` end marker, and whether a post-`ret` local/debug trailer was parsed instead of being misclassified as stray opcodes.
|
||||
|
||||
`ops` is intentionally lossless. Each decoded op keeps:
|
||||
|
||||
|
|
@ -442,6 +479,8 @@ annotation_hints:
|
|||
- exact raw bytes for the whole op
|
||||
- parsed operands as typed fields
|
||||
|
||||
`debug_symbols` preserves the owner-loaded post-`ret` local metadata block. Current evidence from `crusader-disasm` and the live extracted chunks shows that many bodies end as: executable ops -> `ret` -> local/debug symbol rows -> `0x7a` end. Those rows are not executable bytecode and should survive round-trip as structured metadata rather than raw tail bytes.
|
||||
|
||||
`annotation_hints` is the bridge to Ghidra. It is not a source-language feature. It exists so a later importer can attach the right comments and bookmarks to the compiled VM/runtime addresses without trying to infer them from free text.
|
||||
|
||||
### Opcode result policy
|
||||
|
|
@ -451,7 +490,7 @@ The parser should use four result classes only:
|
|||
- `decoded_op`: normal parsed opcode with structured operands
|
||||
- `unknown_opcode`: one-byte opcode not yet modeled; stop or fall back conservatively
|
||||
- `raw_tail`: remaining undecoded bytes after a stop condition
|
||||
- `debug_blob`: symbol/debug tail such as `0x5c`-anchored metadata
|
||||
- `debug_blob`: post-`ret` local/debug trailer ending in `0x7a`
|
||||
|
||||
That keeps the IR trustworthy even before the whole Crusader VM is modeled.
|
||||
|
||||
|
|
@ -474,16 +513,23 @@ annotation_hints:
|
|||
runtime_family: slot-backed-owner-loaded-body
|
||||
payload_shape_hint: signed_word
|
||||
compiled_anchors:
|
||||
- address: 000d:51fd
|
||||
role: slot_value_loader
|
||||
- address: 000d:5572
|
||||
role: slot_value_plus_offset
|
||||
- address: 000d:46ec
|
||||
role: context_create_from_slot
|
||||
- address: 000d:ebe3
|
||||
role: opcode_sequence_run
|
||||
- address: 000d:0988
|
||||
role: referent_chain_mutator
|
||||
- address: 000d:208b
|
||||
role: materialize_or_forward_value
|
||||
- address: 000d:21ed
|
||||
role: prepend_inline_payload
|
||||
- address: 000d:22bc
|
||||
role: matrix_pushback_stage
|
||||
- address: 000d:2104
|
||||
role: finalize_to_outptr
|
||||
- address: 000d:ebe3
|
||||
role: opcode_sequence_run
|
||||
runtime_stage_hints:
|
||||
- stage_address: 000d:0988
|
||||
ir_name: APPEND_UNIQUE_INDIRECT
|
||||
```
|
||||
|
||||
This is deliberately smaller than a full import format. It keeps the parser reusable even if the first Ghidra-side importer is only a comment/bookmark script.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue