diff --git a/decompile/objdiff.json b/decompile/objdiff.json index 4b711b0..4c6af55 100644 --- a/decompile/objdiff.json +++ b/decompile/objdiff.json @@ -14,10 +14,10 @@ "source_path": "src/JSRF/Jet2.cpp" }, "symbol_mappings": { - "?main_funcinfo@@3UFuncInfo@@A": "$T745", - "?main_handler@@YAXPAUEHExceptionRecord@@PAKPAXPAU_xDISPATCHER_CONTEXT@@@Z": "$L749", - "?main_handler_unwind1@@YAXXZ": "$L741", - "?main_unwindmap@@3PAUUnwindMapEntry@@A": "$T751", + "?main_funcinfo@@3UFuncInfo@@A": "$T747", + "?main_handler@@YAXPAUEHExceptionRecord@@PAKPAXPAU_xDISPATCHER_CONTEXT@@@Z": "$L751", + "?main_handler_unwind1@@YAXXZ": "$L743", + "?main_unwindmap@@3PAUUnwindMapEntry@@A": "$T753", "[.rdata-0]": "[.xdata$x-0]" } }, @@ -42,6 +42,15 @@ "?finalizeGameData@@YAXXZ": "_$E2", "?initGameData@@YAXXZ": "_$E1" } + }, + { + "name": "XDK/Xapi/xapi0", + "target_path": "target/XDK/Xapi/xapi0.obj", + "base_path": "src/XDK/Xapi/xapi0.obj", + "metadata": { + "complete": false, + "source_path": "src/XDK/Xapi/xapi0.c" + } } ] } \ No newline at end of file diff --git a/decompile/src/XDK/CRT/crt0.c b/decompile/src/XDK/CRT/crt0.c deleted file mode 100644 index 4432fdc..0000000 --- a/decompile/src/XDK/CRT/crt0.c +++ /dev/null @@ -1,33 +0,0 @@ -/* JSRF Decompilation: XDK/CRT0.c -C runtime initialization. -Like other CRT code, there's some magic here with symbols that get special -treatment from the compiler and linker. -*/ - -#include "../Win32.h" - - -// Every program is supposed to have a main(), so we can just assume its -// existence with a declaration here -int main(void); - - -void __cdecl mainCRTStartup (void); -DWORD __stdcall mainXapiStartup(LPVOID lpThreadParameter); - - -// Address: 0x00148023 -// Status: unimplemented -void __cdecl mainCRTStartup(void) { -/* The true entrypoint of the game, spawning a thread for the rest to run in -The linker automatically sets this function to the entrypoint. -*/ -} - -// Address: 0x00147FB4 -// Status: unimplemented -DWORD __stdcall mainXapiStartup(LPVOID const lpThreadParameter) { -/* Runs some initialization and then calls main() */ - main(); - return 0; -} diff --git a/decompile/src/XDK/Win32.h b/decompile/src/XDK/Win32.h index 8bb6546..d6192d7 100644 --- a/decompile/src/XDK/Win32.h +++ b/decompile/src/XDK/Win32.h @@ -25,7 +25,7 @@ typedef __int64 LONGLONG; typedef unsigned char BYTE; typedef unsigned short WORD; -typedef unsigned long DWORD; +typedef unsigned long DWORD, * PDWORD; typedef void VOID; typedef void * LPVOID; @@ -45,6 +45,8 @@ union LARGE_INTEGER { LONGLONG QuadPart; }; +typedef void * HANDLE; + // Return codes typedef long HRESULT; diff --git a/decompile/src/XDK/Xapi/xapi0.c b/decompile/src/XDK/Xapi/xapi0.c new file mode 100644 index 0000000..ee9c93b --- /dev/null +++ b/decompile/src/XDK/Xapi/xapi0.c @@ -0,0 +1,83 @@ +/* JSRF Decompilation: XDK/Xapi/xapi0.c +Entrypoint for Xbox programs. +Awkwardly, the placement in the .xbe suggests this is not part of the C runtime +(which is normally what provides mainCRTStartup()), but rather part of Xapi. +It's been named xapi0 in analogy to crt0 in a C runtime. +*/ + +#include "../CRT/stddef.h" +#include "../Win32.h" + + +// Things that should get declared in a header somewhere +#define XLD_LAUNCH_DASHBOARD_ERROR 1 +#define XLD_ERROR_INVALID_XBE 1 + +extern struct IMAGE_TLS_DIRECTORY { + DWORD StartAddressOfRawData; + DWORD EndAddressOfRawData; + DWORD * AddressOfIndex; + void * AddressOfCallbacks; + DWORD SizeOfZeroFill; + DWORD Characteristics; +} _tls_used; + +extern int XapiTlsSize; + +DWORD __stdcall mainXapiStartup(LPVOID lpThreadParameter); +BOOL __stdcall CloseHandle (HANDLE hHandle ); + +typedef DWORD (__stdcall * LPTHREAD_START_ROUTINE)(LPVOID lpThreadParameter); +HANDLE __stdcall CreateThread( + void * lpThreadAttributes, + DWORD dwStackSize, + LPTHREAD_START_ROUTINE lpStartAddress, + LPVOID lpParameter, + DWORD dwCreationFlags, + PDWORD lpThreadId +); + +void __stdcall XapiBootToDash( + DWORD dwReason, + DWORD dwParameter1, + DWORD dwParameter2 +); + + +// Every program is supposed to have a main(), so we can just assume its +// existence with a declaration here +int main(void); + + +// Address: 0x00147FB4 +// Status: unimplemented +DWORD __stdcall mainXapiStartup(LPVOID lpThreadParameter) { +/* Runs some Xbox-specific initialization and then calls main() */ + main(); + return 0; +} + +// Address: 0x00148023 +// Status: nonmatching +void mainCRTStartup(void) { +/* The true entrypoint of the game, spawning a thread for the rest to run in +The linker automatically sets this function to the entrypoint. +*/ + HANDLE thread; + + // Figure out available thread-local storage + XapiTlsSize = ( + (_tls_used.EndAddressOfRawData - _tls_used.StartAddressOfRawData) + + _tls_used.SizeOfZeroFill + ) + 15 & 0xFFFFFFF0; // Round up to nearest 0x10 + *_tls_used.AddressOfIndex = XapiTlsSize / (-4); + + // Launch program as a thread + thread = CreateThread(NULL, 0, mainXapiStartup, NULL, 0, NULL); + + // Boot back to dashboard with an error message if launching failed + if (thread == NULL) + XapiBootToDash(XLD_LAUNCH_DASHBOARD_ERROR, XLD_ERROR_INVALID_XBE, 0); + + CloseHandle(thread); +} diff --git a/decompile/target/XDK/CRT/.gitkeep b/decompile/target/XDK/CRT/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/decompile/target/XDK/Xapi/.gitkeep b/decompile/target/XDK/Xapi/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/ghidra/ghidra_scripts/MSVC7Mangle.java b/ghidra/ghidra_scripts/MSVC7Mangle.java index 3192ecc..f913567 100644 --- a/ghidra/ghidra_scripts/MSVC7Mangle.java +++ b/ghidra/ghidra_scripts/MSVC7Mangle.java @@ -66,8 +66,10 @@ import ghidra.program.model.symbol.Symbol; import java.util.Arrays; import java.util.ArrayList; import java.util.Collections; +import java.util.HashSet; import java.util.List; import java.util.Optional; +import java.util.Set; import java.util.stream.Collectors; import java.util.stream.IntStream; import java.util.zip.CRC32; @@ -88,28 +90,35 @@ public class MSVC7Mangle extends GhidraScript { setCurrentSelection(addr); } + final HashSet seenSymbols = new HashSet<>(1024); for (final Symbol s : currentProgram.getSymbolTable() .getPrimarySymbolIterator(currentSelection, true)) { - mangle(s); + mangle(s, seenSymbols); // Also mangle everything referenced inside functions // if headless if ( isRunningHeadless() && s.getObject() instanceof Function f - ) mangleRefs(f); + ) mangleRefs(f, seenSymbols); } } - private void mangle(final Symbol s) throws Exception { + private void mangle( + final Symbol s, + final Set seenSymbols + ) throws Exception { /* Set the given symbol's name to its mangled version */ // Skip if already mangled; skip jump tables final String name = s.getName(true); if ( + seenSymbols.contains(s) || name.charAt(0) == '?' || name.startsWith("switchD_") ) return; + seenSymbols.add(s); + // Get mangled name final String mangled = switch (s.getObject()) { case Function f -> mangleFn (f); @@ -622,7 +631,10 @@ public class MSVC7Mangle extends GhidraScript { return (int)crc.getValue() ^ 0xFFFFFFFF; } - private void mangleRefs(final Function f) throws Exception { + private void mangleRefs( + final Function f, + final Set seenSymbols + ) throws Exception { /* Mangle all symbols referenced in the body of a function */ for ( Instruction ins = getFirstInstruction(f); @@ -650,7 +662,7 @@ public class MSVC7Mangle extends GhidraScript { continue; } - mangle(symbol); + mangle(symbol, seenSymbols); } } } diff --git a/ghidra/objects.csv b/ghidra/objects.csv index 0206fb8..5344983 100644 --- a/ghidra/objects.csv +++ b/ghidra/objects.csv @@ -3,13 +3,14 @@ JSRF/Core.obj,true,0x00011000-0x00013FEB,,,0x00186BA0-0x00186C14,,,,,,0x001C4390 JSRF/GameData.obj,true,0x00039B50-0x0003B937,0x0018AD60-0x0018AD75,0x0018C9A0-0x0018C9AA,,,,,,,0x001CA16C-0x001CA3DB,,0x001EB790-0x001EB793,0x001EFC88-0x001F7047, JSRF/Jet2.obj,true,0x0006F9E0-0x0006FA6F,,,0x00187710-0x00187724,,,,,,,0x001E620C-0x001E622F,,0x0022FCE0-0x0022FCE3, ADX (need to decompose),false,0x0013A570-0x0014555F,?,?,?,?,,,,,,?,?,?, -XDK Core (need to decompose),false,0x00145560-0x0014B79F,?,?,?,?,,,,,,?,?,?, +Xapi (need to decompose),false,0x00145560-0x0014B79F,?,?,?,?,,,,,,?,?,?, +XDK/Xapi/xapi0.obj,true,0x00147FB4-0x0014807C,,,,,,,,,,,,, Smilebit libs (need to decompose),false,0x0014B7A0-0x0017BF3F,?,?,?,?,,,,,,?,?,?-0x0022ED2B, -C runtime,false,0x0017BF40-0x00182B80,?,?,?,,,,,?,?,0x0022ED2C-?,?,?, +C runtime (need to decompose),false,0x0017BF40-0x00182B80,?,?,?,,,,,?,?,0x0022ED2C-?,?,?, Unknown MS math lib,false,0x00182B81-0x0018694F,?,?,?,,,,,?,?,?,?,?, Another (tiny) Smilebit math lib,false,0x00186950-0x00186B7F,?,?,?,,,,,?,?,?,?,?, Direct3D8 (need to decompose),false,,?,,0x0018CB40-0x0019E334,,,,,?,?,?,?,?, DirectSound8 (need to decompose),false,,?,?,,,0x0019E340-0x001BA89B,,,,?,?,?,?,0x0027E080-0x00284E17 -MMatrix.obj,false,,?,,,,0x001BA8A0-0x001BBAAF,,,,,?,?,0x00264BD8-0x00264C13, +Smilebit/MMatrix.obj,false,,?,,,,0x001BA8A0-0x001BBAAF,,,,,?,?,0x00264BD8-0x00264C13, Xgraphics (need to decompose),false,,?,,,,,0x001BBAC0-0x001BC7BB,,,,?,?,, XDK Peripherals (need to decompose),false,,?,,,,,,0x001BC7C0-0x001C3F57,,,?,?,, diff --git a/readme.md b/readme.md index c865555..49cfa1e 100644 --- a/readme.md +++ b/readme.md @@ -2,8 +2,8 @@ A matching decompilation of the Xbox game Jet Set Radio Future. ## Progress -- Delinking progress: 1.02% (26359 out of 2574172 bytes in XBE address space) -- Decompilation progress: 18.7% (31 out of the 166 functions delinked so far) +- Delinking progress: 1.03% (26559 out of 2574172 bytes in XBE address space) +- Decompilation progress: 18.5% (31 out of the 168 functions delinked so far) - **Estimated total progress: 0.19%** (previous two multiplied together) ## Roadmap