- Introduced `Ghidra Coverage Batch Director` and `Ghidra Coverage Mini` agents for improved parallel analysis and function coverage in `CRUSADER.EXE`. - Updated `ghidra.instructions.md` to clarify documentation practices and legacy file handling. - Added recent verified function coverage updates to `crusader_decompilation_notes.md` and `plan-mid.md` for better tracking of analysis progress. - Included new binary files for enhanced data handling in the project.
103 KiB
Crusader: No Remorse — Decompilation Notes
This file is an index. Detailed notes have been split into the docs/ folder by topic.
Active live analysis target is now CRUSADER.EXE. Existing CRUSADER-RAW.EXE notes remain in scope as cross-reference evidence and should be cited alongside live NE addresses when they support a rename, variable role, or behavior claim.
Recent verified NE function-coverage follow-up: a broad live MCP CRUSADER.EXE continuation wave pushed three caller-first 1000 buffered-I/O bundles, one 1078/1060 ItemCache relink bundle, and one 1348/1360 SpriteNode-NewGump geometry bundle. The durable rename set in this wave is 1000:5c9d = stream_count_buffered_newlines, 1000:5d1f = buffered_stream_seek, 1000:5e7f = fwrite_buffered, 1078:01c8 = ItemCache_RelinkForwardLink_1078, 1078:01f9 = ItemCache_RelinkMovedBlockLinks_1078, 1078:023e = ItemCache_RelinkAroundMovedBlock_1078, 1360:0b65 = sprite_tree_update_dirty_state, and 1360:0bb2 = sprite_tree_relink_child_after_head; the same wave also strengthened neutral evidence comments on 1000:578a, 1000:58c8, 1000:6c73, 1000:5d9f, 1000:5f0a, 1000:5f48, 1000:5fc0, 1060:1c70, 1060:0ecf, 1348:0b39, 1348:0c92, 1348:0d07, and 1348:0d81, while confirming the already-named geometry anchors 1360:0b43 = sprite_tree_point_in_bounds and 1360:0c00 = sprite_tree_sum_x_offset. Starting from the prior verified 1095 unnamed baseline, these eight additional safe renames move the working coverage floor to 1087 unnamed overall. Current best next step remains caller/callee closure in the still-dense 1000 stdio/buffer family, especially the Filespec_1238_032e / UProcess_1420_062f context around 1000:58c8, 1000:578a, 1000:6c73, and the still-comment-only sync helper 1000:5d9f, with a secondary caller-first pass on the 1348 SpriteNode/NewGump wrappers now that the adjacent 1360 geometry lane is better anchored.
Recent verified NE function-coverage follow-up: a third live CRUSADER.EXE MCP coverage wave ran as six parallel Ghidra Decomp Mini passes at 4 target functions each. The durable rename set in this batch is 1000:3ce9 = vsscanf_number_parser, 1000:3f6f = dos_apply_datetime_from_words, 1000:3faf = dos_get_datetime_words, 1000:5886 = refill_buffers_for_open_files, 1000:58f3 = parse_fopen_mode_flags, 1000:59af = open_stream_with_mode, 1078:0000 = DList_InsertBeforeHead_1078, 1078:00cf = DList_UnlinkNode_1078, 1078:0106 = DList_InsertAfterNode_1078, 1078:013c = DList_SpliceNode_1078, 1190:0000 = rect_intersect_inplace, 1190:01d9 = list_pop_front, 1190:022c = list_push_back, 1348:0000 = spritenode_invoke_0x4c, 1348:00b5 = spritenode_invoke_0x50, 1348:00d8 = spritenode_create_and_invoke_0x50, 1348:0124 = spritenode_create, 1360:02a5 = list_find_entry_by_type, 1360:02e4 = list_find_entry_by_high_byte, and 1360:031f = list_find_entry_by_two_byte_key; the same wave also added neutral evidence comments at 1000:3cbe, 1000:58c8, 1190:01b4, and 1360:0269. Current best next step is caller/callee closure around the remaining 1000 stdio/buffer helpers and follow-through on the surrounding 1190/1348 helper families, not another broad pass over the already named list-entry and SpriteNode wrappers.
Recent verified NE function-coverage follow-up: a second live CRUSADER.EXE MCP coverage wave ran as six parallel Ghidra Decomp Mini passes with explicit quotas of 3/3/3/3/3/6 functions. The durable rename set in this batch is 1000:37ca = vsscanf_engine, 1000:37de = advance_dest_by_char_size, 1000:56bd = buffer_normalize_and_refill, 11d0:15f2 = FindLinearCapableProcessForItemType, 1078:0046 = DList_InsertAfterHead_1078, 1078:0098 = DList_UnlinkNode_1078, 1190:006d = rect_union_inplace, 1190:00da = global_list_pop_head_1478_2cc3, 1190:0112 = global_list_push_head_1478_2cc3, 1348:0023 = spritenode_create_and_invoke_0x50, 1348:006f = spritenode_invoke_0x50, 1348:0092 = spritenode_invoke_0x50_alt, 1360:00c7 = alloc_init_1360_obj, and 1360:0113 = destroy_1360_obj; the same batch also added neutral evidence comments on 1000:578a, 11d0:0255, 11d0:04cd, 1360:0161, 1360:017a, 1360:01c7, and 1360:0218. Coverage improved from 3032/1140 unnamed to 3032/1126 unnamed. Current best workload rule for future GPT-5.4 mini passes is 4 functions by default, with 6 reserved for bundles dominated by small wrappers or loop/search helpers rather than deeper subsystem reasoning.
Recent verified NE function-coverage follow-up: live MCP sanity checks on active CRUSADER.EXE succeeded normally, and a six-Ghidra Decomp Mini coverage sweep plus one direct MCP edit-plan follow-up landed new evidence-backed names/comments in selectors 1000, 10e8, 11d0, 1078, 1190, 1348, and 1360. The durable rename set in this batch is 1000:626f = itoa, 1000:636e = memmove, 10e8:00c9 = NPC_SavegameWrite, 10e8:00f2 = NPC_SavegameRead, 11d0:2491 = kernel_process_snapshot_writer, and 11d0:39e6 = read_bios_keyboard_shift_cache; smaller helpers in 1078, 1190, 1348, and 1360 now also carry neutral evidence comments rather than raw placeholders. Current best next step is caller-driven closure of the still-ambiguous 1000 DOS/file-I/O wrapper cluster (37b0..37ff, 56bd..5825) and the heavier 11d0 table/dispatch families, not another blind sweep over already annotated small wrappers.
Recent verified PSX CLUT override-routing follow-up: docs/psx/art-binding-recovery.md now records a 2026-04-12 live MCP closure pass on 0x80041458, 0x80041144, 0x80044bdc, 0x80044e9c, 0x800a9f48, and 0x800a9f66. Current best read is now exporter-critical and executable-backed: main-visible injects authored high-byte palette token while special-visible does not, override selection is gated by flags & 0xfffffff0, active override resolution diverges by submitter/resource-format lane, and token 0 is effectively no-override in the world-object draw path.
Recent verified PSX palette/export follow-up: docs/psx/art-binding-recovery.md now records the 2026-04-12 lock-in for the prior VRAM-dump mode 1 palette proof. Current best read is now export-explicit: mode 1 bundles should render against a shared contiguous 256-entry CLUT equivalent to live row 0xF0, x=0, while the bundle header palette index stays diagnostic only as defaultPaletteIndex. The same follow-up also records that the processed PSX catalog already carried 62 maps, so the user-visible "single map" issue was export inclusion rather than cache enumeration.
Recent verified PSX no-placeholder exporter follow-up: docs/psx/art-binding-recovery.md now records the 2026-04-12 focused map 104 cache pass that removes synthetic fallback atlases from the exporter path. Current best read is now provenance-explicit: unresolved mixed-role buckets are resolved per authored-family plus raw-u5 cohort into actual PSX bundle art, and scene mapSource rows now preserve mappingSource plus optional artCohort so provisional donor matches remain auditable. Focused validation now exports 1002 art items, 0 fallback items, 1 atlas, and 136 shape definitions for scene fingerprint 3497e7f641856415.
Recent verified PSX map-104 cohort follow-up: docs/psx/map-rendering.md now records a 2026-04-12 final live pass on active writable SLUS_002.68 anchored to scene fingerprint 3497e7f641856415 and fixed cohorts (root 0x0022 items 25/35, root 0x0030 items 30/31, constructor 0x0030 items 85/86, control 0x0066 item 53). Current best read is now cohort-safe and exporter-ready: constructors preserve authored route seed into obj+0x1c, stage-2 still requires object-local 0x0400, draw submitter remains resource-kind based, and order-policy bits (0x0600) remain ordering-only; practical immediate change is to keep unresolved map-104 fallback partitioned by exact record_u5 seed family (0x0022 vs 0x0030) while runtime typePolicy/resource-kind fields remain unsampled.
Recent verified PSX runtime/control-island follow-up: docs/psx/map-rendering.md now records a 2026-04-12 live policy/island pass on active writable SLUS_002.68 centered on 0x80063e54, 0x80063e68, 0x800675ec, and 0x800675f8. Current best read is now discriminator-explicit: the island is post-load control/runtime gating and policy bits influence ordering/render/publication behavior, but stage-1 versus stage-2 world-visible lane choice still depends primarily on object-local route bit obj+0x1c & 0x0400 and submitter path remains resource-kind based. Practical exporter consequence is to keep island/policy capture as secondary modifiers while prioritizing resource-kind, latched frame token, and route bit for unresolved map-104 cohort splitting.
Recent verified PSX selector/transition follow-up: docs/psx/map-rendering.md now records a 2026-04-12 live pre-latch closure pass on active writable SLUS_002.68 centered on 0x80018578, 0x8001bca0, 0x8001e6e8, 0x800260e8, and 0x80025d68, with direct type-0x0042 row bytes recovered from 0x80063c1c and 0x80063d68. Current best read is now stage-explicit: selector reseat (3/4 and peers) is early-gated by view margin and object lane bit 0x0020, selector install writes obj+0x9e and script cursor, and final visible frame choice still latches at obj+0x94; practical exporter consequence is to keep pre-latch selector and latched frame token as separate channels for unresolved map-104 placeholder families.
Recent verified PSX descriptor-dispatch follow-up: docs/psx/map-storage-model.md now records a 2026-04-12 live section-0 authored-family closure on active SLUS_002.68 around psx_dispatch_section0_dispatch_roots (0x800256b0), psx_dispatch_section0_constructor_placements (0x800258cc), and descriptor table lane 0x80063118 / 0x80063220 / 0x800626f8. Current best read is now dispatch-explicit: unresolved families 0x0042, 0x0049, and 0x0055..0x0063 converge on the same descriptor row (0x800626f8) and therefore share one callback chassis (compound-create + first state advance, main-visible refresh, release) at section-0 entry. Practical consequence is that exporter/model divergence for these families should be attributed to per-type bank/policy/state lanes rather than to a type-unique descriptor-row callback fork.
Recent verified PSX progression-latch follow-up: docs/psx/jl-9-investigation.md now records a live timing pass on 0x80027548 and psx_level_session_set_next_map_id (0x8002ba84). Current best read is now rollover-explicit: progression callback writes deferred next-map latch DAT_800678d0, current_map_id switches only at level-session rollover (0x80031edc), and natural 54 -> 55 miss risk is therefore a deferred preemption window (transition exit before slot-0x0f tuple (0x0a,0x04) emit), not an immediate same-tick overwrite race.
Recent verified PSX transition-callback follow-up: docs/psx/jl-9-investigation.md now records a live provenance pass on callbacks 0x8002745c and 0x80027548. Current best read is now callback-table explicit: both entries are indirect targets from the pointer region around 0x800641f0..0x80064220 with no direct caller xrefs, 0x8002745c has a branch that can carry slot-family tuple opportunity via 0x80020f7c and an alternate branch that skips it, and 0x80027548 progression apply increases natural slot-0x0f tuple timing sensitivity by advancing transition state before later control-event handling.
Recent verified PSX countdown-vs-slot follow-up: docs/psx/jl-9-investigation.md now records a live structure check driven by the timed-segment clue. Current best read is now causality-tight: countdown logic (0x8002b738 -> 0x80020794 -> 0x800205e8) and natural JL-9 arm logic (0x800214ac -> 0x800640a0[0x0f] -> 0x800230e4 -> 0x800232f0) are adjacent lanes sharing control-state/timing helpers, with no recovered direct countdown-to-slot call edge in static flow.
Recent verified PSX slot-handler follow-up: docs/psx/jl-9-investigation.md now records a live sibling-recovery pass on psx_level_gate_slot_handler_table (0x800640a0) with previously raw table-entry handlers defined and named at 0x800215fc, 0x80021810, 0x800219e4, 0x80021fac, 0x80022214, 0x800222e8, 0x800223cc, 0x800226e0, 0x800227ac, 0x80022b50, 0x80023854, and 0x80023af0 plus a control-pair rename at 0x80022940. Current best read is now sibling-explicit for the 0x0a family: slot 0x0a/0x0b/0x0c and slot 0x0d/0x0e subcases are control/message/transition lanes, while slot 0x0f case (0x0a,0x04) remains the only recovered sibling branch that writes psx_debug_extra_channel_gate at 0x800232f0.
Recent verified PSX event-only synthesis continuation: docs/psx/jl-9-investigation.md now records a conservative live Ghidra naming/comment durability pass around the natural gate-arm control-event core. Current best read is now semantics-tight but still cautious: 0x800230e4 is promoted to psx_control_event_slot0f_handler (slot-family role), slot-entry labels for 0x0d/0x0e/0x0f are now explicit at 0x800640d4/0x800640d8/0x800640dc, and upstream 54 -> 49 -> sink remains preserved as structural topology rather than promoted active path until a second caller lane beyond the known <0x0a bound is recovered.
Recent verified PSX experiment follow-up: docs/psx/jl-9-investigation.md now records two more user emulator trials. Current best read is now sharper: a natural MFM4 run did not yield JL-9, while JFM4 plus manual gate byte 0x8006739d = 0x01, then hidden L0SR, then R1 + Circle, still did. Practical consequence is that MFM4 remains only the best natural prime candidate, while the natural in-level gate-arm event is now the dominant unresolved part of the mystery. Deferred follow-up experiments to revisit later are the current plan's 2, 4, 5, and 6.
Recent verified PSX event-only decoding pass: docs/psx/jl-9-in-level-event.md now collects the natural gate-arm work into a dedicated note. Current best read is now event-centered instead of passcode-centered: the sink dispatcher at 0x800214ac..0x800215f8, slot-family handlers 0x0d/0x0e/0x0f, and the exact JL-9 arm tuple (slot 0x0f, arg1 0x0a, arg2 0x04) are separated from the broader JL-9 chain, while the remaining upstream uncertainty is explicitly narrowed to one authored in-level producer rather than the hidden-code half.
Recent verified PSX forced-test closure: docs/psx/jl-9-investigation.md now records user emulator confirmation that manual byte poke 0x8006739d = 0x01, then hidden L0SR, then R1 + Circle successfully adds JL-9. Current best read is now causal-explicit: this proves the downstream hidden/input grant half and shows that the more direct thing being bypassed is the natural in-level gate-arm event rather than the passcode layer itself. Practical consequence is that MFM4 is now best treated as the strongest natural prime candidate, not a required part of the forced memory-edit test.
Recent verified PSX hard-clear and manual-test follow-up: docs/psx/jl-9-investigation.md now includes a focused live SLUS_002.68 check of the "beat the game on hard, then enter L0SR" theory plus a practical emulator poke closure. Current best read is now split: the hard-clear story remains weak because completion-text flows were found but no direct bridge from ending handlers into psx_debug_extra_channel_gate was recovered; meanwhile the manual test is now strong because psx_debug_extra_channel_gate is confirmed as single byte 0x8006739d, read as nonzero in the late JL-9 branch, with no recovered clear in inspected menu/session paths. Practical consequence is that the best forced test is now 0x8006739d = 0x01, then hidden L0SR, then R1 + Circle.
Recent verified PSX published-code sweep: docs/psx/jl-9-investigation.md now includes a focused live SLUS_002.68 pass against the user-supplied mission-code table. Current best read is now candidate-explicit: MFM4 (Level 15 hard) is the only strong published ordinary prime recovered so far because ordinary row i=0x0e returns selector 0x0f, maps to current-level 54, lands in gate family DAT_80063e68[54]=0x0f, and also sets psx_level_runtime_header_state=3. Practical consequence is narrower and more useful: if a real published-code JL-9 route exists, MFM4 is now the lead prime, but it still requires the missing in-level gate-arm event before hidden L0SR plus R1 + Circle.
Recent verified PSX JL-9 step-2 clarification: docs/psx/jl-9-investigation.md now makes the old JL-9 recipe less misleading. Current best read is now explicit that step 2 is not a second passcode-screen action but an in-level scripted/control dispatch through 0x800214ac..0x800215f8, where handler slot 0x0f resolves to psx_set_debug_extra_channel_gate and the exact writer tuple is (param_2==0x0a, param_3==0x04). The same follow-up also tightens hidden-passcode semantics: ?0SR / L0SR still routes through the shared passcode evaluator/decoder, but its selector-0 branch does not take the normal mission/apply-load path. Practical consequence is that the current JL-9 model is now normal passcode prime -> in-level scripted gate-arm event -> hidden passcode -> R1+Circle, with the remaining blocker narrowed to the concrete in-level event label.
Recent verified PSX passcode-screen semantics follow-up: docs/psx/jl-9-investigation.md now includes a focused live SLUS_002.68 closure pass on user-facing step ordering for normal versus hidden passcodes. Current best read is now explicit for screen behavior: both normal and hidden entries route through the same evaluator/decoder path (0x80034e38 -> 0x8003ec8c), hidden 0x10 sets psx_hidden_passcode_flag in that shared decoder (0x8003ed28), and the unnamed caller block around 0x80034c14 has divergent immediate behavior for eval return 0 versus nonzero before transition setup. Practical consequence is that the existing JL-9 sequence remains directionally right on two-phase logic (pre-hidden gate arm then hidden/input trigger) but should be treated as operationally under-specified until the exact player-visible producer for param_2==0x0a,param_3==4 is directly traced.
Recent verified PSX RP-16 startup/default hardening follow-up: docs/psx/jl-9-investigation.md now includes a focused live SLUS_002.68 pass that pushed on undefined nearby init stubs, startup mode-action callsites, selected-id writer closure, active-channel writes, and level mode/difficulty table resolution. Current best read is now stronger and still negative for startup RP-16: committed_selected_item_id has only two recovered writers (0x80039f68 reset and 0x8002f170 table commit sink), startup dispatch stays 8 -> (optional 2) -> 4 without a direct commit call, and scanned channel_commit_row_selected_item_id rows (0x00..0x19, selected byte +9) contain no 0x01 selected-id entry. Practical classification remains RP-16 not proven startup/default weapon while preserving row 0x01 as real non-startup data.
Recent verified PSX JL-9 sequence follow-up: docs/psx/jl-9-investigation.md now includes a focused live SLUS_002.68 special-passcode priming closure pass on ?0SR/?RTN/?QQQ against psx_level_runtime_header_state, psx_hidden_passcode_flag, and psx_debug_extra_channel_gate. Current best read is now explicit: ?RTN clears header state to 0, ?0SR sets hidden flag to 1, ?QQQ returns sentinel 0x12 without priming header state, and none of the recovered specials alone satisfy gate-arm (hidden==0 && header_state==3) at 0x800232f0, so strongest JL-9 model remains two-phase (pre-hidden gate arm, then hidden/input trigger).
Recent verified PSX RP-16 startup/default follow-up: docs/psx/jl-9-investigation.md now includes a focused live SLUS_002.68 startup-path closure pass over psx_level_post_load_runtime_reset (0x80039ef4), psx_weapon_channels_init_mode_loadout (0x8002f814), and psx_weapon_channels_apply_mode_transition_state (0x8002f278). Current best read is now explicit for the startup question: selected-id state is reset to 0 before init dispatch, no fixed selected-id 0x01 seed appears in the named loadout/mode-transition initializers, and fixed-immediate commit sites with 0x11/0x12/(contextual 0x01) currently sit in gameplay/control lanes rather than fresh-start init. Practical classification is now RP-16 not proven startup/default weapon while still remaining a real row with unresolved non-startup acquisition role.
Recent verified PSX JL-9 gate follow-up: docs/psx/jl-9-investigation.md now includes a focused live SLUS_002.68 final-enable-sequence closure pass around 0x8002ba9c, 0x800232f0, 0x8002fd90, and 0x8002fff4. Current best read is now explicit at instruction level: the extra 0x0d unlock (JL-9 lane) is gated by psx_debug_extra_channel_gate read at 0x8002fff4, that gate is written only when psx_hidden_passcode_flag==0 && psx_level_runtime_header_state==3 at 0x800232f0, and the grant helper entry still requires psx_hidden_passcode_flag!=0 plus input code 0x1e at 0x80013174, making a two-phase hidden flow the strongest practical model.
Recent verified PSX RP-16 follow-up: docs/psx/jl-9-investigation.md now includes a focused live SLUS_002.68 row/acquisition pass for selected-id 0x01. Current best read is now explicit: RP-16 row 0x01 is real populated weapon-definition data (0x80064690), direct shop unlock progression still uses 03..0c in the primary unlock branch, and the only observed shop-side 0x01 occurrence is in the secondary 0x0a..0x0e ammo-top-up branch rather than a direct unlock call. This tightens RP-16 away from "invalid slot" and toward a real early row with legacy/startup/placeholder-like behavior in the current image, pending one concrete non-debug normal acquisition writer.
Recent verified PSX JL follow-up: docs/psx/jl-9-investigation.md now includes a focused live SLUS_002.68 legitimate-acquisition closure pass with emulator-grounded selected-id mapping (0x8014577e, 0x0c=JL-2, 0x0d=JL-9). Current best read is tighter by lane: normal loadout and shop direct unlocks remain capped at <=0x0c, hidden/debug gating still provides the only recovered fixed-immediate unlock(0x0d) site (0x80030004), and scripted packed-action dispatch remains the only plausible non-debug 0x0d exception but is still data-dependent and unproven without a concrete shipped section0 marker/action row.
Recent verified PSX JL follow-up: docs/psx/jl-9-investigation.md now incorporates the user-verified selected-weapon byte mapping at 0x8014577e (00..0d) and corrects the earlier local-id shorthand into a stricter two-domain model: caller-side compact channel/local codes feed psx_apply_channel_effect_and_commit_selected_item_id (0x8002ef34), then 0x8002f15c resolves through channel_commit_row_selected_item_id[(channel*10)+9] into committed row-id domain (00..0d) before 0x8002f168 writes the nested runtime +0x1c field.
Recent verified PSX JL follow-up: docs/psx/jl-9-investigation.md now records the starter-only RAM compare that retracts the earlier 0x1456fc inventory-list interpretation. The attractive 0x1456fc..0x145748 0x0002..0x000b sequence is unchanged across the all-weapons and starter-only dumps, so it is not the live owned-weapon list; the dynamic region begins at 0x14574c, and the strongest current executable-backed field closure is now byte 0x14577e (0x0c all-weapons vs 0x02 starter-only) as selected/committed weapon row-id state inside a nested runtime block. The separate watch at 0x67944 still changes, but no direct static xrefs for 0x80067944 were recovered in the current image.
Recent verified PSX JL follow-up: docs/psx/jl-9-investigation.md now incorporates the stronger emulator-grounded selected-weapon byte mapping at 0x8014577e: 00 none, 01 RP-16, 02 RP-22, 03 RP-32, 04 SG-A1, 05 AC-88, 06 PA-31, 07 EM-4, 08 PL-1, 09 UV-9, 0A GL-303, 0B AR-7, 0C JL-2, 0D JL-9. This replaces the older JL-?=11 inference and shifts the remaining open questions to legitimate JL-9 acquisition and RP-16 role/acquisition.
Recent verified PSX JL follow-up: docs/psx/jl-9-investigation.md now records the RAM-search blocker in a narrower form. The current best runtime-state lead is no longer the nearby commit-table neighborhood but denser small-byte clusters around file offsets 0x133000, 0x133416, and 0x1335d4, with a weaker secondary candidate near 0x422c..0x4440; the sampled DAT_80064355[(channel*10)+9] field does not behave like a plain final JL row id in this dump. The same pass also promotes psx_handle_special_input_code as the strongest upstream helper for the special 0x1e trigger range while still leaving the exact controller-chord mapping open.
Recent verified PSX JL follow-up: docs/psx/jl-9-investigation.md now records the current RAM-search pivot more accurately. The earlier nearby commit-table neighborhood remains useful for the executable-side channel chain, but in the sampled main-RAM dump it does not show plain final JL ids at the tested byte field; the stronger current live-state candidates have moved to denser 0x0c/0x0d table-like clusters around file offsets 0x133000, 0x133416, and 0x1335d4. The same pass also names psx_handle_special_input_code as the strongest current upstream helper for the hidden 0x1e input-code lane, while still stopping short of a closed R1 + Circle proof.
Recent verified PSX JL follow-up: docs/psx/jl-9-investigation.md now records a six-agent pass that tightened both the JL-9 trigger story and the main-RAM angle. Current best read is: the code closes the path hidden passcode active -> gated input code 0x1e -> psx_debug_grant_weapon_channels_and_ammo -> extra 0x0d unlock, but still does not statically prove the exact folklore button chord; the 2 MiB binary/Crusader - No Remorse Weapons Main Ram.bin artifact is still plausible main RAM and now has candidate compact slot-like records, but it still needs one executable-side inventory/HUD anchor before it can decode the runtime weapon list cleanly.
Recent verified PSX JL follow-up: docs/psx/jl-9-investigation.md now distinguishes the two checked dump artifacts instead of treating them as one generic memory lead. Current best read is: binary/Crusader - No Remorse Memdump Weapons.bin is still VRAM/HUD-side evidence, binary/Crusader - No Remorse Weapons Main Ram.bin is plausible main RAM but still not self-identifying enough to close the selected JL-? slot without the executable-side id resolver, and JL-2 AMMO remains the strongest next normal-lane clue while JL-9 stays the stronger extra hidden/debug-conditioned lane.
Recent verified PSX JL follow-up: docs/psx/jl-9-investigation.md was normalized after a fresh six-agent SLUS_002.68 pass so the note now reads as one fact-first summary instead of stacked dated rechecks. The strongest new practical deltas in that pass are: the checked binary/Crusader - No Remorse Memdump Weapons.bin artifact is still useful only as PSX VRAM/HUD evidence rather than direct slot RAM, and JL-2 now becomes the clearer next unknown because JL-2 AMMO is present as a direct UI string while JL-9 remains the stronger extra hidden/debug-conditioned lane.
Recent verified PSX JL follow-up: docs/psx/jl-9-investigation.md now records a narrow live SLUS_002.68 cleanup pass that promoted several previously documented JL-lane globals from raw DAT_ placeholders to evidence-backed live symbols (psx_hidden_passcode_flag, psx_debug_extra_channel_gate, psx_level_runtime_header_state, channel_commit_row_table, channel_commit_row_selected_item_id, committed_selected_item_id, psx_weapon_spawn_type, psx_weapon_spawn_audio_event_id, psx_weapon_spawn_state_selector) and reclassified the checked binary/Crusader - No Remorse Memdump Weapons.bin artifact as a full 1 MiB PSX VRAM dump rather than weapon-slot RAM. The same note now also makes the next pivot explicit: if the extra hidden/debug lane remains JL-9, then JL-2 becomes the next main unknown because JL-2 AMMO exists as a direct UI string while no matching plain JL-9 AMMO string has yet been recovered.
Recent verified PSX JL follow-up: docs/psx/jl-9-investigation.md now includes a focused four-lane MCP-only comparison on live SLUS_002.68 across normal loadout (0x8002f814), shop (0x8003de68), scripted grant (0x800311f4..0x800313b4), and debug grant (0x8002fd90). The strongest new discriminator is explicit in disassembly: only debug grant performs the post-0x0c extra unlock (0x80030004 -> ...unlock_and_seed_markers(0x0d)) behind DAT_8006739d read at 0x8002fff4, while the L0SR-linked chain still enters via 0x8003ed28 (DAT_80067454=1) and 0x80013174. Current best conclusion remains JL-9 (0x80064858, index 0x0d) as the extra post-L0SR non-PC lane over JL-2 (0x80064832, index 0x0c).
Recent verified PSX JL follow-up: docs/psx/jl-9-investigation.md now includes an MCP-only row-field consumer trace on live SLUS_002.68 for JL-2 (0x80064832) versus JL-9 (0x80064858). Current best read is stable and narrower: both rows still share base type-art lane +0x1c=0x18 into the type-indexed active art-header path (DAT_800758d8 via object constructors), while +0x24 diverges (0x4b vs 0x0f) as a transition/state selector lane rather than a direct base-resource pointer. Runtime capture remains required only for exact final frame/resource-token closure.
Recent verified PSX concrete-sample follow-up: docs/psx/map-rendering.md and docs/psx/map-viewer-plan.md now capture another six-agent pass over the fixed map 104 0x0042 sample pack instead of broadening the search surface. The live PSX database now also names psx_transition_selector_probe_nearby_overlap, psx_transition_selector_probe_marker_overlap, psx_snapshot_active_object_runtime_rows, psx_release_all_active_objects_and_reset_type_runtime_banks, and a wider root marker/channel helper family. Current best read is tighter in three practical ways: the 64x64 versus 64x40 0x0042 split still reads more like shared-resource / different-frame-state behavior than a different constructor bind path; the selector-to-frame bridge is now explicit through obj+0x9e install, obj+0x94 latch, and the later frame-geometry consumers; and the stage-1 versus stage-2 branch point is now explicit at psx_object_integrate_motion_and_route_visible, even though the exact live 0x0400 provenance for the fixed sample pack still needs capture. The next map 104 pass should therefore stay on items 25/30/31/35/85/86 and capture bound resource identity plus live frame/state and route-bit state at the decisive branch instead of widening heuristics.
Recent verified PSX concrete-sample follow-up: docs/psx/map-rendering.md and docs/psx/map-viewer-plan.md now capture the first map 104 0x0042 pass grounded directly in exported runtimeDiagnostic scene items rather than generic family hypotheses. The live PSX database now names the spawn-side selector bridge psx_transition_spawn_and_seed_selector_from_record, the root-family decoder helper psx_section0_dispatch_root_find_marker_record_by_channel, the nearby resource-upload helpers psx_upload_spec_wdl_image_pair_to_vram and psx_restore_display_draw_env_after_spec_upload, the level-loaded policy pointer psx_type_policy_table_ptr, and the selector/policy row tables psx_type_transition_mode_policy_rows / psx_type_transition_selector_rows. Current best read is tighter in four viewer-facing ways: root and constructor section-0 families now have explicit named entry points but still converge through the same shared 0x0042 descriptor row; constructors directly copy the authored lane word into obj+0x1c, so exported initialWord values are real authored state; the strongest recovered 0x0400 stage-selection write is still nested-state-side rather than a direct object-local 0x0042 writer; and DAT_800675f8 now reads as a level-loaded per-type policy pointer rather than a per-lane discriminator. The next map 104 runtime pass should therefore stay on the fixed sample pack (item:25/30/31/35/85/86) and sample bound-resource identity plus live frame/state instead of broadening donor heuristics again.
Recent verified PSX exporter follow-up: docs/psx/map-rendering.md, docs/psx/map-viewer-plan.md, and map_renderer/src/lib/psx-cache.js now capture the viewer-side half of the current 0x0042 narrowing step. The PSX cache builder now emits scene version psx-runtime-record-probe-v10 with a per-item runtimeDiagnostic payload that mirrors the current Ghidra-side channel split: object-local route flags, selector seed/pre-latch hint, exporter-side latched-state candidate, nested-runtime placeholders, resource-kind hints, and a placeholder slot for the live DAT_800675f8 word. The next map 104 runtime pass should therefore work against those exported channels directly instead of redefining the channel model again.
Recent verified PSX separate-batch follow-up: docs/psx/map-rendering.md, docs/psx/map-viewer-plan.md, and docs/psx/psx.md now capture another focused live SLUS_002.68 round where six sub-passes covered non-overlapping areas: constructor-placement sample path, root-dispatch/mode gate, pre-latch 0x0042 transition path, anonymous route-bit writer islands, the DAT_800675f8 policy lane, and the per-type art/cache lane. Current best read is tighter in three practical ways: the art/cache pair now reads as psx_type_art_active_header_bank plus psx_type_art_built_resource_bank; psx_type42_transition_selector_tick now clearly gates on psx_object_is_within_view_margin before emitting pre-latch selector 3/4; and the anonymous island recovery proves wider runtime-state 0x0400 writes and related policy control without yet proving a direct object-local obj+0x1c |= 0x0400 writer for 0x0042. The next runtime sample should therefore log object-local flags, nested runtime state, pre-latch selector, latched state word, and bound resource kind as separate channels instead of collapsing them into one route/state summary.
Recent verified PSX static-discriminator follow-up: docs/psx/map-rendering.md, docs/psx/map-viewer-plan.md, and docs/psx/psx.md now capture the next focused live SLUS_002.68 pass on unresolved 0x0042. Current best read is tighter in four direct viewer-facing ways: type 0x0042 is now pinned to exact descriptor-table slot 0x80063220 -> 0x800626f8; type 0x0042 also has a dedicated transition helper psx_type42_transition_selector_tick that can dispatch low selectors 3/4 before the +0x94-style runtime latch copy; constructors still seed obj+0x1c from authored u5 while the named transition-table path only mutates bit 0x0002; and DAT_800675f8 now reads as policy bits for nearby-publication, stage-1 ordering, and semitrans draw policy rather than as the main missing route split. The next useful 0x0042 sample should therefore correlate pre-latch selector dispatch, latched obj+0x94, obj+0x1c bit 0x0400, and resource kind on representative map-facing cases instead of widening generic family heuristics again.
Recent verified PSX route/state follow-up: docs/psx/map-rendering.md, docs/psx/map-viewer-plan.md, and docs/psx/psx.md now capture the next focused live SLUS_002.68 batch around unresolved 0x0042 routing rather than another broad helper sweep. Current best read is tighter in three ways that matter directly to the viewer: constructor and root-dispatch records both hand authored flags into obj+0x1c, where 0x0020 stays the broad world-visible gate, 0x0002 stays orientation/extents behavior, and 0x0400 is now the strongest recovered stage-2 selector; psx_object_select_state_from_transition_table now exposes a concrete per-type selector source through DAT_80063b4c ahead of psx_object_select_state_script; and the loader side is narrower because psx_load_type_state_banks owns DAT_800758cc/d0/d4, psx_stream_install_type_runtime_banks is the packed-stream all-bank installer, and DAT_80067794 now reads as save/transition runtime-header state rather than the missing 0x0042 art-binding lane. The next viewer-facing sample should therefore correlate type 0x0042 transition-table outputs with representative map 104 obj+0x1c / obj+0x10 / obj+0x94 runtime values.
Recent verified PSX six-track consolidation: docs/psx/map-rendering.md and docs/psx/map-viewer-plan.md now consolidate the latest live SLUS_002.68 Ghidra sweep across descriptor callbacks, interaction/reselection, world-frame wrappers, visible-list ordering, resource submission, and HUD/overlay presentation. Current best read is narrower than before in three practical ways: 0x0042 still shares the generic 0x003e..0x0050 descriptor cluster instead of owning a special descriptor fork, constructor-placement 0x0042 still reads as a compound/main-visible route inside that shared family, and both world-facing draw lanes choose sprite versus image-table submission from the bound resource header kind while the HUD/overlay lane remains a non-map-facing exception. The next viewer-facing recovery step should therefore sample representative map 104 0x0042 runtime-bank/state/resource-kind combinations rather than widening donor reuse or looking for a hidden descriptor-table split.
Recent verified PSX interaction/reselection cleanup: docs/psx/map-rendering.md now captures a focused live SLUS_002.68 pass around psx_type4_reselect_motion_state / psx_object_update_nearby_interactions and nearby helpers. The PSX database now names 0x80028050 = psx_object_test_strict_nonoverlap_flag8_pair, 0x800281d4 = psx_object_test_strict_nonoverlap_flag8_subject, 0x80028700 = psx_object_adjust_param9c_by_view_side, 0x800287bc = psx_object_update_param9c_from_contact_target, 0x80028eb4 = psx_object_apply_contact_push_bias, and 0x8002923c = psx_object_spawn_type11_contact_proxy, with concise technical comments at each entry. Current best read is that this lane materially mutates runtime interaction state post-spawn (+0x30..+0x38, +0x9c) and can spawn type-0x11 contact proxies in-flow, so exporter fallback logic should continue treating it as runtime behavior, not static authored placement metadata.
Recent verified PSX presentation-lane cleanup: docs/psx/map-rendering.md now captures a focused live SLUS_002.68 pass around the HUD/overlay neighborhood. The PSX database now names 0x80035cc0 = psx_overlay_slot_create, 0x80036000 = psx_overlay_slot_release, 0x80038114 = psx_overlay_slot_step_color_fade, and 0x800388a8 = psx_hud_overlay_init_resources, and replaces the earlier generic in-database "verified by subagent pass" note on psx_draw_hud_overlay_pass with direct technical call-chain and table-behavior comments. Current best read remains that this helper cluster is a non-map-facing presentation lane layered after world stage-1/stage-2 draw submission; for viewer recovery it should be treated as a false-match guardrail, not as map-art placement evidence.
Recent verified PSX map-viewer batch: docs/psx/psx.md, docs/psx/map-rendering.md, docs/psx/map-viewer-plan.md, docs/psx/art-binding-recovery.md, and the new docs/psx/map-storage-model.md now record both the latest executable-backed renderer findings and a focused evidence-backed write-up for how PSX level storage differs from the PC build. Current best read is that the cache builder still exports executable-named section-0 visible families (section0_dispatch_roots, section0_constructor_placements), runtime/state layers for DAT_800758d8, DAT_800758d0, DAT_800758cc, DAT_800758d4, and one offline FUN_8003b00c decode candidate for DAT_8006b5d8 -> DAT_8006769c, but it now also treats large zero-block DAT_800758d8 constructor-placement bands as inherited-art candidates before falling back to placeholders. That donor-based recovery path moved the built cache from 58,262 fallback items / 1,714 bundle-mapped items down to 25,038 fallback items / 34,938 bundle-mapped items, making maps such as 0, 9, and 43 mostly real-art while leaving map 104 as the clearest remaining outlier. The newest pass does not change the earlier DAT_800758d4 conclusion: psx_object_advance_state_script still sign-extends those three bytes into obj+0x30/+0x34/+0x38 for overlap/contact-style consumers rather than the draw path, and the unresolved blocker still sits later in the live state-to-resource/frame bridge for the remaining constructor-placement families. Follow-up live MCP passes now also tighten the per-frame interaction/control side in Ghidra: psx_script_dispatch_audio_event closes the 0xfffe script sentinel as an audio/sequence side-effect opcode, psx_heading16_lookup_unit_vector closes the heading-token vector table used during target-vector reselection, psx_authored_record_in_view_bounds now closes the authored-record cull gate alongside psx_world_point_in_view_bounds, the old post-projection FUN_80027f80 cleanup is now identified as the nearby-interaction active-set lane (psx_reset_nearby_interaction_list, psx_nearby_interaction_list_add, psx_nearby_interaction_list_remove, psx_update_motion_and_nearby_interactions) rather than a hidden render-only path, and the adjacent 0x80023c..2b1.. block is now grounded as a control-script lane (psx_object_run_control_opcode, psx_control_move_player_to_point, psx_control_move_object_to_point, psx_control_wait_ticks, psx_control_configure_fixed_camera_anchor, psx_control_set_facing_direction, psx_queue_deferred_control_command, psx_flush_deferred_control_queue, psx_apply_deferred_control_command, psx_apply_deferred_control_to_dispatch_roots, psx_apply_deferred_control_to_live_objects), with case 8 now narrowed further through direct callee naming as psx_spawn_object_compound_effect_variant3. The same pass also refreshes the live PSX naming census from MCP list_functions: 1274 local 0x800... functions with 917 named and 357 still anonymous, for a current local naming floor of 71.98%.
Recent verified combat-data batch: docs/combat-dat.md now documents the shipped COMBAT.DAT archive end to end. Current best read is that all local Remorse/Regret variants ship the same 14-record combat-tactic archive, each record contains a 16-byte name plus four block offsets and bytecode, and the tactic VM is now grounded both in the live CRUSADER.EXE helpers (Attack_SetupForTacticNo, Attack_SetupForBlockNo, NPC_Get/SetNPCTacticNo) and in ScummVM's readable Crusader attack-process implementation. The new note also promotes the per-tactic human-readable catalog, including the midpoint-pressure, marker-shuttle, step-out-shoot, and stationary-chaos families.
Recent verified NE movement/collision batch: docs/raw-0008-000c.md now extends the live AreaSearch_CollideMove lane one helper layer deeper. Current best read is that the collision-storage queue is no longer only anchored at StorageDataProcess_Create / Run and the legal-move wrappers: the live database now also carries the step-aware seg029 sweep helpers AreaSearch_SweepShapeBetweenPoints, AreaSearch_SweepItemToPointWithStepUp, and AreaSearch_SweepShapeBetweenPointsWithStepUp, the seg031 release-side queue cleanup pair StorageDataProcess_Release and storage_process_ref_list_terminate_item_matches, and adjacent seg090 helper ItemCache_PushAndPopToDirectionalOffset. The practical remaining gap in this lane is now earlier caller policy rather than local helper identity.
Recent map-renderer egg-link follow-up: docs/map_renderer/egg-identification.md now closes the old No Regret map-3 destination-egg 102 gap. Current best read is that Regret uses a second elevator family at shape:400 (0x0190) in addition to the earlier Remorse-focused shape:542 rule: recovered Regret ELEVATOR::gotHit accepts QLo >= 100, treats QLo < 0x00c8 as the generic same-map lane, and map 3 contains a concrete source object item:664:fixed:400:0:44030:9662:0 with quality 614 (QLo 102) that resolves the previously unexplained destination egg 102.
Recent map-renderer editor-object follow-up: docs/map_renderer/trigger-usecode-links.md and docs/map_renderer/editor-object-survey.md now promote a Regret-only controller cluster that had still been sitting in the unresolved editor bucket. Current best read is that 0x04c6 / 0x04de are WATCHNS / WATCHEW secret-door watcher controllers, 0x0510 is their nearby SECRET_DOOR_POST target keyed by shared QLo, 0x05e1 is CRYOBOX, and 0x05df / 0x05e0 are the paired pressure-barrier faces it drives by shared QLo. The same batch also closes 0x0451 / 0x05ae as CRAZYEW / CRAZYNS hit-driven NPC wake-up relays and 0x056d as VIDEOBOX, then promotes cautious local viewer arrows for WATCH* -> 0x0510 and CRYOBOX -> 0x05DF/0x05E0 in the map renderer.
Recent map-renderer control-pad follow-up: docs/map_renderer/egg-identification.md, docs/map_renderer/trigger-usecode-links.md, and docs/map_renderer/editor-object-survey.md now tighten the 0x0318 / 0x0366 read using the decompressed .cache scenes rather than the packed site export. Current best read is that 0x0318 is CRUMORPH, not a generic placeholder cube: both extracted usecode corpora expose class 0x0318 as CRUMORPH, and the recovered equip body is a control-transfer pad that scans nearby NPCs for a local-QLo actor-key match before bracketing TRIGGER.slot_20. The strongest current viewer promotion is still grounded in Regret scene evidence, where authored same-QLo local CRUMORPH -> 0x04B1 matches are strong enough to expose, while 0x0366 remains NPC_ONLY, a hit-driven NPC-only trigger pad keyed by an internal actor field.
Recent map-renderer controller follow-up: docs/map_renderer/trigger-usecode-links.md and docs/map_renderer/editor-object-survey.md now tighten three more shared controller shapes. Current best read is that 0x00A2 is PANELEW, the east-west panel-switch counterpart to PANELNS; 0x03C1 is GENERATR, a destroyable generator/controller whose gotHit body immediately forwards into TRIGGER.slot_20 lane 0; and 0x04E7 is the same DEATHBOX class in both Remorse and Regret rather than a Remorse-only crosswalk. The map viewer now has enough evidence to label those shapes directly, open PANELEW::use / GENERATR::gotHit, and expose cautious same-QLo cmd-link arrows for PANELEW and GENERATR.
Recent actor-key follow-up: the same map-renderer notes now make the current blocker explicit instead of leaving it as an implied missing export. Current best read is that the hidden actor-side value behind CRUMORPH / NPC_ONLY is mutable actor field 0x63, not a stable DTABLE row: sampled Regret DTABLE rows still read as zero at record byte 0x63, while recovered TRIGGER.slot_29 / slot_2B lanes can rewrite actor field 0x63 on nearby matched NPCs after load. The same pass also widens the sibling-family set that uses this mechanism: WATCHNS / WATCHEW, THRMBCKN / THRMBCKE, and SURCAMNS / SURCAMEW all compare controller-local bytes against actor field 0x63 in recovered lanes, so the viewer now documents a broader actor-key controller family while still withholding speculative actor-target arrows.
Recent verified PSX pre-alpha batch: docs/psx/prealpha.md now records a focused Ghidra pass on /psx/prealpha/SLUS_002.68 plus a disc-tree comparison against the released PlayStation Crusader: No Remorse build. Current best read is that this pre-pre alpha still looks much more like a trimmed early No Remorse PSX branch than a clearly rebranded Crusader 2 executable: it still carries direct Crusader: No Remorse save/quit text, the renamed wdl_resource_bundle_load_by_index still embeds the full retail \LSET1\L through \LSET7\L prefix table and the same 10/20/30/40/50/60 threshold ladder, and the mission/passcode UI scaffolding is still present with the same visible 15 mission briefing strings and consonant/digit passcode alphabet. The main concrete differences in this batch are the heavily reduced shipped content (3 level bundles, 1 XA, no .STR movies) and the surviving architectural leftovers that no longer match the current disc literally, especially the missing-file \AUDIO\TALK1.XA;1 path and the LoadExec helper for MENU.EXE / ENGINE.EXE / PSX.EXE.
Recent verified PSX executable batch: docs/psx/psx.md now records a focused Ghidra pass on SLUS_002.68 for mission/map inventory, passcode handling, and catalog text. Current best read is that the PSX loader hardcodes seven \LSETn\L folder prefixes and the extracted disc ships 62 level bundles (L0..L58, L62..L64) with a real gap at L59..L61, while the executable still exposes only 15 plain-text Mission Briefing ^Mission N strings. The same pass closes the visible passcode-generation side too: mission-complete flow synthesizes 4-character passcodes from the alphabet BCDFGHJKLMNPQRSTVWXZ0123456789, and the executable preserves direct ammo/item/weapon name tables. The hidden password-screen cheat codes remain less direct: public PSX references point to XXXX and L0SR/L0SER, but those values are not stored as plain ASCII in SLUS_002.68, so the compare path still looks numeric or transformed rather than table-driven.
Recent verified Japanese-build batch: docs/jp-remorse-windows9x-investigation.md now records a focused live-Ghidra investigation of /ja/CRUSADER.EXE around the claim that the Japanese release runs natively on Windows 95 / Windows 9x instead of requiring a DOS boot path. Current best static-analysis read is strongly in favor: the JP executable is a flat Win32 image with PE-style sections, a Windows import table, native window creation, DirectDraw/DirectSound initialization, registry-backed config under Software\Electronic Arts\Crusader: No Remorse\J1.21, and a meaningful GetVersion-based Win9x compatibility branch that changes TLS allocation behavior when the classic Win9x high bit is set. The only remaining uncertainty is practical deployment rather than architecture: this pass did not runtime-test on real Win95 or prove which DirectX/runtime prerequisites are required.
Recent verified Japanese-build follow-up: docs/jp-remorse-cheats-and-launch-params.md now records a focused pass on the surviving cheat/debug and startup-argument lanes in /ja/CRUSADER.EXE. Current best read is that the JP Win32 build kept real executable cheat/debug machinery, not just leftover strings: -laurie is still a special parser case, the hidden JASSICA16 sequence matcher still toggles the cheat-active state with live Cheats are now active/inactive. messages, the option-key handler still contains the immortality toggle path, and the command-line parser still executes live handlers for -debug, -u <arg>, -warp <mission>, -skill <n>, -mapoff <delta>, -egg <id>, and -demo. The same pass also narrows one important difference from older DOS-side notes: the JP Win32 parser has not yet been proven to support positional -warp <mission> <x> <y> <z> consumption, so that form should not currently be assumed for this build.
Recent verified localized-build batch: docs/spanish-cheat-differences.md now records a tighter live-Ghidra comparison against /es/CRUSADER.EXE for the known cheat/debug control areas. Current best read is now narrower than the earlier "moved matcher" theory: the Spanish executable still preserves the same broad cheat/debug framework as the English build with relocated addresses rather than different behavior, but it does not preserve the English jassica16 table as the same static data object and this pass also failed to recover any replacement compiled matcher or any translated ~ cheat-latch toggle. The -laurie parser still sets the broad cheat/debug gate (1478:0910), the gameplay-input gate still exists at 1478:0927, and Hack Mover still toggles through 13e8:24a5; but the old English-side slot at 1478:2833 now contains pointer-like words, the old English immortality-string slots at 1478:2850/2866 are also repurposed as non-string data in Spanish, 1478:0910 has only the -laurie write at 1050:0985, 1478:5fb3 only has the Laurie-hint helper writes at 13e8:0071/0077, World_HandleKeyboardInput does not expose a recovered 0x7e / tilde branch, and 1478:8ad6 still has no recovered writer even though Hack Mover checks it. The new keyboard-side conclusion is stronger too: 1478:5fb3 does not act like a live positive enable latch in Spanish, because every recovered consumer requires it to be zero and the Laurie-hint helper pulses it back to zero immediately, while the nearby 8ad7/8ad8/8ad9 runtime-state writes still do not explain 8ad6. The Hack Mover runtime chain is also tighter now: 1478:5fb2 is the actual on/off toggle, 13e8:0ef9 / 13e8:0f77 clear it, 13e8:282f is the adjacent runtime helper using 1478:8ad9, and 13e8:2f0e / 13e8:3009 bracket the active drag state via 1478:8ac0, 1478:8acc, and 1478:8ace. Current safest localized-build read is therefore -laurie is the only recovered positive enabler for the surviving broad Spanish cheat/debug family; no replacement hidden matcher, no runtime keyboard-latch bootstrap, and no direct Spanish F10 cheat branch have been recovered, with the remaining open question narrowed to whether 1478:8ad6 is written through an analysis-dark path or is just a dead leftover gate.
Recent startup fixed-map patch batch: docs/startup-map-patch-file.md now records the current evidence-backed read of the retail Using map patch file. startup line. Current best read is that Init_Everything at 1048:039b prints that line only if static\fixed.dat exists, and the later fixed-map loader path treats that file as a preferred alternate FIXED.DAT source by loading it into DAT_1478_1064 and choosing that handle over the base archive handle when present. The safest current wording is therefore alternate fixed-map archive selected at startup, not the -u usecode override and not a proven per-record merge overlay.
Recent Remorse NPC class-lift batch: new note docs/npc-action-process-class-layout.md now records the first owner-first lift of the bounded seg033 NPC AI process family in live CRUSADER.EXE. Current best read is that this lane now has real class ownership rather than only flat function names: Remorse::NPCActionProcess owns the shared create/destroy/no-op surface, Remorse::{StandProcess,PaceProcess,SurrenderProcess,GuardProcess,LoiterProcess} now own their direct create/run/destroy methods, and the guard/loiter-only helper NPC_DoRandomIdleAnimTwiceIfNotBusy remains intentionally outside those class owners until stronger single-class ownership evidence appears. The remaining open work in this lane is now datatype and slot semantics, not basic object identity.
New roadmap note: docs/function-knowledge-roadmap.md now turns the current decompilation state into a concrete path toward broad function coverage, with explicit completion criteria, lane priorities, and batch rules. The same note now also records the latest applied process-family rename work: 1100:0437 = SurrenderProcess_Destroy, 1100:0913 = NPC_DoRandomIdleAnimTwiceIfNotBusy, 1100:0d3e = LoiterProcess_VtableSlot10DispatchByShapeIfAlive, 1100:0fe8 = PaceProcess_Destroy, 1100:0f95 = GuardProcess_Destroy, 1100:0f47 = LoiterProcess_Destroy, 1100:1036 = StandProcess_Destroy, 1100:1084 = NPCActionProcess_RunNoop, 1100:1089 = NPCActionProcess_Destroy, 1100:0fe3 = NPCActionProcess_VtableSlot10Noop, 1128:1e14 = CruHealer_Destroy, 1128:1fbe = BatteryChargerProcess_Destroy, 1128:22ca = DeathSilenceProcess_Destroy, 1110:0f19 = PathfinderProcess_Destroy, 1090:0aaf = TeleporterProcess_Destroy, 1090:0a60 = EggHatcherProcess_Destroy, 1020:087e = MapJumpProcess_Destroy, 1028:06bd = FadeProcess1_Destroy, 1030:03cc = AnimProcess_Destroy, 1058:08fc = SnapProcess_Destroy, 10a0:4437 = ItemProcess_Destroy, 1138:0819 = SuperSpriteProcess_Destroy, 1150:32d3 = OneFrameDelayProc_Destroy, 1180:1e0a = CameraProcess_Destroy, 11b8:0293 = KeyDaemonProcess_Destroy, 11b8:04c5 = KeyboardProcess_Destroy, 11c0:06df = AccWaitProcess_Destroy, 11c0:0748 = SystemTimerProcess_RunNoop, 11c0:074d = SystemTimerProcess_Destroy, 11c8:03fd = BiosProcess_Destroy, 13b8:012e = CustomWaitProcess_Destroy, 1430:0363 = DumbTimerProcess_Destroy, 1438:0557 = CycleProcess_Destroy, 1440:0f67 = FadeProcAlt_Destroy, 1468:4322 = MyTimerProcess_Destroy, 1468:0494 = VideoPlayer_Destroy, 1468:03e3 = VideoPlayer_InitializePlayback, 1468:2f7c = VideoPlayer_OpenMediaFiles, 1468:32cb = VideoPlayer_AllocPlaybackBuffers, 1468:3904 = VideoPlayer_OpenMoviListAndPrimeStreams, 1468:0483 = VideoPlayer_StopAndDestroyWrapper, 1468:431d = VideoPlayerProcess_VtableSlot11Noop, 1468:001a = File_Exists, 1468:03b4 = VideoPlayer_FormatErrorMessage, 1468:17b0 = VideoPlayer_AdvanceChunkCursor, 1468:1d3d = VideoPlayer_AdvanceChunkCursorWrapper, 1468:1ef7 = VideoPlayer_LoadAudioChunk, 1468:1929 = VideoPlayer_LoadVideoChunk, 1468:1a92 = VideoPlayer_BlitDecodedFrame, 1030:0428 = GameTimeProcess_Destroy, 1030:03c7 = AnimProcess_RunNoop, 1048:0d3e = Process1048_0000_RunNoop, 1048:0d43 = Process1048_0000_Destroy, 1050:051f = SavegameSlot_GetLabelPtr, 1050:0532 = SavegameSlot_SetLabel, 1050:057e = File_CloseAndMaybeFree, 10c0:00b9 = MapJumpProcess_VtableSlot10AdvanceItemFind, 10e8:4192 = AnimPrimitiveProcessSomethingElse_Destroy, 10f8:0120 = ItemScript_AppendBytes, 10f8:0161 = ItemTypeflagRecord_ResetDefaults, 1150:2f20 = AnimPrimitiveProcessFamily_VtableSlot11CallSlot3, 1188:0057 = Process1188_0000_RunOnTimerDelta, 1188:0979 = Process1188_0000_Destroy, 11c0:0483 = WaitProcessFamily_VtableSlot10DispatchByPair, 11c0:0691 = WaitProcess_Destroy, 11c0:02bf = AccWaitProcess_VtableSlot10DispatchByAnimation, 1138:0444 = SpriteProcess_Destroy, 12e0:0151 = ASS_StoreInitCallbackState, 13c8:03f5 = MainMenu_Destroy, 13c8:04ee = MainMenu_DrawCornerDecorations, 13c8:06a4 = MainMenu_HandleButtonClick, 13c8:06cd = MainMenu_HandleKey, 13c8:082e = MainMenu_ActivateSelection, 13c8:0ce7 = MainMenuOptionButtonGump_Create, 13c8:0dc0 = MainMenuOptionButtonGump_HandlePointerEvent, 13c8:0e2d = MainMenuOptionButtonGump_SelectPeer, 13c8:0e94 = MainMenuOptionButtonGump_Draw, 13c8:0ece = MainMenuOptionsPanel_Create, 13d0:0000 = SavegameNameField_MapInputChar, 13d0:0226 = SavegameMenu_Destroy, 13d0:02cb = SavegameMenu_HandleKey, 13d0:03dd = SavegameMenu_HandleSlotAction, 13d0:058c = SavegameSlot_DrawCornerDecorations, 13d0:074e = SavegameSlotGump_Create, 13d0:0841 = SavegameSlotGump_Destroy, 13d0:08a8 = SavegameNameField_HandleKey, 13d0:0b0a = SavegameSlot_HandleClick, 13d0:0b89 = SavegameSlot_BeginEditOrActivate, 13d0:0cd5 = SavegameNameField_Draw, 13d0:0e18 = SavegameSlot_Select, 11c8:03c9 = BiosProcess_VtableSlot10DosRealFarCall, 1108:2259 = AttackProcess_VtableSlot10DispatchByClip, 1030:0183 = AnimProcess_VtableSlot10DispatchByPort, 1300:0d4e = BaseCameraProcess_VtableSlot10SetViewportRect, 1300:0d76 = BaseCameraProcess_VtableSlot11FreeBuffer, 13b8:021a = CustomWaitProcess_VtableSlot11ArmAndRun, 1440:03a0 = FadeProcess2_VtableSlot10BlendTowardTargetPalette, 1448:08fd = FlicPlayProcess_Destroy, 1448:3290 = FlicWaitProcess_Destroy, 11f8:00a4 = MusicPlayerProcess_RunNoop, 11f8:035e = MusicPlayerProcess_Destroy, 11f8:028e = Music_RestorePreviousTrackFromStack, 11f8:02bf = Music_LoadStateAndReplayCurrentTrack, 11f8:0311 = Music_SaveState, 12e0:0267 = AssProcess_Destroy, 1448:00eb = FlicWaitProcess_VtableSlot10TickAndMaybeAdvance, 11f8:00a9 = MusicPlayerProcess_VtableSlot10Noop, 12e0:00ed = AssProcess_VtableSlot5ClearCreatedFlag, 12e0:0105 = AssProcess_VtableSlot6SetCreatedFlag, 1020:08cd = Process_VtableSlot4Noop, 1020:08d2 = Process_VtableSlot8Noop, 1028:0724 = Process_VtableSlot9ReturnZero, 1468:0114 = MyTimerProcess_VtableSlot10IncrementCounterOnTick, 11f0:02b9 = StdIntHandlerProcess_Destroy, 12f8:0530 = GumpShared_DestroyNoop, 12f8:0544 = KeyboardInputHandler_DestroyNoop, 12f8:0553 = GumpShared_VtableSlot10Noop, 12f8:0578 = KeyboardInputHandler_VtableSlot10Noop, 12f8:057d = KeyboardInputHandler_VtableSlot11Noop, 1308:0616 = ButtonGump_Destroy, 13c0:04ee = KeypadGump_Destroy, 13c0:0a94 = KeypadButtonGump_Destroy, 13e8:3aae = HelpGump_Destroy, 13e8:3ba5 = HelpGump_RefreshPage, 13e8:3d53 = HelpGump_HandleAdvanceAction, 13e8:3d99 = HelpGump_HandleNavigationKey, 13e8:3ec8 = HelpGump_RunAmbientSfxTick, 13e8:3fd8 = RunCreditsProcess_Destroy, 13f8:01e9 = QuickSaveLoadExitGump_Destroy, 13f8:0510 = Gump13f80383_Destroy, 13f8:058c = Gump13f80383_Draw, 13f8:05c6 = Gump13f80383_VtableSlot10Noop, and 13f8:05cb = Gump13f80383_VtableSlot11Noop. That work is now live in Ghidra, and it also confirms the practical write-path rule for future work: read-only MCP analysis can stay on the live GUI session, while live write-capable scripts can land small verified rename/comment batches when the simpler edit-plan route refuses to commit.
Latest broad-sweep correction batch: several previously over-specific gump no-op names were generalized after direct table evidence showed they are shared across multiple gump families, not keyboard-only handlers. The live database now carries 12f8:0535 = GumpShared_VtableSlot3Noop, 12f8:0544 = GumpShared_VtableSlot7Noop, 12f8:0549 = GumpShared_VtableSlot8Noop, 12f8:054e = GumpShared_VtableSlot9Noop, 12f8:0578 = GumpShared_VtableSlot16Noop, and 12f8:057d = GumpShared_VtableSlot17Noop from direct g_helpGumpFnPtr / g_gump13f80383FnPtr slot reuse at 1478:6241 and 1478:6346.
Latest broad-sweep UI follow-up: the same UI-heavy lane is now tighter in three more local families without requiring deeper subsystem claims. The live database now carries 12f8:02e4 = GumpShared_DestroyCommon, 13f8:0237 = QuickSaveLoadExitGump_HandleChildButtonEvent, 13f8:0299 = QuickSaveLoadExitGump_HandleKey, 13f8:0349 = QuickSaveLoadExitGump_DrawLabel, 13f8:0383 = QuickSaveLoadExitGump_Create, 13c8:2f37 = MainMenuOptionsPanelButtonGump_Create, 13c8:2fca = MainMenuOptionsPanelButtonGump_DrawLabel, 13c8:3004 = MainMenuOptionsPanelButtonGump_Select, 13c8:3030 = MainMenuOptionsPanelButtonGump_Deselect, 13c8:1759 = MainMenuOptionsMenu_Destroy, 13c8:17c5 = MainMenuOptionsMenu_Create, 13c8:1e62 = MainMenuOptionsMenu_GetOptionRect, 13c8:2975 = MainMenuOptionsMenu_HandleChildButtonEvent, 13c8:29b3 = MainMenuOptionsMenu_HandleKey, 13c8:2b16 = MainMenuOptionsMenu_DrawTitle, and 13c8:2c56 = MainMenuOptionsMenuButtonGump_DrawLabel. Current best read is that 12f8:02e4 is the shared gump base destroy path used by multiple UI families, the 13f8: mini-cluster is the quick save/load/exit modal's constructor-plus-local input/draw surface, the 13c8:2f37..3030 cluster is the options-panel-specific button wrapper layered over the generic 1308: button-gump create path, and the separate 13c8:1759..2c56 lane is now clearly the main options-menu create/destroy/input surface because its teardown path saves the current options back to config before the shared gump cleanup.
Recent retail debugger-entry follow-up: docs/retail-debugger-entry-options.md now consolidates the hidden-debugger entry question with the newer live Ghidra evidence instead of leaving it split across the older -debug and patch-attempt notes. Current best read is now tighter in seven ways: first, fresh data-use recovery still finds reads but no writer for the debugger-state global at 1478:659c/659e; second, fresh decompiles of usecode_debugger_open_for_current_unit, usecode_debugger_open_modal, usecode_debugger_gump_create, and usecode_debugger_handle_event confirm that the debugger UI and event bundle are real but only meaningful after a valid break-state object/gump already exists; third, the retail seg109 naming backlog is now partly landed live in CRUSADER.EXE; fourth, the follow-up live pass sharpened the retail child-pane split by identifying 13a0:16ee/1791/193f as a watch-pane create/draw/click trio and 13a0:0ae8 as the source-pane line-state initializer from break-state current-line data; fifth, the latest MCP decompiles now make retail's surviving debugger surface explicit once the gump exists, including real file-open, run/step, go-to-line, watch, inspect, change-global, search, breakpoint-display, and pointer-to-source navigation logic; sixth, the same pass now closes the retail source-buffer lane far enough to describe the real file-ingestion path end to end, from source-buffer allocation/open through whole-file text load, in-place line splitting, and shared line-pointer lookup; and seventh, the decisive retail gap versus Regret remains unchanged and now better evidenced live: no caller to 1408:0000 Create, no caller to the two open wrappers, and retail callback slot 0 still lands on the no-op 1408:046f stub instead of a live frontend callback.
Recent No Regret debugger follow-up: docs/regret-hidden-debugger-investigation.md now also records the debugger-side cleanup pass after the first source-loader/runtime split, the final deep caller-recovery closure in the upstream Regret VM lane, and the first practical seeding model. The live REGRET.EXE database now has names and comments not just for the breakpoint/current-entry helpers, but also for the source-pane constructor/pointer/draw/viewport methods, the full source-buffer create/load/split/destroy chain, the interpreter saved-farptr helpers at 13f0:0000/003c, the interpreter-context create/init pair at 13f0:00e8/0244, and the shared slot-chunk accessor at 13f8:1d72. The practical model is tighter in four ways: first, the broader usecode_debugger_handle_event map now shows explicit line-search, goto-line, and breakpoint-clear interactions over that loaded source buffer; second, the compiled-usecode question is no longer ambiguous, because retail Remorse already carries parsed LINE_NUMBER ops while Regret currently does not; third, the remaining debugger-seeding uncertainty is now narrowly bounded inside the already-identified interpreter dispatcher path rather than in some still-unmapped Regret-side subsystem; fourth, the seeding record itself now reads as a serialization of existing live interpreter pointers, which means a future bring-up can probably reuse in-process VM state with a small patch instead of depending on any hypothetical external preprocessor. It would improve source correlation, but even a future Regret-focused line-number injector would still not replace the missing interpreter-seeded current-entry stack needed for stable RUN / step behavior.
Recent JP hidden-debugger follow-up: docs/jp-remorse-hidden-debugger-investigation.md now records the first debugger-focused comparison pass on /ja/CRUSADER.EXE. Current best read is narrower than the No Regret result but still useful: the JP Win32 build clearly retains broad executable cheat/debug features, but this pass did not recover the classic hidden usecode-debugger UI signature bundle. Live byte searches on the active JP image found known positive-control strings like JASSICA16, Immortality enabled., and Cheats are now active., but returned no hits for the debugger-only strings Goto Line, Watch what?, Inspect what?, Global name, Search for, FILE NOT FOUND, Unable to open this file, Nothing to find, Not found, and Done. The practical outcome is that JP currently strengthens the broad cheat/debug support survived in Win32 story, but not the JP preserved the missing retail debugger bootstrap theory; No Regret remains the stronger sibling-build anchor for the hidden-debugger unlock problem.
Recent verified batch: docs/retail-debug-arg.md now records the live NE proof that retail CRUSADER.EXE still recognizes and executes a real -debug command-line branch. That branch prints Debugging mode ON., sets g_debugMsgLevel at 1478:87e0, and toggles two debug globals at 1478:0845/0859. The later sink pass also closes the text-output target more tightly: ProbablyPrintDebugMessage formats through the static stdio-style table at 1478:6c32..6c81 and writes to the handle-1 entry at 1478:6c46, so the non-video side is ordinary DOS stdout gated by the debug threshold, plus the already-confirmed AVI timing overlay. Current best read remains surviving debug-output / instrumentation switch, not the missing bootstrap for the hidden seg109/seg1408 usecode debugger. The same batch also leaves the earlier -laurie and 0x659c/659e debugger-state conclusions intact: -debug is a separate switch and is not currently evidenced as constructing the hidden usecode-debugger break-state object.
Recent tooling batch: docs/map-rendering.md now starts a dedicated offline map-rendering lane. tools/render_crusader_map.py can load FIXED.DAT, expand GLOB.FLX, decode the required SHAPES.FLX entries with Crusader frame headers, apply GAMEPAL.PAL, and write a first-pass PNG, with a --fixed-dat override so the same pipeline can be pointed at either game's map file. The current renderer is intentionally limited to fixed-map content and a simple deterministic painter rather than the full Pentagram/ScummVM dependency sorter, and the current workspace caveat is that STATIC_REGRET still lacks a copied FIXED.DAT, so No Regret rendering needs that file supplied explicitly.
Recent map/editor visibility batch: docs/editor-object-visibility.md now records a focused live-Ghidra pass on whether retail CRUSADER.EXE explicitly hides editor-only map objects and whether any built-in switch re-enables them. The current best read is now tighter than the first pass: Item_PaintSprite at 1198:02e4 does contain a real downstream flags2 & 1 (SI_EDITOR) early-out, but the active world-item renderer also has an upstream controlling skip at 1180:0951..095c that filters editor-tagged shapes before draw-node allocation. That corrected render-path model explains why the first executable patch attempt, which only flipped the downstream draw-time branch, produced no visible change in-game. The same note still closes the negative side of the question more tightly: no recovered retail -debug, cheat/debug hotkey, Laurie/usecode-debugger path, or 0x410 event behavior currently reaches either gate or exposes a show editor items state. The closest confirmed toggle remains ScummVM's own _showEditorItems debugger command, which is an engine-added reimplementation feature rather than evidence of a retail built-in toggle.
Recent map-viewer trigger batch: docs/map_renderer/trigger-usecode-links.md now records the evidence-backed editor/controller-object -> USECODE mapping used by the viewer when opening readable pseudocode from pinned tooltips. Current best read is that the stable viewer targets are BOX_EW, PANELNS, CARD_NS, and SPANEL -> use; FASTSKIL -> enterFastArea; SKILLBOX, EVENT, ALARMHAT, and ALRMTRIG -> equip; TRIGPAD and NPC_ONLY -> gotHit; and the 0x04B1 cmd helper itself -> TRIGGER.slot_20, the shared high-slot fan-out lane that nearby controller families keep spawning.
Recent usecode-event vocabulary note: docs/usecode-event-slots-combine-cast-fast-area.md now records a focused evidence pass on the suspicious inherited combine / cast slot names and the current best meaning of fast area. Current best read is that shipped Crusader usecode exposes no recovered slot_0C handler bodies at all, while slot_11 cast is real but acts as a scripted activation/dispatch lane rather than a classic spell system. The same note also tightens enterFastArea / leaveFastArea to authored proximity/activation region callbacks instead of the older speed-zone-style shorthand.
Recent startup/map-selection batch: docs/first-mission-map-selection.md now records the live proof that fresh-game map choice is code-selected rather than read from CRUSADER.CFG or another external mission-mapping file. For CRUSADER.EXE, the normal fresh-game path still hardcodes map 1, egg 0x1e inside Game_Start. For REGRET.EXE, the same values are hardcoded twice: once in an early Game_Start selector and again in the later FUN_1030_032d mission-start path that actually controls a real new game. The same note also captures the separate debug -warp mission path: it indexes a small executable-embedded mission-to-map word table at 1478:0488 (0,1,3,5,...,0x1d,0x28) and then applies -mapoff, while the actual map contents remain external in FIXED.DAT.
New REGRET startup-flow batch: docs/regret-game-start.md now documents the live REGRET.EXE Game_Start neighborhood more thoroughly. That note promotes HandleCommandlineArgs, Game_RunNewGameFlow, Game_DrawCenteredStartupSplash, Game_EnterFrontendMenuViewport, and Game_RestoreGameplayViewport in the live database, records the startup-state globals used by the new-game and -warp lanes, and explains the current best reason map 1 is set twice in No Regret: two separate live startup entry paths still own their own teleporter literals instead of sharing one final startup-map source.
New command-line argument batch: docs/command-line-parameters.md now consolidates the currently recovered startup/debug argument set across the retail Crusader executables. The key new closure is the actual direct-warp syntax in REGRET.EXE: -warp <mission> [x y z] rather than separate -x/-y/-z switches. The same note also records the now-proven precedence rule that nonnegative -egg overrides beat the X/Y/Z teleport path, the practical parameter-only route into eggless maps (-warp <mission> <x> <y> <z> plus -mapoff, with -egg omitted), and the current best read of -setver as a displayed version/build-string override rather than a gameplay compatibility switch.
Follow-up No Remorse cross-check: the same command-line note and docs/first-mission-map-selection.md now record the matching live CRUSADER.EXE proof. HandleCommandlineArgs at 1048:0adc uses the same positional -warp <mission> [x y z] syntax as Regret, and Game_Start at 1020:029e / 1020:02d0 applies the same precedence rule where nonnegative -egg overrides beat the direct-coordinate NPC_Teleport path.
Latest warp-table follow-up: the same docs/first-mission-map-selection.md and docs/regret-game-start.md notes now close the missing No Regret table details directly. Live REGRET.EXE Game_RunNewGameFlow indexes the -warp mission base-map table at 1480:075c, and retail byte checks now show the same 17-word payload as No Remorse: 0,1,3,5,7,9,11,13,15,17,19,21,23,25,27,29,40, followed by a 0,0 terminator. The public renderer project now also has a dedicated extractor that writes both retail tables into Crusader_Decomp_Public/map_renderer/.cache/mission-map-data.generated.json for scene-metadata use.
Latest command-line follow-up: that same docs/command-line-parameters.md note now closes the retail non-Japanese -u lane as well. In live CRUSADER.EXE, the parser case at 1048:0a46 copies the following token into 1478:065a, and the newly named startup_apply_u_override_if_present at 1420:0cdf consumes that buffer to load an alternate usecode/EUSECODE source into 1478:6611/6613 before rebuilding the cumulative slot-base words. Current best read is therefore real startup usecode override, not JP-only feature and not dead parser-table residue. The same follow-up also means the older consolidated -setver note is now weaker on the CRUSADER side and should be treated as needing a direct retail re-close.
Latest -u deep dive: new note docs/usecode-startup-override.md now follows that retail override into the live usecode runtime itself. Current best read is that -u replaces the single live usecode root at 1478:6611/6613 rather than adding a parallel overlay. The same root is later consumed by Usecode_ItemCallEvent, UsecodeProcess_CreateProcess, Interpreter_NextUsecodeOp, and Item_GetDamaged, so the override reaches ordinary scripted gameplay behavior, not just a startup-only side lane. Current safest tooling implication is runtime replacement for the existing Crusader usecode VM, not arbitrary native plug-in system.
Latest -u token-shape follow-up: the same docs/usecode-startup-override.md note now tightens the argument semantics materially. In live retail CRUSADER.EXE, startup_apply_u_override_if_present does not pass the copied argv token through as an arbitrary final filename. It loads the mutable filename template eusecode.flx from 1478:07a0 via the far pointer at 1478:06d6/06d8, forces the first byte to 'e', and calls Filespec_GetFullPath(0, s_usecode, "eusecode.flx", 0). Current safest read is therefore path/root override for the standard EUSECODE archive family, not free-form arbitrary filename switch. The same note now also separates the stock-path status more cleanly: the raw-side VM bootstrap is strongly cross-referenced, but the exact live-NE writer that seeds 1478:6611/6613 without -u is still not directly closed.
Latest -u practical follow-up: that same docs/usecode-startup-override.md and docs/command-line-parameters.md notes now make the immediate user-facing consequence explicit. Failed attempts like -u USECODE/FLICTEST.FLX and -u FLICTEST.FLX fit the live helper badly because retail No Remorse still appends the fixed filename template EUSECODE.FLX; the copied argv token is only the path/root component. Current safest experiment shape is therefore directory passed to -u, complete replacement archive named EUSECODE.FLX inside that directory, not free-form archive basename override.
Latest -u loader-layout follow-up: the same docs/usecode-startup-override.md note now records the direct constructor/loader pair behind the override in the live NE session. 1420:1499 is now renamed entity_vm_runtime_create and currently reads as a 0x1319-byte runtime-object constructor with a 0x1300-byte front region that behaves like 0x80 stride-0x26 slot/runtime records plus tail metadata at 0x1300..0x1318. 1430:0000 is now renamed entity_vm_runtime_owner_resource_create and currently reads as the compact 0x14-byte file-backed helper allocated from the resolved eusecode.flx path and attached to the runtime object at +0x1315/+0x1317.
Latest doc-reconciliation batch: docs/ne-segment1.md now has a combined hidden-debugger component table that explicitly separates the seg109/raw-reference UI wrappers (000b:9a86, 000b:9c0d, 000b:b3b1, 000b:b62c, 000b:2882) from the live seg1408 breakpoint-state helpers (1408:0000, 1408:0053, 1408:00dd, 1408:029e, 1408:03b0, 1408:03f7, 1408:0419, 1408:0432, 1408:0444) and the interpreter hook at 1418:04aa..04b5. Current best read remains two connected layers of one hidden usecode debugger, not conflicting address claims for the same function family.
Latest hidden-debugger floor pass: docs/retail-debugger-patch-attempts.md now records a fresh live-Ghidra constraint check on the smallest viable retail unlock patch. Current best read is tighter than the earlier "there must be one tiny jump" idea: retail still has no recovered writer for 1478:659c/659e, the constructor still seeds only the inert shared callbacks 1478:65ab -> 1408:046f / 1478:65af -> 1408:0474, and the interpreter-side gate at 1418:049e..04b5 only checks for a non-null debugger object before handing off to 1408:0053. That makes the current O/P interpreter-callsite-retarget family the smallest structurally defensible executable patch shape so far, because smaller one-site ideas lose either object bootstrap, one-shot deferred gating, or wrapper-argument sanitation.
Follow-up cheat-key correction pass: docs/ne-segment1.md now also records a live NE cleanup of several folklore keyboard-cheat claims. ~ is a real runtime cheat-latch toggle at 13e8:203d, Ctrl+C is wrong for this build and should be Ctrl+L for the coordinate popup at 13e8:255e, and the third F7-family overlay really does exist as a separate Ctrl+F7 path at 13e8:1a20 alongside the other two cheat-gated F7 overlay toggles.
That same note now also separates ~ from jassica16 more cleanly: jassica16 is the raw scan-code unlock path that toggles both 1478:0844 and 1478:6045 and sets the extra post-sequence latch 1478:8c52, while ~ is only the later translated logical-0x7e hotkey that flips 1478:6045 after 1478:0844 is already on. The F7-family clarification is tighter too: Ctrl+F7 is best read as an egg-hatcher trigger-range overlay rather than a third generic background grid.
The same docs/ne-segment1.md note now also has the first consolidated cheat/debug key matrix for the live NE target, including which paths need the broader Laurie/debug master gate (1478:0844), which ones need the full keyboard-cheat latch (1478:6045), and which ones depend on the extra post-jassica16 latch (1478:8c52). That pass also expands the egg-hatcher explanation: Ctrl+F7 is now documented as a live EggHatcherProcess range visualizer, with practical guidance on where to look for egg-trigger regions in gameplay.
Latest F7 overlay follow-up: new note docs/f7-overlays.md now separates the three F7-family debug overlays more rigorously at the geometry level. The main correction is on Alt+F7: instead of treating it as broad egg coverage, the current best read now follows the live SnapProcess path directly, where Snap_AddSnapEgg is only reached for shape 0x04fe and Snap_GetSnapEggRange derives the overlay rectangle from that item's QHi, mapNum, and npcNum bytes. The same note also clarifies the viewer-facing rule for plain F7: use an origin-aligned infinite 0x200-unit world lattice across the visible viewport, not a screen-centered patch.
Documentation Structure
| File | Contents |
|---|---|
| docs/overview.md | Binary overview, installed copy findings, address space layout, NE fixup placeholder, segment map, NE import details, next steps |
| docs/combat-dat.md | COMBAT.DAT archive layout, live CRUSADER.EXE tactic-field integration, shipped opcode subset, and a human-readable catalog of all 14 tactic records |
| docs/phar-lap-extender.md | DOS extender architecture, named functions (entry, loading, memory, I/O, interrupts), key string references |
| docs/ne-segment1.md | NE Segment 1 full analysis: cursor, input, entity system, shot lifecycle, combat, weapons, AI, player/HUD, destruction, entity constants, vtable index, cheat system |
| docs/f7-overlays.md | Focused note on the three cheat-gated F7 debug overlays: toggle sites, live consumers, recovered geometry math, what each overlay represents, and the current viewer-safe reproduction rules |
| docs/jp-remorse-windows9x-investigation.md | Focused note on the Japanese /ja/CRUSADER.EXE Windows-native claim: PE/Win32 image evidence, Win32 windowing, DirectDraw/DirectSound, registry config under J1.21, IME/DBCS clues, and the GetVersion-driven Win9x compatibility branch |
| docs/jp-remorse-cheats-and-launch-params.md | Focused note on surviving JP /ja/CRUSADER.EXE cheat/debug and startup-argument lanes: -laurie, JASSICA16, immortality, the recovered Win32 parser table, the live -u usecode override, and the current caution that JP -warp is only directly proven in mission-only form |
| docs/jp-remorse-hidden-debugger-investigation.md | Focused first pass on whether the JP Win32 build kept the classic hidden usecode debugger: positive-control hits for surviving cheat/debug strings, but no live hits for the debugger-only UI string bundle (Goto Line, Watch what?, Inspect what?, Global name, Search for, FILE NOT FOUND, Unable to open this file, Nothing to find, Not found, Done) |
| docs/spanish-cheat-differences.md | Focused comparison note for /es/CRUSADER.EXE versus the English build's known cheat/debug lanes: -laurie, broad cheat gate, gameplay-input gate, low-level keyboard latch, Ctrl+Q, Hack Mover, and the current status of the unresolved secret sequence |
| docs/raw-porting-progress.md | seg091 RNG, 0x4588 callback lifecycle batches 1-6, 0007 gameplay helper batches, snap_entity_to_ground, AI sweep, animation/range/command globals, seg043 boundary recovery |
| docs/raw-000e.md | 000e parser helper cluster (record table init/parse/dispatch), 000e RIFF/animation cluster (animation object field map, RIFF format, constructor variants) |
| docs/raw-0007-rendering.md | Draw list node format and functions, world-to-screen isometric, tile visibility system, scroll/camera functions, scroll region table, save slot system, string/memory utilities, coordinate transform deep analysis |
| docs/raw-0008-000c.md | 0008 dispatch helpers (init, pair-sync, flag helpers, word-list, gate-callbacks) and 000c state machine (tick dispatch, flag guards, palette fade, mini-VM, cursor nav) |
| docs/raw-000a-000d.md | 000d proximity/visibility buckets, 000a tracked handles, cache manager, init/shutdown, seg082 allocator, seg137/138 palette helpers, seg004/005 startup, 0x4588 object-role evidence, 000d VM owner/resource loader follow-up |
| docs/far-call-targets.md | Top-104 most-called far-call targets (Tiers 1-5, ranks 1-104), supporting functions discovered, analysis gaps and seg043 reconciliation |
| docs/function-knowledge-roadmap.md | Concrete path to broad function coverage: completion criteria, prioritized workstreams, write-path discipline, staged rename batches, and immediate next steps |
| docs/crusader-disasm-reference.md | Local auxiliary disassembly corpus at K:/ghidra/crusader-disasm: handwritten notes, shape tables, map dumps, opcode lists, intrinsic/function dumps, and the safe reuse rules for porting into CRUSADER.EXE |
| docs/ne-hole-filling-priorities.md | Ranked CRUSADER.EXE hole-filling tracker: NE-side unclear lanes, the verified raw-side knowledge that can close them, and the recommended order for old-to-new porting passes |
| docs/retail-debugger-patch-attempts.md | Chronological log of retail CRUSADER.EXE debugger-unlock patch attempts, byte-level designs, runtime failures, root-cause findings, and the current live candidate |
| docs/retail-debugger-entry-options.md | Focused retail hidden-debugger entry analysis: fresh live-Ghidra proof that 1478:659c/659e still lacks a recovered writer, what the seg109 wrappers and gump constructor now prove about real reachability, why -u is the best low-risk experiment surface but still not a direct debugger bootstrap, and why No Regret / JP comparison is now the preferred next move |
| docs/regret-hidden-debugger-investigation.md | Focused live REGRET.EXE hidden-debugger comparison: recovered wrapper/gump/dispatcher equivalents, the seg13e0 break-state family, the seg13f0 interpreter-side consumer, the debugger global at 1480:712c/712e, and the compact bootstrap stub at 1398:0000 that writes that global |
| docs/retail-debug-arg.md | Focused note on the retail -debug command-line switch: live parser evidence, exact startup message, surviving globals, segment 1468 instrumentation path, and why it is currently separate from the hidden usecode debugger bootstrap |
| docs/startup-map-patch-file.md | Focused note on the retail Using map patch file. startup line: exact Init_Everything print gate, static\fixed.dat detection, the later ItemCache_InitAndLoadFixedDat archive load, and the current evidence that fixed-map reads prefer the alternate archive when present |
| docs/remorse-class-candidate-inventory.md | Evidence-backed inventory of the strongest current Remorse class families, with confidence, ctor/dtor/vtable/layout anchors, and recommended modeling order for later Ghidra class work |
| docs/remorse-class-lift-index.md | Central navigation note for the Remorse class-lift and C++-reconstruction prep lane, grouping the plan, candidate inventory, ABI notes, endpoint spec, and family-specific layout notes into one work order |
| docs/remorse-first-class-authoring-checklist.md | Operational checklist for the first real Ghidra/MCP class-authoring batch, including pilot-family order, authoring rules, and source-emission readiness gates |
| docs/remorse-cpp-decompilation-plan.md | Plan for shifting the current Remorse decompilation from flat C-like recovery toward evidence-backed C++ classes, typed object models, and an eventual recompilable source tree |
| docs/remorse-cpp-compatibility-header-draft.md | Draft contract for the future compatibility/support header that early Remorse C++ skeletons should target: exact-width aliases, packing markers, calling-convention placeholders, segmented-pointer helpers, and slot-order guardrails |
| docs/remorse-toolchain-fingerprint-evidence.md | Focused evidence note for the current toolchain story behind Remorse reconstruction: bound MZ -> NE structure, Phar Lap runtime, loader-patched far calls, and the current High-C-related runtime fingerprints |
| docs/ghidra-mcp-class-lifting-endpoint-spec.md | Draft endpoint surface for future GhidraMCP class-lifting work: namespace/class creation, symbol moves, struct and vtable authoring, this typing, and transactional class-layout application |
| docs/scummvm-crusader-reference.md | ScummVM Ultima8/Pentagram Crusader integration survey: USECODE/event tables, FLEX/resource formats, world/map loaders, HUD/media, and RE follow-up priorities |
| docs/pentagram-crusader-reference.md | Pentagram-source Crusader/U8 reference: direct Crusader USECODE parser and VM evidence, U8 usecode docs, runtime-confidence limits, and cross-checks against the ScummVM note |
| docs/map-rendering.md | Offline map-rendering lane: FIXED.DAT/GLOB.FLX/SHAPES.FLX/GAMEPAL.PAL format notes, current Python renderer, supported inputs, and fidelity gaps |
| docs/editor-object-visibility.md | Focused note on retail editor-only map object hiding: the live 1198:02e4 SI_EDITOR early-out in the normal item paint path, the lack of a recovered retail visibility toggle, and the ScummVM/Pentagram cross-check that treats show editor items as an engine-side debug feature |
| docs/entity-class-family-split.md | Focused working note on the large seg001 Entity lane: shared base-layout evidence, conservative split into projectile, debris, corpse/remnant, and adjacent non-entity families, and the recommended promotion order for later class lifting |
| docs/entity-dispatch-entry-class-layout.md | Focused working note for the EntityDispatchEntry family: base versus derived split, stable field groups, constructor and release surfaces, candidate method map, and conservative future Ghidra modeling order |
| docs/entity-vm-runtime-owner-resource-layout.md | Focused working note for the VM runtime lane: EntityVmRuntime, EntityVmOwnerResource, and EntityVmContext ownership, stable layout claims, masked-create helpers, and the safest current class-lift order |
| docs/npc-action-process-class-layout.md | Focused working note for the seg033 NPC AI process family: current owner-first class lift, direct per-family create/run/destroy ownership, the shared guard/loiter idle helper, and the remaining slot/datatype gaps |
| docs/presentation-callback-broker-layout.md | Focused working note for the 0x4588 callback-object lane: install/teardown lifecycle, global state cluster, provisional vtable slots, payload-pair evidence, and conservative class-lift guidance |
| docs/map_renderer/trigger-usecode-links.md | Evidence-backed map-viewer note for editor/controller shapes that now expose direct USECODE navigation, including the stable class/event targets and the special TRIGGER.slot_20 handling for 0x04B1 cmd helpers |
| docs/map_1_spawners_targeted_investigation.md | Focused map-1 note on suspicious 0x04D0 frame-paired spawners: decompressed-cache examples, the recovered MONSTER -> ITEM.slot_2D -> create NPC chain, QLo-based pairing, and the corrected mapNum bit 0x08 enter-area interpretation |
| docs/first-mission-map-selection.md | Focused note on fresh-game startup map selection: No Remorse Game_Start, No Regret's early and later mission-start selectors, the separate embedded -warp mission table, and the split between code-selected startup and external FIXED.DAT map content |
| docs/regret-game-start.md | Detailed REGRET.EXE startup-flow note: Game_Start, Game_RunNewGameFlow, newly named helpers, startup override globals, and the current best explanation for the duplicated map-1 selector |
| docs/remorse-rebuild-abi-notes.md | Working note for rebuild constraints: segmented-memory model, far-call provenance, runtime/toolchain evidence, ABI guardrails, and the split between original-style executable reconstruction and a behaviorally equivalent port |
| docs/command-line-parameters.md | Consolidated startup/debug argument reference for the retail Crusader executables: live retail -u usecode override, the current -setver caution, -debug, -asylum, -warp, -skill, -mapoff, -egg, -demo, the -laurie cross-reference, and the evidence-backed direct-coordinate warp syntax/limits |
| docs/psx/psx.md | PlayStation SLUS_002.68 and disc-resource note: boot/load layout, LSET/menu WDL structure, executable-backed map inventory, passcode alphabet/display path, recovered PSX ammo/item/weapon tables, and current unresolved enemy/password-compare gaps |
| docs/psx/jl-9-investigation.md | Focused PSX SLUS_002.68 note on the JL-9 weapon: recovered row/index evidence, the hidden transformed passcode branch that sets the debug gate, the bulk weapon-unlock helper's extra late channel, current art/level-placement status, and the remaining proof gaps |
| docs/psx/map-rendering.md | Detailed PlayStation map-rendering architecture note: LSET*.WDL storage model, constructor record layouts, runtime banks, state/variant/art bridge, stage-1 versus stage-2 render lanes, visible-list sorting, final primitive submission, and the current best recipe for reconstructing PSX maps in the viewer |
| docs/psx/art-binding-recovery.md | Focused PSX viewer recovery note: extraction-versus-render diagnosis, zero-block DAT_800758d8 constructor-family donor heuristics, measured fallback reduction, representative map stats, and the remaining outlier families/maps |
| docs/psx/prealpha.md | PlayStation pre-pre alpha /psx/prealpha/SLUS_002.68 comparison note: reduced disc inventory, retained retail-style LSET loader, surviving No Remorse branding, stale TALK1.XA and LoadExec leftovers, and the current read that this build is closer to an unfinished No Remorse PSX branch than to a visibly rebranded sequel executable |
| docs/sprite-node-class-layout.md | Focused working note for the SpriteNode family: current core layout, destructor and event-dispatch evidence, candidate virtual slots, and a conservative Ghidra modeling plan |
| docs/usecode-startup-override.md | Focused retail -u deep dive: startup call order, why the override looks like full live-root replacement rather than addition, which event/process/interpreter consumers use that root, and what that implies for future custom usecode experiments |
| docs/usecode-roundtrip-ir.md | ScummVM-to-binary USECODE cross-walk, owner-loaded class-layout and header/event-count reconciliation, conservative IR v0 plan, and the generated class-event/body-window outputs that now ground reversible _BOOT, SURCAM*, and environmental family decompile artifacts plus repeated-family regression checks |
| docs/usecode-event-slots-combine-cast-fast-area.md | Focused note on whether inherited event labels combine and cast are actually live in shipped Crusader usecode, plus the current evidence-backed explanation of enterFastArea / leaveFastArea as proximity-trigger callbacks |
| docs/usecode-pentagram-ghidra-path.md | Pentagram-derived Crusader USECODE parser plan, proof-of-concept workflow, canonical IR v1 goals, and the Ghidra-side annotation import path |
| docs/usecode-tooling-comparison.md | Comparison of Pentagram's converter/disassembler, the local crusader-disasm corpus/scripts, and the current workspace parser/pseudocode exporter, with emphasis on assumptions, strengths, and repo-specific differences |
| docs/usecode-tool-improvement-plan.md | Concrete next-step plan for the local USECODE parser/decompiler, distilled from the Pentagram and crusader-disasm comparison into prioritized parser, loop-decoding, intrinsic, trailer, corpus, and runtime-bridge upgrades |
| docs/usecode-jelyhack-analysis.md | Focused analysis of exported JELYHACK / JELYH2 pseudocode, the tiny shared use stub, and why the current best model remains referent anchor + neighboring event-bearing attachment |
| docs/usecode-equipment-system.md | Evidence-backed note on Crusader's surviving equip / unequip event system, including live compiled-side dispatcher proof, corpus-wide slot counts, actor/turret/environment examples, and the current best model of equip as a generalized inherited Ultima-style item event |
| docs/usecode-alarmhat-analysis.md | Focused analysis of exported ALARMHAT::equip, the nearby shape 0x04D0 equip loops, alarm-family comparisons, and the current gameplay-facing read of ALARMHAT as a local alarm-state driver |
| docs/usecode/flictest-investigation.md | Focused investigation of FLICTEST: class/body structure, Remorse versus Regret differences, confirmed caller scripts, and the stronger Regret finding that a hidden KEYPADNS VIDEO PLAYER route appears to have matching shipped placement evidence |
| docs/usecode/windsurf-regret-vs-remorse.md | Side-by-side comparison of WINDSURF in Regret and No Remorse, including shared slot behavior, helper-family drift, body-size differences, and the current best read of WINDSURF as a directional wind-force helper used by vent scripts |
| docs/removed_items.md | Evidence summary for suspicious removed item shapes in old No Remorse maps: grenade-family leftovers 0343/034E/034F/0350, the inventory-labeled 0548 Invalid item, and unresolved non-pickup shapes 0110/0112 |