PlayState rename (#835)

* PlayState rename

* Make this work with the latest master and run format

* Respond to hensldm's review

* Respond to Elliptic's review

* Add Effect_GetPlayState to namefixer.py

* Add missed comma
This commit is contained in:
Tom Overton
2022-06-26 08:57:37 -07:00
committed by GitHub
parent f77c4770f7
commit 158d1b26b0
1175 changed files with 36240 additions and 36688 deletions
+139 -139
View File
@@ -16,15 +16,15 @@ Again our example will be taken from a small NPC: this time, `EnMs` (Bean Seller
#define THIS ((EnMs*)thisx)
void EnMs_Init(Actor* thisx, GlobalContext* globalCtx);
void EnMs_Destroy(Actor* thisx, GlobalContext* globalCtx);
void EnMs_Update(Actor* thisx, GlobalContext* globalCtx);
void EnMs_Draw(Actor* thisx, GlobalContext* globalCtx);
void EnMs_Init(Actor* thisx, PlayState* play);
void EnMs_Destroy(Actor* thisx, PlayState* play);
void EnMs_Update(Actor* thisx, PlayState* play);
void EnMs_Draw(Actor* thisx, PlayState* play);
void func_80952734(EnMs* this, GlobalContext* globalCtx);
void func_809527F8(EnMs* this, GlobalContext* globalCtx);
void func_809529AC(EnMs* this, GlobalContext* globalCtx);
void func_80952A1C(EnMs *this, GlobalContext *globalCtx);
void func_80952734(EnMs* this, PlayState* play);
void func_809527F8(EnMs* this, PlayState* play);
void func_809529AC(EnMs* this, PlayState* play);
void func_80952A1C(EnMs* this, PlayState* play);
const ActorInit En_Ms_InitVars = {
ACTOR_EN_MS,
@@ -56,13 +56,13 @@ extern InitChainEntry D_80952BCC[];
extern AnimationHeader D_060005EC;
extern FlexSkeletonHeader D_06003DC0;
void EnMs_Init(Actor* thisx, GlobalContext* globalCtx) {
void EnMs_Init(Actor* thisx, PlayState* play) {
EnMs* this = THIS;
Actor_ProcessInitChain(thisx, D_80952BCC);
SkelAnime_InitFlex(globalCtx, &this->skelAnime, &D_06003DC0, &D_060005EC, this->jointTable, this->morphTable, 9);
Collider_InitCylinder(globalCtx, &this->collider);
Collider_SetCylinderType1(globalCtx, &this->collider, &this->actor, &D_80952BA0);
SkelAnime_InitFlex(play, &this->skelAnime, &D_06003DC0, &D_060005EC, this->jointTable, this->morphTable, 9);
Collider_InitCylinder(play, &this->collider);
Collider_SetCylinderType1(play, &this->collider, &this->actor, &D_80952BA0);
ActorShape_Init(&this->actor.shape, 0.0f, ActorShadow_DrawCircle, 35.0f);
Actor_SetScale(&this->actor, 0.015f);
this->actor.colChkInfo.mass = 0xFF;
@@ -72,13 +72,13 @@ void EnMs_Init(Actor* thisx, GlobalContext* globalCtx) {
this->actor.gravity = -1.0f;
}
void EnMs_Destroy(Actor* thisx, GlobalContext* globalCtx) {
void EnMs_Destroy(Actor* thisx, PlayState* play) {
EnMs* this = THIS;
Collider_DestroyCylinder(globalCtx, &this->collider);
Collider_DestroyCylinder(play, &this->collider);
}
void func_80952734(EnMs* this, GlobalContext* globalCtx) {
void func_80952734(EnMs* this, PlayState* play) {
s16 temp_v1 = this->actor.yawTowardsPlayer - this->actor.shape.rot.y;
if (gSaveContext.save.inventory.items[10] == ITEM_NONE) {
@@ -87,40 +87,40 @@ void func_80952734(EnMs* this, GlobalContext* globalCtx) {
this->actor.textId = 0x932;
}
if (Actor_ProcessTalkRequest(&this->actor, &globalCtx->state) != 0) {
if (Actor_ProcessTalkRequest(&this->actor, &play->state) != 0) {
this->actionFunc = func_809527F8;
return;
}
if (this->actor.xzDistToPlayer < 90.0f) {
if (ABS_ALT(temp_v1) < 0x2000) {
func_800B8614(&this->actor, globalCtx, 90.0f);
func_800B8614(&this->actor, play, 90.0f);
}
}
}
#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Ms/func_809527F8.s")
void func_809529AC(EnMs *this, GlobalContext *globalCtx) {
if (Actor_HasParent(&this->actor, globalCtx)) {
void func_809529AC(EnMs* this, PlayState* play) {
if (Actor_HasParent(&this->actor, play)) {
this->actor.textId = 0;
func_800B8500(&this->actor, globalCtx, this->actor.xzDistToPlayer, this->actor.playerHeightRel, 0);
func_800B8500(&this->actor, play, this->actor.xzDistToPlayer, this->actor.playerHeightRel, 0);
this->actionFunc = func_80952A1C;
} else {
Actor_PickUp(&this->actor, globalCtx, 0x35, this->actor.xzDistToPlayer, this->actor.playerHeightRel);
Actor_PickUp(&this->actor, play, 0x35, this->actor.xzDistToPlayer, this->actor.playerHeightRel);
}
}
void func_80952A1C(EnMs *this, GlobalContext *globalCtx) {
if (Actor_ProcessTalkRequest(&this->actor, &globalCtx->state)) {
func_80151938(globalCtx, 0x936U);
void func_80952A1C(EnMs* this, PlayState* play) {
if (Actor_ProcessTalkRequest(&this->actor, &play->state)) {
func_80151938(play, 0x936U);
this->actionFunc = func_809527F8;
} else {
func_800B8500(&this->actor, globalCtx, this->actor.xzDistToPlayer, this->actor.playerHeightRel, -1);
func_800B8500(&this->actor, play, this->actor.xzDistToPlayer, this->actor.playerHeightRel, -1);
}
}
void EnMs_Update(Actor* thisx, GlobalContext* globalCtx) {
void EnMs_Update(Actor* thisx, PlayState* play) {
s32 pad;
EnMs* this = THIS;
@@ -128,16 +128,16 @@ void EnMs_Update(Actor* thisx, GlobalContext* globalCtx) {
this->actor.targetArrowOffset = 500.0f;
Actor_SetScale(&this->actor, 0.015f);
SkelAnime_Update(&this->skelAnime);
this->actionFunc(this, globalCtx);
this->actionFunc(this, play);
Collider_UpdateCylinder(&this->actor, &this->collider);
CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base);
CollisionCheck_SetOC(play, &play->colChkCtx, &this->collider.base);
}
void EnMs_Draw(Actor* thisx, GlobalContext* globalCtx) {
void EnMs_Draw(Actor* thisx, PlayState* play) {
EnMs* this = THIS;
func_8012C28C(globalCtx->state.gfxCtx);
SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, NULL,
func_8012C28C(play->state.gfxCtx);
SkelAnime_DrawFlexOpa(play, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, NULL,
NULL, &this->actor);
}
@@ -152,53 +152,53 @@ $ ../mips_to_c/mips_to_c.py asm/non_matchings/overlays/ovl_En_Ms/func_809527F8.s
```
```C
void func_809527F8(EnMs *this, GlobalContext *globalCtx) {
void func_809527F8(EnMs* this, PlayState* play) {
u8 temp_v0;
u8 temp_v0_2;
temp_v0 = Message_GetState(&globalCtx->msgCtx);
temp_v0 = Message_GetState(&play->msgCtx);
if (temp_v0 != 4) {
if (temp_v0 != 5) {
if ((temp_v0 == 6) && (Message_ShouldAdvance(globalCtx) != 0)) {
if ((temp_v0 == 6) && (Message_ShouldAdvance(play) != 0)) {
this->actionFunc = func_80952734;
return;
}
// Duplicate return node #17. Try simplifying control flow for better match
return;
}
if (Message_ShouldAdvance(globalCtx) != 0) {
func_801477B4(globalCtx);
Actor_PickUp((Actor *) this, globalCtx, 0x35, this->actor.xzDistToPlayer, this->actor.playerHeightRel);
if (Message_ShouldAdvance(play) != 0) {
func_801477B4(play);
Actor_PickUp((Actor *) this, play, 0x35, this->actor.xzDistToPlayer, this->actor.playerHeightRel);
this->actionFunc = func_809529AC;
return;
}
// Duplicate return node #17. Try simplifying control flow for better match
return;
}
if (Message_ShouldAdvance(globalCtx) != 0) {
temp_v0_2 = globalCtx->msgCtx.choiceIndex;
if (Message_ShouldAdvance(play) != 0) {
temp_v0_2 = play->msgCtx.choiceIndex;
if (temp_v0_2 != 0) {
if (temp_v0_2 != 1) {
}
func_8019F230();
func_80151938(globalCtx, 0x934U);
func_80151938(play, 0x934U);
// Duplicate return node #17. Try simplifying control flow for better match
return;
}
func_801477B4(globalCtx);
func_801477B4(play);
if ((s32) gSaveContext.save.playerData.rupees < 0xA) {
play_sound(0x4806U);
func_80151938(globalCtx, 0x935U);
func_80151938(play, 0x935U);
return;
}
if ((s32) gSaveContext.save.inventory.ammo[gItemSlots[0xA]] >= 0x14) {
play_sound(0x4806U);
func_80151938(globalCtx, 0x937U);
func_80151938(play, 0x937U);
return;
}
func_8019F208();
Actor_PickUp((Actor *) this, globalCtx, 0x35, 90.0f, 10.0f);
Actor_PickUp((Actor *) this, play, 0x35, 90.0f, 10.0f);
func_801159EC(-0xA);
this->actionFunc = func_809529AC;
}
@@ -208,10 +208,10 @@ void func_809527F8(EnMs *this, GlobalContext *globalCtx) {
which is long, messy, and contains some rather nasty-looking control flow, including horrors like
```C
temp_v0 = Message_GetState(&globalCtx->msgCtx);
temp_v0 = Message_GetState(&play->msgCtx);
if (temp_v0 != 4) {
if (temp_v0 != 5) {
if ((temp_v0 == 6) && (Message_ShouldAdvance(globalCtx) != 0)) {
if ((temp_v0 == 6) && (Message_ShouldAdvance(play) != 0)) {
this->actionFunc = func_80952734;
return;
}
@@ -234,11 +234,11 @@ For didactic purposes, we'll use a feature of mips2c called goto-only mode to ex
instead will produce
```C
void func_809527F8(EnMs *this, GlobalContext *globalCtx) {
void func_809527F8(EnMs* this, PlayState* play) {
u8 temp_v0;
u8 temp_v0_2;
temp_v0 = Message_GetState(&globalCtx->msgCtx);
temp_v0 = Message_GetState(&play->msgCtx);
if (temp_v0 == 4) {
goto block_7;
}
@@ -248,24 +248,24 @@ void func_809527F8(EnMs *this, GlobalContext *globalCtx) {
if (temp_v0 != 6) {
goto block_17;
}
if (Message_ShouldAdvance(globalCtx) == 0) {
if (Message_ShouldAdvance(play) == 0) {
goto block_17;
}
this->actionFunc = func_80952734;
return;
block_5:
if (Message_ShouldAdvance(globalCtx) == 0) {
if (Message_ShouldAdvance(play) == 0) {
goto block_17;
}
func_801477B4(globalCtx);
Actor_PickUp((Actor *) this, globalCtx, 0x35, this->actor.xzDistToPlayer, this->actor.playerHeightRel);
func_801477B4(play);
Actor_PickUp((Actor *) this, play, 0x35, this->actor.xzDistToPlayer, this->actor.playerHeightRel);
this->actionFunc = func_809529AC;
return;
block_7:
if (Message_ShouldAdvance(globalCtx) == 0) {
if (Message_ShouldAdvance(play) == 0) {
goto block_17;
}
temp_v0_2 = globalCtx->msgCtx.choiceIndex;
temp_v0_2 = play->msgCtx.choiceIndex;
if (temp_v0_2 == 0) {
goto block_11;
}
@@ -274,29 +274,29 @@ block_7:
}
goto block_16;
block_11:
func_801477B4(globalCtx);
func_801477B4(play);
if ((s32) gSaveContext.save.playerData.rupees >= 0xA) {
goto block_13;
}
play_sound(0x4806U);
func_80151938(globalCtx, 0x935U);
func_80151938(play, 0x935U);
return;
block_13:
if ((s32) gSaveContext.save.inventory.ammo[gItemSlots[0xA]] < 0x14) {
goto block_15;
}
play_sound(0x4806U);
func_80151938(globalCtx, 0x937U);
func_80151938(play, 0x937U);
return;
block_15:
func_8019F208();
Actor_PickUp((Actor *) this, globalCtx, 0x35, 90.0f, 10.0f);
Actor_PickUp((Actor *) this, play, 0x35, 90.0f, 10.0f);
func_801159EC(-0xA);
this->actionFunc = func_809529AC;
return;
block_16:
func_8019F230();
func_80151938(globalCtx, 0x934U);
func_80151938(play, 0x934U);
block_17:
return;
}
@@ -316,7 +316,7 @@ The simplest sort of block label to eliminate is one that is only used once, and
goto block_13;
}
play_sound(0x4806U);
func_80151938(globalCtx, 0x935U);
func_80151938(play, 0x935U);
return;
block_13:
```
@@ -326,7 +326,7 @@ Currently, this says to jump over the code block `play_sound...` if the conditio
```C
if (gSaveContext.save.playerData.rupees < 0xA) {
play_sound(0x4806U);
func_80151938(globalCtx, 0x935U);
func_80151938(play, 0x935U);
return;
}
```
@@ -336,11 +336,11 @@ Likewise, one can do this with `block_15`.
If you examine appropriate part of the diff, you will usually find that such eliminations make no, or very little, difference to the compiled code.
```C
void func_809527F8(EnMs *this, GlobalContext *globalCtx) {
void func_809527F8(EnMs* this, PlayState* play) {
u8 temp_v0;
u8 temp_v0_2;
temp_v0 = Message_GetState(&globalCtx->msgCtx);
temp_v0 = Message_GetState(&play->msgCtx);
if (temp_v0 == 4) {
goto block_7;
}
@@ -350,24 +350,24 @@ void func_809527F8(EnMs *this, GlobalContext *globalCtx) {
if (temp_v0 != 6) {
goto block_17;
}
if (Message_ShouldAdvance(globalCtx) == 0) {
if (Message_ShouldAdvance(play) == 0) {
goto block_17;
}
this->actionFunc = func_80952734;
return;
block_5:
if (Message_ShouldAdvance(globalCtx) == 0) {
if (Message_ShouldAdvance(play) == 0) {
goto block_17;
}
func_801477B4(globalCtx);
Actor_PickUp((Actor *) this, globalCtx, 0x35, this->actor.xzDistToPlayer, this->actor.playerHeightRel);
func_801477B4(play);
Actor_PickUp((Actor *) this, play, 0x35, this->actor.xzDistToPlayer, this->actor.playerHeightRel);
this->actionFunc = func_809529AC;
return;
block_7:
if (Message_ShouldAdvance(globalCtx) == 0) {
if (Message_ShouldAdvance(play) == 0) {
goto block_17;
}
temp_v0_2 = globalCtx->msgCtx.choiceIndex;
temp_v0_2 = play->msgCtx.choiceIndex;
if (temp_v0_2 == 0) {
goto block_11;
}
@@ -376,27 +376,27 @@ block_7:
}
goto block_16;
block_11:
func_801477B4(globalCtx);
func_801477B4(play);
if (gSaveContext.save.playerData.rupees < 0xA) {
play_sound(0x4806U);
func_80151938(globalCtx, 0x935U);
func_80151938(play, 0x935U);
return;
}
if (gSaveContext.save.inventory.ammo[gItemSlots[0xA]] >= 0x14) {
play_sound(0x4806U);
func_80151938(globalCtx, 0x937U);
func_80151938(play, 0x937U);
return;
}
func_8019F208();
Actor_PickUp((Actor *) this, globalCtx, 0x35, 90.0f, 10.0f);
Actor_PickUp((Actor *) this, play, 0x35, 90.0f, 10.0f);
func_801159EC(-0xA);
this->actionFunc = func_809529AC;
return;
block_16:
func_8019F230();
func_80151938(globalCtx, 0x934U);
func_80151938(play, 0x934U);
block_17:
return;
}
@@ -406,11 +406,11 @@ We can't apply this rule any more, so we need to move on to the next: `block_17`
```C
void func_809527F8(EnMs *this, GlobalContext *globalCtx) {
void func_809527F8(EnMs* this, PlayState* play) {
u8 temp_v0;
u8 temp_v0_2;
temp_v0 = Message_GetState(&globalCtx->msgCtx);
temp_v0 = Message_GetState(&play->msgCtx);
if (temp_v0 == 4) {
goto block_7;
}
@@ -420,24 +420,24 @@ void func_809527F8(EnMs *this, GlobalContext *globalCtx) {
if (temp_v0 != 6) {
return;
}
if (Message_ShouldAdvance(globalCtx) == 0) {
if (Message_ShouldAdvance(play) == 0) {
return;
}
this->actionFunc = func_80952734;
return;
block_5:
if (Message_ShouldAdvance(globalCtx) == 0) {
if (Message_ShouldAdvance(play) == 0) {
return;
}
func_801477B4(globalCtx);
Actor_PickUp((Actor *) this, globalCtx, 0x35, this->actor.xzDistToPlayer, this->actor.playerHeightRel);
func_801477B4(play);
Actor_PickUp((Actor *) this, play, 0x35, this->actor.xzDistToPlayer, this->actor.playerHeightRel);
this->actionFunc = func_809529AC;
return;
block_7:
if (Message_ShouldAdvance(globalCtx) == 0) {
if (Message_ShouldAdvance(play) == 0) {
return;
}
temp_v0_2 = globalCtx->msgCtx.choiceIndex;
temp_v0_2 = play->msgCtx.choiceIndex;
if (temp_v0_2 == 0) {
goto block_11;
}
@@ -446,27 +446,27 @@ block_7:
}
goto block_16;
block_11:
func_801477B4(globalCtx);
func_801477B4(play);
if (gSaveContext.save.playerData.rupees < 0xA) {
play_sound(0x4806U);
func_80151938(globalCtx, 0x935U);
func_80151938(play, 0x935U);
return;
}
if (gSaveContext.save.inventory.ammo[gItemSlots[0xA]] >= 0x14) {
play_sound(0x4806U);
func_80151938(globalCtx, 0x937U);
func_80151938(play, 0x937U);
return;
}
func_8019F208();
Actor_PickUp((Actor *) this, globalCtx, 0x35, 90.0f, 10.0f);
Actor_PickUp((Actor *) this, play, 0x35, 90.0f, 10.0f);
func_801159EC(-0xA);
this->actionFunc = func_809529AC;
return;
block_16:
func_8019F230();
func_80151938(globalCtx, 0x934U);
func_80151938(play, 0x934U);
}
```
@@ -475,7 +475,7 @@ Our next rule is about non-crossing blocks. If two code blocks do not contain an
Now let's start thinking about switches. A good indicator of a switch in goto-only mode is something like
```C
temp_v0_2 = globalCtx->msgCtx.choiceIndex;
temp_v0_2 = play->msgCtx.choiceIndex;
if (temp_v0_2 == 0) {
goto block_11;
}
@@ -493,23 +493,23 @@ because
So let us rewrite the entire second half as a switch:
```C
switch (globalCtx->msgCtx.choiceIndex) {
switch (play->msgCtx.choiceIndex) {
case 0:
func_801477B4(globalCtx);
func_801477B4(play);
if (gSaveContext.save.playerData.rupees < 0xA) {
play_sound(0x4806U);
func_80151938(globalCtx, 0x935U);
func_80151938(play, 0x935U);
return;
}
if (gSaveContext.save.inventory.ammo[gItemSlots[0xA]] >= 0x14) {
play_sound(0x4806U);
func_80151938(globalCtx, 0x937U);
func_80151938(play, 0x937U);
return;
}
func_8019F208();
Actor_PickUp((Actor *) this, globalCtx, 0x35, 90.0f, 10.0f);
Actor_PickUp((Actor *) this, play, 0x35, 90.0f, 10.0f);
func_801159EC(-0xA);
this->actionFunc = func_809529AC;
return;
@@ -518,7 +518,7 @@ So let us rewrite the entire second half as a switch:
case 1:
default:
func_8019F230();
func_80151938(globalCtx, 0x934U);
func_80151938(play, 0x934U);
break;
}
```
@@ -528,19 +528,19 @@ There's a couple of other obvious things here:
- a common pattern everywhere, a sequence of ifs with returns as the last thing inside is the same as an if-else chain, so we can rewrite these as
```C
switch (globalCtx->msgCtx.choiceIndex) {
switch (play->msgCtx.choiceIndex) {
case 0:
func_801477B4(globalCtx);
func_801477B4(play);
if (gSaveContext.save.playerData.rupees < 0xA) {
play_sound(0x4806U);
func_80151938(globalCtx, 0x935U);
func_80151938(play, 0x935U);
} else if (gSaveContext.save.inventory.ammo[gItemSlots[0xA]] >= 0x14) {
play_sound(0x4806U);
func_80151938(globalCtx, 0x937U);
func_80151938(play, 0x937U);
} else {
func_8019F208();
Actor_PickUp((Actor *) this, globalCtx, 0x35, 90.0f, 10.0f);
Actor_PickUp((Actor *) this, play, 0x35, 90.0f, 10.0f);
func_801159EC(-0xA);
this->actionFunc = func_809529AC;
}
@@ -549,7 +549,7 @@ There's a couple of other obvious things here:
case 1:
default:
func_8019F230();
func_80151938(globalCtx, 0x934U);
func_80151938(play, 0x934U);
break;
}
```
@@ -557,7 +557,7 @@ There's a couple of other obvious things here:
Well, at least the bottom half looks respectable now. Again, there is no code after the switch, so the next thing up, namely
```C
if (Message_ShouldAdvance(globalCtx) == 0) {
if (Message_ShouldAdvance(play) == 0) {
return;
}
```
@@ -565,10 +565,10 @@ Well, at least the bottom half looks respectable now. Again, there is no code af
can be swapped round and made to wrap the switch. This leaves us with
```C
void func_809527F8(EnMs *this, GlobalContext *globalCtx) {
void func_809527F8(EnMs* this, PlayState* play) {
u8 temp_v0;
temp_v0 = Message_GetState(&globalCtx->msgCtx);
temp_v0 = Message_GetState(&play->msgCtx);
if (temp_v0 == 4) {
goto block_7;
}
@@ -578,34 +578,34 @@ void func_809527F8(EnMs *this, GlobalContext *globalCtx) {
if (temp_v0 != 6) {
return;
}
if (Message_ShouldAdvance(globalCtx) == 0) {
if (Message_ShouldAdvance(play) == 0) {
return;
}
this->actionFunc = func_80952734;
return;
block_5:
if (Message_ShouldAdvance(globalCtx) == 0) {
if (Message_ShouldAdvance(play) == 0) {
return;
}
func_801477B4(globalCtx);
Actor_PickUp((Actor *) this, globalCtx, 0x35, this->actor.xzDistToPlayer, this->actor.playerHeightRel);
func_801477B4(play);
Actor_PickUp((Actor *) this, play, 0x35, this->actor.xzDistToPlayer, this->actor.playerHeightRel);
this->actionFunc = func_809529AC;
return;
block_7:
if (Message_ShouldAdvance(globalCtx) != 0) {
switch (globalCtx->msgCtx.choiceIndex) {
if (Message_ShouldAdvance(play) != 0) {
switch (play->msgCtx.choiceIndex) {
case 0:
func_801477B4(globalCtx);
func_801477B4(play);
if (gSaveContext.save.playerData.rupees < 0xA) {
play_sound(0x4806U);
func_80151938(globalCtx, 0x935U);
func_80151938(play, 0x935U);
} else if (gSaveContext.save.inventory.ammo[gItemSlots[0xA]] >= 0x14) {
play_sound(0x4806U);
func_80151938(globalCtx, 0x937U);
func_80151938(play, 0x937U);
} else {
func_8019F208();
Actor_PickUp((Actor *) this, globalCtx, 0x35, 90.0f, 10.0f);
Actor_PickUp((Actor *) this, play, 0x35, 90.0f, 10.0f);
func_801159EC(-0xA);
this->actionFunc = func_809529AC;
}
@@ -614,7 +614,7 @@ block_7:
case 1:
default:
func_8019F230();
func_80151938(globalCtx, 0x934U);
func_80151938(play, 0x934U);
break;
}
}
@@ -623,7 +623,7 @@ block_7:
Now, the top of the function also looks like a switch:
```C
temp_v0 = Message_GetState(&globalCtx->msgCtx);
temp_v0 = Message_GetState(&play->msgCtx);
if (temp_v0 == 4) {
goto block_7;
}
@@ -640,36 +640,36 @@ Interestingly, this time the blocks are the other way round. Also, the last stat
Putting all this together, we write down a function with no gotos in it:
```C
void func_809527F8(EnMs *this, GlobalContext *globalCtx) {
switch (Message_GetState(&globalCtx->msgCtx)) {
void func_809527F8(EnMs* this, PlayState* play) {
switch (Message_GetState(&play->msgCtx)) {
case 6:
this->actionFunc = func_80952734;
break;
case 5:
if (Message_ShouldAdvance(globalCtx) == 0) {
if (Message_ShouldAdvance(play) == 0) {
return;
}
func_801477B4(globalCtx);
Actor_PickUp((Actor *) this, globalCtx, 0x35, this->actor.xzDistToPlayer, this->actor.playerHeightRel);
func_801477B4(play);
Actor_PickUp((Actor *) this, play, 0x35, this->actor.xzDistToPlayer, this->actor.playerHeightRel);
this->actionFunc = func_809529AC;
break;
case 4:
if (Message_ShouldAdvance(globalCtx) != 0) {
switch (globalCtx->msgCtx.choiceIndex) {
if (Message_ShouldAdvance(play) != 0) {
switch (play->msgCtx.choiceIndex) {
case 0:
func_801477B4(globalCtx);
func_801477B4(play);
if (gSaveContext.save.playerData.rupees < 0xA) {
play_sound(0x4806U);
func_80151938(globalCtx, 0x935U);
func_80151938(play, 0x935U);
} else if (gSaveContext.save.inventory.ammo[gItemSlots[0xA]] >= 0x14) {
play_sound(0x4806U);
func_80151938(globalCtx, 0x937U);
func_80151938(play, 0x937U);
} else {
func_8019F208();
Actor_PickUp((Actor *) this, globalCtx, 0x35, 90.0f, 10.0f);
Actor_PickUp((Actor *) this, play, 0x35, 90.0f, 10.0f);
func_801159EC(-0xA);
this->actionFunc = func_809529AC;
}
@@ -678,7 +678,7 @@ void func_809527F8(EnMs *this, GlobalContext *globalCtx) {
case 1:
default:
func_8019F230();
func_80151938(globalCtx, 0x934U);
func_80151938(play, 0x934U);
break;
}
}
@@ -693,35 +693,35 @@ void func_809527F8(EnMs *this, GlobalContext *globalCtx) {
Lastly, we can simplify `case 5` to replace the return in the if by the rest of the code, and we end up with
```C
void func_809527F8(EnMs *this, GlobalContext *globalCtx) {
switch (Message_GetState(&globalCtx->msgCtx)) {
void func_809527F8(EnMs* this, PlayState* play) {
switch (Message_GetState(&play->msgCtx)) {
case 6:
this->actionFunc = func_80952734;
break;
case 5:
if (Message_ShouldAdvance(globalCtx) != 0) {
func_801477B4(globalCtx);
Actor_PickUp((Actor *) this, globalCtx, 0x35, this->actor.xzDistToPlayer, this->actor.playerHeightRel);
if (Message_ShouldAdvance(play) != 0) {
func_801477B4(play);
Actor_PickUp((Actor *) this, play, 0x35, this->actor.xzDistToPlayer, this->actor.playerHeightRel);
this->actionFunc = func_809529AC;
}
break;
case 4:
if (Message_ShouldAdvance(globalCtx) != 0) {
switch (globalCtx->msgCtx.choiceIndex) {
if (Message_ShouldAdvance(play) != 0) {
switch (play->msgCtx.choiceIndex) {
case 0:
func_801477B4(globalCtx);
func_801477B4(play);
if (gSaveContext.save.playerData.rupees < 0xA) {
play_sound(0x4806U);
func_80151938(globalCtx, 0x935U);
func_80151938(play, 0x935U);
} else if (gSaveContext.save.inventory.ammo[gItemSlots[0xA]] >= 0x14) {
play_sound(0x4806U);
func_80151938(globalCtx, 0x937U);
func_80151938(play, 0x937U);
} else {
func_8019F208();
Actor_PickUp((Actor *) this, globalCtx, 0x35, 90.0f, 10.0f);
Actor_PickUp((Actor *) this, play, 0x35, 90.0f, 10.0f);
func_801159EC(-0xA);
this->actionFunc = func_809529AC;
}
@@ -730,7 +730,7 @@ void func_809527F8(EnMs *this, GlobalContext *globalCtx) {
case 1:
default:
func_8019F230();
func_80151938(globalCtx, 0x934U);
func_80151938(play, 0x934U);
break;
}
}
+30 -30
View File
@@ -21,10 +21,10 @@ The actor file starts off looking like:
#define THIS ((EnRecepgirl*)thisx)
// --------------- 3 ---------------
void EnRecepgirl_Init(Actor* thisx, GlobalContext* globalCtx);
void EnRecepgirl_Destroy(Actor* thisx, GlobalContext* globalCtx);
void EnRecepgirl_Update(Actor* thisx, GlobalContext* globalCtx);
void EnRecepgirl_Draw(Actor* thisx, GlobalContext* globalCtx);
void EnRecepgirl_Init(Actor* thisx, PlayState* play);
void EnRecepgirl_Destroy(Actor* thisx, PlayState* play);
void EnRecepgirl_Update(Actor* thisx, PlayState* play);
void EnRecepgirl_Draw(Actor* thisx, PlayState* play);
// --------------- 4 ---------------
#if 0
@@ -115,7 +115,7 @@ The header file looks like this at the moment:
struct EnRecepgirl;
typedef void (*EnRecepgirlActionFunc)(struct EnRecepgirl*, GlobalContext*);
typedef void (*EnRecepgirlActionFunc)(struct EnRecepgirl*, PlayState*);
typedef struct EnRecepgirl {
/* 0x0000 */ Actor actor;
@@ -141,7 +141,7 @@ The general rule for order of decompilation is
- Next, decompile any other functions from the actor you have found in `Init`. You generally start with the action functions, because they return nothing and all take the same arguments,
```C
void func_80whatever(EnRecepgirl* this, GlobalContext* globalCtx);
void func_80whatever(EnRecepgirl* this, PlayState* play);
```
- Decompile each action function in turn until you run out. Along the way, do any other functions in the actor for which you have discovered the argument types. (You are probably better doing depth-first on action functions than breadth-first: it's normally easier to follow along one branch of the actions than be thinking about several at once.)
@@ -207,14 +207,14 @@ static void *D_80C106B0[4] = {(void *)0x600F8F0, (void *)0x600FCF0, (void *)0x60
static s32 D_80C106C8 = 0;
InitChainEntry D_80C106C0[2]; // unable to generate initializer
void EnRecepgirl_Init(EnRecepgirl *this, GlobalContext *globalCtx) {
void EnRecepgirl_Init(EnRecepgirl* this, PlayState* play) {
EnRecepgirl* this = (EnRecepgirl *) thisx;
void **temp_s0;
void **phi_s0;
Actor_ProcessInitChain((Actor *) this, D_80C106C0);
ActorShape_Init(&this->actor.shape, -60.0f, NULL, 0.0f);
SkelAnime_InitFlex(globalCtx, (SkelAnime *) this->unk_144, &D_06011B60, (AnimationHeader *) &D_06009890, this + 0x188, this + 0x218, 0x18);
SkelAnime_InitFlex(play, (SkelAnime *) this->unk_144, &D_06011B60, (AnimationHeader *) &D_06009890, this + 0x188, this + 0x218, 0x18);
phi_s0 = D_80C106B0;
if (D_80C106C8 == 0) {
do {
@@ -225,7 +225,7 @@ void EnRecepgirl_Init(EnRecepgirl *this, GlobalContext *globalCtx) {
D_80C106C8 = 1;
}
this->unk_2AC = 2;
if (Flags_GetSwitch(globalCtx, (s32) this->actor.params) != 0) {
if (Flags_GetSwitch(play, (s32) this->actor.params) != 0) {
this->actor.textId = 0x2ADC;
} else {
this->actor.textId = 0x2AD9;
@@ -244,14 +244,14 @@ static void *D_80C106B0[4] = {(void *)0x600F8F0, (void *)0x600FCF0, (void *)0x60
static s32 D_80C106C8 = 0;
InitChainEntry D_80C106C0[2]; // unable to generate initializer
void EnRecepgirl_Init(Actor *thisx, GlobalContext *globalCtx) {
void EnRecepgirl_Init(Actor* thisx, PlayState* play) {
EnRecepgirl* this = (EnRecepgirl *) thisx;
void **temp_s0;
void **phi_s0;
Actor_ProcessInitChain((Actor *) this, D_80C106C0);
ActorShape_Init(&this->actor.shape, -60.0f, NULL, 0.0f);
SkelAnime_InitFlex(globalCtx, (SkelAnime *) this->unk_144, &D_06011B60, (AnimationHeader *) &D_06009890, this + 0x188, this + 0x218, 0x18);
SkelAnime_InitFlex(play, (SkelAnime *) this->unk_144, &D_06011B60, (AnimationHeader *) &D_06009890, this + 0x188, this + 0x218, 0x18);
phi_s0 = D_80C106B0;
if (D_80C106C8 == 0) {
do {
@@ -262,7 +262,7 @@ void EnRecepgirl_Init(Actor *thisx, GlobalContext *globalCtx) {
D_80C106C8 = 1;
}
this->unk_2AC = 2;
if (Flags_GetSwitch(globalCtx, (s32) this->actor.params) != 0) {
if (Flags_GetSwitch(play, (s32) this->actor.params) != 0) {
this->actor.textId = 0x2ADC;
} else {
this->actor.textId = 0x2AD9;
@@ -300,14 +300,14 @@ static void *D_80C106B0[4] = {(void *)0x600F8F0, (void *)0x600FCF0, (void *)0x60
static s32 D_80C106C8 = 0;
InitChainEntry D_80C106C0[2]; // unable to generate initializer
void EnRecepgirl_Init(Actor *thisx, GlobalContext *globalCtx) {
void EnRecepgirl_Init(Actor* thisx, PlayState* play) {
EnRecepgirl* this = THIS;
void **temp_s0;
void **phi_s0;
Actor_ProcessInitChain(&this->actor, D_80C106C0);
ActorShape_Init(&this->actor.shape, -60.0f, NULL, 0.0f);
SkelAnime_InitFlex(globalCtx, (SkelAnime *) this->unk_144, &D_06011B60, (AnimationHeader *) &D_06009890, this + 0x188, this + 0x218, 0x18);
SkelAnime_InitFlex(play, (SkelAnime *) this->unk_144, &D_06011B60, (AnimationHeader *) &D_06009890, this + 0x188, this + 0x218, 0x18);
phi_s0 = D_80C106B0;
if (D_80C106C8 == 0) {
do {
@@ -318,7 +318,7 @@ void EnRecepgirl_Init(Actor *thisx, GlobalContext *globalCtx) {
D_80C106C8 = 1;
}
this->unk_2AC = 2;
if (Flags_GetSwitch(globalCtx, (s32) this->actor.params) != 0) {
if (Flags_GetSwitch(play, (s32) this->actor.params) != 0) {
this->actor.textId = 0x2ADC;
} else {
this->actor.textId = 0x2AD9;
@@ -388,7 +388,7 @@ This is the combined system that handles actors' skeletons and their animations.
```C
Actor_ProcessInitChain(&this->actor, D_80C106C0);
ActorShape_Init(&this->actor.shape, -60.0f, NULL, 0.0f);
SkelAnime_InitFlex(globalCtx, (SkelAnime *) this->unk_144, &D_06011B60, (AnimationHeader *) &D_06009890, this + 0x188, this + 0x218, 0x18);
SkelAnime_InitFlex(play, (SkelAnime *) this->unk_144, &D_06011B60, (AnimationHeader *) &D_06009890, this + 0x188, this + 0x218, 0x18);
phi_s0 = D_80C106B0;
```
@@ -396,7 +396,7 @@ An actor with SkelAnime has three structs in the Actor struct that handle it: on
There are two different sorts of SkelAnime, although for decompilation purposes there is not much difference between them. Looking at the prototype of `SkelAnime_InitFlex` from `functions.h` (or even the definition in `z_skelanime.c`),
```C
void SkelAnime_InitFlex(GlobalContext* globalCtx, SkelAnime* skelAnime, FlexSkeletonHeader* skeletonHeaderSeg,
void SkelAnime_InitFlex(PlayState* play, SkelAnime* skelAnime, FlexSkeletonHeader* skeletonHeaderSeg,
AnimationHeader* animation, Vec3s* jointTable, Vec3s* morphTable, s32 limbCount);
```
we can read off the types of the various arguments:
@@ -432,7 +432,7 @@ As with the data, these externed symbols should be kept in increasing address or
They are both passed to the function as pointers, so need `&` to pass the address instead of the actual data. Hence we end up with
```C
SkelAnime_InitFlex(globalCtx, &this->skelAnime, &D_06011B60, &D_06009890, this->jointTable, this->morphTable, 24);
SkelAnime_InitFlex(play, &this->skelAnime, &D_06011B60, &D_06009890, this->jointTable, this->morphTable, 24);
```
note that `this->jointTable` and `this->morphTable` are arrays, so are already effectively pointers and don't need a `&`.
@@ -487,14 +487,14 @@ Removing some of the declarations for data that we have accounted for, the funct
```C
? func_80C10148(EnRecepgirl *); // extern
void EnRecepgirl_Init(Actor *thisx, GlobalContext *globalCtx) {
void EnRecepgirl_Init(Actor* thisx, PlayState* play) {
EnRecepgirl* this = THIS;
void **temp_s0;
void **phi_s0;
Actor_ProcessInitChain(&this->actor, D_80C106C0);
ActorShape_Init(&this->actor.shape, -60.0f, NULL, 0.0f);
SkelAnime_InitFlex(globalCtx, &this->skelAnime, &D_06011B60, &D_06009890, this->jointTable, this->morphTable, 24);
SkelAnime_InitFlex(play, &this->skelAnime, &D_06011B60, &D_06009890, this->jointTable, this->morphTable, 24);
phi_s0 = D_80C106B0;
if (D_80C106C8 == 0) {
@@ -507,7 +507,7 @@ void EnRecepgirl_Init(Actor *thisx, GlobalContext *globalCtx) {
}
this->unk_2AC = 2;
if (Flags_GetSwitch(globalCtx, (s32) this->actor.params) != 0) {
if (Flags_GetSwitch(play, (s32) this->actor.params) != 0) {
this->actor.textId = 0x2ADC;
} else {
this->actor.textId = 0x2AD9;
@@ -520,7 +520,7 @@ We have one significant problem and a few minor ones left.
### Casts and boolean functions
mips2c likes casting a lot: this is useful for getting types, less so when the type is changed automatically, such as in `Flags_GetSwitch(globalCtx, (s32) this->actor.params)`. Also, if we look at this function's definition, we discover it will only return `true` or `false`, so we can remove the `!= 0`.
mips2c likes casting a lot: this is useful for getting types, less so when the type is changed automatically, such as in `Flags_GetSwitch(play, (s32) this->actor.params)`. Also, if we look at this function's definition, we discover it will only return `true` or `false`, so we can remove the `!= 0`.
### Functions called
@@ -529,14 +529,14 @@ One minor problem is what `func_80C10148` is: C needs a prototype to compile it
2. It's not used to assign a value
To this experience will add a third reason:
3. This is probably a setup function for an actionFunc, which are usually either `void (*)(ActorType*)` or `void (*)(ActorType*, GlobalContext*)`.
3. This is probably a setup function for an actionFunc, which are usually either `void (*)(ActorType*)` or `void (*)(ActorType*, PlayState*)`.
The upshot of all this is to remove mips2c's `? func_80C10148(EnRecepgirl *); // extern`, and add a `void func_80C10148(EnRecepgirl* this);` underneath the declarations for the main four functions:
```C
void EnRecepgirl_Init(Actor* thisx, GlobalContext* globalCtx);
void EnRecepgirl_Destroy(Actor* thisx, GlobalContext* globalCtx);
void EnRecepgirl_Update(Actor* thisx, GlobalContext* globalCtx);
void EnRecepgirl_Draw(Actor* thisx, GlobalContext* globalCtx);
void EnRecepgirl_Init(Actor* thisx, PlayState* play);
void EnRecepgirl_Destroy(Actor* thisx, PlayState* play);
void EnRecepgirl_Update(Actor* thisx, PlayState* play);
void EnRecepgirl_Draw(Actor* thisx, PlayState* play);
void func_80C10148(EnRecepgirl* this);
```
@@ -625,12 +625,12 @@ Hence we end up with
void func_80C10148(EnRecepgirl* this);
[...]
void EnRecepgirl_Init(Actor *thisx, GlobalContext *globalCtx) {
void EnRecepgirl_Init(Actor* thisx, PlayState* play) {
EnRecepgirl* this = THIS;
Actor_ProcessInitChain(&this->actor, D_80C106C0);
ActorShape_Init(&this->actor.shape, -60.0f, NULL, 0.0f);
SkelAnime_InitFlex(globalCtx, &this->skelAnime, &D_06011B60, &D_06009890, this->jointTable, this->morphTable, 24);
SkelAnime_InitFlex(play, &this->skelAnime, &D_06011B60, &D_06009890, this->jointTable, this->morphTable, 24);
if (D_80C106C8 == 0) {
for (i = 0; i < 4; i++) {
@@ -641,7 +641,7 @@ void EnRecepgirl_Init(Actor *thisx, GlobalContext *globalCtx) {
this->unk_2AC = 2;
if (Flags_GetSwitch(globalCtx, this->actor.params)) {
if (Flags_GetSwitch(play, this->actor.params)) {
this->actor.textId = 0x2ADC;
} else {
this->actor.textId = 0x2AD9;
+13 -13
View File
@@ -51,7 +51,7 @@ You can keep the diff open in the terminal, and it will refresh when the C file
In this case, we see that various branches are happening in the wrong place. Here I fear experience is necessary: notice that the function has three blocks that look quite similar, and three separate conditionals that depend on the same variable. This is a good indicator of a switch. Changing the function to use a switch,
```C
void EnJj_Init(Actor* thisx, GlobalContext* globalCtx) {
void EnJj_Init(Actor* thisx, PlayState* play) {
EnJj* this = THIS;
s32 sp4C;
@@ -64,7 +64,7 @@ void EnJj_Init(Actor* thisx, GlobalContext* globalCtx) {
switch (temp_v0) {
case -1:
SkelAnime_InitFlex(globalCtx, &this->skelAnime, &D_0600B9A8, &D_06001F4C, this->jointTable,
SkelAnime_InitFlex(play, &this->skelAnime, &D_0600B9A8, &D_06001F4C, this->jointTable,
this->morphTable, 22);
Animation_PlayLoop(&this->skelAnime, &D_06001F4C);
this->unk_30A = 0;
@@ -78,24 +78,24 @@ void EnJj_Init(Actor* thisx, GlobalContext* globalCtx) {
func_80A87800(this, func_80A87C30);
}
this->childActor = Actor_SpawnAsChild(
&globalCtx->actorCtx, &this->dyna.actor, globalCtx, ACTOR_EN_JJ, this->dyna.actor.world.pos.x - 10.0f,
&play->actorCtx, &this->dyna.actor, play, ACTOR_EN_JJ, this->dyna.actor.world.pos.x - 10.0f,
this->dyna.actor.world.pos.y, this->dyna.actor.world.pos.z, 0, this->dyna.actor.world.rot.y, 0, 0);
DynaPolyActor_Init(&this->dyna, 0);
CollisionHeader_GetVirtual(&D_06000A1C, &sp4C);
this->dyna.bgId =
DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, sp4C);
Collider_InitCylinder(globalCtx, &this->collider);
Collider_SetCylinder(globalCtx, &this->collider, &this->dyna.actor, &D_80A88CB4);
DynaPoly_SetBgActor(play, &play->colCtx.dyna, &this->dyna.actor, sp4C);
Collider_InitCylinder(play, &this->collider);
Collider_SetCylinder(play, &this->collider, &this->dyna.actor, &D_80A88CB4);
this->dyna.actor.colChkInfo.mass = 0xFF;
break;
case 0:
DynaPolyActor_Init(&this->dyna, 0);
CollisionHeader_GetVirtual(&D_06001830, &sp4C);
// temp_a1_2 = &globalCtx->colCtx.dyna;
// temp_a1_2 = &play->colCtx.dyna;
// sp44 = temp_a1_2;
this->dyna.bgId =
DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, sp4C);
func_8003ECA8(globalCtx, &globalCtx->colCtx.dyna, this->dyna.bgId);
DynaPoly_SetBgActor(play, &play->colCtx.dyna, &this->dyna.actor, sp4C);
func_8003ECA8(play, &play->colCtx.dyna, this->dyna.bgId);
this->dyna.actor.update = func_80A87F44;
this->dyna.actor.draw = NULL;
Actor_SetScale(&this->dyna.actor, 0.087f);
@@ -104,7 +104,7 @@ void EnJj_Init(Actor* thisx, GlobalContext* globalCtx) {
DynaPolyActor_Init(&this->dyna, 0);
CollisionHeader_GetVirtual(&D_0600BA8C, &sp4C);
this->dyna.bgId =
DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, sp4C);
DynaPoly_SetBgActor(play, &play->colCtx.dyna, &this->dyna.actor, sp4C);
this->dyna.actor.update = func_80A87F44;
this->dyna.actor.draw = NULL;
Actor_SetScale(&this->dyna.actor, 0.087f);
@@ -123,10 +123,10 @@ we see that the diff is nearly correct (note that `-3` lets you compare current
![Init diff 2](images/init_diff2.png)
</details>
except we still have some stack issues. Now that `temp_v0` is only used once, it looks fake. Eliminating it actually seems to make the stack worse. To fix this, we employ something that we have evidence that the developers did: namely, we make a copy of `globalCtx` (the theory is that they actually used `gameState` as an argument of the main 4 functions, just like we used `Actor* thisx` as the first argument.) The quick way to do this is to change the top of the function to
except we still have some stack issues. Now that `temp_v0` is only used once, it looks fake. Eliminating it actually seems to make the stack worse. To fix this, we employ something that we have evidence that the developers did: namely, we make a copy of `play` (the theory is that they actually used `gameState` as an argument of the main 4 functions, just like we used `Actor* thisx` as the first argument.) The quick way to do this is to change the top of the function to
```C
void EnJj_Init(Actor* thisx, GlobalContext* globalCtx2) {
GlobalContext* globalCtx = globalCtx2;
void EnJj_Init(Actor* thisx, PlayState* play2) {
PlayState* play = play2;
EnJj* this = THIS;
...
```
+45 -45
View File
@@ -40,15 +40,15 @@ Large code block, click to show
#define THIS ((EnRecepgirl*)thisx)
void EnRecepgirl_Init(Actor* thisx, GlobalContext* globalCtx);
void EnRecepgirl_Destroy(Actor* thisx, GlobalContext* globalCtx);
void EnRecepgirl_Update(Actor* thisx, GlobalContext* globalCtx);
void EnRecepgirl_Draw(Actor* thisx, GlobalContext* globalCtx);
void EnRecepgirl_Init(Actor* thisx, PlayState* play);
void EnRecepgirl_Destroy(Actor* thisx, PlayState* play);
void EnRecepgirl_Update(Actor* thisx, PlayState* play);
void EnRecepgirl_Draw(Actor* thisx, PlayState* play);
void func_80C10148(EnRecepgirl* this);
void func_80C1019C(EnRecepgirl* this, GlobalContext* globalCtx);
void func_80C1019C(EnRecepgirl* this, PlayState* play);
void func_80C10290(EnRecepgirl* this);
void func_80C102D4(EnRecepgirl * this, GlobalContext * globalCtx);
void func_80C102D4(EnRecepgirl* this, PlayState* play);
const ActorInit En_Recepgirl_InitVars = {
ACTOR_EN_RECEPGIRL,
@@ -73,13 +73,13 @@ static InitChainEntry D_80C106C0[] = {
static s32 D_80C106C8 = 0;
// #pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Recepgirl/EnRecepgirl_Init.s")
void EnRecepgirl_Init(Actor* thisx, GlobalContext* globalCtx) {
void EnRecepgirl_Init(Actor* thisx, PlayState* play) {
EnRecepgirl* this = THIS;
s32 i;
Actor_ProcessInitChain(&this->actor, D_80C106C0);
ActorShape_Init(&this->actor.shape, -60.0f, NULL, 0.0f);
SkelAnime_InitFlex(globalCtx, &this->skelAnime, &object_bg_Skel_011B60, &object_bg_Anim_009890, this->jointTable, this->morphTable, 24);
SkelAnime_InitFlex(play, &this->skelAnime, &object_bg_Skel_011B60, &object_bg_Anim_009890, this->jointTable, this->morphTable, 24);
if (D_80C106C8 == 0) {
for (i = 0; i < 4; i++) {
@@ -90,7 +90,7 @@ void EnRecepgirl_Init(Actor* thisx, GlobalContext* globalCtx) {
this->unk_2AC = 2;
if (Flags_GetSwitch(globalCtx, this->actor.params)) {
if (Flags_GetSwitch(play, this->actor.params)) {
this->actor.textId = 0x2ADC;
} else {
this->actor.textId = 0x2AD9;
@@ -100,7 +100,7 @@ void EnRecepgirl_Init(Actor* thisx, GlobalContext* globalCtx) {
}
// #pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Recepgirl/EnRecepgirl_Destroy.s")
void EnRecepgirl_Destroy(Actor* thisx, GlobalContext* globalCtx) {
void EnRecepgirl_Destroy(Actor* thisx, PlayState* play) {
}
// #pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Recepgirl/func_80C100DC.s")
@@ -127,7 +127,7 @@ void func_80C10148(EnRecepgirl *this) {
}
// #pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Recepgirl/func_80C1019C.s")
void func_80C1019C(EnRecepgirl* this, GlobalContext* globalCtx) {
void func_80C1019C(EnRecepgirl* this, PlayState* play) {
if (SkelAnime_Update(&this->skelAnime) != 0) {
if (this->skelAnime.animation == &object_bg_Anim_00A280) {
Animation_MorphToPlayOnce(&this->skelAnime, &object_bg_Anim_00AD98, 5.0f);
@@ -136,13 +136,13 @@ void func_80C1019C(EnRecepgirl* this, GlobalContext* globalCtx) {
}
}
if (Actor_ProcessTalkRequest(&this->actor, &globalCtx->state) != 0) {
if (Actor_ProcessTalkRequest(&this->actor, &play->state) != 0) {
func_80C10290(this);
} else if (Actor_IsFacingPlayer(&this->actor, 0x2000)) {
func_800B8614(&this->actor, globalCtx, 60.0f);
if (Player_GetMask(globalCtx) == 2) {
func_800B8614(&this->actor, play, 60.0f);
if (Player_GetMask(play) == 2) {
this->actor.textId = 0x2367;
} else if (Flags_GetSwitch(globalCtx, this->actor.params)) {
} else if (Flags_GetSwitch(play, this->actor.params)) {
this->actor.textId = 0x2ADC;
} else {
this->actor.textId = 0x2AD9;
@@ -157,7 +157,7 @@ void func_80C10290(EnRecepgirl *this) {
}
// #pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Recepgirl/func_80C102D4.s")
void func_80C102D4(EnRecepgirl *this, GlobalContext *globalCtx) {
void func_80C102D4(EnRecepgirl* this, PlayState* play) {
u8 temp_v0_2;
if (SkelAnime_Update(&this->skelAnime) != 0) {
@@ -176,16 +176,16 @@ void func_80C102D4(EnRecepgirl *this, GlobalContext *globalCtx) {
}
}
temp_v0_2 = Message_GetState(&globalCtx->msgCtx);
temp_v0_2 = Message_GetState(&play->msgCtx);
if (temp_v0_2 == 2) {
this->actor.textId = 0x2ADC;
func_80C10148(this);
return;
}
if ((temp_v0_2 == 5) && (Message_ShouldAdvance(globalCtx) != 0)) {
if ((temp_v0_2 == 5) && (Message_ShouldAdvance(play) != 0)) {
if (this->actor.textId == 0x2AD9) {
Flags_SetSwitch(globalCtx, this->actor.params);
Flags_SetSwitch(play, this->actor.params);
Animation_MorphToPlayOnce(&this->skelAnime, &object_bg_Anim_00AD98, 10.0f);
if ((gSaveContext.save.weekEventReg[63] & 0x80)) {
this->actor.textId = 0x2ADF;
@@ -205,23 +205,23 @@ void func_80C102D4(EnRecepgirl *this, GlobalContext *globalCtx) {
this->actor.textId = 0x2AE0;
}
}
func_80151938(globalCtx, this->actor.textId);
func_80151938(play, this->actor.textId);
}
}
// #pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Recepgirl/EnRecepgirl_Update.s")
void EnRecepgirl_Update(Actor *thisx, GlobalContext *globalCtx) {
void EnRecepgirl_Update(Actor* thisx, PlayState* play) {
s32 pad;
EnRecepgirl* this = THIS;
Vec3s sp30;
this->actionFunc(this, globalCtx);
Actor_TrackPlayer(globalCtx, &this->actor, &this->unk_2AE, &sp30, this->actor.focus.pos);
this->actionFunc(this, play);
Actor_TrackPlayer(play, &this->actor, &this->unk_2AE, &sp30, this->actor.focus.pos);
func_80C100DC(this);
}
// #pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Recepgirl/func_80C10558.s")
s32 func_80C10558(GlobalContext *globalCtx, s32 limbIndex, Gfx **dList, Vec3f *pos, Vec3s *rot, Actor *thisx) {
s32 func_80C10558(PlayState* play, s32 limbIndex, Gfx **dList, Vec3f *pos, Vec3s *rot, Actor *thisx) {
EnRecepgirl* this = THIS;
if (limbIndex == 5) {
@@ -231,7 +231,7 @@ s32 func_80C10558(GlobalContext *globalCtx, s32 limbIndex, Gfx **dList, Vec3f *p
}
// #pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Recepgirl/func_80C10590.s")
void func_80C10590(GlobalContext *globalCtx, s32 limbIndex, Actor *thisx) {
void func_80C10590(PlayState* play, s32 limbIndex, Actor *thisx) {
EnRecepgirl* this = THIS;
if (limbIndex == 5) {
@@ -241,18 +241,18 @@ void func_80C10590(GlobalContext *globalCtx, s32 limbIndex, Actor *thisx) {
}
// #pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Recepgirl/EnRecepgirl_Draw.s")
void EnRecepgirl_Draw(Actor *thisx, GlobalContext *globalCtx) {
void EnRecepgirl_Draw(Actor* thisx, PlayState* play) {
EnRecepgirl* this = THIS;
OPEN_DISPS(globalCtx->state.gfxCtx);
OPEN_DISPS(play->state.gfxCtx);
func_8012C28C(globalCtx->state.gfxCtx);
func_8012C28C(play->state.gfxCtx);
gSPSegment(POLY_OPA_DISP++, 0x08, D_80C106B0[this->unk_2AC]);
SkelAnime_DrawTransformFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, func_80C10558, NULL, func_80C10590, &this->actor);
SkelAnime_DrawTransformFlexOpa(play, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, func_80C10558, NULL, func_80C10590, &this->actor);
CLOSE_DISPS(globalCtx->state.gfxCtx);
CLOSE_DISPS(play->state.gfxCtx);
}
```
@@ -412,7 +412,7 @@ Finally, clearly `4` is linked to the data over which we're iterating: namely it
We've got one struct variable left. To find out what it does, we can look at a function that uses it, for example
```C
s32 EnRecepgirl_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot,
s32 EnRecepgirl_OverrideLimbDraw(PlayState* play, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot,
Actor* thisx) {
EnRecepgirl* this = THIS;
@@ -422,7 +422,7 @@ s32 EnRecepgirl_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx**
return false;
}
void EnRecepgirl_UnkLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Actor* thisx) {
void EnRecepgirl_UnkLimbDraw(PlayState* play, s32 limbIndex, Actor* thisx) {
EnRecepgirl* this = THIS;
if (limbIndex == 5) {
@@ -449,7 +449,7 @@ void func_80C10148(EnRecepgirl* this) {
this->actionFunc = func_80C1019C;
}
void func_80C1019C(EnRecepgirl* this, GlobalContext* globalCtx) {
void func_80C1019C(EnRecepgirl* this, PlayState* play) {
if (SkelAnime_Update(&this->skelAnime) != 0) {
if (this->skelAnime.animation == &object_bg_Anim_00A280) {
Animation_MorphToPlayOnce(&this->skelAnime, &object_bg_Anim_00AD98, 5.0f);
@@ -458,13 +458,13 @@ void func_80C1019C(EnRecepgirl* this, GlobalContext* globalCtx) {
}
}
if (Actor_ProcessTalkRequest(&this->actor, &globalCtx->state) != 0) {
if (Actor_ProcessTalkRequest(&this->actor, &play->state) != 0) {
func_80C10290(this);
} else if (Actor_IsFacingPlayer(&this->actor, 0x2000)) {
func_800B8614(&this->actor, globalCtx, 60.0f);
if (Player_GetMask(globalCtx) == PLAYER_MASK_KAFEIS_MASK) {
func_800B8614(&this->actor, play, 60.0f);
if (Player_GetMask(play) == PLAYER_MASK_KAFEIS_MASK) {
this->actor.textId = 0x2367; // "... doesn't Kafei want to break off his engagement ... ?"
} else if (Flags_GetSwitch(globalCtx, this->actor.params)) {
} else if (Flags_GetSwitch(play, this->actor.params)) {
this->actor.textId = 0x2ADC; // hear directions again?
} else {
this->actor.textId = 0x2AD9; // "Welcome..."
@@ -477,7 +477,7 @@ void func_80C10290(EnRecepgirl* this) {
this->actionFunc = func_80C102D4;
}
void func_80C102D4(EnRecepgirl* this, GlobalContext* globalCtx) {
void func_80C102D4(EnRecepgirl* this, PlayState* play) {
u8 temp_v0_2;
if (SkelAnime_Update(&this->skelAnime)) {
@@ -496,13 +496,13 @@ void func_80C102D4(EnRecepgirl* this, GlobalContext* globalCtx) {
}
}
temp_v0_2 = Message_GetState(&globalCtx->msgCtx);
temp_v0_2 = Message_GetState(&play->msgCtx);
if (temp_v0_2 == 2) {
this->actor.textId = 0x2ADC; // hear directions again?
func_80C10148(this);
} else if ((temp_v0_2 == 5) && (Message_ShouldAdvance(globalCtx) != 0)) {
} else if ((temp_v0_2 == 5) && (Message_ShouldAdvance(play) != 0)) {
if (this->actor.textId == 0x2AD9) { // "Welcome..."
Flags_SetSwitch(globalCtx, this->actor.params);
Flags_SetSwitch(play, this->actor.params);
Animation_MorphToPlayOnce(&this->skelAnime, &object_bg_Anim_00AD98, 10.0f);
if (gSaveContext.save.weekEventReg[63] & 0x80) { // showed Couple's Mask to meeting
this->actor.textId = 0x2ADF; // Mayor's office is on the left (meeting ended)
@@ -524,11 +524,11 @@ void func_80C102D4(EnRecepgirl* this, GlobalContext* globalCtx) {
this->actor.textId = 0x2AE0; // drawing room on the right, don't go in without an appointment
}
}
func_80151938(globalCtx, this->actor.textId);
func_80151938(play, this->actor.textId);
}
}
```
All this branching is to make the conversation look more diverse and interesting. Notably, though, `func_80C1019C` is set to start with, and is only changed when `Actor_ProcessTalkRequest(&this->actor, &globalCtx->state) != 0`. This is something to do with talking. The other function handles the rest of the conversation, and hands back to the first if `Message_GetState(&globalCtx->msgCtx) == 2`. This function is *something* to do with the text state, which will require `z_message` to be decomped. However, observation in-game will reveal this is something to do with ending dialogue. So we can conclude that the action functions are `EnRecepgirl_Wait` and `EnRecepgirl_Talk`. The setup functions are thus `EnRecepgirl_SetupWait` and `EnRecepgirl_SetupTalk`.
All this branching is to make the conversation look more diverse and interesting. Notably, though, `func_80C1019C` is set to start with, and is only changed when `Actor_ProcessTalkRequest(&this->actor, &play->state) != 0`. This is something to do with talking. The other function handles the rest of the conversation, and hands back to the first if `Message_GetState(&play->msgCtx) == 2`. This function is *something* to do with the text state, which will require `z_message` to be decomped. However, observation in-game will reveal this is something to do with ending dialogue. So we can conclude that the action functions are `EnRecepgirl_Wait` and `EnRecepgirl_Talk`. The setup functions are thus `EnRecepgirl_SetupWait` and `EnRecepgirl_SetupTalk`.
For more complex actors, we have a tool called `graphovl.py` that can produce function flow graphs for actors: running
```
@@ -551,7 +551,7 @@ We like to make macros for reading an actor's `params` (indeed, this is required
Actor_SetScale(&this->dyna.actor, 0.1f);
DynaPolyActor_Init(&this->dyna, 1);
CollisionHeader_GetVirtual(&object_tree_Colheader_001B2C, &colHeader);
this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader);
this->dyna.bgId = DynaPoly_SetBgActor(play, &play->colCtx.dyna, &this->dyna.actor, colHeader);
}
```
@@ -571,7 +571,7 @@ Notice that we use `thisx`: this makes the form of every one of these macros the
Actor_SetScale(&this->dyna.actor, 0.1f);
DynaPolyActor_Init(&this->dyna, 1);
CollisionHeader_GetVirtual(&object_tree_Colheader_001B2C, &colHeader);
this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader);
this->dyna.bgId = DynaPoly_SetBgActor(play, &play->colCtx.dyna, &this->dyna.actor, colHeader);
}
```
+34 -34
View File
@@ -12,29 +12,29 @@ This document will be a bit different: we will look at the draw functions in EnR
Unless it is completely invisible, an actor usually has a draw function as one of the main four actor functions. Hence its prototype looks like
```C
void EnRecepgirl_Draw(Actor* thisx, GlobalContext* globalCtx);
void EnRecepgirl_Draw(Actor* thisx, PlayState* play);
```
From now on, the process is rather different from the decompilation process used for the other functions. Here is the output of mips2c after sorting out the actor struct from Init, and with the arguments set back to `Actor* thisx`:
```C
s32 func_80C10558(GlobalContext *globalCtx, s32 limbIndex, Gfx **dList, Vec3f *pos, Vec3s *rot, Actor *actor); // extern
void func_80C10590(GlobalContext *globalCtx, s32 limbIndex, Actor *actor); // extern
s32 func_80C10558(PlayState* play, s32 limbIndex, Gfx **dList, Vec3f *pos, Vec3s *rot, Actor *actor); // extern
void func_80C10590(PlayState* play, s32 limbIndex, Actor *actor); // extern
void *D_80C106B0[4] = {(void *)0x600F8F0, (void *)0x600FCF0, (void *)0x60100F0, (void *)0x600FCF0};
void EnRecepgirl_Draw(Actor *thisx, GlobalContext *globalCtx) {
void EnRecepgirl_Draw(Actor* thisx, PlayState* play) {
EnRecepgirl* this = (EnRecepgirl *) thisx;
GraphicsContext *sp30;
Gfx *temp_v1;
GraphicsContext *temp_a0;
temp_a0 = globalCtx->state.gfxCtx;
temp_a0 = play->state.gfxCtx;
sp30 = temp_a0;
func_8012C28C(temp_a0);
temp_v1 = sp30->polyOpa.p;
sp30->polyOpa.p = temp_v1 + 8;
temp_v1->words.w0 = 0xDB060020;
temp_v1->words.w1 = (u32) D_80C106B0[this->unk_2AC];
SkelAnime_DrawTransformFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, (s32) this->skelAnime.dListCount, func_80C10558, NULL, func_80C10590, (Actor *) this);
SkelAnime_DrawTransformFlexOpa(play, this->skelAnime.skeleton, this->skelAnime.jointTable, (s32) this->skelAnime.dListCount, func_80C10558, NULL, func_80C10590, (Actor *) this);
}
```
@@ -91,25 +91,25 @@ If you have worked on OoT, you will be aware of the functions `Graph_OpenDisps`
It's sensible to eliminate all the `gfxCtx` temps and reintroduce as needed. Also remember to change the prototype and function definition back!
```C
s32 func_80C10558(GlobalContext *globalCtx, s32 limbIndex, Gfx **dList, Vec3f *pos, Vec3s *rot, Actor *actor);
s32 func_80C10558(PlayState* play, s32 limbIndex, Gfx **dList, Vec3f *pos, Vec3s *rot, Actor *actor);
#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Recepgirl/func_80C10558.s")
void func_80C10590(GlobalContext *globalCtx, s32 limbIndex, Actor *actor);
void func_80C10590(PlayState* play, s32 limbIndex, Actor *actor);
#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Recepgirl/func_80C10590.s")
// #pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Recepgirl/EnRecepgirl_Draw.s")
void EnRecepgirl_Draw(Actor *thisx, GlobalContext *globalCtx) {
void EnRecepgirl_Draw(Actor* thisx, PlayState* play) {
EnRecepgirl* this = THIS;
OPEN_DISPS(globalCtx->state.gfxCtx);
OPEN_DISPS(play->state.gfxCtx);
func_8012C28C(globalCtx->state.gfxCtx);
func_8012C28C(play->state.gfxCtx);
gSPSegment(POLY_OPA_DISP++, 0x08, D_80C106B0[this->unk_2AC]);
SkelAnime_DrawTransformFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, func_80C10558, NULL, func_80C10590, &this->actor);
SkelAnime_DrawTransformFlexOpa(play, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, func_80C10558, NULL, func_80C10590, &this->actor);
CLOSE_DISPS(globalCtx->state.gfxCtx);
CLOSE_DISPS(play->state.gfxCtx);
}
```
@@ -117,20 +117,20 @@ And this matches.
The last two functions in the actor are used as arguments in `SkelAnime_DrawTransformFlexOpa`. This is a `SkelAnime` function, except unlike the OoT ones, it has three function callback arguments instead of two: in `functions.h` or `z_skelanime.c`, we find
```C
void SkelAnime_DrawTransformFlexOpa(GlobalContext* globalCtx, void** skeleton, Vec3s* jointTable, s32 dListCount,
void SkelAnime_DrawTransformFlexOpa(PlayState* play, void** skeleton, Vec3s* jointTable, s32 dListCount,
OverrideLimbDrawOpa overrideLimbDraw, PostLimbDrawOpa postLimbDraw, TransformLimbDrawOpa transformLimbDraw, Actor* actor)
```
The typedefs of the callbacks it uses are in `z64animation.h`:
```C
typedef s32 (*OverrideLimbDrawOpa)(struct GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot,
typedef s32 (*OverrideLimbDrawOpa)(struct PlayState* play, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot,
struct Actor* thisx);
typedef void (*PostLimbDrawOpa)(struct GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot,
typedef void (*PostLimbDrawOpa)(struct PlayState* play, s32 limbIndex, Gfx** dList, Vec3s* rot,
struct Actor* thisx);
[...]
typedef void (*TransformLimbDrawOpa)(struct GlobalContext* globalCtx, s32 limbIndex, struct Actor* thisx);
typedef void (*TransformLimbDrawOpa)(struct PlayState* play, s32 limbIndex, struct Actor* thisx);
```
which is where mips2c got them from.
@@ -140,7 +140,7 @@ In this case, only two of them are used, and it is these that are the last funct
Well, we don't have a PostLimbDraw here, but as we see from the prototype, it's much the same as the OverrideLimbDraw but without the `pos` argument and no return value.
```C
s32 func_80C10558(GlobalContext *globalCtx, s32 limbIndex, Gfx **dList, Vec3f *pos, Vec3s *rot, Actor *actor) {
s32 func_80C10558(PlayState* play, s32 limbIndex, Gfx **dList, Vec3f *pos, Vec3s *rot, Actor *actor) {
if (limbIndex == 5) {
rot->x += actor->unk2B0;
}
@@ -149,7 +149,7 @@ s32 func_80C10558(GlobalContext *globalCtx, s32 limbIndex, Gfx **dList, Vec3f *p
```
Only two things to do here: we need to use `EnRecepgirl` to get to `actor + 0x2B0`, and the return value is used as a boolean, so we replace `0` by `false` (`true` means "don't draw the limb", and is hardly ever used).
```C
s32 func_80C10558(GlobalContext *globalCtx, s32 limbIndex, Gfx **dList, Vec3f *pos, Vec3s *rot, Actor *thisx) {
s32 func_80C10558(PlayState* play, s32 limbIndex, Gfx **dList, Vec3f *pos, Vec3s *rot, Actor *thisx) {
EnRecepgirl* this = THIS;
if (limbIndex == 5) {
@@ -161,7 +161,7 @@ s32 func_80C10558(GlobalContext *globalCtx, s32 limbIndex, Gfx **dList, Vec3f *p
As for the TransformLimbDraw, it has a much simpler prototype. mips2c gives
```C
void func_80C10590(GlobalContext *globalCtx, s32 limbIndex, Actor *actor) {
void func_80C10590(PlayState* play, s32 limbIndex, Actor *actor) {
if (limbIndex == 5) {
Matrix_RotateYS((s16) (0x400 - actor->unk2AE), 1);
Matrix_MultVecX(500.0f, (Vec3f *) &actor->focus);
@@ -173,7 +173,7 @@ There is only minor cleanup needed here:
- replacing the last argument of `Matrix_RotateYS` by the enum `MTXMODE_APPLY` (which means "use the current matrix instead of starting from a new identity matrix"), and the first argument by `0x400 - this->unk_2AE.x`.
- `(Vec3f *) &actor->focus` to `&actor->focus.pos` (this is the same issue as `(Actor*)this`, where mips2c doesn't climb deep enough into the struct).
```C
void func_80C10590(GlobalContext *globalCtx, s32 limbIndex, Actor *thisx) {
void func_80C10590(PlayState* play, s32 limbIndex, Actor *thisx) {
EnRecepgirl* this = THIS;
if (limbIndex == 5) {
@@ -187,7 +187,7 @@ void func_80C10590(GlobalContext *globalCtx, s32 limbIndex, Actor *thisx) {
Since EnRecepgirl was a bit light on graphics macros, we will look at an example that has a few more. A nice simple one is `ObjTree_Draw`: the original mips2c output is
```C
void ObjTree_Draw(Actor *thisx, GlobalContext *globalCtx) {
void ObjTree_Draw(Actor* thisx, PlayState* play) {
s16 sp36;
s16 sp34;
Gfx *sp28;
@@ -201,14 +201,14 @@ void ObjTree_Draw(Actor *thisx, GlobalContext *globalCtx) {
sp36 = (s16) (s32) (f32) thisx->shape.rot.x;
sp34 = (s16) (s32) (f32) thisx->shape.rot.z;
temp_a0 = globalCtx->state.gfxCtx;
temp_a0 = play->state.gfxCtx;
temp_s0 = temp_a0;
func_8012C28C(temp_a0);
temp_v0 = temp_s0->polyOpa.p;
temp_s0->polyOpa.p = temp_v0 + 8;
temp_v0->words.w0 = 0xDA380003;
sp28 = temp_v0;
sp28->words.w1 = Matrix_NewMtx(globalCtx->state.gfxCtx);
sp28->words.w1 = Matrix_NewMtx(play->state.gfxCtx);
temp_v0_2 = temp_s0->polyOpa.p;
temp_s0->polyOpa.p = temp_v0_2 + 8;
temp_v0_2->words.w1 = (u32) &D_06000680;
@@ -218,7 +218,7 @@ void ObjTree_Draw(Actor *thisx, GlobalContext *globalCtx) {
temp_s0->polyOpa.p = temp_v0_3 + 8;
temp_v0_3->words.w0 = 0xDA380003;
sp20 = temp_v0_3;
sp20->words.w1 = Matrix_NewMtx(globalCtx->state.gfxCtx);
sp20->words.w1 = Matrix_NewMtx(play->state.gfxCtx);
temp_v0_4 = temp_s0->polyOpa.p;
temp_s0->polyOpa.p = temp_v0_4 + 8;
temp_v0_4->words.w1 = (u32) &D_060007C8;
@@ -231,7 +231,7 @@ We can see there are four blocks here, although only two different macros:
temp_s0->polyOpa.p = temp_v0 + 8;
temp_v0->words.w0 = 0xDA380003;
sp28 = temp_v0;
sp28->words.w1 = Matrix_NewMtx(globalCtx->state.gfxCtx);
sp28->words.w1 = Matrix_NewMtx(play->state.gfxCtx);
```
gfxdis gives
```
@@ -240,7 +240,7 @@ gSPMatrix(POLY_OPA_DISP++, 0x12345678, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVI
```
so it becomes
```C
gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
```
```C
@@ -263,7 +263,7 @@ gSPDisplayList(POLY_OPA_DISP++, D_06000680);
temp_s0->polyOpa.p = temp_v0_3 + 8;
temp_v0_3->words.w0 = 0xDA380003;
sp20 = temp_v0_3;
sp20->words.w1 = Matrix_NewMtx(globalCtx->state.gfxCtx);
sp20->words.w1 = Matrix_NewMtx(play->state.gfxCtx);
```
This is the same as the first one. Indeed, it's identical.
```C
@@ -276,21 +276,21 @@ This is the same as the second one, but with a different second word.
Tidying up and inserting `OPEN_DISPS` and `CLOSE_DISPS`, we end up with
```C
void ObjTree_Draw(Actor* thisx, GlobalContext* globalCtx) {
void ObjTree_Draw(Actor* thisx, PlayState* play) {
s16 sp36 = (f32) thisx->shape.rot.x;
s16 sp34 = (f32) thisx->shape.rot.z;
OPEN_DISPS(globalCtx->state.gfxCtx);
OPEN_DISPS(play->state.gfxCtx);
func_8012C28C(globalCtx->state.gfxCtx);
gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
func_8012C28C(play->state.gfxCtx);
gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_OPA_DISP++, D_06000680);
Matrix_RotateZYX(sp36, 0, sp34, MTXMODE_APPLY);
gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_OPA_DISP++, D_060007C8);
CLOSE_DISPS(globalCtx->state.gfxCtx);
CLOSE_DISPS(play->state.gfxCtx);
}
```
+4 -4
View File
@@ -40,13 +40,13 @@ glabel func_809529AC
and turn it into compilable C code:
```C
void func_809529AC(EnMs *this, GlobalContext *globalCtx) {
if (Actor_HasParent(&this->actor, globalCtx)) {
void func_809529AC(EnMs* this, PlayState* play) {
if (Actor_HasParent(&this->actor, play)) {
this->actor.textId = 0;
func_800B8500(&this->actor, globalCtx, this->actor.xzDistToPlayer, this->actor.playerHeightRel, 0);
func_800B8500(&this->actor, play, this->actor.xzDistToPlayer, this->actor.playerHeightRel, 0);
this->actionFunc = func_80952A1C;
} else {
Actor_PickUp(&this->actor, globalCtx, 0x35, this->actor.xzDistToPlayer, this->actor.playerHeightRel);
Actor_PickUp(&this->actor, play, 0x35, this->actor.xzDistToPlayer, this->actor.playerHeightRel);
}
}
```
+1 -1
View File
@@ -61,7 +61,7 @@ Now, we can redefine the `jointTable` and `morphTable` in terms of the limb enum
We can also use our new skeleton name and limb enum when initialization the skeleton like so:
```c
SkelAnime_Init(globalCtx, &this->skelAnime, &gKingsChamberDekuGuardSkel, NULL, this->jointTable, this->morphTable, KINGS_CHAMBER_DEKU_GUARD_LIMB_MAX);
SkelAnime_Init(play, &this->skelAnime, &gKingsChamberDekuGuardSkel, NULL, this->jointTable, this->morphTable, KINGS_CHAMBER_DEKU_GUARD_LIMB_MAX);
```
Lastly, we can use our limb enum in `EnDns_PostLimbDraw`. Where the code originally had:
+49 -49
View File
@@ -26,7 +26,7 @@ void EnRecepgirl_Destroy(s32 arg0, ? arg1) {
```
so it really does do nothing. It is worth staying on this briefly to understand what is is doing, though. Even with no context, mips2c knows it takes two arguments because it does two saves onto the stack: the calling convention the N64 uses requires the first four arguments be saved from the registers onto the stack, since the registers are expected to be cleared when a function call happens. It's done a bad job of guessing what they are, but that's to be expected: the assembly only tells us they're words. Thankfully we already know in this case, so we can just replace the `GLOBAL_ASM` by
```C
void EnRecepgirl_Destroy(Actor* thisx, GlobalContext* globalCtx) {
void EnRecepgirl_Destroy(Actor* thisx, PlayState* play) {
}
```
@@ -54,11 +54,11 @@ void func_80C10148(EnRecepgirl *this) {
This gives us some information immediately: `D_0600AD98` is an `AnimationHeader`, and `func_80C1019C` is set as the action function. This means that we know its type, even though mips2c does not: looking in the header, we see the typedef is
```C
typedef void (*EnRecepgirlActionFunc)(struct EnRecepgirl*, GlobalContext*);
typedef void (*EnRecepgirlActionFunc)(struct EnRecepgirl*, PlayState*);
```
and so we prototype `func_80C1019C` as
```C
void func_80C1019C(EnRecepgirl* this, GlobalContext* globalCtx);
void func_80C1019C(EnRecepgirl* this, PlayState* play);
```
at the top (were it above the function we're currently working on, the prototype could eventually be replaced by the function definition itself, but since it isn't, it goes at the top with the others).
@@ -73,7 +73,7 @@ If we tackle these, we end up with
```C
void func_80C10148(EnRecepgirl* this);
void func_80C1019C(EnRecepgirl* this, GlobalContext* globalCtx);
void func_80C1019C(EnRecepgirl* this, PlayState* play);
[...]
@@ -103,7 +103,7 @@ Remake the context and run mips2c on this function's assembly file. We get
```C
? func_80C10290(EnRecepgirl *); // extern
void func_80C1019C(EnRecepgirl *this, GlobalContext *globalCtx) {
void func_80C1019C(EnRecepgirl* this, PlayState* play) {
SkelAnime *sp24;
SkelAnime *temp_a0;
@@ -116,17 +116,17 @@ void func_80C1019C(EnRecepgirl *this, GlobalContext *globalCtx) {
Animation_ChangeTransitionRepeat(temp_a0, &D_06009890, -4.0f);
}
}
if (Actor_ProcessTalkRequest((Actor *) this, globalCtx) != 0) {
if (Actor_ProcessTalkRequest((Actor *) this, play) != 0) {
func_80C10290(this);
return;
}
if (Actor_IsFacingPlayer((Actor *) this, 0x2000) != 0) {
func_800B8614((Actor *) this, globalCtx, 60.0f);
if (Player_GetMask(globalCtx) == 2) {
func_800B8614((Actor *) this, play, 60.0f);
if (Player_GetMask(play) == 2) {
this->actor.textId = 0x2367;
return;
}
if (Flags_GetSwitch(globalCtx, (s32) this->actor.params) != 0) {
if (Flags_GetSwitch(play, (s32) this->actor.params) != 0) {
this->actor.textId = 0x2ADC;
return;
}
@@ -147,7 +147,7 @@ This is a bit juicier! We can do some preliminary cleanup, then worry about the
Changing all these, we end up with
```C
void func_80C10148(EnRecepgirl* this);
void func_80C1019C(EnRecepgirl* this, GlobalContext* globalCtx);
void func_80C1019C(EnRecepgirl* this, PlayState* play);
void func_80C10290(EnRecepgirl* this);
[...]
@@ -160,7 +160,7 @@ extern FlexSkeletonHeader D_06011B60;
[...]
void func_80C1019C(EnRecepgirl *this, GlobalContext *globalCtx) {
void func_80C1019C(EnRecepgirl* this, PlayState* play) {
if (SkelAnime_Update(&this->skelAnime) != 0) {
if (&D_0600A280 == this->skelAnime.animation) {
Animation_MorphToPlayOnce(&this->skelAnime, &D_0600AD98, 5.0f);
@@ -168,17 +168,17 @@ void func_80C1019C(EnRecepgirl *this, GlobalContext *globalCtx) {
Animation_ChangeTransitionRepeat(&this->skelAnime, &D_06009890, -4.0f);
}
}
if (Actor_ProcessTalkRequest(&this->actor, &globalCtx->state) != 0) {
if (Actor_ProcessTalkRequest(&this->actor, &play->state) != 0) {
func_80C10290(this);
return;
}
if (Actor_IsFacingPlayer(&this->actor, 0x2000) != 0) {
func_800B8614(&this->actor, globalCtx, 60.0f);
if (Player_GetMask(globalCtx) == 2) {
func_800B8614(&this->actor, play, 60.0f);
if (Player_GetMask(play) == 2) {
this->actor.textId = 0x2367;
return;
}
if (Flags_GetSwitch(globalCtx, this->actor.params)) {
if (Flags_GetSwitch(play, this->actor.params)) {
this->actor.textId = 0x2ADC;
return;
}
@@ -194,7 +194,7 @@ If we look with diff.py, we find this matches. But we can replace some of the `r
Here, it's debatable whether to keep the first, since `func_80C10290` is likely a setup function. The latter two should be changed to elses, though. For now, let's replace all of them. This leaves us with
```C
void func_80C1019C(EnRecepgirl* this, GlobalContext* globalCtx) {
void func_80C1019C(EnRecepgirl* this, PlayState* play) {
if (SkelAnime_Update(&this->skelAnime) != 0) {
if (this->skelAnime.animation == &D_0600A280) {
Animation_MorphToPlayOnce(&this->skelAnime, &D_0600AD98, 5.0f);
@@ -203,13 +203,13 @@ void func_80C1019C(EnRecepgirl* this, GlobalContext* globalCtx) {
}
}
if (Actor_ProcessTalkRequest(&this->actor, &globalCtx->state) != 0) {
if (Actor_ProcessTalkRequest(&this->actor, &play->state) != 0) {
func_80C10290(this);
} else if (Actor_IsFacingPlayer(&this->actor, 0x2000)) {
func_800B8614(&this->actor, globalCtx, 60.0f);
if (Player_GetMask(globalCtx) == 2) {
func_800B8614(&this->actor, play, 60.0f);
if (Player_GetMask(play) == 2) {
this->actor.textId = 0x2367;
} else if (Flags_GetSwitch(globalCtx, this->actor.params)) {
} else if (Flags_GetSwitch(play, this->actor.params)) {
this->actor.textId = 0x2ADC;
} else {
this->actor.textId = 0x2AD9;
@@ -227,7 +227,7 @@ typedef enum {
/* 0x19 */ PLAYER_MASK_MAX
} PlayerMask;
```
and so we can write the last if as `Player_GetMask(globalCtx) == PLAYER_MASK_KAFEIS_MASK`.
and so we can write the last if as `Player_GetMask(play) == PLAYER_MASK_KAFEIS_MASK`.
Again, we have no choice in what to do next.
@@ -236,7 +236,7 @@ Again, we have no choice in what to do next.
Remaking the context and running mips2c gives
```C
void func_80C102D4(EnRecepgirl *, GlobalContext *); // extern
void func_80C102D4(EnRecepgirl*, PlayState*); // extern
void func_80C10290(EnRecepgirl *this) {
Animation_MorphToPlayOnce(&this->skelAnime, &D_0600A280, -4.0f);
@@ -256,7 +256,7 @@ Large code block, click to show
```C
extern AnimationHeader D_06000968;
void func_80C102D4(EnRecepgirl *this, GlobalContext *globalCtx) {
void func_80C102D4(EnRecepgirl* this, PlayState* play) {
SkelAnime *sp20;
AnimationHeader *temp_v0;
SkelAnime *temp_a0;
@@ -282,16 +282,16 @@ void func_80C102D4(EnRecepgirl *this, GlobalContext *globalCtx) {
Animation_MorphToPlayOnce(sp20, &D_0600A280, -4.0f);
}
}
temp_v0_2 = Message_GetState(&globalCtx->msgCtx);
temp_v0_2 = Message_GetState(&play->msgCtx);
if (temp_v0_2 == 2) {
this->actor.textId = 0x2ADC;
func_80C10148(this);
return;
}
if (((temp_v0_2 & 0xFF) == 5) && (Message_ShouldAdvance(globalCtx) != 0)) {
if (((temp_v0_2 & 0xFF) == 5) && (Message_ShouldAdvance(play) != 0)) {
temp_v0_3 = this->actor.textId;
if (temp_v0_3 == 0x2AD9) {
Flags_SetSwitch(globalCtx, (s32) this->actor.params);
Flags_SetSwitch(play, (s32) this->actor.params);
Animation_MorphToPlayOnce(sp20, &D_0600AD98, 10.0f);
if ((*(&gSaveContext + 0xF37) & 0x80) != 0) {
this->actor.textId = 0x2ADF;
@@ -312,7 +312,7 @@ void func_80C102D4(EnRecepgirl *this, GlobalContext *globalCtx) {
this->actor.textId = 0x2AE0;
}
}
func_80151938(globalCtx, this->actor.textId);
func_80151938(play, this->actor.textId);
}
}
```
@@ -321,7 +321,7 @@ void func_80C102D4(EnRecepgirl *this, GlobalContext *globalCtx) {
Well, this is a big one! We get one more extern, for `D_06000968`. A lot of the temps used in the conditionals look fake, with the exception of `temp_v0_2`: because the function is only called once but the temp is used twice, the temp must be real. Removing the others and switching the `animation` conditionals,
```C
void func_80C102D4(EnRecepgirl *this, GlobalContext *globalCtx) {
void func_80C102D4(EnRecepgirl* this, PlayState* play) {
u8 temp_v0_2;
if (SkelAnime_Update(&this->skelAnime) != 0) {
@@ -340,16 +340,16 @@ void func_80C102D4(EnRecepgirl *this, GlobalContext *globalCtx) {
}
}
temp_v0_2 = Message_GetState(&globalCtx->msgCtx);
temp_v0_2 = Message_GetState(&play->msgCtx);
if (temp_v0_2 == 2) {
this->actor.textId = 0x2ADC;
func_80C10148(this);
return;
}
if (((temp_v0_2 & 0xFF) == 5) && (Message_ShouldAdvance(globalCtx) != 0)) {
if (((temp_v0_2 & 0xFF) == 5) && (Message_ShouldAdvance(play) != 0)) {
if (this->actor.textId == 0x2AD9) {
Flags_SetSwitch(globalCtx, this->actor.params);
Flags_SetSwitch(play, this->actor.params);
Animation_MorphToPlayOnce(&this->skelAnime, &D_0600AD98, 10.0f);
if ((*(&gSaveContext + 0xF37) & 0x80) != 0) {
this->actor.textId = 0x2ADF;
@@ -369,7 +369,7 @@ void func_80C102D4(EnRecepgirl *this, GlobalContext *globalCtx) {
this->actor.textId = 0x2AE0;
}
}
func_80151938(globalCtx, this->actor.textId);
func_80151938(play, this->actor.textId);
}
}
```
@@ -390,14 +390,14 @@ The yellow shows registers that don't match, the different colours on the regist
```
somehow we skipped over `t0`. Where is this in the code? The `153` in the middle is the line number in the C file (the `3f0`s are the offsets into the assembly file), we have `--source` if you want to see the code explicitly, or you can do it the old-fashioned way, and work it out from nearby function calls. In this case, `func_80C10148` is run straight after, and the only place that is called is
```C
temp_v0_2 = Message_GetState(&globalCtx->msgCtx);
temp_v0_2 = Message_GetState(&play->msgCtx);
if (temp_v0_2 == 2) {
this->actor.textId = 0x2ADC;
func_80C10148(this);
return;
}
if (((temp_v0_2 & 0xFF) == 5) && (Message_ShouldAdvance(globalCtx) != 0)) {
if (((temp_v0_2 & 0xFF) == 5) && (Message_ShouldAdvance(play) != 0)) {
```
If you look at the conditionals and the declaration of `temp_v0_2`, you may notice something odd: `temp_v0_2` is a `u8`. Therefore the `& 0xFF` does nothing! It's surprisingly common for this to happen, be it leaving out a `& 0xFF` or adding an extraneous one. If we remove it, we get a match:
@@ -417,12 +417,12 @@ Remake the context and run mips2c:
```C
? func_80C100DC(EnRecepgirl *); // extern
void EnRecepgirl_Update(Actor *thisx, GlobalContext *globalCtx) {
void EnRecepgirl_Update(Actor* thisx, PlayState* play) {
EnRecepgirl* this = (EnRecepgirl *) thisx;
? sp30;
this->actionFunc(this, globalCtx);
Actor_TrackPlayer(globalCtx, (Actor *) this, this + 0x2AE, (Vec3s *) &sp30, (bitwise Vec3f) this->actor.focus.pos.x, this->actor.focus.pos.y, this->actor.focus.pos.z);
this->actionFunc(this, play);
Actor_TrackPlayer(play, (Actor *) this, this + 0x2AE, (Vec3s *) &sp30, (bitwise Vec3f) this->actor.focus.pos.x, this->actor.focus.pos.y, this->actor.focus.pos.z);
func_80C100DC(this);
}
```
@@ -435,18 +435,18 @@ void func_80C100DC(EnRecepgirl *);
[...]
void EnRecepgirl_Update(Actor *thisx, GlobalContext *globalCtx) {
void EnRecepgirl_Update(Actor* thisx, PlayState* play) {
EnRecepgirl* this = THIS;
? sp30;
this->actionFunc(this, globalCtx);
Actor_TrackPlayer(globalCtx, &this->actor, this + 0x2AE, (Vec3s *) &sp30, (bitwise Vec3f) this->actor.focus.pos.x, this->actor.focus.pos.y, this->actor.focus.pos.z);
this->actionFunc(this, play);
Actor_TrackPlayer(play, &this->actor, this + 0x2AE, (Vec3s *) &sp30, (bitwise Vec3f) this->actor.focus.pos.x, this->actor.focus.pos.y, this->actor.focus.pos.z);
func_80C100DC(this);
}
```
Now, our problem is `Actor_TrackPlayer`. The arguments all look terrible! Indeed, if we look at the actual function in `src/code/code_800E8EA0.c` (found by searching), we find that it should be
```C
s32 Actor_TrackPlayer(GlobalContext* globalCtx, Actor* actor, Vec3s* headRot, Vec3s* torsoRot, Vec3f focusPos)
s32 Actor_TrackPlayer(PlayState* play, Actor* actor, Vec3s* headRot, Vec3s* torsoRot, Vec3f focusPos)
```
So mips2c has made a bit of a mess here:
- the third argument should be a `Vec3s`. Hence `this + 0x2AE` is a `Vec3s*`, and so `this->unk_2AE` is a `Vec3s`
@@ -455,12 +455,12 @@ So mips2c has made a bit of a mess here:
Fixing all of this, we end up with
```C
void EnRecepgirl_Update(EnRecepgirl *this, GlobalContext *globalCtx) {
void EnRecepgirl_Update(EnRecepgirl* this, PlayState* play) {
EnRecepgirl* this = THIS;
Vec3s sp30;
this->actionFunc(this, globalCtx);
Actor_TrackPlayer(globalCtx, &this->actor, &this->unk_2AE, &sp30, this->actor.focus.pos);
this->actionFunc(this, play);
Actor_TrackPlayer(play, &this->actor, &this->unk_2AE, &sp30, this->actor.focus.pos);
func_80C100DC(this);
}
```
@@ -482,21 +482,21 @@ It's entirely possible that `unk_2AD` is not real, and is just padding: see [Typ
![EnRecepgirl_Update's stack difference](images/EnRecepgirl_stack_diff.png)
So `sp30` is in the wrong place: it's `4` too high on the stack in ours. This is because the main four functions do not actually take `GlobalContext`: they really take `Gamestate` and recast it with a temp, just like `EnRecepgirl* this = THIS;`. We haven't implemented this in the repo yet, though, so for now, it suffices to put a pad on the stack where it would go instead: experience has shown when it matters, it goes above the actor recast, so we end up with
So `sp30` is in the wrong place: it's `4` too high on the stack in ours. This is because the main four functions do not actually take `PlayState`: they really take `Gamestate` and recast it with a temp, just like `EnRecepgirl* this = THIS;`. We haven't implemented this in the repo yet, though, so for now, it suffices to put a pad on the stack where it would go instead: experience has shown when it matters, it goes above the actor recast, so we end up with
```C
void EnRecepgirl_Update(Actor *thisx, GlobalContext *globalCtx) {
void EnRecepgirl_Update(Actor* thisx, PlayState* play) {
s32 pad;
EnRecepgirl* this = THIS;
Vec3s sp30;
this->actionFunc(this, globalCtx);
Actor_TrackPlayer(globalCtx, &this->actor, &this->unk_2AE, &sp30, this->actor.focus.pos);
this->actionFunc(this, play);
Actor_TrackPlayer(play, &this->actor, &this->unk_2AE, &sp30, this->actor.focus.pos);
func_80C100DC(this);
}
```
and this now matches.
**N.B.** sometimes using an actual `GlobalContext* globalCtx` temp is required for matching: add it to your bag o' matching memes.
**N.B.** sometimes using an actual `PlayState* play` temp is required for matching: add it to your bag o' matching memes.
### *Some remarks about the function stack
+1 -1
View File
@@ -33,7 +33,7 @@ Useful data for guessing types:
Another useful thing to put here: the typedef for an action function is
```C
typedef void (*ActorNameActionFunc)(struct ActorName*, GlobalContext*);
typedef void (*ActorNameActionFunc)(struct ActorName*, PlayState*);
```
where you replace `ActorName` by the actual actor name as used elsewhere in the actor, e.g. `EnRecepgirl`. In MM these typedefs have been automatically generated, so you don't need to constantly copy from here or another actor any more.