Merge branch 'master' into misc-decomp

This commit is contained in:
Ethan Roseman 2021-01-01 10:20:39 +09:00
commit 1c7a19255b
33 changed files with 884 additions and 398 deletions

View File

@ -1,7 +1,7 @@
{ {
"recommendations": [ "recommendations": [
"ms-vscode.cpptools", "ms-vscode.cpptools",
"nanaian.vscode-star-rod", "nanaian.papermario",
"notskm.clang-tidy", "notskm.clang-tidy",
"EditorConfig.EditorConfig", "EditorConfig.EditorConfig",
], ],

View File

@ -2,7 +2,11 @@ FROM ubuntu:20.04 as build
ENV DEBIAN_FRONTEND=noninteractive ENV DEBIAN_FRONTEND=noninteractive
RUN sudo ./install.sh COPY requirements.txt /
COPY requirements_extra.txt /
COPY install.sh /
RUN apt-get update && apt-get install -y sudo && ./install.sh --extra
RUN mkdir /papermario RUN mkdir /papermario
WORKDIR /papermario WORKDIR /papermario

View File

@ -80,7 +80,7 @@ CC=tools/$(OS)/cc1
### Compiler Options ### ### Compiler Options ###
CPPFLAGS := -Iinclude -Isrc -D _LANGUAGE_C -ffreestanding -DF3DEX_GBI_2 -D_MIPS_SZLONG=32 -Wundef -Wcomment CPPFLAGS := -Iinclude -Isrc -D _LANGUAGE_C -D _FINALROM -ffreestanding -DF3DEX_GBI_2 -D_MIPS_SZLONG=32 -Wundef -Wcomment
ASFLAGS := -EB -Iinclude -march=vr4300 -mtune=vr4300 ASFLAGS := -EB -Iinclude -march=vr4300 -mtune=vr4300
OLDASFLAGS := -EB -Iinclude -G 0 OLDASFLAGS := -EB -Iinclude -G 0
CFLAGS := -O2 -quiet -G 0 -mcpu=vr4300 -mfix4300 -mips3 -mgp32 -mfp32 -Wimplicit -Wuninitialized -Wshadow CFLAGS := -O2 -quiet -G 0 -mcpu=vr4300 -mfix4300 -mips3 -mgp32 -mfp32 -Wimplicit -Wuninitialized -Wshadow

View File

@ -24,6 +24,16 @@ $ ./install.sh
Our install script does not yet support distros other than Ubuntu, Arch, and their derivatives. Please consider contributing to the script if you use another distro! Our install script does not yet support distros other than Ubuntu, Arch, and their derivatives. Please consider contributing to the script if you use another distro!
##### Docker
A Docker image containing all dependencies can be built and ran as follows:
```sh
# build image
$ docker build . -t pm
# spin up container, mounting current directory inside
$ docker run --rm -ti -v $(pwd):/papermario pm
```
#### Base ROM #### Base ROM
You'll need a Paper Mario (USA) ROM to work on this project. Copy it into the root directory of the repository with the name `baserom.z64`. You'll need a Paper Mario (USA) ROM to work on this project. Copy it into the root directory of the repository with the name `baserom.z64`.

View File

@ -1,27 +0,0 @@
.set noat # allow manual use of $at
.set noreorder # don't insert nops after branches
glabel nuBoot
/* 39CB0 8005E8B0 27BDFFE0 */ addiu $sp, $sp, -0x20
/* 39CB4 8005E8B4 AFBF001C */ sw $ra, 0x1c($sp)
/* 39CB8 8005E8B8 0C01A9C2 */ jal osInitialize
/* 39CBC 8005E8BC AFB00018 */ sw $s0, 0x18($sp)
/* 39CC0 8005E8C0 3C10800A */ lui $s0, %hi(D_800A4270)
/* 39CC4 8005E8C4 26104270 */ addiu $s0, $s0, %lo(D_800A4270)
/* 39CC8 8005E8C8 0200202D */ daddu $a0, $s0, $zero
/* 39CCC 8005E8CC 3C02800A */ lui $v0, %hi(nuScStack)
/* 39CD0 8005E8D0 244265D0 */ addiu $v0, $v0, %lo(nuScStack)
/* 39CD4 8005E8D4 AFA20010 */ sw $v0, 0x10($sp)
/* 39CD8 8005E8D8 2402000A */ addiu $v0, $zero, 0xa
/* 39CDC 8005E8DC 24050001 */ addiu $a1, $zero, 1
/* 39CE0 8005E8E0 3C068006 */ lui $a2, %hi(boot_idle)
/* 39CE4 8005E8E4 24C6E90C */ addiu $a2, $a2, %lo(boot_idle)
/* 39CE8 8005E8E8 0000382D */ daddu $a3, $zero, $zero
/* 39CEC 8005E8EC 0C019798 */ jal osCreateThread
/* 39CF0 8005E8F0 AFA20014 */ sw $v0, 0x14($sp)
/* 39CF4 8005E8F4 0C019808 */ jal osStartThread
/* 39CF8 8005E8F8 0200202D */ daddu $a0, $s0, $zero
/* 39CFC 8005E8FC 8FBF001C */ lw $ra, 0x1c($sp)
/* 39D00 8005E900 8FB00018 */ lw $s0, 0x18($sp)
/* 39D04 8005E904 03E00008 */ jr $ra
/* 39D08 8005E908 27BD0020 */ addiu $sp, $sp, 0x20

View File

@ -21,6 +21,12 @@ typedef struct Vec2bu {
/* 0x01 */ u8 y; /* 0x01 */ u8 y;
} Vec2bu; // size = 0x02 } Vec2bu; // size = 0x02
typedef struct Vec3b {
/* 0x00 */ s8 x;
/* 0x01 */ s8 y;
/* 0x02 */ s8 z;
} Vec3b; // size = 0x03
typedef struct Vec3f { typedef struct Vec3f {
/* 0x00 */ f32 x; /* 0x00 */ f32 x;
/* 0x04 */ f32 y; /* 0x04 */ f32 y;
@ -658,31 +664,6 @@ typedef struct TextureHeader {
/* 0x2F */ u8 filtering; /* 0x2F */ u8 filtering;
} TextureHeader; // size = 0x30 } TextureHeader; // size = 0x30
typedef struct StaticActorData {
/* 0x00 */ s32 flags;
/* 0x04 */ char unk_04;
/* 0x05 */ u8 type;
/* 0x06 */ u8 level;
/* 0x07 */ u8 maxHP;
/* 0x08 */ s16 partCount;
/* 0x0A */ char unk_0A[2];
/* 0x0C */ struct StaticActorPart** partsData;
/* 0x10 */ UNK_PTR script;
/* 0x14 */ UNK_PTR statusTable;
/* 0x18 */ u8 escapeChance;
/* 0x19 */ u8 airLiftChance;
/* 0x1A */ u8 spookChance;
/* 0x1B */ u8 baseStatusChance;
/* 0x1C */ u8 upAndAwayChance;
/* 0x1D */ u8 spinSmashReq;
/* 0x1E */ u8 powerBounceChance;
/* 0x1F */ u8 coinReward;
/* 0x20 */ u8 size[2];
/* 0x22 */ Vec2b hpBarOffset;
/* 0x24 */ Vec2b statusIconOffset;
/* 0x26 */ Vec2b statusMessageOffset;
} StaticActorData; // size = 0x28
typedef struct StaticMove { typedef struct StaticMove {
/* 0x00 */ s32 moveNameID; /* 0x00 */ s32 moveNameID;
/* 0x04 */ s32 flags; /* 0x04 */ s32 flags;
@ -1045,7 +1026,7 @@ typedef struct ActorPartMovement {
typedef struct ActorPart { typedef struct ActorPart {
/* 0x00 */ s32 flags; /* 0x00 */ s32 flags;
/* 0x04 */ s32 targetFlags; /* initialized to 0 */ /* 0x04 */ s32 targetFlags; /* initialized to 0 */
/* 0x08 */ struct StaticActorPart* staticData; /* 0x08 */ struct ActorPartDesc* staticData;
/* 0x0C */ struct ActorPart* nextPart; /* 0x0C */ struct ActorPart* nextPart;
/* 0x10 */ struct ActorPartMovement* movement; /* 0x10 */ struct ActorPartMovement* movement;
/* 0x14 */ Vec3s partOffset; /* 0x14 */ Vec3s partOffset;
@ -1239,7 +1220,7 @@ typedef struct ActorFlyPos {
typedef struct Actor { typedef struct Actor {
/* 0x000 */ s32 flags; /* 0x000 */ s32 flags;
/* 0x004 */ char unk_04[4]; /* 0x004 */ char unk_04[4];
/* 0x008 */ struct StaticActorData* staticActorData; /* 0x008 */ struct ActorDesc* staticActorData;
/* 0x00C */ ActorMovePos movePos; /* 0x00C */ ActorMovePos movePos;
/* 0x030 */ char unk_30[24]; /* 0x030 */ char unk_30[24];
/* 0x048 */ f32 jumpAccel; /* 0x048 */ f32 jumpAccel;
@ -1361,19 +1342,6 @@ typedef struct Actor {
/* 0x440 */ struct MenuIcon* ptrDefuffIcon; /* 0x440 */ struct MenuIcon* ptrDefuffIcon;
} Actor; // size = 0x444 } Actor; // size = 0x444
typedef struct StaticActorPart {
/* 0x00 */ s32 flags;
/* 0x04 */ s8 index;
/* 0x05 */ u8 posOffset[3];
/* 0x08 */ u8 targetOffset[2];
/* 0x0A */ s16 opacity;
/* 0x0C */ u32* idleAnimations;
/* 0x10 */ u32* defenseTable;
/* 0x14 */ s32 eventFlags;
/* 0x18 */ s32 flags3;
/* 0x1C */ char unk_1C[8];
} StaticActorPart; // size = 0x24
typedef struct TileDescriptor { typedef struct TileDescriptor {
/* 0x00 */ s8 name[32]; /* 0x00 */ s8 name[32];
/* 0x20 */ s16 auxW; /* 0x20 */ s16 auxW;

View File

@ -5,6 +5,11 @@
#include "common_structs.h" #include "common_structs.h"
#include "enums.h" #include "enums.h"
void nuBoot(void);
void boot_idle(void);
void boot_main(void);
void osCleanupThread(void); void osCleanupThread(void);
s32 heap_malloc(s32 size); s32 heap_malloc(s32 size);

View File

@ -12,6 +12,8 @@
#define ALIGN16(val) (((val) + 0xF) & ~0xF) #define ALIGN16(val) (((val) + 0xF) & ~0xF)
#define N(sym) NS(NAMESPACE, sym)
#define ARRAY_COUNT(arr) (s32)(sizeof(arr) / sizeof(arr[0])) #define ARRAY_COUNT(arr) (s32)(sizeof(arr) / sizeof(arr[0]))
#define ARRAY_COUNTU(arr) (u32)(sizeof(arr) / sizeof(arr[0])) #define ARRAY_COUNTU(arr) (u32)(sizeof(arr) / sizeof(arr[0]))

View File

@ -7,8 +7,6 @@
// TODO: consider moving Npc here // TODO: consider moving Npc here
#define N(sym) NS(NAMESPACE, sym)
#define ENTRY_COUNT(entryList) (sizeof(entryList) / sizeof(Vec4f)) #define ENTRY_COUNT(entryList) (sizeof(entryList) / sizeof(Vec4f))
typedef Vec4f EntryList[0]; typedef Vec4f EntryList[0];

View File

@ -246,4 +246,9 @@ extern s16 gMusicTargetVolume;
extern MusicPlayer gMusicPlayers[4]; extern MusicPlayer gMusicPlayers[4];
extern MusicPlayer D_8014F6F0; extern MusicPlayer D_8014F6F0;
// OS
extern OSThread D_800A4270; // idle thread, id 1
extern OSThread D_800A4420; // id 3
extern s32 D_800B8590;
#endif #endif

477
src/battle/actor/goomba.c Normal file
View File

@ -0,0 +1,477 @@
#include "common.h"
#include "battle/battle.h"
#include "script_api/battle.h"
#include "sprite/npc/goomba.h"
#include "goomba.h"
ApiStatus func_8021818C_430B2C(ScriptInstance* script, s32 isInitialCall);
s32 goomba_anims_running[];
s32 goomba_anims[];
s32 goomba_defense_table[];
s32 goomba_status_table[];
s32 goomba_defense_table[];
ActorPartDesc goomba_parts[];
Script goomba_init;
Script goomba_turn;
Script goomba_idle;
Script goomba_dispatch;
s32 goomba_defense_table[] = {
Element_NORMAL, 0,
Element_END,
};
s32 goomba_status_table[] = {
Debuff_NORMAL, 0,
Debuff_DEFAULT, 0,
Debuff_SLEEP, 100,
Debuff_POISON, 100,
Debuff_FROZEN, 100,
Debuff_DIZZY, 100,
Debuff_FEAR, 100,
Debuff_STATIC, 100,
Debuff_PARALYZE, 100,
Debuff_SHRINK, 100,
Debuff_STOP, 100,
Debuff_DEFAULT_TURN_MOD, 0,
Debuff_SLEEP_TURN_MOD, 0,
Debuff_POISON_TURN_MOD, 0,
Debuff_FROZEN_TURN_MOD, 0,
Debuff_DIZZY_TURN_MOD, 0,
Debuff_FEAR_TURN_MOD, 0,
Debuff_STATIC_TURN_MOD, 0,
Debuff_PARALYZE_TURN_MOD, 0,
Debuff_SHRINK_TURN_MOD, 0,
Debuff_STOP_TURN_MOD, 0,
Debuff_END,
};
ActorPartDesc goomba_parts[] = {
{
.flags = 0x00800000,
.index = 1,
.posOffset = { 0, 0, 0 },
.targetOffset = { 0, 20 },
.opacity = 0xFF,
.idleAnimations = goomba_anims,
.defenseTable = goomba_defense_table,
.eventFlags = 0,
.elementImmunityFlags = 0,
0x00, 0xF6,
},
};
ActorDesc goomba = {
.flags = 0,
.type = 7,
.level = 5,
.maxHP = 2,
.partCount = 1,
.partsData = &goomba_parts,
.script = &goomba_init,
.statusTable = &goomba_status_table,
.escapeChance = 90,
.airLiftChance = 100,
.spookChance = 90,
.baseStatusChance = 100,
.upAndAwayChance = 95,
.spinSmashReq = 0,
.powerBounceChance = 100,
.coinReward = 1,
.size = { 24, 24 },
.hpBarOffset = { 0, 0 },
.statusIconOffset = { -10, 20 },
.statusMessageOffset = { 10, 20 },
};
s32 goomba_anims[] = {
Debuff_NORMAL, NPC_ANIM(goomba, normal, idle),
Debuff_STONE, NPC_ANIM(goomba, normal, still),
Debuff_SLEEP, NPC_ANIM(goomba, normal, asleep),
Debuff_POISON, NPC_ANIM(goomba, normal, idle),
Debuff_STOP, NPC_ANIM(goomba, normal, still),
Debuff_STATIC, NPC_ANIM(goomba, normal, idle),
Debuff_PARALYZE, NPC_ANIM(goomba, normal, still),
Debuff_DIZZY, NPC_ANIM(goomba, normal, dizzy),
Debuff_FEAR, NPC_ANIM(goomba, normal, dizzy),
Debuff_END,
};
s32 goomba_anims_running[] = {
Debuff_NORMAL, NPC_ANIM(goomba, normal, run),
Debuff_STONE, NPC_ANIM(goomba, normal, still),
Debuff_SLEEP, NPC_ANIM(goomba, normal, asleep),
Debuff_POISON, NPC_ANIM(goomba, normal, idle),
Debuff_STOP, NPC_ANIM(goomba, normal, still),
Debuff_STATIC, NPC_ANIM(goomba, normal, run),
Debuff_PARALYZE, NPC_ANIM(goomba, normal, still),
Debuff_DIZZY, NPC_ANIM(goomba, normal, dizzy),
Debuff_FEAR, NPC_ANIM(goomba, normal, dizzy),
Debuff_END,
};
Script goomba_init = SCRIPT({
BindTakeTurn(ActorID_SELF, goomba_turn);
BindIdle(ActorID_SELF, goomba_idle);
BindHandleEvent(ActorID_SELF, goomba_dispatch);
});
Script goomba_idle = SCRIPT({
10:
RandInt(80, SI_VAR(0));
SI_VAR(0) += 80;
loop SI_VAR(0) {
0:
GetStatusFlags(ActorID_SELF, SI_VAR(1));
if (SI_VAR(1) & 0x35D000) {
sleep 1;
goto 0;
}
sleep 1;
}
GetActorPos(ActorID_SELF, SI_VAR(0), SI_VAR(1), SI_VAR(2));
SI_VAR(0) += 5;
SetActorIdleSpeed(ActorID_SELF, 1.0);
SetIdleAnimations(ActorID_SELF, 1, goomba_anims_running);
SetIdleGoal(ActorID_SELF, SI_VAR(0), SI_VAR(1), SI_VAR(2));
IdleRunToGoal(ActorID_SELF, 0);
SetIdleAnimations(ActorID_SELF, 1, goomba_anims);
loop 20 {
1:
GetStatusFlags(ActorID_SELF, SI_VAR(1));
if (SI_VAR(1) & 0x35D000) {
sleep 1;
goto 1;
}
sleep 1;
}
GetActorPos(ActorID_SELF, SI_VAR(0), SI_VAR(1), SI_VAR(2));
SI_VAR(0) -= 5;
SetActorIdleSpeed(ActorID_SELF, 1.0);
SetIdleAnimations(ActorID_SELF, 1, goomba_anims_running);
SetIdleGoal(ActorID_SELF, SI_VAR(0), SI_VAR(1), SI_VAR(2));
IdleRunToGoal(ActorID_SELF, 0);
SetIdleAnimations(ActorID_SELF, 1, goomba_anims);
loop 80 {
2:
GetStatusFlags(ActorID_SELF, SI_VAR(1));
if (SI_VAR(1) & 0x35D000) {
sleep 1;
goto 2;
}
sleep 1;
}
goto 10;
});
Script goomba_dispatch = SCRIPT({
UseIdleAnimation(ActorID_SELF, 0);
EnableIdleScript(ActorID_SELF, 0);
SetActorScale(ActorID_SELF, 1.0, 1.0, 1.0);
GetLastEvent(ActorID_SELF, SI_VAR(0));
match SI_VAR(0) {
Event_HIT_COMBO, Event_HIT {
SI_VAR(0) = c 1;
SI_VAR(1) = c NPC_ANIM(goomba, normal, pain);
await DoNormalHit;
}
== Event_BURN_HIT {
SI_VAR(0) = c 1;
SI_VAR(1) = c NPC_ANIM(goomba, normal, burn_pain);
SI_VAR(2) = c NPC_ANIM(goomba, normal, burn_dead);
await DoBurnHit;
}
== Event_BURN_DEATH {
SI_VAR(0) = c 1;
SI_VAR(1) = c NPC_ANIM(goomba, normal, burn_pain);
SI_VAR(2) = c NPC_ANIM(goomba, normal, burn_dead);
await DoBurnHit;
SI_VAR(0) = c 1;
SI_VAR(1) = c NPC_ANIM(goomba, normal, burn_dead);
await DoDeath;
return;
}
== Event_SPIN_SMASH_HIT {
SI_VAR(0) = c 1;
SI_VAR(1) = c NPC_ANIM(goomba, normal, pain);
await DoSpinSmashHit;
}
== Event_SPIN_SMASH_DEATH {
SI_VAR(0) = c 1;
SI_VAR(1) = c NPC_ANIM(goomba, normal, pain);
await DoSpinSmashHit;
SI_VAR(0) = c 1;
SI_VAR(1) = c NPC_ANIM(goomba, normal, dead);
await DoDeath;
return;
}
== Event_SHOCK_HIT {
SI_VAR(0) = c 1;
SI_VAR(1) = c NPC_ANIM(goomba, normal, electrocute);
await DoShockHit;
SI_VAR(0) = c 1;
SI_VAR(1) = c NPC_ANIM(goomba, normal, pain);
await DoJumpBack;
JumpToGoal(ActorID_SELF, 5, 0, 1, 0);
SetAnimationRate(ActorID_SELF, 1, 2.0);
SetAnimation(ActorID_SELF, 1, NPC_ANIM(goomba, normal, dizzy));
SetGoalToHome(ActorID_SELF);
SetActorSpeed(ActorID_SELF, 8.0);
RunToGoal(ActorID_SELF, 0, 0);
SetAnimationRate(ActorID_SELF, 1, 1.0);
sleep 5;
SetAnimation(ActorID_SELF, 1, NPC_ANIM(goomba, normal, idle));
SetActorJumpGravity(ActorID_SELF, 1.6);
JumpToGoal(ActorID_SELF, 5, 0, 1, 0);
}
== Event_SHOCK_DEATH {
SI_VAR(0) = c 1;
SI_VAR(1) = c NPC_ANIM(goomba, normal, electrocute);
await DoShockHit;
SI_VAR(0) = c 1;
SI_VAR(1) = c NPC_ANIM(goomba, normal, dead);
await DoDeath;
return;
}
== Event_STAR_BEAM, 23, Event_IMMUNE, Event_AIR_LIFT_FAILED {
SI_VAR(0) = c 1;
SI_VAR(1) = c NPC_ANIM(goomba, normal, idle);
await DoImmune;
}
== Event_DEATH {
SI_VAR(0) = c 1;
SI_VAR(1) = c NPC_ANIM(goomba, normal, pain);
await DoNormalHit;
sleep 10;
SI_VAR(0) = c 1;
SI_VAR(1) = c NPC_ANIM(goomba, normal, dead);
await DoDeath;
return;
}
== Event_END_FIRST_STRIKE {
SetAnimationRate(ActorID_SELF, 1, 2.0);
SetAnimation(ActorID_SELF, 1, NPC_ANIM(goomba, normal, run));
SetGoalToHome(ActorID_SELF);
SetActorSpeed(ActorID_SELF, 4.0);
RunToGoal(ActorID_SELF, 0, 0);
SetAnimationRate(ActorID_SELF, 1, 1.0);
HPBarToHome(ActorID_SELF);
}
== Event_RECOVER_STATUS {
SI_VAR(0) = c 1;
SI_VAR(1) = c NPC_ANIM(goomba, normal, idle);
await DoRecover;
}
== Event_SCARE_AWAY {
SI_VAR(0) = c 1;
SI_VAR(1) = c NPC_ANIM(goomba, normal, run);
SI_VAR(2) = c NPC_ANIM(goomba, normal, pain);
await DoScareAway;
return;
}
== Event_BEGIN_AIR_LIFT {
SI_VAR(0) = c 1;
SI_VAR(1) = c NPC_ANIM(goomba, normal, run);
await DoAirLift;
}
== Event_BLOW_AWAY {
SI_VAR(0) = c 1;
SI_VAR(1) = c NPC_ANIM(goomba, normal, pain);
await DoBlowAway;
return;
} else {
}
}
SetAnimation(ActorID_SELF, 1, NPC_ANIM(goomba, normal, idle));
EnableIdleScript(ActorID_SELF, 1);
UseIdleAnimation(ActorID_SELF, 1);
});
f32 float_table_for_func_80218000[] = {
0.000000f, 0.017452f, 0.034899f, 0.052336f, 0.069756f, 0.087156f, 0.104528f, 0.121869f,
0.139173f, 0.156434f, 0.173648f, 0.190809f, 0.207912f, 0.224951f, 0.241922f, 0.258819f,
0.275637f, 0.292372f, 0.309017f, 0.325568f, 0.342020f, 0.358368f, 0.374607f, 0.390731f,
0.406737f, 0.422618f, 0.438371f, 0.453990f, 0.469472f, 0.484810f, 0.500000f, 0.515038f,
0.529919f, 0.544639f, 0.559193f, 0.573576f, 0.587785f, 0.601815f, 0.615661f, 0.629320f,
0.642788f, 0.656059f, 0.669131f, 0.681998f, 0.694658f, 0.707107f, 0.719340f, 0.731354f,
0.743145f, 0.754710f, 0.766044f, 0.777146f, 0.788011f, 0.798636f, 0.809017f, 0.819152f,
0.829038f, 0.838671f, 0.848048f, 0.857167f, 0.866025f, 0.874620f, 0.882948f, 0.891007f,
0.898794f, 0.906308f, 0.913545f, 0.920505f, 0.927184f, 0.933580f, 0.939693f, 0.945519f,
0.951057f, 0.956305f, 0.961262f, 0.965926f, 0.970296f, 0.974370f, 0.978148f, 0.981627f,
0.984808f, 0.987688f, 0.990268f, 0.992546f, 0.994522f, 0.996195f, 0.997564f, 0.998630f,
0.999391f, 0.999848f, 1.000000f,
};
Script goomba_turn = SCRIPT({
UseIdleAnimation(ActorID_SELF, 0);
EnableIdleScript(ActorID_SELF, 0);
SetTargetActor(ActorID_SELF, 0);
UseCamPreset(63);
BattleCamTargetActor(ActorID_SELF);
0x8024ECF8(-1, 1, 0);
SetAnimation(ActorID_SELF, 1, NPC_ANIM(goomba, normal, run));
SetGoalToTarget(ActorID_SELF);
AddGoalPos(ActorID_SELF, 50, 0, 0);
SetActorSpeed(ActorID_SELF, 6.0);
RunToGoal(ActorID_SELF, 0, 0);
SetAnimation(ActorID_SELF, 1, NPC_ANIM(goomba, normal, idle));
SetActorDispOffset(ActorID_SELF, 0, -1, 0);
sleep 1;
SetActorDispOffset(ActorID_SELF, 0, -2, 0);
sleep 5;
SetActorDispOffset(ActorID_SELF, 0, 0, 0);
SetAnimation(ActorID_SELF, 1, NPC_ANIM(goomba, normal, headbonk));
EnemyTestTarget(ActorID_SELF, SI_VAR(0), 0, 0, 1, 16);
match SI_VAR(0) {
6, 5 {
SI_VAR(10) = SI_VAR(0);
SetGoalToTarget(ActorID_SELF);
GetGoalPos(ActorID_SELF, SI_VAR(0), SI_VAR(1), SI_VAR(2));
SI_VAR(0) -= 10;
SI_VAR(1) = 10;
SI_VAR(2) += 3;
SetGoalPos(ActorID_SELF, SI_VAR(0), SI_VAR(1), SI_VAR(2));
SetActorJumpGravity(ActorID_SELF, 1.2);
spawn {
GetActorPos(ActorID_SELF, SI_VAR(1), SI_VAR(2), SI_VAR(0));
SI_VAR(0) = 0;
loop 16 {
GetActorPos(ActorID_SELF, SI_VAR(4), SI_VAR(5), SI_VAR(6));
func_8021818C_430B2C(SI_VAR(1), SI_VAR(2), SI_VAR(4), SI_VAR(5), SI_VAR(0));
SetActorRotation(ActorID_SELF, 0, 0, SI_VAR(0));
SI_VAR(1) = SI_VAR(4);
SI_VAR(2) = SI_VAR(5);
SI_VAR(3) = SI_VAR(6);
sleep 1;
}
}
spawn {
sleep 6;
SetAnimation(ActorID_SELF, 1, NPC_ANIM(goomba, normal, headbonk));
}
JumpToGoal(ActorID_SELF, 16, 0, 1, 0);
SetAnimation(ActorID_SELF, 1, NPC_ANIM(goomba, normal, asleep));
SetActorScale(ActorID_SELF, 1.1, 0.8, 1.0);
SetActorDispOffset(ActorID_SELF, 0, 5, 0);
sleep 1;
SetActorScale(ActorID_SELF, 1.3, 0.5, 1.0);
SetActorDispOffset(ActorID_SELF, 0, -2, 0);
sleep 1;
SetActorScale(ActorID_SELF, 1.0, 1.0, 1.0);
SetActorDispOffset(ActorID_SELF, 0, 7, 0);
SetAnimation(ActorID_SELF, 1, NPC_ANIM(goomba, normal, pain));
sleep 5;
if (SI_VAR(10) == 5) {
EnemyTestTarget(ActorID_SELF, SI_VAR(0), 0x80000000, 0, 0, 0);
}
sleep 5;
SetActorDispOffset(ActorID_SELF, 0, 0, 0);
SetAnimation(ActorID_SELF, 1, NPC_ANIM(goomba, normal, headbonk));
SetGoalToTarget(ActorID_SELF);
GetGoalPos(ActorID_SELF, SI_VAR(0), SI_VAR(1), SI_VAR(2));
SI_VAR(0) += 20;
SI_VAR(1) = 0;
SetGoalPos(ActorID_SELF, SI_VAR(0), SI_VAR(1), SI_VAR(2));
SetActorJumpGravity(ActorID_SELF, 2.0);
spawn {
sleep 4;
SI_VAR(0) = 180;
loop 4 {
SI_VAR(0) -= 45;
SetActorRotation(ActorID_SELF, 0, 0, SI_VAR(0));
sleep 1;
}
SetAnimation(ActorID_SELF, 1, NPC_ANIM(goomba, normal, headbonk));
}
JumpToGoal(ActorID_SELF, 15, 0, 1, 0);
SetAnimation(ActorID_SELF, 1, NPC_ANIM(goomba, normal, dizzy));
sleep 5;
UseCamPreset(2);
YieldTurn();
SetActorYaw(ActorID_SELF, 180);
AddActorDecoration(ActorID_SELF, 1, 0, 2);
SetAnimationRate(ActorID_SELF, 1, 2.0);
SetGoalToHome(ActorID_SELF);
SetActorSpeed(ActorID_SELF, 8.0);
RunToGoal(ActorID_SELF, 0, 0);
SetAnimationRate(ActorID_SELF, 1, 1.0);
SetActorYaw(ActorID_SELF, 0);
sleep 5;
SetAnimation(ActorID_SELF, 1, NPC_ANIM(goomba, normal, idle));
SetActorJumpGravity(ActorID_SELF, 1.6);
JumpToGoal(ActorID_SELF, 5, 0, 1, 0);
RemoveActorDecoration(ActorID_SELF, 1, 0);
EnableIdleScript(ActorID_SELF, 1);
UseIdleAnimation(ActorID_SELF, 1);
return;
} else {
SetGoalToTarget(ActorID_SELF);
SetActorJumpGravity(ActorID_SELF, 1.2);
spawn {
GetActorPos(ActorID_SELF, SI_VAR(1), SI_VAR(2), SI_VAR(0));
SI_VAR(0) = 0;
loop 16 {
GetActorPos(ActorID_SELF, SI_VAR(4), SI_VAR(5), SI_VAR(6));
func_8021818C_430B2C(SI_VAR(1), SI_VAR(2), SI_VAR(4), SI_VAR(5), SI_VAR(0));
SetActorRotation(ActorID_SELF, 0, 0, SI_VAR(0));
SI_VAR(1) = SI_VAR(4);
SI_VAR(2) = SI_VAR(5);
SI_VAR(3) = SI_VAR(6);
sleep 1;
}
}
spawn {
sleep 6;
SetAnimation(ActorID_SELF, 1, NPC_ANIM(goomba, normal, headbonk));
}
JumpToGoal(ActorID_SELF, 16, 0, 1, 0);
SetAnimation(ActorID_SELF, 1, NPC_ANIM(goomba, normal, pre_headbonk));
SetActorScale(ActorID_SELF, 1.1, 0.8, 1.0);
sleep 1;
SetActorScale(ActorID_SELF, 1.3, 0.5, 1.0);
sleep 1;
}
}
EnemyDamageTarget(ActorID_SELF, SI_VAR(0), 0, 0, 0, 1, 32);
match SI_VAR(0) {
0, 2 {
UseCamPreset(2);
SetActorScale(ActorID_SELF, 1.1, 0.8, 1.0);
sleep 1;
SetActorScale(ActorID_SELF, 1.0, 1.0, 1.0);
sleep 1;
SetActorRotation(ActorID_SELF, 0, 0, 0);
SetActorDispOffset(ActorID_SELF, 0, 0, 0);
SetAnimation(ActorID_SELF, 1, NPC_ANIM(goomba, normal, idle));
GetGoalPos(ActorID_SELF, SI_VAR(0), SI_VAR(1), SI_VAR(2));
SI_VAR(0) += 40;
SI_VAR(1) = 0;
SetActorJumpGravity(ActorID_SELF, 1.8);
SetGoalPos(ActorID_SELF, SI_VAR(0), SI_VAR(1), SI_VAR(2));
JumpToGoal(ActorID_SELF, 10, 0, 1, 0);
SI_VAR(0) += 30;
SetGoalPos(ActorID_SELF, SI_VAR(0), SI_VAR(1), SI_VAR(2));
JumpToGoal(ActorID_SELF, 8, 0, 1, 0);
SI_VAR(0) += 20;
SetGoalPos(ActorID_SELF, SI_VAR(0), SI_VAR(1), SI_VAR(2));
JumpToGoal(ActorID_SELF, 6, 0, 1, 0);
SetAnimation(ActorID_SELF, 1, NPC_ANIM(goomba, normal, idle));
sleep 3;
YieldTurn();
SetAnimationRate(ActorID_SELF, 1, 2.0);
SetAnimation(ActorID_SELF, 1, NPC_ANIM(goomba, normal, run));
SetGoalToHome(ActorID_SELF);
SetActorSpeed(ActorID_SELF, 8.0);
RunToGoal(ActorID_SELF, 0, 0);
SetAnimationRate(ActorID_SELF, 1, 1.0);
}
}
EnableIdleScript(ActorID_SELF, 1);
UseIdleAnimation(ActorID_SELF, 1);
});

View File

@ -1,7 +1,9 @@
#ifndef _BATTLE_ACTOR_GOOMBA_ #ifndef _BATTLE_ACTOR_GOOMBA_
#define _BATTLE_ACTOR_GOOMBA_ #define _BATTLE_ACTOR_GOOMBA_
// TODO: disasm #include "common.h"
#define goomba 0x802196EC #include "battle/battle.h"
ActorDesc goomba;
#endif #endif

View File

@ -1,7 +1,9 @@
#ifndef _BATTLE_ACTOR_PARAGOOMBA_ #ifndef _BATTLE_ACTOR_PARAGOOMBA_
#define _BATTLE_ACTOR_PARAGOOMBA_ #define _BATTLE_ACTOR_PARAGOOMBA_
#include "battle/battle.h"
// TODO: disasm // TODO: disasm
#define paragoomba 0x8021CD00 extern ActorDesc paragoomba;
#endif #endif

View File

@ -1,7 +1,9 @@
#ifndef _BATTLE_ACTOR_SPIKED_GOOMBA_ #ifndef _BATTLE_ACTOR_SPIKED_GOOMBA_
#define _BATTLE_ACTOR_SPIKED_GOOMBA_ #define _BATTLE_ACTOR_SPIKED_GOOMBA_
#include "battle/battle.h"
// TODO: disasm // TODO: disasm
#define spikedGoomba 0x8021B0AC extern ActorDesc spiked_goomba;
#endif #endif

View File

@ -1,4 +1,4 @@
#include "common.h" #include "battle/battle.h"
#define NAMESPACE b_area_arn #define NAMESPACE b_area_arn

View File

@ -2,7 +2,7 @@
#define NAMESPACE b_area_kmr_part_1 #define NAMESPACE b_area_kmr_part_1
INCLUDE_ASM(s32, "battle/area_kmr_part_1/4309A0", func_80218000_4309A0); INCLUDE_ASM(s32, "battle/area_kmr_part_1/4309A0", func_80218000_4309A0); // goomba.c
INCLUDE_ASM(s32, "battle/area_kmr_part_1/4309A0", func_8021818C_430B2C); INCLUDE_ASM(s32, "battle/area_kmr_part_1/4309A0", func_8021818C_430B2C);

View File

@ -8,66 +8,66 @@
#define NAMESPACE b_area_kmr_part_1 #define NAMESPACE b_area_kmr_part_1
Formation formation0 = { Formation formation0 = {
{ goomba, .position = 1, .priority = 10 }, { &goomba, .position = 1, .priority = 10 },
}; };
Formation formation1 = { Formation formation1 = {
{ goomba, .position = 1, .priority = 10 }, { &goomba, .position = 1, .priority = 10 },
{ goomba, .position = 2, .priority = 9 }, { &goomba, .position = 2, .priority = 9 },
}; };
Formation formation2 = { Formation formation2 = {
{ goomba, .position = 0, .priority = 10 }, { &goomba, .position = 0, .priority = 10 },
{ goomba, .position = 1, .priority = 9 }, { &goomba, .position = 1, .priority = 9 },
{ goomba, .position = 2, .priority = 8 }, { &goomba, .position = 2, .priority = 8 },
}; };
Formation formation3 = { Formation formation3 = {
{ goomba, .position = 1, .priority = 10 }, { &goomba, .position = 1, .priority = 10 },
{ paragoomba, .position = 6, .priority = 9 }, { &paragoomba, .position = 6, .priority = 9 },
}; };
Formation formation4 = { Formation formation4 = {
{ goomba, .position = 0, .priority = 10 }, { &goomba, .position = 0, .priority = 10 },
{ goomba, .position = 1, .priority = 9 }, { &goomba, .position = 1, .priority = 9 },
{ goomba, .position = 2, .priority = 8 }, { &goomba, .position = 2, .priority = 8 },
{ goomba, .position = 3, .priority = 7 }, { &goomba, .position = 3, .priority = 7 },
}; };
Formation formation5 = { Formation formation5 = {
{ goomba, .position = 1, .priority = 10 }, { &goomba, .position = 1, .priority = 10 },
{ spikedGoomba, .position = 2, .priority = 9 }, { &spiked_goomba, .position = 2, .priority = 9 },
}; };
Formation formation6 = { Formation formation6 = {
{ goomba, .position = 0, .priority = 10 }, { &goomba, .position = 0, .priority = 10 },
{ paragoomba, .position = 5, .priority = 9 }, { &paragoomba, .position = 5, .priority = 9 },
{ goomba, .position = 2, .priority = 8 }, { &goomba, .position = 2, .priority = 8 },
{ paragoomba, .position = 7, .priority = 7 }, { &paragoomba, .position = 7, .priority = 7 },
}; };
Formation formation7 = { Formation formation7 = {
{ paragoomba, .position = 5, .priority = 10 }, { &paragoomba, .position = 5, .priority = 10 },
}; };
Formation formation8 = { Formation formation8 = {
{ paragoomba, .position = 5, .priority = 10 }, { &paragoomba, .position = 5, .priority = 10 },
{ paragoomba, .position = 6, .priority = 9 }, { &paragoomba, .position = 6, .priority = 9 },
}; };
Formation formation9 = { Formation formation9 = {
{ paragoomba, .position = 4, .priority = 10 }, { &paragoomba, .position = 4, .priority = 10 },
{ paragoomba, .position = 5, .priority = 9 }, { &paragoomba, .position = 5, .priority = 9 },
{ paragoomba, .position = 6, .priority = 8 }, { &paragoomba, .position = 6, .priority = 8 },
}; };
Formation formation10 = { Formation formation10 = {
{ spikedGoomba, .position = 1, .priority = 10 }, { &spiked_goomba, .position = 1, .priority = 10 },
}; };
Formation formation11 = { Formation formation11 = {
{ spikedGoomba, .position = 1, .priority = 10 }, { &spiked_goomba, .position = 1, .priority = 10 },
{ goomba, .position = 2, .priority = 9 }, { &goomba, .position = 2, .priority = 9 },
}; };
BattleList area_kmr_part_1_battles = { BattleList area_kmr_part_1_battles = {

View File

@ -3,6 +3,31 @@
#include "common.h" #include "common.h"
typedef struct ActorDesc {
/* 0x00 */ s32 flags;
/* 0x04 */ char unk_04;
/* 0x05 */ u8 type;
/* 0x06 */ u8 level;
/* 0x07 */ u8 maxHP;
/* 0x08 */ s16 partCount;
/* 0x0A */ char unk_0A[2];
/* 0x0C */ struct ActorPartDesc** partsData;
/* 0x10 */ Bytecode* script;
/* 0x14 */ s32* statusTable;
/* 0x18 */ u8 escapeChance;
/* 0x19 */ u8 airLiftChance;
/* 0x1A */ u8 spookChance;
/* 0x1B */ u8 baseStatusChance;
/* 0x1C */ u8 upAndAwayChance;
/* 0x1D */ u8 spinSmashReq;
/* 0x1E */ u8 powerBounceChance;
/* 0x1F */ u8 coinReward;
/* 0x20 */ Vec2b size;
/* 0x22 */ Vec2b hpBarOffset;
/* 0x24 */ Vec2b statusIconOffset;
/* 0x26 */ Vec2b statusMessageOffset;
} ActorDesc; // size = 0x28
typedef struct Stage { typedef struct Stage {
/* 0x00 */ const char* texture; /* 0x00 */ const char* texture;
/* 0x04 */ const char* shape; /* 0x04 */ const char* shape;
@ -23,7 +48,7 @@ typedef struct StageListRow {
} StageList[]; // size = 0x08 * n } StageList[]; // size = 0x08 * n
typedef struct FormationRow { typedef struct FormationRow {
/* 0x00 */ StaticActorData* actor; /* 0x00 */ ActorDesc* actor;
/* 0x04 */ s32 position; ///< Home position. May also be a `Vector3*`. /* 0x04 */ s32 position; ///< Home position. May also be a `Vector3*`.
/* 0x08 */ s32 priority; ///< Actors with higher priority values take their turn first. /* 0x08 */ s32 priority; ///< Actors with higher priority values take their turn first.
/* 0x0C */ s32 var0; /* 0x0C */ s32 var0;
@ -45,4 +70,24 @@ typedef struct Battle {
// TODO: enum for home position (0..3 are floor, 4..7 are air, etc.) // TODO: enum for home position (0..3 are floor, 4..7 are air, etc.)
typedef struct {
Element element;
s32 defense;
} DefenseTableEntry;
typedef DefenseTableEntry DefenseTable[];
typedef struct ActorPartDesc {
/* 0x00 */ s32 flags;
/* 0x04 */ s8 index;
/* 0x05 */ Vec3b posOffset;
/* 0x08 */ Vec2b targetOffset;
/* 0x0A */ s16 opacity;
/* 0x0C */ s32* idleAnimations;
/* 0x10 */ s32* defenseTable;
/* 0x14 */ s32 eventFlags;
/* 0x18 */ s32 elementImmunityFlags;
/* 0x1C */ char unk_1C[8];
} ActorPartDesc; // size = 0x24
#endif #endif

View File

@ -1,4 +1,4 @@
#include "common.h" #include "battle/battle.h"
INCLUDE_ASM(s32, "code_190B20", create_target_list); INCLUDE_ASM(s32, "code_190B20", create_target_list);
@ -93,11 +93,6 @@ INCLUDE_ASM(s32, "code_190B20", func_80265CE8);
INCLUDE_ASM(s32, "code_190B20", func_80265D44); INCLUDE_ASM(s32, "code_190B20", func_80265D44);
typedef struct {
Element element;
s32 defense;
} DefenseTableEntry;
s32 lookup_defense(DefenseTableEntry* defenseTable, Element elementKey) { s32 lookup_defense(DefenseTableEntry* defenseTable, Element elementKey) {
DefenseTableEntry* row; DefenseTableEntry* row;
s32 normalDefense = 0; s32 normalDefense = 0;

View File

@ -1,4 +1,5 @@
#include "common.h" #include "common.h"
#include "battle/battle.h"
s32 count_targets(Actor* actor, s32 targetHomeIndex, s32 targetSelectionFlags) { s32 count_targets(Actor* actor, s32 targetHomeIndex, s32 targetSelectionFlags) {
BattleStatus* battleStatus = BATTLE_STATUS; BattleStatus* battleStatus = BATTLE_STATUS;

View File

@ -1,4 +1,5 @@
#include "common.h" #include "common.h"
#include "battle/battle.h"
void dispatch_event_actor(Actor* actor, Event event); void dispatch_event_actor(Actor* actor, Event event);

View File

@ -1,4 +1,5 @@
#include "common.h" #include "common.h"
#include "battle/battle.h"
INCLUDE_ASM(s32, "code_1AC760", dispatch_event_partner); INCLUDE_ASM(s32, "code_1AC760", dispatch_event_partner);

View File

@ -1,5 +1,4 @@
#include "common.h" #include "battle/battle.h"
#include "map.h"
ApiStatus N(UnkBattleFunc1)(ScriptInstance* script, s32 isInitialCall) { ApiStatus N(UnkBattleFunc1)(ScriptInstance* script, s32 isInitialCall) {
Bytecode* args = script->ptrReadPos; Bytecode* args = script->ptrReadPos;

View File

@ -1,5 +1,36 @@
#include "common.h" #include "common.h"
#include "nu/nusys.h"
#include "functions.h"
#include "variables.h"
INCLUDE_ASM(s32, "os/code_39cb0_len_100", nuBoot); // TODO: create src/os/nusys/nuSched.h?
extern u64 nuScStack[NU_SC_STACK_SIZE / sizeof(u64)];
INCLUDE_ASM(s32, "os/code_39cb0_len_100", boot_idle); //void (*nuIdleFunc)(void) = NULL;
void nuBoot(void) {
osInitialize(); // __osInitialize_common
osCreateThread(&D_800A4270, NU_IDLE_THREAD_ID, boot_idle, NULL, &nuScStack, 10);
osStartThread(&D_800A4270);
}
#ifdef NON_MATCHING
void boot_idle(void) {
nuIdleFunc = NULL;
nuPiInit();
nuScCreateScheduler(OS_VI_NTSC_LAN1, 1);
osViSetSpecialFeatures(OS_VI_GAMMA_OFF | OS_VI_GAMMA_DITHER_OFF | OS_VI_DIVOT_ON | OS_VI_DITHER_FILTER_ON);
osCreateThread(&D_800A4420, NU_MAIN_THREAD_ID, boot_main, NULL, &D_800B8590, NU_MAIN_THREAD_PRI);
osStartThread(&D_800A4420);
osSetThreadPri(&D_800A4270, NU_IDLE_THREAD_PRI);
while (1) {
if (nuIdleFunc != NULL) {
nuIdleFunc();
}
}
}
#else
INCLUDE_ASM(void, "os/code_39cb0_len_100", boot_idle, void);
#endif

View File

@ -77,7 +77,8 @@ script_parser = Lark(r"""
| "<" -> cond_op_lt | "<" -> cond_op_lt
| ">=" -> cond_op_ge | ">=" -> cond_op_ge
| "<=" -> cond_op_le | "<=" -> cond_op_le
| "?" -> cond_op_flag | "&" -> cond_op_flag
| "!&" -> cond_op_not_flag
match_stmt: "match" expr "{" (match_cases SEMICOLON*)? "}" match_stmt: "match" expr "{" (match_cases SEMICOLON*)? "}"
match_const_stmt: "matchc" expr "{" (match_cases SEMICOLON*)? "}" match_const_stmt: "matchc" expr "{" (match_cases SEMICOLON*)? "}"
@ -259,12 +260,12 @@ class LabelAllocation(Visitor):
raise CompileError(f"label `{name}' already declared", tree.meta) raise CompileError(f"label `{name}' already declared", tree.meta)
try: try:
label_idx = int(name, base=0) label_idx = int(name)
while len(self.labels) < label_idx: while len(self.labels) <= label_idx:
self.labels.append(None) self.labels.append(None)
self.labels.insert(label_idx, name) self.labels[label_idx] = name
except ValueError: except ValueError:
self.labels.append(name) self.labels.append(name)
@ -353,7 +354,8 @@ class Compile(Transformer):
def cond_op_gt(self, tree): return { "if": "ScriptOpcode_IF_GT", "case": "ScriptOpcode_CASE_GT" } def cond_op_gt(self, tree): return { "if": "ScriptOpcode_IF_GT", "case": "ScriptOpcode_CASE_GT" }
def cond_op_le(self, tree): return { "if": "ScriptOpcode_IF_LE", "case": "ScriptOpcode_CASE_LE" } def cond_op_le(self, tree): return { "if": "ScriptOpcode_IF_LE", "case": "ScriptOpcode_CASE_LE" }
def cond_op_ge(self, tree): return { "if": "ScriptOpcode_IF_GE", "case": "ScriptOpcode_CASE_GE" } def cond_op_ge(self, tree): return { "if": "ScriptOpcode_IF_GE", "case": "ScriptOpcode_CASE_GE" }
def cond_op_flag(self, tree): return { "if": "ScriptOpcode_IF_FLAG", "case": "ScriptOpcode_CASE_FLAG" } def cond_op_flag(self, tree): return { "__op__": "&", "if": "ScriptOpcode_IF_FLAG", "case": "ScriptOpcode_CASE_FLAG" }
def cond_op_not_flag(self, tree): return { "__op__": "!&", "if": "ScriptOpcode_IF_NOT_FLAG" }
def match_stmt(self, tree): def match_stmt(self, tree):
expr = tree.children[0] expr = tree.children[0]
@ -389,13 +391,21 @@ class Compile(Transformer):
return [tree.children[0], *tree.children[1]] return [tree.children[0], *tree.children[1]]
def case_else(self, tree): def case_else(self, tree):
return [Cmd("ScriptOpcode_ELSE"), *tree.children[0]] return [Cmd("ScriptOpcode_CASE_ELSE"), *tree.children[0]]
def case_op(self, tree): def case_op(self, tree):
if len(tree.children) == 4: if len(tree.children) == 4:
op, expr, multi_case, block = tree.children op, expr, multi_case, block = tree.children
if not "case" in op:
raise CompileError(f"operation `{opcodes['__op__']}' not supported in match cases", tree.meta)
return [Cmd(op["case"], expr), *multi_case, *block, Cmd("ScriptOpcode_END_CASE_MULTI")] return [Cmd(op["case"], expr), *multi_case, *block, Cmd("ScriptOpcode_END_CASE_MULTI")]
else: else:
op, expr, block = tree.children op, expr, block = tree.children
if not "case" in op:
raise CompileError(f"operation `{opcodes['__op__']}' not supported in match cases", tree.meta)
return [Cmd(op["case"], expr), *block] return [Cmd(op["case"], expr), *block]
def case_range(self, tree): def case_range(self, tree):
if len(tree.children) == 4: if len(tree.children) == 4:
@ -583,33 +593,6 @@ class Compile(Transformer):
"const": "ScriptOpcode_OR_CONST", "const": "ScriptOpcode_OR_CONST",
} }
def label_decl(self, tree):
if len(tree.children) == 1:
label = tree.children[0]
return Cmd("ScriptOpcode_LABEL", label, meta=tree.meta)
else:
label, cmd_or_block = tree.children
if type(cmd_or_block) is not list:
cmd_or_block = [cmd_or_block]
for cmd in cmd_or_block:
if isinstance(cmd, BaseCmd):
cmd.add_context(LabelCtx(label))
return [
Cmd("ScriptOpcode_LABEL", label, meta=tree.meta),
*cmd_or_block
]
def label_goto(self, tree):
label = tree.children[0]
return Cmd("ScriptOpcode_GOTO", label, meta=tree.meta)
def label(self, tree):
name = tree.children[0]
if name in self.alloc.labels:
return self.alloc.labels.index(name)
raise CompileError(f"label `{name}' is undeclared", tree.meta)
def variable(self, tree): def variable(self, tree):
name = tree.children[0] name = tree.children[0]
return self.alloc.variables.index(name) - 30000000 return self.alloc.variables.index(name) - 30000000

View File

@ -1,232 +0,0 @@
#! /usr/bin/python3
import sys
import os
import yaml
import json
from struct import unpack
import disasm_script
def disassemble(bytes, offset, midx, symbol_map = {}, map_name = "map"):
out = ""
found_data = False
while len(midx) > 0:
struct = midx.pop(0)
name = struct["name"]
if name == "Script_Main": name = f"M(Main)"
#print(f"{offset:X} ({name}, start = {struct['start']:X}, len = {struct['length']:X})")
if struct["start"] == offset:
found_data = True
if struct["start"] != offset:
# end of data / padding
break
# format struct
if struct["type"].startswith("Script"):
pos = bytes.tell()
try:
out += disasm_script.ScriptDSLDisassembler(bytes, f"M({name})", symbol_map).disassemble()
except disasm_script.UnsupportedScript as e:
print(f"Unable to use DSL for {struct['name']}: {e}")
bytes.seek(pos)
out += disasm_script.ScriptDisassembler(bytes, f"M({name})", symbol_map).disassemble()
elif struct["type"] == "Padding":
# nops at end of file
bytes.seek(offset % 4, 1)
return out
elif struct["type"] == "EntryList":
out += f"EntryList M(entryList) = {{"
for i in range(0, struct["length"], 4 * 4):
x,y,z,yaw = unpack(">ffff", bytes.read(4 * 4))
out += f"\n {{ {x}f, {y}f, {z}f, {yaw}f }},"
out += f"\n}};\n"
elif struct["type"] == "Header":
out += f"MapConfig M(config) = {{\n"
bytes.read(0x10)
main,entry_list,entry_count = unpack(">IIi", bytes.read(4 * 3))
out += f" .main = M(Main),\n"
out += f" .entryList = M(entryList),\n"
out += f" .entryCount = ENTRY_COUNT(M(entryList)),\n"
bytes.read(0x1C)
bg,tattle = unpack(">II", bytes.read(4 * 2))
if bg == 0x80200000:
out += f" .background = &gBackgroundImage,\n"
elif bg != 0:
raise Exception(f"unknown MapConfig background {bg:X}")
out += f" .tattle = 0x{tattle:X},\n"
out += f"}};\n"
elif struct["type"] == "ASCII":
string_data = bytes.read(struct["length"]).decode("ascii")
# strip null terminator(s)
while string_data[-1] == "\0":
string_data = string_data[:-1]
string_literal = json.dumps(string_data)
out += f"const char M({struct['name']})[] = {string_literal};"
else: # unknown type of struct
out += f"s32 M({name})[] = {{"
for i in range(0, struct["length"], 4):
if (i % 0x20) == 0:
out += f"\n "
word = int.from_bytes(bytes.read(4), byteorder="big")
if word in symbol_map:
out += f" {symbol_map[word]},"
else:
out += f" 0x{word:08X},"
out += f"\n}};\n"
out += "\n"
elif found_data:
if struct["type"] != "Padding":
# put struct back on list
midx.insert(0, struct)
# nops at end of file
bytes.seek(offset % 4, 1)
return out
if struct["type"] != "Function" and not struct["type"] == "Padding" and not (struct["type"] == "Missing" and not found_data):
offset += struct["length"]
# end of data
return out
def parse_midx(file, prefix = ""):
structs = []
for line in file.readlines():
s = line.split("#")
if len(s) == 5:
if s[0] == "$Start": continue
if s[0] == "$End": continue
structs.append({
"name": prefix + name_struct(s[0]),
"type": s[1],
"start": int(s[2], 16),
"vaddr": int(s[3], 16),
"length": int(s[4], 16),
"end": int(s[2], 16) + int(s[4], 16),
})
elif "Missing" in s:
start = int(s[1], 16)
end = int(s[2], 16)
vaddr = start + 0x80240000
structs.append({
"name": f"{prefix}unk_missing_{vaddr:X}",
"type": "Missing",
"start": start,
"vaddr": vaddr,
"length": end - start,
"end": end,
})
elif "Padding" in s:
start = int(s[1], 16)
end = int(s[2], 16)
vaddr = start + 0x80240000
structs.append({
"name": f"{prefix}__padding__",
"type": "Padding",
"start": start,
"vaddr": vaddr,
"length": end - start,
"end": end,
})
structs.sort(key=lambda s: s["start"])
return structs
def name_struct(s):
s = s[1:].replace("???", "unk")
# use ThisCase for scripts
if s.startswith("Script_"):
s = s[7].upper() + s[8:]
# if `s` is hex, prefix it with Script_ again
try:
int(s, 16)
return "Script_" + s
except Exception:
pass
if s.startswith("Main"):
return "Main"
return s
if s.startswith("ASCII"):
return s
return s[0].lower() + s[1:]
if __name__ == "__main__":
if len(sys.argv) == 1:
print("usage: ./disasm_map.py <file.midx>")
print("Converts split map data into C files using a .midx file from Star Rod.")
exit()
map_name = os.path.splitext(os.path.basename(sys.argv[1]))[0]
area_name = "area_" + map_name.split("_")[0]
if len(area_name) > 8:
area_name = area_name[:8]
with open(sys.argv[1], "r") as f:
midx = parse_midx(f)
symbol_map = {}
for struct in midx:
symbol_map[struct["vaddr"]] = "M(" + struct["name"] + ")"
bin_dir = f"bin/world/{area_name}/{map_name}"
src_dir = f"src/world/{area_name}/{map_name}"
splits = []
rom_start = 0
with open(os.path.join(os.path.dirname(__file__), "splat.yaml")) as splat:
splat = yaml.safe_load(splat)
for segment in splat["segments"]:
if type(segment) == dict and segment.get("name") == f"world/{area_name}/{map_name}/":
rom_start = segment.get("start", 0)
splits = segment.get("files", [])
continue
if len(splits) == 0:
print(f"unable to find {map_name} in splat.yaml")
exit(1)
# advance to the EntryList (start of data)
while midx[0]["type"] != "EntryList":
midx.pop(0)
for split in splits:
rom_addr = split[0]
filetype = split[1]
if filetype == "bin":
with open(f"{bin_dir}/{rom_addr:X}.bin", "rb") as bytes:
print(f"Disassembling {rom_addr:X}")
disasm = disassemble(bytes, rom_addr - rom_start, midx, symbol_map, map_name)
if len(disasm.strip()) > 0:
with open(f"{src_dir}/{rom_addr:X}.bin.c", "w") as f:
f.write(f'#include "{map_name}.h"\n\n')
f.write(disasm.rstrip() + "\n")

View File

@ -433,11 +433,11 @@ class ScriptDSLDisassembler(ScriptDisassembler):
self.write_line("}") self.write_line("}")
if opcode == 0x01: if opcode == 0x01:
if self.out.endswith("return\n"): if self.out.endswith("return;\n"):
# implicit return; break # implicit return; break
self.out = self.out[:-7].rstrip() + "\n" self.out = self.out[:-8].rstrip() + "\n"
else: else:
self.write_line("break") self.write_line("break;")
self.indent -= 1 self.indent -= 1
@ -446,7 +446,10 @@ class ScriptDSLDisassembler(ScriptDisassembler):
self.done = True self.done = True
elif opcode == 0x02: self.write_line(f"return;") elif opcode == 0x02: self.write_line(f"return;")
elif opcode == 0x03: self.write_line(f"{self.var(argv[0])}:") elif opcode == 0x03:
self.indent -= 1
self.write_line(f"{self.var(argv[0])}:")
self.indent += 1
elif opcode == 0x04: self.write_line(f"goto {self.var(argv[0])};") elif opcode == 0x04: self.write_line(f"goto {self.var(argv[0])};")
elif opcode == 0x05: elif opcode == 0x05:
if argv[0] == 0: if argv[0] == 0:
@ -479,7 +482,10 @@ class ScriptDSLDisassembler(ScriptDisassembler):
self.write_line(f"if ({self.var(argv[0])} >= {self.var(argv[1])}) {{") self.write_line(f"if ({self.var(argv[0])} >= {self.var(argv[1])}) {{")
self.indent += 1 self.indent += 1
elif opcode == 0x10: elif opcode == 0x10:
self.write_line(f"if ({self.var(argv[0])} ? {self.var(argv[1])}) {{") self.write_line(f"if ({self.var(argv[0])} & {self.var(argv[1])}) {{")
self.indent += 1
elif opcode == 0x11:
self.write_line(f"if ({self.var(argv[0])} !& {self.var(argv[1])}) {{")
self.indent += 1 self.indent += 1
elif opcode == 0x12: elif opcode == 0x12:
self.indent -= 1 self.indent -= 1
@ -595,7 +601,7 @@ class ScriptDSLDisassembler(ScriptDisassembler):
elif opcode == 0x42: self.write_line(f"{self.var(argv[0])} |=c {argv[1]:X};") elif opcode == 0x42: self.write_line(f"{self.var(argv[0])} |=c {argv[1]:X};")
elif opcode == 0x43: elif opcode == 0x43:
argv_str = ", ".join(self.var(arg) for arg in argv[1:]) argv_str = ", ".join(self.var(arg) for arg in argv[1:])
self.write_line(f"{self.addr_ref(argv[0])}({argv_str})") self.write_line(f"{self.addr_ref(argv[0])}({argv_str});")
elif opcode == 0x44: self.write_line(f"spawn {self.addr_ref(argv[0])};") elif opcode == 0x44: self.write_line(f"spawn {self.addr_ref(argv[0])};")
elif opcode == 0x45: self.write_line(f"{self.var(argv[1])} = spawn {self.addr_ref(argv[0])};") elif opcode == 0x45: self.write_line(f"{self.var(argv[1])} = spawn {self.addr_ref(argv[0])};")
elif opcode == 0x46: self.write_line(f"await {self.addr_ref(argv[0])};") elif opcode == 0x46: self.write_line(f"await {self.addr_ref(argv[0])};")

View File

@ -938,7 +938,8 @@ segments:
- [0x431660, c] - [0x431660, c]
- [0x4318D0, c] - [0x4318D0, c]
- [0x431B80, .data, battle/area_kmr_part_1/battles] - [0x431B80, .data, battle/area_kmr_part_1/battles]
- [0x431FB0, bin, battle/area_kmr_part_1/goomba] - [0x431FB0, .data, battle/actor/goomba]
- [0x433970, bin]
- [0x4398A0, .rodata, battle/area_kmr_part_1/battles] - [0x4398A0, .rodata, battle/area_kmr_part_1/battles]
- [0x439984, bin] - [0x439984, bin]
- name: battle/area_kmr_part_2/ - name: battle/area_kmr_part_2/

198
tools/star_rod_idx_to_c.py Executable file
View File

@ -0,0 +1,198 @@
#! /usr/bin/python3
import sys
import os
import yaml
import json
from struct import unpack
import argparse
import disasm_script
DIR = os.path.dirname(__file__)
def disassemble(bytes, midx, symbol_map={}, comments=True, romstart=0):
out = ""
entry_list_name = None
main_script_name = None
while len(midx) > 0:
struct = midx.pop(0)
name = struct["name"]
if comments:
out += f"// {romstart+struct['start']:X}-{romstart+struct['end']:X} (VRAM: {struct['vaddr']:X})\n"
# format struct
if struct["type"].startswith("Script"):
if struct["type"] == "Script_Main":
main_script_name = name
pos = bytes.tell()
try:
out += disasm_script.ScriptDSLDisassembler(bytes, name, symbol_map).disassemble()
except disasm_script.UnsupportedScript as e:
out += f"// Unable to use DSL: {e}\n"
bytes.seek(pos)
out += disasm_script.ScriptDisassembler(bytes, name, symbol_map).disassemble()
elif struct["type"] == "EntryList":
entry_list_name = name
out += f"EntryList {name} = {{"
for i in range(0, struct["length"], 4 * 4):
x,y,z,yaw = unpack(">ffff", bytes.read(4 * 4))
out += f"\n {{ {x}f, {y}f, {z}f, {yaw}f }},"
out += f"\n}};\n"
elif struct["type"] == "Header":
out += f"MapConfig {name} = {{\n"
bytes.read(0x10)
main,entry_list,entry_count = unpack(">IIi", bytes.read(4 * 3))
out += f" .main = {main_script_name},\n"
out += f" .entryList = {entry_list_name},\n"
out += f" .entryCount = ENTRY_COUNT({entry_list_name}),\n"
bytes.read(0x1C)
bg,tattle = unpack(">II", bytes.read(4 * 2))
if bg == 0x80200000:
out += f" .background = &gBackgroundImage,\n"
elif bg != 0:
raise Exception(f"unknown MapConfig background {bg:X}")
out += f" .tattle = 0x{tattle:X},\n"
out += f"}};\n"
elif struct["type"] == "ASCII":
string_data = bytes.read(struct["length"]).decode("ascii")
# strip null terminator(s)
while string_data[-1] == "\0":
string_data = string_data[:-1]
string_literal = json.dumps(string_data)
out += f"const char {struct['name']}[] = {string_literal};\n"
elif struct["type"].startswith("Function"):
bytes.read(struct["length"])
out += f"// function: {name}\n"
elif struct["type"] == "FloatTable":
out += f"f32 {name}[] = {{"
for i in range(0, struct["length"], 4):
if (i % 0x20) == 0:
out += f"\n "
word = unpack(">f", bytes.read(4))[0]
out += " %ff," % word
out += f"\n}};\n"
else: # unknown type of struct
out += f"s32 {name}[] = {{"
for i in range(0, struct["length"], 4):
if (i % 0x20) == 0:
out += f"\n "
word = int.from_bytes(bytes.read(4), byteorder="big")
if word in symbol_map:
out += f" {symbol_map[word]},"
else:
out += f" 0x{word:08X},"
out += f"\n}};\n"
out += "\n"
# end of data
return out
def parse_midx(file, prefix = ""):
structs = []
for line in file.readlines():
s = line.split("#")
if len(s) == 5:
if s[0] == "$Start": continue
if s[0] == "$End": continue
structs.append({
"name": prefix + name_struct(s[0]),
"type": s[1],
"start": int(s[2], 16),
"vaddr": int(s[3], 16),
"length": int(s[4], 16),
"end": int(s[2], 16) + int(s[4], 16),
})
elif "Missing" in s:
start = int(s[1], 16)
end = int(s[2], 16)
vaddr = start + 0x80240000
structs.append({
"name": f"{prefix}unk_missing_{vaddr:X}",
"type": "Missing",
"start": start,
"vaddr": vaddr,
"length": end - start,
"end": end,
})
elif "Padding" in s:
start = int(s[1], 16)
end = int(s[2], 16)
vaddr = start + 0x80240000
structs.append({
"name": f"{prefix}pad_{start:X}",
"type": "Padding",
"start": start,
"vaddr": vaddr,
"length": end - start,
"end": end,
})
structs.sort(key=lambda s: s["start"])
return structs
def name_struct(s):
s = s[1:].replace("???", "unk")
"""
# use ThisCase for scripts
if s.startswith("Script_"):
s = s[7].upper() + s[8:]
# if `s` is hex, prefix it with Script_ again
try:
int(s, 16)
return "Script_" + s
except Exception:
pass
if s.startswith("Main"):
return "Main"
return s
"""
if s.startswith("ASCII"):
return s
return s[0].lower() + s[1:]
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Converts split data to C using a Star Rod idx file")
parser.add_argument("idxfile", help="Input .*idx file from Star Rod dump")
parser.add_argument("offset", help="Starting ROM offset")
parser.add_argument("--comments", action="store_true", help="Write offset/vaddr comments")
args = parser.parse_args()
with open(args.idxfile, "r") as f:
midx = parse_midx(f)
symbol_map = {}
for struct in midx:
symbol_map[struct["vaddr"]] = struct["name"]
with open(os.path.join(DIR, "../baserom.z64"), "rb") as romfile:
romfile.seek(eval(args.offset))
disasm = disassemble(romfile, midx, symbol_map, args.comments, eval(args.offset))
print(disasm.rstrip())

View File

@ -61,3 +61,4 @@ func_802AA0D8 = 0x802AA0D8;
func_802AB330 = 0x802AB330; func_802AB330 = 0x802AB330;
func_802AB338 = 0x802AB338; func_802AB338 = 0x802AB338;
func_802B71E8 = 0x802B71E8; func_802B71E8 = 0x802B71E8;
__osInitialize_common = 0x8006A708;

View File

@ -43,6 +43,10 @@ D_800A0960 = 0x800A0960;
D_800A0963 = 0x800A0963; D_800A0963 = 0x800A0963;
D_800A0964 = 0x800A0964; D_800A0964 = 0x800A0964;
D_800A4270 = 0x800A4270;
D_800A4420 = 0x800A4420;
D_800B8590 = 0x800B8590;
D_800E92D8 = 0x800E92D8; D_800E92D8 = 0x800E92D8;
gPartnerAnimations = 0x800F8348; gPartnerAnimations = 0x800F8348;
@ -116,6 +120,7 @@ nuGfxZBuffer = 0x8009A5DC;
nuGfxDisplay = 0x8009A5F8; nuGfxDisplay = 0x8009A5F8;
gGameState = 0x8009A600; gGameState = 0x8009A600;
D_8009A610 = 0x8009A610; D_8009A610 = 0x8009A610;
D_8009A630 = 0x8009A630;
nuGfxTaskSpool = 0x8009A618; nuGfxTaskSpool = 0x8009A618;
carthandle = 0x8009A638; carthandle = 0x8009A638;
@ -330,6 +335,10 @@ DoShockHit = 0x8029A6FC;
DoSleepHit = 0x802945E0; DoSleepHit = 0x802945E0;
DoSpinSmashHit = 0x8029B998; DoSpinSmashHit = 0x8029B998;
DoStopHit = 0x80294650; DoStopHit = 0x80294650;
DoAirLift = 0x8029C37C;
DoBlowAway = 0x8029C4A8;
DoBurnHit = 0x8029A0D0;
DoDeath = 0x8029AEC0;
ShakeCam1 = 0x802D9CB0; ShakeCam1 = 0x802D9CB0;
ShakeCamX = 0x802D9CE8; ShakeCamX = 0x802D9CE8;
@ -360,3 +369,6 @@ D_DE003E00 = 0xDE003E00;
D_C1F06370 = 0xC1F06370; D_C1F06370 = 0xC1F06370;
D_DE001F00 = 0xDE001F00; D_DE001F00 = 0xDE001F00;
D_DE007C00 = 0xDE007C00; D_DE007C00 = 0xDE007C00;
paragoomba = 0x8021CD00;
spiked_goomba = 0x8021B0AC;

View File

@ -546,7 +546,6 @@ D_8009A61C = 0x8009A61C;
D_8009A620 = 0x8009A620; D_8009A620 = 0x8009A620;
D_8009A628 = 0x8009A628; D_8009A628 = 0x8009A628;
D_8009A62C = 0x8009A62C; D_8009A62C = 0x8009A62C;
D_8009A630 = 0x8009A630;
D_8009A634 = 0x8009A634; D_8009A634 = 0x8009A634;
D_8009A63C = 0x8009A63C; D_8009A63C = 0x8009A63C;
D_8009A640 = 0x8009A640; D_8009A640 = 0x8009A640;
@ -654,8 +653,6 @@ D_800A425C = 0x800A425C;
D_800A4260 = 0x800A4260; D_800A4260 = 0x800A4260;
D_800A4264 = 0x800A4264; D_800A4264 = 0x800A4264;
D_800A4268 = 0x800A4268; D_800A4268 = 0x800A4268;
D_800A4270 = 0x800A4270;
D_800A4420 = 0x800A4420;
D_800AC6B0 = 0x800AC6B0; D_800AC6B0 = 0x800AC6B0;
D_800AE6D0 = 0x800AE6D0; D_800AE6D0 = 0x800AE6D0;
D_800AE6D4 = 0x800AE6D4; D_800AE6D4 = 0x800AE6D4;
@ -761,7 +758,6 @@ D_800B451E = 0x800B451E;
D_800B451F = 0x800B451F; D_800B451F = 0x800B451F;
D_800B4520 = 0x800B4520; D_800B4520 = 0x800B4520;
D_800B6590 = 0x800B6590; D_800B6590 = 0x800B6590;
D_800B8590 = 0x800B8590;
D_800B91A0 = 0x800B91A0; D_800B91A0 = 0x800B91A0;
D_800B91D0 = 0x800B91D0; D_800B91D0 = 0x800B91D0;
D_800D91D0 = 0x800D91D0; D_800D91D0 = 0x800D91D0;