diff --git a/include/variables.h b/include/variables.h index 2692283dea..e3b353a074 100644 --- a/include/variables.h +++ b/include/variables.h @@ -81,12 +81,12 @@ extern Gfx gCullBackDList[]; extern Gfx gCullFrontDList[]; extern Gfx gEmptyDL[]; extern u32 gBitFlags[32]; -extern u16 gEquipMasks[4]; -extern u16 gEquipNegMasks[4]; -extern u32 gUpgradeMasks[8]; -extern u8 gEquipShifts[4]; -extern u8 gUpgradeShifts[8]; -extern u16 gUpgradeCapacities[8][4]; +extern u16 gEquipMasks[EQUIP_TYPE_MAX]; +extern u16 gEquipNegMasks[EQUIP_TYPE_MAX]; +extern u32 gUpgradeMasks[UPG_MAX]; +extern u8 gEquipShifts[EQUIP_TYPE_MAX]; +extern u8 gUpgradeShifts[UPG_MAX]; +extern u16 gUpgradeCapacities[UPG_MAX][4]; extern u32 gGsFlagsMasks[4]; extern u32 gGsFlagsShifts[4]; extern void* gItemIcons[0x82]; diff --git a/include/z64item.h b/include/z64item.h index 5b93e87135..3bb1014b41 100644 --- a/include/z64item.h +++ b/include/z64item.h @@ -78,7 +78,8 @@ typedef enum { /* 0x04 */ UPG_WALLET, /* 0x05 */ UPG_BULLET_BAG, /* 0x06 */ UPG_STICKS, - /* 0x07 */ UPG_NUTS + /* 0x07 */ UPG_NUTS, + /* 0x08 */ UPG_MAX } UpgradeType; typedef enum { diff --git a/include/z64save.h b/include/z64save.h index b5756e63b2..ae2d7de2f5 100644 --- a/include/z64save.h +++ b/include/z64save.h @@ -38,6 +38,19 @@ typedef struct { /* 0x08 */ s16 angle; } HorseData; // size = 0x0A +/** + * The respawn mode names refer to the perceived player movement when respawning + * "down": being on ground + * "return": coming from the ground + * "top": coming from the air + */ +typedef enum { + /* 0x00 */ RESPAWN_MODE_DOWN, /* Normal Void Outs */ + /* 0x01 */ RESPAWN_MODE_RETURN, /* Grotto Returnpoints */ + /* 0x02 */ RESPAWN_MODE_TOP, /* Farore's Wind */ + /* 0x03 */ RESPAWN_MODE_MAX +} RespawnMode; + typedef struct { /* 0x00 */ Vec3f pos; /* 0x0C */ s16 yaw; @@ -117,7 +130,7 @@ typedef struct { /* 0x135C */ s32 gameMode; /* 0x1360 */ s32 sceneSetupIndex; /* 0x1364 */ s32 respawnFlag; // "restart_flag" - /* 0x1368 */ RespawnData respawn[3]; // "restart_data" + /* 0x1368 */ RespawnData respawn[RESPAWN_MODE_MAX]; // "restart_data" /* 0x13BC */ f32 entranceSpeed; /* 0x13C0 */ u16 entranceSound; /* 0x13C2 */ char unk_13C2[0x0001]; @@ -175,12 +188,6 @@ typedef struct { /* 0x1424 */ s16 healthAccumulator; } SaveContext; // size = 0x1428 -typedef enum { - /* 0x00 */ RESPAWN_MODE_DOWN, /* Normal Void Outs */ - /* 0x01 */ RESPAWN_MODE_RETURN, /* Grotto Returnpoints */ - /* 0x02 */ RESPAWN_MODE_TOP /* Farore's Wind */ -} RespawnMode; - typedef enum { /* 0x00 */ BTN_ENABLED, /* 0xFF */ BTN_DISABLED = 0xFF diff --git a/src/code/code_80097A00.c b/src/code/code_80097A00.c index 6e624ed173..bf9d205fa1 100644 --- a/src/code/code_80097A00.c +++ b/src/code/code_80097A00.c @@ -11,26 +11,67 @@ u32 gBitFlags[] = { (1 << 24), (1 << 25), (1 << 26), (1 << 27), (1 << 28), (1 << 29), (1 << 30), (1 << 31), }; -u16 gEquipMasks[] = { 0x000F, 0x00F0, 0x0F00, 0xF000 }; -u16 gEquipNegMasks[] = { 0xFFF0, 0xFF0F, 0xF0FF, 0x0FFF }; -u32 gUpgradeMasks[] = { - 0x00000007, 0x00000038, 0x000001C0, 0x00000E00, 0x00003000, 0x0001C000, 0x000E0000, 0x00700000, +u16 gEquipMasks[EQUIP_TYPE_MAX] = { + 0xF << (EQUIP_TYPE_SWORD * 4), // EQUIP_TYPE_SWORD + 0xF << (EQUIP_TYPE_SHIELD * 4), // EQUIP_TYPE_SHIELD + 0xF << (EQUIP_TYPE_TUNIC * 4), // EQUIP_TYPE_TUNIC + 0xF << (EQUIP_TYPE_BOOTS * 4), // EQUIP_TYPE_BOOTS }; -u32 gUpgradeNegMasks[] = { - 0xFFFFFFF8, 0xFFFFFFC7, 0xFFFFFE3F, 0xFFFFF1FF, 0xFFFFCFFF, 0xFFFE3FFF, 0xFFF1FFFF, 0xFF8FFFFF, +u16 gEquipNegMasks[EQUIP_TYPE_MAX] = { + (u16) ~(0xF << (EQUIP_TYPE_SWORD * 4)), // EQUIP_TYPE_SWORD + (u16) ~(0xF << (EQUIP_TYPE_SHIELD * 4)), // EQUIP_TYPE_SHIELD + (u16) ~(0xF << (EQUIP_TYPE_TUNIC * 4)), // EQUIP_TYPE_TUNIC + (u16) ~(0xF << (EQUIP_TYPE_BOOTS * 4)), // EQUIP_TYPE_BOOTS }; -u8 gEquipShifts[] = { 0, 4, 8, 12 }; -u8 gUpgradeShifts[] = { 0, 3, 6, 9, 12, 14, 17, 20 }; -u16 gUpgradeCapacities[][4] = { - { 0, 30, 40, 50 }, // Quivers - { 0, 20, 30, 40 }, // Bomb Bags - { 0, 0, 0, 0 }, // Unused (Scale) - { 0, 0, 0, 0 }, // Unused (Strength) - { 99, 200, 500, 500 }, // Wallets - { 0, 30, 40, 50 }, // Deku Seed Bullet Bags - { 0, 10, 20, 30 }, // Deku Stick Upgrades - { 0, 20, 30, 40 }, // Deku Nut Upgrades +u32 gUpgradeMasks[UPG_MAX] = { + 0x00000007, // UPG_QUIVER + 0x00000038, // UPG_BOMB_BAG + 0x000001C0, // UPG_STRENGTH + 0x00000E00, // UPG_SCALE + 0x00003000, // UPG_WALLET + 0x0001C000, // UPG_BULLET_BAG + 0x000E0000, // UPG_STICKS + 0x00700000, // UPG_NUTS +}; +u32 gUpgradeNegMasks[UPG_MAX] = { + ~0x00000007, // UPG_QUIVER + ~0x00000038, // UPG_BOMB_BAG + ~0x000001C0, // UPG_STRENGTH + ~0x00000E00, // UPG_SCALE + ~0x00003000, // UPG_WALLET + ~0x0001C000, // UPG_BULLET_BAG + ~0x000E0000, // UPG_STICKS + ~0x00700000, // UPG_NUTS +}; + +u8 gEquipShifts[EQUIP_TYPE_MAX] = { + EQUIP_TYPE_SWORD * 4, // EQUIP_TYPE_SWORD + EQUIP_TYPE_SHIELD * 4, // EQUIP_TYPE_SHIELD + EQUIP_TYPE_TUNIC * 4, // EQUIP_TYPE_TUNIC + EQUIP_TYPE_BOOTS * 4, // EQUIP_TYPE_BOOTS +}; + +u8 gUpgradeShifts[UPG_MAX] = { + 0, // UPG_QUIVER + 3, // UPG_BOMB_BAG + 6, // UPG_STRENGTH + 9, // UPG_SCALE + 12, // UPG_WALLET + 14, // UPG_BULLET_BAG + 17, // UPG_STICKS + 20, // UPG_NUTS +}; + +u16 gUpgradeCapacities[UPG_MAX][4] = { + { 0, 30, 40, 50 }, // UPG_QUIVER + { 0, 20, 30, 40 }, // UPG_BOMB_BAG + { 0, 0, 0, 0 }, // UPG_STRENGTH (unused) + { 0, 0, 0, 0 }, // UPG_SCALE (unused) + { 99, 200, 500, 500 }, // UPG_WALLET + { 0, 30, 40, 50 }, // UPG_BULLET_BAG + { 0, 10, 20, 30 }, // UPG_STICKS + { 0, 20, 30, 40 }, // UPG_NUTS }; u32 gGsFlagsMasks[] = { 0x000000FF, 0x0000FF00, 0x00FF0000, 0xFF000000 }; diff --git a/src/overlays/actors/ovl_Demo_Kankyo/z_demo_kankyo.c b/src/overlays/actors/ovl_Demo_Kankyo/z_demo_kankyo.c index 60a2ba1c10..8702c513af 100644 --- a/src/overlays/actors/ovl_Demo_Kankyo/z_demo_kankyo.c +++ b/src/overlays/actors/ovl_Demo_Kankyo/z_demo_kankyo.c @@ -874,7 +874,7 @@ void DemoKankyo_DrawWarpSparkles(Actor* thisx, GlobalContext* globalCtx) { sWarpSparkleEnvColors[globalCtx->msgCtx.lastPlayedSong].g, sWarpSparkleEnvColors[globalCtx->msgCtx.lastPlayedSong].b, 255); } else { - s8 respawnData = gSaveContext.respawn[1].data; + s8 respawnData = gSaveContext.respawn[RESPAWN_MODE_RETURN].data; gDPSetEnvColor(POLY_XLU_DISP++, sWarpSparkleEnvColors[respawnData].r, sWarpSparkleEnvColors[respawnData].g, sWarpSparkleEnvColors[respawnData].b, 255); diff --git a/src/overlays/actors/ovl_En_Elf/z_en_elf.c b/src/overlays/actors/ovl_En_Elf/z_en_elf.c index 5cef6a1541..d90b3f7726 100644 --- a/src/overlays/actors/ovl_En_Elf/z_en_elf.c +++ b/src/overlays/actors/ovl_En_Elf/z_en_elf.c @@ -1406,8 +1406,14 @@ void func_80A053F0(Actor* thisx, GlobalContext* globalCtx) { } else { this->actionFunc(this, globalCtx); thisx->shape.rot.y = this->unk_2BC; - nREG(80) = HIGH_SCORE(HS_HBA); + // `gSaveContext.sceneFlags[127].chest` (like in the debug string) instead of `HIGH_SCORE(HS_HBA)` matches too, + // but, with how the `SaveContext` struct is currently defined, it is an out-of-bounds read in the `sceneFlags` + // array. + // It is theorized the original `room_inf` (currently `sceneFlags`) was an array of length 128, not broken up + // like currently into structs. Structs are currently used because they're easier to work with and still match. + // There is another occurrence of this elsewhere. + nREG(80) = HIGH_SCORE(HS_HBA); if ((nREG(81) != 0) && (HIGH_SCORE(HS_HBA) != 0)) { LOG_NUM("z_common_data.memory.information.room_inf[127][ 0 ]", HIGH_SCORE(HS_HBA), "../z_en_elf.c", 2595); } diff --git a/src/overlays/actors/ovl_En_Ge1/z_en_ge1.c b/src/overlays/actors/ovl_En_Ge1/z_en_ge1.c index bbff338d0f..59ba65b313 100644 --- a/src/overlays/actors/ovl_En_Ge1/z_en_ge1.c +++ b/src/overlays/actors/ovl_En_Ge1/z_en_ge1.c @@ -629,6 +629,8 @@ void EnGe1_TalkNoPrize_Archery(EnGe1* this, GlobalContext* globalCtx) { void EnGe1_TalkAfterGame_Archery(EnGe1* this, GlobalContext* globalCtx) { CLEAR_EVENTINF(EVENTINF_HORSES_08); LOG_NUM("z_common_data.yabusame_total", gSaveContext.minigameScore, "../z_en_ge1.c", 1110); + // With the current `SaveContext` struct definition, the expression in the debug string is an out-of-bounds read, + // see the other occurrence of this for more details. LOG_NUM("z_common_data.memory.information.room_inf[127][ 0 ]", HIGH_SCORE(HS_HBA), "../z_en_ge1.c", 1111); this->actor.flags |= ACTOR_FLAG_16; diff --git a/src/overlays/actors/ovl_player_actor/z_player.c b/src/overlays/actors/ovl_player_actor/z_player.c index 05d59feca4..59a3c72b77 100644 --- a/src/overlays/actors/ovl_player_actor/z_player.c +++ b/src/overlays/actors/ovl_player_actor/z_player.c @@ -8913,7 +8913,7 @@ void func_80845EF8(Player* this, GlobalContext* globalCtx) { func_80097534(globalCtx, &globalCtx->roomCtx); } func_8005B1A4(Gameplay_GetCamera(globalCtx, CAM_ID_MAIN)); - Gameplay_SetupRespawnPoint(globalCtx, 0, 0xDFF); + Gameplay_SetupRespawnPoint(globalCtx, RESPAWN_MODE_DOWN, 0xDFF); } return; } @@ -9296,8 +9296,8 @@ void Player_Init(Actor* thisx, GlobalContext* globalCtx2) { SceneTableEntry* scene = globalCtx->loadedScene; u32 titleFileSize; s32 initMode; - s32 sp50; - s32 sp4C; + s32 respawnFlag; + s32 respawnMode; globalCtx->shootingGalleryStatus = globalCtx->bombchuBowlingStatus = 0; @@ -9322,34 +9322,34 @@ void Player_Init(Actor* thisx, GlobalContext* globalCtx2) { Player_InitCommon(this, globalCtx, gPlayerSkelHeaders[((void)0, gSaveContext.linkAge)]); this->giObjectSegment = (void*)(((u32)ZeldaArena_MallocDebug(0x3008, "../z_player.c", 17175) + 8) & ~0xF); - sp50 = gSaveContext.respawnFlag; + respawnFlag = gSaveContext.respawnFlag; - if (sp50 != 0) { - if (sp50 == -3) { + if (respawnFlag != 0) { + if (respawnFlag == -3) { thisx->params = gSaveContext.respawn[RESPAWN_MODE_RETURN].playerParams; } else { - if ((sp50 == 1) || (sp50 == -1)) { + if ((respawnFlag == 1) || (respawnFlag == -1)) { this->unk_A86 = -2; } - if (sp50 < 0) { - sp4C = 0; + if (respawnFlag < 0) { + respawnMode = RESPAWN_MODE_DOWN; } else { - sp4C = sp50 - 1; - Math_Vec3f_Copy(&thisx->world.pos, &gSaveContext.respawn[sp50 - 1].pos); + respawnMode = respawnFlag - 1; + Math_Vec3f_Copy(&thisx->world.pos, &gSaveContext.respawn[respawnMode].pos); Math_Vec3f_Copy(&thisx->home.pos, &thisx->world.pos); Math_Vec3f_Copy(&thisx->prevPos, &thisx->world.pos); this->fallStartHeight = thisx->world.pos.y; - this->currentYaw = thisx->shape.rot.y = gSaveContext.respawn[sp4C].yaw; - thisx->params = gSaveContext.respawn[sp4C].playerParams; + this->currentYaw = thisx->shape.rot.y = gSaveContext.respawn[respawnMode].yaw; + thisx->params = gSaveContext.respawn[respawnMode].playerParams; } - globalCtx->actorCtx.flags.tempSwch = gSaveContext.respawn[sp4C].tempSwchFlags & 0xFFFFFF; - globalCtx->actorCtx.flags.tempCollect = gSaveContext.respawn[sp4C].tempCollectFlags; + globalCtx->actorCtx.flags.tempSwch = gSaveContext.respawn[respawnMode].tempSwchFlags & 0xFFFFFF; + globalCtx->actorCtx.flags.tempCollect = gSaveContext.respawn[respawnMode].tempCollectFlags; } } - if ((sp50 == 0) || (sp50 < -1)) { + if ((respawnFlag == 0) || (respawnFlag < -1)) { titleFileSize = scene->titleFile.vromEnd - scene->titleFile.vromStart; if ((titleFileSize != 0) && gSaveContext.showTitleCard) { if ((gSaveContext.sceneSetupIndex < 4) && @@ -9364,7 +9364,7 @@ void Player_Init(Actor* thisx, GlobalContext* globalCtx2) { gSaveContext.showTitleCard = true; } - if (func_80845C68(globalCtx, (sp50 == 2) ? 1 : 0) == 0) { + if (func_80845C68(globalCtx, (respawnFlag == 2) ? 1 : 0) == 0) { gSaveContext.respawn[RESPAWN_MODE_DOWN].playerParams = (thisx->params & 0xFF) | 0xD00; } diff --git a/src/overlays/gamestates/ovl_file_choose/z_file_choose.c b/src/overlays/gamestates/ovl_file_choose/z_file_choose.c index 894e5b3b09..b855b1a17d 100644 --- a/src/overlays/gamestates/ovl_file_choose/z_file_choose.c +++ b/src/overlays/gamestates/ovl_file_choose/z_file_choose.c @@ -1458,7 +1458,7 @@ void FileChoose_LoadGame(GameState* thisx) { this->state.running = false; } - gSaveContext.respawn[0].entranceIndex = ENTR_LOAD_OPENING; + gSaveContext.respawn[RESPAWN_MODE_DOWN].entranceIndex = ENTR_LOAD_OPENING; gSaveContext.respawnFlag = 0; gSaveContext.seqId = (u8)NA_BGM_DISABLED; gSaveContext.natureAmbienceId = 0xFF;