diff --git a/.vscode/extensions.json b/.vscode/extensions.json index a40435c7fa..8e081bdb4d 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,7 +1,7 @@ { "recommendations": [ "ms-vscode.cpptools", - "nanaian.vscode-star-rod", + "nanaian.papermario", "notskm.clang-tidy", "EditorConfig.EditorConfig", ], diff --git a/Dockerfile b/Dockerfile index f787524548..f6a908cfc2 100644 --- a/Dockerfile +++ b/Dockerfile @@ -2,7 +2,11 @@ FROM ubuntu:20.04 as build 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 WORKDIR /papermario diff --git a/Makefile b/Makefile index ecc6a12958..e09c5c697c 100644 --- a/Makefile +++ b/Makefile @@ -80,7 +80,7 @@ CC=tools/$(OS)/cc1 ### 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 OLDASFLAGS := -EB -Iinclude -G 0 CFLAGS := -O2 -quiet -G 0 -mcpu=vr4300 -mfix4300 -mips3 -mgp32 -mfp32 -Wimplicit -Wuninitialized -Wshadow diff --git a/README.md b/README.md index 03882ee791..d599574a5f 100644 --- a/README.md +++ b/README.md @@ -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! +##### 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 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`. diff --git a/asm/nonmatchings/os/code_39cb0_len_100/boot_idle.s b/asm/nonmatchings/os/code_39cb0_len_100/boot_idle.s index 1545a44358..531b2d3fa9 100644 --- a/asm/nonmatchings/os/code_39cb0_len_100/boot_idle.s +++ b/asm/nonmatchings/os/code_39cb0_len_100/boot_idle.s @@ -37,11 +37,11 @@ glabel boot_idle /* 39D88 8005E988 8C42A630 */ lw $v0, %lo(D_8009A630)($v0) .L8005E98C: /* 39D8C 8005E98C 1040FFFF */ beqz $v0, .L8005E98C -/* 39D90 8005E990 00000000 */ nop +/* 39D90 8005E990 00000000 */ nop /* 39D94 8005E994 0040F809 */ jalr $v0 -/* 39D98 8005E998 00000000 */ nop +/* 39D98 8005E998 00000000 */ nop /* 39D9C 8005E99C 08017A61 */ j .L8005E984 -/* 39DA0 8005E9A0 00000000 */ nop -/* 39DA4 8005E9A4 00000000 */ nop -/* 39DA8 8005E9A8 00000000 */ nop -/* 39DAC 8005E9AC 00000000 */ nop +/* 39DA0 8005E9A0 00000000 */ nop +/* 39DA4 8005E9A4 00000000 */ nop +/* 39DA8 8005E9A8 00000000 */ nop +/* 39DAC 8005E9AC 00000000 */ nop diff --git a/asm/nonmatchings/os/code_39cb0_len_100/nuBoot.s b/asm/nonmatchings/os/code_39cb0_len_100/nuBoot.s deleted file mode 100644 index 311c616db4..0000000000 --- a/asm/nonmatchings/os/code_39cb0_len_100/nuBoot.s +++ /dev/null @@ -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 diff --git a/include/common_structs.h b/include/common_structs.h index 5b50a9a254..f6ba5b430e 100644 --- a/include/common_structs.h +++ b/include/common_structs.h @@ -21,6 +21,12 @@ typedef struct Vec2bu { /* 0x01 */ u8 y; } Vec2bu; // size = 0x02 +typedef struct Vec3b { + /* 0x00 */ s8 x; + /* 0x01 */ s8 y; + /* 0x02 */ s8 z; +} Vec3b; // size = 0x03 + typedef struct Vec3f { /* 0x00 */ f32 x; /* 0x04 */ f32 y; @@ -658,31 +664,6 @@ typedef struct TextureHeader { /* 0x2F */ u8 filtering; } 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 { /* 0x00 */ s32 moveNameID; /* 0x04 */ s32 flags; @@ -1045,7 +1026,7 @@ typedef struct ActorPartMovement { typedef struct ActorPart { /* 0x00 */ s32 flags; /* 0x04 */ s32 targetFlags; /* initialized to 0 */ - /* 0x08 */ struct StaticActorPart* staticData; + /* 0x08 */ struct ActorPartDesc* staticData; /* 0x0C */ struct ActorPart* nextPart; /* 0x10 */ struct ActorPartMovement* movement; /* 0x14 */ Vec3s partOffset; @@ -1239,7 +1220,7 @@ typedef struct ActorFlyPos { typedef struct Actor { /* 0x000 */ s32 flags; /* 0x004 */ char unk_04[4]; - /* 0x008 */ struct StaticActorData* staticActorData; + /* 0x008 */ struct ActorDesc* staticActorData; /* 0x00C */ ActorMovePos movePos; /* 0x030 */ char unk_30[24]; /* 0x048 */ f32 jumpAccel; @@ -1361,19 +1342,6 @@ typedef struct Actor { /* 0x440 */ struct MenuIcon* ptrDefuffIcon; } 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 { /* 0x00 */ s8 name[32]; /* 0x20 */ s16 auxW; diff --git a/include/functions.h b/include/functions.h index 05371b45ef..e3fddee28b 100644 --- a/include/functions.h +++ b/include/functions.h @@ -5,6 +5,11 @@ #include "common_structs.h" #include "enums.h" + +void nuBoot(void); +void boot_idle(void); +void boot_main(void); + void osCleanupThread(void); s32 heap_malloc(s32 size); diff --git a/include/macros.h b/include/macros.h index 5ceef36488..67fb52c759 100644 --- a/include/macros.h +++ b/include/macros.h @@ -12,6 +12,8 @@ #define ALIGN16(val) (((val) + 0xF) & ~0xF) +#define N(sym) NS(NAMESPACE, sym) + #define ARRAY_COUNT(arr) (s32)(sizeof(arr) / sizeof(arr[0])) #define ARRAY_COUNTU(arr) (u32)(sizeof(arr) / sizeof(arr[0])) diff --git a/include/map.h b/include/map.h index 4405156225..03e9cb971e 100644 --- a/include/map.h +++ b/include/map.h @@ -7,8 +7,6 @@ // TODO: consider moving Npc here -#define N(sym) NS(NAMESPACE, sym) - #define ENTRY_COUNT(entryList) (sizeof(entryList) / sizeof(Vec4f)) typedef Vec4f EntryList[0]; diff --git a/include/variables.h b/include/variables.h index 031cf2b4d2..762a4acb3f 100644 --- a/include/variables.h +++ b/include/variables.h @@ -246,4 +246,9 @@ extern s16 gMusicTargetVolume; extern MusicPlayer gMusicPlayers[4]; extern MusicPlayer D_8014F6F0; +// OS +extern OSThread D_800A4270; // idle thread, id 1 +extern OSThread D_800A4420; // id 3 +extern s32 D_800B8590; + #endif diff --git a/src/battle/actor/goomba.c b/src/battle/actor/goomba.c new file mode 100644 index 0000000000..1ed7f9d1e1 --- /dev/null +++ b/src/battle/actor/goomba.c @@ -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); +}); + diff --git a/src/battle/actor/goomba.h b/src/battle/actor/goomba.h index 04e018590b..3845021230 100644 --- a/src/battle/actor/goomba.h +++ b/src/battle/actor/goomba.h @@ -1,7 +1,9 @@ #ifndef _BATTLE_ACTOR_GOOMBA_ #define _BATTLE_ACTOR_GOOMBA_ -// TODO: disasm -#define goomba 0x802196EC +#include "common.h" +#include "battle/battle.h" + +ActorDesc goomba; #endif diff --git a/src/battle/actor/paragoomba.h b/src/battle/actor/paragoomba.h index 0f90efadec..1bea71359c 100644 --- a/src/battle/actor/paragoomba.h +++ b/src/battle/actor/paragoomba.h @@ -1,7 +1,9 @@ #ifndef _BATTLE_ACTOR_PARAGOOMBA_ #define _BATTLE_ACTOR_PARAGOOMBA_ +#include "battle/battle.h" + // TODO: disasm -#define paragoomba 0x8021CD00 +extern ActorDesc paragoomba; #endif diff --git a/src/battle/actor/spiked_goomba.h b/src/battle/actor/spiked_goomba.h index 0c1b89f363..f4a8bb9eb1 100644 --- a/src/battle/actor/spiked_goomba.h +++ b/src/battle/actor/spiked_goomba.h @@ -1,7 +1,9 @@ #ifndef _BATTLE_ACTOR_SPIKED_GOOMBA_ #define _BATTLE_ACTOR_SPIKED_GOOMBA_ +#include "battle/battle.h" + // TODO: disasm -#define spikedGoomba 0x8021B0AC +extern ActorDesc spiked_goomba; #endif diff --git a/src/battle/area_arn/4FF010.c b/src/battle/area_arn/4FF010.c index 2a0886a142..656a16afb3 100644 --- a/src/battle/area_arn/4FF010.c +++ b/src/battle/area_arn/4FF010.c @@ -1,4 +1,4 @@ -#include "common.h" +#include "battle/battle.h" #define NAMESPACE b_area_arn diff --git a/src/battle/area_kmr_part_1/4309A0.c b/src/battle/area_kmr_part_1/4309A0.c index 8148291386..b107cb429e 100644 --- a/src/battle/area_kmr_part_1/4309A0.c +++ b/src/battle/area_kmr_part_1/4309A0.c @@ -2,7 +2,7 @@ #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); diff --git a/src/battle/area_kmr_part_1/battles.c b/src/battle/area_kmr_part_1/battles.c index 19d32736af..3b9c3ae854 100644 --- a/src/battle/area_kmr_part_1/battles.c +++ b/src/battle/area_kmr_part_1/battles.c @@ -8,66 +8,66 @@ #define NAMESPACE b_area_kmr_part_1 Formation formation0 = { - { goomba, .position = 1, .priority = 10 }, + { &goomba, .position = 1, .priority = 10 }, }; Formation formation1 = { - { goomba, .position = 1, .priority = 10 }, - { goomba, .position = 2, .priority = 9 }, + { &goomba, .position = 1, .priority = 10 }, + { &goomba, .position = 2, .priority = 9 }, }; Formation formation2 = { - { goomba, .position = 0, .priority = 10 }, - { goomba, .position = 1, .priority = 9 }, - { goomba, .position = 2, .priority = 8 }, + { &goomba, .position = 0, .priority = 10 }, + { &goomba, .position = 1, .priority = 9 }, + { &goomba, .position = 2, .priority = 8 }, }; Formation formation3 = { - { goomba, .position = 1, .priority = 10 }, - { paragoomba, .position = 6, .priority = 9 }, + { &goomba, .position = 1, .priority = 10 }, + { ¶goomba, .position = 6, .priority = 9 }, }; Formation formation4 = { - { goomba, .position = 0, .priority = 10 }, - { goomba, .position = 1, .priority = 9 }, - { goomba, .position = 2, .priority = 8 }, - { goomba, .position = 3, .priority = 7 }, + { &goomba, .position = 0, .priority = 10 }, + { &goomba, .position = 1, .priority = 9 }, + { &goomba, .position = 2, .priority = 8 }, + { &goomba, .position = 3, .priority = 7 }, }; Formation formation5 = { - { goomba, .position = 1, .priority = 10 }, - { spikedGoomba, .position = 2, .priority = 9 }, + { &goomba, .position = 1, .priority = 10 }, + { &spiked_goomba, .position = 2, .priority = 9 }, }; Formation formation6 = { - { goomba, .position = 0, .priority = 10 }, - { paragoomba, .position = 5, .priority = 9 }, - { goomba, .position = 2, .priority = 8 }, - { paragoomba, .position = 7, .priority = 7 }, + { &goomba, .position = 0, .priority = 10 }, + { ¶goomba, .position = 5, .priority = 9 }, + { &goomba, .position = 2, .priority = 8 }, + { ¶goomba, .position = 7, .priority = 7 }, }; Formation formation7 = { - { paragoomba, .position = 5, .priority = 10 }, + { ¶goomba, .position = 5, .priority = 10 }, }; Formation formation8 = { - { paragoomba, .position = 5, .priority = 10 }, - { paragoomba, .position = 6, .priority = 9 }, + { ¶goomba, .position = 5, .priority = 10 }, + { ¶goomba, .position = 6, .priority = 9 }, }; Formation formation9 = { - { paragoomba, .position = 4, .priority = 10 }, - { paragoomba, .position = 5, .priority = 9 }, - { paragoomba, .position = 6, .priority = 8 }, + { ¶goomba, .position = 4, .priority = 10 }, + { ¶goomba, .position = 5, .priority = 9 }, + { ¶goomba, .position = 6, .priority = 8 }, }; Formation formation10 = { - { spikedGoomba, .position = 1, .priority = 10 }, + { &spiked_goomba, .position = 1, .priority = 10 }, }; Formation formation11 = { - { spikedGoomba, .position = 1, .priority = 10 }, - { goomba, .position = 2, .priority = 9 }, + { &spiked_goomba, .position = 1, .priority = 10 }, + { &goomba, .position = 2, .priority = 9 }, }; BattleList area_kmr_part_1_battles = { diff --git a/src/battle/battle.h b/src/battle/battle.h index 2d97adfa11..1496cb0600 100644 --- a/src/battle/battle.h +++ b/src/battle/battle.h @@ -3,6 +3,31 @@ #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 { /* 0x00 */ const char* texture; /* 0x04 */ const char* shape; @@ -23,7 +48,7 @@ typedef struct StageListRow { } StageList[]; // size = 0x08 * n typedef struct FormationRow { - /* 0x00 */ StaticActorData* actor; + /* 0x00 */ ActorDesc* actor; /* 0x04 */ s32 position; ///< Home position. May also be a `Vector3*`. /* 0x08 */ s32 priority; ///< Actors with higher priority values take their turn first. /* 0x0C */ s32 var0; @@ -45,4 +70,24 @@ typedef struct Battle { // 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 diff --git a/src/code_190B20.c b/src/code_190B20.c index d7e1ab9de9..79a4910d82 100644 --- a/src/code_190B20.c +++ b/src/code_190B20.c @@ -1,4 +1,4 @@ -#include "common.h" +#include "battle/battle.h" 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); -typedef struct { - Element element; - s32 defense; -} DefenseTableEntry; - s32 lookup_defense(DefenseTableEntry* defenseTable, Element elementKey) { DefenseTableEntry* row; s32 normalDefense = 0; diff --git a/src/code_197F40.c b/src/code_197F40.c index 0cc624d125..29db736be7 100644 --- a/src/code_197F40.c +++ b/src/code_197F40.c @@ -1,4 +1,5 @@ #include "common.h" +#include "battle/battle.h" s32 count_targets(Actor* actor, s32 targetHomeIndex, s32 targetSelectionFlags) { BattleStatus* battleStatus = BATTLE_STATUS; diff --git a/src/code_1A5830.c b/src/code_1A5830.c index f9ce9b8779..29ffdf7a7a 100644 --- a/src/code_1A5830.c +++ b/src/code_1A5830.c @@ -1,4 +1,5 @@ #include "common.h" +#include "battle/battle.h" void dispatch_event_actor(Actor* actor, Event event); diff --git a/src/code_1AC760.c b/src/code_1AC760.c index 4a27ff9f56..2059bc8e35 100644 --- a/src/code_1AC760.c +++ b/src/code_1AC760.c @@ -1,4 +1,5 @@ #include "common.h" +#include "battle/battle.h" INCLUDE_ASM(s32, "code_1AC760", dispatch_event_partner); diff --git a/src/common/UnkBattleFunc1.inc.c b/src/common/UnkBattleFunc1.inc.c index 551b12c27f..0e6879c47c 100644 --- a/src/common/UnkBattleFunc1.inc.c +++ b/src/common/UnkBattleFunc1.inc.c @@ -1,5 +1,4 @@ -#include "common.h" -#include "map.h" +#include "battle/battle.h" ApiStatus N(UnkBattleFunc1)(ScriptInstance* script, s32 isInitialCall) { Bytecode* args = script->ptrReadPos; diff --git a/src/os/code_39cb0_len_100.c b/src/os/code_39cb0_len_100.c index 32a49c67c6..f14ff4d712 100644 --- a/src/os/code_39cb0_len_100.c +++ b/src/os/code_39cb0_len_100.c @@ -1,5 +1,36 @@ #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 diff --git a/tools/compile_dsl_macros.py b/tools/compile_dsl_macros.py index 14b7d01aac..379c27eb4f 100755 --- a/tools/compile_dsl_macros.py +++ b/tools/compile_dsl_macros.py @@ -77,7 +77,8 @@ script_parser = Lark(r""" | "<" -> cond_op_lt | ">=" -> cond_op_ge | "<=" -> cond_op_le - | "?" -> cond_op_flag + | "&" -> cond_op_flag + | "!&" -> cond_op_not_flag match_stmt: "match" 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) 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.insert(label_idx, name) + self.labels[label_idx] = name except ValueError: 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_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_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): expr = tree.children[0] @@ -389,13 +391,21 @@ class Compile(Transformer): return [tree.children[0], *tree.children[1]] 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): if len(tree.children) == 4: 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")] else: 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] def case_range(self, tree): if len(tree.children) == 4: @@ -583,33 +593,6 @@ class Compile(Transformer): "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): name = tree.children[0] return self.alloc.variables.index(name) - 30000000 diff --git a/tools/disasm_map.py b/tools/disasm_map.py deleted file mode 100755 index d0ffe9b910..0000000000 --- a/tools/disasm_map.py +++ /dev/null @@ -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 ") - 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") diff --git a/tools/disasm_script.py b/tools/disasm_script.py index 9c571b931c..75d58efd81 100755 --- a/tools/disasm_script.py +++ b/tools/disasm_script.py @@ -433,11 +433,11 @@ class ScriptDSLDisassembler(ScriptDisassembler): self.write_line("}") if opcode == 0x01: - if self.out.endswith("return\n"): + if self.out.endswith("return;\n"): # implicit return; break - self.out = self.out[:-7].rstrip() + "\n" + self.out = self.out[:-8].rstrip() + "\n" else: - self.write_line("break") + self.write_line("break;") self.indent -= 1 @@ -446,7 +446,10 @@ class ScriptDSLDisassembler(ScriptDisassembler): self.done = True 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 == 0x05: 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.indent += 1 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 elif opcode == 0x12: 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 == 0x43: 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 == 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])};") diff --git a/tools/splat.yaml b/tools/splat.yaml index 2d7dfdef86..a752334430 100644 --- a/tools/splat.yaml +++ b/tools/splat.yaml @@ -938,7 +938,8 @@ segments: - [0x431660, c] - [0x4318D0, c] - [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] - [0x439984, bin] - name: battle/area_kmr_part_2/ diff --git a/tools/star_rod_idx_to_c.py b/tools/star_rod_idx_to_c.py new file mode 100755 index 0000000000..b1d561a246 --- /dev/null +++ b/tools/star_rod_idx_to_c.py @@ -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()) diff --git a/undefined_funcs.txt b/undefined_funcs.txt index c8189fe23e..53e88de141 100644 --- a/undefined_funcs.txt +++ b/undefined_funcs.txt @@ -61,3 +61,4 @@ func_802AA0D8 = 0x802AA0D8; func_802AB330 = 0x802AB330; func_802AB338 = 0x802AB338; func_802B71E8 = 0x802B71E8; +__osInitialize_common = 0x8006A708; diff --git a/undefined_syms.txt b/undefined_syms.txt index 175b84bf8a..9d439aff3a 100644 --- a/undefined_syms.txt +++ b/undefined_syms.txt @@ -43,6 +43,10 @@ D_800A0960 = 0x800A0960; D_800A0963 = 0x800A0963; D_800A0964 = 0x800A0964; +D_800A4270 = 0x800A4270; +D_800A4420 = 0x800A4420; +D_800B8590 = 0x800B8590; + D_800E92D8 = 0x800E92D8; gPartnerAnimations = 0x800F8348; @@ -116,6 +120,7 @@ nuGfxZBuffer = 0x8009A5DC; nuGfxDisplay = 0x8009A5F8; gGameState = 0x8009A600; D_8009A610 = 0x8009A610; +D_8009A630 = 0x8009A630; nuGfxTaskSpool = 0x8009A618; carthandle = 0x8009A638; @@ -330,6 +335,10 @@ DoShockHit = 0x8029A6FC; DoSleepHit = 0x802945E0; DoSpinSmashHit = 0x8029B998; DoStopHit = 0x80294650; +DoAirLift = 0x8029C37C; +DoBlowAway = 0x8029C4A8; +DoBurnHit = 0x8029A0D0; +DoDeath = 0x8029AEC0; ShakeCam1 = 0x802D9CB0; ShakeCamX = 0x802D9CE8; @@ -360,3 +369,6 @@ D_DE003E00 = 0xDE003E00; D_C1F06370 = 0xC1F06370; D_DE001F00 = 0xDE001F00; D_DE007C00 = 0xDE007C00; + +paragoomba = 0x8021CD00; +spiked_goomba = 0x8021B0AC; diff --git a/undefined_syms_auto.txt b/undefined_syms_auto.txt index a0f9b79cf2..d01d3c733d 100644 --- a/undefined_syms_auto.txt +++ b/undefined_syms_auto.txt @@ -546,7 +546,6 @@ D_8009A61C = 0x8009A61C; D_8009A620 = 0x8009A620; D_8009A628 = 0x8009A628; D_8009A62C = 0x8009A62C; -D_8009A630 = 0x8009A630; D_8009A634 = 0x8009A634; D_8009A63C = 0x8009A63C; D_8009A640 = 0x8009A640; @@ -654,8 +653,6 @@ D_800A425C = 0x800A425C; D_800A4260 = 0x800A4260; D_800A4264 = 0x800A4264; D_800A4268 = 0x800A4268; -D_800A4270 = 0x800A4270; -D_800A4420 = 0x800A4420; D_800AC6B0 = 0x800AC6B0; D_800AE6D0 = 0x800AE6D0; D_800AE6D4 = 0x800AE6D4; @@ -761,7 +758,6 @@ D_800B451E = 0x800B451E; D_800B451F = 0x800B451F; D_800B4520 = 0x800B4520; D_800B6590 = 0x800B6590; -D_800B8590 = 0x800B8590; D_800B91A0 = 0x800B91A0; D_800B91D0 = 0x800B91D0; D_800D91D0 = 0x800D91D0;