|
|
|
@@ -0,0 +1,59 @@
|
|
|
|
|
/*
|
|
|
|
|
* func_8007F9B0 — 108 bytes at 0x8007F9B0..0x8007FA1C
|
|
|
|
|
*
|
|
|
|
|
* Gate on a three-level dereference, then set two fields and call a helper. No frame beyond the
|
|
|
|
|
* return address. **Second spelling, default toolchain.**
|
|
|
|
|
*
|
|
|
|
|
* addiu sp,sp,-0x18 / sw ra,16(sp) frame 24, ra at 0x10, no other save.
|
|
|
|
|
* lw v0,12(a0) / nop v0 = *(int *)(a0 + 12)
|
|
|
|
|
* lw v0,344(v0) / nop v0 = *(int *)(v0 + 344)
|
|
|
|
|
* lw v1,56(v0) v1 = *(int *)(v0 + 56) <-- THE THIRD LEVEL
|
|
|
|
|
* li v0,6 / bne v1,v0,END / nop if (that != 6) return;
|
|
|
|
|
* lw v0,28(a0) / nop the a0->f28 chain is RE-READ for each statement
|
|
|
|
|
* lw v1,8(v0) / li v0,62
|
|
|
|
|
* sw v0,48(v1) *(int *)(*(int *)(a0+28)+8)+48) = 62; WORD store
|
|
|
|
|
* lw v0,28(a0) / nop / lw v1,8(v0) ... re-read for the SECOND statement
|
|
|
|
|
* li v0,20 / sb v0,52(v1) *(... +52) = 20; BYTE store
|
|
|
|
|
* lh a0,2(a0) the call's first argument is a SIGNED HALFWORD
|
|
|
|
|
* jal 0x80043DC4 / li a1,62 func_80043DC4(*(short *)(a0 + 2), 62);
|
|
|
|
|
*
|
|
|
|
|
* FOUR THINGS ARE BYTE-REQUIRED:
|
|
|
|
|
*
|
|
|
|
|
* 1. **THE GATE IS THREE DEREFERENCES, NOT TWO.** `a0->f12 -> f344 -> f56`, and the innermost
|
|
|
|
|
* load is easy to drop when transcribing from a disassembly: writing
|
|
|
|
|
* `*(int *)(*(int *)(a0 + 12) + 344) != 6` compiles to a body **two instructions SHORT**
|
|
|
|
|
* (100 vs 108) because the missing load and its load-delay `nop` are exactly the deficit.
|
|
|
|
|
* **Diagnostic (cookbook 49's family): a body exactly two instructions short where the
|
|
|
|
|
* original has one more `lw` in a pointer chain means a MISSING DEREFERENCE LEVEL, not a
|
|
|
|
|
* missing statement -- recompute the chain from the displacements rather than eyeballing it.**
|
|
|
|
|
* 2. **THE a0->f28 CHAIN IS RE-READ PER STATEMENT** (cookbook 105's per-statement setting): the
|
|
|
|
|
* original loads `28(a0)` and `8(v0)` TWICE, once for each of the two stores. Binding either
|
|
|
|
|
* to a local would collapse one of the pairs.
|
|
|
|
|
* 3. **THE STORE WIDTHS ARE THE LAYOUT** (cookbook 93): `sw` for the field at 48 and `sb` for the
|
|
|
|
|
* field at 52 -- there is no other evidence for these two fields' types.
|
|
|
|
|
* 4. **THE CALL'S FIRST ARGUMENT IS A SIGNED HALFWORD**: `lh a0,2(a0)`, not `lw`. The `lh` is the
|
|
|
|
|
* only reason to call it `short`, and it is the same parameter register being reused.
|
|
|
|
|
*
|
|
|
|
|
* The gate's chained loads keep the SAME register (`lw v0,12(a0)` then `lw v0,344(v0)`), which is
|
|
|
|
|
* why the body carries a load-delay `nop` after each of the first two loads: the next instruction
|
|
|
|
|
* loads FROM the register just loaded, which is exactly maspsx's nop predicate (cookbook 27).
|
|
|
|
|
*
|
|
|
|
|
* LIMITS: func_80043DC4 is a cross-reference by address only and its purpose is not established.
|
|
|
|
|
* The object at a0 and the two objects reached through the chains are typed by their access widths
|
|
|
|
|
* and displacements only -- offsets 12, 344, 56, 28, 8, 48, 52 and 2, and the `short` at +2 is the
|
|
|
|
|
* one type claim, earned from the `lh`. The field names and the meaning of the `!= 6` gate are not
|
|
|
|
|
* recovered. The function returns nothing (`void`): the epilogue sets no value in v0.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
extern void func_80043DC4(int a0, int a1);
|
|
|
|
|
|
|
|
|
|
void func_8007F9B0(int a0)
|
|
|
|
|
{
|
|
|
|
|
if (*(int *)(*(int *)(*(int *)(a0 + 12) + 344) + 56) != 6)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
*(int *)(*(int *)(*(int *)(a0 + 28) + 8) + 48) = 62;
|
|
|
|
|
*(char *)(*(int *)(*(int *)(a0 + 28) + 8) + 52) = 20;
|
|
|
|
|
|
|
|
|
|
func_80043DC4(*(short *)(a0 + 2), 62);
|
|
|
|
|
}
|