diff --git a/include/ctr/jitpool.h b/include/ctr/jitpool.h new file mode 100644 index 000000000..d3f28791c --- /dev/null +++ b/include/ctr/jitpool.h @@ -0,0 +1,19 @@ +#pragma once + +#include + +typedef struct JitPool +{ + LinkedList free; + LinkedList taken; + + s32 maxItems; + u32 itemSize; + s32 poolSize; + void* ptrPoolData; +} JitPool; + +void JitPool_Clear(JitPool* AP); +void JitPool_Init(JitPool* AP, s32 maxItems, s32 itemSize); +Item* JitPool_Add(JitPool* AP); +void JitPool_Remove(JitPool* AP, Item* item); \ No newline at end of file diff --git a/include/ctr/list.h b/include/ctr/list.h new file mode 100644 index 000000000..29635920f --- /dev/null +++ b/include/ctr/list.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +typedef struct Item +{ + struct Item* next; + struct Item* prev; +} Item; + +typedef struct LinkedList +{ + Item* first; + Item* last; + s32 count; +} LinkedList; + +void LIST_Clear(LinkedList* list); +void LIST_AddFront(LinkedList* list, Item* item); +void LIST_AddBack(LinkedList* list, Item* item); +void* LIST_GetNextItem(Item* item); +void* LIST_GetFirstItem(LinkedList* list); +Item* LIST_RemoveMember(LinkedList* list, Item* item); +Item* LIST_RemoveFront(LinkedList* list); +Item* LIST_RemoveBack(LinkedList* list); +void LIST_Init(LinkedList* list, Item* item, s32 itemSize, s32 numItems); + +//not a real ND function, just a helper macro. The value of an "Item" is the memory right after the Item struct. +#define LIST_GetItem(itemPtr) ((void*)(((Item*)itemPtr) + 1)) \ No newline at end of file diff --git a/include/ctr/nd.h b/include/ctr/nd.h index 45b9b5b92..e07944904 100644 --- a/include/ctr/nd.h +++ b/include/ctr/nd.h @@ -36,4 +36,15 @@ void ND_COLL_CalculateTrianglePlane(const CollDCache* cache, CollVertex* v1, con void ND_COLL_LoadVerticeData(CollDCache* cache); s32 ND_COLL_BarycentricTest(TestVertex* t, const CollVertex* v1, const CollVertex* v2, const CollVertex* v3); void ND_COLL_TestTriangle(CollDCache* cache, const CollVertex* v1, const CollVertex* v2, const CollVertex* v3); -void ND_COLL_TestLeaf_Quadblock(const Quadblock* quadblock, CollDCache* cache); \ No newline at end of file +void ND_COLL_TestLeaf_Quadblock(const Quadblock* quadblock, CollDCache* cache); + +/* LIST */ +void ND_LIST_Init(LinkedList* list, Item* item, s32 itemSize, s32 numItems); + +/* MEMPACK */ +void* ND_MEMPACK_AllocMem(s32 size); + +/* MISC */ +//TODO: ensure that the signedness of s32 for both of these are correct +void* memset(void* dest, u8 val, s32 len); +void* memcpy(void* dest, const void* src, s32 count); \ No newline at end of file diff --git a/include/ctr/test.h b/include/ctr/test.h index 2cf6a28c0..ba630b8df 100644 --- a/include/ctr/test.h +++ b/include/ctr/test.h @@ -7,15 +7,19 @@ #include #include #include +#include +#include extern const char* s_nameTestedFunc; void TEST_WRAPPER(); -void LoadTestPatches(); +void TEST_LoadPatches(); +void TEST_Init(); +bool TEST_Memcmp(const void* expected, const void* actual, u32 n); u32 PatchFunction_Beg(u32* index, const char* funcName); void PatchFunction_End(u32 index); -u32 PrintSVectorDiff(const SVec3* expected, const SVec3* ret); -u32 PrintMatrixDiff(const Matrix* expected, const Matrix* ret, u32 cmpTrans); +u32 TEST_PrintSVectorDiff(const SVec3* expected, const SVec3* ret); +u32 TEST_PrintMatrixDiff(const Matrix* expected, const Matrix* ret, u32 cmpTrans); force_inline void FlushCache() { @@ -24,11 +28,26 @@ force_inline void FlushCache() ((void (*)())0xa0)(); } -#define BACKUP_ADDR 0x80400000 +#define TEST +#if defined(TEST) +#define DYNAMIC_ASSERT(expected, actual, msg) //TODO +#else +#define DYNAMIC_ASSERT(expected, actual, msg) +#endif + +#if defined(TEST) +#define STATIC_ASSERT //TODO +#else +#define STATIC_ASSERT +#endif + +#if defined(TEST) #define TEST_MATH_IMPL #define TEST_RNG_IMPL #define TEST_COLL_IMPL +#define TEST_LIST_IMPL +#endif #ifdef TEST_MATH_IMPL void TEST_MATH_Sin(u32 angle, s32 ret); @@ -51,16 +70,30 @@ force_inline void FlushCache() #endif #ifdef TEST_RNG_IMPL + typedef struct BDATA_RNG_Rand + { + u32 e_seed; //backup of 0x8008d424 + } BDATA_RNG_Rand; void BACKUP_RNG_Rand(); + void RESTORE_RNG_Rand(BDATA_RNG_Rand* restore); void TEST_RNG_Rand(); + + typedef struct BDATA_RNG_RandInt + { + RNGSeed e_gameTracker_seed; //backup of e_gameTracker->seed + } BDATA_RNG_RandInt; void BACKUP_RNG_RandInt(); + void RESTORE_RNG_RandInt(BDATA_RNG_RandInt* restore); 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 RESTORE_RNG_Rand(restore) #define TEST_RNG_Rand() #define BACKUP_RNG_RandInt() + #define RESTORE_RNG_RandInt(restore) #define TEST_RNG_RandInt(n, ret) #define TEST_RNG_PseudoRand(n, ret) #define TEST_RNG_Random(seed, ret) @@ -70,11 +103,40 @@ force_inline void FlushCache() void TEST_COLL_ProjectPointToEdge(const SVec3* v1, const SVec3* v2, const SVec3* point, const SVec3* ret); void TEST_COLL_CalculateTrianglePlane(const CollDCache* cache, CollVertex* v1, const CollVertex* v2, const CollVertex* v3, const CollVertex* ret); void TEST_COLL_LoadVerticeData(CollDCache* cache); - void TEST_COLL_LoadQuadblockData_LowLOD(CollDCache* cache, const Quadblock* quadblock, const CollDCache* ret); - void TEST_COLL_LoadQuadblockData_HighLOD(CollDCache* cache, const Quadblock* quadblock, const CollDCache* ret); + + typedef struct BDATA_COLL_LoadQuadblockData_LowLOD + { + CollDCache cache; //backup of *cache + } BDATA_COLL_LoadQuadblockData_LowLOD; + void BACKUP_COLL_LoadQuadblockData_LowLOD(CollDCache* cache); + void RESTORE_COLL_LoadQuadblockData_LowLOD(BDATA_COLL_LoadQuadblockData_LowLOD* restore, CollDCache* cache); + void TEST_COLL_LoadQuadblockData_LowLOD(const Quadblock* quadblock, CollDCache* cache); + + typedef struct BDATA_COLL_LoadQuadblockData_HighLOD + { + CollDCache cache; //backup of *cache + } BDATA_COLL_LoadQuadblockData_HighLOD; + void BACKUP_COLL_LoadQuadblockData_HighLOD(CollDCache* cache); + void RESTORE_COLL_LoadQuadblockData_HighLOD(BDATA_COLL_LoadQuadblockData_HighLOD* restore, CollDCache* cache); + void TEST_COLL_LoadQuadblockData_HighLOD(const Quadblock* quadblock, CollDCache* cache); + void TEST_COLL_BarycentricTest(TestVertex* t, const CollVertex* v1, const CollVertex* v2, const CollVertex* v3, const SVec3* pos, s32 ret); - void TEST_COLL_TestTriangle(CollDCache* cache, const CollVertex* v1, const CollVertex* v2, const CollVertex* v3, const CollDCache* ret); - void TEST_COLL_TestLeaf_Quadblock(const Quadblock* quadblock, CollDCache* cache, const CollDCache* ret); + + typedef struct BDATA_COLL_TestTriangle + { + CollDCache cache; //backup of *cache + } BDATA_COLL_TestTriangle; + void BACKUP_COLL_TestTriangle(CollDCache* cache); + void RESTORE_COLL_TestTriangle(BDATA_COLL_TestTriangle* restore, CollDCache* cache); + void TEST_COLL_TestTriangle(const CollVertex* v1, const CollVertex* v2, const CollVertex* v3, CollDCache* cache); + + typedef struct BDATA_COLL_TestLeaf_Quadblock + { + CollDCache cache; //backup of *cache + } BDATA_COLL_TestLeaf_Quadblock; + void BACKUP_COLL_TestLeaf_Quadblock(CollDCache* cache); + void RESTORE_COLL_TestLeaf_Quadblock(BDATA_COLL_TestLeaf_Quadblock* restore, CollDCache* cache); + void TEST_COLL_TestLeaf_Quadblock(const Quadblock* quadblock, CollDCache* cache); #else #define TEST_COLL_ProjectPointToEdge(out, v1, v2, point) #define TEST_COLL_CalculateTrianglePlane(cache, v1, v2, v3, ret) @@ -84,4 +146,16 @@ force_inline void FlushCache() #define TEST_COLL_BarycentricTest(t, v1, v2, v3, pos, ret) #define TEST_COLL_TestTriangle(cache, v1, v2, v3, ret) #define TEST_COLL_TestLeaf_Quadblock(quadblock, cache, ret) +#endif + +#ifdef TEST_LIST_IMPL + typedef struct BDATA_LIST_Init + { + void asdf; + } BDATA_LIST_Init; + void BACKUP_LIST_Init(const void* source); + void RESTORE_LIST_Init(BDATA_LIST_Init* restore, void* destination); + void TEST_LIST_Init(LinkedList* list, Item* item, s32 itemSize, s32 numItems); +#else + #define TEST_LIST_Init(list, item, itemSize, numItems) #endif \ No newline at end of file diff --git a/include/ctr/test_backup.h b/include/ctr/test_backup.h new file mode 100644 index 000000000..d1494f5a3 --- /dev/null +++ b/include/ctr/test_backup.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +#define BACKUP_STACK_BASE_ADDR ((u32)0x80400000u) +#define BACKUP_STACK_HEAD_PTR ((volatile u32*)(BACKUP_STACK_BASE_ADDR + 0x0)) +#define BACKUP_STACK_DATA_BEGIN ((u8*)(BACKUP_STACK_BASE_ADDR + 0x8)) +#define BACKUP_STACK_DATA_END ((u8*)(0x80500000u)) +#define BACKUP_ALIGN ((u32)4) + +void BACKUP_INIT(void); +void* BACKUP_PUSH(const void* src, u32 size); +void* BACKUP_PEEK(u32 depth, u32* out_size); +s32 BACKUP_POP(void); //add variable for "how many times to pop" +s32 BACKUP_POP_MULTIPLE(u32 count); \ No newline at end of file diff --git a/rewrite/src/exe/coll.c b/rewrite/src/exe/coll.c index 1b374af07..311e0c8ed 100644 --- a/rewrite/src/exe/coll.c +++ b/rewrite/src/exe/coll.c @@ -101,11 +101,12 @@ static void _COLL_LoadQuadblockData_LowLOD(CollDCache* cache, const Quadblock* q static void COLL_LoadQuadblockData_LowLOD(CollDCache* cache, const Quadblock* quadblock) { -#ifdef TEST_COLL_IMPL - *(CollDCache*)(BACKUP_ADDR) = *cache; -#endif + BACKUP_COLL_LoadQuadblockData_LowLOD(cache); //global state before + _COLL_LoadQuadblockData_LowLOD(cache, quadblock); - TEST_COLL_LoadQuadblockData_LowLOD((CollDCache*)(BACKUP_ADDR), quadblock, cache); + + BACKUP_COLL_LoadQuadblockData_LowLOD(cache); //global state after (result from decomp) + TEST_COLL_LoadQuadblockData_LowLOD(quadblock, cache); } /* Address: 0x8001f6f0 */ @@ -137,11 +138,12 @@ static void _COLL_LoadQuadblockData_HighLOD(CollDCache* cache, const Quadblock* static void COLL_LoadQuadblockData_HighLOD(CollDCache* cache, const Quadblock* quadblock) { -#ifdef TEST_COLL_IMPL - *(CollDCache*)(BACKUP_ADDR) = *cache; -#endif + BACKUP_COLL_LoadQuadblockData_HighLOD(cache); //global state before + _COLL_LoadQuadblockData_HighLOD(cache, quadblock); - TEST_COLL_LoadQuadblockData_HighLOD((CollDCache*)(BACKUP_ADDR), quadblock, cache); + + BACKUP_COLL_LoadQuadblockData_HighLOD(cache); //global state after (result from decomp) + TEST_COLL_LoadQuadblockData_HighLOD(quadblock, cache); } /* Address: 0x8001f928 */ @@ -294,6 +296,9 @@ static void _COLL_TestTriangle(CollDCache* cache, const CollVertex* v1, const Co } if ((distTriNextPos >= cache->inputHitRadius) || ((!triggerScript) && (distTriNextPos > distTriCurrPos))) { return; } + + + u32 crossedPlane = false; const SVec3 deltaPos = { .x = cache->inputNextPos.x - cache->collInput.quadblock.driverPos.x, @@ -382,12 +387,12 @@ static void _COLL_TestTriangle(CollDCache* cache, const CollVertex* v1, const Co static void COLL_TestTriangle(CollDCache* cache, const CollVertex* v1, const CollVertex* v2, const CollVertex* v3) { -#ifdef TEST_COLL_IMPL - const u32 backupAddr = BACKUP_ADDR + sizeof(CollDCache); - *(CollDCache*)(backupAddr) = *cache; -#endif - _COLL_TestTriangle(cache, v1, v2, v3); - TEST_COLL_TestTriangle((CollDCache*)(backupAddr), v1, v2, v3, cache); + BACKUP_COLL_TestTriangle(cache); //global state before + + _COLL_TestTriangle(cache, v1, v2, v3); + + BACKUP_COLL_TestTriangle(cache); //global state after (result from decomp) + TEST_COLL_TestTriangle(v1, v2, v3, cache); } /* Address: 0x80020064 */ @@ -450,19 +455,13 @@ static void _COLL_TestLeaf_Quadblock(const Quadblock* quadblock, CollDCache* cac void COLL_TestLeaf_Quadblock(const Quadblock* quadblock, CollDCache* cache) { -#ifdef TEST_COLL_IMPL - *(CollDCache*)(BACKUP_ADDR) = *cache; -#endif - _COLL_TestLeaf_Quadblock(quadblock, cache); -#ifdef TEST_COLL_IMPL - const u32 retAddr = BACKUP_ADDR + sizeof(CollDCache); - *(CollDCache*)(retAddr) = *cache; - *cache = *(CollDCache*)(BACKUP_ADDR); -#endif - TEST_COLL_TestLeaf_Quadblock(quadblock, cache, (CollDCache*)(retAddr)); -#ifdef TEST_COLL_IMPL - *cache = *(CollDCache*)(retAddr); -#endif + BACKUP_COLL_TestLeaf_Quadblock(cache); //global state before + + _COLL_TestLeaf_Quadblock(quadblock, cache); + + BACKUP_COLL_TestLeaf_Quadblock(cache); //global state after (result from decomp) + TEST_COLL_TestLeaf_Quadblock(quadblock, cache); + /* This is a hand written assembly function that breaks the ABI, and some callers expect the argument registers to be untouched */ __asm__ volatile("move $t9, %0" : : "r"((u32)quadblock)); diff --git a/rewrite/src/exe/jitpool.c b/rewrite/src/exe/jitpool.c new file mode 100644 index 000000000..e74f68f20 --- /dev/null +++ b/rewrite/src/exe/jitpool.c @@ -0,0 +1,43 @@ +#include +#include + +void JitPool_Clear(JitPool* AP) +{ + Item* item = (Item*)AP->ptrPoolData; + LIST_Clear(&AP->free); + LIST_Clear(&AP->taken); + for (s32 i = 0; i < AP->maxItems; i++) + { + LIST_AddFront(&AP->free, item); + //oddly, if AP->itemSize is not aligned to 4 bytes, this will align it DOWN to the nearest 4 byte boundary. + //will this cause clobbering? + item = (struct Item*)((u32)&item->next + (AP->itemSize & 0xfffffffc)); + } +} + +void JitPool_Init(JitPool* AP, s32 maxItems, s32 itemSize) +{ + memset((void*)AP, '\0', sizeof(JitPool)); + AP->maxItems = maxItems; + AP->itemSize = itemSize; + AP->poolSize = maxItems * itemSize; + void* poolData = ND_MEMPACK_AllocMem(maxItems * itemSize); + AP->ptrPoolData = poolData; + JitPool_Clear(AP); +} + +Item* JitPool_Add(JitPool* AP) +{ + Item* item = AP->free.first; + if (item != NULL) { + LIST_RemoveMember(&AP->free, item); + LIST_AddFront(&AP->taken, item); + } + return item; +} + +void JitPool_Remove(JitPool* AP, Item* item) +{ + LIST_RemoveMember(&AP->taken, item); + LIST_AddFront(&AP->free, item); +} \ No newline at end of file diff --git a/rewrite/src/exe/list.c b/rewrite/src/exe/list.c new file mode 100644 index 000000000..f34c373d0 --- /dev/null +++ b/rewrite/src/exe/list.c @@ -0,0 +1,112 @@ +#include + +void LIST_Clear(LinkedList* list) +{ + list->first = NULL; + list->last = NULL; + list->count = 0; +} + +void LIST_AddFront(LinkedList* list, Item* item) +{ + if (item == NULL) { return; } + + item->prev = NULL; + Item* oldFirst = list->first; + item->next = oldFirst; + + if (oldFirst == NULL) { list->last = item; } + else { list->first->prev = item; } + + list->first = item; + list->count++; +} + +void LIST_AddBack(LinkedList* list, Item* item) +{ + if (item == NULL) { return; } + + item->next = NULL; + Item* oldLast = list->last; + item->prev = oldLast; + + if (oldLast == NULL) { list->first = item; } + else { list->last->next = item; } + + list->last = item; + list->count++; +} + +void* LIST_GetNextItem(Item* item) +{ + return item->next; +} + +void* LIST_GetFirstItem(LinkedList* list) +{ + return list->first; +} + +Item* LIST_RemoveMember(LinkedList* list, Item* item) +{ + if (item == NULL || list->first == NULL) { return item; } + + if (item->prev == NULL) { list->first = item->next; } + else { item->prev->next = item->next; } + + if (item->next == NULL) { list->last = item->prev; } + else { item->next->prev = item->prev; } + + list->count--; + item->next = NULL; + item->prev = NULL; + return item; +} + +Item* LIST_RemoveFront(LinkedList* list) +{ + Item* removedItem = list->first; + if (removedItem == NULL) { return removedItem; } + + if (removedItem->prev == NULL) { list->first = removedItem->next; } + else { removedItem->prev->next = removedItem->next; } + + if (removedItem->next == NULL) { list->last = removedItem->prev; } + else { removedItem->next->prev = removedItem->prev; } + + list->count--; + removedItem->next = NULL; + removedItem->prev = NULL; + return removedItem; +} + +Item* LIST_RemoveBack(LinkedList* list) +{ + //for some reason, LIST_RemoveBack and LIST_RemoveFront aren't mirrors of each other. + Item* removedItem = list->last; + if (removedItem == NULL) { return removedItem; } + + if (list->first != NULL) + { + if (removedItem->prev == NULL) { list->first = removedItem->next; } + else { removedItem->prev->next = removedItem->next; } + + if (removedItem->next == NULL) { list->last = removedItem->prev; } + else { removedItem->next->prev = removedItem->prev; } + list->count--; + } + + removedItem->next = NULL; + removedItem->prev = NULL; + return removedItem; +} + +void LIST_Init(LinkedList* list, Item* item, s32 itemSize, s32 numItems) +{ + for(; numItems > 0; numItems--) + { + LIST_AddBack(list, item); + + item = (Item*)((s32)item + itemSize); + } +} \ No newline at end of file diff --git a/rewrite/src/exe/rng.c b/rewrite/src/exe/rng.c index d105f8bba..e45156fcd 100644 --- a/rewrite/src/exe/rng.c +++ b/rewrite/src/exe/rng.c @@ -9,8 +9,11 @@ /* Address: 0x8003ea28 */ u32 RNG_Rand() { - BACKUP_RNG_Rand(); + BACKUP_RNG_Rand(); //global state before + e_seed = (e_seed * RNG_MULT_FACTOR + RNG_INC_FACTOR) & 0xFFFF; + + BACKUP_RNG_Rand(); //global state after (result from decomp) TEST_RNG_Rand(); return e_seed; } @@ -18,9 +21,12 @@ u32 RNG_Rand() /* Address: 0x8003ea6c */ s32 RNG_RandInt(u32 n) { - BACKUP_RNG_RandInt(); + BACKUP_RNG_RandInt(); //global state before + const u32 rand = RNG_Random(&e_gameTracker->seed); const s32 ret = ((s32) ((rand & 0xFFFF) * n)) >> 16; + + BACKUP_RNG_RandInt(); //global state after (result from decomp) TEST_RNG_RandInt(n, ret); return ret; } diff --git a/rewrite/src/hooks/dll/load_decomp.c b/rewrite/src/hooks/dll/load_decomp.c index d5bdcfb46..8a7559c2d 100644 --- a/rewrite/src/hooks/dll/load_decomp.c +++ b/rewrite/src/hooks/dll/load_decomp.c @@ -14,7 +14,7 @@ void LoadDecomp() #ifndef REWRITE_PROFILER ND_LOAD_XnfFile("\\TESTS.BIN;1", testAddr, &dummy); - LoadTestPatches(); + TEST_Init(); #endif ND_LOAD_XnfFile("\\DECOMP.BIN;1", decompAddr, &dummy); diff --git a/rewrite/src/tests/test.c b/rewrite/src/tests/test.c index 071af047f..f5cc79eaa 100644 --- a/rewrite/src/tests/test.c +++ b/rewrite/src/tests/test.c @@ -1,4 +1,5 @@ #include +#include #define JMP(dest) ((((u32)dest) & 0x3FFFFFF) >> 2 | 0x8000000) #define NOP 0x0 @@ -34,7 +35,25 @@ FunctionPatch s_functions[] = const char* s_nameTestedFunc = nullptr; -void LoadTestPatches() +void TEST_Init() +{ + BACKUP_INIT(); + TEST_LoadPatches(); +} + +bool TEST_Memcmp(const void* expected, const void* actual, u32 n) +{ + bool failed = false; + const u8* pExpected = (const u8*)expected; + const u8* pActual = (const u8*)actual; + for (u32 i = 0; i < n; i++) + { + if (pExpected[i] != pActual[i]) { ND_printf("[%s] Test Failed:\nOffset %x: %d, got: %d\n", s_nameTestedFunc, i, (u32)pExpected[i], (u32)pActual[i]); failed = true; } + } + return failed; +} + +void TEST_LoadPatches() { const u32 funcCount = ARR_LEN(s_functions); for (u32 i = 0; i < funcCount; i++) @@ -79,7 +98,7 @@ void PatchFunction_End(u32 index) FlushCache(); } -u32 PrintSVectorDiff(const SVec3* expected, const SVec3* ret) +u32 TEST_PrintSVectorDiff(const SVec3* expected, const SVec3* ret) { u32 failed = 0; for (u32 i = 0; i < 3; i++) @@ -93,7 +112,7 @@ u32 PrintSVectorDiff(const SVec3* expected, const SVec3* ret) return failed; } -u32 PrintMatrixDiff(const Matrix* expected, const Matrix* ret, u32 cmpTrans) +u32 TEST_PrintMatrixDiff(const Matrix* expected, const Matrix* ret, u32 cmpTrans) { u32 failed = 0; for (u32 i = 0; i < 3; i++) diff --git a/rewrite/src/tests/test_backup.c b/rewrite/src/tests/test_backup.c new file mode 100644 index 000000000..a1872a0cc --- /dev/null +++ b/rewrite/src/tests/test_backup.c @@ -0,0 +1,86 @@ +#include +#include + +//this type isn't part of the public API +typedef struct FrameHeader { + u32 prev; + u32 size; +} FrameHeader; + +force_inline u32 align_up(u32 n, u32 a) +{ + u32 r = n % a; + return r ? (n + (a - r)) : n; +} + +force_inline u8* top_next_free_ptr(void) +{ + u32 head = *BACKUP_STACK_HEAD_PTR; + if (!head) { return BACKUP_STACK_DATA_BEGIN; } + + FrameHeader* h = (FrameHeader*)((u8*)0 + head); + u8* after = (u8*)(h + 1) + h->size; + u32 aligned = align_up((u32)(after - (u8*)0), BACKUP_ALIGN); + return (u8*)((u8*)0 + aligned); +} + +void BACKUP_INIT(void) +{ + *BACKUP_STACK_HEAD_PTR = 0; +} + +/* PUSH: + - If src == NULL: reserves 'size' bytes; returns writable payload pointer. + - If src != NULL: copies 'size' bytes from src into payload; returns payload pointer. + Returns NULL on OOM. */ +void* BACKUP_PUSH(const void* src, u32 size) +{ + u8* where = top_next_free_ptr(); + u32 need = align_up((u32)sizeof(FrameHeader) + size, BACKUP_ALIGN); + + if (where + need > BACKUP_STACK_DATA_END) { return 0; } + + FrameHeader* h = (FrameHeader*)where; + h->prev = *BACKUP_STACK_HEAD_PTR; + h->size = size; + + void* payload = (void*)(h + 1); + if (src && size) { memcpy(payload, src, (s32)size); } + + *BACKUP_STACK_HEAD_PTR = (u32)(where - (u8*)0); + return payload; +} + +/* PEEK depth n (0 = top). Returns payload pointer or NULL if out-of-range. + Optionally writes size to *out_size. */ +void* BACKUP_PEEK(u32 depth, u32* out_size) +{ + u32 addr = *BACKUP_STACK_HEAD_PTR; + FrameHeader* h = (FrameHeader*)((u8*)0 + addr); + + while (h && depth--) { + h = (FrameHeader*)((u8*)0 + h->prev); + } + if (!h) { return 0; } + + if (out_size) { *out_size = h->size; } + return (void*)(h + 1); +} + +/* POP top frame. Returns 0 on success, nonzero if empty. */ +s32 BACKUP_POP(void) +{ + u32 head = *BACKUP_STACK_HEAD_PTR; + if (!head) { return -1; } + + FrameHeader* h = (FrameHeader*)((u8*)0 + head); + *BACKUP_STACK_HEAD_PTR = h->prev; + return 0; +} + +s32 BACKUP_POP_MULTIPLE(u32 count) +{ + for (u32 i = 0; i < count; i++) { + if (BACKUP_POP() != 0) { return -1; } + } +} \ No newline at end of file diff --git a/rewrite/src/tests/test_coll.c b/rewrite/src/tests/test_coll.c index 5172951b8..936d1fca6 100644 --- a/rewrite/src/tests/test_coll.c +++ b/rewrite/src/tests/test_coll.c @@ -2,19 +2,7 @@ #ifdef TEST_COLL_IMPL -static u32 PrintDCacheDiff(const CollDCache* expected, const CollDCache* ret) -{ - u32 failed = false; - const u8* pExpected = (const u8*) expected; - const u8* pRet = (const u8*) ret; - const u32 len = sizeof(CollDCache); - for (u32 i = 0; i < len; i++) - { - if (pExpected[i] != pRet[i]) { ND_printf("[%s] Test Failed:\nOffset %x: %d, got: %d\n", s_nameTestedFunc, i, (u32) pExpected[i], (u32) pRet[i]); failed = true; } - } - return failed; -} - +#pragma region COLL_ProjectPointToEdge void TEST_COLL_ProjectPointToEdge(const SVec3* v1, const SVec3* v2, const SVec3* point, const SVec3* ret) { const u32 index = PatchFunction_Beg((u32*)(&ND_COLL_ProjectPointToEdge), "COLL_ProjectPointToEdge"); @@ -22,22 +10,26 @@ void TEST_COLL_ProjectPointToEdge(const SVec3* v1, const SVec3* v2, const SVec3* typedef void (*Func)(SVec3* out, const SVec3* v1, const SVec3* v2, const SVec3* point); Func func = (Func) TEST_WRAPPER; func(&expected, v1, v2, point); - PrintSVectorDiff(&expected, ret); + TEST_PrintSVectorDiff(&expected, ret); PatchFunction_End(index); } +#pragma endregion +#pragma region COLL_CalculateTrianglePlane void TEST_COLL_CalculateTrianglePlane(const CollDCache* cache, CollVertex* v1, const CollVertex* v2, const CollVertex* v3, const CollVertex* ret) { const u32 index = PatchFunction_Beg((u32*)(&ND_COLL_CalculateTrianglePlane), "COLL_CalculateTrianglePlane"); typedef void (*Func)(const CollDCache* cache, CollVertex* v1, const CollVertex* v2, const CollVertex* v3); Func func = (Func) TEST_WRAPPER; func(cache, v1, v2, v3); - PrintSVectorDiff(&v1->triNormal, &ret->triNormal); + TEST_PrintSVectorDiff(&v1->triNormal, &ret->triNormal); if (v1->planeDist != ret->planeDist) { ND_printf("[%s] Test Failed:\nDist: %d\nResult: %d\n", s_nameTestedFunc, v1->planeDist, ret->planeDist); } if (v1->normalDominantAxis != ret->normalDominantAxis) { ND_printf("[%s] Test Failed:\nAxis: %d\nResult: %d\n", s_nameTestedFunc, v1->normalDominantAxis, ret->normalDominantAxis); } PatchFunction_End(index); } +#pragma endregion +#pragma region COLL_LoadVerticeData void TEST_COLL_LoadVerticeData(CollDCache* cache) { CollVertex vertices[NUM_VERTICES_QUADBLOCK]; @@ -59,7 +51,7 @@ void TEST_COLL_LoadVerticeData(CollDCache* cache) func(cache); for (u32 i = 0; i < NUM_VERTICES_QUADBLOCK; i++) { - PrintSVectorDiff(&cache->quadblockCollVertices[i].pos, &vertices[i].pos); + TEST_PrintSVectorDiff(&cache->quadblockCollVertices[i].pos, &vertices[i].pos); if (cache->quadblockCollVertices[i].levVertex != vertices[i].levVertex) { ND_printf("[%s] Test Failed: levVertex at index %d\n", s_nameTestedFunc, i); @@ -69,56 +61,151 @@ void TEST_COLL_LoadVerticeData(CollDCache* cache) if (cache->quadblockFourthIndex != fourthIndex) { ND_printf("[%s] Test Failed:\nfourthIndex: %d\nResult:%d\n", s_nameTestedFunc, cache->quadblockFourthIndex, fourthIndex);} PatchFunction_End(index); } +#pragma endregion + +#pragma region COLL_LoadQuadblockData_LowLOD +void BACKUP_COLL_LoadQuadblockData_LowLOD(CollDCache* cache) +{ + BDATA_COLL_LoadQuadblockData_LowLOD backup = {}; + backup.cache = *cache; + BACKUP_PUSH(&backup, sizeof(backup)); +} + +void RESTORE_COLL_LoadQuadblockData_LowLOD(BDATA_COLL_LoadQuadblockData_LowLOD* restore, CollDCache* cache) +{ + *cache = restore->cache; +} -void TEST_COLL_LoadQuadblockData_LowLOD(CollDCache* cache, const Quadblock* quadblock, const CollDCache* ret) +void TEST_COLL_LoadQuadblockData_LowLOD(const Quadblock* quadblock, CollDCache* cache) { const u32 index = PatchFunction_Beg((u32*)(&ND_COLL_LoadQuadblockData_LowLOD), "COLL_LoadQuadblockData_LowLOD"); + + BDATA_COLL_LoadQuadblockData_LowLOD* before = (BDATA_COLL_LoadQuadblockData_LowLOD*)BACKUP_PEEK(1, NULL); + BDATA_COLL_LoadQuadblockData_LowLOD* resultFromDecomp = (BDATA_COLL_LoadQuadblockData_LowLOD*)BACKUP_PEEK(0, NULL); + RESTORE_COLL_LoadQuadblockData_LowLOD(before, cache); typedef void (*Func)(CollDCache* cache, const Quadblock* quadblock); Func func = (Func) TEST_WRAPPER; func(cache, quadblock); - PrintDCacheDiff(cache, ret); + BACKUP_COLL_LoadQuadblockData_LowLOD(cache); + BDATA_COLL_LoadQuadblockData_LowLOD* resultFromND = (BDATA_COLL_LoadQuadblockData_LowLOD*)BACKUP_PEEK(0, NULL); + + BACKUP_POP_MULTIPLE(3); + + TEST_Memcmp(&resultFromND->cache, &resultFromDecomp->cache, sizeof(resultFromND->cache)); PatchFunction_End(index); } +#pragma endregion + +#pragma region COLL_LoadQuadblockData_HighLOD +void BACKUP_COLL_LoadQuadblockData_HighLOD(CollDCache* cache) +{ + BDATA_COLL_LoadQuadblockData_HighLOD backup = {}; + backup.cache = *cache; + BACKUP_PUSH(&backup, sizeof(backup)); +} -void TEST_COLL_LoadQuadblockData_HighLOD(CollDCache* cache, const Quadblock* quadblock, const CollDCache* ret) +void RESTORE_COLL_LoadQuadblockData_HighLOD(BDATA_COLL_LoadQuadblockData_HighLOD* restore, CollDCache* cache) +{ + *cache = restore->cache; +} + +void TEST_COLL_LoadQuadblockData_HighLOD(const Quadblock* quadblock, CollDCache* cache) { const u32 index = PatchFunction_Beg((u32*)(&ND_COLL_LoadQuadblockData_HighLOD), "COLL_LoadQuadblockData_HighLOD"); + + BDATA_COLL_LoadQuadblockData_HighLOD* before = (BDATA_COLL_LoadQuadblockData_HighLOD*)BACKUP_PEEK(1, NULL); + BDATA_COLL_LoadQuadblockData_HighLOD* resultFromDecomp = (BDATA_COLL_LoadQuadblockData_HighLOD*)BACKUP_PEEK(0, NULL); + RESTORE_COLL_LoadQuadblockData_HighLOD(before, cache); typedef void (*Func)(CollDCache* cache, const Quadblock* quadblock); Func func = (Func) TEST_WRAPPER; func(cache, quadblock); - PrintDCacheDiff(cache, ret); + BACKUP_COLL_LoadQuadblockData_HighLOD(cache); + BDATA_COLL_LoadQuadblockData_HighLOD* resultFromND = (BDATA_COLL_LoadQuadblockData_HighLOD*)BACKUP_PEEK(0, NULL); + + BACKUP_POP_MULTIPLE(3); + + TEST_Memcmp(&resultFromND->cache, &resultFromDecomp->cache, sizeof(resultFromND->cache)); PatchFunction_End(index); } +#pragma endregion +#pragma region COLL_BarycentricTest void TEST_COLL_BarycentricTest(TestVertex* t, const CollVertex* v1, const CollVertex* v2, const CollVertex* v3, const SVec3* pos, s32 ret) { const u32 index = PatchFunction_Beg((u32*)(&ND_COLL_BarycentricTest), "COLL_BarycentricTest"); typedef s32 (*Func)(TestVertex* t, const CollVertex* v1, const CollVertex* v2, const CollVertex* v3); Func func = (Func) TEST_WRAPPER; const s32 expected = func(t, v1, v2, v3); - PrintSVectorDiff(&t->pos, pos); + TEST_PrintSVectorDiff(&t->pos, pos); if (expected != ret) { ND_printf("[%s] Test Failed:\nExpected: %d\nResult: %d\n", s_nameTestedFunc, expected, ret); } PatchFunction_End(index); } +#pragma endregion + +#pragma region COLL_TestTriangle +void BACKUP_COLL_TestTriangle(CollDCache* cache) +{ + BDATA_COLL_TestTriangle backup = {}; + backup.cache = *cache; + BACKUP_PUSH(&backup, sizeof(backup)); +} -void TEST_COLL_TestTriangle(CollDCache* cache, const CollVertex* v1, const CollVertex* v2, const CollVertex* v3, const CollDCache* ret) +void RESTORE_COLL_TestTriangle(BDATA_COLL_TestTriangle* restore, CollDCache* cache) +{ + *cache = restore->cache; +} + +void TEST_COLL_TestTriangle(const CollVertex* v1, const CollVertex* v2, const CollVertex* v3, CollDCache* cache) { const u32 index = PatchFunction_Beg((u32*)(&ND_COLL_TestTriangle), "COLL_TestTriangle"); + + BDATA_COLL_TestTriangle* before = (BDATA_COLL_TestTriangle*)BACKUP_PEEK(1, NULL); + BDATA_COLL_TestTriangle* resultFromDecomp = (BDATA_COLL_TestTriangle*)BACKUP_PEEK(0, NULL); + RESTORE_COLL_TestTriangle(before, cache); typedef void (*Func)(CollDCache* cache, const CollVertex* v1, const CollVertex* v2, const CollVertex* v3); Func func = (Func) TEST_WRAPPER; func(cache, v1, v2, v3); - PrintDCacheDiff(cache, ret); + BACKUP_COLL_TestTriangle(cache); + BDATA_COLL_TestTriangle* resultFromND = (BDATA_COLL_TestTriangle*)BACKUP_PEEK(0, NULL); + + BACKUP_POP_MULTIPLE(3); + + TEST_Memcmp(&resultFromND->cache, &resultFromDecomp->cache, sizeof(resultFromND->cache)); PatchFunction_End(index); } +#pragma endregion + +#pragma region COLL_TestLeaf_Quadblock +void BACKUP_COLL_TestLeaf_Quadblock(CollDCache* cache) +{ + BDATA_COLL_TestLeaf_Quadblock backup = {}; + backup.cache = *cache; + BACKUP_PUSH(&backup, sizeof(backup)); +} + +void RESTORE_COLL_TestLeaf_Quadblock(BDATA_COLL_TestLeaf_Quadblock* restore, CollDCache* cache) +{ + *cache = restore->cache; +} -void TEST_COLL_TestLeaf_Quadblock(const Quadblock* quadblock, CollDCache* cache, const CollDCache* ret) +void TEST_COLL_TestLeaf_Quadblock(const Quadblock* quadblock, CollDCache* cache) { const u32 index = PatchFunction_Beg((u32*)(&ND_COLL_TestLeaf_Quadblock), "COLL_TestLeaf_Quadblock"); + + BDATA_COLL_TestLeaf_Quadblock* before = (BDATA_COLL_TestLeaf_Quadblock*)BACKUP_PEEK(1, NULL); + BDATA_COLL_TestLeaf_Quadblock* resultFromDecomp = (BDATA_COLL_TestLeaf_Quadblock*)BACKUP_PEEK(0, NULL); + RESTORE_COLL_TestLeaf_Quadblock(before, cache); typedef void (*Func)(const Quadblock* quadblock, CollDCache* cache); Func func = (Func) TEST_WRAPPER; func(quadblock, cache); - PrintDCacheDiff(cache, ret); + BACKUP_COLL_TestLeaf_Quadblock(cache); + BDATA_COLL_TestLeaf_Quadblock* resultFromND = (BDATA_COLL_TestLeaf_Quadblock*)BACKUP_PEEK(0, NULL); + + BACKUP_POP_MULTIPLE(3); + + TEST_Memcmp(&resultFromND->cache, &resultFromDecomp->cache, sizeof(resultFromND->cache)); PatchFunction_End(index); } +#pragma endregion #endif // TEST_COLL_IMPL \ No newline at end of file diff --git a/rewrite/src/tests/test_list.c b/rewrite/src/tests/test_list.c new file mode 100644 index 000000000..320c6d4cd --- /dev/null +++ b/rewrite/src/tests/test_list.c @@ -0,0 +1,55 @@ +#include + +#ifdef TEST_LIST_IMPL + +#pragma region LIST_Init +void BACKUP_RNG_Rand() +{ + BDATA_RNG_Rand backup = { + .e_seed = e_seed + }; + BACKUP_PUSH(&backup, sizeof(backup)); +} + +void RESTORE_RNG_Rand(BDATA_RNG_Rand* restore) +{ + e_seed = restore->e_seed; +} + +void TEST_RNG_Rand() +{ + const u32 index = PatchFunction_Beg((u32*)(&ND_RNG_Rand), "RNG_Rand"); + + BDATA_RNG_Rand* before = (BDATA_RNG_Rand*)BACKUP_PEEK(1, NULL); + BDATA_RNG_Rand* resultFromDecomp = (BDATA_RNG_Rand*)BACKUP_PEEK(0, NULL); + RESTORE_RNG_Rand(before); + ND_RNG_Rand(); + BACKUP_RNG_Rand(); + BDATA_RNG_Rand* resultFromND = (BDATA_RNG_Rand*)BACKUP_PEEK(0, NULL); + + bool failedGlobalState = TEST_Memcmp(resultFromDecomp, resultFromND, sizeof(BDATA_RNG_Rand)); + + BACKUP_POP_MULTIPLE(3); + + if (failedGlobalState) { ND_printf("[%s] Test Failed (global state mismatch)\n", s_nameTestedFunc); } + if (resultFromND->e_seed != resultFromDecomp->e_seed) { ND_printf("[%s] Test Failed:\nExpected: %d\nResult: %d\n", s_nameTestedFunc, resultFromND->e_seed, resultFromDecomp->e_seed); } + PatchFunction_End(index); +} + +void BACKUP_LIST_Init(const void* source) +{ + +} + +void RESTORE_LIST_Init(BDATA_LIST_Init* restore, void* destination) +{ + +} + +void TEST_LIST_Init(LinkedList* list, Item* item, s32 itemSize, s32 numItems) +{ + const u32 index = PatchFunction_Beg((u32*)(&ND_LIST_Init), "LIST_Init"); +} +#pragma endregion + +#endif //TEST_LIST_IMPL \ No newline at end of file diff --git a/rewrite/src/tests/test_math.c b/rewrite/src/tests/test_math.c index 312c339a7..eddc74d81 100644 --- a/rewrite/src/tests/test_math.c +++ b/rewrite/src/tests/test_math.c @@ -2,6 +2,7 @@ #ifdef TEST_MATH_IMPL +#pragma region MATH_Sin void TEST_MATH_Sin(u32 angle, s32 ret) { const u32 index = PatchFunction_Beg((u32*)(&ND_MATH_Sin), "MATH_Sin"); @@ -9,7 +10,9 @@ void TEST_MATH_Sin(u32 angle, s32 ret) if (expected != ret) { ND_printf("[%s] Test Failed:\nInput: %d\nExpected: %d\nResult: %d\n", s_nameTestedFunc, angle, expected, ret); } PatchFunction_End(index); } +#pragma endregion +#pragma region MATH_Cos void TEST_MATH_Cos(u32 angle, s32 ret) { const u32 index = PatchFunction_Beg((u32*)(&ND_MATH_Cos), "MATH_Cos"); @@ -17,7 +20,9 @@ void TEST_MATH_Cos(u32 angle, s32 ret) if (expected != ret) { ND_printf("[%s] Test Failed:\nInput: %d\nExpected: %d\nResult: %d\n", s_nameTestedFunc, angle, expected, ret); } PatchFunction_End(index); } +#pragma endregion +#pragma region MATH_Sqrt void TEST_MATH_Sqrt(u32 n, u32 shift, u32 ret) { const u32 index = PatchFunction_Beg((u32*)(&ND_MATH_Sqrt), "MATH_Sqrt"); @@ -25,16 +30,20 @@ void TEST_MATH_Sqrt(u32 n, u32 shift, u32 ret) if (expected != ret) { ND_printf("[%s] Test Failed:\nInput: %d %d\nExpected: %d\nResult: %d\n", s_nameTestedFunc, n, shift, expected, ret); } PatchFunction_End(index); } +#pragma endregion +#pragma region MATH_GetInverseMatrixTransformation void TEST_MATH_GetInverseMatrixTransformation(const Matrix* matrix, const Matrix* ret) { Matrix out; const u32 index = PatchFunction_Beg((u32*)(&ND_MATH_GetInverseMatrixTransformation), "MATH_GetInverseMatrixTransformation"); ND_MATH_GetInverseMatrixTransformation(&out, matrix); - PrintMatrixDiff(&out, ret, true); + TEST_PrintMatrixDiff(&out, ret, true); PatchFunction_End(index); } +#pragma endregion +#pragma region MATH_VectorLength void TEST_MATH_VectorLength(const SVec3* vector, s32 ret) { const u32 index = PatchFunction_Beg((u32*)(&ND_MATH_VectorLength), "MATH_VectorLength"); @@ -42,31 +51,38 @@ void TEST_MATH_VectorLength(const SVec3* vector, s32 ret) if (expected != ret) { ND_printf("[%s] Test Failed:\nInput: %d %d %d\nExpected: %d\nResult: %d\n", s_nameTestedFunc, vector->x, vector->y, vector->z, expected, ret); } PatchFunction_End(index); } +#pragma endregion +#pragma region MATH_VectorNormalize void TEST_MATH_VectorNormalize(SVec3* vector, const SVec3* ret) { const u32 index = PatchFunction_Beg((u32*)(&ND_MATH_VectorNormalize), "MATH_VectorNormalize"); ND_MATH_VectorNormalize(vector); - PrintSVectorDiff(vector, ret); + TEST_PrintSVectorDiff(vector, ret); PatchFunction_End(index); } +#pragma endregion +#pragma region MATH_CombineMatrixTransformation void TEST_MATH_CombineMatrixTransformation(const Matrix* m, const Matrix* n, const Matrix* ret) { Matrix expected; const u32 index = PatchFunction_Beg((u32*)(&ND_MATH_CombineMatrixTransformation), "MATH_CombineMatrixTransformation"); ND_MATH_CombineMatrixTransformation(&expected, m, n); - PrintMatrixDiff(&expected, ret, true); + TEST_PrintMatrixDiff(&expected, ret, true); PatchFunction_End(index); } +#pragma endregion +#pragma region MATH_MatrixMultiplication void TEST_MATH_MatrixMultiplication(const Matrix* m, const Matrix* n, const Matrix* ret) { Matrix expected; const u32 index = PatchFunction_Beg((u32*)(&ND_MATH_MatrixMultiplication), "MATH_MatrixMultiplication"); ND_MATH_MatrixMultiplication(&expected, m, n); - PrintMatrixDiff(&expected, ret, false); + TEST_PrintMatrixDiff(&expected, ret, false); PatchFunction_End(index); } +#pragma endregion #endif // TEST_MATH_IMPL \ No newline at end of file diff --git a/rewrite/src/tests/test_rng.c b/rewrite/src/tests/test_rng.c index 6f7d5434a..02a1885fe 100644 --- a/rewrite/src/tests/test_rng.c +++ b/rewrite/src/tests/test_rng.c @@ -2,37 +2,77 @@ #ifdef TEST_RNG_IMPL +#pragma region RNG_Rand void BACKUP_RNG_Rand() { - u32* seedAddr = (u32*) BACKUP_ADDR; - *seedAddr = e_seed; + BDATA_RNG_Rand backup = { + .e_seed = e_seed + }; + BACKUP_PUSH(&backup, sizeof(backup)); +} + +void RESTORE_RNG_Rand(BDATA_RNG_Rand* restore) +{ + e_seed = restore->e_seed; } void TEST_RNG_Rand() { const u32 index = PatchFunction_Beg((u32*)(&ND_RNG_Rand), "RNG_Rand"); - const u32 ret = e_seed; - e_seed = *(u32*) BACKUP_ADDR; + + BDATA_RNG_Rand* before = (BDATA_RNG_Rand*)BACKUP_PEEK(1, NULL); + BDATA_RNG_Rand* resultFromDecomp = (BDATA_RNG_Rand*)BACKUP_PEEK(0, NULL); + RESTORE_RNG_Rand(before); ND_RNG_Rand(); - if (e_seed != ret) { ND_printf("[%s] Test Failed:\nExpected: %d\nResult: %d\n", s_nameTestedFunc, e_seed, ret); } + BACKUP_RNG_Rand(); + BDATA_RNG_Rand* resultFromND = (BDATA_RNG_Rand*)BACKUP_PEEK(0, NULL); + + bool failedGlobalState = TEST_Memcmp(resultFromDecomp, resultFromND, sizeof(BDATA_RNG_Rand)); + + BACKUP_POP_MULTIPLE(3); + + if (failedGlobalState) { ND_printf("[%s] Test Failed (global state mismatch)\n", s_nameTestedFunc); } + if (resultFromND->e_seed != resultFromDecomp->e_seed) { ND_printf("[%s] Test Failed:\nExpected: %d\nResult: %d\n", s_nameTestedFunc, resultFromND->e_seed, resultFromDecomp->e_seed); } PatchFunction_End(index); } +#pragma endregion +#pragma region RNG_RandInt void BACKUP_RNG_RandInt() { - RNGSeed* seedAddr = (RNGSeed*) BACKUP_ADDR; - *seedAddr = e_gameTracker->seed; + BDATA_RNG_RandInt backup = { + .e_gameTracker_seed = e_gameTracker->seed + }; + BACKUP_PUSH(&backup, sizeof(backup)); +} + +void RESTORE_RNG_RandInt(BDATA_RNG_RandInt* restore) +{ + e_gameTracker->seed = restore->e_gameTracker_seed; } void TEST_RNG_RandInt(u32 n, s32 ret) { const u32 index = PatchFunction_Beg((u32*)(&ND_RNG_RandInt), "RNG_RandInt"); - e_gameTracker->seed = *(RNGSeed*) BACKUP_ADDR; + + BDATA_RNG_RandInt* before = (BDATA_RNG_RandInt*)BACKUP_PEEK(1, NULL); + BDATA_RNG_RandInt* resultFromDecomp = (BDATA_RNG_RandInt*)BACKUP_PEEK(0, NULL); + RESTORE_RNG_RandInt(before); const s32 expected = ND_RNG_RandInt(n); + BACKUP_RNG_RandInt(); + BDATA_RNG_RandInt* resultFromND = (BDATA_RNG_RandInt*)BACKUP_PEEK(0, NULL); + + bool failedGlobalState = TEST_Memcmp(resultFromDecomp, resultFromND, sizeof(BDATA_RNG_RandInt)); + + BACKUP_POP_MULTIPLE(3); + + if (failedGlobalState) { ND_printf("[%s] Test Failed (global state mismatch)\n", s_nameTestedFunc); } if (expected != ret) { ND_printf("[%s] Test Failed:\nExpected: %d\nResult: %d\n", s_nameTestedFunc, expected, ret); } PatchFunction_End(index); } +#pragma endregion +#pragma region RNG_PseudoRand void TEST_RNG_PseudoRand(u16 n, u16 ret) { const u32 index = PatchFunction_Beg((u32*)(&ND_RNG_PseudoRand), "RNG_PseudoRand"); @@ -40,7 +80,9 @@ void TEST_RNG_PseudoRand(u16 n, u16 ret) if (expected != ret) { ND_printf("[%s] Test Failed:\nExpected: %d\nResult: %d\n", s_nameTestedFunc, expected, ret); } PatchFunction_End(index); } +#pragma endregion +#pragma region RNG_Random void TEST_RNG_Random(RNGSeed* seed, const RNGSeed* ret) { const u32 index = PatchFunction_Beg((u32*)(&ND_RNG_Random), "RNG_Random"); @@ -50,5 +92,6 @@ void TEST_RNG_Random(RNGSeed* seed, const RNGSeed* ret) if (expected != ret->b) { ND_printf("[%s] Test Failed:\nExpected: %d\nret: %d\n", s_nameTestedFunc, expected, ret->b); } PatchFunction_End(index); } +#pragma endregion #endif // TEST_RNG_IMPL \ No newline at end of file