- Implemented a Python script to extract data from the EUSECODE.FLX file format. - Defined data structures for candidate entries and extracted chunks using dataclasses. - Added functions to read and parse the FLX table, extract candidate data, and generate human-readable output files. - Included functionality for analyzing extracted data, including generating summaries, descriptors, and event family reports. - Implemented utilities for calculating printable ratios, zero ratios, and identifying text-like data. - Added support for writing various output formats, including JSON, TSV, and Markdown.
17 KiB
17 KiB
Far-Call Targets: Top-104 Most-Called Functions
Content extracted from crusader_decompilation_notes.md. Named via systematic analysis of 11,692 NE relocation fixup entries — the functions most frequently called through the CALLF 0x0000:ffff thunk mechanism.
Tier 1: Ranks 1–20 (73+ callers)
| Rank | Address | Name | Calls | Description |
|---|---|---|---|---|
| 1 | 000a:44fd |
seg091_func_00fd |
331 | Recovered boundary. Shares init flag 0x44a4 with runtime_init_or_abort; thunk-heavy non-returning wrapper. |
| 2 | 0003:ac7e |
mem_alloc |
272 | Allocation wrapper → seg082:0000 (0009:a200) |
| 3 | 0008:dbec |
entity_word_list_destroy |
238 | Frees entity word-list buffer. |
| 4 | 0003:a751 |
mem_free |
207 | Free wrapper → seg082:007a (0009:a27a = mem_free_checked) |
| 5 | 0008:bb4f |
mem_alloc_far |
174 | Thin wrapper → mem_alloc |
| 6 | 0003:a897 |
far_memcpy |
165 | REP MOVSW + trailing MOVSB |
| 7 | 0005:088f |
entity_get_type_word |
130 | Returns type word from table 0x7df9 indexed by slot |
| 8 | 000b:358d |
sprite_tree_accumulate_pos |
122 | Recursively sums X/Y offsets (+0x21/+0x23) through linked child nodes (+0x19/+0x1b), copies 8-byte position block via far_memcpy |
| 9 | 0008:ce3d |
entity_call_two_vtables |
118 | Calls vtable[+4] at entity+0x1e and +0x28 |
| 10 | 0004:26cd |
nop_void_stub |
118 | Empty function, returns void |
| 11 | 0008:ce00 |
entity_call_two_vtables_base |
117 | Calls vtable[0] at entity+0x1e and +0x28 |
| 12 | 0008:bb8c |
entity_check_flag_0x4000 |
115 | Short-circuits if flag 0x4000 set at +0x16 |
| 13 | 0008:cda7 |
entity_free_both_word_lists |
115 | Frees word lists at entity+0x1e and +0x28 if optional pointers at +0x24/+0x26 and +0x2e/+0x30 non-null. Both call entity_word_list_free_existing. |
| 14 | 0004:26d2 |
nop_void_stub_b |
111 | Empty function, returns void |
| 15 | 000a:45fe |
runtime_init_or_abort |
108 | Reentrancy-guarded init. Flag at 0x44a4; flushes via FUN_000a_4a56, then calls crt_exit_wrapper(1). Hidden code gap 0x4616-0x4643. |
| 16 | 0004:3324 |
nop_return_zero |
95 | Returns 0 |
| 17 | 0009:c563 |
event_queue_push |
82 | Circular buffer enqueue. Ring index (+0xe) masked 0x3f, slot masked 0xfff8. Writes event type word + data byte pair. |
| 18 | 0005:c448 |
list_remove_and_free |
74 | Unlinks node from linked list via FUN_0005_c495, optionally calls mem_free if bit 0 of flags set |
| 19 | 000b:2e00 |
(no function in Ghidra) | 74 | Analysis gap at seg109:0000. Needs manual function creation. |
| 20 | 0009:1f12 |
dos_file_lseek |
73 | DOS LSEEK (INT 21h AH=42h) wrapper with error reporting to 0x867a |
Tier 2: Ranks 21–40 (56–73 callers)
| Rank | Address | Name | Calls | Description |
|---|---|---|---|---|
| 21 | 0009:3600 |
rotating_buffer_advance |
73 | Advances 5-slot circular counter at 0x3eb6, zeros pointer in table at 0x867c, dispatches via jump table |
| 22 | 0009:943a |
entity_rect_compare_and_dispatch |
68 | Compares bounding rectangles of two entities, dispatches based on flag bits 4/2/1 at +0x16 |
| 23 | 0009:1e61 |
dos_file_close |
65 | DOS file close (INT 21h), error reporting, sets handle to -1 |
| 24 | 0005:e252 |
(unnamed — unclear) | 65 | Copies 11 words from Phar Lap extender area (FUN_0000_12c6+5), then calls thunk. Interrupt/trampoline setup? |
| 25 | 0003:dbcc |
crt_format_string |
64 | MetaWare High C formatting wrapper. Calls FUN_0003_bb92 with runtime format dispatch table. |
| 26 | 0007:5a00 |
(no function in Ghidra) | 64 | High-traffic raw target at seg043:0000. Earlier debris_spawn / seg001 mapping was rejected after checking relocation labels. Still needs manual function creation and direct analysis. |
| 27 | 000a:4742 |
assert_buffer_valid |
63 | Validates handle: asserts param_2 == cookie at 0x45a6 and param_1 < limit at 0x87e0 |
| 28 | 0009:9216 |
entity_conditional_render_dispatch |
63 | Checks entity flag bits 4 and 1 at +0x16, dispatches to vtable[+0xc] or thunk |
| 29 | 0008:cb2c |
entity_flag20_clear_and_update_target |
61 | Clears flag bit 0x20, writes target +0x12/+0x14, calls refresh |
| 30 | 0008:cb5c |
entity_flag20_set_and_init_target |
61 | Sets flag bit 0x20, inits target if zero, calls refresh |
| 31 | 0007:7306 |
entity_create_stack_object |
58 | Allocates 0xCC bytes on stack, inits via object_init_zero_fields (0005:c400), calls thunk |
| 32 | 0007:8709 |
entity_mark_dirty_and_sync_tile_aux |
58 | Syncs tile aux, sets flag bit 0x04 at +0x42 |
| 33 | 0007:87c5 |
entity_set_flag20_from_field42 |
58 | Reads entity +0x42/+0x44, calls entity_flag20_set_and_init_target with those values |
| 34 | 0007:8508 |
entity_table_lookup_and_dispatch |
58 | Searches table at 0x2b46, dispatches via indirect jump |
| 35 | 0007:8920 |
entity_call_vtable_slot0c |
58 | Calls vtable entry at +0x0c |
| 36 | 000a:b988 |
sprite_node_get_or_traverse |
57 | If child pointer at +0x19/+0x1b non-null, traverses; otherwise returns leaf value |
| 37 | 0003:a98b |
crt_signed_div32 |
56 | Entry: adjusts near→far stack, sets CX=0 (signed quotient), jumps to crt_div32_impl |
| 38 | 000a:7b44 |
nop_return_void_a |
56 | Empty function (default vtable slot?) |
| 39 | 000a:7b49 |
nop_return_void_b |
56 | Empty function (default vtable slot?) |
| 40 | 000a:7b53 |
nop_return_void_c |
56 | Empty function (default vtable slot?) |
Supporting Functions Discovered
| Address | Name | Description |
|---|---|---|
000b:3a00 |
sprite_tree_sum_x_offset |
Recursive: sums field +0x21 through child chain +0x19/+0x1b |
000b:3a35 |
sprite_tree_sum_y_offset |
Recursive: sums field +0x23 through child chain +0x19/+0x1b |
0003:a845 |
crt_exit_wrapper |
Calls crt_exit_impl(param,0,0) |
0003:a7ee |
crt_exit_impl |
Full C exit: atexit handlers, stdio flush, MetaWare runtime cleanup |
0003:a9a8 |
crt_div32_impl |
32-bit division core. CX flags: bit0=unsigned, bit1=modulo, bit2=negate |
0005:c400 |
object_init_zero_fields |
Zeros fields +0x25, +0x29, +0x31, +0x32 of a struct. Returns pointer. |
000a:4440 |
joystick_read_axes_and_buttons |
Reads PC game port 0x201. Times axis responses, reads button nibble to 0x44a2 |
000b:3380 |
sprite_node_is_dirty |
Checks flags at obj+0x29 & 3 == 1 or 3 → returns bool |
000b:33a6 |
sprite_node_mark_dirty |
If not dirty, calls FUN_000b_3965 with mode=3 to invalidate |
Tier 3: Ranks 41–60 (42–56 callers)
| Rank | Address | Name | Calls | Description |
|---|---|---|---|---|
| 41 | 000a:7b58 |
nop_return_zero_b |
56 | Returns 0 (default vtable slot) |
| 42 | 000b:3ab2 |
sprite_node_dispatch_event |
56 | Large event dispatch: checks event type (2/4/8/0x100), updates global focus ptr at [0x4fd0:4fd2], dispatches via vtable methods [+0x14/+0x18/+0x20/+0x24] by event code. Switch table for 16 event types. |
| 43 | 000a:48ff |
rng_next_modulo |
55 | Advances seg091 RNG state and returns the result modulo the requested bound; returns 0 when bound is 0. |
| 44 | 000b:3362 |
sprite_tree_unwind_check |
55 | Validates SS == param_2 (stack segment guard), then decrements global counter at [0x4fd6] |
| 45 | 000b:40ee |
sprite_node_update_and_dispatch |
55 | If sprite_node_is_dirty returns false: marks dirty, calcs accumulated bounds via sprite_tree_get_accumulated_bounds (3ed8), then dispatches via thunk |
| 46 | 000a:7b5f |
vtable_stub_trampoline |
55 | Calls through fixup thunk (forwarder to another function) |
| 47 | 000a:7b78 |
nop_return_void_e |
55 | Empty function (default vtable slot) |
| 48 | 000a:7b7d |
nop_return_void_f |
55 | Empty function (default vtable slot) |
| 49 | 000a:7b4e |
nop_return_void_d |
54 | Empty function (default vtable slot) |
| 50 | 000b:330c |
sprite_tree_dispatch_wrapper |
52 | Pure thunk wrapper: calls through fixup |
| 51 | 0009:2034 |
dos_file_seek |
51 | INT 21h AH=42h (LSEEK). Takes file object ptr, extracts handle at obj+4, seeks to offset param. Error reporting to [0x867a]. |
| 52 | 0005:0466 |
entity_resolve_slot_ptr |
50 | (pre-existing name) |
| 53 | 0003:a880 |
(no function in Ghidra) | 49 | Analysis gap in CRT segment |
| 54 | 0006:170c |
tile_class_get_byte |
47 | Looks up class data: indexes into table at [0x7e1e] by (*param_1 * 0x79), returns byte at offset +0xc |
| 55 | 000b:4097 |
sprite_dispatch_with_event |
45 | Pushes event params + global [0x49c2:0x49c4], calls thunk |
| 56 | 0005:02c1 |
entity_is_type_match |
43 | Compares *param_1 against global at [0x27c8], returns 1 if equal, 0 otherwise |
| 57 | 0003:ad75 |
(no function in Ghidra) | 43 | Analysis gap in CRT segment |
| 58 | 000a:e709 |
render_dispatch_by_flag |
43 | Dispatches between two thunk paths based on boolean flag at stack+0x10 |
| 59 | 0003:d0ff |
crt_sprintf_wrapper |
42 | Calls FUN_0003_bb92 (format engine) with rearranged params and string constant at 0x67ac |
| 60 | 000b:326e |
sprite_node_destroy |
42 | Destructor: sets vtable ptr to 0x501a, clears global [0x4fd0:4fd2] if self, releases child nodes, calls mem_free via thunk |
Analysis Gaps
| Address | NE Segment | Callers | Notes |
|---|---|---|---|
000a:44fd |
seg091:00fd | 331 | Recovered as seg091_func_00fd; thunk-heavy init wrapper sharing flag 0x44a4. |
000b:2e00 |
seg109:0000 | 74 | Start of segment 109. |
0007:5a00 |
seg043:0000 | 64 | Start of segment 43. Earlier seg001 debris_spawn port was rejected; still needs manual function creation and direct analysis. |
000a:48ff |
seg091:04ff | 55 | Recovered as rng_next_modulo; bounded wrapper around seg091 RNG state advance. |
0003:a880 |
seg005:0880 | 49 | In CRT segment near far_memcpy. |
0003:ad75 |
seg005:0d75 | 43 | In CRT segment near mem_alloc. |
000a:454d |
seg091:014d | 32 | Recovered as seg091_func_014d; init/context helper using the 0x45a6 cookie/context global. |
seg043 reconciliation:
- The earlier standalone seg001 port hypothesis in this subrange was wrong.
- Relocation data places raw
0007:5a00atseg043:0000, and the named helper at0007:5b6fsits atseg043:016f. - Because of that segment placement, standalone seg001 names such as
debris_spawn(0x7490) andentity_die(0x75ff) should NOT be ported into this raw range. 0007:5b6fno longer exists as a function after the PyGhidra repair pass. Its behavior now lines up with the repaired function0007:5b7a = entity_set_at_target_update_facing.- The currently repaired functions at
0007:5a90and0007:5c1cshould keep their positional names until a later pass resolves the thunk-heavy bodies more clearly.
Tier 4: Ranks 61–80 (29–42 callers)
| Rank | Address | Name | Calls | Description |
|---|---|---|---|---|
| 61 | 000b:30a5 |
sprite_tree_forward_wrapper |
42 | Pure thunk forwarder |
| 62 | 0008:bc27 |
entity_set_event_type_checked |
41 | Sets event code at +0x06 with range/timer checks |
| 63 | 0008:d214 |
entity_dispatch_entry_ctor_vtbl_3aa6 |
40 | Constructor: alloc 0x40, vtbl 3AA6, flag 0x200 |
| 64 | 0005:1565 |
entity_action_by_type_dispatch |
39 | Checks entity type against whitelist (0x432,0x5a0,0x1fd,0x1fe,0x8f,0x59f,0x2b3,0x2ca), dispatches by flags at [0xc76] and [0x85f] |
| 65 | 0008:4bba |
channel_slot_enable |
39 | Sets enable byte=1 in 5-slot table at 0x84ca (slot * 0xd stride) |
| 66 | 0009:6f5a |
vga_palette_write |
38 | Writes RGB triplets to VGA DAC (port 0x3C8/0x3C9). Range param_2..param_3 from palette data at *param_1 |
| 67 | 0009:8ef6 |
line_draw_dispatch |
38 | Compares abs(dx) vs abs(dy) to determine major axis, dispatches to appropriate line draw routine |
| 68 | 000a:7b30 |
nop_return_void_g |
38 | Empty function (default vtable slot) |
| 69 | 000a:7b3f |
nop_return_void_h |
38 | Empty function (default vtable slot) |
| 70 | 0009:6e7f |
palette_free_if_set |
35 | Frees existing palette data if ptr non-null, checks alignment |
| 71 | 000a:7b35 |
nop_return_void_i |
35 | Empty function (default vtable slot) |
| 72 | 0009:c433 |
event_queue_align_index |
34 | Returns param_1 & 0xFFF8 — aligns ring index to 8-byte event slot boundary |
| 73 | 0009:2156 |
dos_file_get_size |
33 | Saves file position, does INT 21h AH=42h AL=02 (seek to end), restores position. Returns file size in DX:AX |
| 74 | 000a:2c41 |
list_iterate_next |
33 | Linked list iterator: if *out==0 returns first from obj+2; else follows next at ptr+2/+4. Returns bool (has more) |
| 75 | 000a:454d |
seg091_func_014d |
32 | Recovered boundary. Shares flag 0x44a4; checks optional long argument against the 0x45a6 cookie/context global. |
| 76 | 000b:2446 |
sprite_clear_redraw_flag |
31 | Clears flag at obj+0x17e, then dispatches via thunk |
| 77 | 0005:1238 |
entity_get_class_word |
30 | Looks up table at [0x7e01] indexed by *param_1 * 2, returns word. Sister of entity_get_type_word (which uses [0x7df9]) |
| 78 | 000b:1446 |
display_null_check_dispatch |
30 | Null-checks far ptr params, dispatches to different thunks based on result |
| 79 | 000d:85da |
vga_palette_set_all_black |
29 | Sets byte at global_obj[0x6828]+0x40 = 1 if global non-null, then calls thunk. (Note: earlier name map_object_set_dirty_flag was incorrect; corrected in seg137 analysis.) |
| 80 | 0005:1511 |
entity_destroy_trampoline |
29 | Pure thunk forwarder to entity destruction |
Tier 5: Ranks 81–104 (24–29 callers)
| Rank | Address | Name | Calls | Description |
|---|---|---|---|---|
| 81 | 0009:1c00 |
dos_file_handle_init |
29 | Inits 6-byte file handle struct: dword=0, word+4=0xFFFF (invalid). Aborts on null ptr |
| 82 | 0008:75f3 |
entity_get_ptr |
29 | Looks up entity far ptr from table at DS:0x39b0, indexed by id*4 |
| 83 | 0006:0208 |
entity_class_get_flag4 |
29 | Returns bit 2 of classinfo byte at [0x7e1e]+*p1*0x79+0x13 → 0 or 1 |
| 84 | 000a:30d7 |
list_node_set_if_context |
29 | Sets node fields +2/+4 if params match context globals at 0x45a6/0x45a8 |
| 85 | 0009:c45f |
object_init_and_get_next |
29 | Calls object_init_zero_fields then returns *(result+2) — init+accessor combo |
| 86 | 0004:d7a0 |
object_deref_get_word4 |
28 | Dereferences far ptr chain: returns word at *(*(param_1)+4) |
| 87 | 000a:5276 |
debug_check_flag_45aa |
28 | If byte at DS:0x45aa non-zero, calls thunk (diagnostic/assert check) |
| 88 | 0003:d94f |
far_memset |
28 | Wrapper reordering params for CRT memset impl at 0003:d92b (odd-aligned, word-fill loop) |
| 89 | 000a:7b3a |
nop_return_void_j |
28 | Empty function (default vtable slot) |
| 90 | 0008:ca18 |
entity_pair_sync_b |
27 | Pairwise sync wrapper direction B |
| 91 | 0008:bd20 |
entity_sprite_set_target_pos |
27 | Sets flag 0x1000, copies player pos to entity +0x0a/+0x0c |
| 92 | 0009:3ceb |
buffer_release_and_dispatch |
27 | Frees far ptr at obj+0x3b if set, nulls it; conditionally dispatches on bit 0 |
| 93 | 0005:09b4 |
entity_get_flags_byte |
27 | Reads byte from [0x7dfd]+id, conditionally extends with classinfo byte at [0x7e1e]+id*0x79+0xf |
| 94 | 0005:0fbb |
entity_lookup_sprite_word |
27 | Returns word from [0x7e05]+*p1*2 — sprite/visual index table |
| 95 | 0008:d27e |
entity_dispatch_trampoline_b |
26 | Pure forwarder thunk (CALLF thunk only) |
| 96 | 0005:0376 |
entity_resolve_base_type |
26 | Walks entity class hierarchy (bit 8 in [0x7e01]) via [0x7ded], returns base type from [0x7df1] |
| 97 | 000b:2492 |
sprite_redraw_if_needed |
26 | If redraw flag at +0x17e is clear, calls update routine + thunk |
| 98 | 0003:e4d3 |
dos_file_open_wrapper |
26 | Zeros output byte, delegates to file open impl at 0003:bb92 |
| 99 | 0005:033e |
entity_resolve_base_parent |
25 | Same hierarchy walk as entity_resolve_base_type but returns parent from [0x7ded] |
| 100 | 000a:87fd |
render_clip_rect_to_viewport |
25 | Clips 4 rect params to viewport bounds at [0x4014], sets dirty flag at 0x8a16, increments draw counter at 0x4716 |
| 101 | 0005:3cf5 |
entity_class_has_flag2000 |
25 | Returns (entity_get_class_word(slot) & 0x2000) != 0 |
| 102 | 0009:80db |
bbox_overlap_test |
25 | Boolean rectangle overlap test for two 4-word bbox structs; unlike bbox_intersect, it does not write back an intersection |
| 103 | 000d:cc00 |
entity_compute_proximity_or_visibility_bucket |
25 | Returns 0x40 if entity is null or projected bbox overlaps viewport; otherwise buckets world-distance from current reference entity (0x7e22) into 0x32/0x20/0x10/0x08 |
| 104 | 000d:d413 |
entity_refresh_recent_proximity_or_visibility_buckets |
24 | Recomputes bucket values for the last four active entries in 0x69ac and notifies backing handles via 000a:6343 when a bucket changes |