diff --git a/conker/src/game_131F30.c b/conker/src/game_131F30.c index e3fb0776..d379ad4c 100644 --- a/conker/src/game_131F30.c +++ b/conker/src/game_131F30.c @@ -2,12 +2,74 @@ #include "functions.h" #include "variables.h" +// D_800DCE50 holds per-slot object list heads; D_800A5770 is declared as a +// bare s32 in variables.h but is used as a 2 element array (see func_151494E0). +typedef struct FileNode { + u8 pad0[0x8]; + struct FileNode *unk8; + u8 padC[0x7]; + u8 unk13; + u8 pad14[0x14]; + s32 unk28; +} FileNode; + +extern FileNode *D_800DCE50[2][104]; + +typedef struct { s32 unk0; s32 unk4; u8 unk8; } Blk; +typedef struct { s32 unk0; Blk *unk4; struct127 *unk8; } Arg; +extern u32 *D_800DBF94; +extern Blk D_800A2380; +extern Blk D_800A238C; +extern Blk D_800A2398; #pragma GLOBAL_ASM("asm/nonmatchings/game_131F30/func_15104A80.s") #pragma GLOBAL_ASM("asm/nonmatchings/game_131F30/func_15104C44.s") #pragma GLOBAL_ASM("asm/nonmatchings/game_131F30/func_15104FF8.s") +// PERMUTER CANDIDATE. Best attempt: score 16. Every instruction, register and immediate matches; +// the only difference is the frame's local-slot base - the target spills the +// func_1000EF40 address to 0x3c(sp) and the &arg0->unk28 pointer to 0x40(sp), +// while this spills them to 0x38(sp)/0x3c(sp) (frame size 0x48 is correct). +// The target's frame reserves one extra word below the spill area; adding any +// third local shifts the spills correctly but grows the frame to 0x50. +// +// // structs.h lacks the s16 x/y/z at the head of struct205 and a field at +// // offset 8 in struct206, so file-local equivalents are used. +// typedef struct { +// s16 unk0; +// s16 unk2; +// s16 unk4; +// u8 pad6[0xE]; +// u8 unk14; +// } FileObj; +// +// typedef struct { +// FileObj *unk0; +// u16 unk4; +// u8 pad6[0x2]; +// s16 unk8; +// } FileSub; +// +// typedef struct { +// u8 pad0[0x28]; +// FileSub unk28; +// } FileOwner; +// +// void func_15104FF8(FileOwner *arg0, s32 arg1, u8 arg2) { +// FileSub *sub = &arg0->unk28; +// +// if (arg2 == 0x38) { +// if (sub->unk0->unk14 == 1) { +// FileObj *obj = sub->unk0; +// +// sub->unk8 = 300; +// func_1000FD38(func_1000EF40, obj, 0); +// func_1000FA64(566, sub->unk0->unk0, sub->unk0->unk2, sub->unk0->unk4, +// 16384, 1500, 1000, func_1000EF40, sub->unk0, 0, 8, 0); +// } +// } +// } #pragma GLOBAL_ASM("asm/nonmatchings/game_131F30/func_151050B0.s") @@ -148,42 +210,29 @@ void func_15105BC8(struct204 *arg0) { } } -#pragma GLOBAL_ASM("asm/nonmatchings/game_131F30/func_15105C24.s") -// void *func_15105C24(s32 arg0) { -// s32 temp_t4; -// s32 temp_t5; -// void *temp_a0; -// void *temp_a0_2; -// s32 phi_v1; -// s32 phi_v0; -// void *phi_a0; -// -// phi_v0 = 0; -// loop_1: -// phi_v1 = 0; -// loop_2: -// temp_a0 = *((((((phi_v1 * 4) - phi_v1) * 4) + phi_v1) << 5) + 0x800DCE50 + (((phi_v0 * 4) + 0x800A0000)->unk5770 * 4)); -// phi_a0 = temp_a0; -// if (temp_a0 != 0) { -// loop_3: -// if ((phi_a0->unk13 == 0x2E) && (arg0 == phi_a0->unk28)) { -// return phi_a0; -// } -// temp_a0_2 = phi_a0->unk8; -// phi_a0 = temp_a0_2; -// if (temp_a0_2 != 0) { -// goto loop_3; -// } -// } -// temp_t4 = (phi_v1 + 1) & 0xFF; -// phi_v1 = temp_t4; -// if (temp_t4 < 2) { -// goto loop_2; -// } -// temp_t5 = (phi_v0 + 1) & 0xFF; -// phi_v0 = temp_t5; -// if (temp_t5 < 2) { -// goto loop_1; -// } -// return NULL; -// } +FileNode *func_15105C24(s32 arg0) { + u8 i; + u8 j; + FileNode *p; + + for (i = 0; i < 2; i++) { + j = 0; +inner: + p = D_800DCE50[j][(&D_800A5770)[i]]; + if (p != NULL) { +list: + if ((p->unk13 == 0x2E) && (arg0 == p->unk28)) { + return p; + } + p = p->unk8; + if (p != NULL) { + goto list; + } + } + j++; + if (j < 2) { + goto inner; + } + } + return NULL; +} diff --git a/conker/src/game_14FF90.c b/conker/src/game_14FF90.c index 236f981f..fdd55b06 100644 --- a/conker/src/game_14FF90.c +++ b/conker/src/game_14FF90.c @@ -269,27 +269,23 @@ void func_15122AE0(void) { // } #pragma GLOBAL_ASM("asm/nonmatchings/game_14FF90/func_15123070.s") -// NON-MATCHING: 1 missing lui +// NON-MATCHING (best 165): logic byte-identical EXCEPT the if-branch's `b` delay slot. +// Target duplicates the -360.0f `lui at,0xc3b4` into BOTH predecessors (b delay slot + +// else path) so the merge is a single `mtc1`; IDO here fills the b delay slot with the +// `swc1 ...0x390` store instead, leaving the merge to do the lui (1 fewer lui in the +// if-path). Delay-slot/PRE decision, not controllable from C. PERMUTER CANDIDATE. // void func_15123070(struct108 *arg0) { // f32 temp_f0; // struct17 tmp; -// // if ((arg0->unk6C8 != 0) && ((arg0->unk6FC == 10) || (arg0->unk6FC == 14))) { -// func_15048F90(&arg0->unk618, &arg0->unk2A4, &tmp, arg0); +// func_15048F90(&arg0->unk618, &arg0->unk2A4, &tmp); // arg0->unk390 = arg0->unk37C - func_15048FC8(&tmp); // } else { // temp_f0 = arg0->unk3D0->unk40 - arg0->unk37C - 180.0f; -// -// while (temp_f0 < 0.0f) { -// temp_f0 += 360.0f; -// } -// +// while (temp_f0 < 0.0f) { temp_f0 += 360.0f; } // arg0->unk390 = temp_f0; // } -// -// while (arg0->unk390 < -360.0f) { -// arg0->unk390 += 360.0f; -// } +// while (arg0->unk390 < -360.0f) { arg0->unk390 += 360.0f; } // } #pragma GLOBAL_ASM("asm/nonmatchings/game_14FF90/func_1512317C.s") @@ -306,34 +302,154 @@ void func_15123508(struct108 *arg0) { } #pragma GLOBAL_ASM("asm/nonmatchings/game_14FF90/func_15123568.s") +// NON-MATCHING (best 610): logic byte-identical (every lw/andi/branch/store/call matches). +// The only diffs are temp-register rotation: the constant 4 stays in v1 the whole function +// in the target (so the v0>0 block's subtraction lands in t1, shifting the L600 temps to +// t3..t9), whereas IDO here reuses v1 (dead-after-guard) for the subtraction and the const 1, +// shifting temps down by two. Register-allocation only. PERMUTER CANDIDATE. +// void func_15123568(struct108 *arg0) { +// s32 v0, t1; +// if ((arg0->unk3D4->unk120 == 4) && (arg0->unk2C != 0x40) && (*((u8 *) arg0 + 0x92C) == 0)) { +// arg0->unk1B4 = 1; +// *(f32 *) ((u8 *) arg0 + 0x7C0) = D_800A34D8; +// func_15124B18(arg0); +// arg0->unk198 = 0.0f; +// arg0->unk190 = 0.0f; +// return; +// } +// v0 = *(s32 *) &arg0->unk7B8; +// if (v0 > 0) { +// t1 = v0 - D_800BE9E4; +// *(s32 *) &arg0->unk7B8 = t1; +// if (t1 < 0) *(s32 *) &arg0->unk7B8 = 0; +// return; +// } +// if ((arg0->unk84 & 4) == 0) return; +// if ((arg0->unk36A & 8) == 0) return; +// if (arg0->unkDC == 4) return; +// if ((arg0->unk6C8 != 0) && (arg0->unk6FC != 4)) return; +// if ((arg0->unk3D0->in_water == 1) && (arg0->unk1B4 == 1)) return; +// v0 = arg0->unk1B4; +// if (v0 == 1) return; +// do { +// arg0->unk1B4 = v0 - 1; +// v0 = arg0->unk1B4; +// if (v0 <= 0) { arg0->unk1B4 = 3; v0 = arg0->unk1B4; } +// } while (((1 << v0) & arg0->unk1E0) == 0); +// func_15124B18(arg0); +// func_15125608(arg0); +// *(s32 *) &arg0->unk7B8 = 20; +// } + #pragma GLOBAL_ASM("asm/nonmatchings/game_14FF90/func_151236D0.s") -#pragma GLOBAL_ASM("asm/nonmatchings/game_14FF90/func_15123934.s") -// NON-MATCHING: not sure what is up with arg0 -// s32 func_15123934(struct108 *arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) { -// struct108 *temp_v0; -// struct108 *temp_v1; -// -// temp_v1 = &arg0[arg4]; // ??? -// if (temp_v1->unk20C == 0) { -// temp_v1->unk2 = arg0->unk0; -// temp_v0 = &arg0[arg4 * 4]; // ??? -// temp_v0->unk30 = arg0->unk2C; -// temp_v0->unk88 = arg0->unk84; -// temp_v0->unkE0 = arg0->unkDC; -// temp_v0->unk138 = arg0->unk134; -// temp_v1->unk1B6[0] = arg0->unk1B4; -// temp_v1->unk1E2 = arg0->unk1E0; -// arg0->unk2C = arg1; -// arg0->unkDC = arg2; -// arg0->unk134 = arg3; -// temp_v1->unk20C = 1; -// func_15125394(); -// return 1; +// NON-MATCHING (best 645): logic byte-identical (all ~100 instrs of the flag blocks match +// exactly, plus every andi/lw/branch/jal of the dispatch). The only diffs are in the ~13-instr +// dispatch tail: the target hoists the shared-block `unk5F0` load into the a2-skip branch's +// branch-likely delay slot (bnel) and keeps the value in v1 (with b0=v1&1 filling the beqz-a2 +// delay); IDO here fills that delay with nop and loads f into v0 after the beqz, cascading +// register renames. Same CFG, delay-slot/register-alloc only. PERMUTER CANDIDATE. +// (unk3D4 is declared struct127* in the header but really points to a larger struct; below +// uses a file-local A3D4 view. func_1515BAxx take the unk23D byte.) +// extern void func_1515BA10(s32); func_1515BA1C; func_1515BA48; func_1515BA54; func_1515BA80; func_1515BAAC; +// typedef struct { u8 pad0[8]; s16 unk8; u8 pad0A[0xC]; u8 unk16; u8 pad17[0x37]; +// u8 unk4E; u8 pad4F[0x46]; u8 unk95; u8 pad96[6]; s32 unk9C; } A3D4; +// void func_151236D0(struct108 *arg0) { +// s32 a2 = arg0->unk5F0 & 1; +// if (arg0->unk3D4 != NULL) { +// if ((((A3D4 *) arg0->unk3D4)->unk9C != 0) || +// ((((A3D4 *) arg0->unk3D4)->unk95 != 0) && (((u8 *) arg0->unk3D0)[0x137] == 0))) +// arg0->unk5F0 |= 0x40; +// else arg0->unk5F0 &= ~0x40; +// if (arg0->unk3D0->unk28 == 0.0f) arg0->unk5F0 &= ~0x400; +// if ((((A3D4 *) arg0->unk3D4)->unk8 != 0) && (((A3D4 *) arg0->unk3D4)->unk16 == 0)) +// arg0->unk5F0 |= 8; +// else arg0->unk5F0 &= ~8; +// if (((A3D4 *) arg0->unk3D4)->unk4E == 2) arg0->unk5F0 |= 0x80; +// else arg0->unk5F0 &= ~0x80; +// } +// if ((arg0->unk2FC <= *(f32 *) ((u8 *) arg0 + 0x360)) && (arg0->unk2C != 0x100)) +// arg0->unk5F0 |= 1; +// else arg0->unk5F0 &= ~1; +// if (arg0->unk23C != 0) { +// if (arg0->unk5F0 & 1) func_1515BA80(arg0->unk23D); +// else { func_1515BA48(arg0->unk23D); *(f32 *) ((u8 *) arg0 + 0x7B0) = 0.0f; } +// return; +// } +// if (a2 == 0) { +// s32 g = arg0->unk5F0; +// if (g & 1) { +// if ((g & 4) || ((arg0->unk2C << 13) < 0)) func_1515BA80(arg0->unk23D); +// else func_1515BAAC(arg0->unk23D); +// return; +// } +// } +// { s32 f = arg0->unk5F0; s32 b0 = f & 1; +// if ((a2 != 0) && (b0 == 0)) { +// if ((f & 4) || ((arg0->unk2C << 13) < 0)) { func_1515BA48(arg0->unk23D); *(f32 *) ((u8 *) arg0 + 0x7B0) = 0.0f; } +// else { func_1515BA54(arg0->unk23D); func_15124B18(arg0); } +// return; +// } +// if (b0) func_1515BA1C(arg0->unk23D); else func_1515BA10(arg0->unk23D); // } -// return 0; // } +// structs.h models the parallel "state stack" members of struct108 as scalars +// followed by padding; they are really 0x15-entry arrays (and unk1B6 is signed). +// Declare a file-local view here since shared headers may not be edited. +typedef struct { + /* 0x000 */ u16 unk0; + /* 0x002 */ u16 unk2[0x15]; + /* 0x02C */ s32 unk2C; + /* 0x030 */ s32 unk30[0x15]; + /* 0x084 */ s32 unk84; + /* 0x088 */ s32 unk88[0x15]; + /* 0x0DC */ s32 unkDC; + /* 0x0E0 */ s32 unkE0[0x15]; + /* 0x134 */ s32 unk134; + /* 0x138 */ s32 unk138[0x15]; + /* 0x18C */ u8 pad18C[0x28]; + /* 0x1B4 */ s16 unk1B4; + /* 0x1B6 */ s16 unk1B6[0x15]; + /* 0x1E0 */ s16 unk1E0; + /* 0x1E2 */ s16 unk1E2[0x15]; + /* 0x20C */ s16 unk20C[0x15]; +} StateStack; + +#define SS(p) ((StateStack *) (p)) + +s32 func_15123934(struct108 *arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) { + if (SS(arg0)->unk20C[arg4] == 0) { + SS(arg0)->unk2[arg4] = SS(arg0)->unk0; + SS(arg0)->unk30[arg4] = SS(arg0)->unk2C; + SS(arg0)->unk88[arg4] = SS(arg0)->unk84; + SS(arg0)->unkE0[arg4] = SS(arg0)->unkDC; + SS(arg0)->unk138[arg4] = SS(arg0)->unk134; + SS(arg0)->unk1B6[arg4] = SS(arg0)->unk1B4; + SS(arg0)->unk1E2[arg4] = SS(arg0)->unk1E0; + SS(arg0)->unk2C = arg1; + SS(arg0)->unkDC = arg2; + SS(arg0)->unk134 = arg3; + SS(arg0)->unk20C[arg4] = 1; + func_15125394(arg0); + return 1; + } + return 0; +} -#pragma GLOBAL_ASM("asm/nonmatchings/game_14FF90/func_151239CC.s") +s32 func_151239CC(struct108 *arg0, s32 arg1) { + if (SS(arg0)->unk20C[arg1] != 0) { + SS(arg0)->unk0 = SS(arg0)->unk2[arg1]; + SS(arg0)->unk2C = SS(arg0)->unk30[arg1]; + SS(arg0)->unkDC = SS(arg0)->unkE0[arg1]; + SS(arg0)->unk84 = SS(arg0)->unk88[arg1]; + SS(arg0)->unk134 = SS(arg0)->unk138[arg1]; + SS(arg0)->unk1B4 = SS(arg0)->unk1B6[arg1]; + SS(arg0)->unk1E0 = SS(arg0)->unk1E2[arg1]; + func_15124B18(arg0); + SS(arg0)->unk20C[arg1] = 0; + return 1; + } + return 0; +} #pragma GLOBAL_ASM("asm/nonmatchings/game_14FF90/func_15123A54.s") void func_15124770(struct108 *arg0, s32 arg1) { @@ -458,15 +574,33 @@ s32 func_151253CC(struct108 *arg0) { return 0; } -// no idea what going on here -#pragma GLOBAL_ASM("asm/nonmatchings/game_14FF90/func_15125490.s") +s32 func_15125490(struct108 *arg0) { + struct127 *temp_v0 = arg0->unk3D0; + s32 temp_v1; + + if (temp_v0->in_water == 1) { + temp_v1 = (s32) fabsf(temp_v0->y_position - temp_v0->unk118); + if (temp_v1 < 100) { + return 0; + } + if (temp_v1 >= 301) { + return 1; + } + } else { + return 0; + } +} #pragma GLOBAL_ASM("asm/nonmatchings/game_14FF90/func_151254F4.s") -// NON-MATCHING: first statements in wrong order +// NON-MATCHING (best 120): body is byte-identical; ONLY the prologue const-load ordering +// differs. Target emits `lui at,%hi(D_800A352C); lwc1 f0,%lo(...)` BEFORE `sw ra`/`sw a1`; +// IDO here schedules the two spills first. Inlining the const (no temp) is worse (960); +// changing store order is worse (558). Could not force the const-load ahead of the register +// spills from C. PERMUTER CANDIDATE (prologue scheduling). // void func_151254F4(struct108 *arg0, s32 arg1) { -// f32 tmp = D_800A352C; -// arg0->unk3A0 = tmp * arg0->unk380; -// arg0->unk398 = tmp * arg0->unk388; +// f32 temp = D_800A352C; +// arg0->unk3A0 = arg0->unk380 * temp; +// arg0->unk398 = arg0->unk388 * temp; // func_15124AB4(arg0); // func_151239CC(arg0, 1); // arg0->unk3D4->unk198 = 0; @@ -498,8 +632,17 @@ void func_15125608(struct108 *arg0) { arg0->unk250 = 2.5f; } -// ??? #pragma GLOBAL_ASM("asm/nonmatchings/game_14FF90/func_15125628.s") +// NON-MATCHING (best 900): logic below is correct, but IDO materializes each global's +// address once (lui;addiu base, reused for load+store with 0-offset) whereas the target +// keeps separate lui + %lo-in-offset for load and store (no address CSE). Tried temp-split +// load/store across the branch; IDO still CSEs the base. PERMUTER CANDIDATE (addressing-idiom). +// void func_15125628(void) { +// if (D_800DBFF4[0] != 0) D_800DBFF4[0]--; +// if (D_800DBFF5 != 0) D_800DBFF5--; +// if (D_800DBFF6 != 0) D_800DBFF6--; +// if ((&D_800DBFF6)[1] != 0) (&D_800DBFF6)[1]--; +// } void func_15125690(struct108 *arg0, s32 arg1) { u8 *temp_v0 = &D_800DBFF4[arg0->unk23D]; @@ -509,8 +652,86 @@ void func_15125690(struct108 *arg0, s32 arg1) { } } -#pragma GLOBAL_ASM("asm/nonmatchings/game_14FF90/func_151256BC.s") +extern void func_1508EF80(f32 *arg0, f32 *arg1, f32 arg2, f32 *arg3); + +#define F29C (*(f32 *) ((u8 *) arg0 + 0x29C)) +#define F5EC (*(f32 *) ((u8 *) arg0 + 0x5EC)) + +void func_151256BC(struct108 *arg0) { + f32 twoPi; + f32 f12; + f32 m1; + f32 m2; + u32 v; + + if ((arg0->unk2C & 0x80000) || + (((arg0->unk5F0 & 8) != 0) && ((arg0->unk2C << 13) >= 0))) { + v = func_150ADA20(); + twoPi = D_800A3534; + F29C += 2.0f * (f32) (v % 3) * D_800A3538 * D_800BE9A4; + while (twoPi < F29C) { + F29C -= twoPi; + } + F5EC += ((sinf(F29C) * 4.0f) - F5EC) * D_800A353C; + f12 = F5EC * D_800A3540; + if (arg0->unk2C & 0x80000) { + m1 = 4.0f; + m2 = 4.0f; + } else { + m1 = 1.0f; + m2 = 20.0f; + } + func_1508EF80(&arg0->unk2F8, &arg0->unk2BC, f12 * m1, &arg0->unk2F8); + func_1508EF80(&arg0->unk2BC, &arg0->unk2F8, f12 * m2, &arg0->unk2BC); + } else { + if (F29C == 0.0f) { + return; + } + F29C -= F29C * D_800A3544; + F5EC += ((sinf(F29C) * 4.0f) - F5EC) * D_800A3548; + f12 = F5EC * D_800A354C; + func_1508EF80(&arg0->unk2F8, &arg0->unk2BC, f12, &arg0->unk2F8); + func_1508EF80(&arg0->unk2BC, &arg0->unk2F8, f12 * 20.0f, &arg0->unk2BC); + } +} + +#undef F29C +#undef F5EC + #pragma GLOBAL_ASM("asm/nonmatchings/game_14FF90/func_15125924.s") +// NON-MATCHING (best 450): logic byte-identical (every call/store matches). Two coupled +// codegen diffs in the first block: (1) phi_a1/phi_v1 land in swapped registers (target +// a1/v1, IDO here v1/a1), (2) target loads arg0->unk2C in BOTH arms of the unk3D4 if +// (plain beqz + b + duplicated lw), IDO here uses a branch-likely beqzl (one lw in the +// delay slot), leaving the tail shifted by one instruction. Neither controllable from C. +// PERMUTER CANDIDATE. +// void func_15125924(struct108 *arg0) { +// s32 phi_a1 = 0, phi_v1 = 0; +// f32 temp_f0; +// if (arg0->unk3D4 != NULL) { +// phi_v1 = ((u8 *) arg0->unk3D4)[0x4E]; +// phi_a1 = (s32) arg0->unk3D4->z_position; +// } +// if ((arg0->unk2C & 0x40) != 0) return; +// if (((arg0->unk84 & 0x4000) == 0) && (arg0->unk3D0->unk102 == 0)) return; +// if ((func_15125490(arg0) != 0) && (phi_v1 == 0) && (phi_a1 == 0)) { +// if ((func_15123934(arg0, 0x80, 1, 1, 0xD) != 0) && (arg0->unk6C8 == 0)) { +// func_15124B18(arg0); +// arg0->unk5F0 |= 0x1000; +// } +// return; +// } +// if ((arg0->unk2C & 0x80) != 0) { +// func_151239CC(arg0, 0xD); +// arg0->unk190 = 0.0f; +// } +// if ((arg0->unk5F0 & 0x1000) != 0) { +// arg0->unk5F0 &= ~0x1000; +// temp_f0 = arg0->unk2FC - arg0->unk354; +// *(f32 *) ((u8 *) arg0 + 0x344) = temp_f0; +// arg0->unk348 = temp_f0; +// } +// } #pragma GLOBAL_ASM("asm/nonmatchings/game_14FF90/func_15125A6C.s") // NON-MATCHING: miles away // void func_15125A6C(struct108 *arg0) { @@ -559,6 +780,41 @@ void func_15125690(struct108 *arg0, s32 arg1) { // } #pragma GLOBAL_ASM("asm/nonmatchings/game_14FF90/func_15125C40.s") +// NON-MATCHING (best 425): logic byte-identical (every load/xor/branch/call/store matches). +// Only diff is register allocation: the const 0x1A lands in a3 (target: a2) and `e` gets an +// extra `move a0,a1` (target keeps e in a1, fills the beqz delay slot with the e^3 for the +// (e==3) test). This one extra move cascades register renames through the whole body. Tried +// declaration reorder (no effect). Register-alloc/delay-slot only. PERMUTER CANDIDATE. +// void func_15125C40(struct108 *arg0) { +// s32 flag; +// s32 dec; +// s32 nv; +// u8 e = arg0->unk23E; +// flag = (D_800D1940 == 0x42) && (e == 0x1A); +// dec = (e == 3) || (e == 0x1A) || flag; +// nv = arg0->unk7CC - 1; +// arg0->unk7CC = nv; +// if (dec) { +// if (nv != 0) return; +// if (flag) { +// func_1509BFB0(3, 0x9000, 0x18, (s32) arg0->unk3D0->unk40, 0, 0xFA); +// } else if (e == 0x1A) { +// func_1509BFB0(3, 0x9000, 0x18, 0, 0, 0xFA); +// } +// arg0->unk5F0 |= 2; +// *arg0->unk36C |= 0x10; +// arg0->unk36A |= 0x10; +// arg0->unk7CC = 1; +// } else { +// if ((arg0->unk5F0 & 2) && !dec) { +// func_1509BFB0(1, 0x9000, 0x10, 0); +// func_1509BFB0(1, 0x9000, 0xF, 0); +// arg0->unk5F0 &= ~2; +// } +// arg0->unk7CC = 2; +// } +// } + #pragma GLOBAL_ASM("asm/nonmatchings/game_14FF90/func_15125DB4.s") void func_15126138(struct108 *arg0) { @@ -596,37 +852,79 @@ void func_15126138(struct108 *arg0) { #pragma GLOBAL_ASM("asm/nonmatchings/game_14FF90/func_1512623C.s") -// ooh mama +// NON-MATCHING (best 1150): logic reconstructed (arg0/arg1 = struct127*, arg3-5 = f32* outs, +// arg6 flag). phi_v0 lands in v1 (IDO) vs v0 (target, the return reg), and IDO packs the +// phi-spill low (0x20) making the frame 0x40 vs the target's 0x48 (spill at 0x44, after the +// two struct17 locals + sp28). Every stack-offset then differs. Register-alloc/spill-layout. +// PERMUTER CANDIDATE. +// s32 func_1512623C(struct127 *arg0, struct127 *arg1, s32 arg2, f32 *arg3, f32 *arg4, f32 *arg5, s32 arg6) { +// struct17 sp38, sp2C; s32 sp28, phi_v0 = 1; +// if (arg0->unk1D4 != NULL) { +// phi_v0 = 0; +// if (arg6 != 0) { sp28 = 3; sp38.unk0 = 0.0f; sp38.unk4 = 20.0f; sp38.unk8 = 0.0f; } +// else if (arg2 == 0x1B) { sp28 = 4; sp38.unk0 = 0.0f; sp38.unk4 = 116.0f; sp38.unk8 = 130.0f; } +// else phi_v0 = 1; +// if (phi_v0 == 0) { +// func_15143134(&sp38, &sp2C, (u8 *) arg0->unk1D4 + sp28 * 0x40); +// *arg3 = sp2C.unk0; *arg4 = sp2C.unk4; *arg5 = sp2C.unk8; +// } +// } +// if (phi_v0 != 0) { +// *arg3 = arg0->x_position; +// *arg4 = arg0->y_position + (f32) *(s16 *) ((u8 *) arg1 + 0x114) * 0.75f; +// *arg5 = arg0->z_position; +// } +// return phi_v0; +// } #pragma GLOBAL_ASM("asm/nonmatchings/game_14FF90/func_15126378.s") #pragma GLOBAL_ASM("asm/nonmatchings/game_14FF90/func_15127520.s") #pragma GLOBAL_ASM("asm/nonmatchings/game_14FF90/func_151277B0.s") #pragma GLOBAL_ASM("asm/nonmatchings/game_14FF90/func_151279A0.s") +// D_800BE628 is declared `s32` but holds a pointer to an array of 0x180-byte structs; +// unk2FC is padding in struct127 in the shared header. File-local views (headers unedited). +typedef struct { + u8 pad0[0x84]; + f32 unk84; + u8 pad88[0xF8]; +} BE628Elem; /* 0x180 */ + +typedef struct { + u8 pad0[0x65]; + u8 unk65; + u8 pad66[0xE]; + u8 unk74; + u8 pad75[0x287]; + u8 unk2FC; +} S127Bits; + #pragma GLOBAL_ASM("asm/nonmatchings/game_14FF90/func_15127EB8.s") -// NON-MATCHING: not too far away +// NON-MATCHING (best 100): logic byte-identical. Only remaining diff is the +// `v0 = arg0->unk3D0` default: the target emits an un-coalesced `move v0,v1` +// BEFORE the beqz (keeping the default live in v0 across the if-body, so idx +// lands in a0 and the array-index result overwrites v0). IDO here coalesces +// temp_v0 with temp_v1 (idx→v0, no move). Register-coalescing decision not +// controllable from C. PERMUTER CANDIDATE. // void func_15127EB8(struct108 *arg0) { -// struct127 *phi_v0; -// +// struct127 *temp_v0, *temp_v1; +// u8 temp_a0; // func_151239CC(arg0, 1); -// arg0->unk3D4->unk197 = (u8)0; +// arg0->unk3D4->unk197 = 0; // func_151C9ED4(arg0); // arg0->unk19C = 0.0f; // arg0->unk1A0 = 0.0f; // arg0->unk1A4 = 0.0f; // arg0->unk1A8 = 0.0f; // func_1510B32C(arg0->unk23D, 0.0f, 0.0f, 1.0f); -// D_800BE628[arg0->unk23D].unk84 = 1.0f; +// ((BE628Elem *) D_800BE628)[arg0->unk23D].unk84 = 1.0f; // func_150627D4(arg0->unk3D0); -// D_800DBFF4[arg0->unk23D] = (u8)2; -// -// phi_v0 = &arg0->unk3D0; -// if (phi_v0->unk65 != 0) { -// phi_v0 = &D_800CC2D0[phi_v0->unk65]; -// } -// -// phi_v0->unk2FC &= ~(1 << arg0->unk23D); -// phi_v0->unk74 &= ~(1 << arg0->unk23D); -// arg0->unk23C = (u8)1; +// D_800DBFF4[arg0->unk23D] = 2; +// temp_v1 = arg0->unk3D0; +// temp_a0 = temp_v1->unk65; +// temp_v0 = (temp_a0 != 0) ? &D_800CC2D0[temp_a0 - 1] : temp_v1; +// ((S127Bits *) temp_v0)->unk2FC &= ~(1 << arg0->unk23D); +// temp_v0->unk74 &= ~(1 << arg0->unk23D); +// arg0->unk23C = 1; // } void func_15127FEC(struct108 *arg0, s32 arg1, s32 arg2) { @@ -639,8 +937,78 @@ void func_15127FEC(struct108 *arg0, s32 arg1, s32 arg2) { #pragma GLOBAL_ASM("asm/nonmatchings/game_14FF90/func_15128030.s") #pragma GLOBAL_ASM("asm/nonmatchings/game_14FF90/func_151283B8.s") -#pragma GLOBAL_ASM("asm/nonmatchings/game_14FF90/func_151284C4.s") -#pragma GLOBAL_ASM("asm/nonmatchings/game_14FF90/func_15128540.s") +// NON-MATCHING (best 200): byte-identical EXCEPT the case-8 zero argument -> target emits +// `li a1,0` (addiu a1,zero,0), IDO here emits `move a1,zero` (or). Tried 0 / NULL / prototype; +// li-vs-move for a 0 arg is not controllable from C. PERMUTER CANDIDATE (li/move). +// (Case bodies MUST be in this source order to match the target body layout.) + +// structs.h types unk5FE as u16, but the original code reads it signed. +typedef struct { + u8 pad0[0x5FE]; + s16 unk5FE; +} Struct108_5FE; + +void func_151284C4(struct108 *arg0) { + struct127 *temp_v0; + + func_1512C490(arg0); + temp_v0 = arg0->unk3D0; + arg0->unk2B0 = temp_v0->x_position; + arg0->unk2B4 = temp_v0->y_position; + arg0->unk2B8 = temp_v0->z_position; + if (arg0->unk23C != 0) { + arg0->unk23C -= 1; + } + if ((arg0->unk84 & 8) != 0) { + if (((Struct108_5FE *) arg0)->unk5FE <= 0) { + ((Struct108_5FE *) arg0)->unk5FE = 60; + func_15128774(arg0, arg0->unk3D0); + } + } +} + +s32 func_15128540(struct108 *arg0) { + s32 temp; + + if (func_151253CC(arg0) != 0) { + return 1; + } + if (func_15128030(arg0) != 0) { + return 0; + } + temp = arg0->unk2C; + if (temp & 0x80000) { + func_1512A360(arg0); + *(struct17 *) &arg0->unk2BC = *(struct17 *) &arg0->unk2A4; + func_151256BC(arg0); + func_151236D0(arg0); + func_151284C4(arg0); + return 1; + } + if (temp & 0x100000) { + func_1512E4B0(arg0); + func_151236D0(arg0); + func_151284C4(arg0); + return 1; + } + if (temp & 0x200000) { + func_151219D0(arg0); + func_151236D0(arg0); + func_151284C4(arg0); + return 1; + } + if (temp & 0x400000) { + func_1512D380(arg0); + func_151236D0(arg0); + func_151284C4(arg0); + return 1; + } + if (func_15126378(arg0) != 0) { + func_151284C4(arg0); + return 1; + } + return 0; +} void func_15128680(struct108 *arg0) { } diff --git a/conker/src/game_161520.c b/conker/src/game_161520.c index 2c5e6185..be5b6137 100644 --- a/conker/src/game_161520.c +++ b/conker/src/game_161520.c @@ -2,6 +2,124 @@ #include "functions.h" #include "variables.h" +// File-local declarations: structs.h lacks/mistypes these layouts and the +// shared headers may not be edited. +typedef struct { + u8 pad0[0x1C]; + s16 unk1C; + u8 pad1E[0xE]; + f32 unk2C; + f32 unk30; + u8 pad34[0x28]; + u8 unk5C; + u8 pad5D[0xCB]; + f32 unk128[1]; +} Local161520A; + +typedef struct { + u8 pad0[0x60]; + s32 unk60; + u8 pad64[0x10]; + u8 unk74; + u8 pad75[0xFB]; + s32 unk170; + u8 pad174[0x60]; + s32 unk1D4; +} Local161520B; + +typedef struct { + u8 pad0[0x10]; + s32 unk10; + u8 pad14[0x4]; + u8 unk18; + u8 pad19[0x3]; + s32 unk1C; +} Local161520C; + +typedef struct { + u8 pad0[0x28]; + s16 unk28; +} Local15134DACsrc; + +typedef struct { + u8 pad0[0x10]; + s32 unk10; + s32 unk14; + u8 unk18[0x3C]; + s16 unk54; + u8 pad56[0x1A]; + f32 unk70; + f32 unk74; + f32 unk78; +} Local15134DAC; + +extern void *func_15167A68(s32, s32, s32, s32, u8, s32); + +extern f32 D_800A45B0; + +typedef struct { + u8 pad0[0x16]; + u8 unk16; +} Local15134908src; + +typedef struct { + u8 pad0[0x10]; + f32 *unk10; + f32 *unk14; + f32 *unk18; + f32 unk1C; + u8 pad20[0xC]; + f32 unk2C; + f32 unk30; + f32 unk34; + f32 unk38; + f32 unk3C; +} Local15134908; + +typedef struct { + u8 pad0[0x14]; + f32 unk14; + u8 pad18[0x4]; + f32 unk1C; + u8 pad20[0xE]; + u8 unk2E; + u8 pad2F[0x41]; + f32 unk70; + f32 unk74; +} Local15134CEC; + +typedef struct { + s32 unk0; + u8 unk4; +} Local161520D; + +typedef struct { + u8 pad0[0x10]; +} Local161520E; + +typedef struct { + s32 unk0; + s32 unk4; +} Local161520F; + +extern f32 D_800A4828; +extern Local161520E D_800A3FD8[]; + +typedef struct { + u8 pad0[0x44]; + u16 unk44; + u8 pad46[0xA]; + u8 unk50; +} Local1513532C; + +extern void (*D_80089B70[6])(Local1513532C *); +extern void (*D_80089B88[6])(Local1513532C *); + +void func_151BC5A4(struct102 *arg0, s32 arg1, u8 arg2); +void func_1513A5E0(s32 arg0, u8 arg1, s32 arg2); +void func_15145EA4(Local161520F *arg0, Local161520F *arg1, s32 arg2, s32 arg3); +void func_1513555C(Local161520C *arg0, Local161520D *arg1, u8 arg2); + // requires jump table #pragma GLOBAL_ASM("asm/nonmatchings/game_161520/func_15134070.s") @@ -13,43 +131,64 @@ s32 func_1513416C(struct102 *arg0) { return 1; } -#pragma GLOBAL_ASM("asm/nonmatchings/game_161520/func_1513418C.s") -// void *func_1513418C(s32 arg0, s32 arg1 /* size/offset */, u8 arg2, s32 arg3) { -// void *sp24; -// s32 temp_v1; -// u8 temp_a0; -// void *temp_ret; -// void *temp_v0; -// struct127 *temp_v0_2; -// -// temp_ret = func_15167A68(0x28, arg3, arg1 + 0x58, 1, arg2, 1); -// temp_v0 = temp_ret; -// if (temp_v0 == 0) { -// return NULL; -// } -// sp24 = temp_v0; -// memcpy(&temp_v0->unk10, arg0, 0x30); //, temp_v0); -// temp_a0 = temp_v0->unk3A; -// if ((temp_a0 & 2) != 0) { -// temp_v0_2 = temp_v0->unk1C; -// if ((temp_v0_2->unk0 == 0) || (temp_v0->unk18 != temp_v0_2->unk3B)) { -// func_1516972C(temp_v0); -// return NULL; -// } -// temp_v1 = temp_v0_2->unk1D4; -// if ((temp_v1 != 0) && ((temp_v0_2->unk74 & 0xF) != 0xF)) { -// sp24 = temp_v0; -// func_15143134(temp_v0->unk24, temp_v0->unk40, temp_v1 + (temp_v0->unk20 << 6), temp_v0); -// } else { -// temp_v0->unk3A = (u8) (temp_a0 | 8); -// } -// } else { -// temp_v0->unk3A = (u8) (temp_a0 | 0x18); -// } -// temp_ret->unk50 = 0.0f; -// temp_ret->unk4C = (f32) (1.0f / (2.0f * temp_ret->unk30)); -// return temp_ret; -// } +typedef struct { + s32 unk0; + u8 pad4[0x37]; + u8 unk3B; + u8 pad3C[0x38]; + u8 unk74; + u8 pad75[0x15F]; + s32 unk1D4; +} Local1513418Cb; + +typedef struct { + u8 pad0[0x10]; + u8 unk10[0x8]; + u8 unk18; + u8 pad19[0x3]; + Local1513418Cb *unk1C; + u8 unk20; + u8 pad21[0x3]; + u8 unk24[0xC]; + f32 unk30; + u8 pad34[0x6]; + u8 unk3A; + u8 pad3B[0x5]; + u8 unk40[0xC]; + f32 unk4C; + f32 unk50; +} Local1513418Ca; + +void *func_1513418C(void *arg0, s32 arg1, u8 arg2, s32 arg3) { + Local1513418Ca *ret = (Local1513418Ca *) func_15167A68(0x28, arg3, arg1 + 0x58, 1, arg2, 1); + Local1513418Cb *v0_2; + s32 temp; + u8 flags; + + if (ret == NULL) { + return NULL; + } + memcpy(&ret->unk10, arg0, 0x30); + flags = ret->unk3A; + if ((flags & 2) != 0) { + v0_2 = ret->unk1C; + if ((v0_2->unk0 == 0) || (v0_2->unk3B != ret->unk18)) { + func_1516972C(ret); + return NULL; + } + temp = v0_2->unk1D4; + if ((temp != 0) && ((v0_2->unk74 & 0xF) != 0xF)) { + func_15143134(&ret->unk24, &ret->unk40, (u8 *) temp + (ret->unk20 << 6)); + } else { + ret->unk3A = flags | 8; + } + } else { + ret->unk3A = flags | 0x18; + } + ret->unk4C = 1.0f / (ret->unk30 + ret->unk30); + ret->unk50 = 0.0f; + return ret; +} #pragma GLOBAL_ASM("asm/nonmatchings/game_161520/func_151342BC.s") @@ -88,41 +227,227 @@ void func_1513477C(struct102 *arg0) { D_80089AD4[idx](); } +// PERMUTER CANDIDATE: byte-identical EXCEPT the case-0x2D else-if emits +// `bnel v1,t9` where target has `bnel t9,v1` (symmetric equality, operands +// swapped). IDO orders the two live regs by age here; not flippable from C +// source order. One instruction from a full match. +// typedef struct { u8 pad0[0x18]; u8 unk18; u8 pad19[0x3]; s32 unk1C; +// u8 pad20[0x1D]; u8 unk3D; } Local151347CCa; +// typedef struct { s32 unk0; s32 unk4; u8 unk8; u8 unk9; } Local151347CCb; +// void func_151347CC(Local151347CCa *arg0, Local161520D *arg1, u8 arg2) { +// if ((arg2 == 0) || (arg2 == 3)) { +// if ((arg1->unk0 == arg0->unk1C) || (arg1->unk4 == arg0->unk18)) { +// func_1516972C(arg0); +// } +// } else if (arg2 == 0x11) { +// if (arg0->unk3D == 5) { +// if ((arg1->unk0 == arg0->unk1C) || (arg1->unk4 == arg0->unk18)) { +// func_1516972C(arg0); +// } +// } +// } else if (arg2 == 0x16) { +// if ((s32) arg1 == arg0->unk1C) { +// func_1516972C(arg0); +// } +// } else if (arg2 == 0x2D) { +// Local151347CCb *p = (Local151347CCb *) arg1; +// s32 d = p->unk0; +// s32 c = arg0->unk1C; +// if (d == c) { +// arg0->unk1C = p->unk4; +// arg0->unk18 = p->unk9; +// } else if (p->unk4 == c) { +// arg0->unk1C = d; +// arg0->unk18 = p->unk8; +// } +// } +// } #pragma GLOBAL_ASM("asm/nonmatchings/game_161520/func_151347CC.s") -#pragma GLOBAL_ASM("asm/nonmatchings/game_161520/func_151348F0.s") +void func_151348F0(f32 arg0, f32 arg1, s32 arg2, s32 arg3) { +} + +Local15134908 *func_15134908(Local15134908src *arg0, s32 arg1, u8 arg2, s32 arg3) { + Local15134908 *temp = func_15167A68(0x2A, arg3, arg1 + 0x40, 1, arg2, 1); -#pragma GLOBAL_ASM("asm/nonmatchings/game_161520/func_15134908.s") + if (temp == NULL) { + return NULL; + } + arg0->unk16 |= 2; + memcpy(&temp->unk10, arg0, 0x1C); + temp->unk2C = *temp->unk10; + temp->unk30 = *temp->unk14; + temp->unk34 = *temp->unk18; + temp->unk38 = 1.0f / (2.0f * temp->unk1C); + temp->unk3C = 0.0f; + return temp; +} #pragma GLOBAL_ASM("asm/nonmatchings/game_161520/func_151349D0.s") -#pragma GLOBAL_ASM("asm/nonmatchings/game_161520/func_15134C98.s") +void func_15134C98(struct102 *arg0, s32 arg1, u8 arg2) { + if (arg0->unk28 == 1) { + func_151BC5A4(arg0, arg1, arg2); + } +} -#pragma GLOBAL_ASM("asm/nonmatchings/game_161520/func_15134CD4.s") +void func_15134CD4(f32 arg0, f32 arg1, s32 arg2, s32 arg3) { +} -#pragma GLOBAL_ASM("asm/nonmatchings/game_161520/func_15134CEC.s") +s32 func_15134CEC(Local15134CEC *arg0) { + s32 v1 = arg0->unk2E; + arg0->unk70 += 0.125f * D_800BE9A4; + arg0->unk74 += D_800A45B0 * D_800BE9A4; + arg0->unk14 += arg0->unk70 * D_800BE9A4; + arg0->unk1C += arg0->unk74 * D_800BE9A4; + if (130.0f < arg0->unk14) { + return 0; + } + v1 -= 2 * D_800BE9E4; + if (v1 < 0) { + return 0; + } + arg0->unk2E = v1; + return 1; +} + +// best 20 (own score): byte-perfect except arg0->unk28 lands in $a0 (reused after +// memcpy) vs target $t9 (fresh temp), cascading to the const-1 register. permuter +// exhausted to 20 only; the $a0 allocation is not controllable from C here. +// Local15134DAC *func_15134DAC(Local15134DACsrc *arg0, s32 arg1) { +// Local15134DAC *temp = func_15167A68(0x29, 0, arg1 + 0x80, 1, 0xFF, 1); +// s32 neg; +// if (temp == NULL) { return NULL; } +// memcpy(&temp->unk18, arg0, 0x3C); +// neg = arg0->unk28; +// temp->unk10 = 1; +// temp->unk54 = -neg; +// temp->unk14 = 0; +// temp->unk70 = 0.0f; +// temp->unk74 = 0.0f; +// temp->unk78 = 0.0f; +// return temp; +// } #pragma GLOBAL_ASM("asm/nonmatchings/game_161520/func_15134DAC.s") #pragma GLOBAL_ASM("asm/nonmatchings/game_161520/func_15134E48.s") -#pragma GLOBAL_ASM("asm/nonmatchings/game_161520/func_151352EC.s") +void func_151352EC(struct102 *arg0) { + func_15169804(arg0); +} -#pragma GLOBAL_ASM("asm/nonmatchings/game_161520/func_1513530C.s") +void func_1513530C(struct102 *arg0) { + func_15169824(arg0); +} -#pragma GLOBAL_ASM("asm/nonmatchings/game_161520/func_1513532C.s") +void func_1513532C(Local1513532C *arg0) { + u16 temp; + s32 idx = arg0->unk50; -#pragma GLOBAL_ASM("asm/nonmatchings/game_161520/func_151353A8.s") + if (idx < 0) { + idx = 0; + } else if (idx >= 6) { + idx = 0; + } + temp = arg0->unk44; + if (temp != 0) { + func_100111C8(temp); + arg0->unk44 = 0; + } + D_80089B70[idx](arg0); +} -#pragma GLOBAL_ASM("asm/nonmatchings/game_161520/func_15135424.s") +void func_151353A8(Local1513532C *arg0) { + u16 temp; + s32 idx = arg0->unk50; + if (idx < 0) { + idx = 0; + } else if (idx >= 6) { + idx = 0; + } + temp = arg0->unk44; + if (temp != 0) { + func_100111C8(temp); + arg0->unk44 = 0; + } + D_80089B88[idx](arg0); +} + +void func_15135424(s32 arg0, Local161520F arg1, Local161520F arg2) { + Local161520F sp20; + Local161520F sp18; + + sp20.unk0 = arg1.unk0; + sp20.unk4 = arg1.unk4; + sp18.unk0 = arg2.unk0; + sp18.unk4 = arg2.unk4; + func_15145EA4(&sp20, &sp18, arg0, 2); +} + +// PERMUTER CANDIDATE: byte-identical structure EXCEPT the case-0x2D swap emits a +// v0<->v1 register rename (p->unk0 lands in v0 here, target wants v1), which also +// flips the else-if `bnel` operands and a `move a2,a3` vs `nop`. Same unsolvable +// v0/v1 swap as func_151347CC's 0x2D case; not controllable from C. +// typedef struct { u8 pad0[0x18]; u8 unk18; u8 pad19[0x3]; s32 unk1C; +// u8 pad20[0x30]; u8 unk50; } Local15135480a; +// typedef struct { s32 unk0; s32 unk4; u8 unk8; u8 unk9; } Local15135480b; +// void func_15135480(Local15135480a *arg0, void *arg1, u8 arg2) { +// if (arg2 == 0x2D) { +// Local15135480b *p = (Local15135480b *) arg1; +// if (arg0->unk1C == p->unk0) { +// arg0->unk1C = p->unk4; arg0->unk18 = p->unk9; +// } else if (p->unk4 == arg0->unk1C) { +// arg0->unk1C = p->unk0; arg0->unk18 = p->unk8; +// } +// } +// switch (arg0->unk50) { +// case 1: func_151355B8(arg0, arg1, arg2); break; +// case 2: func_1513555C((Local161520C *) arg0, (Local161520D *) arg1, arg2); break; +// default: +// if (arg2 == 0) { +// Local161520D *q = (Local161520D *) arg1; +// if ((q->unk0 == arg0->unk1C) || (q->unk4 == arg0->unk18)) { +// func_1516972C(arg0); +// } +// } +// break; +// } +// } #pragma GLOBAL_ASM("asm/nonmatchings/game_161520/func_15135480.s") -#pragma GLOBAL_ASM("asm/nonmatchings/game_161520/func_1513555C.s") +void func_1513555C(Local161520C *arg0, Local161520D *arg1, u8 arg2) { + if ((arg2 == 0) || (arg2 == 0x12)) { + if ((arg0->unk1C == arg1->unk0) || (arg0->unk18 == arg1->unk4)) { + func_1516972C((struct102 *) arg0); + } + } +} +// best 100 (own score): switch matches byte-for-byte except the target emits a +// duplicated `sw t6,0x10(a0)` store in the case-3 body (IDO branch-likely artifact +// not reproducible from clean C). See below. +// void func_151355B8(Local161520C *arg0, Local161520D *arg1, u8 arg2) { +// switch (arg2) { +// case 0: +// if ((arg0->unk1C == arg1->unk0) || (arg0->unk18 == arg1->unk4)) { +// func_1516972C((struct102 *) arg0); +// } +// break; +// case 3: +// if ((arg0->unk1C == arg1->unk0) || (arg0->unk18 == arg1->unk4)) { +// arg0->unk10 &= ~1; +// } +// break; +// } +// } #pragma GLOBAL_ASM("asm/nonmatchings/game_161520/func_151355B8.s") -#pragma GLOBAL_ASM("asm/nonmatchings/game_161520/func_15135658.s") +s32 func_15135658(struct259 *arg0) { + arg0->unk74 = 1.0f; + return 1; +} f32 func_15135670(s32 arg0) { // "power", "../Effects/Blood/blood.c" @@ -139,13 +464,117 @@ f32 func_15135670(s32 arg0) { #pragma GLOBAL_ASM("asm/nonmatchings/game_161520/func_15136698.s") -#pragma GLOBAL_ASM("asm/nonmatchings/game_161520/func_151368A8.s") +s32 func_151368A8(Local161520A *arg0) { + s16 v0 = arg0->unk1C; + f32 *p = arg0->unk128; + s32 v1; -#pragma GLOBAL_ASM("asm/nonmatchings/game_161520/func_15136918.s") + if (v0 < 32) { + v1 = v0 * 8; + if (v1 < arg0->unk5C) { + arg0->unk5C = v1; + } + } + arg0->unk2C += *p * D_800BE9A4; + arg0->unk30 += *p * D_800BE9A4; + return 1; +} -#pragma GLOBAL_ASM("asm/nonmatchings/game_161520/func_15136A1C.s") +typedef struct { + s32 unk0; + s16 unk4; + u8 unk6; + u8 pad7; + s32 unk8; + s32 unkC; + u8 unk10; + u8 unk11; + u8 unk12; + u8 unk13; + u8 unk14; + u8 unk15; + u8 unk16; + u8 unk17; + s32 unk18; + u8 pad1C[0xC]; +} Local15136918; + +typedef struct { + f32 x; + f32 y; + f32 z; +} Vec3f15136918; + +void func_15136918(f32 arg0, u8 arg1, u8 arg2, s16 arg3, s16 arg4, s32 arg5, Vec3f15136918 *arg6, u8 arg7, s32 arg8) { + Local15136918 sp48; + s32 pad[2]; + + sp48.unk6 = 0x55; + sp48.unk0 = 0x300; + sp48.unk8 = 0; + sp48.unkC = 0; + sp48.unk10 = arg1; + sp48.unk11 = arg2; + sp48.unk12 = 0; + sp48.unk13 = 0; + sp48.unk14 = 0; + sp48.unk15 = 0; + sp48.unk18 = 0x280001; + sp48.unk16 = 1; + sp48.unk17 = 1; + if (arg3 == -1) { + sp48.unk4 = 300; + } else { + sp48.unk0 = 0x301; + sp48.unk4 = arg3 + 0x20; + } + func_1513C73C((s32) &sp48, 0xD, 0, arg5, arg6->x, arg6->y, arg6->z, arg0, arg0, arg4, 0, 0, arg7, arg8); +} + +s32 func_15136A1C(struct102 *arg0) { + s16 temp_v0 = arg0->unk1C; + s32 temp_v1; + + if (temp_v0 < 32) { + temp_v1 = temp_v0 * 8; + if (temp_v1 < arg0->unk28) { + arg0->unk28 = temp_v1; + } + } + return 1; +} -#pragma GLOBAL_ASM("asm/nonmatchings/game_161520/func_15136A50.s") +extern f32 D_800A461C; +extern f32 D_800A4620; + +typedef struct { + s32 unk0; + s32 unk4; + s32 unk8; + f32 unkC; + f32 unk10; + s16 unk14; + u8 unk16; + u8 unk17; + u8 unk18; + s8 unk19; +} Local15136A50; + +void func_15136A50(s32 arg0, s32 arg1, s32 arg2, s16 arg3, s32 arg4, s32 arg5) { + Local15136A50 sp1C; + + sp1C.unk0 = arg0; + sp1C.unk4 = arg1; + sp1C.unk8 = arg2; + sp1C.unkC = D_800A461C; + sp1C.unk10 = D_800A4620; + sp1C.unk14 = arg3; + sp1C.unk16 = 5; + sp1C.unk17 = 5; + sp1C.unk18 = 2; + sp1C.unk19 = -1; + func_15134908((Local15134908src *) &sp1C, 0, arg4, arg5); +} #pragma GLOBAL_ASM("asm/nonmatchings/game_161520/func_15136AE4.s") @@ -159,22 +588,177 @@ f32 func_15135670(s32 arg0) { #pragma GLOBAL_ASM("asm/nonmatchings/game_161520/func_15137C64.s") -#pragma GLOBAL_ASM("asm/nonmatchings/game_161520/func_15137E10.s") +s32 func_15137E10(struct259 *arg0) { + arg0->unk74 = (func_150ADA68() * 50.0f + 580.0f) * D_800A4828; + return 1; +} -#pragma GLOBAL_ASM("asm/nonmatchings/game_161520/func_15137E60.s") +typedef struct { + f32 x; + f32 y; + f32 z; +} Vec3f37F30; + +typedef struct { + u8 unk0; + u8 unk1; + u8 pad2[0xA]; + u8 unkC; + u8 padD[0x67]; + f32 unk74; +} Struct37F30; + +typedef struct { + f32 unk0; + u8 pad4; + u8 unk5; + s16 unk6; + f32 unk8; + Vec3f37F30 unkC; + Vec3f37F30 unk18; + Vec3f37F30 unk24; +} Buf37E60; + +extern f32 D_800A482C; + +void func_15137F30(Vec3f37F30 *arg0, Vec3f37F30 *arg1, Vec3f37F30 *arg2, Vec3f37F30 *arg3, f32 arg4, Struct37F30 *arg5, Vec3f37F30 *arg6, Vec3f37F30 *arg7, Vec3f37F30 *arg8, f32 *arg9, s16 *argA, u8 *argB, f32 *argC); +extern void func_151D9014(Vec3f37F30 *, Vec3f37F30 *, s32, f32, s16, u8, f32, s32, f32, f32, s32, s32, s32, s32, u8, u8); + +void func_15137E60(Vec3f37F30 *arg0, Vec3f37F30 *arg1, Vec3f37F30 *arg2, Vec3f37F30 *arg3, f32 arg4, Struct37F30 *arg5) { + Buf37E60 buf; + + func_15137F30(arg0, arg1, arg2, arg3, arg4, arg5, &buf.unk24, &buf.unk18, &buf.unkC, &buf.unk8, &buf.unk6, &buf.unk5, &buf.unk0); + func_151D9014(&buf.unk24, &buf.unkC, 0, buf.unk8, buf.unk6, buf.unk5, buf.unk0, 0, 1.0f, 1.0f, 1, 0, 1, 0, arg5->unkC, arg5->unk1); +} -#pragma GLOBAL_ASM("asm/nonmatchings/game_161520/func_15137F30.s") +void func_15137F30(Vec3f37F30 *arg0, Vec3f37F30 *arg1, Vec3f37F30 *arg2, Vec3f37F30 *arg3, f32 arg4, Struct37F30 *arg5, Vec3f37F30 *arg6, Vec3f37F30 *arg7, Vec3f37F30 *arg8, f32 *arg9, s16 *argA, u8 *argB, f32 *argC) { + arg6->x = arg0->x + arg2->x * arg4; + arg6->y = arg0->y + arg2->y * arg4; + arg6->z = arg0->z + arg2->z * arg4; + arg7->x = arg1->x + arg3->x * arg4; + arg7->y = arg1->y + arg3->y * arg4; + arg7->z = arg1->z + arg3->z * arg4; + arg8->x = (arg7->x - arg6->x) * arg5->unk74; + arg8->y = (arg7->y - arg6->y) * arg5->unk74; + arg8->z = (arg7->z - arg6->z) * arg5->unk74; + *arg9 = (func_150ADA68() * 217.0f + -456.0f) * D_800A482C; + *argA = (u32) func_150ADA20() % 0x1F + 0x1E; + *argB = (u32) func_150ADA20() % 0x9C + 0x64; + *argC = func_150ADA68() * 35.0f + 40.0f; +} -#pragma GLOBAL_ASM("asm/nonmatchings/game_161520/func_151380B4.s") +s32 func_151380B4(Local161520B *arg0, s32 arg1, s32 arg2) { + s32 temp_v0 = arg0->unk1D4; + + if (temp_v0 == 0) { + return 0; + } + if ((arg0->unk74 & 0xF) == 0xF) { + return 0; + } + func_15143134(&D_800A3FD8[arg1], arg2, temp_v0 + 0x300); + return 1; +} #pragma GLOBAL_ASM("asm/nonmatchings/game_161520/func_15138120.s") -#pragma GLOBAL_ASM("asm/nonmatchings/game_161520/func_151382E0.s") +typedef struct { + s32 a; + s32 b; + s32 c; +} Vec3w382E0; + +typedef struct { + s16 unk0; + s16 unk2; + s16 unk4; + s16 unk6; + Vec3w382E0 unk8; + f32 unk14; + f32 unk18; + f32 unk1C; + f32 unk20; + f32 unk24; + f32 unk28; + s16 unk2C; + s16 unk2E; + s16 unk30; + s16 unk32; + s16 unk34; + s16 unk36; + s16 unk38; + s16 unk3A; + u8 unk3C; + u8 pad3D[3]; + f32 unk40; + s16 unk44; + s16 unk46; + s32 unk48; +} Local151382E0; + +extern f32 D_800A4830, D_800A4834, D_800A4838, D_800A483C, D_800A4840; +extern u8 D_800A3FE6[]; +extern void func_15153F18(Local151382E0 *, Vec3w382E0 *, void *, u8, s32); + +void func_151382E0(void *arg0, s32 arg1, void *arg2, u8 arg3, s32 arg4) { + Local151382E0 sp24; + + if (D_800A3FE6[arg1 * 16] != 2) { + sp24.unk8 = *(Vec3w382E0 *) arg0; + sp24.unk2C = 0x12; + sp24.unk2E = 7; + sp24.unk2 = 0xFF; + sp24.unk0 = 0; + sp24.unk4 = -63; + sp24.unk6 = 78; + sp24.unk30 = 3; + sp24.unk32 = 3; + sp24.unk14 = D_800A4830; + sp24.unk18 = D_800A4834; + sp24.unk1C = D_800A4838; + sp24.unk20 = D_800A483C; + sp24.unk24 = 4.0f; + sp24.unk28 = 9.0f; + sp24.unk34 = 0x14; + sp24.unk36 = 0x1E; + sp24.unk38 = 0x9B; + sp24.unk3A = 0x64; + sp24.unk44 = 0x10; + sp24.unk46 = 0xF; + sp24.unk48 = 0; + sp24.unk40 = D_800A4840; + if (D_800A3FE6[arg1 * 16] == 1) { + sp24.unk3C = 1; + } else { + sp24.unk3C = 0; + } + func_15153F18(&sp24, &sp24.unk8, arg2, arg3, arg4); + } +} #pragma GLOBAL_ASM("asm/nonmatchings/game_161520/func_15138424.s") #pragma GLOBAL_ASM("asm/nonmatchings/game_161520/func_151389A8.s") +// PERMUTER CANDIDATE: logic byte-identical, but IDO spills (u8)arg1 to a fresh +// stack slot (0x28) and reloads, vs target rematerializing lbu 0x67 per call. +// The extra spill slot grows the frame 0x60->0x68; not controllable from C. +// void func_15138BC0(void *arg0, s32 arg1, s32 arg2) { +// s32 s1; +// u8 sp50[0x10]; +// u8 sp4F; +// u8 sp28[0x27]; +// s1 = func_15134070(arg0); +// if (s1 != 0x63) { +// sp4F = func_151380B4(arg0, s1, sp50); +// func_15138120(arg0, s1, 1); +// if (sp4F != 0) { +// func_1504715C(sp28, arg0); +// func_151382E0(sp50, s1, sp28, (u8) arg1, arg2); +// func_15138424(arg0, sp50, s1, sp28, (u8) arg1, arg2); +// } +// } +// } #pragma GLOBAL_ASM("asm/nonmatchings/game_161520/func_15138BC0.s") #pragma GLOBAL_ASM("asm/nonmatchings/game_161520/func_15138C80.s") @@ -189,16 +773,114 @@ f32 func_15135670(s32 arg0) { #pragma GLOBAL_ASM("asm/nonmatchings/game_161520/func_1513A24C.s") +typedef struct { + s32 unk0; + s32 unk4; + s32 unk8; +} Vec3w161520; + +typedef struct { + s32 unk0; + s32 unk4; + Vec3w161520 unk8; + s16 unk14; + s16 unk16; + s16 unk18; + s16 unk1A; + f32 unk1C; + f32 unk20; + f32 unk24; + f32 unk28; + s16 unk2C; + s16 unk2E; + f32 unk30; + f32 unk34; + f32 unk38; +} Local1513A5E0; + +extern f32 D_800A4950, D_800A4954, D_800A4958, D_800A495C; +extern f32 D_800A4960, D_800A4964, D_800A4968, D_800A496C; +extern s32 D_800A4260, D_800A4264, D_800A4268, D_800A4270; +extern void func_15152190(Local1513A5E0 *, void *, void *, s32, f32, s32, u8, s32); + +// PERMUTER CANDIDATE (best ~1661): reconstruction is algorithmically exact and all +// FP/int registers match. ONLY the duplicated float LOAD (D_800A4950 -> unk30/unk34) +// must be scheduled into the prologue BEFORE `sw ra` (getting $f0); IDO instead +// schedules it after the register-saves. Placing the load first in source, chaining, +// or a local `temp` all fix the REGISTER (f0) but not the prologue placement. +// void func_1513A48C(s32 arg0, u8 arg1, s32 arg2) { +// Local1513A5E0 sp2C; +// f32 temp = D_800A4950; +// sp2C.unk0 = 8; +// sp2C.unk4 = 4; +// sp2C.unk8 = *(Vec3w161520 *) arg0; +// sp2C.unk14 = 0; +// sp2C.unk16 = 0xFF; +// sp2C.unk18 = -55; +// sp2C.unk1A = 32; +// sp2C.unk2C = 40; +// sp2C.unk2E = 20; +// sp2C.unk30 = temp; +// sp2C.unk34 = temp; +// sp2C.unk1C = 10.0f; +// sp2C.unk20 = 9.0f; +// sp2C.unk24 = D_800A4954; +// sp2C.unk28 = D_800A4958; +// sp2C.unk38 = D_800A495C; +// func_15152190(&sp2C, &D_800A4260, &D_800A4264, 1, 0.0f, 1, arg1, arg2); +// } +// permuter NO ZERO, best 240 (permuter improved to 120 only; the duplicated +// D_800A4950 float load will not cross the sw-ra prologue boundary from C). #pragma GLOBAL_ASM("asm/nonmatchings/game_161520/func_1513A48C.s") +// PERMUTER CANDIDATE (best ~310): logic correct; target reloads arg0 into $v0 and +// returns it, but any form that keeps the dead `arg0->unk1D4` check loads arg0 into +// $v1 and needs `move v0,v1` at the returns; an empty-if form drops the check +// entirely. v0/v1 near-miss not resolvable from C. +// Local161520B *func_1513A594(Local161520B *arg0, s32 arg1, s32 arg2, u8 arg3, s32 arg4) { +// func_1513A5E0(arg1, arg3, arg4); +// if (arg0->unk1D4) { return arg0; } +// return arg0; +// } #pragma GLOBAL_ASM("asm/nonmatchings/game_161520/func_1513A594.s") +// PERMUTER CANDIDATE (best 931): algorithmically exact; same blocker as func_1513A48C. +// The literal 4.0 hoists to the prologue ($f0) via chaining, but the duplicated float +// LOAD D_800A4960 (unk30/unk34) will not prologue-hoist to $f2 from C — IDO leaves it +// in the mid-body float group ($f4), cascading the FP register numbers and store order. +// void func_1513A5E0(s32 arg0, u8 arg1, s32 arg2) { +// Local1513A5E0 sp2C; +// sp2C.unk0 = 7; +// sp2C.unk4 = 7; +// sp2C.unk8 = *(Vec3w161520 *) arg0; +// sp2C.unk14 = 0; +// sp2C.unk16 = 0xFF; +// sp2C.unk18 = -50; +// sp2C.unk1A = 27; +// sp2C.unk1C = sp2C.unk20 = 4.0f; +// sp2C.unk30 = sp2C.unk34 = D_800A4960; +// sp2C.unk2C = 25; +// sp2C.unk2E = 40; +// sp2C.unk24 = D_800A4964; +// sp2C.unk28 = D_800A4968; +// sp2C.unk38 = D_800A496C; +// func_15152190(&sp2C, &D_800A4268, &D_800A4270, 2, 0.0f, 1, arg1, arg2); +// } +// permuter NO ZERO, best 931 (permuter plateaued at 515 only via a semantically +// invalid unk24-after-call reorder; prologue-hoist blocker unsolved from C). #pragma GLOBAL_ASM("asm/nonmatchings/game_161520/func_1513A5E0.s") #pragma GLOBAL_ASM("asm/nonmatchings/game_161520/func_1513A6E0.s") #pragma GLOBAL_ASM("asm/nonmatchings/game_161520/func_1513ABB8.s") -#pragma GLOBAL_ASM("asm/nonmatchings/game_161520/func_1513B0B8.s") +void func_1513B0B8(Local161520B *arg0, s32 arg1, u8 arg2) { + s32 *p = &arg0->unk170; + if (arg2 == 0x45) { + if (--*p < 0) { + arg0->unk60 |= 0x80; + } + } +} #pragma GLOBAL_ASM("asm/nonmatchings/game_161520/func_1513B0F8.s") diff --git a/conker/src/game_168A90.c b/conker/src/game_168A90.c index 3ab77be0..6382d4ff 100644 --- a/conker/src/game_168A90.c +++ b/conker/src/game_168A90.c @@ -4,9 +4,123 @@ #include "variables.h" +// Local view of this file's object: struct132 declares pad0[0x48], so the +// fields at 0x10/0x11/0x14 used by func_1513B798 are not nameable there. +typedef struct { + u8 pad0[0x10]; + u8 unk10; + s8 unk11; + u8 pad12[0x2]; + s16 unk14; + u8 pad16[0x32]; + u8 unk48; + u8 pad49[0x3]; + s32 unk4C; +} Obj168A90; + +extern s32 (*D_80089C18[])(Obj168A90 *); + +// Not present in functions.h; the u8 third parameter is required for matching. +void func_15109064(struct132 *arg0, s32 arg1, u8 arg2); +void func_151BA468(struct132 *arg0, s32 arg1, u8 arg2); + +// File-local declarations for symbols not present in the shared headers. +extern f32 D_800A4A84, D_800A4A88, D_800A4A8C, D_800A4A90, D_800A4A94; + +typedef struct { + u8 pad0[0x50]; + /* 0x50 */ s32 unk50; +} ObjBase168A90; + +typedef struct { + /* 0x00 */ f32 unk0; + /* 0x04 */ f32 unk4; + /* 0x08 */ f32 unk8; + /* 0x0C */ f32 unkC; + /* 0x10 */ f32 unk10; +} Blk168A90; + +typedef struct { + u8 pad0[0x10]; + /* 0x10 */ u8 unk10; + u8 pad11[0x1]; + /* 0x12 */ s8 unk12; + u8 pad13[0x36]; + /* 0x49 */ u8 unk49; + u8 pad4A[0xA]; + /* 0x54 */ s32 unk54; +} Obj168A90B; + +extern s32 (*D_80089C28[])(Obj168A90B *, s32); + +typedef struct { + /* 0x00 */ u8 unk0; + /* 0x01 */ u8 unk1; + /* 0x02 */ u8 unk2; + u8 pad3; + /* 0x04 */ s16 unk4; + u8 pad6[2]; + /* 0x08 */ s32 unk8; + /* 0x0C */ s32 unkC; + /* 0x10 */ s32 unk10; + /* 0x14 */ s32 unk14; + /* 0x18 */ s32 unk18; + /* 0x1C */ s32 unk1C; + /* 0x20 */ s32 unk20; + /* 0x24 */ u8 unk24; + /* 0x25 */ u8 unk25; + u8 pad26[0xA]; + /* 0x30 */ s32 unk30; + /* 0x34 */ s32 unk34; + /* 0x38 */ u8 unk38; + u8 pad39[3]; +} Descriptor168A90; + +ObjBase168A90 *func_1513B5E0(Descriptor168A90 *arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4); + #pragma GLOBAL_ASM("asm/nonmatchings/game_168A90/func_1513B5E0.s") -#pragma GLOBAL_ASM("asm/nonmatchings/game_168A90/func_1513B798.s") -#pragma GLOBAL_ASM("asm/nonmatchings/game_168A90/func_1513B83C.s") + +void func_1513B798(Obj168A90 *arg0) { + s32 ret; + u8 kill; + + kill = 0; + if ((arg0->unk10 & 1) != 0) { + arg0->unk14 -= D_800BE9E4; + if (arg0->unk14 < 0) { + kill = 1; + } + } + if (!kill) { + if (arg0->unk11 != -1) { + ret = D_80089C18[arg0->unk11](arg0); + if (!ret) { + kill = 1; + } + } + } + if (kill) { + func_1516972C(arg0); + } +} + +Gfx *func_1513B83C(Gfx *arg0, Obj168A90B *arg1, s16 arg2) { + s8 v0; + + if (((arg1->unk10 & 2) != 0) && ((arg1->unk49 & (1 << arg2)) == 0)) { + return arg0; + } + v0 = arg1->unk12; + if (v0 != -1) { + if (D_80089C28[v0](arg1, (arg2 << 0x10) >> 0x10) == 0) { + return arg0; + } + } + { Gfx *g = arg0++; g->words.w0 = 0xDA380003; g->words.w1 = (u32)((u8 *)arg1 + (D_800BE9C0 << 6) + 0x78); } + { Gfx *g = arg0++; g->words.w0 = 0xDB060004; g->words.w1 = *(u32 *)((u8 *)arg1 + (D_800BE9C0 << 4) + (arg2 << 2) + 0x58); } + { Gfx *g = arg0++; g->words.w0 = 0xDE000000; g->words.w1 = arg1->unk54; } + return arg0; +} s32 func_1513B968(s32 arg0, s32 arg1) { // FIXME: &arg0->unk_120[D_800BE9C0] @@ -32,12 +146,82 @@ void func_1513BA44(struct132 *arg0) { D_80089C54[arg0->unk48](); } -#pragma GLOBAL_ASM("asm/nonmatchings/game_168A90/func_1513BA78.s") +void func_1513BA78(struct132 *arg0, s32 arg1, u8 arg2) { + switch (arg0->unk48) { + case 1: + func_15109064(arg0, arg1, arg2); + break; + case 2: + func_151BA468(arg0, arg1, arg2); + break; + } +} s32 func_1513BAD4(s32 arg0, s32 arg1) { return 0; } -#pragma GLOBAL_ASM("asm/nonmatchings/game_168A90/func_1513BAE8.s") +ObjBase168A90 *func_1513BAE8(void) { + ObjBase168A90 *v0; + Descriptor168A90 d; + f32 sp24[5]; + + d.unk1 = 2; + d.unk2 = 5; + d.unk4 = 0x12C; + d.unk30 = 9; + sp24[0] = 0.0f; + sp24[1] = 0.0f; + sp24[3] = 0.0f; + sp24[4] = 0.0f; + sp24[2] = 0.0f; + d.unk0 = 0; + d.unk34 = 0x1AE; + d.unk8 = 1; + d.unkC = 0x220205; + d.unk10 = 0x40600; + d.unk24 = 0; + d.unk25 = 0; + d.unk14 = 1; + d.unk18 = 0x36; + d.unk1C = 0x80; + d.unk20 = 0x20; + d.unk38 = 3; + v0 = func_1513B5E0(&d, 1, 0x14, 0xFF, 1); + if (v0 != 0) { + if (v0->unk50 != 0x1180) { + func_1516972C(v0); + } else { + memcpy((u8 *)v0 + v0->unk50 + 0xF8, sp24, 0x14); + } + } + return v0; +} #pragma GLOBAL_ASM("asm/nonmatchings/game_168A90/func_1513BBFC.s") -#pragma GLOBAL_ASM("asm/nonmatchings/game_168A90/func_1513BEB0.s") +s32 func_1513BEB0(ObjBase168A90 *arg0) { + Blk168A90 *blk = (Blk168A90 *)((u8 *)arg0 + arg0->unk50 + 0xF8); + f32 temp; + + blk->unk8 += D_800A4A84 * D_800BE9A4; + temp = func_15144B68(blk->unk8); + blk->unk8 = temp; + blk->unk0 += D_800A4A88 * D_800BE9A4; + blk->unk4 = sinf(temp) * D_800A4A8C; + blk->unkC += D_800A4A90 * D_800BE9A4; + blk->unk10 += D_800A4A94 * D_800BE9A4; + blk->unkC = func_15144B68(blk->unkC); + blk->unk10 = func_15144B68(blk->unk10); + while (blk->unk0 > 4096.0f) { + blk->unk0 -= 4096.0f; + } + while (blk->unk0 < 0.0f) { + blk->unk0 += 4096.0f; + } + while (blk->unk4 > 4096.0f) { + blk->unk4 -= 4096.0f; + } + while (blk->unk4 < 0.0f) { + blk->unk4 += 4096.0f; + } + return 1; +} diff --git a/conker/src/game_169510.c b/conker/src/game_169510.c index c59b819a..3eac32f5 100644 --- a/conker/src/game_169510.c +++ b/conker/src/game_169510.c @@ -3,55 +3,269 @@ #include "functions.h" #include "variables.h" +// struct210 with extra fields past 0x120 (full struct not yet in structs.h) +typedef struct { + u8 pad0[0x2C]; + f32 unk2C; + f32 unk30; + u8 pad34[0xF4]; + f32 unk128; +} struct210_2; + +s32 func_1513FAB4(struct210_2 *arg0, u8 arg1, f32 *arg2, s16 arg3); + +// 0xA-byte per-vertex colour source struct (not yet in structs.h) +typedef struct { + s16 unk0; + s16 unk2; + s16 unk4; + s16 unk6; + u16 unk8; +} struct169510Col; + +void func_151400D0(Vtx *arg0, struct169510Col *arg1); +void func_1513FFF4(Vtx *arg0, u8 arg1, u8 arg2); + +// 0xC-byte table entry indexed by func_1513FFF4 (not yet in structs.h) +typedef struct { + u8 pad0[0x6]; + u16 unk6; + u16 unk8; + u8 padA[0x2]; +} struct90B60; + +extern struct90B60 D_80090B60[]; + +extern f32 D_800A5184; + +// object built by func_1513D2F0 (not yet in structs.h) +typedef struct { + u8 pad0[0x10]; + s32 unk10; + s32 unk14; + u8 unk18; + u8 pad19[0x57]; + u8 unk70; + u8 unk71; + u8 unk72; + u8 unk73; + u8 unk74; + u8 pad75[0x3]; + f32 unk78; + u8 pad7C[0x14]; + s32 unk90; + u8 unk94; + u8 unk95; + u8 pad96[0x2]; + s32 unk98; + s32 unk9C; + u8 unkA0; + u8 padA1[0x3]; + s32 unkA4[4]; + s32 unkB4; + s32 unkB8; + u8 padBC[0x4]; + Vtx unkC0[4]; + u8 unk100[0x10]; +} structD2F0; + +// template copied into the object at +0x18 by func_1513D2F0 +typedef struct { + u8 pad0[0x40]; + s32 unk40; + u8 pad44[0x14]; +} structD2F0tmpl; + +// float-view of the struct at struct210+0x110 (unk110 embedded), used by func_1513E6E8 +typedef struct { + u8 pad0[0x8]; + f32 unk8; + f32 unkC; + f32 unk10; + f32 unk14; + f32 unk18; + f32 unk1C; + f32 unk20; + f32 unk24; + f32 unk28; + f32 unk2C; +} struct211_E6E8; + +// float-view of struct210 for func_1513E6E8 (0x34..0x54 are f32 here) +typedef struct { + u8 pad0[0x34]; + f32 unk34; + f32 unk38; + f32 unk3C; + f32 unk40; + f32 unk44; + f32 unk48; + f32 unk4C; + f32 unk50; + f32 unk54; + s32 unk58; + u8 pad5C; + u8 unk5D; + u8 pad5E[0xB2]; + struct211_E6E8 unk110; +} struct210_E6E8; + +// file-local view of struct210 for func_1513C060 (header offsets/types inaccurate here) +typedef struct { + u8 pad0[0x10]; + s32 unk10; + u8 pad14[0x4]; + s32 unk18; + s16 unk1C; + u8 unk1E; + u8 pad1F; + s32 unk20; + s32 unk24; + u8 unk28; + u8 pad29[0x4]; + u8 unk2D; + u8 pad2E[0xC]; + s16 unk3A; + s16 unk3C; + u8 pad3E[0x42]; + u8 unk80; + u8 unk81; +} struct210_C060; + +extern s32 (*D_80089C70[])(); +extern s32 (*D_80089CA8[])(); +extern u8 D_80090B64[]; + +// func_1513C060: state-machine tick over a struct210. Reconstruction below is STRUCTURALLY +// byte-exact (all control flow, mults/divs, stores, branch-likely, and the shared unk18<<15 +// bit-16 test match). Best hand score 818: remaining diffs are pure IDO register allocation +// — the two do-while loops emit `move v0,tN` (store-before-branch) that no equivalent C +// reproduces without adding reloads, plus register-numbering cascades and an arg0->a1 spill +// in the val<0 branch. PERMUTER CANDIDATE. +/* +void func_1513C060(struct210_C060 *arg0) { + s8 flag; + s32 temp; + s32 val; + s32 idx; + if (arg0->unk10 & 1) { + flag = 0; + if (arg0->unk18 & 1) { + arg0->unk1C -= D_800BE9E4; + if (arg0->unk1C < 0) { + flag = 1; + } + } + idx = arg0->unk80; + if (idx != 0) { + if (D_80089C70[idx](arg0) == 0) { + flag = 1; + } + } + if ((arg0->unk18 & 0x8000) && (flag == 0) && (arg0->unk1C < arg0->unk3A) && + ((arg0->unk1C * arg0->unk3C) < arg0->unk28)) { + arg0->unk28 = arg0->unk1C * arg0->unk3C; + } + if (((arg0->unk18 << 15) < 0) && (flag == 0) && (arg0->unk1C < arg0->unk3A) && + ((arg0->unk1C * arg0->unk3C) < arg0->unk28)) { + arg0->unk2D = arg0->unk1C * arg0->unk3C; + } + if (flag == 0) { + if (arg0->unk24 != 0) { + temp = (D_80090B64[arg0->unk1E * 0xC] << 16) - 1; + arg0->unk20 = arg0->unk20 + arg0->unk24 * D_800BE9E4; + val = arg0->unk20; + if (temp < val) { + if (arg0->unk18 & 2) { + flag = 1; + } else if (arg0->unk18 & 0x40) { + arg0->unk24 = 0; + arg0->unk20 = temp; + } else if (arg0->unk18 & 4) { + arg0->unk20 = temp - (val % temp); + arg0->unk24 = -arg0->unk24; + } else { + do { + val -= temp; + arg0->unk20 = val; + } while (temp < val); + } + } else if (val < 0) { + if (arg0->unk18 & 8) { + flag = 1; + } else if (arg0->unk18 & 0x80) { + arg0->unk24 = 0; + arg0->unk20 = 0; + } else if (arg0->unk18 & 4) { + arg0->unk20 = (-val) % temp; + arg0->unk24 = -arg0->unk24; + } else { + do { + val += temp; + arg0->unk20 = val; + } while (val < 0); + } + } + } + } + if (flag != 0) { + idx = arg0->unk81; + if (idx != 0) { + if (D_80089CA8[idx](arg0) != 0) { + func_1516972C((struct102 *)arg0); + } + } else { + func_1516972C((struct102 *)arg0); + } + } + } +} +*/ #pragma GLOBAL_ASM("asm/nonmatchings/game_169510/func_1513C060.s") -#pragma GLOBAL_ASM("asm/nonmatchings/game_169510/func_1513C350.s") -// NON-MATCHING: JUSTREG: final 3 lines :( -// struct210 *func_1513C350(struct210 *arg0, s32 arg1, u8 arg2, u8 arg3, u8 arg4, s32 arg5, s32 arg6, struct167 *arg7, s32 arg8, u8 arg9, s32 argA) { -// struct210 *temp_v0; -// u8 phi_v0; -// s32 i; -// -// if (arg1 == 0) { -// arg1 = &D_800A4AA0; -// } -// if (arg0->unk0 & 0x40000) { -// phi_v0 = 0x4A; -// } else { -// phi_v0 = 0x1B; -// } -// temp_v0 = func_15167A68(phi_v0, argA, arg8 + 0xB0, 1, arg9, 1); -// if (temp_v0 == 0) { -// return NULL; -// } -// memcpy(&temp_v0->unk18.i.unk0, arg0, 0x28); // memcpy -// func_1513FFF4(&temp_v0->unk40, temp_v0->unk1E, arg4); -// func_151400D0(&temp_v0->unk40, arg1); -// temp_v0->unk80 = arg2; -// temp_v0->unk81 = arg3; -// temp_v0->unk84 = arg5; -// temp_v0->unk10 = 1; -// temp_v0->unk14 = 0; -// temp_v0->unkA0 = arg6; -// for (i = 0; i < 4; i++) { -// temp_v0->unk8C[i] = 0; -// } -// -// temp_v0->unk9C = 0; -// if (arg5) { -// for (i = 0; i <= D_80082FA0; i++) { -// temp_v0->unk8C[i] = func_1515D480(arg5); -// } -// temp_v0->unk9C = func_1515D440(); -// } -// if (arg7) { -// temp_v0->unkA4[0] = arg7->unk0; -// temp_v0->unkA4[1] = arg7->unk4; -// temp_v0->unkA4[2] = arg7->unk8; -// } -// return temp_v0; -// } +struct210 *func_1513C350(struct210 *arg0, s32 arg1, u8 arg2, u8 arg3, u8 arg4, s32 arg5, s32 arg6, struct167 *arg7, s32 arg8, u8 arg9, s32 argA) { + struct210 *temp_v0; + u8 phi_v0; + s32 i; + + if (arg1 == 0) { + arg1 = &D_800A4AA0; + } + if (arg0->unk0 & 0x40000) { + phi_v0 = 0x4A; + } else { + phi_v0 = 0x1B; + } + temp_v0 = func_15167A68(phi_v0, argA, arg8 + 0xB0, 1, arg9, 1); + if (temp_v0 == 0) { + return NULL; + } + memcpy(&temp_v0->unk18.i.unk0, arg0, 0x28); // memcpy + func_1513FFF4(&temp_v0->unk40, temp_v0->unk1E, arg4); + func_151400D0(&temp_v0->unk40, arg1); + temp_v0->unk80 = arg2; + temp_v0->unk81 = arg3; + temp_v0->unk84 = arg5; + temp_v0->unk10 = 1; + temp_v0->unk14 = 0; + temp_v0->unkA0 = arg6; + for (i = 0; i < 4; i++) { + temp_v0->unk8C[i] = 0; + } + + temp_v0->unk9C = 0; + if (arg5) { + for (i = 0; i <= D_80082FA0; i++) { + temp_v0->unk8C[i] = func_1515D480(arg5); + } + temp_v0->unk9C = func_1515D440(); + } + if (arg7) { + *(struct167 *)temp_v0->unkA4 = *arg7; + } + return temp_v0; +} struct210 *func_1513C4EC(s32 arg0, s32 arg1, u8 arg2, u8 arg3, f32 arg4, f32 arg5, f32 arg6, f32 arg7, f32 arg8, u8 arg9, u8 argA, s32 argB, s32 argC, s32 argD, u8 argE, s32 argF) { s32 temp_v0; @@ -227,6 +441,27 @@ void func_1513CFD0(s32 arg0, s32 arg1, s32 arg2) { #pragma GLOBAL_ASM("asm/nonmatchings/game_169510/func_1513CFE4.s") #pragma GLOBAL_ASM("asm/nonmatchings/game_169510/func_1513D2F0.s") +// best 771 (structurally exact; scheduling/reg diff only: IDO preserves flags in t8 +// via `sll t8,v0,0` and defers arg9+0x110 to the mode-branch delay slot, and swaps the +// argB/arg9 load order — a delay-slot rotation not controllable from C). structD2F0 / +// structD2F0tmpl float-view structs defined above. Permuter candidate. Reconstruction: +// void *func_1513D2F0(void *arg0, s32 arg1, u8 arg2, u8 arg3, u8 arg4, u8 arg5, u8 arg6, s32 arg7, s32 arg8, s32 arg9, u8 argA, s32 argB) { +// structD2F0 *obj; s32 type; s32 flags = ((structD2F0tmpl *)arg0)->unk40; s32 i; +// if (flags & 0x800000) type = 0x56; else if (flags & 0x2000000) type = 0x49; else type = 0x1C; +// obj = func_15167A68(type, argB, arg9 + 0x110, 1, argA, (flags < 0) ? 2 : 1); +// if (obj == 0) return 0; +// memcpy(&obj->unk18, arg0, 0x58); +// obj->unk70 = arg2; obj->unk71 = arg3; obj->unk72 = arg4; obj->unk73 = arg5; obj->unk74 = 0; +// bzero(&obj->unk100, 0x10); +// func_1513FFF4(&obj->unkC0[0], obj->unk18, arg6); +// func_151400D0(&obj->unkC0[0], arg1); +// obj->unk10 = 1; obj->unk14 = 0; obj->unk98 = 0; obj->unk95 = 0; obj->unk94 = 0; obj->unk90 = 0; +// obj->unk9C = arg7; obj->unk78 = D_800A5184; obj->unkA0 = 0; obj->unkB8 = arg8; +// for (i = 0; i < 4; i++) obj->unkA4[i] = 0; +// obj->unkB4 = 0; +// if (arg7) { for (i = 0; i <= D_80082FA0; i++) obj->unkA4[i] = func_1515D480(arg7); obj->unkB4 = func_1515D440(); } +// return obj; +// } void func_1513D4B8(s32 arg0, s32 arg1, u8 arg2, u8 arg3, u8 arg4, u8 arg5, u8 arg6, s32 arg7, u8 arg8, s32 arg9) { func_1513D2F0(arg0, arg1, arg2, arg3, arg4, arg5, arg6, 0, 0, arg7, arg8, arg9); @@ -384,7 +619,19 @@ void func_1513D668(s32 arg0, s32 arg1, u8 arg2, u8 arg3, u8 arg4, u8 arg5, s16 a #pragma GLOBAL_ASM("asm/nonmatchings/game_169510/func_1513DB00.s") -// jump table +// func_1513DF9C: CODE IS BYTE-PERFECT (verified via objdump vs target: every text +// instruction identical incl. jump-table body layout & regenerated jtbl data). asm-differ +// still reports 1725 because the seeded expected object references EXTERNAL jtbl_800A5188 +// while compiled C emits a LOCAL .rodata jump table — the object-vs-object diff cannot +// reconcile the two. Needs jtbl_800A5188 RODATA MIGRATION out of asm/data/249560.rodata.s +// (lines 1593-1609), which is outside this .c file. Reconstruction below (extern decl: +// void (*D_80089F64[])(struct210*, s32, u8)): +// if ((arg0->unk58 & 0x400000) == 0) { result = 0; } +// else switch (arg0->unk18.b.unk1) { // case order = IDO source-order body layout +// case 12: result=0xA; case 11: result=0x9; case 0: case 2: result=0; case 1: result=1; +// case 5: result=2; case 6: result=3; case 3: result=4; case 7: result=5; case 8: result=6; +// case 9: result=7; case 4: result=8; default: result=0; } +// D_80089F64[result](arg0, arg1, arg2); #pragma GLOBAL_ASM("asm/nonmatchings/game_169510/func_1513DF9C.s") void func_1513E070(s32 arg0, s32 arg1, s32 arg2) { @@ -418,15 +665,8 @@ void func_1513E134(void) { // f32 sp48; // f32 sp30; // f32 sp1C; -// -// f32 temp_f0; -// f32 temp_f12; -// f32 temp_f14; -// f32 temp_f16; -// f32 temp_f18; -// f32 temp_f2; +// f32 temp_f0, temp_f12, temp_f14, temp_f16, temp_f18, temp_f2; // s32 temp_f10; -// // sp48 = func_151423D8((arg6 - 0x40)); // temp_f0 = func_151423D8(arg6); // temp_f12 = arg5 * sp48; @@ -450,7 +690,7 @@ void func_1513E134(void) { // sp30 = arg3 - temp_f16; // arg0->unk64 = sp30 - temp_f2; // arg0->unk74 = sp30 + temp_f2; -// } +// } // best 3348: reg-alloc + 2 spills, permuter candidate void func_1513E2A4(void) { } @@ -458,6 +698,44 @@ void func_1513E2A4(void) { #pragma GLOBAL_ASM("asm/nonmatchings/game_169510/func_1513E2AC.s") #pragma GLOBAL_ASM("asm/nonmatchings/game_169510/func_1513E6E8.s") +// best 1775. Reconstruction below is logically correct (struct210_E6E8/struct211_E6E8 +// float-views defined above) but IDO folds &arg0->unk110 into a0-absolute offsets +// instead of materializing addiu v0,a0,0x110; register naming (v0/v1,a1/a2) also +// differs. Structural codegen heuristic — permuter candidate. +// s32 func_1513E6E8(struct210_E6E8 *arg0) { +// struct211_E6E8 *temp_v0, *temp_v0_2, *temp_v0_3, *temp_v0_4; +// s32 phi_v1, phi_v1_2; +// if ((arg0->unk58 & 0x20) != 0) { +// temp_v0 = &arg0->unk110; +// temp_v0->unkC = temp_v0->unkC + (temp_v0->unk2C * D_800BE9A4); +// } +// phi_v1 = arg0->unk58; +// if ((arg0->unk58 & 0x40) != 0) { +// temp_v0_2 = &arg0->unk110; +// arg0->unk34 = arg0->unk34 + (temp_v0_2->unk8 * D_800BE9A4); +// arg0->unk38 = arg0->unk38 + (temp_v0_2->unkC * D_800BE9A4); +// arg0->unk3C = arg0->unk3C + (temp_v0_2->unk10 * D_800BE9A4); +// phi_v1 = arg0->unk58; +// } +// temp_v0_3 = &arg0->unk110; +// phi_v1_2 = phi_v1; +// if ((phi_v1 & 0x80) != 0) { +// arg0->unk40 = arg0->unk40 + (temp_v0_3->unk14 * D_800BE9A4); +// arg0->unk44 = arg0->unk44 + (temp_v0_3->unk18 * D_800BE9A4); +// arg0->unk48 = arg0->unk48 + (temp_v0_3->unk1C * D_800BE9A4); +// phi_v1_2 = arg0->unk58; +// } +// temp_v0_4 = &arg0->unk110; +// if ((phi_v1_2 & 0x100) != 0) { +// arg0->unk4C = arg0->unk4C + (temp_v0_4->unk20 * D_800BE9A4); +// arg0->unk50 = arg0->unk50 + (temp_v0_4->unk24 * D_800BE9A4); +// arg0->unk54 = arg0->unk54 + (temp_v0_4->unk28 * D_800BE9A4); +// } +// arg0->unk5D = 0xFF; +// return 1; +// } + +// old mips_to_c reconstruction (pointer-typed): // s32 func_1513E6E8(void *arg0) { // void *temp_v0; // void *temp_v0_2; @@ -517,6 +795,51 @@ void func_1513F4B0(struct210 *arg0, s16 arg1) { func_15140410(arg0, &arg0->unk110, &arg0->unk11C, arg1); } +typedef struct { + /* 0x00 */ u8 unk0; + /* 0x01 */ u8 unk1; + /* 0x02 */ u8 unk2; + /* 0x03 */ u8 unk3; + /* 0x04 */ u8 unk4; + /* 0x05 */ u8 unk5; + /* 0x06 */ u8 unk6; + /* 0x07 */ u8 unk7; + /* 0x08 */ u8 unk8; + /* 0x09 */ u8 unk9; + /* 0x0A */ u8 unkA; + /* 0x0B */ u8 unkB; + /* 0x0C */ u8 unkC; + /* 0x0D */ u8 unkD; + /* 0x0E */ u8 unkE; + /* 0x0F */ u8 unkF; +} struct4BA8; +extern struct4BA8 D_800A4BA8[]; +extern s16 D_800DD1BE; +s32 func_15143044(u8, s32); + +// func_1513F4E4: G_SETCOMBINE gfx builder (struct4BA8 combine table entry). Reconstruction +// below reaches best 610: header, gDPPipeSync branch, and the w0 (combine word0) OR-tree are +// ALL byte-exact. Only w1's internal OR-tree scheduling differs by one IDO scheduler tie-break +// (andi(unkF) vs sll(unk9)) that then cascades register allocation for the rest of w1. The OR +// reduction order (unkF,unk9,unkC,unkE,unkB,unkD,unk1,unk3,unk5,unk7) is verified byte-correct; +// no C-level lever changes the schedule without changing the reduction. PERMUTER CANDIDATE. +// Gfx *func_1513F4E4(Gfx *gdl, u8 arg1, u8 *arg2) { +// s32 temp_v0; struct4BA8 *e; +// temp_v0 = func_15143044(arg1, 0); +// if (temp_v0 != D_800DD1BE) { +// if (*arg2 == 1) { gDPPipeSync(gdl++); *arg2 = 0; } +// e = &D_800A4BA8[arg1]; +// gDPSetCombine(gdl++, +// (e->unkA & 0x1F) | ((e->unk8 & 0xF) << 5) | ((e->unk0 & 0xF) << 20) | +// ((e->unk2 & 0x1F) << 15) | ((e->unk4 & 0x7) << 12) | ((e->unk6 & 0x7) << 9), +// (e->unkF & 0x7) | ((e->unk9 & 0xF) << 24) | ((e->unkC & 0x7) << 21) | +// ((e->unkE & 0x7) << 18) | ((e->unkB & 0x7) << 6) | ((e->unkD & 0x7) << 3) | +// (e->unk1 << 28) | ((e->unk3 & 0x7) << 15) | ((e->unk5 & 0x7) << 12) | +// ((e->unk7 & 0x7) << 9)); +// D_800DD1BE = temp_v0; +// } +// return gdl; +// } #pragma GLOBAL_ASM("asm/nonmatchings/game_169510/func_1513F4E4.s") void func_1513F680(struct171 *arg0, u8 arg1, u8 arg2, u8 arg3, u8 arg4) { @@ -526,21 +849,96 @@ void func_1513F680(struct171 *arg0, u8 arg1, u8 arg2, u8 arg3, u8 arg4) { arg0->unk73 = arg4; } -#pragma GLOBAL_ASM("asm/nonmatchings/game_169510/func_1513F6C0.s") +void func_1513F6C0(struct210 *arg0, u8 arg1, u8 arg2) { + arg0->unk80 = arg1; + arg0->unk81 = arg2; +} -#pragma GLOBAL_ASM("asm/nonmatchings/game_169510/func_1513F6E8.s") +s32 func_1513F6E8(struct210_2 *arg0) { + arg0->unk2C += arg0->unk128 * D_800BE9A4; + arg0->unk30 += arg0->unk128 * D_800BE9A4; + return 1; +} #pragma GLOBAL_ASM("asm/nonmatchings/game_169510/func_1513F728.s") -#pragma GLOBAL_ASM("asm/nonmatchings/game_169510/func_1513FA2C.s") +s32 func_1513FA2C(struct210_2 *arg0, s16 arg1) { + f32 sp18[2]; + + sp18[0] = 1.0f; + sp18[1] = 1.0f; + return func_1513FAB4(arg0, 0, sp18, arg1); +} -#pragma GLOBAL_ASM("asm/nonmatchings/game_169510/func_1513FA70.s") +s32 func_1513FA70(struct210_2 *arg0, s16 arg1) { + f32 sp18[2]; + + sp18[0] = 1.0f; + sp18[1] = 1.0f; + return func_1513FAB4(arg0, 1, sp18, arg1); +} #pragma GLOBAL_ASM("asm/nonmatchings/game_169510/func_1513FAB4.s") -#pragma GLOBAL_ASM("asm/nonmatchings/game_169510/func_1513FFF4.s") +void func_1513FFF4(Vtx *arg0, u8 arg1, u8 arg2) { + struct90B60 *e; + u16 P; + u16 Q; + s32 tmp; -#pragma GLOBAL_ASM("asm/nonmatchings/game_169510/func_151400D0.s") + if (arg1 == 0xFF) { + return; + } + e = &D_80090B60[arg1]; + P = e->unk6 - 1; + Q = e->unk8 - 1; + + tmp = (arg2 & 1) ? (P << 6) : 0; + arg0[3].v.tc[0] = tmp; + arg0[0].v.tc[0] = tmp; + tmp = (arg2 & 1) ? 0 : (P << 6); + arg0[2].v.tc[0] = tmp; + arg0[1].v.tc[0] = tmp; + tmp = (arg2 & 2) ? (Q << 6) : 0; + arg0[1].v.tc[1] = tmp; + arg0[0].v.tc[1] = tmp; + tmp = (arg2 & 2) ? 0 : (Q << 6); + arg0[3].v.tc[1] = tmp; + arg0[2].v.tc[1] = tmp; +} + +void func_151400D0(Vtx *arg0, struct169510Col *arg1) { + arg0->v.flag = arg1->unk8; + arg0->v.cn[0] = arg1->unk0; + arg0->v.cn[1] = arg1->unk2; + arg0->v.cn[2] = arg1->unk4; + arg0->v.cn[3] = arg1->unk6; + arg0->v.flag = 0; + arg0++; + arg1++; + arg0->v.flag = arg1->unk8; + arg0->v.cn[0] = arg1->unk0; + arg0->v.cn[1] = arg1->unk2; + arg0->v.cn[2] = arg1->unk4; + arg0->v.cn[3] = arg1->unk6; + arg0->v.flag = 0; + arg0++; + arg1++; + arg0->v.flag = arg1->unk8; + arg0->v.cn[0] = arg1->unk0; + arg0->v.cn[1] = arg1->unk2; + arg0->v.cn[2] = arg1->unk4; + arg0->v.cn[3] = arg1->unk6; + arg0->v.flag = 0; + arg0++; + arg1++; + arg0->v.flag = arg1->unk8; + arg0->v.cn[0] = arg1->unk0; + arg0->v.cn[1] = arg1->unk2; + arg0->v.cn[2] = arg1->unk4; + arg0->v.cn[3] = arg1->unk6; + arg0->v.flag = 0; +} #pragma GLOBAL_ASM("asm/nonmatchings/game_169510/func_15140190.s") @@ -552,6 +950,28 @@ void func_151403DC(s32 arg0, u8 arg1) { func_15169260(&D_800A5178, 3, arg0, arg1); } +// func_15140410: quad-vertex generator. Vertex math is byte-exact; best hand score 743. +// The ONLY diff is an IDO block-scheduler artifact: the target wedges the `return 0` exit +// block between the memcpy(flag) block and the math merge, filling a `b`-delay-slot with a +// duplicated `lw arg1` — reachable neither by top nor bottom placement of the early return +// (flat `if(x==0)return` -> return-0 at top / 763; nested `if(x!=0){}return 0` -> at bottom / 743). +// Also flag stack slot lands at 0x5f not 0x47 (not controllable via decl order). PERMUTER CANDIDATE. +// s32 func_15140410(structQuad *arg0, f32 *arg1, f32 *arg2, s16 arg3): +// func_151D5D60(&arg0->unk100[0], arg3, 0x40, &out, &flag); result = out; +// if (out) { if (flag) { memcpy(unk100[arg3],&unkC0,0x40); memcpy(unk100[arg3]+0x40,&unkC0,0x40); } +// ax=arg1[i]*unk2C; bx=arg2[i]*unk30; out[0]=center+a+b; out[1]=center-a+b; out[2]=center-a-b; +// out[3]=center+a-b; each .ob[]=trunc, .flag=0; return result; } return 0; #pragma GLOBAL_ASM("asm/nonmatchings/game_169510/func_15140410.s") +// func_151406AC: insertion-sort of a doubly-linked list into a stack sentinel (self-contained, +// integer-only). Algorithm fully reconstructed & structurally correct (frame 0x138 matches; +// node ~0x110 with unk4=prev, unk8=next, key = (unk18<<8)+(unk20>>16)). Best hand score 2005, +// but IDO's register coloring differs (target: key in t1, walking cand in v0; hand version +// rotates them) and the inner-loop uses branch-likely (bnezl) that the natural continue/do-while +// won't reproduce. Register-allocation + branch-likely near-miss. PERMUTER CANDIDATE. +// slot=&D_800DCE50[arg2][arg1]; head=*slot; sentinel.unk18=0; sentinel.unk20=0; sentinel.unk8=head; +// if(head){ head->unk4=&sentinel; for(cur=head->unk8; cur; cur=next){ prev=cur->unk4; next=cur->unk8; +// key=...; for(cand=prev; cand; ){ if(key < candKey){cand=cand->unk4; continue;} +// if(cand!=prev){ unlink cur; insert cur after cand; } break; } } } +// *slot=sentinel.unk8; sentinel.unk8->unk4=0; return arg0; #pragma GLOBAL_ASM("asm/nonmatchings/game_169510/func_151406AC.s") diff --git a/conker/src/game_16DC80.c b/conker/src/game_16DC80.c index f2dd9c70..af9f2bc8 100644 --- a/conker/src/game_16DC80.c +++ b/conker/src/game_16DC80.c @@ -3,8 +3,115 @@ #include "functions.h" #include "variables.h" +typedef struct { + u8 pad0[0x44]; + vertex *unk44; +} struct_16DC80_0; + +typedef struct { + u8 pad0[0x34]; + f32 unk34; + f32 unk38; + f32 unk3C; + f32 unk40; + f32 unk44; + f32 unk48; + u8 pad4C[0xC4]; + struct_16DC80_0 unk110; + u8 pad158[0x18]; + f32 unk170; + f32 unk174; + vertex *unk178; + vertex unk17C; +} struct_16DC80_1; + +// structs.h's struct210 is declared too short (ends at 0x120), so this file-local view is +// used for the 0x154..0x17C range. +typedef struct { + f32 unk0; + f32 unk4; + f32 unk8; + f32 unkC; +} struct_16DC80_4; + +typedef struct { + u8 pad0[0x154]; + union { + void *p; + s32 i; + } unk154; + f32 unk158; + u8 pad15C[0xC]; + u8 unk168; + u8 pad169[0x7]; + struct_16DC80_4 unk170; +} struct_16DC80_2; + +// 8-byte template copied out of D_8008A074 (typed s32[2] in variables.h, so it has to be +// re-viewed as a struct here to get the 2-word block copy). +typedef struct { + s32 unk0; + s32 unk4; +} struct_16DC80_5; + +typedef struct { + u8 pad0[0x48]; + f32 unk48; +} struct_16DC80_8; + +typedef struct { + f32 unk0; + f32 unk4; + f32 unk8; + f32 unkC; + f32 unk10; + f32 unk14; + f32 unk18; +} struct_16DC80_9; + +typedef struct { + u8 pad0[0x180]; + f32 unk180; +} struct_16DC80_A; + +extern void (*D_80089F9C[])(struct_16DC80_2 *); +extern void (*D_80089FE4[])(struct_16DC80_2 *); +extern s32 D_800DC9F0; + +void func_1517E134(void *arg0); +void func_150A7960(void *arg0, f32 arg1, f32 arg2, f32 arg3, f32 *arg4, f32 *arg5, f32 *arg6); + +void func_1514182C(struct_16DC80_1 *arg0, vertex *arg1, f32 arg2, f32 arg3, f32 arg4, f32 arg5); #pragma GLOBAL_ASM("asm/nonmatchings/game_16DC80/func_151407D0.s") +// PERMUTER CANDIDATE (best 600, but algorithmically exact - all ops/args byte-correct; the only +// diffs are IDO delay-slot scheduling: the target defers the &D_800DC9F0 address (lui into the +// block1-exit `b` delay, addiu into the `beqz` delay) whereas IDO computes it eagerly, which +// pushes the `lb arg7` load late and turns the increment guard into `beqzl`+duplicated move. +// permuter NO ZERO (~20.7k iters): normalized base 540, best 60; normalized <= real so best 60 +// => real pscore >= 60, never a match. The delay-slot scheduling is not reachable from C. +// Needs the following file-local support (functions.h wrongly declares func_1513D524 as void): +// #define func_1513D524 func_1513D524_hdr (around the functions.h include) +// #undef func_1513D524 +// extern s32 func_1513D524(s32,u8,u8,u8,u8,u8,s32,u8,s32); +// typedef struct { u8 pad0[1]; u8 unk1; u8 pad2[0x3E]; s32 unk40; } struct_16DC80_3; +// s32 func_151407D0(void *arg0, s32 arg1, struct_16DC80_3 *arg2, u8 arg3, u8 arg4, u8 arg5, +// u8 arg6, s8 arg7, u8 arg8, s32 arg9) { +// s32 ret; u8 *p; +// arg2->unk1 = 3; +// arg2->unk40 |= 0x40400000; +// ret = func_1513D524((s32)arg2, arg3, arg4, arg5, 1, arg6, arg1, arg8, arg9); +// if (ret != 0) { +// p = (u8 *)ret + 0x110; +// memcpy(p, arg0, arg1); +// *(s32 *)(p + 0x44) = 0; +// *(s8 *)(p + 0x59) = arg7; +// } else { +// return 0; +// } +// if (ret != 0) { D_800DC9F0++; } +// return ret; +// } #pragma GLOBAL_ASM("asm/nonmatchings/game_16DC80/func_151408A4.s") @@ -16,56 +123,151 @@ void func_151411C4(struct210 *arg0) { func_1513CAA0(arg0); } -#pragma GLOBAL_ASM("asm/nonmatchings/game_16DC80/func_151411E4.s") +void func_151411E4(struct_16DC80_2 *arg0) { + if (arg0->unk154.i != 0) { + func_1517E134(arg0->unk154.p); + } + D_800DC9F0--; + D_80089F9C[arg0->unk168](arg0); +} -#pragma GLOBAL_ASM("asm/nonmatchings/game_16DC80/func_15141250.s") +void func_15141250(struct_16DC80_2 *arg0) { + if (arg0->unk154.i != 0) { + func_1517E134(arg0->unk154.p); + } + D_800DC9F0--; + D_80089FE4[arg0->unk168](arg0); +} #pragma GLOBAL_ASM("asm/nonmatchings/game_16DC80/func_151412BC.s") #pragma GLOBAL_ASM("asm/nonmatchings/game_16DC80/func_1514143C.s") +// PERMUTER CANDIDATE. Best 120: 14/15 instrs match; only `addiu v0, a0, 0x110` is one slot +// early (hoisted before `beqz` instead of first instr of the if-body). Independently confirmed +// the addiu position is fixed by `sub`'s declaration point: declaring `sub = &arg0->unk110` +// before the if keeps the base+0x44(v0) form for the body but always hoists the addiu ahead of +// the branch (score 120, regardless of whether the condition is written as `sub->unk44`, +// `arg0->unk110.unk44`, or a direct 0x154 cast - all fold the condition read to 0x154(a0)); +// assigning `sub` inside the body lets copy-prop fold the body reads to 0x154(a0) and drops the +// intermediate entirely (score 255). Needs the permuter's instruction reordering. #pragma GLOBAL_ASM("asm/nonmatchings/game_16DC80/func_15141478.s") +// PERMUTER CANDIDATE (best 54). Structurally exact: both pointers materialize (v1=s0+0x170, +// a0=s0+0x110), the bc1fl branch, the nested if(func_150ADA20()&3), and every load/store/op +// match byte-for-byte. Only two register-level diffs remain: (1) the three `r * X` multiplies +// emit the operand order swapped (IDO puts the freshly-loaded operand first; target puts the +// call result f0 first) - localizing X into a named local fixes the order but adds a -g3 slot +// that grows the frame 0x30->0x38 and shifts every FP reg (net worse, 190); (2) the v1 spill +// lands at 0x20(sp) vs the target's 0x24(sp). Both are exactly what the permuter cracks. +// Uses struct_16DC80_8/9/A (defined above). +// permuter NO ZERO. Two runs (~19.5k iters each, incl. chained from the improved base) plateau at +// normalized best 10; permuter-normalized <= real, so best 10 => real pscore >= 10, never a match. +// Inlining the else-branch call (`b->unkC = func_150ADA68() * (b->unk8-b->unk0) + b->unk0;`) does +// lower real pscore 54->44 but is still non-zero, so it stays a comment per the hard rule. +// s32 func_15141478(struct_16DC80_A *arg0) { +// struct_16DC80_8 *a = (struct_16DC80_8 *)((u8 *)arg0 + 0x110); +// struct_16DC80_9 *b = (struct_16DC80_9 *)((u8 *)arg0 + 0x170); +// f32 r; +// arg0->unk180 -= D_800BE9A4; +// if (arg0->unk180 < 0.0f) { +// r = func_150ADA68(); +// b->unk10 = r * b->unk14; +// if (func_150ADA20() & 3) { +// r = func_150ADA68(); +// b->unkC = r * (b->unk0 - b->unk4) + b->unk4; +// } else { +// r = func_150ADA68(); +// b->unkC = r * (b->unk8 - b->unk0) + b->unk0; +// } +// } +// a->unk48 = a->unk48 + (b->unkC - a->unk48) * b->unk18; +// return 1; +// } #pragma GLOBAL_ASM("asm/nonmatchings/game_16DC80/func_15141564.s") +// PERMUTER CANDIDATE. Best: 26/28 instrs, all operations byte-correct; only differs in +// register/spill allocation. Target materializes `v1 = &arg0->unk170` (addiu v1,a0,0x170) and +// keeps it live across func_15144B68 (spilled to 0x18(sp), frame 0x28); IDO folds the pointer +// back to a0-relative addressing (0x170(a0)..0x17c(a0)), keeps arg0 in a0 across the call, and +// uses a 0x18 frame. Tried: pointer assigned before/after sinf, sinf arg via pointer vs arg0, +// (u8*) cast pointer arithmetic - IDO copy-propagates &arg0->unk170 to a0-relative in every +// form. Needs the permuter to force the v1 materialization / spill choice. +// permuter NO ZERO (best pscore 8): permuter's scorer normalizes the frame-size/spill offsets +// that ARE the remaining diff, so it reports a FALSE base score 0 and is blind to this near-miss. +// s32 func_15141564(struct_16DC80_2 *arg0) { +// struct_16DC80_4 *p = &arg0->unk170; +// f32 s = sinf(p->unk8); +// arg0->unk158 = p->unk0 + (p->unk4 * s); +// p->unk8 = p->unk8 + (p->unkC * D_800BE9A4); +// p->unk8 = func_15144B68(p->unk8); +// return 1; +// } #pragma GLOBAL_ASM("asm/nonmatchings/game_16DC80/func_151415D4.s") #pragma GLOBAL_ASM("asm/nonmatchings/game_16DC80/func_151416E8.s") +// PERMUTER CANDIDATE (algorithmically correct, but IDO folds `v1 = &arg0->unk110` to a0-relative +// addressing (0x168(a0)/0x169(a0)) instead of materializing `addiu v1,a0,0x110`, does not CSE the +// &D_8008A02C base, and picks bnel over bne on the 0x25 test - same class as func_15141564. +// Uses struct_16DC80_6 { u8 pad0[0x58]; u8 unk58; s8 unk59; } and +// extern void (*D_8008A02C[])(void *, u8 *, u8); +// void func_151416E8(void *arg0, u8 *arg1, u8 arg2) { +// struct_16DC80_6 *v1; +// if (D_8008A02C[((u8 *)arg0)[0x168]] != NULL) { +// D_8008A02C[((u8 *)arg0)[0x168]](arg0, arg1, arg2); +// } +// if (arg2 == 0x22 || arg2 == 0x24 || arg2 == 0x25) { +// v1 = (struct_16DC80_6 *)((u8 *)arg0 + 0x110); +// if (*arg1 == v1->unk58) { +// if (arg2 == 0x22) { func_1516972C(arg0); } +// else if (arg2 == 0x24) { v1->unk59 = -1; } +// else if (arg2 == 0x25) { v1->unk59 = 2; } +// } +// } +// } // ??? #pragma GLOBAL_ASM("asm/nonmatchings/game_16DC80/func_151417C4.s") +// PERMUTER CANDIDATE (best: only ONE extra instr - a `move a0, v0`). With the merged struct + +// u8/u8 args below EVERYTHING is byte-correct (block copy, local.b at 0x24, andi a3, lbu 0x2b, +// all four args); IDO just builds the block-copy destination in v0 (`addiu v0,sp,0x1c` + `move +// a0,v0`) instead of directly in a0 the way the target does (`addiu a0,sp,0x1c`). Confirmed the +// move is immovable from C (matches the prior agent's finding; the merged-struct here additionally +// fixes the local.b byte-position that earlier attempts got wrong). Needs the permuter. +// permuter NO ZERO, best 580 (never improved; the move a0,v0 is immovable from C). +// void func_151417C4(u8 arg0, u8 arg1) { +// struct { struct_16DC80_5 a; u8 b; } local; +// local.a = *(struct_16DC80_5 *)D_8008A074; +// local.b = arg0; +// func_15169260((s32)&local.a, 2, (s32)&local.b, arg1); +// } s32 func_15141818(s32 arg0, s32 arg1) { return 0; } -#pragma GLOBAL_ASM("asm/nonmatchings/game_16DC80/func_1514182C.s") -// f32 func_1514182C(void *arg0, void *arg1, s32 arg2, f32 arg3, s32 arg4, s32 arg5) { -// f32 sp6C; -// f32 sp68; -// f32 sp64; -// ? sp34; -// f32 temp_f0; -// f32 temp_f12; -// f32 temp_f2; -// -// func_150A8050(&sp34, arg4, 0, arg5); -// sp64 = arg1->unk0; -// sp68 = arg1->unk4; -// sp6C = arg1->unk8; -// func_150A7960(&sp34, 0, arg2, 0, arg0 + 0x34, arg0 + 0x38, arg0 + 0x3C); -// temp_f0 = arg0->unk34; -// temp_f2 = arg0->unk38; -// temp_f12 = arg0->unk3C; -// arg0->unk40 = (f32) (temp_f0 + ((temp_f0 - arg1->unk0) * arg3 * 500.0f)); -// arg0->unk44 = (f32) (temp_f2 + ((temp_f2 - arg1->unk4) * arg3 * 500.0f)); -// arg0->unk48 = (f32) (temp_f12 + ((temp_f12 - arg1->unk8) * arg3 * 500.0f)); -// return temp_f0; -// } +void func_1514182C(struct_16DC80_1 *arg0, vertex *arg1, f32 arg2, f32 arg3, f32 arg4, f32 arg5) { + f32 temp0; + f32 temp1; + f32 temp2; + f32 mtx[4][4]; -#pragma GLOBAL_ASM("asm/nonmatchings/game_16DC80/func_15141928.s") -// s32 func_15141928(void *arg0) { -// void *temp_v0 = arg0->unk178; -// func_1514182C(arg0, arg0->unk17C, arg0->unk170, arg0->unk174, temp_v0->unk0, temp_v0->unk8); -// return 1; -// } + func_150A8050(mtx, arg4, 0.0f, arg5); + mtx[3][0] = arg1->x; + mtx[3][1] = arg1->y; + mtx[3][2] = arg1->z; + func_150A7960(mtx, 0.0f, arg2, 0.0f, &arg0->unk34, &arg0->unk38, &arg0->unk3C); + temp0 = (arg0->unk34 - arg1->x) * arg3; + temp1 = (arg0->unk38 - arg1->y) * arg3; + temp2 = (arg0->unk3C - arg1->z) * arg3; + arg0->unk40 = arg0->unk34 + temp0 * 500.0f; + arg0->unk44 = arg0->unk38 + temp1 * 500.0f; + arg0->unk48 = arg0->unk3C + temp2 * 500.0f; +} + +s32 func_15141928(struct_16DC80_1 *arg0) { + vertex *temp = arg0->unk178; + + func_1514182C(arg0, &arg0->unk17C, arg0->unk170, arg0->unk174, temp->x, temp->z); + return 1; +} diff --git a/conker/src/game_16EE20.c b/conker/src/game_16EE20.c index 46dc83c4..8b7b0453 100644 --- a/conker/src/game_16EE20.c +++ b/conker/src/game_16EE20.c @@ -3,6 +3,76 @@ #include "functions.h" #include "variables.h" +extern f32 D_800A5624; +extern f32 D_800A5628; +extern f32 D_800A5694; +extern f32 D_800A5698; +extern f32 D_800A569C; +extern u8 D_800C3E90; +extern u8 D_8008A160[]; +extern u32 D_800DD200; +extern u32 D_800DD1FC; +extern u32 D_800DD218; +extern u32 D_800DD21C; +extern s16 D_800DD1C8; +extern s16 D_800DD1CA; +extern s16 D_800DD1CC; +extern s16 D_800DD1CE; +extern s16 D_800DD204; +extern s16 D_800DD206; +extern s16 D_800DD1C0; +extern s16 D_800DD1C2; +extern s16 D_800DD1C4; +extern s16 D_800DD1C6; + +// 0x40-stride record for func_15142314: hi s16 vec at 0x18, f32 vec at 0x30, +// lo s16 vec at 0x38 (aliases the f32 z) +typedef struct { + u8 pad0[0x18]; + s16 unk18; + s16 unk1A; + s16 unk1C; + u8 pad1E[0x12]; + f32 unk30; + f32 unk34; + s16 unk38; + s16 unk3A; + s16 unk3C; + u8 pad3E[0x2]; +} struct_fv; + +// struct134's unk6/unk8/unkA are u16 in structs.h but accessed signed (lh) here +typedef struct { + u8 pad0[0x6]; + s16 unk6; + s16 unk8; + s16 unkA; + u8 padC[0x9]; + u8 unk15; +} struct134_s; + +void func_15143794(s16, s16, f32, struct17 *); +s32 func_1514563C(struct17 *, struct17 *, struct17 *, struct17 *, f32 *); + +typedef struct { s32 unk[6]; } struct_s6; +extern struct_s6 D_800A5200; +s32 func_150A2AEC(s32, s32, struct_s6 *, struct127 *); + +void func_150A7960(void *arg0, f32 arg1, f32 arg2, f32 arg3, f32 *arg4, f32 *arg5, f32 *arg6); +s32 func_151452C4(struct17 *, struct17 *, struct17 *, f32, struct17 *, struct17 *, f32 *, f32 *); + +// transform record: f32 rotation vec at 0x0, s16 translation vec at 0x10 +typedef struct { + f32 unk0; + f32 unk4; + f32 unk8; + u8 padC[4]; + s16 unk10; + s16 unk12; + s16 unk14; +} struct_xf; + + void func_15141970(struct37 *arg0) { func_1514EDF0(arg0, arg0->unk2C); @@ -16,7 +86,28 @@ void func_151419B0(void *arg0) { func_15141970(arg0); } -#pragma GLOBAL_ASM("asm/nonmatchings/game_16EE20/func_151419D0.s") +typedef struct { u8 pad0[4]; s32 unk4; s32 unk8; } struct102_sub; +void func_151419D0(struct102 *arg0, u8 *arg1, u8 arg2) { + struct102_sub *sub = (struct102_sub *)((u8 *)arg0 + 0x28); + s32 *new_var; + if (arg2 == 0) { + s32 v = *(s32 *)arg1; + if (v == sub->unk4 || *(u8 *)&sub->unk8 == arg1[4]) { + func_1516972C(arg0); + } + } else { + new_var = &sub->unk4; + if (arg2 == 0x2D) { + if (*(s32 *)arg1 == *new_var) { + sub->unk4 = *(s32 *)(arg1 + 4); + *(u8 *)&sub->unk8 = arg1[9]; + } else if (*(s32 *)(arg1 + 4) == *new_var) { + sub->unk4 = *(s32 *)arg1; + *(u8 *)&sub->unk8 = arg1[8]; + } + } + } +} #pragma GLOBAL_ASM("asm/nonmatchings/game_16EE20/func_15141A7C.s") // requires jump table #pragma GLOBAL_ASM("asm/nonmatchings/game_16EE20/func_15141C0C.s") @@ -34,45 +125,51 @@ void func_15141DA4(void *arg0, s32 arg1, s32 arg2) { } #pragma GLOBAL_ASM("asm/nonmatchings/game_16EE20/func_15141E38.s") -#pragma GLOBAL_ASM("asm/nonmatchings/game_16EE20/func_15141F78.s") -// NON-MATCHING: need to determine arguments -// void func_1513C650(s32, s32, s32, u16, s32, s32, s32, f32, f32, s32, s32, s32, s32, s32, u8, s32); -// s32 func_1513C650(s32 arg0, u8 arg1, u8 arg2, s32 arg3, f32 arg4, f32 arg5, f32 arg6, f32 arg7, f32 arg8, u8 arg9, u8 argA, s32 argB, s32 argC, s32 argD, u8 argE, s32 argF); -// void func_15141F78(u8 arg0, struct157 *arg1, f32 arg2, s32 arg3, struct157 *arg4, u8 arg5) { -// struct157 tmp; -// f32 temp_f2; -// s32 phi_v0; -// -// tmp.unk6 = arg0; -// tmp.unk7 = 0; -// tmp.unk0 = 0x6F701; -// tmp.unk4 = (func_150ADA20() % 61U) + 100; -// tmp.unk8 = 0; -// tmp.unkC = 0; -// tmp.unk10 = (func_150ADA20() & 0x7F) + 128; -// tmp.unk11 = 0xFF; -// tmp.unk12 = 0xFF; -// tmp.unk13 = 0xFF; -// tmp.unk14 = 0xFF; -// tmp.unk15 = 0xFF; -// tmp.unk18 = 0x3B0002; -// tmp.unk16 = 0; -// tmp.unk17 = 7; -// tmp.unk20 = 0xFF; -// tmp.unk1C = arg1->unk18; -// tmp.unk22 = 0x28; -// tmp.unk24 = 6; -// temp_f2 = ((func_150ADA68() * 5.0f) + 10.0f) * arg2; -// // --- matching to here --- -// if (arg5 == 2) { -// phi_v0 = 1; -// } else { -// phi_v0 = 0; -// } -// func_1513C650(&tmp, 0, 0, arg1->unk4, arg4->unk0, arg1->unk0, arg4->unk8, temp_f2, temp_f2, arg3, phi_v0, 3, 1, 0, 0xFF, 1); -// } -#pragma GLOBAL_ASM("asm/nonmatchings/game_16EE20/func_151420F8.s") +void func_15141F78(u8 arg0, struct241 *arg1, f32 arg2, u8 arg3, struct17 *arg4, u8 arg5) { + struct157 tmp; + f32 temp_f2; + s32 phi_v0; + + tmp.unk6 = arg0; + tmp.unk7 = 0; + tmp.unk0 = 0x6F701; + tmp.unk4 = (func_150ADA20() % 61U) + 100; + tmp.unk8 = 0; + tmp.unkC = 0; + tmp.unk10 = (func_150ADA20() & 0x7F) + 128; + // unk11-unk15/unk20 are likely u8 (colour components); struct157 declares + // them s8, so store through a u8 cast to keep the 0xFF immediate unsigned + *(u8 *) &tmp.unk11 = 0xFF; + *(u8 *) &tmp.unk12 = 0xFF; + *(u8 *) &tmp.unk13 = 0xFF; + *(u8 *) &tmp.unk14 = 0xFF; + *(u8 *) &tmp.unk15 = 0xFF; + tmp.unk18 = 0x3B0002; + tmp.unk16 = 0; + tmp.unk17 = 7; + *(u8 *) &tmp.unk20 = 0xFF; + tmp.unk1C = arg1->unk18; + tmp.unk22 = 0x28; + tmp.unk24 = 6; + temp_f2 = ((func_150ADA68() * 5.0f) + 10.0f) * arg2; + if (arg5 == 2) { + phi_v0 = 1; + } else { + phi_v0 = 0; + } + func_1513C650(&tmp, 0, 0, &arg1->unk4, arg4->unk0, arg1->unk0, arg4->unk8, temp_f2, temp_f2, arg3, phi_v0, 3, 1, 0, 0xFF, 1); +} + +s32 func_151420F8(struct127 *arg0) { + struct_s6 sp18; + + sp18 = D_800A5200; + if (func_150A2AEC(arg0 - D_800CC2D0, 6, &sp18, arg0) == -1) { + return 0; + } + return 1; +} #pragma GLOBAL_ASM("asm/nonmatchings/game_16EE20/func_15142180.s") s32 func_151422C0(s32 arg0, s32 arg1, s32 arg2, s32 arg3) { @@ -88,24 +185,196 @@ s32 func_151422F8(s32 arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) { } #pragma GLOBAL_ASM("asm/nonmatchings/game_16EE20/func_15142314.s") -#pragma GLOBAL_ASM("asm/nonmatchings/game_16EE20/func_151423D8.s") +// NON-MATCHING: score 905, all instructions byte-identical & aligned; only the +// branch region differs (target: plain beqz with `addu v0,a0,t7` in delay + a +// speculatively-hoisted `lui 0x3780`; mine: beqzl with else-load in delay). +// Pure -O2 scheduling. Permuter candidate. Reconstruction (struct_fv, D_800C3E90): +// void func_15142314(struct_fv *arg0, s32 arg1, struct17 *arg2) { +// if (D_800C3E90 != 0) { +// arg2->unk0 = ((f32)(arg0[arg1].unk18 << 16) + (f32)arg0[arg1].unk38) * 0.0000152587890625f; +// arg2->unk4 = ((f32)(arg0[arg1].unk1A << 16) + (f32)arg0[arg1].unk3A) * 0.0000152587890625f; +// arg2->unk8 = ((f32)(arg0[arg1].unk1C << 16) + (f32)arg0[arg1].unk3C) * 0.0000152587890625f; +// } else { +// arg2->unk0 = arg0[arg1].unk30; +// arg2->unk4 = arg0[arg1].unk34; +// arg2->unk8 = *(f32 *)&arg0[arg1].unk38; +// } +// } +f32 func_151423D8(u8 arg0) { + s32 idx; + u16 q; + if (arg0 & 0x40) { idx = 0x40 - (arg0 & 0x3F); } else { idx = arg0 & 0x3F; } + q = arg0 & 0xC0; + if ((q == 0) || (q == 0xC0)) { return D_8009A220[idx]; } + return -D_8009A220[idx]; +} #pragma GLOBAL_ASM("asm/nonmatchings/game_16EE20/func_15142444.s") -#pragma GLOBAL_ASM("asm/nonmatchings/game_16EE20/func_151424F4.s") +// NON-MATCHING: score ~945, correct logic. JUSTREG: target keeps arg0 in a0/v0 +// throughout (calls func_15083E90 with arg0 already in a0); my C relocates arg0 +// to a2 and adds a move. Also the func_15083E90 result checks want beqzl +// (branch-likely) vs my bnez. Permuter candidate. Field at 0x1D4 accessed via cast. +// struct127 *func_15142444(u8 arg0, struct127 *arg1) { +// struct127 *temp; +// if (arg0 == 0xFF) { +// if (*(s32*)((u8*)arg1 + 0x1D4) != 0) return arg1; +// return NULL; +// } +// if (arg1 != NULL && arg1->interaction_state != 0 && arg0 == arg1->unique_id) { +// if (*(s32*)((u8*)arg1 + 0x1D4) != 0) return arg1; +// return NULL; +// } +// temp = func_15083E90(arg0); +// if (temp == NULL) return NULL; +// if (*(s32*)((u8*)temp + 0x1D4) == 0) return NULL; +// return temp; +// } +void func_151424F4(Mtx *arg0, f32 arg1, f32 arg2, f32 arg3, f32 arg4, f32 arg5, f32 arg6, f32 arg7, f32 arg8, f32 arg9, f32 arg10, f32 arg11) { + f32 mtx[4][4]; + + func_150A8050(mtx, arg3, arg4, arg5); + mtx[3][0] = arg9; + mtx[3][1] = arg10; + mtx[3][2] = arg11; + mtx[0][0] *= arg6 * arg1; + mtx[0][1] *= arg7 * arg1; + mtx[0][2] *= arg8 * arg1; + mtx[1][0] *= arg6 * arg2; + mtx[1][1] *= arg7 * arg2; + mtx[1][2] *= arg8 * arg2; + mtx[2][0] *= arg6 * arg1; + mtx[2][1] *= arg7 * arg1; + mtx[2][2] *= arg8 * arg1; + guMtxF2L(mtx, arg0); +} #pragma GLOBAL_ASM("asm/nonmatchings/game_16EE20/func_15142600.s") -#pragma GLOBAL_ASM("asm/nonmatchings/game_16EE20/func_15142838.s") -#pragma GLOBAL_ASM("asm/nonmatchings/game_16EE20/func_15142914.s") -#pragma GLOBAL_ASM("asm/nonmatchings/game_16EE20/func_151429E0.s") -#pragma GLOBAL_ASM("asm/nonmatchings/game_16EE20/func_15142A5C.s") -#pragma GLOBAL_ASM("asm/nonmatchings/game_16EE20/func_15142A80.s") -#pragma GLOBAL_ASM("asm/nonmatchings/game_16EE20/func_15142AC0.s") -#pragma GLOBAL_ASM("asm/nonmatchings/game_16EE20/func_15142B04.s") -#pragma GLOBAL_ASM("asm/nonmatchings/game_16EE20/func_15142B44.s") -#pragma GLOBAL_ASM("asm/nonmatchings/game_16EE20/func_15142B7C.s") -#pragma GLOBAL_ASM("asm/nonmatchings/game_16EE20/func_15142C10.s") -#pragma GLOBAL_ASM("asm/nonmatchings/game_16EE20/func_15142CF0.s") +void func_15142838(Mtx *arg0, f32 arg1, f32 arg2, f32 arg3, f32 arg4, f32 arg5, f32 arg6, f32 arg7, f32 arg8) { + f32 mtx[4][4]; + + func_150A8050(mtx, arg3, arg4, arg5); + mtx[3][0] = arg6; + mtx[3][1] = arg7; + mtx[3][2] = arg8; + mtx[0][0] *= arg1; + mtx[0][1] *= arg1; + mtx[0][2] *= arg1; + mtx[1][0] *= arg2; + mtx[1][1] *= arg2; + mtx[1][2] *= arg2; + mtx[2][0] *= arg1; + mtx[2][1] *= arg1; + mtx[2][2] *= arg1; + guMtxF2L(mtx, arg0); +} +void func_15142914(f32 arg0[4][4], f32 arg1, f32 arg2, f32 arg3, f32 arg4, f32 arg5, f32 arg6, f32 arg7, f32 arg8) { + func_150A8050(arg0, arg3, arg4, arg5); + arg0[3][0] = arg6; + arg0[3][1] = arg7; + arg0[3][2] = arg8; + arg0[0][0] *= arg1; + arg0[0][1] *= arg1; + arg0[0][2] *= arg1; + arg0[1][0] *= arg2; + arg0[1][1] *= arg2; + arg0[1][2] *= arg2; + arg0[2][0] *= arg1; + arg0[2][1] *= arg1; + arg0[2][2] *= arg1; +} +void func_151429E0(u8 arg0, u8 *arg1, u8 *arg2, u8 *arg3) { + u8 *temp; + s32 r; + + r = func_150ADA20() & 3; + temp = &D_8008A160[r * 3 + arg0 * 12]; + *arg1 = temp[0]; + *arg2 = temp[1]; + *arg3 = temp[2]; +} + +s32 func_15142A5C(struct127 *arg0) { + struct113 *temp = (struct113 *) arg0->unk2D0; + + if (temp->unk3C > 0) { + return 1; + } + return 0; +} + +f32 func_15142A80(f32 arg0) { + return (1.0f - arg0) * (arg0 - 2.0f) * arg0 * D_800A5624; +} + +f32 func_15142AC0(f32 arg0) { + return (arg0 + 1.0f) * (arg0 - 1.0f) * (arg0 - 2.0f) * 0.5f; +} + +f32 func_15142B04(f32 arg0) { + return (2.0f - arg0) * (arg0 + 1.0f) * arg0 * 0.5f; +} + +f32 func_15142B44(f32 arg0) { + return (arg0 + 1.0f) * (arg0 - 1.0f) * arg0 * D_800A5628; +} + +Gfx *func_15142B7C(Gfx *gdl, u32 arg1, u32 arg2) { + if ((~D_800DD200 & arg2) != 0) { + gSPGeometryMode(gdl++, arg2, 0); + D_800DD200 |= arg2; + } + if ((~D_800DD1FC & arg1) != 0) { + gSPGeometryMode(gdl++, 0, arg1); + D_800DD1FC |= arg1; + } + return gdl; +} +Gfx *func_15142C10(Gfx *gdl, s32 r, s32 g, s32 b, s32 a, u8 *arg5) { + if ((r != D_800DD1C8) || (g != D_800DD1CA) || (b != D_800DD1CC) || (a != D_800DD1CE)) { + if (*arg5 == 1) { + gDPPipeSync(gdl++); + *arg5 = 0; + } + gDPSetEnvColor(gdl++, r, g, b, a); + D_800DD1C8 = r; + D_800DD1CA = g; + D_800DD1CC = b; + D_800DD1CE = a; + } + return gdl; +} +Gfx *func_15142CF0(Gfx *gdl, s32 m, s32 l, s32 r, s32 g, s32 b, s32 a, u8 *arg7) { + if ((m != D_800DD204) || (l != D_800DD206) || (r != D_800DD1C0) || (g != D_800DD1C2) || (b != D_800DD1C4) || (a != D_800DD1C6)) { + if (*arg7 == 1) { + gDPPipeSync(gdl++); + *arg7 = 0; + } + gDPSetPrimColor(gdl++, m, l, r, g, b, a); + D_800DD204 = m; + D_800DD206 = l; + D_800DD1C0 = r; + D_800DD1C2 = g; + D_800DD1C4 = b; + D_800DD1C6 = a; + } + return gdl; +} #pragma GLOBAL_ASM("asm/nonmatchings/game_16EE20/func_15142E24.s") -#pragma GLOBAL_ASM("asm/nonmatchings/game_16EE20/func_15142FBC.s") -#pragma GLOBAL_ASM("asm/nonmatchings/game_16EE20/func_15143044.s") +Gfx *func_15142FBC(Gfx *gdl, u32 arg1, u32 arg2, u8 *arg3) { + if ((arg1 != D_800DD218) || (arg2 != D_800DD21C)) { + if (*arg3 == 1) { + gDPPipeSync(gdl++); + *arg3 = 0; + } + gDPSetOtherMode(gdl++, arg1 | 0xF, arg2); + D_800DD218 = arg1; + D_800DD21C = arg2; + } + return gdl; +} + +s16 func_15143044(u8 arg0, u8 arg1) { + return 0x7FFF - arg0; +} + #pragma GLOBAL_ASM("asm/nonmatchings/game_16EE20/func_1514306C.s") #pragma GLOBAL_ASM("asm/nonmatchings/game_16EE20/func_15143134.s") @@ -162,35 +431,228 @@ s32 func_151422F8(s32 arg0, s32 arg1, s32 arg2, s32 arg3, s32 arg4) { // } -#pragma GLOBAL_ASM("asm/nonmatchings/game_16EE20/func_151436B4.s") -#pragma GLOBAL_ASM("asm/nonmatchings/game_16EE20/func_1514373C.s") -#pragma GLOBAL_ASM("asm/nonmatchings/game_16EE20/func_15143794.s") -#pragma GLOBAL_ASM("asm/nonmatchings/game_16EE20/func_15143834.s") -#pragma GLOBAL_ASM("asm/nonmatchings/game_16EE20/func_15143874.s") +void func_151436B4(f32 arg0, f32 arg1, f32 arg2, struct17 *arg3) { + f32 ca = cosf(arg0); + f32 sa = sinf(arg0); + f32 cb = cosf(arg1); + f32 sb = sinf(arg1); + + arg3->unk0 = (arg2 * cb) * sa; + arg3->unk4 = -arg2 * sb; + arg3->unk8 = (arg2 * cb) * ca; +} + +void func_1514373C(f32 arg0, f32 arg1, f32 *arg2, f32 *arg3) { + f32 c = cosf(arg0); + f32 s = sinf(arg0); + + *arg2 = arg1 * s; + *arg3 = arg1 * c; +} + +void func_15143794(s16 arg0, s16 arg1, f32 arg2, struct17 *arg3) { + f32 sa = func_151423D8((u8) arg0); + f32 ca = func_151423D8((u8) (arg0 - 0x40)); + f32 sb = func_151423D8((u8) arg1); + f32 cb = func_151423D8((u8) (arg1 - 0x40)); + + arg3->unk0 = (arg2 * sb) * ca; + arg3->unk4 = -arg2 * cb; + arg3->unk8 = (arg2 * sb) * sa; +} + +void func_15143834(s16 arg0, s16 arg1, f32 arg2, struct17 *arg3) { + func_15143794(arg0, arg1, arg2, arg3); +} + +void func_15143874(s16 arg0, f32 arg1, f32 *arg2, f32 *arg3) { + f32 r1 = func_151423D8((u8)arg0); + f32 r2 = func_151423D8((u8)(arg0 - 0x40)); + + *arg2 = arg1 * r2; + *arg3 = arg1 * r1; +} + #pragma GLOBAL_ASM("asm/nonmatchings/game_16EE20/func_151438D8.s") #pragma GLOBAL_ASM("asm/nonmatchings/game_16EE20/func_15143D18.s") #pragma GLOBAL_ASM("asm/nonmatchings/game_16EE20/func_15143DA8.s") +// NON-MATCHING: score 805 with the xor-swap idiom below (was ~1075). Target spills +// arg0 to 0(sp) and reloads it into fresh temps (t6/t7) per use, and swaps arg1/arg2 +// via a temp-derive xor form - both register-pressure artifacts my clean C avoids. +// PERMUTER CANDIDATE. +// s32 func_15143DA8(s32 *arg0, s32 arg1, s32 arg2) { // clamp *arg0 into [min,max] +// if (arg2 < arg1) { arg1 ^= arg2; arg2 ^= arg1; arg1 ^= arg2; } +// if (*arg0 < arg1) { *arg0 = arg1; return 1; } +// if (arg2 < *arg0) { *arg0 = arg2; return 2; } +// return 0; +// } s32 func_15143E08(struct127 *arg0) { return (((s32) arg0->unk7A >> 8) + 64) & 0xFF; } -#pragma GLOBAL_ASM("asm/nonmatchings/game_16EE20/func_15143E24.s") -#pragma GLOBAL_ASM("asm/nonmatchings/game_16EE20/func_15143E64.s") +s16 func_15143E24(struct127 *arg0) { + if (arg0->unk31C != 0) { + return (arg0->unk7A - arg0->unk31C->unk12) >> 8; + } + return arg0->unk7A >> 8; +} + +f32 func_15143E64(struct17 *arg0) { + f32 x; + f32 y; + f32 z; + + x = arg0->unk0; + y = arg0->unk4; + z = arg0->unk8; + return sqrtf((x * x) + (y * y) + (z * z)); +} + #pragma GLOBAL_ASM("asm/nonmatchings/game_16EE20/func_15143E94.s") #pragma GLOBAL_ASM("asm/nonmatchings/game_16EE20/func_1514401C.s") +// permuter NO ZERO, best 545 +// NON-MATCHING: score 545, logic byte-identical, all instructions aligned. The +// only diff: `val` splits into two temps (t1 for div/loops, t2 for the underflow +// `val<0` check) which adds one `move` at the top and reschedules the `arg3&1` +// andi; target coalesces val into t1 everywhere. Pure register-coalescing near- +// miss - PERMUTER CANDIDATE. Reconstruction (extern u8 D_80090B64[], stride 12): +// s32 func_1514401C(u8 arg0, s32 *arg1, s32 *arg2, u8 arg3) { +// s32 ret = 0, b = D_80090B64[arg0 * 12], max = (b << 16) - 1, val; +// *arg2 += *arg1 * D_800BE9E4; +// val = *arg2; +// if (max < val) { +// if (arg3 & 1) ret = 1; +// else if (arg3 & 2) { *arg1 = 0; *arg2 = max; } +// else if (arg3 & 4) { *arg2 = max - (*arg2 % max); *arg1 = -*arg1; } +// else { do { *arg2 -= max; val = *arg2; } while (max < val); } +// } else if (val < 0) { +// if (arg3 & 8) {} +// else if (arg3 & 0x10) { *arg1 = 0; *arg2 = 0; } +// else if (arg3 & 4) { *arg2 = (-*arg2) % max; *arg1 = -*arg1; } +// else { do { *arg2 += max; val = *arg2; } while (val < 0); } +// } +// return ret; +// } #pragma GLOBAL_ASM("asm/nonmatchings/game_16EE20/func_151441A4.s") #pragma GLOBAL_ASM("asm/nonmatchings/game_16EE20/func_151442FC.s") -#pragma GLOBAL_ASM("asm/nonmatchings/game_16EE20/func_151444DC.s") -#pragma GLOBAL_ASM("asm/nonmatchings/game_16EE20/func_15144528.s") -#pragma GLOBAL_ASM("asm/nonmatchings/game_16EE20/func_15144598.s") -#pragma GLOBAL_ASM("asm/nonmatchings/game_16EE20/func_1514462C.s") + +s32 func_151444DC(s32 arg0, s32 arg1, s32 arg2) { + s32 range; + + if (arg0 > arg1) { + range = (arg1 - arg2) + 1; + do { + arg0 -= range; + } while (arg0 > arg1); + } + if (arg0 < arg2) { + range = (arg1 - arg2) + 1; + do { + arg0 += range; + } while (arg0 < arg2); + } + return arg0; +} + +f32 func_15144528(f32 arg0, f32 arg1, f32 arg2) { + while (arg0 > arg1) { + arg0 -= arg1 - arg2; + } + while (arg0 < arg2) { + arg0 += arg1 - arg2; + } + return arg0; +} +f32 func_15144598(struct134 *arg0_) { + struct134_s *arg0 = (struct134_s *) arg0_; + f32 ret; + s16 v; + + switch (arg0->unk15 & 3) { + case 2: + ret = (f32) (arg0->unk6 * arg0->unkA) * 4.0f; + break; + case 0: + case 1: + v = arg0->unk6; + ret = (f32) (v * v) * D_800A5694; + break; + default: + ret = 1.0f; + break; + } + return ret; +} +f32 func_1514462C(s32 arg0_) { + struct134_s *arg0 = (struct134_s *) arg0_; + f32 ret; + s16 v; + f32 fv; + + switch (arg0->unk15 & 3) { + case 2: + ret = (f32) (arg0->unk6 * arg0->unk8 * arg0->unkA); + break; + case 0: + v = arg0->unk6; + ret = (f32) (v * v) * D_800A5698 * (f32) arg0->unk8; + break; + case 1: + fv = arg0->unk6; + ret = fv * D_800A569C * fv * fv; + break; + default: + ret = 1.0f; + break; + } + return ret; +} #pragma GLOBAL_ASM("asm/nonmatchings/game_16EE20/func_1514470C.s") -#pragma GLOBAL_ASM("asm/nonmatchings/game_16EE20/func_15144A74.s") -#pragma GLOBAL_ASM("asm/nonmatchings/game_16EE20/func_15144AA8.s") -#pragma GLOBAL_ASM("asm/nonmatchings/game_16EE20/func_15144B34.s") + +f32 func_15144A74(struct17 *arg0, struct17 *arg1) { + return (arg0->unk0 * arg1->unk0) + (arg0->unk4 * arg1->unk4) + (arg0->unk8 * arg1->unk8); +} + +f32 func_15144AA8(s32 arg0) { + f32 v = D_800DBFF0[arg0].unk380; + + while (v > 360.0f) { + v -= 360.0f; + } + while (v < 0.0f) { + v += 360.0f; + } + return v; +} + +struct17 *func_15144B34(s32 arg0) { + return (struct17 *) &D_800DBFF0[arg0].unk2F8; +} + #pragma GLOBAL_ASM("asm/nonmatchings/game_16EE20/func_15144B68.s") -#pragma GLOBAL_ASM("asm/nonmatchings/game_16EE20/func_15144BC8.s") +// NON-MATCHING: score 60, ONE instruction misscheduled. Identical in shape to the +// matching func_15144BC8 below, but its limit is a memory load (lwc1 D_800A56A4) +// rather than a literal; IDO fills the load-delay slot with "mov.s f2,f12" whereas +// the target emits it after the first compare. Not controllable from C. +// f32 func_15144B68(f32 arg0) { +// f32 v = arg0; +// while (v > D_800A56A4) { v -= D_800A56A4; } +// while (v < 0.0f) { v += D_800A56A4; } +// return v; +// } + +f32 func_15144BC8(f32 arg0) { + f32 v = arg0; + + while (v > 360.0f) { + v -= 360.0f; + } + while (v < 0.0f) { + v += 360.0f; + } + return v; +} s32 func_15144C2C(s16 arg0) { s16 tmp1 = arg0; @@ -227,9 +689,74 @@ void func_151450B4(struct17 *arg0, struct17 *arg1, struct17 *arg2) { arg2->unk8 = arg0->unk0 * arg1->unk4 - arg0->unk4 * arg1->unk0; } -#pragma GLOBAL_ASM("asm/nonmatchings/game_16EE20/func_15145128.s") -#pragma GLOBAL_ASM("asm/nonmatchings/game_16EE20/func_151451F0.s") +s32 func_15145128(struct17 *arg0, struct17 *arg1, f32 *arg2, f32 *arg3) { + f32 mag2; + f32 local; + + if (arg3 == NULL) { + arg3 = &local; + } + mag2 = arg0->unk0 * arg0->unk0 + arg0->unk4 * arg0->unk4 + arg0->unk8 * arg0->unk8; + if (mag2 == 0.0f) { + return 0; + } + if (arg2 != NULL) { + *arg2 = sqrtf(mag2); + *arg3 = 1.0f / *arg2; + } else { + *arg3 = 1.0f / sqrtf(mag2); + } + arg1->unk0 = *arg3 * arg0->unk0; + arg1->unk4 = *arg3 * arg0->unk4; + arg1->unk8 = *arg3 * arg0->unk8; + return 1; +} +s32 func_151451F0(struct17 *arg0, struct17 *arg1, struct17 *arg2, f32 arg3, f32 arg5, struct17 *arg6, struct17 *arg7, f32 *arg8, f32 *arg9) { + f32 p8; + + if (func_151452C4(arg0, arg1, arg2, arg3, arg6, arg7, arg8, arg9) != 0) { + p8 = *arg8; + if (p8 < 0.0f) { + if (*arg9 < 0.0f) { + return 0; + } + } + if (0.0f <= p8) { + if (*arg9 < 0.0f) { + return 1; + } + } + if (p8 < arg5) { + return 1; + } + return 0; + } + return 0; +} #pragma GLOBAL_ASM("asm/nonmatchings/game_16EE20/func_151452C4.s") +// NON-MATCHING: score 1697, logic/structure byte-correct (line-sphere intersection). +// Reg-alloc near-miss: d4/d8 land in f2/f12 (mine) vs f12/f14 (target), cascading +// through all FP register numbers; target also spills `disc` to 0x1c(sp) which my C +// keeps in a register. Frame 0x68 vs 0x70. Permuter candidate. Reconstruction: +// s32 func_151452C4(struct17 *a0, struct17 *a1, struct17 *a2, f32 arg3, struct17 *arg5, +// struct17 *arg6, f32 *arg7, f32 *arg8) { +// struct17 vv, pp, delta; f32 d0,d4,d8,dot,disc,r2,s,t1,t2; +// d0 = a2->unk0 - a0->unk0; d4 = a2->unk4 - a0->unk4; d8 = a2->unk8 - a0->unk8; +// vv = *a1; pp = *a0; r2 = arg3 * arg3; +// dot = d0*vv.unk0 + d4*vv.unk4 + d8*vv.unk8; +// disc = (d0*d0 + d4*d4 + d8*d8) - dot*dot; +// if (r2 < disc) return 0; +// s = sqrtf(r2 - disc); +// if (dot < s) s = -s; +// t1 = dot - s; t2 = dot + s; +// arg5->unk0 = t1*vv.unk0 + pp.unk0; arg5->unk4 = t1*vv.unk4 + pp.unk4; arg5->unk8 = t1*vv.unk8 + pp.unk8; +// *arg7 = t1; +// arg6->unk0 = t2*vv.unk0 + pp.unk0; arg6->unk4 = t2*vv.unk4 + pp.unk4; arg6->unk8 = t2*vv.unk8 + pp.unk8; +// *arg8 = t2; +// delta.unk0 = arg5->unk0 - a0->unk0; delta.unk4 = arg5->unk4 - a0->unk4; delta.unk8 = arg5->unk8 - a0->unk8; +// if (func_15144A74(&delta, a1) < 0.0f) return 0; +// return 1; +// } s32 func_151454BC(u8 arg0, f32 arg1, struct17 *arg2) { f32 tmp1; @@ -248,8 +775,41 @@ s32 func_151454BC(u8 arg0, f32 arg1, struct17 *arg2) { return 1; } -#pragma GLOBAL_ASM("asm/nonmatchings/game_16EE20/func_15145548.s") +void func_15145548(struct17 *arg0, struct17 *arg1, struct17 *arg2, struct17 *arg3, f32 *arg4) { + f32 local; + + if (arg4 == NULL) { + arg4 = &local; + } + if (func_1514563C(arg0, arg1, arg2, arg3, arg4) != 0) { + if (*arg4 < 0.0f) { + *arg3 = *arg0; + } else if (1.0f < *arg4) { + arg3->unk0 = arg0->unk0 + arg1->unk0; + arg3->unk4 = arg0->unk4 + arg1->unk4; + arg3->unk8 = arg0->unk8 + arg1->unk8; + } + } else { + *arg3 = *arg0; + } +} #pragma GLOBAL_ASM("asm/nonmatchings/game_16EE20/func_1514563C.s") +// NON-MATCHING: score 150, frame size correct (0x20), logic byte-identical. +// Remaining: FP register renames near the divide + target homes `t` to 0(sp) +// (swc1) which my C omits. Register-allocation near-miss; permuter candidate. +// s32 func_1514563C(struct17 *arg0, struct17 *arg1, struct17 *arg2, struct17 *arg3, f32 *arg4) { +// f32 local, mag2, da, t; +// if (arg4 == NULL) arg4 = &local; +// mag2 = arg1->unk0*arg1->unk0 + arg1->unk4*arg1->unk4 + arg1->unk8*arg1->unk8; +// if (mag2 == 0.0f) return 0; +// da = arg1->unk0*arg0->unk0 + arg1->unk4*arg0->unk4 + arg1->unk8*arg0->unk8; +// t = (arg1->unk0*arg2->unk0 + arg1->unk4*arg2->unk4 + arg1->unk8*arg2->unk8 - da) / mag2; +// *arg4 = t; +// arg3->unk0 = t * arg1->unk0 + arg0->unk0; +// arg3->unk4 = *arg4 * arg1->unk4 + arg0->unk4; +// arg3->unk8 = *arg4 * arg1->unk8 + arg0->unk8; +// return 1; +// } #pragma GLOBAL_ASM("asm/nonmatchings/game_16EE20/func_15145740.s") // NON-MATCHING: 90% there @@ -331,11 +891,49 @@ u8 func_15145C90(s32 arg0) { } #pragma GLOBAL_ASM("asm/nonmatchings/game_16EE20/func_15145CD0.s") +// NON-MATCHING: score 3036, loop body byte-perfect. Reg-alloc near-miss: target +// homes arg1/arg2 across func_150A8050 and reuses arg0's saved reg (s1) for arg1; +// my C keeps arg0 in a 5th saved reg (s4). Also bnez vs bgtz on the back-edge. +// Permuter candidate. Reconstruction (struct_xf, struct17): +// void func_15145CD0(struct_xf *arg0, struct17 **arg1, struct17 **arg2, s32 arg3) { +// f32 mtx[4][4]; s32 i; struct17 *in; struct17 *out; +// func_150A8050(mtx, arg0->unk0, arg0->unk4, arg0->unk8); +// mtx[3][0]=arg0->unk10; mtx[3][1]=arg0->unk12; mtx[3][2]=arg0->unk14; +// for (i = arg3; i > 0; i--) { +// in = *arg1; out = *arg2; +// func_150A7960(mtx, in->unk0, in->unk4, in->unk8, &out->unk0, &out->unk4, &out->unk8); +// arg1++; arg2++; +// } +// } #pragma GLOBAL_ASM("asm/nonmatchings/game_16EE20/func_15145DB4.s") +// NON-MATCHING: score 2016, loop body byte-correct. Reg-alloc/iterator strength- +// reduction near-miss: target reuses arg0's saved reg (s0) for arg1 and precomputes +// arg2+4/arg2+8 iterators; my C differs on register assignment. Permuter candidate. +// Same shape as func_15145CD0 but arg1/arg2 are contiguous struct17 arrays (stride 0xC): +// void func_15145DB4(struct_xf *arg0, struct17 *arg1, struct17 *arg2, s32 arg3) { +// f32 mtx[4][4]; s32 i; +// func_150A8050(mtx, arg0->unk0, arg0->unk4, arg0->unk8); +// mtx[3][0]=arg0->unk10; mtx[3][1]=arg0->unk12; mtx[3][2]=arg0->unk14; +// for (i = arg3; i > 0; i--) { +// func_150A7960(mtx, arg1->unk0, arg1->unk4, arg1->unk8, &arg2->unk0, &arg2->unk4, &arg2->unk8); +// arg1++; arg2++; +// } +// } #pragma GLOBAL_ASM("asm/nonmatchings/game_16EE20/func_15145EA4.s") #pragma GLOBAL_ASM("asm/nonmatchings/game_16EE20/func_15146078.s") #pragma GLOBAL_ASM("asm/nonmatchings/game_16EE20/func_151462C8.s") -#pragma GLOBAL_ASM("asm/nonmatchings/game_16EE20/func_151464B8.s") + +u8 func_151464B8(s16 *arg0) { + s32 i; + s16 mask; + u8 ret; + + for (i = 0, mask = 0; i <= D_80082FA0; i++) { + mask |= 1 << i; + } + ret = (arg0[1] & mask) == 0; + return ret; +} void func_15146508(struct127 *arg0, struct127 *arg1) { struct193 tmp; @@ -349,15 +947,16 @@ void func_15146508(struct127 *arg0, struct127 *arg1) { #pragma GLOBAL_ASM("asm/nonmatchings/game_16EE20/func_1514654C.s") -#pragma GLOBAL_ASM("asm/nonmatchings/game_16EE20/func_1514672C.s") -// NON-MATCHING: JUSTREG: first 3 statements are out of order -// s32 func_1514672C(struct17 *arg0) { -// if ((D_800A56C4 < fabsf(arg0->unk0)) || (D_800A56C4 < fabsf(arg0->unk8)) || (D_800A56C4 < arg0->unk4) || (arg0->unk4 < D_800A56C8)) { -// return 0; -// } else { -// return 1; -// } -// } +s32 func_1514672C(struct17 *arg0) { + f32 new_var; + new_var = D_800A56C4; +dummy_label_28409: ; + if ((new_var < fabsf(arg0->unk0)) || (new_var < fabsf(arg0->unk8)) || (new_var < arg0->unk4) || (arg0->unk4 < D_800A56C8)) { + return 0; + } else { + return 1; + } +} void func_151467A4(f32 *arg0, f32 arg1, f32 *arg2, f32 arg3, f32 arg4, f32 arg5, f32 arg6, f32 *arg7) { *arg0 = *arg0 - D_800BE9A4; diff --git a/conker/src/game_1897A0.c b/conker/src/game_1897A0.c index 3798d88f..56568ae3 100644 --- a/conker/src/game_1897A0.c +++ b/conker/src/game_1897A0.c @@ -3,98 +3,210 @@ #include "functions.h" #include "variables.h" +// Local struct definitions: the types used by this file are not described in +// structs.h, so they are declared here rather than editing the shared header. +typedef struct { + /* 0x00 */ s32 unk0; + /* 0x04 */ s32 unk4; + /* 0x08 */ s32 unk8; +} Trail1897A0Data; // size 0xC -#pragma GLOBAL_ASM("asm/nonmatchings/game_1897A0/func_1515C2F0.s") -// void *func_1515C2F0(void *arg0, void *arg1, s32 arg2, s32 arg3, u8 arg4, s32 arg5) { -// void *sp3C; -// void *temp_v0; -// -// arg0->unk10 = 0xA; -// temp_v0 = func_15147A80(&arg1->unk40, 0x10, 8, 8, 8, 0, 0, arg3, (?32) arg4, arg5); -// if (temp_v0 == 0) { -// return NULL; -// } -// sp3C = temp_v0; -// memcpy(temp_v0->unk98, arg2, 0x3C); // memcpy -// return sp3C; -// } +typedef struct { + /* 0x00 */ Trail1897A0Data unk0; + /* 0x0C */ f32 unkC; +} Trail1897A0Entry; // size 0x10 + +typedef struct { + /* 0x00 */ f32 unk0; + /* 0x04 */ f32 unk4; + /* 0x08 */ f32 unk8; +} DataF1897A0; // size 0xC + +typedef struct { + /* 0x00 */ DataF1897A0 unk0; + /* 0x0C */ f32 unkC; +} EntryF1897A0; // size 0x10, float view of Trail1897A0Entry + +typedef struct { + /* 0x00 */ f32 unk0; + /* 0x04 */ f32 unk4; + /* 0x08 */ u8 pad08[0x4]; + /* 0x0C */ f32 unkC; + /* 0x10 */ f32 unk10; + /* 0x14 */ u8 unk14; + /* 0x15 */ u8 pad15[0x5]; + /* 0x1A */ u8 unk1A; + /* 0x1B */ u8 pad1B[0x11]; + /* 0x2C */ s16 unk2C; + /* 0x2E */ s16 unk2E; + /* 0x30 */ s16 unk30; + /* 0x32 */ u8 unk32; + /* 0x33 */ u8 pad33[0x1]; + /* 0x34 */ f32 unk34; + /* 0x38 */ s8 unk38; + /* 0x39 */ s8 unk39; +} Params1897A0; // size at least 0x3A + +typedef struct { + /* 0x00 */ u8 pad00[0x10]; + /* 0x10 */ Trail1897A0Data unk10; + /* 0x1C */ s16 unk1C; + /* 0x1E */ u8 pad1E[0x7]; + /* 0x25 */ u8 unk25; + /* 0x26 */ u8 pad26[0x6]; + /* 0x2C */ s8 unk2C; + /* 0x2D */ s8 unk2D; + /* 0x2E */ s8 unk2E; + /* 0x2F */ u8 pad2F[0x25]; + /* 0x54 */ DataF1897A0 unk54; + /* 0x60 */ u8 pad60[0x34]; + /* 0x94 */ Trail1897A0Entry *unk94; + /* 0x98 */ Params1897A0 *unk98; +} Trail1897A0; // size at least 0x9C + +typedef struct { + /* 0x00 */ u8 pad00[0x8]; + /* 0x08 */ f32 unk8; + /* 0x0C */ u8 pad0C[0x2D]; + /* 0x39 */ s8 unk39; +} Owner1897A0; // size at least 0x3A + +typedef struct { + /* 0x00 */ u8 pad00[0x18]; + /* 0x18 */ s32 unk18; +} Src1897A0; + +typedef struct { + /* 0x00 */ Src1897A0 *unk0; + /* 0x04 */ f32 unk4; + /* 0x08 */ u8 unk8; +} Msg1897A0; // size 0xC + +typedef struct { + /* 0x00 */ u8 pad00[0x98]; + /* 0x98 */ void *unk98; +} Alloc1897A0; // size at least 0x9C + +extern Alloc1897A0 *func_15147A80(void *, void *, s32, s32, s32, s32, s32, s32, s32, u8, s32); + +Alloc1897A0 *func_1515C2F0(void *arg0, void *arg1, void *arg2, s32 arg3, u8 arg4, s32 arg5) { + Alloc1897A0 *ret; + + *(s32 *)((u8 *)arg0 + 0x10) = 0xA; + ret = func_15147A80(arg0, (u8 *)arg1 + 0x40, 0x10, 8, 8, 8, 0, 0, arg3, arg4, arg5); + if (ret == NULL) { + return NULL; + } + memcpy(ret->unk98, arg2, 0x3C); + return ret; +} + +extern s32 (*D_8008B080[])(Trail1897A0 *); #pragma GLOBAL_ASM("asm/nonmatchings/game_1897A0/func_1515C388.s") + +extern s32 (*D_8008B084[])(Trail1897A0 *); + #pragma GLOBAL_ASM("asm/nonmatchings/game_1897A0/func_1515C534.s") -#pragma GLOBAL_ASM("asm/nonmatchings/game_1897A0/func_1515C6F4.s") -#pragma GLOBAL_ASM("asm/nonmatchings/game_1897A0/func_1515CF9C.s") -// s32 func_1515CF9C(void *arg0, void *arg1) { -// s32 temp_v1; -// s8 temp_v0; -// void *temp_t1; +// permuter NO ZERO, best 765 (permuter reached 540 on mutated forms but no match; the +// v0/v1-first register-allocation blocker below holds) +// PERMUTER CANDIDATE. Best attempt: score 765, but every instruction, immediate and +// memory offset matches the target - the entire remaining diff is register allocation. +// The target keeps `p` (arg0->unk98) in $a1 (spilled to 0x1c(sp) across the call), which +// cascades r->$a3, p->unk14->$a0, product->$v1, and leaves a `nop` in the guard delay slot. +// IDO here allocates the longest-lived value (p) to $v1 (it fills v0/v1 first), forcing +// r->$a1 and an extra `move $a3,$a1` in the guard delay slot. Per ido-matching-tricks: +// "leaf functions that want a value in a1 not v0/v1 are often unmatchable - IDO allocates +// v0/v1 first; no C form flips it." Tried: hoisting v to fn scope (780), a tmp for the +// subcall result (1164), explicit return if-form (765, best). Left for the permuter batch. +// The float loop unrolls-by-4 exactly from the plain `while (n) { p->unk0 *= p->unk34; n--; }`. // -// temp_v0 = arg0->unk2C; -// if ((s32) temp_v0 < (arg0->unk25 - 1)) { -// temp_v1 = arg0->unk94; -// arg0->unk2C = (s8) (temp_v0 + 1); -// temp_t1 = temp_v1 + (arg0->unk2E * 0x10); -// temp_t1->unk0 = (s32) arg0->unk10; -// temp_t1->unk4 = (s32) arg0->unk14; -// temp_t1->unk8 = (s32) arg0->unk18; -// (temp_v1 + (arg0->unk2E * 0x10))->unkC = (f32) arg1->unk8; -// arg0->unk2E = (s8) (arg0->unk2E + 1); -// if (arg0->unk25 == arg0->unk2E) { -// arg0->unk2E = (u8)0; +// s32 func_1515C534(Trail1897A0 *obj) { +// Params1897A0 *p = obj->unk98; +// u8 r = 0; +// +// if (p->unk39 != -1) { +// r = (D_8008B084[p->unk39](obj) == 0); +// } +// if (r == 0) { +// if ((p->unk14 & 4) && (obj->unk1C < p->unk2C)) { +// s32 v = obj->unk1C * p->unk2E; +// if (v < p->unk1A) { +// p->unk1A = v; +// } +// } +// if ((p->unk14 & 8) && (obj->unk1C < p->unk30) && (p->unk32 == 0)) { +// s32 n = D_800BE9E4; +// while (n != 0) { +// p->unk0 = p->unk0 * p->unk34; +// n--; +// } // } -// } else { -// arg1->unk39 = (u8)-1; // } -// return 1; +// if (r == 0) { +// return 1; +// } +// return 0; // } - -#pragma GLOBAL_ASM("asm/nonmatchings/game_1897A0/func_1515D030.s") -// s32 func_1515D030(void *arg0, ? arg1) { -// s8 temp_v0; -// s32 phi_v1; +#pragma GLOBAL_ASM("asm/nonmatchings/game_1897A0/func_1515C6F4.s") +#pragma GLOBAL_ASM("asm/nonmatchings/game_1897A0/func_1515CF9C.s") +// PERMUTER CANDIDATE. Best attempt: score 165. Every instruction matches except the tail: the target +// emits "sb zero,0x2e(a0) / b / li v0,1" (the branch delay slot holds a copy +// of the exit block's "li v0,1"), while IDO here fills that delay slot with the +// preceding "sb zero,0x2e(a0)" and shares a single "li v0,1", making the function +// one instruction short. Tried: shared trailing return, per-branch returns, a phi +// return variable, inverted/empty-arm inner if, single-line bodies, and a dead +// trailing statement - none change the delay-slot choice. // -// temp_v0 = arg0->unk2C; -// if ((s32) temp_v0 >= 3) { -// arg0->unk2C = (s8) (temp_v0 - 1); -// arg0->unk2E = (s8) (arg0->unk2E - 1); -// phi_v1 = 1; -// if ((s32) arg0->unk2E < 0) { -// arg0->unk2E = (s8) (arg0->unk25 - 1); -// phi_v1 = 1; +// s32 func_1515CF9C(Trail1897A0 *arg0, Owner1897A0 *arg1) { +// Trail1897A0Entry *entries; +// +// if (arg0->unk2C < arg0->unk25 - 1) { +// entries = arg0->unk94; +// arg0->unk2C = arg0->unk2C + 1; +// entries[arg0->unk2E].unk0 = arg0->unk10; +// entries[arg0->unk2E].unkC = arg1->unk8; +// arg0->unk2E = arg0->unk2E + 1; +// if (arg0->unk2E == arg0->unk25) { +// arg0->unk2E = 0; // } // } else { -// phi_v1 = 0; +// arg1->unk39 = -1; // } -// return phi_v1; +// return 1; // } -#pragma GLOBAL_ASM("asm/nonmatchings/game_1897A0/func_1515D088.s") -// s32 func_1515D088(void *arg0) { -// s32 sp3C; -// s8 sp38; -// f32 sp34; -// void *sp30; -// s32 temp_t7; -// s32 temp_v0; -// void * temp_v0_2; -// s32 phi_v1; -// -// temp_t7 = arg0->unk18; -// temp_v0 = temp_t7 & 0xFF; -// sp38 = (s8) temp_t7; -// if ((temp_v0 < 0) || (temp_v0 >= 2)) { -// return 0; -// } -// sp30 = arg0; -// sp34 = 0.0f; -// temp_v0_2 = func_151491F4(0x12C, -1, 0x11, 0, 0xD, 0xC, 0xFF, 1); -// phi_v1 = &temp_v0_2; -// if (temp_v0_2 != 0) { -// sp3C = temp_v0_2; -// memcpy(temp_v0_2->unk28, &sp30, 0xC); // memcpy -// phi_v1 = sp3C; -// } -// return phi_v1; -// } +s32 func_1515D030(Trail1897A0 *arg0, s32 arg1) { + s32 ret = 1; + + if (arg0->unk2C >= 3) { + arg0->unk2C = arg0->unk2C - 1; + arg0->unk2E = arg0->unk2E - 1; + if (arg0->unk2E < 0) { + arg0->unk2E = arg0->unk25 - 1; + } + } else { + ret = 0; + } + return ret; +} + +s32 func_1515D088(Src1897A0 *arg0) { + struct37 *temp_v0; + Msg1897A0 tmp; + + tmp.unk8 = arg0->unk18; + if ((tmp.unk8 < 0) || (tmp.unk8 >= 2)) { + return 0; + } + tmp.unk0 = arg0; + tmp.unk4 = 0.0f; + temp_v0 = (struct37 *)func_151491F4(0x12C, -1, 0x11, 0, 0xD, 0xC, 0xFF, 1); + if (temp_v0 != NULL) { + memcpy(&temp_v0->unk28, &tmp, 0xC); + } + return (s32)temp_v0; +} // fat struct #pragma GLOBAL_ASM("asm/nonmatchings/game_1897A0/func_1515D130.s") diff --git a/conker/src/game_18D770.c b/conker/src/game_18D770.c index 2f492546..7ecf484b 100644 --- a/conker/src/game_18D770.c +++ b/conker/src/game_18D770.c @@ -2,7 +2,135 @@ #include "functions.h" #include "variables.h" +// File-local views of structures that structs.h models incorrectly for these +// functions (structs.h's struct225 types unk18/unk1C/unk20 as inline scalars, +// but the func_15163414 family stores a struct245 payload of three f32* there). +typedef struct { + f32 *unk0; + f32 *unk4; + f32 *unk8; + s8 unkC; + u8 unkD; + u8 padE[0x2]; +} struct245_ext; + +typedef struct { + u8 pad0[0x14]; + struct226 *unk14; + struct245_ext unk18; +} struct225_245; + +typedef struct { + f32 unk0; + u8 unk4; + u8 unk5; + u8 unk6; + u8 unk7; +} struct243_ext; + +typedef struct { + u8 pad0[0x14]; + struct226 *unk14; + struct243_ext unk18; +} struct225_243; + +typedef struct { + struct127 *unk0; + u8 unk4; + u8 pad5[0x3]; +} struct244_ext; + +typedef struct { + struct127 *unk0; + struct127 *unk4; + u8 unk8; + u8 unk9; +} struct244_swap; + +typedef struct { + s32 unk0; + s32 unk4; + s32 unk8; +} coords3; + +typedef struct { + struct127 *unk0; + u8 unk4; + u8 unk5; + u8 pad6[0x2]; + coords3 unk8; + s8 unk14; + u8 unk15; + u8 pad16[0x2]; +} struct15160A58_payload; + +typedef struct { + struct127 *unk0; + u8 unk4; + u8 unk5; + u8 pad6[0x2]; + coords3 unk8; + coords3 unk14; + f32 unk20; + u8 unk24; + u8 unk25; + u8 pad26[0x2]; +} struct15160CDC_payload; + +typedef struct { + u8 pad0[0x18]; + struct244_ext unk18; +} struct225_244; + +typedef struct { + s32 unk0; + struct244_ext *unk4; +} struct225_244_arg; + +typedef struct { + u8 pad0[0x18]; + struct244_ext unk18; + u8 pad20[0x1D]; + u8 unk3D; +} struct225_244b; + +extern void (*D_8008B37C[])(s32); +extern s32 (*D_8008B358[])(struct225_244 *, struct244_ext *, u8); + +typedef struct { + f32 unk0; + f32 unk4; + f32 unk8; + void *unkC; +} struct227_ext; + +typedef struct { + struct17 unk0; + struct17 unkC; + f32 unk18; + f32 unk1C; +} struct151623F4_src; + s32 func_151149AC(u32); +void func_1516441C(struct225 *arg0, void *arg1); +void func_150A7960(f32 mtx[4][4], f32 arg1, s32 arg2, f32 arg3, f32 *arg4, f32 *arg5, f32 *arg6); +void func_15143134(f32 *arg0, f32 *arg1, s32 arg2); + +typedef struct { + f32 unk0; + f32 unk4; + f32 unk8; + f32 unkC; + s16 unk10; + s16 unk12; + s16 unk14; +} struct227_pos; + +typedef struct { + u8 pad0[0x14]; + struct226 *unk14; + struct227_pos *unk18; +} struct225_pos; struct225 *func_151602C0(Header *header, Header2 *header2, s32 arg2, s32 arg3, s32 arg4, s32 arg5, u8 arg6, u8 arg7, s32 offset, u8 arg9, s32 argA); struct225 *func_1516037C(Header *src, struct226 *arg1, s32 size, u8 arg3, s32 arg4); @@ -12,6 +140,7 @@ s32 func_1516065C(struct225 *arg0); s32 func_15160684(struct225 *arg0); s32 func_151607A4(struct225 *arg0); void func_15160954(f32 *arg0, f32 *arg1, f32 *arg2, f32 *arg3, struct225 *arg4); +void func_151618BC(u16 arg0, s16 arg1, u8 arg2, s32 arg3, struct17 *arg4, s16 arg5, s16 arg6); s32 func_15161238(struct127 *arg0, struct127 *arg1); struct225 *func_1516127C(s32 arg0, u8 arg1, s32 arg2); struct225 *func_15161334(s32 arg0, u8 arg1, s32 arg2); @@ -123,33 +252,36 @@ s32 func_15160684(struct225 *arg0) { return 1; } -#pragma GLOBAL_ASM("asm/nonmatchings/game_18D770/func_151606A8.s") -// NON-MATCHING: something along these lines... -// void func_150A7960(s32, f32, f32, f32, f32*, f32*, f32*); -// s32 func_151606A8(struct225 *arg0) { -// struct227 *temp_v0; -// struct226 *temp_v0_2; -// -// f32 tmp[16]; -// -// f32 sp3C; -// f32 sp38; -// f32 sp34; -// -// temp_v0 = arg0->unk18; -// func_150A8050(&tmp[1], temp_v0->unk0, temp_v0->unk4, temp_v0->unk8); -// -// temp_v0_2 = arg0->unk18; -// tmp[13] = temp_v0_2->unk10; -// tmp[14] = temp_v0_2->unk12; -// tmp[15] = temp_v0_2->unk14; -// -// func_150A7960(&tmp, D_800A66B4[0], D_800A66B4[1], D_800A66B4[2], &sp34, &sp38, &sp3C); -// arg0->unk14->unkE = sp34; -// arg0->unk14->unk10 = sp38; -// arg0->unk14->unk12 = sp3C; -// return 1; -// } +// PERMUTER CANDIDATE: best score 160 (inflated by cascade). Reconstruction is +// algorithmically exact -- build a translation matrix from arg0->unk18->unk0/4/8 +// via func_150A8050, poke mtx rows 12/13/14 with (f32)unk10/12/14, transform +// D_800A66B4 through func_150A7960, store the truncated results to +// arg0->unk14->unkE/10/12, return 1. Frame size (0x88) and all stack ordering +// now match. Sole residual: the 3 arg0->unk18 pointer reloads fold to +// `lw 0x18(s0)` here, while the target dedicates a register to the field +// address (`addiu v0,s0,0x18; lw 0(v0)` x3) and puts D_800A66B4 in v1 -- a +// register-allocation tie IDO breaks the other way. One missing instr cascades +// every following offset. Needs the permuter. +s32 func_151606A8(struct225_pos *arg0) { + struct227_pos *temp; + struct227_pos **pp; + f32 tmp[16]; + f32 sp3C; + f32 sp38; + f32 sp34; + + temp = arg0->unk18; + pp = &arg0->unk18; + func_150A8050(tmp, temp->unk0, temp->unk4, temp->unk8); + tmp[12] = (*pp)->unk10; + tmp[13] = (*pp)->unk12; + tmp[14] = (*pp)->unk14; + func_150A7960(tmp, D_800A66B4[0], *(s32 *)&D_800A66B4[1], D_800A66B4[2], &sp34, &sp38, &sp3C); + arg0->unk14->unkE = sp34; + arg0->unk14->unk10 = sp38; + arg0->unk14->unk12 = sp3C; + return 1; +} s32 func_151607A4(struct225 *arg0) { struct227 *temp_a1 = &arg0->unk18; @@ -192,57 +324,123 @@ void func_15160954(f32 *arg0, f32 *arg1, f32 *arg2, f32 *arg3, struct225 *arg4) } } -#pragma GLOBAL_ASM("asm/nonmatchings/game_18D770/func_15160A58.s") -// struct225 *func_15160A58(void *arg0, u8 arg1, void *arg2, u8 arg3, s16 arg4, s32 arg5, s32 arg6, s32 arg7, s32 arg8, s8 argB, s32 argC, u8 argD, u8 argE, s32 argF) { -// s32 sp64; -// -// s8 sp60; -// s16 sp5E; -// s8 sp5D; -// u8 sp5C; -// u8 sp59; -// s8 sp58; -// ? sp4C; -// u8 sp49; -// u8 sp48; -// void *sp44; -// -// ?32 sp40; -// ?32 sp3C; -// ?32 sp38; -// struct225 *temp_v0; -// s32 phi_v1; +struct225 *func_15160A58(struct127 *arg0, u8 arg1, coords3 *arg2, u8 arg3, s16 arg4, s32 arg5, s32 arg6, s32 arg7, s32 arg8, s32 arg9, s32 arg10, s8 arg11, s32 arg12, u8 arg13, u8 arg14, s32 arg15) { + struct225 *temp_v0; + Header header; + struct15160A58_payload src; + Header2 header2; + + if (arg0 == NULL) { + return NULL; + } + + header.unk0 = arg3; + header.unk1 = 5; + header.unk2 = arg4; + header.unk4 = 0x10; + src.unk0 = arg0; + src.unk4 = arg0->unique_id; + src.unk5 = arg1; + src.unk8 = *arg2; + src.unk14 = arg11; + src.unk15 = arg13; + + header2.unk0 = 0; + header2.unk4 = 0; + header2.unk8 = 0; + + temp_v0 = func_151602C0(&header, &header2, arg5, arg6, arg7, arg8, 0xFF, 0, arg12 + sizeof(src), arg14, arg15); + if (temp_v0 != NULL) { + memcpy(&temp_v0->unk18, &src, sizeof(src)); + } + return temp_v0; +} + +typedef struct { + u8 pad0[0x14]; + struct226 *unk14; + struct15160A58_payload unk18; +} struct225_A58v; + +// PERMUTER CANDIDATE: best score 416 (inflated by cascade). Reconstruction is +// algorithmically exact and byte-identical except two coloring diffs: (1) the +// target duplicates the `lb v0, 0x14(v1)` index-load into path A's exit-branch +// delay slot (+ a load-delay nop after the final trunc/mfc1 store), while IDO +// here fills that delay slot with the last store and SHARES the index-load at +// the merge -- making mine 2 instrs shorter and cascading every following +// offset; (2) the u8 `ret` spill slot lands at 0x2f (adjacent to sp20) here vs +// 0x37 (adjacent to the arg-save area) in the target. Pure tail-duplication / +// spill-slot coloring; needs the permuter. +// s32 func_15160B74(struct225_A58v *arg0) { +// struct127 *temp_v0; +// struct15160A58_payload *temp_v1; +// u8 ret = 1; +// f32 sp20[3]; // -// if (arg0 == 0) { -// return NULL; +// temp_v0 = arg0->unk18.unk0; +// temp_v1 = &arg0->unk18; +// if (temp_v0->interaction_state == 0) { +// return 0; +// } +// if (temp_v0->unique_id != temp_v1->unk4) { +// return 0; // } -// sp5C = arg3; -// sp5D = 5; -// sp60 = 0x10; -// sp5E = arg4; -// sp44 = arg0; -// sp49 = arg1; -// sp48 = arg0->unk3B; -// sp4C.unk0 = (s32) arg2->unk0; -// sp4C.unk4 = (s32) arg2->unk4; -// sp4C.unk8 = (s32) arg2->unk8; -// sp38 = 0; -// sp3C = 0; -// sp40 = 0; -// sp58 = argB; -// sp59 = argD; -// temp_v0 = func_151602C0(&sp5C, &sp38, arg5, arg6, arg7, arg8, 0xFF, 0, argC + 0x18, argE, argF); -// if (temp_v0 != NULL) { -// memcpy(&temp_v0->unk18, &sp44, 0x18); +// if ((temp_v0->unk1D4 != 0) && ((temp_v0->unk74 & 0xF) != 0xF)) { +// func_15143134((f32 *)&temp_v1->unk8, sp20, (s32)temp_v0->unk1D4 + (temp_v1->unk5 << 6)); +// arg0->unk14->unkE = sp20[0]; +// arg0->unk14->unk10 = sp20[1]; +// arg0->unk14->unk12 = sp20[2]; +// } else { +// arg0->unk14->unkE = temp_v0->x_position; +// arg0->unk14->unk10 = temp_v0->y_position; +// arg0->unk14->unk12 = temp_v0->z_position; // } -// return temp_v0; +// if (temp_v1->unk14 != -1) { +// ret = D_8008B1F8[temp_v1->unk14](arg0); +// } +// return ret; // } - -// ?? #pragma GLOBAL_ASM("asm/nonmatchings/game_18D770/func_15160B74.s") -// another struct definition -#pragma GLOBAL_ASM("asm/nonmatchings/game_18D770/func_15160CDC.s") +struct225 *func_15160CDC(struct127 *arg0, u8 arg1, coords3 *arg2, coords3 *arg3, f32 arg4, u8 arg5, s16 arg6, s32 arg7, s32 arg8, s32 arg9, s32 arg10, u8 arg11, u8 arg12, u8 arg13, u8 arg14, s32 arg15) { + struct225 *temp_v0; + Header header; + struct15160CDC_payload src; + Header2 header2; + s32 sp_v1; + s32 sp_v0; + + if (arg0 == NULL) { + return NULL; + } + + header.unk0 = arg5; + header.unk1 = 6; + header.unk2 = arg6; + header.unk4 = 0x11; + src.unk0 = arg0; + src.unk4 = arg0->unique_id; + src.unk5 = arg1; + src.unk8 = *arg2; + src.unk14 = *arg3; + + src.unk20 = arg4; + sp_v1 = (arg11 != 0) ? 1 : 0; + sp_v0 = (arg13 != 0) ? 2 : 0; + src.unk24 = sp_v0 | sp_v1; + + header2.unk0 = 0; + header2.unk4 = 0; + header2.unk8 = 0; + + src.unk25 = arg12; + + temp_v0 = func_151602C0(&header, &header2, arg7, arg8, arg9, arg10, 0xFF, 0, sizeof(src), arg14, arg15); + if (temp_v0 != NULL) { + memcpy(&temp_v0->unk18, &src, sizeof(src)); + } + return temp_v0; +} // similar to func_15160B74 #pragma GLOBAL_ASM("asm/nonmatchings/game_18D770/func_15160E30.s") @@ -458,30 +656,17 @@ void func_15161860(struct225 *arg0) { D_8008B2B0[arg0->unk12](arg0); } -#pragma GLOBAL_ASM("asm/nonmatchings/game_18D770/func_151618BC.s") -// void func_151618BC(u16 arg0, s16 arg1, u8 arg2, s32 arg3, void *arg4, s16 arg5, s16 arg6) { -// ? sp38; -// ? *temp_t6; -// void *temp_t7; -// void *phi_t7; -// ? *phi_t6; -// -// phi_t7 = &D_800A66E4; -// phi_t6 = &sp38; -// loop_1: -// temp_t7 = phi_t7 + 0xC; -// temp_t6 = phi_t6 + 0xC; -// temp_t6->unk-C = (s32) *phi_t7; -// temp_t6->unk-8 = (s32) temp_t7->unk-8; -// temp_t6->unk-4 = (s32) temp_t7->unk-4; -// phi_t7 = temp_t7; -// phi_t6 = temp_t6; -// if (temp_t7 != (&D_800A66E4 + 0x24)) { -// goto loop_1; -// } -// temp_t6->unk0 = (s32) temp_t7->unk0; -// func_10010F88((sp + ((func_150ADA20() % 0xAU) * 4))->unk38, arg0, arg1, arg2, arg3, (s32) arg4->unk0, (s32) arg4->unk4, (s32) arg4->unk8, (?32) arg5, (?32) arg6); -// } +typedef struct { + s32 unk0[10]; +} struct66E4; + +void func_151618BC(u16 arg0, s16 arg1, u8 arg2, s32 arg3, struct17 *arg4, s16 arg5, s16 arg6) { + struct66E4 sp38; + + sp38 = *(struct66E4 *)D_800A66E4; + func_10010F88(sp38.unk0[func_150ADA20() % 10U], arg0, arg1, arg2, arg3, + (s32)arg4->unk0, (s32)arg4->unk4, (s32)arg4->unk8, arg5, arg6); +} struct225 *func_151619A0(s32 arg0, s16 arg1, u8 arg2, s32 arg3) { struct225 *temp_v0; @@ -607,6 +792,30 @@ void func_15161F2C(struct225 *arg0) { func_15163F50(arg0, &arg0->unk18); } +// PERMUTER CANDIDATE: best score 165. Instruction sequence is correct; the +// dispatch handler IS called with three args -- handler(arg0, arg1, arg2) -- +// which forces arg1/arg2 to be spilled/reloaded around the func_1516972C call +// on the arg2==0 path (that was the big win from 1120 -> 165). The residual +// diff is ONLY on that arg2==0 call-path: the target reloads a3 before the +// branch and duplicates the `arg0->unk1D` load into the branch's delay slot, +// while IDO here shares that load at the merge (making it 1 instr shorter and +// cascading every following branch-target offset). Pure tail-duplication / +// delay-slot-fill coloring; needs the permuter. +// void func_15161F4C(struct225_244 *arg0, struct244_ext *arg1, u8 arg2) { +// struct244_ext *self = &arg0->unk18; +// s32 (*handler)(struct225_244 *, struct244_ext *, u8); +// if (arg2 == 0) { +// if (arg1->unk0 == self->unk0 || arg1->unk4 == self->unk4) { +// func_1516972C(arg0); +// } +// } else if (arg2 == 0x2D) { +// struct244_swap *msg = (struct244_swap *)arg1; +// if (self->unk0 == msg->unk0) { self->unk0 = msg->unk4; self->unk4 = msg->unk9; } +// else if (self->unk0 == msg->unk4) { self->unk0 = msg->unk0; self->unk4 = msg->unk8; } +// } +// handler = D_8008B358[((u8 *)arg0)[0x1D]]; +// if (handler != NULL) { handler(arg0, arg1, arg2); } +// } #pragma GLOBAL_ASM("asm/nonmatchings/game_18D770/func_15161F4C.s") struct225 *func_15162034(s32 arg0, u8 arg1, s32 arg2) { @@ -655,48 +864,61 @@ void func_15162110(s32 arg0) { } } -#pragma GLOBAL_ASM("asm/nonmatchings/game_18D770/func_151621B8.s") -// NON-MATCHING: not quite right here -// struct235 *func_151621B8(struct236 *arg0) { -// struct235 *temp_v0; -// f32 temp_f0; -// u8 temp_a0; -// u8 temp_a1; -// u8 temp_a2; -// -// temp_v0 = &arg0->unk28; -// temp_f0 = sinf(temp_v0->unk18); -// -// temp_a0 = temp_v0->unk0 + (temp_f0 * temp_v0->unkC); -// temp_a1 = temp_v0->unk4 + (temp_f0 * temp_v0->unk10); -// temp_a2 = temp_v0->unk8 + (temp_f0 * temp_v0->unk14); -// -// -// func_1515D4D4(temp_a0, temp_a1, temp_a2, 0); -// temp_v0->unk18 += temp_v0->unk1C * D_800BE9A4; -// temp_v0->unk18 = func_15144B68(temp_v0->unk18); -// -// return temp_v0; -// } +void func_151621B8(struct236 *arg0) { + struct235 *temp_v0; + f32 temp_f0; -// help. -#pragma GLOBAL_ASM("asm/nonmatchings/game_18D770/func_151623F4.s") -#pragma GLOBAL_ASM("asm/nonmatchings/game_18D770/func_15162510.s") -// s32 func_15162510(struct237 *arg0) { -// struct235 *temp_v1; -// f32 temp_f0; -// -// temp_f0 = sinf(arg0->unk18.unk18); -// temp_v1 = &arg0->unk18; -// -// arg0->unk14->unk5 = temp_v1->unk0 + (temp_f0 * arg0->unk0[2]); -// arg0->unk14->unk6 = temp_v1->unk4 + (temp_f0 * temp_v1->unk10); -// arg0->unk14->unk7 = temp_v1->unk8 + (temp_f0 * temp_v1->unk14); -// -// temp_v1->unk18 += temp_v1->unk1C * D_800BE9A4; -// temp_v1->unk18 = func_15144B68(temp_v1->unk18); -// return 1; -// } + temp_v0 = &arg0->unk28; + temp_f0 = sinf(temp_v0->unk18); + + func_1515D4D4(temp_v0->unk0 + (temp_f0 * temp_v0->unkC), + temp_v0->unk4 + (temp_f0 * temp_v0->unk10), + temp_v0->unk8 + (temp_f0 * temp_v0->unk14), 0); + temp_v0->unk18 += temp_v0->unk1C * D_800BE9A4; + temp_v0->unk18 = func_15144B68(temp_v0->unk18); +} + +struct225 *func_151623F4(s32 arg0, u8 arg1, u8 arg2, u8 arg3, s8 arg4, s16 arg5, u8 arg6, s32 arg7) { + struct151623F4_src src; + Header header; + struct225 *temp_v0; + + if (arg1 >= 3) { + return NULL; + } + + src.unk0 = *(struct17 *)D_800A670C[arg1]; + src.unkC = *(struct17 *)D_800A6730[arg1]; + src.unk18 = 0.0f; + src.unk1C = D_800A6754[arg1]; + + header.unk0 = arg3; + header.unk1 = arg4; + header.unk2 = arg5; + header.unk4 = arg2; + + temp_v0 = func_1516037C(&header, arg0, sizeof(src), arg6, arg7); + if (temp_v0 != NULL) { + memcpy(&temp_v0->unk18, &src, sizeof(src)); + } + return temp_v0; +} + +s32 func_15162510(struct237 *arg0) { + struct235 *temp_v1; + f32 temp_f0; + + temp_v1 = &arg0->unk18; + temp_f0 = sinf(temp_v1->unk18); + + arg0->unk14->unk5 = temp_v1->unk0 + (temp_f0 * temp_v1->unkC); + arg0->unk14->unk6 = temp_v1->unk4 + (temp_f0 * temp_v1->unk10); + arg0->unk14->unk7 = temp_v1->unk8 + (temp_f0 * temp_v1->unk14); + + temp_v1->unk18 += temp_v1->unk1C * D_800BE9A4; + temp_v1->unk18 = func_15144B68(temp_v1->unk18); + return 1; +} struct225 *func_15162740(s32 arg0, u8 arg1, u8 arg2, u8 arg3, s16 arg4, s8 arg5, u8 arg6, s32 arg7) { struct225 *temp_v0; @@ -751,69 +973,35 @@ struct225 *func_1516284C(Header *header, s32 arg1, s32 arg2, s32 arg3, s32 arg4, return temp_v0; } -#pragma GLOBAL_ASM("asm/nonmatchings/game_18D770/func_1516295C.s") +// PERMUTER CANDIDATE: best score 2777. Algorithm/instruction sequence correct +// (FP running-sum unk14/unk18/unk1C, src.unk24 comparison cascade, 1/temp_f18). +// Two allocation blockers: (1) IDO here spreads the 6 table loads across +// $f0/$f2/$f12/$f14/... but the target pins each to $f2/$f12/$f6/$f0/$f18/$f16; +// (2) the target overlaps `header` into src's dead tail padding (frame 0x68) +// while IDO keeps them separate (frame 0x78). Reconstruction below is believed +// semantically exact; needs the permuter for register/stack coloring. // s32 func_1516295C(s32 arg0, u8 arg1, u8 arg2, u8 arg3, s16 arg4, s8 arg5, s8 arg6, s8 arg7, s8 arg8, s8 arg9, s32 argA, u8 argB, s32 argC) { -// struct225 *temp_v0; -// Header header; -// struct238 src; -// -// f32 temp_f0; -// f32 temp_f12; -// f32 temp_f16; -// f32 temp_f18; -// f32 temp_f2; -// f32 temp_f8; -// -// if (arg1 >= 5) { -// return 0; -// } -// -// temp_f2 = D_800A6880[arg1]; -// temp_f12 = D_800A6894[arg1]; -// temp_f8 = D_800A68A8[arg1]; -// temp_f0 = D_800A68BC[arg1]; -// temp_f18 = D_800A68D0[arg1]; -// temp_f16 = D_800A68E4[arg1] ; -// -// src.unk0 = temp_f2; // sp30 -// src.unk4 = temp_f12; -// src.unk8 = temp_f2 - temp_f12; -// src.unkC = temp_f8; -// src.unk10 = temp_f0; -// src.unk14 = temp_f18 + temp_f0; -// src.unk18 = temp_f18 + temp_f0 + temp_f16; -// src.unk1C = temp_f18 + temp_f0 + temp_f16 + temp_f18; -// -// src.unk25 = arg6; -// src.unk26 = arg7; -// src.unk27 = arg8; -// src.unk28 = arg9; -// -// if (src.unkC < temp_f0) { -// src.unk24 = 0; -// } else if (src.unkC < src.unk14) { -// src.unk24 = 1; -// } else if (src.unkC < src.unk18) { -// src.unk24 = 2; -// } else if (src.unkC < src.unk1C) { -// src.unk24 = 3; -// } else { -// src.unk24 = 4; -// } -// -// header.unk0 = arg3; -// header.unk1 = arg5; -// header.unk2 = arg4; -// header.unk4 = arg2; -// +// struct225 *temp_v0; Header header; struct238 src; +// f32 temp_f0, temp_f12, temp_f16, temp_f18, temp_f2, temp_f8; +// if (arg1 >= 5) return 0; +// temp_f2 = D_800A6880[arg1]; temp_f12 = D_800A6894[arg1]; temp_f8 = D_800A68A8[arg1]; +// temp_f0 = D_800A68BC[arg1]; temp_f18 = D_800A68D0[arg1]; temp_f16 = D_800A68E4[arg1]; +// src.unk0 = temp_f2; src.unk4 = temp_f12; src.unk8 = temp_f2 - temp_f12; +// src.unkC = temp_f8; src.unk10 = temp_f0; src.unk14 = temp_f18 + temp_f0; +// src.unk18 = temp_f16 + src.unk14; src.unk1C = temp_f18 + src.unk18; +// src.unk25 = arg6; src.unk26 = arg7; src.unk27 = arg8; src.unk28 = arg9; +// if (src.unkC < temp_f0) src.unk24 = 0; +// else if (src.unkC < src.unk14) src.unk24 = 1; +// else if (src.unkC < src.unk18) src.unk24 = 2; +// else if (src.unkC < src.unk1C) src.unk24 = 3; +// else src.unk24 = 4; +// header.unk0 = arg3; header.unk1 = arg5; header.unk2 = arg4; header.unk4 = arg2; // src.unk20 = 1.0f / temp_f18; -// // temp_v0 = func_1516037C(&header, arg0, argA + 0x30, argB, argC); -// if (temp_v0 != NULL) { -// memcpy(&temp_v0->unk18, &src, 0x30); -// } +// if (temp_v0 != NULL) memcpy(&temp_v0->unk18, &src, 0x30); // return temp_v0; // } +#pragma GLOBAL_ASM("asm/nonmatchings/game_18D770/func_1516295C.s") // a biggun' #pragma GLOBAL_ASM("asm/nonmatchings/game_18D770/func_15162B28.s") @@ -902,28 +1090,28 @@ struct225 *func_15163414(Header *header, f32* arg1, f32* arg2, f32* arg3, s8 arg return temp_v0; } -#pragma GLOBAL_ASM("asm/nonmatchings/game_18D770/func_15163504.s") -// NON-MATCHING: something is missing.. -// s32 func_15163504(struct225 *arg0) { -// s32 ret = 1; -// arg0->unk14->unkE = arg0->unk18->unk0; -// arg0->unk14->unk10 = arg0->unk18->unk4; -// arg0->unk14->unk12 = arg0->unk18->unk8; -// if (arg0->unk24 != -1) { -// ret = D_8008B36C[arg0->unk24](); -// } -// -// return ret; -// } +s32 func_15163504(struct225_245 *arg0) { + struct245_ext *temp; -#pragma GLOBAL_ASM("asm/nonmatchings/game_18D770/func_151635A8.s") -// NON-MATCHING: similar issue to func_15163504 -// void func_151635A8(struct225 *arg0, s32 arg1, u8 arg2) { -// s32 (*func)(s32) = D_8008B370[arg0->unk25]; -// if (func != NULL) { -// func(arg2); -// } -// } + temp = &arg0->unk18; + arg0->unk14->unkE = *temp->unk0; + arg0->unk14->unk10 = *temp->unk4; + arg0->unk14->unk12 = *temp->unk8; + if (arg0->unk18.unkC != -1) { + return D_8008B36C[temp->unkC](); + } + + return 1; +} + +void func_151635A8(struct225_245 *arg0, s32 arg1, u8 arg2) { + struct245_ext *temp; + + temp = &arg0->unk18; + if (D_8008B370[arg0->unk18.unkD] != NULL) { + ((void (*)(struct225_245 *, s32, u8))D_8008B370[temp->unkD])(arg0, arg1, arg2); + } +} struct225 *func_15163604(s32 arg0, u8 arg1, u8 arg2, s16 arg3, u8 arg4, s32 arg5, u8 arg6, s32 arg7) { struct225 *temp_v0; @@ -991,19 +1179,32 @@ void func_1516387C(s32 arg0, u8 arg1, s8 arg2, s16 arg3, u8 arg4, s32 offset, u8 func_1516037C(&tmp, arg0, offset, arg6, arg7); } -#pragma GLOBAL_ASM("asm/nonmatchings/game_18D770/func_151638E0.s") -// NON-MATCHING: struct isnt quite right.. -// s32 func_151638E0(struct225 *arg0) { -// struct227 *temp_v1; -// f32 sp20[3]; -// f32 sp1C; -// -// temp_v1 = &arg0->unk18; -// func_15187FC0(arg0->unk18, &sp20); // missing a lw here -// func_15188010(temp_v1, &sp1C); -// arg0->unk14->unk2F = (temp_v1->unk4 + (temp_v1->unk8 * sp1C)); -// return 1; -// } +void func_15187FC0(void *arg0, f32 *arg1); +void func_15188010(void *arg0, f32 *arg1); + +typedef struct { + void *unk0; + f32 unk4; + f32 unk8; +} struct227_638E0; + +typedef struct { + u8 pad0[0x14]; + struct226 *unk14; + struct227_638E0 unk18; +} struct225_638E0; + +s32 func_151638E0(struct225_638E0 *arg0) { + struct227_638E0 *temp_v1; + f32 sp20[3]; + f32 sp1C; + + temp_v1 = &arg0->unk18; + func_15187FC0(temp_v1->unk0, sp20); + func_15188010(temp_v1->unk0, &sp1C); + arg0->unk14->unk2F = temp_v1->unk4 + (temp_v1->unk8 * sp1C); + return 1; +} s32 func_151639D0(struct225 *arg0, s32 arg1, u8 arg2) { if (arg2 == 0x27) { @@ -1129,11 +1330,87 @@ s32 func_15163F50(struct225 *arg0, struct225 *arg1) { return 1; } -// ??? -#pragma GLOBAL_ASM("asm/nonmatchings/game_18D770/func_15163FEC.s") +void func_15163FEC(struct225 *arg0, struct244_ext *arg1, u8 arg2) { + struct244_ext *self = (struct244_ext *)&arg0->unk18; + if (arg2 == 0) { + struct127 *t = arg1->unk0; + if (t == self->unk0 || arg1->unk4 == self->unk4) { + func_1516972C(arg0); + } + } else if (arg2 == 0x2D) { + struct244_swap *msg = (struct244_swap *)arg1; + if (self->unk0 == msg->unk0) { + self->unk0 = msg->unk4; + self->unk4 = msg->unk9; + } else if (self->unk0 == msg->unk4) { + self->unk0 = msg->unk0; + self->unk4 = msg->unk8; + } + } else { + void (*handler)(s32) = D_8008B374[arg0->unk2D]; + if (handler != NULL) { + handler((s32)arg0); + } + } +} #pragma GLOBAL_ASM("asm/nonmatchings/game_18D770/func_151640C0.s") +// NON-MATCHING: best score 859. Instruction sequence and all operations are +// correct; only register allocation differs. The target spills arg0 to its +// incoming home slot (sw a0,0x18(sp) / lw a0,0x18(sp)) and therefore has to +// materialize `&arg0->unk18` with a real `addiu v0,v0,0x18`, while IDO here +// keeps arg0 in a register and folds the address into the two load offsets. +// Tried: ||-chain vs nested ifs, an extra `obj = arg0` local, an eagerly +// evaluated unique_id local, a 4th dummy parameter, keeping `other` live to +// the end, K&R-style parameter declarations - none forced the arg0 spill. +// void func_151640C0(struct225_244 *arg0, struct225_244_arg *arg1, u8 arg2) { +// struct244_ext *temp; +// struct244_ext *other; +// struct127 *sp0; +// struct127 *sp1; +// u8 sp2; +// u8 sp3; +// +// if (arg2 == 41) { +// other = arg1->unk4; +// temp = &arg0->unk18; +// sp0 = other->unk0; +// sp1 = arg0->unk18.unk0; +// sp2 = other->unk4; +// sp3 = temp->unk4; +// if (sp1 != sp0) { +// if (sp2 != sp3) { +// if (sp2 != sp1->unique_id) { +// return; +// } +// } +// } +// func_1516972C(arg0); +// } +// } -#pragma GLOBAL_ASM("asm/nonmatchings/game_18D770/func_15164134.s") +void func_15164134(struct225_244b *arg0, struct244_ext *arg1, u8 arg2) { + struct244_ext *self = (struct244_ext *)&arg0->unk18; + if (arg2 == 0) { + struct127 *t = arg1->unk0; + if (t == self->unk0 || arg1->unk4 == self->unk4) { + func_1516972C(arg0); + } + } else if (arg2 == 0x2D) { + struct244_swap *msg = (struct244_swap *)arg1; + if (self->unk0 == msg->unk0) { + self->unk0 = msg->unk4; + self->unk4 = msg->unk9; + } else if (self->unk0 == msg->unk4) { + self->unk0 = msg->unk0; + self->unk4 = msg->unk8; + } + } else { + void (*handler)(s32) = D_8008B37C[arg0->unk3D]; + if (handler != NULL) { + handler((s32)arg0); + } + } +} struct225 *func_15164208(s32 arg0, u8 arg1, u8 arg2, s32 arg3) { struct225 *temp_v0; @@ -1161,8 +1438,19 @@ s32 func_1516429C(struct237 *arg0) { return 1; } -// what structs? -#pragma GLOBAL_ASM("asm/nonmatchings/game_18D770/func_1516434C.s") +void func_1516434C(struct225_243 *arg0, struct243_ext *arg1, u8 arg2) { + struct243_ext *temp; + + temp = &arg0->unk18; + if (arg2 == 51) { + if (temp->unk4 == arg1->unk4) { + temp->unk0 = arg1->unk0; + arg0->unk14->unk5 = arg1->unk5; + arg0->unk14->unk6 = arg1->unk6; + arg0->unk14->unk7 = arg1->unk7; + } + } +} void func_151643A8(struct225 *arg0, s32 arg1, u8 arg2) { struct227 *tmp; @@ -1179,8 +1467,18 @@ s32 func_151643F8(struct225 *arg0) { return 1; } -// ??? -#pragma GLOBAL_ASM("asm/nonmatchings/game_18D770/func_1516441C.s") +void func_1516441C(struct225 *arg0, void *arg1) { + void *sp2C; + void *sp28; + f32 sp1C[3]; + + sp2C = arg1; + sp28 = sp1C; + func_15145CD0(((struct227_ext *)arg1)->unkC, &sp2C, &sp28, 1); + arg0->unk14->unkE = sp1C[0]; + arg0->unk14->unk10 = sp1C[1]; + arg0->unk14->unk12 = sp1C[2]; +} s32 func_151644A8(struct242 *arg0) { f32 temp_f0; @@ -1192,26 +1490,73 @@ s32 func_151644A8(struct242 *arg0) { return 1; } -#pragma GLOBAL_ASM("asm/nonmatchings/game_18D770/func_151644F4.s") -// NON-MATCHING: close... -// void func_151644F4(struct242 *arg0, struct17 *arg1, s32 arg2, f32 arg3, f32 arg4) { -// f32 tmp1; -// f32 tmp2; -// f32 tmp3; -// -// f32 tmp[16]; -// -// func_150A8050(&tmp, arg3, 0.0f, arg4); -// -// tmp[13] = arg1->unk0; -// tmp[14] = arg1->unk4; -// tmp[15] = arg1->unk8; -// func_150A7960(&tmp, NULL, arg0, NULL, &tmp1, &tmp2, &tmp3); -// -// arg0->unk14->unkE = tmp1; -// arg0->unk14->unk10 = tmp2; -// arg0->unk14->unk12 = tmp3; -// } +void func_151644F4(struct242 *arg0, struct17 *arg1, s32 arg2, f32 arg3, f32 arg4) { + f32 tmp3; + f32 tmp2; + f32 tmp1; + f32 tmp[16]; + + func_150A8050(tmp, arg3, 0.0f, arg4); + tmp[12] = arg1->unk0; + tmp[13] = arg1->unk4; + tmp[14] = arg1->unk8; + func_150A7960(tmp, 0.0f, arg2, 0.0f, &tmp1, &tmp2, &tmp3); + arg0->unk14->unkE = tmp1; + arg0->unk14->unk10 = tmp2; + arg0->unk14->unk12 = tmp3; +} -// loop -#pragma GLOBAL_ASM("asm/nonmatchings/game_18D770/func_151645C4.s") +void func_1511172C(s32 arg0); +void func_1515F170(s32 arg0, s32 arg1); +extern u8 D_800886F0[]; +extern u8 D_800886F4[]; +extern u8 D_800886F8[]; + +void func_151645C4(u8 arg0) { + D_800DCDD0 = arg0; + if (arg0 != 0) { + switch (D_800BE9F0) { + case 0xB: + func_1511172C(4); + break; + case 6: + func_1515F170(0xA, 1); + func_1511172C(4); + /* fallthrough */ + case 0x39: + ((u8 *)&D_800DCD20)[0] = D_800886F0[0]; + ((u8 *)&D_800DCD20)[1] = D_800886F0[1]; + ((u8 *)&D_800DCD20)[2] = D_800886F0[2]; + D_800B0DF0->unk5 = D_800886F4[0]; + D_800B0DF0->unk6 = D_800886F4[1]; + D_800B0DF0->unk7 = D_800886F4[2]; + break; + case 7: + case 0xC: + break; + } + } else { + switch (D_800BE9F0) { + case 6: + func_1515F170(0xA, 0); + /* fallthrough */ + case 7: + case 0xC: + case 0x39: + func_1511172C(6); + ((u8 *)&D_800DCD20)[0] = D_800886F8[0]; + ((u8 *)&D_800DCD20)[1] = D_800886F8[1]; + ((u8 *)&D_800DCD20)[2] = D_800886F8[2]; + D_800B0DF0->unk5 = 0xFF; + D_800B0DF0->unk6 = 0xFF; + D_800B0DF0->unk7 = 0xFF; + break; + case 0xB: + func_1511172C(6); + break; + case 0x29: + func_1511172C(1); + break; + } + } +} diff --git a/conker/src/game_1944C0.c b/conker/src/game_1944C0.c index c012c69c..7222f086 100644 --- a/conker/src/game_1944C0.c +++ b/conker/src/game_1944C0.c @@ -3,58 +3,367 @@ #include "functions.h" #include "variables.h" +extern u8 D_800D2DAB; +extern void (*D_8008CA20[])(); + +void func_15168B10(s32 arg0, s32 arg1); +void func_15169070(s32 arg0, s32 arg1, s32 arg2, u8 arg3); + +// Local struct: structs.h's struct102/struct114 do not cover all of the fields +// (0x4/0x8 links, 0xC, 0x3F) touched by the functions below. +typedef struct Obj { + u8 unk0; + u8 unk1; + u8 pad2[2]; + struct Obj *unk4; + struct Obj *unk8; + u8 unkC; + u8 padD[7]; + s32 unk14; + u8 pad18[0x20]; + s16 unk38; + u8 pad3A; + u8 unk3B; + u8 pad3C[3]; + u8 unk3F; +} Obj; + +typedef struct { + void *unk0; + u8 unk4; +} ObjRef; + +// 8-byte display-list command entry (byte0 = opcode, byte3 flag, word4 payload) +typedef struct { + s8 cmd; + u8 pad1[2]; + u8 unk3; + s32 unk4; +} GfxCmd; + +typedef struct { + u8 pad0[4]; + u8 unk4; // frame count +} AnimSrc; + +typedef struct { + u8 pad0[0x10]; + AnimSrc *unk10; + s16 unk14; + s16 unk16; + u8 pad18[0xA]; + s8 unk22; + u8 unk23; + u8 unk24; +} AnimObj; + +extern Obj *D_800DCE50[2][104]; + +void func_15168A4C(Obj *arg0, s32 arg1); + +// structs.h types D_8008B4A8's entries with a struct102* parameter; these two +// slots are actually called with no arguments. +typedef struct { + void (*unk0)(void); + u8 pad0[0x14]; + void (*unk18)(void); + u8 pad1[0x18]; +} Handler; + +#define HANDLERS ((Handler *)D_8008B4A8) + +// struct115 (D_8008B4A8 entries) really has two consecutive Obj* handlers at +// 0x0/0x4; the header only declares 0x0 and 0x18. +typedef struct { + void (*unk0)(Obj *arg0); + void (*unk4)(Obj *arg0); + u8 pad[0x2C]; +} Handler2; + +extern void (*D_8008CB64[3])(void); +// D_8008CB70 is &D_8008CB64[3]; referenced by name so the relocation matches. +extern void (*D_8008CB70[])(void); + #pragma GLOBAL_ASM("asm/nonmatchings/game_1944C0/func_15167010.s") -// NON-MATCHING: not hugely far away +// NON-MATCHING: best score 280. Every loop-body instruction matches byte-for-byte. +// The target strength-reduces an induction index `i` into the pointer compare +// (`sltu it,end` with `end = it + 0x1484` derived from s0) but KEEPS i's saved +// register s1 reserved-yet-unused (frame 0x28, saves s0/s1/s2). By hand IDO either +// keeps i as a live counter (for-loop w/ it++,i++ -> matches frame+savedregs but no +// reduction, score 840) OR fully strength-reduces AND eliminates i (HANDLERS[i] +// access -> end in s1, `!=` compare, no phantom, score 700; do-while it sltu +// but end absolute in s1, score 400). -g3 here eliminates dead vars, so the +// saved-but-dead s1 can't be forced by hand. PERMUTER CANDIDATE. // void func_15167010(void) { -// void (*func)(void); -// s32 i; -// -// for (i = 0; i < 24; i++) -// { -// func = D_8008B4A8[i].unk18; -// if (func != NULL) { -// func(); -// } +// Handler *it; +// Handler *end; +// it = HANDLERS; +// end = (Handler *)((s32)it + 0x1484); +// for (;;) { +// if (it->unk18 != NULL) { it->unk18(); } +// it++; +// if (it >= end) { break; } // } // } -#pragma GLOBAL_ASM("asm/nonmatchings/game_1944C0/func_1516706C.s") +void func_1516706C(void) { + void (**it)(void); + void (**end)(void); + + for (it = D_8008CB64, end = D_8008CB70;;) { + if (*it != NULL) { + (*it)(); + } + it++; + if (it == end) { + break; + } + } +} #pragma GLOBAL_ASM("asm/nonmatchings/game_1944C0/func_151670C0.s") +// NON-MATCHING: best score 25. Byte-identical ops; only a v0<->t9 rename on the +// loop-carried handler pointer `fn` (cascades to the D_800DD190-- temp t9<->t0). +// Permuter running. Reconstruction (twin of func_151671E8, uses h->unk0): +// void func_151670C0(void) { +// s32 col, i; Handler2 *h; Obj *obj, **sp; void (*fn)(Obj *); +// for (col = 0; col != 2; col++) { +// h = (Handler2 *)D_8008B4A8; +// for (i = 0; i != 0x65; i++) { +// if (h->unk0 != NULL) { +// obj = D_800DCE50[col][i]; +// D_800DD190++; +// if (obj != NULL) { +// sp = &((Obj **)D_800DD198)[D_800DD190]; +// do { +// fn = h->unk0; *sp = obj->unk8; fn(obj); +// sp = &((Obj **)D_800DD198)[D_800DD190]; obj = *sp; +// } while (obj != NULL); +// } +// D_800DD190--; +// } +// h++; +// } +// } +// } #pragma GLOBAL_ASM("asm/nonmatchings/game_1944C0/func_151671E8.s") +// NON-MATCHING: best score 25. Twin of func_151670C0 but uses h->unk4. Same +// v0<->t9 rename on the loop-carried handler pointer. Apply permuter fix from +// func_151670C0 (swap h->unk0 for h->unk4). #pragma GLOBAL_ASM("asm/nonmatchings/game_1944C0/func_15167310.s") #pragma GLOBAL_ASM("asm/nonmatchings/game_1944C0/func_151674F8.s") -#pragma GLOBAL_ASM("asm/nonmatchings/game_1944C0/func_15167A68.s") -#pragma GLOBAL_ASM("asm/nonmatchings/game_1944C0/func_15167AD8.s") +Obj *func_15167A68(s32 arg0, s32 arg1, s32 arg2, s32 arg3, u8 arg4, u8 arg5) { + Obj *obj; + + obj = (Obj *)func_10003C6C(arg2, 1, arg3, 0, arg5); + if (obj != NULL) { + obj->unk1 = arg1; + func_15168A4C(obj, arg0); + obj->unkC = arg4; + } + return obj; +} +void func_15167AD8(s32 arg0, u8 arg1, s32 arg2) { + void *obj; + + obj = func_15167A68(3, arg2, 0x28, 0, arg1, 1); + if (obj != NULL) { + bcopy((void *)arg0, (u8 *)obj + 0x10, 0x18); + *((u8 *)obj + 0x23) = 0xFF; + } +} #pragma GLOBAL_ASM("asm/nonmatchings/game_1944C0/func_15167B44.s") +// NON-MATCHING: best score 40. Every instruction/op/branch matches byte-for-byte; +// only a1<->a2 are swapped in the ctrl<0 block (target holds unk14/pos in a2 and +// -ctrl in a1; this build does the reverse). Pure arg-register tie-break. +// void func_15167B44(AnimObj *arg0) { // fields 0x10 ptr, 0x14/0x16 s16, 0x22 s8, 0x23/0x24 u8 +// s8 ctrl; +// if (arg0->unk24 != 0) { D_8008CA20[arg0->unk24](); } +// ctrl = arg0->unk22; +// if (ctrl > 0) { +// if (ctrl < arg0->unk23) { arg0->unk23 = arg0->unk23 - ctrl; } +// else { arg0->unk14 = arg0->unk10->unk4 << 8; } +// } else if (ctrl < 0) { +// if (arg0->unk14 / 256 >= arg0->unk10->unk4 - 1) { +// if (-ctrl < arg0->unk23) { +// arg0->unk23 = arg0->unk23 - (-ctrl); +// arg0->unk14 = arg0->unk14 - arg0->unk16; +// } +// } +// } +// arg0->unk14 = arg0->unk14 + arg0->unk16; +// if (arg0->unk14 / 256 >= arg0->unk10->unk4) { func_1516972C((struct102 *)arg0); } +// } #pragma GLOBAL_ASM("asm/nonmatchings/game_1944C0/func_15167C58.s") -#pragma GLOBAL_ASM("asm/nonmatchings/game_1944C0/func_15167D84.s") +// NON-MATCHING: best score 540. EVERY instruction, operand, branch and delay slot +// matches byte-for-byte. The only differences are (1) a uniform temp-register +// rotation: the target's temp allocation starts at t7 (flag=1 -> t7), this build +// starts at t6, so all ~10 temps are shifted by +1; and (2) -g3 reserves stack +// slots for extra named locals in the original, giving the frame gaps +// idx@0x48 / flag@0x50 / vtx@0x5C (0x70 frame) that this packed build can only +// approximate. Both are allocation-seed artifacts -> PERMUTER CANDIDATE. +// Callee sigs used: func_15142E24 takes 11 args (gdl,unk10,unk14<<8,2,0x100,0x100, +// unk10->unk0[unk14>>8],5,&vtx,&flag,3) -> Gfx*; func_15142FBC(gdl,0x2C00, +// (A[idx].unk4|A[idx].unk0)|4,&flag); func_15095760(gdl,&vtx). A = D_800A4AC8 +// (array of {s32 unk0; s32 unk4;}). idx = (obj->unk22==0xFF)?9:3 via if/else-if. +// void func_15167C58(Gfx *gdl, DrawObj *obj, s32 arg2 /*homed, unused*/) { +// Vtx2 vtx; s32 flag; s32 idx; +// flag = 1; +// gdl = func_15142E24(gdl, obj->unk10, obj->unk14 << 8, 2, 0x100, 0x100, +// obj->unk10->unk0[obj->unk14 >> 8], 5, &vtx, &flag, 3); +// vtx.unk0 = obj->unk18; vtx.unk2 = obj->unk1A; vtx.unk4 = obj->unk1C; +// vtx.unk6 = obj->unk1E; vtx.unk8 = obj->unk20; vtx.unkD = 0; +// vtx.unkA = obj->unk23; +// if (obj->unk22 != 0xFF) { idx = 3; } else if (obj->unk22 == 0xFF) { idx = 9; } +// gdl = func_15142FBC(gdl, 0x2C00, +// (D_800A4AC8[idx].unk4 | D_800A4AC8[idx].unk0) | 4, (u8*)&flag); +// func_15095760(gdl, &vtx); +// } +// DrawObj: 0x10 FooSrc*{u32* unk0}; 0x14 s16 unk14; 0x18/1A/1C/1E/20 s16; +// 0x22 s8 unk22; 0x23 u8 unk23. Vtx2: s16[5] @0,u8 @A, u8 @D (14 bytes). +void *func_15167D84(s32 arg0, s32 arg1, s32 arg2, s8 arg3, u8 arg4, s32 arg5) { + void *obj; + + obj = func_15167A68((arg1 == 0) ? 5 : 0x42, arg5, arg2 + 0x50, 0, arg4, 1); + if (obj == NULL) { + return obj; + } + bcopy((void *)arg0, (u8 *)obj + 0x10, 0x38); + *((s8 *)obj + 0x48) = arg3; + return obj; +} #pragma GLOBAL_ASM("asm/nonmatchings/game_1944C0/func_15167E0C.s") #pragma GLOBAL_ASM("asm/nonmatchings/game_1944C0/func_15168118.s") #pragma GLOBAL_ASM("asm/nonmatchings/game_1944C0/func_1516865C.s") -#pragma GLOBAL_ASM("asm/nonmatchings/game_1944C0/func_15168800.s") -#pragma GLOBAL_ASM("asm/nonmatchings/game_1944C0/func_15168870.s") -#pragma GLOBAL_ASM("asm/nonmatchings/game_1944C0/func_15168A2C.s") -#pragma GLOBAL_ASM("asm/nonmatchings/game_1944C0/func_15168A4C.s") -#pragma GLOBAL_ASM("asm/nonmatchings/game_1944C0/func_15168A9C.s") -// void *func_15168A9C(struct12 *arg0) { -// void *temp_a1; -// void *temp_v0; -// void *temp_v0_2; -// -// temp_a1 = (arg0->unk1 * 0x1A0) + (arg0->unk0 * 4) + 0x800DCE50; -// if (arg0 == *temp_a1) { -// *temp_a1 = (void *) arg0->unk8; -// } -// temp_v0_2 = arg0->unk8; -// if (temp_v0_2 != 0) { -// temp_v0_2->unk4 = (void *) arg0->unk4; +// NON-MATCHING: best score 425. Leaf function; EVERY instruction, operand, +// branch and delay slot matches byte-for-byte (loop body + entire tail are +// exact). The whole remaining diff is a single register-coloring tie-break: the +// target pairs the two "swap" pairs va/vb -> v0/v1 and tc/td -> t1/t2, but this +// build allocates tc=v1 and vb=t1 (a clean t1<->v1 swap). That one decision +// cascades: because vb lands in a temp instead of v1, its scaled-value +0x100 +// is done in-place (one scheduling flip), and the branch recompute blocks rotate +// t6..t9 by one. IDO makes the pairing by swap-pair foresight; no C form forces +// it (tested: operand/decl reorder, split vs inline scale, flags u16->s32, +// entry/tc ordering -> 1465/795/705/570/515/445/425, 425 is the hand floor). +// PERMUTER CANDIDATE. The reconstruction below is algorithmically exact. +// typedef struct { u8 pad0[6]; s16 f6; s16 f8; s16 fA; u8 fC,fD,fE,fF; } Slot865; +// typedef struct { Slot865 slots[8]; u8 pad80[8]; s16 unk88; u8 pad8A[0xE]; +// u16 unk98; u8 pad9A[2]; s16 unk9C; s16 unk9E; u8 unkA0; } Obj865; +// typedef struct { u8 pad0[6]; u16 unk6; u16 unk8; } Tune865; +// extern Tune865 *D_8008CA4C[]; +// void func_1516865C(Obj865 *arg0, u8 arg1, u8 arg2, u8 arg3, u8 arg4) { +// Tune865 *entry; Slot865 *p; s32 i, va, tc, vb, td, flags; s16 tmp; +// va = 0x2000; tc = 0x2000; +// entry = D_8008CA4C[arg0->unkA0]; +// vb = ((entry->unk8 + 0x100) << 21) >> 16; +// td = ((entry->unk6 + 0x100) << 21) >> 16; +// i = 0; p = &arg0->slots[0]; +// for (; i < 2; i++) { +// p += 4; +// p[-3].f6=0; p[-3].fC=arg1; p[-3].fD=arg2; p[-3].fE=arg3; p[-3].fF=arg4; +// p[-2].f6=0; p[-2].fC=arg1; p[-2].fD=arg2; p[-2].fE=arg3; p[-2].fF=arg4; +// p[-1].f6=0; p[-1].fC=arg1; p[-1].fD=arg2; p[-1].fE=arg3; p[-1].fF=arg4; +// p[-4].f6=0; p[-4].fC=arg1; p[-4].fD=arg2; p[-4].fE=arg3; p[-4].fF=arg4; // } -// temp_v0 = arg0->unk4; -// if (temp_v0 != 0) { -// temp_v0->unk8 = (void *) arg0->unk8; +// flags = arg0->unk98; +// if (flags & 0x80) { va = ((D_8008CA4C[arg0->unkA0]->unk8+0x100)<<21)>>16; vb = 0x2000; } +// if (flags & 0x100) { td = 0x2000; tc = ((D_8008CA4C[arg0->unkA0]->unk6+0x100)<<21)>>16; } +// arg0->slots[6].f8 = tc; +// tmp = arg0->slots[6].f8; +// arg0->slots[7].fA = va; arg0->slots[7].f8 = td; +// arg0->slots[2].f8 = tmp; arg0->slots[5].f8 = tmp; arg0->slots[1].f8 = tmp; +// tmp = arg0->slots[7].fA; +// arg0->slots[5].fA = vb; arg0->unk88 = 0; +// arg0->slots[3].fA = tmp; arg0->slots[6].fA = tmp; arg0->slots[2].fA = tmp; +// tmp = arg0->slots[7].f8; arg0->unk9C = 0; arg0->unk9E = 0; +// arg0->slots[3].f8 = tmp; arg0->slots[4].f8 = tmp; arg0->slots[0].f8 = tmp; +// tmp = arg0->slots[5].fA; +// arg0->slots[1].fA = tmp; arg0->slots[4].fA = tmp; arg0->slots[0].fA = tmp; +// } +void *func_15168800(s32 arg0, u8 arg1, s32 arg2) { + void *obj; + + obj = func_15167A68(0xE, arg2, 0xB8, 1, arg1, 1); + if (obj == NULL) { + return NULL; + } + bcopy((void *)arg0, (u8 *)obj + 0x10, 0xA8); + return obj; +} +#pragma GLOBAL_ASM("asm/nonmatchings/game_1944C0/func_15168870.s") +// permuter NO ZERO, best 65 (hand). Permuter improved to real pscore 75 via a dead +// `product=` pre-assignment but hard-plateaued there across ~30k iterations; the +// coupled a2/a3 + t0/t9 register swap cannot be forced from C. +// NON-MATCHING: best score 65. EVERY instruction, operand order, branch and +// delay slot match byte-for-byte; the only differences are a coupled temp- +// register coloring tie-break: the target keeps `delta`(unk9A) in a2 and the +// multiply result in t1, and puts the D_800BE9E4 base in t0 / the unk98 reload +// in t9; this build swaps them (delta->a3, product->a2, D_800BE9E4->t9, +// unk98->t0). Non-overlapping ranges, so no C form forces it (tested: operand +// swap, product hoist/placement, frame local, unk98 preload, decl reorder -> +// 260/220/135/75/255; the form below is the hand floor at 65). PERMUTER CANDIDATE. +// Uses a file-local view of the 0x32C object (header struct127 types at +// pad98/unkA8/unkB2 disagree). D_8008CA4C[] = anim sources (0x4 = frame count); +// D_8008C9C8[] = per-object callbacks. +// typedef struct { u8 pad0[0x98]; s16 unk98; s16 unk9A; u8 pad9C[0xC]; +// u16 unkA8; u8 padAA[6]; u8 unkB0; u8 padB1; s8 unkB2; } CtrObj; +// typedef struct { u8 pad0[4]; u8 unk4; } CtrSrc; +// extern CtrSrc *D_8008CA4C[]; extern void (*D_8008C9C8[])(CtrObj *arg0); +// void func_15168870(CtrObj *arg0) { +// s32 v0, v1; void (*func)(CtrObj *); s16 delta; +// delta = arg0->unk9A; +// if (delta != 0) { +// s32 product; +// v1 = (D_8008CA4C[arg0->unkB0]->unk4 << 8) - 1; +// product = delta * D_800BE9E4; +// arg0->unk98 = arg0->unk98 + product; +// v0 = arg0->unk98; +// if (v0 > v1) { +// if (arg0->unkA8 & 0x40) { arg0->unk98 = v1 - (v0 % v1); arg0->unk9A = -delta; } +// else if (arg0->unkA8 & 0x4) { arg0->unk98 = -1; } +// else { do { arg0->unk98 = v0 - v1; v0 = arg0->unk98; } while (v0 > v1); } +// } else if (v0 < 0) { +// if (arg0->unkA8 & 0x40) { arg0->unk98 = (-v0) % v1; arg0->unk9A = -delta; } +// else if (arg0->unkA8 & 0x4) { arg0->unk98 = -1; } +// else { do { arg0->unk98 = v0 + v1; v0 = arg0->unk98; } while (v0 < 0); } +// } // } -// return temp_v0; +// v0 = arg0->unkB2; +// if (v0 != -1) { func = D_8008C9C8[v0]; if (func != NULL) { func(arg0); } } +// if (arg0->unk98 == -1) { func_1516972C((struct102 *)arg0); } +// } +void func_15168A2C(s32 arg0) { + func_15168B10(arg0, 0); +} +void func_15168A4C(Obj *arg0, s32 arg1) { + s32 type; + Obj **head; + + type = arg0->unk1; + head = &D_800DCE50[type][arg1]; + arg0->unk8 = *head; + if (arg0->unk8 != NULL) { + arg0->unk8->unk4 = arg0; + } + arg0->unk0 = arg1; + arg0->unk4 = NULL; + *head = arg0; +} +#pragma GLOBAL_ASM("asm/nonmatchings/game_1944C0/func_15168A9C.s") +// NON-MATCHING: best score 395. Every instruction/op is byte-identical; only a +// register-allocation shuffle - the target keeps the two indices + the reused +// next/prev chain in v0/v1 (so `prev` lands in v0 and the return is free), +// while this build spreads them across t6/t8 and returns prev via `move v0,v1`. +// Permuter candidate. Reconstruction: +// Obj *func_15168A9C(Obj *arg0) { +// Obj **head; Obj *next, *prev; +// head = &D_800DCE50[arg0->unk1][arg0->unk0]; +// if (arg0 == *head) { *head = arg0->unk8; } +// next = arg0->unk8; +// if (next != NULL) { next->unk4 = arg0->unk4; } +// prev = arg0->unk4; +// if (prev != NULL) { prev->unk8 = arg0->unk8; } +// return prev; // } @@ -64,26 +373,157 @@ void func_15168B10(s32 arg0, s32 arg1) { } #pragma GLOBAL_ASM("asm/nonmatchings/game_1944C0/func_15168B44.s") -#pragma GLOBAL_ASM("asm/nonmatchings/game_1944C0/func_15168BAC.s") -#pragma GLOBAL_ASM("asm/nonmatchings/game_1944C0/func_15168BE4.s") +// NON-MATCHING: best score 155. Instruction sequence, operand order, branch +// shape and delay slots all match; only temp-register naming in the first +// branch differs (this build puts the `unk14 & 0xFFFF` CSE in a1, target t6), +// plus the li 0x1e / lui 0xffff constants are materialized in swapped order. +// Permuter candidate. Reconstruction: +// void func_15168B44(Obj *arg0) { +// s32 v; +// v = (arg0->unk14 & 0xFFFF) - 1; +// if ((arg0->unk14 & 0xFFFF) != 0) { +// arg0->unk14 &= 0xFFFF0000; arg0->unk38 = 0x1E; +// arg0->unk14 |= v & 0xFFFF; return; +// } +// v = arg0->unk14 >> 16; +// if ((u16)v < arg0->unk3F) { +// arg0->unk3F = arg0->unk3F - (u16)v; arg0->unk38 = 0x1E; return; +// } +// arg0->unk38 = 0; +// } + +void func_15168BAC(u8 *arg0) { + if (arg0[0xE4] != 0) { + D_8008CA20[arg0[0xE4]](); + } +} +void func_15168BE4(s32 *arg0, u8 arg1, s32 arg2) { + void *obj; + + if (arg0[0x10] != 0) { + obj = func_15167A68(0x10, arg2, 0xF0, 1, arg1, 1); + if (obj != NULL) { + bcopy((void *)arg0, (u8 *)obj + 0x90, 0x60); + } + } +} #pragma GLOBAL_ASM("asm/nonmatchings/game_1944C0/func_15168C4C.s") -#pragma GLOBAL_ASM("asm/nonmatchings/game_1944C0/func_15168E34.s") +void func_15168E34(s32 *arg0, s32 arg1) { + if ((*arg0 & 0xF000000) == 0) { + *arg0 += arg1; + } +} #pragma GLOBAL_ASM("asm/nonmatchings/game_1944C0/func_15168E54.s") +// NON-MATCHING: best score 625. Same issue as its twin func_15168F08 - identical +// instruction count/ops, only a CSE'd `move v1,v0` where the target reloads with +// a fresh `lb v0,0(s1)`, cascading into a v0/v1/a2 rename. Permuter candidate. +// void func_15168E54(GfxCmd *arg0, s32 arg1) { +// s32 i; GfxCmd *entry; s32 cmd; +// i = 0; entry = arg0; +// if (arg0->cmd != -0x21) { +// cmd = entry->cmd; +// do { +// if (cmd == 1 || (cmd == -0x24 && entry->unk3 == 0xE)) { +// func_15168E34(&entry->unk4, arg1); +// } +// i++; +// entry = arg0 + i; +// cmd = entry->cmd; +// } while (cmd != -0x21); +// } +// } #pragma GLOBAL_ASM("asm/nonmatchings/game_1944C0/func_15168F08.s") -#pragma GLOBAL_ASM("asm/nonmatchings/game_1944C0/func_15168F84.s") -#pragma GLOBAL_ASM("asm/nonmatchings/game_1944C0/func_15169040.s") +// NON-MATCHING: best score 335. Instruction COUNT and every op match; the only +// difference is IDO CSEs the loop-cmd init (`move a1,a2`) with the guard load, +// where the target keeps them as two separate `lb`s - which cascades into a +// v0/a1/a2 register rename. Cannot be defeated from C (IDO CSEs arg0->cmd and +// entry->cmd regardless). Permuter candidate (reload mutation). +// void func_15168F08(GfxCmd *arg0, s32 arg1) { +// s32 i; GfxCmd *entry; s32 cmd; +// i = 0; entry = arg0; +// if (arg0->cmd != -0x21) { +// cmd = entry->cmd; +// do { +// i++; +// if (cmd == 1 || (cmd == -0x24 && entry->unk3 == 0xE)) { +// entry->unk4 = entry->unk4 & 0xFFFFFF; +// entry->unk4 = entry->unk4 + arg1; +// } +// entry = arg0 + i; +// cmd = entry->cmd; +// } while (cmd != -0x21); +// } +// } +void func_15168F84(s32 arg0, s32 *arg1, s32 *arg2) { + if (arg0 == 0) { + *arg1 = 1; + *arg2 = 0x41; + } else if (arg0 == 1) { + *arg1 = 0x42; + *arg2 = 0x4F; + } else if (arg0 == 2) { + *arg1 = 0x50; + *arg2 = 0x58; + } else if (arg0 == 3) { + *arg1 = 0x59; + *arg2 = 0x5C; + } else if (arg0 == 5) { + *arg1 = 0x61; + *arg2 = 0x63; + } else if (arg0 == 6) { + *arg1 = 0x64; + *arg2 = 0x65; + } else { + *arg1 = 0x5D; + *arg2 = 0x60; + } +} +void func_15169040(s32 arg0, u8 arg1) { + func_15169070(0, 0x68, arg0, arg1); +} #pragma GLOBAL_ASM("asm/nonmatchings/game_1944C0/func_15169070.s") #pragma GLOBAL_ASM("asm/nonmatchings/game_1944C0/func_15169260.s") #pragma GLOBAL_ASM("asm/nonmatchings/game_1944C0/func_1516944C.s") -#pragma GLOBAL_ASM("asm/nonmatchings/game_1944C0/func_151695F0.s") -#pragma GLOBAL_ASM("asm/nonmatchings/game_1944C0/func_1516962C.s") -#pragma GLOBAL_ASM("asm/nonmatchings/game_1944C0/func_15169668.s") -#pragma GLOBAL_ASM("asm/nonmatchings/game_1944C0/func_1516968C.s") -#pragma GLOBAL_ASM("asm/nonmatchings/game_1944C0/func_151696DC.s") +void func_151695F0(Obj *arg0, u8 arg1) { + ObjRef sp18; + + sp18.unk0 = arg0; + sp18.unk4 = arg0->unk3B; + func_15169040((s32)&sp18, arg1); +} + +void func_1516962C(s32 arg0, Obj *arg1, u8 arg2) { + ObjRef sp18; + + sp18.unk0 = arg1; + sp18.unk4 = arg1->unk3B; + func_1516944C(arg0, (s32)&sp18, arg2); +} +s32 func_15169668(s32 arg0, u8 arg1, u8 arg2, u8 arg3) { + D_800D2DAB = 1; + return arg0; +} +void func_1516968C(Obj *arg0, Obj *arg1, u8 arg2) { + if ((arg2 == 0xF) || (arg2 == 0x10)) { + if (arg0->unkC == arg1->unk0) { + func_1516972C((struct102 *)arg0); + } + } +} + +void func_151696DC(Obj *arg0) { + s8 i; + + for (i = 0; i < D_800DD190; i++) { + if (((Obj **)D_800DD198)[i] == arg0) { + ((Obj **)D_800DD198)[i] = arg0->unk8; + } + } +} void func_1516972C(struct102 *arg0) { void (*func)(struct102 *arg0); - func_151696DC(); + func_151696DC((Obj *)arg0); if (arg0->unk0 >= 2) { func = D_8008B4D0[arg0->unk0].unk0; @@ -98,7 +538,7 @@ void func_1516972C(struct102 *arg0) { void func_1516979C(struct102 *arg0) { void (*func)(struct102 *arg0); - func_151696DC(); + func_151696DC((Obj *)arg0); func = D_8008B4D4[arg0->unk0].unk0; if (func != NULL) { func(arg0); @@ -117,3 +557,24 @@ void func_15169824(struct102 *arg0) { } #pragma GLOBAL_ASM("asm/nonmatchings/game_1944C0/func_15169850.s") +// NON-MATCHING: best score 30. Structure/ops/branches all byte-identical; only +// a v0<->v1 rename in the arg1==0x2D branch (target loads *arg0 into v1 and +// *arg2 into v0; this build does the reverse). Pure allocator artifact - IDO +// fixes the register regardless of operand order (verified swap = still 30). +// Permuter candidate. +// void func_15169850(s32 arg0, u8 arg1, s32 arg2, s32 arg3, s32 arg4) { +// struct12 *p = (struct12 *)arg0; +// s32 *q = (s32 *)arg2; +// u8 *r = (u8 *)arg3; +// if (arg1 == 0) { +// if ((p->unk0 == *q) || (((u8 *)arg0)[4] == *r)) { +// func_1516972C((struct102 *)arg4); +// } +// } else if (arg1 == 0x2D) { +// if (p->unk0 == *q) { +// *q = p->unk4; *r = p->unk9; +// } else if (p->unk4 == *q) { +// *q = p->unk0; *r = p->unk8; +// } +// } +// } diff --git a/conker/src/game_1BFC70.c b/conker/src/game_1BFC70.c index 80fc4037..ee61ce59 100644 --- a/conker/src/game_1BFC70.c +++ b/conker/src/game_1BFC70.c @@ -3,13 +3,13 @@ #include "variables.h" -#pragma GLOBAL_ASM("asm/nonmatchings/game_1BFC70/func_151927C0.s") -// JUSTREG: using t2 not t3 -// void func_151927C0(struct114 *arg0) { -// s32 tmp0; -// tmp0 = (s16)((arg0->unk14 & 0xFF) >> 1); -// arg0->unk38 = 300; -// arg0->unk3A = 10; -// arg0->unk14 = tmp0 * 65537; -// arg0->unk3B = 0; -// } +void func_151927C0(struct114 *arg0) { + s32 tmp0; + s32 tmp1; + tmp0 = (s16)((arg0->unk14 & 0xFF) >> 1); + tmp1 = (tmp0 << 16) + tmp0; + arg0->unk38 = 300; + arg0->unk3A = 10; + arg0->unk14 = tmp1; + arg0->unk3B = 0; +} diff --git a/conker/src/game_1FFF60.c b/conker/src/game_1FFF60.c index 743fb6df..d0568c7f 100644 --- a/conker/src/game_1FFF60.c +++ b/conker/src/game_1FFF60.c @@ -6,6 +6,47 @@ void func_151D3354(struct224 *arg0); void func_151D3308(struct224 *arg0); +// structs.h's struct224 is missing several fields used by this file (0x18, 0x1D, +// 0x2A) and types 0x34 as three loose words, so a file-local view is declared +// here rather than editing the shared header. +typedef struct { + s32 unk0; + s32 unk4; + s32 unk8; +} Vec1FFF60; + +typedef struct Struct1FFF60 Struct1FFF60; + +struct Struct1FFF60 { + /* 0x00 */ u8 pad0[0x10]; + /* 0x10 */ s32 unk10; + /* 0x14 */ u8 unk14; + /* 0x15 */ u8 pad15[0x3]; + /* 0x18 */ u8 unk18; + /* 0x19 */ u8 pad19[0x4]; + /* 0x1D */ u8 unk1D; + /* 0x1E */ u8 pad1E[0x2]; + /* 0x20 */ s16 unk20; + /* 0x22 */ u8 pad22[0x2]; + /* 0x24 */ Struct1FFF60 *unk24; + /* 0x28 */ union { + s32 w; + struct { + u8 unk0; + u8 unk1; + s8 unk2; + u8 unk3; + } b; + } unk28; + /* 0x2C */ s32 unk2C; + /* 0x30 */ Struct1FFF60 *unk30; + /* 0x34 */ Vec1FFF60 unk34; + /* 0x40 */ Struct1FFF60 *unk40; + /* 0x44 */ Struct1FFF60 *unk44; +}; + +Struct1FFF60 *func_15167A68(s32, s32, s32, s32, u8, s32); + void func_151D2AB0(s32 arg0) { u32 tmp; @@ -26,24 +67,20 @@ void func_151D2B4C(s32 arg0) { } } -#pragma GLOBAL_ASM("asm/nonmatchings/game_1FFF60/func_151D2BA4.s") -// s32 func_15167A68(s32, s32, s32, s32, s32, s32); -// NON-MATCHING: JUSTREG -// struct224 *func_151D2BA4(s32 arg0, struct00 *arg1, s32 arg2, u8 arg3, s32 arg4) { -// struct224 *tmp = func_15167A68(0x3D, arg4, arg2 + 0x48, 1, arg3, 1); -// -// if (tmp == 0) { -// return NULL; -// } -// -// memcpy(&tmp->unk10, arg0, 36); -// tmp->unk34 = arg1->unk0; -// tmp->unk38 = arg1->unk4; -// tmp->unk3C = arg1->unk8; -// -// func_151D3308(tmp); -// return tmp; -// } +Struct1FFF60 *func_151D2BA4(void *arg0, Vec1FFF60 *arg1, s32 arg2, u8 arg3, s32 arg4) { + Struct1FFF60 *tmp; + + tmp = func_15167A68(0x3D, arg4, arg2 + 0x48, 1, arg3, 1); + if (tmp == NULL) { + return NULL; + } + + memcpy(&tmp->unk10, arg0, 0x24); + tmp->unk34 = *arg1; + + func_151D3308(tmp); + return tmp; +} #pragma GLOBAL_ASM("asm/nonmatchings/game_1FFF60/func_151D2C40.s") @@ -73,37 +110,87 @@ void func_151D2E14(struct102 *arg0) { func_15169824(tmp); } -#pragma GLOBAL_ASM("asm/nonmatchings/game_1FFF60/func_151D2E5C.s") -// NON-MATCHING: JUSTREG (mostly) -// s32 func_151D2E5C(struct16 *arg0, struct223 *arg1, u8 arg2) { -// s32 temp_v0; -// u8 temp_a2; -// u8 temp_a3; -// -// if (arg2 == 0) { -// temp_v0 = arg0->unk10; -// temp_a2 = arg0->unk14; -// temp_a3 = arg1->unk4.ub; -// if ((temp_v0 == arg1->unk0) || (temp_a3 == temp_a2)) { -// func_1516972C(temp_a2); -// } -// } else if (arg2 == 0x2D) { -// temp_v0 = arg1->unk0; -// if (temp_v0 == arg0->unk10) { -// arg0->unk10 = arg1->unk4.w; -// arg0->unk14 = arg1->unk9; -// } else if (arg1->unk4.w == arg0->unk10) { -// arg0->unk10 = temp_v0; -// arg0->unk14 = arg1->unk8; -// } -// } -// } - -#pragma GLOBAL_ASM("asm/nonmatchings/game_1FFF60/func_151D2F00.s") +// NON-MATCHING: best 3 of 41 instructions differ (instruction order/shape exact). +// In the arg2 == 0x2D arm the target holds arg1->unk0 in v0 and arg0->unk10 in +// v1; every variant tried puts them in the opposite pair (or spills one to a2). +// The arg2 == 0 arm matches exactly with the four locals below. +void func_151D2E5C(Struct1FFF60 *arg0, struct223 *arg1, u8 arg2) { + Struct1FFF60 *new_var; + s32 tmp0; + s32 tmp1; + s32 tmp2; + s32 tmp3; + + new_var = arg0; + if (arg2 == 0) { + tmp0 = new_var->unk10; + tmp1 = arg1->unk0; + tmp2 = new_var->unk14; + tmp3 = arg1->unk4.ub; + if ((tmp0 == tmp1) || (tmp3 == tmp2)) { + func_1516972C(new_var); + } + } else if (arg2 == 0x2D) { + if (new_var->unk10 == arg1->unk0) { + arg0->unk10 = arg1->unk4.w; + arg0->unk14 = arg1->unk9; + } else if (new_var->unk10 == arg1->unk4.w) { + new_var->unk10 = arg1->unk0; + new_var->unk14 = arg1->unk8; + } + } +} + +Struct1FFF60 *func_151D2F00(void *arg0, s32 arg1, u8 arg2, s32 arg3) { + Struct1FFF60 *tmp; + + tmp = func_15167A68(0x3E, arg3, arg1 + 0x30, 1, arg2, 1); + if (tmp == NULL) { + return NULL; + } + + memcpy(&tmp->unk10, arg0, 0x10); + tmp->unk20 = 0; + tmp->unk24 = NULL; + tmp->unk28.w = 0; + tmp->unk18 &= ~2; + return tmp; +} #pragma GLOBAL_ASM("asm/nonmatchings/game_1FFF60/func_151D2F90.s") -#pragma GLOBAL_ASM("asm/nonmatchings/game_1FFF60/func_151D3130.s") +// NON-MATCHING: best 3 of 49 instructions differ; all registers and all other +// instructions are exact. The target rotates the loop (move s0,s1 / bnezl s1 / +// duplicated "lb v0,0x2a(s0)" in the likely-delay slot); every loop form tried +// (while, do/while on cur, do/while on next, for) sinks "move s0,s1" into a +// plain "bnez s1" delay slot instead. +extern void (*D_8008FC48[])(Struct1FFF60 *, Vec1FFF60 *); +extern void (*D_8008FC5C[])(Struct1FFF60 *); +void func_1514EDF0(Struct1FFF60 *, s32); + +void func_151D3130(Struct1FFF60 *arg0) { + void (*func)(Struct1FFF60 *); + Struct1FFF60 *cur; + + func = D_8008FC5C[arg0->unk1D]; + if (func != NULL) { + func(arg0); + } + + cur = arg0->unk24; + while (cur != NULL) { + Struct1FFF60 *next; + next = cur->unk40; + if (cur->unk28.b.unk2 != -1) { + D_8008FC48[cur->unk28.b.unk2](cur, &cur->unk34); + } + cur->unk2C = 0; + func_1516972C(cur); + cur = next; + } + + func_1514EDF0(arg0, arg0->unk10); +} void func_151D31F4(struct102 *arg0) { func_151D3130(arg0); @@ -115,7 +202,35 @@ void func_151D3220(struct102 *arg0) { func_15169824(arg0); } -#pragma GLOBAL_ASM("asm/nonmatchings/game_1FFF60/func_151D324C.s") +// NON-MATCHING: best 2 of 47 instructions differ; every register is exact. +// After the func_151D33FC call the target keeps "lbu a2,0x23(sp)" as a real +// instruction and fills the following "b" delay slot by duplicating the join's +// "lbu t1,0x1d(a0)"; this version sinks the a2 reload into that delay slot +// instead (one instruction shorter). Restructuring as a switch made it worse. +extern void (*D_8008FC64[])(Struct1FFF60 *, struct223 *, u8); +void func_151D33FC(struct224 *, struct223 *); + +void func_151D324C(Struct1FFF60 *arg0, struct223 *arg1, u8 arg2) { + void (*func)(Struct1FFF60 *, struct223 *, u8); + + if (arg2 == 0) { + func_151D33FC(arg0, arg1); + dummy_label:; + } else if (arg2 == 0x2D) { + if (arg0->unk10 == arg1->unk0) { + arg0->unk10 = arg1->unk4.w; + arg0->unk14 = arg1->unk9; + } else if (arg0->unk10 == arg1->unk4.w) { + arg0->unk10 = arg1->unk0; + arg0->unk14 = arg1->unk8; + } + } + + func = D_8008FC64[arg0->unk1D]; + if (func != NULL) { + func(arg0, arg1, arg2); + } +} void func_151D3308(struct224 *arg0) { struct224 *temp_v0; @@ -181,9 +296,12 @@ void func_151D33FC(struct224 *arg0, struct223 *arg1) { } } +// NON-MATCHING (best 290): body is byte-correct but the target fuses the struct-copy +// destination and the call's first arg into a0; functions.h declares func_15169260's +// first param as s32, forcing a pointer->s32 conversion (move a0,v0). BLOCKED on +// func_15169260's real signature (pointer first param). Revisit after game_1944C0. +// void func_151D343C(s32 arg0, u8 arg1) { +// s32 sp1C = D_800AB168[0]; +// func_15169260(&sp1C, 1, arg0, arg1); // needs pointer-typed param1 +// } #pragma GLOBAL_ASM("asm/nonmatchings/game_1FFF60/func_151D343C.s") -// NON-MATCHING: ops in wrong order -// void func_151D343C(s32 arg0, u8 arg1) { -// s32 sp1C[1] = D_800AB168; -// func_15169260(sp1C, 1, arg0, arg1); -// }