467 lines
No EOL
24 KiB
Markdown
467 lines
No EOL
24 KiB
Markdown
# Retail Usecode Debugger Entry Options
|
|
|
|
This note consolidates the current best entry-path analysis for the hidden retail usecode debugger in live `CRUSADER.EXE`.
|
|
|
|
Question:
|
|
|
|
- What is the lowest-modification path that could realistically get the hidden debugger menu working?
|
|
- Can ordinary usecode or a `-u` startup override reach it without another fragile executable patch?
|
|
- If retail still cannot do it cleanly, what should the next comparison pass in No Regret and JP No Remorse look for?
|
|
|
|
## Short Answer
|
|
|
|
Current best answer:
|
|
|
|
- No zero-modification retail entry path is currently evidenced.
|
|
- `-debug`, `-laurie`, `jassica16`, `~`, and `Ctrl+Q` still do **not** provide a proven bootstrap into the hidden debugger.
|
|
- The hidden debugger UI is real and usable, but it expects a live seg1408 break-state object at `1478:659c/659e` and valid current-unit/runtime context.
|
|
- The cleanest non-EXE exploration path is now the `-u` usecode-root override, but current evidence still does **not** show a script-visible way to construct the break-state object or write `1478:659c/659e`.
|
|
- The smallest structurally defensible executable patch is still the current interpreter-callsite-retarget family, but that remains more complex than a one-site tweak and therefore is not the preferred next move unless cross-build comparison fails.
|
|
- The best next investigation is a comparison pass in `REGRET.EXE` and JP `/ja/CRUSADER.EXE` looking specifically for a surviving writer/bootstrap path for `1478:659c/659e`, a constructor caller for `1408:0000`, or a direct caller to the seg109 wrappers.
|
|
|
|
## New Live-Ghidra Findings From This Pass
|
|
|
|
### 1. The missing retail bootstrap is still missing
|
|
|
|
Fresh live data-use recovery on `1478:659c` still shows reads only.
|
|
|
|
Current confirmed reader families:
|
|
|
|
- `Interpreter_NextUsecodeOp` at multiple sites including `1418:049e`, `1418:04b1`, `1418:0519`, and several later helper windows
|
|
- `usecode_debugger_open_for_current_unit` at `13a0:00af` / `13a0:0165`
|
|
- `usecode_debugger_format_expression_to_shared_buffer` at `13a0:03db` / `13a0:03f4`
|
|
- `usecode_debugger_handle_event` at `13a0:1e13`, `13a0:1e3b`, `13a0:1e5d`, `13a0:20b2`, `13a0:20b6`
|
|
- one additional seg109 local helper `FUN_13a0_1791`
|
|
|
|
What is still missing:
|
|
|
|
- no recovered retail writer to `1478:659c/659e`
|
|
- no recovered retail caller of `1408:0000 Create`
|
|
- no recovered direct caller of `13a0:0086 usecode_debugger_open_for_current_unit`
|
|
- no recovered direct caller of `13a0:020d usecode_debugger_open_modal`
|
|
|
|
That keeps the current retail model unchanged:
|
|
|
|
- the seg109 UI is real
|
|
- the seg1408 break-state object is real
|
|
- the interpreter callback lane is real
|
|
- but retail still looks like an orphaned debugger subsystem whose bootstrap/entry wiring was removed or compiled out
|
|
|
|
### 2. The UI wrappers are valid, but they are not safe cold-entry targets
|
|
|
|
Fresh decompile reads tighten the wrapper roles:
|
|
|
|
#### `13a0:0086 usecode_debugger_open_for_current_unit`
|
|
|
|
- immediately calls `usecode_debugger_gump_create(..., mode=1)`
|
|
- pulls the current unit name from `Remorse::UsecodeDebuggerBreakState::CurrentEntryGetUnitName(_DAT_1478_659c)`
|
|
- resolves a usecode path under `s_usecode`
|
|
- loads the corresponding unit file into the debugger pane
|
|
- centers on `current_line - 1`
|
|
- then enters `Dispatch_ModalGump`
|
|
|
|
Implication:
|
|
|
|
- this wrapper is closest to the original intended debugger experience
|
|
- but it absolutely expects a valid debugger object and current-entry state first
|
|
|
|
#### `13a0:020d usecode_debugger_open_modal`
|
|
|
|
- also calls `usecode_debugger_gump_create`, but with generic mode `0`
|
|
- skips current-unit preload and line-centering
|
|
- enters the same modal debugger gump
|
|
|
|
Implication:
|
|
|
|
- this is the safer force-open target than `13a0:0086`
|
|
- but it still assumes the surrounding caller/context is sane enough for the debugger gump to live
|
|
|
|
### 3. `usecode_debugger_gump_create` proves the debugger control bundle is complete once the gump exists
|
|
|
|
Fresh decompile of `13a0:19b1 usecode_debugger_gump_create` now gives the cleanest current constructor summary.
|
|
|
|
Verified behavior:
|
|
|
|
- allocates a `0x50`-byte root gump object when `this == null`
|
|
- builds the debugger menubar and child panes
|
|
- initializes the shared watch table
|
|
- resolves the base `usecode` path with `Filespec_GetFullPath(0, s_usecode, 0, 0)`
|
|
- registers the debugger/control event bundle through `NewGump_1360_0f2a`
|
|
|
|
Recovered registered event set from this pass:
|
|
|
|
- `0x13d`
|
|
- `0x443`
|
|
- `0x142`
|
|
- `0x141`
|
|
- `0x143`
|
|
- `0x23f`
|
|
- `0x43e`
|
|
- `0x41f`
|
|
- `0x417`
|
|
- `0x431`
|
|
- `0x411`
|
|
- `0x410`
|
|
- `0x441`
|
|
- `0x421`
|
|
- `0x22d`
|
|
|
|
Current direct callers are still only:
|
|
|
|
- `13a0:009b` from `usecode_debugger_open_for_current_unit`
|
|
- `13a0:0256` from `usecode_debugger_open_modal`
|
|
|
|
This strengthens one important boundary:
|
|
|
|
- `0x410` is a real debugger-gump event once the debugger UI has already been created
|
|
- it is not evidence that retail gameplay already has a reachable path that creates the gump
|
|
|
|
### 4. `0x410` remains parallel debugger UI state, not the debugger bootstrap itself
|
|
|
|
`usecode_debugger_handle_event` at `13a0:1df3` still confirms the same split, but the current decompile makes it easier to summarize.
|
|
|
|
Verified cases include debugger-style commands for:
|
|
|
|
- open file
|
|
- resume / break-next / single-step state changes
|
|
- go to line
|
|
- watch / inspect / clear watches
|
|
- change global memory
|
|
- find / search again
|
|
- local breakpoint/debug actions
|
|
|
|
Relevant `0x410` detail:
|
|
|
|
- incoming event `0x410` is rewritten to local state `0x0e`
|
|
- case `0x0e` clears one local selection/watch slot and refreshes debugger state
|
|
|
|
Implication:
|
|
|
|
- the debugger's own event machine knows what to do with `0x410`
|
|
- but only after the debugger gump already exists and is registered
|
|
- this does **not** give us a new no-patch retail entry path by itself
|
|
|
|
### 5. Retail still preserves a substantial debugger UI and command surface once the gump exists
|
|
|
|
The latest live `CRUSADER.EXE` decompile pass makes the surviving retail debugger capability map much clearer.
|
|
|
|
What retail can still do once a valid debugger object and gump already exist:
|
|
|
|
- `usecode_debugger_build_menubar` still builds the full hidden debugger menu bar with File, Run, Breakpoints, Search, and Data menus.
|
|
- `usecode_debugger_translate_registered_event` still translates the registered debugger/control event bundle into the local debugger command ids consumed by `usecode_debugger_handle_event`.
|
|
- `usecode_debugger_handle_event` still implements real debugger actions rather than decorative UI stubs: open file, run, break-next, single-step, go to line, watch, inspect, change global, search, search again, break to TDP, and watch clearing.
|
|
- `usecode_debugger_source_pane_draw_visible_lines` still clamps the source viewport, highlights the current line, scans the breakpoint table for visible marks, and draws the loaded source rows from the `.unk` buffer.
|
|
- `usecode_debugger_source_pane_handle_pointer_event` still converts pointer position into source line and column state and also drives scroll-style commands when the pointer is above or below the pane.
|
|
- the child-pane split is now clearer in retail than in the earlier first-pass mirror list: one pane is the loaded source view, while the sibling `13a0:16ee/1791/193f` lane is a separate watch-pane create/draw/click surface.
|
|
|
|
The live retail file-loading path is now also much clearer than before.
|
|
|
|
- `usecode_debugger_source_pane_load_file` resets local source-pane cursor state, destroys any previous source buffer, allocates a fresh source-buffer object from the requested path, updates the pane's line-count field, and refreshes the child widgets.
|
|
- `usecode_debugger_source_buffer_create_from_path` and `usecode_debugger_source_buffer_open_from_path` still build a dedicated far-memory file object, normalize the requested path, open the file, and pass it into the text-loader path.
|
|
- `usecode_debugger_source_buffer_load_text` still reads the whole file into far memory, rejects obviously non-text inputs, and hands the buffer to `usecode_debugger_source_buffer_split_lines_in_place`.
|
|
- `usecode_debugger_source_buffer_split_lines_in_place` still walks the loaded text, zero-terminates newline boundaries in place, and populates the per-line pointer table later consumed by draw, click, search, and goto-line logic.
|
|
- `usecode_debugger_source_buffer_get_line_ptr` is still the shared accessor used by source draw, pointer handling, find/find-next, and source-pane command handling.
|
|
|
|
Retail source navigation helpers are now closed well enough to describe the pane behavior directly.
|
|
|
|
- `usecode_debugger_set_line_selection` clamps the requested line against the loaded file range, clears transient cursor state, optionally updates the anchor line, and forces a redraw.
|
|
- `usecode_debugger_center_on_line` stores the target current line, computes a top-of-window line from the visible row count, and then delegates to `usecode_debugger_set_line_selection`.
|
|
- this means retail still preserves the ordinary source-browser workflow expected from the strings: load file, jump to line, center on current line, search through lines, and redraw the viewport around the active selection.
|
|
|
|
The watch-pane side is also more concrete now.
|
|
|
|
- `usecode_debugger_watch_pane_create` still allocates a real watch child gump and installs the watch-pane vtable.
|
|
- `usecode_debugger_watch_pane_draw` still iterates the shared 10-slot watch table at `1478:5580`, asks the break-state callback table to format each populated watch entry, and highlights the selected row.
|
|
- `usecode_debugger_watch_pane_handle_click` still converts pointer Y position into a watch-row index, updates the selected watch slot, and triggers a repaint.
|
|
- that is stronger evidence for `interactive watch list still survives` than the earlier weaker `watch-related strings still exist` wording.
|
|
|
|
Current safest interpretation:
|
|
|
|
- retail did not merely keep a few string tables or unused menu labels
|
|
- retail still keeps a functioning debugger front-end shell with real search, breakpoint, watch, inspect, and source-view logic
|
|
- the main missing piece is still how execution ever reaches that shell with valid live state
|
|
|
|
### 6. Retail callback and reachability limits are still the decisive difference from Regret
|
|
|
|
The same live pass also tightened the `what retail still cannot do` side.
|
|
|
|
Current direct-caller state in live `CRUSADER.EXE`:
|
|
|
|
- `usecode_debugger_open_for_current_unit` still has no recovered callers
|
|
- `usecode_debugger_open_modal` still has no recovered callers
|
|
- `Remorse::UsecodeDebuggerBreakState::Create` at `1408:0000` still has no recovered callers
|
|
- `usecode_debugger_handle_event` is still only reached through `usecode_debugger_translate_registered_event` and `usecode_debugger_forward_child_event`
|
|
|
|
Current callback state is still the key retail blocker too:
|
|
|
|
- `Remorse::UsecodeDebuggerBreakState::OnBreakTriggeredNoop` at `1408:046f` is still the live slot-0 callback
|
|
- `Remorse::UsecodeDebuggerBreakState::VtableSlot1ReturnZero` at `1408:0474` is still the live slot-1 callback
|
|
- unlike Regret, retail still has no recovered bootstrap that rewires the break-state object onto a live frontend-aware vtable
|
|
|
|
So the retail-versus-Regret split is now even sharper:
|
|
|
|
- both builds preserve a large debugger UI/event subsystem
|
|
- retail still lacks the recovered object bootstrap and live callback target that would naturally open the debugger on break
|
|
- Regret keeps both the writer/bootstrap path and the vtable upgrade that turns break-state callback slot `0` into a real `open_for_current_unit` launch path
|
|
|
|
That is why retail still reads as `functional debugger shell plus dormant break-state object`, while Regret reads as `same shell plus a surviving end-to-end open-on-break path`.
|
|
|
|
One practical refinement from the latest retail pass is that the shell is not merely a static menu/window skeleton.
|
|
|
|
- the file-open path still reaches a real far-memory source-buffer loader
|
|
- the search path still walks live source lines through `usecode_debugger_source_buffer_get_line_ptr`
|
|
- the goto-line path still updates line selection through the same source-pane helpers used by current-line centering
|
|
- the watch path still stores, formats, selects, clears, and redraws real watch rows
|
|
|
|
So the remaining barrier is still entry/bootstrap, not lack of interior debugger behavior after entry.
|
|
|
|
## What This Means For Usecode As An Entry Path
|
|
|
|
## Current Best Read
|
|
|
|
The `-u` retail override is now the best non-EXE exploration tool, but not yet a proven debugger-entry solution.
|
|
|
|
Why it still matters:
|
|
|
|
- `startup_apply_u_override_if_present` at `1420:0cdf` is a real startup hook
|
|
- it is called from `Init_Everything` at `1048:05d3`
|
|
- it swaps the single live usecode runtime root rather than layering a second source
|
|
- that replacement root is then consumed by ordinary compiled paths like `Usecode_ItemCallEvent`, `UsecodeProcess_CreateProcess`, and `Interpreter_NextUsecodeOp`
|
|
|
|
So `-u` gives a practical route to:
|
|
|
|
- replace scripted behavior with minimal executable modification
|
|
- test whether a data-driven/usecode-side path can indirectly reach an otherwise hidden compiled control lane
|
|
|
|
But the current negative evidence is still stronger than the hopeful side:
|
|
|
|
- no recovered usecode intrinsic or compiled helper currently reads like `open usecode debugger`
|
|
- no recovered usecode-visible primitive currently reads like `construct debugger break state`
|
|
- no recovered usecode-visible primitive currently writes `1478:659c/659e`
|
|
- current script/event scans still do not show a plain usecode literal/ordinal trail for event `0x410`
|
|
|
|
Current safest conclusion:
|
|
|
|
- a `-u` archive replacement is the least invasive *experimental platform*
|
|
- it is not yet an evidence-backed direct debugger unlock
|
|
|
|
## Best Script-Side Host Families If We Try `-u`
|
|
|
|
If the next step is an asset-only experiment, the best current targets remain:
|
|
|
|
- `MONITNS` / monitor-computer families
|
|
- `SURCAMNS` / `SURCAMEW`
|
|
- `NPCTRIG`
|
|
|
|
Why these are stronger than a generic chest or one-off gadget:
|
|
|
|
- they already sit near real modal UI / camera / event-control behavior
|
|
- they are more plausible bridges into a hidden control/event lane than ordinary loot or animation scripts
|
|
|
|
What they still do **not** currently prove:
|
|
|
|
- direct debugger construction
|
|
- direct seg109 wrapper calls
|
|
- direct `1478:659c/659e` bootstrap
|
|
|
|
So the immediate asset-only goal should be framed narrowly:
|
|
|
|
- not `open the debugger from usecode directly`
|
|
- but `test whether any scripted family can reach a compiled control path closer to the hidden debugger than the currently known public gumps`
|
|
|
|
## Ranked Entry Options By Modification Cost
|
|
|
|
### 1. Zero-modification retail route
|
|
|
|
Current status: no proven path.
|
|
|
|
Still ruled out by current evidence:
|
|
|
|
- `-debug`
|
|
- `-laurie`
|
|
- `jassica16`
|
|
- `~`
|
|
- `Ctrl+Q` / event `0x410`
|
|
|
|
Why:
|
|
|
|
- they toggle debug/cheat/display behavior
|
|
- they do not currently create the seg1408 break-state object or enter the seg109 wrappers
|
|
|
|
### 2. Asset-only route via `-u` replacement `EUSECODE.FLX`
|
|
|
|
Current status: lowest-modification practical experiment, but unproven as a debugger route.
|
|
|
|
Advantages:
|
|
|
|
- no retail EXE byte changes required
|
|
- fully reversible by swapping the override directory/archive
|
|
- reaches normal compiled usecode consumers
|
|
|
|
Limitations:
|
|
|
|
- still no direct evidence that usecode can create/write the required debugger state
|
|
- more likely to find an indirect bridge than a direct `open debugger` primitive
|
|
|
|
Current recommendation:
|
|
|
|
- prefer this over new ad hoc retail EXE patching if the goal is only to test indirect control-flow ideas
|
|
|
|
### 3. Minimal executable route: interpreter-callsite-retarget family
|
|
|
|
Current status: still the smallest structurally defensible retail patch family.
|
|
|
|
Why it remains the floor:
|
|
|
|
- one-site retarget ideas fail because they do not also create/store the debugger object
|
|
- direct shared-callback patching is too global and has already caused startup failures
|
|
- direct cold-calls into the UI wrappers use the wrong stack/context
|
|
|
|
What the current viable family still needs:
|
|
|
|
- lazy create-or-reuse of the seg1408 break-state object
|
|
- store into `1478:659c/659e`
|
|
- preserve deferred interpreter timing
|
|
- sanitize wrapper arguments before the seg109 UI entry
|
|
|
|
So even the best retail EXE path is still not a tiny one-byte/two-byte unlock.
|
|
|
|
### 4. Cross-build bootstrap recovery
|
|
|
|
Current status: best next investigation.
|
|
|
|
Why this is now preferred over more retail patch fishing:
|
|
|
|
- if No Regret or JP No Remorse kept any surviving debugger bootstrap, it could collapse the retail problem from `invent a new path` to `port or mimic one missing write/call`
|
|
- that is more likely to produce a truly minimal modification than another speculative retail patch chain
|
|
|
|
## Retail Ghidra Naming Backlog
|
|
|
|
The current note corpus now supports a tighter retail seg109 naming batch than the live authoring summary currently reflects.
|
|
|
|
These are the most important retail debugger-side helpers to promote explicitly in the active `CRUSADER.EXE` database before any new patch design work:
|
|
|
|
- `13a0:2882` = `usecode_debugger_build_menubar`
|
|
- `13a0:088f` = `usecode_debugger_source_pane_create`
|
|
- `13a0:0ae8` = `usecode_debugger_source_pane_init_view_from_break_state`
|
|
- `13a0:0ba7` = `usecode_debugger_source_pane_handle_command`
|
|
- `13a0:0f16` = `usecode_debugger_source_pane_handle_pointer_event`
|
|
- `13a0:1088` = `usecode_debugger_source_line_copy_for_display`
|
|
- `13a0:1118` = `usecode_debugger_source_pane_draw_visible_lines`
|
|
- `13a0:1413` = `usecode_debugger_source_pane_clamp_viewport`
|
|
- `13a0:15ac` = `usecode_debugger_source_pane_load_file`
|
|
- `13a0:16ee` = `usecode_debugger_watch_pane_create`
|
|
- `13a0:1791` = `usecode_debugger_watch_pane_draw`
|
|
- `13a0:193f` = `usecode_debugger_watch_pane_handle_click`
|
|
- `13a0:1c2c` = `usecode_debugger_translate_registered_event`
|
|
- `13a0:1dc6` = `usecode_debugger_forward_child_event`
|
|
- `13a0:2c2e` = `usecode_debugger_source_buffer_create_from_path`
|
|
- `13a0:2ca0` = `usecode_debugger_source_buffer_destroy`
|
|
- `13a0:2d14` = `usecode_debugger_source_buffer_open_from_path`
|
|
- `13a0:2e0a` = `usecode_debugger_source_buffer_load_text`
|
|
- `13a0:2f4f` = `usecode_debugger_source_buffer_split_lines_in_place`
|
|
- `13a0:301d` = `usecode_debugger_source_buffer_get_line_ptr`
|
|
|
|
Why this batch matters before more patching:
|
|
|
|
- it converts the remaining patch-target area from anonymous `FUN_13a0_xxxx` bodies into named UI, event, and source-buffer lanes
|
|
- it reduces the chance of patching the wrong helper when the debugger gump is already on-screen but still miswired
|
|
- it makes runtime-only experiments easier to reason about because the gump lifecycle, source loading, and event forwarding chain become legible in-session
|
|
|
|
The current strongest provenance for this retail batch is the combined retail `000b:* -> 13a0:*` table in `ne-segment1.md` plus the one-to-one structural match against the now-closed Regret `1398:*` family.
|
|
|
|
One live correction from the follow-up rename pass matters here: retail `13a0:16ee/1791/193f` reads as a watch-pane constructor/draw/click trio, while the source-pane lane remains centered on `13a0:088f/0ae8/0ba7/0f16/1088/1118/1413/15ac`. So the Regret-side structural match is still valuable, but the retail child-pane split is now sharper than the earlier first-pass list implied.
|
|
|
|
## Practical Alternatives To Manual Hex Patching
|
|
|
|
The current blocker is no longer `we do not know what to patch`. It is `the delivery path needs to stop depending on blind byte edits`.
|
|
|
|
### 1. Runtime-only proof via DOSBox-X debugger or equivalent live memory tooling
|
|
|
|
This is now the cleanest first confirmation path.
|
|
|
|
Use it to:
|
|
|
|
- keep the retail executable on disk unchanged
|
|
- patch or seed the debugger object only in live memory
|
|
- prove whether a create/store/open sequence is sufficient before committing to any permanent binary patch
|
|
|
|
What this should target first:
|
|
|
|
- seeding `1478:659c/659e` with a valid debugger-state object
|
|
- reusing the existing interpreter callback lane at `1418:049e..04b5`
|
|
- testing whether `13a0:020d` or the vtable callback path can open a stable debugger gump once state exists
|
|
|
|
This is especially attractive because it turns the current question from `did we edit the NE file correctly?` into `does the runtime model itself actually work?`
|
|
|
|
### 2. Scripted patch application to a writable clone, not manual hex editing
|
|
|
|
If a permanent retail patch is still wanted, the next step should be a reproducible patcher, not another manual byte-edit round.
|
|
|
|
That means:
|
|
|
|
- keep a dedicated writable clone of the executable
|
|
- store each patch as `address + expected old bytes + new bytes + reason`
|
|
- apply it through a scriptable patcher that validates the original bytes before writing
|
|
- regenerate the same patch on demand instead of hand-transcribing offsets each time
|
|
|
|
This can be done with PowerShell or Python against raw file offsets even if Ghidra export remains unreliable.
|
|
|
|
### 3. Use Ghidra only for analysis and verified byte plans
|
|
|
|
The current evidence does not support treating Ghidra export as the final patch-delivery mechanism for this lane.
|
|
|
|
What still works well:
|
|
|
|
- identify the correct callsite and byte budget in Ghidra
|
|
- annotate the patch rationale in-session
|
|
- test the control-flow hypothesis on a writable target
|
|
- then convert the verified result into an external patch manifest or launcher-side patcher
|
|
|
|
That keeps Ghidra in the role it is good at here: reverse-engineering and patch design, not blind final-binary distribution.
|
|
|
|
### 4. Keep `-u` as the low-risk data-side experiment surface
|
|
|
|
The `-u` override still does not solve the missing bootstrap, but it remains valuable for adjacent experiments that do not require byte writes.
|
|
|
|
Use it for:
|
|
|
|
- testing whether scripted monitor/camera/control families can get closer to debugger-adjacent compiled paths
|
|
- validating source-file and unit-name assumptions without touching the executable
|
|
- separating `data-side idea failed` from `patching workflow failed`
|
|
|
|
It should stay in the toolbox, but it should not be mistaken for a direct replacement for the missing retail bootstrap.
|
|
|
|
## Current Recommendation
|
|
|
|
If the goal is the minimum modification that still has a realistic chance to work, the order should now be:
|
|
|
|
1. Promote the retail seg109 naming backlog above so the remaining debugger lanes are explicit in Ghidra.
|
|
2. Use runtime-only memory seeding on a clean executable to prove or kill the bootstrap theory without committing file changes.
|
|
3. Compare `REGRET.EXE` and JP `/ja/CRUSADER.EXE` for any surviving debugger bootstrap/writer that can replace a custom retail bootstrap.
|
|
4. Keep `-u` / replacement `EUSECODE.FLX` as the preferred low-risk experiment surface for any script-side proxy ideas.
|
|
5. Do **not** resume broader retail executable patching unless the runtime proof or cross-build pass yields one clear small patch plan that can be applied by script to a writable clone.
|
|
|
|
That ranking fits both the new live evidence and the user's practical constraint that complex retail patch attempts have already been unstable.
|
|
|
|
## Cross-Build Exploration Note
|
|
|
|
When this moves to No Regret and JP No Remorse, the focused targets should be:
|
|
|
|
1. any write to the debugger global pointer equivalent of `1478:659c/659e`
|
|
2. any caller of `1408:0000 Create` or its build-specific equivalent
|
|
3. any direct caller of `usecode_debugger_open_for_current_unit`
|
|
4. any direct caller of `usecode_debugger_open_modal`
|
|
5. any non-stub debugger vtable slot replacing retail `1408:046f` / `1408:0474`
|
|
6. any command-line or cheat/debug hotkey path that lands near the seg109 wrappers or seg1408 constructor
|
|
7. any usecode/runtime path that seeds current-unit state for the debugger without using the orphaned retail bootstrap
|
|
|
|
## Bottom Line
|
|
|
|
Retail No Remorse still looks like it shipped with a real hidden usecode debugger whose UI, event dispatcher, and break-state object all survived, but whose bootstrap path did not.
|
|
|
|
That means the lowest-modification *currently evidenced* route is no longer "guess one more retail patch". It is:
|
|
|
|
- first, look for the missing bootstrap in sibling builds,
|
|
- second, use `-u` and a replacement `EUSECODE.FLX` only as a low-risk exploration surface,
|
|
- and only third, return to the interpreter-callsite-retarget patch family if the cross-build pass gives no smaller bootstrap to port. |