Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,6 @@ games/settings.json
*.dll

# MyMods folder
mods/MyMods/*
mods/MyMods/*

log.txt
2 changes: 1 addition & 1 deletion config.json
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@
"name": "ctr-u.bin",
"symbols":
[
"gcc-syms-rewrite.txt"
"gcc-syms-rewrite.txt", "gcc-extern-rewrite.txt"
],
"build_id": 9999
}
Expand Down
12 changes: 12 additions & 0 deletions include/ctr/game_tracker.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#pragma once

#include <ctr/macros.h>
#include <ctr/rng.h>

typedef struct GameTracker
{
u8 fill[0x252c];
RNGSeed seed;
} GameTracker;

extern GameTracker* e_gameTracker;
8 changes: 3 additions & 5 deletions include/ctr/gte.h
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
#ifndef CTR_GTE_H
#define CTR_GTE_H
#pragma once

#include <ctr_math.h>
#include <ctr/math.h>
#include <psn00bsdk/include/inline_c.h>

typedef enum GTE_ROW_INDEX
Expand Down Expand Up @@ -64,5 +63,4 @@ typedef enum GTE_MAC
#define gte_loadVec(v, vecType) CAT(_gte_loadVec_, vecType)(v)
#define gte_loadRowMatrix(v, rowIndex, matrixType) CAT3(_gte_loadSVecMatrix_, matrixType, _##rowIndex)(v)
#define gte_mulMatrixVec(out, matrixType, vecType) _gte_mulMatrixVec(out, matrixType, vecType, 1)
#define gte_dotProduct(out, rowIndex, matrixType, vecType) CAT(_gte_dotProduct_, rowIndex)(out, matrixType, vecType)
#endif
#define gte_dotProduct(out, rowIndex, matrixType, vecType) CAT(_gte_dotProduct_, rowIndex)(out, matrixType, vecType)
7 changes: 2 additions & 5 deletions include/ctr/macros.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#ifndef MACROS_H
#define MACROS_H
#pragma once

#include <stdbool.h>
#include <stddef.h>
Expand Down Expand Up @@ -47,6 +46,4 @@ typedef int8_t s8;
#define OFFSETOF(TYPE, ELEMENT) ((unsigned int)&(((TYPE *)0)->ELEMENT))

#define nullptr ((void *) 0)
#define force_inline static inline __attribute__((always_inline))

#endif
#define force_inline static inline __attribute__((always_inline))
7 changes: 2 additions & 5 deletions include/ctr/math.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#ifndef CTR_MATH_H
#define CTR_MATH_H
#pragma once

#include <ctr/macros.h>
#include <ctr/gte.h>
Expand Down Expand Up @@ -125,6 +124,4 @@ void MATH_GetInverseMatrixTransformation(Matrix* out, const Matrix* matrix);
s32 MATH_VectorLength(const SVec3* vector);
void MATH_VectorNormalize(SVec3* vector);
void MATH_CombineMatrixTransformation(Matrix* out, const Matrix* m, const Matrix* n);
void MATH_MatrixMultiplication(Matrix* out, const Matrix* m, const Matrix* n); // overwrites Rotation Matrix in the GTE with resulting matrix

#endif
void MATH_MatrixMultiplication(Matrix* out, const Matrix* m, const Matrix* n); // overwrites Rotation Matrix in the GTE with resulting matrix
10 changes: 7 additions & 3 deletions include/ctr/nd.h
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#ifndef ND_H
#define ND_H
#pragma once

#include <ctr/macros.h>
#include <ctr/math.h>
#include <ctr/rng.h>

void ND_LOAD_XnfFile(char* filename, u32 address, char* dummy);
s32 ND_SquareRoot0_stub(s32 n);
Expand All @@ -19,4 +19,8 @@ void ND_MATH_VectorNormalize(SVec3* vector);
void ND_MATH_CombineMatrixTransformation(Matrix* out, const Matrix* m, const Matrix* n);
void ND_MATH_MatrixMultiplication(Matrix* out, const Matrix* m, const Matrix* n);

#endif
/* RNG */
u32 ND_RNG_Rand();
s32 ND_RNG_RandInt(u32 n);
u16 ND_RNG_PseudoRand(u16 n);
u32 ND_RNG_Random(RNGSeed* seed);
7 changes: 2 additions & 5 deletions include/ctr/prim.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#ifndef PRIM_H
#define PRIM_H
#pragma once

#include <ctr/macros.h>

Expand Down Expand Up @@ -276,6 +275,4 @@ void GetPrimitiveMem(void ** ppPrim, size_t primSize);
void AddPrimitive(void * pPrim, void * pOt);

#define GetPrimMem(p) GetPrimitiveMem((void **) &p, sizeof(*p))
*/

#endif
*/
7 changes: 2 additions & 5 deletions include/ctr/redux.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#ifndef REDUX_H
#define REDUX_H
#pragma once

#include <ctr/macros.h>

Expand All @@ -10,6 +9,4 @@ force_inline void pcsx_exit(int code) { *((volatile int16_t* const)0x1f802082) =
force_inline void pcsx_message(const char* msg) { *((volatile const char** const)0x1f802084) = msg; }
force_inline void pcsx_checkKernel(int enable) { *((volatile char*)0x1f802088) = enable; }
force_inline int pcsx_isCheckingKernel() { return *((volatile char* const)0x1f802088) != 0; }
force_inline int pcsx_present() { return *((volatile uint32_t* const)0x1f802080) == 0x58534350; }

#endif
force_inline int pcsx_present() { return *((volatile uint32_t* const)0x1f802080) == 0x58534350; }
16 changes: 16 additions & 0 deletions include/ctr/rng.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#pragma once

#include <ctr/macros.h>

typedef struct RNGSeed
{
u32 a;
u32 b;
} RNGSeed;

u32 RNG_Rand();
s32 RNG_RandInt(u32 n);
u16 RNG_PseudoRand(u16 n);
u32 RNG_Random(RNGSeed* seed);

extern u32 e_seed; // 0x8008d424
61 changes: 42 additions & 19 deletions include/ctr/test.h
Original file line number Diff line number Diff line change
@@ -1,32 +1,55 @@
#ifndef TEST_H
#define TEST_H
#pragma once

#include <ctr/macros.h>
#include <ctr/math.h>
#include <ctr/redux.h>
#include <ctr/nd.h>
#include <ctr/game_tracker.h>
#include <ctr/math.h>
#include <ctr/rng.h>

void LoadTestPatches();
u32 PatchFunction_Beg(u32* index);
void PatchFunction_End(u32 index);
u32 PrintSVectorDiff(const char* name, const SVec3* expected, const SVec3* ret);
u32 PrintMatrixDiff(const char* name, const Matrix* expected, const Matrix* ret, u32 cmpTrans);

#define BACKUP_ADDR 0x80400000

#define TEST_MATH_IMPL
#define TEST_RNG_IMPL

#ifdef TEST_MATH_IMPL
void TEST_MATH_Sin(u32 angle, s32 res);
void TEST_MATH_Cos(u32 angle, s32 res);
void TEST_MATH_Sqrt(u32 n, u32 shift, u32 res);
void TEST_MATH_GetInverseMatrixTransformation(const Matrix* matrix, const Matrix* res);
void TEST_MATH_VectorLength(const SVec3* vector, s32 res);
void TEST_MATH_VectorNormalize(SVec3* vector, const SVec3* res);
void TEST_MATH_CombineMatrixTransformation(const Matrix* m, const Matrix* n, const Matrix* res);
void TEST_MATH_MatrixMultiplication(const Matrix* m, const Matrix* n, const Matrix* res);
void TEST_MATH_Sin(u32 angle, s32 ret);
void TEST_MATH_Cos(u32 angle, s32 ret);
void TEST_MATH_Sqrt(u32 n, u32 shift, u32 ret);
void TEST_MATH_GetInverseMatrixTransformation(const Matrix* matrix, const Matrix* ret);
void TEST_MATH_VectorLength(const SVec3* vector, s32 ret);
void TEST_MATH_VectorNormalize(SVec3* vector, const SVec3* ret);
void TEST_MATH_CombineMatrixTransformation(const Matrix* m, const Matrix* n, const Matrix* ret);
void TEST_MATH_MatrixMultiplication(const Matrix* m, const Matrix* n, const Matrix* ret);
#else
#define TEST_MATH_Sin(angle, res)
#define TEST_MATH_Cos(angle, res)
#define TEST_MATH_Sqrt(n, shift, res)
#define TEST_MATH_GetInverseMatrixTransformation(matrix, res)
#define TEST_MATH_VectorLength(vector, res)
#define TEST_MATH_VectorNormalize(vector, res)
#define TEST_MATH_CombineMatrixTransformation(m, n, res)
#define TEST_MATH_MatrixMultiplication(m, n, res)
#define TEST_MATH_Sin(angle, ret)
#define TEST_MATH_Cos(angle, ret)
#define TEST_MATH_Sqrt(n, shift, ret)
#define TEST_MATH_GetInverseMatrixTransformation(matrix, ret)
#define TEST_MATH_VectorLength(vector, ret)
#define TEST_MATH_VectorNormalize(vector, ret)
#define TEST_MATH_CombineMatrixTransformation(m, n, ret)
#define TEST_MATH_MatrixMultiplication(m, n, ret)
#endif

#ifdef TEST_RNG_IMPL
void BACKUP_RNG_Rand();
void TEST_RNG_Rand();
void BACKUP_RNG_RandInt();
void TEST_RNG_RandInt(u32 n, s32 ret);
void TEST_RNG_PseudoRand(u16 n, u16 ret);
void TEST_RNG_Random(RNGSeed* seed, const RNGSeed* ret);
#else
#define BACKUP_RNG_Rand()
#define TEST_RNG_Rand()
#define BACKUP_RNG_RandInt()
#define TEST_RNG_RandInt(n, ret)
#define TEST_RNG_PseudoRand(n, ret)
#define TEST_RNG_Random(seed, ret)
#endif
22 changes: 22 additions & 0 deletions rewrite/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Strategy
This folder is an ongoing decompilation project for the PSX game Crash Team Racing. This is a non byte matching decompilation, aiming for code quality while achieving the same functionality as the original game. This project adopts the [Ship of Theseus](https://en.wikipedia.org/wiki/Ship_of_Theseus) strategy. By taking advantage of [PCSX-Redux](https://github.com/grumpycoders/pcsx-redux/) 8MB memory expansion, we can re-write game functions and load them in memory, while the original game stays intact. Then, we can force the game to call each function we re-wrote, and compare the output of the decompiled function with the original game function.

# Folder Structure
`src/hooks/dll/`: boot loader for the decompile.

`src/exe/`: main executable decomp.

`src/tests/`: tests for each decompiled function.

`scripts/`: helpful misc scripts.

`../include/ctr/`: decompile headers.

`../symbols/gcc-syms-rewrite.txt`: original function addresses.

`../symbols/gcc-extern-rewrite.txt`: extern global variable addresses.

# Tests
Each decompiled function must have an entry in `s_functions` table at `src/tests/test.c`. During boot, this entry is used to patch each original function so that the decompile version can be called. At the end of each decompiled function, a call to a `TEST` function should be made, which restores the state before the decompiled function was called, then patches the game to call the original function, and then compared the output of the original function with the decompiled function. For simple test functions that don't use any global variables, see `src/tests/test_math.c` and `src/exe/math.c`. For functions that change global variables, a call to a `BACKUP` function should be made to store the state upon function call. For examples, see `str/tests/test_rng.c` and `src/exe/rng.c`.

Note: the original function name must have the same name of the decompiled function + `ND_` prefix.
4 changes: 2 additions & 2 deletions rewrite/buildList.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ common, exe, 0x8003c63c, 0x0, src/hooks/dll/hook.s
common, header, 0x8000B0B8, 0x0, src/hooks/dll/load_decomp.c

// decomp files
common, DLL, 0x80200000, 0x0, src/math.c src/prim.c, DECOMP.BIN
common, DLL, 0x80600000, 0x0, src/tests/test.c, TESTS.BIN
common, DLL, 0x80200000, 0x0, src/exe/*.c, DECOMP.BIN
common, DLL, 0x80600000, 0x0, src/tests/*.c, TESTS.BIN
89 changes: 89 additions & 0 deletions rewrite/scripts/codeflow/asm.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
from symbols import Syms, RAM_BASE

class ASMInstruction:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's this do?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

analizes the log of instructions of one frame and displays the function calls

def __init__(self, line: str, sym: Syms) -> None:
self.failed_decoding = False
try:
self.return_address = int("0x" + line[0][:8], 0) + 8
except Exception:
self.failed_decoding = True
return
self.sym = sym
self.instruction = line[2]
self.destination = line[3]
switch = {
"jal" : self.case_jal,
"jalr" : self.case_jalr,
"jr" : self.case_jr
}
self.symbol = None
self.has_returned = False
self.returning = False
self.func_call = False
if self.instruction in switch:
switch[self.instruction]()
self.func_call = True

def case_jal(self):
self.destination = int(self.destination[:10], 0)
self.symbol = self.sym.get_symbol(self.destination)
if self.symbol is None:
name = str()
if self.destination < RAM_BASE:
name = "0" + hex(self.destination + RAM_BASE)[3:]
else:
name = hex(self.destination)[2:]
self.symbol = "FUN_" + name

def case_jalr(self):
self.destination = int("0x" + self.destination[4:12], 0)
self.symbol = self.sym.get_symbol(self.destination)
if self.symbol is None:
name = str()
if self.destination < RAM_BASE:
name = "0" + hex(self.destination + RAM_BASE)[3:]
else:
name = hex(self.destination)[2:]
self.symbol = "FUN_" + name

def case_jr(self):
self.destination = int("0x" + self.destination[4:12], 0)
self.returning = True

class ASMAnalyzer:
def __init__(self, sym: Syms, output: str, space_size: int) -> None:
self.sym = sym
self.output = output
self.space_size = space_size

def analyze_log(self, log: str, start_label: str) -> None:
output = open(self.output, "w")
buffer = start_label + "()\n"
curr_spacing = self.space_size
possible_func_calls = []
with open(log, "r") as file:
for line in file:
line = line.split()
if len(line) < 4:
continue
instruction = ASMInstruction(line, self.sym)
if instruction.failed_decoding:
continue
if instruction.returning:
for i in range(len(possible_func_calls) - 1, -1, -1):
func_call = possible_func_calls[i][0]
if (not func_call.has_returned) and (func_call.return_address == instruction.destination):
possible_func_calls[i][0].has_returned = True
curr_spacing = possible_func_calls[i][1]
elif instruction.func_call:
possible_func_calls.append([instruction, curr_spacing])
curr_spacing += self.space_size

for pfc in possible_func_calls:
func_call = pfc[0]
if func_call.has_returned:
spacing = pfc[1]
buffer += (" " * spacing) + func_call.symbol + "()\n"

output.write(buffer)
output.close()
8 changes: 8 additions & 0 deletions rewrite/scripts/codeflow/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from symbols import Syms
from asm import ASMAnalyzer

syms = Syms()
syms.add_symbols("../../../symbols/gcc-syms-rewrite.txt")

asm = ASMAnalyzer(syms, "output.txt", 2)
asm.analyze_log("log.txt", "main")
Loading