Add detailed class event processing and family comparison tools
- Enhance `extract_eusecode_flx.py` to derive class event rows with additional metadata including derived body windows and repeated template statuses. - Introduce `usecode_family_compare.py` for comparing event families, analyzing commonalities in event bodies, and generating reports on identical groups and differences. - Implement new data structures for managing class event rows and family artifact specifications. - Update output formats to include derived body information and repeated family regression checks. - Ensure robust validation of repeated family expectations against actual extracted data.
This commit is contained in:
parent
de42fd1ea1
commit
4d3c8cd81b
23 changed files with 15033 additions and 14221 deletions
Binary file not shown.
Binary file not shown.
|
|
@ -9,644 +9,7 @@
|
|||
</SAVE_STATE>
|
||||
</PROJECT_DATA_XML_NAME>
|
||||
<TOOL_MANAGER ACTIVE_WORKSPACE="Workspace">
|
||||
<WORKSPACE NAME="Workspace" ACTIVE="true">
|
||||
<RUNNING_TOOL TOOL_NAME="CodeBrowser">
|
||||
<ROOT_NODE X_POS="1" Y_POS="73" WIDTH="1815" HEIGHT="1110" EX_STATE="0">
|
||||
<SPLIT_NODE WIDTH="100" HEIGHT="100" DIVIDER_LOCATION="0" ORIENTATION="VERTICAL">
|
||||
<SPLIT_NODE WIDTH="1801" HEIGHT="1014" DIVIDER_LOCATION="880" ORIENTATION="VERTICAL">
|
||||
<SPLIT_NODE WIDTH="1801" HEIGHT="889" DIVIDER_LOCATION="863" ORIENTATION="VERTICAL">
|
||||
<SPLIT_NODE WIDTH="100" HEIGHT="100" DIVIDER_LOCATION="0" ORIENTATION="VERTICAL">
|
||||
<SPLIT_NODE WIDTH="1621" HEIGHT="816" DIVIDER_LOCATION="148" ORIENTATION="VERTICAL">
|
||||
<COMPONENT_NODE TOP_INFO="0">
|
||||
<COMPONENT_INFO NAME="Entropy" OWNER="EntropyPlugin" TITLE="Entropy" ACTIVE="false" GROUP="Header" INSTANCE_ID="3207819926581772885" />
|
||||
<COMPONENT_INFO NAME="Overview" OWNER="OverviewPlugin" TITLE="Overview" ACTIVE="false" GROUP="Header" INSTANCE_ID="3207819926581772883" />
|
||||
</COMPONENT_NODE>
|
||||
<SPLIT_NODE WIDTH="1801" HEIGHT="764" DIVIDER_LOCATION="143" ORIENTATION="HORIZONTAL">
|
||||
<SPLIT_NODE WIDTH="257" HEIGHT="764" DIVIDER_LOCATION="640" ORIENTATION="VERTICAL">
|
||||
<SPLIT_NODE WIDTH="257" HEIGHT="486" DIVIDER_LOCATION="502" ORIENTATION="VERTICAL">
|
||||
<COMPONENT_NODE TOP_INFO="0">
|
||||
<COMPONENT_INFO NAME="Program Tree" OWNER="ProgramTreePlugin" TITLE="Program Trees" ACTIVE="true" GROUP="Default" INSTANCE_ID="3720737536777513708" />
|
||||
</COMPONENT_NODE>
|
||||
<COMPONENT_NODE TOP_INFO="0">
|
||||
<COMPONENT_INFO NAME="Symbol Tree" OWNER="SymbolTreePlugin" TITLE="Symbol Tree" ACTIVE="true" GROUP="Default" INSTANCE_ID="3720737536777513703" />
|
||||
</COMPONENT_NODE>
|
||||
</SPLIT_NODE>
|
||||
<COMPONENT_NODE TOP_INFO="0">
|
||||
<COMPONENT_INFO NAME="DataTypes Provider" OWNER="DataTypeManagerPlugin" TITLE="Data Type Manager" ACTIVE="true" GROUP="Default" INSTANCE_ID="3720738032686852857" />
|
||||
</COMPONENT_NODE>
|
||||
</SPLIT_NODE>
|
||||
<SPLIT_NODE WIDTH="1540" HEIGHT="764" DIVIDER_LOCATION="785" ORIENTATION="VERTICAL">
|
||||
<SPLIT_NODE WIDTH="1386" HEIGHT="638" DIVIDER_LOCATION="705" ORIENTATION="VERTICAL">
|
||||
<SPLIT_NODE WIDTH="1540" HEIGHT="597" DIVIDER_LOCATION="490" ORIENTATION="HORIZONTAL">
|
||||
<COMPONENT_NODE TOP_INFO="0">
|
||||
<COMPONENT_INFO NAME="Listing" OWNER="CodeBrowserPlugin" TITLE="Listing: CRUSADER-RAW.EXE" ACTIVE="true" GROUP="Core" INSTANCE_ID="3720737536777513718" />
|
||||
</COMPONENT_NODE>
|
||||
<COMPONENT_NODE TOP_INFO="0">
|
||||
<COMPONENT_INFO NAME="Decompiler" OWNER="DecompilePlugin" TITLE="Decompile: keyboard_state_read" ACTIVE="true" GROUP="Default" INSTANCE_ID="3720737536777513709" />
|
||||
<COMPONENT_INFO NAME="Bytes" OWNER="ByteViewerPlugin" TITLE="Bytes: CRUSADER-RAW.EXE" ACTIVE="false" GROUP="Default" INSTANCE_ID="3720737536777513711" />
|
||||
<COMPONENT_INFO NAME="Data Window" OWNER="DataWindowPlugin" TITLE="Defined Data" ACTIVE="false" GROUP="Default" INSTANCE_ID="3720738032686852862" />
|
||||
<COMPONENT_INFO NAME="Defined Strings" OWNER="ViewStringsPlugin" TITLE="Defined Strings" ACTIVE="false" GROUP="Default" INSTANCE_ID="3720738032917539554" />
|
||||
<COMPONENT_INFO NAME="Equates Table" OWNER="EquateTablePlugin" TITLE="Equates Table" ACTIVE="false" GROUP="Default" INSTANCE_ID="3720737536777513715" />
|
||||
<COMPONENT_INFO NAME="External Programs" OWNER="ReferencesPlugin" TITLE="External Programs" ACTIVE="false" GROUP="Default" INSTANCE_ID="3720737536777513719" />
|
||||
<COMPONENT_INFO NAME="Functions Window" OWNER="FunctionWindowPlugin" TITLE="Functions" ACTIVE="false" GROUP="Default" INSTANCE_ID="3720737536777513722" />
|
||||
<COMPONENT_INFO NAME="Relocation Table" OWNER="RelocationTablePlugin" TITLE="Relocation Table" ACTIVE="false" GROUP="Default" INSTANCE_ID="3720738032917539553" />
|
||||
</COMPONENT_NODE>
|
||||
</SPLIT_NODE>
|
||||
<SPLIT_NODE WIDTH="1386" HEIGHT="189" DIVIDER_LOCATION="495" ORIENTATION="HORIZONTAL">
|
||||
<COMPONENT_NODE TOP_INFO="0">
|
||||
<COMPONENT_INFO NAME="Data Type Preview" OWNER="DataTypePreviewPlugin" TITLE="Data Type Preview" ACTIVE="false" GROUP="Default" INSTANCE_ID="3720738032686852855" />
|
||||
</COMPONENT_NODE>
|
||||
<COMPONENT_NODE TOP_INFO="0">
|
||||
<COMPONENT_INFO NAME="Virtual Disassembler - Current Instruction" OWNER="DisassembledViewPlugin" TITLE="Disassembled View" ACTIVE="false" GROUP="Default" INSTANCE_ID="3720737536777513714" />
|
||||
</COMPONENT_NODE>
|
||||
</SPLIT_NODE>
|
||||
</SPLIT_NODE>
|
||||
<COMPONENT_NODE TOP_INFO="0">
|
||||
<COMPONENT_INFO NAME="Console" OWNER="ConsolePlugin" TITLE="Console" ACTIVE="true" GROUP="Default" INSTANCE_ID="3720737536777513710" />
|
||||
<COMPONENT_INFO NAME="Bookmarks" OWNER="BookmarkPlugin" TITLE="Bookmarks" ACTIVE="false" GROUP="Core.Bookmarks" INSTANCE_ID="3720737536777513707" />
|
||||
</COMPONENT_NODE>
|
||||
</SPLIT_NODE>
|
||||
</SPLIT_NODE>
|
||||
</SPLIT_NODE>
|
||||
<COMPONENT_NODE TOP_INFO="0">
|
||||
<COMPONENT_INFO NAME="Function Call Trees" OWNER="CallTreePlugin" TITLE="Function Call Trees" ACTIVE="false" GROUP="Default" INSTANCE_ID="3720737536777513704" />
|
||||
</COMPONENT_NODE>
|
||||
</SPLIT_NODE>
|
||||
<COMPONENT_NODE TOP_INFO="0">
|
||||
<COMPONENT_INFO NAME="Domain Events" OWNER="DomainEventDisplayPlugin" TITLE="Domain Object Event Display" ACTIVE="true" GROUP="Default" INSTANCE_ID="3720737536777513716" />
|
||||
</COMPONENT_NODE>
|
||||
</SPLIT_NODE>
|
||||
<COMPONENT_NODE TOP_INFO="0">
|
||||
<COMPONENT_INFO NAME="Plugin Event Display" OWNER="EventDisplayPlugin" TITLE="Plugin Event Display" ACTIVE="true" GROUP="Default" INSTANCE_ID="3720737536777513713" />
|
||||
</COMPONENT_NODE>
|
||||
</SPLIT_NODE>
|
||||
<COMPONENT_NODE TOP_INFO="0">
|
||||
<COMPONENT_INFO NAME="Database Viewer" OWNER="DbViewerPlugin" TITLE="Database Viewer" ACTIVE="false" GROUP="Default" INSTANCE_ID="3720738034586872545" />
|
||||
</COMPONENT_NODE>
|
||||
</SPLIT_NODE>
|
||||
<WINDOW_NODE X_POS="426" Y_POS="178" WIDTH="1033" HEIGHT="689">
|
||||
<COMPONENT_NODE TOP_INFO="0">
|
||||
<COMPONENT_INFO NAME="Script Manager" OWNER="GhidraScriptMgrPlugin" TITLE="Script Manager" ACTIVE="false" GROUP="Script Group" INSTANCE_ID="3720737536777513705" />
|
||||
</COMPONENT_NODE>
|
||||
</WINDOW_NODE>
|
||||
<WINDOW_NODE X_POS="423" Y_POS="144" WIDTH="927" HEIGHT="370">
|
||||
<COMPONENT_NODE TOP_INFO="0">
|
||||
<COMPONENT_INFO NAME="Memory Map" OWNER="MemoryMapPlugin" TITLE="Memory Map" ACTIVE="false" GROUP="Default" INSTANCE_ID="3720737536777513700" />
|
||||
</COMPONENT_NODE>
|
||||
</WINDOW_NODE>
|
||||
<WINDOW_NODE X_POS="383" Y_POS="7" WIDTH="1020" HEIGHT="1038">
|
||||
<COMPONENT_NODE TOP_INFO="0">
|
||||
<COMPONENT_INFO NAME="Function Graph" OWNER="FunctionGraphPlugin" TITLE="Function Graph" ACTIVE="false" GROUP="Function Graph" INSTANCE_ID="3720738032917539555" />
|
||||
</COMPONENT_NODE>
|
||||
</WINDOW_NODE>
|
||||
<WINDOW_NODE X_POS="550" Y_POS="206" WIDTH="655" HEIGHT="509">
|
||||
<COMPONENT_NODE TOP_INFO="0">
|
||||
<COMPONENT_INFO NAME="Register Manager" OWNER="RegisterPlugin" TITLE="Register Manager" ACTIVE="false" GROUP="Default" INSTANCE_ID="3720737536777513721" />
|
||||
</COMPONENT_NODE>
|
||||
</WINDOW_NODE>
|
||||
<WINDOW_NODE X_POS="287" Y_POS="186" WIDTH="1424" HEIGHT="666">
|
||||
<SPLIT_NODE WIDTH="1408" HEIGHT="559" DIVIDER_LOCATION="573" ORIENTATION="HORIZONTAL">
|
||||
<COMPONENT_NODE TOP_INFO="0">
|
||||
<COMPONENT_INFO NAME="Symbol Table" OWNER="SymbolTablePlugin" TITLE="Symbol Table" ACTIVE="false" GROUP="symbolTable" INSTANCE_ID="3720738032686852863" />
|
||||
</COMPONENT_NODE>
|
||||
<COMPONENT_NODE TOP_INFO="0">
|
||||
<COMPONENT_INFO NAME="Symbol References" OWNER="SymbolTablePlugin" TITLE="Symbol References" ACTIVE="false" GROUP="symbolTable" INSTANCE_ID="3720738032917539552" />
|
||||
</COMPONENT_NODE>
|
||||
</SPLIT_NODE>
|
||||
</WINDOW_NODE>
|
||||
<WINDOW_NODE X_POS="-1" Y_POS="-1" WIDTH="0" HEIGHT="0">
|
||||
<COMPONENT_NODE TOP_INFO="0">
|
||||
<COMPONENT_INFO NAME="Checksum Generator" OWNER="ComputeChecksumsPlugin" TITLE="Checksum Generator" ACTIVE="false" GROUP="Default" INSTANCE_ID="3720737536777513717" />
|
||||
</COMPONENT_NODE>
|
||||
</WINDOW_NODE>
|
||||
<WINDOW_NODE X_POS="-1" Y_POS="-1" WIDTH="0" HEIGHT="0">
|
||||
<COMPONENT_NODE TOP_INFO="0">
|
||||
<COMPONENT_INFO NAME="Function Tags" OWNER="FunctionTagPlugin" TITLE="Function Tags" ACTIVE="false" GROUP="Default" INSTANCE_ID="3720738032686852858" />
|
||||
</COMPONENT_NODE>
|
||||
</WINDOW_NODE>
|
||||
<WINDOW_NODE X_POS="-1" Y_POS="-1" WIDTH="0" HEIGHT="0">
|
||||
<COMPONENT_NODE TOP_INFO="0">
|
||||
<COMPONENT_INFO NAME="Comment Window" OWNER="CommentWindowPlugin" TITLE="Comments" ACTIVE="false" GROUP="Default" INSTANCE_ID="3720738032686852861" />
|
||||
</COMPONENT_NODE>
|
||||
</WINDOW_NODE>
|
||||
<WINDOW_NODE X_POS="-1" Y_POS="-1" WIDTH="0" HEIGHT="0">
|
||||
<COMPONENT_NODE TOP_INFO="0">
|
||||
<COMPONENT_INFO NAME="Python" OWNER="InterpreterPanelPlugin" TITLE="Python" ACTIVE="false" GROUP="Default" INSTANCE_ID="3207819978370941531" />
|
||||
</COMPONENT_NODE>
|
||||
</WINDOW_NODE>
|
||||
<WINDOW_NODE X_POS="0" Y_POS="0" WIDTH="0" HEIGHT="0">
|
||||
<COMPONENT_NODE TOP_INFO="0">
|
||||
<COMPONENT_INFO NAME="Jython" OWNER="Jython" TITLE="Jython" ACTIVE="false" GROUP="Default" INSTANCE_ID="3720738032686852859" />
|
||||
</COMPONENT_NODE>
|
||||
</WINDOW_NODE>
|
||||
<WINDOW_NODE X_POS="0" Y_POS="0" WIDTH="0" HEIGHT="0">
|
||||
<COMPONENT_NODE TOP_INFO="0">
|
||||
<COMPONENT_INFO NAME="Bundle Manager" OWNER="GhidraScriptMgrPlugin" TITLE="Bundle Manager" ACTIVE="false" GROUP="Default" INSTANCE_ID="3720737536777513706" />
|
||||
</COMPONENT_NODE>
|
||||
</WINDOW_NODE>
|
||||
<WINDOW_NODE X_POS="0" Y_POS="0" WIDTH="0" HEIGHT="0">
|
||||
<COMPONENT_NODE TOP_INFO="0">
|
||||
<COMPONENT_INFO NAME="PyGhidra" OWNER="PyGhidra" TITLE="PyGhidra" ACTIVE="false" GROUP="Default" INSTANCE_ID="3720738032686852860" />
|
||||
</COMPONENT_NODE>
|
||||
</WINDOW_NODE>
|
||||
<WINDOW_NODE X_POS="0" Y_POS="0" WIDTH="0" HEIGHT="0">
|
||||
<COMPONENT_NODE TOP_INFO="0">
|
||||
<COMPONENT_INFO NAME="Source Files and Transforms" OWNER="SourceFilesTablePlugin" TITLE="Source Files and Transforms" ACTIVE="false" GROUP="Default" INSTANCE_ID="3720737536777513720" />
|
||||
</COMPONENT_NODE>
|
||||
</WINDOW_NODE>
|
||||
<WINDOW_NODE X_POS="0" Y_POS="0" WIDTH="0" HEIGHT="0">
|
||||
<COMPONENT_NODE TOP_INFO="0">
|
||||
<COMPONENT_INFO NAME="Function Call Graph" OWNER="FunctionCallGraphPlugin" TITLE="Function Call Graph" ACTIVE="false" GROUP="Function Call Graph" INSTANCE_ID="3720738032686852856" />
|
||||
</COMPONENT_NODE>
|
||||
</WINDOW_NODE>
|
||||
<WINDOW_NODE X_POS="588" Y_POS="50" WIDTH="1018" HEIGHT="1087">
|
||||
<COMPONENT_NODE TOP_INFO="0">
|
||||
<COMPONENT_INFO NAME="Graph" OWNER="DefaultGraphDisplay" TITLE="AST Data Flow Graph For entity_state_tick_dispatch" ACTIVE="false" GROUP="ProgramGraph" INSTANCE_ID="3720233517670421199" />
|
||||
</COMPONENT_NODE>
|
||||
</WINDOW_NODE>
|
||||
<WINDOW_NODE X_POS="0" Y_POS="0" WIDTH="0" HEIGHT="0">
|
||||
<COMPONENT_NODE TOP_INFO="0">
|
||||
<COMPONENT_INFO NAME="Window Locations" OWNER="WindowLocationPlugin" TITLE="Window Locations" ACTIVE="false" GROUP="Default" INSTANCE_ID="3720737536777513701" />
|
||||
</COMPONENT_NODE>
|
||||
</WINDOW_NODE>
|
||||
</ROOT_NODE>
|
||||
<DATA_STATE>
|
||||
<PLUGIN NAME="NavigationHistoryPlugin">
|
||||
<XML NAME="HISTORY_LIST_0">
|
||||
<SAVE_STATE>
|
||||
<STATE NAME="CURRENT_LOC_INDEX" TYPE="int" VALUE="6" />
|
||||
<STATE NAME="LOCATION_COUNT" TYPE="int" VALUE="7" />
|
||||
<STATE NAME="MEMENTO_CLASS0" TYPE="string" VALUE="ghidra.app.plugin.core.gotoquery.DefaultNavigatableLocationMemento" />
|
||||
<STATE NAME="MEMENTO_CLASS1" TYPE="string" VALUE="ghidra.app.plugin.core.gotoquery.DefaultNavigatableLocationMemento" />
|
||||
<STATE NAME="MEMENTO_CLASS2" TYPE="string" VALUE="ghidra.app.plugin.core.gotoquery.DefaultNavigatableLocationMemento" />
|
||||
<STATE NAME="MEMENTO_CLASS3" TYPE="string" VALUE="ghidra.app.plugin.core.gotoquery.DefaultNavigatableLocationMemento" />
|
||||
<STATE NAME="MEMENTO_CLASS4" TYPE="string" VALUE="ghidra.app.plugin.core.gotoquery.DefaultNavigatableLocationMemento" />
|
||||
<STATE NAME="MEMENTO_CLASS5" TYPE="string" VALUE="ghidra.app.plugin.core.gotoquery.DefaultNavigatableLocationMemento" />
|
||||
<STATE NAME="MEMENTO_CLASS6" TYPE="string" VALUE="ghidra.app.plugin.core.gotoquery.DefaultNavigatableLocationMemento" />
|
||||
<XML NAME="MEMENTO_DATA0">
|
||||
<SAVE_STATE>
|
||||
<XML NAME="MEMENTO0">
|
||||
<SAVE_STATE>
|
||||
<STATE NAME="CURSOR_OFFSET" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="MEMENTO_CLASS" TYPE="string" VALUE="ghidra.app.plugin.core.codebrowser.CodeViewerLocationMemento" />
|
||||
<STATE NAME="NAV_ID" TYPE="long" VALUE="3720737536777513718" />
|
||||
<STATE NAME="PROGRAM_ID" TYPE="long" VALUE="3720180275162699156" />
|
||||
<STATE NAME="PROGRAM_PATH_" TYPE="string" VALUE="Crusader:/CRUSADER-RAW.EXE" />
|
||||
<STATE NAME="_ADDRESS" TYPE="string" VALUE="0000:0000" />
|
||||
<STATE NAME="_BYTE_ADDR" TYPE="string" VALUE="0000:0000" />
|
||||
<STATE NAME="_CHAR_OFFSET" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="_CLASSNAME" TYPE="string" VALUE="ghidra.program.util.MemoryBlockStartFieldLocation" />
|
||||
<STATE NAME="_COLUMN" TYPE="int" VALUE="0" />
|
||||
<ARRAY NAME="_COMMENT" TYPE="string">
|
||||
<A VALUE="//" />
|
||||
<A VALUE="// ram" />
|
||||
<A VALUE="// ram:0000:0000-ram:000f:2285" />
|
||||
<A VALUE="//" />
|
||||
</ARRAY>
|
||||
<STATE NAME="_ROW" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="_TYPE" TYPE="int" VALUE="-1" />
|
||||
</SAVE_STATE>
|
||||
</XML>
|
||||
<STATE NAME="MEMENTO_CLASS" TYPE="string" VALUE="ghidra.app.plugin.core.gotoquery.DefaultNavigatableLocationMemento" />
|
||||
<STATE NAME="NUM_MEMENTOS" TYPE="int" VALUE="1" />
|
||||
<STATE NAME="PROGRAM_ID" TYPE="long" VALUE="3720180275162699156" />
|
||||
<STATE NAME="PROGRAM_PATH_" TYPE="string" VALUE="Crusader:/CRUSADER-RAW.EXE" />
|
||||
<STATE NAME="_ADDRESS" TYPE="string" VALUE="0000:0000" />
|
||||
<STATE NAME="_BYTE_ADDR" TYPE="string" VALUE="0000:0000" />
|
||||
<STATE NAME="_CHAR_OFFSET" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="_CLASSNAME" TYPE="string" VALUE="ghidra.program.util.MemoryBlockStartFieldLocation" />
|
||||
<STATE NAME="_COLUMN" TYPE="int" VALUE="0" />
|
||||
<ARRAY NAME="_COMMENT" TYPE="string">
|
||||
<A VALUE="//" />
|
||||
<A VALUE="// ram" />
|
||||
<A VALUE="// ram:0000:0000-ram:000f:2285" />
|
||||
<A VALUE="//" />
|
||||
</ARRAY>
|
||||
<STATE NAME="_ROW" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="_TYPE" TYPE="int" VALUE="-1" />
|
||||
</SAVE_STATE>
|
||||
</XML>
|
||||
<XML NAME="MEMENTO_DATA1">
|
||||
<SAVE_STATE>
|
||||
<XML NAME="MEMENTO0">
|
||||
<SAVE_STATE>
|
||||
<STATE NAME="INDEX" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="MEMENTO_CLASS" TYPE="string" VALUE="ghidra.app.plugin.core.decompile.DecompilerLocationMemento" />
|
||||
<STATE NAME="NAV_ID" TYPE="long" VALUE="3720737536777513709" />
|
||||
<STATE NAME="PROGRAM_ID" TYPE="long" VALUE="3720180275162699156" />
|
||||
<STATE NAME="PROGRAM_PATH_" TYPE="string" VALUE="Crusader:/CRUSADER-RAW.EXE" />
|
||||
<STATE NAME="X_OFFSET" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="Y_OFFSET" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="_ADDRESS" TYPE="string" VALUE="0007:04dc" />
|
||||
<STATE NAME="_BYTE_ADDR" TYPE="string" VALUE="0007:04dc" />
|
||||
<STATE NAME="_CHAR_OFFSET" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="_CLASSNAME" TYPE="string" VALUE="ghidra.program.util.FunctionReturnTypeFieldLocation" />
|
||||
<STATE NAME="_COLUMN" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="_FUNC_ADDRESS" TYPE="string" VALUE="0007:04dc" />
|
||||
<STATE NAME="_RETURN_TYPE" TYPE="string" VALUE="undefined" />
|
||||
<STATE NAME="_ROW" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="_SIGNATURE" TYPE="string" VALUE="undefined __cdecl16far keyboard_input_cheat_dispatch()" />
|
||||
</SAVE_STATE>
|
||||
</XML>
|
||||
<XML NAME="MEMENTO1">
|
||||
<SAVE_STATE>
|
||||
<STATE NAME="CURSOR_OFFSET" TYPE="int" VALUE="283" />
|
||||
<STATE NAME="MEMENTO_CLASS" TYPE="string" VALUE="ghidra.app.plugin.core.codebrowser.CodeViewerLocationMemento" />
|
||||
<STATE NAME="NAV_ID" TYPE="long" VALUE="3720737536777513718" />
|
||||
<STATE NAME="PROGRAM_ID" TYPE="long" VALUE="3720180275162699156" />
|
||||
<STATE NAME="PROGRAM_PATH_" TYPE="string" VALUE="Crusader:/CRUSADER-RAW.EXE" />
|
||||
<STATE NAME="_ADDRESS" TYPE="string" VALUE="0007:04dc" />
|
||||
<STATE NAME="_BYTE_ADDR" TYPE="string" VALUE="0007:04dc" />
|
||||
<STATE NAME="_CHAR_OFFSET" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="_CLASSNAME" TYPE="string" VALUE="ghidra.program.util.FunctionReturnTypeFieldLocation" />
|
||||
<STATE NAME="_COLUMN" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="_FUNC_ADDRESS" TYPE="string" VALUE="0007:04dc" />
|
||||
<STATE NAME="_RETURN_TYPE" TYPE="string" VALUE="undefined" />
|
||||
<STATE NAME="_ROW" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="_SIGNATURE" TYPE="string" VALUE="undefined __cdecl16far keyboard_input_cheat_dispatch()" />
|
||||
</SAVE_STATE>
|
||||
</XML>
|
||||
<STATE NAME="MEMENTO_CLASS" TYPE="string" VALUE="ghidra.app.plugin.core.gotoquery.DefaultNavigatableLocationMemento" />
|
||||
<STATE NAME="NUM_MEMENTOS" TYPE="int" VALUE="2" />
|
||||
<STATE NAME="PROGRAM_ID" TYPE="long" VALUE="3720180275162699156" />
|
||||
<STATE NAME="PROGRAM_PATH_" TYPE="string" VALUE="Crusader:/CRUSADER-RAW.EXE" />
|
||||
<STATE NAME="_ADDRESS" TYPE="string" VALUE="0007:04dc" />
|
||||
<STATE NAME="_BYTE_ADDR" TYPE="string" VALUE="0007:04dc" />
|
||||
<STATE NAME="_CHAR_OFFSET" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="_CLASSNAME" TYPE="string" VALUE="ghidra.program.util.FunctionReturnTypeFieldLocation" />
|
||||
<STATE NAME="_COLUMN" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="_FUNC_ADDRESS" TYPE="string" VALUE="0007:04dc" />
|
||||
<STATE NAME="_RETURN_TYPE" TYPE="string" VALUE="undefined" />
|
||||
<STATE NAME="_ROW" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="_SIGNATURE" TYPE="string" VALUE="undefined __cdecl16far keyboard_input_cheat_dispatch()" />
|
||||
</SAVE_STATE>
|
||||
</XML>
|
||||
<XML NAME="MEMENTO_DATA2">
|
||||
<SAVE_STATE>
|
||||
<XML NAME="MEMENTO0">
|
||||
<SAVE_STATE>
|
||||
<STATE NAME="INDEX" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="MEMENTO_CLASS" TYPE="string" VALUE="ghidra.app.plugin.core.decompile.DecompilerLocationMemento" />
|
||||
<STATE NAME="NAV_ID" TYPE="long" VALUE="3720737536777513709" />
|
||||
<STATE NAME="PROGRAM_ID" TYPE="long" VALUE="3720180275162699156" />
|
||||
<STATE NAME="PROGRAM_PATH_" TYPE="string" VALUE="Crusader:/CRUSADER-RAW.EXE" />
|
||||
<STATE NAME="X_OFFSET" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="Y_OFFSET" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="_ADDRESS" TYPE="string" VALUE="0008:a3fb" />
|
||||
<STATE NAME="_BYTE_ADDR" TYPE="string" VALUE="0008:a3fb" />
|
||||
<STATE NAME="_CHAR_OFFSET" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="_CLASSNAME" TYPE="string" VALUE="ghidra.program.util.FunctionReturnTypeFieldLocation" />
|
||||
<STATE NAME="_COLUMN" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="_FUNC_ADDRESS" TYPE="string" VALUE="0008:a3fb" />
|
||||
<STATE NAME="_RETURN_TYPE" TYPE="string" VALUE="undefined" />
|
||||
<STATE NAME="_ROW" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="_SIGNATURE" TYPE="string" VALUE="undefined keyboard_acquire()" />
|
||||
</SAVE_STATE>
|
||||
</XML>
|
||||
<XML NAME="MEMENTO1">
|
||||
<SAVE_STATE>
|
||||
<STATE NAME="CURSOR_OFFSET" TYPE="int" VALUE="278" />
|
||||
<STATE NAME="MEMENTO_CLASS" TYPE="string" VALUE="ghidra.app.plugin.core.codebrowser.CodeViewerLocationMemento" />
|
||||
<STATE NAME="NAV_ID" TYPE="long" VALUE="3720737536777513718" />
|
||||
<STATE NAME="PROGRAM_ID" TYPE="long" VALUE="3720180275162699156" />
|
||||
<STATE NAME="PROGRAM_PATH_" TYPE="string" VALUE="Crusader:/CRUSADER-RAW.EXE" />
|
||||
<STATE NAME="_ADDRESS" TYPE="string" VALUE="0008:a3fb" />
|
||||
<STATE NAME="_BYTE_ADDR" TYPE="string" VALUE="0008:a3fb" />
|
||||
<STATE NAME="_CHAR_OFFSET" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="_CLASSNAME" TYPE="string" VALUE="ghidra.program.util.FunctionReturnTypeFieldLocation" />
|
||||
<STATE NAME="_COLUMN" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="_FUNC_ADDRESS" TYPE="string" VALUE="0008:a3fb" />
|
||||
<STATE NAME="_RETURN_TYPE" TYPE="string" VALUE="undefined" />
|
||||
<STATE NAME="_ROW" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="_SIGNATURE" TYPE="string" VALUE="undefined keyboard_acquire()" />
|
||||
</SAVE_STATE>
|
||||
</XML>
|
||||
<STATE NAME="MEMENTO_CLASS" TYPE="string" VALUE="ghidra.app.plugin.core.gotoquery.DefaultNavigatableLocationMemento" />
|
||||
<STATE NAME="NUM_MEMENTOS" TYPE="int" VALUE="2" />
|
||||
<STATE NAME="PROGRAM_ID" TYPE="long" VALUE="3720180275162699156" />
|
||||
<STATE NAME="PROGRAM_PATH_" TYPE="string" VALUE="Crusader:/CRUSADER-RAW.EXE" />
|
||||
<STATE NAME="_ADDRESS" TYPE="string" VALUE="0008:a3fb" />
|
||||
<STATE NAME="_BYTE_ADDR" TYPE="string" VALUE="0008:a3fb" />
|
||||
<STATE NAME="_CHAR_OFFSET" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="_CLASSNAME" TYPE="string" VALUE="ghidra.program.util.FunctionReturnTypeFieldLocation" />
|
||||
<STATE NAME="_COLUMN" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="_FUNC_ADDRESS" TYPE="string" VALUE="0008:a3fb" />
|
||||
<STATE NAME="_RETURN_TYPE" TYPE="string" VALUE="undefined" />
|
||||
<STATE NAME="_ROW" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="_SIGNATURE" TYPE="string" VALUE="undefined keyboard_acquire()" />
|
||||
</SAVE_STATE>
|
||||
</XML>
|
||||
<XML NAME="MEMENTO_DATA3">
|
||||
<SAVE_STATE>
|
||||
<XML NAME="MEMENTO0">
|
||||
<SAVE_STATE>
|
||||
<STATE NAME="INDEX" TYPE="int" VALUE="210" />
|
||||
<STATE NAME="MEMENTO_CLASS" TYPE="string" VALUE="ghidra.app.plugin.core.decompile.DecompilerLocationMemento" />
|
||||
<STATE NAME="NAV_ID" TYPE="long" VALUE="3720737536777513709" />
|
||||
<STATE NAME="PROGRAM_ID" TYPE="long" VALUE="3720180275162699156" />
|
||||
<STATE NAME="PROGRAM_PATH_" TYPE="string" VALUE="Crusader:/CRUSADER-RAW.EXE" />
|
||||
<STATE NAME="X_OFFSET" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="Y_OFFSET" TYPE="int" VALUE="-9" />
|
||||
<STATE NAME="_ADDRESS" TYPE="string" VALUE="0007:04dc" />
|
||||
<STATE NAME="_BYTE_ADDR" TYPE="string" VALUE="0007:04dc" />
|
||||
<STATE NAME="_CHAR_OFFSET" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="_CLASSNAME" TYPE="string" VALUE="ghidra.program.util.FunctionReturnTypeFieldLocation" />
|
||||
<STATE NAME="_COLUMN" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="_FUNC_ADDRESS" TYPE="string" VALUE="0007:04dc" />
|
||||
<STATE NAME="_RETURN_TYPE" TYPE="string" VALUE="undefined" />
|
||||
<STATE NAME="_ROW" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="_SIGNATURE" TYPE="string" VALUE="undefined keyboard_input_cheat_dispatch()" />
|
||||
</SAVE_STATE>
|
||||
</XML>
|
||||
<XML NAME="MEMENTO1">
|
||||
<SAVE_STATE>
|
||||
<STATE NAME="CURSOR_OFFSET" TYPE="int" VALUE="278" />
|
||||
<STATE NAME="MEMENTO_CLASS" TYPE="string" VALUE="ghidra.app.plugin.core.codebrowser.CodeViewerLocationMemento" />
|
||||
<STATE NAME="NAV_ID" TYPE="long" VALUE="3720737536777513718" />
|
||||
<STATE NAME="PROGRAM_ID" TYPE="long" VALUE="3720180275162699156" />
|
||||
<STATE NAME="PROGRAM_PATH_" TYPE="string" VALUE="Crusader:/CRUSADER-RAW.EXE" />
|
||||
<STATE NAME="_ADDRESS" TYPE="string" VALUE="0007:04dc" />
|
||||
<STATE NAME="_BYTE_ADDR" TYPE="string" VALUE="0007:04dc" />
|
||||
<STATE NAME="_CHAR_OFFSET" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="_CLASSNAME" TYPE="string" VALUE="ghidra.program.util.FunctionReturnTypeFieldLocation" />
|
||||
<STATE NAME="_COLUMN" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="_FUNC_ADDRESS" TYPE="string" VALUE="0007:04dc" />
|
||||
<STATE NAME="_RETURN_TYPE" TYPE="string" VALUE="undefined" />
|
||||
<STATE NAME="_ROW" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="_SIGNATURE" TYPE="string" VALUE="undefined __cdecl16far keyboard_input_cheat_dispatch()" />
|
||||
</SAVE_STATE>
|
||||
</XML>
|
||||
<STATE NAME="MEMENTO_CLASS" TYPE="string" VALUE="ghidra.app.plugin.core.gotoquery.DefaultNavigatableLocationMemento" />
|
||||
<STATE NAME="NUM_MEMENTOS" TYPE="int" VALUE="2" />
|
||||
<STATE NAME="PROGRAM_ID" TYPE="long" VALUE="3720180275162699156" />
|
||||
<STATE NAME="PROGRAM_PATH_" TYPE="string" VALUE="Crusader:/CRUSADER-RAW.EXE" />
|
||||
<STATE NAME="_ADDRESS" TYPE="string" VALUE="0007:04dc" />
|
||||
<STATE NAME="_BYTE_ADDR" TYPE="string" VALUE="0007:04dc" />
|
||||
<STATE NAME="_CHAR_OFFSET" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="_CLASSNAME" TYPE="string" VALUE="ghidra.program.util.FunctionReturnTypeFieldLocation" />
|
||||
<STATE NAME="_COLUMN" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="_FUNC_ADDRESS" TYPE="string" VALUE="0007:04dc" />
|
||||
<STATE NAME="_RETURN_TYPE" TYPE="string" VALUE="undefined" />
|
||||
<STATE NAME="_ROW" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="_SIGNATURE" TYPE="string" VALUE="undefined keyboard_input_cheat_dispatch()" />
|
||||
</SAVE_STATE>
|
||||
</XML>
|
||||
<XML NAME="MEMENTO_DATA4">
|
||||
<SAVE_STATE>
|
||||
<XML NAME="MEMENTO0">
|
||||
<SAVE_STATE>
|
||||
<STATE NAME="INDEX" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="MEMENTO_CLASS" TYPE="string" VALUE="ghidra.app.plugin.core.decompile.DecompilerLocationMemento" />
|
||||
<STATE NAME="NAV_ID" TYPE="long" VALUE="3720737536777513709" />
|
||||
<STATE NAME="PROGRAM_ID" TYPE="long" VALUE="3720180275162699156" />
|
||||
<STATE NAME="PROGRAM_PATH_" TYPE="string" VALUE="Crusader:/CRUSADER-RAW.EXE" />
|
||||
<STATE NAME="X_OFFSET" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="Y_OFFSET" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="_ADDRESS" TYPE="string" VALUE="0008:a77d" />
|
||||
<STATE NAME="_BYTE_ADDR" TYPE="string" VALUE="0008:a77d" />
|
||||
<STATE NAME="_CHAR_OFFSET" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="_CLASSNAME" TYPE="string" VALUE="ghidra.program.util.FunctionReturnTypeFieldLocation" />
|
||||
<STATE NAME="_COLUMN" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="_FUNC_ADDRESS" TYPE="string" VALUE="0008:a77d" />
|
||||
<STATE NAME="_RETURN_TYPE" TYPE="string" VALUE="undefined" />
|
||||
<STATE NAME="_ROW" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="_SIGNATURE" TYPE="string" VALUE="undefined keyboard_interrupt_call()" />
|
||||
</SAVE_STATE>
|
||||
</XML>
|
||||
<XML NAME="MEMENTO1">
|
||||
<SAVE_STATE>
|
||||
<STATE NAME="CURSOR_OFFSET" TYPE="int" VALUE="278" />
|
||||
<STATE NAME="MEMENTO_CLASS" TYPE="string" VALUE="ghidra.app.plugin.core.codebrowser.CodeViewerLocationMemento" />
|
||||
<STATE NAME="NAV_ID" TYPE="long" VALUE="3720737536777513718" />
|
||||
<STATE NAME="PROGRAM_ID" TYPE="long" VALUE="3720180275162699156" />
|
||||
<STATE NAME="PROGRAM_PATH_" TYPE="string" VALUE="Crusader:/CRUSADER-RAW.EXE" />
|
||||
<STATE NAME="_ADDRESS" TYPE="string" VALUE="0008:a77d" />
|
||||
<STATE NAME="_BYTE_ADDR" TYPE="string" VALUE="0008:a77d" />
|
||||
<STATE NAME="_CHAR_OFFSET" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="_CLASSNAME" TYPE="string" VALUE="ghidra.program.util.FunctionReturnTypeFieldLocation" />
|
||||
<STATE NAME="_COLUMN" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="_FUNC_ADDRESS" TYPE="string" VALUE="0008:a77d" />
|
||||
<STATE NAME="_RETURN_TYPE" TYPE="string" VALUE="undefined" />
|
||||
<STATE NAME="_ROW" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="_SIGNATURE" TYPE="string" VALUE="undefined __cdecl16far keyboard_interrupt_call()" />
|
||||
</SAVE_STATE>
|
||||
</XML>
|
||||
<STATE NAME="MEMENTO_CLASS" TYPE="string" VALUE="ghidra.app.plugin.core.gotoquery.DefaultNavigatableLocationMemento" />
|
||||
<STATE NAME="NUM_MEMENTOS" TYPE="int" VALUE="2" />
|
||||
<STATE NAME="PROGRAM_ID" TYPE="long" VALUE="3720180275162699156" />
|
||||
<STATE NAME="PROGRAM_PATH_" TYPE="string" VALUE="Crusader:/CRUSADER-RAW.EXE" />
|
||||
<STATE NAME="_ADDRESS" TYPE="string" VALUE="0008:a77d" />
|
||||
<STATE NAME="_BYTE_ADDR" TYPE="string" VALUE="0008:a77d" />
|
||||
<STATE NAME="_CHAR_OFFSET" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="_CLASSNAME" TYPE="string" VALUE="ghidra.program.util.FunctionReturnTypeFieldLocation" />
|
||||
<STATE NAME="_COLUMN" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="_FUNC_ADDRESS" TYPE="string" VALUE="0008:a77d" />
|
||||
<STATE NAME="_RETURN_TYPE" TYPE="string" VALUE="undefined" />
|
||||
<STATE NAME="_ROW" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="_SIGNATURE" TYPE="string" VALUE="undefined keyboard_interrupt_call()" />
|
||||
</SAVE_STATE>
|
||||
</XML>
|
||||
<XML NAME="MEMENTO_DATA5">
|
||||
<SAVE_STATE>
|
||||
<XML NAME="MEMENTO0">
|
||||
<SAVE_STATE>
|
||||
<STATE NAME="INDEX" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="MEMENTO_CLASS" TYPE="string" VALUE="ghidra.app.plugin.core.decompile.DecompilerLocationMemento" />
|
||||
<STATE NAME="NAV_ID" TYPE="long" VALUE="3720737536777513709" />
|
||||
<STATE NAME="PROGRAM_ID" TYPE="long" VALUE="3720180275162699156" />
|
||||
<STATE NAME="PROGRAM_PATH_" TYPE="string" VALUE="Crusader:/CRUSADER-RAW.EXE" />
|
||||
<STATE NAME="X_OFFSET" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="Y_OFFSET" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="_ADDRESS" TYPE="string" VALUE="0008:a43c" />
|
||||
<STATE NAME="_BYTE_ADDR" TYPE="string" VALUE="0008:a43c" />
|
||||
<STATE NAME="_CHAR_OFFSET" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="_CLASSNAME" TYPE="string" VALUE="ghidra.program.util.FunctionReturnTypeFieldLocation" />
|
||||
<STATE NAME="_COLUMN" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="_FUNC_ADDRESS" TYPE="string" VALUE="0008:a43c" />
|
||||
<STATE NAME="_RETURN_TYPE" TYPE="string" VALUE="undefined" />
|
||||
<STATE NAME="_ROW" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="_SIGNATURE" TYPE="string" VALUE="undefined keyboard_release()" />
|
||||
</SAVE_STATE>
|
||||
</XML>
|
||||
<XML NAME="MEMENTO1">
|
||||
<SAVE_STATE>
|
||||
<STATE NAME="CURSOR_OFFSET" TYPE="int" VALUE="278" />
|
||||
<STATE NAME="MEMENTO_CLASS" TYPE="string" VALUE="ghidra.app.plugin.core.codebrowser.CodeViewerLocationMemento" />
|
||||
<STATE NAME="NAV_ID" TYPE="long" VALUE="3720737536777513718" />
|
||||
<STATE NAME="PROGRAM_ID" TYPE="long" VALUE="3720180275162699156" />
|
||||
<STATE NAME="PROGRAM_PATH_" TYPE="string" VALUE="Crusader:/CRUSADER-RAW.EXE" />
|
||||
<STATE NAME="_ADDRESS" TYPE="string" VALUE="0008:a43c" />
|
||||
<STATE NAME="_BYTE_ADDR" TYPE="string" VALUE="0008:a43c" />
|
||||
<STATE NAME="_CHAR_OFFSET" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="_CLASSNAME" TYPE="string" VALUE="ghidra.program.util.FunctionReturnTypeFieldLocation" />
|
||||
<STATE NAME="_COLUMN" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="_FUNC_ADDRESS" TYPE="string" VALUE="0008:a43c" />
|
||||
<STATE NAME="_RETURN_TYPE" TYPE="string" VALUE="undefined" />
|
||||
<STATE NAME="_ROW" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="_SIGNATURE" TYPE="string" VALUE="undefined __cdecl16far keyboard_release()" />
|
||||
</SAVE_STATE>
|
||||
</XML>
|
||||
<STATE NAME="MEMENTO_CLASS" TYPE="string" VALUE="ghidra.app.plugin.core.gotoquery.DefaultNavigatableLocationMemento" />
|
||||
<STATE NAME="NUM_MEMENTOS" TYPE="int" VALUE="2" />
|
||||
<STATE NAME="PROGRAM_ID" TYPE="long" VALUE="3720180275162699156" />
|
||||
<STATE NAME="PROGRAM_PATH_" TYPE="string" VALUE="Crusader:/CRUSADER-RAW.EXE" />
|
||||
<STATE NAME="_ADDRESS" TYPE="string" VALUE="0008:a43c" />
|
||||
<STATE NAME="_BYTE_ADDR" TYPE="string" VALUE="0008:a43c" />
|
||||
<STATE NAME="_CHAR_OFFSET" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="_CLASSNAME" TYPE="string" VALUE="ghidra.program.util.FunctionReturnTypeFieldLocation" />
|
||||
<STATE NAME="_COLUMN" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="_FUNC_ADDRESS" TYPE="string" VALUE="0008:a43c" />
|
||||
<STATE NAME="_RETURN_TYPE" TYPE="string" VALUE="undefined" />
|
||||
<STATE NAME="_ROW" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="_SIGNATURE" TYPE="string" VALUE="undefined keyboard_release()" />
|
||||
</SAVE_STATE>
|
||||
</XML>
|
||||
<XML NAME="MEMENTO_DATA6">
|
||||
<SAVE_STATE>
|
||||
<XML NAME="MEMENTO0">
|
||||
<SAVE_STATE>
|
||||
<STATE NAME="INDEX" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="MEMENTO_CLASS" TYPE="string" VALUE="ghidra.app.plugin.core.decompile.DecompilerLocationMemento" />
|
||||
<STATE NAME="NAV_ID" TYPE="long" VALUE="3720737536777513709" />
|
||||
<STATE NAME="PROGRAM_ID" TYPE="long" VALUE="3720180275162699156" />
|
||||
<STATE NAME="PROGRAM_PATH_" TYPE="string" VALUE="Crusader:/CRUSADER-RAW.EXE" />
|
||||
<STATE NAME="X_OFFSET" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="Y_OFFSET" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="_ADDRESS" TYPE="string" VALUE="0008:a43c" />
|
||||
<STATE NAME="_BYTE_ADDR" TYPE="string" VALUE="0008:a43c" />
|
||||
<STATE NAME="_CHAR_OFFSET" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="_CLASSNAME" TYPE="string" VALUE="ghidra.program.util.FunctionReturnTypeFieldLocation" />
|
||||
<STATE NAME="_COLUMN" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="_FUNC_ADDRESS" TYPE="string" VALUE="0008:a43c" />
|
||||
<STATE NAME="_RETURN_TYPE" TYPE="string" VALUE="undefined" />
|
||||
<STATE NAME="_ROW" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="_SIGNATURE" TYPE="string" VALUE="undefined keyboard_release()" />
|
||||
</SAVE_STATE>
|
||||
</XML>
|
||||
<XML NAME="MEMENTO1">
|
||||
<SAVE_STATE>
|
||||
<STATE NAME="CURSOR_OFFSET" TYPE="int" VALUE="278" />
|
||||
<STATE NAME="MEMENTO_CLASS" TYPE="string" VALUE="ghidra.app.plugin.core.codebrowser.CodeViewerLocationMemento" />
|
||||
<STATE NAME="NAV_ID" TYPE="long" VALUE="3720737536777513718" />
|
||||
<STATE NAME="PROGRAM_ID" TYPE="long" VALUE="3720180275162699156" />
|
||||
<STATE NAME="PROGRAM_PATH_" TYPE="string" VALUE="Crusader:/CRUSADER-RAW.EXE" />
|
||||
<STATE NAME="_ADDRESS" TYPE="string" VALUE="0008:a3e6" />
|
||||
<STATE NAME="_BYTE_ADDR" TYPE="string" VALUE="0008:a3e6" />
|
||||
<STATE NAME="_CHAR_OFFSET" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="_CLASSNAME" TYPE="string" VALUE="ghidra.program.util.FunctionReturnTypeFieldLocation" />
|
||||
<STATE NAME="_COLUMN" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="_FUNC_ADDRESS" TYPE="string" VALUE="0008:a3e6" />
|
||||
<STATE NAME="_RETURN_TYPE" TYPE="string" VALUE="undefined" />
|
||||
<STATE NAME="_ROW" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="_SIGNATURE" TYPE="string" VALUE="undefined __cdecl16far keyboard_state_read()" />
|
||||
</SAVE_STATE>
|
||||
</XML>
|
||||
<STATE NAME="MEMENTO_CLASS" TYPE="string" VALUE="ghidra.app.plugin.core.gotoquery.DefaultNavigatableLocationMemento" />
|
||||
<STATE NAME="NUM_MEMENTOS" TYPE="int" VALUE="2" />
|
||||
<STATE NAME="PROGRAM_ID" TYPE="long" VALUE="3720180275162699156" />
|
||||
<STATE NAME="PROGRAM_PATH_" TYPE="string" VALUE="Crusader:/CRUSADER-RAW.EXE" />
|
||||
<STATE NAME="_ADDRESS" TYPE="string" VALUE="0008:a3e6" />
|
||||
<STATE NAME="_BYTE_ADDR" TYPE="string" VALUE="0008:a3e6" />
|
||||
<STATE NAME="_CHAR_OFFSET" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="_CLASSNAME" TYPE="string" VALUE="ghidra.program.util.FunctionReturnTypeFieldLocation" />
|
||||
<STATE NAME="_COLUMN" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="_FUNC_ADDRESS" TYPE="string" VALUE="0008:a3e6" />
|
||||
<STATE NAME="_RETURN_TYPE" TYPE="string" VALUE="undefined" />
|
||||
<STATE NAME="_ROW" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="_SIGNATURE" TYPE="string" VALUE="undefined keyboard_state_read()" />
|
||||
</SAVE_STATE>
|
||||
</XML>
|
||||
<STATE NAME="NAV_ID" TYPE="long" VALUE="-1" />
|
||||
</SAVE_STATE>
|
||||
</XML>
|
||||
<STATE NAME="LIST_COUNT" TYPE="int" VALUE="1" />
|
||||
</PLUGIN>
|
||||
<PLUGIN NAME="ProgramTreePlugin">
|
||||
<STATE NAME="Current Viewname" TYPE="string" VALUE="Program Tree" />
|
||||
<ARRAY NAME="GroupNameProgram Tree0" TYPE="string">
|
||||
<A VALUE="CRUSADER.EXE" />
|
||||
</ARRAY>
|
||||
<STATE NAME="NavigationToggleState" TYPE="boolean" VALUE="false" />
|
||||
<STATE NAME="NumberOfGroupsProgram Tree" TYPE="int" VALUE="1" />
|
||||
<STATE NAME="NumberOfViews" TYPE="int" VALUE="1" />
|
||||
<STATE NAME="TreeName-0" TYPE="string" VALUE="Program Tree" />
|
||||
</PLUGIN>
|
||||
<PLUGIN NAME="DecompilePlugin">
|
||||
<STATE NAME="INDEX" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="NAV_ID" TYPE="long" VALUE="3720737536777513709" />
|
||||
<STATE NAME="Num Disconnected" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="Y_OFFSET" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="_ADDRESS" TYPE="string" VALUE="0008:a3e6" />
|
||||
<STATE NAME="_BYTE_ADDR" TYPE="string" VALUE="0008:a3e6" />
|
||||
<STATE NAME="_CHAR_OFFSET" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="_CLASSNAME" TYPE="string" VALUE="ghidra.program.util.FunctionReturnTypeFieldLocation" />
|
||||
<STATE NAME="_COLUMN" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="_FUNC_ADDRESS" TYPE="string" VALUE="0008:a3e6" />
|
||||
<STATE NAME="_RETURN_TYPE" TYPE="string" VALUE="undefined" />
|
||||
<STATE NAME="_ROW" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="_SIGNATURE" TYPE="string" VALUE="undefined keyboard_state_read()" />
|
||||
</PLUGIN>
|
||||
<PLUGIN NAME="ByteViewerPlugin">
|
||||
<STATE NAME="Block Column" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="Block Num" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="Block Offset" TYPE="string" VALUE="0" />
|
||||
<STATE NAME="Index" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="NAV_ID" TYPE="long" VALUE="3720737536777513711" />
|
||||
<STATE NAME="Num Disconnected" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="X Offset" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="Y Offset" TYPE="int" VALUE="0" />
|
||||
</PLUGIN>
|
||||
<PLUGIN NAME="ProgramManagerPlugin">
|
||||
<STATE NAME="CURRENT_FILE" TYPE="string" VALUE="CRUSADER-RAW.EXE" />
|
||||
<STATE NAME="LOCATION_0" TYPE="string" VALUE="/K:/ghidra/Crusader_Decomp/" />
|
||||
<STATE NAME="NUM_PROGRAMS" TYPE="int" VALUE="1" />
|
||||
<STATE NAME="PATHNAME_0" TYPE="string" VALUE="/CRUSADER-RAW.EXE" />
|
||||
<STATE NAME="PROJECT_NAME_0" TYPE="string" VALUE="Crusader" />
|
||||
<STATE NAME="VERSION_0" TYPE="int" VALUE="-1" />
|
||||
<STATE NAME="_ADDRESS" TYPE="string" VALUE="0008:a3e6" />
|
||||
<STATE NAME="_BYTE_ADDR" TYPE="string" VALUE="0008:a3e6" />
|
||||
<STATE NAME="_CHAR_OFFSET" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="_CLASSNAME" TYPE="string" VALUE="ghidra.program.util.FunctionReturnTypeFieldLocation" />
|
||||
<STATE NAME="_COLUMN" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="_FUNC_ADDRESS" TYPE="string" VALUE="0008:a3e6" />
|
||||
<STATE NAME="_RETURN_TYPE" TYPE="string" VALUE="undefined" />
|
||||
<STATE NAME="_ROW" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="_SIGNATURE" TYPE="string" VALUE="undefined keyboard_state_read()" />
|
||||
</PLUGIN>
|
||||
<PLUGIN NAME="FunctionGraphPlugin">
|
||||
<SAVE_STATE NAME="COMPLEX_LAYOUT_NAME" TYPE="SaveState">
|
||||
<COMPLEX_LAYOUT_NAME>
|
||||
<STATE NAME="LAYOUT_CLASS_NAME" TYPE="string" VALUE="ghidra.app.plugin.core.functiongraph.graph.layout.DecompilerNestedLayoutProvider" />
|
||||
<STATE NAME="LAYOUT_NAME" TYPE="string" VALUE="Nested Code Layout" />
|
||||
</COMPLEX_LAYOUT_NAME>
|
||||
</SAVE_STATE>
|
||||
<STATE NAME="DISPLAY_POPUPS" TYPE="boolean" VALUE="true" />
|
||||
<STATE NAME="DISPLAY_SATELLITE" TYPE="boolean" VALUE="true" />
|
||||
<STATE NAME="DOCK_SATELLITE" TYPE="boolean" VALUE="true" />
|
||||
<STATE NAME="DOCK_SATELLITE_POSITION" TYPE="string" VALUE="LOWER_RIGHT" />
|
||||
<STATE NAME="Disconnected Count" TYPE="int" VALUE="0" />
|
||||
<ENUM NAME="EDGE_HOVER_HIGHLIGHT" TYPE="enum" CLASS="ghidra.app.plugin.core.functiongraph.EdgeDisplayType" VALUE="ScopedFlowsFromVertex" />
|
||||
<ENUM NAME="EDGE_SELECTION_HIGHLIGHT" TYPE="enum" CLASS="ghidra.app.plugin.core.functiongraph.EdgeDisplayType" VALUE="AllCycles" />
|
||||
<STATE NAME="NAV_ID" TYPE="long" VALUE="3720738032917539555" />
|
||||
</PLUGIN>
|
||||
<PLUGIN NAME="CodeBrowserPlugin">
|
||||
<STATE NAME="INDEX" TYPE="int" VALUE="566227" />
|
||||
<STATE NAME="NAV_ID" TYPE="long" VALUE="3720737536777513718" />
|
||||
<STATE NAME="Num Disconnected" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="Y_OFFSET" TYPE="int" VALUE="-10" />
|
||||
<STATE NAME="_ADDRESS" TYPE="string" VALUE="0008:a3e6" />
|
||||
<STATE NAME="_BYTE_ADDR" TYPE="string" VALUE="0008:a3e6" />
|
||||
<STATE NAME="_CHAR_OFFSET" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="_CLASSNAME" TYPE="string" VALUE="ghidra.program.util.FunctionReturnTypeFieldLocation" />
|
||||
<STATE NAME="_COLUMN" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="_FUNC_ADDRESS" TYPE="string" VALUE="0008:a3e6" />
|
||||
<STATE NAME="_RETURN_TYPE" TYPE="string" VALUE="undefined" />
|
||||
<STATE NAME="_ROW" TYPE="int" VALUE="0" />
|
||||
<STATE NAME="_SIGNATURE" TYPE="string" VALUE="undefined __cdecl16far keyboard_state_read()" />
|
||||
</PLUGIN>
|
||||
</DATA_STATE>
|
||||
</RUNNING_TOOL>
|
||||
</WORKSPACE>
|
||||
<WORKSPACE NAME="Workspace" ACTIVE="true" />
|
||||
</TOOL_MANAGER>
|
||||
</PROJECT>
|
||||
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -1,6 +1,6 @@
|
|||
# EUSECODE.FLX First-Pass Extraction
|
||||
|
||||
Input: k:\ghidra\Crusader_Decomp\USECODE\EUSECODE.FLX
|
||||
Input: USECODE\EUSECODE.FLX
|
||||
File size: 0x87E45 (556613 bytes)
|
||||
Candidate entries: 403
|
||||
|
||||
|
|
@ -423,7 +423,9 @@ ASCII: `........................................................................
|
|||
- `.strings.txt` files are the main human-readable output for now; `.txt` files are emitted only for chunks that look text-like.
|
||||
- `descriptor_index.tsv` summarizes guessed class labels, field names, and compact tag patterns for descriptor-like chunks.
|
||||
- `class_layout_index.tsv` records the conservative owner-loaded class parsing state: object index, class id, class-name hint, raw bytes-8..11 field, derived code-base-minus-one, and event-count/table-end values when the local divisibility and bounds checks succeed.
|
||||
- `class_event_index.tsv` expands parsed owner-loaded classes into raw 6-byte event rows with slot numbers, ScummVM event-name hints for `0x00..0x1f`, unresolved leading words, and raw code-offset dwords for round-trip tooling work.
|
||||
- `class_event_index.tsv` now also emits derived body-window columns (`derived_body_start`, `derived_body_end`, `derived_body_length`) plus conservative `repeated_template_status` tags for verified repeated families.
|
||||
- `boot_family_decompile.md` / `.tsv`, `callback_family_decompile.md` / `.tsv`, and `environmental_family_decompile.md` / `.tsv` now provide reversible per-class decompile artifacts for the `_BOOT`, `SURCAM*`, and environmental repeated-family lanes.
|
||||
- `repeated_family_regressions.tsv` enforces the current repeated-family slot sets plus the verified raw-row and derived body-window fields for `JELYHACK/JELYH2`, `_BOOT`, `SURCAM*`, and `FLAMEBOX/NOSTRIL/STEAMBOX`.
|
||||
- `descriptor_neighborhoods.tsv` captures local table neighborhoods around trigger/event-related classes such as `JELYHACK`, `NPCTRIG`, `CRUZTRIG`, `TRIGPAD`, and `SPECIAL`.
|
||||
- `referent_anchor_event_graph.tsv` groups referent-bearing descriptors with nearby event-bearing neighbors so the attachment model can be inspected without ad hoc grepping.
|
||||
- `jelyhack_island_graph.md` now uses a wider local window so the `JELYHACK` / `JELYH2` anchors can be inspected alongside the nearby event-bearing `REE_BOOT`, `SURCAMEW`, and `SFXTRIG` descriptors rather than stopping at the referent-only neighbors.
|
||||
|
|
|
|||
239
USECODE/EUSECODE_extracted/boot_family_decompile.md
Normal file
239
USECODE/EUSECODE_extracted/boot_family_decompile.md
Normal file
|
|
@ -0,0 +1,239 @@
|
|||
# _BOOT Family Decompiled Event Sketches
|
||||
|
||||
This is a reversible per-class rendering derived directly from `class_event_index.tsv` plus the raw extracted chunk bytes.
|
||||
ScummVM event labels remain hints only; the authoritative data here is the slot id, raw row bytes, and derived body window.
|
||||
|
||||
## AND_BOOT
|
||||
|
||||
```yaml
|
||||
class:
|
||||
entry_index: 0x0AB
|
||||
class_id: 0x314
|
||||
class_name: AND_BOOT
|
||||
class_object_index: 0x316
|
||||
raw_code_base_u32: 0xD4
|
||||
code_base_minus_one: 0xD3
|
||||
conservative_event_count: 32
|
||||
events:
|
||||
- slot: 0x0a
|
||||
event_name_hint: equip
|
||||
raw_event_entry_word: 0x0253
|
||||
raw_code_offset: 0x00000001
|
||||
derived_body_start: 0x00d4
|
||||
derived_body_end: 0x0327
|
||||
derived_body_length: 595
|
||||
repeated_template_status: boot-event-core/shared-slot-0x0A/shared-slot-template
|
||||
body_sha1: d7a28ffc24ab488970328a576f16af20f76d2a71
|
||||
body_prefix_hex: 5a025c2902414e44
|
||||
body_suffix_hex: 6f756e746572007a
|
||||
- slot: 0x0f
|
||||
event_name_hint: enterFastArea
|
||||
raw_event_entry_word: 0x0237
|
||||
raw_code_offset: 0x00000254
|
||||
derived_body_start: 0x0327
|
||||
derived_body_end: 0x055e
|
||||
derived_body_length: 567
|
||||
repeated_template_status: boot-event-core/shared-slot-0x0F/shared-slot-template
|
||||
body_sha1: 29fa661d1f7934f505c9efa6e7ec0bcbb7506b77
|
||||
body_prefix_hex: 5a045c0e02414e44
|
||||
body_suffix_hex: 6f756e746572007a
|
||||
- slot: 0x10
|
||||
event_name_hint: leaveFastArea
|
||||
raw_event_entry_word: 0x003b
|
||||
raw_code_offset: 0x0000048b
|
||||
derived_body_start: 0x055e
|
||||
derived_body_end: 0x0599
|
||||
derived_body_length: 59
|
||||
repeated_template_status: boot-event-core/shared-slot-0x10/same-length-template
|
||||
body_sha1: 7e3cc8034632df1963951b6c26e8cef2f18e2616
|
||||
body_prefix_hex: 5a005c2700414e44
|
||||
body_suffix_hex: 666572656e74007a
|
||||
```
|
||||
|
||||
## BRO_BOOT
|
||||
|
||||
```yaml
|
||||
class:
|
||||
entry_index: 0x0AC
|
||||
class_id: 0x316
|
||||
class_name: BRO_BOOT
|
||||
class_object_index: 0x318
|
||||
raw_code_base_u32: 0xD4
|
||||
code_base_minus_one: 0xD3
|
||||
conservative_event_count: 32
|
||||
events:
|
||||
- slot: 0x0a
|
||||
event_name_hint: equip
|
||||
raw_event_entry_word: 0x02d5
|
||||
raw_code_offset: 0x00000001
|
||||
derived_body_start: 0x00d4
|
||||
derived_body_end: 0x03a9
|
||||
derived_body_length: 725
|
||||
repeated_template_status: boot-event-core/shared-slot-0x0A/shared-slot-template
|
||||
body_sha1: bd08c6f4b1201d500ee2722b9cd4a2e3eb89af5f
|
||||
body_prefix_hex: 5a025cab0242524f
|
||||
body_suffix_hex: 6f756e746572007a
|
||||
- slot: 0x0f
|
||||
event_name_hint: enterFastArea
|
||||
raw_event_entry_word: 0x024c
|
||||
raw_code_offset: 0x000002d6
|
||||
derived_body_start: 0x03a9
|
||||
derived_body_end: 0x05f5
|
||||
derived_body_length: 588
|
||||
repeated_template_status: boot-event-core/shared-slot-0x0F/shared-slot-template
|
||||
body_sha1: 8b22769c8386fb4f8592aaf958820a452679a6b0
|
||||
body_prefix_hex: 5a045c230242524f
|
||||
body_suffix_hex: 6f756e746572007a
|
||||
- slot: 0x10
|
||||
event_name_hint: leaveFastArea
|
||||
raw_event_entry_word: 0x003b
|
||||
raw_code_offset: 0x00000522
|
||||
derived_body_start: 0x05f5
|
||||
derived_body_end: 0x0630
|
||||
derived_body_length: 59
|
||||
repeated_template_status: boot-event-core/shared-slot-0x10/same-length-template
|
||||
body_sha1: dd5ecced3b31dda2e5cd6a8d8cb2e9df41669ebd
|
||||
body_prefix_hex: 5a005c270042524f
|
||||
body_suffix_hex: 666572656e74007a
|
||||
```
|
||||
|
||||
## COR_BOOT
|
||||
|
||||
```yaml
|
||||
class:
|
||||
entry_index: 0x0BD
|
||||
class_id: 0x360
|
||||
class_name: COR_BOOT
|
||||
class_object_index: 0x362
|
||||
raw_code_base_u32: 0xD4
|
||||
code_base_minus_one: 0xD3
|
||||
conservative_event_count: 32
|
||||
events:
|
||||
- slot: 0x0a
|
||||
event_name_hint: equip
|
||||
raw_event_entry_word: 0x0227
|
||||
raw_code_offset: 0x00000001
|
||||
derived_body_start: 0x00d4
|
||||
derived_body_end: 0x02fb
|
||||
derived_body_length: 551
|
||||
repeated_template_status: boot-event-core/shared-slot-0x0A/shared-slot-template
|
||||
body_sha1: 97f1df8d8e6c9c7e151904d9ac1296f27d93581d
|
||||
body_prefix_hex: 5a025cfd01434f52
|
||||
body_suffix_hex: 6f756e746572007a
|
||||
- slot: 0x0f
|
||||
event_name_hint: enterFastArea
|
||||
raw_event_entry_word: 0x0234
|
||||
raw_code_offset: 0x00000228
|
||||
derived_body_start: 0x02fb
|
||||
derived_body_end: 0x052f
|
||||
derived_body_length: 564
|
||||
repeated_template_status: boot-event-core/shared-slot-0x0F/shared-slot-template
|
||||
body_sha1: 7b2509b86cd4228c2a06efbdcabe5b3b660fba4d
|
||||
body_prefix_hex: 5a045c0b02434f52
|
||||
body_suffix_hex: 6f756e746572007a
|
||||
- slot: 0x10
|
||||
event_name_hint: leaveFastArea
|
||||
raw_event_entry_word: 0x003b
|
||||
raw_code_offset: 0x0000045c
|
||||
derived_body_start: 0x052f
|
||||
derived_body_end: 0x056a
|
||||
derived_body_length: 59
|
||||
repeated_template_status: boot-event-core/shared-slot-0x10/same-length-template
|
||||
body_sha1: c0958d58cd7492fdc4b809db0634325f70fea009
|
||||
body_prefix_hex: 5a005c2700434f52
|
||||
body_suffix_hex: 666572656e74007a
|
||||
```
|
||||
|
||||
## REE_BOOT
|
||||
|
||||
```yaml
|
||||
class:
|
||||
entry_index: 0x11B
|
||||
class_id: 0x4DB
|
||||
class_name: REE_BOOT
|
||||
class_object_index: 0x4DD
|
||||
raw_code_base_u32: 0xD4
|
||||
code_base_minus_one: 0xD3
|
||||
conservative_event_count: 32
|
||||
events:
|
||||
- slot: 0x0a
|
||||
event_name_hint: equip
|
||||
raw_event_entry_word: 0x034b
|
||||
raw_code_offset: 0x00000001
|
||||
derived_body_start: 0x00d4
|
||||
derived_body_end: 0x041f
|
||||
derived_body_length: 843
|
||||
repeated_template_status: boot-event-core/shared-slot-0x0A/shared-slot-template
|
||||
body_sha1: bae629a3de3884d6919863daab5fe25dfc24cf13
|
||||
body_prefix_hex: 5a025c2103524545
|
||||
body_suffix_hex: 6f756e746572007a
|
||||
- slot: 0x0f
|
||||
event_name_hint: enterFastArea
|
||||
raw_event_entry_word: 0x025c
|
||||
raw_code_offset: 0x0000034c
|
||||
derived_body_start: 0x041f
|
||||
derived_body_end: 0x067b
|
||||
derived_body_length: 604
|
||||
repeated_template_status: boot-event-core/shared-slot-0x0F/shared-slot-template
|
||||
body_sha1: fb0e1e9e0a7b508f635df648aa8d3e3c72b6d0a2
|
||||
body_prefix_hex: 5a045c3302524545
|
||||
body_suffix_hex: 6f756e746572007a
|
||||
- slot: 0x10
|
||||
event_name_hint: leaveFastArea
|
||||
raw_event_entry_word: 0x003b
|
||||
raw_code_offset: 0x000005a8
|
||||
derived_body_start: 0x067b
|
||||
derived_body_end: 0x06b6
|
||||
derived_body_length: 59
|
||||
repeated_template_status: boot-event-core/shared-slot-0x10/same-length-template
|
||||
body_sha1: 577c61e9c4c6fb3e8b38f1e998699184b8e6e4f5
|
||||
body_prefix_hex: 5a005c2700524545
|
||||
body_suffix_hex: 666572656e74007a
|
||||
```
|
||||
|
||||
## VAR_BOOT
|
||||
|
||||
```yaml
|
||||
class:
|
||||
entry_index: 0x0FC
|
||||
class_id: 0x45C
|
||||
class_name: VAR_BOOT
|
||||
class_object_index: 0x45E
|
||||
raw_code_base_u32: 0xD4
|
||||
code_base_minus_one: 0xD3
|
||||
conservative_event_count: 32
|
||||
events:
|
||||
- slot: 0x0a
|
||||
event_name_hint: equip
|
||||
raw_event_entry_word: 0x029a
|
||||
raw_code_offset: 0x00000001
|
||||
derived_body_start: 0x00d4
|
||||
derived_body_end: 0x036e
|
||||
derived_body_length: 666
|
||||
repeated_template_status: boot-event-core/shared-slot-0x0A/shared-slot-template
|
||||
body_sha1: edc529e375d63cc79454b66c00acef51c5a0bd8a
|
||||
body_prefix_hex: 5a025c7002564152
|
||||
body_suffix_hex: 6f756e746572007a
|
||||
- slot: 0x0f
|
||||
event_name_hint: enterFastArea
|
||||
raw_event_entry_word: 0x0244
|
||||
raw_code_offset: 0x0000029b
|
||||
derived_body_start: 0x036e
|
||||
derived_body_end: 0x05b2
|
||||
derived_body_length: 580
|
||||
repeated_template_status: boot-event-core/shared-slot-0x0F/shared-slot-template
|
||||
body_sha1: 6cdb54664e36def9bb6770e19632cdccbdf280a1
|
||||
body_prefix_hex: 5a045c1b02564152
|
||||
body_suffix_hex: 6f756e746572007a
|
||||
- slot: 0x10
|
||||
event_name_hint: leaveFastArea
|
||||
raw_event_entry_word: 0x003b
|
||||
raw_code_offset: 0x000004df
|
||||
derived_body_start: 0x05b2
|
||||
derived_body_end: 0x05ed
|
||||
derived_body_length: 59
|
||||
repeated_template_status: boot-event-core/shared-slot-0x10/same-length-template
|
||||
body_sha1: 83c328a3cadc280a1798c2362f8d1e9ccbe3f78e
|
||||
body_prefix_hex: 5a005c2700564152
|
||||
body_suffix_hex: 666572656e74007a
|
||||
```
|
||||
16
USECODE/EUSECODE_extracted/boot_family_decompile.tsv
Normal file
16
USECODE/EUSECODE_extracted/boot_family_decompile.tsv
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
entry_index class_id class_name slot event_name_hint raw_event_entry_word raw_code_offset derived_body_start derived_body_end derived_body_length repeated_template_status body_sha1 body_prefix_hex body_suffix_hex
|
||||
171 0x314 AND_BOOT 0x0A equip 0x0253 0x00000001 0x00D4 0x0327 595 boot-event-core/shared-slot-0x0A/shared-slot-template d7a28ffc24ab488970328a576f16af20f76d2a71 5a025c2902414e44 6f756e746572007a
|
||||
171 0x314 AND_BOOT 0x0F enterFastArea 0x0237 0x00000254 0x0327 0x055E 567 boot-event-core/shared-slot-0x0F/shared-slot-template 29fa661d1f7934f505c9efa6e7ec0bcbb7506b77 5a045c0e02414e44 6f756e746572007a
|
||||
171 0x314 AND_BOOT 0x10 leaveFastArea 0x003B 0x0000048B 0x055E 0x0599 59 boot-event-core/shared-slot-0x10/same-length-template 7e3cc8034632df1963951b6c26e8cef2f18e2616 5a005c2700414e44 666572656e74007a
|
||||
172 0x316 BRO_BOOT 0x0A equip 0x02D5 0x00000001 0x00D4 0x03A9 725 boot-event-core/shared-slot-0x0A/shared-slot-template bd08c6f4b1201d500ee2722b9cd4a2e3eb89af5f 5a025cab0242524f 6f756e746572007a
|
||||
172 0x316 BRO_BOOT 0x0F enterFastArea 0x024C 0x000002D6 0x03A9 0x05F5 588 boot-event-core/shared-slot-0x0F/shared-slot-template 8b22769c8386fb4f8592aaf958820a452679a6b0 5a045c230242524f 6f756e746572007a
|
||||
172 0x316 BRO_BOOT 0x10 leaveFastArea 0x003B 0x00000522 0x05F5 0x0630 59 boot-event-core/shared-slot-0x10/same-length-template dd5ecced3b31dda2e5cd6a8d8cb2e9df41669ebd 5a005c270042524f 666572656e74007a
|
||||
189 0x360 COR_BOOT 0x0A equip 0x0227 0x00000001 0x00D4 0x02FB 551 boot-event-core/shared-slot-0x0A/shared-slot-template 97f1df8d8e6c9c7e151904d9ac1296f27d93581d 5a025cfd01434f52 6f756e746572007a
|
||||
189 0x360 COR_BOOT 0x0F enterFastArea 0x0234 0x00000228 0x02FB 0x052F 564 boot-event-core/shared-slot-0x0F/shared-slot-template 7b2509b86cd4228c2a06efbdcabe5b3b660fba4d 5a045c0b02434f52 6f756e746572007a
|
||||
189 0x360 COR_BOOT 0x10 leaveFastArea 0x003B 0x0000045C 0x052F 0x056A 59 boot-event-core/shared-slot-0x10/same-length-template c0958d58cd7492fdc4b809db0634325f70fea009 5a005c2700434f52 666572656e74007a
|
||||
283 0x4DB REE_BOOT 0x0A equip 0x034B 0x00000001 0x00D4 0x041F 843 boot-event-core/shared-slot-0x0A/shared-slot-template bae629a3de3884d6919863daab5fe25dfc24cf13 5a025c2103524545 6f756e746572007a
|
||||
283 0x4DB REE_BOOT 0x0F enterFastArea 0x025C 0x0000034C 0x041F 0x067B 604 boot-event-core/shared-slot-0x0F/shared-slot-template fb0e1e9e0a7b508f635df648aa8d3e3c72b6d0a2 5a045c3302524545 6f756e746572007a
|
||||
283 0x4DB REE_BOOT 0x10 leaveFastArea 0x003B 0x000005A8 0x067B 0x06B6 59 boot-event-core/shared-slot-0x10/same-length-template 577c61e9c4c6fb3e8b38f1e998699184b8e6e4f5 5a005c2700524545 666572656e74007a
|
||||
252 0x45C VAR_BOOT 0x0A equip 0x029A 0x00000001 0x00D4 0x036E 666 boot-event-core/shared-slot-0x0A/shared-slot-template edc529e375d63cc79454b66c00acef51c5a0bd8a 5a025c7002564152 6f756e746572007a
|
||||
252 0x45C VAR_BOOT 0x0F enterFastArea 0x0244 0x0000029B 0x036E 0x05B2 580 boot-event-core/shared-slot-0x0F/shared-slot-template 6cdb54664e36def9bb6770e19632cdccbdf280a1 5a045c1b02564152 6f756e746572007a
|
||||
252 0x45C VAR_BOOT 0x10 leaveFastArea 0x003B 0x000004DF 0x05B2 0x05ED 59 boot-event-core/shared-slot-0x10/same-length-template 83c328a3cadc280a1798c2362f8d1e9ccbe3f78e 5a005c2700564152 666572656e74007a
|
||||
|
142
USECODE/EUSECODE_extracted/callback_family_decompile.md
Normal file
142
USECODE/EUSECODE_extracted/callback_family_decompile.md
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
# SURCAM Callback Family Decompiled Event Sketches
|
||||
|
||||
This is a reversible per-class rendering derived directly from `class_event_index.tsv` plus the raw extracted chunk bytes.
|
||||
ScummVM event labels remain hints only; the authoritative data here is the slot id, raw row bytes, and derived body window.
|
||||
|
||||
## SURCAMEW
|
||||
|
||||
```yaml
|
||||
class:
|
||||
entry_index: 0x11C
|
||||
class_id: 0x4DE
|
||||
class_name: SURCAMEW
|
||||
class_object_index: 0x4E0
|
||||
raw_code_base_u32: 0xE6
|
||||
code_base_minus_one: 0xE5
|
||||
conservative_event_count: 35
|
||||
events:
|
||||
- slot: 0x01
|
||||
event_name_hint: use
|
||||
raw_event_entry_word: 0x00f7
|
||||
raw_code_offset: 0x000000d2
|
||||
derived_body_start: 0x01b7
|
||||
derived_body_end: 0x02ae
|
||||
derived_body_length: 247
|
||||
repeated_template_status: callback-eventtrigger/shared-slot-0x01/shared-slot-template
|
||||
body_sha1: a132370f9360cae36a81dd0372108c555a964d88
|
||||
body_prefix_hex: 5a005ce300535552
|
||||
body_suffix_hex: 666572656e74007a
|
||||
- slot: 0x0a
|
||||
event_name_hint: equip
|
||||
raw_event_entry_word: 0x00d1
|
||||
raw_code_offset: 0x00000001
|
||||
derived_body_start: 0x00e6
|
||||
derived_body_end: 0x01b7
|
||||
derived_body_length: 209
|
||||
repeated_template_status: callback-eventtrigger/shared-slot-0x0A/same-length-template
|
||||
body_sha1: 61ffc6347df026ded22dfebd2afe55826f1e9ad2
|
||||
body_prefix_hex: 5a005cb500535552
|
||||
body_suffix_hex: 690a00766172007a
|
||||
- slot: 0x20
|
||||
event_name_hint:
|
||||
raw_event_entry_word: 0x02ba
|
||||
raw_code_offset: 0x000001c9
|
||||
derived_body_start: 0x02ae
|
||||
derived_body_end: 0x0568
|
||||
derived_body_length: 698
|
||||
repeated_template_status: callback-eventtrigger/shared-slot-0x20/same-length-template
|
||||
body_sha1: 155c3cf663c03a6f53846938ac7c289aeb3c4c26
|
||||
body_prefix_hex: 5a0b5c6302535552
|
||||
body_suffix_hex: f500636f6465007a
|
||||
- slot: 0x21
|
||||
event_name_hint:
|
||||
raw_event_entry_word: 0x0655
|
||||
raw_code_offset: 0x00000483
|
||||
derived_body_start: 0x0568
|
||||
derived_body_end: 0x0bbd
|
||||
derived_body_length: 1621
|
||||
repeated_template_status: callback-eventtrigger/shared-slot-0x21/shared-slot-template
|
||||
body_sha1: dd8da26eae780920efc8ae8c51db5e9e8151914c
|
||||
body_prefix_hex: 5a145ce205535552
|
||||
body_suffix_hex: 000062ec007a007a
|
||||
- slot: 0x22
|
||||
event_name_hint:
|
||||
raw_event_entry_word: 0x01a3
|
||||
raw_code_offset: 0x00000ad8
|
||||
derived_body_start: 0x0bbd
|
||||
derived_body_end: 0x0d60
|
||||
derived_body_length: 419
|
||||
repeated_template_status: callback-eventtrigger/shared-slot-0x22/same-length-template
|
||||
body_sha1: 0dd40a9416581d71aed72d5cdb63656468f50d43
|
||||
body_prefix_hex: 5a035c6b01535552
|
||||
body_suffix_hex: 756e6447756e007a
|
||||
```
|
||||
|
||||
## SURCAMNS
|
||||
|
||||
```yaml
|
||||
class:
|
||||
entry_index: 0x10D
|
||||
class_id: 0x4C6
|
||||
class_name: SURCAMNS
|
||||
class_object_index: 0x4C8
|
||||
raw_code_base_u32: 0xE6
|
||||
code_base_minus_one: 0xE5
|
||||
conservative_event_count: 35
|
||||
events:
|
||||
- slot: 0x01
|
||||
event_name_hint: use
|
||||
raw_event_entry_word: 0x0051
|
||||
raw_code_offset: 0x000000d2
|
||||
derived_body_start: 0x01b7
|
||||
derived_body_end: 0x0208
|
||||
derived_body_length: 81
|
||||
repeated_template_status: callback-eventtrigger/shared-slot-0x01/shared-slot-template
|
||||
body_sha1: af6e6f93e4879920b189bfdeede69bb18e3307d5
|
||||
body_prefix_hex: 5a005c3d00535552
|
||||
body_suffix_hex: 666572656e74007a
|
||||
- slot: 0x0a
|
||||
event_name_hint: equip
|
||||
raw_event_entry_word: 0x00d1
|
||||
raw_code_offset: 0x00000001
|
||||
derived_body_start: 0x00e6
|
||||
derived_body_end: 0x01b7
|
||||
derived_body_length: 209
|
||||
repeated_template_status: callback-eventtrigger/shared-slot-0x0A/same-length-template
|
||||
body_sha1: bb2bc85fb9064de32bb1d2807ab41d0634fba228
|
||||
body_prefix_hex: 5a005cb500535552
|
||||
body_suffix_hex: 690a00766172007a
|
||||
- slot: 0x20
|
||||
event_name_hint:
|
||||
raw_event_entry_word: 0x02ba
|
||||
raw_code_offset: 0x00000123
|
||||
derived_body_start: 0x0208
|
||||
derived_body_end: 0x04c2
|
||||
derived_body_length: 698
|
||||
repeated_template_status: callback-eventtrigger/shared-slot-0x20/same-length-template
|
||||
body_sha1: 137f2bb8750946fa2c84750edcc6866fb77b2874
|
||||
body_prefix_hex: 5a0b5c6302535552
|
||||
body_suffix_hex: f500636f6465007a
|
||||
- slot: 0x21
|
||||
event_name_hint:
|
||||
raw_event_entry_word: 0x0709
|
||||
raw_code_offset: 0x000003dd
|
||||
derived_body_start: 0x04c2
|
||||
derived_body_end: 0x0bcb
|
||||
derived_body_length: 1801
|
||||
repeated_template_status: callback-eventtrigger/shared-slot-0x21/shared-slot-template
|
||||
body_sha1: 215c83fb3e76bf447b8768b537edfb99f58e600b
|
||||
body_prefix_hex: 5a145c9606535552
|
||||
body_suffix_hex: 000062ec007a007a
|
||||
- slot: 0x22
|
||||
event_name_hint:
|
||||
raw_event_entry_word: 0x01a3
|
||||
raw_code_offset: 0x00000ae6
|
||||
derived_body_start: 0x0bcb
|
||||
derived_body_end: 0x0d6e
|
||||
derived_body_length: 419
|
||||
repeated_template_status: callback-eventtrigger/shared-slot-0x22/same-length-template
|
||||
body_sha1: e01ce4b7741b642ddc4ebd220aafe847bd07300b
|
||||
body_prefix_hex: 5a035c6b01535552
|
||||
body_suffix_hex: 756e6447756e007a
|
||||
```
|
||||
11
USECODE/EUSECODE_extracted/callback_family_decompile.tsv
Normal file
11
USECODE/EUSECODE_extracted/callback_family_decompile.tsv
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
entry_index class_id class_name slot event_name_hint raw_event_entry_word raw_code_offset derived_body_start derived_body_end derived_body_length repeated_template_status body_sha1 body_prefix_hex body_suffix_hex
|
||||
284 0x4DE SURCAMEW 0x01 use 0x00F7 0x000000D2 0x01B7 0x02AE 247 callback-eventtrigger/shared-slot-0x01/shared-slot-template a132370f9360cae36a81dd0372108c555a964d88 5a005ce300535552 666572656e74007a
|
||||
284 0x4DE SURCAMEW 0x0A equip 0x00D1 0x00000001 0x00E6 0x01B7 209 callback-eventtrigger/shared-slot-0x0A/same-length-template 61ffc6347df026ded22dfebd2afe55826f1e9ad2 5a005cb500535552 690a00766172007a
|
||||
284 0x4DE SURCAMEW 0x20 0x02BA 0x000001C9 0x02AE 0x0568 698 callback-eventtrigger/shared-slot-0x20/same-length-template 155c3cf663c03a6f53846938ac7c289aeb3c4c26 5a0b5c6302535552 f500636f6465007a
|
||||
284 0x4DE SURCAMEW 0x21 0x0655 0x00000483 0x0568 0x0BBD 1621 callback-eventtrigger/shared-slot-0x21/shared-slot-template dd8da26eae780920efc8ae8c51db5e9e8151914c 5a145ce205535552 000062ec007a007a
|
||||
284 0x4DE SURCAMEW 0x22 0x01A3 0x00000AD8 0x0BBD 0x0D60 419 callback-eventtrigger/shared-slot-0x22/same-length-template 0dd40a9416581d71aed72d5cdb63656468f50d43 5a035c6b01535552 756e6447756e007a
|
||||
269 0x4C6 SURCAMNS 0x01 use 0x0051 0x000000D2 0x01B7 0x0208 81 callback-eventtrigger/shared-slot-0x01/shared-slot-template af6e6f93e4879920b189bfdeede69bb18e3307d5 5a005c3d00535552 666572656e74007a
|
||||
269 0x4C6 SURCAMNS 0x0A equip 0x00D1 0x00000001 0x00E6 0x01B7 209 callback-eventtrigger/shared-slot-0x0A/same-length-template bb2bc85fb9064de32bb1d2807ab41d0634fba228 5a005cb500535552 690a00766172007a
|
||||
269 0x4C6 SURCAMNS 0x20 0x02BA 0x00000123 0x0208 0x04C2 698 callback-eventtrigger/shared-slot-0x20/same-length-template 137f2bb8750946fa2c84750edcc6866fb77b2874 5a0b5c6302535552 f500636f6465007a
|
||||
269 0x4C6 SURCAMNS 0x21 0x0709 0x000003DD 0x04C2 0x0BCB 1801 callback-eventtrigger/shared-slot-0x21/shared-slot-template 215c83fb3e76bf447b8768b537edfb99f58e600b 5a145c9606535552 000062ec007a007a
|
||||
269 0x4C6 SURCAMNS 0x22 0x01A3 0x00000AE6 0x0BCB 0x0D6E 419 callback-eventtrigger/shared-slot-0x22/same-length-template e01ce4b7741b642ddc4ebd220aafe847bd07300b 5a035c6b01535552 756e6447756e007a
|
||||
|
File diff suppressed because it is too large
Load diff
145
USECODE/EUSECODE_extracted/environmental_family_decompile.md
Normal file
145
USECODE/EUSECODE_extracted/environmental_family_decompile.md
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
# Environmental Family Decompiled Event Sketches
|
||||
|
||||
This is a reversible per-class rendering derived directly from `class_event_index.tsv` plus the raw extracted chunk bytes.
|
||||
ScummVM event labels remain hints only; the authoritative data here is the slot id, raw row bytes, and derived body window.
|
||||
|
||||
## FLAMEBOX
|
||||
|
||||
```yaml
|
||||
class:
|
||||
entry_index: 0x0E5
|
||||
class_id: 0x403
|
||||
class_name: FLAMEBOX
|
||||
class_object_index: 0x405
|
||||
raw_code_base_u32: 0xE0
|
||||
code_base_minus_one: 0xDF
|
||||
conservative_event_count: 34
|
||||
events:
|
||||
- slot: 0x0a
|
||||
event_name_hint: equip
|
||||
raw_event_entry_word: 0x026a
|
||||
raw_code_offset: 0x00000001
|
||||
derived_body_start: 0x00e0
|
||||
derived_body_end: 0x034a
|
||||
derived_body_length: 618
|
||||
repeated_template_status: environmental-event/shared-slot-0x0A/shared-slot-template
|
||||
body_sha1: ae2a680ccf984b74404e7c105e0b2f9331e58a11
|
||||
body_prefix_hex: 5a065c2902464c41
|
||||
body_suffix_hex: 656374696f6e007a
|
||||
- slot: 0x20
|
||||
event_name_hint:
|
||||
raw_event_entry_word: 0x01ac
|
||||
raw_code_offset: 0x0000026b
|
||||
derived_body_start: 0x034a
|
||||
derived_body_end: 0x04f6
|
||||
derived_body_length: 428
|
||||
repeated_template_status: environmental-event/shared-slot-0x20/shared-slot-template
|
||||
body_sha1: 73ea45e739673c86eb92fe6a328a1a8442da767d
|
||||
body_prefix_hex: 5a045c6b01464c41
|
||||
body_suffix_hex: 666c616d6532007a
|
||||
- slot: 0x21
|
||||
event_name_hint:
|
||||
raw_event_entry_word: 0x029a
|
||||
raw_code_offset: 0x00000417
|
||||
derived_body_start: 0x04f6
|
||||
derived_body_end: 0x0790
|
||||
derived_body_length: 666
|
||||
repeated_template_status: environmental-event/shared-slot-0x21/shared-slot-template
|
||||
body_sha1: 8bdf9b3c2792514b300c2a4768f21e525313e86d
|
||||
body_prefix_hex: 5a065c4d02464c41
|
||||
body_suffix_hex: 657754797065007a
|
||||
```
|
||||
|
||||
## NOSTRIL
|
||||
|
||||
```yaml
|
||||
class:
|
||||
entry_index: 0x0ED
|
||||
class_id: 0x43E
|
||||
class_name: NOSTRIL
|
||||
class_object_index: 0x440
|
||||
raw_code_base_u32: 0xE0
|
||||
code_base_minus_one: 0xDF
|
||||
conservative_event_count: 34
|
||||
events:
|
||||
- slot: 0x0a
|
||||
event_name_hint: equip
|
||||
raw_event_entry_word: 0x00c0
|
||||
raw_code_offset: 0x00000001
|
||||
derived_body_start: 0x00e0
|
||||
derived_body_end: 0x01a0
|
||||
derived_body_length: 192
|
||||
repeated_template_status: environmental-event/shared-slot-0x0A/shared-slot-template
|
||||
body_sha1: a9d5700c4ff677d813fffdd67444ebd4ac6f4b19
|
||||
body_prefix_hex: 5a025c99004e4f53
|
||||
body_suffix_hex: fe0266697265007a
|
||||
- slot: 0x20
|
||||
event_name_hint:
|
||||
raw_event_entry_word: 0x0129
|
||||
raw_code_offset: 0x000000c1
|
||||
derived_body_start: 0x01a0
|
||||
derived_body_end: 0x02c9
|
||||
derived_body_length: 297
|
||||
repeated_template_status: environmental-event/shared-slot-0x20/shared-slot-template
|
||||
body_sha1: fd7fca65f43267c20931c91f1db269cccde92947
|
||||
body_prefix_hex: 5a045cf8004e4f53
|
||||
body_suffix_hex: 026669726532007a
|
||||
- slot: 0x21
|
||||
event_name_hint:
|
||||
raw_event_entry_word: 0x01be
|
||||
raw_code_offset: 0x000001ea
|
||||
derived_body_start: 0x02c9
|
||||
derived_body_end: 0x0487
|
||||
derived_body_length: 446
|
||||
repeated_template_status: environmental-event/shared-slot-0x21/shared-slot-template
|
||||
body_sha1: e3257250cfa12e430d872bd0ecc03e7e8d41a63d
|
||||
body_prefix_hex: 5a065c8d014e4f53
|
||||
body_suffix_hex: 00636f756e74007a
|
||||
```
|
||||
|
||||
## STEAMBOX
|
||||
|
||||
```yaml
|
||||
class:
|
||||
entry_index: 0x128
|
||||
class_id: 0x500
|
||||
class_name: STEAMBOX
|
||||
class_object_index: 0x502
|
||||
raw_code_base_u32: 0xE0
|
||||
code_base_minus_one: 0xDF
|
||||
conservative_event_count: 34
|
||||
events:
|
||||
- slot: 0x0a
|
||||
event_name_hint: equip
|
||||
raw_event_entry_word: 0x0266
|
||||
raw_code_offset: 0x00000001
|
||||
derived_body_start: 0x00e0
|
||||
derived_body_end: 0x0346
|
||||
derived_body_length: 614
|
||||
repeated_template_status: environmental-event/shared-slot-0x0A/shared-slot-template
|
||||
body_sha1: 0c7a45d14c66b5b6d8611f3eea647657e982e8c9
|
||||
body_prefix_hex: 5a065c2502535445
|
||||
body_suffix_hex: 656374696f6e007a
|
||||
- slot: 0x20
|
||||
event_name_hint:
|
||||
raw_event_entry_word: 0x01f6
|
||||
raw_code_offset: 0x00000267
|
||||
derived_body_start: 0x0346
|
||||
derived_body_end: 0x053c
|
||||
derived_body_length: 502
|
||||
repeated_template_status: environmental-event/shared-slot-0x20/shared-slot-template
|
||||
body_sha1: b42657cfea765abc07c9b0e99020d7c8783f06ef
|
||||
body_prefix_hex: 5a045cb501535445
|
||||
body_suffix_hex: 737465616d32007a
|
||||
- slot: 0x21
|
||||
event_name_hint:
|
||||
raw_event_entry_word: 0x02a7
|
||||
raw_code_offset: 0x0000045d
|
||||
derived_body_start: 0x053c
|
||||
derived_body_end: 0x07e3
|
||||
derived_body_length: 679
|
||||
repeated_template_status: environmental-event/shared-slot-0x21/shared-slot-template
|
||||
body_sha1: a7cf9924083dbcb16dbb2372a6c4f8ffeec578db
|
||||
body_prefix_hex: 5a045c6602535445
|
||||
body_suffix_hex: 737465616d32007a
|
||||
```
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
entry_index class_id class_name slot event_name_hint raw_event_entry_word raw_code_offset derived_body_start derived_body_end derived_body_length repeated_template_status body_sha1 body_prefix_hex body_suffix_hex
|
||||
229 0x403 FLAMEBOX 0x0A equip 0x026A 0x00000001 0x00E0 0x034A 618 environmental-event/shared-slot-0x0A/shared-slot-template ae2a680ccf984b74404e7c105e0b2f9331e58a11 5a065c2902464c41 656374696f6e007a
|
||||
229 0x403 FLAMEBOX 0x20 0x01AC 0x0000026B 0x034A 0x04F6 428 environmental-event/shared-slot-0x20/shared-slot-template 73ea45e739673c86eb92fe6a328a1a8442da767d 5a045c6b01464c41 666c616d6532007a
|
||||
229 0x403 FLAMEBOX 0x21 0x029A 0x00000417 0x04F6 0x0790 666 environmental-event/shared-slot-0x21/shared-slot-template 8bdf9b3c2792514b300c2a4768f21e525313e86d 5a065c4d02464c41 657754797065007a
|
||||
237 0x43E NOSTRIL 0x0A equip 0x00C0 0x00000001 0x00E0 0x01A0 192 environmental-event/shared-slot-0x0A/shared-slot-template a9d5700c4ff677d813fffdd67444ebd4ac6f4b19 5a025c99004e4f53 fe0266697265007a
|
||||
237 0x43E NOSTRIL 0x20 0x0129 0x000000C1 0x01A0 0x02C9 297 environmental-event/shared-slot-0x20/shared-slot-template fd7fca65f43267c20931c91f1db269cccde92947 5a045cf8004e4f53 026669726532007a
|
||||
237 0x43E NOSTRIL 0x21 0x01BE 0x000001EA 0x02C9 0x0487 446 environmental-event/shared-slot-0x21/shared-slot-template e3257250cfa12e430d872bd0ecc03e7e8d41a63d 5a065c8d014e4f53 00636f756e74007a
|
||||
296 0x500 STEAMBOX 0x0A equip 0x0266 0x00000001 0x00E0 0x0346 614 environmental-event/shared-slot-0x0A/shared-slot-template 0c7a45d14c66b5b6d8611f3eea647657e982e8c9 5a065c2502535445 656374696f6e007a
|
||||
296 0x500 STEAMBOX 0x20 0x01F6 0x00000267 0x0346 0x053C 502 environmental-event/shared-slot-0x20/shared-slot-template b42657cfea765abc07c9b0e99020d7c8783f06ef 5a045cb501535445 737465616d32007a
|
||||
296 0x500 STEAMBOX 0x21 0x02A7 0x0000045D 0x053C 0x07E3 679 environmental-event/shared-slot-0x21/shared-slot-template a7cf9924083dbcb16dbb2372a6c4f8ffeec578db 5a045c6602535445 737465616d32007a
|
||||
|
49
USECODE/EUSECODE_extracted/repeated_family_regressions.tsv
Normal file
49
USECODE/EUSECODE_extracted/repeated_family_regressions.tsv
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
record_type class_name slot expected actual status
|
||||
slot-set AND_BOOT * 0x0A,0x0F,0x10 0x0A,0x0F,0x10 ok
|
||||
slot-set BRO_BOOT * 0x0A,0x0F,0x10 0x0A,0x0F,0x10 ok
|
||||
slot-set COR_BOOT * 0x0A,0x0F,0x10 0x0A,0x0F,0x10 ok
|
||||
slot-set FLAMEBOX * 0x0A,0x20,0x21 0x0A,0x20,0x21 ok
|
||||
slot-set JELYH2 * 0x01 0x01 ok
|
||||
slot-set JELYHACK * 0x01 0x01 ok
|
||||
slot-set NOSTRIL * 0x0A,0x20,0x21 0x0A,0x20,0x21 ok
|
||||
slot-set REE_BOOT * 0x0A,0x0F,0x10 0x0A,0x0F,0x10 ok
|
||||
slot-set STEAMBOX * 0x0A,0x20,0x21 0x0A,0x20,0x21 ok
|
||||
slot-set SURCAMEW * 0x01,0x0A,0x20,0x21,0x22 0x01,0x0A,0x20,0x21,0x22 ok
|
||||
slot-set SURCAMNS * 0x01,0x0A,0x20,0x21,0x22 0x01,0x0A,0x20,0x21,0x22 ok
|
||||
slot-set VAR_BOOT * 0x0A,0x0F,0x10 0x0A,0x0F,0x10 ok
|
||||
row JELYHACK 0x01 0x002A|0x00000001|0x00D4|0x00FE|42|referent-anchor-twin/shared-slot-0x01/same-length-template 0x002A|0x00000001|0x00D4|0x00FE|42|referent-anchor-twin/shared-slot-0x01/same-length-template ok
|
||||
row JELYH2 0x01 0x002A|0x00000001|0x00D4|0x00FE|42|referent-anchor-twin/shared-slot-0x01/same-length-template 0x002A|0x00000001|0x00D4|0x00FE|42|referent-anchor-twin/shared-slot-0x01/same-length-template ok
|
||||
row AND_BOOT 0x0A 0x0253|0x00000001|0x00D4|0x0327|595|boot-event-core/shared-slot-0x0A/shared-slot-template 0x0253|0x00000001|0x00D4|0x0327|595|boot-event-core/shared-slot-0x0A/shared-slot-template ok
|
||||
row AND_BOOT 0x0F 0x0237|0x00000254|0x0327|0x055E|567|boot-event-core/shared-slot-0x0F/shared-slot-template 0x0237|0x00000254|0x0327|0x055E|567|boot-event-core/shared-slot-0x0F/shared-slot-template ok
|
||||
row AND_BOOT 0x10 0x003B|0x0000048B|0x055E|0x0599|59|boot-event-core/shared-slot-0x10/same-length-template 0x003B|0x0000048B|0x055E|0x0599|59|boot-event-core/shared-slot-0x10/same-length-template ok
|
||||
row BRO_BOOT 0x0A 0x02D5|0x00000001|0x00D4|0x03A9|725|boot-event-core/shared-slot-0x0A/shared-slot-template 0x02D5|0x00000001|0x00D4|0x03A9|725|boot-event-core/shared-slot-0x0A/shared-slot-template ok
|
||||
row BRO_BOOT 0x0F 0x024C|0x000002D6|0x03A9|0x05F5|588|boot-event-core/shared-slot-0x0F/shared-slot-template 0x024C|0x000002D6|0x03A9|0x05F5|588|boot-event-core/shared-slot-0x0F/shared-slot-template ok
|
||||
row BRO_BOOT 0x10 0x003B|0x00000522|0x05F5|0x0630|59|boot-event-core/shared-slot-0x10/same-length-template 0x003B|0x00000522|0x05F5|0x0630|59|boot-event-core/shared-slot-0x10/same-length-template ok
|
||||
row COR_BOOT 0x0A 0x0227|0x00000001|0x00D4|0x02FB|551|boot-event-core/shared-slot-0x0A/shared-slot-template 0x0227|0x00000001|0x00D4|0x02FB|551|boot-event-core/shared-slot-0x0A/shared-slot-template ok
|
||||
row COR_BOOT 0x0F 0x0234|0x00000228|0x02FB|0x052F|564|boot-event-core/shared-slot-0x0F/shared-slot-template 0x0234|0x00000228|0x02FB|0x052F|564|boot-event-core/shared-slot-0x0F/shared-slot-template ok
|
||||
row COR_BOOT 0x10 0x003B|0x0000045C|0x052F|0x056A|59|boot-event-core/shared-slot-0x10/same-length-template 0x003B|0x0000045C|0x052F|0x056A|59|boot-event-core/shared-slot-0x10/same-length-template ok
|
||||
row REE_BOOT 0x0A 0x034B|0x00000001|0x00D4|0x041F|843|boot-event-core/shared-slot-0x0A/shared-slot-template 0x034B|0x00000001|0x00D4|0x041F|843|boot-event-core/shared-slot-0x0A/shared-slot-template ok
|
||||
row REE_BOOT 0x0F 0x025C|0x0000034C|0x041F|0x067B|604|boot-event-core/shared-slot-0x0F/shared-slot-template 0x025C|0x0000034C|0x041F|0x067B|604|boot-event-core/shared-slot-0x0F/shared-slot-template ok
|
||||
row REE_BOOT 0x10 0x003B|0x000005A8|0x067B|0x06B6|59|boot-event-core/shared-slot-0x10/same-length-template 0x003B|0x000005A8|0x067B|0x06B6|59|boot-event-core/shared-slot-0x10/same-length-template ok
|
||||
row VAR_BOOT 0x0A 0x029A|0x00000001|0x00D4|0x036E|666|boot-event-core/shared-slot-0x0A/shared-slot-template 0x029A|0x00000001|0x00D4|0x036E|666|boot-event-core/shared-slot-0x0A/shared-slot-template ok
|
||||
row VAR_BOOT 0x0F 0x0244|0x0000029B|0x036E|0x05B2|580|boot-event-core/shared-slot-0x0F/shared-slot-template 0x0244|0x0000029B|0x036E|0x05B2|580|boot-event-core/shared-slot-0x0F/shared-slot-template ok
|
||||
row VAR_BOOT 0x10 0x003B|0x000004DF|0x05B2|0x05ED|59|boot-event-core/shared-slot-0x10/same-length-template 0x003B|0x000004DF|0x05B2|0x05ED|59|boot-event-core/shared-slot-0x10/same-length-template ok
|
||||
row SURCAMNS 0x01 0x0051|0x000000D2|0x01B7|0x0208|81|callback-eventtrigger/shared-slot-0x01/shared-slot-template 0x0051|0x000000D2|0x01B7|0x0208|81|callback-eventtrigger/shared-slot-0x01/shared-slot-template ok
|
||||
row SURCAMNS 0x0A 0x00D1|0x00000001|0x00E6|0x01B7|209|callback-eventtrigger/shared-slot-0x0A/same-length-template 0x00D1|0x00000001|0x00E6|0x01B7|209|callback-eventtrigger/shared-slot-0x0A/same-length-template ok
|
||||
row SURCAMNS 0x20 0x02BA|0x00000123|0x0208|0x04C2|698|callback-eventtrigger/shared-slot-0x20/same-length-template 0x02BA|0x00000123|0x0208|0x04C2|698|callback-eventtrigger/shared-slot-0x20/same-length-template ok
|
||||
row SURCAMNS 0x21 0x0709|0x000003DD|0x04C2|0x0BCB|1801|callback-eventtrigger/shared-slot-0x21/shared-slot-template 0x0709|0x000003DD|0x04C2|0x0BCB|1801|callback-eventtrigger/shared-slot-0x21/shared-slot-template ok
|
||||
row SURCAMNS 0x22 0x01A3|0x00000AE6|0x0BCB|0x0D6E|419|callback-eventtrigger/shared-slot-0x22/same-length-template 0x01A3|0x00000AE6|0x0BCB|0x0D6E|419|callback-eventtrigger/shared-slot-0x22/same-length-template ok
|
||||
row SURCAMEW 0x01 0x00F7|0x000000D2|0x01B7|0x02AE|247|callback-eventtrigger/shared-slot-0x01/shared-slot-template 0x00F7|0x000000D2|0x01B7|0x02AE|247|callback-eventtrigger/shared-slot-0x01/shared-slot-template ok
|
||||
row SURCAMEW 0x0A 0x00D1|0x00000001|0x00E6|0x01B7|209|callback-eventtrigger/shared-slot-0x0A/same-length-template 0x00D1|0x00000001|0x00E6|0x01B7|209|callback-eventtrigger/shared-slot-0x0A/same-length-template ok
|
||||
row SURCAMEW 0x20 0x02BA|0x000001C9|0x02AE|0x0568|698|callback-eventtrigger/shared-slot-0x20/same-length-template 0x02BA|0x000001C9|0x02AE|0x0568|698|callback-eventtrigger/shared-slot-0x20/same-length-template ok
|
||||
row SURCAMEW 0x21 0x0655|0x00000483|0x0568|0x0BBD|1621|callback-eventtrigger/shared-slot-0x21/shared-slot-template 0x0655|0x00000483|0x0568|0x0BBD|1621|callback-eventtrigger/shared-slot-0x21/shared-slot-template ok
|
||||
row SURCAMEW 0x22 0x01A3|0x00000AD8|0x0BBD|0x0D60|419|callback-eventtrigger/shared-slot-0x22/same-length-template 0x01A3|0x00000AD8|0x0BBD|0x0D60|419|callback-eventtrigger/shared-slot-0x22/same-length-template ok
|
||||
row FLAMEBOX 0x0A 0x026A|0x00000001|0x00E0|0x034A|618|environmental-event/shared-slot-0x0A/shared-slot-template 0x026A|0x00000001|0x00E0|0x034A|618|environmental-event/shared-slot-0x0A/shared-slot-template ok
|
||||
row FLAMEBOX 0x20 0x01AC|0x0000026B|0x034A|0x04F6|428|environmental-event/shared-slot-0x20/shared-slot-template 0x01AC|0x0000026B|0x034A|0x04F6|428|environmental-event/shared-slot-0x20/shared-slot-template ok
|
||||
row FLAMEBOX 0x21 0x029A|0x00000417|0x04F6|0x0790|666|environmental-event/shared-slot-0x21/shared-slot-template 0x029A|0x00000417|0x04F6|0x0790|666|environmental-event/shared-slot-0x21/shared-slot-template ok
|
||||
row NOSTRIL 0x0A 0x00C0|0x00000001|0x00E0|0x01A0|192|environmental-event/shared-slot-0x0A/shared-slot-template 0x00C0|0x00000001|0x00E0|0x01A0|192|environmental-event/shared-slot-0x0A/shared-slot-template ok
|
||||
row NOSTRIL 0x20 0x0129|0x000000C1|0x01A0|0x02C9|297|environmental-event/shared-slot-0x20/shared-slot-template 0x0129|0x000000C1|0x01A0|0x02C9|297|environmental-event/shared-slot-0x20/shared-slot-template ok
|
||||
row NOSTRIL 0x21 0x01BE|0x000001EA|0x02C9|0x0487|446|environmental-event/shared-slot-0x21/shared-slot-template 0x01BE|0x000001EA|0x02C9|0x0487|446|environmental-event/shared-slot-0x21/shared-slot-template ok
|
||||
row STEAMBOX 0x0A 0x0266|0x00000001|0x00E0|0x0346|614|environmental-event/shared-slot-0x0A/shared-slot-template 0x0266|0x00000001|0x00E0|0x0346|614|environmental-event/shared-slot-0x0A/shared-slot-template ok
|
||||
row STEAMBOX 0x20 0x01F6|0x00000267|0x0346|0x053C|502|environmental-event/shared-slot-0x20/shared-slot-template 0x01F6|0x00000267|0x0346|0x053C|502|environmental-event/shared-slot-0x20/shared-slot-template ok
|
||||
row STEAMBOX 0x21 0x02A7|0x0000045D|0x053C|0x07E3|679|environmental-event/shared-slot-0x21/shared-slot-template 0x02A7|0x0000045D|0x053C|0x07E3|679|environmental-event/shared-slot-0x21/shared-slot-template ok
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"input_path": "k:\\ghidra\\Crusader_Decomp\\USECODE\\EUSECODE.FLX",
|
||||
"input_path": "USECODE\\EUSECODE.FLX",
|
||||
"file_size": 556613,
|
||||
"header_preview_hex": "1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a0000020c000001000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||
"header_preview_ascii": "................................................................................................................................",
|
||||
|
|
|
|||
|
|
@ -16,4 +16,4 @@ This file is an index. Detailed notes have been split into the `docs/` folder by
|
|||
| [docs/raw-000a-000d.md](docs/raw-000a-000d.md) | 000d proximity/visibility buckets, 000a tracked handles, cache manager, init/shutdown, seg082 allocator, seg137/138 palette helpers, seg004/005 startup, 0x4588 object-role evidence, 000d VM owner/resource loader follow-up |
|
||||
| [docs/far-call-targets.md](docs/far-call-targets.md) | Top-104 most-called far-call targets (Tiers 1-5, ranks 1-104), supporting functions discovered, analysis gaps and seg043 reconciliation |
|
||||
| [docs/scummvm-crusader-reference.md](docs/scummvm-crusader-reference.md) | ScummVM Ultima8/Pentagram Crusader integration survey: USECODE/event tables, FLEX/resource formats, world/map loaders, HUD/media, and RE follow-up priorities |
|
||||
| [docs/usecode-roundtrip-ir.md](docs/usecode-roundtrip-ir.md) | ScummVM-to-binary USECODE cross-walk, owner-loaded class-layout and header/event-count reconciliation, and conservative IR v0 plan for human-readable, editable, recompilable scripts |
|
||||
| [docs/usecode-roundtrip-ir.md](docs/usecode-roundtrip-ir.md) | ScummVM-to-binary USECODE cross-walk, owner-loaded class-layout and header/event-count reconciliation, conservative IR v0 plan, and the generated class-event/body-window outputs that now ground reversible `_BOOT`, `SURCAM*`, and environmental family decompile artifacts plus repeated-family regression checks |
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
"2","code","0x40000","0x2B0","None","","","","crusader_ne_segments.csv"
|
||||
"3","code","0x40400","0x55A","None","","","","crusader_ne_segments.csv"
|
||||
"4","code","0x40A00","0x10B1","Foothold","Reset/cache entry path","runtime_cache_reset_sequence","ASYLUM.24 and downstream reset callers still need tighter classification","crusader_decompilation_notes.md; plan-mid.md"
|
||||
"5","code","0x41E00","0x8D7","Foothold","Startup/display orchestration and large runtime handoff","FUN_0004_60c0; FUN_0004_1e00","Recovered bodies now exist, but exact subsystem naming and higher-level transition meaning remain open","crusader_decompilation_notes.md; plan-mid.md"
|
||||
"5","code","0x41E00","0x8D7","Partial","Startup/display transition prepare/driver lane","startup_display_transition_prepare; startup_display_transition_driver","The two main seg005 bodies are now named and tied to caller-side validation through vtable +0x0c, the seg108 0x4f38 sprite/object helper lane, the shared active-dispatch hold byte at 0x6828, the seg049 watch/controller lane at 0x2bd8, and the seg126 follow-up path; the exact higher-level state label is still unresolved","crusader_decompilation_notes.md; plan-mid.md"
|
||||
"6","code","0x42C00","0x75E","None","","","","crusader_ne_segments.csv"
|
||||
"7","code","0x43600","0x484","None","","","","crusader_ne_segments.csv"
|
||||
"8","code","0x43C00","0x1386","None","","","","crusader_ne_segments.csv"
|
||||
|
|
@ -47,7 +47,7 @@
|
|||
"46","code","0x7A200","0x7DC","None","","","","crusader_ne_segments.csv"
|
||||
"47","code","0x7AC00","0x9B4","None","","","","crusader_ne_segments.csv"
|
||||
"48","code","0x7B800","0x63","None","","","","crusader_ne_segments.csv"
|
||||
"49","code","0x7BA00","0x1E3F","Foothold","Watch/camera controller object lane","watch_entity_controller_create_global; watch_entity_controller_create; watch_entity_controller_dispatch_if_present; entity_set_watch_ptr","Exact controller-vs-watched-entity ownership still needs caller-side confirmation, but 0x2bd8 is now clearly a real controller object lane","crusader_decompilation_notes.md; plan-mid.md"
|
||||
"49","code","0x7BA00","0x1E3F","Foothold","Watch/camera controller object lane","watch_entity_controller_create_global; watch_entity_controller_create; watch_entity_controller_dispatch_if_present; entity_set_watch_ptr","Exact controller-vs-watched-entity ownership is still open, but startup_display_transition_driver now gives caller-side confirmation that the shared active-dispatch hold byte is raised before the 0x2bd8 vtable +0x2c dispatch and cleared again immediately after the same watch/controller phase","crusader_decompilation_notes.md; plan-mid.md"
|
||||
"50","code","0x7DE00","0x9C8","None","","","","crusader_ne_segments.csv"
|
||||
"51","code","0x7EA00","0x1D02","None","","","","crusader_ne_segments.csv"
|
||||
"52","code","0x80A00","0x1D65","None","","","","crusader_ne_segments.csv"
|
||||
|
|
@ -106,7 +106,7 @@
|
|||
"105","code","0xAEC00","0x9F6","None","","","","crusader_ne_segments.csv"
|
||||
"106","code","0xAF800","0x1795","None","","","","crusader_ne_segments.csv"
|
||||
"107","code","0xB1400","0x40C","None","","","","crusader_ne_segments.csv"
|
||||
"108","code","0xB1A00","0x113F","Foothold","Active sprite/object state lane","sprite_object_clear_flag40_if_present; sprite_object_set_flag40_if_present","Higher-level meaning of bit 0x40 and its relation to 0x2bd8 and 0x4588 is still unresolved","crusader_decompilation_notes.md; plan-mid.md"
|
||||
"108","code","0xB1A00","0x113F","Foothold","Active sprite/object state lane","sprite_object_clear_flag40_if_present; sprite_object_set_flag40_if_present","startup_display_transition_prepare now confirms repeated seg108 helper use around the shared active-dispatch creation, and the same window shows a bounded local counter/stack at object +0x196/+0x186 rather than reuse of the caller object validated through vtable +0x0c; the local bit 0x40 contract at 0x4f38+0x32 is now separated from the shared active-dispatch owner byte at 0x6828+0x40, but the higher-level meaning of the sprite/object lane and its relation to 0x4588 is still unresolved","crusader_decompilation_notes.md; plan-mid.md"
|
||||
"109","code","0xB2E00","0x1424","None","","","High-value gap around 000b:2e00 still unresolved","crusader_ne_segments.csv; crusader_decomp_progress.md"
|
||||
"110","code","0xB4400","0x4C4","None","","","","crusader_ne_segments.csv"
|
||||
"111","code","0xB4A00","0x489","None","","","","crusader_ne_segments.csv"
|
||||
|
|
@ -124,8 +124,8 @@
|
|||
"123","code","0xC3C00","0xE6D","None","","","","crusader_ne_segments.csv"
|
||||
"124","code","0xC4E00","0x3DD","None","","","","crusader_ne_segments.csv"
|
||||
"125","code","0xC5400","0x1A3E","None","","","","crusader_ne_segments.csv"
|
||||
"126","code","0xC7400","0x402A","Partial","Transition-entry wrappers, pre-entry setup/script, and exit control","FUN_000c_7412; transition_preentry_setup_resources; transition_preentry_release_resources; transition_preentry_run_until_complete_or_abort; transition_preentry_step_script; wait_for_vga_vertical_retrace; thunk_callf_0000_ffff_000c_827d; thunk_callf_0000_ffff_000c_82f9; FUN_000c_834a","The seg126 helper family is structurally recovered and now ties into a paired temporary text-renderer lane at 0x8c5c/0x8c60, an external input/event gate at 0x31a2, and the shared active-dispatch owner at 0x6828 whose +0x40 byte follows that same gate; remaining open work is the exact UI role of the renderer pair, the DS:0x6341 to 0x6828 animation-owner relationship, and the separate oversized overlap rooted at 000c:db68","crusader_decompilation_notes.md; plan-mid.md"
|
||||
"127","code","0xCC600","0x8F6","Partial","Palette fade controller and transition-state gate","palette_fade_begin_full_up; palette_fade_begin_full_down; transition_palette_fade_begin; transition_palette_fade_tick; transition_palette_fade_out_step; transition_palette_fade_in_step","Exact transition states and palette-source owners are still unresolved, but the local fade controller, default fade entry paths, and active/direction state at 0x630a/0x630b are now clear","crusader_decompilation_notes.md; plan-mid.md"
|
||||
"126","code","0xC7400","0x402A","Partial","Transition-entry wrappers, pre-entry setup/script, and exit control","FUN_000c_7412; transition_preentry_setup_resources; transition_preentry_release_resources; transition_preentry_run_until_complete_or_abort; transition_preentry_step_script; wait_for_vga_vertical_retrace; thunk_callf_0000_ffff_000c_827d; thunk_callf_0000_ffff_000c_82f9; FUN_000c_834a","The seg126 helper family is structurally recovered and now ties into a paired temporary text-renderer lane at 0x8c5c/0x8c60, a shared async break/hold depth at 0x31a2 whose outer-loop exit test is visible at 000c:ca11, and the shared active-dispatch owner at 0x6828 whose +0x40 byte is raised immediately after the DS:0x6341 animation ctor path; remaining open work is the exact UI role of the renderer pair, the unresolved script bytes beyond the now-anchored fade controls, and the separate oversized overlap rooted at 000c:db68","crusader_decompilation_notes.md; plan-mid.md"
|
||||
"127","code","0xCC600","0x8F6","Partial","Palette fade controller and transition-state gate","palette_fade_begin_full_up; palette_fade_begin_full_down; transition_palette_fade_begin; transition_palette_fade_tick; transition_palette_fade_out_step; transition_palette_fade_in_step","Exact higher-level transition states and palette-source owners are still unresolved, but the local fade controller, default fade entry paths, active/direction state at 0x630a/0x630b, and the seg126 script-byte selectors 0x5e -> full-down / 0x26 -> full-up (with 0x2a sharing the same post-fade bookkeeping path) are now clear","crusader_decompilation_notes.md; plan-mid.md"
|
||||
"128","code","0xCD200","0x5D0","None","","","","crusader_ne_segments.csv"
|
||||
"129","code","0xCDA00","0xD77","None","","","","crusader_ne_segments.csv"
|
||||
"130","code","0xCEA00","0x47D","None","","","","crusader_ne_segments.csv"
|
||||
|
|
@ -134,9 +134,9 @@
|
|||
"133","code","0xD3800","0x215A","None","","","","crusader_ne_segments.csv"
|
||||
"134","code","0xD6000","0xEF0","Foothold","VM runtime bootstrap and post-init seeding","entity_vm_runtime_init_from_path_if_configured; entity_vm_referent_registry_init; entity_vm_runtime_release_slots; entity_vm_runtime_init_slots","Configured path/global at 0x65a and the exact external file format behind the 0x6611 runtime owner table still need tighter classification","plan-mid.md; docs/raw-0008-000c.md"
|
||||
"135","code","0xD7000","0x3B7","Foothold","VM runtime owner-resource helper","entity_vm_runtime_owner_resource_create; entity_vm_runtime_owner_resource_destroy","Embedded file-backed helper class and 0x0d-stride slot-table population semantics still need callee-side recovery","plan-mid.md; docs/raw-0008-000c.md"
|
||||
"136","code","0xD7600","0x5BD","Foothold","Active dispatch-entry lifecycle helpers","active_dispatch_entry_mark_enabled; active_dispatch_entry_mark_disabled; active_dispatch_entry_create_default","Broader meaning of the active dispatch entry and its relationship to the startup/display lane still needs caller-side confirmation","crusader_decompilation_notes.md; plan-mid.md"
|
||||
"136","code","0xD7600","0x5BD","Partial","Shared active dispatch-entry owner and hold-state controller","active_dispatch_entry_mark_enabled; active_dispatch_entry_mark_disabled; active_dispatch_entry_create_default","The shared active entry is now tied to the seg126 DS:0x6341 transition-animation path and to the shared 0x31a2 break/hold depth; current evidence also separates its borrowed +0x40 presentation hold token from the seg108-local 0x4f38 bit-0x40 lane, but the exact higher-level transition/callback subsystem name is still unresolved","crusader_decompilation_notes.md; plan-mid.md"
|
||||
"137","code","0xD7E00","0xFBB","Partial","Palette and dispatch-entry emission helper family","entity_dispatch_entry_init_runtime_state; entity_dispatch_entry_release_runtime_state; vga_palette_set_all_black; vga_palette_set_all_white; vga_palette_set_all_rgb; dispatch_entry_create_black_palette_state_active; dispatch_entry_create_grayscale_palette_state_active; dispatch_entry_create_solid_palette_state_active","Higher-level event/script meaning is still unresolved, especially the paired 0x68bf object and the exact role of the 0004:5ad4-5b6e caller sequence","crusader_decompilation_notes.md; plan-mid.md"
|
||||
"138","code","0xD9200","0x32E4","Foothold","Entity cleanup/finalize with callback and dispatch-entry palette emission","entity_cleanup_resources_and_dispatch; sprite_redraw_global_if_active","Concrete callback-object subsystem naming is still unresolved; FUN_000d_938c is now verified as a caller-side dispatch-entry/palette emission helper but remains intentionally unnamed","crusader_decompilation_notes.md; plan-mid.md"
|
||||
"138","code","0xD9200","0x32E4","Partial","Entity cleanup/finalize with callback, watch-controller release, and dispatch-entry palette emission","entity_cleanup_resources_and_dispatch; sprite_redraw_global_if_active; FUN_000d_938c","Concrete callback-object subsystem naming is still unresolved, but this lane now has verified caller-side control of watch/controller state at 0x2bd8, uses the shared active-dispatch byte +0x40 as a borrowed presentation hold token rather than a local owner install, and emits two distinct 0x4588 payload pairs (entity +0x12d/+0x12f and +0x74f/+0x751) in addition to the palette-emission helpers","crusader_decompilation_notes.md; plan-mid.md"
|
||||
"139","code","0xDCC00","0x984","None","","","","crusader_ne_segments.csv"
|
||||
"140","code","0xDD800","0xC6F","None","","","","crusader_ne_segments.csv"
|
||||
"141","code","0xDE600","0x2B","None","","","Short stub-sized segment","crusader_ne_segments.csv"
|
||||
|
|
|
|||
|
|
|
@ -198,7 +198,7 @@ Second sweep through `000c` adjacent helpers — gated thunk wrappers and input/
|
|||
| `000c:9f74` | `entity_state_flag100_check_and_dispatch` | Init latch guard at `[0x6053]`; clears `[0x8c55]` on first call; checks `[ptr+0x5b]` bits `0x100` and `0x40`; three-path thunk dispatch |
|
||||
| `000c:a1ad` | `entity_state_clear_flag40_and_dispatch` | Skips if `[ptr+0x5b]` has `0x180` bits set; if `0x40` set, clears it and calls far ptr at `[0x5e82/0x5e84]`; then dispatches twice with args `(0x0b,0x10,0x1,0x0)` (record/state-key pattern) |
|
||||
| `000c:a74e` | `entity_state_dispatch_if_flag_bit2` | Tests `[ptr+0x5b]` bit `0x2`; if set pushes extra arg + ptr and dispatches via thunk |
|
||||
| `000c:84c3` | `entity_state_set_byte40_at_global_ptr` | Sets byte `[DAT_0000_6828 ptr + 0x40] = 1` then calls thunk unconditionally; enables global entity flag |
|
||||
| `000c:84c3` | `entity_state_set_byte40_at_global_ptr` | Sets byte `[g_active_dispatch_entry_farptr + 0x40] = 1` then calls thunk unconditionally; current evidence treats this as raising the shared active-entry transition/display hold byte rather than toggling an unrelated global |
|
||||
| `000c:ac55` | `entity_state_fire_if_handle_valid` | Guard: fires thunk dispatch only when `[0x6054] != -1`; no-op otherwise |
|
||||
| `000c:ac6d` | `entity_state_fire_with_args_if_handle_valid` | 3-arg variant: pushes `[BP+0xe]` (byte), `[BP+0xc]`, `[BP+0xa]`, handle `[0x6054]`, then `CALLF 0000:ffff` |
|
||||
| `000c:afa5` | `entity_state_check_field49_and_call_vfunc3c` | Checks field `[ptr+0x49]`: −1→reset to 0 return 1; 2→call `vtable[0x3c]` return 0; else thunk dispatch |
|
||||
|
|
@ -214,7 +214,7 @@ Second sweep through `000c` adjacent helpers — gated thunk wrappers and input/
|
|||
- `field49` = state-sequence index; 0=reset, 2=vtable callback, 4=triggered end
|
||||
- `field47` = keystroke-combo counter
|
||||
- `field3f` = linked data pointer (event/record reference)
|
||||
- `[0x6054]` = current entity handle; `[0x6828]` = another global entity far pointer
|
||||
- `[0x6054]` = current entity handle; `[0x6828]` = `g_active_dispatch_entry_farptr`, the shared active-dispatch entry owner whose byte `+0x40` is reused across the startup/display lane as a hold/busy token
|
||||
- Bits in `[ptr+0x5b]`: `0x1=init`, `0x2=active/event`, `0x40=pending dispatch`, `0x100=flag100`, `0x180=skip-all mask`
|
||||
|
||||
---
|
||||
|
|
@ -263,7 +263,8 @@ Globals used: `[0x6312]`=start index, `[0x6314]`=count, `[0x630e]`=palette src p
|
|||
- `entity_vm_context_save` / `entity_vm_context_load` / `entity_vm_context_destroy` / `entity_vm_context_free_buffer` (`000d:498f`, `000d:4a78`, `000d:4962`, `000d:48b6`) now pin down the lifecycle of this object family rather than leaving the whole `000d:45xx..4exx` island anonymous
|
||||
- `entity_vm_context_try_create_masked_for_entity` is now better constrained at the return-value level too: after the runtime-disable check at `0x6610` and the owner-side slot-mask test succeed, it reports two distinct success shapes. Immediate-flagged contexts (`+0x16 & 0x0008`) clear the caller output word, while object-backed contexts return the created object's low word. That makes the helper a typed bridge from gameplay entities into VM-backed object results, not only a yes/no mask probe.
|
||||
- `entity_vm_runtime_owner_resource_create` (`000d:7000`) is now one step tighter too: the embedded seg069/070 helper is file-backed rather than abstract. Construction starts with `dos_file_handle_init` (`0009:1c00`), then uses helper vtable slot `+0x04` as the size query that drives the child `+0x10/+0x12` allocation and helper vtable slot `+0x0c` as the table-population callback for the `0x0d`-stride owner table.
|
||||
- That file-backed helper is now tighter one step deeper as well. The seg070 loops rooted at raw windows `0009:67b6` and `0009:6916` walk helper-owned record arrays at object `+0x10/+0x18`, format per-entry paths through the seg001 string helpers (`0003:e4d3` / `0003:e590`), then open, read, and close each file through `file_handle_alloc_init_and_open` (`0009:1c3a`), `dos_file_seek` (`0009:2034`), and `dos_file_close` (`0009:1e61`). That is strong evidence that `000d:7000` seeds the owner table from an indexed external file set rather than by copying one monolithic in-memory descriptor blob.
|
||||
- That file-backed helper is now tighter one step deeper as well. The seg070 loops rooted at raw windows `0009:67b6` and `0009:6916` walk helper-owned record arrays at object `+0x10/+0x18`, format per-entry paths through the seg001 string helpers (`0003:e4d3` / `0003:e590`), then open, read, and close each file through `file_handle_alloc_init_and_open` (`0009:1c3a`), `dos_file_seek` (`0009:2034`), and `dos_file_close` (`0009:1e61`). The paired `+0x18` entries are consumed as 16-bit ids passed into those path-format loops beside the far-pointer path table at `+0x10`; no object-1 or `classid + 2` arithmetic appears there, so the safest current read is slot-local file ids rather than exposed original class/object indices. That is strong evidence that `000d:7000` seeds the owner table from an indexed external file set rather than by copying one monolithic in-memory descriptor blob.
|
||||
- A final loader-side tightening from the current pass is that `0009:67b6` and `0009:6916` now read as twin entry walkers rather than one isolated path-format callback. Both windows iterate the helper-owned count at `+0x14`, index the far-pointer path table at `+0x10` and paired 16-bit id table at `+0x18`, check the source path through `0003:e669`, build formatted paths with distinct local format strings (`DS:3f2d` vs `DS:3f40`), and then reach the same file open/read/close lane. The remaining open question is not whether they are file-backed, but whether they represent two file families, two record templates, or two load phases inside the same helper class.
|
||||
- The caller-side bootstrap for that helper is now anchored too: `entity_vm_runtime_init_from_path_if_configured` (`000d:44df`) first checks the configured byte/string global at `0x65a`, builds a path through seg072 helper `0009:3600` using globals `0x6d6:0x6d8` plus `0x65a`, validates that path through `000a:500a`, then calls `entity_vm_runtime_create(0,0,path)`. This is the first verified source-argument path for `entity_vm_runtime_owner_resource_create`, and it strongly suggests the owner/resource table is loaded from an external configured file rather than from a purely in-memory descriptor blob.
|
||||
- Seg072 helper `0009:3600` is now classified more tightly as a rotating slash-aware path composer rather than a generic buffer advance helper. Its prologue cycles through five `0x50`-byte temp buffers, and its inner cases append optional string parts while inserting `\` only when adjacent path components need a separator. That narrows the two globals used by `000d:44df`: `0x65a` behaves as the configured relative runtime-owner filename/path component, while `0x6d6:0x6d8` behaves as the mutable base/resource-root path buffer that gets joined with `0x65a` before `000a:500a` validation.
|
||||
- The two still-xref-dark wrappers `0005:2c35` and `0005:2c68` are also narrower now. Their signed extra word does not participate in owner-mask selection inside `entity_vm_context_try_create_masked_for_entity`; it is forwarded into `entity_vm_context_create_from_slot_index`, stored in context field `+0x34`, and passed on to `entity_vm_slot_load_value_plus_offset`. The best current reading is therefore `offset-specialized masked context creation`, not a separate direct selector lane.
|
||||
|
|
@ -289,7 +290,8 @@ Globals used: `[0x6312]`=start index, `[0x6314]`=count, `[0x630e]`=palette src p
|
|||
- `entity_vm_state_copy` (`000c:f772`) copies that same `+0xcc..+0xd2` stream/base quartet verbatim when one mini-VM object is cloned.
|
||||
- Upstream of the setup helper, `000d:46ec` derives the source payload from the runtime owner table behind `0x6611 -> +0x1315/+0x1317`: with slot index `SI`, it walks owner table `*(owner+0x10/+0x12) + 0x0d*SI + 4`, passes that far pointer into `000c:f844`, and mirrors the resulting per-slot source into `0x39ca[slot]`.
|
||||
- This sharpens the current JELYHACK-side model rather than overturning it: the code-side producer recovered in this batch is still a generic slot-backed VM source object keyed by gameplay-entity slot selection and owner-side mask bits, not a direct hard-coded descriptor-class switch on `JELYHACK` or `JELYH2`. Combined with the extractor evidence that `JELYHACK` / `JELYH2` remain referent-only while `REE_BOOT` / `SFXTRIG` keep active `event` tags and `SURCAMEW` keeps `eventTrigger`, the better fit is still `referent anchor -> slot-backed payload chain -> neighboring event-bearing attachment`.
|
||||
- The `0x39ca` mirror question is narrower now too. Fresh windows at `0008:709c/70cb`, `0008:7309/7338`, and `0008:85f9/8617` show only global base-pointer save/restore and allocation/zeroing of the `0x39ca:0x39cc` table itself. The only verified per-slot row writer in this lane remains `entity_vm_context_create_from_slot_index` (`000d:46ec`), which writes `0x39ca[context_slot] = {source_off, source_seg}` after it derives the slot-backed payload source.
|
||||
- The `0x39ca` mirror question is now split more cleanly. Fresh windows at `0008:709c/70cb`, `0008:7309/7338`, and `0008:85f9/8617` still show only global base-pointer save/restore and allocation/zeroing of the `0x39ca:0x39cc` table itself, but two additional per-slot row writers are now verified in `000d`: `FUN_000d_7299` writes static source `DS:67f2` to `0x39ca[obj+2]` after creating a `0x44`-byte object, and `active_dispatch_entry_create_default` (`000d:761c`) writes static source `DS:6872` to `0x39ca[obj+2]` for the default active dispatch entry. `entity_vm_context_create_from_slot_index` (`000d:46ec`) remains the only confirmed owner-table-derived writer, but it is no longer the only concrete row writer overall.
|
||||
- The current pass narrows that split one step further. `entity_vm_context_create_from_slot_index` (`000d:46ec`) still derives its row from runtime owner table `(+0x10/+0x12) + 0x0d*slot + 4` before mirroring it into `0x39ca[slot]`, while `000d:7299` and `000d:761c` never touch the owner table at all in the verified windows. Instead they allocate local dispatch-entry-style objects, derive the row index from object field `+0x2`, and seed `0x39ca[row]` from fixed static sources `DS:67f2` and `DS:6872`. The safest current interpretation is therefore `owner-backed VM source mirror` versus `dispatch-entry-local static seed rows`, not three competing writers to the same semantic lane.
|
||||
- One exact numeric collision is now ruled out as unrelated noise rather than a second VM source: `000e:0953` in the animation/audio lane pushes literal `0x410` into imported `ASYLUM.27` immediately after setting the local audio-completion byte at `+0xef1`. Because `ASYLUM.DLL` is the `ASS_*` audio/media library, this does not weaken the attribution of gameplay event `0x410` to the `000d` VM/USECODE lane.
|
||||
- Current best JELYHACK reading after this pass: `JELYHACK` itself still looks like a referent-only map/object descriptor, but that no longer makes it inert. A referent-only record can still matter by supplying the referent id that populates the VM referent registry, while neighboring classes such as `REE_BOOT`, `SURCAMEW`, and `SFXTRIG` supply the event-bearing logic attached to the same local object island.
|
||||
|
||||
|
|
@ -337,8 +339,9 @@ Conservative case identity mapping from this pass:
|
|||
|
||||
Still unresolved after this pass:
|
||||
|
||||
- Direct CALL xrefs into `FUN_000d_ebe3` are now confirmed from `animation_ctor_variant_a/b/c` at `000e:283e`, `000e:2931`, and `000e:29e4`, so the entry is no longer globally xref-dark.
|
||||
- Those constructor callsites still do not expose a new concrete wrapper-level opcode number or the direct write/read path for `[BP-0x32]`; no additional opcode id can yet be assigned uniquely beyond the internal `0x19/0x1a/0x1b` family already proven inside `000d:0988`.
|
||||
- The animation constructor near calls at `000e:283e`, `000e:2931`, and `000e:29e4` land on a separate mis-split `000e:ebe3` region, not on `FUN_000d_ebe3`. They therefore no longer count as direct xref evidence for the `000d` dispatcher.
|
||||
- The true upstream selector/write path for `[BP-0x32]` in `FUN_000d_ebe3` is still unresolved, and no additional opcode id can yet be assigned uniquely beyond the internal `0x19/0x1a/0x1b` family already proven inside `000d:0988`.
|
||||
- Repeated MCP-visible instruction and data-use searches still do not produce a real direct caller edge for `FUN_000d_ebe3`, `0005:2c35`, or `0005:2c68`. For now that makes the next defensible route `caller-frame / shared-consumer recovery`, not more recycled raw call searches or the retired `000a:44fd` and `000e:ebe3` hypotheses.
|
||||
|
||||
### First readable VM IR sketch (verified-only)
|
||||
|
||||
|
|
@ -461,6 +464,7 @@ The next gameplay-side wrapper pass now extends well past the three earlier seed
|
|||
#### Concrete caller/xref addendum from the next pass
|
||||
|
||||
- Direct callsites are now pinned for the simpler wrappers: `0005:0292 -> 0005:2c06`, `0005:0fee -> 0005:2cd2`, `0005:5946/59e9 -> 0005:2c9b`, and `0007:814e/822e -> 0005:2d01`.
|
||||
- The two direct `0005:2d30` callers are now role-shaped as well: `0005:5370` reaches slot `0x0f` only after `entity_class_has_flag2000` succeeds and class-word bit `0x8000` is clear, while `0005:6f47` reaches the same gate from the complementary branch where class-word bit `0x2000` is still clear before the caller continues into its larger state/update flow.
|
||||
- `0005:2c68` is no longer usable as indirect selector evidence. The `0007:e521` and `0007:e73c` instruction windows do push `0x2c68` immediately before `CALLF 000a:44fd`, but decompile now shows that value is the caller-local data pointer `DAT_0000_2c68` passed into a fatal-report helper, not an indirect call to wrapper `0005:2c68`.
|
||||
- `0005:2c35` and `0005:2c68` therefore both remain unresolved in direct caller/xref evidence, and the real selector work stays centered on the still-xref-dark upstream edge into `FUN_000d_ebe3` rather than the disproven `000a:44fd` hypothesis.
|
||||
- Net effect: the active-event ecosystem fit is reinforced by direct caller behavior and payload shapes, but final slot-to-descriptor ownership still requires real caller-role recovery for the remaining xref-dark entry points.
|
||||
|
|
|
|||
|
|
@ -220,11 +220,103 @@ Current verified behavior:
|
|||
|
||||
| Address | Name | Notes |
|
||||
|---------|------|-------|
|
||||
| `0004:60c0` | `FUN_0004_60c0` | Startup/display orchestration path: broad setup calls, reads the live VGA palette, validates a caller-provided object through vtable slot `+0x0c`, drives the sprite/object lane through `0x4f38`, runs the seg137 palette and dispatch-entry helper family, creates the default active dispatch entry through `active_dispatch_entry_create_default`, programs mouse interrupt state via seg056 `INT 33h` wrappers, then hands off into the still-unrecovered `0004:1e00` routine. |
|
||||
| `0004:60c0` | `startup_display_transition_prepare` | Startup/display transition prepare step: broad setup calls, reads the live VGA palette, validates a caller-provided object through vtable slot `+0x0c`, drives the sprite/object lane through `0x4f38`, creates the default active dispatch entry through `active_dispatch_entry_create_default`, programs mouse interrupt state via seg056 `INT 33h` wrappers, then hands off into `startup_display_transition_driver`. |
|
||||
| `0004:1e00` | `startup_display_transition_driver` | Non-return startup/display transition driver: raises the shared active-dispatch hold byte around the seg049 watch/controller lane, then clears it before the seg080 redraw and seg126 follow-up path. |
|
||||
| `000d:7600` | `active_dispatch_entry_mark_enabled` | Marks the active dispatch entry enabled |
|
||||
| `000d:760e` | `active_dispatch_entry_mark_disabled` | Marks the active dispatch entry disabled |
|
||||
| `000d:761c` | `active_dispatch_entry_create_default` | Creates the default active dispatch entry |
|
||||
|
||||
Current verified caller-side detail:
|
||||
- `startup_display_transition_prepare` now has enough exact instruction evidence to pin the seg108 lane down more tightly. Window `0004:618e..620c` calls the `0x4f38` sprite/object helpers in a stable sequence around the shared active-entry creation: seg108 `000b:1e39`, `000b:2492`, and `000b:26bd` run before `active_dispatch_entry_create_default`, and seg108 `000b:2706` runs again immediately before the handoff into `startup_display_transition_driver`.
|
||||
- The same seg108 window now shows a local bounded counter/stack contract instead of reuse of the validated caller object. `000b:26bd` increments object word `+0x196` up to `7` before calling the common local helper at `000b:2592`, and `000b:2706` reads one prior slot from `+0x186`, decrements `+0x196`, and replays the same helper before `startup_display_transition_driver` takes over.
|
||||
- The seg108 helper pair is now named too: `sprite_object_push_state_word` (`000b:26bd`) increments the bounded local stack depth at `+0x196`, stores the incoming word into the per-object stack at `+0x186`, refreshes local sprite state through `000b:2592`, and replays redraw when the object was already marked dirty; `sprite_object_pop_state_word` (`000b:2706`) returns the previous top word, decrements the same bounded depth, and reapplies the new top through that same helper. This makes the `0x4f38` lane read more like a self-contained sprite/object state stack than a reused validated caller object.
|
||||
- `startup_display_transition_driver` now has exact hold-token ordering too. Window `0004:2013..212c` raises `g_active_dispatch_entry_farptr[+0x40]`, dispatches the seg049 watch/controller object at `0x2bd8` through vtable slot `+0x2c`, runs the intervening transition call at `0004:eece`, and then clears the same shared hold byte again just before the seg080 redraw pair and seg126 follow-up.
|
||||
- Upstream caller tracing now shows that the four `0004:eece` call shapes are chosen from one startup switch-parser lane rather than from a named local phase enum. Window `0004:63c1..66fb` walks an argv-like table against a local dispatch/jump table, and the `0004:64ff..65c1` case loads globals `0x84a/0x84c/0x84e/0x850` plus scalar `0x856`; `startup_display_transition_driver` later fans those values into the `0004:2049`, `0004:20b3`, `0004:20c6`, and `0004:20fe` call variants. The other direct caller anchors at `0004:2657`, `000c:8786`, and `000c:742c` all remain inside the same startup/display presentation-handoff family, so the safest output is still tighter caller-family semantics rather than a new neutral state label.
|
||||
- The seg049 and seg108 globals are now better separated by direct decompile evidence rather than only call-window correlation. `watch_entity_controller_dispatch_if_present` confirms that `0x2bd8` is a real controller object with active vtable dispatch at slots `+0x2c` and `+0x30`, while `sprite_object_set_flag40_if_present` and `sprite_object_clear_flag40_if_present` show that `0x4f38` is a separate global object lane whose immediate local contract is only bit `0x40` in object word `+0x32`.
|
||||
- The seg127 fade-controller ownership is also one step tighter in the same lane. `transition_preentry_setup_resources` resets `0x630a` at `000c:c855`, `transition_preentry_step_script` now has a verified early gate at `000c:ca25` that yields to the fade controller whenever `0x630a` is active, and `transition_palette_fade_begin` at `000c:cdca` explicitly installs palette source/range/step state into `0x630e..0x6316`, asserts `0x630a`, and kicks one immediate fade tick.
|
||||
- Fade direction is now pinned to seg126 script-control bytes rather than the outer seg005 wrappers. Inside `transition_preentry_step_script`, control byte `0x5e` reaches `palette_fade_begin_full_down` at `000c:cb06`, while control byte `0x26` reaches `palette_fade_begin_full_up` at `000c:cd1a`; control byte `0x2a` shares the same post-fade bookkeeping path after the full-up call.
|
||||
- The upstream producer path for the remaining seg126 control bytes is now tighter too. `transition_preentry_setup_resources` composes one path from the mutable base at `0x6aa:0x6ac` plus local name buffers (`0x631c`, `0x6335`) through the seg072 slash-aware path helper `0009:3600`, opens that file through `file_handle_alloc_init_and_open`, allocates a buffer of the returned size, reads the full payload into `0x6301:0x6303`, and seeds `0x62fa/0x62fc/0x62ff/0x6305/0x630a/0x6318` before the loop starts. Current best reading is therefore `file-backed transition script/control buffer`, not locally synthesized opcodes.
|
||||
- The remaining `transition_preentry_step_script` opcodes now have stable local mechanics even though the higher-level text semantics are still open. Control byte `0x21` consumes the next script word into `SI` and advances `0x62ff` by two, which makes it the current baseline/start-position loader for later text draws. Control byte `0x40` renders one null-terminated entry from the same script buffer through renderer object `0x8c5c:0x8c5e`, while control byte `0x24` mirrors that behavior through `0x8c60:0x8c62`; both paths measure width through the renderer vtable, draw through seg088 `000a:30d7`, blit through seg080 `0009:943a`, advance `SI` by rendered width plus four, and then scan forward to the next opcode byte. Control byte `0x23` sets local completion byte `0x62fe = 1` and returns, so the outer shell exits on the next loop test instead of iterating further.
|
||||
- Secondary renderer-factory sampling keeps the `0x8c5c` / `0x8c60` split conservative. Other sampled `000a:9748` xrefs use different adjacent preset pairs such as `0x0d/0x0c` at `0007:df30/df3f` and `0x0c/0x0f` at `0008:47c9/4851`, while no sampled caller reproduced the exact `0x10/0x11` startup pair outside `transition_preentry_setup_resources`. That supports keeping these as paired preset text renderers without forcing a title/body or normal/highlight label.
|
||||
- The missing seg126 step body at `000c:ca1d` still cannot be split out safely because `create_function_by_address` collides with the existing oversized overlap namespace, so this pass preserved the recovery as a decompiler comment instead of forcing a destructive boundary repair. Current best reading is still that `000c:ca1d..cd34` is the real `transition_preentry_step_script` body and that `000c:cd35` starts the fade-tick helper.
|
||||
|
||||
---
|
||||
|
||||
## Follow-up: `0x31a2` Break/Hold Depth and Active Dispatch Ownership
|
||||
|
||||
This pass tightened the shared startup/display transition lane enough to preserve the gate semantics directly in Ghidra and to promote the active-dispatch helpers out of an isolated foothold.
|
||||
|
||||
### Verified `0x31a2` role
|
||||
|
||||
- `0008:a283` increments `0x31a2` while installing one live far-pointer slot record into the seg008 per-index table, and the paired path at `0008:a314` decrements `0x31a2` while clearing that same record.
|
||||
- `0005:453a` is now commented in Ghidra as a plain getter for the shared `0x31a2` depth word.
|
||||
- `transition_preentry_run_until_complete_or_abort` (`000c:c9f4`) now decompiles cleanly and confirms the main transition loop runs until either local completion byte `0x62fe` becomes non-zero or `0x31a2` becomes positive.
|
||||
- The shell around `transition_preentry_step_script` is now tighter too: `000c:ca11` is the direct second exit test in the outer seg126 loop, so a positive `0x31a2` falls straight into `transition_preentry_release_resources` even when local completion byte `0x62fe` is still clear.
|
||||
- `0004:c24d` and `000c:e4d8` are now tightened as pure busy-wait edge loops on `0x31a2`, while `000c:e546` and `000c:e5c6` are the same break-depth check embedded in local presentation/cleanup loops rather than plain one-shot flag tests.
|
||||
- The blocking/waiting readers around `000c:e4d8`, `000c:e546`, and `000c:e5c6` treat `0x31a2` as an asynchronous break condition rather than a local state bit: they either busy-wait for a positive edge or abort their local presentation loop early when the depth is already positive.
|
||||
- Additional caller-side reads at `000d:9304`, `000d:b6b1`, and `000d:c0ee` all use `0x31a2 > 0` to short-circuit or advance local dispatch-entry state, which fits a shared break/hold depth better than a one-shot acknowledge flag. `dispatch_entry_kind2_tick_hold_and_maybe_dispatch` (`000d:92eb`) is still the clearest recovered dispatch-entry example: when a kind-`0x0002` entry is pending, a positive `0x31a2` lets the helper dispatch vtable slot `+0x08` even if the local `+0x40` hold byte is still asserted, after which it decrements that local byte. The newly checked `000d:b6b1` reader is narrower: it advances one local state-`5` branch only when entity byte `+0x78` is set and class/state word `+0x16` carries bit `0x4000`, then optionally runs the seg092 follow-up when the `0x45aa` gate and entity byte `+0x74a` are both active.
|
||||
|
||||
Current best neutral description: `0x31a2` is a shared asynchronous break/hold depth maintained by the seg008 install/remove path and consumed by transition/presentation code as a positive-count modal break condition.
|
||||
|
||||
### `0x6341` to `0x6828` relationship
|
||||
|
||||
- The missing seg126 function object at `000c:c63a` is now created and named `transition_preentry_setup_resources`; its body allocates the paired temporary text-renderer objects at `0x8c5c/0x8c60`, draws the preset `0x10` and `0x11` text variants, loads the file-backed buffer into `0x6301:0x6303`, and seeds the pre-entry state bytes before the main loop starts.
|
||||
- The paired helper `transition_preentry_release_resources` (`000c:c890`) still handles teardown, but its late branch at `000c:c958` also constructs the transition-local animation object at `DS:0x6341` through `animation_ctor_variant_a`, then immediately sets `g_active_dispatch_entry_farptr[+0x40] = 1` at `000c:c963`.
|
||||
- `active_dispatch_entry_create_default` (`000d:761c`) still owns the canonical `g_active_dispatch_entry_farptr` installation.
|
||||
- `dispatch_entry_kind2_tick_hold_and_maybe_dispatch` (`000d:92eb`) now makes that paired readback explicit: when a kind-`0x0002` dispatch entry is pending and either entry byte `+0x40` is already non-zero or the shared break depth `0x31a2` is positive, it dispatches vtable slot `+0x08` and then decrements `+0x40`.
|
||||
- `FUN_000d_938c` and `entity_cleanup_resources_and_dispatch` both clear `g_active_dispatch_entry_farptr[+0x40]` after their palette/presentation handoff work, which ties the active entry to the same transition-owned presentation lane rather than to an isolated constructor helper.
|
||||
- `entity_cleanup_resources_and_dispatch` is now tighter on the caller side too. Its first `0x4588` callback emit at `000d:9d5e` is confirmed as the `entity +0x12d/+0x12f` payload-pair path, while the later emit at `000d:a3b7` uses the separate `entity +0x74f/+0x751` pair. Both still sit inside the same palette/watch-controller cleanup body rather than a separate callback-only helper.
|
||||
- Caller-role alignment is now tighter across the remaining startup/display cleanup bodies. The mis-split seg126 window `000c:6176/619c -> 000c:6226` constructs only a temporary local animation payload, frees it, dispatches the seg049 watch/controller object at `0x2bd8` through vtable slot `+0x2c`, and then clears the shared owner byte `+0x40`; `FUN_000d_938c` similarly waits on two temporary palette/state entries, redraws, clears the same shared hold byte at `000d:958d`, and only then dispatches its caller object through vtable slot `+0x08`; `entity_cleanup_resources_and_dispatch` clears `g_active_dispatch_entry_farptr[+0x40]` at `000d:a1cf` only on the branch where entity byte `+0x737` is set and no temporary object remains, before falling into the same watch/controller dispatch at `000d:a1ed`. That is enough to align them as consumers of one shared presentation hold token around the seg049 lane, but still not enough to justify a single higher-level subsystem rename for `FUN_000d_938c` or the mis-split seg126 body.
|
||||
|
||||
### Follow-up: shared owner versus borrowed hold-token model
|
||||
|
||||
- `active_dispatch_entry_mark_enabled` (`000d:7600`) and `active_dispatch_entry_mark_disabled` (`000d:760e`) are now verified as tiny wrappers that only write the shared owner byte `g_active_dispatch_entry_farptr[+0x40] = 1/0`; they do not install or replace the owner object.
|
||||
- `entity_dispatch_entry_init_runtime_state` (`000d:7e00`) now tightens that ownership split further. When it builds a runtime-state dispatch entry, it copies the current shared owner byte at `g_active_dispatch_entry_farptr[+0x40]` into the new entry's local byte `+0x40`; if the new entry stays inactive while a shared owner exists, it raises the shared owner's `+0x40` byte to `1` instead of replacing the owner pointer.
|
||||
- The paired destructor `entity_dispatch_entry_release_runtime_state` (`000d:8078`) clears `g_active_dispatch_entry_farptr[+0x40]` when the runtime-state entry was marked as owner-propagating (`+0x41 != 0`) or when the entry's local hold byte was never asserted. This matches borrowed hold-state propagation, not separate owner creation.
|
||||
- `startup_display_transition_driver` now provides the clearest caller-side proof in seg005: it raises `g_active_dispatch_entry_farptr[+0x40]` at `0004:2013` before the seg049 watch/camera path and the `0004:eece` transition call, and clears that same byte again at `0004:2128` before the seg080 display update pair, sprite redraw, and seg126 follow-up thunk `000c:82f9`.
|
||||
- The still-mis-split seg126 window around `000c:6176/619c` also fits the same model. It makes one mode-dependent `animation_ctor_variant_a` call on a temporary local object, frees that temporary object, reloads the palette, dispatches the `0x2bd8` watch/camera controller through vtable slot `+0x2c`, and later clears `g_active_dispatch_entry_farptr[+0x40]` at `000c:6226`. No canonical owner installation is visible in that body.
|
||||
- The thin seg005 wrappers at `0005:3c36` and `0005:3c5b` are now confirmed as pure `animation_ctor_variant_a` preset shims. Combined with the seg126 windows above, current best reading is: `DS:0x6341` and the other constructor callsites build transition-local or display-local animation payloads, while `0x6828` remains the shared active-dispatch owner installed elsewhere.
|
||||
|
||||
Current best model: `g_active_dispatch_entry_farptr` is a shared owner installed by `active_dispatch_entry_create_default`, and the startup/display transition lane mostly borrows and propagates the owner's byte `+0x40` as a hold/busy token while palette/runtime-state helpers run. The remaining open problem is the exact state/object label behind the seg049 watch/camera path, the seg108 sprite/object lane, and the cleanup branches that consume the same token.
|
||||
|
||||
### Current batch: presentation handoff family versus single-owner hypothesis
|
||||
|
||||
- The exact late sequencing now supports one stricter neutral read: these bodies behave like a shared startup/display presentation handoff family, but not like one single owner-object family. In `startup_display_transition_prepare`, the validated caller object (vtable `+0x0c`), the seg108 `0x4f38` lane, and the seg049 `0x2bd8` watch/controller lane stay separated by direct instruction windows rather than collapsing into one reused object path.
|
||||
- `transition_preentry_setup_resources` also tightened the paired renderer role one step further. Window `000c:c659..c6ab` allocates renderer presets `0x10` and `0x11` through seg099 `000a:9748`, stores them at `0x8c5c:0x8c5e` and `0x8c60:0x8c62`, and immediately draws the same seed text buffer `DS:0x631a` at `(0x0a,0x0a)` through both objects. That makes the pair look like two preset text-render variants inside the same temporary presentation lane, not two separate owner objects.
|
||||
- The shared hold-token consumers now line up more exactly than before. `startup_display_transition_driver` raises `g_active_dispatch_entry_farptr[+0x40]` before the seg049 `+0x2c` dispatch and clears it again before the redraw/follow-up path; `transition_preentry_release_resources` tears down the paired renderers and script buffer, and only on its late completion branch builds local `DS:0x6341` then raises the same shared byte; the mis-split `000c:6176/619c -> 000c:6226` body frees its temporary local animation object, reloads the palette, dispatches `0x2bd8`, and only then clears the shared byte; `FUN_000d_938c` waits on two temporary palette/state entries, redraws, clears the shared byte, and only then dispatches caller vtable `+0x08`; `entity_cleanup_resources_and_dispatch` clears the shared byte only on the late `+0x737` cleanup branch immediately before the same `0x2bd8` dispatch.
|
||||
- The seg049 controller lane is also slightly tighter locally. `watch_entity_controller_create_global` (`0007:ba00`) delegates to `watch_entity_controller_create` (`0007:ba45`), which stamps type `0x2c2b`, stores the global object at `0x2bd8`, and seeds static row `DS:0x2be4` into `0x39ca[obj+2]`; the common `watch_entity_controller_dispatch_if_present` wrapper (`0007:ba13`) then runs both vtable slots `+0x2c` and `+0x30`. That still supports a real controller object, but not a strong enough state label to rename the wider family.
|
||||
- The seg108 lane is one step tighter in the same pass. `sprite_redraw_if_needed` (`000b:2492`) remains the redraw-facing helper, while the newly named `sprite_object_push_state_word` / `sprite_object_pop_state_word` pair show that prepare-time use of `0x4f38` is bracketed by a bounded per-object state-word stack at `+0x186/+0x196` rather than by reuse of the validated caller object or the seg049 controller.
|
||||
|
||||
Current safest naming conclusion from this batch: keep the existing concrete function names, treat `FUN_000d_938c` and the related seg126/seg138 callers as one shared startup/display presentation handoff family, and defer any stronger single-state or single-owner rename until a caller-side state discriminator appears.
|
||||
|
||||
This is enough to treat seg136 as a shared active-dispatch owner/hold-state controller and seg138 as a real cleanup/presentation caller family, even though the final subsystem name is still open.
|
||||
|
||||
### Current batch: exact edge waits, interleaved handoff, and broader sprite-stack reuse
|
||||
|
||||
- The remaining pure `0x31a2` edge waits are now exact rather than inferred. `0004:c24d` is a two-phase wait that first spins while the shared break/hold depth is non-zero and then spins until it becomes positive again before continuing, while `000c:e4d8` is the simpler positive-edge gate that only waits for `0x31a2 > 0` and immediately returns into the local presentation path.
|
||||
- `FUN_000d_938c` is now slightly tighter on sequencing even though it still does not justify a rename. The first scratch-palette runtime-state entry (`kind 0x3c`) is only built on the branch where global `0x68e6` is not already in the `0x13:0x0008` mode or entity byte `+0x33` is clear; after that wait completes, the helper clears the seg049 controller bit and dispatches `0x2bd8` through vtable slot `+0x2c`, performs one rectangle/display sync, and only then conditionally builds the current-palette runtime-state entry (`kind 0x14`) before the final redraw, shared hold-byte clear, and caller-object vtable `+0x08` dispatch. That keeps the body inside the same presentation-handoff family while making it less plausible as a single-owner constructor.
|
||||
- Additional seg108 push/pop callsites now show that the `0x4f38` lane is reused outside the startup prepare shell. Windows `000b:9bb8/9bda` and `000b:9c3c/9c6e` bracket transient seg101 presentation helpers with the same `sprite_object_push_state_word` / `sprite_object_pop_state_word` pair on global sprite object `0x5e82:0x5e84`, while `000c:831f`, `000c:8845`, `000c:8909`, and `000c:a05f` pop that same state stack during later UI or object-cleanup flows. This strengthens the neutral reading that `0x4f38` is a generic sprite-object state stack, not the validated prepare-time caller object and not the `0x2bd8` watch/controller object.
|
||||
|
||||
### Current batch: shared hold-token ownership closure
|
||||
|
||||
- The highest-value remaining ownership question in the startup/display lane is now narrow enough to close without a speculative rename. `active_dispatch_entry_create_default` remains the canonical installer for `g_active_dispatch_entry_farptr`, while the later seg005/seg126/seg138 bodies only borrow or propagate the shared owner byte `+0x40` as a transition/presentation hold token.
|
||||
- The set/clear sites now line up as one borrowed-hold family instead of competing owner installs. `startup_display_transition_driver` raises the shared byte at `0004:2013` before the seg049 controller dispatch and clears it at `0004:2128`; `transition_preentry_release_resources` raises it only on the late completion branch at `000c:c963` after building temporary `DS:0x6341`; `FUN_000d_938c` clears it at `000d:958d` after both temporary palette/state waits and redraw; `entity_cleanup_resources_and_dispatch` clears it at `000d:a1cf` only on the late cleanup branch immediately before the same seg049 controller dispatch.
|
||||
- The seg049 and seg108 lanes stay separate in the exact places that matter for ownership. `watch_entity_controller_dispatch_if_present` (`0007:ba13`) confirms `0x2bd8` is a real controller object dispatched through vtable slots `+0x2c/+0x30`, while `sprite_object_clear_flag40_if_present` / `sprite_object_set_flag40_if_present` (`000b:2b08` / `000b:2b20`) only toggle bit `0x40` in the separate global sprite/object at `0x4f38 + 0x32`.
|
||||
- The owner/borrow split also remains visible inside the dispatch-entry helpers. `entity_dispatch_entry_init_runtime_state` copies the shared owner byte into new runtime-state entries and re-raises the owner's `+0x40` byte when needed, which matches propagation of borrowed hold state rather than transfer of owner identity.
|
||||
|
||||
Current best neutral conclusion from this pass: the shared `g_active_dispatch_entry_farptr[+0x40]` byte is a startup/display presentation hold token borrowed across the seg049 controller lane and later cleanup/handoff bodies; the seg108 `0x4f38` lane is a separate local sprite-object state stack with its own bit-`0x40` contract, not the owner of the shared active-dispatch token.
|
||||
|
||||
### Current batch: seg126 control-stream producer tightening and completed `0x31a2` read classes
|
||||
|
||||
- The higher-level seg126 control-byte producer is now tighter without breaking the conservative file-backed model. `transition_preentry_run_until_complete_or_abort` (`000c:c9f4`) still has no data/bytecode arguments and is only reached from the local wrappers at `000c:7427` and `000c:0d0d`; both wrappers only stage the surrounding presentation lane before entering the seg126 loop, and neither injects script bytes or a script pointer.
|
||||
- `transition_preentry_setup_resources` (`000c:c63a`) remains the only verified source of the consumed bytes. It copies the shared mutable base path from `0x6aa:0x6ac`, composes local filenames through the slash-aware seg072 helper `0009:3600` using local buffers `0x631c`, `0x6323`, `0x632c`, and `0x6335`, opens the resulting file through the seg070 file-handle lane, allocates a buffer of the returned size, and reads the full payload into `0x6301:0x6303` before seeding `0x62fa/0x62fc/0x62ff/0x6305/0x630a/0x6318`.
|
||||
- Neighboring seg126 code now supports the same selector-path reading. Window `000c:b018..b03d` also reloads the same shared base path from `0x6aa:0x6ac` and composes a sibling local filename through `0009:3600` using `0x621c/0x6223`, which makes the startup/display lane look more like a family of file-selected transition assets than a local script-byte emitter.
|
||||
- The upstream `0x6aa:0x6ac` question is now narrow enough to close as an earlier inherited path lane rather than an in-scope seg126 producer. Literal-address instruction search still finds no store into `0x6aa` or `0x6ac`; the seg004 parser window only mutates the first byte of the pointed buffer at `0004:0ccd` / `0004:0cd8`, while the same parser explicitly installs the sibling root `0x6ae:0x6b0` from parsed input at `0004:0d28..0d2c`. Current best read is therefore: `0x6aa:0x6ac` already points at a mutable external/default base-path buffer before the seg126 startup/display family begins composing filenames on top of it.
|
||||
- The neighboring seg126 helper family sharpens that close further. Window `000c:afa5..b152` keys object field `+0x49` through local values `0`, `1`, and `4`, composes three sibling filenames from the shared stem buffer `0x621c` plus suffix buffers `0x6223`, `0x622d`, and `0x6237`, loads the selected file into object `+0x520` through seg002 `0004:0098`, then runs the same display/update chain; wrapper `000c:b153..b25f` increments or decrements `+0x49` on selected event codes and re-enters that same loader. This makes the nearby seg126 lane look like a local three-way transition-asset family selector layered on top of the inherited shared base path.
|
||||
- The overlap check does not force repair yet. `analyze_function_boundaries` still reports `000c:ca1d` as a plausible function entry and `000c:cd53` as the next clean function, while the oversized overlap rooted at `000c:db68` still pollutes the namespace. That overlap no longer blocks byte-behavior or read-site classification, but it still blocks clean function recovery for `transition_preentry_step_script`.
|
||||
- The in-scope `0x31a2` readers are now classed cleanly by role. `0004:c24d` and `000c:e4d8` are edge waits; `000c:ca11` is the seg126 modal-break exit; `000c:e546`, `000c:e5c6`, and `000d:c0ee` are cleanup-abort exits; `000d:9304` and `000d:b6b1` are deferred dispatch/state-advance gates.
|
||||
- Two remaining `0x31a2` reads stay outside that presentation classification set. `0005:453d` is only a plain getter wrapper for the shared depth word, and `0008:5149` is a seg008 internal/accounting-side read that adds the current depth to another local count before tripping a `>= 0x10` capacity flag.
|
||||
|
||||
---
|
||||
|
||||
## Follow-up: `0x4588` Object-Role Evidence
|
||||
|
|
@ -282,7 +374,9 @@ The next ScummVM-guided validation step now confirms that the sampled owner-load
|
|||
- Scanning with the previously noted ScummVM-style `(base_offset + 19) / 6` interpretation overruns into inline payload/name bytes on these owner-loaded records, so the local sample set does not support that exact event-count formula as written.
|
||||
- The best current arithmetic fit is now tighter: ScummVM's decremented `base_offset` is also used as the live code-stream base in `uc_machine.cpp`, so the local owner-loaded records fit best if bytes `8..11` are the first code-byte offset and event-count derivation is `(base_offset - 19) / 6`, which is exactly equivalent here to `(raw_u32_at_8_11 - 20) / 6`.
|
||||
- Current `000d` loader evidence does not point to a header rewrite before VM consumption. `entity_vm_runtime_init_from_path_if_configured` (`000d:44df`) only builds the external path and creates the runtime, `entity_vm_runtime_create` (`000d:4c99`) only installs the helper returned by `000d:7000`, `entity_vm_runtime_owner_resource_create` (`000d:7000`) only allocates the child owner table and fills it through helper vtable `+0x0c`, and `entity_vm_context_create_from_slot_index` (`000d:46ec`) directly reads slot-backed source data from that owner table. No local step is yet verified as rewriting the sampled class headers.
|
||||
- `entity_vm_runtime_owner_resource_create` (`000d:7000`) still does not expose a direct binary-side class-name lookup or explicit `classid + 2` arithmetic. What it does expose is an indexed file-set loader contract: helper-owned count at `+0x14`, far-pointer table at `+0x10`, paired per-entry word table at `+0x18`, vtable `+0x04` size query, and vtable `+0x0c` materialization of the `0x0d`-stride owner records later consumed by `entity_vm_context_create_from_slot_index`.
|
||||
- `entity_vm_runtime_owner_resource_create` (`000d:7000`) still does not expose a direct binary-side class-name lookup or explicit `classid + 2` arithmetic. What it does expose is an indexed file-set loader contract: helper-owned count at `+0x14`, far-pointer table at `+0x10`, paired per-entry word table at `+0x18`, vtable `+0x04` size query, and vtable `+0x0c` materialization of the `0x0d`-stride owner records later consumed by `entity_vm_context_create_from_slot_index`. The current pass also makes the helper shape slightly more concrete: the two raw seg070 windows at `0009:67b6` and `0009:6916` are twin per-entry path/read loops with distinct format strings (`DS:3f2d` and `DS:3f40`) but the same `+0x10/+0x18` indexing and file open/read/close lane, which is better evidence for a multi-table or multi-phase external loader than for direct in-memory descriptor iteration.
|
||||
- The signed slot-offset lane used by the still-xref-dark wrappers `0005:2c35` / `0005:2c68` is also no longer confined to `entity_vm_context_create_from_slot_index` (`000d:46ec`). Inside `entity_vm_runtime_create`, the pre-entry body at `000d:4c25..4c90` reloads object fields `+0x32/+0x34` through `entity_vm_slot_load_value_plus_offset` (`000d:5572`), stores that returned pair into object fields `+0x10c/+0x10e`, and also caches the owner-source far pointer at `+0x117/+0x119`. The paired save path at `000d:49ec` then serializes `+0x10c` through seg070 `0009:2034`, which makes the slot-plus-offset pair a persisted runtime/dispatch state lane rather than a transient wrapper-only argument.
|
||||
- Additional `0x39ca` consumers are now classified more cleanly. Beyond the already-known static seeds at `000d:7299 -> DS:67f2` and `000d:761c -> DS:6872`, the constructor-like windows at `000d:929a` and `000d:963c` seed rows `DS:68ec` and `DS:68f5` respectively before enabling local timer/dispatch behavior. Those writes behave like dispatch-entry-local static seed rows, not owner-table mirrors. Separately, `FUN_000d_938c` reads temporary dispatch-entry fields `+0x32/+0x34` at `000d:9449..9468` and `000d:9547..9566` only as a wait/poll condition on the scratch-palette (`kind 0x3c`) and current-palette (`kind 0x14`) entries it creates, which further separates active dispatch-entry state from the owner-backed `0x39ca[slot] = {source_off, source_seg}` rows written by `000d:46ec`.
|
||||
- Safe event-label correlation remains intentionally narrow after this pass. The sampled low slot ids are now concrete, but none of them yet have a verified binary-side behavior match strong enough to promote a ScummVM label like `look`, `use`, or `cachein`.
|
||||
|
||||
### Conservative parser rule from this batch
|
||||
|
|
|
|||
|
|
@ -42,7 +42,8 @@ A small helper cluster in the raw `000e:` area implements a fixed-size CRLF reco
|
|||
- `table_end = 0x6090`, which matches the first non-zero payload offset
|
||||
- `403` non-zero entries in the current file
|
||||
- `tools/extract_eusecode_flx.py` now parses the full validated table and emits all `403` non-zero entries under `USECODE/EUSECODE_extracted/`, including `entry_index.tsv`, `descriptor_index.tsv`, `descriptor_neighborhoods.tsv`, `summary.json`, per-chunk `.bin`, and `.strings.txt` sidecars.
|
||||
- The extractor now also carries the conservative owner-loaded class rule directly into machine-readable outputs: `class_layout_index.tsv` records `object_index`, `class_id`, the raw bytes-`8..11` field, derived `code_base_minus_one`, and `conservative_event_count`, while `class_event_index.tsv` expands parsed classes into raw 6-byte event rows with slot numbers, ScummVM event-name hints for `0x00..0x1f`, unresolved leading words, and raw code-offset dwords.
|
||||
- The extractor now also carries the conservative owner-loaded class rule directly into machine-readable outputs: `class_layout_index.tsv` records `object_index`, `class_id`, the raw bytes-`8..11` field, derived `code_base_minus_one`, and `conservative_event_count`, while `class_event_index.tsv` expands parsed classes into raw 6-byte event rows with slot numbers, ScummVM event-name hints for `0x00..0x1f`, unresolved leading words, raw code-offset dwords, derived body-window columns, and conservative repeated-template status tags for the verified repeated families.
|
||||
- The extractor now emits one concrete generated per-class decompile artifact for the cleanest repeated lane too: `boot_family_decompile.md` / `.tsv` render the five `_BOOT` classes slot-by-slot with raw row bytes, derived body windows, repeated-template status, and stable body digests.
|
||||
- The generated reports now expose lightweight descriptor summaries (`primary_label`, `field_names`, `field_tags`) so the object lane can be searched by field grammar instead of only by raw names.
|
||||
- The extracted data now separates into at least two lanes:
|
||||
- text-heavy records that fit the `000e:` CRLF parser model, such as `DATALINK` mission/objective text and `TEXTFIL1` message banks
|
||||
|
|
@ -129,6 +130,19 @@ A small helper cluster in the raw `000e:` area implements a fixed-size CRLF reco
|
|||
- The environmental hazard lane is now similarly constrained. `environmental_family_compare.tsv` shows that `FLAMEBOX` and `STEAMBOX` are close structural siblings with the same active-event backbone (`referent,event,<hazard>,<hazard2>,direction,count`) and matching `24:0A02 / 24:FC02 / 24:FE02` object-link pattern, while `NOSTRIL` is a smaller fire-specific variant that keeps the active `event` plus dual fire references and count fields but drops the direction/newType side.
|
||||
- Their neighborhoods are different enough to matter: `environmental_event_graph.md` shows `FLAMEBOX` embedded among vent/door/bridge/copy records, `NOSTRIL` among flame/pad/desk/blaster/keypad records, and `STEAMBOX` among bounce/hover/fade/steam/flame box records. So this looks like one hazard-event descriptor family reused across distinct local object islands rather than one single environmental mega-cluster.
|
||||
- The callback lane is tighter too. `callback_trigger_compare.tsv` confirms that `SURCAMNS` and `SURCAMEW` are effectively the same callback-trigger template: identical field set (`referent,textFile,monit,valueBox,passcode,link,code,screen,cameraEgg,trueRef,therma,eventTrigger,foundGun`) and identical tag grammar except for the `therma` slot offset (`24:F102` vs `24:F602`). That keeps the `eventTrigger` split credible as a true callback/attachment lane rather than only a spelling variation on active `event` carriers.
|
||||
- Mining the new `class_layout_index.tsv` / `class_event_index.tsv` outputs now gives a first small safe set of repeated non-zero slot patterns:
|
||||
- `JELYHACK` and `JELYH2` are exact referent-anchor twins at the event-table level too: both have only slot `0x01` non-zero, with the same row `0x002A / 0x00000001`.
|
||||
- The five `_BOOT` event cores (`AND_BOOT`, `BRO_BOOT`, `COR_BOOT`, `REE_BOOT`, `VAR_BOOT`) all share the same three-slot pattern `0x0A / 0x0F / 0x10`. The clearest exact repeated row is slot `0x10`, where all five use `raw_event_entry_word = 0x003B` with class-specific code offsets.
|
||||
- `SURCAMNS` and `SURCAMEW` share one exact five-slot callback pattern `0x01 / 0x0A / 0x20 / 0x21 / 0x22`, including the same `0x0A = 0x00D1 / 0x00000001` anchor row and the same `0x22` event-table word `0x01A3`.
|
||||
- `FLAMEBOX`, `NOSTRIL`, and `STEAMBOX` share one environmental-event pattern `0x0A / 0x20 / 0x21`, which is enough to treat the higher slots as real repeated structure even though the exact row values differ by class.
|
||||
- `EVENT` and `SFXTRIG` both participate in the wide `0x0A` lane, but that lane is broad enough that the slot number is currently more trustworthy than the ScummVM label attached to it.
|
||||
- The next body-window pass now confirms that repeated slot rows are usually near-templates rather than clones. Using `body_start = code_base_minus_one + raw_code_offset` and the next non-zero slot offset or chunk EOF as the body end:
|
||||
- `JELYHACK` and `JELYH2` slot `0x01` are both `42` bytes long with a shared `10`-byte prefix and `17`-byte suffix, but are not byte-identical.
|
||||
- `_BOOT` slot `0x10` is a clean short-template lane: all five bodies are exactly `59` bytes long, share the same first `5` bytes and last `17` bytes, but each has a distinct digest.
|
||||
- `_BOOT` slots `0x0A` and `0x0F` are larger variants of the same pattern: shared suffix-heavy structure, class-local middles, no exact clones.
|
||||
- `SURCAMNS` and `SURCAMEW` slots `0x20` and `0x22` are same-length near-templates (`698` and `419` bytes respectively), while slot `0x21` diverges more strongly (`1801` vs `1621` bytes) even though it still keeps a common tail.
|
||||
- That makes the current best human-readable script model more precise: preserve repeated-family status and exact row bytes, but record byte-identity as a separate property so “same slot template” does not get mistaken for “same compiled body.”
|
||||
- That pattern pass materially improves what a decompiled USECODE script can look like right now. The honest current form is not a pretty source language; it is a reversible descriptor-plus-event-table rendering with raw slot ids, raw event-entry words, raw code offsets, and optional ScummVM labels marked as hints only. The concrete examples now live in `docs/usecode-roundtrip-ir.md` and are grounded in `readable_script_ir.md`, `readable_descriptor_templates.md`, and `runtime_descriptor_family_rankings.md`.
|
||||
- The first runtime-side follow-through on those descriptor gains is now a little tighter too. Instruction search around `000d:ebe3` confirms one fixed sequenced VM/opcode driver body, not just a vague constructor helper: it calls `000d:177c`, `000d:1acb`, `000d:0988`, the internal `000d:22bc` link-matrix block, then `000d:1d4a` and `000d:2104` in order. The key negative result is just as useful: `000d:ec31` is only the internal `CALL 000d:22bc` site inside that body, not a standalone function entry.
|
||||
- Ghidra now carries that as a conservative disassembly comment at `000d:ebe3`. That is still short of a safe rename, but it does promote the lane from “suspected constructor chain” to “verified ordered opcode/handler sequence,” which is the clearest current bridge from the descriptor-side event families back into the `000d` VM/object runtime.
|
||||
|
||||
|
|
@ -191,17 +205,17 @@ All three constructor variants (`000e:2777`, `000e:2860`, `000e:2969`) follow th
|
|||
|
||||
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` directly (confirmed CALL sites at `000e:283e`, `000e:2931`, `000e:29e4`; multi-step chain initializer: calls `177c`, `1acb`, `0988`, `22bc`, `1d4a`, `2104` in sequence)
|
||||
3. Call near target `000e:ebe3` directly (confirmed CALL sites at `000e:283e`, `000e:2931`, `000e:29e4`; this is a separate mis-split `000e` region, not `FUN_000d_ebe3`)
|
||||
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`.
|
||||
The old assumption that these constructor calls fed the `000d` VM sequencer is now retired. Raw instruction search shows the direct near calls land on `000e:ebe3`, whose current body is still mis-split/garbled and cannot yet be tied to the `000d:177c` / `000d:1acb` / `000d:0988` / `000d:22bc` / `000d:2104` chain.
|
||||
|
||||
The constructor-side field setup before that sequencer is now slightly tighter too:
|
||||
|
||||
- variants A and B both set `+0xc0 = 1` before the direct `000d:ebe3` call and derive `+0xc2` from `DS:0x604e`
|
||||
- variant C instead sets `+0xc0 = 0`, `+0xc2 = 1`, and `+0x4c = 0x000d` before the same sequencer call
|
||||
- these direct xrefs make `000d:ebe3` a constructor-side animation sequencer rather than a globally xref-dark dispatcher, but they still do not expose any new wrapper-level opcode number beyond the internal `0x19/0x1a/0x1b` family already proven inside `000d:0988`
|
||||
- variants A and B both set `+0xc0 = 1` before the direct `000e:ebe3` call and derive `+0xc2` from `DS:0x604e`
|
||||
- variant C instead sets `+0xc0 = 0`, `+0xc2 = 1`, and `+0x4c = 0x000d` before the same near-call lane
|
||||
- this remains useful for the animation subsystem, but it no longer counts as upstream xref evidence for `FUN_000d_ebe3`; the true selector/write path into the `000d` dispatcher is still unresolved
|
||||
|
||||
### Constructor variant renames
|
||||
|
||||
|
|
|
|||
|
|
@ -234,6 +234,315 @@ For near-term local RE and tooling:
|
|||
- Treat `JELYHACK` and `JELYH2` as referent-anchor classes, not standalone event records.
|
||||
- Treat `SURCAMNS` and `SURCAMEW` as callback/eventTrigger holders, not proven active-event cores.
|
||||
|
||||
## Repeated Slot Patterns Safe To Reuse Now
|
||||
|
||||
The latest pass over `class_layout_index.tsv` and `class_event_index.tsv` adds a small set of repeatable slot patterns that are safe enough to carry into decompiler output.
|
||||
|
||||
What is authoritative here:
|
||||
|
||||
- whether a class has a non-zero slot entry at a given slot id
|
||||
- the raw `u16` event word for that slot
|
||||
- the raw `u32` code offset for that slot
|
||||
- repeated slot-set structure across several classes
|
||||
|
||||
What is still hint-level only:
|
||||
|
||||
- the ScummVM event-name labels for slots `0x00..0x1f`
|
||||
- any mapping from one repeated slot directly to one recovered `000d` opcode family
|
||||
- any claim that one repeated slot family is already tied to one exact gameplay subsystem in the DOS binary
|
||||
|
||||
Current small safe candidate sets:
|
||||
|
||||
| Family | Classes | Non-zero slots | Safe implication |
|
||||
|---|---|---|---|
|
||||
| referent-anchor twin | `JELYHACK`, `JELYH2` | `0x01` only | these are structurally anchor-only classes, not active event hubs |
|
||||
| boot-event-core | `AND_BOOT`, `BRO_BOOT`, `COR_BOOT`, `REE_BOOT`, `VAR_BOOT` | `0x0A`, `0x0F`, `0x10` | one reusable three-slot active-event core template |
|
||||
| callback-eventtrigger | `SURCAMNS`, `SURCAMEW` | `0x01`, `0x0A`, `0x20`, `0x21`, `0x22` | one shared callback-oriented multi-slot template |
|
||||
| environmental-event | `FLAMEBOX`, `NOSTRIL`, `STEAMBOX` | `0x0A`, `0x20`, `0x21` | one shared hazard/event template with two extra high slots |
|
||||
| broad active-event lane | `EVENT`, `SFXTRIG`, and several non-island classes | `0x0A` only | slot `0x0A` is widespread enough to treat as a real repeated event slot, but too broad to over-specialize |
|
||||
|
||||
Concrete repeated evidence worth preserving in IR:
|
||||
|
||||
- `JELYHACK` and `JELYH2` both carry only slot `0x01` with the exact same row: `raw_event_entry_word = 0x002A`, `raw_code_offset = 0x00000001`.
|
||||
- The five `_BOOT` cores all share slot `0x10` with the exact same `raw_event_entry_word = 0x003B`, while the `raw_code_offset` varies by class (`0x0000045c` on `COR_BOOT`, `0x0000048b` on `AND_BOOT`, `0x00000522` on `BRO_BOOT`, `0x000004df` on `VAR_BOOT`, `0x000005a8` on `REE_BOOT`). That is a good example of repeated structure without identical bodies.
|
||||
- `SURCAMNS` and `SURCAMEW` share the same five-slot layout and the same low/high anchor rows (`0x0A = 0x00D1/0x00000001`, `0x22 = 0x01A3/...`), but differ in the middle high-slot offsets. That looks like one shared callback template with instance-specific bodies, not two unrelated classes.
|
||||
- `FLAMEBOX`, `NOSTRIL`, and `STEAMBOX` all share one `0x0A` low slot plus two extra high slots `0x20` and `0x21`. Their exact words differ, so the safe reading is shared layout, not identical compiled behavior.
|
||||
- `EVENT` and `SFXTRIG` both participate in the wide `0x0A` lane, but that family is broad enough that the slot number is more trustworthy than the ScummVM name hint.
|
||||
|
||||
## Byte-Level Body Comparison Rules And Results
|
||||
|
||||
The next step after repeated row mining is to derive the chunk-local body window for each non-zero slot and compare the actual bytes instead of only the 6-byte event-table row.
|
||||
|
||||
Current conservative body-window rule:
|
||||
|
||||
- `body_start = code_base_minus_one + raw_code_offset`
|
||||
- `body_end = code_base_minus_one + next_non_zero_raw_code_offset` in the same class, or chunk EOF when there is no later non-zero slot
|
||||
- this keeps the representation reversible because it is computed only from preserved header and event-table fields plus the raw chunk bytes
|
||||
|
||||
This rule is now carried directly by the extractor outputs instead of living only in notes:
|
||||
|
||||
- `USECODE/EUSECODE_extracted/class_event_index.tsv` now emits `derived_body_start`, `derived_body_end`, `derived_body_length`, and conservative `repeated_template_status` columns per slot row.
|
||||
- `USECODE/EUSECODE_extracted/boot_family_decompile.md` / `.tsv`, `callback_family_decompile.md` / `.tsv`, and `environmental_family_decompile.md` / `.tsv` now provide concrete generated per-class decompile artifacts for the `_BOOT`, `SURCAM*`, and environmental repeated-family lanes, each grounded in emitted output rather than prose-only examples.
|
||||
- `USECODE/EUSECODE_extracted/repeated_family_regressions.tsv` now records and enforces the current repeated-family slot sets plus the verified raw-row and derived body-window fields for `JELYHACK/JELYH2`, `_BOOT`, `SURCAMNS/SURCAMEW`, and `FLAMEBOX/NOSTRIL/STEAMBOX` so extractor changes fail fast if those verified baselines drift.
|
||||
|
||||
What this confirms on the current repeated families:
|
||||
|
||||
- `JELYHACK` and `JELYH2` slot `0x01` are exact row twins but not exact body twins. Both bodies are `42` bytes long, both start at `0x00d4`, both keep `raw_event_entry_word = 0x002A`, and both share a `10`-byte prefix plus a `17`-byte suffix. The first differences are at body offsets `10,11,12,24`, which is consistent with one reused mini-template carrying class-local literals rather than one identical compiled body.
|
||||
- `_BOOT` slot `0x10` is the cleanest repeated-body example. All five classes have a `59`-byte body, all share the same row word `0x003B`, all share the same first `5` bytes and the same last `17` bytes, and none are byte-identical across the family. This is strong evidence for one shared short-template tail with class-local identifiers or immediates in the middle.
|
||||
- `_BOOT` slots `0x0A` and `0x0F` show the same pattern at larger sizes. Slot `0x0A` bodies range from `551` to `843` bytes and share only a `3`-byte prefix but a `39`-byte suffix; slot `0x0F` bodies range from `564` to `604` bytes and share a `3`-byte prefix plus a `38`-byte suffix. These are repeated family bodies, but not clones.
|
||||
- `SURCAMNS` and `SURCAMEW` high slots `0x20` and `0x22` also behave like near-templates, not clones. Slot `0x20` is `698` bytes in both classes with an `11`-byte common prefix and an `84`-byte common suffix. Slot `0x22` is `419` bytes in both classes with an `11`-byte common prefix and a `53`-byte common suffix.
|
||||
- `SURCAM` slot `0x21` is the strongest within-family divergence in this batch. `SURCAMNS` uses row word `0x0709` and a body length of `1801`, while `SURCAMEW` uses row word `0x0655` and a body length of `1621`. They still share a `20`-byte suffix, so this is best read as one callback-family slot with materially different instance bodies rather than a parsing mistake.
|
||||
|
||||
The practical IR consequence is important: repeated-family status should be recorded separately from byte-identity status. A human-readable decompile should be able to say “same family slot template” without falsely implying “same body bytes.”
|
||||
|
||||
## What A Decompiled Script Looks Like Today
|
||||
|
||||
The most honest present-day decompilation is not a polished source language. It is a reversible descriptor-plus-event-table rendering with optional VM-op vocabulary attached where the `000d` lane is already verified.
|
||||
|
||||
### Level 0: Raw event row plus derived body window
|
||||
|
||||
This is the minimal human-usable row form. It preserves the original six-byte event entry, explains how the body window is derived, and records whether the slot looks like an exact twin, a near-template, or a unique body.
|
||||
|
||||
```yaml
|
||||
class_name: REE_BOOT
|
||||
slot: 0x10
|
||||
event_name_hint_scummvm: leaveFastArea
|
||||
raw_event_entry_word: 0x003b
|
||||
raw_code_offset: 0x000005a8
|
||||
code_base_minus_one: 0x00d3
|
||||
derived_body_start: 0x067b
|
||||
derived_body_end: 0x06b6
|
||||
derived_body_length: 59
|
||||
repeated_template_status: boot-event-core/shared-slot-0x10
|
||||
body_identity_status: non-identical; shared 5-byte prefix and 17-byte suffix across all five _BOOT bodies
|
||||
body_sha1: 577c61e9c4c6...
|
||||
```
|
||||
|
||||
Field meaning, using only what is currently verified:
|
||||
|
||||
- `class_name`: authoritative class label from object `1` in the owner-loaded class table
|
||||
- `slot`: authoritative numeric slot id from the event table; this is safer than any guessed semantic name
|
||||
- `event_name_hint_scummvm`: external label for slots `0x00..0x1f`; useful for orientation, not yet verified as the local class-specific meaning
|
||||
- `raw_event_entry_word`: the unresolved leading `u16` from the 6-byte event record; authoritative bytes, unresolved semantics
|
||||
- `raw_code_offset`: the authoritative row `u32`; currently best read as a 1-based offset relative to `code_base_minus_one`
|
||||
- `code_base_minus_one`: derived from bytes `8..11` in the class header using the current conservative rule
|
||||
- `derived_body_start` and `derived_body_end`: computed chunk-local byte window for the slot body; useful for diffing and future recompilation, and now emitted directly in the extractor outputs
|
||||
- `repeated_template_status`: whether the row participates in a repeated family pattern such as `JELY` anchor twin, `_BOOT` event core, or `SURCAM` callback template
|
||||
- `body_identity_status`: whether the extracted body bytes are exact twins, near-templates, or materially different within that family
|
||||
- `body_sha1`: stable digest for exact identity checks without pretending the digest itself has semantic meaning
|
||||
|
||||
### Level 1: Lossless event-table IR
|
||||
|
||||
This is the form that is closest to a future round-trip compiler.
|
||||
|
||||
```yaml
|
||||
class:
|
||||
entry_index: 0x0115
|
||||
class_id: 0x04d3
|
||||
class_name: JELYHACK
|
||||
class_object_index: 0x04d5
|
||||
raw_code_base_u32: 0x00d4
|
||||
code_base_minus_one: 0x00d3
|
||||
conservative_event_count: 32
|
||||
descriptor_fields:
|
||||
- referent
|
||||
events:
|
||||
- slot: 0x01
|
||||
event_name_hint_scummvm: use
|
||||
raw_event_entry_word: 0x002a
|
||||
raw_code_offset: 0x00000001
|
||||
derived_body_start: 0x00d4
|
||||
derived_body_end: 0x00fe
|
||||
derived_body_length: 42
|
||||
repeated_template_status: referent-anchor-twin/shared-slot-0x01
|
||||
body_identity_status: near-template-with-JELYH2
|
||||
confidence: authoritative-bytes, hinted-label
|
||||
```
|
||||
|
||||
That is already a real decompilation output. It keeps the exact slot id, the exact six-byte row contents, and the exact class-header facts, while refusing to pretend that `use` is already a proven semantic name for this class.
|
||||
|
||||
Here is the same style for one active event-bearing attachment class in the same island:
|
||||
|
||||
```yaml
|
||||
class:
|
||||
entry_index: 0x011b
|
||||
class_id: 0x04db
|
||||
class_name: REE_BOOT
|
||||
class_object_index: 0x04dd
|
||||
raw_code_base_u32: 0x00d4
|
||||
code_base_minus_one: 0x00d3
|
||||
conservative_event_count: 32
|
||||
descriptor_fields:
|
||||
- referent
|
||||
- event
|
||||
- counter
|
||||
- item
|
||||
events:
|
||||
- slot: 0x0a
|
||||
event_name_hint_scummvm: equip
|
||||
raw_event_entry_word: 0x034b
|
||||
raw_code_offset: 0x00000001
|
||||
derived_body_start: 0x00d4
|
||||
derived_body_end: 0x041f
|
||||
derived_body_length: 843
|
||||
repeated_template_status: boot-event-core/shared-slot-0x0a
|
||||
body_identity_status: same-family-body-not-identical
|
||||
confidence: authoritative-bytes, hinted-label
|
||||
- slot: 0x0f
|
||||
event_name_hint_scummvm: enterFastArea
|
||||
raw_event_entry_word: 0x025c
|
||||
raw_code_offset: 0x0000034c
|
||||
derived_body_start: 0x041f
|
||||
derived_body_end: 0x067b
|
||||
derived_body_length: 604
|
||||
repeated_template_status: boot-event-core/shared-slot-0x0f
|
||||
body_identity_status: same-family-body-not-identical
|
||||
confidence: authoritative-bytes, hinted-label
|
||||
- slot: 0x10
|
||||
event_name_hint_scummvm: leaveFastArea
|
||||
raw_event_entry_word: 0x003b
|
||||
raw_code_offset: 0x000005a8
|
||||
derived_body_start: 0x067b
|
||||
derived_body_end: 0x06b6
|
||||
derived_body_length: 59
|
||||
repeated_template_status: boot-event-core/shared-slot-0x10
|
||||
body_identity_status: same-family-body-not-identical
|
||||
confidence: authoritative-bytes, hinted-label
|
||||
```
|
||||
|
||||
And here is one callback-style multi-slot class, which shows why the high slots should stay numeric for now:
|
||||
|
||||
```yaml
|
||||
class:
|
||||
entry_index: 0x011c
|
||||
class_id: 0x04de
|
||||
class_name: SURCAMEW
|
||||
class_object_index: 0x04e0
|
||||
raw_code_base_u32: 0x00e6
|
||||
code_base_minus_one: 0x00e5
|
||||
conservative_event_count: 35
|
||||
descriptor_fields:
|
||||
- referent
|
||||
- textFile
|
||||
- monit
|
||||
- valueBox
|
||||
- passcode
|
||||
- link
|
||||
- code
|
||||
- screen
|
||||
- cameraEgg
|
||||
- trueRef
|
||||
- therma
|
||||
- eventTrigger
|
||||
- foundGun
|
||||
events:
|
||||
- slot: 0x01
|
||||
event_name_hint_scummvm: use
|
||||
raw_event_entry_word: 0x00f7
|
||||
raw_code_offset: 0x000000d2
|
||||
- slot: 0x0a
|
||||
event_name_hint_scummvm: equip
|
||||
raw_event_entry_word: 0x00d1
|
||||
raw_code_offset: 0x00000001
|
||||
- slot: 0x20
|
||||
event_name_hint_scummvm: null
|
||||
raw_event_entry_word: 0x02ba
|
||||
raw_code_offset: 0x000001c9
|
||||
derived_body_start: 0x02ae
|
||||
derived_body_end: 0x0568
|
||||
derived_body_length: 698
|
||||
repeated_template_status: callback-eventtrigger/shared-slot-0x20
|
||||
body_identity_status: same-family-body-not-identical
|
||||
- slot: 0x21
|
||||
event_name_hint_scummvm: null
|
||||
raw_event_entry_word: 0x0655
|
||||
raw_code_offset: 0x00000483
|
||||
derived_body_start: 0x0568
|
||||
derived_body_end: 0x0bbd
|
||||
derived_body_length: 1621
|
||||
repeated_template_status: callback-eventtrigger/shared-slot-0x21
|
||||
body_identity_status: same-family-body-not-identical
|
||||
- slot: 0x22
|
||||
event_name_hint_scummvm: null
|
||||
raw_event_entry_word: 0x01a3
|
||||
raw_code_offset: 0x00000ad8
|
||||
derived_body_start: 0x0bbd
|
||||
derived_body_end: 0x0d60
|
||||
derived_body_length: 419
|
||||
repeated_template_status: callback-eventtrigger/shared-slot-0x22
|
||||
body_identity_status: same-family-body-not-identical
|
||||
```
|
||||
|
||||
The extra derived fields are worth keeping because they answer the immediate human question that the bare event table does not: not only “which slots exist,” but also “how much body belongs to each slot” and “whether this body is a true clone or only a same-family variant.”
|
||||
|
||||
### Level 2: Friendly but still reversible hinted form
|
||||
|
||||
This is the highest-level script shape that is justified right now.
|
||||
|
||||
```text
|
||||
anchor JELYHACK(referent)
|
||||
|
||||
# authoritative event rows for the anchor itself
|
||||
slot 0x01 hint=use? raw_word=0x002A code_off=0x00000001 body=0x00D4..0x00FE family=JELY-anchor identity=near-template-with-JELYH2
|
||||
|
||||
# nearby attachment classes from the same local island
|
||||
attach REE_BOOT(referent,event,counter,item)
|
||||
slot 0x0A hint=equip? raw_word=0x034B code_off=0x00000001 body=0x00D4..0x041F family=_BOOT-core identity=shared-template-not-clone
|
||||
slot 0x0F hint=enterFastArea? raw_word=0x025C code_off=0x0000034C body=0x041F..0x067B family=_BOOT-core identity=shared-template-not-clone
|
||||
slot 0x10 hint=leaveFastArea? raw_word=0x003B code_off=0x000005A8 body=0x067B..0x06B6 family=_BOOT-core identity=shared-template-not-clone
|
||||
|
||||
callback SURCAMEW(referent,textFile,monit,valueBox,passcode,link,code,screen,cameraEgg,trueRef,therma,eventTrigger,foundGun)
|
||||
slot 0x01 hint=use? raw_word=0x00F7 code_off=0x000000D2 body=0x01B7..0x02AE
|
||||
slot 0x0A hint=equip? raw_word=0x00D1 code_off=0x00000001 body=0x00E6..0x02AE
|
||||
slot 0x20 raw_word=0x02BA code_off=0x000001C9 body=0x02AE..0x0568 family=SURCAM-callback identity=shared-template-not-clone
|
||||
slot 0x21 raw_word=0x0655 code_off=0x00000483 body=0x0568..0x0BBD family=SURCAM-callback identity=shared-template-with-stronger-divergence
|
||||
slot 0x22 raw_word=0x01A3 code_off=0x00000AD8 body=0x0BBD..0x0D60 family=SURCAM-callback identity=shared-template-not-clone
|
||||
|
||||
attach SFXTRIG(referent,event)
|
||||
slot 0x0A hint=equip? raw_word=0x00B8 code_off=0x00000001
|
||||
```
|
||||
|
||||
This is decompiled enough to read, diff, and later recompile because it preserves:
|
||||
|
||||
- the original class identity
|
||||
- the exact non-zero event rows
|
||||
- the derived chunk-local body window for each row
|
||||
- which names are authoritative fields versus external hints
|
||||
- which nearby descriptors appear to be anchors, active event attachments, or callback attachments
|
||||
- whether a repeated family slot is an exact twin or only a structurally similar body
|
||||
|
||||
### Level 2.5: Human annotation layer
|
||||
|
||||
The last layer is prose, not syntax. It should explain the honest current reading of each field so a modder can see what is safe to edit and what still needs caution.
|
||||
|
||||
- Class name is authoritative at the container level: it comes from the owner-loaded class-name table and is not a guess.
|
||||
- Slot id is authoritative at the event-table level: this is the safest event identifier currently available.
|
||||
- Event-name hint is external: use it as orientation only when the slot is inside `0x00..0x1f` and the local behavior has not yet been reverified in binary.
|
||||
- Raw event word is authoritative but semantically unresolved: it must survive round-trip intact.
|
||||
- Raw code offset is authoritative and operational: combined with `code_base_minus_one`, it tells us where the slot body starts in the chunk.
|
||||
- Body-window length is derived but useful: it tells a human whether a slot is a tiny stub-like record or a large body that deserves its own diff or annotation block.
|
||||
- Repeated-template status is about family structure, not byte identity: a `_BOOT` slot can be “the same template role” without being byte-equal across classes.
|
||||
- Body-identity status answers the concrete modding question “am I looking at a clone, a parameterized variant, or a different body that only occupies the same family slot?”
|
||||
|
||||
### Level 3: Where the current VM IR can be attached
|
||||
|
||||
For classes in the active-event ecosystems (`EVENT`, `_BOOT`, `NPCTRIG`, `SFXTRIG`, and the environmental family), the current `000d` work is strong enough to attach the known operator vocabulary without pretending one exact class-to-opcode decode already exists.
|
||||
|
||||
```text
|
||||
vm_effect_possible:
|
||||
APPEND_UNIQUE_INLINE
|
||||
APPEND_UNIQUE_INDIRECT
|
||||
REMOVE_MATCHING_INDIRECT
|
||||
REMOVE_MATCHING_INLINE
|
||||
MATERIALIZE_OR_FORWARD_VALUE
|
||||
PREPEND_INLINE_PAYLOAD
|
||||
BUILD_ENTITY_LINK_MATRIX
|
||||
EMIT_OR_PUSHBACK_RESULT
|
||||
FINALIZE_MIXED_VALUE_TO_OUTPTR
|
||||
```
|
||||
|
||||
That operator block is authoritative as a recovered VM vocabulary, but only ecosystem-level when attached to one specific descriptor family.
|
||||
|
||||
## Conservative Parser Rule To Adopt Now
|
||||
|
||||
For the current owner-loaded EUSECODE and round-trip IR work, the safest reversible rule is:
|
||||
|
|
|
|||
523
plan-mid.md
523
plan-mid.md
|
|
@ -2,427 +2,188 @@
|
|||
|
||||
## Purpose
|
||||
|
||||
This file is the workspace-facing mid-project tracker for the Crusader decompilation effort.
|
||||
It is intended to answer four questions clearly:
|
||||
This file is the live mid-project tracker for the Crusader decompilation effort.
|
||||
|
||||
1. How far along is the project?
|
||||
2. What is already solid?
|
||||
3. What still blocks broader decompilation?
|
||||
4. What should be implemented next?
|
||||
Keep it focused on:
|
||||
|
||||
The estimates below are intentionally conservative. They measure verified behavioral understanding, not just renamed symbols.
|
||||
1. current verified state,
|
||||
2. active blockers,
|
||||
3. next resume work,
|
||||
4. and the remaining path to a reasonably complete decompilation.
|
||||
|
||||
Detailed completed analysis belongs in the files under `docs/`, not in this plan.
|
||||
|
||||
## Progress Snapshot
|
||||
|
||||
## Working Progress
|
||||
|
||||
### Last Confirmed State
|
||||
|
||||
- Priority 0 has started: `crusader_segment_coverage_ledger.csv` exists and contains a first-pass 145-row ledger.
|
||||
- The currently seeded ledger rows are conservative and strongest around seg001, seg004, seg021, seg043, seg080, seg082/083/085, seg091, seg094, and seg095.
|
||||
- Priority 1 has started on the cache/backend cluster: the seg082 allocator mechanics are now materially recovered (`allocator_head_try_alloc_block`, `allocator_head_free_block`, `allocator_free_block_by_ptr`, `allocator_try_alloc_from_head_table`, `allocator_phase_finalize_pass`), and the `0x4588` path now has named lifecycle helpers (`runtime_callback_object_init_once`, `runtime_callback_object_teardown_once`).
|
||||
- The `0x4588` blocker is tighter than before: `000a:b988` boundary repair now includes both callback sync callsites (`000a:b9e5` / `000a:ba66`) inside one real function body, `000d:9d5e` / `000d:a3b7` are confirmed inside `entity_cleanup_resources_and_dispatch`, and adjacent helpers are now clarified as `allocator_head_finalize_sweep` (`0009:a961`), `video_bios_state_snapshot` (`000a:4a1f`), and `video_mode_set_and_record_state` (`000a:4972`). Concrete subsystem identity is still unresolved.
|
||||
- A larger MCP rename batch completed for cleanup callees: `palette_buffer_alloc_and_init_256` (`0009:7853`), `file_handle_alloc_init_and_open` (`0009:1c3a`), `file_handle_open_with_mode` (`0009:1d6a`), `surface_release_internal` (`0009:8d7b`), `surface_release_and_maybe_free` (`0009:8e0a`), and `sprite_redraw_global_if_active` (`000d:9231`). This reduces `entity_cleanup_resources_and_dispatch` ambiguity on file/surface/palette teardown paths.
|
||||
- The previously missing `000d:7e00` function object is now recovered and named `entity_dispatch_entry_init_runtime_state`, with paired destructor `entity_dispatch_entry_release_runtime_state` at `000d:8078`. Adjacent missing helpers `0003:a880` and `0003:b8e2` were also recovered, with `0003:b8e2` promoted to `far_buffer_alloc_with_mode_flags`.
|
||||
- Additional helper stabilization now covers seg061/064/076: `vga_palette_read` (`0009:6ec7`) is confirmed alongside existing palette write/free paths, `timer_entity_enable_wrapper` (`0008:d3ba`) is named, and seg064 one-shot gate helpers around `0x3b72/0x3b73` are documented with conservative comments while keeping speculative naming deferred.
|
||||
- Constructor-lane semantics tightened further: `entity_set_update_period_and_reschedule` (`0008:d27e`) and `palette_buffer_alloc_copy_from_source` (`0009:7905`) are now named, and both `0x4588` callback emit callsites (`000d:9d5e`, `000d:a3b7`) now have explicit payload-pair annotations in disassembly.
|
||||
- The seg082 allocator table structure is now pinned down as the allocator head table at `0x8724` and active head count at `0x879c`, and the old structural helpers at `0009:b06b` / `0009:b1c3` are now promoted to `allocator_try_alloc_from_head_table` and `allocator_phase_finalize_pass`.
|
||||
- New caller-side seg138 evidence now exists at `FUN_000d_938c` (`000d:938c-000d:9583`): it builds one scratch-palette dispatch entry (`kind 0x3c`) and one current-palette dispatch entry (`kind 0x14`) through `entity_dispatch_entry_init_runtime_state`, waits for each entry's active flag to clear, then redraws the global sprite path and dispatches through the input object's vtable slot `+0x08`. This narrows the open lane to presentation/dispatch semantics without yet justifying a concrete subsystem rename.
|
||||
- seg137 is now promoted from `Foothold` to `Partial`: direct MCP recovery stabilized a coherent palette/dispatch-entry helper family with safe renames for all-black, all-white, arbitrary-RGB, grayscale, black-state, and solid-color state builders around the same `entity_dispatch_entry_init_runtime_state` lane. The remaining gap is the higher-level event/script meaning of those helpers, not the local mechanics.
|
||||
- seg005 and seg136 now have new high-value footholds: `FUN_0004_60c0` is recovered as a startup/display orchestration handoff that drives the seg137 palette helper family, validates an object through vtable `+0x0c`, creates the default active dispatch entry, programs mouse state, and then hands off into `0004:1e00`; nearby seg136 helpers are now stabilized as `active_dispatch_entry_mark_enabled`, `active_dispatch_entry_mark_disabled`, and `active_dispatch_entry_create_default`.
|
||||
- The downstream seg005 handoff body is now also classified further: `FUN_0004_1e00` (`0004:1e00-0004:2420`) is a non-return startup/display transition driver with confirmed use of `vga_palette_set_all_black`, `animation_ctor_variant_b`, `sprite_node_get_or_traverse`, seg064 gate helpers, the `0x2bd8` vtable lane, and the `0x4aa/0x7e22` resource/object lane. The remaining work is naming the exact state label, not repairing the structure.
|
||||
- seg126 is now promoted from `Foothold` to `Partial`: `FUN_000c_7412`, `transition_preentry_setup_resources`, `transition_preentry_release_resources`, `transition_preentry_run_until_complete_or_abort`, `transition_preentry_step_script`, `thunk_callf_0000_ffff_000c_827d`, `thunk_callf_0000_ffff_000c_82f9`, and `FUN_000c_834a` now show a coherent pre-entry, guarded-entry, script/fade step, and post-transition control shell around the same `FUN_0004_1e00` startup/display state.
|
||||
- seg127 is now promoted from `Foothold` to `Partial`: `palette_fade_begin_full_up`, `palette_fade_begin_full_down`, `transition_palette_fade_begin`, `transition_palette_fade_tick`, `transition_palette_fade_out_step`, and `transition_palette_fade_in_step` form a concrete local palette-fade controller with verified full-range wrappers and caller-side state gating immediately beside the same seg126/seg005 transition lane.
|
||||
- seg049 is no longer blank: `watch_entity_controller_create_global`, `watch_entity_controller_create`, and `watch_entity_controller_dispatch_if_present` now show that `0x2bd8` is a real type-stamped watch/camera controller object lane rather than only a raw watched-entity pointer, and that same controller is exercised from `FUN_0004_1e00`.
|
||||
- seg108 is no longer blank: `sprite_object_clear_flag40_if_present` and `sprite_object_set_flag40_if_present` now anchor the `0x4f38` global sprite/object lane as a real state-bit-controlled object path used beside the same `0x4588` callback sync and startup/display transition flow.
|
||||
- Direct MCP follow-up on seg126 and seg127 now recovered the missing helper bodies after boundary repair: `transition_preentry_setup_resources` (`000c:c63a`), `transition_preentry_release_resources` (`000c:c890`), `transition_preentry_run_until_complete_or_abort` (`000c:c9f4`), `transition_preentry_step_script` (`000c:ca1d`), and the neighboring `transition_palette_fade_tick` / `transition_palette_fade_begin` / `transition_palette_fade_out_step` / `transition_palette_fade_in_step` chain are now named against verified behavior. The latest semantic pass also tightened the two main open globals: `0x8c5c` / `0x8c60` are now best understood as a paired temporary text-renderer lane, while `0x31a2` behaves like an external input/event break gate maintained by queue/interrupt-side code. The remaining structural cleanup is the separate oversized overlap rooted at `000c:db68`, not the seg126 helper family.
|
||||
- Bonus cheat-lane cleanup is now visible in Ghidra too: `cheat_code_check` has recovered local names (`input_event_record`, `input_event_offset`, `new_cheat_enabled`, `cheat_status_display_root`) and a decompiler comment stating that it matches the five-byte event-code sequence `50 80 3e fd 27 00` before toggling the cheat-state bytes and taking one of two local notification paths.
|
||||
- Point 8 cheat/input-lane pass is complete. `keyboard_input_cheat_dispatch` (`0007:04dc`) is renamed and has a full scan-code mapping decompiler comment. `cheat_entity_slot_cycle_and_update_sprite` (`000c:8072`) and `cheat_anim_type_cycle_and_refresh` (`000c:81c0`) are named. Three `DS:0x6050` gate helpers (`000c:8221/8227/822b`) are named. All seven cheat event case-handlers in the 000c dispatch function now have labels and disassembly comments (`event_0x141/0x241/0x441_cheat_debug_overlay_toggle`, `event_0x7e_cheat_latch_runtime_toggle`, `event_0x142/0x143_cheat_fullscreen_mode1/0_refresh`, `event_0x410_cheat_flag_604f_toggle`). The cheat-related string table in seg014 is documented (including the dev Easter-egg `"FART ...TRY... -laurie"`). HACK MOVER / Immortality strings confirmed present with no static code xrefs — attributed to USECODE scripting layer. `0x844` (master cheat flag) vs `0x6045` (live cheat latch) separation remains solid.
|
||||
- User-directed JELYHACK producer tracing is now tightened one layer upstream of `000d:208b` / `000d:21ed`: the immediate stream producer is the embedded mini-VM object created at context `+0x36`. `entity_vm_context_create_from_slot_index` (`000d:46ec`) feeds that object through `entity_vm_context_setup` (`000c:f844`), which uses `entity_vm_stack_init_with_data` (`000c:f6e8`) and `entity_vm_state_copy` (`000c:f772`) semantics to seed or clone `[+0xcc..+0xd2]`. The actual source payload comes from the runtime owner table at `0x6611 -> +0x1315/+0x1317 -> +0x10/+0x12`, addressed as `base + 0x0d*slot + 4`, and the resulting per-slot source is mirrored into `0x39ca`. This still does not expose a direct `JELYHACK`-named producer object, but it strengthens the current reading that `JELYHACK` / `JELYH2` contribute referent identity while neighboring `REE_BOOT` / `SURCAMEW` / `SFXTRIG` descriptors remain better candidates for event-bearing attachments.
|
||||
- The next USECODE/JELYHACK pass now resolves the immediate owner-object writer too. `entity_vm_runtime_create` (`000d:4c99`) is the only writer of runtime `+0x1315/+0x1317`, via newly recovered `entity_vm_runtime_owner_resource_create` (`000d:7000`), and the companion `entity_vm_runtime_owner_resource_destroy` (`000d:70fd`) releases that helper. The `000d:7000` body does not copy a caller-supplied table directly: it constructs one embedded seg069/070 helper object, queries that helper for the required table size via vtable `+0x04`, allocates child `+0x10/+0x12`, then populates the `0x0d`-stride per-slot producer records through vtable `+0x0c`. Wrapper classification around `entity_vm_context_try_create_masked_for_entity` is tighter too: local wrapper `0004:f033` uses slot mask `0x8000:0007`, `FUN_0004_f05c` uses `0x2000:0015` and is reached from `0004:f2b3` after overlap/proximity and entity byte `+0x32` state checks, and `FUN_0005_27a4` uses `0x0001:0000` from the `000c:a09e` entity `+0x5b` bit-`0x0004` branch. This is enough for a conservative owner/resource classification, but not yet for a source-format-specific or descriptor-specific rename beyond that partial role name.
|
||||
- The seg091 `000a:44fd/454d/45fe` cluster is now corrected too: these are reentrancy-guarded fatal-report helpers, not a wrapper selector or live event-dispatch lane. In particular, `entity_vm_slot_load_value` (`000d:51fd`) reaches `000a:44fd` via `PUSH 0x410; PUSH DS; PUSH 0x6616; CALLF 000a:44fd`, which now reads as an error/assert report path rather than a gameplay immortality-event producer.
|
||||
- The `000d:7000` owner/resource helper is one step tighter as well: its embedded seg069/070 object is file-backed rather than abstract. Construction starts with `dos_file_handle_init` (`0009:1c00`), then uses helper vtable slot `+0x04` as the size query for the child `+0x10/+0x12` allocation and helper vtable slot `+0x0c` as the callback that populates the `0x0d`-stride slot table.
|
||||
- The caller side for that owner/resource path is now anchored too. A new function object at `000d:44df` is recovered and named `entity_vm_runtime_init_from_path_if_configured`: it checks configured global/string `0x65a`, builds a path through seg072 helper `0009:3600` using globals `0x6d6:0x6d8` plus `0x65a`, validates it through `000a:500a`, then calls `entity_vm_runtime_create(0,0,path)` and seeds post-init runtime state at `0x6611`, `0x6615`, and `0x8c7c`.
|
||||
- Follow-on correlation for the `000d:21ed/22bc` lane is now tighter: the two bytes consumed at `000d:22d2` and `000d:22ee` are confirmed as compact signed shape/count metadata for the link-matrix walk (not descriptor ids), while the streamed words consumed through `000d:2324..237b` and `entity_link` (`0008:7d27`) behave as runtime entity/link ids with `0x0400` list-flag filtering on writeback. The source stream provenance remains slot-indexed owner-table data (`(+0x10/+0x12) + 0x0d*slot + 4`) mirrored to `0x39ca`, so descriptor-family correlation is strengthened only at the ecosystem level (generic active-event neighborhoods) and not as a direct `SURCAM*` or class-id keyed lane.
|
||||
- Pass 4 dispatcher/xref follow-up is now partially closed: `FUN_000d_ebe3` sequencing is re-verified (`177c -> 1acb -> 0988 -> 22bc -> optional 1d4a -> 2104`) and the opcode selector local is pinned to `[BP-0x32]` inside `000d:0988` with concrete compares against `0x19/0x1a/0x1b` (`000d:099b`, `000d:09a1`, `000d:0a07`, `000d:0a0d`). The direct upstream selector edge into entry `000d:ebe3` remains unresolved in MCP xref output, so no additional wrapper-level opcode number is promoted yet beyond that internal family identity.
|
||||
- Readable EUSECODE targeting moved one concrete step forward again. Fresh decompile on `entity_vm_context_create_from_slot_index` re-verifies that the slot-backed VM source copied from the runtime owner table (`(+0x10/+0x12) + 0x0d*slot + 4`) is also written into the per-slot mirror row addressed through `0x39ca[context_slot]` during context creation, which tightens the current source model without over-claiming unique ownership of the mirror. Combined with the extracted `jelyhack_*` and `event_*` reports, this now supports a first verified pseudo-script rendering of two descriptor-side forms: `referent anchor + event-bearing attachment` (`JELYHACK` / `JELYH2` beside `REE_BOOT` / `SFXTRIG` / `SURCAMEW`) and `event hub + trigger-side neighbors` (`EVENT` / `COR_BOOT` / `NPCTRIG` inside the `ROLL_NS..VMAIL` island). The direct descriptor-id bridge and the upstream selector into `FUN_000d_ebe3` both remain unresolved.
|
||||
- The next wrapper-expansion pass is now also complete. The `0005:2867/2918/2ae2/2d30` family and its adjacent `2c06/2c35/2c68/2c9b/2cd2/2d01` siblings now form one verified mask ladder around `entity_vm_context_try_create_masked_for_entity`: `0x0002:0001`, `0x0020:0005`, `0x0004:0002`, `0x0200:0009`, `0x0400:000a`, `0x0800:000b`, `0x0010:0004`, `0x1000:000c`, `0x4000:000e`, and `0x8000:000f`, alongside the earlier seeds `0x0001:0000`, `0x8000:0007`, and `0x2000:0015`. The strongest new caller-side evidence is gameplay-state-oriented rather than descriptor-id-oriented: `0005:2867` feeds `000c` state helpers that store the result into entity field `+0x39`, `0005:2918` is only reached from a `+0x3c == 0x20b` object lane and carries caller fields `+0x36/+0x38` as an extra dword, and `0005:2d30` gates on entity id range, class word bit `0x8000`, class-record `0x7e46` flag bits, class nibble values `4/7/8`, and a `0x0f16` / `0x20f` dispatch-entry emission path before attempting mask `0x8000:000f`. That makes the wrapper family correlate more naturally with active-event ecosystems (`EVENT` / `NPCTRIG` / `_BOOT`) than with a direct `JELYHACK` referent-anchor lookup, while still stopping short of a hard descriptor-class switch.
|
||||
- First concrete follow-on pass for the adjacent wrapper entries is now in: direct caller anchors are confirmed for `0005:2c06` (`0005:0292`), `0005:2cd2` (`0005:0fee`), `0005:2c9b` (`0005:5946/59e9`), and `0005:2d01` (`0007:814e/822e`). The old `0005:2c68` indirect-dispatch evidence is now rejected: `0007:e521` and `0007:e73c` push caller-local data `DAT_0000_2c68` into the fatal-report helper `000a:44fd`, not into wrapper `0005:2c68`. That leaves both `0005:2c35` and `0005:2c68` genuinely xref-dark and shifts the remaining selector work back to the true upstream edge into `FUN_000d_ebe3`.
|
||||
- The current VM owner/path lane is now tighter too. Seg072 helper `0009:3600` is not a raw buffer-advance helper; it is a rotating slash-aware path composer that uses five `0x50`-byte temp buffers and inserts `\` only when adjacent path parts need it. In `entity_vm_runtime_init_from_path_if_configured` (`000d:44df`), `0x65a` therefore reads as the configured relative runtime-owner filename/path component, while `0x6d6:0x6d8` reads as the mutable base/resource-root path buffer. The still-xref-dark wrappers `0005:2c35` / `0005:2c68` are narrower too: their signed extra word is forwarded through `entity_vm_context_create_from_slot_index` into `entity_vm_slot_load_value_plus_offset` and stored in context field `+0x34`, so they are offset-specialized mask wrappers rather than plain duplicates. Direct CALL xrefs into `FUN_000d_ebe3` are also now confirmed from `animation_ctor_variant_a/b/c`, but no new wrapper-level opcode number is proven yet beyond internal `0x19/0x1a/0x1b`.
|
||||
- The owner/resource helper is now tighter at the file-loader level too. The embedded seg070 methods rooted at raw windows `0009:67b6` and `0009:6916` iterate helper-owned tables at object `+0x10/+0x18`, format per-entry paths with the seg001 string helpers, then open/read/close through `0009:1c3a` / `0009:2034` / `0009:1e61`. That pushes `entity_vm_runtime_owner_resource_create` (`000d:7000`) beyond a generic file-backed label: it now looks like an indexed external file-set loader whose vtable `+0x0c` callback materializes the `0x0d`-stride owner records consumed by the VM runtime.
|
||||
- The `0x39ca` mirror follow-up is narrower now too. The newly checked `0008:709c/70cb`, `0008:7309/7338`, and `0008:85f9/8617` windows only save/restore or allocate the global `0x39ca:0x39cc` base pointer and zero the backing table. No new competing per-slot row writer is verified there; `entity_vm_context_create_from_slot_index` (`000d:46ec`) still remains the only confirmed writer of concrete `0x39ca[slot] = {source_off, source_seg}` mirror rows.
|
||||
- The descriptor-side tooling is now one step closer to readable USECODE too. `tools/extract_eusecode_flx.py` widens the JELYHACK local window to include the nearby event-bearing `REE_BOOT` / `SURCAMEW` / `SFXTRIG` records and now emits `readable_descriptor_templates.md` / `.tsv`, which render conservative pseudo-script sketches for the current anchor, event-hub, environmental, and callback lanes rather than only raw descriptor indexes.
|
||||
- The script-facing bridge artifact is now tighter too. `tools/extract_eusecode_flx.py` emits `runtime_descriptor_family_rankings.md` / `.tsv`, which rank descriptor families against the verified VM/runtime lanes: `EVENT` is the strongest active-event payload fit, `_BOOT` cores and `NPCTRIG` remain strong active-event satellites, `SFXTRIG` and the environmental classes stay moderate active-event fits, `JELYHACK` / `JELYH2` now occupy a dedicated referent-anchor/payload-owner lane, and `SURCAMNS` / `SURCAMEW` stay in a weaker callback/attachment lane until callback-specific opcode or mask evidence appears.
|
||||
- External reference pass completed against ScummVM's Ultima 8 / Crusader engine. New note `docs/scummvm-crusader-reference.md` records concrete Crusader-specific evidence for `usecode/*.cpp`, `convert/crusader/*.h`, FLEX parsing, sound/speech/movie handling, map record layout, Crusader-only shape/typeflag handling, HUD gumps, and startup/world-state differences. Highest immediate payoff is on USECODE parsing: `usecode/usecode_flex.cpp` gives ScummVM's Crusader class-header/event-count interpretation, while `convert_usecode_crusader.h` provides named event ids `0x00..0x1f` and a large intrinsic-signature table that can be cross-checked against current VM/runtime work.
|
||||
- The first ScummVM-guided USECODE cross-walk batch is now complete. `usecode/usecode_flex.cpp`, `usecode/usecode.cpp`, `usecode/uc_machine.cpp`, and the Crusader/Regret conversion tables now externally anchor the Crusader class/event model (`classid + 2`, names from object `1`, base offset from bytes `8..11` minus `1`, six-byte event records, event-number call translation through opcode `0x11`) and confirm that Remorse uses a `ByteSet(0x1000)` VM with the shared Crusader event-name table but version-specific intrinsic dispatch. New note `docs/usecode-roundtrip-ir.md` records the safe annotation policy plus a reversible IR v0 that preserves raw class/event bytes, intrinsic ordinals, inline-versus-indirect payload distinctions, and opaque-opcode fallback. Headline estimate is intentionally unchanged because this batch tightened interpretation more than direct binary coverage.
|
||||
- The next binary-side validation step for that ScummVM cross-walk is now complete too. Sampled owner-loaded EUSECODE class records (`EVENT`, `NPCTRIG`, `SURCAMNS`, `JELYHACK`, `REE_BOOT`, `SURCAMEW`, `SFXTRIG`) now confirm the object-1 name-table and `classid + 2` body lookup locally: deriving `object_index = (table_offset - 0x80) / 8`, `class_id = object_index - 2`, and then reading object `1` at `4 + 13 * class_id` yields the expected class names. The same samples confirm a real header dword at bytes `8..11` and a 6-byte event-slot table at `+20` with `u16 unknown + u32 code/payload` structure.
|
||||
- The current USECODE-header mismatch is now narrowed further and has a conservative working resolution. `uc_machine.cpp` uses ScummVM's decremented `get_class_base_offset()` as the live code-stream base, while the local owner-loaded records still fit bytes `8..11 = first code-byte offset` with 1-based event code offsets. Under that reading the local event-count rule is `(base_offset - 19) / 6`, equivalently `(raw_u32_at_8_11 - 20) / 6`, which matches the validated `32/33/35` slot tables from the `0x00d4/0x00da/0x00e6` headers. The `000d:44df -> 000d:4c99 -> 000d:7000 -> 000d:46ec` runtime path still shows indexed file loading and slot-table consumption but no verified per-class header rewrite, so the mismatch currently looks best explained by a ScummVM interpretation/detail issue rather than a proven owner-loader transform. No safe new event-label-to-runtime correlation was promoted from this pass, and the headline estimate remains unchanged.
|
||||
- The conservative owner-loaded class rule is now implemented in `tools/extract_eusecode_flx.py` and refreshed on the current EUSECODE sample. New outputs `class_layout_index.tsv` and `class_event_index.tsv` now expose object index, class id, class-name hint, raw bytes `8..11`, derived `code_base_minus_one`, conservative event counts, and raw 6-byte event rows with ScummVM slot-name hints, giving the round-trip work a concrete parser baseline instead of only prose notes.
|
||||
|
||||
### Current Focus
|
||||
|
||||
1. User-directed USECODE/JELYHACK lane: use the updated parser outputs to recover a small safe set of non-zero slot semantics, then tighten the reversible script IR around the already verified `000d` VM families before returning to deeper producer/dispatcher mapping.
|
||||
2. Finish Priority 0 refinement by promoting more exact segment rows where notes already support a verified foothold.
|
||||
3. Continue the Priority 1 pass by tracing the higher-level startup/display callers, branch outcomes, pre-entry object lanes, palette-fade ownership, watch/camera controller ownership, and active sprite/object ownership that stitch the seg137 palette helper family into the wider `0x4588` / dispatch-entry object-role lane.
|
||||
|
||||
### Next Resume Point
|
||||
|
||||
1. Continue the user-directed USECODE/JELYHACK follow-on from the recovered producer chain, now with the ScummVM class/event cross-walk in place, especially by:
|
||||
- validating the new conservative rule against any future main USECODE container sample that becomes available, to decide whether the current mismatch is EUSECODE-specific or whether ScummVM's `get_class_event_count()` arithmetic should be treated as the outlier for Crusader,
|
||||
- mining the new `class_layout_index.tsv` / `class_event_index.tsv` outputs for repeat non-zero slot patterns before doing more ad hoc byte inspection,
|
||||
- broadening the local owner-loaded spot-check beyond the first named cluster when convenient, especially across additional `_BOOT`, environmental-event, and callback-eventtrigger classes, while treating the present object-1 / `classid + 2` indexing as the current working model,
|
||||
- determining whether the owner/resource helper behind `entity_vm_runtime_owner_resource_create` (`000d:7000`) exposes original object indices through its helper `+0x18` table or only slot-local file ids, now that the `+0x10/+0x14/+0x18` table contract is verified,
|
||||
- re-tracing `0005:2c35` / `0005:2c68` through real caller-role recovery now that they are narrowed to signed slot-offset wrappers, while keeping the disproven `000a:44fd` selector hypothesis retired,
|
||||
- mapping only the now-verified non-zero low slot ids from sampled classes (`JELYHACK` slot `1`; `EVENT`/`SFXTRIG` slot `10`; `NPCTRIG` slots `10/32`; `REE_BOOT` slots `10/15/16`; `SURCAMNS`/`SURCAMEW` slots `1/10/32/33/34`) onto ScummVM event labels where binary behavior actually matches, and otherwise keeping them as numeric slot annotations,
|
||||
- separating safe Remorse annotations from Regret-only intrinsic numbering by treating ScummVM intrinsic names as ordinal/signature hints rather than rename authority,
|
||||
- turning the new conservative parser rule into tooling/tests first, while preserving raw bytes `8..11`, raw 6-byte event rows, and the unresolved leading event word in emitted IR artifacts,
|
||||
- resolving the remaining selector/opcode path inside `FUN_000d_ebe3` by lifting the write/read path for opcode-local `[BP-0x32]` and any hidden jump/call-table case entry from the now-confirmed `animation_ctor_variant_a/b/c` caller lane,
|
||||
- hardening the first reversible script IR around preserved class headers, raw event-entry words, intrinsic ordinals, inline-versus-indirect payload forms, and opaque-opcode fallback,
|
||||
- identifying the concrete seg069/070 helper class behind `entity_vm_runtime_owner_resource_create` (`000d:7000`) now that the helper is narrowed to an indexed external file-set loader around raw windows `0009:67b6` / `0009:6916`,
|
||||
- tracing the remaining caller roles for the verified ladder entries `0005:2c06/2c35/2c68/2c9b/2cd2/2d01` and the larger `0005:2d30` gate so the slot-mask groups can be mapped to concrete gameplay object/state classes rather than only mask numbers,
|
||||
- and checking whether any runtime-owner helper besides `entity_vm_context_create_from_slot_index` writes per-slot mirror rows through the far array rooted at `0x39ca`, now that the currently checked `0008:709c/70cb`, `0008:7309/7338`, and `0008:85f9/8617` sites are constrained to base-pointer save/restore or table allocation rather than row writes.
|
||||
2. Keep classifying the seg126 pre-entry text-renderer lane around `transition_preentry_setup_resources`, `transition_preentry_step_script`, and `transition_preentry_release_resources`, especially by:
|
||||
- comparing more preset `0x10` / `0x11` text-renderer callsites,
|
||||
- tracing who owns the rendered buffer loaded into `0x6301:0x6303`,
|
||||
- mapping the control bytes `0x21` / `0x23` / `0x24` / `0x26` / `0x2a` / `0x40` / `0x5e` to concrete display behavior,
|
||||
- and deciding whether the paired `0x8c5c` / `0x8c60` lane is a title/body pair, normal/highlight pair, or another fixed UI pairing.
|
||||
3. Finish the `0x31a2` gate pass as one batch:
|
||||
- classify the read sites at `0004:c24d`, `000c:ca11`, `000c:e4d8`, `000c:e546`, `000c:e5c6`, `000d:9304`, `000d:b6b1`, and `000d:c0ee`,
|
||||
- relate them back to interrupt-side updates at `0008:a283` / `0008:a314`,
|
||||
- and decide whether `0x31a2` is best described as user-acknowledge, queued-input depth, or a broader event-break gate.
|
||||
4. Tighten the `DS:0x6341` to `0x6828` relationship:
|
||||
- compare the seg126 `animation_ctor_variant_a` call with the other raw callsites at `0005:3c4f`, `0005:3c74`, `000c:6176`, and `000c:619c`,
|
||||
- map who owns `g_active_dispatch_entry_farptr[+0x40]`,
|
||||
- and classify whether seg126 is constructing a transition-local animation payload for the shared active dispatch entry or only toggling an owner-side state bit after setup.
|
||||
5. Identify which higher-level transition states own the seg127 fade-controller inputs at `0x630a-0x6316` and how that fade state is chosen from the seg005/seg126 startup path.
|
||||
6. Repair the still-oversized overlap rooted at `000c:db68` only if it blocks follow-on analysis or decompiler visibility in the same transition lane.
|
||||
7. Clarify the relationship between the seg049 watch/camera controller at `0x2bd8`, the seg108 sprite/object lane at `0x4f38`, and the object validated through `FUN_0004_60c0` vtable slot `+0x0c`.
|
||||
8. Continue caller-role classification inside `entity_cleanup_resources_and_dispatch` (contains both `000d:9d5e` and `000d:a3b7`) and map how it relates to `FUN_000d_938c`, `FUN_0004_60c0`, `FUN_000c_7412`, `transition_preentry_release_resources`, and the seg136/seg137 active-dispatch helper family.
|
||||
8. ~~Cheat/input side lane~~ — **COMPLETED** this pass. All point-8 sub-items are now resolved:
|
||||
- `keyboard_input_cheat_dispatch` renamed; full scan-code table documented in decompiler comment.
|
||||
- `cheat_entity_slot_cycle_and_update_sprite` and `cheat_anim_type_cycle_and_refresh` named.
|
||||
- `DS:0x287b` / `DS:0x2892` success-path presentation: confirmed as opaque near-code discriminator values stored at `+0x49` in the display notification object; the cheat-on/off display is built via `display_null_check_dispatch` + `sprite_node_get_or_traverse`.
|
||||
- All seven cheat event case-handlers in the 000c dispatch function labeled and commented.
|
||||
- `0x844` (master) vs `0x6045` (live latch) separation confirmed solid; `0x604b` / `0x604f` / `0x6050` also documented.
|
||||
- HACK MOVER: no static code xrefs; attributed to USECODE scripting layer. Cheat string table in 000e fully documented.
|
||||
- Remaining open: exact user-facing identity of events `0x141/0x241/0x441` overlays (strings suggest targeting-reticle / CD-transfer-display), exact DS:0x6087/6091 notification objects, and any further depth on the `0x4f38` / `0x2bd8` vtable path taken by the overlay events.
|
||||
- The Immortality cheat mechanics are now fully traced at the C level: event `0x410` toggles `DS:0x604f`; the sole read site is `player_receive_damage_and_dispatch_effects` (`0004:c055`) at `0004:c205`, which divides all incoming 32-bit damage by `0x40000` (262,144) when the flag is set, making HP loss negligible while the hit-stagger animation still plays. No static C keyboard dispatch generates event `0x410` — confirmed USECODE/ASYLUM scripting layer only. `DS:0x60d2` / `DS:0x60ee` are the "Immortality enabled." / "Immortality disabled." notification pointers. A parallel handler at `000b:b62c` sets the associated USECODE process state to `0xe` when the event arrives.
|
||||
- `tools/extract_eusecode_flx.py` now parses the validated full EUSECODE table (`count @ 0x54`, table @ `0x80`) rather than the old heuristic header scan. Current run extracts all `403` non-zero entries and emits a searchable `entry_index.tsv` with `primary_label` and `field_names` summaries.
|
||||
- The extractor now also emits `descriptor_index.tsv` and `descriptor_neighborhoods.tsv`, which summarize per-class field-tag patterns and the local neighborhoods around trigger/event-related classes.
|
||||
- Current EUSECODE split is now clearer: the `000e` parser lane plausibly covers text-heavy records like `DATALINK` and `TEXTFIL1`, while the binary descriptor lane exposes object classes such as `EVENT`, `NPCTRIG`, `CRUZTRIG`, `TRIGPAD`, `SPECIAL`, `SURCAMNS`, `SURCAMEW`, `JELYHACK`, and `JELYH2`.
|
||||
- The descriptor lane now has a real structural foothold too: field-name strings are preceded by short tagged metadata records (`69 xx 00 <name>`, `24 xx 02 <name>`, etc.) in multiple classes. This looks like compact field-definition encoding rather than arbitrary string spill.
|
||||
- That tag grammar is now useful enough to search semantically: `69:0A00 -> event` is stable across `EVENT`, `NPCTRIG`, `SFXTRIG`, and several `*_BOOT` classes, while `24:0A02 -> eventTrigger` shows up in `SURCAMNS` / `SURCAMEW`.
|
||||
- Immortality-specific follow-on is now narrowed but not closed: `JELYHACK` and `JELYH2` are confirmed as real referent-only EUSECODE descriptors; `NPCTRIG` is confirmed as an event-capable trigger descriptor; `CRUZTRIG` / `TRIGPAD` expose `referent,item,elev`; but no extracted record has yet been tied directly to binary event value `0x410`.
|
||||
- The clustering pass tightened the local candidate set around `JELYHACK`: the immediate neighborhood now includes `SPECIAL`, `TRIGPAD`, `DATALINK`, `HOFFMAN`, `REE_BOOT`, `SURCAMEW`, and `SFXTRIG`, which is a plausible map/object island rather than random sparse table order.
|
||||
- The strongest `record_table_parse_buffer` caller evidence (`000e:1b9f..1d49`) now appears to belong to the animation-object field lane, because the surrounding setup manipulates the already-mapped animation fields at `+0x117/+0x11b/+0x11f/+0x123` and `+0xeaf/+0xeb1`. That weakens the earlier assumption that `000e:3639` is the primary EUSECODE loader and shifts the likely binary-descriptor consumer search back toward the `000d` VM/object path.
|
||||
- The first concrete `000c` to `000d` bridge in that direction is now visible at `entity_vm_set_value_from_slot_plus_offset` (`000c:f95f`): it calls `entity_vm_slot_load_value_plus_offset` (`000d:5572`) and stores the return pair into object fields `+0xd6/+0xd8`. Supporting slot helpers in the same lane are now named too (`entity_vm_slot_find_or_select`, `entity_vm_slot_decrement_use_count`, `entity_vm_slot_release_value`). The previously noted `000d:51fd` `PUSH 0x410` site is now reclassified as a fatal-report call into `000a:44fd` with `DS:6616`, so it no longer supports a direct compiled-code immortality-event bridge.
|
||||
- The adjacent `000d:45xx..4exx` island is now promoted out of `FUN_*` placeholders as one coherent VM runtime/context family. Newly named helpers include `entity_vm_runtime_create` / `entity_vm_runtime_init_slots` / `entity_vm_runtime_release_slots` / `entity_vm_runtime_destroy`, `entity_vm_slot_index_from_entity`, `entity_vm_context_try_create_masked_for_entity`, `entity_vm_context_create_from_slot_index`, `entity_vm_context_sync_global_value_and_dispatch`, and the context save/load/destroy helpers. The runtime global at `0x6611` now reads as a real owner for this lane rather than an opaque far pointer.
|
||||
- Two large caller bodies at `000d:208b` and `000d:21ed` now stand out as concrete context-construction sites: both feed per-object stream/data state from `+0xcc/+0xce` into `entity_vm_context_create_from_slot_index`, then continue by reading from the seeded `+0xd6/+0xd8` bytecode/value lane. This is the clearest current evidence that the `000d` interpreter/object family, not the `000e` text parser, is the near-runtime consumer to keep following for the immortality trigger.
|
||||
- A second supporting lane is now named too: `entity_vm_referent_registry_init` / `destroy` / `alloc` / `release_by_id` / `free_node` show that `0x8c8c/0x8c8e/0x8c90/0x8c94` form a free-list-backed referent registry. `entity_vm_set_field_da_to_global` writes `0x8c94` from the context `+0xda` lane before entering the still-misaligned `000c:3350` body, which is the first concrete runtime mechanism explaining how referent-only descriptors such as `JELYHACK` can still participate in script state.
|
||||
- That referent-registry lane is now better structured too: `entity_vm_referent_chain_copy`, `entity_vm_referent_chain_append_unique_from`, `entity_vm_referent_chain_remove_matching_from`, `entity_vm_referent_chain_contains_entry`, `entity_vm_referent_chain_get_entry_data_at`, `entity_vm_referent_chain_set_entry_data_at`, and `entity_vm_referent_chain_get_indirect_data` show that the runtime can build, subtract, and mutate payload chains hanging off one referent anchor. This is the first runtime shape that looks directly useful for a future human-readable / modifiable script IR.
|
||||
- `entity_vm_opcode_finish` (`000d:3350`) is now identified as the shared opcode epilogue for this family rather than an opaque helper: it writes `0x8c94` from frame-local state, unwinds the temporary slot-array state at `0x659c/0x659e` when present, and returns the current opcode result.
|
||||
- The runtime/context half of that lane is now named too. The `0x6611` global is managed by `entity_vm_runtime_create` / `entity_vm_runtime_init_slots` / `entity_vm_runtime_release_slots` / `entity_vm_runtime_destroy`, while `entity_vm_slot_index_from_entity`, `entity_vm_context_try_create_masked_for_entity`, and `entity_vm_context_create_from_slot_index` now show how gameplay entities are tested against one owner-side slot-mask table before a `0x6714` VM context is created.
|
||||
- That context family is no longer anonymous either: `entity_vm_context_sync_global_value_and_dispatch`, `entity_vm_context_save`, `entity_vm_context_load`, `entity_vm_context_destroy`, and `entity_vm_context_free_buffer` now pin down the lifecycle around the same `+0xd6/+0xd8`, `+0x102`, `+0x10c/+0x10e`, and `+0x11b/+0x11d` fields.
|
||||
- Current best near-runtime callsites for further immortality work are the large `000d:208b` and `000d:21ed` bodies, which both build one VM context from caller stream/data state and then continue by consuming bytes from the seeded context value lane.
|
||||
- The first opcode family under that lane is also less anonymous now: `000d:0988` can either append unique payload entries or remove matching ones depending on the opcode id (`0x1a/0x1b` taking the removal path), and both branches return through `entity_vm_opcode_finish`.
|
||||
- That opcode family is now classified one step further: `0x19` = append-unique indirect/string-like payloads, `0x1a` = remove-matching indirect/string-like payloads, `0x1b` = remove-matching inline payloads, and the same helper body strongly implies `0x18` as the missing append-unique inline sibling.
|
||||
- The first stable `+0xd6/+0xd8` byte-lane semantics are now visible in the two large caller bodies too. The `000d:208b` block is a simple materialize-or-forward path after `entity_vm_context_create_from_slot_index`, while `000d:21ed` copies a caller-owned inline blob into the context `+0x102` buffer and then consumes two stream bytes as compact shape/count metadata before building an `entity_link` closure matrix from the following caller-stream words.
|
||||
- EUSECODE readability moved one concrete step forward in this pass: decompile output now supports a first verified IR vocabulary for the same lane — `APPEND_UNIQUE_INLINE` (implied `0x18` sibling), `APPEND_UNIQUE_INDIRECT` (`0x19`), `REMOVE_MATCHING_INDIRECT` (`0x1a`), `REMOVE_MATCHING_INLINE` (`0x1b`), `MATERIALIZE_OR_FORWARD_VALUE` (`000d:208b`), `PREPEND_INLINE_PAYLOAD` (`000d:21ed`), and `BUILD_ENTITY_LINK_MATRIX` (`000d:22bc` with `entity_link` at `0008:7d27`). The `000d:22bc` tail also confirms a pushback filter where non-`0x0400` results are written back to the caller stream before `entity_vm_opcode_finish`.
|
||||
- Current best JELYHACK reading is tighter than before: the extracted chunks still only expose `referent`, but the new referent-registry work means that does not relegate them to inert map labels. The most defensible present model is `JELYHACK/JELYH2 = referent anchors`, with the actual immortality/event behavior carried by neighboring event-capable descriptors in the same local island (`REE_BOOT`, `SURCAMEW`, `SFXTRIG`, or a nearby generic event/trigger record).
|
||||
- That readability step now has a first concrete artifact: `tools/extract_eusecode_flx.py` emits `referent_anchor_event_graph.tsv` plus a focused `jelyhack_island_graph.md`, which turns the local table neighborhood into a first readable anchor-to-event view instead of only raw descriptor rows.
|
||||
- The extractor now also emits `jelyhack_descriptor_compare.tsv`, and its first result is useful: `JELYHACK` and `JELYH2` have identical first 16 header words as referent-only sibling descriptors, while `REE_BOOT`, `SURCAMEW`, and `SFXTRIG` show materially richer header/state patterns consistent with the event-bearing side of the island.
|
||||
- Latest opcode-side refinement: `entity_vm_opcode_finish` (`000d:3350`) is now the shared epilogue for the chain-mutating handlers, while `entity_vm_referent_chain_remove_matching_from` (`000d:6a9a`) and `entity_vm_referent_chain_set_entry_data_at` (`000d:6cf6`) show that the VM can subtract and rewrite payload chains in place, not just append/copy them.
|
||||
- The `000d:21ed` follow-on is now better anchored semantically too: its nested callee `0008:7d27` is `entity_link`, so the `22bc..2433` block is building a bidirectional entity-link closure matrix from streamed entity ids rather than only emitting an opaque table. A conservative disassembly comment is now in place at `000d:22bc`; rename deferred until the bad outer function split is repaired.
|
||||
- The extractor work now scales beyond the JELYHACK case: reusable focused-report helpers emit both `jelyhack_*` and `event_*` cluster artifacts, and the first new result is strong. The `EVENT` island (`ROLL_NS`, `COR_BOOT`, `EVENT`, `NPCTRIG`, `CRUZTRIG`, `NPC_ONLY`, `VMAIL`) contains a compact three-node event-bearing core (`COR_BOOT`, `EVENT`, `NPCTRIG`) surrounded by referent/link/text satellites.
|
||||
- That second island materially improves the EUSECODE model: instead of one special-case `JELYHACK` anchor plus neighbors, we now have a broader pattern of `event-bearing core embedded in referent-neighbor island`, with `EVENT` acting as a large hub descriptor (`source/dest/door/link/time/counter/post1/post2/floor/flicMan`) and `ROLL_NS` / `CRUZTRIG` / `NPC_ONLY` / `VMAIL` reading as attached state or trigger-side records rather than peer event hubs.
|
||||
- The descriptor-side taxonomy is now wider too: `event_family_index.tsv` / `event_family_summary.md` classify all current event-tagged descriptors into reusable families. The active `69:0A00 -> event` lane now breaks cleanly into one `EVENT` hub, five `_BOOT` event cores, one NPC trigger core, one minimal event core (`SFXTRIG`), and three environmental event classes (`FLAMEBOX`, `NOSTRIL`, `STEAMBOX`), while the surveillance pair `SURCAMNS` / `SURCAMEW` is now cleanly separated as `callback-eventtrigger` rather than generic event-bearing descriptors.
|
||||
- The `_BOOT` family is now better constrained too. `boot_family_compare.tsv` shows that `AND_BOOT`, `BRO_BOOT`, `COR_BOOT`, `VAR_BOOT`, and `REE_BOOT` all share one common header/template shape, so the family now reads as repeated instantiations of the same event-core descriptor rather than structurally different boot subclasses.
|
||||
- The best remaining `_BOOT` frontier is now explicit in extractor output as well: `boot_frontier_graph.md` shows `AND_BOOT` / `BRO_BOOT` embedded in a compact referent-heavy neighborhood (`OFFWORK`, `GUARD`, `GDOOR_*`, `BIGCAN`, `CRUMORPH`, `GUARDSQ`, `CARD_*`, wall variants), which is the cleanest unresolved object-side context for the boot-event template.
|
||||
- The environmental event lane is now promoted out of a generic family label into a clearer structural pattern. `environmental_family_compare.tsv` shows `FLAMEBOX` and `STEAMBOX` as close hazard-event siblings with the same active-event backbone plus direction/count, while `NOSTRIL` is the smaller fire-specific variant that keeps the dual-hazard references and counters but drops the direction/newType side.
|
||||
- The callback-trigger lane is also more defensible now: `callback_trigger_compare.tsv` confirms that `SURCAMNS` and `SURCAMEW` are effectively one shared callback template, differing only in one `therma` slot tag offset. That keeps the active `event` lane and callback `eventTrigger` lane separated by more than just naming convention.
|
||||
- Runtime follow-through has resumed too: `000d:ebe3` is now backed by direct instruction evidence as one ordered VM/opcode driver body that calls `000d:177c`, `000d:1acb`, `000d:0988`, internal block `000d:22bc`, then `000d:1d4a` and `000d:2104` in sequence. `000d:ec31` is confirmed as only the internal `CALL 000d:22bc` site inside that body, so the inner block is still not a safe standalone rename target.
|
||||
- Payload-shape reuse inside that same `FUN_000d_ebe3` sequencer is now partially classified: `000d:177c` behaves as a word-literal stream push, `000d:1acb` consumes one streamed dword pair and pushes a boolean word, `000d:21ed/22bc` remains the signed-byte metadata plus word-id matrix lane, `000d:1d4a` is still a boundary-suspect trap island, and `000d:2104` is a mixed scalar/handle out-pointer finalizer. This is now documented as a compact opcode-to-payload-shape matrix in docs.
|
||||
- `entity_vm_context_try_create_masked_for_entity` (`000d:463a`) is now pinned down one step further: it first checks the runtime-disable byte at `0x6610`, computes the entity slot, tests the owner-side slot mask in the runtime owner table, and only then creates a context. On success it reports either an immediate result (success with cleared output word) or an object-backed result (success with the created object's low word), which is the clearest current typed boundary between gameplay entities and VM-backed object results.
|
||||
- The immediate owner-object writer is now identified too. `entity_vm_runtime_create` (`000d:4c99`) stores the only verified runtime `+0x1315/+0x1317` value by calling the newly recovered `entity_vm_runtime_owner_resource_create` (`000d:7000`), whose helper-managed body allocates child `+0x10/+0x12` from a vtable `+0x04` size query and fills the `0x0d`-stride slot table through vtable `+0x0c`. The paired release path is `entity_vm_runtime_owner_resource_destroy` (`000d:70fd`).
|
||||
- The first wrapper-side mask families are now anchored by direct instruction evidence as well: local wrapper `0004:f033` passes `0x8000:0007`, `FUN_0004_f05c` passes `0x2000:0015` from the `0004:f2b3` overlap/proximity branch with entity byte `+0x32` state toggling, and `FUN_0005_27a4` passes `0x0001:0000` from the `000c:a09e` entity `+0x5b` bit-`0x0004` branch. This is enough to distinguish at least three gameplay-side mask lanes without yet claiming descriptor-specific ownership such as `JELYHACK` versus `REE_BOOT`.
|
||||
- One exact `0x410` collision that could have reopened the wrong lane is now ruled out: `000e:0953` pushes literal `0x410` into imported `ASYLUM.27` from the animation/audio path after setting the `+0xef1` audio-completion byte. Because `ASYLUM.DLL` is the `ASS_*` audio/media library, this is not evidence for a second gameplay or USECODE event source; the other previously suspected compiled-code bridge at `000d:51fd` is now ruled out too because that site calls the seg091 fatal-report helper `000a:44fd` with `DS:6616`, not gameplay dispatch.
|
||||
9. Revisit `allocator_phase_finalize_pass` only where it intersects the same callback object semantics, rather than broad allocator mechanics that are already sufficiently constrained.
|
||||
10. Continue `ASYLUM.24` only after the `0x4588` / dispatch-entry lane and `0004:1e00` transition path have no further cheap wins.
|
||||
11. User-directed USECODE/JELYHACK side lane (next actionable IR step): map the new sequencer-local payload-shape matrix to concrete opcode numbers by recovering the upstream opcode dispatcher lane that selects `FUN_000d_ebe3`, then test whether those opcode numbers correlate better with active-event families (`EVENT`/`NPCTRIG`/`*_BOOT`/`SFXTRIG`) than with callback-trigger (`SURCAM*`) descriptors.
|
||||
12. Use the new ScummVM reference note as a focused cross-check batch before deeper parser or VM work:
|
||||
- compare local USECODE/EUSECODE container assumptions against ScummVM's Crusader `UsecodeFlex` class-header parsing (`classid + 2`, class-name table at object `1`, base offset from bytes `8..11`, event-count formula `(base + 19) / 6`),
|
||||
- import the conservative Crusader event-name table from `convert_usecode_crusader.h` (`look/use/anim/cachein/hit/gotHit/hatch/schedule/release/equip/unequip/combine/calledFromAnim/enterFastArea/leaveFastArea/avatarStoleSomething/animGetHit/unhatch`) into the current USECODE annotation workflow where they match verified behavior,
|
||||
- compare current weapon/ammo and item-family reads against ScummVM's `WeaponInfo`, `ShapeInfo`, and `ItemFactory` structures so quality/ammo/clip semantics are kept aligned with evidence,
|
||||
- and prioritize local parsers or validators for the ScummVM-loaded Crusader data files that are still weakly covered here: `dtable.flx`, `damage.flx`, `glob.flx`, `wpnovlay.dat`, `sound.flx`, and per-shape speech FLEX archives.
|
||||
|
||||
### Headline Estimate
|
||||
|
||||
- Overall useful decompilation progress: about 37%
|
||||
- Reasonable uncertainty band: about 31% to 40%
|
||||
|
||||
This is the best single-number estimate for the full game right now.
|
||||
|
||||
### Supporting Metrics
|
||||
|
||||
| Metric | Estimate | Meaning |
|
||||
|---|---:|---|
|
||||
| Top 100 far-call target coverage | about 80% | Roughly 80 of the top 100 most-called far-call targets have been named or materially classified |
|
||||
| Whole-program behavioral coverage | about 37% | Verified subsystem and function understanding across the executable |
|
||||
| Segment spread with meaningful analysis | about 20% to 26% | Segments with more than a trivial foothold or isolated note |
|
||||
| Tooling maturity for continued work | about 75% | Core repair, lookup, and fallback automation needed for continued progress |
|
||||
|
||||
### Why These Numbers Differ
|
||||
|
||||
- The hot-target metric is much higher because the project has already focused on the most shared and most-called helpers.
|
||||
- The whole-program metric is lower because most of the 145 NE segments still have not had systematic coverage passes.
|
||||
- The segment-spread metric is lower still because only a subset of segments have coherent subsystem-level treatment.
|
||||
|
||||
## What Is Already In Place
|
||||
|
||||
### Workflow and Tooling
|
||||
|
||||
- Raw full-EXE Ghidra target is established and in active use.
|
||||
- Verified raw-import mapping exists for seg001 and seg021.
|
||||
- NE relocation parsing has been implemented.
|
||||
- Internal literal far-call fixups have been applied to the raw import.
|
||||
- PyGhidra fallback tooling exists for create/delete function work and batch scripted edits.
|
||||
- Conservative boundary-repair workflow already exists and has been used successfully.
|
||||
- Notes are detailed enough to support a formal executable-wide tracker.
|
||||
|
||||
### Objective Milestones Already Reached
|
||||
|
||||
- 145 NE segments identified from the internal NE header.
|
||||
- 8851 internal literal CALLF sites patched to real targets in the raw import.
|
||||
- 2841 non-CALLF far-pointer relocations identified and deferred.
|
||||
- 119 import callsites annotated.
|
||||
- Top 100 far-call target list processed through five tiers, with about 80 named or materially classified.
|
||||
|
||||
## Strongly Advanced Areas
|
||||
|
||||
### Core Gameplay and Entity Work
|
||||
|
||||
- seg001 gameplay, cursor, entity lifecycle, projectile, combat, and AI footholds are strong.
|
||||
- A verified seg001 raw-port path is working and already used for multiple projectile helpers.
|
||||
- Entity table, class-table, and several global gameplay fields are partially mapped.
|
||||
- Overall useful decompilation progress: about 42%
|
||||
- Reasonable uncertainty band: about 36% to 45%
|
||||
- Top 100 far-call target coverage: about 80%
|
||||
- Segment spread with meaningful analysis: about 22% to 28%
|
||||
- Tooling maturity for continued work: about 75%
|
||||
|
||||
### Why The Estimate Stays Here
|
||||
|
||||
- Recent work materially improved semantic confidence inside the startup/display, cache/allocator, callback-object, and USECODE/VM lanes.
|
||||
- The startup/display lane now has a verified owner split: `g_active_dispatch_entry_farptr[+0x40]` is a borrowed shared presentation hold token, while the seg108 `0x4f38` bit-`0x40` lane stays local to the sprite/object stack.
|
||||
- The seg126 control stream is now tighter at the producer side too: the traced setup path still supports a shared-base-path file selector feeding a full external script/control buffer, the `0x6aa:0x6ac` base now reads as an inherited external/default path buffer rather than a stronger in-code producer, and the in-scope `0x31a2` transition/presentation readers are now classified by role.
|
||||
- That work reduced ambiguity inside already-active clusters more than it expanded whole-program breadth, so the headline only moves modestly.
|
||||
|
||||
## Current Verified State
|
||||
|
||||
### Timer, Event, and State Systems
|
||||
### Primary Tracking Assets
|
||||
|
||||
- `crusader_segment_coverage_ledger.csv` now exists for all 145 NE segments and should remain the primary coverage tracker.
|
||||
- `crusader_decompilation_notes.md` is now only an index; detailed evidence lives in `docs/`.
|
||||
- The raw full-EXE porting workflow is stable for the verified seg001 and seg021 mappings.
|
||||
|
||||
### Strong Or Stable Areas
|
||||
|
||||
- seg021 timer and event-dispatch work has meaningful coverage.
|
||||
- 000c state-dispatch, cursor-nav, UI-listbox, palette-fade, and mini-VM clusters have footholds.
|
||||
- seg001 gameplay/input/projectile work is deep enough to support verified raw-name ports.
|
||||
- raw 0007 rendering/camera/tile-visibility work is structurally strong.
|
||||
- 0008 dispatch-entry helpers and 000c state-machine helpers have broad partial coverage.
|
||||
- 000a/000d tracked-handle, cache, allocator, dispatch-entry, and startup/display support lanes now have a coherent partial map.
|
||||
- 000e parser and animation subsystems have a real partial map.
|
||||
- The USECODE/VM owner/resource/runtime lane now has a workable partial model plus supporting extraction/reporting tooling.
|
||||
|
||||
### Rendering and Camera
|
||||
### Recently Closed Or No Longer Live
|
||||
|
||||
- 0007 rendering, draw-list, tile-visibility, and camera work has strong structural coverage.
|
||||
- `world_to_screen_coords` and adjacent geometric helpers are understood well enough to support further caller analysis.
|
||||
- `ASYLUM.24` is resolved as `_ASS_StopAllSFX`; it is no longer an open plan item.
|
||||
- The cheat/input side lane is complete enough to leave the live queue.
|
||||
- The segment coverage ledger is no longer a missing artifact; only refinement remains.
|
||||
- The startup/display lane now has named outer shells (`startup_display_transition_prepare`, `startup_display_transition_driver`) plus named seg126/127/136/137/138 helper families. The remaining work is higher-level ownership and state semantics, not basic structural recovery.
|
||||
- The top startup/display ownership question is now narrowed: `active_dispatch_entry_create_default` owns `g_active_dispatch_entry_farptr`, while seg049/seg126/seg138 helpers only borrow or clear the shared byte `+0x40`; the seg108 `0x4f38` lane is separate local sprite/object state.
|
||||
- The shared seg126 base-path question is effectively closed: literal-address search still shows no store into `0x6aa:0x6ac`, seg004 only mutates the pointed buffer while separately assigning sibling root `0x6ae:0x6b0`, and the startup/display family continues to treat `0x6aa:0x6ac` as an inherited mutable external/default base path.
|
||||
- The in-scope `0x31a2` transition/presentation reader pass is complete: the remaining reads in this lane now split into edge wait, modal break, deferred dispatch/state advance, and cleanup-abort roles.
|
||||
|
||||
### Dispatch and Pair-Sync Helpers
|
||||
## Live Blockers
|
||||
|
||||
- 0008 dispatch-entry helper families have multiple verified rename batches.
|
||||
- Pair-sync and target-state helper clusters are no longer isolated unknowns.
|
||||
1. The startup/display transition lane still lacks exact higher-level owner/state labels across seg005, seg049, seg108, seg126, seg127, seg136, seg137, and seg138, even though the shared `g_active_dispatch_entry_farptr[+0x40]` hold token is now separated from the seg108-local `0x4f38` bit-`0x40` lane.
|
||||
2. The oversized overlap rooted at `000c:db68` still blocks safe recovery of the real `transition_preentry_step_script` body.
|
||||
3. The `0x4588` callback object is better constrained but still not behaviorally classified enough for a confident subsystem rename.
|
||||
4. The USECODE/VM sequencer still lacks the real upstream selector/caller path into `FUN_000d_ebe3`, and wrappers `0005:2c35` / `0005:2c68` remain caller-dark.
|
||||
5. High-value missing or weak function objects still exist in hot ranges such as `000b:2e00`, `0007:5a00`, and `000e:ffb0`.
|
||||
6. Non-CALLF far-pointer relocations and weakly covered resource/data loaders remain real second-pass blockers, even though they are not the first thing to attack.
|
||||
|
||||
### Cache, Tracked Handles, and Bucket Logic
|
||||
## Current Focus
|
||||
|
||||
- 000a cache manager layer is structurally mapped.
|
||||
- 000a tracked-handle table is structurally mapped.
|
||||
- 000d tracked bucket / proximity / visibility bucket logic has several meaningful behavioral names.
|
||||
- The client/cache distinction is much clearer than before.
|
||||
1. Finish the startup/display transition lane while it is still producing direct executable coverage.
|
||||
2. Continue the USECODE/VM lane only where it yields concrete caller, selector, or loader evidence rather than repeated direct-xref dead ends.
|
||||
3. Refine the coverage ledger from already-verified notes before broadening into fresh segment sweeps.
|
||||
4. Use boundary repair only on active blockers with clear payoff.
|
||||
|
||||
### Parser and Animation Framework
|
||||
## Next Resume Point
|
||||
|
||||
- 000e parser cluster has a stable set of verified names.
|
||||
- 000e animation framework has a real foothold: chunk lookup, audio load, tick, frame advance, and constructor variants are partly mapped.
|
||||
1. Continue the adjacent seg126 startup/display clarification from the local three-way file-family selector at `000c:afa5..b152` and nearby seg049 `0x2bd8` dispatch sites, but only where it sharpens the validated presentation-handoff model without speculative renames.
|
||||
2. Repair the `000c:db68` overlap only if needed to split `transition_preentry_step_script` into its own clean function object and preserve the already-verified `000c:ca1d..cd4f` body in Ghidra.
|
||||
3. Classify the exact UI role of the paired `0x8c5c` / `0x8c60` renderer presets, the `+0x49` selector states, and the neighboring seg127 fade inputs only where the caller evidence stays inside the same startup/display family.
|
||||
4. Recover the real upstream caller/selector path into `FUN_000d_ebe3` from persisted context/save/load or shared-consumer paths instead of repeating exhausted direct xref hunts.
|
||||
5. Recover real caller roles for `0005:2c35` and `0005:2c68`, now that both are narrowed to signed slot-offset wrappers feeding the VM context lane.
|
||||
6. Clarify whether the seg070 twin loops at `0009:67b6` and `0009:6916` represent two file families, two table formats, or two loader phases of the same helper behind `entity_vm_runtime_owner_resource_create`.
|
||||
7. Promote additional ledger rows where the current docs already justify `Foothold`, `Partial`, or `Deep`.
|
||||
8. Revisit `000e:ffb0` and other high-value overlap targets only after the current startup/display and VM lanes stop yielding near-term wins.
|
||||
|
||||
### Local Repair Successes
|
||||
## Remaining Work To Reach A Reasonably Complete Decompilation State
|
||||
|
||||
- seg043 overlap repair succeeded and recovered multiple valid function objects.
|
||||
- seg091 boundary recovery succeeded and exposed RNG helpers plus local init/context helpers.
|
||||
- Recent seg004 reset-path recovery and cache-reset follow-up added a new high-value analysis cluster.
|
||||
### 1. Coverage And Tracker Completion
|
||||
|
||||
## What Still Blocks Broader Coverage
|
||||
- Promote the existing 145-row ledger from a seeded first pass into a trustworthy executable-wide coverage dashboard.
|
||||
- Sweep untouched segments cluster-by-cluster instead of one-off function hunting, using adjacency and call relationships.
|
||||
- Convert more segments from `None` to `Foothold` / `Partial` where current notes already support it.
|
||||
- Close the largest remaining hot-target gaps so the far-call ranking list stays representative of real coverage.
|
||||
- Keep the plan, docs, and ledger synchronized after each verified batch.
|
||||
|
||||
### High-Value Classification Gaps
|
||||
### 2. Startup/Display And Presentation Lane
|
||||
|
||||
- The object rooted at `0x4588` is still not classified well enough to safely rename the callback object itself beyond the current allocator-side glue names.
|
||||
- `ASYLUM.24` is only known as an import site, not yet a confidently identified routine.
|
||||
- Some structural names in the cache/backend/finalize cluster are waiting on object-role confirmation.
|
||||
- Finish semantic ownership across seg005, seg049, seg108, seg126, seg127, seg136, seg137, and seg138.
|
||||
- Resolve the remaining role of the shared active-dispatch hold token versus local per-entry hold bytes.
|
||||
- Recover the higher-level meaning of the file-backed seg126 control stream without speculating beyond verified byte behaviors.
|
||||
- Classify the exact UI role of the paired `0x8c5c` / `0x8c60` text-renderer lane if stronger caller evidence appears.
|
||||
- Finish the fade-controller producer path so seg127 fade inputs are tied to higher-level transition states, not only local opcodes.
|
||||
- Classify `FUN_000d_938c`, `transition_preentry_release_resources`, and `entity_cleanup_resources_and_dispatch` by role once their shared-hold semantics are fully separated.
|
||||
- Remove the remaining overlap blockers in this lane, with `000c:db68` first.
|
||||
|
||||
### Boundary and Decompiler Gaps
|
||||
### 3. VM / USECODE / Scripting Lane
|
||||
|
||||
- Some high-caller targets still require conservative boundary repair or follow-up validation.
|
||||
- Certain functions still decompile poorly because of overlaps, thunk-heavy paths, or unresolved downstream targets.
|
||||
- `000e:ffb0` remains a notable animation/video-side blocker because of overlapping instructions.
|
||||
- Recover the upstream selector into `FUN_000d_ebe3` and map payload-shape handlers to real opcode dispatch.
|
||||
- Recover real caller roles for the dark mask wrappers `0005:2c35` and `0005:2c68`.
|
||||
- Keep separating owner-table-backed `0x39ca` rows from static dispatch-entry seed rows.
|
||||
- Finish classifying the seg069/070 helper behind `entity_vm_runtime_owner_resource_create`.
|
||||
- Broaden owner-loaded class/event validation beyond the first strong sample families.
|
||||
- Keep event-label mapping conservative: only promote ScummVM event names where binary behavior and slot reuse agree.
|
||||
- Mature the reversible script IR until it can represent raw headers, event rows, payload forms, and unresolved opcodes without information loss.
|
||||
- Continue extracting readable descriptor-family artifacts, but treat them as evidence aids rather than rename authority.
|
||||
|
||||
### Coverage Management Gap
|
||||
### 4. Cache / Allocator / Callback-Object Lane
|
||||
|
||||
- A first-pass normalized segment-by-segment coverage ledger now exists for all 145 NE segments.
|
||||
- The remaining gap is refinement rather than absence: most segments still need manual promotion from `None` to `Foothold` / `Partial` / `Deep` as coverage expands.
|
||||
- Finish classifying the object rooted at `0x4588` so the allocator finalize path and callback emissions can receive behaviorally meaningful names.
|
||||
- Tighten the role of `allocator_phase_finalize_pass` only where it intersects callback-object semantics or active runtime users.
|
||||
- Separate generic cache-manager mechanics from game-specific client behavior wherever caller evidence supports it.
|
||||
- Clarify remaining object-role names around tracked handles, dispatch-entry lifecycle helpers, and palette-backed state builders.
|
||||
- Keep `_ASS_StopAllSFX` and the resolved audio-import lane closed; do not treat it as an open blocker again.
|
||||
|
||||
### Deferred Data Work
|
||||
### 5. Rendering, Palette, Animation, And UI Support Lanes
|
||||
|
||||
- Non-CALLF far-pointer relocations still exist and will matter for deeper object/table recovery.
|
||||
- They are no longer the main blocker, but they remain a real second-pass problem.
|
||||
- Finish the remaining caller-side semantics for raw 0007 rendering helpers, seg049 controller dispatch, seg108 sprite/object helpers, and seg137/138 palette state builders.
|
||||
- Revisit `000e:ffb0` and adjacent 000e video/animation overlap only when it blocks active analysis or offers a strong isolated win.
|
||||
- Expand the palette/VGA helper family only where it clarifies higher-level behavior rather than duplicating low-level helper names.
|
||||
- Keep validating startup/display assumptions against raw 0007/0008/000d caller behavior instead of renaming isolated helpers in a vacuum.
|
||||
|
||||
## Current Best Assessment Of Remaining Work
|
||||
### 6. Boundary Repair And Function Hygiene
|
||||
|
||||
The project has solved most of the architectural uncertainty needed to keep going efficiently.
|
||||
The remaining effort is mainly a scaling problem:
|
||||
- Create or repair missing function objects in the highest-traffic unresolved ranges first.
|
||||
- Fix only overlaps that block live lanes or high-caller targets.
|
||||
- Preserve conservative naming for repaired functions until direct caller or data evidence justifies promotion.
|
||||
- Continue rejecting disproven ports or stale hypotheses instead of preserving them in live work queues.
|
||||
|
||||
- expand coverage across many more segments,
|
||||
- remove the last high-value boundary blockers,
|
||||
- convert structural names into subsystem names when evidence is strong enough,
|
||||
- and normalize progress tracking so the whole program can be managed deliberately.
|
||||
### 7. Data, Imports, And Resource-Format Coverage
|
||||
|
||||
- Work through the deferred non-CALLF far-pointer relocations when they become necessary for object/table recovery.
|
||||
- Expand coverage of weakly mapped resource/data loaders such as FLEX-derived descriptors, tables, caches, and per-shape data files.
|
||||
- Cross-check current data-structure assumptions against external references like ScummVM only as supporting evidence, not as rename authority.
|
||||
- Keep external import identities synchronized with verified import-table evidence.
|
||||
|
||||
In practical terms, this looks like a true mid-project state rather than an early exploratory state or a late polish state.
|
||||
### 8. Completion Criteria
|
||||
|
||||
## Implementation Priorities
|
||||
A reasonably complete decompilation state should mean:
|
||||
|
||||
### Priority 0: Coverage Ledger
|
||||
- most actively used subsystems are behaviorally named rather than only structurally named,
|
||||
- the major live blockers (`000c:db68`, `000e:ffb0`, hot missing function objects, dark VM selector path, `0x4588` object role) are either resolved or reduced to low-impact residuals,
|
||||
- the far-call hot list has very few meaningful unknowns left,
|
||||
- the ledger gives a credible whole-program view rather than a sparse seed set,
|
||||
- and the remaining gaps are mostly long-tail cleanup, low-traffic helpers, or data polish instead of core architecture uncertainty.
|
||||
|
||||
First pass completed: an executable-wide coverage ledger now exists for all 145 NE segments in `crusader_segment_coverage_ledger.csv`.
|
||||
## Priority Order
|
||||
|
||||
Next work under Priority 0:
|
||||
1. Startup/display transition lane
|
||||
2. VM / USECODE selector and loader lane
|
||||
3. Coverage-ledger refinement from already-verified notes
|
||||
4. High-value overlap repair (`000c:db68`, then `000e:ffb0` when justified)
|
||||
5. `0x4588` callback-object classification
|
||||
6. Broader segment sweeps and second-pass data/relocation work
|
||||
|
||||
1. Promote additional segments from `None` where notes already support a verified foothold.
|
||||
2. Normalize raw-address subsystem islands (notably the `000e:` parser/animation cluster) back onto exact NE segment rows.
|
||||
3. Keep the ledger updated together with `crusader_decompilation_notes.md` after each verified batch.
|
||||
## Evidence Anchors
|
||||
|
||||
Minimum columns:
|
||||
|
||||
| Column | Meaning |
|
||||
|---|---|
|
||||
| Segment | NE segment number |
|
||||
| Type | Code or data |
|
||||
| File offset | From the NE segment table |
|
||||
| Length | Segment length |
|
||||
| Coverage status | None, foothold, partial, deep |
|
||||
| Known subsystem | Best current classification |
|
||||
| Key named functions | Short summary only |
|
||||
| Blockers | Boundary, import, thunk, overlap, unknown object, etc. |
|
||||
| Notes source | Notes section or evidence anchor |
|
||||
|
||||
This is the most important missing artifact because it will make the percentage estimates maintainable.
|
||||
|
||||
### Priority 1: Finish The New Cache/Backend Cluster
|
||||
|
||||
Work the newest verified reset-path cluster to closure:
|
||||
|
||||
1. Trace more callers of `0009:b06b`.
|
||||
2. Trace more callers of `FUN_0009_a961`.
|
||||
3. Classify the object rooted at `0x4588`.
|
||||
4. Revisit `allocator_phase_finalize_pass` once the object role is clearer.
|
||||
|
||||
This is currently the best next analysis target because it closes a live cluster that already has fresh verified work around it.
|
||||
|
||||
### Priority 2: `ASYLUM.24` Resolved
|
||||
|
||||
`ASYLUM.DLL` was imported as a separate NE program in Ghidra and its export table is now verified as an `ASS_*` audio DLL, not the immortality/USECODE interpreter lane.
|
||||
|
||||
Resolved result:
|
||||
|
||||
- `ASYLUM.24` = `_ASS_StopAllSFX` at `1018:0681`
|
||||
- `runtime_cache_reset_sequence` therefore performs an audio stop before the cache/tracked-handle reset work
|
||||
- this import is not evidence for the immortality cheat path; the `0x410` toggle remains attributed to the interpreted `EUSECODE.FLX` lane rather than `ASYLUM.DLL`
|
||||
|
||||
### Priority 3: Continue Small-Batch Boundary Repair
|
||||
|
||||
Use the existing conservative repair approach for remaining high-value blockers.
|
||||
|
||||
Good candidates include:
|
||||
|
||||
- unresolved high-caller function objects,
|
||||
- ranges that still steal bytes from adjacent real bodies,
|
||||
- and overlaps that block decompilation of already-active subsystems.
|
||||
|
||||
### Priority 4: Finish Partial Subsystem Islands Before Expanding Broadly
|
||||
|
||||
Recommended order:
|
||||
|
||||
1. seg043 plus connected seg004 reset and dispatch paths
|
||||
2. 000e animation/video overlap at `000e:ffb0`
|
||||
3. 000c UI-listbox, mini-VM, and cursor-nav families
|
||||
4. Remaining structural 0007 and 0008 helper cohorts
|
||||
|
||||
The goal is to reduce the number of half-understood islands before starting broad segment sweeps.
|
||||
|
||||
### Priority 5: Broaden Coverage Across The Remaining Executable
|
||||
|
||||
Once the ledger exists and the current hot cluster is closed, broaden analysis segment by segment.
|
||||
|
||||
Preferred method:
|
||||
|
||||
1. Group segments by adjacency and call relationships.
|
||||
2. Identify entry points and hot callees first.
|
||||
3. Classify globals and tables next.
|
||||
4. Promote helper names only when supported by strong evidence.
|
||||
|
||||
## Recommended Tracking Model
|
||||
|
||||
Use these status values for segment coverage:
|
||||
|
||||
| Status | Meaning |
|
||||
|---|---|
|
||||
| None | No meaningful verified analysis yet |
|
||||
| Foothold | One or two verified entry points or helper names, but no subsystem picture |
|
||||
| Partial | Several verified names plus some globals/tables or object fields |
|
||||
| Deep | Coherent subsystem-level understanding with multiple verified related functions |
|
||||
|
||||
Use these status values for subsystem maturity:
|
||||
|
||||
| Status | Meaning |
|
||||
|---|---|
|
||||
| Unknown | Not enough evidence to classify |
|
||||
| Structural | Behavior is partly mapped but still generic |
|
||||
| Behavioral | Confident subsystem role is known |
|
||||
| Stable | Multiple connected functions and data objects support the classification |
|
||||
|
||||
## Suggested Immediate Work Queue
|
||||
|
||||
### Queue A: Highest Leverage
|
||||
|
||||
1. Expand the first-pass segment coverage ledger beyond the currently seeded segments.
|
||||
2. Trace `allocator_try_alloc_from_head_table`, `allocator_head_finalize_sweep`, and `allocator_phase_finalize_pass`.
|
||||
3. Identify `ASYLUM.24`.
|
||||
|
||||
### Queue B: Repair And Stabilize
|
||||
|
||||
1. Review remaining high-caller gap functions.
|
||||
2. Repair any still-blocking overlaps in small batches.
|
||||
3. Re-decompile repaired ranges and keep only evidence-backed names.
|
||||
|
||||
### Queue C: Broaden Carefully
|
||||
|
||||
1. Expand into adjacent segments connected to already-understood clusters.
|
||||
2. Avoid speculative naming.
|
||||
3. Update the notes and the coverage ledger together after each verified batch.
|
||||
|
||||
## Concrete Progress Interpretation
|
||||
|
||||
If a single number is needed, use 25%.
|
||||
|
||||
If a more honest dashboard is acceptable, use all three:
|
||||
|
||||
- 80% of top-100 hot targets processed
|
||||
- 25% overall behavioral decompilation progress
|
||||
- 10% to 15% segment spread with meaningful analysis
|
||||
|
||||
That combination best reflects the actual state of the project.
|
||||
|
||||
## Source Anchors
|
||||
|
||||
Primary sources for this file:
|
||||
Primary files backing this plan state:
|
||||
|
||||
- `crusader_segment_coverage_ledger.csv`
|
||||
- `crusader_decompilation_notes.md`
|
||||
- `crusader_ne_segments.csv`
|
||||
- `tier4_output.txt`
|
||||
- `tier5_output.txt`
|
||||
- repo memory progress summary
|
||||
- `docs/overview.md`
|
||||
- `docs/raw-porting-progress.md`
|
||||
- `docs/raw-0008-000c.md`
|
||||
- `docs/raw-000a-000d.md`
|
||||
- `docs/raw-000e.md`
|
||||
- `docs/far-call-targets.md`
|
||||
- `docs/usecode-roundtrip-ir.md`
|
||||
- `docs/scummvm-crusader-reference.md`
|
||||
|
||||
## Next Update Rule
|
||||
## Update Rule
|
||||
|
||||
Update this file when one of the following happens:
|
||||
|
||||
- the overall estimate changes materially,
|
||||
- a new subsystem reaches behavioral or stable status,
|
||||
- a major blocker such as `0x4588`, `allocator_phase_finalize_pass`, or `ASYLUM.24` is resolved,
|
||||
- or the segment coverage ledger is created and becomes the new primary progress source.
|
||||
- the headline estimate changes materially,
|
||||
- a live blocker is resolved,
|
||||
- a subsystem moves from structural to behavioral understanding,
|
||||
- a segment cluster is promoted materially in the ledger,
|
||||
- or the next resume point changes enough that the current handoff would mislead the next pass.
|
||||
|
||||
Keep the file short. Move detailed completed analysis into the appropriate file under `docs/` and leave only the current state, blockers, and forward path here.
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ to support the next decoding pass.
|
|||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
import pathlib
|
||||
import struct
|
||||
|
|
@ -61,6 +62,21 @@ class ExtractedChunk:
|
|||
class_parse_status: str | None = None
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ClassEventRow:
|
||||
entry_index: int
|
||||
object_index: int
|
||||
class_id: int
|
||||
class_name_hint: str
|
||||
slot: int
|
||||
event_name_hint: str | None
|
||||
raw_event_entry_word: int
|
||||
raw_code_offset: int
|
||||
derived_body_start: int | None
|
||||
derived_body_end: int | None
|
||||
derived_body_length: int | None
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class FlxTable:
|
||||
entry_count: int
|
||||
|
|
@ -69,6 +85,25 @@ class FlxTable:
|
|||
entries: list[CandidateEntry]
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class FamilyArtifactSpec:
|
||||
output_stem: str
|
||||
title: str
|
||||
labels: tuple[str, ...]
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class RepeatedFamilyRowExpectation:
|
||||
class_name: str
|
||||
slot: int
|
||||
raw_event_entry_word: int
|
||||
raw_code_offset: int
|
||||
derived_body_start: int
|
||||
derived_body_end: int
|
||||
derived_body_length: int
|
||||
repeated_template_status: str
|
||||
|
||||
|
||||
def read_u32_le(data: bytes, offset: int) -> int:
|
||||
return struct.unpack_from("<I", data, offset)[0]
|
||||
|
||||
|
|
@ -454,6 +489,73 @@ SCUMMVM_EVENT_NAME_HINTS: tuple[str, ...] = (
|
|||
)
|
||||
|
||||
|
||||
VERIFIED_REPEATED_TEMPLATE_FAMILIES: tuple[tuple[str, tuple[str, ...]], ...] = (
|
||||
("referent-anchor-twin", ("JELYHACK", "JELYH2")),
|
||||
("boot-event-core", ("AND_BOOT", "BRO_BOOT", "COR_BOOT", "REE_BOOT", "VAR_BOOT")),
|
||||
("callback-eventtrigger", ("SURCAMNS", "SURCAMEW")),
|
||||
("environmental-event", ("FLAMEBOX", "NOSTRIL", "STEAMBOX")),
|
||||
)
|
||||
|
||||
|
||||
FAMILY_ARTIFACT_SPECS: tuple[FamilyArtifactSpec, ...] = (
|
||||
FamilyArtifactSpec(
|
||||
output_stem="boot_family_decompile",
|
||||
title="_BOOT Family Decompiled Event Sketches",
|
||||
labels=("AND_BOOT", "BRO_BOOT", "COR_BOOT", "REE_BOOT", "VAR_BOOT"),
|
||||
),
|
||||
FamilyArtifactSpec(
|
||||
output_stem="callback_family_decompile",
|
||||
title="SURCAM Callback Family Decompiled Event Sketches",
|
||||
labels=("SURCAMNS", "SURCAMEW"),
|
||||
),
|
||||
FamilyArtifactSpec(
|
||||
output_stem="environmental_family_decompile",
|
||||
title="Environmental Family Decompiled Event Sketches",
|
||||
labels=("FLAMEBOX", "NOSTRIL", "STEAMBOX"),
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
VERIFIED_REPEATED_FAMILY_ROW_EXPECTATIONS: tuple[RepeatedFamilyRowExpectation, ...] = (
|
||||
RepeatedFamilyRowExpectation("JELYHACK", 0x01, 0x002A, 0x00000001, 0x00D4, 0x00FE, 42, "referent-anchor-twin/shared-slot-0x01/same-length-template"),
|
||||
RepeatedFamilyRowExpectation("JELYH2", 0x01, 0x002A, 0x00000001, 0x00D4, 0x00FE, 42, "referent-anchor-twin/shared-slot-0x01/same-length-template"),
|
||||
RepeatedFamilyRowExpectation("AND_BOOT", 0x0A, 0x0253, 0x00000001, 0x00D4, 0x0327, 595, "boot-event-core/shared-slot-0x0A/shared-slot-template"),
|
||||
RepeatedFamilyRowExpectation("AND_BOOT", 0x0F, 0x0237, 0x00000254, 0x0327, 0x055E, 567, "boot-event-core/shared-slot-0x0F/shared-slot-template"),
|
||||
RepeatedFamilyRowExpectation("AND_BOOT", 0x10, 0x003B, 0x0000048B, 0x055E, 0x0599, 59, "boot-event-core/shared-slot-0x10/same-length-template"),
|
||||
RepeatedFamilyRowExpectation("BRO_BOOT", 0x0A, 0x02D5, 0x00000001, 0x00D4, 0x03A9, 725, "boot-event-core/shared-slot-0x0A/shared-slot-template"),
|
||||
RepeatedFamilyRowExpectation("BRO_BOOT", 0x0F, 0x024C, 0x000002D6, 0x03A9, 0x05F5, 588, "boot-event-core/shared-slot-0x0F/shared-slot-template"),
|
||||
RepeatedFamilyRowExpectation("BRO_BOOT", 0x10, 0x003B, 0x00000522, 0x05F5, 0x0630, 59, "boot-event-core/shared-slot-0x10/same-length-template"),
|
||||
RepeatedFamilyRowExpectation("COR_BOOT", 0x0A, 0x0227, 0x00000001, 0x00D4, 0x02FB, 551, "boot-event-core/shared-slot-0x0A/shared-slot-template"),
|
||||
RepeatedFamilyRowExpectation("COR_BOOT", 0x0F, 0x0234, 0x00000228, 0x02FB, 0x052F, 564, "boot-event-core/shared-slot-0x0F/shared-slot-template"),
|
||||
RepeatedFamilyRowExpectation("COR_BOOT", 0x10, 0x003B, 0x0000045C, 0x052F, 0x056A, 59, "boot-event-core/shared-slot-0x10/same-length-template"),
|
||||
RepeatedFamilyRowExpectation("REE_BOOT", 0x0A, 0x034B, 0x00000001, 0x00D4, 0x041F, 843, "boot-event-core/shared-slot-0x0A/shared-slot-template"),
|
||||
RepeatedFamilyRowExpectation("REE_BOOT", 0x0F, 0x025C, 0x0000034C, 0x041F, 0x067B, 604, "boot-event-core/shared-slot-0x0F/shared-slot-template"),
|
||||
RepeatedFamilyRowExpectation("REE_BOOT", 0x10, 0x003B, 0x000005A8, 0x067B, 0x06B6, 59, "boot-event-core/shared-slot-0x10/same-length-template"),
|
||||
RepeatedFamilyRowExpectation("VAR_BOOT", 0x0A, 0x029A, 0x00000001, 0x00D4, 0x036E, 666, "boot-event-core/shared-slot-0x0A/shared-slot-template"),
|
||||
RepeatedFamilyRowExpectation("VAR_BOOT", 0x0F, 0x0244, 0x0000029B, 0x036E, 0x05B2, 580, "boot-event-core/shared-slot-0x0F/shared-slot-template"),
|
||||
RepeatedFamilyRowExpectation("VAR_BOOT", 0x10, 0x003B, 0x000004DF, 0x05B2, 0x05ED, 59, "boot-event-core/shared-slot-0x10/same-length-template"),
|
||||
RepeatedFamilyRowExpectation("SURCAMNS", 0x01, 0x0051, 0x000000D2, 0x01B7, 0x0208, 81, "callback-eventtrigger/shared-slot-0x01/shared-slot-template"),
|
||||
RepeatedFamilyRowExpectation("SURCAMNS", 0x0A, 0x00D1, 0x00000001, 0x00E6, 0x01B7, 209, "callback-eventtrigger/shared-slot-0x0A/same-length-template"),
|
||||
RepeatedFamilyRowExpectation("SURCAMNS", 0x20, 0x02BA, 0x00000123, 0x0208, 0x04C2, 698, "callback-eventtrigger/shared-slot-0x20/same-length-template"),
|
||||
RepeatedFamilyRowExpectation("SURCAMNS", 0x21, 0x0709, 0x000003DD, 0x04C2, 0x0BCB, 1801, "callback-eventtrigger/shared-slot-0x21/shared-slot-template"),
|
||||
RepeatedFamilyRowExpectation("SURCAMNS", 0x22, 0x01A3, 0x00000AE6, 0x0BCB, 0x0D6E, 419, "callback-eventtrigger/shared-slot-0x22/same-length-template"),
|
||||
RepeatedFamilyRowExpectation("SURCAMEW", 0x01, 0x00F7, 0x000000D2, 0x01B7, 0x02AE, 247, "callback-eventtrigger/shared-slot-0x01/shared-slot-template"),
|
||||
RepeatedFamilyRowExpectation("SURCAMEW", 0x0A, 0x00D1, 0x00000001, 0x00E6, 0x01B7, 209, "callback-eventtrigger/shared-slot-0x0A/same-length-template"),
|
||||
RepeatedFamilyRowExpectation("SURCAMEW", 0x20, 0x02BA, 0x000001C9, 0x02AE, 0x0568, 698, "callback-eventtrigger/shared-slot-0x20/same-length-template"),
|
||||
RepeatedFamilyRowExpectation("SURCAMEW", 0x21, 0x0655, 0x00000483, 0x0568, 0x0BBD, 1621, "callback-eventtrigger/shared-slot-0x21/shared-slot-template"),
|
||||
RepeatedFamilyRowExpectation("SURCAMEW", 0x22, 0x01A3, 0x00000AD8, 0x0BBD, 0x0D60, 419, "callback-eventtrigger/shared-slot-0x22/same-length-template"),
|
||||
RepeatedFamilyRowExpectation("FLAMEBOX", 0x0A, 0x026A, 0x00000001, 0x00E0, 0x034A, 618, "environmental-event/shared-slot-0x0A/shared-slot-template"),
|
||||
RepeatedFamilyRowExpectation("FLAMEBOX", 0x20, 0x01AC, 0x0000026B, 0x034A, 0x04F6, 428, "environmental-event/shared-slot-0x20/shared-slot-template"),
|
||||
RepeatedFamilyRowExpectation("FLAMEBOX", 0x21, 0x029A, 0x00000417, 0x04F6, 0x0790, 666, "environmental-event/shared-slot-0x21/shared-slot-template"),
|
||||
RepeatedFamilyRowExpectation("NOSTRIL", 0x0A, 0x00C0, 0x00000001, 0x00E0, 0x01A0, 192, "environmental-event/shared-slot-0x0A/shared-slot-template"),
|
||||
RepeatedFamilyRowExpectation("NOSTRIL", 0x20, 0x0129, 0x000000C1, 0x01A0, 0x02C9, 297, "environmental-event/shared-slot-0x20/shared-slot-template"),
|
||||
RepeatedFamilyRowExpectation("NOSTRIL", 0x21, 0x01BE, 0x000001EA, 0x02C9, 0x0487, 446, "environmental-event/shared-slot-0x21/shared-slot-template"),
|
||||
RepeatedFamilyRowExpectation("STEAMBOX", 0x0A, 0x0266, 0x00000001, 0x00E0, 0x0346, 614, "environmental-event/shared-slot-0x0A/shared-slot-template"),
|
||||
RepeatedFamilyRowExpectation("STEAMBOX", 0x20, 0x01F6, 0x00000267, 0x0346, 0x053C, 502, "environmental-event/shared-slot-0x20/shared-slot-template"),
|
||||
RepeatedFamilyRowExpectation("STEAMBOX", 0x21, 0x02A7, 0x0000045D, 0x053C, 0x07E3, 679, "environmental-event/shared-slot-0x21/shared-slot-template"),
|
||||
)
|
||||
|
||||
|
||||
def scummvm_event_name_hint(slot: int) -> str | None:
|
||||
if 0 <= slot < len(SCUMMVM_EVENT_NAME_HINTS):
|
||||
return SCUMMVM_EVENT_NAME_HINTS[slot]
|
||||
|
|
@ -532,6 +634,368 @@ def annotate_class_layout(chunks: list[ExtractedChunk]) -> None:
|
|||
chunk.class_parse_status = "parsed-class-layout"
|
||||
|
||||
|
||||
def derive_class_event_rows(chunk: ExtractedChunk, raw_data: bytes) -> list[ClassEventRow]:
|
||||
if chunk.class_parse_status != "parsed-class-layout":
|
||||
return []
|
||||
if chunk.object_index is None or chunk.class_id is None or chunk.conservative_event_count is None:
|
||||
return []
|
||||
|
||||
provisional_rows: list[tuple[int, int, int]] = []
|
||||
for slot in range(chunk.conservative_event_count):
|
||||
entry_offset = 20 + 6 * slot
|
||||
raw_word = read_u16_le(raw_data, entry_offset)
|
||||
raw_code_offset = read_u32_le(raw_data, entry_offset + 2)
|
||||
provisional_rows.append((slot, raw_word, raw_code_offset))
|
||||
|
||||
non_zero_offsets = sorted(
|
||||
{
|
||||
raw_code_offset
|
||||
for _, _, raw_code_offset in provisional_rows
|
||||
if raw_code_offset != 0
|
||||
}
|
||||
)
|
||||
rows: list[ClassEventRow] = []
|
||||
|
||||
for slot, raw_word, raw_code_offset in provisional_rows:
|
||||
derived_body_start: int | None = None
|
||||
derived_body_end: int | None = None
|
||||
derived_body_length: int | None = None
|
||||
|
||||
if raw_code_offset != 0 and chunk.code_base_minus_one is not None:
|
||||
body_start = chunk.code_base_minus_one + raw_code_offset
|
||||
next_offsets = [offset for offset in non_zero_offsets if offset > raw_code_offset]
|
||||
body_end = chunk.code_base_minus_one + next_offsets[0] if next_offsets else len(raw_data)
|
||||
if 0 <= body_start <= body_end <= len(raw_data):
|
||||
derived_body_start = body_start
|
||||
derived_body_end = body_end
|
||||
derived_body_length = body_end - body_start
|
||||
|
||||
rows.append(
|
||||
ClassEventRow(
|
||||
entry_index=chunk.index,
|
||||
object_index=chunk.object_index,
|
||||
class_id=chunk.class_id,
|
||||
class_name_hint=chunk.class_name_hint or "",
|
||||
slot=slot,
|
||||
event_name_hint=scummvm_event_name_hint(slot),
|
||||
raw_event_entry_word=raw_word,
|
||||
raw_code_offset=raw_code_offset,
|
||||
derived_body_start=derived_body_start,
|
||||
derived_body_end=derived_body_end,
|
||||
derived_body_length=derived_body_length,
|
||||
)
|
||||
)
|
||||
|
||||
return rows
|
||||
|
||||
|
||||
def build_class_event_rows(
|
||||
parsed_class_chunks: list[ExtractedChunk],
|
||||
) -> tuple[list[ClassEventRow], dict[int, list[ClassEventRow]], dict[int, bytes]]:
|
||||
all_rows: list[ClassEventRow] = []
|
||||
rows_by_entry: dict[int, list[ClassEventRow]] = {}
|
||||
raw_data_by_entry: dict[int, bytes] = {}
|
||||
|
||||
for chunk in parsed_class_chunks:
|
||||
raw_data = pathlib.Path(chunk.raw_path).read_bytes()
|
||||
raw_data_by_entry[chunk.index] = raw_data
|
||||
rows = derive_class_event_rows(chunk, raw_data)
|
||||
rows_by_entry[chunk.index] = rows
|
||||
all_rows.extend(rows)
|
||||
|
||||
return all_rows, rows_by_entry, raw_data_by_entry
|
||||
|
||||
|
||||
def build_repeated_template_status_map(
|
||||
parsed_class_chunks: list[ExtractedChunk],
|
||||
rows_by_entry: dict[int, list[ClassEventRow]],
|
||||
raw_data_by_entry: dict[int, bytes],
|
||||
) -> dict[tuple[int, int], str]:
|
||||
status_by_row: dict[tuple[int, int], str] = {}
|
||||
chunk_by_label = {
|
||||
chunk.primary_label: chunk
|
||||
for chunk in parsed_class_chunks
|
||||
if chunk.primary_label
|
||||
}
|
||||
|
||||
for family_name, labels in VERIFIED_REPEATED_TEMPLATE_FAMILIES:
|
||||
family_chunks = [chunk_by_label[label] for label in labels if label in chunk_by_label]
|
||||
if len(family_chunks) < 2:
|
||||
continue
|
||||
|
||||
rows_by_slot: dict[int, list[tuple[ExtractedChunk, ClassEventRow, bytes]]] = {}
|
||||
for chunk in family_chunks:
|
||||
raw_data = raw_data_by_entry.get(chunk.index)
|
||||
if raw_data is None:
|
||||
continue
|
||||
for row in rows_by_entry.get(chunk.index, []):
|
||||
if row.raw_code_offset == 0:
|
||||
continue
|
||||
if row.derived_body_start is None or row.derived_body_end is None:
|
||||
continue
|
||||
body = raw_data[row.derived_body_start:row.derived_body_end]
|
||||
rows_by_slot.setdefault(row.slot, []).append((chunk, row, body))
|
||||
|
||||
for slot, slot_rows in rows_by_slot.items():
|
||||
if len(slot_rows) < 2:
|
||||
continue
|
||||
|
||||
lengths = {len(body) for _, _, body in slot_rows}
|
||||
bodies = {body for _, _, body in slot_rows}
|
||||
if len(bodies) == 1:
|
||||
status_suffix = "exact-body-clone"
|
||||
elif len(lengths) == 1:
|
||||
status_suffix = "same-length-template"
|
||||
else:
|
||||
status_suffix = "shared-slot-template"
|
||||
|
||||
status = f"{family_name}/shared-slot-0x{slot:02X}/{status_suffix}"
|
||||
for chunk, row, _ in slot_rows:
|
||||
status_by_row[(chunk.index, row.slot)] = status
|
||||
|
||||
return status_by_row
|
||||
|
||||
|
||||
def format_optional_hex(value: int | None, width: int = 0) -> str:
|
||||
if value is None:
|
||||
return ""
|
||||
if width > 0:
|
||||
return f"0x{value:0{width}X}"
|
||||
return f"0x{value:X}"
|
||||
|
||||
|
||||
def hex_edge(data: bytes, width: int = 8) -> str:
|
||||
if not data:
|
||||
return ""
|
||||
return data[:width].hex()
|
||||
|
||||
|
||||
def hex_tail(data: bytes, width: int = 8) -> str:
|
||||
if not data:
|
||||
return ""
|
||||
return data[-width:].hex()
|
||||
|
||||
|
||||
def write_family_decompile_artifact(
|
||||
out_dir: pathlib.Path,
|
||||
parsed_class_chunks: list[ExtractedChunk],
|
||||
rows_by_entry: dict[int, list[ClassEventRow]],
|
||||
raw_data_by_entry: dict[int, bytes],
|
||||
repeated_status_by_row: dict[tuple[int, int], str],
|
||||
spec: FamilyArtifactSpec,
|
||||
) -> None:
|
||||
family_labels = set(spec.labels)
|
||||
family_chunks = [chunk for chunk in parsed_class_chunks if chunk.primary_label in family_labels]
|
||||
if not family_chunks:
|
||||
return
|
||||
|
||||
family_chunks.sort(key=lambda chunk: chunk.primary_label or "")
|
||||
|
||||
tsv_lines = [
|
||||
"entry_index\tclass_id\tclass_name\tslot\tevent_name_hint\traw_event_entry_word\traw_code_offset\tderived_body_start\tderived_body_end\tderived_body_length\trepeated_template_status\tbody_sha1\tbody_prefix_hex\tbody_suffix_hex"
|
||||
]
|
||||
md_lines = [
|
||||
f"# {spec.title}",
|
||||
"",
|
||||
"This is a reversible per-class rendering derived directly from `class_event_index.tsv` plus the raw extracted chunk bytes.",
|
||||
"ScummVM event labels remain hints only; the authoritative data here is the slot id, raw row bytes, and derived body window.",
|
||||
"",
|
||||
]
|
||||
|
||||
for chunk in family_chunks:
|
||||
rows = [row for row in rows_by_entry.get(chunk.index, []) if row.raw_code_offset != 0]
|
||||
if not rows:
|
||||
continue
|
||||
raw_data = raw_data_by_entry[chunk.index]
|
||||
|
||||
md_lines.extend([
|
||||
f"## {chunk.primary_label}",
|
||||
"",
|
||||
"```yaml",
|
||||
"class:",
|
||||
f" entry_index: 0x{chunk.index:03X}",
|
||||
f" class_id: 0x{chunk.class_id:X}",
|
||||
f" class_name: {chunk.primary_label}",
|
||||
f" class_object_index: 0x{chunk.object_index:X}",
|
||||
f" raw_code_base_u32: 0x{chunk.raw_code_base_u32:X}",
|
||||
f" code_base_minus_one: 0x{chunk.code_base_minus_one:X}",
|
||||
f" conservative_event_count: {chunk.conservative_event_count}",
|
||||
" events:",
|
||||
])
|
||||
|
||||
for row in rows:
|
||||
body = b""
|
||||
if row.derived_body_start is not None and row.derived_body_end is not None:
|
||||
body = raw_data[row.derived_body_start:row.derived_body_end]
|
||||
repeated_status = repeated_status_by_row.get((row.entry_index, row.slot), "")
|
||||
body_sha1 = hashlib.sha1(body).hexdigest() if body else ""
|
||||
|
||||
md_lines.extend([
|
||||
f" - slot: 0x{row.slot:02x}",
|
||||
f" event_name_hint: {row.event_name_hint or ''}",
|
||||
f" raw_event_entry_word: 0x{row.raw_event_entry_word:04x}",
|
||||
f" raw_code_offset: 0x{row.raw_code_offset:08x}",
|
||||
f" derived_body_start: {format_optional_hex(row.derived_body_start, 4).lower() or 'null'}",
|
||||
f" derived_body_end: {format_optional_hex(row.derived_body_end, 4).lower() or 'null'}",
|
||||
f" derived_body_length: {row.derived_body_length if row.derived_body_length is not None else 'null'}",
|
||||
f" repeated_template_status: {repeated_status or 'unique-or-unclassified'}",
|
||||
f" body_sha1: {body_sha1 or 'null'}",
|
||||
f" body_prefix_hex: {hex_edge(body) or 'null'}",
|
||||
f" body_suffix_hex: {hex_tail(body) or 'null'}",
|
||||
])
|
||||
|
||||
tsv_lines.append(
|
||||
"{entry_index}\t0x{class_id:X}\t{class_name}\t0x{slot:02X}\t{event_name_hint}\t0x{raw_event_entry_word:04X}\t0x{raw_code_offset:08X}\t{derived_body_start}\t{derived_body_end}\t{derived_body_length}\t{repeated_template_status}\t{body_sha1}\t{body_prefix_hex}\t{body_suffix_hex}".format(
|
||||
entry_index=row.entry_index,
|
||||
class_id=row.class_id,
|
||||
class_name=chunk.primary_label or "",
|
||||
slot=row.slot,
|
||||
event_name_hint=row.event_name_hint or "",
|
||||
raw_event_entry_word=row.raw_event_entry_word,
|
||||
raw_code_offset=row.raw_code_offset,
|
||||
derived_body_start=format_optional_hex(row.derived_body_start, 4),
|
||||
derived_body_end=format_optional_hex(row.derived_body_end, 4),
|
||||
derived_body_length=(row.derived_body_length if row.derived_body_length is not None else ""),
|
||||
repeated_template_status=repeated_status,
|
||||
body_sha1=body_sha1,
|
||||
body_prefix_hex=hex_edge(body),
|
||||
body_suffix_hex=hex_tail(body),
|
||||
)
|
||||
)
|
||||
|
||||
md_lines.extend([
|
||||
"```",
|
||||
"",
|
||||
])
|
||||
|
||||
(out_dir / f"{spec.output_stem}.md").write_text("\n".join(md_lines), encoding="utf-8")
|
||||
(out_dir / f"{spec.output_stem}.tsv").write_text("\n".join(tsv_lines) + "\n", encoding="utf-8")
|
||||
|
||||
|
||||
def validate_verified_repeated_family_regressions(
|
||||
parsed_class_chunks: list[ExtractedChunk],
|
||||
rows_by_entry: dict[int, list[ClassEventRow]],
|
||||
repeated_status_by_row: dict[tuple[int, int], str],
|
||||
) -> list[str]:
|
||||
chunk_by_label = {
|
||||
chunk.primary_label: chunk
|
||||
for chunk in parsed_class_chunks
|
||||
if chunk.primary_label
|
||||
}
|
||||
expected_slots_by_class: dict[str, set[int]] = {}
|
||||
for expectation in VERIFIED_REPEATED_FAMILY_ROW_EXPECTATIONS:
|
||||
expected_slots_by_class.setdefault(expectation.class_name, set()).add(expectation.slot)
|
||||
|
||||
report_lines = [
|
||||
"record_type\tclass_name\tslot\texpected\tactual\tstatus"
|
||||
]
|
||||
errors: list[str] = []
|
||||
|
||||
for class_name, expected_slots in sorted(expected_slots_by_class.items()):
|
||||
chunk = chunk_by_label.get(class_name)
|
||||
actual_slots: set[int] = set()
|
||||
if chunk is not None:
|
||||
actual_slots = {
|
||||
row.slot
|
||||
for row in rows_by_entry.get(chunk.index, [])
|
||||
if row.raw_code_offset != 0
|
||||
}
|
||||
status = "ok" if actual_slots == expected_slots else "mismatch"
|
||||
report_lines.append(
|
||||
"slot-set\t{class_name}\t*\t{expected}\t{actual}\t{status}".format(
|
||||
class_name=class_name,
|
||||
expected=",".join(f"0x{slot:02X}" for slot in sorted(expected_slots)),
|
||||
actual=",".join(f"0x{slot:02X}" for slot in sorted(actual_slots)),
|
||||
status=status,
|
||||
)
|
||||
)
|
||||
if status != "ok":
|
||||
errors.append(
|
||||
f"{class_name}: expected non-zero slots {sorted(expected_slots)}, found {sorted(actual_slots)}"
|
||||
)
|
||||
|
||||
for expectation in VERIFIED_REPEATED_FAMILY_ROW_EXPECTATIONS:
|
||||
chunk = chunk_by_label.get(expectation.class_name)
|
||||
if chunk is None:
|
||||
errors.append(f"missing repeated-family class {expectation.class_name}")
|
||||
report_lines.append(
|
||||
f"row\t{expectation.class_name}\t0x{expectation.slot:02X}\tpresent\tmissing-class\tmismatch"
|
||||
)
|
||||
continue
|
||||
|
||||
row = next(
|
||||
(candidate for candidate in rows_by_entry.get(chunk.index, []) if candidate.slot == expectation.slot),
|
||||
None,
|
||||
)
|
||||
if row is None:
|
||||
errors.append(f"missing row {expectation.class_name} slot 0x{expectation.slot:02X}")
|
||||
report_lines.append(
|
||||
f"row\t{expectation.class_name}\t0x{expectation.slot:02X}\tpresent\tmissing-row\tmismatch"
|
||||
)
|
||||
continue
|
||||
|
||||
actual_values = (
|
||||
row.raw_event_entry_word,
|
||||
row.raw_code_offset,
|
||||
row.derived_body_start,
|
||||
row.derived_body_end,
|
||||
row.derived_body_length,
|
||||
repeated_status_by_row.get((row.entry_index, row.slot), ""),
|
||||
)
|
||||
expected_values = (
|
||||
expectation.raw_event_entry_word,
|
||||
expectation.raw_code_offset,
|
||||
expectation.derived_body_start,
|
||||
expectation.derived_body_end,
|
||||
expectation.derived_body_length,
|
||||
expectation.repeated_template_status,
|
||||
)
|
||||
status = "ok" if actual_values == expected_values else "mismatch"
|
||||
report_lines.append(
|
||||
"row\t{class_name}\t0x{slot:02X}\t{expected}\t{actual}\t{status}".format(
|
||||
class_name=expectation.class_name,
|
||||
slot=expectation.slot,
|
||||
expected="|".join(
|
||||
[
|
||||
f"0x{expectation.raw_event_entry_word:04X}",
|
||||
f"0x{expectation.raw_code_offset:08X}",
|
||||
f"0x{expectation.derived_body_start:04X}",
|
||||
f"0x{expectation.derived_body_end:04X}",
|
||||
str(expectation.derived_body_length),
|
||||
expectation.repeated_template_status,
|
||||
]
|
||||
),
|
||||
actual="|".join(
|
||||
[
|
||||
f"0x{row.raw_event_entry_word:04X}",
|
||||
f"0x{row.raw_code_offset:08X}",
|
||||
format_optional_hex(row.derived_body_start, 4),
|
||||
format_optional_hex(row.derived_body_end, 4),
|
||||
str(row.derived_body_length if row.derived_body_length is not None else ""),
|
||||
repeated_status_by_row.get((row.entry_index, row.slot), ""),
|
||||
]
|
||||
),
|
||||
status=status,
|
||||
)
|
||||
)
|
||||
if status != "ok":
|
||||
errors.append(
|
||||
"{class_name} slot 0x{slot:02X}: expected {expected}, found {actual}".format(
|
||||
class_name=expectation.class_name,
|
||||
slot=expectation.slot,
|
||||
expected=expected_values,
|
||||
actual=actual_values,
|
||||
)
|
||||
)
|
||||
|
||||
if errors:
|
||||
raise ValueError(
|
||||
"repeated-family regression mismatch:\n- " + "\n- ".join(errors)
|
||||
)
|
||||
|
||||
return report_lines
|
||||
|
||||
|
||||
def readable_neighbor_chunks(
|
||||
center: ExtractedChunk,
|
||||
chunk_by_index: dict[int, ExtractedChunk],
|
||||
|
|
@ -1556,6 +2020,17 @@ def write_summary(out_dir: pathlib.Path, input_path: pathlib.Path, data: bytes,
|
|||
"entry_index\tobject_index\tclass_id\tclass_name_hint\traw_code_base_u32\tcode_base_minus_one\tconservative_event_count\tevent_table_end\tclass_parse_status\tdata_offset\tdeclared_size\tprimary_label"
|
||||
]
|
||||
parsed_class_chunks = [chunk for chunk in chunks if chunk.class_parse_status == "parsed-class-layout"]
|
||||
class_event_rows, rows_by_entry, raw_data_by_entry = build_class_event_rows(parsed_class_chunks)
|
||||
repeated_status_by_row = build_repeated_template_status_map(
|
||||
parsed_class_chunks,
|
||||
rows_by_entry,
|
||||
raw_data_by_entry,
|
||||
)
|
||||
repeated_family_regression_lines = validate_verified_repeated_family_regressions(
|
||||
parsed_class_chunks,
|
||||
rows_by_entry,
|
||||
repeated_status_by_row,
|
||||
)
|
||||
for chunk in parsed_class_chunks:
|
||||
class_layout_lines.append(
|
||||
"{index}\t0x{object_index:X}\t0x{class_id:X}\t{class_name_hint}\t0x{raw_code_base_u32:X}\t0x{code_base_minus_one:X}\t{conservative_event_count}\t0x{event_table_end:X}\t{class_parse_status}\t0x{data_offset:X}\t0x{declared_size:X}\t{primary_label}".format(
|
||||
|
|
@ -1576,28 +2051,39 @@ def write_summary(out_dir: pathlib.Path, input_path: pathlib.Path, data: bytes,
|
|||
(out_dir / "class_layout_index.tsv").write_text("\n".join(class_layout_lines) + "\n", encoding="utf-8")
|
||||
|
||||
class_event_lines = [
|
||||
"entry_index\tobject_index\tclass_id\tclass_name_hint\tslot\tevent_name_hint\traw_event_entry_word\traw_code_offset"
|
||||
"entry_index\tobject_index\tclass_id\tclass_name_hint\tslot\tevent_name_hint\traw_event_entry_word\traw_code_offset\tderived_body_start\tderived_body_end\tderived_body_length\trepeated_template_status"
|
||||
]
|
||||
for chunk in parsed_class_chunks:
|
||||
raw_data = pathlib.Path(chunk.raw_path).read_bytes()
|
||||
assert chunk.conservative_event_count is not None
|
||||
for slot in range(chunk.conservative_event_count):
|
||||
entry_offset = 20 + 6 * slot
|
||||
raw_word = read_u16_le(raw_data, entry_offset)
|
||||
raw_code_offset = read_u32_le(raw_data, entry_offset + 2)
|
||||
class_event_lines.append(
|
||||
"{entry_index}\t0x{object_index:X}\t0x{class_id:X}\t{class_name_hint}\t0x{slot:02X}\t{event_name_hint}\t0x{raw_word:04X}\t0x{raw_code_offset:08X}".format(
|
||||
entry_index=chunk.index,
|
||||
object_index=chunk.object_index,
|
||||
class_id=chunk.class_id,
|
||||
class_name_hint=chunk.class_name_hint or "",
|
||||
slot=slot,
|
||||
event_name_hint=scummvm_event_name_hint(slot) or "",
|
||||
raw_word=raw_word,
|
||||
raw_code_offset=raw_code_offset,
|
||||
)
|
||||
for row in class_event_rows:
|
||||
class_event_lines.append(
|
||||
"{entry_index}\t0x{object_index:X}\t0x{class_id:X}\t{class_name_hint}\t0x{slot:02X}\t{event_name_hint}\t0x{raw_event_entry_word:04X}\t0x{raw_code_offset:08X}\t{derived_body_start}\t{derived_body_end}\t{derived_body_length}\t{repeated_template_status}".format(
|
||||
entry_index=row.entry_index,
|
||||
object_index=row.object_index,
|
||||
class_id=row.class_id,
|
||||
class_name_hint=row.class_name_hint,
|
||||
slot=row.slot,
|
||||
event_name_hint=row.event_name_hint or "",
|
||||
raw_event_entry_word=row.raw_event_entry_word,
|
||||
raw_code_offset=row.raw_code_offset,
|
||||
derived_body_start=format_optional_hex(row.derived_body_start, 4),
|
||||
derived_body_end=format_optional_hex(row.derived_body_end, 4),
|
||||
derived_body_length=(row.derived_body_length if row.derived_body_length is not None else ""),
|
||||
repeated_template_status=repeated_status_by_row.get((row.entry_index, row.slot), ""),
|
||||
)
|
||||
)
|
||||
(out_dir / "class_event_index.tsv").write_text("\n".join(class_event_lines) + "\n", encoding="utf-8")
|
||||
for family_artifact_spec in FAMILY_ARTIFACT_SPECS:
|
||||
write_family_decompile_artifact(
|
||||
out_dir,
|
||||
parsed_class_chunks,
|
||||
rows_by_entry,
|
||||
raw_data_by_entry,
|
||||
repeated_status_by_row,
|
||||
family_artifact_spec,
|
||||
)
|
||||
(out_dir / "repeated_family_regressions.tsv").write_text(
|
||||
"\n".join(repeated_family_regression_lines) + "\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
neighborhood_lines = [
|
||||
"center_index\tneighbor_index\tprimary_label\tfield_names\tfield_tags"
|
||||
|
|
@ -1763,7 +2249,9 @@ def write_summary(out_dir: pathlib.Path, input_path: pathlib.Path, data: bytes,
|
|||
lines.append("- `.strings.txt` files are the main human-readable output for now; `.txt` files are emitted only for chunks that look text-like.")
|
||||
lines.append("- `descriptor_index.tsv` summarizes guessed class labels, field names, and compact tag patterns for descriptor-like chunks.")
|
||||
lines.append("- `class_layout_index.tsv` records the conservative owner-loaded class parsing state: object index, class id, class-name hint, raw bytes-8..11 field, derived code-base-minus-one, and event-count/table-end values when the local divisibility and bounds checks succeed.")
|
||||
lines.append("- `class_event_index.tsv` expands parsed owner-loaded classes into raw 6-byte event rows with slot numbers, ScummVM event-name hints for `0x00..0x1f`, unresolved leading words, and raw code-offset dwords for round-trip tooling work.")
|
||||
lines.append("- `class_event_index.tsv` now also emits derived body-window columns (`derived_body_start`, `derived_body_end`, `derived_body_length`) plus conservative `repeated_template_status` tags for verified repeated families.")
|
||||
lines.append("- `boot_family_decompile.md` / `.tsv`, `callback_family_decompile.md` / `.tsv`, and `environmental_family_decompile.md` / `.tsv` now provide reversible per-class decompile artifacts for the `_BOOT`, `SURCAM*`, and environmental repeated-family lanes.")
|
||||
lines.append("- `repeated_family_regressions.tsv` enforces the current repeated-family slot sets plus the verified raw-row and derived body-window fields for `JELYHACK/JELYH2`, `_BOOT`, `SURCAM*`, and `FLAMEBOX/NOSTRIL/STEAMBOX`.")
|
||||
lines.append("- `descriptor_neighborhoods.tsv` captures local table neighborhoods around trigger/event-related classes such as `JELYHACK`, `NPCTRIG`, `CRUZTRIG`, `TRIGPAD`, and `SPECIAL`.")
|
||||
lines.append("- `referent_anchor_event_graph.tsv` groups referent-bearing descriptors with nearby event-bearing neighbors so the attachment model can be inspected without ad hoc grepping.")
|
||||
lines.append("- `jelyhack_island_graph.md` now uses a wider local window so the `JELYHACK` / `JELYH2` anchors can be inspected alongside the nearby event-bearing `REE_BOOT`, `SURCAMEW`, and `SFXTRIG` descriptors rather than stopping at the referent-only neighbors.")
|
||||
|
|
|
|||
165
tools/usecode_family_compare.py
Normal file
165
tools/usecode_family_compare.py
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
import csv
|
||||
import glob
|
||||
import hashlib
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
LAYOUT_PATH = ROOT / "USECODE" / "EUSECODE_extracted" / "class_layout_index.tsv"
|
||||
EVENT_PATH = ROOT / "USECODE" / "EUSECODE_extracted" / "class_event_index.tsv"
|
||||
CHUNKS_DIR = ROOT / "USECODE" / "EUSECODE_extracted" / "chunks"
|
||||
|
||||
FAMILIES = {
|
||||
"BOOT": {
|
||||
"classes": ["AND_BOOT", "BRO_BOOT", "COR_BOOT", "REE_BOOT", "VAR_BOOT"],
|
||||
"slots": [0x0A, 0x0F, 0x10],
|
||||
},
|
||||
"SURCAM": {
|
||||
"classes": ["SURCAMNS", "SURCAMEW"],
|
||||
"slots": [0x20, 0x21, 0x22],
|
||||
},
|
||||
"JELY": {
|
||||
"classes": ["JELYHACK", "JELYH2"],
|
||||
"slots": [0x01],
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def parse_hex(value: str) -> int:
|
||||
return int(value, 16)
|
||||
|
||||
|
||||
def common_prefix_length(blobs: list[bytes]) -> int:
|
||||
if not blobs:
|
||||
return 0
|
||||
limit = min(len(blob) for blob in blobs)
|
||||
for index in range(limit):
|
||||
current = blobs[0][index]
|
||||
if any(blob[index] != current for blob in blobs[1:]):
|
||||
return index
|
||||
return limit
|
||||
|
||||
|
||||
def common_suffix_length(blobs: list[bytes]) -> int:
|
||||
return common_prefix_length([blob[::-1] for blob in blobs])
|
||||
|
||||
|
||||
def first_diff_positions(blobs: list[bytes], limit: int = 8) -> list[int]:
|
||||
positions: list[int] = []
|
||||
max_len = max(len(blob) for blob in blobs)
|
||||
for index in range(max_len):
|
||||
values = {blob[index] if index < len(blob) else None for blob in blobs}
|
||||
if len(values) > 1:
|
||||
positions.append(index)
|
||||
if len(positions) >= limit:
|
||||
break
|
||||
return positions
|
||||
|
||||
|
||||
def load_layouts(targets: set[str]) -> dict[str, dict[str, str]]:
|
||||
layouts: dict[str, dict[str, str]] = {}
|
||||
with LAYOUT_PATH.open("r", encoding="utf-8", newline="") as handle:
|
||||
reader = csv.DictReader(handle, delimiter="\t")
|
||||
for row in reader:
|
||||
if row["class_name_hint"] in targets:
|
||||
layouts[row["class_name_hint"]] = row
|
||||
return layouts
|
||||
|
||||
|
||||
def load_events(targets: set[str]) -> dict[str, list[dict[str, str]]]:
|
||||
events: dict[str, list[dict[str, str]]] = {}
|
||||
with EVENT_PATH.open("r", encoding="utf-8", newline="") as handle:
|
||||
reader = csv.DictReader(handle, delimiter="\t")
|
||||
for row in reader:
|
||||
if row["class_name_hint"] in targets:
|
||||
events.setdefault(row["class_name_hint"], []).append(row)
|
||||
for rows in events.values():
|
||||
rows.sort(key=lambda row: parse_hex(row["slot"]))
|
||||
return events
|
||||
|
||||
|
||||
def resolve_chunk(data_offset: int) -> Path:
|
||||
matches = glob.glob(str(CHUNKS_DIR / f"chunk_*_off_{data_offset:06X}_len_*.bin"))
|
||||
if len(matches) != 1:
|
||||
raise RuntimeError(f"chunk lookup failed for 0x{data_offset:06X}: {matches}")
|
||||
return Path(matches[0])
|
||||
|
||||
|
||||
def build_rows() -> dict[tuple[str, int], dict[str, object]]:
|
||||
targets = {name for family in FAMILIES.values() for name in family["classes"]}
|
||||
layouts = load_layouts(targets)
|
||||
events = load_events(targets)
|
||||
rows_by_key: dict[tuple[str, int], dict[str, object]] = {}
|
||||
|
||||
for class_name, layout in layouts.items():
|
||||
chunk_path = resolve_chunk(parse_hex(layout["data_offset"]))
|
||||
blob = chunk_path.read_bytes()
|
||||
code_base_minus_one = parse_hex(layout["code_base_minus_one"])
|
||||
nonzero_rows = [row for row in events[class_name] if parse_hex(row["raw_code_offset"]) != 0]
|
||||
offsets = sorted({parse_hex(row["raw_code_offset"]) for row in nonzero_rows})
|
||||
|
||||
for row in nonzero_rows:
|
||||
slot = parse_hex(row["slot"])
|
||||
code_offset = parse_hex(row["raw_code_offset"])
|
||||
start = code_base_minus_one + code_offset
|
||||
next_offsets = [offset for offset in offsets if offset > code_offset]
|
||||
end = code_base_minus_one + next_offsets[0] if next_offsets else len(blob)
|
||||
body = blob[start:end]
|
||||
rows_by_key[(class_name, slot)] = {
|
||||
"class_name": class_name,
|
||||
"slot": slot,
|
||||
"event_name_hint": row["event_name_hint"],
|
||||
"raw_event_entry_word": row["raw_event_entry_word"],
|
||||
"raw_code_offset": row["raw_code_offset"],
|
||||
"start": start,
|
||||
"end": end,
|
||||
"length": len(body),
|
||||
"sha1": hashlib.sha1(body).hexdigest(),
|
||||
"preview": body[:16].hex(" "),
|
||||
"chunk_path": str(chunk_path).replace("\\", "/"),
|
||||
"body": body,
|
||||
}
|
||||
|
||||
return rows_by_key
|
||||
|
||||
|
||||
def main() -> None:
|
||||
rows_by_key = build_rows()
|
||||
for family_name, family in FAMILIES.items():
|
||||
print(f"## {family_name}")
|
||||
for slot in family["slots"]:
|
||||
print(f"SLOT 0x{slot:02X}")
|
||||
subset = [rows_by_key[(class_name, slot)] for class_name in family["classes"]]
|
||||
for row in subset:
|
||||
print(
|
||||
"\t".join(
|
||||
[
|
||||
str(row["class_name"]),
|
||||
str(row["event_name_hint"]),
|
||||
str(row["raw_event_entry_word"]),
|
||||
str(row["raw_code_offset"]),
|
||||
f"{row['start']:04X}-{row['end']:04X}",
|
||||
str(row["length"]),
|
||||
str(row["sha1"])[:12],
|
||||
str(row["preview"]),
|
||||
str(row["chunk_path"]),
|
||||
]
|
||||
)
|
||||
)
|
||||
|
||||
groups: dict[str, list[str]] = {}
|
||||
for row in subset:
|
||||
groups.setdefault(str(row["sha1"]), []).append(str(row["class_name"]))
|
||||
blobs = [row["body"] for row in subset]
|
||||
print("identical_groups=" + json.dumps(list(groups.values())))
|
||||
print(
|
||||
f"common_prefix_len={common_prefix_length(blobs)} "
|
||||
f"common_suffix_len={common_suffix_length(blobs)}"
|
||||
)
|
||||
print("first_diff_positions=" + json.dumps(first_diff_positions(blobs)))
|
||||
print()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Loading…
Add table
Add a link
Reference in a new issue