Compare commits

...

3 commits

Author SHA1 Message Date
KeybadeBlox
823b19371c Fully decompile xapi0 2026-02-22 14:04:18 -05:00
KeybadeBlox
4e20347b7c Decompile mainCRTStartup()
Incidental changes include fixes for Xapi in the Makefille and
objdiff.json, as well as new compiler flags for Xapi.
2026-02-22 10:41:15 -05:00
KeybadeBlox
cbd63865e2 Rename Smilebit library to MUSASHI
Strings in the .rdata portion of the Smilebit in-house library code
suggest that this was its name, with its contents having names beginning
with an M (whence MMATRIX, for example).
2026-02-22 10:37:59 -05:00
9 changed files with 114 additions and 25 deletions

View file

@ -3,7 +3,7 @@
# All object files to link together
OBJ = src/JSRF/Jet2.obj src/JSRF/Core.obj src/JSRF/GameData.obj\
src/XDK/CRT/CRT0.obj
src/XDK/Xapi/xapi0.obj
# Import library for the only thing we don't compile ourselves, the Xbox kernel
LIB = lib/xboxkrnl.lib
@ -14,7 +14,7 @@ all: $(OBJ) # For now, just compile all the object files
## Build commands
.SUFFIXES: .cpp .obj .def .lib .exe.xbe
.SUFFIXES: .c .cpp .obj .def .lib .exe.xbe
# Convert compiled executable into a working Xbox executable
# (TODO: we may want to fork cxbe to add section checksums and the game ID)
@ -32,7 +32,7 @@ src/JSRF/Jet2.exe: $(OBJ) $(LIB)
# Compile object files from source
.c.obj:
CL.EXE /nologo /Wall /W4 /Ogityb0 /GfX /Fo$@ /c $<
CL.EXE /nologo /Wall /W4 /O1giy /Fo$@ /c $<
.cpp.obj:
CL.EXE /nologo /Wall /W4 /Ogityb0 /GfX /Fo$@ /c $<
@ -41,9 +41,9 @@ src/JSRF/Jet2.exe: $(OBJ) $(LIB)
src/JSRF/Jet2.obj: src/JSRF/Core.hpp src/XDK/CRT/stddef.h src/XDK/D3D.h\
src/XDK/Win32.h
src/JSRF/Core.obj: src/JSRF/Core.hpp src/Smilebit/MMatrix.hpp\
src/JSRF/Core.obj: src/JSRF/Core.hpp src/MUSASHI/MMatrix.hpp\
src/XDK/CRT/stddef.h src/XDK/D3D.h src/XDK/Win32.h
src/JSRF/GameData.obj: src/JSRF/GameData.hpp
src/XDK/CRT/CRT0.obj: src/XDK/Win32.h
src/XDK/Xapi/xapi0.obj: src/XDK/Win32.h

View file

@ -1,6 +1,8 @@
{
"custom_make": "NMAKE.EXE",
"watch_patterns": [
"*.c",
"*.h",
"*.cpp",
"*.hpp"
],
@ -48,7 +50,7 @@
"target_path": "target/XDK/Xapi/xapi0.obj",
"base_path": "src/XDK/Xapi/xapi0.obj",
"metadata": {
"complete": false,
"complete": true,
"source_path": "src/XDK/Xapi/xapi0.c"
}
}

View file

@ -5,7 +5,7 @@ Game and GameObj classes that form the foundation of the JSRF game code.
#ifndef CORE_HPP
#define CORE_HPP
#include "../Smilebit/MMatrix.hpp"
#include "../MUSASHI/MMatrix.hpp"
#include "../XDK/CRT/stddef.h"
#include "../XDK/D3D.h"
#include "../XDK/Win32.h"

View file

@ -21,11 +21,14 @@ extern struct IMAGE_TLS_DIRECTORY {
DWORD SizeOfZeroFill;
DWORD Characteristics;
} _tls_used;
extern int _tls_array;
extern int _tls_index;
extern int XapiTlsSize;
DWORD __stdcall mainXapiStartup(LPVOID lpThreadParameter);
BOOL __stdcall CloseHandle (HANDLE hHandle );
void __stdcall XapiInitProcess(void);
BOOL __stdcall CloseHandle (HANDLE hHandle);
typedef DWORD (__stdcall * LPTHREAD_START_ROUTINE)(LPVOID lpThreadParameter);
HANDLE __stdcall CreateThread(
@ -43,22 +46,106 @@ void __stdcall XapiBootToDash(
DWORD dwParameter2
);
void __stdcall _rtinit(void);
void __stdcall _cinit (void);
// Every program is supposed to have a main(), so we can just assume its
// existence with a declaration here
int main(void);
// existence with a declaration here. Interestingly, it's called with
// arguments here despite being declared without any on the developer's side.
int main(
int argc,
char * * argv,
char * * envp
);
// Address: 0x00147FB4
// Status: unimplemented
DWORD __stdcall mainXapiStartup(LPVOID lpThreadParameter) {
/* Runs some Xbox-specific initialization and then calls main() */
main();
return 0;
// Status: matching
__declspec(naked) DWORD __stdcall mainXapiStartup(LPVOID lpThreadParameter) {
/* Runs some Xbox-specific initialization and then calls main()
It appears to be truly impossible in Visual C++ 7.0 to access the FS register
without writing assembly, and also for inline assembly to write to a variable
in a register rather than on the stack. Thus, the only apparent way to make
the middle part of the function match is through writing the instructions
directly. Given that the C parts of this function are so simple that a human
and a compiler would reasonably produce the exact same code, and this function
exhibits other oddities mentioned in the body, it's likely this whole function
was originally written purely in assemblys. In the spirit of decompilation,
however, we'll lift what we can into C.
*/
XapiInitProcess();
// Honestly unsure what's being done here, as FS doesn't seem to follow
// the same structure as 32-bit Windows
__asm {
mov eax, fs:[0x20]
mov eax, [eax+0x250]
test eax, eax
je zero
mov ecx, [eax+0x24]
jmp store
/*
All this jumping around is strange, as it could easily be a
single branch, which is very plain to see if written out as C
(in fact, while MSVC's behaviour hasn't been tested, it
wouldn't take a very smart compiler to optimize this out if it
was written this way in C). It seems likely this is awkward
human-written assembly.
*/
zero:
xor ecx, ecx
store:
test ecx, ecx
je end
/*
This section is suspect because of how it preserves edi despite
it not being used anywhere else, as if it was a separately
written function. If it was a function, it must have been
inlined, but it couldn't be because it must be called from
assembly to use the value in ecx that never went on the stack
(as passing data from C to assembly has to do). Notably, this
function's calling convention does require edi to be preserved,
but if left up to the compiler, it will push and pop edi in the
prologue and epilogue, not here (which this function must be
declared as naked to avoid).
It may be that this function was originally written entirely in
assembly and declared naked, with the author deciding to
preserve edi only in this one section using it.
*/
push edi
mov eax, fs:[0x28]
mov edi, fs:[_tls_array]
mov edx, [_tls_index]
mov edx, [edi + edx*0x4]
sub edx, [eax + 0x28]
mov byte ptr [ecx], 0x1
add edx, _tls_array
mov [ecx + 0x4], edx
pop edi
end:
};
_rtinit();
_cinit();
main(0, NULL, NULL);
XapiBootToDash(XLD_LAUNCH_DASHBOARD_ERROR, XLD_ERROR_INVALID_XBE, 0);
// Return 0 to satisfy signature required for thread functions
_asm {
xor eax, eax
ret 4
};
}
// Address: 0x00148023
// Status: nonmatching
// Status: matching
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.
@ -66,11 +153,11 @@ The linker automatically sets this function to the entrypoint.
HANDLE thread;
// Figure out available thread-local storage
XapiTlsSize = (
XapiTlsSize = 4 + ((
(_tls_used.EndAddressOfRawData - _tls_used.StartAddressOfRawData) +
_tls_used.SizeOfZeroFill
) + 15 & 0xFFFFFFF0; // Round up to nearest 0x10
*_tls_used.AddressOfIndex = XapiTlsSize / (-4);
) + 15 & 0xFFFFFFF0); // Round up to nearest 0x10
*_tls_used.AddressOfIndex = -1 * XapiTlsSize/4;
// Launch program as a thread
thread = CreateThread(NULL, 0, mainXapiStartup, NULL, 0, NULL);

View file

@ -11,7 +11,7 @@ HEADERS="
Std.hpp
XDK/Win32.h
XDK/D3D.h
Smilebit/MMatrix.hpp
MUSASHI/MMatrix.hpp
JSRF/Core.hpp
JSRF/GameData.hpp
"

View file

@ -5,12 +5,12 @@ JSRF/Jet2.obj,true,0x0006F9E0-0x0006FA6F,,,0x00187710-0x00187724,,,,,,,0x001E620
ADX (need to decompose),false,0x0013A570-0x0014555F,?,?,?,?,,,,,,?,?,?,
Xapi (need to decompose),false,0x00145560-0x0014B79F,?,?,?,?,,,,,,?,?,?,
XDK/Xapi/xapi0.obj,true,0x00147FB4-0x0014807C,,,,,,,,,,,,,
Smilebit libs (need to decompose),false,0x0014B7A0-0x0017BF3F,?,?,?,?,,,,,,?,?,?-0x0022ED2B,
Smilebit MUSASHI libs (need to decompose),false,0x0014B7A0-0x0017BF3F,?,?,?,?,,,,,,?,?,?-0x0022ED2B,
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
Smilebit/MMatrix.obj,false,,?,,,,0x001BA8A0-0x001BBAAF,,,,,?,?,0x00264BD8-0x00264C13,
MUSASHI/MMatrix.obj,false,,?,,,,0x001BA8A0-0x001BBAAF,,,,,?,?,0x00264BD8-0x00264C13,
Xgraphics (need to decompose),false,,?,,,,,0x001BBAC0-0x001BC7BB,,,,?,?,,
XDK Peripherals (need to decompose),false,,?,,,,,,0x001BC7C0-0x001C3F57,,,?,?,,

1 Object Delink? .text .text$yc .text$yd .text$x D3D DSOUND MMATRIX XGRPH XPP .rdata .rdata$x .data$CRT .data DOLBY
5 ADX (need to decompose) false 0x0013A570-0x0014555F ? ? ? ? ? ? ?
6 Xapi (need to decompose) false 0x00145560-0x0014B79F ? ? ? ? ? ? ?
7 XDK/Xapi/xapi0.obj true 0x00147FB4-0x0014807C
8 Smilebit libs (need to decompose) Smilebit MUSASHI libs (need to decompose) false 0x0014B7A0-0x0017BF3F ? ? ? ? ? ? ?-0x0022ED2B
9 C runtime (need to decompose) false 0x0017BF40-0x00182B80 ? ? ? ? ? 0x0022ED2C-? ? ?
10 Unknown MS math lib false 0x00182B81-0x0018694F ? ? ? ? ? ? ? ?
11 Another (tiny) Smilebit math lib false 0x00186950-0x00186B7F ? ? ? ? ? ? ? ?
12 Direct3D8 (need to decompose) false ? 0x0018CB40-0x0019E334 ? ? ? ? ?
13 DirectSound8 (need to decompose) false ? ? 0x0019E340-0x001BA89B ? ? ? ? 0x0027E080-0x00284E17
14 Smilebit/MMatrix.obj MUSASHI/MMatrix.obj false ? 0x001BA8A0-0x001BBAAF ? ? 0x00264BD8-0x00264C13
15 Xgraphics (need to decompose) false ? 0x001BBAC0-0x001BC7BB ? ?
16 XDK Peripherals (need to decompose) false ? 0x001BC7C0-0x001C3F57 ? ?

View file

@ -3,8 +3,8 @@ A matching decompilation of the Xbox game Jet Set Radio Future.
## Progress
- 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)
- Decompilation progress: 19.6% (33 out of the 168 functions delinked so far)
- **Estimated total progress: 0.20%** (previous two multiplied together)
## Roadmap
The approach of this decompilation is to: