* ObjTree OK, data imported

* EnMs OK, data imported

* And the spec

* OK

* Some minor edits

* A lot of preliminary stuff

* Mostly complete beginning

* First draft of other functions doc

* Whoops, forgot the GlobalContext pad

* Draw functions (minus colour), create Data

* Data

* gitignore, some progress on documenting

* Review comments, continue documenting

* spec

* Finish off documentation

* undefined_syms

* Add a couple of todos

* One more

* At least add tools for object decomp

* Start conversion table stuff

* Document ObjTree

* Document EnMs

* Add more tables to conversions

* Maide's review

* Review

* Review

* Typos and incomplete thoughts

* Update vscode.md

* Correct function/variable names

* Review suggestions

* Format

* Missed one

* Rename functions and format

* Fix ObjTree

* Update actorfixer.py, fix some variable names

* Some review

* Review suggestions

* More review

* Hopefully fix all the thisx references

* Missed one
This commit is contained in:
EllipticEllipsis 2021-12-16 23:47:18 +00:00 committed by GitHub
parent a6cd0e4427
commit d5b71bd0f5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
54 changed files with 4878 additions and 270 deletions

1
.gitignore vendored
View File

@ -44,6 +44,7 @@ tools/mips_to_c/
# Docs
!docs/**/*.png
!.vscode/extensions.json
# Per-user configuration
.python-version

View File

@ -0,0 +1,747 @@
# Advanced control flow
Nice as `EnRecepgirl` was, she was somewhat lacking in complexity. In this document, we'll look at something rather more complicated than any of the functions she had.
Again our example will be taken from a small NPC: this time, `EnMs` (Bean Seller). Most of its functions are even simpler than `EnRecepgirl`'s, and fairly quickly we can get to
<details>
<summary>
Large code block, click to show.
</summary>
```C
#include "z_en_ms.h"
#define FLAGS 0x00000009
#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 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);
const ActorInit En_Ms_InitVars = {
ACTOR_EN_MS,
ACTORCAT_NPC,
FLAGS,
OBJECT_MS,
sizeof(EnMs),
(ActorFunc)EnMs_Init,
(ActorFunc)EnMs_Destroy,
(ActorFunc)EnMs_Update,
(ActorFunc)EnMs_Draw,
};
static ColliderCylinderInitType1 D_80952BA0 = {
{ COLTYPE_NONE, AT_NONE, AC_ON | AC_TYPE_PLAYER, OC1_ON | OC1_TYPE_ALL, COLSHAPE_CYLINDER, },
{ ELEMTYPE_UNK0, { 0x00000000, 0x00, 0x00 }, { 0xF7CFFFFF, 0x00, 0x00 }, TOUCH_NONE | TOUCH_SFX_NORMAL, BUMP_ON, OCELEM_ON, },
{ 22, 37, 0, { 0, 0, 0 } },
};
static InitChainEntry D_80952BCC[] = {
ICHAIN_U8(targetMode, 2, ICHAIN_CONTINUE),
ICHAIN_F32(targetArrowOffset, 500, ICHAIN_STOP),
};
extern ColliderCylinderInitType1 D_80952BA0;
extern InitChainEntry D_80952BCC[];
extern AnimationHeader D_060005EC;
extern FlexSkeletonHeader D_06003DC0;
void EnMs_Init(Actor* thisx, GlobalContext* globalCtx) {
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);
ActorShape_Init(&this->actor.shape, 0.0f, func_800B3FC0, 35.0f);
Actor_SetScale(&this->actor, 0.015f);
this->actor.colChkInfo.mass = 0xFF;
this->actionFunc = func_80952734;
this->actor.speedXZ = 0.0f;
this->actor.velocity.y = 0.0f;
this->actor.gravity = -1.0f;
}
void EnMs_Destroy(Actor* thisx, GlobalContext* globalCtx) {
EnMs* this = THIS;
Collider_DestroyCylinder(globalCtx, &this->collider);
}
void func_80952734(EnMs* this, GlobalContext* globalCtx) {
s16 temp_v1 = this->actor.yawTowardsPlayer - this->actor.shape.rot.y;
if (gSaveContext.inventory.items[10] == ITEM_NONE) {
this->actor.textId = 0x92E;
} else {
this->actor.textId = 0x932;
}
if (func_800B84D0(&this->actor, globalCtx) != 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);
}
}
}
#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)) {
this->actor.textId = 0;
func_800B8500(&this->actor, globalCtx, this->actor.xzDistToPlayer, this->actor.playerHeightRel, 0);
this->actionFunc = func_80952A1C;
} else {
func_800B8A1C(&this->actor, globalCtx, 0x35, this->actor.xzDistToPlayer, this->actor.playerHeightRel);
}
}
void func_80952A1C(EnMs *this, GlobalContext *globalCtx) {
if (func_800B84D0(&this->actor, globalCtx)) {
func_80151938(globalCtx, 0x936U);
this->actionFunc = func_809527F8;
} else {
func_800B8500(&this->actor, globalCtx, this->actor.xzDistToPlayer, this->actor.playerHeightRel, -1);
}
}
void EnMs_Update(Actor* thisx, GlobalContext* globalCtx) {
s32 pad;
EnMs* this = THIS;
Actor_SetHeight(&this->actor, 20.0f);
this->actor.targetArrowOffset = 500.0f;
Actor_SetScale(&this->actor, 0.015f);
SkelAnime_Update(&this->skelAnime);
this->actionFunc(this, globalCtx);
Collider_UpdateCylinder(&this->actor, &this->collider);
CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base);
}
void EnMs_Draw(Actor* thisx, GlobalContext* globalCtx) {
EnMs* this = THIS;
func_8012C28C(globalCtx->state.gfxCtx);
SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, NULL,
NULL, &this->actor);
}
```
</details>
(Skipping any documentation we might have done.) Indeed, this actor is so simple so far that you can see why it wasn't worth using most of it for the rest of the tutorial. `func_809527F8` is a different story, however. We know it's an action function since it's set to the `actionFunc` in `func_80952A1C`. But mips2c gives us
```bash
$ ../mips_to_c/mips_to_c.py asm/non_matchings/overlays/ovl_En_Ms/func_809527F8.s --context ctx.c --gotos-only
```
```C
void func_809527F8(EnMs *this, GlobalContext *globalCtx) {
u8 temp_v0;
u8 temp_v0_2;
temp_v0 = func_80152498(&globalCtx->msgCtx);
if (temp_v0 != 4) {
if (temp_v0 != 5) {
if ((temp_v0 == 6) && (func_80147624(globalCtx) != 0)) {
this->actionFunc = func_80952734;
return;
}
// Duplicate return node #17. Try simplifying control flow for better match
return;
}
if (func_80147624(globalCtx) != 0) {
func_801477B4(globalCtx);
func_800B8A1C((Actor *) this, globalCtx, 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 (func_80147624(globalCtx) != 0) {
temp_v0_2 = globalCtx->msgCtx.choiceIndex;
if (temp_v0_2 != 0) {
if (temp_v0_2 != 1) {
}
func_8019F230();
func_80151938(globalCtx, 0x934U);
// Duplicate return node #17. Try simplifying control flow for better match
return;
}
func_801477B4(globalCtx);
if ((s32) gSaveContext.rupees < 0xA) {
play_sound(0x4806U);
func_80151938(globalCtx, 0x935U);
return;
}
if ((s32) gSaveContext.inventory.ammo[gItemSlots[0xA]] >= 0x14) {
play_sound(0x4806U);
func_80151938(globalCtx, 0x937U);
return;
}
func_8019F208();
func_800B8A1C((Actor *) this, globalCtx, 0x35, 90.0f, 10.0f);
func_801159EC(-0xA);
this->actionFunc = func_809529AC;
}
}
```
which is long, messy, and contains some rather nasty-looking control flow, including horrors like
```C
temp_v0 = func_80152498(&globalCtx->msgCtx);
if (temp_v0 != 4) {
if (temp_v0 != 5) {
if ((temp_v0 == 6) && (func_80147624(globalCtx) != 0)) {
this->actionFunc = func_80952734;
return;
}
// Duplicate return node #17. Try simplifying control flow for better match
return;
}
```
If you read the OoT tutorial, you'll know these nested negated ifs all using the same variable are a good indicator that there's a switch. The problem is working out how to write it.
## Goto-only mode
For didactic purposes, we'll use a feature of mips2c called goto-only mode to examine this. *This is not the only way of doing it*, but it is good practice for a beginner to this sort of control flow. Running
```bash
../mips_to_c/mips_to_c.py asm/non_matchings/overlays/ovl_En_Ms/func_809527F8.s --context ctx.c --gotos-only
```
instead will produce
```C
void func_809527F8(EnMs *this, GlobalContext *globalCtx) {
u8 temp_v0;
u8 temp_v0_2;
temp_v0 = func_80152498(&globalCtx->msgCtx);
if (temp_v0 == 4) {
goto block_7;
}
if (temp_v0 == 5) {
goto block_5;
}
if (temp_v0 != 6) {
goto block_17;
}
if (func_80147624(globalCtx) == 0) {
goto block_17;
}
this->actionFunc = func_80952734;
return;
block_5:
if (func_80147624(globalCtx) == 0) {
goto block_17;
}
func_801477B4(globalCtx);
func_800B8A1C((Actor *) this, globalCtx, 0x35, this->actor.xzDistToPlayer, this->actor.playerHeightRel);
this->actionFunc = func_809529AC;
return;
block_7:
if (func_80147624(globalCtx) == 0) {
goto block_17;
}
temp_v0_2 = globalCtx->msgCtx.choiceIndex;
if (temp_v0_2 == 0) {
goto block_11;
}
if (temp_v0_2 == 1) {
goto block_16;
}
goto block_16;
block_11:
func_801477B4(globalCtx);
if ((s32) gSaveContext.rupees >= 0xA) {
goto block_13;
}
play_sound(0x4806U);
func_80151938(globalCtx, 0x935U);
return;
block_13:
if ((s32) gSaveContext.inventory.ammo[gItemSlots[0xA]] < 0x14) {
goto block_15;
}
play_sound(0x4806U);
func_80151938(globalCtx, 0x937U);
return;
block_15:
func_8019F208();
func_800B8A1C((Actor *) this, globalCtx, 0x35, 90.0f, 10.0f);
func_801159EC(-0xA);
this->actionFunc = func_809529AC;
return;
block_16:
func_8019F230();
func_80151938(globalCtx, 0x934U);
block_17:
return;
}
```
which in many ways looks worse: you can see why the use of gotos in code is strongly discouraged. However, if you throw this in `diff.py`, you'll find it's rather closer than you'd have thought. Goto-only mode has the advantages that
- code is always in the right order: mips2c has not had to reorder anything to get the ifs to work out
- it is often possible to get quite close with gotos, then start removing them, checking the matching status at each point. This is usually easier than trying to puzzle out the way it's trying to jump out of an `if ( || )` or similar.
- if you're trying to keep track of where you are in the code, the gotos mean that it is closer to the assembly in the first place.
## Eliminating the gotos
The simplest sort of block label to eliminate is one that is only used once, and where the corresponding goto jumps over a simple block of code with no extra internal control flow structure. There are two obvious examples of this here, the first being
```C
if ((s32) gSaveContext.rupees >= 0xA) {
goto block_13;
}
play_sound(0x4806U);
func_80151938(globalCtx, 0x935U);
return;
block_13:
```
Currently, this says to jump over the code block `play_sound...` if the condition in the if is satisfied. In non-goto terms, this means that the block should be run if the condition is *not* satisfied. This also illustrates a general property of goto-only mode: you have to reverse the senses of all of the ifs. Therefore the appropriate approach is to swap the if round, put the code block inside, and remove the goto and the label:
```C
if (gSaveContext.rupees < 0xA) {
play_sound(0x4806U);
func_80151938(globalCtx, 0x935U);
return;
}
```
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) {
u8 temp_v0;
u8 temp_v0_2;
temp_v0 = func_80152498(&globalCtx->msgCtx);
if (temp_v0 == 4) {
goto block_7;
}
if (temp_v0 == 5) {
goto block_5;
}
if (temp_v0 != 6) {
goto block_17;
}
if (func_80147624(globalCtx) == 0) {
goto block_17;
}
this->actionFunc = func_80952734;
return;
block_5:
if (func_80147624(globalCtx) == 0) {
goto block_17;
}
func_801477B4(globalCtx);
func_800B8A1C((Actor *) this, globalCtx, 0x35, this->actor.xzDistToPlayer, this->actor.playerHeightRel);
this->actionFunc = func_809529AC;
return;
block_7:
if (func_80147624(globalCtx) == 0) {
goto block_17;
}
temp_v0_2 = globalCtx->msgCtx.choiceIndex;
if (temp_v0_2 == 0) {
goto block_11;
}
if (temp_v0_2 == 1) {
goto block_16;
}
goto block_16;
block_11:
func_801477B4(globalCtx);
if (gSaveContext.rupees < 0xA) {
play_sound(0x4806U);
func_80151938(globalCtx, 0x935U);
return;
}
if (gSaveContext.inventory.ammo[gItemSlots[0xA]] >= 0x14) {
play_sound(0x4806U);
func_80151938(globalCtx, 0x937U);
return;
}
func_8019F208();
func_800B8A1C((Actor *) this, globalCtx, 0x35, 90.0f, 10.0f);
func_801159EC(-0xA);
this->actionFunc = func_809529AC;
return;
block_16:
func_8019F230();
func_80151938(globalCtx, 0x934U);
block_17:
return;
}
```
We can't apply this rule any more, so we need to move on to the next: `block_17` just contains a `return`. So we can replace it by `return` everywhere it appears.
```C
void func_809527F8(EnMs *this, GlobalContext *globalCtx) {
u8 temp_v0;
u8 temp_v0_2;
temp_v0 = func_80152498(&globalCtx->msgCtx);
if (temp_v0 == 4) {
goto block_7;
}
if (temp_v0 == 5) {
goto block_5;
}
if (temp_v0 != 6) {
return;
}
if (func_80147624(globalCtx) == 0) {
return;
}
this->actionFunc = func_80952734;
return;
block_5:
if (func_80147624(globalCtx) == 0) {
return;
}
func_801477B4(globalCtx);
func_800B8A1C((Actor *) this, globalCtx, 0x35, this->actor.xzDistToPlayer, this->actor.playerHeightRel);
this->actionFunc = func_809529AC;
return;
block_7:
if (func_80147624(globalCtx) == 0) {
return;
}
temp_v0_2 = globalCtx->msgCtx.choiceIndex;
if (temp_v0_2 == 0) {
goto block_11;
}
if (temp_v0_2 == 1) {
goto block_16;
}
goto block_16;
block_11:
func_801477B4(globalCtx);
if (gSaveContext.rupees < 0xA) {
play_sound(0x4806U);
func_80151938(globalCtx, 0x935U);
return;
}
if (gSaveContext.inventory.ammo[gItemSlots[0xA]] >= 0x14) {
play_sound(0x4806U);
func_80151938(globalCtx, 0x937U);
return;
}
func_8019F208();
func_800B8A1C((Actor *) this, globalCtx, 0x35, 90.0f, 10.0f);
func_801159EC(-0xA);
this->actionFunc = func_809529AC;
return;
block_16:
func_8019F230();
func_80151938(globalCtx, 0x934U);
}
```
Our next rule is about non-crossing blocks. If two code blocks do not contain any jumps between them, we can treat them separately. This is *almost* true for the code after `block_7`, were it not for the returns; of course returns are a special case because they can be used to be escape from a function at any point. This doesn't get us very far in this case, unfortunately, but it *does* tell us we can look at the second half of the function separately.
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;
if (temp_v0_2 == 0) {
goto block_11;
}
if (temp_v0_2 == 1) {
goto block_16;
}
goto block_16;
```
because
- there are multiple ifs that are simple numeric comparisons of the same argument
- the goto blocks are in the same order as the ifs
- there is one last goto at the end that triggers if none of the ifs does: this sounds an awful lot like a `default`!
So let us rewrite the entire second half as a switch:
```C
switch (globalCtx->msgCtx.choiceIndex) {
case 0:
func_801477B4(globalCtx);
if (gSaveContext.rupees < 0xA) {
play_sound(0x4806U);
func_80151938(globalCtx, 0x935U);
return;
}
if (gSaveContext.inventory.ammo[gItemSlots[0xA]] >= 0x14) {
play_sound(0x4806U);
func_80151938(globalCtx, 0x937U);
return;
}
func_8019F208();
func_800B8A1C((Actor *) this, globalCtx, 0x35, 90.0f, 10.0f);
func_801159EC(-0xA);
this->actionFunc = func_809529AC;
return;
break;
case 1:
default:
func_8019F230();
func_80151938(globalCtx, 0x934U);
break;
}
```
There's a couple of other obvious things here:
- the last `return` in `case 0` is unnecessary since there is no other code after the switch, so breaking is equivalent to the return`
- 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) {
case 0:
func_801477B4(globalCtx);
if (gSaveContext.rupees < 0xA) {
play_sound(0x4806U);
func_80151938(globalCtx, 0x935U);
} else if (gSaveContext.inventory.ammo[gItemSlots[0xA]] >= 0x14) {
play_sound(0x4806U);
func_80151938(globalCtx, 0x937U);
} else {
func_8019F208();
func_800B8A1C((Actor *) this, globalCtx, 0x35, 90.0f, 10.0f);
func_801159EC(-0xA);
this->actionFunc = func_809529AC;
}
break;
case 1:
default:
func_8019F230();
func_80151938(globalCtx, 0x934U);
break;
}
```
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 (func_80147624(globalCtx) == 0) {
return;
}
```
can be swapped round and made to wrap the switch. This leaves us with
```C
void func_809527F8(EnMs *this, GlobalContext *globalCtx) {
u8 temp_v0;
temp_v0 = func_80152498(&globalCtx->msgCtx);
if (temp_v0 == 4) {
goto block_7;
}
if (temp_v0 == 5) {
goto block_5;
}
if (temp_v0 != 6) {
return;
}
if (func_80147624(globalCtx) == 0) {
return;
}
this->actionFunc = func_80952734;
return;
block_5:
if (func_80147624(globalCtx) == 0) {
return;
}
func_801477B4(globalCtx);
func_800B8A1C((Actor *) this, globalCtx, 0x35, this->actor.xzDistToPlayer, this->actor.playerHeightRel);
this->actionFunc = func_809529AC;
return;
block_7:
if (func_80147624(globalCtx) != 0) {
switch (globalCtx->msgCtx.choiceIndex) {
case 0:
func_801477B4(globalCtx);
if (gSaveContext.rupees < 0xA) {
play_sound(0x4806U);
func_80151938(globalCtx, 0x935U);
} else if (gSaveContext.inventory.ammo[gItemSlots[0xA]] >= 0x14) {
play_sound(0x4806U);
func_80151938(globalCtx, 0x937U);
} else {
func_8019F208();
func_800B8A1C((Actor *) this, globalCtx, 0x35, 90.0f, 10.0f);
func_801159EC(-0xA);
this->actionFunc = func_809529AC;
}
break;
case 1:
default:
func_8019F230();
func_80151938(globalCtx, 0x934U);
break;
}
}
}
```
Now, the top of the function also looks like a switch:
```C
temp_v0 = func_80152498(&globalCtx->msgCtx);
if (temp_v0 == 4) {
goto block_7;
}
if (temp_v0 == 5) {
goto block_5;
}
if (temp_v0 != 6) {
return;
}
```
Interestingly, this time the blocks are the other way round. Also, the last statement is a `!=` rather than an `==`: this should be the default this time. The code order takes priority over the check order, because the compiler likes to put those in numerical order. There will be cases 4,5,6, but in the order 6,5,4, because that's how the code ordering goes. Also, notice that every case returns at the end: this means there's nothing else in the function after this switch, so everything after `block_7` is actually part of `case 4`.
Putting all this together, we write down a function with no gotos in it:
```C
void func_809527F8(EnMs *this, GlobalContext *globalCtx) {
switch (func_80152498(&globalCtx->msgCtx)) {
case 6:
this->actionFunc = func_80952734;
break;
case 5:
if (func_80147624(globalCtx) == 0) {
return;
}
func_801477B4(globalCtx);
func_800B8A1C((Actor *) this, globalCtx, 0x35, this->actor.xzDistToPlayer, this->actor.playerHeightRel);
this->actionFunc = func_809529AC;
break;
case 4:
if (func_80147624(globalCtx) != 0) {
switch (globalCtx->msgCtx.choiceIndex) {
case 0:
func_801477B4(globalCtx);
if (gSaveContext.rupees < 0xA) {
play_sound(0x4806U);
func_80151938(globalCtx, 0x935U);
} else if (gSaveContext.inventory.ammo[gItemSlots[0xA]] >= 0x14) {
play_sound(0x4806U);
func_80151938(globalCtx, 0x937U);
} else {
func_8019F208();
func_800B8A1C((Actor *) this, globalCtx, 0x35, 90.0f, 10.0f);
func_801159EC(-0xA);
this->actionFunc = func_809529AC;
}
break;
case 1:
default:
func_8019F230();
func_80151938(globalCtx, 0x934U);
break;
}
}
break;
default:
break;
}
}
```
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 (func_80152498(&globalCtx->msgCtx)) {
case 6:
this->actionFunc = func_80952734;
break;
case 5:
if (func_80147624(globalCtx) != 0) {
func_801477B4(globalCtx);
func_800B8A1C((Actor *) this, globalCtx, 0x35, this->actor.xzDistToPlayer, this->actor.playerHeightRel);
this->actionFunc = func_809529AC;
}
break;
case 4:
if (func_80147624(globalCtx) != 0) {
switch (globalCtx->msgCtx.choiceIndex) {
case 0:
func_801477B4(globalCtx);
if (gSaveContext.rupees < 0xA) {
play_sound(0x4806U);
func_80151938(globalCtx, 0x935U);
} else if (gSaveContext.inventory.ammo[gItemSlots[0xA]] >= 0x14) {
play_sound(0x4806U);
func_80151938(globalCtx, 0x937U);
} else {
func_8019F208();
func_800B8A1C((Actor *) this, globalCtx, 0x35, 90.0f, 10.0f);
func_801159EC(-0xA);
this->actionFunc = func_809529AC;
}
break;
case 1:
default:
func_8019F230();
func_80151938(globalCtx, 0x934U);
break;
}
}
break;
default:
break;
}
}
```
And this matches!
We will not document this now, although even with so few function named it seems pretty clear that it's to do with buying beans (and indeed, Magic Beans cost 10 Rupees and have Get Item ID `0x35`) You might like to try to match this function without using goto-only mode, to compare. It is also an interesting exercise to see what each elimination does to the diff: sometimes it will stray surprisingly far for a small change.

View File

@ -0,0 +1,675 @@
# Beginning decompilation: the Init function and the Actor struct
Up: [Contents](contents.md)
Open the C file and the H file with your actor's name from the appropriate directory in `src/overlays/actors/`. These will be the main files we work with. We will be using EnRecepgirl (the rather forward Mayor's receptionist in the Mayor's residence in East Clock Town) as our example: it is a nice simple NPC with most of the common features of an NPC.
Each actor has associated to it a data file and one assembly file per function. During the process, we will transfer the contents of all or most of these into the main C file. VSCode's search feature usually makes it quite easy to find the appropriate files without troubling the directory tree.
## Anatomy of the C file
The actor file starts off looking like:
```C
// --------------- 1 ---------------
// --------------- 2 ---------------
#include "z_en_recepgirl.h"
#define FLAGS 0x00000009
#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);
// --------------- 4 ---------------
#if 0
const ActorInit En_Recepgirl_InitVars = {
ACTOR_EN_RECEPGIRL,
ACTORCAT_NPC,
FLAGS,
OBJECT_BG,
sizeof(EnRecepgirl),
(ActorFunc)EnRecepgirl_Init,
(ActorFunc)EnRecepgirl_Destroy,
(ActorFunc)EnRecepgirl_Update,
(ActorFunc)EnRecepgirl_Draw,
};
// static InitChainEntry sInitChain[] = {
static InitChainEntry D_80C106C0[] = {
ICHAIN_U8(targetMode, 6, ICHAIN_CONTINUE),
ICHAIN_F32(targetArrowOffset, 1000, ICHAIN_STOP),
};
#endif
// --------------- 5 ---------------
extern InitChainEntry D_80C106C0[];
extern UNK_TYPE D_06001384;
extern UNK_TYPE D_06009890;
extern UNK_TYPE D_0600A280;
// --------------- 6 ---------------
#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Recepgirl/EnRecepgirl_Init.s")
#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Recepgirl/EnRecepgirl_Destroy.s")
#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Recepgirl/func_80C100DC.s")
#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Recepgirl/func_80C10148.s")
#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Recepgirl/func_80C1019C.s")
#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Recepgirl/func_80C10290.s")
#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Recepgirl/func_80C102D4.s")
#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Recepgirl/EnRecepgirl_Update.s")
#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Recepgirl/func_80C10558.s")
#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")
```
It is currently divided into six sections as follows:
1. Description of the actor. This is not present for all actors, (and indeed, is not present here) but gives a short description based on what we know about the actor already. It may be inaccurate, so feel free to correct it after you understand the actor better, or add it. It currently has the form
```C
/*
* File: z_en_recepgirl.c
* Overlay: ovl_En_Recepgirl
* Description: Mayor's receptionist
*/
```
2. Specific `include`s and `define`s for the actor. You may need to add more header files, but otherwise this section is unlikely to change.
3. These are prototypes for the "main four" functions that almost every actor has. You add more functions here if they need to be declared above their first use.
5. `if`'d-out section containing the `InitVars` and a few other common pieces of data. This can be ignored until we import the data.
4. A set of `extern`s. These refer to the data in the previous section, and, data that comes from other files, usually in the actor's corresponding object file. The latter point to addresses in the ROM where assets are stored (usually collision data, animations or display lists). Once the corresponding object files have been decompiled, these will simply be replaced by including the object file (see [Object Decompilation](object_decomp.md) for how this process works). These symbols have been automatically extracted from the MIPS code. There may turn out to be some that were not caught by the script, in which case they need to be placed in the file called `undefined_syms.txt` in the root directory of the project. Ask in Discord for how to do this: it is simple, but rare enough to not be worth covering here.
6. List of functions. Each `#pragma GLOBAL_ASM` is letting the compiler use the corresponding assembly file while we do not have decompiled C code for that function. The majority of the decompilation work is converting these functions into C that it looks like a human wrote.
## Header file
The header file looks like this at the moment:
```C
#ifndef Z_EN_RECEPGIRL_H
#define Z_EN_RECEPGIRL_H
#include "global.h"
struct EnRecepgirl;
typedef void (*EnRecepgirlActionFunc)(struct EnRecepgirl*, GlobalContext*);
typedef struct EnRecepgirl {
/* 0x0000 */ Actor actor;
/* 0x0144 */ char unk_144[0x164];
/* 0x02A8 */ EnRecepgirlActionFunc actionFunc;
/* 0x02AC */ char unk_2AC[0x8];
} EnRecepgirl; // size = 0x2B4
extern const ActorInit En_Recepgirl_InitVars;
#endif // Z_EN_RECEPGIRL_H
```
The struct currently contains a variable that is the `Actor` struct, which all actors use one way or another, plus other items. Currently we don't know what most of those items are, so we have arrays of chars as padding instead, just so the struct is the right size. As we understand the actor better, we will be able to gradually replace this padding with the actual variables that the actor uses.
The header file is also used to declare structs and other information about the actor that is needed by other files (e.g. by other actors): one can simply `#include` the header rather than `extern`ing it.
## Order of decompilation
The general rule for order of decompilation is
- Start with `Init`, because it usually contains the most information about the structure of the actor. You can also do `Destroy`, which is generally simpler than `Init`.
- 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);
```
- 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.)
- After you've run out, do `Update`. This usually provides the rest of the function tree, apart from possibly some draw functions.
- Finally, do the draw functions.
The above is a rough ordering for the beginner. As you become more experienced, you can deviate from this scheme, but the general principle remains that you should work on functions that you already know something about. (This is why it's good to start on actors: they are self-contained, we already know a lot about some of the functions, and the function flow tends to be both logical and provide information about every function.)
## Data
![Fresh actor data](images/fresh_actor_data.png)
Associated to each actor is a `.data` file, containing data that the actor uses. This ranges from spawn positions, to animation information, to even assets that we have to extract from the ROM. Since the structure of the data is very inconsistent between actors, automatic importing has been very limited, so the vast majority must be done manually.
There are two ways of transfering the data into an actor: we can either
- import it all naively as words (`s32`s), which will still allow it to compile, and sort out the actual types later, or
- we can extern each piece of data as we come across it, and come back to it later when we have a better idea of what it is.
We will concentrate on the second here; the other is covered in [the document about data](data.md). Thankfully this means we essentially don't have to do anything to the data yet. Nevertheless, it is often quite helpful to copy over at least some of the data and leave it commented out for later replacement. *Data must go in the same order as in the data file, and data is "all or nothing": you cannot only import some of it*.
**WARNING** The way in which the data was extracted from the ROM means that there are sometimes "fake symbols" in the data, which have to be removed to avoid confusing the compiler. Thankfully it will turn out that this is not the case here.
(Sometimes it is useful to import the data in the middle of doing functions: you just have to choose an appropriate moment.)
Some actors also have a `.bss` file. This is just data that is initialised to 0, and can be imported immediately once you know what type it is, by declaring it without giving it a value. (bss is a significant problem for code files, but not *usually* for actors.)
## Init
The Init function sets up the various components of the actor when it is first loaded. It is hence usually very useful for finding out what is in the actor struct, and so we usually start with it. (Some people like starting with Destroy, which is usually shorter and simpler, but gives some basic information about the actor, but Init is probably best for beginners.)
### mips2c
The first stage of decompilation is done by a program called mips_to_c, often referred to as mips2c, which constructs a C interpretation of the assembly code based on reading it very literally. This means that considerable cleanup will be required to turn it into something that firstly compiles at all, and secondly looks like a human wrote it, let alone a Zelda developer from the late '90s.
The web version of mips2c can be found [here](https://simonsoftware.se/other/mips_to_c.py). This was [covered in the OoT tutorial](https://github.com/zeldaret/oot/blob/master/docs/tutorial/beginning_decomp.md). We shall instead use the repository. Clone [the mips_to_c repository](https://github.com/matt-kempster/mips_to_c) into a separate directory (we will assume on the same level as the `mm/` directory). Since it's Python, we don't have to do any compilation or anything in the mips_to_c directory.
Since the actor depends on the rest of the codebase, we can't expect to get much intelligible out of mips2c without giving it some context. We make this using a Python script in the `tools` directory called `m2ctx.py`, so run
```
$ ./tools/m2ctx.py <path_to_c_file>
```
from the main directory of the repository. In this case, the C file is `src/overlays/actors/ovl_En_Recepgirl/z_en_recepgirl.c`. This generates a file called `ctx.c` in the main directory of the repository.
To get mips_to_c to decompile a function, the bare minimum is to run
```
$ ../mips_to_c/mips_to_c.py <path_to_function_assembly_file>
```
(from the root directory of `mm`). We can tell mips2c to use the context file we just generated by adding `--context ctx.c`. If we have data, mips2c may be able to assist with that as well.
In this case, we want the assembly file for `EnRecepgirl_Init`. You can copy the path to the file in VSCode or similar, or just tab-complete it once you know the directory structure well enough: it turns out to be `asm/non_matchings/overlays/ovl_En_Recepgirl/EnRecepgirl_Init.s`.
**N.B.** You want the file in `nonmatchings`! the files in the other directories in `asm/` are the *unsplit* asm, which can be used, but is less convenient (you would need to include the rodata, for example, and it will do the whole file at once. This is sometimes useful, but we'll go one function at a time today to keep things simple).
We shall also include the data file, which is located at `data/overlays/ovl_En_Recepgirl/ovl_En_Recepgirl.data.s`. Hence the whole command will be
```
$ ../mips_to_c/mips_to_c.py asm/non_matchings/overlays/ovl_En_Recepgirl/EnRecepgirl_Init.s data/ovl_En_Recepgirl/ovl_En_Recepgirl.data.s --context ctx.c
? func_80C10148(EnRecepgirl *); // extern
extern FlexSkeletonHeader D_06011B60;
static void *D_80C106B0[4] = {(void *)0x600F8F0, (void *)0x600FCF0, (void *)0x60100F0, (void *)0x600FCF0};
static s32 D_80C106C8 = 0;
InitChainEntry D_80C106C0[2]; // unable to generate initializer
void EnRecepgirl_Init(EnRecepgirl *this, GlobalContext *globalCtx) {
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);
phi_s0 = D_80C106B0;
if (D_80C106C8 == 0) {
do {
temp_s0 = phi_s0 + 4;
temp_s0->unk-4 = Lib_SegmentedToVirtual(*phi_s0);
phi_s0 = temp_s0;
} while (temp_s0 != D_80C106C0);
D_80C106C8 = 1;
}
this->unk_2AC = 2;
if (Flags_GetSwitch(globalCtx, (s32) this->actor.params) != 0) {
this->actor.textId = 0x2ADC;
} else {
this->actor.textId = 0x2AD9;
}
func_80C10148(this);
}
```
Comment out the `GLOBAL_ASM` line for `Init`, and paste all of this into the file just underneath it:
```C
[...]
// #pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Recepgirl/EnRecepgirl_Init.s")
? func_80C10148(EnRecepgirl *); // extern
extern FlexSkeletonHeader D_06011B60;
static void *D_80C106B0[4] = {(void *)0x600F8F0, (void *)0x600FCF0, (void *)0x60100F0, (void *)0x600FCF0};
static s32 D_80C106C8 = 0;
InitChainEntry D_80C106C0[2]; // unable to generate initializer
void EnRecepgirl_Init(Actor *thisx, GlobalContext *globalCtx) {
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);
phi_s0 = D_80C106B0;
if (D_80C106C8 == 0) {
do {
temp_s0 = phi_s0 + 4;
temp_s0->unk-4 = Lib_SegmentedToVirtual(*phi_s0);
phi_s0 = temp_s0;
} while (temp_s0 != D_80C106C0);
D_80C106C8 = 1;
}
this->unk_2AC = 2;
if (Flags_GetSwitch(globalCtx, (s32) this->actor.params) != 0) {
this->actor.textId = 0x2ADC;
} else {
this->actor.textId = 0x2AD9;
}
func_80C10148(this);
}
[...]
```
</details>
Typically for all but the simplest functions, there is a lot that needs fixing before we are anywhere near seeing how close we are to the original code. You will notice that mips2c creates a lot of temporary variables. Usually most of these will turn out to not be real, and we need to remove the right ones to get the code to match.
To allow the function to find the variables, we need another correction. Half of this has already been done at the top of the file, where we have
```C
#define THIS ((EnRecepgirl*)thisx)
```
To do the other half, replace the recast at the beginning of the function, before any declarations:
```C
EnRecepgirl* this = THIS;
```
Now everything points to the right place, even though the argument of the function seems inconsistent with the contents.
(Again: this step is only necessary for the "main four" functions, and sometimes functions that are used by these: it relates to how such functions are used outside the actor.)
While we are carrying out initial changes, you can also find-and-replace any instances of `(Actor *) this` by `&this->actor`. The function now looks like this:
```C
? func_80C10148(EnRecepgirl *); // extern
extern FlexSkeletonHeader D_06011B60;
static void *D_80C106B0[4] = {(void *)0x600F8F0, (void *)0x600FCF0, (void *)0x60100F0, (void *)0x600FCF0};
static s32 D_80C106C8 = 0;
InitChainEntry D_80C106C0[2]; // unable to generate initializer
void EnRecepgirl_Init(Actor *thisx, GlobalContext *globalCtx) {
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);
phi_s0 = D_80C106B0;
if (D_80C106C8 == 0) {
do {
temp_s0 = phi_s0 + 4;
temp_s0->unk-4 = Lib_SegmentedToVirtual(*phi_s0);
phi_s0 = temp_s0;
} while (temp_s0 != D_80C106C0);
D_80C106C8 = 1;
}
this->unk_2AC = 2;
if (Flags_GetSwitch(globalCtx, (s32) this->actor.params) != 0) {
this->actor.textId = 0x2ADC;
} else {
this->actor.textId = 0x2AD9;
}
func_80C10148(this);
}
```
### (Not) dealing with Data
For now, we do not want to consider the data that mips2c has kindly imported for us: it will only get in the way when we want to rebuild the file to check for OK (`diff.py` will not care, but `make` will complain if it notices a symbol defined twice, and if some data is included twice the ROM will not match anyway). Therefore, put it in the `#if`'d out section and add some externs with the types:
```C
#if 0
const ActorInit En_Recepgirl_InitVars = {
ACTOR_EN_RECEPGIRL,
ACTORCAT_NPC,
FLAGS,
OBJECT_BG,
sizeof(EnRecepgirl),
(ActorFunc)EnRecepgirl_Init,
(ActorFunc)EnRecepgirl_Destroy,
(ActorFunc)EnRecepgirl_Update,
(ActorFunc)EnRecepgirl_Draw,
};
static void* D_80C106B0[4] = { (void*)0x600F8F0, (void*)0x600FCF0, (void*)0x60100F0, (void*)0x600FCF0 };
// static InitChainEntry sInitChain[] = {
static InitChainEntry D_80C106C0[] = {
ICHAIN_U8(targetMode, 6, ICHAIN_CONTINUE),
ICHAIN_F32(targetArrowOffset, 1000, ICHAIN_STOP),
};
static s32 D_80C106C8 = 0;
#endif
extern void* D_80C106B0[];
extern InitChainEntry D_80C106C0[];
extern s32 D_80C106C8;
```
**N.B.** As is covered in more detail in [the document about data](data.md), the data *must* be declared in the same order in C as it was in the data assembly file: notice that the order in this example is `En_Recepgirl_InitVars`, `D_80C106B0`, `D_80C106C0`, `D_80C106C8`, the same as in `data/ovl_En_Recepgirl/ovl_En_Recepgirl.data.s`.
In the next sections, we shall sort out the various initialisation functions that occur in Init. This actor contains several of the most common ones, but it does not have, for example, a collider. The process is similar to what we discuss below, or you can check the OoT tutorial.
<!-- ### Data and function prototypes
Let's first look at the block of stuff that mips2c has put above the function. This usually contains useful information, but often needs work to make it compile and be in the right place. -->
### Init chains
Almost always, one of the first items in `Init` is a function that looks like
```C
Actor_ProcessInitChain(&this->actor, D_80C106C0);
```
which initialises common properties of actor using an InitChain, which is usually somewhere near the top of the data, in this case in the variable `D_80C106C0`. This is already included in the `#if`'d out data at the top if the file, so we don't have to do anything for now. We can correct the mips2c output for the extern, though: I actually did this when moving the rest of the data in the previous section.
### SkelAnime
This is the combined system that handles actors' skeletons and their animations. It is the other significant part of most actor structs. We see its initialisation in this part of the code:
```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);
phi_s0 = D_80C106B0;
```
An actor with SkelAnime has three structs in the Actor struct that handle it: one called SkelAnime, and two arrays of `Vec3s`, called `jointTable` and `morphTable`. Usually, although not always, they are next to one another.
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,
AnimationHeader* animationSeg, Vec3s* jointTable, Vec3s* morphTable, s32 limbCount);
```
we can read off the types of the various arguments:
- The `SkelAnime` struct is at `this + 0x144`
- The `jointTable` is at `this + 0x188`
- The `morphTable` is at `this + 0x218`
- The number of limbs is `0x18 = 24` (we use dec for the number of limbs)
- Because of how SkelAnime works, this means that the `jointTable` and `morphTable` both have `24` elements
Looking in `z64animation.h`, we find that `SkelAnime` has size `0x44`, and looking in `z64math.h`, that `Vec3s` has size `0x6`. Since ` 0x144 + 0x44 = 0x188 `, `jointTable` is immediately after the `SkelAnime`, and since `0x188 + 0x6 * 0x18 = 0x218`, `morphTable` is immediately after the `jointTable`. Finally, `0x218 + 0x6 * 0x18 = 0x2A8`, and we have filled all the space between the `actor` and `actionFunc`. Therefore the struct now looks like
```C
typedef struct EnRecepgirl {
/* 0x0000 */ Actor actor;
/* 0x0144 */ SkelAnime skelAnime;
/* 0x0188 */ Vec3s jointTable[24];
/* 0x0218 */ Vec3s morphTable[24];
/* 0x02A8 */ EnRecepgirlActionFunc actionFunc;
/* 0x02AC */ char unk_2AC[0x8];
} EnRecepgirl; // size = 0x2B4
```
The last information we get from the SkelAnime function is the types of two of the externed symbols: `D_06011B60` is a `FlexSkeletonHeader`, and `D_06009890` is an `AnimationHeader`. So we can change/add these at the top of the C file:
```C
extern InitChainEntry D_80C106C0[];
extern UNK_TYPE D_06001384;
extern AnimationHeader D_06009890;
extern UNK_TYPE D_0600A280;
extern FlexSkeletonHeader D_06011B60;
```
As with the data, these externed symbols should be kept in increasing address order.
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);
```
note that `this->jointTable` and `this->morphTable` are arrays, so are already effectively pointers and don't need a `&`.
### More struct variables: a brief detour into reading some assembly
This function also gives us information about other things in the struct. The only other reference to `this` (rather than `this->actor` or similar) is in
```C
this->unk_2AC = 2;
```
This doesn't tell us much except that at `this + 0x2AC` is a number of some kind. What sort of number? For that we will have to look in the assembly code. This will probably look quite intimidating the first time, but it's usually not too bad if you use functions as signposts: IDO will never change the order of function calls, and tends to keep code between functions in roughly the same place, so you can usually guess where you are.
In this case, we are looking for `this + 0x2AC`. `0x2AC` is not a very common number, so hopefully the only mention of it is in referring to this struct variable. Indeed, if we search the file, we find that the only instruction mentioning `0x2AC` is here:
```mips
/* 0000B0 80C10080 24090002 */ addiu $t1, $zero, 2
/* 0000B4 80C10084 A24902AC */ sb $t1, 0x2ac($s2)
```
`addiu` ("add unsigned immediate") adds the last two things and puts the result in the register in the first position. So this says `$t1 = 0 + 2`. The next instruction, `sb` ("store byte") puts the value in the register in the first position in the memory location in the second, which in this case says `$s2 + 0x2ac = $t1`. We can go and find out what is in `$s2` is: it is set *all* the way at the top of the function, in this line:
```mips
/* 000008 80C0FFD8 00809025 */ move $s2, $a0
```
This simply copies the contents of the second register into the first one. In this case, it is copying the contents of the function's first argument into `$s2` (because it wants to use it later, and the `$a` registers are assumed to be cleared after a function call). In this case, the first argument is a pointer to `this` (well, `thisx`, but the struct starts with an `Actor`, so it's the same address). So line `B4` of the asm really is saving `2` into the memory location `this + 0x2AC`.
Anyway, this tells us that the variable is a byte of some kind, so `s8` or `u8`: if it was an `s16/u16` it would have said `sh`, and if it was an `s32/u32` it would have said `sw`. Unfortunately this is all we can determine from this function: MIPS does not have separate instructions for saving signed and unsigned bytes.
At this point you have two options: guess based on statistics/heuristics, or go and look in the other functions in the actor to find out more information. The useful statistic here is that `u8` is far more common than `s8`, but let's look in the other functions, since we're pretty confident after finding `0x2ac` so easily in `Init`. So, let us grep the actor's assembly folder:
```
$ grep -r '0x2ac' asm/non_matchings/overlays/ovl_En_Recepgirl/
asm/non_matchings/overlays/ovl_En_Recepgirl/EnRecepgirl_Draw.s:/* 00065C 80C1062C 921902AC */ lbu $t9, 0x2ac($s0)
asm/non_matchings/overlays/ovl_En_Recepgirl/func_80C100DC.s:/* 000114 80C100E4 908202AC */ lbu $v0, 0x2ac($a0)
asm/non_matchings/overlays/ovl_En_Recepgirl/func_80C100DC.s:/* 00012C 80C100FC A08E02AC */ sb $t6, 0x2ac($a0)
asm/non_matchings/overlays/ovl_En_Recepgirl/func_80C100DC.s:/* 000134 80C10104 A08002AC */ sb $zero, 0x2ac($a0)
asm/non_matchings/overlays/ovl_En_Recepgirl/func_80C100DC.s:/* 00015C 80C1012C 909802AC */ lbu $t8, 0x2ac($a0)
asm/non_matchings/overlays/ovl_En_Recepgirl/func_80C100DC.s:/* 000164 80C10134 A09902AC */ sb $t9, 0x2ac($a0)
asm/non_matchings/overlays/ovl_En_Recepgirl/EnRecepgirl_Init.s:/* 0000B4 80C10084 A24902AC */ sb $t1, 0x2ac($s2)
```
in which we clearly see `lbu` ("load byte unsigned"), and hence this variable really is a `u8`. Hence we can add this to the actor struct too:
```C
typedef struct EnRecepgirl {
/* 0x0000 */ Actor actor;
/* 0x0144 */ SkelAnime skelAnime;
/* 0x0188 */ Vec3s jointTable[24];
/* 0x0218 */ Vec3s morphTable[24];
/* 0x02A8 */ EnRecepgirlActionFunc actionFunc;
/* 0x02AC */ u8 unk_2AC;
/* 0x02AD */ char unk_2AD[0x7];
} EnRecepgirl; // size = 0x2B4
```
You might think that was a lot of work for one variable, but it's pretty quick when you know what to do. Obviously this would be more difficult with a more common number, but it's often still worth trying.
Removing some of the declarations for data that we have accounted for, the function now looks like this:
```C
? func_80C10148(EnRecepgirl *); // extern
void EnRecepgirl_Init(Actor *thisx, GlobalContext *globalCtx) {
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);
phi_s0 = D_80C106B0;
if (D_80C106C8 == 0) {
do {
temp_s0 = phi_s0 + 4;
temp_s0->unk-4 = Lib_SegmentedToVirtual(*phi_s0);
phi_s0 = temp_s0;
} while (temp_s0 != D_80C106C0);
D_80C106C8 = 1;
}
this->unk_2AC = 2;
if (Flags_GetSwitch(globalCtx, (s32) this->actor.params) != 0) {
this->actor.textId = 0x2ADC;
} else {
this->actor.textId = 0x2AD9;
}
func_80C10148(this);
}
```
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`.
### Functions called
One minor problem is what `func_80C10148` is: C needs a prototype to compile it properly. mips2c has offered us `? func_80C10148(EnRecepgirl *); // extern`, but this is obviously incomplete: there's no `?` type in C! We shall guess for now that this function returns `void`, for two reasons:
1. It's not used as a condition in a conditional or anything
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*)`.
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 func_80C10148(EnRecepgirl* this);
```
(we usually leave a blank line after the main four, and put all further declarations in address order).
### Loops
Loops are often some of the hardest things to decompile, because there are many ways to write a loop, only some of which will generate the same assembly. mips2c has had a go at the one in this function, but it usually struggles with loops: don't expect it to get a loop correct, well, at all.
The code in question is
```C
void **temp_s0;
void **phi_s0;
[...]
phi_s0 = D_80C106B0;
if (D_80C106C8 == 0) {
do {
temp_s0 = phi_s0 + 4;
temp_s0->unk-4 = Lib_SegmentedToVirtual(*phi_s0);
phi_s0 = temp_s0;
} while (temp_s0 != D_80C106C0);
D_80C106C8 = 1;
}
```
`D_80C106B0` is the array that mips2c has declared above the function, a set of 8-digit hex numbers starting `0x06`. These are likely to be *segmented pointers*, but this is not a very useful piece of information yet. `D_80C106C0` is the InitChain, though, and it seems pretty unlikely that it would be seriously involved in any sort of loop. Indeed, if you tried to compile this now, you would get an error:
```
cfe: Error: src/overlays/actors/ovl_En_Recepgirl/z_en_recepgirl.c, line 61: Unacceptable operand of == or !=
} while (temp_s0 != D_80C106C0);
-------------------------^
```
so this can't possibly be right.
So what on earth is this loop doing? Probably the best thing to do is manually unroll it and see what it's doing each time.
0. `phi_s0 = D_80C106B0`, aka `&D_80C106B0[0]`, to `temp_s0 = D_80C106B0 + 4`, i.e. `&D_80C106B0[1]`. But then `temp_s0->unk-4` is 4 backwards from `&D_80C106B0[1]`, which is back at `&D_80C106B0[0]`; the `->` means to look at what is at this address, so `temp_s0->unk-4` is `D_80C106B0[0]`. Equally, `*phi_s0` is the thing at `&D_80C106B0[0]`, i.e. `D_80C106B0[0]`. So the actual thing the first pass does is
```C
D_80C106B0[0] = Lib_SegmentedToVirtual(D_80C106B0[0]);
```
it then proceeds to set `phi_s0 = &D_80C106B0[1]` for the next iteration.
1. We go through the same reasoning and find the inside of the loop is
```C
temp_s0 = &D_80C106B0[2];
D_80C106B0[1] = Lib_SegmentedToVirtual(D_80C106B0[1]);
phi_s0 = &D_80C106B0[2];
```
2.
```C
temp_s0 = &D_80C106B0[3];
D_80C106B0[2] = Lib_SegmentedToVirtual(D_80C106B0[2]);
phi_s0 = &D_80C106B0[3];
```
3.
```C
temp_s0 = &D_80C106B0[4];
D_80C106B0[3] = Lib_SegmentedToVirtual(D_80C106B0[3]);
phi_s0 = &D_80C106B0[4];
```
But now, `&D_80C106B0[4] = D_80C106B0 + 4 * 4 = D_80C106B0 + 0x10`, and `0x10` after this array's starting address is `D_80C106C0`, i.e. the InitChhain. Hence at this point the looping ends.
So what this loop actually does is run `Lib_SegmentedToVirtual` on each element of the array `D_80C106B0`.
At this point, I confess that I guessed what this loop does, and rewrote it how I would have written it, namely how one usually iterates over an array:
```C
s32 i;
[...]
for (i = 0; i < 4; i++) {
D_80C106B0[i] = Lib_SegmentedToVirtual(D_80C106B0[i]);
}
```
This is a dangerous game, since there is no guarantee that what you think is the right way to write something bears any relation to either what the original was like, or more importantly, what will give the same codegen as the original. This is a significant leap, since the original appears to be using a pointer iterator!
However, this is certainly at least equivalent to the original (or at least, to what mips2c gave us: it's not infallible): we can be certain of this because we wrote the thing out in its entirety to understand it! This also allows us to eliminate one of the temps: you'll find with even simple loops mips2c will usually make two temps for the loop variable.
Hence we end up with
```C
void func_80C10148(EnRecepgirl* this);
[...]
void EnRecepgirl_Init(Actor *thisx, GlobalContext *globalCtx) {
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);
if (D_80C106C8 == 0) {
for (i = 0; i < 4; i++) {
D_80C106B0[i] = Lib_SegmentedToVirtual(D_80C106B0[i]);
}
D_80C106C8 = 1;
}
this->unk_2AC = 2;
if (Flags_GetSwitch(globalCtx, this->actor.params)) {
this->actor.textId = 0x2ADC;
} else {
this->actor.textId = 0x2AD9;
}
func_80C10148(this);
}
```
as our first guess. This doesn't look unreasonable... the question is, does it match?
## Diff
Once preliminary cleanup and struct filling is done, most time spent matching functions is done by comparing the original code with the code you have compiled. This is aided by a program called `diff.py`.
In order to use `diff.py` with the symbol names, we need a copy of the code to compare against. In MM this is done as part of `make init`, and you can regenerate the `expected` directory (which is simply a known-good copy of `build` directory) by running `make diff-init`, which will check for an OK ROM and copy the build directory over. (Of course you need an OK ROM to do this; worst-case, you can checkout master and do a complete rebuild to get it). (You need to remake `expected` if you want to diff a function you have renamed: `diff.py` looks in the mapfiles for the function name, which won't work if the name has changed!)
Now, we run diff on the function name: in the main directory,
```
$ ./diff.py -mwo3 EnRecepgirl_Init
```
(To see what these arguments do, run it with `./diff.py -h` or look in the scripts documentation.)
![FeelsOKMan completely white diff](images/EnRecepgirl_Init_diff_matching.png)
And err, well, everything is white, so it matches. Whoops. Guess we'll cover `diff.py` properly next time! (Notice that even though the diff is completely white, there are some differences in the `%hi`s and `%lo`s that access data, because it is now accessed with a relative address rather than an absolute one. If you have the data in the file in the right order, this shouldn't matter.)
And with that, we have successfully matched our first function.
**N.B** Notice that we don't yet have much idea of what this code actually does: this should be clarified by going through the rest of the actor's functions, which is discussed in the next document.
Next: [Other functions in the actor](other_functions.md)

56
docs/tutorial/contents.md Normal file
View File

@ -0,0 +1,56 @@
# Getting started
## [Introduction to decomp](introduction.md)
- What we are doing
- Structure of the code
## Pre-decompilation
- Building the repo (follow the instructions in the [README.md](../../README.md))
- Most of us use VSCode. Some useful information is [here](vscode.md).
<!-- Feel free to document Emacs/Vi/Sublime/whatever if you're familiar with them -->
- Choosing a first actor (You want something small that has simple interactions with the environment. A simple NPC can also work, and is what we will use as an illustration for most of the tutorial. There is a collection of actors we think are suitable for beginners on the spreadsheet or Trello)
## Decompilation
- [Begining decompilation: order, Init and the actor struct](beginning_decomp.md)
- Order of decompilation
- Init and common actor features
- Initchains
- Actors and dynapoly actors
- Colliders
- Skelanime
- [The rest of the functions in the actor](other_functions.md)
- Order of decompilation
- Action Functions and other functions
- [Draw functions](draw_functions.md)
- [Data, migration and non-migration](data.md)
- Importing the data: early and late
- Segmented pointers
- Fake symbols
- Inlining
## [Object Decompilation](object_decomp.md) (TODO)
- Object files
- How we decompile objects
## After Decompilation
- See the [CONTRIBUTING.md](../../CONTRIBUTING.md) for most of the details for submitting PRs. Remember to format again after making adjustments from reviews!
- More information about specific preparations is in [this document](merging.md).
## Appendices
- [Types, Structs and Padding](types_structs_padding.md) (a miscellany of useful stuff)
- [Advanced control flow](advanced_control_flow.md) (an example of a more complex function which mips2c is not so good at)
- [Using the diff script and the permuter](diff_and_permuter.md) (using the diff script and the permuter to match something)
- control flow (branches) -> instruction ordering -> register allocation -> stack
- [Helper scripts] TODO: link when merged
To be written, maybe
- How we use git and GitHub
- Some notes on the basic structure of N64 MIPS
- Glossary
- Conventions

171
docs/tutorial/data.md Normal file
View File

@ -0,0 +1,171 @@
# Data
Up: [Contents](contents.md)
Previous: [Draw functions](draw_functions.md)
## Table of Contents
- [Data first](#data-first)
- [Extern and data last](#extern-and-data-last)
- [Segmented pointers](#segmented-pointers)
- [Fake symbols](#fake-symbols)
- [Inlining](#inlining)
Each actor's data is stored in a separate file. EnRecepgirl's data is in `data/overlays/ovl_En_Recepgirl/ovl_En_Recepgirl.data.s`, for example. At some point in the decompilation process we need to convert this raw data into recognisable information for the C to use.
There are two main ways to do this: either
1. import the data first and type it later, or
2. wait until the data appears in functions, extern it, then import it at the end
Sometimes something between these two is appropriate: wait until the largest or strangest bits of data appear in functions, get some typing information out of that, and then import it, but for now, let's stick to both of these.
Both approaches have their advantages and disadvantages.
## Data first
This way is good for smaller actors with little data. The OoT tutorial [covers this in plenty of detail](https://github.com/zeldaret/oot/blob/master/docs/tutorial/data.md), and the process in MM is essentially identical, so we won't go over it here.
## Extern and data last
Externing is explained in detail in the document about the [Init function](beginning_decomp.md). To summarize, every time a `D_address` appears that is in the data file, we put a
```C
extern UNK_TYPE D_address;
```
at the top of the file, in the same order that the data appears in the data file. We can also give it a type if we know what the type actually is (e.g. for colliders, initchains, etc.), and convert the actual data and place it commented-out under the corresponding line. This means we don't have to do everything at once at the end.
Once we have decompiled enough things to know what the data is, we can import it. The advantage of doing it this way is we should know what type everything is already: in our work on EnRecepgirl, for example, we ended up with the following data at the top of the file
```C
#if 0
const ActorInit En_Recepgirl_InitVars = {
ACTOR_EN_RECEPGIRL,
ACTORCAT_NPC,
FLAGS,
OBJECT_BG,
sizeof(EnRecepgirl),
(ActorFunc)EnRecepgirl_Init,
(ActorFunc)EnRecepgirl_Destroy,
(ActorFunc)EnRecepgirl_Update,
(ActorFunc)EnRecepgirl_Draw,
};
static void* D_80C106B0[4] = { (void*)0x600F8F0, (void*)0x600FCF0, (void*)0x60100F0, (void*)0x600FCF0 };
// static InitChainEntry sInitChain[] = {
static InitChainEntry D_80C106C0[] = {
ICHAIN_U8(targetMode, 6, ICHAIN_CONTINUE),
ICHAIN_F32(targetArrowOffset, 1000, ICHAIN_STOP),
};
static s32 D_80C106C8 = 0;
#endif
```
and the main thing we need to understand is `D_80C106B0`
*Before doing anything else, make sure `make` gives `OK`.*
First, we tell the compiler to ignore the original data file. To do this, open the file called `spec` in the main directory of the repository, and search for the actor name. You will find a section that looks like
```
beginseg
name "ovl_En_Recepgirl"
compress
include "build/src/overlays/actors/ovl_En_Recepgirl/z_en_recepgirl.o"
include "build/data/ovl_En_Recepgirl/ovl_En_Recepgirl.data.o"
include "build/data/ovl_En_Recepgirl/ovl_En_Recepgirl.reloc.o"
endseg
```
We will eventually remove both of the bottom two lines and replace them with our own reloc file, but for now, just comment out the data line:
```
beginseg
name "ovl_En_Recepgirl"
compress
include "build/src/overlays/actors/ovl_En_Recepgirl/z_en_recepgirl.o"
//include "build/data/ovl_En_Recepgirl/ovl_En_Recepgirl.data.o"
include "build/data/ovl_En_Recepgirl/ovl_En_Recepgirl.reloc.o"
endseg
```
Next remove all the externs, and uncomment their corresponding commented data:
```C
const ActorInit En_Recepgirl_InitVars = {
ACTOR_EN_RECEPGIRL,
ACTORCAT_NPC,
FLAGS,
OBJECT_BG,
sizeof(EnRecepgirl),
(ActorFunc)EnRecepgirl_Init,
(ActorFunc)EnRecepgirl_Destroy,
(ActorFunc)EnRecepgirl_Update,
(ActorFunc)EnRecepgirl_Draw,
};
static void* D_80C106B0[4] = { (void*)0x600F8F0, (void*)0x600FCF0, (void*)0x60100F0, (void*)0x600FCF0 };
// static InitChainEntry sInitChain[] = {
static InitChainEntry D_80C106C0[] = {
ICHAIN_U8(targetMode, 6, ICHAIN_CONTINUE),
ICHAIN_F32(targetArrowOffset, 1000, ICHAIN_STOP),
};
static s32 D_80C106C8 = 0;
```
That should be everything, and we should now be able to `make` without the data file with no issues.
## Segmented pointers
The game has a convenient system that allows it to sometimes effectively use offsets into a file instead of raw memory addresses to reference things. This is done by setting a file address to a *segment*. A segmented address is of the form `0x0XYYYYYY`, where `X` is the segment number. There are 16 available segments, and actors always set segment 6 to their object file, which is a file containing assets (skeleton, animations, textures, etc.) that they use. This is what all those `D_06...` are, and it is also what the entries in `D_80C106B0` are: they are currently raw numbers instead of symbols, though, and we would like to replace them.
There is an obvious problem here, which is that is that these symbols have to be defined *somewhere*, or the linker will complain (indeed, if we change the ones in the array to `D_...`, even if we extern them, we get
```
mips-linux-gnu-ld: build/src/overlays/actors/ovl_En_Recepgirl/z_en_recepgirl.o:(.data+0x20): undefined reference to `D_0600F8F0'
````
As we'd expect, of course: we didn't fulfil our promise that they were defined elsewhere.)
This is mitigated by use of the file `undefined_syms.txt`, which feeds the linker the raw addresses to use as the symbol definitions. If you find a segmented address that is not in already externed, `extern` it at the top of the file and add it to the actor's section in undefined_syms:
```C
extern void* D_0600F8F0;
extern void* D_0600FCF0;
extern void* D_060100F0;
extern void* D_0600FCF0;
static void* D_80C106B0[4] = { &D_0600F8F0, &D_0600FCF0, &D_060100F0, &D_0600FCF0 };
```
and in undefined_syms.txt:
```
// ovl_En_Recepgirl
D_06000968 = 0x06000968;
D_06001384 = 0x06001384;
D_06009890 = 0x06009890;
D_0600A280 = 0x0600A280;
D_0600AD98 = 0x0600AD98;
D_0600F8F0 = 0x0600F8F0;
D_0600FCF0 = 0x0600FCF0;
D_060100F0 = 0x060100F0;
D_06011B60 = 0x06011B60;
```
We will come back and name these later when we do the object.
## Fake symbols
Some symbols in the data have been decompiled wrongly, being incorrectly separated from the previous symbol due to how it was accessed by the actor's functions. However, most of these have now been fixed. Some more detail is given in [Types, structs and padding](types_structs_padding.md) If you are unsure, ask!
## Inlining
After the file is finished, it is possible to move some static data into functions. This requires that:
1. The data is used in only one function
2. The ordering of the data can be maintained
Additionally, we prefer to keep larger data (more than a line or two) out of functions anyway.
# Finally: .bss
A .bss contains data that is uninitialised (actually initialised to `0`). For most actors all you need to do is declare it at the top of the actor file without giving it a value, once you find out what type it is. In `code`, it's much more of a problem.
Next: [Documenting](documenting.md)

View File

@ -0,0 +1,145 @@
# `diff.py` and the permuter
This document is intended as a step-by-step demonstration of matching a reasonably complex function using the diff script `diff.py` and the decomp permuter, both included in the repo. For general information on both see [the tools documentation](../tools.md).
Until such time as someone finds a suitable function, you can look at the OoT tutorial: [here for diff.py](https://github.com/zeldaret/oot/blob/master/docs/tutorial/beginning_decomp.md#diff) and [here for the permuter](https://github.com/zeldaret/oot/blob/master/docs/tutorial/other_functions.md#the-permuter).
<!--
The following is left here to give a rough idea of what the diff script doc could look like.
This gives the following:
<details>
<summary>
Large image, click to show.
</summary>
![Init diff 1](images/init_diff1.png)
</details>
The code we want is on the left, current code on the right. To spot where the function ends, either look for where stuff is added and subtracted from the stack pointer in successive lines, or for a
```MIPS
jr ra
nop
```
The colours mean the following:
- White/gray is matching lines
- Red is lines missing
- Green is extra lines
- Blue denotes significant differences in instructions, be they just numerical ones, or whole instructions
- Yellow/Gold denotes that instructions are correct but register usage is wrong
- Other colors are used to distinguish incorrectly used registers or stack variables, to make it easy to follow where they are used.
- The colored arrows denote branching. An arrow of one color on the right leads to the arrow of the same color on the left.
Obviously we want to make the whole thing white. This is the tricky bit: you have to have the imagination to try different things until you get the diff to match. You learn these with experience.
Generally, the order of what to fix should be:
1. Control flow (conditionals, where branches go)
2. Instruction ordering and type (functions cannot change order, which is a useful indicator)
3. Regalloc (register allocation) differences
4. Stack differences
(It is this order because the things that happen earlier can influence the things that happen later.)
You can keep the diff open in the terminal, and it will refresh when the C file (but not the H file) is changed with these settings.
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) {
EnJj* this = THIS;
s32 sp4C;
s16 temp_v0;
sp4C = 0;
Actor_ProcessInitChain(&this->dyna.actor, D_80A88CE0);
ActorShape_Init(&this->dyna.actor.shape, 0.0f, NULL, 0.0f);
temp_v0 = this->dyna.actor.params;
switch (temp_v0) {
case -1:
SkelAnime_InitFlex(globalCtx, &this->skelAnime, &D_0600B9A8, &D_06001F4C, this->jointTable,
this->morphTable, 22);
Animation_PlayLoop(&this->skelAnime, &D_06001F4C);
this->unk_30A = 0;
this->unk_30E = 0;
this->unk_30F = 0;
this->unk_310 = 0;
this->unk_311 = 0;
if ((gSaveContext.eventChkInf[3] & 0x400) != 0) {
func_80A87800(this, func_80A87BEC);
} else {
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,
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);
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;
// 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);
this->dyna.actor.update = func_80A87F44;
this->dyna.actor.draw = NULL;
Actor_SetScale(&this->dyna.actor, 0.087f);
break;
case 1:
DynaPolyActor_Init(&this->dyna, 0);
CollisionHeader_GetVirtual(&D_0600BA8C, &sp4C);
this->dyna.bgId =
DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, sp4C);
this->dyna.actor.update = func_80A87F44;
this->dyna.actor.draw = NULL;
Actor_SetScale(&this->dyna.actor, 0.087f);
break;
}
}
```
we see that the diff is nearly correct (note that `-3` lets you compare current with previous):
<details>
<summary>
Large image, click to show.
</summary>
![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
```C
void EnJj_Init(Actor* thisx, GlobalContext* globalCtx2) {
GlobalContext* globalCtx = globalCtx2;
EnJj* this = THIS;
...
```
It turns out that this is enough to completely fix the diff:
![Init diff 2](images/init_diff3top.png)
(last two edits, only top shown for brevity)
Everything *looks* fine, but we only know for sure when we run `make`. Thankfully doing so gives
```
zelda_ocarina_mq_dbg.z64: OK
```
which is either a sense of triumph or relief depending on how long you've spent on a function. -->

View File

@ -0,0 +1,36 @@
# Disassembly quirks
As MM's disassembly is automatic, there are certain unique problems it has.
## Renaming functions and variables
A function must be renamed in `tools/disasm/functions.txt` in addition to the source code, for the disassembler to know what to call the symbol at that address when it sees it.
Variables must be renamed in `tools/disasm/variables.txt`. It may also be necessary to change their type, count or size to stop the disassembler misusing them.
You can avoid having to redisassemble every time by running `rename_global_asm.py`, which will rename the individual functions' assembly files in `asm/nonmatchings/` to the name of the function they contain.
## Fake and incorrect symbols
TODO
## Resplitting a file
The files `boot` and `code` are each divided up into dozens of separate files, that are all joined together into one text, data, rodata and bss section when building the ROM. As such, it has been necessary to guess where the file boundaries are, and not every file contains the correct functions or the correct data (rodata is mostly the exception since it is automatically split).
To change a split for a file, find its entry in `tools/disasm/files.txt`, and change or create entries to accurately reflect where the file(s) should start. For example, it was found that the last function in `z_nmi_buff.c` had nothing to do with the rest, so it should be split into its own file. Looking up the address of the last function, it was found to be at `0x8010C1B0`, so adding the line:
```diff
0x8010C0C0 : "z_nmi_buff",
+++ 0x8010C1B0 : "code_8010C1B0",
0x8010C230 : "z_olib",
```
to the file will extract it correctly as a separate file. It also is necessary to make a new C file and move the `GLOBAL_ASM` declaration into it.
Unfortunately you essentially have to redisassemble after telling the disassembler to resplit a file.
##

View File

@ -0,0 +1,601 @@
# Documenting
Up: [Contents](contents.md)
Previous: [Data](data.md)
Decompilation is only the first step: since the point of this project is to understand the game better than ever before, the code needs documentation. In this document, we will go through the basic stuff that it's good to do for any actor: we will not try to understand every single thing the actor does in full detail, but try to name the functions and variables usefully for a full documentation pass later to take advantage of.
It is helpful to document the functions and variables in the actor before you Pull Request it. The aim is to provide code that is sufficiently clear to be self-documenting, but it is worth leaving a comment on anything you find obscure or confusing. (Pull Request reviews will let you know if you are leaving too many comments.) Useful things to do documentation-wise:
- Name all (or most) of the functions.
- Name all the variables in the actor struct.
- Create enums for params, and any other numbers that would benefit from that sort of clarity.
You can test things using the practice rom for a retail version (watches and memory view is especially helpful), as well as the generated rom with Project 64 and something like Spectrum.
If you want to use `diff.py` after renaming anything, particularly functions, remember to rerun `make diff-init` so it can use the correct symbols.
Finally, *if you are not sure what something does, either ask or leave it unnamed: it will be less confusing later if things are unnamed than if they are wrongly named*
## Renaming things
Because MM needs to regenerate the assembly code, it is necessary to tell the disassembler the names of functions and variables, so it knows what symbols to assign in the code. This is done via `functions.txt` and `variables.txt`. The best way to rename functions and symbols is via global rename in an editor like VSCode. The next best way is to run `tools/rename_sym.sh`. You should be careful with this script: it has no error-checking!
Renaming symbols in theory requires re-disassembly. This can often be avoided in the case of functions by running `tools/rename_global_asm.py`, which will rename any individual functions' assembly files with the wrong names, so that the `GLOBAL_ASM`s can spot them. Renaming variables *may* require redisassembly (and if fake symbols are removed, it *will*).
## EnRecepgirl
Currently, the file looks like this:
<details>
<summary>
Large code block, click to show
</summary>
```C
#include "z_en_recepgirl.h"
#define FLAGS 0x00000009
#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 func_80C10148(EnRecepgirl* this);
void func_80C1019C(EnRecepgirl* this, GlobalContext* globalCtx);
void func_80C10290(EnRecepgirl* this);
void func_80C102D4(EnRecepgirl * this, GlobalContext * globalCtx);
const ActorInit En_Recepgirl_InitVars = {
ACTOR_EN_RECEPGIRL,
ACTORCAT_NPC,
FLAGS,
OBJECT_BG,
sizeof(EnRecepgirl),
(ActorFunc)EnRecepgirl_Init,
(ActorFunc)EnRecepgirl_Destroy,
(ActorFunc)EnRecepgirl_Update,
(ActorFunc)EnRecepgirl_Draw,
};
extern void* D_0600F8F0;
extern void* D_0600FCF0;
extern void* D_060100F0;
static void* D_80C106B0[4] = { &D_0600F8F0, &D_0600FCF0, &D_060100F0, &D_0600FCF0 };
// static InitChainEntry sInitChain[] = {
static InitChainEntry D_80C106C0[] = {
ICHAIN_U8(targetMode, 6, ICHAIN_CONTINUE),
ICHAIN_F32(targetArrowOffset, 1000, ICHAIN_STOP),
};
static s32 D_80C106C8 = 0;
extern AnimationHeader D_06000968;
extern AnimationHeader D_06001384;
extern AnimationHeader D_06009890;
extern AnimationHeader D_0600A280;
extern AnimationHeader D_0600AD98;
extern FlexSkeletonHeader D_06011B60;
// #pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Recepgirl/EnRecepgirl_Init.s")
void EnRecepgirl_Init(Actor* thisx, GlobalContext* globalCtx) {
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, &D_06011B60, &D_06009890, this->jointTable, this->morphTable, 24);
if (D_80C106C8 == 0) {
for (i = 0; i < 4; i++) {
D_80C106B0[i] = Lib_SegmentedToVirtual(D_80C106B0[i]);
}
D_80C106C8 = 1;
}
this->unk_2AC = 2;
if (Flags_GetSwitch(globalCtx, this->actor.params)) {
this->actor.textId = 0x2ADC;
} else {
this->actor.textId = 0x2AD9;
}
func_80C10148(this);
}
// #pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Recepgirl/EnRecepgirl_Destroy.s")
void EnRecepgirl_Destroy(Actor* thisx, GlobalContext* globalCtx) {
}
// #pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Recepgirl/func_80C100DC.s")
void func_80C100DC(EnRecepgirl *this) {
if (this->unk_2AC != 0) {
this->unk_2AC++;
if (this->unk_2AC == 4) {
this->unk_2AC = 0;
return;
}
return;
}
if (Rand_ZeroOne() < 0.02f) {
this->unk_2AC++;
}
}
// #pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Recepgirl/func_80C10148.s")
void func_80C10148(EnRecepgirl *this) {
if (this->skelAnime.animation == &D_06001384) {
Animation_MorphToPlayOnce(&this->skelAnime, &D_0600AD98, 5.0f);
}
this->actionFunc = func_80C1019C;
}
// #pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Recepgirl/func_80C1019C.s")
void func_80C1019C(EnRecepgirl* this, GlobalContext* globalCtx) {
if (SkelAnime_Update(&this->skelAnime) != 0) {
if (this->skelAnime.animation == &D_0600A280) {
Animation_MorphToPlayOnce(&this->skelAnime, &D_0600AD98, 5.0f);
} else {
Animation_ChangeTransitionRepeat(&this->skelAnime, &D_06009890, -4.0f);
}
}
if (func_800B84D0(&this->actor, globalCtx) != 0) {
func_80C10290(this);
} else if (Actor_IsActorFacingLink(&this->actor, 0x2000)) {
func_800B8614(&this->actor, globalCtx, 60.0f);
if (Player_GetMask(globalCtx) == 2) {
this->actor.textId = 0x2367;
} else if (Flags_GetSwitch(globalCtx, this->actor.params)) {
this->actor.textId = 0x2ADC;
} else {
this->actor.textId = 0x2AD9;
}
}
}
// #pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Recepgirl/func_80C10290.s")
void func_80C10290(EnRecepgirl *this) {
Animation_MorphToPlayOnce(&this->skelAnime, &D_0600A280, -4.0f);
this->actionFunc = func_80C102D4;
}
// #pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Recepgirl/func_80C102D4.s")
void func_80C102D4(EnRecepgirl *this, GlobalContext *globalCtx) {
u8 temp_v0_2;
if (SkelAnime_Update(&this->skelAnime) != 0) {
if (this->skelAnime.animation == &D_0600A280) {
Animation_ChangeDefaultRepeat(&this->skelAnime, &D_06001384);
} else if (this->skelAnime.animation == &D_0600AD98) {
if (this->actor.textId == 0x2ADA) {
Animation_MorphToPlayOnce(&this->skelAnime, &D_06000968, 10.0f);
} else {
Animation_ChangeTransitionRepeat(&this->skelAnime, &D_06009890, 10.0f);
}
} else if (this->actor.textId == 0x2ADA) {
Animation_ChangeTransitionRepeat(&this->skelAnime, &D_06009890, 10.0f);
} else {
Animation_MorphToPlayOnce(&this->skelAnime, &D_0600A280, -4.0f);
}
}
temp_v0_2 = func_80152498(&globalCtx->msgCtx);
if (temp_v0_2 == 2) {
this->actor.textId = 0x2ADC;
func_80C10148(this);
return;
}
if ((temp_v0_2 == 5) && (func_80147624(globalCtx) != 0)) {
if (this->actor.textId == 0x2AD9) {
Actor_SetSwitchFlag(globalCtx, this->actor.params);
Animation_MorphToPlayOnce(&this->skelAnime, &D_0600AD98, 10.0f);
if ((gSaveContext.weekEventReg[63] & 0x80)) {
this->actor.textId = 0x2ADF;
} else {
this->actor.textId = 0x2ADA;
}
} else if (this->actor.textId == 0x2ADC) {
Animation_MorphToPlayOnce(&this->skelAnime, &D_0600AD98, 10.0f);
this->actor.textId = 0x2ADD;
} else {
Animation_MorphToPlayOnce(&this->skelAnime, &D_06000968, 10.0f);
if (this->actor.textId == 0x2ADD) {
this->actor.textId = 0x2ADE;
} else if (this->actor.textId == 0x2ADA) {
this->actor.textId = 0x2ADB;
} else {
this->actor.textId = 0x2AE0;
}
}
func_80151938(globalCtx, this->actor.textId);
}
}
// #pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Recepgirl/EnRecepgirl_Update.s")
void EnRecepgirl_Update(Actor *thisx, GlobalContext *globalCtx) {
s32 pad;
EnRecepgirl* this = THIS;
Vec3s sp30;
this->actionFunc(this, globalCtx);
func_800E9250(globalCtx, &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) {
EnRecepgirl* this = THIS;
if (limbIndex == 5) {
rot->x += this->unk_2AE.y;
}
return false;
}
// #pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Recepgirl/func_80C10590.s")
void func_80C10590(GlobalContext *globalCtx, s32 limbIndex, Actor *thisx) {
EnRecepgirl* this = THIS;
if (limbIndex == 5) {
Matrix_RotateY(0x400 - this->unk_2AE.x, MTXMODE_APPLY);
Matrix_GetStateTranslationAndScaledX(500.0f, &this->actor.focus.pos);
}
}
// #pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Recepgirl/EnRecepgirl_Draw.s")
void EnRecepgirl_Draw(Actor *thisx, GlobalContext *globalCtx) {
EnRecepgirl* this = THIS;
OPEN_DISPS(globalCtx->state.gfxCtx);
func_8012C28C(globalCtx->state.gfxCtx);
gSPSegment(POLY_OPA_DISP++, 0x08, D_80C106B0[this->unk_2AC]);
func_801343C0(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, func_80C10558, NULL, func_80C10590, &this->actor);
CLOSE_DISPS(globalCtx->state.gfxCtx);
}
```
</details>
(We can delete the `GLOBAL_ASM` lines now.)
The worst part of documentation is finding somewhere to start. We have a decent place to start here, though, in that we already know the function (or rather, the use) of a couple of the functions, namely the LimbDraws. So we can rename `func_80C10558` to `EnRecepgirl_OverrideLimbDraw` and `func_80C10590` to `EnRecepgirl_UnkLimbDraw`. Remember to do a global rename so that the functions in the assembly are renamed, use `rename_global_asm`,
```
$ ./tools/rename_global_asm.py
asm/non_matchings/overlays/ovl_En_Recepgirl/func_80C10558.s --> asm/non_matchings/overlays/ovl_En_Recepgirl/EnRecepgirl_OverrideLimbDraw.s
asm/non_matchings/overlays/ovl_En_Recepgirl/func_80C10590.s --> asm/non_matchings/overlays/ovl_En_Recepgirl/EnRecepgirl_UnkLimbDraw.s
```
as well as the mentions in this chunk of `functions.txt`:
```
0x80C0FFD0:("EnRecepgirl_Init",),
0x80C100CC:("EnRecepgirl_Destroy",),
0x80C100DC:("func_80C100DC",),
0x80C10148:("func_80C10148",),
0x80C1019C:("func_80C1019C",),
0x80C10290:("func_80C10290",),
0x80C102D4:("func_80C102D4",),
0x80C104E8:("EnRecepgirl_Update",),
0x80C10558:("func_80C10558",),
0x80C10590:("func_80C10590",),
0x80C105EC:("EnRecepgirl_Draw",),
```
That's probably as much as we can do on functions for now. Next let's think about some of the variables. We have essentially 3 sorts of variable here
- struct variables
- data/bss
- intrafunction/stack variables
and this is roughly the order of preference for naming them (although not necessarily the logical order to determine what they do). This actor is quite limited in the last category: only `sp30` is unnamed at the moment. Even though `func_800E9250` is decomped, the purpose of the argument in which `sp30` is placed is not clear (and, indeed, is not named), so it's probably best to leave it unnamed for now. (With greater experience, you might analyse `func_800E9250` to work out what this argument is for, but let's not worry about that for now.)
As for the struct, there are two unnamed variables at the moment:
```C
typedef struct EnRecepgirl {
/* 0x000 */ Actor actor;
/* 0x144 */ SkelAnime skelAnime;
/* 0x188 */ Vec3s jointTable[24];
/* 0x218 */ Vec3s morphTable[24];
/* 0x2A8 */ EnRecepgirlActionFunc actionFunc;
/* 0x2AC */ u8 unk_2AC;
/* 0x2AE */ Vec3s unk_2AE;
} EnRecepgirl; // size = 0x2B4
```
Let's start with `unk_2AC`. This is set to `2` in `Init`, something interesting happens to it in `func_80C100DC`, but it is used in the `Draw`, here:
```C
gSPSegment(POLY_OPA_DISP++, 0x08, D_80C106B0[this->unk_2AC]);
```
So it is used as an index into the array `D_80C106B0`, and the element with that index is placed on segment `8`. So we need to work out what this array is to name `unk_2AC`.
As we discussed last time, `D_80C106B0` is an array of [segmented pointers](data.md#segmented-pointers). Since they are in segment `6`, they are in the actor's object file. Which object? The InitVars tell us: namely,
```C
const ActorInit En_Recepgirl_InitVars = {
ACTOR_EN_RECEPGIRL,
ACTORCAT_NPC,
FLAGS,
OBJECT_BG,
```
the fourth element is the object (it is actually an enum, but the file itself has the same name as the object enum). So, we need to look at the object file. We are very lucky that a custom tool has been written for such a thing: Z64Utils.
## Z64Utils
The latest release of Z64Utils can be downloaded from [https://github.com/Random06457/Z64Utils/releases]. To use it with MM, you also need a json file to fill in the file names: the latest version can be obtained from [https://github.com/Random06457/Z64Utils-Config]. It should work on Wine. Some graphics cards don't love it, but the 3D graphical part is only required for skeleton and animations.
Having downloaded and unzipped it, open the baserom file. This will populate the main window with a list:
![Z64Utils' main window](images/z64utils_main.png)
Search for the object file, right-click and select "Open in Object Analyzer". It will ask you to choose a segment: this is the segment that the file is put on, and allows Z64Utils to resolve the segmented addresses it references into symbols. The json already knows it should be segment `6`, so just click okay. This will open this window:
![Z64Utils' object analyzer window](images/z64utils_object_analyzer.png)
Go to "Analysis -> Find Dlists" and press OK (the defaults are usually fine). This will automatically search for displaylists in the object, which are a sufficiently distinctive format to be easy to find. We want to see the other stuff in the object too, so also do "Analysis -> Analyze Dlists". This will populate the window with even more stuff:
![Z64Utils, with an analyzed object](images/z64utils_object_analyzed.png)
We will talk about what all these types of data are next time, but for now, all we want to know is what
```C
extern void* D_0600F8F0;
extern void* D_0600FCF0;
extern void* D_060100F0;
```
actually are. We know they are set on segment 8, so we need to find where the skeleton uses them. We know from `extern FlexSkeletonHeader D_06011B60;` that this is at `0x06011B60`, so scroll down to it, right-click on it, and choose "Open in Skeleton Viewer". Pick an animation that we know it uses (sometimes Z64Utils misidentifies other things for animations), such as `extern AnimationHeader D_06000968;`, and you will get this error:
![Z64Utils, error when viewing skeleton](images/z64utils_skeleton_error.png)
It needs something to be set to segment `8`. Well, that's good, we know that the code does that! Let's find out what. Z64Utils tells you the address, so we can look up the displaylist that wants it: the relevant block is
```C
[...]
// Multi Command Macro Found (6 instructions)
0600DE70: gsDPLoadTLUT(256, 0x100, D_0600F6F0),
// Multi Command Macro Found (7 instructions)
0600DEA0: gsDPLoadTextureBlock(D_08000000, G_IM_FMT_CI, G_IM_SIZ_8b, 32, 32, 0, G_TX_MIRROR | G_TX_CLAMP, G_TX_NOMIRROR | G_TX_CLAMP, 5, 5, 0, 0),
0600DED8: gsDPSetTileSize(G_TX_RENDERTILE, 0, 0, (63<<2), (31<<2)),
0600DEE0: gsSPVertex(D_0600B3F0, 19, 0),
0600DEE8: gsSP2Triangles(0, 1, 2, 0, 3, 4, 5, 0),
0600DEF0: gsSP2Triangles(6, 7, 5, 0, 8, 4, 3, 0),
0600DEF8: gsSP2Triangles(7, 6, 9, 0, 5, 10, 3, 0),
0600DF00: gsSP2Triangles(5, 7, 10, 0, 11, 9, 6, 0),
0600DF08: gsSP2Triangles(11, 12, 9, 0, 4, 1, 13, 0),
0600DF10: gsSP2Triangles(6, 14, 11, 0, 13, 5, 4, 0),
0600DF18: gsSP2Triangles(5, 14, 6, 0, 15, 14, 5, 0),
0600DF20: gsSP2Triangles(8, 1, 4, 0, 2, 1, 8, 0),
0600DF28: gsSP2Triangles(13, 16, 5, 0, 13, 14, 17, 0),
0600DF30: gsSP2Triangles(18, 11, 14, 0, 12, 11, 18, 0),
0600DF38: gsSP1Triangle(13, 1, 0, 0),
0600DF40: gsDPPipeSync(),
[...]
```
so we see that segment `8` is expecting a texture (we'll go into more detail about precisely what when we talk about making the XML file to extract the object). Therefore, `D_80C106B0` is a set of textures. We have a special type for textures, namely `TexturePtr`.
## Back to the data
But what sort of textures? This is an NPC, so what textures on the model would it want to change? The answer is of course the eyes: most NPCs have eye textures, with some sort of routine for changing them to appear to blink. We can set the different textures onto segment `8` and see which is which, but this is enough to know that `D_80C106B0` can be `sEyeTextures` (`s` for `static`: they essentially have to be static so that we can name them like this without the names clashing), and that `unk_2AC` is `eyeTexIndex` (these names are not completely standard, but it's best to be as consistent as possible).
**N.B.** static data should not be renamed in the assembly or `variables.txt`, since assembly has no notion of file locality and there can be symbol clashes. Therefore it should only be renamed in its respective file, not globally.
```C
extern void* D_0600F8F0;
extern void* D_0600FCF0;
extern void* D_060100F0;
static TexturePtr sEyeTextures[] = { &D_0600F8F0, &D_0600FCF0, &D_060100F0, &D_0600FCF0 };
```
And now it's rather more obvious what
```C
void func_80C100DC(EnRecepgirl* this) {
if (this->eyeTexIndex != 0) {
this->eyeTexIndex++;
if (this->eyeTexIndex == 4) {
this->eyeTexIndex = 0;
}
} else if (Rand_ZeroOne() < 0.02f) {
this->eyeTexIndex++;
}
}
```
is doing: it's running a kind of blink routine. This is slightly nonstandard: usually there is a separate timer, but this one simply perturbs the index away from `0` every frame with a 2% chance. This sort of function is usually called `Blink` or `UpdateEyes`. Since it is explicitly called in `Update`, we'll call it `UpdateEyes`, but either is fine; we'll standardise later.
We have two other pieces of data. There is a suggested name for the InitChain in the code already; just replace it and replace the first line in the definition.
This leaves one piece of data unnamed, `D_80C106C8`. This is initially set to `0`, checked in `Init` to decide whether to run the loop, and then set to `1` after the loop is finished:
```C
if (D_80C106C8 == 0) {
for (i = 0; i < 4; i++) {
sEyeTextures[i] = Lib_SegmentedToVirtual(sEyeTextures[i]);
}
D_80C106C8 = 1;
}
```
What is this doing? We need to understand that to name this variable.
The N64's processors cannot use segmented addresses: they need actual RAM addresses. Therefore the segmented addresses have to be converted before being placed on a segment: this is what `Lib_SegmentedToVirtual` does. So (somewhat unusually) this loop is modifying the addresses in the actor's actual data in RAM. Having converted the addresses once, it wouldn't make any sense to convert them again, but `Init` would run every time an instantiation of the actor is created. Therefore `D_80C106C8` is present to ensure that the addresses only get converted once: it is really a boolean that indicates if the addresses have been converted. So let's call it `texturesDesegmented`, and replace its values by `true` and `false`.
Finally, clearly `4` is linked to the data over which we're iterating: namely it's the size of the array. We have a macro for this, `ARRAY_COUNT(sEyeTextures)`.
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,
Actor* thisx) {
EnRecepgirl* this = THIS;
if (limbIndex == 5) {
rot->x += this->unk_2AE.y;
}
return false;
}
void EnRecepgirl_UnkLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Actor* thisx) {
EnRecepgirl* this = THIS;
if (limbIndex == 5) {
Matrix_RotateY(0x400 - this->unk_2AE.x, MTXMODE_APPLY);
Matrix_GetStateTranslationAndScaledX(500.0f, &this->actor.focus.pos);
}
}
```
It is used to do a rotation of whatever limb `5` is. (The `+=` is because `rot->x` is the base rotation of the limb, and we have to add the same thing to it every frame to keep the angle changed and constant.) We can use Z64Utils to : setting segment `8` to one of what we know now are the eye textures, we can view the model in the skeleton viewer. The limb numbers in the object are one smaller than those in the actor (the root limb is only a concept for the code, not the object), so we find limb 4:
![Z64Utils highlighting a limb](images/z64utils_skeleton_head.png)
Hence this is changing the head rotation. An obvious name is `headRot`.
## Functions
Finally, we have to name the rest of the functions. Setup functions are usually named as `<ActorName>_Setup<ActionName>`, so we really only have to name two functions. They are both related to text. if we annotate all the textIds (do not quote the whole message, just give an unambiguous summary), the flow becomes a bit clearer:
```C
void func_80C10148(EnRecepgirl* this) {
if (this->skelAnime.animation == &D_06001384) {
Animation_MorphToPlayOnce(&this->skelAnime, &D_0600AD98, 5.0f);
}
this->actionFunc = func_80C1019C;
}
void func_80C1019C(EnRecepgirl* this, GlobalContext* globalCtx) {
if (SkelAnime_Update(&this->skelAnime) != 0) {
if (this->skelAnime.animation == &D_0600A280) {
Animation_MorphToPlayOnce(&this->skelAnime, &D_0600AD98, 5.0f);
} else {
Animation_ChangeTransitionRepeat(&this->skelAnime, &D_06009890, -4.0f);
}
}
if (func_800B84D0(&this->actor, globalCtx) != 0) {
func_80C10290(this);
} else if (Actor_IsActorFacingLink(&this->actor, 0x2000)) {
func_800B8614(&this->actor, globalCtx, 60.0f);
if (Player_GetMask(globalCtx) == 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)) {
this->actor.textId = 0x2ADC; // hear directions again?
} else {
this->actor.textId = 0x2AD9; // "Welcome..."
}
}
}
void func_80C10290(EnRecepgirl* this) {
Animation_MorphToPlayOnce(&this->skelAnime, &D_0600A280, -4.0f);
this->actionFunc = func_80C102D4;
}
void func_80C102D4(EnRecepgirl* this, GlobalContext* globalCtx) {
u8 temp_v0_2;
if (SkelAnime_Update(&this->skelAnime)) {
if (this->skelAnime.animation == &D_0600A280) {
Animation_ChangeDefaultRepeat(&this->skelAnime, &D_06001384);
} else if (this->skelAnime.animation == &D_0600AD98) {
if (this->actor.textId == 0x2ADA) { // Mayor's office is on the left (meeting ongoing)
Animation_MorphToPlayOnce(&this->skelAnime, &D_06000968, 10.0f);
} else {
Animation_ChangeTransitionRepeat(&this->skelAnime, &D_06009890, 10.0f);
}
} else if (this->actor.textId == 0x2ADA) { // Mayor's office is on the left (meeting ongoing)
Animation_ChangeTransitionRepeat(&this->skelAnime, &D_06009890, 10.0f);
} else {
Animation_MorphToPlayOnce(&this->skelAnime, &D_0600A280, -4.0f);
}
}
temp_v0_2 = func_80152498(&globalCtx->msgCtx);
if (temp_v0_2 == 2) {
this->actor.textId = 0x2ADC; // hear directions again?
func_80C10148(this);
} else if ((temp_v0_2 == 5) && (func_80147624(globalCtx) != 0)) {
if (this->actor.textId == 0x2AD9) { // "Welcome..."
Actor_SetSwitchFlag(globalCtx, this->actor.params);
Animation_MorphToPlayOnce(&this->skelAnime, &D_0600AD98, 10.0f);
if (gSaveContext.weekEventReg[63] & 0x80) { // showed Couple's Mask to meeting
this->actor.textId = 0x2ADF; // Mayor's office is on the left (meeting ended)
} else {
this->actor.textId = 0x2ADA; // Mayor's office is on the left (meeting ongoing)
}
} else if (this->actor.textId == 0x2ADC) { // hear directions again?
Animation_MorphToPlayOnce(&this->skelAnime, &D_0600AD98, 10.0f);
this->actor.textId = 0x2ADD; // "So..."
} else {
Animation_MorphToPlayOnce(&this->skelAnime, &D_06000968, 10.0f);
if (this->actor.textId == 0x2ADD) { // "So..."
this->actor.textId = 0x2ADE; // Mayor's office is on the left, drawing room on the right
} else if (this->actor.textId ==
0x2ADA) { // Mayor's office is on the left (meeting ongoing)
this->actor.textId = 0x2ADB; // drawing room on the right
} else {
this->actor.textId = 0x2AE0; // drawing room on the right, don't go in without an appointment
}
}
func_80151938(globalCtx, 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 `func_800B84D0(&this->actor, globalCtx) != 0`. This is something to do with talking. The other function handles the rest of the conversation, and hands back to the first if `func_80152498(&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`.
For more complex actors, we have a tool called `graphovl.py` that can produce function flow graphs for actors: running
```
$ ./tools/graphovl/graphovl.py En_Recepgirl
```
produces
![EnRecepgirl's function flow graph](images/En_Recepgirl.gv.png)
## Miscellaneous other documentation
We like to make macros for reading an actor's `params` (indeed, this is required even if you don't know what the params are for). A simple example is `ObjTree`, which has the following code in its `Init` function:
```c
if (this->dyna.actor.params & 0x8000) {
Actor_SetScale(&this->dyna.actor, 0.15f);
this->dyna.actor.uncullZoneForward = 4000.0f;
} else {
Actor_SetScale(&this->dyna.actor, 0.1f);
DynaPolyActor_Init(&this->dyna, 1);
CollisionHeader_GetVirtual(&D_06001B2C, &colHeader);
this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader);
}
```
Looking through the rest of the actor, it becomes apparent that `params & 0x8000` is only used for changing the size of the tree: ones with this bit set are larger. So we make a macro in the header:
```c
#define OBJTREE_ISLARGE(thisx) ((thisx)->params & 0x8000)
```
Notice that we use `thisx`: this makes the form of every one of these macros the same. However, we only use `thisx` if required for matching, so when we add it to the actor, we use `&this->dyna.actor` (in this case, since `ObjTree` is a dynapoly actor).
```c
if (OBJTREE_ISLARGE(&this->dyna.actor)) {
Actor_SetScale(&this->dyna.actor, 0.15f);
this->dyna.actor.uncullZoneForward = 4000.0f;
} else {
Actor_SetScale(&this->dyna.actor, 0.1f);
DynaPolyActor_Init(&this->dyna, 1);
CollisionHeader_GetVirtual(&D_06001B2C, &colHeader);
this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader);
}
```
Much clearer!
We have now essentially documented this as far as we can without the object, so we'd better do that next.
Next: [Analysing object files](object_decomp.md)

View File

@ -0,0 +1,303 @@
# Draw functions
Up: [Contents](contents.md)
Previous: [The rest of the functions in the actor](other_functions.md)
Draw functions behave completely differently from the other functions in an actor. They often use a lot of macros.
This document will be a bit different: we will look at the draw functions in EnRecepgirl, then consider some more complicated examples.
## A first example
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);
```
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
void *D_80C106B0[4] = {(void *)0x600F8F0, (void *)0x600FCF0, (void *)0x60100F0, (void *)0x600FCF0};
void EnRecepgirl_Draw(Actor *thisx, GlobalContext *globalCtx) {
EnRecepgirl* this = (EnRecepgirl *) thisx;
GraphicsContext *sp30;
Gfx *temp_v1;
GraphicsContext *temp_a0;
temp_a0 = globalCtx->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];
func_801343C0(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, (s32) this->skelAnime.dListCount, func_80C10558, NULL, func_80C10590, (Actor *) this);
}
```
Notable features are the GraphicsContext temps, and blocks of the form
```C
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];
```
(This is a particularly simple example, since there's only one of these blocks. We will give a more involved example later.)
Each of these blocks converts into a graphics macro. They are usually (but not always) straightforward, but manually converting them is a pain, and there are sometimes special cases. To deal with them easily, we will use a tool from glank's N64 tools. To install these, follow the instructions [here](https://practicerom.com/public/packages/debian/howto.txt).
For our purposes, we only need one of the programs this provides: `gfxdis.f3dex2`.
Graphics are actually 64-bit on the Nintendo 64. This code block is a result of instructions telling the processor what to do with the graphics pointer. There are two main types of graphics pointer (there are a couple of others used in `code`, but actors will only use these two),
- polyOpa ("opaque") for solid textures
- polyXlu ("Xlucent" i.e. "translucent") for translucent textures
Our example is polyOpa, not surprisingly since our receptionist is solid.
`words.w0` and `words.w1` contain the actual graphics instruction, in hex format. Usually, `w0` is constant and `w1` contains the arguments. To find out what sort of macro we are dealing with, we use `gfxdis.f3dex2`. `w1` is variable, but we need to give the program a constant placeholder. A common word to use is 12345678, so in this case we run
```
gfxdis.f3dex2 -x -g "POLY_OPA_DISP++" -d DB06002012345678
```
- `-x` uses hex instead of the default qu macros (never mind what those are, MM doesn't use them)
- `-g` is used to specify which graphics pointer macro to use
- `-d` is for the graphics dword
Our standard now is to use decimal colors. If you have a constant second argument rather than a variable one, you can also use `-dc` to get decimal colors instead of the default hex.
The output looks like
```
gSPSegment(POLY_OPA_DISP++, 0x08, 0x12345678);
```
We can now replace the `0x12345678` by the actual second word, namely `D_80C106B0[this->unk_2AC]`. We can see mips2c has pulled in this data again: we saw it before in the `Init`.
The words look like pointers to assets in the actor's object segment, which would make sense if we're looking for textures to draw. Because this data is used in a graphics macro, it will be either a displaylist or a texture; it may as well stay as `void*` until we come back to it later.
```C
gSPSegment(POLY_OPA_DISP++, 0x08, D_80C106B0[this->unk_2AC]);
```
You repeat this for every block in the function.
If you have worked on OoT, you will be aware of the functions `Graph_OpenDisps` and `Graph_CloseDisps`, and might be surprised to see them missing here. These functions are actually a debug feature: the `OPEN_DISPS` and `CLOSE_DISPS` macros still exist, but they don't expand to functions. Of course this means you have to guess where they go. A sensible guess for `OPEN_DISPS` is where the `gfxCtx` temp assignment first happens; `CLOSE_DISPS` is a bit harder, although it's basically just a `}`, so it *shouldn't* matter as much.
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);
#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Recepgirl/func_80C10558.s")
void func_80C10590(GlobalContext *globalCtx, 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) {
EnRecepgirl* this = THIS;
OPEN_DISPS(globalCtx->state.gfxCtx);
func_8012C28C(globalCtx->state.gfxCtx);
gSPSegment(POLY_OPA_DISP++, 0x08, D_80C106B0[this->unk_2AC]);
func_801343C0(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount, func_80C10558, NULL, func_80C10590, &this->actor);
CLOSE_DISPS(globalCtx->state.gfxCtx);
}
```
And this matches.
The last two functions in the actor are used as arguments in `func_801343C0`. 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 func_801343C0(GlobalContext* globalCtx, void** skeleton, Vec3s* jointTable, s32 dListCount,
OverrideLimbDraw overrideLimbDraw, PostLimbDraw postLimbDraw, UnkActorDraw unkDraw, Actor* actor)
```
The typedefs of the callbacks it uses are in `z64animation.h`:
```C
typedef s32 (*OverrideLimbDraw)(struct GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot,
struct Actor* actor);
typedef void (*PostLimbDraw)(struct GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3s* rot,
struct Actor* actor);
[...]
typedef void (*UnkActorDraw)(struct GlobalContext* globalCtx, s32 limbIndex, struct Actor* actor);
```
which is where mips2c got them from.
In this case, only two of them are used, and it is these that are the last functions standing between us and a decompiled actor.
## OverrideLimbDraw, PostLimbDraw, UnkActorDraw
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) {
if (limbIndex == 5) {
rot->x += actor->unk2B0;
}
return 0;
}
```
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) {
EnRecepgirl* this = THIS;
if (limbIndex == 5) {
rot->x += this->unk_2AE.y;
}
return false;
}
```
As for the UnkActorDraw, it has a much simpler prototype. mips2c gives
```C
void func_80C10590(GlobalContext *globalCtx, s32 limbIndex, Actor *actor) {
if (limbIndex == 5) {
Matrix_RotateY((s16) (0x400 - actor->unk2AE), 1);
Matrix_GetStateTranslationAndScaledX(500.0f, (Vec3f *) &actor->focus);
}
}
```
There is only minor cleanup needed here:
- recasting the last argument,
- replacing the last argument of `Matrix_RotateY` 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) {
EnRecepgirl* this = THIS;
if (limbIndex == 5) {
Matrix_RotateY(0x400 - this->unk_2AE.x, MTXMODE_APPLY);
Matrix_GetStateTranslationAndScaledX(500.0f, &this->actor.focus.pos);
}
}
```
## Some more examples: ObjTree
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) {
s16 sp36;
s16 sp34;
Gfx *sp28;
Gfx *sp20;
Gfx *temp_v0;
Gfx *temp_v0_2;
Gfx *temp_v0_3;
Gfx *temp_v0_4;
GraphicsContext *temp_a0;
GraphicsContext *temp_s0;
sp36 = (s16) (s32) (f32) thisx->shape.rot.x;
sp34 = (s16) (s32) (f32) thisx->shape.rot.z;
temp_a0 = globalCtx->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);
temp_v0_2 = temp_s0->polyOpa.p;
temp_s0->polyOpa.p = temp_v0_2 + 8;
temp_v0_2->words.w1 = (u32) &D_06000680;
temp_v0_2->words.w0 = 0xDE000000;
Matrix_InsertRotation(sp36, 0, sp34, 1);
temp_v0_3 = temp_s0->polyOpa.p;
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);
temp_v0_4 = temp_s0->polyOpa.p;
temp_s0->polyOpa.p = temp_v0_4 + 8;
temp_v0_4->words.w1 = (u32) &D_060007C8;
temp_v0_4->words.w0 = 0xDE000000;
}
```
We can see there are four blocks here, although only two different macros:
```C
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);
```
gfxdis gives
```
$ gfxdis.f3dex2 -x -g POLY_OPA_DISP++ -d DA38000312345678
gSPMatrix(POLY_OPA_DISP++, 0x12345678, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
```
so it becomes
```C
gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
```
```C
temp_v0_2 = temp_s0->polyOpa.p;
temp_s0->polyOpa.p = temp_v0_2 + 8;
temp_v0_2->words.w1 = (u32) &D_06000680;
temp_v0_2->words.w0 = 0xDE000000;
```
```
$ gfxdis.f3dex2 -x -g POLY_OPA_DISP++ -d DE00000012345678
gSPDisplayList(POLY_OPA_DISP++, 0x12345678);
```
so this one is
```C
gSPDisplayList(POLY_OPA_DISP++, D_06000680);
```
```C
temp_v0_3 = temp_s0->polyOpa.p;
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);
```
This is the same as the first one. Indeed, it's identical.
```C
temp_v0_4 = temp_s0->polyOpa.p;
temp_s0->polyOpa.p = temp_v0_4 + 8;
temp_v0_4->words.w1 = (u32) &D_060007C8;
temp_v0_4->words.w0 = 0xDE000000;
```
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) {
s16 sp36 = (f32) thisx->shape.rot.x;
s16 sp34 = (f32) thisx->shape.rot.z;
OPEN_DISPS(globalCtx->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);
gSPDisplayList(POLY_OPA_DISP++, D_06000680);
Matrix_InsertRotation(sp36, 0, sp34, MTXMODE_APPLY);
gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_OPA_DISP++, D_060007C8);
CLOSE_DISPS(globalCtx->state.gfxCtx);
}
```
## RGB macros and bitpacking
TODO: find some examples for this one.
For even more examples, you can consult [the OoT tutorial](https://github.com/zeldaret/oot/blob/master/docs/tutorial/draw_functions.md)
Next: [Data](data.md)

Binary file not shown.

After

Width:  |  Height:  |  Size: 113 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

View File

@ -0,0 +1,87 @@
# Introduction
In this project, we are decompiling The Legend of Zelda: Majora's Mask. This means that we take the assembly language that is on the cartridge,
```
glabel func_809529AC
/* 00038C 809529AC 27BDFFE0 */ addiu $sp, $sp, -0x20
/* 000390 809529B0 AFBF001C */ sw $ra, 0x1c($sp)
/* 000394 809529B4 AFA40020 */ sw $a0, 0x20($sp)
/* 000398 809529B8 0C02E27E */ jal Actor_HasParent
/* 00039C 809529BC AFA50024 */ sw $a1, 0x24($sp)
/* 0003A0 809529C0 8FA40020 */ lw $a0, 0x20($sp)
/* 0003A4 809529C4 1040000C */ beqz $v0, .L809529F8
/* 0003A8 809529C8 8FA50024 */ lw $a1, 0x24($sp)
/* 0003AC 809529CC A4800116 */ sh $zero, 0x116($a0)
/* 0003B0 809529D0 8C860098 */ lw $a2, 0x98($a0)
/* 0003B4 809529D4 8C87009C */ lw $a3, 0x9c($a0)
/* 0003B8 809529D8 AFA40020 */ sw $a0, 0x20($sp)
/* 0003BC 809529DC 0C02E140 */ jal func_800B8500
/* 0003C0 809529E0 AFA00010 */ sw $zero, 0x10($sp)
/* 0003C4 809529E4 8FA40020 */ lw $a0, 0x20($sp)
/* 0003C8 809529E8 3C0E8095 */ lui $t6, %hi(func_80952A1C)
/* 0003CC 809529EC 25CE2A1C */ addiu $t6, $t6, %lo(func_80952A1C)
/* 0003D0 809529F0 10000006 */ b .L80952A0C
/* 0003D4 809529F4 AC8E01F4 */ sw $t6, 0x1f4($a0)
.L809529F8:
/* 0003D8 809529F8 C484009C */ lwc1 $f4, 0x9c($a0)
/* 0003DC 809529FC 8C870098 */ lw $a3, 0x98($a0)
/* 0003E0 80952A00 24060035 */ addiu $a2, $zero, 0x35
/* 0003E4 80952A04 0C02E287 */ jal func_800B8A1C
/* 0003E8 80952A08 E7A40010 */ swc1 $f4, 0x10($sp)
.L80952A0C:
/* 0003EC 80952A0C 8FBF001C */ lw $ra, 0x1c($sp)
/* 0003F0 80952A10 27BD0020 */ addiu $sp, $sp, 0x20
/* 0003F4 80952A14 03E00008 */ jr $ra
/* 0003F8 80952A18 00000000 */ nop
```
(the commented numbers on the left are the original machine code, the middle the translation into MIPS assembly, the right useful information about the numbers in the code)
and turn it into compilable C code:
```C
void func_809529AC(EnMs *this, GlobalContext *globalCtx) {
if (Actor_HasParent(&this->actor, globalCtx)) {
this->actor.textId = 0;
func_800B8500(&this->actor, globalCtx, this->actor.xzDistToPlayer, this->actor.playerHeightRel, 0);
this->actionFunc = func_80952A1C;
} else {
func_800B8A1C(&this->actor, globalCtx, 0x35, this->actor.xzDistToPlayer, this->actor.playerHeightRel);
}
}
```
which is intended to be as close to the original code as we can get just by looking at the assembly. We are doing *matching* decomp: in the right context, and with the right compiler settings, the above C compiles into *precisely* the assembly code above, not just equivalent code.
N.B. We are using only publicly available code. In particular, we are not looking at any of the recent Nintendo source code leaks.
Progress of the project can be found at [https://zelda64.dev]. The long-term goal of this project is to obtain a complete compilable version of the code for every publicly released version of Majora's Mask (in the same way as the Ocarina of Time project and many other Zelda games). *We are not working on a PC Port, and neither this project nor the ZeldaRET organisation will not be making one*, although the resulting code will be very useful if someone does intend to make such a port.
Most of the discussion on the project takes place on the Zelda Decompilation Discord (linked in the [README.md](../../README.md)). We are very welcoming to newcomers and are happy to help you with any problems you might have with the decompilation process.
## What do I need to know to take part?
Basic knowledge of C, particularly arrays and pointers, is extremely useful. Knowledge of MIPS is not required initially, but if you are serious about decompilation you will soon pick up a lot of it.
Knowledge of the fundamentals of git and GitHub is required. There are a number of tutorials available online, and a later document in this tutorial describes how you contribute to this project outside the actual decompilation process.
The most useful knowledge to have is a general understanding of how the game works. An afternoon of constructive mucking about in the [Practice Rom](https://kz.zeldacodes.org/) (aka KZ) will be very beneficial if you have not looked at the game's subsurface workings before.
## Structure of the code
A lot of work has already been done on the code to bring it into a format that is easy to decompile. I will discuss actors, since this is where the majority of new people should begin.
An *actor* is any thing in the game that moves or performs actions or interactions: Link is an actor, enemies are actors, NPCs are actors, props like grass are actors. The vast majority of actors are *overlays*, which means they are loaded only when the game needs them.
In the code, each actor is associated to several files: there is
- the main .c file, e.g. `src/overlays/actors/ovl_En_Ms/z_en_ms.c`
- the actor's Header file, e.g. `src/overlays/actors/ovl_En_Ms/z_en_ms.h`
- various .o files that tell the `make` script how to incorporate it into building the ROM,
and then for undecompiled actors, various assembly (.s) files, generally including:
- one for the actor's *data* (this usually includes things like its collision information about how to draw it, and various other stuff that is used in it), e.g. `data/overlays/actors/ovl_En_Ms.data.s`
- one for each function in the actor, e.g. `asm/non_matchings/overlays/actors/ovl_En_Ms/func_809529AC.s`
(In this project, all assembly code and asset files are extracted from a user-provided ROM: if you look in the GitHub repository, you will see that only decompiled source code is present.)
The basic process of decomp is to take one or more of the .s files, run it through a decompilation program (mips_to_c) that reads the ASM very literally, and then, through human ingenuity, reshape it into code that not only compiles in the first place, but completely matches the assembly generation of the original code (well-written or otherwise; it's also very likely that our constructed code differs significantly from the original, even if it still compiles to the same thing).

97
docs/tutorial/merging.md Normal file
View File

@ -0,0 +1,97 @@
# The merging process
Up: [Contents](contents.md)
Previous: [Documenting](documenting.md)
## Preparing to PR
### Change the `spec`
Specifically, to use the automatically generated reloc, rather than the original. In the case of an entirely matched actor, you find the section relating to the actor that you edited before:
```
beginseg
name "ovl_En_Recepgirl"
include "build/src/overlays/actors/ovl_En_Recepgirl/z_en_recepgirl.o"
//include "build/data/overlays/actors/ovl_En_Recepgirl.data.o"
include "build/data/overlays/actors/ovl_En_Recepgirl.reloc.o"
endseg
```
and change to use our reloc:
```
beginseg
name "ovl_En_Recepgirl"
include "build/src/overlays/actors/ovl_En_Recepgirl/z_en_recepgirl.o"
include "build/src/overlays/actors/ovl_En_Recepgirl/ovl_En_Recepgirl_reloc.o"
endseg
```
(copy the path, then copy the directory name and put `_reloc.o` after it).
### Non-matchings
If you can't match a function even with everyone's help in the `mm-decomp-help` discord channel, don't worry overlong about it. Hopefully you can get it to do the same thing as the original (non-matching), and then you set it up to use the original asm for the matching build, and your code for the non-matching. This looks like
```c
#ifdef NON_MATCHING
// Helpful comment about the nature of the nonmatching
void function() {
// ...
}
#else
#pragma GLOBAL_ASM(asm/.../function.s)
#endif
```
in the C file. Also, due to the way `GLOBAL_ASM` works, we also cannot use generated reloc for overlays with nonmatchings, so we have to do the same thing for the reloc file in the spec:
```
beginseg
name "ovl_En_Recepgirl"
compress
include "build/src/overlays/actors/ovl_En_Recepgirl/z_en_recepgirl.o"
#ifdef NON_MATCHING
include "build/src/overlays/actors/ovl_En_Recepgirl/ovl_En_Recepgirl_reloc.o"
#else
include "build/data/overlays/actors/ovl_En_Recepgirl.reloc.o"
#endif
endseg
```
Ideally you should at least be able to get a function to have equivalent behaviour; if not, and you have exhausted all other avenues of getting help, it should be marked in the C file as `NON_EQUIVALENT`, in the same way as a nonmatching. We do not change the spec for non-equivalents: they are treated the same as undecompiled code from a building perspective, lest they break things.
### Format
Run the formatting script `format.sh`, to format the C files in the standard way we use. If you have some arrays or struct definitions in your file, check that they have not been obnoxiously padded out: you can usually get a better format without a final comma for short things.
**N.B.** this is now essential: the CI will fail immediately if it detects files that change when formatted.
### Merge master
To make sure the PR builds correctly with the current master, you need to merge `upstream/master` before you make the PR. This tends to break things, that you have to fix to get it to compile correctly again.
## Pull Requests
Push commits to your fork of the repository on GitHub, and then open a pull request. Name the PR something sensible, like
- `EnRecepgirl OK and documented` (if all the functions match and your documentation is fairly complete)
- `EnRecepgirl OK` (if all the functions match)
- `EnRecepgirl (n nonmatching)` (if you couldn't get one or more functions to match, but to the best of your knowledge they are equivalent code)
- `EnRecepgirl (n nonequivalent)` (if you couldn't get one or more functions to match, and do not believe the code in them has the same effect)
and so on, although these four tend to cover most cases. Feel free to add a comment describing anything interesting you had to do or issues in non-matchings.
Please also update the status of the file on Trello/the spreadsheet.
### Reviews
Pull requests may be reviewed by anyone (who knows enough about the conventions of the project), and all must be reviewed and approved by two leads and one extra contributor.
To implement suggestions made in reviews, it is generally easier to be consistent if you push more commits from your local branch. It is also quite possible that in the meantime some other PR has gone in, and git will ask you to merge master before you add more commits. This is normally fairly painless, although often you have to resolve merge conflicts. If in doubt, backup your work before doing anything, and ask in Discord before doing anything drastic, or if you don't understand what git is telling you.
There is no need to wait for your PR to be approved and committed before working on your next file.

View File

@ -0,0 +1,14 @@
# Analysing object files
Up: [Contents](contents.md)
Previous: [Documenting](documenting.md)
TODO: To be written once automated objects are present.
## Useful tools
We are very fortunate that several nice tools have been written recently that are excellent for documenting asset files:
- [Z64Utils](https://github.com/Random06457/Z64Utils/releases), for looking at displaylists, textures they reference, the skeleton, animations, etc.
- [Texture64](https://github.com/queueRAM/Texture64/releases), for looking at textures in all the common N64 formats (needed since Z64Utils cannot interpret textures not explicitly referenced in displaylists currently)
Next: [The merging process](merging.md)

View File

@ -0,0 +1,605 @@
# The rest of the functions in the actor
Up: [Contents](contents.md)
Previous: [Beginning decompilation: the Init function and the Actor struct](beginning_decomp.md)
## Now what?
At this point we have a choice to make. Either we could follow the main function flow and decompile `func_80C10148`, or take a look at `Destroy`, which for smaller actors can often be done straight after Init, since it usually just removes colliders and deallocates dynapoly.
## Destroy
Destroy will be a dead end, but we might as well do it now. Usually we would regenerate the context first and apply it to mips2c as with `Init`, but if we look at the assembly...
```mips
glabel EnRecepgirl_Destroy
/* 0000FC 80C100CC AFA40000 */ sw $a0, ($sp)
/* 000100 80C100D0 AFA50004 */ sw $a1, 4($sp)
/* 000104 80C100D4 03E00008 */ jr $ra
/* 000108 80C100D8 00000000 */ nop
```
It doesn't seem to do anything. Indeed, chucking it in mips2c,
```
$ ../mips_to_c/mips_to_c.py asm/non_matchings/overlays/ovl_En_Recepgirl/EnRecepgirl_Destroy.s
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) {
}
```
and cross this function off.
## `func_80C10148`
We don't really have a choice now, we have to look at this function. Remake the context (no need to change the function type this time), and run mips2c on the function's assembly file:
```
$ ../mips_to_c/mips_to_c.py asm/non_matchings/overlays/ovl_En_Recepgirl/func_80C10148.s data/ovl_En_Recepgirl/ovl_En_Recepgirl.data.s --context ctx.c
extern AnimationHeader D_0600AD98;
extern ? func_80C1019C;
void func_80C10148(EnRecepgirl *this) {
SkelAnime *temp_a0;
temp_a0 = &this->skelAnime;
if (&D_06001384 == this->skelAnime.animation) {
this = this;
Animation_MorphToPlayOnce(temp_a0, &D_0600AD98, 5.0f);
}
this->actionFunc = &func_80C1019C;
}
```
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*);
```
and so we prototype `func_80C1019C` as
```C
void func_80C1019C(EnRecepgirl* this, GlobalContext* globalCtx);
```
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).
There are several rather odd things going on here:
- `temp_a0` is only used once. As such it's probably fake.
- There's a weird `this = this` that does nothing
- `if (&D_06001384 == this->skelAnime.animation)` is a bit of a funny way to write the condition: it seems more likely it would be the other way round.
- Also, if we look up `animation`, we find it is an `AnimationHeader*`, so `D_06001384` can be externed as `AnimationHeader`.
- `func_80C1019C` is already a pointer, so the `&` is ineffectual. Our style is to not use `&` on function pointers.
If we tackle these, we end up with
```C
void func_80C10148(EnRecepgirl* this);
void func_80C1019C(EnRecepgirl* this, GlobalContext* globalCtx);
[...]
extern AnimationHeader D_06001384;
extern AnimationHeader D_06009890;
extern UNK_TYPE D_0600A280;
extern AnimationHeader D_0600AD98;
extern FlexSkeletonHeader D_06011B60;
[...]
void func_80C10148(EnRecepgirl *this) {
if (this->skelAnime.animation == &D_06001384) {
Animation_MorphToPlayOnce(&this->skelAnime, &D_0600AD98, 5.0f);
}
this->actionFunc = func_80C1019C;
}
```
This is a common type of function called a setup (action) function. It runs once and prepares the ground for its corresponding actionfunction to run, whereas the actionfunction is usually run every frame by `Update` (but more on that later). Running `make`, we get OK again.
Again we have only one way to go
## `func_80C1019C`
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) {
SkelAnime *sp24;
SkelAnime *temp_a0;
temp_a0 = &this->skelAnime;
sp24 = temp_a0;
if (SkelAnime_Update(temp_a0) != 0) {
if (&D_0600A280 == this->skelAnime.animation) {
Animation_MorphToPlayOnce(temp_a0, &D_0600AD98, 5.0f);
} else {
Animation_ChangeTransitionRepeat(temp_a0, &D_06009890, -4.0f);
}
}
if (func_800B84D0((Actor *) this, globalCtx) != 0) {
func_80C10290(this);
return;
}
if (Actor_IsActorFacingLink((Actor *) this, 0x2000) != 0) {
func_800B8614((Actor *) this, globalCtx, 60.0f);
if (Player_GetMask(globalCtx) == 2) {
this->actor.textId = 0x2367;
return;
}
if (Flags_GetSwitch(globalCtx, (s32) this->actor.params) != 0) {
this->actor.textId = 0x2ADC;
return;
}
this->actor.textId = 0x2AD9;
// Duplicate return node #12. Try simplifying control flow for better match
}
}
```
This is a bit juicier! We can do some preliminary cleanup, then worry about the control flow.
- `sp24` does nothing, so is almost certainly fake.
- `temp_a0` is used in 3 different places, but they're all right next to one another and are unlikely to be required since there's no nontrivial calculation or anything happening. Let's remove it too and see what happens.
- We've got another reversed comparison, `&D_0600A280 == this->skelAnime.animation`.
- `D_0600A280` is an `AnimationHeader`.
- `(Actor *) this` should be replaced by `&this->actor`.
- `Flags_GetSwitch is a boolean and we don't need to cast the argument, as we have discussed before. (We don't know about the other functions in the conditions, so leave them for now.)
- Prototype `func_80C10290`: it is reasonable to guess it's another setup function, so `void func_80C10290(EnRecepgirl* this);`.
Changing all these, we end up with
```C
void func_80C10148(EnRecepgirl* this);
void func_80C1019C(EnRecepgirl* this, GlobalContext* globalCtx);
void func_80C10290(EnRecepgirl* this);
[...]
extern AnimationHeader D_06001384;
extern AnimationHeader D_06009890;
extern AnimationHeader D_0600A280;
extern AnimationHeader D_0600AD98;
extern FlexSkeletonHeader D_06011B60;
[...]
void func_80C1019C(EnRecepgirl *this, GlobalContext *globalCtx) {
if (SkelAnime_Update(&this->skelAnime) != 0) {
if (&D_0600A280 == this->skelAnime.animation) {
Animation_MorphToPlayOnce(&this->skelAnime, &D_0600AD98, 5.0f);
} else {
Animation_ChangeTransitionRepeat(&this->skelAnime, &D_06009890, -4.0f);
}
}
if (func_800B84D0(&this->actor, globalCtx) != 0) {
func_80C10290(this);
return;
}
if (Actor_IsActorFacingLink(&this->actor, 0x2000) != 0) {
func_800B8614(&this->actor, globalCtx, 60.0f);
if (Player_GetMask(globalCtx) == 2) {
this->actor.textId = 0x2367;
return;
}
if (Flags_GetSwitch(globalCtx, this->actor.params)) {
this->actor.textId = 0x2ADC;
return;
}
this->actor.textId = 0x2AD9;
// Duplicate return node #12. Try simplifying control flow for better match
}
}
```
If we look with diff.py, we find this matches. But we can replace some of the `return`s by `else`s: generally, we use elses unless
- After an `Actor_Kill`
- Sometimes after setting an actionfunction
- There's no way to avoid an early return
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) {
if (SkelAnime_Update(&this->skelAnime) != 0) {
if (this->skelAnime.animation == &D_0600A280) {
Animation_MorphToPlayOnce(&this->skelAnime, &D_0600AD98, 5.0f);
} else {
Animation_ChangeTransitionRepeat(&this->skelAnime, &D_06009890, -4.0f);
}
}
if (func_800B84D0(&this->actor, globalCtx) != 0) {
func_80C10290(this);
} else if (Actor_IsActorFacingLink(&this->actor, 0x2000)) {
func_800B8614(&this->actor, globalCtx, 60.0f);
if (Player_GetMask(globalCtx) == 2) {
this->actor.textId = 0x2367;
} else if (Flags_GetSwitch(globalCtx, this->actor.params)) {
this->actor.textId = 0x2ADC;
} else {
this->actor.textId = 0x2AD9;
}
}
}
```
which still matches. Lastly, we have an enum for the output of `Player_GetMask` and other mask-related things: in `z64player.h` we find
```C
typedef enum {
/* 0x00 */ PLAYER_MASK_NONE,
/* 0x01 */ PLAYER_MASK_MASK_OF_TRUTH,
/* 0x02 */ PLAYER_MASK_KAFEIS_MASK,
[...]
/* 0x19 */ PLAYER_MASK_MAX
} PlayerMask;
```
and so we can write the last if as `Player_GetMask(globalCtx) == PLAYER_MASK_KAFEIS_MASK`.
Again, we have no choice in what to do next.
## `func_80C10290`
Remaking the context and running mips2c gives
```C
void func_80C102D4(EnRecepgirl *, GlobalContext *); // extern
void func_80C10290(EnRecepgirl *this) {
Animation_MorphToPlayOnce(&this->skelAnime, &D_0600A280, -4.0f);
this->actionFunc = func_80C102D4;
}
```
so all we have to do is add the function prototype for the newest action function. Not surprisingly, this matches without changing anything.
## `func_80C102D4`
<details>
<summary>
Large code block, click to show
</summary>
```C
extern AnimationHeader D_06000968;
void func_80C102D4(EnRecepgirl *this, GlobalContext *globalCtx) {
SkelAnime *sp20;
AnimationHeader *temp_v0;
SkelAnime *temp_a0;
u16 temp_v0_3;
u16 temp_v0_4;
u8 temp_v0_2;
temp_a0 = &this->skelAnime;
sp20 = temp_a0;
if (SkelAnime_Update(temp_a0) != 0) {
temp_v0 = this->skelAnime.animation;
if (&D_0600A280 == temp_v0) {
Animation_ChangeDefaultRepeat(sp20, &D_06001384);
} else if (&D_0600AD98 == temp_v0) {
if (this->actor.textId == 0x2ADA) {
Animation_MorphToPlayOnce(sp20, &D_06000968, 10.0f);
} else {
Animation_ChangeTransitionRepeat(sp20, &D_06009890, 10.0f);
}
} else if (this->actor.textId == 0x2ADA) {
Animation_ChangeTransitionRepeat(sp20, &D_06009890, 10.0f);
} else {
Animation_MorphToPlayOnce(sp20, &D_0600A280, -4.0f);
}
}
temp_v0_2 = func_80152498(&globalCtx->msgCtx);
if (temp_v0_2 == 2) {
this->actor.textId = 0x2ADC;
func_80C10148(this);
return;
}
if (((temp_v0_2 & 0xFF) == 5) && (func_80147624(globalCtx) != 0)) {
temp_v0_3 = this->actor.textId;
if (temp_v0_3 == 0x2AD9) {
Actor_SetSwitchFlag(globalCtx, (s32) this->actor.params);
Animation_MorphToPlayOnce(sp20, &D_0600AD98, 10.0f);
if ((*(&gSaveContext + 0xF37) & 0x80) != 0) {
this->actor.textId = 0x2ADF;
} else {
this->actor.textId = 0x2ADA;
}
} else if (temp_v0_3 == 0x2ADC) {
Animation_MorphToPlayOnce(sp20, &D_0600AD98, 10.0f);
this->actor.textId = 0x2ADD;
} else {
Animation_MorphToPlayOnce(sp20, &D_06000968, 10.0f);
temp_v0_4 = this->actor.textId;
if (temp_v0_4 == 0x2ADD) {
this->actor.textId = 0x2ADE;
} else if (temp_v0_4 == 0x2ADA) {
this->actor.textId = 0x2ADB;
} else {
this->actor.textId = 0x2AE0;
}
}
func_80151938(globalCtx, this->actor.textId);
}
}
```
</details>
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) {
u8 temp_v0_2;
if (SkelAnime_Update(&this->skelAnime) != 0) {
if (this->skelAnime.animation == &D_0600A280) {
Animation_ChangeDefaultRepeat(&this->skelAnime, &D_06001384);
} else if (this->skelAnime.animation == &D_0600AD98) {
if (this->actor.textId == 0x2ADA) {
Animation_MorphToPlayOnce(&this->skelAnime, &D_06000968, 10.0f);
} else {
Animation_ChangeTransitionRepeat(&this->skelAnime, &D_06009890, 10.0f);
}
} else if (this->actor.textId == 0x2ADA) {
Animation_ChangeTransitionRepeat(&this->skelAnime, &D_06009890, 10.0f);
} else {
Animation_MorphToPlayOnce(&this->skelAnime, &D_0600A280, -4.0f);
}
}
temp_v0_2 = func_80152498(&globalCtx->msgCtx);
if (temp_v0_2 == 2) {
this->actor.textId = 0x2ADC;
func_80C10148(this);
return;
}
if (((temp_v0_2 & 0xFF) == 5) && (func_80147624(globalCtx) != 0)) {
if (this->actor.textId == 0x2AD9) {
Actor_SetSwitchFlag(globalCtx, this->actor.params);
Animation_MorphToPlayOnce(&this->skelAnime, &D_0600AD98, 10.0f);
if ((*(&gSaveContext + 0xF37) & 0x80) != 0) {
this->actor.textId = 0x2ADF;
} else {
this->actor.textId = 0x2ADA;
}
} else if (this->actor.textId == 0x2ADC) {
Animation_MorphToPlayOnce(&this->skelAnime, &D_0600AD98, 10.0f);
this->actor.textId = 0x2ADD;
} else {
Animation_MorphToPlayOnce(&this->skelAnime, &D_06000968, 10.0f);
if (this->actor.textId == 0x2ADD) {
this->actor.textId = 0x2ADE;
} else if (this->actor.textId == 0x2ADA) {
this->actor.textId = 0x2ADB;
} else {
this->actor.textId = 0x2AE0;
}
}
func_80151938(globalCtx, this->actor.textId);
}
}
```
There remains one thing we need to fix before trying to compile it, namely `*(&gSaveContext + 0xF37) & 0x80`. This is really a funny way of writing an array access, because mips2c will get confused about arrays in structs. Opening up `z64save.h`, we find in the `SaveContext` struct that
```C
/* 0x0EF8 */ u8 weekEventReg[100]; // "week_event_reg"
/* 0x0F5C */ u32 mapsVisited; // "area_arrival"
```
so it's somewhere in `weekEventReg`. `0xF37 - 0xEF8 = 0x3F = 63`, and it's a byte array, so the access is actually `gSaveContext.weekEventReg[63] & 0x80`. Now it will compile. We also don't use `!= 0` for flag comparisons: just `if (gSaveContext.weekEventReg[63] & 0x80)` will do.
Running `./diff.py -mwo3 func_80C102D4` and scrolling down, we discover that this doesn't match!
![First run of diff.py on func_80C102D4](images/func_80C102D4_diff1.png)
The yellow shows registers that don't match, the different colours on the registers help you to estimate where the problems are. Usually it's best to start at the top and work down if possible: any regalloc problems at the top tend to propagate most of the way down. In our case, the first problem is
```
3f0: andi t0,v0,0xff r 153 3f0: andi t1,v0,0xff
```
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 = func_80152498(&globalCtx->msgCtx);
if (temp_v0_2 == 2) {
this->actor.textId = 0x2ADC;
func_80C10148(this);
return;
}
if (((temp_v0_2 & 0xFF) == 5) && (func_80147624(globalCtx) != 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:
![func_80C102D4 now matching](images/func_80C102D4_diff2.png)
Notice that indeed the subsequent regalloc, which might have looked like a bigger problem than the initial part, was also fixed: skipping a register in one place will throw the registers off below too.
And now we've run out of functions. Time for `Update`.
## Update
Update runs every frame and usually is responsible for the actor's common logic updates: for example, updating timers, blinking, updating collision, running the `actionFunc`, and so on, either directly or through other functions it calls. A lot of subsidiary functions that are not common to every state (e.g. updating position, or the text when talking, etc.) are carried out by one of the action functions we have already decomped.
Remake the context and run mips2c:
```C
? func_80C100DC(EnRecepgirl *); // extern
void EnRecepgirl_Update(Actor *thisx, GlobalContext *globalCtx) {
EnRecepgirl* this = (EnRecepgirl *) thisx;
? sp30;
this->actionFunc(this, globalCtx);
func_800E9250(globalCtx, (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);
}
```
If we search for `func_80C100DC`, we find that this is the only time it is used. Hence we can be almost certain that its prototype is `void func_80C100DC(EnRecepgirl* this);`. This function occurs above `Update`, so you can put the prototype next to the `GLOBAL_ASM` and remove it when we decompile that function.
Change the function and the prototype back to `Actor* thisx`, and add the casting temp:
```C
void func_80C100DC(EnRecepgirl *);
#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Recepgirl/func_80C100DC.s")
[...]
void EnRecepgirl_Update(Actor *thisx, GlobalContext *globalCtx) {
EnRecepgirl* this = THIS;
? sp30;
this->actionFunc(this, globalCtx);
func_800E9250(globalCtx, &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 `func_800E9250`. 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 func_800E9250(GlobalContext* globalCtx, Actor* actor, Vec3s* param_3, Vec3s* param_4, Vec3f param_5)
```
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`
- `&sp30` is a `Vec3s*`, so `sp30` is a `Vec3s` (it's clearly not used for anything, just used to "dump" a side-effect of the function)
- The last argument is supposed to be an actual `Vec3f`
Fixing all of this, we end up with
```C
void EnRecepgirl_Update(EnRecepgirl *this, GlobalContext *globalCtx) {
EnRecepgirl* this = THIS;
Vec3s sp30;
this->actionFunc(this, globalCtx);
func_800E9250(globalCtx, &this->actor, &this->unk_2AE, &sp30, this->actor.focus.pos);
func_80C100DC(this);
}
```
and can fill in the top end of the struct:
```C
typedef struct EnRecepgirl {
/* 0x0000 */ Actor actor;
/* 0x0144 */ SkelAnime skelAnime;
/* 0x0188 */ Vec3s jointTable[24];
/* 0x0218 */ Vec3s morphTable[24];
/* 0x02A8 */ EnRecepgirlActionFunc actionFunc;
/* 0x02AC */ u8 unk_2AC;
/* 0x02AD */ char unk_2AD[0x1];
/* 0x02AE */ Vec3s unk_2AE;
} EnRecepgirl; // size = 0x2B4
```
It's entirely possible that `unk_2AD` is not real, and is just padding: see [Types, structs, and padding](types_structs_padding.md) for the details. We'll find out once we've finished all the functions. If we look at the diff, we find that one line is different:
![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
```C
void EnRecepgirl_Update(Actor *thisx, GlobalContext *globalCtx) {
s32 pad;
EnRecepgirl* this = THIS;
Vec3s sp30;
this->actionFunc(this, globalCtx);
func_800E9250(globalCtx, &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.
### *Some remarks about the function stack
(Feel free to skip this if you'd rather finish the actor first.)
The (function) stack is used to store variables. It has rather more space and is somewhat less volatile than registers (it still can't be used outside a function, except by a called function accessing its arguments). The stack a function sets up for itself to use is called its *stack frame* or *function frame* (or just *function stack* or *the stack*, although strictly speaking the frame itself is not a stack, since not just the top variable is accessed), and the function frames themselves form an (genuine) stack called the *call stack*. In MIPS this stack grows downwards, and its size is always a multiple of 0x8 (in case you want to put a 64-bit value on it, although almost no N64 game functions do this). The compiler uses the stack in a single function frame in the following way:
| user-defined variables |
| compiler-defined varibles |
| saved registers |
| argument registers/stack |
where sp is at the very bottom of this table, and the function that called the current function would have its frame above this one. We have seen a couple of aspects of this stack behaviour already: saving the function arguments onto it in [`EnRecepgirl_Destroy`](#destroy), and here, requiring an extre user stack variable to be the correct size.
Anyway, back to EnRecepgirl. 4 functions to go...
## `func_80C100DC`
This is the final non-draw function. You know what to do now: remake the context and run mips2c:
```C
void func_80C100DC(EnRecepgirl *this) {
u8 temp_t6;
u8 temp_v0;
temp_v0 = this->unk_2AC;
temp_t6 = temp_v0 + 1;
if (temp_v0 != 0) {
this->unk_2AC = temp_t6;
if ((temp_t6 & 0xFF) == 4) {
this->unk_2AC = 0;
return;
}
// Duplicate return node #5. Try simplifying control flow for better match
return;
}
if (Rand_ZeroOne() < 0.02f) {
this->unk_2AC += 1;
}
}
```
Well, hmm. It's pretty hard to tell what's going on here. Finally it's time to really use `diff.py`. this function is a bit more typical of what to expect: this actor has been very easy so far!
![func_80C100DC, first diff](images/func_80C100DC_diff1.png)
Well, it's still *pretty* close. But the registers are all wrong. Firstly, `temp_t6` is already `u8`, so the `& 0xFF` is again ineffective, so let's try removing it...
![func_80C100DC, second diff](images/func_80C100DC_diff2.png)
It's not obvious that did much: it even looks a bit worse.
```C
temp_v0 = this->unk_2AC;
temp_t6 = temp_v0 + 1;
if (temp_v0 != 0) {
this->unk_2AC = temp_t6;
```
may remind you of that loop we decompiled, where mips2c unnecessarily made two temps. Let's walk through what this does.
- First, it saves the value of `this->unk_2AC` into `v0`
- Then, it adds one to it and stores it in `t6`.
- It checks if the first saved value is zero
- If it is, it sets `this->unk_2AC` to the incremented value and carries on.
Well, if we allow ourselves to bend the order of operations a little, there's a much simpler way to write this with no temps, namely
```C
if (this->unk_2AC != 0) {
this->unk_2AC++;
```
So let's try removing both temps:
```C
void func_80C100DC(EnRecepgirl *this) {
if (this->unk_2AC != 0) {
this->unk_2AC++;
if (this->unk_2AC == 4) {
this->unk_2AC = 0;
return;
}
return;
}
if (Rand_ZeroOne() < 0.02f) {
this->unk_2AC++;
}
}
```
![func_80C100DC, matching](images/func_80C100DC_diff3.png)
There we go.
Even though this matches, it is not quite according to our style: remember what was said earlier about early returns. Here, both of them can be removed and replaced by a single else without affecting matching:
```C
void func_80C100DC(EnRecepgirl *this) {
if (this->unk_2AC != 0) {
this->unk_2AC++;
if (this->unk_2AC == 4) {
this->unk_2AC = 0;
}
} else if (Rand_ZeroOne() < 0.02f) {
this->unk_2AC++;
}
}
```
and this is how we prefer it to be written.
With that, the last remaining function is `EnJj_Draw`. Draw functions have an extra layer of macroing that is required, so we shall cover them separately.
Next: [Draw functions](draw_functions.md)

View File

@ -0,0 +1,159 @@
# Types, structs, and padding
Reminders:
- In N64 MIPS, 1 word is 4 bytes (yes, the N64 is meant to be 64-bit, but it mostly isn't used like it in MM or OoT)
- A byte is 8 bits, or 2 hex digits
## Types
The following are the common data types used everywhere:
| Name | Size | Comment |
| ---- | ----- | -------- |
| char | 1 byte | character |
| u8 | 1 byte | unsigned byte |
| s8 | 1 byte | signed byte |
| u16 | 2 bytes | unsigned short |
| s16 | 2 bytes | signed short |
| u32 | 4 bytes/1 word | unsigned int |
| s32 | 4 bytes/1 word | signed int |
| void* | 4 bytes/1 word | pointer |
| uintptr_t | 4 bytes/1 word | pointer^ |
| intptr_t | 4 bytes/1 word | pointer^ |
A pointer is sometimes mistaken for an `s32`. The last two, marked with `^`, are special types allowing for arithmetic on generic pointers, and are to be used over `u32`.
`s32` is the default thing to use in the absence of any other information about the data.
Useful data for guessing types:
- `u8` is about 7 times more common than `s8`
- `s16` is about 16 times more common than `u16`
- `s32` is about 8 times more common than `u32`
Another useful thing to put here: the typedef for an action function is
```C
typedef void (*ActorNameActionFunc)(struct ActorName*, GlobalContext*);
```
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.
## Some Common Structs
Here are the usual names and the sizes of some of the most common structs used in actors and their structs:
| Type | Usual name | Size |
| ----------------------- | --------------------- | --------------- |
| `Actor` | `actor` | 0x144 |
| `DynaPolyActor` | `dyna` | 0x15C |
| `Vec3f` | | 0xC |
| `Vec3s` | | 0x6 |
| `SkelAnime` | `skelAnime` | 0x44 |
| `Vec3s[limbCount]` | `jointTable` | 0x6 * limbCount |
| `Vec3s[limbCount]` | `morphTable` | 0x6 * limbCount |
| `ColliderCylinder` | `collider` | 0x4C |
| `ColliderQuad` | `collider` | 0x80 |
| `ColliderJntSph` | `collider` | 0x20 |
| `ColliderJntSphElement` | `colliderElements[n]` | 0x40 * n |
| `ColliderTris` | `collider` | 0x20 |
| `ColliderTrisElement` | `colliderElements[n]` | 0x5C * n |
Note that `Actor` and `DynaPolyActor` have changed size from OoT.
## Padding
### Alignment
A stored variable or piece of data does not always start immediately after the previous one: there may be padding in between: `0`s that are never written or referred to, and so ignored. This is to do with how the processor accesses memory: it reads 1 word at a time, so multibyte objects are aligned so they cross as few word boundaries as possible.
The clearest example of this is that variables with types that are 1 word in size (`s32`s and pointers, for example) are automatically shifted so that they start at the beginning of the next word, i.e. at an offset ending with one of `0,4,8,C`: this is called 4-alignment. This will also happen to `s16`s, but with 2-alignment
### Struct padding
In actor structs, this manifests as some of the char arrays not being completely replaced by actual variables.
```C
typedef struct EnRecepgirl {
/* 0x0000 */ Actor actor;
/* 0x0144 */ SkelAnime skelAnime;
/* 0x0188 */ Vec3s jointTable[24];
/* 0x0218 */ Vec3s morphTable[24];
/* 0x02A8 */ EnRecepgirlActionFunc actionFunc;
/* 0x02AC */ u8 unk_2AC;
/* 0x02AE */ Vec3s unk_2AE;
} EnRecepgirl; // size = 0x2B4
```
Notice that even though `unk_2AC` is a `u8`, `unk_2AE` is at `this + 0x2AE`, rather than `0x2AD`: we removed the extra char of padding while working on this actor.
How do structs themselves align? A struct has the same alignment properties as its longest constituent (that is not itself a struct). For example, a `Vec3f` has 4-alignment, while a `Vec3s` has 2-alignment.
A struct may also pad at the end: it will pad to the size of its largest non-struct element. Notably, every actor struct has size a whole number of words as well, so this phenomenon also occurs at the ends of structs. For example, ObjTree has the following actor struct:
```C
typedef struct ObjTree {
/* 0x0000 */ DynaPolyActor dyna;
/* 0x015C */ ColliderCylinder collider;
/* 0x01A8 */ ObjTreeActionFunc actionFunc;
/* 0x01AC */ f32 unk_1AC;
/* 0x01B0 */ s16 unk_1B0;
/* 0x01B2 */ s16 unk_1B2;
/* 0x01B4 */ s16 unk_1B4;
} ObjTree; // size = 0x1B8
```
The struct pads to be `0x1B8` in size even though the last actual variable ends at `0x1B6`.
For more information on this topic, there are plenty of guides elsewhere on the Internet, for example [The Lost Art of Structure Packing](http://www.catb.org/esr/structure-packing/). *The main thing to bear in mind for decomp purposes is that after finishing the functions, there may be some small parts of the actor struct that are just not used, because they were originally just struct padding.*
### Padding at the end of sections
In the ROM, each actor is layed out in the following order:
- .text (Function instructions, separated into .s files, aka .text)
- .data (contents of the .data.s file)
- .rodata (read-only data, includes strings, floats, jumptables etc., almost entirely moved to the appropriate function files in the MM repo)
- .bss (varibles initialised to 0, not assigned a value when declared)
- .reloc (relocation information: you can ignore this)
Each section is 0x10/16-aligned (qword aligned), i.e. each new section begins at an address with last digit `0`. This means that there can occur up to three words of padding at the end of each section.
(The same occurs with any object divided into multiple .c files: each new file becomes 0x10 aligned.)
#### Padding at the end of .text (function instructions)
In function instructions, this manifests as a set of `nop`s at the end of the last function: for example, in EnRecepGirl,
```
/* 0006B0 80C10680 27BD0038 */ addiu $sp, $sp, 0x38
/* 0006B4 80C10684 03E00008 */ jr $ra
/* 0006B8 80C10688 00000000 */ nop
/* 0006BC 80C1068C 00000000 */ nop
```
the second `nop` is just extra `0`s of padding, as you can see in the machine code (third column in the comment)
Once the rest of the functions match, this is automatic. So you never need to worry about these.
#### Padding at the end of .data
In data, the last entry may contain up to 3 words of 0s as padding. These can safely be removed when migrating data, but make sure that you don't remove something that actually is accessed by the function and happens to be 0!
For example, in `ObjTree` we found that the last symbol in the data,
```
glabel D_80B9A5BC
/* 00006C 80B9A5BC */ .word 0x08000000
/* 000070 80B9A5C0 */ .word 0x00000000
/* 000074 80B9A5C4 */ .word 0xFE000000
/* 000078 80B9A5C8 */ .word 0x00000000
/* 00007C 80B9A5CC */ .word 0x00000000
```
had 2 words of padding: only the first 3 words are actually used in the `CollisionCheckInfoInit2`.
### Padding within the .data section
Every distinct symbol in data is 4-aligned (word-aligned). So in the data, even if you have two `u8`s, they will be stored in addresses starting successive words:
```C
u8 byte1 = 1 // will go to address ending in 0
u8 byte2 = 2 // Will go to address ending in 4
```

84
docs/tutorial/vscode.md Normal file
View File

@ -0,0 +1,84 @@
# VSCode
A lot of people on this project use VSCode as their coding environment.
## Extensions
There are a number of useful extensions available to make work more efficient:
- C/C++ IntelliSense
- Clang-Format
- HexInspector (hover on numbers for float and other info)
- NumberMonger (convert hex to decimal and vice versa)
- ~~bracket pair colorizer 2~~ (now obsolete due to VSCode's built-in bracket colouring)
- Better MIPS Support
## Useful stuff to know:
- Ctrl + Alt + Up/Down (on Windows, on Linux it's Ctrl + Shift + Up/Down or Shift + Alt + Up/Down) gives multicursors across consecutive lines. If you want several cursors in a more diverse arrangement, middle clicking works, at least on Windows.
- Alt + Up/Down moves lines up/down.
- Shift + Alt + Up/Down (Linux: Ctrl + Shift + Alt + Up/Down) copies lines up/down.
- Ctrl + P offers a box to use to search for and open files.
- Ctrl + Shift + P offers a box for commands like editing settings or reloading the window.
- Make use of VSCode's search/search-and-replace features.
- Ctrl + Click goes to a definition.
- Ctrl + F for search in current file
- Ctrl + H for replace in current file
- Ctrl + Shift + F for search in all files
- Ctrl + Shift + H for replace in all files
- F2 for Rename symbol
Many of VS Code's other shortcuts can be found on [its getting started page](https://code.visualstudio.com/docs/getstarted/keybindings), which also has links to OS-specific PDFs.
## C/C++ configuration
You can create a `.vscode/c_cpp_properties.json` file with `C/C++: Edit Configurations (JSON)` in the command box to customise how IntelliSense reads the repository (stuff like where to look for includes, flags, compiler defines, etc.) to make VSCode's IntelliSense plugin better able to understand the structure of the repository. This is a good default one to use for this project's repository:
```jsonc
{
"configurations": [
{
"name": "Linux",
"compilerPath": "${default}", // Needs to not be "" for -m32 to work
"compilerArgs": [
"-m32" // Removes integer truncation warnings with gbi macros
],
"intelliSenseMode": "${default}", // Shouldn't matter
"includePath": [ // Matches makefile's includes
"${workspaceFolder}/**",
"src",
"assets",
"build",
"include"
],
"defines": [
"_LANGUAGE_C" // For gbi.h
],
"cStandard": "gnu89", // C89 + some GNU extensions from C99 like C++ comments
"cppStandard": "${default}" // Only ZAPD uses C++, so doesn't really matter
}
],
"version": 4
}
```
## Settings
Add the following to (or create) the `.vscode/settings.json` file for VSCode to search the gitignored asset and assembly files by default:
```jsonc
{
"search.useIgnoreFiles": false,
"search.exclude": {
"**/.git": true,
"baserom/**": true,
"build/**": true,
"expected/**": true,
"nonmatchings/**": true,
},
}
```

275
docs/useful_conversions.md Normal file
View File

@ -0,0 +1,275 @@
# Useful conversions
This article contains some useful conversion tables. Beware that we will omit the `0x` prefix on hex numbers: it will be evident from context which base is intended.
- [Degrees and hex/binary angles](#degrees-and-hex-binary-angles)
* [Small angles](#small-angles)
* [Larger angles](#larger-angles)
- [Round decimal numbers in hex](#round-decimal-numbers-in-hex)
* [Small](#small)
* [Medium](#medium)
* [Large](#large)
- [Extra large](#extra-large)
- [Shifts/powers of 2 in dec and hex](#shifts-powers-of-2-in-dec-and-hex)
## Degrees and hex/binary angles
Conversion of degrees to binary angles in the two common ways, that give different answers. Table is produced using this script:
```bash
$ printf "%s\t%s\t%s\n" "d" "d * 2^16/360" "2^16/360 * d" ; for i in {0..360..5} ; do printf "%d\t%X\t\t%X\n" "$i" $(( "$i" * 0x10000 / 360 )) $(( 0x10000 / 360 * "$i" )) ; done
```
### Small angles
| `d` | `d * 2^16/360` | `2^16/360 * d` |
| ----: | -------------: | -------------: |
| 0 | 0 | 0 |
| 1 | B6 | B6 |
| 2 | 16C | 16C |
| 3 | 222 | 222 |
| 4 | 2D8 | 2D8 |
| 5 | 38E | 38E |
| 6 | 444 | 444 |
| 7 | 4FA | 4FA |
| 8 | 5B0 | 5B0 |
| 9 | 666 | 666 |
| 10 | 71C | 71C |
| 11 | 7D2 | 7D2 |
| 12 | 888 | 888 |
| 13 | 93E | 93E |
| 14 | 9F4 | 9F4 |
| 15 | AAA | AAA |
| 16 | B60 | B60 |
| 17 | C16 | C16 |
| 18 | CCC | CCC |
| 19 | D82 | D82 |
| 20 | E38 | E38 |
| 21 | EEE | EEE |
| 22 | FA4 | FA4 |
| 23 | 105B | 105A |
| 24 | 1111 | 1110 |
| 25 | 11C7 | 11C6 |
| 26 | 127D | 127C |
| 27 | 1333 | 1332 |
| 28 | 13E9 | 13E8 |
| 29 | 149F | 149E |
| 30 | 1555 | 1554 |
### Larger angles
| `d` | `d * 2^16/360` | `2^16/360 * d` |
| -----: | -------------: | -------------: |
| 0 | 0 | 0 |
| 5 | 38E | 38E |
| 10 | 71C | 71C |
| 15 | AAA | AAA |
| 20 | E38 | E38 |
| 25 | 11C7 | 11C6 |
| 30 | 1555 | 1554 |
| 35 | 18E3 | 18E2 |
| 40 | 1C71 | 1C70 |
| 45 | 2000 | 1FFE |
| 50 | 238E | 238C |
| 55 | 271C | 271A |
| 60 | 2AAA | 2AA8 |
| 65 | 2E38 | 2E36 |
| 70 | 31C7 | 31C4 |
| 75 | 3555 | 3552 |
| 80 | 38E3 | 38E0 |
| 85 | 3C71 | 3C6E |
| 90 | 4000 | 3FFC |
| 95 | 438E | 438A |
| 100 | 471C | 4718 |
| 105 | 4AAA | 4AA6 |
| 110 | 4E38 | 4E34 |
| 115 | 51C7 | 51C2 |
| 120 | 5555 | 5550 |
| 125 | 58E3 | 58DE |
| 130 | 5C71 | 5C6C |
| 135 | 6000 | 5FFA |
| 140 | 638E | 6388 |
| 145 | 671C | 6716 |
| 150 | 6AAA | 6AA4 |
| 155 | 6E38 | 6E32 |
| 160 | 71C7 | 71C0 |
| 165 | 7555 | 754E |
| 170 | 78E3 | 78DC |
| 175 | 7C71 | 7C6A |
| 180 | 8000 | 7FF8 |
| 185 | 838E | 8386 |
| 190 | 871C | 8714 |
| 195 | 8AAA | 8AA2 |
| 200 | 8E38 | 8E30 |
| 205 | 91C7 | 91BE |
| 210 | 9555 | 954C |
| 215 | 98E3 | 98DA |
| 220 | 9C71 | 9C68 |
| 225 | A000 | 9FF6 |
| 230 | A38E | A384 |
| 235 | A71C | A712 |
| 240 | AAAA | AAA0 |
| 245 | AE38 | AE2E |
| 250 | B1C7 | B1BC |
| 255 | B555 | B54A |
| 260 | B8E3 | B8D8 |
| 265 | BC71 | BC66 |
| 270 | C000 | BFF4 |
| 275 | C38E | C382 |
| 280 | C71C | C710 |
| 285 | CAAA | CA9E |
| 290 | CE38 | CE2C |
| 295 | D1C7 | D1BA |
| 300 | D555 | D548 |
| 305 | D8E3 | D8D6 |
| 310 | DC71 | DC64 |
| 315 | E000 | DFF2 |
| 320 | E38E | E380 |
| 325 | E71C | E70E |
| 330 | EAAA | EA9C |
| 335 | EE38 | EE2A |
| 340 | F1C7 | F1B8 |
| 345 | F555 | F546 |
| 350 | F8E3 | F8D4 |
| 355 | FC71 | FC62 |
| 360 | 10000 | FFF0 |
Similarly for small angles with a smaller increment:
## Round decimal numbers in hex
```bash
printf "%s\t%s\n" "dec" "hex" ; for i in {0..100..5} ; do printf "%d\t%X\n" "$i" "$i" ; done
```
### Small
| dec | hex |
| -----: | --: |
| 0 | 0 |
| 5 | 5 |
| 10 | A |
| 15 | F |
| 20 | 14 |
| 25 | 19 |
| 30 | 1E |
| 35 | 23 |
| 40 | 28 |
| 45 | 2D |
| 50 | 32 |
| 55 | 37 |
| 60 | 3C |
| 65 | 41 |
| 70 | 46 |
| 75 | 4B |
| 80 | 50 |
| 85 | 55 |
| 90 | 5A |
| 95 | 5F |
| 100 | 64 |
### Medium
| dec | hex |
| -----: | ---: |
| 100 | 64 |
| 150 | 96 |
| 200 | C8 |
| 250 | FA |
| 300 | 12C |
| 350 | 15E |
| 400 | 190 |
| 450 | 1C2 |
| 500 | 1F4 |
| 550 | 226 |
| 600 | 258 |
| 650 | 28A |
| 700 | 2BC |
| 750 | 2EE |
| 800 | 320 |
| 850 | 352 |
| 900 | 384 |
| 950 | 3B6 |
| 1000 | 3E8 |
### Large
| dec | hex |
| -----: | ----: |
| 1000 | 3E8 |
| 1500 | 5DC |
| 2000 | 7D0 |
| 2500 | 9C4 |
| 3000 | BB8 |
| 3500 | DAC |
| 4000 | FA0 |
| 4500 | 1194 |
| 5000 | 1388 |
| 5500 | 157C |
| 6000 | 1770 |
| 6500 | 1964 |
| 7000 | 1B58 |
| 7500 | 1D4C |
| 8000 | 1F40 |
| 8500 | 2134 |
| 9000 | 2328 |
| 9500 | 251C |
| 10000 | 2710 |
## Extra large
| dec | hex |
| -----: | ----: |
| 10000 | 2710 |
| 11000 | 2AF8 |
| 12000 | 2EE0 |
| 13000 | 32C8 |
| 14000 | 36B0 |
| 15000 | 3A98 |
| 16000 | 3E80 |
| 17000 | 4268 |
| 18000 | 4650 |
| 19000 | 4A38 |
| 20000 | 4E20 |
| 21000 | 5208 |
| 22000 | 55F0 |
| 23000 | 59D8 |
| 24000 | 5DC0 |
| 25000 | 61A8 |
| 26000 | 6590 |
| 27000 | 6978 |
| 28000 | 6D60 |
| 29000 | 7148 |
| 30000 | 7530 |
## Shifts/powers of 2 in dec and hex
```bash
$ printf "%s\t%s\t%s\n" "n" "1 << n (hex)" "1 << n (dec)" ; for i in {0..15..1} ; do printf "%d\t%X\t\t%d\n" "$i" $(( 1 << "$i" )) $(( 1 << "$i" )) ; done
```
`1 << n` is the same as `2^n`.
| `n` | `1 << n` (hex) | `1 << n` (dec) |
| -----: | -------------: | -------------: |
| 0 | 1 | 1 |
| 1 | 2 | 2 |
| 2 | 4 | 4 |
| 3 | 8 | 8 |
| 4 | 10 | 16 |
| 5 | 20 | 32 |
| 6 | 40 | 64 |
| 7 | 80 | 128 |
| 8 | 100 | 256 |
| 9 | 200 | 512 |
| 10 | 400 | 1024 |
| 11 | 800 | 2048 |
| 12 | 1000 | 4096 |
| 13 | 2000 | 8192 |
| 14 | 4000 | 16384 |
| 15 | 8000 | 32768 |

View File

@ -57,7 +57,7 @@ typedef enum {
/* 0x07 */ SLOT_BOMBCHU,
/* 0x08 */ SLOT_STICK,
/* 0x09 */ SLOT_NUT,
/* 0x0A */ SLOT_BEAN,
/* 0x0A */ SLOT_MAGIC_BEANS,
/* 0x0B */ SLOT_TRADE_KEY_MAMA,
/* 0x0C */ SLOT_POWDER_KEG,
/* 0x0D */ SLOT_PICTO_BOX,
@ -85,7 +85,7 @@ typedef enum {
/* 0x23 */ SLOT_MASK_GORON,
/* 0x24 */ SLOT_MASK_ROMANI,
/* 0x25 */ SLOT_MASK_CIRCUS_LEADER,
/* 0x26 */ SLOT_MASK_KAFEI,
/* 0x26 */ SLOT_MASK_KAFEIS_MASK,
/* 0x27 */ SLOT_MASK_COUPLE,
/* 0x28 */ SLOT_MASK_TRUTH,
/* 0x29 */ SLOT_MASK_ZORA,
@ -109,7 +109,7 @@ typedef enum {
/* 0x07 */ ITEM_BOMBCHU,
/* 0x08 */ ITEM_STICK,
/* 0x09 */ ITEM_NUT,
/* 0x0A */ ITEM_BEAN,
/* 0x0A */ ITEM_MAGIC_BEANS,
/* 0x0B */ ITEM_SLINGSHOT,
/* 0x0C */ ITEM_POWDER_KEG,
/* 0x0D */ ITEM_PICTO_BOX,
@ -146,7 +146,7 @@ typedef enum {
/* 0x2C */ ITEM_DEED_OCEAN,
/* 0x2D */ ITEM_ROOM_KEY,
/* 0x2E */ ITEM_LETTER_MAMA,
/* 0x2F */ ITEM_LETTER_KAFEI,
/* 0x2F */ ITEM_LETTER_TO_KAFEI,
/* 0x30 */ ITEM_PENDANT_MEMORIES,
/* 0x31 */ ITEM_TINGLE_MAP,
/* 0x32 */ ITEM_MASK_DEKU,
@ -154,7 +154,7 @@ typedef enum {
/* 0x34 */ ITEM_MASK_ZORA,
/* 0x35 */ ITEM_MASK_FIERCE_DEITY,
/* 0x36 */ ITEM_MASK_TRUTH,
/* 0x37 */ ITEM_MASK_KAFEI,
/* 0x37 */ ITEM_MASK_KAFEIS_MASK,
/* 0x38 */ ITEM_MASK_ALL_NIGHT,
/* 0x39 */ ITEM_MASK_BUNNY,
/* 0x3A */ ITEM_MASK_KEATON,
@ -294,6 +294,7 @@ typedef enum {
/* 0x2A */ GI_NUTS_10 = 0x2A,
/* 0x32 */ GI_SHIELD_HERO = 0x32,
/* 0x33 */ GI_SHIELD_MIRROR,
/* 0x35 */ GI_MAGIC_BEANS = 0x35,
/* 0x3C */ GI_KEY_SMALL = 0x3C,
/* 0x3E */ GI_MAP = 0x3E,
/* 0x3F */ GI_COMPASS,
@ -308,7 +309,7 @@ typedef enum {
/* 0x7A */ GI_MASK_ZORA,
/* 0x7B */ GI_MASK_FIERCE_DEITY,
/* 0x7C */ GI_MASK_TRUTH,
/* 0x7D */ GI_MASK_KAFEI,
/* 0x7D */ GI_MASK_KAFEIS_MASK,
/* 0x7E */ GI_MASK_ALL_NIGHT,
/* 0x7F */ GI_MASK_BUNNY,
/* 0x80 */ GI_MASK_KEATON,

View File

@ -207,7 +207,7 @@ typedef enum {
/* 0x0C3 */ OBJECT_GI_SHIELD_3,
/* 0x0C4 */ OBJECT_UNSET_C4,
/* 0x0C5 */ OBJECT_UNSET_C5,
/* 0x0C6 */ OBJECT_GI_BEAN,
/* 0x0C6 */ OBJECT_GI_MAGIC_BEANS,
/* 0x0C7 */ OBJECT_GI_FISH,
/* 0x0C8 */ OBJECT_UNSET_C8,
/* 0x0C9 */ OBJECT_UNSET_C9,

View File

@ -36,7 +36,7 @@ typedef enum {
typedef enum {
/* 0x00 */ PLAYER_MASK_NONE,
/* 0x01 */ PLAYER_MASK_TRUTH,
/* 0x02 */ PLAYER_MASK_KAFEI,
/* 0x02 */ PLAYER_MASK_KAFEIS_MASK,
/* 0x03 */ PLAYER_MASK_ALL_NIGHT,
/* 0x04 */ PLAYER_MASK_BUNNY,
/* 0x05 */ PLAYER_MASK_KEATON,
@ -106,15 +106,15 @@ typedef enum {
/* 0x2A */ PLAYER_AP_MOON_TEAR,
/* 0x2B */ PLAYER_AP_DEED_LAND,
/* 0x2C */ PLAYER_AP_ROOM_KEY,
/* 0x2D */ PLAYER_AP_LETTER_KAFEI,
/* 0x2E */ PLAYER_AP_BEAN,
/* 0x2D */ PLAYER_AP_LETTER_TO_KAFEI,
/* 0x2E */ PLAYER_AP_MAGIC_BEANS,
/* 0x2F */ PLAYER_AP_DEED_SWAMP,
/* 0x30 */ PLAYER_AP_DEED_MOUNTAIN,
/* 0x31 */ PLAYER_AP_DEED_OCEAN,
/* 0x33 */ PLAYER_AP_LETTER_MAMA = 0x33,
/* 0x36 */ PLAYER_AP_PENDANT_MEMORIES = 0x36,
/* 0x3A */ PLAYER_AP_MASK_TRUTH = 0x3A,
/* 0x3B */ PLAYER_AP_MASK_KAFEI,
/* 0x3B */ PLAYER_AP_MASK_KAFEIS_MASK,
/* 0x3C */ PLAYER_AP_MASK_ALL_NIGHT,
/* 0x3D */ PLAYER_AP_MASK_BUNNY,
/* 0x3E */ PLAYER_AP_MASK_KEATON,

9
spec
View File

@ -1627,8 +1627,7 @@ beginseg
name "ovl_En_Ms"
compress
include "build/src/overlays/actors/ovl_En_Ms/z_en_ms.o"
include "build/data/ovl_En_Ms/ovl_En_Ms.data.o"
include "build/data/ovl_En_Ms/ovl_En_Ms.reloc.o"
include "build/src/overlays/actors/ovl_En_Ms/ovl_En_Ms_reloc.o"
endseg
beginseg
@ -4457,8 +4456,7 @@ beginseg
name "ovl_Obj_Tree"
compress
include "build/src/overlays/actors/ovl_Obj_Tree/z_obj_tree.o"
include "build/data/ovl_Obj_Tree/ovl_Obj_Tree.data.o"
include "build/data/ovl_Obj_Tree/ovl_Obj_Tree.reloc.o"
include "build/src/overlays/actors/ovl_Obj_Tree/ovl_Obj_Tree_reloc.o"
endseg
beginseg
@ -5264,8 +5262,7 @@ beginseg
name "ovl_En_Recepgirl"
compress
include "build/src/overlays/actors/ovl_En_Recepgirl/z_en_recepgirl.o"
include "build/data/ovl_En_Recepgirl/ovl_En_Recepgirl.data.o"
include "build/data/ovl_En_Recepgirl/ovl_En_Recepgirl.reloc.o"
include "build/src/overlays/actors/ovl_En_Recepgirl/ovl_En_Recepgirl_reloc.o"
endseg
beginseg

View File

@ -45,7 +45,7 @@ void* gItemIcons[] = {
0x08007000, // ITEM_BOMBCHU
0x08008000, // ITEM_STICK
0x08009000, // ITEM_NUT
0x0800A000, // ITEM_BEAN
0x0800A000, // ITEM_MAGIC_BEANS
0x0800B000, // ITEM_SLINGSHOT
0x0800C000, // ITEM_POWDER_KEG
0x0800D000, // ITEM_PICTO_BOX
@ -82,7 +82,7 @@ void* gItemIcons[] = {
0x0802C000, // ITEM_DEED_OCEAN
0x0802D000, // ITEM_ROOM_KEY
0x0802E000, // ITEM_LETTER_MAMA
0x0802F000, // ITEM_LETTER_KAFEI
0x0802F000, // ITEM_LETTER_TO_KAFEI
0x08030000, // ITEM_PENDANT_MEMORIES
0x08031000, // ITEM_TINGLE_MAP
0x08032000, // ITEM_MASK_DEKU
@ -90,7 +90,7 @@ void* gItemIcons[] = {
0x08034000, // ITEM_MASK_ZORA
0x08035000, // ITEM_MASK_FIERCE_DEITY
0x08036000, // ITEM_MASK_TRUTH
0x08037000, // ITEM_MASK_KAFEI
0x08037000, // ITEM_MASK_KAFEIS_MASK
0x08038000, // ITEM_MASK_ALL_NIGHT
0x08039000, // ITEM_MASK_BUNNY
0x0803A000, // ITEM_MASK_KEATON
@ -180,7 +180,7 @@ u8 gItemSlots[] = {
SLOT_BOMBCHU, // ITEM_BOMBCHU
SLOT_STICK, // ITEM_STICK
SLOT_NUT, // ITEM_NUT
SLOT_BEAN, // ITEM_BEAN
SLOT_MAGIC_BEANS, // ITEM_MAGIC_BEANS
SLOT_TRADE_KEY_MAMA, // ITEM_SLINGSHOT
SLOT_POWDER_KEG, // ITEM_POWDER_KEG
SLOT_PICTO_BOX, // ITEM_PICTO_BOX
@ -217,7 +217,7 @@ u8 gItemSlots[] = {
SLOT_TRADE_DEED, // ITEM_DEED_OCEAN
SLOT_TRADE_KEY_MAMA, // ITEM_ROOM_KEY
SLOT_TRADE_KEY_MAMA, // ITEM_LETTER_MAMA
SLOT_TRADE_COUPLE, // ITEM_LETTER_KAFEI
SLOT_TRADE_COUPLE, // ITEM_LETTER_TO_KAFEI
SLOT_TRADE_COUPLE, // ITEM_PENDANT_MEMORIES
SLOT_TRADE_COUPLE, // ITEM_TINGLE_MAP
SLOT_MASK_DEKU, // ITEM_MASK_DEKU
@ -225,7 +225,7 @@ u8 gItemSlots[] = {
SLOT_MASK_ZORA, // ITEM_MASK_ZORA
SLOT_MASK_FIERCE_DEITY, // ITEM_MASK_FIERCE_DEITY
SLOT_MASK_TRUTH, // ITEM_MASK_TRUTH
SLOT_MASK_KAFEI, // ITEM_MASK_KAFEI
SLOT_MASK_KAFEIS_MASK, // ITEM_MASK_KAFEIS_MASK
SLOT_MASK_ALL_NIGHT, // ITEM_MASK_ALL_NIGHT
SLOT_MASK_BUNNY, // ITEM_MASK_BUNNY
SLOT_MASK_KEATON, // ITEM_MASK_KEATON
@ -260,7 +260,7 @@ s16 gItemPrices[] = {
0, // ITEM_BOMBCHU
0, // ITEM_STICK
0, // ITEM_NUT
0, // ITEM_BEAN
0, // ITEM_MAGIC_BEANS
0, // ITEM_SLINGSHOT
0, // ITEM_POWDER_KEG
0, // ITEM_PICTO_BOX

View File

@ -3,7 +3,7 @@
/**
* Indices of the columns of this array:
* - index 0x00: PLAYER_MASK_TRUTH
* - index 0x01: PLAYER_MASK_KAFEI
* - index 0x01: PLAYER_MASK_KAFEIS_MASK
* - index 0x02: PLAYER_MASK_ALL_NIGHT
* - index 0x03: PLAYER_MASK_BUNNY
* - index 0x04: PLAYER_MASK_KEATON

View File

@ -144,7 +144,7 @@ void func_809CD028(EnBji01* this, GlobalContext* globalCtx) {
}
break;
case PLAYER_FORM_HUMAN:
if (Player_GetMask(globalCtx) == PLAYER_MASK_KAFEI) {
if (Player_GetMask(globalCtx) == PLAYER_MASK_KAFEIS_MASK) {
this->textId = 0x236A;
} else if (gSaveContext.weekEventReg[74] & 0x10) {
this->textId = 0x5F6;

View File

@ -184,7 +184,7 @@ void func_809438F8(EnDaiku* this, GlobalContext* globalCtx) {
s32 day = gSaveContext.day - 1;
s32 pad2;
if (Player_GetMask(globalCtx) == PLAYER_MASK_KAFEI) {
if (Player_GetMask(globalCtx) == PLAYER_MASK_KAFEIS_MASK) {
if (this->unk_278 == ENDAIKU_PARAMS_FF_1) {
this->actor.textId = 0x2365;
} else {

View File

@ -130,7 +130,7 @@ u16 EnFsn_GetWelcome(GlobalContext* globalCtx) {
case PLAYER_MASK_GORON:
case PLAYER_MASK_ZORA:
return 0x29FD;
case PLAYER_MASK_KAFEI:
case PLAYER_MASK_KAFEIS_MASK:
return 0x2364;
default:
return 0x29FE;

View File

@ -1140,7 +1140,7 @@ void EnMaYto_DefaultStartDialogue(EnMaYto* this, GlobalContext* globalCtx) {
this->textId = 0x235E;
break;
case PLAYER_MASK_KAFEI:
case PLAYER_MASK_KAFEIS_MASK:
EnMaYto_SetFaceExpression(this, 1, 2);
func_801518B0(globalCtx, 0x235F, &this->actor);
this->textId = 0x235F;
@ -1198,7 +1198,7 @@ void EnMaYto_DinnerStartDialogue(EnMaYto* this, GlobalContext* globalCtx) {
this->textId = 0x235E;
break;
case PLAYER_MASK_KAFEI:
case PLAYER_MASK_KAFEIS_MASK:
func_801518B0(globalCtx, 0x235F, &this->actor);
this->textId = 0x235F;
break;

View File

@ -1,7 +1,7 @@
/*
* File: z_en_ms.c
* Overlay: ovl_En_Ms
* Description: Bean salesman
* Description: Bean Seller
*/
#include "z_en_ms.h"
@ -15,7 +15,11 @@ void EnMs_Destroy(Actor* thisx, GlobalContext* globalCtx);
void EnMs_Update(Actor* thisx, GlobalContext* globalCtx);
void EnMs_Draw(Actor* thisx, GlobalContext* globalCtx);
#if 0
void EnMs_Wait(EnMs* this, GlobalContext* globalCtx);
void EnMs_Talk(EnMs* this, GlobalContext* globalCtx);
void EnMs_Sell(EnMs* this, GlobalContext* globalCtx);
void EnMs_TalkAfterPurchase(EnMs* this, GlobalContext* globalCtx);
const ActorInit En_Ms_InitVars = {
ACTOR_EN_MS,
ACTORCAT_NPC,
@ -28,38 +32,156 @@ const ActorInit En_Ms_InitVars = {
(ActorFunc)EnMs_Draw,
};
// static ColliderCylinderInitType1 sCylinderInit = {
static ColliderCylinderInitType1 D_80952BA0 = {
{ COLTYPE_NONE, AT_NONE, AC_ON | AC_TYPE_PLAYER, OC1_ON | OC1_TYPE_ALL, COLSHAPE_CYLINDER, },
{ ELEMTYPE_UNK0, { 0x00000000, 0x00, 0x00 }, { 0xF7CFFFFF, 0x00, 0x00 }, TOUCH_NONE | TOUCH_SFX_NORMAL, BUMP_ON, OCELEM_ON, },
static ColliderCylinderInitType1 sCylinderInit = {
{
COLTYPE_NONE,
AT_NONE,
AC_ON | AC_TYPE_PLAYER,
OC1_ON | OC1_TYPE_ALL,
COLSHAPE_CYLINDER,
},
{
ELEMTYPE_UNK0,
{ 0x00000000, 0x00, 0x00 },
{ 0xF7CFFFFF, 0x00, 0x00 },
TOUCH_NONE | TOUCH_SFX_NORMAL,
BUMP_ON,
OCELEM_ON,
},
{ 22, 37, 0, { 0, 0, 0 } },
};
// static InitChainEntry sInitChain[] = {
static InitChainEntry D_80952BCC[] = {
static InitChainEntry sInitChain[] = {
ICHAIN_U8(targetMode, 2, ICHAIN_CONTINUE),
ICHAIN_F32(targetArrowOffset, 500, ICHAIN_STOP),
};
#endif
extern AnimationHeader D_060005EC;
extern FlexSkeletonHeader D_06003DC0;
extern ColliderCylinderInit D_80952BA0;
extern InitChainEntry D_80952BCC[];
void EnMs_Init(Actor* thisx, GlobalContext* globalCtx) {
EnMs* this = THIS;
extern UNK_TYPE D_060005EC;
Actor_ProcessInitChain(thisx, sInitChain);
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, &sCylinderInit);
ActorShape_Init(&this->actor.shape, 0.0f, func_800B3FC0, 35.0f);
Actor_SetScale(&this->actor, 0.015f);
this->actor.colChkInfo.mass = MASS_IMMOVABLE; // Eating Magic Beans all day will do that to you
this->actionFunc = EnMs_Wait;
this->actor.speedXZ = 0.0f;
this->actor.velocity.y = 0.0f;
this->actor.gravity = -1.0f;
}
#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Ms/EnMs_Init.s")
void EnMs_Destroy(Actor* thisx, GlobalContext* globalCtx) {
EnMs* this = THIS;
#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Ms/EnMs_Destroy.s")
Collider_DestroyCylinder(globalCtx, &this->collider);
}
#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Ms/func_80952734.s")
void EnMs_Wait(EnMs* this, GlobalContext* globalCtx) {
s16 yawDiff = this->actor.yawTowardsPlayer - this->actor.shape.rot.y;
#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Ms/func_809527F8.s")
if (gSaveContext.inventory.items[SLOT_MAGIC_BEANS] == ITEM_NONE) {
this->actor.textId = 0x92E; // "[...] You're the first customer [...]"
} else {
this->actor.textId = 0x932; // "[...] So you liked my Magic Beans [...]"
}
#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Ms/func_809529AC.s")
if (func_800B84D0(&this->actor, globalCtx) != 0) {
this->actionFunc = EnMs_Talk;
} else if ((this->actor.xzDistToPlayer < 90.0f) && (ABS_ALT(yawDiff) < 0x2000)) {
func_800B8614(&this->actor, globalCtx, 90.0f);
}
}
#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Ms/func_80952A1C.s")
void EnMs_Talk(EnMs* this, GlobalContext* globalCtx) {
switch (func_80152498(&globalCtx->msgCtx)) {
case 6:
if (func_80147624(globalCtx) != 0) {
this->actionFunc = EnMs_Wait;
}
break;
#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Ms/EnMs_Update.s")
case 5:
if (func_80147624(globalCtx) != 0) {
func_801477B4(globalCtx);
func_800B8A1C(&this->actor, globalCtx, GI_MAGIC_BEANS, this->actor.xzDistToPlayer,
this->actor.playerHeightRel);
this->actionFunc = EnMs_Sell;
}
break;
#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Ms/EnMs_Draw.s")
case 4:
if (func_80147624(globalCtx) != 0) {
switch (globalCtx->msgCtx.choiceIndex) {
case 0: // yes
func_801477B4(globalCtx);
if (gSaveContext.rupees < 10) {
play_sound(NA_SE_SY_ERROR);
func_80151938(globalCtx, 0x935); // "[...] You don't have enough Rupees."
} else if (AMMO(ITEM_MAGIC_BEANS) >= 20) {
play_sound(NA_SE_SY_ERROR);
func_80151938(globalCtx, 0x937); // "[...] You can't carry anymore."
} else {
func_8019F208();
func_800B8A1C(&this->actor, globalCtx, GI_MAGIC_BEANS, 90.0f, 10.0f);
func_801159EC(-10);
this->actionFunc = EnMs_Sell;
}
break;
case 1: // no
default:
func_8019F230();
func_80151938(globalCtx, 0x934); // "[...] Well, if your mood changes [...]"
break;
}
}
break;
default:
break;
}
}
void EnMs_Sell(EnMs* this, GlobalContext* globalCtx) {
if (Actor_HasParent(&this->actor, globalCtx)) {
this->actor.textId = 0;
func_800B8500(&this->actor, globalCtx, this->actor.xzDistToPlayer, this->actor.playerHeightRel, 0);
this->actionFunc = EnMs_TalkAfterPurchase;
} else {
func_800B8A1C(&this->actor, globalCtx, GI_MAGIC_BEANS, this->actor.xzDistToPlayer, this->actor.playerHeightRel);
}
}
void EnMs_TalkAfterPurchase(EnMs* this, GlobalContext* globalCtx) {
if (func_800B84D0(&this->actor, globalCtx)) {
func_80151938(globalCtx, 0x936); // "You can plant 'em whenever you want [...]"
this->actionFunc = EnMs_Talk;
} else {
func_800B8500(&this->actor, globalCtx, this->actor.xzDistToPlayer, this->actor.playerHeightRel, -1);
}
}
void EnMs_Update(Actor* thisx, GlobalContext* globalCtx) {
s32 pad;
EnMs* this = THIS;
Actor_SetHeight(&this->actor, 20.0f);
this->actor.targetArrowOffset = 500.0f;
Actor_SetScale(&this->actor, 0.015f);
SkelAnime_Update(&this->skelAnime);
this->actionFunc(this, globalCtx);
Collider_UpdateCylinder(&this->actor, &this->collider);
CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base);
}
void EnMs_Draw(Actor* thisx, GlobalContext* globalCtx) {
EnMs* this = THIS;
func_8012C28C(globalCtx->state.gfxCtx);
SkelAnime_DrawFlexOpa(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount,
NULL, NULL, &this->actor);
}

View File

@ -8,10 +8,12 @@ struct EnMs;
typedef void (*EnMsActionFunc)(struct EnMs*, GlobalContext*);
typedef struct EnMs {
/* 0x0000 */ Actor actor;
/* 0x0144 */ char unk_144[0xB0];
/* 0x01F4 */ EnMsActionFunc actionFunc;
/* 0x01F8 */ char unk_1F8[0x4C];
/* 0x000 */ Actor actor;
/* 0x144 */ SkelAnime skelAnime;
/* 0x188 */ Vec3s jointTable[9];
/* 0x1BE */ Vec3s morphTable[9];
/* 0x1F4 */ EnMsActionFunc actionFunc;
/* 0x1F8 */ ColliderCylinder collider;
} EnMs; // size = 0x244
extern const ActorInit En_Ms_InitVars;

View File

@ -144,7 +144,7 @@ void EnMuto_Idle(EnMuto* this, GlobalContext* globalCtx) {
if (1) {} // Needed to match
if (!this->isInMayorsRoom && Player_GetMask(globalCtx) == PLAYER_MASK_KAFEI) {
if (!this->isInMayorsRoom && Player_GetMask(globalCtx) == PLAYER_MASK_KAFEIS_MASK) {
this->actor.textId = 0x2363;
}

View File

@ -671,7 +671,7 @@ UNK_TYPE* func_80AF8540(EnPm* this, GlobalContext* globalCtx) {
return D_80AFB650;
default:
if (Player_GetMask(globalCtx) == PLAYER_MASK_KAFEI) {
if (Player_GetMask(globalCtx) == PLAYER_MASK_KAFEIS_MASK) {
return D_80AFB744;
}

View File

@ -15,7 +15,11 @@ void EnRecepgirl_Destroy(Actor* thisx, GlobalContext* globalCtx);
void EnRecepgirl_Update(Actor* thisx, GlobalContext* globalCtx);
void EnRecepgirl_Draw(Actor* thisx, GlobalContext* globalCtx);
#if 0
void EnRecepgirl_SetupWait(EnRecepgirl* this);
void EnRecepgirl_Wait(EnRecepgirl* this, GlobalContext* globalCtx);
void EnRecepgirl_SetupTalk(EnRecepgirl* this);
void EnRecepgirl_Talk(EnRecepgirl* this, GlobalContext* globalCtx);
const ActorInit En_Recepgirl_InitVars = {
ACTOR_EN_RECEPGIRL,
ACTORCAT_NPC,
@ -28,38 +32,193 @@ const ActorInit En_Recepgirl_InitVars = {
(ActorFunc)EnRecepgirl_Draw,
};
// static InitChainEntry sInitChain[] = {
static InitChainEntry D_80C106C0[] = {
extern TexturePtr D_0600F8F0;
extern TexturePtr D_0600FCF0;
extern TexturePtr D_060100F0;
static TexturePtr sEyeTextures[] = { &D_0600F8F0, &D_0600FCF0, &D_060100F0, &D_0600FCF0 };
static InitChainEntry sInitChain[] = {
ICHAIN_U8(targetMode, 6, ICHAIN_CONTINUE),
ICHAIN_F32(targetArrowOffset, 1000, ICHAIN_STOP),
};
#endif
static s32 texturesDesegmented = false;
extern InitChainEntry D_80C106C0[];
extern AnimationHeader D_06000968;
extern AnimationHeader D_06001384;
extern AnimationHeader D_06009890;
extern AnimationHeader D_0600A280;
extern AnimationHeader D_0600AD98;
extern FlexSkeletonHeader D_06011B60;
extern UNK_TYPE D_06001384;
extern UNK_TYPE D_06009890;
extern UNK_TYPE D_0600A280;
void EnRecepgirl_Init(Actor* thisx, GlobalContext* globalCtx) {
EnRecepgirl* this = THIS;
s32 i;
#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Recepgirl/EnRecepgirl_Init.s")
Actor_ProcessInitChain(&this->actor, sInitChain);
ActorShape_Init(&this->actor.shape, -60.0f, NULL, 0.0f);
SkelAnime_InitFlex(globalCtx, &this->skelAnime, &D_06011B60, &D_06009890, this->jointTable, this->morphTable, 24);
#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Recepgirl/EnRecepgirl_Destroy.s")
if (!texturesDesegmented) {
for (i = 0; i < ARRAY_COUNT(sEyeTextures); i++) {
sEyeTextures[i] = Lib_SegmentedToVirtual(sEyeTextures[i]);
}
texturesDesegmented = true;
}
#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Recepgirl/func_80C100DC.s")
this->eyeTexIndex = 2;
#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Recepgirl/func_80C10148.s")
if (Flags_GetSwitch(globalCtx, this->actor.params)) {
this->actor.textId = 0x2ADC; // hear directions again?
} else {
this->actor.textId = 0x2AD9; // "Welcome..."
}
#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Recepgirl/func_80C1019C.s")
EnRecepgirl_SetupWait(this);
}
#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Recepgirl/func_80C10290.s")
void EnRecepgirl_Destroy(Actor* thisx, GlobalContext* globalCtx) {
}
#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Recepgirl/func_80C102D4.s")
void EnRecepgirl_UpdateEyes(EnRecepgirl* this) {
if (this->eyeTexIndex != 0) {
this->eyeTexIndex++;
if (this->eyeTexIndex == 4) {
this->eyeTexIndex = 0;
}
} else if (Rand_ZeroOne() < 0.02f) {
this->eyeTexIndex++;
}
}
#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Recepgirl/EnRecepgirl_Update.s")
void EnRecepgirl_SetupWait(EnRecepgirl* this) {
if (this->skelAnime.animation == &D_06001384) {
Animation_MorphToPlayOnce(&this->skelAnime, &D_0600AD98, 5.0f);
}
this->actionFunc = EnRecepgirl_Wait;
}
#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Recepgirl/func_80C10558.s")
void EnRecepgirl_Wait(EnRecepgirl* this, GlobalContext* globalCtx) {
if (SkelAnime_Update(&this->skelAnime) != 0) {
if (this->skelAnime.animation == &D_0600A280) {
Animation_MorphToPlayOnce(&this->skelAnime, &D_0600AD98, 5.0f);
} else {
Animation_MorphToLoop(&this->skelAnime, &D_06009890, -4.0f);
}
}
#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Recepgirl/func_80C10590.s")
if (func_800B84D0(&this->actor, globalCtx) != 0) {
EnRecepgirl_SetupTalk(this);
} else if (Actor_IsActorFacingLink(&this->actor, 0x2000)) {
func_800B8614(&this->actor, globalCtx, 60.0f);
if (Player_GetMask(globalCtx) == 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)) {
this->actor.textId = 0x2ADC; // hear directions again?
} else {
this->actor.textId = 0x2AD9; // "Welcome..."
}
}
}
#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_En_Recepgirl/EnRecepgirl_Draw.s")
void EnRecepgirl_SetupTalk(EnRecepgirl* this) {
Animation_MorphToPlayOnce(&this->skelAnime, &D_0600A280, -4.0f);
this->actionFunc = EnRecepgirl_Talk;
}
void EnRecepgirl_Talk(EnRecepgirl* this, GlobalContext* globalCtx) {
u8 temp_v0_2;
if (SkelAnime_Update(&this->skelAnime)) {
if (this->skelAnime.animation == &D_0600A280) {
Animation_PlayLoop(&this->skelAnime, &D_06001384);
} else if (this->skelAnime.animation == &D_0600AD98) {
if (this->actor.textId == 0x2ADA) { // Mayor's office is on the left (meeting ongoing)
Animation_MorphToPlayOnce(&this->skelAnime, &D_06000968, 10.0f);
} else {
Animation_MorphToLoop(&this->skelAnime, &D_06009890, 10.0f);
}
} else {
if (this->actor.textId == 0x2ADA) { // Mayor's office is on the left (meeting ongoing)
Animation_MorphToLoop(&this->skelAnime, &D_06009890, 10.0f);
} else {
Animation_MorphToPlayOnce(&this->skelAnime, &D_0600A280, -4.0f);
}
}
}
temp_v0_2 = func_80152498(&globalCtx->msgCtx);
if (temp_v0_2 == 2) {
this->actor.textId = 0x2ADC; // hear directions again?
EnRecepgirl_SetupWait(this);
} else if ((temp_v0_2 == 5) && (func_80147624(globalCtx) != 0)) {
if (this->actor.textId == 0x2AD9) { // "Welcome..."
Actor_SetSwitchFlag(globalCtx, this->actor.params);
Animation_MorphToPlayOnce(&this->skelAnime, &D_0600AD98, 10.0f);
if (gSaveContext.weekEventReg[63] & 0x80) { // showed Couple's Mask to meeting
this->actor.textId = 0x2ADF; // Mayor's office is on the left (meeting ended)
} else {
this->actor.textId = 0x2ADA; // Mayor's office is on the left (meeting ongoing)
}
} else if (this->actor.textId == 0x2ADC) { // hear directions again?
Animation_MorphToPlayOnce(&this->skelAnime, &D_0600AD98, 10.0f);
this->actor.textId = 0x2ADD; // "So..."
} else {
Animation_MorphToPlayOnce(&this->skelAnime, &D_06000968, 10.0f);
if (this->actor.textId == 0x2ADD) { // "So..."
this->actor.textId = 0x2ADE; // Mayor's office is on the left, drawing room on the right
} else if (this->actor.textId == 0x2ADA) { // Mayor's office is on the left (meeting ongoing)
this->actor.textId = 0x2ADB; // drawing room on the right
} else {
this->actor.textId = 0x2AE0; // drawing room on the right, don't go in without an appointment
}
}
func_80151938(globalCtx, this->actor.textId);
}
}
void EnRecepgirl_Update(Actor* thisx, GlobalContext* globalCtx) {
s32 pad;
EnRecepgirl* this = THIS;
Vec3s sp30;
this->actionFunc(this, globalCtx);
func_800E9250(globalCtx, &this->actor, &this->headRot, &sp30, this->actor.focus.pos);
EnRecepgirl_UpdateEyes(this);
}
s32 EnRecepgirl_OverrideLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Gfx** dList, Vec3f* pos, Vec3s* rot,
Actor* thisx) {
EnRecepgirl* this = THIS;
if (limbIndex == 5) {
rot->x += this->headRot.y;
}
return false;
}
void EnRecepgirl_UnkLimbDraw(GlobalContext* globalCtx, s32 limbIndex, Actor* thisx) {
EnRecepgirl* this = THIS;
if (limbIndex == 5) {
Matrix_RotateY(0x400 - this->headRot.x, MTXMODE_APPLY);
Matrix_GetStateTranslationAndScaledX(500.0f, &this->actor.focus.pos);
}
}
void EnRecepgirl_Draw(Actor* thisx, GlobalContext* globalCtx) {
EnRecepgirl* this = THIS;
OPEN_DISPS(globalCtx->state.gfxCtx);
func_8012C28C(globalCtx->state.gfxCtx);
gSPSegment(POLY_OPA_DISP++, 0x08, sEyeTextures[this->eyeTexIndex]);
func_801343C0(globalCtx, this->skelAnime.skeleton, this->skelAnime.jointTable, this->skelAnime.dListCount,
EnRecepgirl_OverrideLimbDraw, NULL, EnRecepgirl_UnkLimbDraw, &this->actor);
CLOSE_DISPS(globalCtx->state.gfxCtx);
}

View File

@ -8,10 +8,13 @@ struct EnRecepgirl;
typedef void (*EnRecepgirlActionFunc)(struct EnRecepgirl*, GlobalContext*);
typedef struct EnRecepgirl {
/* 0x0000 */ Actor actor;
/* 0x0144 */ char unk_144[0x164];
/* 0x02A8 */ EnRecepgirlActionFunc actionFunc;
/* 0x02AC */ char unk_2AC[0x8];
/* 0x000 */ Actor actor;
/* 0x144 */ SkelAnime skelAnime;
/* 0x188 */ Vec3s jointTable[24];
/* 0x218 */ Vec3s morphTable[24];
/* 0x2A8 */ EnRecepgirlActionFunc actionFunc;
/* 0x2AC */ u8 eyeTexIndex;
/* 0x2AE */ Vec3s headRot;
} EnRecepgirl; // size = 0x2B4
extern const ActorInit En_Recepgirl_InitVars;

View File

@ -264,7 +264,7 @@ u16 EnSob1_GetWelcome(EnSob1* this, GlobalContext* globalCtx) {
return 0x688;
case PLAYER_MASK_BLAST:
return 0x689;
case PLAYER_MASK_KAFEI:
case PLAYER_MASK_KAFEIS_MASK:
return 0x68A;
}
} else if (this->shopType == ZORA_SHOP) {

View File

@ -1,7 +1,7 @@
/*
* File: z_obj_tree.c
* Overlay: ovl_Obj_Tree
* Description: Single tree (e.g. North Clock Town)
* Description: Single branching tree (e.g. North Clock Town)
*/
#include "z_obj_tree.h"
@ -15,7 +15,10 @@ void ObjTree_Destroy(Actor* thisx, GlobalContext* globalCtx);
void ObjTree_Update(Actor* thisx, GlobalContext* globalCtx);
void ObjTree_Draw(Actor* thisx, GlobalContext* globalCtx);
#if 0
void ObTree_DoNothing(ObjTree* this, GlobalContext* globalCtx);
void ObTree_SetupDoNothing(ObjTree* this);
void ObTree_Sway(ObjTree* this, GlobalContext* globalCtx);
const ActorInit Obj_Tree_InitVars = {
ACTOR_OBJ_TREE,
ACTORCAT_PROP,
@ -28,15 +31,27 @@ const ActorInit Obj_Tree_InitVars = {
(ActorFunc)ObjTree_Draw,
};
// static ColliderCylinderInit sCylinderInit = {
static ColliderCylinderInit D_80B9A570 = {
{ COLTYPE_TREE, AT_NONE, AC_ON | AC_TYPE_PLAYER, OC1_ON | OC1_TYPE_ALL, OC2_TYPE_1, COLSHAPE_CYLINDER, },
{ ELEMTYPE_UNK1, { 0x00000000, 0x00, 0x00 }, { 0x0100020A, 0x00, 0x00 }, TOUCH_NONE | TOUCH_SFX_NORMAL, BUMP_ON, OCELEM_ON, },
static ColliderCylinderInit sCylinderInit = {
{
COLTYPE_TREE,
AT_NONE,
AC_ON | AC_TYPE_PLAYER,
OC1_ON | OC1_TYPE_ALL,
OC2_TYPE_1,
COLSHAPE_CYLINDER,
},
{
ELEMTYPE_UNK1,
{ 0x00000000, 0x00, 0x00 },
{ 0x0100020A, 0x00, 0x00 },
TOUCH_NONE | TOUCH_SFX_NORMAL,
BUMP_ON,
OCELEM_ON,
},
{ 28, 120, 0, { 0, 0, 0 } },
};
// static DamageTable sDamageTable = {
static DamageTable D_80B9A59C = {
static DamageTable sDamageTable = {
/* Deku Nut */ DMG_ENTRY(0, 0x0),
/* Deku Stick */ DMG_ENTRY(0, 0xF),
/* Horse trample */ DMG_ENTRY(0, 0x0),
@ -71,32 +86,115 @@ static DamageTable D_80B9A59C = {
/* Powder Keg */ DMG_ENTRY(0, 0x0),
};
// sColChkInfoInit
static CollisionCheckInfoInit2 D_80B9A5BC = { 8, 0, 0, 0, MASS_HEAVY };
static CollisionCheckInfoInit2 sColchkInfoInit = { 8, 0, 0, 0, MASS_HEAVY };
#endif
extern Gfx D_06000680[];
extern Gfx D_060007C8[];
extern CollisionHeader D_06001B2C;
extern ColliderCylinderInit D_80B9A570;
extern DamageTable D_80B9A59C;
extern CollisionCheckInfoInit2 D_80B9A5BC;
void ObjTree_Init(Actor* thisx, GlobalContext* globalCtx) {
s32 pad;
ObjTree* this = THIS;
CollisionHeader* colHeader = NULL;
extern UNK_TYPE D_06000680;
extern UNK_TYPE D_06001B2C;
if (OBJTREE_ISLARGE(&this->dyna.actor)) {
Actor_SetScale(&this->dyna.actor, 0.15f);
this->dyna.actor.uncullZoneForward = 4000.0f;
} else {
Actor_SetScale(&this->dyna.actor, 0.1f);
DynaPolyActor_Init(&this->dyna, 1);
CollisionHeader_GetVirtual(&D_06001B2C, &colHeader);
this->dyna.bgId = DynaPoly_SetBgActor(globalCtx, &globalCtx->colCtx.dyna, &this->dyna.actor, colHeader);
}
#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Tree/ObjTree_Init.s")
Collider_InitCylinder(globalCtx, &this->collider);
Collider_SetCylinder(globalCtx, &this->collider, &this->dyna.actor, &sCylinderInit);
CollisionCheck_SetInfo2(&this->dyna.actor.colChkInfo, &sDamageTable, &sColchkInfoInit);
#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Tree/ObjTree_Destroy.s")
if (OBJTREE_ISLARGE(&this->dyna.actor)) {
this->collider.dim.height = 220;
}
#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Tree/func_80B9A20C.s")
this->swayAmplitude = 0.0f;
this->swayAngle = 0;
this->swayVelocity = 0;
ObTree_SetupDoNothing(this);
}
#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Tree/func_80B9A220.s")
void ObjTree_Destroy(Actor* thisx, GlobalContext* globalCtx) {
ObjTree* this = THIS;
s32 bgId;
#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Tree/func_80B9A230.s")
if (!OBJTREE_ISLARGE(&this->dyna.actor)) {
bgId = this->dyna.bgId;
DynaPoly_DeleteBgActor(globalCtx, &globalCtx->colCtx.dyna, bgId);
}
#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Tree/func_80B9A27C.s")
Collider_DestroyCylinder(globalCtx, &this->collider);
}
#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Tree/func_80B9A348.s")
void ObTree_SetupDoNothing(ObjTree* this) {
this->actionFunc = ObTree_DoNothing;
}
#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Tree/ObjTree_Update.s")
void ObTree_DoNothing(ObjTree* this, GlobalContext* globalCtx) {
}
#pragma GLOBAL_ASM("asm/non_matchings/overlays/ovl_Obj_Tree/ObjTree_Draw.s")
void ObTree_SetupSway(ObjTree* this) {
this->timer = 0;
this->swayAmplitude = 546.0f;
this->swayVelocity = 35 * 0x10000 / 360;
Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_TREE_SWING);
this->actionFunc = ObTree_Sway;
}
void ObTree_Sway(ObjTree* this, GlobalContext* globalCtx) {
if (this->timer > 80) {
ObTree_SetupDoNothing(this);
return;
}
Math_SmoothStepToF(&this->swayAmplitude, 0.0f, 0.1f, 91.0f, 18.0f);
this->swayVelocity += 1 * 0x10000 / 360;
this->swayAngle += this->swayVelocity;
this->dyna.actor.shape.rot.x = Math_SinS(this->swayAngle) * this->swayAmplitude;
this->dyna.actor.shape.rot.z = Math_CosS(this->swayAngle) * this->swayAmplitude;
this->timer++;
}
void ObTree_UpdateCollision(ObjTree* this, GlobalContext* globalCtx) {
Collider_UpdateCylinder(&this->dyna.actor, &this->collider);
CollisionCheck_SetOC(globalCtx, &globalCtx->colChkCtx, &this->collider.base);
if (this->dyna.actor.xzDistToPlayer < 600.0f) {
CollisionCheck_SetAC(globalCtx, &globalCtx->colChkCtx, &this->collider.base);
if (this->dyna.actor.home.rot.y == 1) {
this->dyna.actor.home.rot.y = 0;
ObTree_SetupSway(this);
}
}
}
void ObjTree_Update(Actor* thisx, GlobalContext* globalCtx) {
ObjTree* this = THIS;
this->actionFunc(this, globalCtx);
ObTree_UpdateCollision(this, globalCtx);
}
void ObjTree_Draw(Actor* thisx, GlobalContext* globalCtx) {
s16 xRot = (f32)thisx->shape.rot.x;
s16 zRot = (f32)thisx->shape.rot.z;
OPEN_DISPS(globalCtx->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);
gSPDisplayList(POLY_OPA_DISP++, D_06000680);
Matrix_InsertRotation(xRot, 0, zRot, MTXMODE_APPLY);
gSPMatrix(POLY_OPA_DISP++, Matrix_NewMtx(globalCtx->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW);
gSPDisplayList(POLY_OPA_DISP++, D_060007C8);
CLOSE_DISPS(globalCtx->state.gfxCtx);
}

View File

@ -7,11 +7,16 @@ struct ObjTree;
typedef void (*ObjTreeActionFunc)(struct ObjTree*, GlobalContext*);
#define OBJTREE_ISLARGE(thisx) ((thisx)->params & 0x8000)
typedef struct ObjTree {
/* 0x0000 */ Actor actor;
/* 0x0144 */ char unk_144[0x64];
/* 0x01A8 */ ObjTreeActionFunc actionFunc;
/* 0x01AC */ char unk_1AC[0xC];
/* 0x000 */ DynaPolyActor dyna;
/* 0x15C */ ColliderCylinder collider;
/* 0x1A8 */ ObjTreeActionFunc actionFunc;
/* 0x1AC */ f32 swayAmplitude;
/* 0x1B0 */ s16 swayAngle;
/* 0x1B2 */ s16 swayVelocity;
/* 0x1B4 */ s16 timer;
} ObjTree; // size = 0x1B8
extern const ActorInit Obj_Tree_InitVars;

View File

@ -154,6 +154,154 @@ animdict = {
"MainHeap_Init": "ZeldaArena_Init",
"MainHeap_Cleanup": "ZeldaArena_Cleanup",
"MainHeap_IsInitialized": "ZeldaArena_IsInitialized",
# "BgCheck_RelocateMeshHeader": "CollisionHeader_GetVirtual",
# "BgCheck_AddActorMesh": "DynaPoly_SetBgActor",
# "BgCheck_RemoveActorMesh": "DynaPoly_DeleteBgActor",
"BgCheck_PolygonLinkedListNodeInit": "SSNode_SetValue",
"BgCheck_PolygonLinkedListResetHead": "SSList_SetNull",
"BgCheck_ScenePolygonListsNodeInsert": "SSNodeList_SetSSListHead",
"BgCheck_PolygonLinkedListNodeInsert": "DynaSSNodeList_SetSSListHead",
"BgCheck_PolygonLinkedListInit": "DynaSSNodeList_Init",
"BgCheck_PolygonLinkedListAlloc": "DynaSSNodeList_Alloc",
"BgCheck_PolygonLinkedListReset": "DynaSSNodeList_ResetCount",
"BgCheck_AllocPolygonLinkedListNode": "DynaSSNodeList_GetNextNodeIdx",
"BgCheck_CreateVec3fFromVertex": "BgCheck_Vec3sToVec3f",
"BgCheck_CreateVertexFromVec3f": "BgCheck_Vec3fToVec3s",
"BgCheck_PolygonGetMinY": "CollisionPoly_GetMinY",
"BgCheck_PolygonGetNormal": "CollisionPoly_GetNormalF",
"func_800C01B8": "CollisionPoly_GetPointDistanceFromPlane",
"BgCheck_CreateTriNormFromPolygon": "CollisionPoly_GetVertices",
"func_800C02C0": "CollisionPoly_GetVerticesByBgId",
"BgCheck_PolygonCollidesWithSphere": "CollisionPoly_SphVsPoly",
"BgCheck_ScenePolygonListsInsertSorted": "StaticLookup_AddPolyToSSList",
"BgCheck_ScenePolygonListsInsert": "StaticLookup_AddPoly",
"BgCheck_GetPolyMinSubdivisions": "BgCheck_GetSubdivisionMinBounds",
"BgCheck_GetPolyMaxSubdivisions": "BgCheck_GetSubdivisionMaxBounds",
"BgCheck_GetPolyMinMaxSubdivisions": "BgCheck_GetPolySubdivisionBounds",
"func_800C2BE0": "BgCheck_PolyIntersectsSubdivision",
"BgCheck_SplitScenePolygonsIntoSubdivisions": "BgCheck_InitStaticLookup",
"BgCheck_GetIsDefaultSpecialScene": "BgCheck_IsSmallMemScene",
"BgCheck_GetSpecialSceneMaxMemory": "BgCheck_TryGetCustomMemsize",
"BgCheck_CalcSubdivisionSize": "BgCheck_SetSubdivisionDimension",
"BgCheck_Init(": "BgCheck_Allocate(",
"func_800C3C00": "BgCheck_SetContextFlags",
"func_800C3C14": "BgCheck_UnsetContextFlags",
"BgCheck_GetActorMeshHeader": "BgCheck_GetCollisionHeader",
"func_800C3D50": "BgCheck_RaycastFloorImpl",
"func_800C3F40": "BgCheck_CameraRaycastFloor1",
"func_800C3FA0": "BgCheck_EntityRaycastFloor1",
"func_800C4000": "BgCheck_EntityRaycastFloor2",
"func_800C4058": "BgCheck_EntityRaycastFloor2_1",
"func_800C40B4": "BgCheck_EntityRaycastFloor3",
"func_800C411C": "BgCheck_EntityRaycastFloor5",
"func_800C4188": "BgCheck_EntityRaycastFloor5_2",
"func_800C41E4": "BgCheck_EntityRaycastFloor5_3",
"func_800C4240": "BgCheck_EntityRaycastFloor6",
"func_800C42A8": "BgCheck_EntityRaycastFloor7",
"func_800C4314": "BgCheck_AnyRaycastFloor1",
"func_800C43CC": "BgCheck_AnyRaycastFloor2",
"func_800C4488": "BgCheck_CameraRaycastFloor2",
"func_800C44F0": "BgCheck_EntityRaycastFloor8",
"func_800C455C": "BgCheck_EntityRaycastFloor9",
"func_800C45C4": "BgCheck_CheckWallImpl",
"func_800C4C74": "BgCheck_EntitySphVsWall1",
"func_800C4CD8": "BgCheck_EntitySphVsWall2",
"func_800C4D3C": "BgCheck_EntitySphVsWall3",
"func_800C4DA4": "BgCheck_EntitySphVsWall4",
"func_800C4E10": "BgCheck_CheckCeilingImpl",
"func_800C4F38": "BgCheck_AnyCheckCeiling",
"func_800C4F84": "BgCheck_EntityCheckCeiling",
"func_800C54AC": "BgCheck_CameraLineTest1",
"func_800C5538": "BgCheck_CameraLineTest2",
"func_800C55C4": "BgCheck_EntityLineTest1",
"func_800C5650": "BgCheck_EntityLineTest2",
"func_800C56E0": "BgCheck_EntityLineTest3",
"func_800C576C": "BgCheck_ProjectileLineTest",
"func_800C57F8": "BgCheck_AnyLineTest1",
"func_800C583C": "BgCheck_AnyLineTest2",
"func_800C58C8": "BgCheck_AnyLineTest3",
"func_800C5954": "BgCheck_SphVsFirstPolyImpl",
"func_800C5A20": "BgCheck_SphVsFirstPoly",
"func_800C5A64": "BgCheck_SphVsFirstWall",
"BgCheck_ScenePolygonListsInit": "SSNodeList_Init",
"BgCheck_ScenePolygonListsAlloc": "SSNodeList_Alloc",
"func_800C5B80": "SSNodeList_GetNextNode",
"BgCheck_ScenePolygonListsReserveNode": "SSNodeList_GetNextNodeIdx",
"BgCheck_ActorMeshParamsInit": "ScaleRotPos_Init",
"BgCheck_SetActorMeshParams": "ScaleRotPos_SetValue",
"BgCheck_ActorMeshPolyListsHeadsInit": "DynaLookup_ResetLists",
"BgCheck_ActorMeshPolyListsInit": "DynaLookup_Reset",
"BgCheck_ActorMeshVerticesIndexInit": "DynaLookup_ResetVtxStartIndex",
"BgCheck_ActorMeshWaterboxesIndexInit": "DynaLookup_ResetWaterBoxStartIndex",
"BgCheck_ActorMeshInit": "BgActor_Init",
"BgCheck_ActorMeshInitFromActor": "BgActor_SetActor",
"BgCheck_HasActorMeshChanged": "BgActor_IsTransformUnchanged",
"BgCheck_PolygonsInit": "DynaPoly_NullPolyList",
"BgCheck_PolygonsAlloc": "DynaPoly_AllocPolyList",
"BgCheck_VerticesInit": "DynaPoly_NullVtxList",
"BgCheck_VerticesListAlloc": "DynaPoly_AllocVtxList",
"BgCheck_WaterboxListInit": "DynaPoly_InitWaterBoxList",
"BgCheck_WaterboxListAlloc": "DynaPoly_AllocWaterBoxList",
"BgCheck_ActorMeshUpdateParams": "DynaPoly_SetBgActorPrevTransform",
"BgCheck_IsActorMeshIndexValid": "DynaPoly_IsBgIdBgActor",
"BgCheck_DynaInit": "DynaPoly_Init",
"BgCheck_DynaAlloc": "DynaPoly_Alloc",
"BgCheck_AddActorMesh": "DynaPoly_SetBgActor",
"BgCheck_GetActorOfMesh": "DynaPoly_GetActor",
"BgCheck_RemoveActorMesh": "DynaPoly_DeleteBgActor",
"BgCheck_AddActorMeshToLists": "DynaPoly_ExpandSRT",
"BgCheck_Update": "DynaPoly_Setup",
"BgCheck_UpdateAllActorMeshes": "DynaPoly_UpdateBgActorTransforms",
"BgCheck_RelocateMeshHeaderPointers": "CollisionHeader_SegmentedToVirtual",
"BgCheck_RelocateMeshHeader": "CollisionHeader_GetVirtual",
"BgCheck_RelocateAllMeshHeaders": "BgCheck_InitCollisionHeaders",
"BgCheck_GetPolygonAttributes": "SurfaceType_GetData",
"func_800C9704": "SurfaceType_GetCamDataIndex",
"func_800C9924": "SurfaceType_GetCamPosData",
"func_800C99AC": "SurfaceType_GetSceneExitIndex",
"func_800C9B90": "SurfaceType_IsHorseBlocked",
"func_800C9BDC": "SurfaceType_GetSfx",
"func_800C9C74": "SurfaceType_GetSlope",
"func_800C9C9C": "SurfaceType_GetLightSettingIndex",
"func_800C9CC4": "SurfaceType_GetEcho",
"func_800C9CEC": "SurfaceType_IsHookshotSurface",
"func_800C9D14": "SurfaceType_IsIgnoredByEntities",
"func_800C9D50": "SurfaceType_IsIgnoredByProjectiles",
"func_800C9D8C": "SurfaceType_IsConveyor",
"func_800C9E18": "SurfaceType_GetConveyorSpeed",
"func_800C9E40": "SurfaceType_GetConveyorDirection",
"func_800C9E88": "SurfaceType_IsWallDamage",
"func_800C9EBC": "WaterBox_GetSurfaceImpl",
"func_800CA1AC": "WaterBox_GetSurface1",
"func_800CA1E8": "WaterBox_GetSurface1_2",
"func_800CA22C": "WaterBox_GetSurface2",
"func_800CA6D8": "WaterBox_GetLightSettingIndex",
"func_80179678": "Math3D_PlaneVsLineSegClosestPoint",
"Math3D_DistanceSquared": "Math3D_Vec3fDistSq",
"Math3D_NormalVector": "Math3D_SurfaceNorm",
"func_8017A954": "Math3D_PointRelativeToCubeFaces",
"func_8017AA0C": "Math3D_PointRelativeToCubeEdges",
"func_8017ABBC": "Math3D_PointRelativeToCubeVertices",
"func_8017AD38": "Math3D_LineVsCube",
"Math3D_NormalizedDistanceFromPlane": "Math3D_UDistPlaneToPos",
"Math3D_NormalizedSignedDistanceFromPlane": "Math3D_DistPlaneToPos",
"func_8017BAD0": "Math3D_TriChkPointParaYDist",
"func_8017BE30": "Math3D_TriChkPointParaYIntersectDist",
"func_8017BEE0": "Math3D_TriChkPointParaYIntersectInsideTri",
"func_8017C008": "Math3D_TriChkLineSegParaYIntersect",
"func_8017C494": "Math3D_TriChkPointParaYIntersectInsideTri2",
"func_8017C540": "Math3D_TriChkPointParaXDist",
"func_8017C850": "Math3D_TriChkPointParaXIntersect",
"func_8017C980": "Math3D_TriChkLineSegParaXIntersect",
"func_8017CB7C": "Math3D_TriChkLineSegParaZDist",
"func_8017CEF0": "Math3D_TriChkPointParaZIntersect",
"func_8017D020": "Math3D_TriChkLineSegParaZIntersect",
"Math3D_ColSphereLineSeg": "Math3D_LineVsSph",
"Math3D_ColSphereSphere(": "Math3D_SphVsSph(",
"func_8017F9C0": "Math3D_XZInSphere",
"func_8017FA34": "Math3D_XYInSphere",
"func_8017FAA8": "Math3D_YZInSphere",
"skelanime.unk03": "skelanime.taper",
"skelanime.animCurrentSeg": "skelanime.animation",
@ -220,11 +368,25 @@ def replace_anim_all(repo):
if(filename.endswith('.c')):
file = subdir + os.sep + filename
replace_anim(file)
for subdir, dirs, files in os.walk(repo + os.sep + 'asm'):
for filename in files:
if(filename.endswith('.s')):
file = subdir + os.sep + filename
replace_anim(file)
for subdir, dirs, files in os.walk(repo + os.sep + 'data'):
for filename in files:
if(filename.endswith('.s')):
file = subdir + os.sep + filename
replace_anim(file)
for subdir, dirs, files in os.walk(repo + os.sep + 'docs'):
for filename in files:
if(filename.endswith('.md')):
file = subdir + os.sep + filename
replace_anim(file)
for subdir, dirs, files in os.walk(repo + os.sep + 'tools' + os.sep + 'sizes'):
for filename in files:
if(filename.endswith('.csv')):

View File

@ -7631,10 +7631,10 @@
0x80951748:("EnGm_Draw",),
0x80952620:("EnMs_Init",),
0x80952708:("EnMs_Destroy",),
0x80952734:("func_80952734",),
0x809527F8:("func_809527F8",),
0x809529AC:("func_809529AC",),
0x80952A1C:("func_80952A1C",),
0x80952734:("EnMs_Wait",),
0x809527F8:("EnMs_Talk",),
0x809529AC:("EnMs_Sell",),
0x80952A1C:("EnMs_TalkAfterPurchase",),
0x80952A8C:("EnMs_Update",),
0x80952B24:("EnMs_Draw",),
0x80952C50:("func_80952C50",),
@ -14941,11 +14941,11 @@
0x80B99798:("EnZot_Draw",),
0x80B9A0B0:("ObjTree_Init",),
0x80B9A1BC:("ObjTree_Destroy",),
0x80B9A20C:("func_80B9A20C",),
0x80B9A220:("func_80B9A220",),
0x80B9A230:("func_80B9A230",),
0x80B9A27C:("func_80B9A27C",),
0x80B9A348:("func_80B9A348",),
0x80B9A20C:("ObTree_SetupDoNothing",),
0x80B9A220:("ObTree_DoNothing",),
0x80B9A230:("ObTree_SetupSway",),
0x80B9A27C:("ObTree_Sway",),
0x80B9A348:("ObTree_UpdateCollision",),
0x80B9A3E8:("ObjTree_Update",),
0x80B9A424:("ObjTree_Draw",),
0x80B9A650:("ObjY2lift_Init",),
@ -16738,14 +16738,14 @@
0x80C0F758:("func_80C0F758",),
0x80C0FFD0:("EnRecepgirl_Init",),
0x80C100CC:("EnRecepgirl_Destroy",),
0x80C100DC:("func_80C100DC",),
0x80C10148:("func_80C10148",),
0x80C1019C:("func_80C1019C",),
0x80C10290:("func_80C10290",),
0x80C102D4:("func_80C102D4",),
0x80C100DC:("EnRecepgirl_UpdateEyes",),
0x80C10148:("EnRecepgirl_SetupWait",),
0x80C1019C:("EnRecepgirl_Wait",),
0x80C10290:("EnRecepgirl_SetupTalk",),
0x80C102D4:("EnRecepgirl_Talk",),
0x80C104E8:("EnRecepgirl_Update",),
0x80C10558:("func_80C10558",),
0x80C10590:("func_80C10590",),
0x80C10558:("EnRecepgirl_OverrideLimbDraw",),
0x80C10590:("EnRecepgirl_UnkLimbDraw",),
0x80C105EC:("EnRecepgirl_Draw",),
0x80C10770:("EnThiefbird_Init",),
0x80C10958:("EnThiefbird_Destroy",),

View File

@ -3691,7 +3691,7 @@
0x801E0148:("D_801E0148","f32","",0x4),
0x801E014C:("D_801E014C","f32","",0x4),
0x801E0150:("Math3D_UnitNormalVector_min_length","f32","",0x4),
0x801E0154:("Math3D_NormalizedDistanceFromPlane_min_length","f32","",0x4),
0x801E0154:("Math3D_UDistPlaneToPos_min_length","f32","",0x4),
0x801E0158:("D_801E0158","f32","",0x4),
0x801E015C:("D_801E015C","f32","",0x4),
0x801E0160:("D_801E0160","f32","",0x4),
@ -4228,8 +4228,8 @@
0x801FBC46:("D_801FBC46","UNK_TYPE1","",0x1),
0x801FBC48:("D_801FBC48","UNK_TYPE1","",0x1),
0x801FBC58:("D_801FBC58","UNK_TYPE1","",0x1),
0x801FBC68:("Math3D_NormalVector_temp1","Vec3f","",0xc),
0x801FBC78:("Math3D_NormalVector_temp2","Vec3f","",0xc),
0x801FBC68:("Math3D_SurfaceNorm_temp1","Vec3f","",0xc),
0x801FBC78:("Math3D_SurfaceNorm_temp2","Vec3f","",0xc),
0x801FBC8C:("D_801FBC8C","f32","",0x4),
0x801FBC90:("D_801FBC90","f32","",0x4),
0x801FBC98:("D_801FBC98","f32","",0x4),

View File

@ -413,33 +413,33 @@ asm/non_matchings/code/z_actor_dlftbls/ActorOverlayTable_FaultPrint.s,ActorOverl
asm/non_matchings/code/z_actor_dlftbls/ActorOverlayTable_FaultAddrConv.s,ActorOverlayTable_FaultAddrConv,0x800BFA78,0x1C
asm/non_matchings/code/z_actor_dlftbls/ActorOverlayTable_Init.s,ActorOverlayTable_Init,0x800BFAE8,0x16
asm/non_matchings/code/z_actor_dlftbls/ActorOverlayTable_Cleanup.s,ActorOverlayTable_Cleanup,0x800BFB40,0x10
asm/non_matchings/code/z_bgcheck/BgCheck_PolygonLinkedListNodeInit.s,BgCheck_PolygonLinkedListNodeInit,0x800BFB80,0x7
asm/non_matchings/code/z_bgcheck/BgCheck_PolygonLinkedListResetHead.s,BgCheck_PolygonLinkedListResetHead,0x800BFB9C,0x4
asm/non_matchings/code/z_bgcheck/BgCheck_ScenePolygonListsNodeInsert.s,BgCheck_ScenePolygonListsNodeInsert,0x800BFBAC,0x16
asm/non_matchings/code/z_bgcheck/BgCheck_PolygonLinkedListNodeInsert.s,BgCheck_PolygonLinkedListNodeInsert,0x800BFC04,0x16
asm/non_matchings/code/z_bgcheck/BgCheck_PolygonLinkedListInit.s,BgCheck_PolygonLinkedListInit,0x800BFC5C,0x5
asm/non_matchings/code/z_bgcheck/BgCheck_PolygonLinkedListAlloc.s,BgCheck_PolygonLinkedListAlloc,0x800BFC70,0x14
asm/non_matchings/code/z_bgcheck/BgCheck_PolygonLinkedListReset.s,BgCheck_PolygonLinkedListReset,0x800BFCC0,0x3
asm/non_matchings/code/z_bgcheck/BgCheck_AllocPolygonLinkedListNode.s,BgCheck_AllocPolygonLinkedListNode,0x800BFCCC,0xC
asm/non_matchings/code/z_bgcheck/BgCheck_CreateVec3fFromVertex.s,BgCheck_CreateVec3fFromVertex,0x800BFCFC,0x11
asm/non_matchings/code/z_bgcheck/BgCheck_CreateVertexFromVec3f.s,BgCheck_CreateVertexFromVec3f,0x800BFD40,0x11
asm/non_matchings/code/z_bgcheck/SSNode_SetValue.s,SSNode_SetValue,0x800BFB80,0x7
asm/non_matchings/code/z_bgcheck/SSList_SetNull.s,SSList_SetNull,0x800BFB9C,0x4
asm/non_matchings/code/z_bgcheck/SSNodeList_SetSSListHead.s,SSNodeList_SetSSListHead,0x800BFBAC,0x16
asm/non_matchings/code/z_bgcheck/DynaSSNodeList_SetSSListHead.s,DynaSSNodeList_SetSSListHead,0x800BFC04,0x16
asm/non_matchings/code/z_bgcheck/DynaSSNodeList_Init.s,DynaSSNodeList_Init,0x800BFC5C,0x5
asm/non_matchings/code/z_bgcheck/DynaSSNodeList_Alloc.s,DynaSSNodeList_Alloc,0x800BFC70,0x14
asm/non_matchings/code/z_bgcheck/DynaSSNodeList_ResetCount.s,DynaSSNodeList_ResetCount,0x800BFCC0,0x3
asm/non_matchings/code/z_bgcheck/DynaSSNodeList_GetNextNodeIdx.s,DynaSSNodeList_GetNextNodeIdx,0x800BFCCC,0xC
asm/non_matchings/code/z_bgcheck/BgCheck_Vec3sToVec3f.s,BgCheck_Vec3sToVec3f,0x800BFCFC,0x11
asm/non_matchings/code/z_bgcheck/BgCheck_Vec3fToVec3s.s,BgCheck_Vec3fToVec3s,0x800BFD40,0x11
asm/non_matchings/code/z_bgcheck/func_800BFD84.s,func_800BFD84,0x800BFD84,0x1A
asm/non_matchings/code/z_bgcheck/func_800BFDEC.s,func_800BFDEC,0x800BFDEC,0x76
asm/non_matchings/code/z_bgcheck/BgCheck_PolygonGetMinY.s,BgCheck_PolygonGetMinY,0x800BFFC4,0x1E
asm/non_matchings/code/z_bgcheck/BgCheck_PolygonGetNormal.s,BgCheck_PolygonGetNormal,0x800C003C,0x16
asm/non_matchings/code/z_bgcheck/CollisionPoly_GetMinY.s,CollisionPoly_GetMinY,0x800BFFC4,0x1E
asm/non_matchings/code/z_bgcheck/CollisionPoly_GetNormalF.s,CollisionPoly_GetNormalF,0x800C003C,0x16
asm/non_matchings/code/z_bgcheck/func_800C0094.s,func_800C0094,0x800C0094,0x49
asm/non_matchings/code/z_bgcheck/func_800C01B8.s,func_800C01B8,0x800C01B8,0x1A
asm/non_matchings/code/z_bgcheck/BgCheck_CreateTriNormFromPolygon.s,BgCheck_CreateTriNormFromPolygon,0x800C0220,0x28
asm/non_matchings/code/z_bgcheck/func_800C02C0.s,func_800C02C0,0x800C02C0,0x20
asm/non_matchings/code/z_bgcheck/CollisionPoly_GetPointDistanceFromPlane.s,CollisionPoly_GetPointDistanceFromPlane,0x800C01B8,0x1A
asm/non_matchings/code/z_bgcheck/CollisionPoly_GetVertices.s,CollisionPoly_GetVertices,0x800C0220,0x28
asm/non_matchings/code/z_bgcheck/CollisionPoly_GetVerticesByBgId.s,CollisionPoly_GetVerticesByBgId,0x800C02C0,0x20
asm/non_matchings/code/z_bgcheck/func_800C0340.s,func_800C0340,0x800C0340,0x4D
asm/non_matchings/code/z_bgcheck/func_800C0474.s,func_800C0474,0x800C0474,0x7D
asm/non_matchings/code/z_bgcheck/func_800C0668.s,func_800C0668,0x800C0668,0x10
asm/non_matchings/code/z_bgcheck/func_800C06A8.s,func_800C06A8,0x800C06A8,0x29
asm/non_matchings/code/z_bgcheck/func_800C074C.s,func_800C074C,0x800C074C,0x29
asm/non_matchings/code/z_bgcheck/func_800C07F0.s,func_800C07F0,0x800C07F0,0xC0
asm/non_matchings/code/z_bgcheck/BgCheck_PolygonCollidesWithSphere.s,BgCheck_PolygonCollidesWithSphere,0x800C0AF0,0x34
asm/non_matchings/code/z_bgcheck/BgCheck_ScenePolygonListsInsertSorted.s,BgCheck_ScenePolygonListsInsertSorted,0x800C0BC0,0x88
asm/non_matchings/code/z_bgcheck/BgCheck_ScenePolygonListsInsert.s,BgCheck_ScenePolygonListsInsert,0x800C0DE0,0x25
asm/non_matchings/code/z_bgcheck/CollisionPoly_SphVsPoly.s,CollisionPoly_SphVsPoly,0x800C0AF0,0x34
asm/non_matchings/code/z_bgcheck/StaticLookup_AddPolyToSSList.s,StaticLookup_AddPolyToSSList,0x800C0BC0,0x88
asm/non_matchings/code/z_bgcheck/StaticLookup_AddPoly.s,StaticLookup_AddPoly,0x800C0DE0,0x25
asm/non_matchings/code/z_bgcheck/func_800C0E74.s,func_800C0E74,0x800C0E74,0xA2
asm/non_matchings/code/z_bgcheck/func_800C10FC.s,func_800C10FC,0x800C10FC,0x4F
asm/non_matchings/code/z_bgcheck/func_800C1238.s,func_800C1238,0x800C1238,0x1B
@ -452,98 +452,98 @@ asm/non_matchings/code/z_bgcheck/func_800C2310.s,func_800C2310,0x800C2310,0x54
asm/non_matchings/code/z_bgcheck/func_800C2460.s,func_800C2460,0x800C2460,0x2D
asm/non_matchings/code/z_bgcheck/func_800C2514.s,func_800C2514,0x800C2514,0x33
asm/non_matchings/code/z_bgcheck/func_800C25E0.s,func_800C25E0,0x800C25E0,0x38
asm/non_matchings/code/z_bgcheck/BgCheck_GetPolyMinSubdivisions.s,BgCheck_GetPolyMinSubdivisions,0x800C26C0,0x69
asm/non_matchings/code/z_bgcheck/BgCheck_GetPolyMaxSubdivisions.s,BgCheck_GetPolyMaxSubdivisions,0x800C2864,0x73
asm/non_matchings/code/z_bgcheck/BgCheck_GetPolyMinMaxSubdivisions.s,BgCheck_GetPolyMinMaxSubdivisions,0x800C2A30,0x6C
asm/non_matchings/code/z_bgcheck/func_800C2BE0.s,func_800C2BE0,0x800C2BE0,0x1D5
asm/non_matchings/code/z_bgcheck/BgCheck_SplitScenePolygonsIntoSubdivisions.s,BgCheck_SplitScenePolygonsIntoSubdivisions,0x800C3334,0x100
asm/non_matchings/code/z_bgcheck/BgCheck_GetIsDefaultSpecialScene.s,BgCheck_GetIsDefaultSpecialScene,0x800C3734,0x11
asm/non_matchings/code/z_bgcheck/BgCheck_GetSpecialSceneMaxMemory.s,BgCheck_GetSpecialSceneMaxMemory,0x800C3778,0x11
asm/non_matchings/code/z_bgcheck/BgCheck_CalcSubdivisionSize.s,BgCheck_CalcSubdivisionSize,0x800C37BC,0x22
asm/non_matchings/code/z_bgcheck/BgCheck_GetSubdivisionMinBounds.s,BgCheck_GetSubdivisionMinBounds,0x800C26C0,0x69
asm/non_matchings/code/z_bgcheck/BgCheck_GetSubdivisionMaxBounds.s,BgCheck_GetSubdivisionMaxBounds,0x800C2864,0x73
asm/non_matchings/code/z_bgcheck/BgCheck_GetPolySubdivisionBounds.s,BgCheck_GetPolySubdivisionBounds,0x800C2A30,0x6C
asm/non_matchings/code/z_bgcheck/BgCheck_PolyIntersectsSubdivision.s,BgCheck_PolyIntersectsSubdivision,0x800C2BE0,0x1D5
asm/non_matchings/code/z_bgcheck/BgCheck_InitStaticLookup.s,BgCheck_InitStaticLookup,0x800C3334,0x100
asm/non_matchings/code/z_bgcheck/BgCheck_IsSmallMemScene.s,BgCheck_IsSmallMemScene,0x800C3734,0x11
asm/non_matchings/code/z_bgcheck/BgCheck_TryGetCustomMemsize.s,BgCheck_TryGetCustomMemsize,0x800C3778,0x11
asm/non_matchings/code/z_bgcheck/BgCheck_SetSubdivisionDimension.s,BgCheck_SetSubdivisionDimension,0x800C37BC,0x22
asm/non_matchings/code/z_bgcheck/BgCheck_GetSpecialSceneMaxObjects.s,BgCheck_GetSpecialSceneMaxObjects,0x800C3844,0x16
asm/non_matchings/code/z_bgcheck/BgCheck_Init.s,BgCheck_Init,0x800C389C,0xD9
asm/non_matchings/code/z_bgcheck/func_800C3C00.s,func_800C3C00,0x800C3C00,0x5
asm/non_matchings/code/z_bgcheck/func_800C3C14.s,func_800C3C14,0x800C3C14,0x6
asm/non_matchings/code/z_bgcheck/BgCheck_GetActorMeshHeader.s,BgCheck_GetActorMeshHeader,0x800C3C2C,0x1A
asm/non_matchings/code/z_bgcheck/BgCheck_SetContextFlags.s,BgCheck_SetContextFlags,0x800C3C00,0x5
asm/non_matchings/code/z_bgcheck/BgCheck_UnsetContextFlags.s,BgCheck_UnsetContextFlags,0x800C3C14,0x6
asm/non_matchings/code/z_bgcheck/BgCheck_GetCollisionHeader.s,BgCheck_GetCollisionHeader,0x800C3C2C,0x1A
asm/non_matchings/code/z_bgcheck/func_800C3C94.s,func_800C3C94,0x800C3C94,0x2F
asm/non_matchings/code/z_bgcheck/func_800C3D50.s,func_800C3D50,0x800C3D50,0x7C
asm/non_matchings/code/z_bgcheck/func_800C3F40.s,func_800C3F40,0x800C3F40,0x18
asm/non_matchings/code/z_bgcheck/func_800C3FA0.s,func_800C3FA0,0x800C3FA0,0x18
asm/non_matchings/code/z_bgcheck/func_800C4000.s,func_800C4000,0x800C4000,0x16
asm/non_matchings/code/z_bgcheck/func_800C4058.s,func_800C4058,0x800C4058,0x17
asm/non_matchings/code/z_bgcheck/func_800C40B4.s,func_800C40B4,0x800C40B4,0x1A
asm/non_matchings/code/z_bgcheck/func_800C411C.s,func_800C411C,0x800C411C,0x1B
asm/non_matchings/code/z_bgcheck/func_800C4188.s,func_800C4188,0x800C4188,0x17
asm/non_matchings/code/z_bgcheck/func_800C41E4.s,func_800C41E4,0x800C41E4,0x17
asm/non_matchings/code/z_bgcheck/func_800C4240.s,func_800C4240,0x800C4240,0x1A
asm/non_matchings/code/z_bgcheck/func_800C42A8.s,func_800C42A8,0x800C42A8,0x1B
asm/non_matchings/code/z_bgcheck/func_800C4314.s,func_800C4314,0x800C4314,0x2E
asm/non_matchings/code/z_bgcheck/func_800C43CC.s,func_800C43CC,0x800C43CC,0x2F
asm/non_matchings/code/z_bgcheck/func_800C4488.s,func_800C4488,0x800C4488,0x1A
asm/non_matchings/code/z_bgcheck/func_800C44F0.s,func_800C44F0,0x800C44F0,0x1B
asm/non_matchings/code/z_bgcheck/func_800C455C.s,func_800C455C,0x800C455C,0x1A
asm/non_matchings/code/z_bgcheck/func_800C45C4.s,func_800C45C4,0x800C45C4,0x1AC
asm/non_matchings/code/z_bgcheck/func_800C4C74.s,func_800C4C74,0x800C4C74,0x19
asm/non_matchings/code/z_bgcheck/func_800C4CD8.s,func_800C4CD8,0x800C4CD8,0x19
asm/non_matchings/code/z_bgcheck/func_800C4D3C.s,func_800C4D3C,0x800C4D3C,0x1A
asm/non_matchings/code/z_bgcheck/func_800C4DA4.s,func_800C4DA4,0x800C4DA4,0x1B
asm/non_matchings/code/z_bgcheck/func_800C4E10.s,func_800C4E10,0x800C4E10,0x4A
asm/non_matchings/code/z_bgcheck/func_800C4F38.s,func_800C4F38,0x800C4F38,0x13
asm/non_matchings/code/z_bgcheck/func_800C4F84.s,func_800C4F84,0x800C4F84,0x14
asm/non_matchings/code/z_bgcheck/BgCheck_RaycastFloorImpl.s,BgCheck_RaycastFloorImpl,0x800C3D50,0x7C
asm/non_matchings/code/z_bgcheck/BgCheck_CameraRaycastFloor1.s,BgCheck_CameraRaycastFloor1,0x800C3F40,0x18
asm/non_matchings/code/z_bgcheck/BgCheck_EntityRaycastFloor1.s,BgCheck_EntityRaycastFloor1,0x800C3FA0,0x18
asm/non_matchings/code/z_bgcheck/BgCheck_EntityRaycastFloor2.s,BgCheck_EntityRaycastFloor2,0x800C4000,0x16
asm/non_matchings/code/z_bgcheck/BgCheck_EntityRaycastFloor2_1.s,BgCheck_EntityRaycastFloor2_1,0x800C4058,0x17
asm/non_matchings/code/z_bgcheck/BgCheck_EntityRaycastFloor3.s,BgCheck_EntityRaycastFloor3,0x800C40B4,0x1A
asm/non_matchings/code/z_bgcheck/BgCheck_EntityRaycastFloor5.s,BgCheck_EntityRaycastFloor5,0x800C411C,0x1B
asm/non_matchings/code/z_bgcheck/BgCheck_EntityRaycastFloor5_2.s,BgCheck_EntityRaycastFloor5_2,0x800C4188,0x17
asm/non_matchings/code/z_bgcheck/BgCheck_EntityRaycastFloor5_3.s,BgCheck_EntityRaycastFloor5_3,0x800C41E4,0x17
asm/non_matchings/code/z_bgcheck/BgCheck_EntityRaycastFloor6.s,BgCheck_EntityRaycastFloor6,0x800C4240,0x1A
asm/non_matchings/code/z_bgcheck/BgCheck_EntityRaycastFloor7.s,BgCheck_EntityRaycastFloor7,0x800C42A8,0x1B
asm/non_matchings/code/z_bgcheck/BgCheck_AnyRaycastFloor1.s,BgCheck_AnyRaycastFloor1,0x800C4314,0x2E
asm/non_matchings/code/z_bgcheck/BgCheck_AnyRaycastFloor2.s,BgCheck_AnyRaycastFloor2,0x800C43CC,0x2F
asm/non_matchings/code/z_bgcheck/BgCheck_CameraRaycastFloor2.s,BgCheck_CameraRaycastFloor2,0x800C4488,0x1A
asm/non_matchings/code/z_bgcheck/BgCheck_EntityRaycastFloor8.s,BgCheck_EntityRaycastFloor8,0x800C44F0,0x1B
asm/non_matchings/code/z_bgcheck/BgCheck_EntityRaycastFloor9.s,BgCheck_EntityRaycastFloor9,0x800C455C,0x1A
asm/non_matchings/code/z_bgcheck/BgCheck_CheckWallImpl.s,BgCheck_CheckWallImpl,0x800C45C4,0x1AC
asm/non_matchings/code/z_bgcheck/BgCheck_EntitySphVsWall1.s,BgCheck_EntitySphVsWall1,0x800C4C74,0x19
asm/non_matchings/code/z_bgcheck/BgCheck_EntitySphVsWall2.s,BgCheck_EntitySphVsWall2,0x800C4CD8,0x19
asm/non_matchings/code/z_bgcheck/BgCheck_EntitySphVsWall3.s,BgCheck_EntitySphVsWall3,0x800C4D3C,0x1A
asm/non_matchings/code/z_bgcheck/BgCheck_EntitySphVsWall4.s,BgCheck_EntitySphVsWall4,0x800C4DA4,0x1B
asm/non_matchings/code/z_bgcheck/BgCheck_CheckCeilingImpl.s,BgCheck_CheckCeilingImpl,0x800C4E10,0x4A
asm/non_matchings/code/z_bgcheck/BgCheck_AnyCheckCeiling.s,BgCheck_AnyCheckCeiling,0x800C4F38,0x13
asm/non_matchings/code/z_bgcheck/BgCheck_EntityCheckCeiling.s,BgCheck_EntityCheckCeiling,0x800C4F84,0x14
asm/non_matchings/code/z_bgcheck/func_800C4FD4.s,func_800C4FD4,0x800C4FD4,0x124
asm/non_matchings/code/z_bgcheck/func_800C5464.s,func_800C5464,0x800C5464,0x12
asm/non_matchings/code/z_bgcheck/func_800C54AC.s,func_800C54AC,0x800C54AC,0x23
asm/non_matchings/code/z_bgcheck/func_800C5538.s,func_800C5538,0x800C5538,0x23
asm/non_matchings/code/z_bgcheck/func_800C55C4.s,func_800C55C4,0x800C55C4,0x23
asm/non_matchings/code/z_bgcheck/func_800C5650.s,func_800C5650,0x800C5650,0x24
asm/non_matchings/code/z_bgcheck/func_800C56E0.s,func_800C56E0,0x800C56E0,0x23
asm/non_matchings/code/z_bgcheck/func_800C576C.s,func_800C576C,0x800C576C,0x23
asm/non_matchings/code/z_bgcheck/func_800C57F8.s,func_800C57F8,0x800C57F8,0x11
asm/non_matchings/code/z_bgcheck/func_800C583C.s,func_800C583C,0x800C583C,0x23
asm/non_matchings/code/z_bgcheck/func_800C58C8.s,func_800C58C8,0x800C58C8,0x23
asm/non_matchings/code/z_bgcheck/func_800C5954.s,func_800C5954,0x800C5954,0x33
asm/non_matchings/code/z_bgcheck/func_800C5A20.s,func_800C5A20,0x800C5A20,0x11
asm/non_matchings/code/z_bgcheck/func_800C5A64.s,func_800C5A64,0x800C5A64,0x12
asm/non_matchings/code/z_bgcheck/BgCheck_ScenePolygonListsInit.s,BgCheck_ScenePolygonListsInit,0x800C5AAC,0x6
asm/non_matchings/code/z_bgcheck/BgCheck_ScenePolygonListsAlloc.s,BgCheck_ScenePolygonListsAlloc,0x800C5AC4,0x2F
asm/non_matchings/code/z_bgcheck/func_800C5B80.s,func_800C5B80,0x800C5B80,0xF
asm/non_matchings/code/z_bgcheck/BgCheck_ScenePolygonListsReserveNode.s,BgCheck_ScenePolygonListsReserveNode,0x800C5BBC,0x5
asm/non_matchings/code/z_bgcheck/BgCheck_ActorMeshParamsInit.s,BgCheck_ActorMeshParamsInit,0x800C5BD0,0xF
asm/non_matchings/code/z_bgcheck/BgCheck_SetActorMeshParams.s,BgCheck_SetActorMeshParams,0x800C5C0C,0x14
asm/non_matchings/code/z_bgcheck/BgCheck_CameraLineTest1.s,BgCheck_CameraLineTest1,0x800C54AC,0x23
asm/non_matchings/code/z_bgcheck/BgCheck_CameraLineTest2.s,BgCheck_CameraLineTest2,0x800C5538,0x23
asm/non_matchings/code/z_bgcheck/BgCheck_EntityLineTest1.s,BgCheck_EntityLineTest1,0x800C55C4,0x23
asm/non_matchings/code/z_bgcheck/BgCheck_EntityLineTest2.s,BgCheck_EntityLineTest2,0x800C5650,0x24
asm/non_matchings/code/z_bgcheck/BgCheck_EntityLineTest3.s,BgCheck_EntityLineTest3,0x800C56E0,0x23
asm/non_matchings/code/z_bgcheck/BgCheck_ProjectileLineTest.s,BgCheck_ProjectileLineTest,0x800C576C,0x23
asm/non_matchings/code/z_bgcheck/BgCheck_AnyLineTest1.s,BgCheck_AnyLineTest1,0x800C57F8,0x11
asm/non_matchings/code/z_bgcheck/BgCheck_AnyLineTest2.s,BgCheck_AnyLineTest2,0x800C583C,0x23
asm/non_matchings/code/z_bgcheck/BgCheck_AnyLineTest3.s,BgCheck_AnyLineTest3,0x800C58C8,0x23
asm/non_matchings/code/z_bgcheck/BgCheck_SphVsFirstPolyImpl.s,BgCheck_SphVsFirstPolyImpl,0x800C5954,0x33
asm/non_matchings/code/z_bgcheck/BgCheck_SphVsFirstPoly.s,BgCheck_SphVsFirstPoly,0x800C5A20,0x11
asm/non_matchings/code/z_bgcheck/BgCheck_SphVsFirstWall.s,BgCheck_SphVsFirstWall,0x800C5A64,0x12
asm/non_matchings/code/z_bgcheck/SSNodeList_Init.s,SSNodeList_Init,0x800C5AAC,0x6
asm/non_matchings/code/z_bgcheck/SSNodeList_Alloc.s,SSNodeList_Alloc,0x800C5AC4,0x2F
asm/non_matchings/code/z_bgcheck/SSNodeList_GetNextNode.s,SSNodeList_GetNextNode,0x800C5B80,0xF
asm/non_matchings/code/z_bgcheck/SSNodeList_GetNextNodeIdx.s,SSNodeList_GetNextNodeIdx,0x800C5BBC,0x5
asm/non_matchings/code/z_bgcheck/ScaleRotPos_Init.s,ScaleRotPos_Init,0x800C5BD0,0xF
asm/non_matchings/code/z_bgcheck/ScaleRotPos_SetValue.s,ScaleRotPos_SetValue,0x800C5C0C,0x14
asm/non_matchings/code/z_bgcheck/BgCheck_AreActorMeshParamsEqual.s,BgCheck_AreActorMeshParamsEqual,0x800C5C5C,0x35
asm/non_matchings/code/z_bgcheck/BgCheck_ActorMeshPolyListsHeadsInit.s,BgCheck_ActorMeshPolyListsHeadsInit,0x800C5D30,0x10
asm/non_matchings/code/z_bgcheck/BgCheck_ActorMeshPolyListsInit.s,BgCheck_ActorMeshPolyListsInit,0x800C5D70,0x8
asm/non_matchings/code/z_bgcheck/BgCheck_ActorMeshVerticesIndexInit.s,BgCheck_ActorMeshVerticesIndexInit,0x800C5D90,0x3
asm/non_matchings/code/z_bgcheck/BgCheck_ActorMeshWaterboxesIndexInit.s,BgCheck_ActorMeshWaterboxesIndexInit,0x800C5D9C,0x3
asm/non_matchings/code/z_bgcheck/BgCheck_ActorMeshInit.s,BgCheck_ActorMeshInit,0x800C5DA8,0x1A
asm/non_matchings/code/z_bgcheck/BgCheck_ActorMeshInitFromActor.s,BgCheck_ActorMeshInitFromActor,0x800C5E10,0x2E
asm/non_matchings/code/z_bgcheck/BgCheck_HasActorMeshChanged.s,BgCheck_HasActorMeshChanged,0x800C5EC8,0xA
asm/non_matchings/code/z_bgcheck/BgCheck_PolygonsInit.s,BgCheck_PolygonsInit,0x800C5EF0,0x3
asm/non_matchings/code/z_bgcheck/BgCheck_PolygonsAlloc.s,BgCheck_PolygonsAlloc,0x800C5EFC,0xF
asm/non_matchings/code/z_bgcheck/BgCheck_VerticesInit.s,BgCheck_VerticesInit,0x800C5F38,0x3
asm/non_matchings/code/z_bgcheck/BgCheck_VerticesListAlloc.s,BgCheck_VerticesListAlloc,0x800C5F44,0x12
asm/non_matchings/code/z_bgcheck/BgCheck_WaterboxListInit.s,BgCheck_WaterboxListInit,0x800C5F8C,0x4
asm/non_matchings/code/z_bgcheck/BgCheck_WaterboxListAlloc.s,BgCheck_WaterboxListAlloc,0x800C5F9C,0xF
asm/non_matchings/code/z_bgcheck/BgCheck_ActorMeshUpdateParams.s,BgCheck_ActorMeshUpdateParams,0x800C5FD8,0x13
asm/non_matchings/code/z_bgcheck/BgCheck_IsActorMeshIndexValid.s,BgCheck_IsActorMeshIndexValid,0x800C6024,0x8
asm/non_matchings/code/z_bgcheck/BgCheck_DynaInit.s,BgCheck_DynaInit,0x800C6044,0x15
asm/non_matchings/code/z_bgcheck/BgCheck_DynaAlloc.s,BgCheck_DynaAlloc,0x800C6098,0x3C
asm/non_matchings/code/z_bgcheck/BgCheck_AddActorMesh.s,BgCheck_AddActorMesh,0x800C6188,0x30
asm/non_matchings/code/z_bgcheck/BgCheck_GetActorOfMesh.s,BgCheck_GetActorOfMesh,0x800C6248,0x1D
asm/non_matchings/code/z_bgcheck/DynaLookup_ResetLists.s,DynaLookup_ResetLists,0x800C5D30,0x10
asm/non_matchings/code/z_bgcheck/DynaLookup_Reset.s,DynaLookup_Reset,0x800C5D70,0x8
asm/non_matchings/code/z_bgcheck/DynaLookup_ResetVtxStartIndex.s,DynaLookup_ResetVtxStartIndex,0x800C5D90,0x3
asm/non_matchings/code/z_bgcheck/DynaLookup_ResetWaterBoxStartIndex.s,DynaLookup_ResetWaterBoxStartIndex,0x800C5D9C,0x3
asm/non_matchings/code/z_bgcheck/BgActor_Init.s,BgActor_Init,0x800C5DA8,0x1A
asm/non_matchings/code/z_bgcheck/BgActor_InitFromActor.s,BgActor_InitFromActor,0x800C5E10,0x2E
asm/non_matchings/code/z_bgcheck/BgActor_IsTransformUnchanged.s,BgActor_IsTransformUnchanged,0x800C5EC8,0xA
asm/non_matchings/code/z_bgcheck/DynaPoly_NullPolyList.s,DynaPoly_NullPolyList,0x800C5EF0,0x3
asm/non_matchings/code/z_bgcheck/DynaPoly_AllocPolyList.s,DynaPoly_AllocPolyList,0x800C5EFC,0xF
asm/non_matchings/code/z_bgcheck/DynaPoly_NullVtxList.s,DynaPoly_NullVtxList,0x800C5F38,0x3
asm/non_matchings/code/z_bgcheck/DynaPoly_AllocVtxList.s,DynaPoly_AllocVtxList,0x800C5F44,0x12
asm/non_matchings/code/z_bgcheck/DynaPoly_InitWaterBoxList.s,DynaPoly_InitWaterBoxList,0x800C5F8C,0x4
asm/non_matchings/code/z_bgcheck/DynaPoly_AllocWaterBoxList.s,DynaPoly_AllocWaterBoxList,0x800C5F9C,0xF
asm/non_matchings/code/z_bgcheck/DynaPoly_SetBgActorPrevTransform.s,DynaPoly_SetBgActorPrevTransform,0x800C5FD8,0x13
asm/non_matchings/code/z_bgcheck/DynaPoly_IsBgIdBgActor.s,DynaPoly_IsBgIdBgActor,0x800C6024,0x8
asm/non_matchings/code/z_bgcheck/DynaPoly_Init.s,DynaPoly_Init,0x800C6044,0x15
asm/non_matchings/code/z_bgcheck/DynaPoly_Alloc.s,DynaPoly_Alloc,0x800C6098,0x3C
asm/non_matchings/code/z_bgcheck/DynaPoly_SetBgActor.s,DynaPoly_SetBgActor,0x800C6188,0x30
asm/non_matchings/code/z_bgcheck/DynaPoly_GetActor.s,DynaPoly_GetActor,0x800C6248,0x1D
asm/non_matchings/code/z_bgcheck/func_800C62BC.s,func_800C62BC,0x800C62BC,0x16
asm/non_matchings/code/z_bgcheck/func_800C6314.s,func_800C6314,0x800C6314,0x16
asm/non_matchings/code/z_bgcheck/func_800C636C.s,func_800C636C,0x800C636C,0x16
asm/non_matchings/code/z_bgcheck/func_800C63C4.s,func_800C63C4,0x800C63C4,0x16
asm/non_matchings/code/z_bgcheck/func_800C641C.s,func_800C641C,0x800C641C,0x16
asm/non_matchings/code/z_bgcheck/func_800C6474.s,func_800C6474,0x800C6474,0x16
asm/non_matchings/code/z_bgcheck/BgCheck_RemoveActorMesh.s,BgCheck_RemoveActorMesh,0x800C64CC,0x22
asm/non_matchings/code/z_bgcheck/DynaPoly_DeleteBgActor.s,DynaPoly_DeleteBgActor,0x800C64CC,0x22
asm/non_matchings/code/z_bgcheck/func_800C6554.s,func_800C6554,0x800C6554,0x6
asm/non_matchings/code/z_bgcheck/BgCheck_CalcWaterboxDimensions.s,BgCheck_CalcWaterboxDimensions,0x800C656C,0xB3
asm/non_matchings/code/z_bgcheck/BgCheck_AddActorMeshToLists.s,BgCheck_AddActorMeshToLists,0x800C6838,0x2C5
asm/non_matchings/code/z_bgcheck/DynaPoly_SetBgActorToLists.s,DynaPoly_SetBgActorToLists,0x800C6838,0x2C5
asm/non_matchings/code/z_bgcheck/BgCheck_ResetFlagsIfLoadedActor.s,BgCheck_ResetFlagsIfLoadedActor,0x800C734C,0x26
asm/non_matchings/code/z_bgcheck/BgCheck_Update.s,BgCheck_Update,0x800C73E4,0x62
asm/non_matchings/code/z_bgcheck/DynaPoly_Setup.s,DynaPoly_Setup,0x800C73E4,0x62
asm/non_matchings/code/z_bgcheck/func_800C756C.s,func_800C756C,0x800C756C,0x3C
asm/non_matchings/code/z_bgcheck/BgCheck_UpdateAllActorMeshes.s,BgCheck_UpdateAllActorMeshes,0x800C765C,0x24
asm/non_matchings/code/z_bgcheck/DynaPoly_SetupAllActorMeshes.s,DynaPoly_SetupAllActorMeshes,0x800C765C,0x24
asm/non_matchings/code/z_bgcheck/func_800C76EC.s,func_800C76EC,0x800C76EC,0xA2
asm/non_matchings/code/z_bgcheck/func_800C7974.s,func_800C7974,0x800C7974,0x133
asm/non_matchings/code/z_bgcheck/func_800C7E40.s,func_800C7E40,0x800C7E40,0x1D0
@ -556,19 +556,19 @@ asm/non_matchings/code/z_bgcheck/func_800C8EEC.s,func_800C8EEC,0x800C8EEC,0x70
asm/non_matchings/code/z_bgcheck/func_800C90AC.s,func_800C90AC,0x800C90AC,0x5C
asm/non_matchings/code/z_bgcheck/func_800C921C.s,func_800C921C,0x800C921C,0x59
asm/non_matchings/code/z_bgcheck/func_800C9380.s,func_800C9380,0x800C9380,0x58
asm/non_matchings/code/z_bgcheck/BgCheck_RelocateMeshHeaderPointers.s,BgCheck_RelocateMeshHeaderPointers,0x800C94E0,0x21
asm/non_matchings/code/z_bgcheck/BgCheck_RelocateMeshHeader.s,BgCheck_RelocateMeshHeader,0x800C9564,0xD
asm/non_matchings/code/z_bgcheck/BgCheck_RelocateAllMeshHeaders.s,BgCheck_RelocateAllMeshHeaders,0x800C9598,0x2A
asm/non_matchings/code/z_bgcheck/CollisionHeader_GetVirtualPointers.s,CollisionHeader_GetVirtualPointers,0x800C94E0,0x21
asm/non_matchings/code/z_bgcheck/CollisionHeader_GetVirtual.s,CollisionHeader_GetVirtual,0x800C9564,0xD
asm/non_matchings/code/z_bgcheck/BgCheck_InitCollisionHeaders.s,BgCheck_InitCollisionHeaders,0x800C9598,0x2A
asm/non_matchings/code/z_bgcheck/func_800C9640.s,func_800C9640,0x800C9640,0x15
asm/non_matchings/code/z_bgcheck/BgCheck_GetPolygonAttributes.s,BgCheck_GetPolygonAttributes,0x800C9694,0x1C
asm/non_matchings/code/z_bgcheck/func_800C9704.s,func_800C9704,0x800C9704,0x9
asm/non_matchings/code/z_bgcheck/SurfaceType_GetData.s,SurfaceType_GetData,0x800C9694,0x1C
asm/non_matchings/code/z_bgcheck/SurfaceType_GetCamDataIndex.s,SurfaceType_GetCamDataIndex,0x800C9704,0x9
asm/non_matchings/code/z_bgcheck/func_800C9728.s,func_800C9728,0x800C9728,0x12
asm/non_matchings/code/z_bgcheck/func_800C9770.s,func_800C9770,0x800C9770,0x22
asm/non_matchings/code/z_bgcheck/func_800C97F8.s,func_800C97F8,0x800C97F8,0x13
asm/non_matchings/code/z_bgcheck/func_800C9844.s,func_800C9844,0x800C9844,0x22
asm/non_matchings/code/z_bgcheck/func_800C98CC.s,func_800C98CC,0x800C98CC,0x16
asm/non_matchings/code/z_bgcheck/func_800C9924.s,func_800C9924,0x800C9924,0x22
asm/non_matchings/code/z_bgcheck/func_800C99AC.s,func_800C99AC,0x800C99AC,0xA
asm/non_matchings/code/z_bgcheck/SurfaceType_GetCamPosData.s,SurfaceType_GetCamPosData,0x800C9924,0x22
asm/non_matchings/code/z_bgcheck/SurfaceType_GetSceneExitIndex.s,SurfaceType_GetSceneExitIndex,0x800C99AC,0xA
asm/non_matchings/code/z_bgcheck/func_800C99D4.s,func_800C99D4,0x800C99D4,0xA
asm/non_matchings/code/z_bgcheck/func_800C99FC.s,func_800C99FC,0x800C99FC,0xA
asm/non_matchings/code/z_bgcheck/func_800C9A24.s,func_800C9A24,0x800C9A24,0xA
@ -579,30 +579,30 @@ asm/non_matchings/code/z_bgcheck/func_800C9AE4.s,func_800C9AE4,0x800C9AE4,0xD
asm/non_matchings/code/z_bgcheck/func_800C9B18.s,func_800C9B18,0x800C9B18,0xA
asm/non_matchings/code/z_bgcheck/func_800C9B40.s,func_800C9B40,0x800C9B40,0xA
asm/non_matchings/code/z_bgcheck/func_800C9B68.s,func_800C9B68,0x800C9B68,0xA
asm/non_matchings/code/z_bgcheck/func_800C9B90.s,func_800C9B90,0x800C9B90,0xA
asm/non_matchings/code/z_bgcheck/SurfaceType_IsHorseBlocked.s,SurfaceType_IsHorseBlocked,0x800C9B90,0xA
asm/non_matchings/code/z_bgcheck/func_800C9BB8.s,func_800C9BB8,0x800C9BB8,0x9
asm/non_matchings/code/z_bgcheck/func_800C9BDC.s,func_800C9BDC,0x800C9BDC,0x12
asm/non_matchings/code/z_bgcheck/SurfaceType_GetSfx.s,SurfaceType_GetSfx,0x800C9BDC,0x12
asm/non_matchings/code/z_bgcheck/func_800C9C24.s,func_800C9C24,0x800C9C24,0x14
asm/non_matchings/code/z_bgcheck/func_800C9C74.s,func_800C9C74,0x800C9C74,0xA
asm/non_matchings/code/z_bgcheck/func_800C9C9C.s,func_800C9C9C,0x800C9C9C,0xA
asm/non_matchings/code/z_bgcheck/func_800C9CC4.s,func_800C9CC4,0x800C9CC4,0xA
asm/non_matchings/code/z_bgcheck/func_800C9CEC.s,func_800C9CEC,0x800C9CEC,0xA
asm/non_matchings/code/z_bgcheck/func_800C9D14.s,func_800C9D14,0x800C9D14,0xF
asm/non_matchings/code/z_bgcheck/func_800C9D50.s,func_800C9D50,0x800C9D50,0xF
asm/non_matchings/code/z_bgcheck/func_800C9D8C.s,func_800C9D8C,0x800C9D8C,0x14
asm/non_matchings/code/z_bgcheck/SurfaceType_GetSlope.s,SurfaceType_GetSlope,0x800C9C74,0xA
asm/non_matchings/code/z_bgcheck/SurfaceType_GetLightSettingIndex.s,SurfaceType_GetLightSettingIndex,0x800C9C9C,0xA
asm/non_matchings/code/z_bgcheck/SurfaceType_GetEcho.s,SurfaceType_GetEcho,0x800C9CC4,0xA
asm/non_matchings/code/z_bgcheck/SurfaceType_IsHookshotSurface.s,SurfaceType_IsHookshotSurface,0x800C9CEC,0xA
asm/non_matchings/code/z_bgcheck/SurfaceType_IsIgnoredByEntities.s,SurfaceType_IsIgnoredByEntities,0x800C9D14,0xF
asm/non_matchings/code/z_bgcheck/SurfaceType_IsIgnoredByProjectiles.s,SurfaceType_IsIgnoredByProjectiles,0x800C9D50,0xF
asm/non_matchings/code/z_bgcheck/SurfaceType_IsConveyor.s,SurfaceType_IsConveyor,0x800C9D8C,0x14
asm/non_matchings/code/z_bgcheck/func_800C9DDC.s,func_800C9DDC,0x800C9DDC,0xF
asm/non_matchings/code/z_bgcheck/func_800C9E18.s,func_800C9E18,0x800C9E18,0xA
asm/non_matchings/code/z_bgcheck/func_800C9E40.s,func_800C9E40,0x800C9E40,0x12
asm/non_matchings/code/z_bgcheck/func_800C9E88.s,func_800C9E88,0x800C9E88,0xD
asm/non_matchings/code/z_bgcheck/func_800C9EBC.s,func_800C9EBC,0x800C9EBC,0xBC
asm/non_matchings/code/z_bgcheck/func_800CA1AC.s,func_800CA1AC,0x800CA1AC,0xF
asm/non_matchings/code/z_bgcheck/func_800CA1E8.s,func_800CA1E8,0x800CA1E8,0x11
asm/non_matchings/code/z_bgcheck/func_800CA22C.s,func_800CA22C,0x800CA22C,0xCF
asm/non_matchings/code/z_bgcheck/SurfaceType_GetConveyorSpeed.s,SurfaceType_GetConveyorSpeed,0x800C9E18,0xA
asm/non_matchings/code/z_bgcheck/SurfaceType_GetConveyorDirection.s,SurfaceType_GetConveyorDirection,0x800C9E40,0x12
asm/non_matchings/code/z_bgcheck/SurfaceType_IsWallDamage.s,SurfaceType_IsWallDamage,0x800C9E88,0xD
asm/non_matchings/code/z_bgcheck/WaterBox_GetSurfaceImpl.s,WaterBox_GetSurfaceImpl,0x800C9EBC,0xBC
asm/non_matchings/code/z_bgcheck/WaterBox_GetSurface1.s,WaterBox_GetSurface1,0x800CA1AC,0xF
asm/non_matchings/code/z_bgcheck/WaterBox_GetSurface1_2.s,WaterBox_GetSurface1_2,0x800CA1E8,0x11
asm/non_matchings/code/z_bgcheck/WaterBox_GetSurface2.s,WaterBox_GetSurface2,0x800CA22C,0xCF
asm/non_matchings/code/z_bgcheck/func_800CA568.s,func_800CA568,0x800CA568,0x33
asm/non_matchings/code/z_bgcheck/func_800CA634.s,func_800CA634,0x800CA634,0x5
asm/non_matchings/code/z_bgcheck/func_800CA648.s,func_800CA648,0x800CA648,0x1C
asm/non_matchings/code/z_bgcheck/func_800CA6B8.s,func_800CA6B8,0x800CA6B8,0x8
asm/non_matchings/code/z_bgcheck/func_800CA6D8.s,func_800CA6D8,0x800CA6D8,0x6
asm/non_matchings/code/z_bgcheck/WaterBox_GetLightSettingIndex.s,WaterBox_GetLightSettingIndex,0x800CA6D8,0x6
asm/non_matchings/code/z_bgcheck/func_800CA6F0.s,func_800CA6F0,0x800CA6F0,0xB8
asm/non_matchings/code/z_bgcheck/func_800CA9D0.s,func_800CA9D0,0x800CA9D0,0x11
asm/non_matchings/code/z_bgcheck/func_800CAA14.s,func_800CAA14,0x800CAA14,0x2F
@ -2770,7 +2770,7 @@ asm/non_matchings/code/sys_math/cos_rad.s,cos_rad,0x80179540,0x15
asm/non_matchings/code/sys_math/Rand_ZeroFloat.s,Rand_ZeroFloat,0x80179594,0xB
asm/non_matchings/code/sys_math/randPlusMinusPoint5Scaled.s,randPlusMinusPoint5Scaled,0x801795C0,0xC
asm/non_matchings/code/sys_math3d/Math3D_Normalize.s,Math3D_Normalize,0x801795F0,0x22
asm/non_matchings/code/sys_math3d/func_80179678.s,func_80179678,0x80179678,0x48
asm/non_matchings/code/sys_math3d/Math3D_PlaneVsLineSegClosestPoint.s,Math3D_PlaneVsLineSegClosestPoint,0x80179678,0x48
asm/non_matchings/code/sys_math3d/func_80179798.s,func_80179798,0x80179798,0xAB
asm/non_matchings/code/sys_math3d/func_80179A44.s,func_80179A44,0x80179A44,0x3C
asm/non_matchings/code/sys_math3d/func_80179B34.s,func_80179B34,0x80179B34,0x18
@ -2792,46 +2792,46 @@ asm/non_matchings/code/sys_math3d/Math3D_XZDistanceSquared.s,Math3D_XZDistanceSq
asm/non_matchings/code/sys_math3d/Math3D_XZDistance.s,Math3D_XZDistance,0x8017A678,0xC
asm/non_matchings/code/sys_math3d/Math3D_LengthSquared.s,Math3D_LengthSquared,0x8017A6A8,0xB
asm/non_matchings/code/sys_math3d/Math3D_Vec3fMagnitude.s,Math3D_Vec3fMagnitude,0x8017A6D4,0x9
asm/non_matchings/code/sys_math3d/Math3D_DistanceSquared.s,Math3D_DistanceSquared,0x8017A6F8,0xA
asm/non_matchings/code/sys_math3d/Math3D_Vec3fDistSq.s,Math3D_Vec3fDistSq,0x8017A6F8,0xA
asm/non_matchings/code/sys_math3d/Math3D_Distance.s,Math3D_Distance,0x8017A720,0x8
asm/non_matchings/code/sys_math3d/Math3D_DistanceS.s,Math3D_DistanceS,0x8017A740,0x1E
asm/non_matchings/code/sys_math3d/func_8017A7B8.s,func_8017A7B8,0x8017A7B8,0x10
asm/non_matchings/code/sys_math3d/func_8017A7F8.s,func_8017A7F8,0x8017A7F8,0x10
asm/non_matchings/code/sys_math3d/func_8017A838.s,func_8017A838,0x8017A838,0x10
asm/non_matchings/code/sys_math3d/Math3D_CrossProduct.s,Math3D_CrossProduct,0x8017A878,0x1D
asm/non_matchings/code/sys_math3d/Math3D_NormalVector.s,Math3D_NormalVector,0x8017A8EC,0x1A
asm/non_matchings/code/sys_math3d/func_8017A954.s,func_8017A954,0x8017A954,0x2E
asm/non_matchings/code/sys_math3d/func_8017AA0C.s,func_8017AA0C,0x8017AA0C,0x6C
asm/non_matchings/code/sys_math3d/func_8017ABBC.s,func_8017ABBC,0x8017ABBC,0x5F
asm/non_matchings/code/sys_math3d/func_8017AD38.s,func_8017AD38,0x8017AD38,0x255
asm/non_matchings/code/sys_math3d/Math3D_SurfaceNorm.s,Math3D_SurfaceNorm,0x8017A8EC,0x1A
asm/non_matchings/code/sys_math3d/Math3D_PointRelativeToCubeFaces.s,Math3D_PointRelativeToCubeFaces,0x8017A954,0x2E
asm/non_matchings/code/sys_math3d/Math3D_PointRelativeToCubeEdges.s,Math3D_PointRelativeToCubeEdges,0x8017AA0C,0x6C
asm/non_matchings/code/sys_math3d/Math3D_PointRelativeToCubeVertices.s,Math3D_PointRelativeToCubeVertices,0x8017ABBC,0x5F
asm/non_matchings/code/sys_math3d/Math3D_LineVsCube.s,Math3D_LineVsCube,0x8017AD38,0x255
asm/non_matchings/code/sys_math3d/func_8017B68C.s,func_8017B68C,0x8017B68C,0x5B
asm/non_matchings/code/sys_math3d/func_8017B7F8.s,func_8017B7F8,0x8017B7F8,0x23
asm/non_matchings/code/sys_math3d/Math3D_UnitNormalVector.s,Math3D_UnitNormalVector,0x8017B884,0x45
asm/non_matchings/code/sys_math3d/Math3D_SignedDistanceFromPlane.s,Math3D_SignedDistanceFromPlane,0x8017B998,0x10
asm/non_matchings/code/sys_math3d/func_8017B9D8.s,func_8017B9D8,0x8017B9D8,0xF
asm/non_matchings/code/sys_math3d/Math3D_NormalizedDistanceFromPlane.s,Math3D_NormalizedDistanceFromPlane,0x8017BA14,0xE
asm/non_matchings/code/sys_math3d/Math3D_NormalizedSignedDistanceFromPlane.s,Math3D_NormalizedSignedDistanceFromPlane,0x8017BA4C,0x21
asm/non_matchings/code/sys_math3d/func_8017BAD0.s,func_8017BAD0,0x8017BAD0,0xB2
asm/non_matchings/code/sys_math3d/Math3D_UDistPlaneToPos.s,Math3D_UDistPlaneToPos,0x8017BA14,0xE
asm/non_matchings/code/sys_math3d/Math3D_DistPlaneToPos.s,Math3D_DistPlaneToPos,0x8017BA4C,0x21
asm/non_matchings/code/sys_math3d/Math3D_TriChkPointParaYDist.s,Math3D_TriChkPointParaYDist,0x8017BAD0,0xB2
asm/non_matchings/code/sys_math3d/func_8017BD98.s,func_8017BD98,0x8017BD98,0x12
asm/non_matchings/code/sys_math3d/func_8017BDE0.s,func_8017BDE0,0x8017BDE0,0x14
asm/non_matchings/code/sys_math3d/func_8017BE30.s,func_8017BE30,0x8017BE30,0x2C
asm/non_matchings/code/sys_math3d/func_8017BEE0.s,func_8017BEE0,0x8017BEE0,0x2B
asm/non_matchings/code/sys_math3d/Math3D_TriChkPointParaYIntersectDist.s,Math3D_TriChkPointParaYIntersectDist,0x8017BE30,0x2C
asm/non_matchings/code/sys_math3d/Math3D_TriChkPointParaYIntersectInsideTri.s,Math3D_TriChkPointParaYIntersectInsideTri,0x8017BEE0,0x2B
asm/non_matchings/code/sys_math3d/func_8017BF8C.s,func_8017BF8C,0x8017BF8C,0x1F
asm/non_matchings/code/sys_math3d/func_8017C008.s,func_8017C008,0x8017C008,0x5D
asm/non_matchings/code/sys_math3d/Math3D_TriChkLineSegParaYIntersect.s,Math3D_TriChkLineSegParaYIntersect,0x8017C008,0x5D
asm/non_matchings/code/sys_math3d/func_8017C17C.s,func_8017C17C,0x8017C17C,0x1D
asm/non_matchings/code/sys_math3d/func_8017C1F0.s,func_8017C1F0,0x8017C1F0,0xA9
asm/non_matchings/code/sys_math3d/func_8017C494.s,func_8017C494,0x8017C494,0x2B
asm/non_matchings/code/sys_math3d/func_8017C540.s,func_8017C540,0x8017C540,0xB2
asm/non_matchings/code/sys_math3d/Math3D_TriChkPointParaYIntersectInsideTri2.s,Math3D_TriChkPointParaYIntersectInsideTri2,0x8017C494,0x2B
asm/non_matchings/code/sys_math3d/Math3D_TriChkPointParaXDist.s,Math3D_TriChkPointParaXDist,0x8017C540,0xB2
asm/non_matchings/code/sys_math3d/func_8017C808.s,func_8017C808,0x8017C808,0x12
asm/non_matchings/code/sys_math3d/func_8017C850.s,func_8017C850,0x8017C850,0x2D
asm/non_matchings/code/sys_math3d/Math3D_TriChkPointParaXIntersect.s,Math3D_TriChkPointParaXIntersect,0x8017C850,0x2D
asm/non_matchings/code/sys_math3d/func_8017C904.s,func_8017C904,0x8017C904,0x1F
asm/non_matchings/code/sys_math3d/func_8017C980.s,func_8017C980,0x8017C980,0x62
asm/non_matchings/code/sys_math3d/Math3D_TriChkLineSegParaXIntersect.s,Math3D_TriChkLineSegParaXIntersect,0x8017C980,0x62
asm/non_matchings/code/sys_math3d/func_8017CB08.s,func_8017CB08,0x8017CB08,0x1D
asm/non_matchings/code/sys_math3d/func_8017CB7C.s,func_8017CB7C,0x8017CB7C,0xCB
asm/non_matchings/code/sys_math3d/Math3D_TriChkLineSegParaZDist.s,Math3D_TriChkLineSegParaZDist,0x8017CB7C,0xCB
asm/non_matchings/code/sys_math3d/func_8017CEA8.s,func_8017CEA8,0x8017CEA8,0x12
asm/non_matchings/code/sys_math3d/func_8017CEF0.s,func_8017CEF0,0x8017CEF0,0x2D
asm/non_matchings/code/sys_math3d/Math3D_TriChkPointParaZIntersect.s,Math3D_TriChkPointParaZIntersect,0x8017CEF0,0x2D
asm/non_matchings/code/sys_math3d/func_8017CFA4.s,func_8017CFA4,0x8017CFA4,0x1F
asm/non_matchings/code/sys_math3d/func_8017D020.s,func_8017D020,0x8017D020,0x63
asm/non_matchings/code/sys_math3d/Math3D_TriChkLineSegParaZIntersect.s,Math3D_TriChkLineSegParaZIntersect,0x8017D020,0x63
asm/non_matchings/code/sys_math3d/func_8017D1AC.s,func_8017D1AC,0x8017D1AC,0x1D
asm/non_matchings/code/sys_math3d/func_8017D220.s,func_8017D220,0x8017D220,0x37
asm/non_matchings/code/sys_math3d/func_8017D2FC.s,func_8017D2FC,0x8017D2FC,0x42
@ -2843,7 +2843,7 @@ asm/non_matchings/code/sys_math3d/func_8017D7C0.s,func_8017D7C0,0x8017D7C0,0x15
asm/non_matchings/code/sys_math3d/func_8017D814.s,func_8017D814,0x8017D814,0x42
asm/non_matchings/code/sys_math3d/func_8017D91C.s,func_8017D91C,0x8017D91C,0x42
asm/non_matchings/code/sys_math3d/func_8017DA24.s,func_8017DA24,0x8017DA24,0x42
asm/non_matchings/code/sys_math3d/Math3D_ColSphereLineSeg.s,Math3D_ColSphereLineSeg,0x8017DB2C,0x82
asm/non_matchings/code/sys_math3d/Math3D_LineVsSph.s,Math3D_LineVsSph,0x8017DB2C,0x82
asm/non_matchings/code/sys_math3d/func_8017DD34.s,func_8017DD34,0x8017DD34,0x50
asm/non_matchings/code/sys_math3d/Math3D_ColSphereTri.s,Math3D_ColSphereTri,0x8017DE74,0x108
asm/non_matchings/code/sys_math3d/func_8017E294.s,func_8017E294,0x8017E294,0x2F
@ -2858,9 +2858,9 @@ asm/non_matchings/code/sys_math3d/Math3D_ColSphereCylinderDistanceAndAmount.s,Ma
asm/non_matchings/code/sys_math3d/Math3D_ColCylinderCylinderAmount.s,Math3D_ColCylinderCylinderAmount,0x8017F45C,0x8
asm/non_matchings/code/sys_math3d/Math3D_ColCylinderCylinderAmountAndDistance.s,Math3D_ColCylinderCylinderAmountAndDistance,0x8017F47C,0x74
asm/non_matchings/code/sys_math3d/Math3d_ColTriTri.s,Math3d_ColTriTri,0x8017F64C,0xDD
asm/non_matchings/code/sys_math3d/func_8017F9C0.s,func_8017F9C0,0x8017F9C0,0x1D
asm/non_matchings/code/sys_math3d/func_8017FA34.s,func_8017FA34,0x8017FA34,0x1D
asm/non_matchings/code/sys_math3d/func_8017FAA8.s,func_8017FAA8,0x8017FAA8,0x1D
asm/non_matchings/code/sys_math3d/Math3D_XZInSphere.s,Math3D_XZInSphere,0x8017F9C0,0x1D
asm/non_matchings/code/sys_math3d/Math3D_XYInSphere.s,Math3D_XYInSphere,0x8017FA34,0x1D
asm/non_matchings/code/sys_math3d/Math3D_YZInSphere.s,Math3D_YZInSphere,0x8017FAA8,0x1D
asm/non_matchings/code/sys_math3d/func_8017FB1C.s,func_8017FB1C,0x8017FB1C,0x8A
asm/non_matchings/code/sys_math3d/func_8017FD44.s,func_8017FD44,0x8017FD44,0x5B
asm/non_matchings/code/sys_math_atan/Math_GetAtan2Tbl.s,Math_GetAtan2Tbl,0x8017FEB0,0xD

1 asm/non_matchings/code/z_en_a_keep/EnAObj_Init.s EnAObj_Init 0x800A5AC0 0x2B
413 asm/non_matchings/code/z_actor_dlftbls/ActorOverlayTable_FaultAddrConv.s ActorOverlayTable_FaultAddrConv 0x800BFA78 0x1C
414 asm/non_matchings/code/z_actor_dlftbls/ActorOverlayTable_Init.s ActorOverlayTable_Init 0x800BFAE8 0x16
415 asm/non_matchings/code/z_actor_dlftbls/ActorOverlayTable_Cleanup.s ActorOverlayTable_Cleanup 0x800BFB40 0x10
416 asm/non_matchings/code/z_bgcheck/BgCheck_PolygonLinkedListNodeInit.s asm/non_matchings/code/z_bgcheck/SSNode_SetValue.s BgCheck_PolygonLinkedListNodeInit SSNode_SetValue 0x800BFB80 0x7
417 asm/non_matchings/code/z_bgcheck/BgCheck_PolygonLinkedListResetHead.s asm/non_matchings/code/z_bgcheck/SSList_SetNull.s BgCheck_PolygonLinkedListResetHead SSList_SetNull 0x800BFB9C 0x4
418 asm/non_matchings/code/z_bgcheck/BgCheck_ScenePolygonListsNodeInsert.s asm/non_matchings/code/z_bgcheck/SSNodeList_SetSSListHead.s BgCheck_ScenePolygonListsNodeInsert SSNodeList_SetSSListHead 0x800BFBAC 0x16
419 asm/non_matchings/code/z_bgcheck/BgCheck_PolygonLinkedListNodeInsert.s asm/non_matchings/code/z_bgcheck/DynaSSNodeList_SetSSListHead.s BgCheck_PolygonLinkedListNodeInsert DynaSSNodeList_SetSSListHead 0x800BFC04 0x16
420 asm/non_matchings/code/z_bgcheck/BgCheck_PolygonLinkedListInit.s asm/non_matchings/code/z_bgcheck/DynaSSNodeList_Init.s BgCheck_PolygonLinkedListInit DynaSSNodeList_Init 0x800BFC5C 0x5
421 asm/non_matchings/code/z_bgcheck/BgCheck_PolygonLinkedListAlloc.s asm/non_matchings/code/z_bgcheck/DynaSSNodeList_Alloc.s BgCheck_PolygonLinkedListAlloc DynaSSNodeList_Alloc 0x800BFC70 0x14
422 asm/non_matchings/code/z_bgcheck/BgCheck_PolygonLinkedListReset.s asm/non_matchings/code/z_bgcheck/DynaSSNodeList_ResetCount.s BgCheck_PolygonLinkedListReset DynaSSNodeList_ResetCount 0x800BFCC0 0x3
423 asm/non_matchings/code/z_bgcheck/BgCheck_AllocPolygonLinkedListNode.s asm/non_matchings/code/z_bgcheck/DynaSSNodeList_GetNextNodeIdx.s BgCheck_AllocPolygonLinkedListNode DynaSSNodeList_GetNextNodeIdx 0x800BFCCC 0xC
424 asm/non_matchings/code/z_bgcheck/BgCheck_CreateVec3fFromVertex.s asm/non_matchings/code/z_bgcheck/BgCheck_Vec3sToVec3f.s BgCheck_CreateVec3fFromVertex BgCheck_Vec3sToVec3f 0x800BFCFC 0x11
425 asm/non_matchings/code/z_bgcheck/BgCheck_CreateVertexFromVec3f.s asm/non_matchings/code/z_bgcheck/BgCheck_Vec3fToVec3s.s BgCheck_CreateVertexFromVec3f BgCheck_Vec3fToVec3s 0x800BFD40 0x11
426 asm/non_matchings/code/z_bgcheck/func_800BFD84.s func_800BFD84 0x800BFD84 0x1A
427 asm/non_matchings/code/z_bgcheck/func_800BFDEC.s func_800BFDEC 0x800BFDEC 0x76
428 asm/non_matchings/code/z_bgcheck/BgCheck_PolygonGetMinY.s asm/non_matchings/code/z_bgcheck/CollisionPoly_GetMinY.s BgCheck_PolygonGetMinY CollisionPoly_GetMinY 0x800BFFC4 0x1E
429 asm/non_matchings/code/z_bgcheck/BgCheck_PolygonGetNormal.s asm/non_matchings/code/z_bgcheck/CollisionPoly_GetNormalF.s BgCheck_PolygonGetNormal CollisionPoly_GetNormalF 0x800C003C 0x16
430 asm/non_matchings/code/z_bgcheck/func_800C0094.s func_800C0094 0x800C0094 0x49
431 asm/non_matchings/code/z_bgcheck/func_800C01B8.s asm/non_matchings/code/z_bgcheck/CollisionPoly_GetPointDistanceFromPlane.s func_800C01B8 CollisionPoly_GetPointDistanceFromPlane 0x800C01B8 0x1A
432 asm/non_matchings/code/z_bgcheck/BgCheck_CreateTriNormFromPolygon.s asm/non_matchings/code/z_bgcheck/CollisionPoly_GetVertices.s BgCheck_CreateTriNormFromPolygon CollisionPoly_GetVertices 0x800C0220 0x28
433 asm/non_matchings/code/z_bgcheck/func_800C02C0.s asm/non_matchings/code/z_bgcheck/CollisionPoly_GetVerticesByBgId.s func_800C02C0 CollisionPoly_GetVerticesByBgId 0x800C02C0 0x20
434 asm/non_matchings/code/z_bgcheck/func_800C0340.s func_800C0340 0x800C0340 0x4D
435 asm/non_matchings/code/z_bgcheck/func_800C0474.s func_800C0474 0x800C0474 0x7D
436 asm/non_matchings/code/z_bgcheck/func_800C0668.s func_800C0668 0x800C0668 0x10
437 asm/non_matchings/code/z_bgcheck/func_800C06A8.s func_800C06A8 0x800C06A8 0x29
438 asm/non_matchings/code/z_bgcheck/func_800C074C.s func_800C074C 0x800C074C 0x29
439 asm/non_matchings/code/z_bgcheck/func_800C07F0.s func_800C07F0 0x800C07F0 0xC0
440 asm/non_matchings/code/z_bgcheck/BgCheck_PolygonCollidesWithSphere.s asm/non_matchings/code/z_bgcheck/CollisionPoly_SphVsPoly.s BgCheck_PolygonCollidesWithSphere CollisionPoly_SphVsPoly 0x800C0AF0 0x34
441 asm/non_matchings/code/z_bgcheck/BgCheck_ScenePolygonListsInsertSorted.s asm/non_matchings/code/z_bgcheck/StaticLookup_AddPolyToSSList.s BgCheck_ScenePolygonListsInsertSorted StaticLookup_AddPolyToSSList 0x800C0BC0 0x88
442 asm/non_matchings/code/z_bgcheck/BgCheck_ScenePolygonListsInsert.s asm/non_matchings/code/z_bgcheck/StaticLookup_AddPoly.s BgCheck_ScenePolygonListsInsert StaticLookup_AddPoly 0x800C0DE0 0x25
443 asm/non_matchings/code/z_bgcheck/func_800C0E74.s func_800C0E74 0x800C0E74 0xA2
444 asm/non_matchings/code/z_bgcheck/func_800C10FC.s func_800C10FC 0x800C10FC 0x4F
445 asm/non_matchings/code/z_bgcheck/func_800C1238.s func_800C1238 0x800C1238 0x1B
452 asm/non_matchings/code/z_bgcheck/func_800C2460.s func_800C2460 0x800C2460 0x2D
453 asm/non_matchings/code/z_bgcheck/func_800C2514.s func_800C2514 0x800C2514 0x33
454 asm/non_matchings/code/z_bgcheck/func_800C25E0.s func_800C25E0 0x800C25E0 0x38
455 asm/non_matchings/code/z_bgcheck/BgCheck_GetPolyMinSubdivisions.s asm/non_matchings/code/z_bgcheck/BgCheck_GetSubdivisionMinBounds.s BgCheck_GetPolyMinSubdivisions BgCheck_GetSubdivisionMinBounds 0x800C26C0 0x69
456 asm/non_matchings/code/z_bgcheck/BgCheck_GetPolyMaxSubdivisions.s asm/non_matchings/code/z_bgcheck/BgCheck_GetSubdivisionMaxBounds.s BgCheck_GetPolyMaxSubdivisions BgCheck_GetSubdivisionMaxBounds 0x800C2864 0x73
457 asm/non_matchings/code/z_bgcheck/BgCheck_GetPolyMinMaxSubdivisions.s asm/non_matchings/code/z_bgcheck/BgCheck_GetPolySubdivisionBounds.s BgCheck_GetPolyMinMaxSubdivisions BgCheck_GetPolySubdivisionBounds 0x800C2A30 0x6C
458 asm/non_matchings/code/z_bgcheck/func_800C2BE0.s asm/non_matchings/code/z_bgcheck/BgCheck_PolyIntersectsSubdivision.s func_800C2BE0 BgCheck_PolyIntersectsSubdivision 0x800C2BE0 0x1D5
459 asm/non_matchings/code/z_bgcheck/BgCheck_SplitScenePolygonsIntoSubdivisions.s asm/non_matchings/code/z_bgcheck/BgCheck_InitStaticLookup.s BgCheck_SplitScenePolygonsIntoSubdivisions BgCheck_InitStaticLookup 0x800C3334 0x100
460 asm/non_matchings/code/z_bgcheck/BgCheck_GetIsDefaultSpecialScene.s asm/non_matchings/code/z_bgcheck/BgCheck_IsSmallMemScene.s BgCheck_GetIsDefaultSpecialScene BgCheck_IsSmallMemScene 0x800C3734 0x11
461 asm/non_matchings/code/z_bgcheck/BgCheck_GetSpecialSceneMaxMemory.s asm/non_matchings/code/z_bgcheck/BgCheck_TryGetCustomMemsize.s BgCheck_GetSpecialSceneMaxMemory BgCheck_TryGetCustomMemsize 0x800C3778 0x11
462 asm/non_matchings/code/z_bgcheck/BgCheck_CalcSubdivisionSize.s asm/non_matchings/code/z_bgcheck/BgCheck_SetSubdivisionDimension.s BgCheck_CalcSubdivisionSize BgCheck_SetSubdivisionDimension 0x800C37BC 0x22
463 asm/non_matchings/code/z_bgcheck/BgCheck_GetSpecialSceneMaxObjects.s BgCheck_GetSpecialSceneMaxObjects 0x800C3844 0x16
464 asm/non_matchings/code/z_bgcheck/BgCheck_Init.s BgCheck_Init 0x800C389C 0xD9
465 asm/non_matchings/code/z_bgcheck/func_800C3C00.s asm/non_matchings/code/z_bgcheck/BgCheck_SetContextFlags.s func_800C3C00 BgCheck_SetContextFlags 0x800C3C00 0x5
466 asm/non_matchings/code/z_bgcheck/func_800C3C14.s asm/non_matchings/code/z_bgcheck/BgCheck_UnsetContextFlags.s func_800C3C14 BgCheck_UnsetContextFlags 0x800C3C14 0x6
467 asm/non_matchings/code/z_bgcheck/BgCheck_GetActorMeshHeader.s asm/non_matchings/code/z_bgcheck/BgCheck_GetCollisionHeader.s BgCheck_GetActorMeshHeader BgCheck_GetCollisionHeader 0x800C3C2C 0x1A
468 asm/non_matchings/code/z_bgcheck/func_800C3C94.s func_800C3C94 0x800C3C94 0x2F
469 asm/non_matchings/code/z_bgcheck/func_800C3D50.s asm/non_matchings/code/z_bgcheck/BgCheck_RaycastFloorImpl.s func_800C3D50 BgCheck_RaycastFloorImpl 0x800C3D50 0x7C
470 asm/non_matchings/code/z_bgcheck/func_800C3F40.s asm/non_matchings/code/z_bgcheck/BgCheck_CameraRaycastFloor1.s func_800C3F40 BgCheck_CameraRaycastFloor1 0x800C3F40 0x18
471 asm/non_matchings/code/z_bgcheck/func_800C3FA0.s asm/non_matchings/code/z_bgcheck/BgCheck_EntityRaycastFloor1.s func_800C3FA0 BgCheck_EntityRaycastFloor1 0x800C3FA0 0x18
472 asm/non_matchings/code/z_bgcheck/func_800C4000.s asm/non_matchings/code/z_bgcheck/BgCheck_EntityRaycastFloor2.s func_800C4000 BgCheck_EntityRaycastFloor2 0x800C4000 0x16
473 asm/non_matchings/code/z_bgcheck/func_800C4058.s asm/non_matchings/code/z_bgcheck/BgCheck_EntityRaycastFloor2_1.s func_800C4058 BgCheck_EntityRaycastFloor2_1 0x800C4058 0x17
474 asm/non_matchings/code/z_bgcheck/func_800C40B4.s asm/non_matchings/code/z_bgcheck/BgCheck_EntityRaycastFloor3.s func_800C40B4 BgCheck_EntityRaycastFloor3 0x800C40B4 0x1A
475 asm/non_matchings/code/z_bgcheck/func_800C411C.s asm/non_matchings/code/z_bgcheck/BgCheck_EntityRaycastFloor5.s func_800C411C BgCheck_EntityRaycastFloor5 0x800C411C 0x1B
476 asm/non_matchings/code/z_bgcheck/func_800C4188.s asm/non_matchings/code/z_bgcheck/BgCheck_EntityRaycastFloor5_2.s func_800C4188 BgCheck_EntityRaycastFloor5_2 0x800C4188 0x17
477 asm/non_matchings/code/z_bgcheck/func_800C41E4.s asm/non_matchings/code/z_bgcheck/BgCheck_EntityRaycastFloor5_3.s func_800C41E4 BgCheck_EntityRaycastFloor5_3 0x800C41E4 0x17
478 asm/non_matchings/code/z_bgcheck/func_800C4240.s asm/non_matchings/code/z_bgcheck/BgCheck_EntityRaycastFloor6.s func_800C4240 BgCheck_EntityRaycastFloor6 0x800C4240 0x1A
479 asm/non_matchings/code/z_bgcheck/func_800C42A8.s asm/non_matchings/code/z_bgcheck/BgCheck_EntityRaycastFloor7.s func_800C42A8 BgCheck_EntityRaycastFloor7 0x800C42A8 0x1B
480 asm/non_matchings/code/z_bgcheck/func_800C4314.s asm/non_matchings/code/z_bgcheck/BgCheck_AnyRaycastFloor1.s func_800C4314 BgCheck_AnyRaycastFloor1 0x800C4314 0x2E
481 asm/non_matchings/code/z_bgcheck/func_800C43CC.s asm/non_matchings/code/z_bgcheck/BgCheck_AnyRaycastFloor2.s func_800C43CC BgCheck_AnyRaycastFloor2 0x800C43CC 0x2F
482 asm/non_matchings/code/z_bgcheck/func_800C4488.s asm/non_matchings/code/z_bgcheck/BgCheck_CameraRaycastFloor2.s func_800C4488 BgCheck_CameraRaycastFloor2 0x800C4488 0x1A
483 asm/non_matchings/code/z_bgcheck/func_800C44F0.s asm/non_matchings/code/z_bgcheck/BgCheck_EntityRaycastFloor8.s func_800C44F0 BgCheck_EntityRaycastFloor8 0x800C44F0 0x1B
484 asm/non_matchings/code/z_bgcheck/func_800C455C.s asm/non_matchings/code/z_bgcheck/BgCheck_EntityRaycastFloor9.s func_800C455C BgCheck_EntityRaycastFloor9 0x800C455C 0x1A
485 asm/non_matchings/code/z_bgcheck/func_800C45C4.s asm/non_matchings/code/z_bgcheck/BgCheck_CheckWallImpl.s func_800C45C4 BgCheck_CheckWallImpl 0x800C45C4 0x1AC
486 asm/non_matchings/code/z_bgcheck/func_800C4C74.s asm/non_matchings/code/z_bgcheck/BgCheck_EntitySphVsWall1.s func_800C4C74 BgCheck_EntitySphVsWall1 0x800C4C74 0x19
487 asm/non_matchings/code/z_bgcheck/func_800C4CD8.s asm/non_matchings/code/z_bgcheck/BgCheck_EntitySphVsWall2.s func_800C4CD8 BgCheck_EntitySphVsWall2 0x800C4CD8 0x19
488 asm/non_matchings/code/z_bgcheck/func_800C4D3C.s asm/non_matchings/code/z_bgcheck/BgCheck_EntitySphVsWall3.s func_800C4D3C BgCheck_EntitySphVsWall3 0x800C4D3C 0x1A
489 asm/non_matchings/code/z_bgcheck/func_800C4DA4.s asm/non_matchings/code/z_bgcheck/BgCheck_EntitySphVsWall4.s func_800C4DA4 BgCheck_EntitySphVsWall4 0x800C4DA4 0x1B
490 asm/non_matchings/code/z_bgcheck/func_800C4E10.s asm/non_matchings/code/z_bgcheck/BgCheck_CheckCeilingImpl.s func_800C4E10 BgCheck_CheckCeilingImpl 0x800C4E10 0x4A
491 asm/non_matchings/code/z_bgcheck/func_800C4F38.s asm/non_matchings/code/z_bgcheck/BgCheck_AnyCheckCeiling.s func_800C4F38 BgCheck_AnyCheckCeiling 0x800C4F38 0x13
492 asm/non_matchings/code/z_bgcheck/func_800C4F84.s asm/non_matchings/code/z_bgcheck/BgCheck_EntityCheckCeiling.s func_800C4F84 BgCheck_EntityCheckCeiling 0x800C4F84 0x14
493 asm/non_matchings/code/z_bgcheck/func_800C4FD4.s func_800C4FD4 0x800C4FD4 0x124
494 asm/non_matchings/code/z_bgcheck/func_800C5464.s func_800C5464 0x800C5464 0x12
495 asm/non_matchings/code/z_bgcheck/func_800C54AC.s asm/non_matchings/code/z_bgcheck/BgCheck_CameraLineTest1.s func_800C54AC BgCheck_CameraLineTest1 0x800C54AC 0x23
496 asm/non_matchings/code/z_bgcheck/func_800C5538.s asm/non_matchings/code/z_bgcheck/BgCheck_CameraLineTest2.s func_800C5538 BgCheck_CameraLineTest2 0x800C5538 0x23
497 asm/non_matchings/code/z_bgcheck/func_800C55C4.s asm/non_matchings/code/z_bgcheck/BgCheck_EntityLineTest1.s func_800C55C4 BgCheck_EntityLineTest1 0x800C55C4 0x23
498 asm/non_matchings/code/z_bgcheck/func_800C5650.s asm/non_matchings/code/z_bgcheck/BgCheck_EntityLineTest2.s func_800C5650 BgCheck_EntityLineTest2 0x800C5650 0x24
499 asm/non_matchings/code/z_bgcheck/func_800C56E0.s asm/non_matchings/code/z_bgcheck/BgCheck_EntityLineTest3.s func_800C56E0 BgCheck_EntityLineTest3 0x800C56E0 0x23
500 asm/non_matchings/code/z_bgcheck/func_800C576C.s asm/non_matchings/code/z_bgcheck/BgCheck_ProjectileLineTest.s func_800C576C BgCheck_ProjectileLineTest 0x800C576C 0x23
501 asm/non_matchings/code/z_bgcheck/func_800C57F8.s asm/non_matchings/code/z_bgcheck/BgCheck_AnyLineTest1.s func_800C57F8 BgCheck_AnyLineTest1 0x800C57F8 0x11
502 asm/non_matchings/code/z_bgcheck/func_800C583C.s asm/non_matchings/code/z_bgcheck/BgCheck_AnyLineTest2.s func_800C583C BgCheck_AnyLineTest2 0x800C583C 0x23
503 asm/non_matchings/code/z_bgcheck/func_800C58C8.s asm/non_matchings/code/z_bgcheck/BgCheck_AnyLineTest3.s func_800C58C8 BgCheck_AnyLineTest3 0x800C58C8 0x23
504 asm/non_matchings/code/z_bgcheck/func_800C5954.s asm/non_matchings/code/z_bgcheck/BgCheck_SphVsFirstPolyImpl.s func_800C5954 BgCheck_SphVsFirstPolyImpl 0x800C5954 0x33
505 asm/non_matchings/code/z_bgcheck/func_800C5A20.s asm/non_matchings/code/z_bgcheck/BgCheck_SphVsFirstPoly.s func_800C5A20 BgCheck_SphVsFirstPoly 0x800C5A20 0x11
506 asm/non_matchings/code/z_bgcheck/func_800C5A64.s asm/non_matchings/code/z_bgcheck/BgCheck_SphVsFirstWall.s func_800C5A64 BgCheck_SphVsFirstWall 0x800C5A64 0x12
507 asm/non_matchings/code/z_bgcheck/BgCheck_ScenePolygonListsInit.s asm/non_matchings/code/z_bgcheck/SSNodeList_Init.s BgCheck_ScenePolygonListsInit SSNodeList_Init 0x800C5AAC 0x6
508 asm/non_matchings/code/z_bgcheck/BgCheck_ScenePolygonListsAlloc.s asm/non_matchings/code/z_bgcheck/SSNodeList_Alloc.s BgCheck_ScenePolygonListsAlloc SSNodeList_Alloc 0x800C5AC4 0x2F
509 asm/non_matchings/code/z_bgcheck/func_800C5B80.s asm/non_matchings/code/z_bgcheck/SSNodeList_GetNextNode.s func_800C5B80 SSNodeList_GetNextNode 0x800C5B80 0xF
510 asm/non_matchings/code/z_bgcheck/BgCheck_ScenePolygonListsReserveNode.s asm/non_matchings/code/z_bgcheck/SSNodeList_GetNextNodeIdx.s BgCheck_ScenePolygonListsReserveNode SSNodeList_GetNextNodeIdx 0x800C5BBC 0x5
511 asm/non_matchings/code/z_bgcheck/BgCheck_ActorMeshParamsInit.s asm/non_matchings/code/z_bgcheck/ScaleRotPos_Init.s BgCheck_ActorMeshParamsInit ScaleRotPos_Init 0x800C5BD0 0xF
512 asm/non_matchings/code/z_bgcheck/BgCheck_SetActorMeshParams.s asm/non_matchings/code/z_bgcheck/ScaleRotPos_SetValue.s BgCheck_SetActorMeshParams ScaleRotPos_SetValue 0x800C5C0C 0x14
513 asm/non_matchings/code/z_bgcheck/BgCheck_AreActorMeshParamsEqual.s BgCheck_AreActorMeshParamsEqual 0x800C5C5C 0x35
514 asm/non_matchings/code/z_bgcheck/BgCheck_ActorMeshPolyListsHeadsInit.s asm/non_matchings/code/z_bgcheck/DynaLookup_ResetLists.s BgCheck_ActorMeshPolyListsHeadsInit DynaLookup_ResetLists 0x800C5D30 0x10
515 asm/non_matchings/code/z_bgcheck/BgCheck_ActorMeshPolyListsInit.s asm/non_matchings/code/z_bgcheck/DynaLookup_Reset.s BgCheck_ActorMeshPolyListsInit DynaLookup_Reset 0x800C5D70 0x8
516 asm/non_matchings/code/z_bgcheck/BgCheck_ActorMeshVerticesIndexInit.s asm/non_matchings/code/z_bgcheck/DynaLookup_ResetVtxStartIndex.s BgCheck_ActorMeshVerticesIndexInit DynaLookup_ResetVtxStartIndex 0x800C5D90 0x3
517 asm/non_matchings/code/z_bgcheck/BgCheck_ActorMeshWaterboxesIndexInit.s asm/non_matchings/code/z_bgcheck/DynaLookup_ResetWaterBoxStartIndex.s BgCheck_ActorMeshWaterboxesIndexInit DynaLookup_ResetWaterBoxStartIndex 0x800C5D9C 0x3
518 asm/non_matchings/code/z_bgcheck/BgCheck_ActorMeshInit.s asm/non_matchings/code/z_bgcheck/BgActor_Init.s BgCheck_ActorMeshInit BgActor_Init 0x800C5DA8 0x1A
519 asm/non_matchings/code/z_bgcheck/BgCheck_ActorMeshInitFromActor.s asm/non_matchings/code/z_bgcheck/BgActor_InitFromActor.s BgCheck_ActorMeshInitFromActor BgActor_InitFromActor 0x800C5E10 0x2E
520 asm/non_matchings/code/z_bgcheck/BgCheck_HasActorMeshChanged.s asm/non_matchings/code/z_bgcheck/BgActor_IsTransformUnchanged.s BgCheck_HasActorMeshChanged BgActor_IsTransformUnchanged 0x800C5EC8 0xA
521 asm/non_matchings/code/z_bgcheck/BgCheck_PolygonsInit.s asm/non_matchings/code/z_bgcheck/DynaPoly_NullPolyList.s BgCheck_PolygonsInit DynaPoly_NullPolyList 0x800C5EF0 0x3
522 asm/non_matchings/code/z_bgcheck/BgCheck_PolygonsAlloc.s asm/non_matchings/code/z_bgcheck/DynaPoly_AllocPolyList.s BgCheck_PolygonsAlloc DynaPoly_AllocPolyList 0x800C5EFC 0xF
523 asm/non_matchings/code/z_bgcheck/BgCheck_VerticesInit.s asm/non_matchings/code/z_bgcheck/DynaPoly_NullVtxList.s BgCheck_VerticesInit DynaPoly_NullVtxList 0x800C5F38 0x3
524 asm/non_matchings/code/z_bgcheck/BgCheck_VerticesListAlloc.s asm/non_matchings/code/z_bgcheck/DynaPoly_AllocVtxList.s BgCheck_VerticesListAlloc DynaPoly_AllocVtxList 0x800C5F44 0x12
525 asm/non_matchings/code/z_bgcheck/BgCheck_WaterboxListInit.s asm/non_matchings/code/z_bgcheck/DynaPoly_InitWaterBoxList.s BgCheck_WaterboxListInit DynaPoly_InitWaterBoxList 0x800C5F8C 0x4
526 asm/non_matchings/code/z_bgcheck/BgCheck_WaterboxListAlloc.s asm/non_matchings/code/z_bgcheck/DynaPoly_AllocWaterBoxList.s BgCheck_WaterboxListAlloc DynaPoly_AllocWaterBoxList 0x800C5F9C 0xF
527 asm/non_matchings/code/z_bgcheck/BgCheck_ActorMeshUpdateParams.s asm/non_matchings/code/z_bgcheck/DynaPoly_SetBgActorPrevTransform.s BgCheck_ActorMeshUpdateParams DynaPoly_SetBgActorPrevTransform 0x800C5FD8 0x13
528 asm/non_matchings/code/z_bgcheck/BgCheck_IsActorMeshIndexValid.s asm/non_matchings/code/z_bgcheck/DynaPoly_IsBgIdBgActor.s BgCheck_IsActorMeshIndexValid DynaPoly_IsBgIdBgActor 0x800C6024 0x8
529 asm/non_matchings/code/z_bgcheck/BgCheck_DynaInit.s asm/non_matchings/code/z_bgcheck/DynaPoly_Init.s BgCheck_DynaInit DynaPoly_Init 0x800C6044 0x15
530 asm/non_matchings/code/z_bgcheck/BgCheck_DynaAlloc.s asm/non_matchings/code/z_bgcheck/DynaPoly_Alloc.s BgCheck_DynaAlloc DynaPoly_Alloc 0x800C6098 0x3C
531 asm/non_matchings/code/z_bgcheck/BgCheck_AddActorMesh.s asm/non_matchings/code/z_bgcheck/DynaPoly_SetBgActor.s BgCheck_AddActorMesh DynaPoly_SetBgActor 0x800C6188 0x30
532 asm/non_matchings/code/z_bgcheck/BgCheck_GetActorOfMesh.s asm/non_matchings/code/z_bgcheck/DynaPoly_GetActor.s BgCheck_GetActorOfMesh DynaPoly_GetActor 0x800C6248 0x1D
533 asm/non_matchings/code/z_bgcheck/func_800C62BC.s func_800C62BC 0x800C62BC 0x16
534 asm/non_matchings/code/z_bgcheck/func_800C6314.s func_800C6314 0x800C6314 0x16
535 asm/non_matchings/code/z_bgcheck/func_800C636C.s func_800C636C 0x800C636C 0x16
536 asm/non_matchings/code/z_bgcheck/func_800C63C4.s func_800C63C4 0x800C63C4 0x16
537 asm/non_matchings/code/z_bgcheck/func_800C641C.s func_800C641C 0x800C641C 0x16
538 asm/non_matchings/code/z_bgcheck/func_800C6474.s func_800C6474 0x800C6474 0x16
539 asm/non_matchings/code/z_bgcheck/BgCheck_RemoveActorMesh.s asm/non_matchings/code/z_bgcheck/DynaPoly_DeleteBgActor.s BgCheck_RemoveActorMesh DynaPoly_DeleteBgActor 0x800C64CC 0x22
540 asm/non_matchings/code/z_bgcheck/func_800C6554.s func_800C6554 0x800C6554 0x6
541 asm/non_matchings/code/z_bgcheck/BgCheck_CalcWaterboxDimensions.s BgCheck_CalcWaterboxDimensions 0x800C656C 0xB3
542 asm/non_matchings/code/z_bgcheck/BgCheck_AddActorMeshToLists.s asm/non_matchings/code/z_bgcheck/DynaPoly_SetBgActorToLists.s BgCheck_AddActorMeshToLists DynaPoly_SetBgActorToLists 0x800C6838 0x2C5
543 asm/non_matchings/code/z_bgcheck/BgCheck_ResetFlagsIfLoadedActor.s BgCheck_ResetFlagsIfLoadedActor 0x800C734C 0x26
544 asm/non_matchings/code/z_bgcheck/BgCheck_Update.s asm/non_matchings/code/z_bgcheck/DynaPoly_Setup.s BgCheck_Update DynaPoly_Setup 0x800C73E4 0x62
545 asm/non_matchings/code/z_bgcheck/func_800C756C.s func_800C756C 0x800C756C 0x3C
546 asm/non_matchings/code/z_bgcheck/BgCheck_UpdateAllActorMeshes.s asm/non_matchings/code/z_bgcheck/DynaPoly_SetupAllActorMeshes.s BgCheck_UpdateAllActorMeshes DynaPoly_SetupAllActorMeshes 0x800C765C 0x24
547 asm/non_matchings/code/z_bgcheck/func_800C76EC.s func_800C76EC 0x800C76EC 0xA2
548 asm/non_matchings/code/z_bgcheck/func_800C7974.s func_800C7974 0x800C7974 0x133
549 asm/non_matchings/code/z_bgcheck/func_800C7E40.s func_800C7E40 0x800C7E40 0x1D0
556 asm/non_matchings/code/z_bgcheck/func_800C90AC.s func_800C90AC 0x800C90AC 0x5C
557 asm/non_matchings/code/z_bgcheck/func_800C921C.s func_800C921C 0x800C921C 0x59
558 asm/non_matchings/code/z_bgcheck/func_800C9380.s func_800C9380 0x800C9380 0x58
559 asm/non_matchings/code/z_bgcheck/BgCheck_RelocateMeshHeaderPointers.s asm/non_matchings/code/z_bgcheck/CollisionHeader_GetVirtualPointers.s BgCheck_RelocateMeshHeaderPointers CollisionHeader_GetVirtualPointers 0x800C94E0 0x21
560 asm/non_matchings/code/z_bgcheck/BgCheck_RelocateMeshHeader.s asm/non_matchings/code/z_bgcheck/CollisionHeader_GetVirtual.s BgCheck_RelocateMeshHeader CollisionHeader_GetVirtual 0x800C9564 0xD
561 asm/non_matchings/code/z_bgcheck/BgCheck_RelocateAllMeshHeaders.s asm/non_matchings/code/z_bgcheck/BgCheck_InitCollisionHeaders.s BgCheck_RelocateAllMeshHeaders BgCheck_InitCollisionHeaders 0x800C9598 0x2A
562 asm/non_matchings/code/z_bgcheck/func_800C9640.s func_800C9640 0x800C9640 0x15
563 asm/non_matchings/code/z_bgcheck/BgCheck_GetPolygonAttributes.s asm/non_matchings/code/z_bgcheck/SurfaceType_GetData.s BgCheck_GetPolygonAttributes SurfaceType_GetData 0x800C9694 0x1C
564 asm/non_matchings/code/z_bgcheck/func_800C9704.s asm/non_matchings/code/z_bgcheck/SurfaceType_GetCamDataIndex.s func_800C9704 SurfaceType_GetCamDataIndex 0x800C9704 0x9
565 asm/non_matchings/code/z_bgcheck/func_800C9728.s func_800C9728 0x800C9728 0x12
566 asm/non_matchings/code/z_bgcheck/func_800C9770.s func_800C9770 0x800C9770 0x22
567 asm/non_matchings/code/z_bgcheck/func_800C97F8.s func_800C97F8 0x800C97F8 0x13
568 asm/non_matchings/code/z_bgcheck/func_800C9844.s func_800C9844 0x800C9844 0x22
569 asm/non_matchings/code/z_bgcheck/func_800C98CC.s func_800C98CC 0x800C98CC 0x16
570 asm/non_matchings/code/z_bgcheck/func_800C9924.s asm/non_matchings/code/z_bgcheck/SurfaceType_GetCamPosData.s func_800C9924 SurfaceType_GetCamPosData 0x800C9924 0x22
571 asm/non_matchings/code/z_bgcheck/func_800C99AC.s asm/non_matchings/code/z_bgcheck/SurfaceType_GetSceneExitIndex.s func_800C99AC SurfaceType_GetSceneExitIndex 0x800C99AC 0xA
572 asm/non_matchings/code/z_bgcheck/func_800C99D4.s func_800C99D4 0x800C99D4 0xA
573 asm/non_matchings/code/z_bgcheck/func_800C99FC.s func_800C99FC 0x800C99FC 0xA
574 asm/non_matchings/code/z_bgcheck/func_800C9A24.s func_800C9A24 0x800C9A24 0xA
579 asm/non_matchings/code/z_bgcheck/func_800C9B18.s func_800C9B18 0x800C9B18 0xA
580 asm/non_matchings/code/z_bgcheck/func_800C9B40.s func_800C9B40 0x800C9B40 0xA
581 asm/non_matchings/code/z_bgcheck/func_800C9B68.s func_800C9B68 0x800C9B68 0xA
582 asm/non_matchings/code/z_bgcheck/func_800C9B90.s asm/non_matchings/code/z_bgcheck/SurfaceType_IsHorseBlocked.s func_800C9B90 SurfaceType_IsHorseBlocked 0x800C9B90 0xA
583 asm/non_matchings/code/z_bgcheck/func_800C9BB8.s func_800C9BB8 0x800C9BB8 0x9
584 asm/non_matchings/code/z_bgcheck/func_800C9BDC.s asm/non_matchings/code/z_bgcheck/SurfaceType_GetSfx.s func_800C9BDC SurfaceType_GetSfx 0x800C9BDC 0x12
585 asm/non_matchings/code/z_bgcheck/func_800C9C24.s func_800C9C24 0x800C9C24 0x14
586 asm/non_matchings/code/z_bgcheck/func_800C9C74.s asm/non_matchings/code/z_bgcheck/SurfaceType_GetSlope.s func_800C9C74 SurfaceType_GetSlope 0x800C9C74 0xA
587 asm/non_matchings/code/z_bgcheck/func_800C9C9C.s asm/non_matchings/code/z_bgcheck/SurfaceType_GetLightSettingIndex.s func_800C9C9C SurfaceType_GetLightSettingIndex 0x800C9C9C 0xA
588 asm/non_matchings/code/z_bgcheck/func_800C9CC4.s asm/non_matchings/code/z_bgcheck/SurfaceType_GetEcho.s func_800C9CC4 SurfaceType_GetEcho 0x800C9CC4 0xA
589 asm/non_matchings/code/z_bgcheck/func_800C9CEC.s asm/non_matchings/code/z_bgcheck/SurfaceType_IsHookshotSurface.s func_800C9CEC SurfaceType_IsHookshotSurface 0x800C9CEC 0xA
590 asm/non_matchings/code/z_bgcheck/func_800C9D14.s asm/non_matchings/code/z_bgcheck/SurfaceType_IsIgnoredByEntities.s func_800C9D14 SurfaceType_IsIgnoredByEntities 0x800C9D14 0xF
591 asm/non_matchings/code/z_bgcheck/func_800C9D50.s asm/non_matchings/code/z_bgcheck/SurfaceType_IsIgnoredByProjectiles.s func_800C9D50 SurfaceType_IsIgnoredByProjectiles 0x800C9D50 0xF
592 asm/non_matchings/code/z_bgcheck/func_800C9D8C.s asm/non_matchings/code/z_bgcheck/SurfaceType_IsConveyor.s func_800C9D8C SurfaceType_IsConveyor 0x800C9D8C 0x14
593 asm/non_matchings/code/z_bgcheck/func_800C9DDC.s func_800C9DDC 0x800C9DDC 0xF
594 asm/non_matchings/code/z_bgcheck/func_800C9E18.s asm/non_matchings/code/z_bgcheck/SurfaceType_GetConveyorSpeed.s func_800C9E18 SurfaceType_GetConveyorSpeed 0x800C9E18 0xA
595 asm/non_matchings/code/z_bgcheck/func_800C9E40.s asm/non_matchings/code/z_bgcheck/SurfaceType_GetConveyorDirection.s func_800C9E40 SurfaceType_GetConveyorDirection 0x800C9E40 0x12
596 asm/non_matchings/code/z_bgcheck/func_800C9E88.s asm/non_matchings/code/z_bgcheck/SurfaceType_IsWallDamage.s func_800C9E88 SurfaceType_IsWallDamage 0x800C9E88 0xD
597 asm/non_matchings/code/z_bgcheck/func_800C9EBC.s asm/non_matchings/code/z_bgcheck/WaterBox_GetSurfaceImpl.s func_800C9EBC WaterBox_GetSurfaceImpl 0x800C9EBC 0xBC
598 asm/non_matchings/code/z_bgcheck/func_800CA1AC.s asm/non_matchings/code/z_bgcheck/WaterBox_GetSurface1.s func_800CA1AC WaterBox_GetSurface1 0x800CA1AC 0xF
599 asm/non_matchings/code/z_bgcheck/func_800CA1E8.s asm/non_matchings/code/z_bgcheck/WaterBox_GetSurface1_2.s func_800CA1E8 WaterBox_GetSurface1_2 0x800CA1E8 0x11
600 asm/non_matchings/code/z_bgcheck/func_800CA22C.s asm/non_matchings/code/z_bgcheck/WaterBox_GetSurface2.s func_800CA22C WaterBox_GetSurface2 0x800CA22C 0xCF
601 asm/non_matchings/code/z_bgcheck/func_800CA568.s func_800CA568 0x800CA568 0x33
602 asm/non_matchings/code/z_bgcheck/func_800CA634.s func_800CA634 0x800CA634 0x5
603 asm/non_matchings/code/z_bgcheck/func_800CA648.s func_800CA648 0x800CA648 0x1C
604 asm/non_matchings/code/z_bgcheck/func_800CA6B8.s func_800CA6B8 0x800CA6B8 0x8
605 asm/non_matchings/code/z_bgcheck/func_800CA6D8.s asm/non_matchings/code/z_bgcheck/WaterBox_GetLightSettingIndex.s func_800CA6D8 WaterBox_GetLightSettingIndex 0x800CA6D8 0x6
606 asm/non_matchings/code/z_bgcheck/func_800CA6F0.s func_800CA6F0 0x800CA6F0 0xB8
607 asm/non_matchings/code/z_bgcheck/func_800CA9D0.s func_800CA9D0 0x800CA9D0 0x11
608 asm/non_matchings/code/z_bgcheck/func_800CAA14.s func_800CAA14 0x800CAA14 0x2F
2770 asm/non_matchings/code/sys_math/Rand_ZeroFloat.s Rand_ZeroFloat 0x80179594 0xB
2771 asm/non_matchings/code/sys_math/randPlusMinusPoint5Scaled.s randPlusMinusPoint5Scaled 0x801795C0 0xC
2772 asm/non_matchings/code/sys_math3d/Math3D_Normalize.s Math3D_Normalize 0x801795F0 0x22
2773 asm/non_matchings/code/sys_math3d/func_80179678.s asm/non_matchings/code/sys_math3d/Math3D_PlaneVsLineSegClosestPoint.s func_80179678 Math3D_PlaneVsLineSegClosestPoint 0x80179678 0x48
2774 asm/non_matchings/code/sys_math3d/func_80179798.s func_80179798 0x80179798 0xAB
2775 asm/non_matchings/code/sys_math3d/func_80179A44.s func_80179A44 0x80179A44 0x3C
2776 asm/non_matchings/code/sys_math3d/func_80179B34.s func_80179B34 0x80179B34 0x18
2792 asm/non_matchings/code/sys_math3d/Math3D_XZDistance.s Math3D_XZDistance 0x8017A678 0xC
2793 asm/non_matchings/code/sys_math3d/Math3D_LengthSquared.s Math3D_LengthSquared 0x8017A6A8 0xB
2794 asm/non_matchings/code/sys_math3d/Math3D_Vec3fMagnitude.s Math3D_Vec3fMagnitude 0x8017A6D4 0x9
2795 asm/non_matchings/code/sys_math3d/Math3D_DistanceSquared.s asm/non_matchings/code/sys_math3d/Math3D_Vec3fDistSq.s Math3D_DistanceSquared Math3D_Vec3fDistSq 0x8017A6F8 0xA
2796 asm/non_matchings/code/sys_math3d/Math3D_Distance.s Math3D_Distance 0x8017A720 0x8
2797 asm/non_matchings/code/sys_math3d/Math3D_DistanceS.s Math3D_DistanceS 0x8017A740 0x1E
2798 asm/non_matchings/code/sys_math3d/func_8017A7B8.s func_8017A7B8 0x8017A7B8 0x10
2799 asm/non_matchings/code/sys_math3d/func_8017A7F8.s func_8017A7F8 0x8017A7F8 0x10
2800 asm/non_matchings/code/sys_math3d/func_8017A838.s func_8017A838 0x8017A838 0x10
2801 asm/non_matchings/code/sys_math3d/Math3D_CrossProduct.s Math3D_CrossProduct 0x8017A878 0x1D
2802 asm/non_matchings/code/sys_math3d/Math3D_NormalVector.s asm/non_matchings/code/sys_math3d/Math3D_SurfaceNorm.s Math3D_NormalVector Math3D_SurfaceNorm 0x8017A8EC 0x1A
2803 asm/non_matchings/code/sys_math3d/func_8017A954.s asm/non_matchings/code/sys_math3d/Math3D_PointRelativeToCubeFaces.s func_8017A954 Math3D_PointRelativeToCubeFaces 0x8017A954 0x2E
2804 asm/non_matchings/code/sys_math3d/func_8017AA0C.s asm/non_matchings/code/sys_math3d/Math3D_PointRelativeToCubeEdges.s func_8017AA0C Math3D_PointRelativeToCubeEdges 0x8017AA0C 0x6C
2805 asm/non_matchings/code/sys_math3d/func_8017ABBC.s asm/non_matchings/code/sys_math3d/Math3D_PointRelativeToCubeVertices.s func_8017ABBC Math3D_PointRelativeToCubeVertices 0x8017ABBC 0x5F
2806 asm/non_matchings/code/sys_math3d/func_8017AD38.s asm/non_matchings/code/sys_math3d/Math3D_LineVsCube.s func_8017AD38 Math3D_LineVsCube 0x8017AD38 0x255
2807 asm/non_matchings/code/sys_math3d/func_8017B68C.s func_8017B68C 0x8017B68C 0x5B
2808 asm/non_matchings/code/sys_math3d/func_8017B7F8.s func_8017B7F8 0x8017B7F8 0x23
2809 asm/non_matchings/code/sys_math3d/Math3D_UnitNormalVector.s Math3D_UnitNormalVector 0x8017B884 0x45
2810 asm/non_matchings/code/sys_math3d/Math3D_SignedDistanceFromPlane.s Math3D_SignedDistanceFromPlane 0x8017B998 0x10
2811 asm/non_matchings/code/sys_math3d/func_8017B9D8.s func_8017B9D8 0x8017B9D8 0xF
2812 asm/non_matchings/code/sys_math3d/Math3D_NormalizedDistanceFromPlane.s asm/non_matchings/code/sys_math3d/Math3D_UDistPlaneToPos.s Math3D_NormalizedDistanceFromPlane Math3D_UDistPlaneToPos 0x8017BA14 0xE
2813 asm/non_matchings/code/sys_math3d/Math3D_NormalizedSignedDistanceFromPlane.s asm/non_matchings/code/sys_math3d/Math3D_DistPlaneToPos.s Math3D_NormalizedSignedDistanceFromPlane Math3D_DistPlaneToPos 0x8017BA4C 0x21
2814 asm/non_matchings/code/sys_math3d/func_8017BAD0.s asm/non_matchings/code/sys_math3d/Math3D_TriChkPointParaYDist.s func_8017BAD0 Math3D_TriChkPointParaYDist 0x8017BAD0 0xB2
2815 asm/non_matchings/code/sys_math3d/func_8017BD98.s func_8017BD98 0x8017BD98 0x12
2816 asm/non_matchings/code/sys_math3d/func_8017BDE0.s func_8017BDE0 0x8017BDE0 0x14
2817 asm/non_matchings/code/sys_math3d/func_8017BE30.s asm/non_matchings/code/sys_math3d/Math3D_TriChkPointParaYIntersectDist.s func_8017BE30 Math3D_TriChkPointParaYIntersectDist 0x8017BE30 0x2C
2818 asm/non_matchings/code/sys_math3d/func_8017BEE0.s asm/non_matchings/code/sys_math3d/Math3D_TriChkPointParaYIntersectInsideTri.s func_8017BEE0 Math3D_TriChkPointParaYIntersectInsideTri 0x8017BEE0 0x2B
2819 asm/non_matchings/code/sys_math3d/func_8017BF8C.s func_8017BF8C 0x8017BF8C 0x1F
2820 asm/non_matchings/code/sys_math3d/func_8017C008.s asm/non_matchings/code/sys_math3d/Math3D_TriChkLineSegParaYIntersect.s func_8017C008 Math3D_TriChkLineSegParaYIntersect 0x8017C008 0x5D
2821 asm/non_matchings/code/sys_math3d/func_8017C17C.s func_8017C17C 0x8017C17C 0x1D
2822 asm/non_matchings/code/sys_math3d/func_8017C1F0.s func_8017C1F0 0x8017C1F0 0xA9
2823 asm/non_matchings/code/sys_math3d/func_8017C494.s asm/non_matchings/code/sys_math3d/Math3D_TriChkPointParaYIntersectInsideTri2.s func_8017C494 Math3D_TriChkPointParaYIntersectInsideTri2 0x8017C494 0x2B
2824 asm/non_matchings/code/sys_math3d/func_8017C540.s asm/non_matchings/code/sys_math3d/Math3D_TriChkPointParaXDist.s func_8017C540 Math3D_TriChkPointParaXDist 0x8017C540 0xB2
2825 asm/non_matchings/code/sys_math3d/func_8017C808.s func_8017C808 0x8017C808 0x12
2826 asm/non_matchings/code/sys_math3d/func_8017C850.s asm/non_matchings/code/sys_math3d/Math3D_TriChkPointParaXIntersect.s func_8017C850 Math3D_TriChkPointParaXIntersect 0x8017C850 0x2D
2827 asm/non_matchings/code/sys_math3d/func_8017C904.s func_8017C904 0x8017C904 0x1F
2828 asm/non_matchings/code/sys_math3d/func_8017C980.s asm/non_matchings/code/sys_math3d/Math3D_TriChkLineSegParaXIntersect.s func_8017C980 Math3D_TriChkLineSegParaXIntersect 0x8017C980 0x62
2829 asm/non_matchings/code/sys_math3d/func_8017CB08.s func_8017CB08 0x8017CB08 0x1D
2830 asm/non_matchings/code/sys_math3d/func_8017CB7C.s asm/non_matchings/code/sys_math3d/Math3D_TriChkLineSegParaZDist.s func_8017CB7C Math3D_TriChkLineSegParaZDist 0x8017CB7C 0xCB
2831 asm/non_matchings/code/sys_math3d/func_8017CEA8.s func_8017CEA8 0x8017CEA8 0x12
2832 asm/non_matchings/code/sys_math3d/func_8017CEF0.s asm/non_matchings/code/sys_math3d/Math3D_TriChkPointParaZIntersect.s func_8017CEF0 Math3D_TriChkPointParaZIntersect 0x8017CEF0 0x2D
2833 asm/non_matchings/code/sys_math3d/func_8017CFA4.s func_8017CFA4 0x8017CFA4 0x1F
2834 asm/non_matchings/code/sys_math3d/func_8017D020.s asm/non_matchings/code/sys_math3d/Math3D_TriChkLineSegParaZIntersect.s func_8017D020 Math3D_TriChkLineSegParaZIntersect 0x8017D020 0x63
2835 asm/non_matchings/code/sys_math3d/func_8017D1AC.s func_8017D1AC 0x8017D1AC 0x1D
2836 asm/non_matchings/code/sys_math3d/func_8017D220.s func_8017D220 0x8017D220 0x37
2837 asm/non_matchings/code/sys_math3d/func_8017D2FC.s func_8017D2FC 0x8017D2FC 0x42
2843 asm/non_matchings/code/sys_math3d/func_8017D814.s func_8017D814 0x8017D814 0x42
2844 asm/non_matchings/code/sys_math3d/func_8017D91C.s func_8017D91C 0x8017D91C 0x42
2845 asm/non_matchings/code/sys_math3d/func_8017DA24.s func_8017DA24 0x8017DA24 0x42
2846 asm/non_matchings/code/sys_math3d/Math3D_ColSphereLineSeg.s asm/non_matchings/code/sys_math3d/Math3D_LineVsSph.s Math3D_ColSphereLineSeg Math3D_LineVsSph 0x8017DB2C 0x82
2847 asm/non_matchings/code/sys_math3d/func_8017DD34.s func_8017DD34 0x8017DD34 0x50
2848 asm/non_matchings/code/sys_math3d/Math3D_ColSphereTri.s Math3D_ColSphereTri 0x8017DE74 0x108
2849 asm/non_matchings/code/sys_math3d/func_8017E294.s func_8017E294 0x8017E294 0x2F
2858 asm/non_matchings/code/sys_math3d/Math3D_ColCylinderCylinderAmount.s Math3D_ColCylinderCylinderAmount 0x8017F45C 0x8
2859 asm/non_matchings/code/sys_math3d/Math3D_ColCylinderCylinderAmountAndDistance.s Math3D_ColCylinderCylinderAmountAndDistance 0x8017F47C 0x74
2860 asm/non_matchings/code/sys_math3d/Math3d_ColTriTri.s Math3d_ColTriTri 0x8017F64C 0xDD
2861 asm/non_matchings/code/sys_math3d/func_8017F9C0.s asm/non_matchings/code/sys_math3d/Math3D_XZInSphere.s func_8017F9C0 Math3D_XZInSphere 0x8017F9C0 0x1D
2862 asm/non_matchings/code/sys_math3d/func_8017FA34.s asm/non_matchings/code/sys_math3d/Math3D_XYInSphere.s func_8017FA34 Math3D_XYInSphere 0x8017FA34 0x1D
2863 asm/non_matchings/code/sys_math3d/func_8017FAA8.s asm/non_matchings/code/sys_math3d/Math3D_YZInSphere.s func_8017FAA8 Math3D_YZInSphere 0x8017FAA8 0x1D
2864 asm/non_matchings/code/sys_math3d/func_8017FB1C.s func_8017FB1C 0x8017FB1C 0x8A
2865 asm/non_matchings/code/sys_math3d/func_8017FD44.s func_8017FD44 0x8017FD44 0x5B
2866 asm/non_matchings/code/sys_math_atan/Math_GetAtan2Tbl.s Math_GetAtan2Tbl 0x8017FEB0 0xD

View File

@ -3444,6 +3444,9 @@ D_06001384 = 0x06001384;
D_06009890 = 0x06009890;
D_0600A280 = 0x0600A280;
D_0600AD98 = 0x0600AD98;
D_0600F8F0 = 0x0600F8F0;
D_0600FCF0 = 0x0600FCF0;
D_060100F0 = 0x060100F0;
D_06011B60 = 0x06011B60;
// ovl_En_Rg