12 KiB
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
-ustartup 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,~, andCtrl+Qstill 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/659eand valid current-unit/runtime context. - The cleanest non-EXE exploration path is now the
-uusecode-root override, but current evidence still does not show a script-visible way to construct the break-state object or write1478: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.EXEand JP/ja/CRUSADER.EXElooking specifically for a surviving writer/bootstrap path for1478:659c/659e, a constructor caller for1408: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_NextUsecodeOpat multiple sites including1418:049e,1418:04b1,1418:0519, and several later helper windowsusecode_debugger_open_for_current_unitat13a0:00af/13a0:0165usecode_debugger_format_expression_to_shared_bufferat13a0:03db/13a0:03f4usecode_debugger_handle_eventat13a0: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 mode0 - 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 whenthis == null - builds the debugger menubar and child panes
- initializes the shared watch table
- resolves the base
usecodepath withFilespec_GetFullPath(0, s_usecode, 0, 0) - registers the debugger/control event bundle through
NewGump_1360_0f2a
Recovered registered event set from this pass:
0x13d0x4430x1420x1410x1430x23f0x43e0x41f0x4170x4310x4110x4100x4410x4210x22d
Current direct callers are still only:
13a0:009bfromusecode_debugger_open_for_current_unit13a0:0256fromusecode_debugger_open_modal
This strengthens one important boundary:
0x410is 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
0x410is rewritten to local state0x0e - case
0x0eclears 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
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_presentat1420:0cdfis a real startup hook- it is called from
Init_Everythingat1048: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, andInterpreter_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
-uarchive 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 familiesSURCAMNS/SURCAMEWNPCTRIG
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/659ebootstrap
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-lauriejassica16~Ctrl+Q/ event0x410
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 debuggerprimitive
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 pathtoport or mimic one missing write/call - that is more likely to produce a truly minimal modification than another speculative retail patch chain
Current Recommendation
If the goal is the minimum modification that still has a realistic chance to work, the order should now be:
- Compare
REGRET.EXEand JP/ja/CRUSADER.EXEfor any surviving debugger bootstrap/writer. - Keep
-u/ replacementEUSECODE.FLXas the preferred low-risk experiment surface for any script-side proxy ideas. - Do not resume broader retail executable patching unless the cross-build pass fails to yield a clearer bootstrap or the existing O/P family gets one clean runtime confirmation target.
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:
- any write to the debugger global pointer equivalent of
1478:659c/659e - any caller of
1408:0000 Createor its build-specific equivalent - any direct caller of
usecode_debugger_open_for_current_unit - any direct caller of
usecode_debugger_open_modal - any non-stub debugger vtable slot replacing retail
1408:046f/1408:0474 - any command-line or cheat/debug hotkey path that lands near the seg109 wrappers or seg1408 constructor
- 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
-uand a replacementEUSECODE.FLXonly 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.