commit b96aaf48c262c184eed613a87eb48f9878244fd1 Author: Marco Date: Thu Mar 19 16:39:57 2026 +0100 First commit diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..7c48093 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,4 @@ +# Git LFS tracking for CRUSADER executables +/CRUSADER.EXE filter=lfs diff=lfs merge=lfs -text +/CRUSADER_NE.EXE filter=lfs diff=lfs merge=lfs -text +/CRUSADER_NE_WRAPPED.EXE filter=lfs diff=lfs merge=lfs -text diff --git a/.github/instructions/ghidra.instructions.md b/.github/instructions/ghidra.instructions.md new file mode 100644 index 0000000..061c025 --- /dev/null +++ b/.github/instructions/ghidra.instructions.md @@ -0,0 +1,69 @@ +--- +applyTo: "**" +--- + +# Crusader Ghidra Workflow + +- Active target is the raw full-EXE Ghidra program `CRUSADER-RAW.EXE` unless explicitly stated otherwise. +- Use Ghidra MCP tools for analysis, decompilation, renaming, comments, and xref work. +- Keep analysis batches small: prefer 1-5 functions, labels, or comments at a time. +- Avoid speculative renames. Prefer names that are supported by one of these: + - verified raw mapping from standalone segment work + - direct string evidence + - clear call/field behavior in decompiler or disassembly + - xref relationships to already-named functions +- When porting names from standalone segment extracts into `CRUSADER-RAW.EXE`, use only verified base mappings. + +# Verified Raw Mapping Rules + +- `seg001` raw base = `0x6E570` +- `seg021` raw base = `0x87170` +- Porting formula: `raw_full_exe_flat = verified_segment_base + standalone_segment_relative_offset` +- `seg001` and `seg021` both contain a keyboard handler; keep the seg001 name as `seg001_input_keyboard_handler` to avoid collision. + +# Working Method + +- Prefer a single decompile call first. +- If the decompiler collapses to thunk-heavy output, use one disassembly lookup to confirm the wrapper or parameter setup. +- Add a short decompiler comment when a rename is mapped from verified notes so the provenance stays visible in Ghidra. +- Keep `crusader_decompilation_notes.md` updated after each verified batch. +- Record raw-import addresses alongside original segment-relative offsets when porting names. + +# Current Verified Raw-Import Ports + +- `0006:e5d0` = `cursor_update_hover` from seg001 `0x0060` +- `0008:7377` = `entity_count_by_type_a` from seg021 `0x0207` +- `0007:28ce` = `shot_entity_alloc` from seg001 `0x435e` +- `0007:2a19` = `shot_entity_free` from seg001 `0x44a9` +- `0007:2bc9` = `projectile_init_vector` from seg001 `0x4659` +- `0007:3001` = `entity_fire_weapon` from seg001 `0x4a91` +- `0007:3088` = `fire_weapon_from_cursor` from seg001 `0x4b18` +- `0007:30e8` = `projectile_check_hit` from seg001 `0x4b78` +- `0007:319e` = `projectile_step_update` from seg001 `0x4c2e` +- `0007:3298` = `projectile_trace_ray` from seg001 `0x4d28` +- `0007:371d` = `projectile_update_tick` from seg001 `0x51ad` +- `0007:4009` = `projectile_apply_hit` from seg001 `0x5a99` + +# Named 000e: Functions (direct analysis — not segment-ported) + +## Parser Cluster (`000e:34xx–38xx`) +- `000e:345e` = `record_table_init` +- `000e:34cc` = `record_table_destroy` +- `000e:35c6` = `record_table_release_buffer` +- `000e:35ef` = `record_table_next_slot` +- `000e:3639` = `record_table_parse_buffer` +- `000e:3798` = `record_parser_read_line` +- `000e:38f8` = `record_parser_find_marker` + +## RIFF/Animation Cluster (`000e:03xx–2xxx`) +- `000e:2a28` = `riff_find_chunk_by_type` (RIFF LIST/RIFF walker; FourCC match at chunk+8) +- `000e:2104` = `animation_start` (finds "movi" chunk, inits timing ring buffer, kicks advance) +- `000e:12f4` = `animation_advance_frame` (fixed-point 0x1000 timer stepper, ring buffer update) +- `000e:103f` = `animation_tick` (guard wrapper — checks +0xd4 != -1, calls advance_frame) +- `000e:06f7` = `anim_load_audio_frame` (checks "01wb" chunk tag 0x62773130, copies audio into ring buffer) + +## Constructor/Assert Helpers (`000e:22xx–29xx`) +- `000e:223d` = `assert_alive_sentinel` (expects +0xd4 == -1; traps on mismatch) +- `000e:2777` = `animation_ctor_variant_a` (alloc + init flags + chained init/assert/finalize) +- `000e:2860` = `animation_ctor_variant_b` (variant A with extra +0x109 init) +- `000e:2969` = `animation_ctor_variant_c` (default static flag profile +0x4c=0xd) diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..71d5c38 --- /dev/null +++ b/.gitignore @@ -0,0 +1,16 @@ +# Ghidra project files +/.ghidra/ +*.rep/ +*.gpr + +# Ghidra caches and temporary files +ghidra_* +*.bak +*.tmp +*.swp + +# IDE and OS files +.vscode/ +.idea/ +.DS_Store +Thumbs.db diff --git a/CRUSADER.EXE b/CRUSADER.EXE new file mode 100644 index 0000000..20c5efd --- /dev/null +++ b/CRUSADER.EXE @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:60dfc1b3deac042e9a177b60a612f3a76828f6206995bcc4740cc7b35ab15636 +size 991878 diff --git a/CRUSADER_NE.EXE b/CRUSADER_NE.EXE new file mode 100644 index 0000000..4c4ac65 --- /dev/null +++ b/CRUSADER_NE.EXE @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ae222ddafd8f073022f9b5ea02e6942f8771e5a23af8879d3abd14f915341efb +size 766742 diff --git a/CRUSADER_NE_WRAPPED.EXE b/CRUSADER_NE_WRAPPED.EXE new file mode 100644 index 0000000..75ddd5b --- /dev/null +++ b/CRUSADER_NE_WRAPPED.EXE @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8b74b055632c1b4a2cad0084249486a80ce310ba2b47062f5316afea845b6431 +size 766806 diff --git a/Crusader.lock b/Crusader.lock new file mode 100644 index 0000000..acab942 --- /dev/null +++ b/Crusader.lock @@ -0,0 +1,9 @@ +#Ghidra Lock File +#Thu Mar 19 10:32:26 CET 2026 +\ Supports\ File\ Channel\ Locking=Channel Lock +Hostname=CYBER-YOUMU +OS\ Architecture=amd64 +OS\ Name=Windows 11 +OS\ Version=10.0 +Timestamp=3/19/26, 10\:32\u202FAM +Username=Maddo diff --git a/Crusader.lock~ b/Crusader.lock~ new file mode 100644 index 0000000..e69de29 diff --git a/NE_segments/seg001_code_off_37600_len_8400.bin b/NE_segments/seg001_code_off_37600_len_8400.bin new file mode 100644 index 0000000..e537eaa Binary files /dev/null and b/NE_segments/seg001_code_off_37600_len_8400.bin differ diff --git a/NE_segments/seg002_code_off_40000_len_2B0.bin b/NE_segments/seg002_code_off_40000_len_2B0.bin new file mode 100644 index 0000000..e13c844 Binary files /dev/null and b/NE_segments/seg002_code_off_40000_len_2B0.bin differ diff --git a/NE_segments/seg003_code_off_40400_len_55A.bin b/NE_segments/seg003_code_off_40400_len_55A.bin new file mode 100644 index 0000000..784a383 Binary files /dev/null and b/NE_segments/seg003_code_off_40400_len_55A.bin differ diff --git a/NE_segments/seg004_code_off_40A00_len_10B1.bin b/NE_segments/seg004_code_off_40A00_len_10B1.bin new file mode 100644 index 0000000..beb5dd0 Binary files /dev/null and b/NE_segments/seg004_code_off_40A00_len_10B1.bin differ diff --git a/NE_segments/seg005_code_off_41E00_len_8D7.bin b/NE_segments/seg005_code_off_41E00_len_8D7.bin new file mode 100644 index 0000000..cf0c599 Binary files /dev/null and b/NE_segments/seg005_code_off_41E00_len_8D7.bin differ diff --git a/NE_segments/seg006_code_off_42C00_len_75E.bin b/NE_segments/seg006_code_off_42C00_len_75E.bin new file mode 100644 index 0000000..757974e Binary files /dev/null and b/NE_segments/seg006_code_off_42C00_len_75E.bin differ diff --git a/NE_segments/seg007_code_off_43600_len_484.bin b/NE_segments/seg007_code_off_43600_len_484.bin new file mode 100644 index 0000000..2a05978 Binary files /dev/null and b/NE_segments/seg007_code_off_43600_len_484.bin differ diff --git a/NE_segments/seg008_code_off_43C00_len_1386.bin b/NE_segments/seg008_code_off_43C00_len_1386.bin new file mode 100644 index 0000000..1c367b1 Binary files /dev/null and b/NE_segments/seg008_code_off_43C00_len_1386.bin differ diff --git a/NE_segments/seg009_code_off_45400_len_495.bin b/NE_segments/seg009_code_off_45400_len_495.bin new file mode 100644 index 0000000..1440a88 Binary files /dev/null and b/NE_segments/seg009_code_off_45400_len_495.bin differ diff --git a/NE_segments/seg010_code_off_45A00_len_D92.bin b/NE_segments/seg010_code_off_45A00_len_D92.bin new file mode 100644 index 0000000..b11f352 Binary files /dev/null and b/NE_segments/seg010_code_off_45A00_len_D92.bin differ diff --git a/NE_segments/seg011_code_off_46E00_len_5B1.bin b/NE_segments/seg011_code_off_46E00_len_5B1.bin new file mode 100644 index 0000000..d2383e3 Binary files /dev/null and b/NE_segments/seg011_code_off_46E00_len_5B1.bin differ diff --git a/NE_segments/seg012_code_off_47600_len_94B.bin b/NE_segments/seg012_code_off_47600_len_94B.bin new file mode 100644 index 0000000..a7f79ca Binary files /dev/null and b/NE_segments/seg012_code_off_47600_len_94B.bin differ diff --git a/NE_segments/seg013_code_off_48200_len_1F6C.bin b/NE_segments/seg013_code_off_48200_len_1F6C.bin new file mode 100644 index 0000000..b7ef1f2 Binary files /dev/null and b/NE_segments/seg013_code_off_48200_len_1F6C.bin differ diff --git a/NE_segments/seg014_code_off_4AA00_len_526.bin b/NE_segments/seg014_code_off_4AA00_len_526.bin new file mode 100644 index 0000000..bb51a15 Binary files /dev/null and b/NE_segments/seg014_code_off_4AA00_len_526.bin differ diff --git a/NE_segments/seg015_code_off_4B200_len_1C68.bin b/NE_segments/seg015_code_off_4B200_len_1C68.bin new file mode 100644 index 0000000..c47cc39 Binary files /dev/null and b/NE_segments/seg015_code_off_4B200_len_1C68.bin differ diff --git a/NE_segments/seg016_code_off_4D400_len_677.bin b/NE_segments/seg016_code_off_4D400_len_677.bin new file mode 100644 index 0000000..5aff96a Binary files /dev/null and b/NE_segments/seg016_code_off_4D400_len_677.bin differ diff --git a/NE_segments/seg017_code_off_4DC00_len_1A7.bin b/NE_segments/seg017_code_off_4DC00_len_1A7.bin new file mode 100644 index 0000000..78229ce Binary files /dev/null and b/NE_segments/seg017_code_off_4DC00_len_1A7.bin differ diff --git a/NE_segments/seg018_code_off_4E000_len_7E9.bin b/NE_segments/seg018_code_off_4E000_len_7E9.bin new file mode 100644 index 0000000..f6204e6 Binary files /dev/null and b/NE_segments/seg018_code_off_4E000_len_7E9.bin differ diff --git a/NE_segments/seg019_code_off_4EA00_len_B4D.bin b/NE_segments/seg019_code_off_4EA00_len_B4D.bin new file mode 100644 index 0000000..30eee7e Binary files /dev/null and b/NE_segments/seg019_code_off_4EA00_len_B4D.bin differ diff --git a/NE_segments/seg020_code_off_4F800_len_878.bin b/NE_segments/seg020_code_off_4F800_len_878.bin new file mode 100644 index 0000000..294848c Binary files /dev/null and b/NE_segments/seg020_code_off_4F800_len_878.bin differ diff --git a/NE_segments/seg021_code_off_50200_len_4486.bin b/NE_segments/seg021_code_off_50200_len_4486.bin new file mode 100644 index 0000000..087a6be Binary files /dev/null and b/NE_segments/seg021_code_off_50200_len_4486.bin differ diff --git a/NE_segments/seg022_code_off_55000_len_2BD6.bin b/NE_segments/seg022_code_off_55000_len_2BD6.bin new file mode 100644 index 0000000..a719143 Binary files /dev/null and b/NE_segments/seg022_code_off_55000_len_2BD6.bin differ diff --git a/NE_segments/seg023_code_off_58200_len_5D6.bin b/NE_segments/seg023_code_off_58200_len_5D6.bin new file mode 100644 index 0000000..de654b5 Binary files /dev/null and b/NE_segments/seg023_code_off_58200_len_5D6.bin differ diff --git a/NE_segments/seg024_code_off_58A00_len_6D7.bin b/NE_segments/seg024_code_off_58A00_len_6D7.bin new file mode 100644 index 0000000..eae388b Binary files /dev/null and b/NE_segments/seg024_code_off_58A00_len_6D7.bin differ diff --git a/NE_segments/seg025_code_off_59200_len_1976.bin b/NE_segments/seg025_code_off_59200_len_1976.bin new file mode 100644 index 0000000..5b94c9b Binary files /dev/null and b/NE_segments/seg025_code_off_59200_len_1976.bin differ diff --git a/NE_segments/seg026_code_off_5AE00_len_4DE.bin b/NE_segments/seg026_code_off_5AE00_len_4DE.bin new file mode 100644 index 0000000..e4cbaf2 Binary files /dev/null and b/NE_segments/seg026_code_off_5AE00_len_4DE.bin differ diff --git a/NE_segments/seg027_code_off_5B400_len_57B.bin b/NE_segments/seg027_code_off_5B400_len_57B.bin new file mode 100644 index 0000000..3d9e416 Binary files /dev/null and b/NE_segments/seg027_code_off_5B400_len_57B.bin differ diff --git a/NE_segments/seg028_code_off_5BA00_len_788.bin b/NE_segments/seg028_code_off_5BA00_len_788.bin new file mode 100644 index 0000000..0ae0ed3 Binary files /dev/null and b/NE_segments/seg028_code_off_5BA00_len_788.bin differ diff --git a/NE_segments/seg029_code_off_5C400_len_190A.bin b/NE_segments/seg029_code_off_5C400_len_190A.bin new file mode 100644 index 0000000..a24e13a Binary files /dev/null and b/NE_segments/seg029_code_off_5C400_len_190A.bin differ diff --git a/NE_segments/seg030_code_off_5E000_len_5071.bin b/NE_segments/seg030_code_off_5E000_len_5071.bin new file mode 100644 index 0000000..ff7b3b1 Binary files /dev/null and b/NE_segments/seg030_code_off_5E000_len_5071.bin differ diff --git a/NE_segments/seg031_code_off_64000_len_6EE.bin b/NE_segments/seg031_code_off_64000_len_6EE.bin new file mode 100644 index 0000000..69c42a0 Binary files /dev/null and b/NE_segments/seg031_code_off_64000_len_6EE.bin differ diff --git a/NE_segments/seg032_code_off_64800_len_56A.bin b/NE_segments/seg032_code_off_64800_len_56A.bin new file mode 100644 index 0000000..1776ee2 Binary files /dev/null and b/NE_segments/seg032_code_off_64800_len_56A.bin differ diff --git a/NE_segments/seg033_code_off_65000_len_10D7.bin b/NE_segments/seg033_code_off_65000_len_10D7.bin new file mode 100644 index 0000000..7024c81 Binary files /dev/null and b/NE_segments/seg033_code_off_65000_len_10D7.bin differ diff --git a/NE_segments/seg034_code_off_66600_len_253A.bin b/NE_segments/seg034_code_off_66600_len_253A.bin new file mode 100644 index 0000000..f3bfc92 Binary files /dev/null and b/NE_segments/seg034_code_off_66600_len_253A.bin differ diff --git a/NE_segments/seg035_code_off_69400_len_F67.bin b/NE_segments/seg035_code_off_69400_len_F67.bin new file mode 100644 index 0000000..3d1c9c2 --- /dev/null +++ b/NE_segments/seg035_code_off_69400_len_F67.bin @@ -0,0 +1,17 @@ +I6gffFgGI"gffFgGIgffFgGIgffFgGIgffFgGIgffFgGIgffFgGIgffFgGIgffFgGIgffFgGIngffFgGIZgffFgGIFgffFgGI2gffFgGIgffFgGI +gffFgGIgffFgGIgffFgGIgffFgGIgffFgGIgffFgGIgffFgGI~gffFgGIjgffFgGIVgffFgGIBgffFgGI.gffFgGIgffFgGIgffFgGIgffFgGIgffFgGIgffFgGIgffFgGIgffFgGIgffFgGIzgffFgGIfgffFgGIRgffFgGI>gffFgGI*gffFgGIgffFgGIgffFgGI gffFgGI gffFgGI gffFgGI gffFgGI gffFgGI gffFgGIv gffFgGIb gffFgGIN gffFgGI: gffFgGI& gffFgGI gffFgGI gffFgGI gffFgGI gffFgGI gffFgGI gffFgGI gffFgGI gffFgGIr gffFgGI^ gffFgGIJ gffFgGI6 gffFgGI" gffFgGI gffFgGI gffFgGI gffFgGI gffFgGI gffFgGI gffFgGI gffFgGI gffFgGIn gffFgGIZ gffFgGIF gffFgGI2 gffFgGI gffFgGI + gffFgGI +gffFgGI +gffFgGI +gffFgGI +gffFgGI +gffFgGI +gffFgGI~ +gffFgGIj +gffFgGIV +gffFgGIB +gffFgGI. +gffFgGI +gffFgGI +gffFgGI gffFgGI gffFgGI gffFgGI gffFgGI gffFgGI gffFgGIz gffFgGIf gffFgGIR gffFgGI> gffFgGI* gffFgGI gffFgGI gffFgGIgffFgGIgffFgGIgffFgGIgffFgGIgffFgGIgffFgGIvgffFgGIbgffFgGINgffFgGI:gffFgGI&gffFgGIgffFgGIgffFgGIgffFgGIgffFgGIgffFgGIgffFgGIgffFgGIgffFgGIrgffFgGI^gffFgGIJgffFgGI6gffFgGI"gffFgGIgffFgGIgffFgGIgffFgGIgffFgGIgffFgGIgffFgGIgffFgGIgffFgGIngffFgGIZgffFgGIFgffFgGI2gffFgGIgffFgGI +gffFgGIgffFgGIgffFgGIgffFgGIgffFgGIgffFgGIgffFgGI~gffFgGIjgffFgGIVgffFgGIBgffFgGI.gffFgGIgffFgGIgffFgGIgffFgGIgffFgGIgffFgGIgffFgGIgffFgGIgffFgGIzgffFgGIfgffFgGIRgffFgGI>gffFgGI*gffFgGIgffFgGIgffFgGIgffFgGIgffFgGIgffFgGIgffFgGIgffFgGIgffFgGIvgffFgGIbgffFgGINgffFgGI:gffFgGI&gffFgGIgffFgGIgffFgGIgffFgGIgffFgGIgffFgGIgffFgGIgffFgGIgffFgGIrgffFgGI^gffFgGIJgffFgGI6gffFgGI"gffFgGIgffFgGIgffFgGIgffFgGI \ No newline at end of file diff --git a/NE_segments/seg036_code_off_6A600_len_69F.bin b/NE_segments/seg036_code_off_6A600_len_69F.bin new file mode 100644 index 0000000..546c52a --- /dev/null +++ b/NE_segments/seg036_code_off_6A600_len_69F.bin @@ -0,0 +1,2 @@ +ffFfgGIhgfffFfgGIKgfffFfgGI.gfffFfgGIgfffFfgGIgfffFfgGIgfffFfgGIgfffFfgGIgfffFfgGIgfffFfgGIcgfffFfgGIFgfffFfgGI)gfffFfgGI gfffFfgGIgfffFfgGIgfffFfgGIgfffFfgGIgfffFfgGI{gfffFfgGI^gfffFfgGIAgfffFfgGI$gfffFfgGIgfffFfgGIgfffFfgGIgfffFfgGIgfffFfgGIgfffFfgGIvgfffFfgGIYgfffFfgGI<gfffFfgGIgfffFfgGIgfffFfgGI gfffFfgGI gfffFfgGI gfffFfgGI gfffFfgGIq gfffFfgGIT gfffFfgGI7 gfffFfgGI gfffFfgGI gfffFfgGI gfffFfgGI gfffFfgGI gfffFfgGI gfffFfgGIl gfffFfgGIO gfffFfgGI2 gfffFfgGI gfffFfgGI gfffFfgGI gfffFfgGI gfffFfgGI gfffFfgGI gfffFfgGIg gfffFfgGIJ gfffFfgGI- gfffFfgGI gfffFfgGI +gfffFf \ No newline at end of file diff --git a/NE_segments/seg037_code_off_6AE00_len_636.bin b/NE_segments/seg037_code_off_6AE00_len_636.bin new file mode 100644 index 0000000..b9cbb41 --- /dev/null +++ b/NE_segments/seg037_code_off_6AE00_len_636.bin @@ -0,0 +1,2 @@ +z gfffFfgGI] gfffFfgGI@ gfffFfgGI# gfffFfgGI gfffFfgGIgfffFfgGIgfffFfgGIgfffFfgGIgfffFfgGIugfffFfgGIXgfffFfgGI;gfffFfgGIgfffFfgGIgfffFfgGIgfffFfgGIgfffFfgGIgfffFfgGIgfffFfgGIpgfffFfgGISgfffFfgGI6gfffFfgGIgfffFfgGIgfffFfgGIgfffFfgGIgfffFfgGIgfffFfgGIgfffFfgGIkgfffFfgGINgfffFfgGI1gfffFfgGIgfffFfgGIgfffFfgGIgfffFfgGIgfffFfgGIgfffFfgGIgfffFfgGIfgfffFfgGIIgfffFfgGI,gfffFfgGIgfffFfgGIgfffFfgGIgfffFfgGIgfffFfgGIgfffFfgGI~gfffFfgGIagfffFfgGIDgfffFfgGI'gfffFfgGI +gfffFfgGIgfffFfgGIgfffFfgGIgfffFfgGIgfffFfgGIygfffFfgGI\gfffFf \ No newline at end of file diff --git a/NE_segments/seg038_code_off_6B600_len_2318.bin b/NE_segments/seg038_code_off_6B600_len_2318.bin new file mode 100644 index 0000000..cfe80a8 Binary files /dev/null and b/NE_segments/seg038_code_off_6B600_len_2318.bin differ diff --git a/NE_segments/seg039_code_off_6E200_len_3416.bin b/NE_segments/seg039_code_off_6E200_len_3416.bin new file mode 100644 index 0000000..63bf32b Binary files /dev/null and b/NE_segments/seg039_code_off_6E200_len_3416.bin differ diff --git a/NE_segments/seg040_code_off_72200_len_1E7A.bin b/NE_segments/seg040_code_off_72200_len_1E7A.bin new file mode 100644 index 0000000..42b5bd6 Binary files /dev/null and b/NE_segments/seg040_code_off_72200_len_1E7A.bin differ diff --git a/NE_segments/seg041_code_off_74600_len_28D.bin b/NE_segments/seg041_code_off_74600_len_28D.bin new file mode 100644 index 0000000..e1330e6 Binary files /dev/null and b/NE_segments/seg041_code_off_74600_len_28D.bin differ diff --git a/NE_segments/seg042_code_off_74A00_len_C9C.bin b/NE_segments/seg042_code_off_74A00_len_C9C.bin new file mode 100644 index 0000000..aa0c688 Binary files /dev/null and b/NE_segments/seg042_code_off_74A00_len_C9C.bin differ diff --git a/NE_segments/seg043_code_off_75A00_len_336F.bin b/NE_segments/seg043_code_off_75A00_len_336F.bin new file mode 100644 index 0000000..2230bd0 Binary files /dev/null and b/NE_segments/seg043_code_off_75A00_len_336F.bin differ diff --git a/NE_segments/seg044_code_off_79400_len_7F8.bin b/NE_segments/seg044_code_off_79400_len_7F8.bin new file mode 100644 index 0000000..249dc13 Binary files /dev/null and b/NE_segments/seg044_code_off_79400_len_7F8.bin differ diff --git a/NE_segments/seg045_code_off_79E00_len_200.bin b/NE_segments/seg045_code_off_79E00_len_200.bin new file mode 100644 index 0000000..360c954 Binary files /dev/null and b/NE_segments/seg045_code_off_79E00_len_200.bin differ diff --git a/NE_segments/seg046_code_off_7A200_len_7DC.bin b/NE_segments/seg046_code_off_7A200_len_7DC.bin new file mode 100644 index 0000000..0946a65 Binary files /dev/null and b/NE_segments/seg046_code_off_7A200_len_7DC.bin differ diff --git a/NE_segments/seg047_code_off_7AC00_len_9B4.bin b/NE_segments/seg047_code_off_7AC00_len_9B4.bin new file mode 100644 index 0000000..17c0c0a Binary files /dev/null and b/NE_segments/seg047_code_off_7AC00_len_9B4.bin differ diff --git a/NE_segments/seg048_code_off_7B800_len_63.bin b/NE_segments/seg048_code_off_7B800_len_63.bin new file mode 100644 index 0000000..b6bdf7c Binary files /dev/null and b/NE_segments/seg048_code_off_7B800_len_63.bin differ diff --git a/NE_segments/seg049_code_off_7BA00_len_1E3F.bin b/NE_segments/seg049_code_off_7BA00_len_1E3F.bin new file mode 100644 index 0000000..b60478c Binary files /dev/null and b/NE_segments/seg049_code_off_7BA00_len_1E3F.bin differ diff --git a/NE_segments/seg050_code_off_7DE00_len_9C8.bin b/NE_segments/seg050_code_off_7DE00_len_9C8.bin new file mode 100644 index 0000000..84ed1c4 Binary files /dev/null and b/NE_segments/seg050_code_off_7DE00_len_9C8.bin differ diff --git a/NE_segments/seg051_code_off_7EA00_len_1D02.bin b/NE_segments/seg051_code_off_7EA00_len_1D02.bin new file mode 100644 index 0000000..468c593 Binary files /dev/null and b/NE_segments/seg051_code_off_7EA00_len_1D02.bin differ diff --git a/NE_segments/seg052_code_off_80A00_len_1D65.bin b/NE_segments/seg052_code_off_80A00_len_1D65.bin new file mode 100644 index 0000000..bba6e13 Binary files /dev/null and b/NE_segments/seg052_code_off_80A00_len_1D65.bin differ diff --git a/NE_segments/seg053_code_off_82C00_len_10DE.bin b/NE_segments/seg053_code_off_82C00_len_10DE.bin new file mode 100644 index 0000000..b0c6e60 Binary files /dev/null and b/NE_segments/seg053_code_off_82C00_len_10DE.bin differ diff --git a/NE_segments/seg054_code_off_84000_len_5.bin b/NE_segments/seg054_code_off_84000_len_5.bin new file mode 100644 index 0000000..f0ee5af --- /dev/null +++ b/NE_segments/seg054_code_off_84000_len_5.bin @@ -0,0 +1 @@ +GP&;G \ No newline at end of file diff --git a/NE_segments/seg055_code_off_84200_len_A06.bin b/NE_segments/seg055_code_off_84200_len_A06.bin new file mode 100644 index 0000000..92b75b8 Binary files /dev/null and b/NE_segments/seg055_code_off_84200_len_A06.bin differ diff --git a/NE_segments/seg056_code_off_85000_len_706.bin b/NE_segments/seg056_code_off_85000_len_706.bin new file mode 100644 index 0000000..2822f59 Binary files /dev/null and b/NE_segments/seg056_code_off_85000_len_706.bin differ diff --git a/NE_segments/seg057_code_off_85A00_len_79B.bin b/NE_segments/seg057_code_off_85A00_len_79B.bin new file mode 100644 index 0000000..8a6682d Binary files /dev/null and b/NE_segments/seg057_code_off_85A00_len_79B.bin differ diff --git a/NE_segments/seg058_code_off_86400_len_44B.bin b/NE_segments/seg058_code_off_86400_len_44B.bin new file mode 100644 index 0000000..74e58be Binary files /dev/null and b/NE_segments/seg058_code_off_86400_len_44B.bin differ diff --git a/NE_segments/seg059_code_off_86A00_len_4288.bin b/NE_segments/seg059_code_off_86A00_len_4288.bin new file mode 100644 index 0000000..1ecb54b Binary files /dev/null and b/NE_segments/seg059_code_off_86A00_len_4288.bin differ diff --git a/NE_segments/seg060_code_off_8B600_len_231.bin b/NE_segments/seg060_code_off_8B600_len_231.bin new file mode 100644 index 0000000..1bfba57 Binary files /dev/null and b/NE_segments/seg060_code_off_8B600_len_231.bin differ diff --git a/NE_segments/seg061_code_off_8BA00_len_1B6C.bin b/NE_segments/seg061_code_off_8BA00_len_1B6C.bin new file mode 100644 index 0000000..6203e94 Binary files /dev/null and b/NE_segments/seg061_code_off_8BA00_len_1B6C.bin differ diff --git a/NE_segments/seg062_code_off_8DA00_len_85F.bin b/NE_segments/seg062_code_off_8DA00_len_85F.bin new file mode 100644 index 0000000..64830d4 Binary files /dev/null and b/NE_segments/seg062_code_off_8DA00_len_85F.bin differ diff --git a/NE_segments/seg063_code_off_8E400_len_519.bin b/NE_segments/seg063_code_off_8E400_len_519.bin new file mode 100644 index 0000000..d0b7ac6 Binary files /dev/null and b/NE_segments/seg063_code_off_8E400_len_519.bin differ diff --git a/NE_segments/seg064_code_off_8EA00_len_3B1.bin b/NE_segments/seg064_code_off_8EA00_len_3B1.bin new file mode 100644 index 0000000..a581dab Binary files /dev/null and b/NE_segments/seg064_code_off_8EA00_len_3B1.bin differ diff --git a/NE_segments/seg065_code_off_8F000_len_5BD.bin b/NE_segments/seg065_code_off_8F000_len_5BD.bin new file mode 100644 index 0000000..ee874c2 Binary files /dev/null and b/NE_segments/seg065_code_off_8F000_len_5BD.bin differ diff --git a/NE_segments/seg066_code_off_8F800_len_4A9.bin b/NE_segments/seg066_code_off_8F800_len_4A9.bin new file mode 100644 index 0000000..98c6255 Binary files /dev/null and b/NE_segments/seg066_code_off_8F800_len_4A9.bin differ diff --git a/NE_segments/seg067_code_off_8FE00_len_839.bin b/NE_segments/seg067_code_off_8FE00_len_839.bin new file mode 100644 index 0000000..86a872a Binary files /dev/null and b/NE_segments/seg067_code_off_8FE00_len_839.bin differ diff --git a/NE_segments/seg068_code_off_90800_len_B4A.bin b/NE_segments/seg068_code_off_90800_len_B4A.bin new file mode 100644 index 0000000..258983f Binary files /dev/null and b/NE_segments/seg068_code_off_90800_len_B4A.bin differ diff --git a/NE_segments/seg069_code_off_91800_len_2A0.bin b/NE_segments/seg069_code_off_91800_len_2A0.bin new file mode 100644 index 0000000..e2d809b Binary files /dev/null and b/NE_segments/seg069_code_off_91800_len_2A0.bin differ diff --git a/NE_segments/seg070_code_off_91C00_len_F24.bin b/NE_segments/seg070_code_off_91C00_len_F24.bin new file mode 100644 index 0000000..850a3f5 Binary files /dev/null and b/NE_segments/seg070_code_off_91C00_len_F24.bin differ diff --git a/NE_segments/seg071_code_off_92E00_len_6C2.bin b/NE_segments/seg071_code_off_92E00_len_6C2.bin new file mode 100644 index 0000000..fd04834 Binary files /dev/null and b/NE_segments/seg071_code_off_92E00_len_6C2.bin differ diff --git a/NE_segments/seg072_code_off_93600_len_CA1.bin b/NE_segments/seg072_code_off_93600_len_CA1.bin new file mode 100644 index 0000000..65dfcbc Binary files /dev/null and b/NE_segments/seg072_code_off_93600_len_CA1.bin differ diff --git a/NE_segments/seg073_code_off_94600_len_9AA.bin b/NE_segments/seg073_code_off_94600_len_9AA.bin new file mode 100644 index 0000000..d1be253 Binary files /dev/null and b/NE_segments/seg073_code_off_94600_len_9AA.bin differ diff --git a/NE_segments/seg074_code_off_95200_len_337.bin b/NE_segments/seg074_code_off_95200_len_337.bin new file mode 100644 index 0000000..a969121 Binary files /dev/null and b/NE_segments/seg074_code_off_95200_len_337.bin differ diff --git a/NE_segments/seg075_code_off_95600_len_1428.bin b/NE_segments/seg075_code_off_95600_len_1428.bin new file mode 100644 index 0000000..31e033f Binary files /dev/null and b/NE_segments/seg075_code_off_95600_len_1428.bin differ diff --git a/NE_segments/seg076_code_off_96E00_len_627.bin b/NE_segments/seg076_code_off_96E00_len_627.bin new file mode 100644 index 0000000..8a79496 Binary files /dev/null and b/NE_segments/seg076_code_off_96E00_len_627.bin differ diff --git a/NE_segments/seg077_code_off_97600_len_616.bin b/NE_segments/seg077_code_off_97600_len_616.bin new file mode 100644 index 0000000..a56a7c2 Binary files /dev/null and b/NE_segments/seg077_code_off_97600_len_616.bin differ diff --git a/NE_segments/seg078_code_off_97E00_len_634.bin b/NE_segments/seg078_code_off_97E00_len_634.bin new file mode 100644 index 0000000..ae9d024 Binary files /dev/null and b/NE_segments/seg078_code_off_97E00_len_634.bin differ diff --git a/NE_segments/seg079_code_off_98600_len_421.bin b/NE_segments/seg079_code_off_98600_len_421.bin new file mode 100644 index 0000000..acc1d7f Binary files /dev/null and b/NE_segments/seg079_code_off_98600_len_421.bin differ diff --git a/NE_segments/seg080_code_off_98C00_len_F27.bin b/NE_segments/seg080_code_off_98C00_len_F27.bin new file mode 100644 index 0000000..ace752e Binary files /dev/null and b/NE_segments/seg080_code_off_98C00_len_F27.bin differ diff --git a/NE_segments/seg081_code_off_99E00_len_320.bin b/NE_segments/seg081_code_off_99E00_len_320.bin new file mode 100644 index 0000000..e223c64 Binary files /dev/null and b/NE_segments/seg081_code_off_99E00_len_320.bin differ diff --git a/NE_segments/seg082_code_off_9A200_len_1C8A.bin b/NE_segments/seg082_code_off_9A200_len_1C8A.bin new file mode 100644 index 0000000..01762fd Binary files /dev/null and b/NE_segments/seg082_code_off_9A200_len_1C8A.bin differ diff --git a/NE_segments/seg083_code_off_9C400_len_31E.bin b/NE_segments/seg083_code_off_9C400_len_31E.bin new file mode 100644 index 0000000..6c01088 Binary files /dev/null and b/NE_segments/seg083_code_off_9C400_len_31E.bin differ diff --git a/NE_segments/seg084_code_off_9C800_len_1478.bin b/NE_segments/seg084_code_off_9C800_len_1478.bin new file mode 100644 index 0000000..583e57f Binary files /dev/null and b/NE_segments/seg084_code_off_9C800_len_1478.bin differ diff --git a/NE_segments/seg085_code_off_9E000_len_404.bin b/NE_segments/seg085_code_off_9E000_len_404.bin new file mode 100644 index 0000000..ef0ede5 Binary files /dev/null and b/NE_segments/seg085_code_off_9E000_len_404.bin differ diff --git a/NE_segments/seg086_code_off_9E600_len_40F6.bin b/NE_segments/seg086_code_off_9E600_len_40F6.bin new file mode 100644 index 0000000..f203acb Binary files /dev/null and b/NE_segments/seg086_code_off_9E600_len_40F6.bin differ diff --git a/NE_segments/seg087_code_off_A2800_len_50C.bin b/NE_segments/seg087_code_off_A2800_len_50C.bin new file mode 100644 index 0000000..8d22fcd Binary files /dev/null and b/NE_segments/seg087_code_off_A2800_len_50C.bin differ diff --git a/NE_segments/seg088_code_off_A2E00_len_523.bin b/NE_segments/seg088_code_off_A2E00_len_523.bin new file mode 100644 index 0000000..03c652a Binary files /dev/null and b/NE_segments/seg088_code_off_A2E00_len_523.bin differ diff --git a/NE_segments/seg089_code_off_A3400_len_373.bin b/NE_segments/seg089_code_off_A3400_len_373.bin new file mode 100644 index 0000000..0617f60 Binary files /dev/null and b/NE_segments/seg089_code_off_A3400_len_373.bin differ diff --git a/NE_segments/seg090_code_off_A3800_len_9C6.bin b/NE_segments/seg090_code_off_A3800_len_9C6.bin new file mode 100644 index 0000000..58eb59f Binary files /dev/null and b/NE_segments/seg090_code_off_A3800_len_9C6.bin differ diff --git a/NE_segments/seg091_code_off_A4400_len_6FA.bin b/NE_segments/seg091_code_off_A4400_len_6FA.bin new file mode 100644 index 0000000..8b4f41a Binary files /dev/null and b/NE_segments/seg091_code_off_A4400_len_6FA.bin differ diff --git a/NE_segments/seg092_code_off_A4E00_len_59E.bin b/NE_segments/seg092_code_off_A4E00_len_59E.bin new file mode 100644 index 0000000..d5c497e Binary files /dev/null and b/NE_segments/seg092_code_off_A4E00_len_59E.bin differ diff --git a/NE_segments/seg093_code_off_A5600_len_4F1.bin b/NE_segments/seg093_code_off_A5600_len_4F1.bin new file mode 100644 index 0000000..b8c2db0 Binary files /dev/null and b/NE_segments/seg093_code_off_A5600_len_4F1.bin differ diff --git a/NE_segments/seg094_code_off_A5E00_len_606.bin b/NE_segments/seg094_code_off_A5E00_len_606.bin new file mode 100644 index 0000000..a975fd3 Binary files /dev/null and b/NE_segments/seg094_code_off_A5E00_len_606.bin differ diff --git a/NE_segments/seg095_code_off_A6600_len_C9F.bin b/NE_segments/seg095_code_off_A6600_len_C9F.bin new file mode 100644 index 0000000..8279ac2 Binary files /dev/null and b/NE_segments/seg095_code_off_A6600_len_C9F.bin differ diff --git a/NE_segments/seg096_code_off_A7600_len_582.bin b/NE_segments/seg096_code_off_A7600_len_582.bin new file mode 100644 index 0000000..62372c8 Binary files /dev/null and b/NE_segments/seg096_code_off_A7600_len_582.bin differ diff --git a/NE_segments/seg097_code_off_A7E00_len_DB0.bin b/NE_segments/seg097_code_off_A7E00_len_DB0.bin new file mode 100644 index 0000000..4e74f9f Binary files /dev/null and b/NE_segments/seg097_code_off_A7E00_len_DB0.bin differ diff --git a/NE_segments/seg098_code_off_A8E00_len_68A.bin b/NE_segments/seg098_code_off_A8E00_len_68A.bin new file mode 100644 index 0000000..f16979b Binary files /dev/null and b/NE_segments/seg098_code_off_A8E00_len_68A.bin differ diff --git a/NE_segments/seg099_code_off_A9600_len_355.bin b/NE_segments/seg099_code_off_A9600_len_355.bin new file mode 100644 index 0000000..26cd905 Binary files /dev/null and b/NE_segments/seg099_code_off_A9600_len_355.bin differ diff --git a/NE_segments/seg100_code_off_A9C00_len_697.bin b/NE_segments/seg100_code_off_A9C00_len_697.bin new file mode 100644 index 0000000..272753d Binary files /dev/null and b/NE_segments/seg100_code_off_A9C00_len_697.bin differ diff --git a/NE_segments/seg101_code_off_AA400_len_17BC.bin b/NE_segments/seg101_code_off_AA400_len_17BC.bin new file mode 100644 index 0000000..3e7ccda Binary files /dev/null and b/NE_segments/seg101_code_off_AA400_len_17BC.bin differ diff --git a/NE_segments/seg102_code_off_AC000_len_73C.bin b/NE_segments/seg102_code_off_AC000_len_73C.bin new file mode 100644 index 0000000..f411988 Binary files /dev/null and b/NE_segments/seg102_code_off_AC000_len_73C.bin differ diff --git a/NE_segments/seg103_code_off_ACA00_len_16CD.bin b/NE_segments/seg103_code_off_ACA00_len_16CD.bin new file mode 100644 index 0000000..4e3d914 Binary files /dev/null and b/NE_segments/seg103_code_off_ACA00_len_16CD.bin differ diff --git a/NE_segments/seg104_code_off_AE600_len_41B.bin b/NE_segments/seg104_code_off_AE600_len_41B.bin new file mode 100644 index 0000000..abc6d7a Binary files /dev/null and b/NE_segments/seg104_code_off_AE600_len_41B.bin differ diff --git a/NE_segments/seg105_code_off_AEC00_len_9F6.bin b/NE_segments/seg105_code_off_AEC00_len_9F6.bin new file mode 100644 index 0000000..5995d10 Binary files /dev/null and b/NE_segments/seg105_code_off_AEC00_len_9F6.bin differ diff --git a/NE_segments/seg106_code_off_AF800_len_1795.bin b/NE_segments/seg106_code_off_AF800_len_1795.bin new file mode 100644 index 0000000..620a951 Binary files /dev/null and b/NE_segments/seg106_code_off_AF800_len_1795.bin differ diff --git a/NE_segments/seg107_code_off_B1400_len_40C.bin b/NE_segments/seg107_code_off_B1400_len_40C.bin new file mode 100644 index 0000000..8c1fbc9 Binary files /dev/null and b/NE_segments/seg107_code_off_B1400_len_40C.bin differ diff --git a/NE_segments/seg108_code_off_B1A00_len_113F.bin b/NE_segments/seg108_code_off_B1A00_len_113F.bin new file mode 100644 index 0000000..034400e Binary files /dev/null and b/NE_segments/seg108_code_off_B1A00_len_113F.bin differ diff --git a/NE_segments/seg109_code_off_B2E00_len_1424.bin b/NE_segments/seg109_code_off_B2E00_len_1424.bin new file mode 100644 index 0000000..444ff57 Binary files /dev/null and b/NE_segments/seg109_code_off_B2E00_len_1424.bin differ diff --git a/NE_segments/seg110_code_off_B4400_len_4C4.bin b/NE_segments/seg110_code_off_B4400_len_4C4.bin new file mode 100644 index 0000000..9e0af9e Binary files /dev/null and b/NE_segments/seg110_code_off_B4400_len_4C4.bin differ diff --git a/NE_segments/seg111_code_off_B4A00_len_489.bin b/NE_segments/seg111_code_off_B4A00_len_489.bin new file mode 100644 index 0000000..c5c3bb9 Binary files /dev/null and b/NE_segments/seg111_code_off_B4A00_len_489.bin differ diff --git a/NE_segments/seg112_code_off_B5000_len_1670.bin b/NE_segments/seg112_code_off_B5000_len_1670.bin new file mode 100644 index 0000000..fe49bb9 Binary files /dev/null and b/NE_segments/seg112_code_off_B5000_len_1670.bin differ diff --git a/NE_segments/seg113_code_off_B6A00_len_4A6.bin b/NE_segments/seg113_code_off_B6A00_len_4A6.bin new file mode 100644 index 0000000..1da6fea Binary files /dev/null and b/NE_segments/seg113_code_off_B6A00_len_4A6.bin differ diff --git a/NE_segments/seg114_code_off_B7000_len_DF1.bin b/NE_segments/seg114_code_off_B7000_len_DF1.bin new file mode 100644 index 0000000..18dcaba Binary files /dev/null and b/NE_segments/seg114_code_off_B7000_len_DF1.bin differ diff --git a/NE_segments/seg115_code_off_B8000_len_978.bin b/NE_segments/seg115_code_off_B8000_len_978.bin new file mode 100644 index 0000000..c5ce454 Binary files /dev/null and b/NE_segments/seg115_code_off_B8000_len_978.bin differ diff --git a/NE_segments/seg116_code_off_B8C00_len_AA3.bin b/NE_segments/seg116_code_off_B8C00_len_AA3.bin new file mode 100644 index 0000000..b34d7fe Binary files /dev/null and b/NE_segments/seg116_code_off_B8C00_len_AA3.bin differ diff --git a/NE_segments/seg117_code_off_B9A00_len_3157.bin b/NE_segments/seg117_code_off_B9A00_len_3157.bin new file mode 100644 index 0000000..f05888f Binary files /dev/null and b/NE_segments/seg117_code_off_B9A00_len_3157.bin differ diff --git a/crusader_decompilation_notes.md b/crusader_decompilation_notes.md new file mode 100644 index 0000000..7f2991f --- /dev/null +++ b/crusader_decompilation_notes.md @@ -0,0 +1,715 @@ +# Crusader: No Remorse - Decompilation Notes + +## Binary Overview + +- **Game**: Crusader: No Remorse (Origin Systems, 1995) +- **Platform**: DOS (16-bit protected mode) +- **DOS Extender**: Phar Lap 286 DOS-Extender (RUN286) +- **Executable Format**: Bound `MZ -> NE` executable with Phar Lap DOS-extender code +- **Entry Point**: `10da:7c40` + +## Installed Copy Findings + +- No standalone `.EXP` file exists in `F:\Apps\Crusader No Remorse`. +- `CRUSADER.EXE` is the original game binary and contains a valid internal `NE` header. +- Outer DOS `MZ` header points to `e_lfanew = 0x36F70`. +- Internal header at `0x36F70` starts with `NE` and describes **145 segments**. +- The NE segment table references data from the original file directly, so there is no separate embedded payload that needs to be carved out first. +- `CNRCEXP.EXE` is a modern Win32 helper tool, not part of the original DOS execution path. + +## Raw Full-EXE Import Mapping + +- A separate raw-binary import of the full executable (`crusader-raw.exe`) is usable: Ghidra discovers thousands of functions across a single flat `ram` block. +- Direct `file_offset -> flat_address` mapping from the standalone segment extracts is not reliable for porting names into that raw import. +- The extracted `segNNN_*.bin` files match `CRUSADER_NE.EXE`, but the raw full-EXE import must be mapped by verified byte signatures / known function bodies. +- Verified segment bases in the raw full-EXE import: + - `seg001` base = `0x6E570` (`cursor_update_hover` at `0006:e5d0`, rel `0x0060`) + - `seg021` base = `0x87170` (`entity_count_by_type_a` at `0008:7377`, rel `0x0207`) +- Porting rule for these verified segments: + - `raw_full_exe_flat = verified_segment_base + standalone_segment_relative_offset` +- Naming note: + - `seg001` and `seg021` both contain a keyboard handler; in the full program database, the seg001 copy is named `seg001_input_keyboard_handler` to avoid a symbol collision with seg021 `input_keyboard_handler`. + +### Latest Raw Full-EXE Porting Progress + +- Newly ported and renamed into `CRUSADER-RAW.EXE` from verified `seg001` mapping (`base 0x6E570`): + - `0007:28ce` = `shot_entity_alloc` (`seg001 + 0x435e`) + - `0007:2a19` = `shot_entity_free` (`seg001 + 0x44a9`) + - `0007:2bc9` = `projectile_init_vector` (`seg001 + 0x4659`) + - `0007:3001` = `entity_fire_weapon` (`seg001 + 0x4a91`) + - `0007:3088` = `fire_weapon_from_cursor` (`seg001 + 0x4b18`) + - `0007:30e8` = `projectile_check_hit` (`seg001 + 0x4b78`) + - `0007:319e` = `projectile_step_update` (`seg001 + 0x4c2e`) + - `0007:3298` = `projectile_trace_ray` (`seg001 + 0x4d28`) + - `0007:371d` = `projectile_update_tick` (`seg001 + 0x51ad`) + - `0007:4009` = `projectile_apply_hit` (`seg001 + 0x5a99`) +- Decompiler comments were added on key raw-import projectile functions to preserve provenance for later passes. +- Quick verification from current raw import: + - `entity_fire_weapon` currently decompiles as a thin wrapper that calls `projectile_init_vector`. + - `fire_weapon_from_cursor` still decompiles poorly in the raw import, but disassembly shows it begins by pushing cursor sprite/state data from the `0x27d6` area, consistent with the existing seg001 notes. + +### Raw 000e Parser Helper Cluster + +- A small helper cluster in the raw `000e:` area now appears to implement a fixed-size CRLF record parser/table builder, likely used by startup/config or script-ish text data. +- Newly renamed helpers: + - `000e:345e` = `record_table_init` + - `000e:34cc` = `record_table_destroy` + - `000e:35c6` = `record_table_release_buffer` + - `000e:35ef` = `record_table_next_slot` + - `000e:3639` = `record_table_parse_buffer` + - `000e:3798` = `record_parser_read_line` + - `000e:38f8` = `record_parser_find_marker` +- Current behavior read from raw-import decompilation/disassembly: + - `record_table_init` clears the table header and zeroes 300 words of inline storage. + - `record_table_parse_buffer` walks a CRLF-separated text buffer, captures each line, splits around a marker helper path, and stores parsed entry state into 0x0c-byte records. + - `record_parser_read_line` advances to the next CRLF-delimited line, rejects lines that start with `@` or with non-identifier punctuation, and terminates the line in-place with `0`. + - `record_parser_find_marker` scans forward until an `@` marker or end-of-data; optionally consumes the remaining length from the parser state. + - Helper at `000e:39cc` remains intentionally unnamed for now; disassembly shows it only activates when the current substring begins with `@`, then skips 7 bytes and dispatches through a thunk. + +### Raw 000e RIFF/Animation Cluster + +The `000e:` segment contains a RIFF/AVI streaming animation subsystem. Animation objects have a confirmed field layout (offsets relative to the object base pointer). + +**Animation object field map:** +- `+0xb0` = active/valid flag +- `+0xb4`, `+0xb6`, `+0xb8`, `+0xba`, `+0xbc`, `+0xbe`, `+0xc0`, `+0xc2` = constructor-initialized flags +- `+0xd4` = alive sentinel (must be `-1` for "alive") +- `+0xe4` = paused flag (0 = running) +- `+0xeaf` / `+0xeb1` = far pointer to current RIFF chunk +- `+0xedb` = animation frame stack depth counter (max 9) +- `+0xee1` = frame data from current chunk `+4` +- `+0xeef` = current subframe index +- `+0x1b3` = subframe count +- `+0xef1` = audio completion flag +- `+0x11b` = ring buffer write pointer +- `+0x11f` = ring buffer read pointer +- `+0x117` = ring buffer base +- `+0x123` = ring buffer end (capacity boundary) +- `+0x102` = resource pointer +- `+0xde` = some entry index (multiplied by `0x30` to reach per-entry data at `+0x1c7`) + +**RIFF format notes:** Game uses standard RIFF/IFF: LIST and RIFF header magic (`0x5453494c` = `"LIST"`, `0x46464952` = `"RIFF"`), `"movi"` FourCC subchunk for frames. Audio frames tagged `"01wb"` (`0x62773130`), video frames in a separate path. + +**Newly renamed functions:** + +| Address | Name | Evidence | +|---------|------|---------| +| `000e:2a28` | `riff_find_chunk_by_type` | Walks RIFF LIST/RIFF chunk list; compares each node's FourCC at `+8` vs `param_2`; returns pointer to matching chunk or NULL | +| `000e:2104` | `animation_start` | Finds `"movi"` chunk via `riff_find_chunk_by_type`, inits ring buffer ptrs at `+0x11b` from `+0x117 + duration`, calls `animation_advance_frame`, loops `anim_load_audio_frame` and a second frame-loader thunk path per subframe | +| `000e:12f4` | `animation_advance_frame` | Fixed-point `0x1000` timer arithmetic; checks `+0xe4` (paused), advances ring buffer `+0x11b`/`+0x11f`/`+0x117`/`+0x123`; calls advance thunk | +| `000e:103f` | `animation_tick` | Guard wrapper: checks `param_1+0xd4 != -1`, then calls `animation_advance_frame(param_1, 0)` | +| `000e:06f7` | `anim_load_audio_frame` | Checks chunk tag == `0x62773130` (`"01wb"` = audio stream 1); computes ring buffer free space; copies chunk payload via `0x0000:ffff` thunk; increments subframe index at `+0xeef`; resets at subframe count `+0x1b3` | + +**Unresolved callee:** +- `000e:053d` → `000e:ffb0` (thin wrapper, ffb0 decompiles garbled due to overlapping instructions at `000f:0085`/`000f:0086`). Likely handles video frame loading to pair with `anim_load_audio_frame`. Not renamed. + +**Constructor pattern (`000e:2777`, `000e:2860`, `000e:2969`):** +All three follow the same layout: +1. Call `FUN_000e_e935` (allocator — produces garbled 11KB decompile, not renamed) +2. Set fields `+0xb4` through `+0xc2` on the result +3. Call `000d:ebe3` (multi-step chain initializer: calls `177c`, `1acb`, `0988`, `22bc`, `1d4a`, `2104` in sequence) +4. Call `assert_alive_sentinel` (assertion: checks `+0xd4 != -1`) +5. Call `func_0x000eec83` + +The chain at `000d:ebe3` steps through VM opcode handlers (`000d:177c`, `000d:1acb`, `000d:0988`) that operate on a bytecode VM object with stack pointer at `+0xcc` (decremented by 2 per push) and segment base at `+0xce`. + +**Constructor variant renames (direct analysis):** +- `000e:223d` = `assert_alive_sentinel` +- `000e:2777` = `animation_ctor_variant_a` +- `000e:2860` = `animation_ctor_variant_b` +- `000e:2969` = `animation_ctor_variant_c` + +## Segment Map + +| Segment | Address Range | Purpose | +|---------|--------------|---------| +| CODE_0 | `1000:0000 - 1000:01ff` | Interrupt dispatch table / thunks | +| CODE_1 | `1020:0000 - 1020:0b9f` | Low-level interrupt handlers, mode switching | +| CODE_2 | `10da:0000 - 10da:25ef` | **Main runtime** — C library, I/O, formatting, entry point | +| CODE_3 | `1339:0000 - 1339:0c2f` | **DOS/DPMI services** — INT 21h/31h wrappers, interrupt vector mgmt, fast memcpy | +| CODE_4 | `13fc:0000 - 13fc:27af` | **String data & runtime constants** — error messages, format strings, Phar Lap ID | +| CODE_5 | `1677:0000 - 1677:0e8f` | **EMS/XMS memory management** — expanded memory handlers | +| CODE_6 | `1760:0000 - 1760:7ccd` | **DOS Extender core** — EXP loader, command-line parser, memory management, system init | +| DATA | `1760:7cd0 - 1760:7cdf` | Global data | +| HEADER | `HEADER::0000 - HEADER::044f` | MZ/P2 file header | + +## Named Functions + +### Entry & Startup +| Address | Name | Description | +|---------|------|-------------| +| `10da:7c40` | `entry` | Program entry point — checks CPU, parses command line, launches game | +| `10da:1816` | `main_init_and_run` | Main initialization — loads child EXP, sets up subsystems, runs game | +| `1760:1432` | `parse_cmdline_and_run` | Parses command-line args and invokes main_init_and_run | +| `1760:42fa` | `init_dos_extender` | Initializes Phar Lap 286 DOS extender (CPU check, VCPI/DPMI setup) | + +### Executable Loading +| Address | Name | Description | +|---------|------|-------------| +| `1760:2cdf` | `load_exp_file` | Loads .EXP executable — opens file, reads headers, allocates memory | +| `1760:1dfc` | `load_executable_image` | Parses P2/MZ headers, loads segments, creates LDT entries | +| `1760:24a6` | `apply_relocations` | Applies segment relocations to loaded executable | +| `1760:5eca` | `exec_child_process` | Executes child process with command-line arguments | +| `1760:5fee` | `exec_program_with_args` | Builds command line, locates and executes a program | +| `10da:1f7e` | `load_and_run_child` | Wrapper: loads child EXP and initializes it | + +### System Services +| Address | Name | Description | +|---------|------|-------------| +| `10da:2330` | `dos_exit` | Calls INT 21h AH=4Ch (terminate program) | +| `1760:42aa` | `detect_cpu_type` | Detects CPU: 0=8086, 2=286, 3=386+ | +| `1339:04a6` | `dpmi_set_interrupt_vector` | INT 31h — DPMI set interrupt vector | +| `1339:06ca` | `switch_to_real_mode` | Switches CPU from protected to real mode | +| `1339:06f2` | `switch_to_protected_mode` | Switches CPU from real to protected mode | +| `1339:0076` | `setup_interrupt_handlers` | Configures interrupt vectors via INT 21h | +| `1339:0a38` | `dos_int21h_wrapper` | Simple INT 21h call wrapper | +| `1339:0a82` | `dos_int21h_with_regs` | INT 21h call with register parameters | +| `10da:2360` | `get_flags_register` | Returns CPU FLAGS register | +| `10da:2363` | `set_flags_register` | Sets CPU FLAGS register | + +### Memory Management +| Address | Name | Description | +|---------|------|-------------| +| `1677:0d12` | `cleanup_ems_memory` | Frees EMS (INT 67h) memory handles | +| `10da:14fc` | `init_stack_fill_cc` | Fills stack with 0xCC (INT 3) for debugging/guard | +| `10da:1706` | `get_segment_base_addr` | Computes linear base address from segment descriptor | + +### Task Management +| Address | Name | Description | +|---------|------|-------------| +| `10da:19ca` | `task_switch_to_child` | Context switch to child process | +| `10da:1946` | `task_switch_from_child` | Context switch back from child process | +| `10da:1af4` | `call_termination_handler` | Calls registered termination callback | + +### I/O & Output +| Address | Name | Description | +|---------|------|-------------| +| `10da:00d6` | `flush_output_buffer` | Flushes buffered output via function pointer | +| `10da:0132` | `putchar_buffered` | Writes character to buffer, flushes on newline | +| `10da:0808` | `memcopy_to_buffer` | Copies N bytes from source to destination buffer | +| `10da:178c` | `print_error_message` | Formats and prints load error (references "not loaded: %s") | +| `10da:09e4` | `print_fatal_error` | Prints "Fatal Error" prefix + message | +| `10da:192a` | `print_internal_error` | Prints "Internal Error" message | + +### Interrupt Management +| Address | Name | Description | +|---------|------|-------------| +| `10da:1ec0` | `restore_interrupt_vectors` | Restores INT 2Fh and INT 67h vectors | +| `10da:2249` | `restore_int_2f_67` | Restores INT 15h vector if saved | +| `1760:3d86` | `init_system_check` | Validates system (CPU, DOS version, VCPI/DPMI, memory) | + +### Utility +| Address | Name | Description | +|---------|------|-------------| +| `10da:15ea` | `check_ds_segment` | Returns true if DS == 0x10 (checks data segment selector) | +| `1760:3c9e` | `nop_stub` | Always returns 0 (unused hook) | + +## Key String References + +| Address | String | Context | +|---------|--------|---------| +| `13fc:0016` | `$Id: comhighc.c 1.1 91/08/06...` | Phar Lap C runtime source ID | +| `13fc:0048` | `$Id: comutils.c 1.1 91/08/06...` | Phar Lap utility functions source ID | +| `13fc:0078` | `Serial Number ` | DOS extender serial validation | +| `13fc:14ca` | `Internal Error` | Error class prefix | +| `13fc:14da` | `Fatal Error` | Fatal error class prefix | +| `13fc:156a-1628` | File error messages | Not found, bad format, no memory, etc. | +| `1760:665c` | `Copyright (C) 1986-93 Phar Lap Software, Inc.` | DOS extender copyright | +| `1760:73da` | `-LDTSIZE 4096 -EXTHIGH D0_0000h -NI 18 -ISTKSIZE 3` | Default extender config | +| `1760:76fc-7c5a` | Numbered error messages | System requirement errors (1000-2170) | + +## Architecture Notes + +### Correction: The Game Ships As A Bound NE Executable +**Important**: The installed copy does **not** contain a separate `.EXP` file. `CRUSADER.EXE` is a bound executable with an outer DOS `MZ` stub and an internal `NE` executable image. The Phar Lap loader/runtime code and the game's real segment layout are both described inside this same file. + +The flow is: +1. `entry` → checks DOS version, CPU type +2. `init_dos_extender` → sets up protected mode (VCPI/DPMI) +3. `load_exp_file` → opens the game's `.EXP` file +4. `load_executable_image` → parses P2/MZ headers, creates segments, applies relocations +5. `task_switch_to_child` → transfers control to the actual game code + +For the installed retail copy, this means the currently loaded Ghidra program is only one interpretation of `CRUSADER.EXE`. The next import should target the **NE layer of the same file**, not a missing external `.EXP`. + +### NE Import Details +- File to import: `F:\Apps\Crusader No Remorse\CRUSADER.EXE` +- Outer DOS header: `MZ` +- `e_lfanew`: `0x36F70` +- Internal executable header: `NE` +- Segment count: `145` +- Initial `CS:IP`: `0001:0000` +- Initial `SS:SP`: `0091:2000` + +The currently analyzed protected-mode code at addresses like `10da:7c40` is consistent with the Phar Lap runtime/loader path. To reach the rest of the program, import `CRUSADER.EXE` again using an **NE-aware loader** or a workflow that starts from the internal NE header rather than the outer DOS stub. + +### Segment 1339: Fast Memory Operations +`FUN_1339_02a8` contains an unrolled loop (Duff's device pattern with 57 iterations) — a hand-optimized **fast memory fill/add** routine, typical in DOS game graphics engines. + +### EMS Memory (Segment 1677) +The game uses **EMS (Expanded Memory Specification)** via INT 67h for additional memory beyond the 1MB real-mode limit. Functions in segment 1677 manage EMS page frames and handle allocation/deallocation. + +## NE Segment 1 Analysis — Game Logic Functions (seg001_code_off_37600_len_8400.bin) + +This segment was imported as Raw Binary at base `0x0000`, language `x86:LE:16:Protected Mode`. +All 35+ identified functions renamed and annotated in Ghidra. + +### Cursor Subsystem (0x0060–0x0d5f) + +| Address | Name | Description | +|----------|---------------------------|-------------| +| `0x0060` | `cursor_update_hover` | Hover update: if mouse active & entity set, calls cursor_set_target | +| `0x00e9` | `cursor_set_target` | Positions cursor on entity, updates sprite + direction visual | +| `0x0322` | `cursor_shutdown` | Frees cursor resources, resets state | +| `0x0398` | `cursor_animation_update` | Angle-based cursor rotation (0x27d4, 0-359 → 0x168=360). Sprite at 0x27d6 | +| `0x050f` | `cursor_draw_tick` | Per-frame cursor draw (calls cursor_animation_update if dirty) | +| `0x0c24` | `action_key_valid` | Returns 1 if action code (param_1) is a valid game action key | +| `0x0d5f` | `cursor_direction_input` | Arrow-key input: rotates cursor angle, updates direction sprite | + +### Input Handling + +| Address | Name | Description | +|----------|-------------------------|-------------| +| `0x0526` | `input_keyboard_handler`| Key dispatch: 0x01=LMB, 0x0D/0x0C=scroll, 0x2C=save, 0x44=load | + +### Cursor State Data (at DS:0x27xx) +| Address | Field | Meaning | +|---------|-------|---------| +| `0x27c4` | cursor_sel1 | Selection counter 1 | +| `0x27c6` | cursor_sel2 | Selection counter 2 | +| `0x27c8` | current_entity | Handle to currently targeted entity | +| `0x27ca–0x27ce` | cursor_state | Cursor interaction state bytes | +| `0x27d0` | cursor_entity_type | Current entity type index | +| `0x27d2` | z_offset | Z-height offset for terrain adjustment | +| `0x27d4` | cursor_angle | Rotation angle (0–359) | +| `0x27d6` | cursor_sprite | Sprite handle for cursor visual | +| `0x27d8` | cursor_dirty | Set when cursor needs redraw | +| `0x27d9` | cursor_active | Master cursor enabled flag | +| `0x27da` | cursor_no_turn | Flag disabling cursor rotation | +| `0x27ed` | difficulty | Enemy accuracy divisor (used in projectile_init_vector) | +| `0x27fd` | hard_mode | Two-step mode (combat vs. explore) | +| `0x27fe` | move_mode | Movement phase flag | +| `0x27ff` | mouse_active | Mouse/input system active | +| `0x2800`–`0x2811` | various | UI state: active sprite, facing byte, cur entity handle | +| `0x283f`/`0x2841` | menu_obj_ptr | Active menu/dialog object far pointer | +| `0x2844` | in_save | In-progress save game flag | +| `0x290e` | entity_count | Number of active entities | +| `0x2910`–`0x2947` | snap_type_ids[10] | Entity types that snap-to-ground in snap_entity_to_ground | + +### Input / Action Dispatch + +| Address | Name | Description | +|----------|---------------------------|-------------| +| `0x2420` | `entity_command_dispatch` | Dispatches player commands to target entity; reads 0x27d0, 0x2de4, sends events 0x14/0xf, handles state machine 0x27ca/0x27cd/0x27ce | +| `0x279a` | `cheat_code_check` | Checks entity byte+1 vs cheat sequence at 0x2833 (counter 0x283d); on full match, toggles 0x844/0x6045 and spawns vtable 0x287b/0x2892 | + +### Menu / Event Callbacks + +| Address | Name | Description | +|----------|---------------------------|-------------| +| `0x2e53` | `cursor_event_notify_a` | Vtable thunk: forwards event to 0x27ca area handler | +| `0x2e96` | `cursor_event_notify_b` | Vtable thunk: forwards event to 0x27ca area handler (alt path) | +| `0x2ed9` | `menu_event_notify_a` | Vtable thunk: forwards event to 0x2843 (near menu object) | +| `0x2f0c` | `menu_event_notify_b` | Vtable thunk: forwards event to 0x2843 (alt path) | +| `0x2ff3` | `stub_noop_2ff3` | Empty stub, noop | +| `0x2ff8` | `entity_collision_callback_a` | Calls touch handler then func(entity+0x1e, seg, 2); opt: extra func if param_3&1 | +| `0x3046` | `set_active_menu` | Writes param_1/param_2 to 0x283f/0x2841 (active menu far pointer) | +| `0x3058` | `entity_collision_callback_b` | Same as entity_collision_callback_a (second vtable entry) | + +### Entity System (0x2401–0x5a50) + +| Address | Name | Description | +|----------|------------------------------|-------------| +| `0x2401` | `clear_cursor_selection` | Zeros 0x27c4/0x27c6 (selection counters) | +| `0x2899` | `cursor_switch_target_entity`| Switches cursor target: unloads old entity, loads new, re-registers | +| `0x29d8` | `get_z_offset` | Returns func() + *(0x27d2) = adjusted Z/height | +| `0x2a09` | `is_player_in_range` | Checks if entity is at player (0x2de4) X/Y +/-0xf0 range | +| `0x2a46` | `entity_ai_update_loop` | Loops entities 2–255, checks visibility, triggers fire/move | +| `0x2c36` | `ui_update_callback` | Calls cursor_state_clear then vtable[2] on menu object | +| `0x2c6b` | `cursor_state_clear` | Clears cursor state bytes 0x27ca–0x27ce, clears entity flag bit1 | +| `0x2c92` | `dialog_spawn` | Allocates dialog object, vtable=0x28b5, registers callback at 0x39ca | +| `0x2d47` | `entity_pick_handler` | Handles entity selection or save-game trigger (type 0x38d) | +| `0x2df9` | `clear_active_menu` | Zeros 0x283f/0x2841 (active menu far pointer) | +| `0x2e18` | `game_mode_init` | Initializes game mode state, resets sprite/cursor/menu state | +| `0x2f3f` | `entity_table_set_sprite` | Reads 0x7df9+slot*2; writes entity type table 0x7e1e[slot*0x79+0x0d]=param_2, +0x10=0 | +| `0x3c97` | `snap_entity_to_ground` | If entity type in snap_type_ids[10], resets Z to 0xf0 and adjusts XY | +| `0x3d6e` | `spawn_entity_checked` | Spawns entity with explosion pool limit check (0x84c0, 0x84c2) | +| `0x3f2f` | `entity_spawn` | Allocates entity, vtable=0x29aa/0x39ca, positions it | +| `0x40d4` | `entity_remove` | Removes entity: destroys sprites, clears 0x2802/0x2804 if needed | +| `0x4172` | `entity_animation_frame_update`| Advances/retreats anim frame ([+0x1d]) toward target [+0x1c/0x1b] based on quality | +| `0x42f8` | `stub_noop_42f8` | Empty stub, noop | +| `0x42fd` | `entity_registry_decrement` | Calls cleanup func then decrements entity count at 0x290e | +| `0x4314` | `entity_sprite_move_delta` | Updates shot sprite handle (entity+0x3f) position by adding delta params | +| `0x4552` | `entity_set_position` | Sets entity+0x3e (type_handle), world_x/y (entity+0x45/47), base_x/y (entity+0x4f/51) | +| `0x452b` | `shot_set_spawn_pos` | Calls entity_set_position then sets entity+0xbe = param_3 (extra spawn field) | +| `0x4591` | `entity_try_place` | entity_set_position with validation — position only set if placement succeeds | +| `0x5092` | `entity_deactivate` | Calls vtable[2] to deactivate, or finds in registry and removes | +| `0x5a50` | `entity_list_contains` | Checks if entity ptr exists in active entity list at 0x294c | +| `0x5b05` | `stub_noop_5b05` | Empty stub, noop | + +### Entity Object Layout (NE Segment 1 entities) +| Offset | Field | Meaning | +|--------|-------|---------| +| `+0x00` | vtable_ptr | Vtable pointer (0x29aa for generic, 0x2a57 for debris) | +| `+0x02` | slot_index | Entity slot index (used for registry at 0x39ca) | +| `+0x04` | entity_type | Entity type ID | +| `+0x19`/`+0x1a` | flags | Entity flags (bit0=debris, bit1=cleared by cursor_state_clear, bit6=active, bit8=valid) | +| `+0x1b` | vel_x | X velocity (clamped ±0x20) | +| `+0x1c` | vel_y | Y velocity (clamped ±0x20) | +| `+0x1d` | vel_z | Z velocity (clamped ±0x10) | +| `+0x1e` | fire_handle | Weapon/fire handle | +| `+0x1f` | is_enemy | 1 if entity is an enemy type | +| `+0x20`/`+0x21` | pos_frac_x/y | Fractional position (sub-tile) for movement | +| `+0x22` | pos_frac_z | Fractional Z | +| `+0x36` | weapon_type | Active weapon type ID | +| `+0x38` | facing | Current facing direction (0–15) | +| `+0x3c` | sprite_handle | Sprite for this entity | +| `+0x3f` | shot_sprite | Sprite handle for active projectile (0xFFFF = none) | +| `+0x45`/`+0x47`/`+0x49` | world_x/y/z | Current world position (integer) | +| `+0x4f`/`+0x51`/`+0x53` | base_x/y/z | Base/spawn position | +| `+0x54`/`+0x56`/`+0x58` | prev_x/y/z | Previous frame position | +| `+0x59` | attack_active | Attack in progress flag | +| `+0x5a` | at_target | Reached target flag | +| `+0x5e`–`+0x65` | delta_x/y/z/high | Per-step movement deltas (fixed point) | +| `+0x66`/`+0x68` | step_active | Stepping active (1=yes, 0=off) | +| `+0x6a`/`+0x6c` | weapon_slot/dist | Weapon slot and total travel distance | +| `+0x6e` | delta_z | Alt Z delta | +| `+0x70` | projectile_type | Projectile class (2/0xD=splash, 3=spread, 5=homing, 0xE=chain) | +| `+0x72`/`+0x74`/`+0x76` | target_x/y/z | Target position with deviation | +| `+0x77` | target_entity | Target entity handle | +| `+0x79` | secondary_pos | Secondary position struct pointer | +| `+0xad` | owner_entity | Owning entity handle | +| `+0xaf` | shot_owner_flags | Shot owner (entity/player) | +| `+0xb1` | bounce_count | Bounce counter (used with homing, type 5) | +| `+0xb3` | has_bounce | Has bounce trajectory active | +| `+0xbd` | actor_type | Actor type byte (used for direction table lookups) | + +### Shot Entity Lifecycle (0x435e–0x44a9) + +| Address | Name | Description | +|----------|----------------------------|-------------| +| `0x435e` | `shot_entity_alloc` | Alloc/init shot entity: vtable=0x297e, registry vtable=0x2969, zeros all state, copies player pos to +0xb5/b7 | +| `0x44a9` | `shot_entity_free` | Cleans up shot entity: frees sprite at +0x3c if valid (set to 0xFFFF), clears callbacks; optional full free if flag&1 | + +### Projectile / Combat (0x4659–0x5a99) + +| Address | Name | Description | +|----------|----------------------------|-------------| +| `0x4659` | `projectile_init_vector` | Sets up shot trajectory: target XY±deviation, step rate from weapon table at 0x2536 | +| `0x4a91` | `entity_fire_weapon` | Fires weapon from entity using 0x129b/0x12ac direction offset tables | +| `0x4b18` | `fire_weapon_from_cursor` | Gets cursor angle sprites, fires projectile at cursor target | +| `0x4b78` | `projectile_check_hit` | Hit test: if entity_type==0 uses bbox+0x79; else full 3D range; copies +0xa0→+0x77 (hit entity) | +| `0x4c2e` | `projectile_step_update` | Advances projectile one step; type 3 spawns sub-shots via spawn_entity_checked | +| `0x4d28` | `projectile_trace_ray` | Interpolated path trace: divides distance/0x10 into steps, collision checks each step; on hit calls projectile_apply_hit + entity_deactivate | +| `0x51ad` | `projectile_update_tick` | Full projectile tick: move, check reach target, bounce, call projectile_check_hit | +| `0x5a99` | `projectile_apply_hit` | Applies hit effects: if impacted obj byte+6 non-zero, calls damage func with weapon_slot/type/target/owner | + +### Weapon Type Table (0x2536) +- Each entry is 0x11 bytes (17), accessed as `weapon_type * 0x11` +- `[0]` = step divisor for distance calculation +- `[0x19]` = max range threshold (used in projectile_update_tick) + +### Direction Tables (0x129b / 0x12ac) +- Indexed by facing (0–15): dx offsets at 0x129b, dy offsets at 0x12ac +- Values are multiplied by distance (e.g. `*0x500`) for projectile spawn offsets + +### Collision Detection (0x60c1–0x621e) + +| Address | Name | Description | +|----------|----------------|-------------| +| `0x60c1` | `aabb_overlaps_3d` | 3D AABB overlap test — box layout [xmin,ymin,zmin,_,_,xmax,_,ymax,_,zmax] | +| `0x621e` | `bbox_translate` | Translates a 3D bounding box by (dx, dy, dz) — both min and max points | + +### Enemy AI / Spawning (0x6aed–0x6d21) + +| Address | Name | Description | +|----------|----------------------------|-------------| +| `0x6aed` | `map_find_spawn_point` | Finds map tile matching entity conditions; returns packed XYZ tile coords | +| `0x6bfc` | `actor_find_in_view` | Finds actor visible in current view frustum (temp data at 0x7eca) | +| `0x6ce9` | `enemy_spawn_with_target` | Wrapper: spawns enemy with player as target (param5=1) | +| `0x6d05` | `enemy_spawn_no_target` | Wrapper: spawns enemy without targeting player (param5=0) | +| `0x6d21` | `enemy_spawn_at_position` | Full enemy spawn: activates entity, assigns velocity from direction table (0x2a00/4/A) | + +### Player / HUD + +| Address | Name | Description | +|----------|-------------------------------|-------------| +| `0x50ee` | `player_position_update` | Updates player position from direction data; clamps to screen bounds | +| `0x6ff7` | `player_health_update_and_effect` | Encodes player HP into RGB bitfields at 0x7e46+0x1bec, spawns effect | + +### Destruction / Death (0x7490–0x75ff) + +| Address | Name | Description | +|----------|---------------|-------------| +| `0x7490` | `debris_spawn`| Spawns debris/fragment: vtable=0x2a57/0x2a1a, velocity, facing, linked list | +| `0x75ff` | `entity_die` | Death handler: spawns 1–4 debris objects, picks best explosion direction | + +### Entity Type Constants (weapon_type/entity class) +| Value | Entity Class | +|--------|--------------| +| `0x17` | Robot/mech type A | +| `0x18` | Robot/mech type B | +| `0x1` through `0x3c` | Various entity/weapon types | +| `0x3d` | Robot/mech type C | +| `0x3e` | Robot/mech type D | +| `0x2f5`–`0x2f7` | Special movement entity | +| `0x595`/`0x597` | Platform/elevator entities | +| `0x31c`/`0x322`–`0x327` | Explosive/effect entities | +| `0x38d` | Save game trigger entity | +| `0x426` | Spark/scatter sub-shot | +| `0x59a` | Player cursor/select indicator | + +### Entity Data Table at 0x7e1e +- Stride: `0x79` bytes (121 bytes per entry) +- Indexed by entity type (integer) or entity slot +- `+0x5a` offset = flags byte (bit4 = special entity flag, bit3 = armor/shield flag) +- `+0x68` = targeting flag + +### Map / Resource Tables +| Address | Content | +|---------|---------| +| `0x2833` | Cheat code input sequence (null-terminated) | +| `0x283d` | Cheat sequence match position counter | +| `0x7ded` | Map X coordinate array (2 bytes per entry) | +| `0x7df1` | Map Y coordinate array (2 bytes per entry) | +| `0x7df5` | Map Z array (1 byte per entry) | +| `0x7df9` | Entity state array (2 bytes per slot) | +| `0x7e46` | Player state block far pointer | +| `0x7e1e` | Entity type table (stride 0x79) | + +### Entity Vtable Index (NE Segment 1) +| Address | Entity Class | +|---------|-------------| +| `0x28b5` | Dialog/menu object vtable | +| `0x287b` | Cheat-spawned entity (cheat ON) vtable | +| `0x2892` | Cheat-spawned entity (cheat OFF) vtable | +| `0x2969` | Entity registry vtable (stored at 0x39ca+slot*4, not entity's own vtable) | +| `0x297e` | Shot/projectile entity vtable | +| `0x29aa` | Generic/AI entity vtable | +| `0x2a1a` | Corpse entity vtable (variant) | +| `0x2a33` | Actor/corpse entity vtable | +| `0x2a57` | Debris fragment entity vtable | + +## Next Steps + +1. ✅ **NE Segment 1 imported and analyzed** — all 58 identified functions renamed and annotated +2. **Import additional NE segments** — priority: segments 22, 30, 59, 86 (segment 21 complete) +3. **Analyze additional segments** — apply same decompile→rename→annotate workflow +4. **Map file format loaders** — `.FLX`, `.SHP`, `.MAP`, `.TNT` resource formats +5. **Cross-reference entity type constants** with game entities (robots, platforms, triggers) +6. **Identify external segment calls** — the `func_0x0000ffff()` placeholders are all cross-segment calls; resolving them requires importing the referenced segments + +--- + +## NE Segment 21 Analysis — Timer/Event Dispatch System + +**File**: `seg021_code_off_50200_len_4486.bin` | **File Offset**: 0x50200 | **Length**: 0x4486 bytes +**Ghidra Load**: RAM `0000:0000 – 0000:4485`, x86 16-bit Protected Mode, base 0x0000 +**Functions**: 88 total (87 renamed + `input_keyboard_handler` pre-existing) + +### Subsystem Summary + +Segment 21 implements the **hardware-level timer interrupt and entity event dispatch system** — Crusader's real-time task scheduler. Key responsibilities: + +- Programs and services the Intel 8253 **PIT timer** (I/O ports 0x40/0x43) +- Manages three **entity dispatch lists**: timer list (0x39d4), input list (0x39e3), render list (0x3a10) +- Maintains the **entity pool** at 0x39b0 (same pool as seg001; these segments share DS) +- Provides **event queue** (32-slot circular buffer at 0x31cc) +- Handles **save/load** serialization of the entire entity system +- Controls **keyboard/interrupt locks** and deferred scheduling + +### Function Groups + +#### Entity Pool Management (0x0207–0x0483) +| Address | Name | Notes | +|---------|------|-------| +| `0x0207` | `entity_count_by_type_a` | Count entities matching type+event; filters DEAD flag (0x8) | +| `0x0297` | `entity_count_by_type_b` | Identical logic to 0x0207 (compiler duplicate) | +| `0x0327` | `entity_find_free_slot` | Scan pool for null entry; calls panic if full; returns slot or 0xFFFF | +| `0x038f` | `entity_register` | Write far ptr to entity_list, group to entity_data, vtable to registry; inc count | +| `0x044d` | `entity_get_ptr_raw` | Read entity far ptr from pool slot (may be null) | +| `0x0483` | `entity_get_ptr` | Safe wrapper: verifies non-null, returns offset only | + +#### Event Dispatch (0x04f3–0x08be) +| Address | Name | Notes | +|---------|------|-------| +| `0x04f3` | `entity_dispatch_reset_all` | Fires event code 0x21 (reset/init) to all entities | +| `0x050d` | `entity_clear_deferred_flags` | Clears DEFERRED bit (0x200) from up to N=0x3998 entities | +| `0x059e` | `entity_fire_event_broadcast` | Dispatch event to all matching entities; calls vtable[6]; respects 0x200 deferred flag | +| `0x06f4` | `entity_fire_event_type_include` | Fire only entities whose type IS in given list (up to 10, 0x0d=end) | +| `0x08be` | `entity_fire_event_type_exclude` | Fire only entities whose type is NOT in given list | +| `0x0a8e` | `input_keyboard_handler` | (pre-existing) OS-level key router: 0x0d=scroll+, 0x01=action, 0x2c=save, 0x44=load | + +#### Entity Iterator / Linker (0x0bb7–0x106b) +| Address | Name | Notes | +|---------|------|-------| +| `0x0bb7` | `entity_link` | Cross-link two entities; skips if flag 0x400 set | +| `0x0c34` | `entity_find_first` | Init iterator 0x39fa=3; find first entity matching saved type/event at 0x399a/0x399c | +| `0x0cec` | `entity_find_next` | Continue iterator from 0x39fa cursor | +| `0x0dad` | `timer_entity_find_by_event` | Find entity handling event in range 0xf0-0xf7; checks bit 0x1000; writes to 0x3993 | +| `0x0e82` | `entity_find_by_priority` | Walk priority chain at 0x39d4; find entity matching source/event at 0x3993 | +| `0x0fc8` | `entity_set_cursor` | Validate flag 0x800; set cursor 0x3993 = param_1 (slot) | +| `0x100c` | `entity_get_cursor` | Return entity at 0x39bf if valid and not dead | +| `0x106b` | `entity_relink` | Re-link: find by event, walk priority chain, call set-link vtable funcs | + +#### Entity Lifecycle (0x1133–0x131d) +| Address | Name | Notes | +|---------|------|-------| +| `0x1133` | `entity_unregister` | Full removal: dec sprite type count, vtable cleanup, dec total, update masks | +| `0x1202` | `entity_slot_clear` | Zero pool slot (0x39b0), registry slot (0x39ca), group data (0x39b4) | +| `0x1245` | `entity_layer_set` | Write 0x39c9 (active layer ID) if changed; set dirty flag 0x39a2 | +| `0x125d` | `entity_check_overdue` | If entity_is_overdue: set bit 0x40 on entity+0x16 | +| `0x127c` | `entity_is_overdue` | Return 1 if entity index > 0x39bf and flag 0x39c2 set | +| `0x129b` | `entity_list_call_update` | For all entities where entity+0x0e & param_3 != 0: call vtable[8] | +| `0x131d` | `entity_set_pending` | Write param to 0x3995 (next entity to register); error if already set | + +#### Entity System Init/Shutdown (0x133e–0x1705) +| Address | Name | Notes | +|---------|------|-------| +| `0x133e` | `entity_system_init` | Alloc all entity pool buffers (see decompiler comment); init three lists; clear event state | +| `0x14bc` | `entity_system_flush_normal` | Finalize (vtable[10]) then free all non-deferred active entities | +| `0x158d` | `entity_system_flush_deferred` | Same as flush_normal for deferred entities | +| `0x165c` | `entity_process_pending_deletes` | Free entities marked DEAD (flag & 0x8); dec 0x399e counter | +| `0x1705` | `entity_system_shutdown` | Full shutdown: flush normal, flush deferred, process deletes, free all pools | + +#### Save / Load (0x1851–0x1d21) +| Address | Name | Notes | +|---------|------|-------| +| `0x1851` | `event_queue_state_reset` | Zero ring buffer state tables (0x334e, 0x364e), queue ptrs (0x31c8/0x31ca) | +| `0x18ce` | `level_load` | Full level load: shutdown + reinit + deserialize all entities via vtable[12] | +| `0x1d21` | `save_game` | Serialize entity system: arrays + each entity via vtable[14]; magic check 0x3a21==0xed | + +#### PIT Timer / Hardware (0x2300–0x2975) +| Address | Name | Notes | +|---------|------|-------| +| `0x2300` | `pit_timer_program` | OUT 0x43, 0x36; OUT 0x40, lo; OUT 0x40, hi — raw PIT channel 0 program | +| `0x2316` | `pit_timer_set_hz` | Validates divisor <= 0xd688; stores at 0x39ce; calls pit_timer_program | +| `0x23a5` | `pit_timer_tick_handler` | Timer ISR: iterates 0x39d4 timer list, fires vtable callbacks per layer/mode | +| `0x25fc` | `timer_entity_active` | Check 0x3987/0x398b for active timer entity (mode-dependent) | +| `0x264c` | `timer_entity_get_current` | Get ptr from 0x3987 or 0x398b based on 0x3991 mode flag | +| `0x2668` | `timer_entity_enable` | Set ENABLED flag (0x400), inc counter, insert into timer list, reprograms PIT | +| `0x2745` | `timer_entity_disable` | Clear ENABLED, dec counter, reprograms PIT; if list empty calls interrupt_request_cancel | +| `0x2975` | `timer_recompute_hz` | Scan timer list; find smallest time_period (+0x38/+0x3a); call pit_timer_set_hz | + +#### Interrupt / Lock Control (0x283a–0x294b) +| Address | Name | Notes | +|---------|------|-------| +| `0x283a` | `interrupt_lock_acquire` | Re-entrant acquire on 0x31c7 (interrupt lock) | +| `0x2870` | `interrupt_lock_release` | Release 0x31c7 | +| `0x289b` | `entity_lock_acquire` | Re-entrant acquire on 0x39aa (entity system lock) | +| `0x28d5` | `entity_lock_release` | Release 0x39aa | +| `0x290d` | `interrupt_request_schedule` | Set deferred IRQ flags 0x39ab and 0x398f (or 0x39a9 in sync mode) | +| `0x294b` | `interrupt_request_cancel` | Clear IRQ request flags | + +#### Timer Loop / Deferred State (0x2a5f–0x2ad8) +| Address | Name | Notes | +|---------|------|-------| +| `0x2a5f` | `timer_event_loop` | **Main game loop**: polls player tick counter at 0x2de4; busy-waits; fires optional callback; stores delta to 0x3a00/0x3a02 | +| `0x2ac2` | `timer_deferred_reschedule` | If deferred mode flag 0x39b8 set, call reschedule | +| `0x2ad8` | `timer_snapshot_deferred` | Copy 0x39a9 → 0x39b8; call interrupt handler if 0x39a9 set | + +#### Event Queue (0x2c73–0x3364) +| Address | Name | Notes | +|---------|------|-------| +| `0x2c73` | `event_queue_drain` | Drain circular queue; call event_queue_dequeue while 0x31c8 != 0x31ca; reset state | +| `0x2ca2` | `mouse_button_check` | Return 1 if BIOS 0x31a4 bit 0x10 set AND 0x39af (mouse enable) set | +| `0x2cbc` | `stub_noop_2cbc` | Empty stub function | +| `0x2cd7` | `bios_keyboard_flags_write` | Write param to 0x400:0017 (BIOS keyboard flags at segment 0x40, offset 0x17) | +| `0x2cf2` | `input_event_dispatch` | Dispatch display event 0x10 to input list entities with flag 0x100 and 0xc bits set | +| `0x2dc3` | `event_queue_push` | Push event to circular queue (write ptr 0x31ca); calls event_queue_is_full check | +| `0x3276` | `keyboard_state_read` | INT 16h AX=0: read raw keyboard state into 0x31a4 | +| `0x328b` | `keyboard_acquire` | If not locked (0x31c6): INT lock, read keyboard, set lock flag | +| `0x32cc` | `keyboard_release` | If locked: unlock, clear 0x31c6 | +| `0x3304` | `event_queue_count` | Count pending events: 0x31ca - 0x31c8 (circular) | +| `0x333d` | `event_queue_is_full` | Return 1 if ((0x31ca+1) mod 32) == 0x31c8 | +| `0x3364` | `event_queue_dequeue` | Read from ring buffer (0x31cc + 0x31c8*0xc, entry size 0xc), advance read ptr | + +#### Event Subscription and Bitmask Helpers (0x34dd–0x3878) +| Address | Name | Notes | +|---------|------|-------| +| `0x34dd` | `event_queue_process_all` | Drain queue; for each event find listener entities in 0x39e3; call vtable[0x14] | +| `0x35e9` | `event_queue_set_mode` | Write low 2 bits to 0x334c; call keyboard_interrupt_call | +| `0x35fb` | `event_queue_set_param` | Write low 5 bits to 0x334d; call keyboard_interrupt_call | +| `0x360d` | `keyboard_interrupt_call` | INT 16h (raw BIOS keyboard services call) | +| `0x3630` | `entity_validate_indices` | Debug assert: verify entity+0x02 (slot_index) == pool position for all entities | +| `0x369b` | `typemask_set_bit` | Set bit at 0x3a04 + (param>>3), bit (param & 7) — entity type present bitmask | +| `0x36d4` | `typemask_clear_bit` | Clear bit in 0x3a04 bitmask | +| `0x370f` | `typemask_update` | If entity type has listeners (entity_find_first != 0): set bit; else clear | +| `0x3744` | `typemask_test_bit` | Test bit in 0x3a04; return 1 if entity type has registered listeners | +| `0x377d` | `event_subscription_set` | Set subscription bit in 0x3a08 buffer | +| `0x37b2` | `event_subscription_clear` | Clear subscription bit in 0x3a08 buffer | +| `0x37e9` | `event_subscription_update` | If entity has listeners: set bit; else clear (driven by entity_find_first result) | +| `0x3825` | `event_subscription_test` | Test subscription bit in 0x3a08; return 1 if subscribed | +| `0x3864` | `event_state_clear` | Zero entire 0x3a0c event use-count buffer (0x4000 bytes = 8192 uint16s) | +| `0x3878` | `event_use_count_increment` | Increment 64-bit counter at 0x3a0c[entity_event_type*4] | + +#### Input / Render List (0x38c2–0x3ae9) +| Address | Name | Notes | +|---------|------|-------| +| `0x38c2` | `input_event_broadcast` | Dispatch input event 0x40 to all render-list entities with flag 0x40; uses counter 0x39ad | +| `0x39a1` | `subscribe_to_render_list` | Add entity to 0x3a10 list; set flag bit 0x40; inc 0x3a1f | +| `0x3a13` | `unsubscribe_from_render_list` | Remove entity from 0x3a10; clear bit 0x40; dec 0x3a1f | +| `0x3404` | `subscribe_to_input_list` | Add entity to 0x39e3 list; check flag 0x100; set bit 0x80; inc 0x39c3 | +| `0x3477` | `unsubscribe_from_input_list` | Remove entity from 0x39e3; clear bit 0x80; dec 0x39c3 | +| `0x3a78` | `entity_lists_init` | Init three linked lists with sentinel vtable 0x3a89; write head vtable 0x2d10 | +| `0x3ae9` | `entity_lists_reset` | Call external reset + reinit 0x39e3 and 0x39d4 lists | + +### Entity Object Field Layout (as used in Seg21) + +| Offset | Field | Type | Description | +|--------|-------|------|-------------| +| `+0x00` | vtable_ptr | far ptr | Pointer to entity's vtable dispatch table | +| `+0x02` | slot_index | uint16 | Entity's own slot number in pool | +| `+0x04` | source_type | uint16 | Source/owner entity type (event matching) | +| `+0x06` | event_type | uint16 | Event type this entity handles | +| `+0x08` | flags_byte | uint8 | Low 5 bits = sprite group ID | +| `+0x0e` | capability_mask | uint16 | Bitmask of supported event capabilities | +| `+0x16` | state_flags | uint16 | bit3=DEAD, bit8=REGISTERED, bit9=ACTIVE, bit10=ENABLED, bit11=HAS_TIMER, bit13=IS_IRQ_HANDLER | +| `+0x18` | flags2 | uint16 | bit6=IN_RENDER_LIST, bit7=IN_INPUT_LIST, bit9=DEFERRED | +| `+0x1e` | priority_chain | far ptr | Priority chain entries (entity_find_by_priority) | +| `+0x20` | priority_count | uint16 | Count of priority chain entries | +| `+0x38` | time_period_lo | uint16 | Timer period low word (PIT frequency calc) | +| `+0x3a` | time_period_hi | uint16 | Timer period high word | + +### Vtable Layout (Seg21 usage) + +| Slot | Byte offset | Prototype | Purpose | +|------|-------------|-----------|---------| +| [6] | `+0x0c` | `handle_event(entity, CS, type, param)` | Event callback | +| [8] | `+0x10` | `update(entity, CS, capability_mask)` | Per-tick update | +| [10] | `+0x14` | `finalize(entity, CS)` | Cleanup/shutdown | +| [12] | `+0x18` | `load(entity, CS, file_ptr, CS)` | Deserialize from save | +| [14] | `+0x1c` | `save(entity, CS, file_ptr, CS)` | Serialize to save | +| [16] | `+0x20` | `set_backref(entity, CS, list_ptr)` | Set back-reference | +| [20] | `+0x28` | `dispatch_callback(entity, CS, event_id, 0, data_ptr)` | Generic dispatch | + +### Key Global Data (Seg21 — additions to DS) + +| Address | Name | Description | +|---------|------|-------------| +| `0x31a4` | bios_key_state | Raw INT 16h keyboard state | +| `0x31c6` | keyboard_lock | Keyboard acquired flag | +| `0x31c7` | interrupt_lock | Interrupt lock flag (re-entrant) | +| `0x31c8` | queue_read_ptr | Event queue read index (0–31) | +| `0x31ca` | queue_write_ptr | Event queue write index | +| `0x31cc` | event_queue_base | Ring buffer, 32 entries × 0xc bytes | +| `0x334c` | queue_mode | Event queue mode bits (0–1) | +| `0x334d` | queue_param | Event queue param bits (0–4) | +| `0x39b0` | entity_list | Far ptr to entity far-ptr array (count×4) — **shared with seg001** | +| `0x39b4` | entity_data | Far ptr to group/sprite-ID array (count×2) | +| `0x39b9` | entity_max_count | Max capacity of entity pool | +| `0x39bb` | entity_count | Total registered entity count | +| `0x39c9` | active_layer | Current active entity layer/group ID | +| `0x39ca` | entity_registry | Far ptr to vtable dispatch array (count×4) — **shared with seg001** | +| `0x39ce` | pit_divisor | Current PIT timer divisor | +| `0x39d4` | timer_list | Intrusive linked list: timer-dispatch entities | +| `0x39e3` | input_list | Intrusive linked list: input-handler entities | +| `0x3a04` | typemask_buf | Far ptr to entity type present bitmask (0x480 bytes) | +| `0x3a08` | evt_sub_buf | Far ptr to event subscription bitmask (0x2400 bytes) | +| `0x3a0c` | evt_state_buf | Far ptr to event use-count table (0x4000 bytes) | +| `0x3a10` | render_list | Intrusive linked list: render-callback entities | +| `0x3a21` | save_magic | Must be 0xed (-0x13) for valid save | +| `0x3a70` | default_registry_vtable | Default vtable written to entity_registry slots on register | +| `0x3a89` | list_sentinel_vtable | Sentinel vtable written to list head nodes | + +--- diff --git a/crusader_ne_segments.csv b/crusader_ne_segments.csv new file mode 100644 index 0000000..af28151 --- /dev/null +++ b/crusader_ne_segments.csv @@ -0,0 +1,146 @@ +"Segment","Type","FileOffset","Length","Flags","MinAlloc","File" +"1","code","0x37600","0x8400","0xD00","0x8400","D:\Ghidra\Crusader\NE_segments\seg001_code_off_37600_len_8400.bin" +"2","code","0x40000","0x2B0","0xD00","0x2B0","D:\Ghidra\Crusader\NE_segments\seg002_code_off_40000_len_2B0.bin" +"3","code","0x40400","0x55A","0xD00","0x55A","D:\Ghidra\Crusader\NE_segments\seg003_code_off_40400_len_55A.bin" +"4","code","0x40A00","0x10B1","0xD00","0x10B1","D:\Ghidra\Crusader\NE_segments\seg004_code_off_40A00_len_10B1.bin" +"5","code","0x41E00","0x8D7","0xD00","0x8D7","D:\Ghidra\Crusader\NE_segments\seg005_code_off_41E00_len_8D7.bin" +"6","code","0x42C00","0x75E","0xD00","0x75E","D:\Ghidra\Crusader\NE_segments\seg006_code_off_42C00_len_75E.bin" +"7","code","0x43600","0x484","0xD00","0x484","D:\Ghidra\Crusader\NE_segments\seg007_code_off_43600_len_484.bin" +"8","code","0x43C00","0x1386","0xD00","0x1386","D:\Ghidra\Crusader\NE_segments\seg008_code_off_43C00_len_1386.bin" +"9","code","0x45400","0x495","0xD00","0x495","D:\Ghidra\Crusader\NE_segments\seg009_code_off_45400_len_495.bin" +"10","code","0x45A00","0xD92","0xD00","0xD92","D:\Ghidra\Crusader\NE_segments\seg010_code_off_45A00_len_D92.bin" +"11","code","0x46E00","0x5B1","0xD00","0x5B1","D:\Ghidra\Crusader\NE_segments\seg011_code_off_46E00_len_5B1.bin" +"12","code","0x47600","0x94B","0xD00","0x94B","D:\Ghidra\Crusader\NE_segments\seg012_code_off_47600_len_94B.bin" +"13","code","0x48200","0x1F6C","0xD00","0x1F6C","D:\Ghidra\Crusader\NE_segments\seg013_code_off_48200_len_1F6C.bin" +"14","code","0x4AA00","0x526","0xD00","0x526","D:\Ghidra\Crusader\NE_segments\seg014_code_off_4AA00_len_526.bin" +"15","code","0x4B200","0x1C68","0xD00","0x1C68","D:\Ghidra\Crusader\NE_segments\seg015_code_off_4B200_len_1C68.bin" +"16","code","0x4D400","0x677","0xC00","0x677","D:\Ghidra\Crusader\NE_segments\seg016_code_off_4D400_len_677.bin" +"17","code","0x4DC00","0x1A7","0xD00","0x1A7","D:\Ghidra\Crusader\NE_segments\seg017_code_off_4DC00_len_1A7.bin" +"18","code","0x4E000","0x7E9","0xD00","0x7E9","D:\Ghidra\Crusader\NE_segments\seg018_code_off_4E000_len_7E9.bin" +"19","code","0x4EA00","0xB4D","0xD00","0xB4D","D:\Ghidra\Crusader\NE_segments\seg019_code_off_4EA00_len_B4D.bin" +"20","code","0x4F800","0x878","0xD00","0x878","D:\Ghidra\Crusader\NE_segments\seg020_code_off_4F800_len_878.bin" +"21","code","0x50200","0x4486","0xD00","0x4486","D:\Ghidra\Crusader\NE_segments\seg021_code_off_50200_len_4486.bin" +"22","code","0x55000","0x2BD6","0xD00","0x2BD6","D:\Ghidra\Crusader\NE_segments\seg022_code_off_55000_len_2BD6.bin" +"23","code","0x58200","0x5D6","0xD00","0x5D6","D:\Ghidra\Crusader\NE_segments\seg023_code_off_58200_len_5D6.bin" +"24","code","0x58A00","0x6D7","0xD00","0x6D7","D:\Ghidra\Crusader\NE_segments\seg024_code_off_58A00_len_6D7.bin" +"25","code","0x59200","0x1976","0xD00","0x1976","D:\Ghidra\Crusader\NE_segments\seg025_code_off_59200_len_1976.bin" +"26","code","0x5AE00","0x4DE","0xC00","0x4DE","D:\Ghidra\Crusader\NE_segments\seg026_code_off_5AE00_len_4DE.bin" +"27","code","0x5B400","0x57B","0xD00","0x57B","D:\Ghidra\Crusader\NE_segments\seg027_code_off_5B400_len_57B.bin" +"28","code","0x5BA00","0x788","0xD00","0x788","D:\Ghidra\Crusader\NE_segments\seg028_code_off_5BA00_len_788.bin" +"29","code","0x5C400","0x190A","0xD00","0x190A","D:\Ghidra\Crusader\NE_segments\seg029_code_off_5C400_len_190A.bin" +"30","code","0x5E000","0x5071","0xD00","0x5071","D:\Ghidra\Crusader\NE_segments\seg030_code_off_5E000_len_5071.bin" +"31","code","0x64000","0x6EE","0xD00","0x6EE","D:\Ghidra\Crusader\NE_segments\seg031_code_off_64000_len_6EE.bin" +"32","code","0x64800","0x56A","0xD00","0x56A","D:\Ghidra\Crusader\NE_segments\seg032_code_off_64800_len_56A.bin" +"33","code","0x65000","0x10D7","0xD00","0x10D7","D:\Ghidra\Crusader\NE_segments\seg033_code_off_65000_len_10D7.bin" +"34","code","0x66600","0x253A","0xD00","0x253A","D:\Ghidra\Crusader\NE_segments\seg034_code_off_66600_len_253A.bin" +"35","code","0x69400","0xF67","0xD00","0xF67","D:\Ghidra\Crusader\NE_segments\seg035_code_off_69400_len_F67.bin" +"36","code","0x6A600","0x69F","0xD00","0x69F","D:\Ghidra\Crusader\NE_segments\seg036_code_off_6A600_len_69F.bin" +"37","code","0x6AE00","0x636","0xD00","0x636","D:\Ghidra\Crusader\NE_segments\seg037_code_off_6AE00_len_636.bin" +"38","code","0x6B600","0x2318","0xD00","0x2318","D:\Ghidra\Crusader\NE_segments\seg038_code_off_6B600_len_2318.bin" +"39","code","0x6E200","0x3416","0xD00","0x3416","D:\Ghidra\Crusader\NE_segments\seg039_code_off_6E200_len_3416.bin" +"40","code","0x72200","0x1E7A","0xD00","0x1E7A","D:\Ghidra\Crusader\NE_segments\seg040_code_off_72200_len_1E7A.bin" +"41","code","0x74600","0x28D","0xC00","0x28D","D:\Ghidra\Crusader\NE_segments\seg041_code_off_74600_len_28D.bin" +"42","code","0x74A00","0xC9C","0xD00","0xC9C","D:\Ghidra\Crusader\NE_segments\seg042_code_off_74A00_len_C9C.bin" +"43","code","0x75A00","0x336F","0xD00","0x336F","D:\Ghidra\Crusader\NE_segments\seg043_code_off_75A00_len_336F.bin" +"44","code","0x79400","0x7F8","0xD00","0x7F8","D:\Ghidra\Crusader\NE_segments\seg044_code_off_79400_len_7F8.bin" +"45","code","0x79E00","0x200","0xD00","0x200","D:\Ghidra\Crusader\NE_segments\seg045_code_off_79E00_len_200.bin" +"46","code","0x7A200","0x7DC","0xD00","0x7DC","D:\Ghidra\Crusader\NE_segments\seg046_code_off_7A200_len_7DC.bin" +"47","code","0x7AC00","0x9B4","0xD00","0x9B4","D:\Ghidra\Crusader\NE_segments\seg047_code_off_7AC00_len_9B4.bin" +"48","code","0x7B800","0x63","0xC00","0x63","D:\Ghidra\Crusader\NE_segments\seg048_code_off_7B800_len_63.bin" +"49","code","0x7BA00","0x1E3F","0xD00","0x1E3F","D:\Ghidra\Crusader\NE_segments\seg049_code_off_7BA00_len_1E3F.bin" +"50","code","0x7DE00","0x9C8","0xD00","0x9C8","D:\Ghidra\Crusader\NE_segments\seg050_code_off_7DE00_len_9C8.bin" +"51","code","0x7EA00","0x1D02","0xD00","0x1D02","D:\Ghidra\Crusader\NE_segments\seg051_code_off_7EA00_len_1D02.bin" +"52","code","0x80A00","0x1D65","0xD00","0x1D65","D:\Ghidra\Crusader\NE_segments\seg052_code_off_80A00_len_1D65.bin" +"53","code","0x82C00","0x10DE","0xD00","0x10DE","D:\Ghidra\Crusader\NE_segments\seg053_code_off_82C00_len_10DE.bin" +"54","code","0x84000","0x5","0xC00","0x5","D:\Ghidra\Crusader\NE_segments\seg054_code_off_84000_len_5.bin" +"55","code","0x84200","0xA06","0xD00","0xA06","D:\Ghidra\Crusader\NE_segments\seg055_code_off_84200_len_A06.bin" +"56","code","0x85000","0x706","0xD00","0x706","D:\Ghidra\Crusader\NE_segments\seg056_code_off_85000_len_706.bin" +"57","code","0x85A00","0x79B","0xD00","0x79B","D:\Ghidra\Crusader\NE_segments\seg057_code_off_85A00_len_79B.bin" +"58","code","0x86400","0x44B","0xD00","0x44B","D:\Ghidra\Crusader\NE_segments\seg058_code_off_86400_len_44B.bin" +"59","code","0x86A00","0x4288","0xD00","0x4288","D:\Ghidra\Crusader\NE_segments\seg059_code_off_86A00_len_4288.bin" +"60","code","0x8B600","0x231","0xD00","0x231","D:\Ghidra\Crusader\NE_segments\seg060_code_off_8B600_len_231.bin" +"61","code","0x8BA00","0x1B6C","0xD00","0x1B6C","D:\Ghidra\Crusader\NE_segments\seg061_code_off_8BA00_len_1B6C.bin" +"62","code","0x8DA00","0x85F","0xD00","0x85F","D:\Ghidra\Crusader\NE_segments\seg062_code_off_8DA00_len_85F.bin" +"63","code","0x8E400","0x519","0xD00","0x519","D:\Ghidra\Crusader\NE_segments\seg063_code_off_8E400_len_519.bin" +"64","code","0x8EA00","0x3B1","0xD00","0x3B1","D:\Ghidra\Crusader\NE_segments\seg064_code_off_8EA00_len_3B1.bin" +"65","code","0x8F000","0x5BD","0xD00","0x5BD","D:\Ghidra\Crusader\NE_segments\seg065_code_off_8F000_len_5BD.bin" +"66","code","0x8F800","0x4A9","0xD00","0x4A9","D:\Ghidra\Crusader\NE_segments\seg066_code_off_8F800_len_4A9.bin" +"67","code","0x8FE00","0x839","0xD00","0x839","D:\Ghidra\Crusader\NE_segments\seg067_code_off_8FE00_len_839.bin" +"68","code","0x90800","0xB4A","0xD00","0xB4A","D:\Ghidra\Crusader\NE_segments\seg068_code_off_90800_len_B4A.bin" +"69","code","0x91800","0x2A0","0xD00","0x2A0","D:\Ghidra\Crusader\NE_segments\seg069_code_off_91800_len_2A0.bin" +"70","code","0x91C00","0xF24","0xD00","0xF24","D:\Ghidra\Crusader\NE_segments\seg070_code_off_91C00_len_F24.bin" +"71","code","0x92E00","0x6C2","0xD00","0x6C2","D:\Ghidra\Crusader\NE_segments\seg071_code_off_92E00_len_6C2.bin" +"72","code","0x93600","0xCA1","0xD00","0xCA1","D:\Ghidra\Crusader\NE_segments\seg072_code_off_93600_len_CA1.bin" +"73","code","0x94600","0x9AA","0xD00","0x9AA","D:\Ghidra\Crusader\NE_segments\seg073_code_off_94600_len_9AA.bin" +"74","code","0x95200","0x337","0xD00","0x337","D:\Ghidra\Crusader\NE_segments\seg074_code_off_95200_len_337.bin" +"75","code","0x95600","0x1428","0xD00","0x1428","D:\Ghidra\Crusader\NE_segments\seg075_code_off_95600_len_1428.bin" +"76","code","0x96E00","0x627","0xD00","0x627","D:\Ghidra\Crusader\NE_segments\seg076_code_off_96E00_len_627.bin" +"77","code","0x97600","0x616","0xD00","0x616","D:\Ghidra\Crusader\NE_segments\seg077_code_off_97600_len_616.bin" +"78","code","0x97E00","0x634","0xD00","0x634","D:\Ghidra\Crusader\NE_segments\seg078_code_off_97E00_len_634.bin" +"79","code","0x98600","0x421","0xC00","0x421","D:\Ghidra\Crusader\NE_segments\seg079_code_off_98600_len_421.bin" +"80","code","0x98C00","0xF27","0xD00","0xF27","D:\Ghidra\Crusader\NE_segments\seg080_code_off_98C00_len_F27.bin" +"81","code","0x99E00","0x320","0xD00","0x320","D:\Ghidra\Crusader\NE_segments\seg081_code_off_99E00_len_320.bin" +"82","code","0x9A200","0x1C8A","0xD00","0x1C8A","D:\Ghidra\Crusader\NE_segments\seg082_code_off_9A200_len_1C8A.bin" +"83","code","0x9C400","0x31E","0xD00","0x31E","D:\Ghidra\Crusader\NE_segments\seg083_code_off_9C400_len_31E.bin" +"84","code","0x9C800","0x1478","0xD00","0x1478","D:\Ghidra\Crusader\NE_segments\seg084_code_off_9C800_len_1478.bin" +"85","code","0x9E000","0x404","0xD00","0x404","D:\Ghidra\Crusader\NE_segments\seg085_code_off_9E000_len_404.bin" +"86","code","0x9E600","0x40F6","0xC00","0x40F6","D:\Ghidra\Crusader\NE_segments\seg086_code_off_9E600_len_40F6.bin" +"87","code","0xA2800","0x50C","0xD00","0x50C","D:\Ghidra\Crusader\NE_segments\seg087_code_off_A2800_len_50C.bin" +"88","code","0xA2E00","0x523","0xD00","0x523","D:\Ghidra\Crusader\NE_segments\seg088_code_off_A2E00_len_523.bin" +"89","code","0xA3400","0x373","0xD00","0x373","D:\Ghidra\Crusader\NE_segments\seg089_code_off_A3400_len_373.bin" +"90","code","0xA3800","0x9C6","0xD00","0x9C6","D:\Ghidra\Crusader\NE_segments\seg090_code_off_A3800_len_9C6.bin" +"91","code","0xA4400","0x6FA","0xD00","0x6FA","D:\Ghidra\Crusader\NE_segments\seg091_code_off_A4400_len_6FA.bin" +"92","code","0xA4E00","0x59E","0xD00","0x59E","D:\Ghidra\Crusader\NE_segments\seg092_code_off_A4E00_len_59E.bin" +"93","code","0xA5600","0x4F1","0xD00","0x4F1","D:\Ghidra\Crusader\NE_segments\seg093_code_off_A5600_len_4F1.bin" +"94","code","0xA5E00","0x606","0xD00","0x606","D:\Ghidra\Crusader\NE_segments\seg094_code_off_A5E00_len_606.bin" +"95","code","0xA6600","0xC9F","0xD00","0xC9F","D:\Ghidra\Crusader\NE_segments\seg095_code_off_A6600_len_C9F.bin" +"96","code","0xA7600","0x582","0xD00","0x582","D:\Ghidra\Crusader\NE_segments\seg096_code_off_A7600_len_582.bin" +"97","code","0xA7E00","0xDB0","0xD00","0xDB0","D:\Ghidra\Crusader\NE_segments\seg097_code_off_A7E00_len_DB0.bin" +"98","code","0xA8E00","0x68A","0xD00","0x68A","D:\Ghidra\Crusader\NE_segments\seg098_code_off_A8E00_len_68A.bin" +"99","code","0xA9600","0x355","0xD00","0x355","D:\Ghidra\Crusader\NE_segments\seg099_code_off_A9600_len_355.bin" +"100","code","0xA9C00","0x697","0xD00","0x697","D:\Ghidra\Crusader\NE_segments\seg100_code_off_A9C00_len_697.bin" +"101","code","0xAA400","0x17BC","0xD00","0x17BC","D:\Ghidra\Crusader\NE_segments\seg101_code_off_AA400_len_17BC.bin" +"102","code","0xAC000","0x73C","0xD00","0x73C","D:\Ghidra\Crusader\NE_segments\seg102_code_off_AC000_len_73C.bin" +"103","code","0xACA00","0x16CD","0xD00","0x16CD","D:\Ghidra\Crusader\NE_segments\seg103_code_off_ACA00_len_16CD.bin" +"104","code","0xAE600","0x41B","0xD00","0x41B","D:\Ghidra\Crusader\NE_segments\seg104_code_off_AE600_len_41B.bin" +"105","code","0xAEC00","0x9F6","0xD00","0x9F6","D:\Ghidra\Crusader\NE_segments\seg105_code_off_AEC00_len_9F6.bin" +"106","code","0xAF800","0x1795","0xD00","0x1795","D:\Ghidra\Crusader\NE_segments\seg106_code_off_AF800_len_1795.bin" +"107","code","0xB1400","0x40C","0xD00","0x40C","D:\Ghidra\Crusader\NE_segments\seg107_code_off_B1400_len_40C.bin" +"108","code","0xB1A00","0x113F","0xD00","0x113F","D:\Ghidra\Crusader\NE_segments\seg108_code_off_B1A00_len_113F.bin" +"109","code","0xB2E00","0x1424","0xD00","0x1424","D:\Ghidra\Crusader\NE_segments\seg109_code_off_B2E00_len_1424.bin" +"110","code","0xB4400","0x4C4","0xD00","0x4C4","D:\Ghidra\Crusader\NE_segments\seg110_code_off_B4400_len_4C4.bin" +"111","code","0xB4A00","0x489","0xD00","0x489","D:\Ghidra\Crusader\NE_segments\seg111_code_off_B4A00_len_489.bin" +"112","code","0xB5000","0x1670","0xD00","0x1670","D:\Ghidra\Crusader\NE_segments\seg112_code_off_B5000_len_1670.bin" +"113","code","0xB6A00","0x4A6","0xD00","0x4A6","D:\Ghidra\Crusader\NE_segments\seg113_code_off_B6A00_len_4A6.bin" +"114","code","0xB7000","0xDF1","0xD00","0xDF1","D:\Ghidra\Crusader\NE_segments\seg114_code_off_B7000_len_DF1.bin" +"115","code","0xB8000","0x978","0xD00","0x978","D:\Ghidra\Crusader\NE_segments\seg115_code_off_B8000_len_978.bin" +"116","code","0xB8C00","0xAA3","0xD00","0xAA3","D:\Ghidra\Crusader\NE_segments\seg116_code_off_B8C00_len_AA3.bin" +"117","code","0xB9A00","0x3157","0xD00","0x3157","D:\Ghidra\Crusader\NE_segments\seg117_code_off_B9A00_len_3157.bin" +"118","code","0xBD400","0xA0A","0xD00","0xA0A","D:\Ghidra\Crusader\NE_segments\seg118_code_off_BD400_len_A0A.bin" +"119","code","0xBE200","0x419","0xD00","0x419","D:\Ghidra\Crusader\NE_segments\seg119_code_off_BE200_len_419.bin" +"120","code","0xBE800","0x9AE","0xD00","0x9AE","D:\Ghidra\Crusader\NE_segments\seg120_code_off_BE800_len_9AE.bin" +"121","code","0xBF400","0xACE","0xD00","0xACE","D:\Ghidra\Crusader\NE_segments\seg121_code_off_BF400_len_ACE.bin" +"122","code","0xC0200","0x3149","0xD00","0x3149","D:\Ghidra\Crusader\NE_segments\seg122_code_off_C0200_len_3149.bin" +"123","code","0xC3C00","0xE6D","0xD00","0xE6D","D:\Ghidra\Crusader\NE_segments\seg123_code_off_C3C00_len_E6D.bin" +"124","code","0xC4E00","0x3DD","0xD00","0x3DD","D:\Ghidra\Crusader\NE_segments\seg124_code_off_C4E00_len_3DD.bin" +"125","code","0xC5400","0x1A3E","0xD00","0x1A3E","D:\Ghidra\Crusader\NE_segments\seg125_code_off_C5400_len_1A3E.bin" +"126","code","0xC7400","0x402A","0xD00","0x402A","D:\Ghidra\Crusader\NE_segments\seg126_code_off_C7400_len_402A.bin" +"127","code","0xCC600","0x8F6","0xD00","0x8F6","D:\Ghidra\Crusader\NE_segments\seg127_code_off_CC600_len_8F6.bin" +"128","code","0xCD200","0x5D0","0xD00","0x5D0","D:\Ghidra\Crusader\NE_segments\seg128_code_off_CD200_len_5D0.bin" +"129","code","0xCDA00","0xD77","0xD00","0xD77","D:\Ghidra\Crusader\NE_segments\seg129_code_off_CDA00_len_D77.bin" +"130","code","0xCEA00","0x47D","0xD00","0x47D","D:\Ghidra\Crusader\NE_segments\seg130_code_off_CEA00_len_47D.bin" +"131","code","0xCF000","0x44D","0xD00","0x44D","D:\Ghidra\Crusader\NE_segments\seg131_code_off_CF000_len_44D.bin" +"132","code","0xCF600","0x3EB8","0xD00","0x3EB8","D:\Ghidra\Crusader\NE_segments\seg132_code_off_CF600_len_3EB8.bin" +"133","code","0xD3800","0x215A","0xD00","0x215A","D:\Ghidra\Crusader\NE_segments\seg133_code_off_D3800_len_215A.bin" +"134","code","0xD6000","0xEF0","0xD00","0xEF0","D:\Ghidra\Crusader\NE_segments\seg134_code_off_D6000_len_EF0.bin" +"135","code","0xD7000","0x3B7","0xD00","0x3B7","D:\Ghidra\Crusader\NE_segments\seg135_code_off_D7000_len_3B7.bin" +"136","code","0xD7600","0x5BD","0xD00","0x5BD","D:\Ghidra\Crusader\NE_segments\seg136_code_off_D7600_len_5BD.bin" +"137","code","0xD7E00","0xFBB","0xD00","0xFBB","D:\Ghidra\Crusader\NE_segments\seg137_code_off_D7E00_len_FBB.bin" +"138","code","0xD9200","0x32E4","0xD00","0x32E4","D:\Ghidra\Crusader\NE_segments\seg138_code_off_D9200_len_32E4.bin" +"139","code","0xDCC00","0x984","0xD00","0x984","D:\Ghidra\Crusader\NE_segments\seg139_code_off_DCC00_len_984.bin" +"140","code","0xDD800","0xC6F","0xD00","0xC6F","D:\Ghidra\Crusader\NE_segments\seg140_code_off_DD800_len_C6F.bin" +"141","code","0xDE600","0x2B","0xC00","0x2B","D:\Ghidra\Crusader\NE_segments\seg141_code_off_DE600_len_2B.bin" +"142","code","0xDE800","0x4371","0xD00","0x4371","D:\Ghidra\Crusader\NE_segments\seg142_code_off_DE800_len_4371.bin" +"143","code","0xE3400","0x6F5","0xD00","0x6F5","D:\Ghidra\Crusader\NE_segments\seg143_code_off_E3400_len_6F5.bin" +"144","data","0xE3C00","0x8DBC","0xD01","0x8DBC","D:\Ghidra\Crusader\NE_segments\seg144_data_off_E3C00_len_8DBC.bin" +"145","data","0x0","0x0","0xC01","0x2000","D:\Ghidra\Crusader\NE_segments\seg145_data_off_0_len_0.bin"