Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add UB-free implementations of RBA and BA #4650

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions soh/soh/SohMenuBar.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1551,8 +1551,6 @@ void DrawEnhancementsMenu() {
UIWidgets::Tooltip("Restore pre-release behavior where defeating a Gold Skulltula will play a cutscene showing it die.");
UIWidgets::PaddedEnhancementCheckbox("Quick Bongo Kill", CVAR_ENHANCEMENT("QuickBongoKill"), true, false);
UIWidgets::Tooltip("Restore a bug from NTSC 1.0 that allows bypassing Bongo Bongo's intro cutscene to quickly kill him");
UIWidgets::PaddedEnhancementCheckbox("Original RBA Values", CVAR_ENHANCEMENT("RestoreRBAValues"), true, false);
UIWidgets::Tooltip("Restores the original outcomes when performing Reverse Bottle Adventure.");
UIWidgets::PaddedEnhancementCheckbox("Early Eyeball Frog", CVAR_ENHANCEMENT("EarlyEyeballFrog"), true, false);
UIWidgets::Tooltip("Restores a bug from NTSC 1.0/1.1 that allows you to obtain the eyeball frog from King Zora instead of the Zora Tunic by holding shield.");

Expand Down
294 changes: 281 additions & 13 deletions soh/src/code/z_parameter.c
Original file line number Diff line number Diff line change
Expand Up @@ -1439,6 +1439,140 @@ Gfx* Gfx_TextureI8(Gfx* displayListHead, void* texture, s16 textureWidth, s16 te
return displayListHead;
}

void Inventory_DoBA(u8 cRight) {
if (cRight >= ITEM_STICK && cRight <= ITEM_POTION_BLUE) {
gSaveContext.equips.buttonItems[0] = gSaveContext.inventory.items[cRight];
} else if (cRight >= ITEM_FAIRY && cRight <= ITEM_MASK_BUNNY) {
gSaveContext.equips.buttonItems[0] = gSaveContext.inventory.ammo[cRight - ITEM_FAIRY];
} else if (cRight == ITEM_MASK_GORON) {
gSaveContext.equips.buttonItems[0] = (gSaveContext.inventory.equipment >> 8) & 0xFF;
} else if (cRight == ITEM_MASK_ZORA) {
gSaveContext.equips.buttonItems[0] = gSaveContext.inventory.equipment & 0xFF;
} else if (cRight == ITEM_MASK_GERUDO || cRight == ITEM_MASK_TRUTH) {
// ITEM_MASK_GERUDO and ITEM_MASK_TRUTH land in padding bytes
gSaveContext.equips.buttonItems[0] = 0;
} else if (cRight == ITEM_SOLD_OUT) {
gSaveContext.equips.buttonItems[0] = (gSaveContext.inventory.upgrades >> 24) & 0xFF;
} else if (cRight == ITEM_POCKET_EGG) {
gSaveContext.equips.buttonItems[0] = (gSaveContext.inventory.upgrades >> 16) & 0xFF;
} else if (cRight == ITEM_POCKET_CUCCO) {
gSaveContext.equips.buttonItems[0] = (gSaveContext.inventory.upgrades >> 8) & 0xFF;
} else if (cRight == ITEM_COJIRO) {
gSaveContext.equips.buttonItems[0] = gSaveContext.inventory.upgrades & 0xFF;
} else if (cRight == ITEM_ODD_MUSHROOM) {
gSaveContext.equips.buttonItems[0] = (gSaveContext.inventory.questItems >> 24) & 0xFF;
} else if (cRight == ITEM_ODD_POTION) {
gSaveContext.equips.buttonItems[0] = (gSaveContext.inventory.questItems >> 16) & 0xFF;
} else if (cRight == ITEM_SAW) {
gSaveContext.equips.buttonItems[0] = (gSaveContext.inventory.questItems >> 8) & 0xFF;
} else if (cRight == ITEM_SWORD_BROKEN) {
gSaveContext.equips.buttonItems[0] = gSaveContext.inventory.questItems & 0xFF;
} else if (cRight >= ITEM_PRESCRIPTION && cRight <= ITEM_BULLET_BAG_30) {
gSaveContext.equips.buttonItems[0] = gSaveContext.inventory.dungeonItems[cRight - ITEM_PRESCRIPTION];
} else if (cRight >= ITEM_BULLET_BAG_40 && cRight <= ITEM_SWORD_KNIFE) {
gSaveContext.equips.buttonItems[0] = gSaveContext.inventory.dungeonKeys[cRight - ITEM_BULLET_BAG_40];
} else if (cRight == ITEM_SONG_BOLERO) {
gSaveContext.equips.buttonItems[0] = gSaveContext.inventory.defenseHearts;
} else if (cRight == ITEM_SONG_SERENADE) {
gSaveContext.equips.buttonItems[0] = (gSaveContext.inventory.gsTokens >> 8) & 0xFF;
} else if (cRight == ITEM_SONG_REQUIEM) {
gSaveContext.equips.buttonItems[0] = gSaveContext.inventory.gsTokens & 0xFF;
} else if (cRight == ITEM_SONG_NOCTURNE || cRight == ITEM_SONG_PRELUDE) {
// ITEM_SONG_NOCTURNE and ITEM_SONG_PRELUDE land in padding bytes
gSaveContext.equips.buttonItems[0] = 0;
} else if (cRight >= ITEM_SONG_LULLABY) {
// The rest of the items fall into the saved scene flags. Let's calculate the scene and which field it pulls from
u32 offset = cRight - ITEM_SONG_LULLABY;
u32 scene = offset / sizeof(SavedSceneFlags);
switch (offset % sizeof(SavedSceneFlags)) {
case 0:
gSaveContext.equips.buttonItems[0] = (gSaveContext.sceneFlags[scene].chest >> 24) & 0xFF;
break;
case 1:
gSaveContext.equips.buttonItems[0] = (gSaveContext.sceneFlags[scene].chest >> 16) & 0xFF;
break;
case 2:
gSaveContext.equips.buttonItems[0] = (gSaveContext.sceneFlags[scene].chest >> 8) & 0xFF;
break;
case 3:
gSaveContext.equips.buttonItems[0] = gSaveContext.sceneFlags[scene].chest & 0xFF;
break;
case 4:
gSaveContext.equips.buttonItems[0] = (gSaveContext.sceneFlags[scene].swch >> 24) & 0xFF;
break;
case 5:
gSaveContext.equips.buttonItems[0] = (gSaveContext.sceneFlags[scene].swch >> 16) & 0xFF;
break;
case 6:
gSaveContext.equips.buttonItems[0] = (gSaveContext.sceneFlags[scene].swch >> 8) & 0xFF;
break;
case 7:
gSaveContext.equips.buttonItems[0] = gSaveContext.sceneFlags[scene].swch & 0xFF;
break;
case 8:
gSaveContext.equips.buttonItems[0] = (gSaveContext.sceneFlags[scene].clear >> 24) & 0xFF;
break;
case 9:
gSaveContext.equips.buttonItems[0] = (gSaveContext.sceneFlags[scene].clear >> 16) & 0xFF;
break;
case 10:
gSaveContext.equips.buttonItems[0] = (gSaveContext.sceneFlags[scene].clear >> 8) & 0xFF;
break;
case 11:
gSaveContext.equips.buttonItems[0] = gSaveContext.sceneFlags[scene].clear & 0xFF;
break;
case 12:
gSaveContext.equips.buttonItems[0] = (gSaveContext.sceneFlags[scene].collect >> 24) & 0xFF;
break;
case 13:
gSaveContext.equips.buttonItems[0] = (gSaveContext.sceneFlags[scene].collect >> 16) & 0xFF;
break;
case 14:
gSaveContext.equips.buttonItems[0] = (gSaveContext.sceneFlags[scene].collect >> 8) & 0xFF;
break;
case 15:
gSaveContext.equips.buttonItems[0] = gSaveContext.sceneFlags[scene].collect & 0xFF;
break;
case 16:
gSaveContext.equips.buttonItems[0] = (gSaveContext.sceneFlags[scene].unk >> 24) & 0xFF;
break;
case 17:
gSaveContext.equips.buttonItems[0] = (gSaveContext.sceneFlags[scene].unk >> 16) & 0xFF;
break;
case 18:
gSaveContext.equips.buttonItems[0] = (gSaveContext.sceneFlags[scene].unk >> 8) & 0xFF;
break;
case 19:
gSaveContext.equips.buttonItems[0] = gSaveContext.sceneFlags[scene].unk & 0xFF;
break;
case 20:
gSaveContext.equips.buttonItems[0] = (gSaveContext.sceneFlags[scene].rooms >> 24) & 0xFF;
break;
case 21:
gSaveContext.equips.buttonItems[0] = (gSaveContext.sceneFlags[scene].rooms >> 16) & 0xFF;
break;
case 22:
gSaveContext.equips.buttonItems[0] = (gSaveContext.sceneFlags[scene].rooms >> 8) & 0xFF;
break;
case 23:
gSaveContext.equips.buttonItems[0] = gSaveContext.sceneFlags[scene].rooms & 0xFF;
break;
case 24:
gSaveContext.equips.buttonItems[0] = (gSaveContext.sceneFlags[scene].floors >> 24) & 0xFF;
break;
case 25:
gSaveContext.equips.buttonItems[0] = (gSaveContext.sceneFlags[scene].floors >> 16) & 0xFF;
break;
case 26:
gSaveContext.equips.buttonItems[0] = (gSaveContext.sceneFlags[scene].floors >> 8) & 0xFF;
break;
case 27:
gSaveContext.equips.buttonItems[0] = gSaveContext.sceneFlags[scene].floors & 0xFF;
break;
}
}
}

void Rando_Inventory_SwapAgeEquipment(void) {
s16 i;
u16 shieldEquipValue;
Expand Down Expand Up @@ -1518,8 +1652,13 @@ void Rando_Inventory_SwapAgeEquipment(void) {
(gSaveContext.equips.buttonItems[i] <= ITEM_POE)) ||
((gSaveContext.equips.buttonItems[i] >= ITEM_WEIRD_EGG) &&
(gSaveContext.equips.buttonItems[i] <= ITEM_CLAIM_CHECK))) {
gSaveContext.equips.buttonItems[i] =
gSaveContext.inventory.items[gSaveContext.equips.cButtonSlots[i - 1]];
if (i == 0) {
// If bottle is on B, it triggers BA, which we need to recreate
Inventory_DoBA(gSaveContext.equips.buttonItems[3]);
} else {
gSaveContext.equips.buttonItems[i] =
gSaveContext.inventory.items[gSaveContext.equips.cButtonSlots[i - 1]];
}
}
}

Expand Down Expand Up @@ -1675,8 +1814,13 @@ void Inventory_SwapAgeEquipment(void) {
((gSaveContext.equips.buttonItems[i] >= ITEM_WEIRD_EGG) &&
(gSaveContext.equips.buttonItems[i] <= ITEM_CLAIM_CHECK))) {
osSyncPrintf("Register_Item_Pt(%d)=%d\n", i, gSaveContext.equips.cButtonSlots[i - 1]);
gSaveContext.equips.buttonItems[i] =
gSaveContext.inventory.items[gSaveContext.equips.cButtonSlots[i - 1]];
if (i == 0) {
// If bottle is on B, it triggers BA, which we need to recreate
Inventory_DoBA(gSaveContext.equips.buttonItems[3]);
} else {
gSaveContext.equips.buttonItems[i] =
gSaveContext.inventory.items[gSaveContext.equips.cButtonSlots[i - 1]];
}
}
}

Expand Down Expand Up @@ -2699,11 +2843,136 @@ s32 Inventory_HasSpecificBottle(u8 bottleItem) {
}
}

void byteSwapInventory() {
gSaveContext.inventory.equipment = BE16SWAP(gSaveContext.inventory.equipment);
gSaveContext.inventory.upgrades = BE32SWAP(gSaveContext.inventory.upgrades);
gSaveContext.inventory.questItems = BE32SWAP(gSaveContext.inventory.questItems);
gSaveContext.inventory.gsTokens = BE16SWAP(gSaveContext.inventory.gsTokens);
void Inventory_DoRBA(u8 cRight, u8 item) {
if (cRight >= ITEM_STICK && cRight <= ITEM_POTION_BLUE) {
gSaveContext.inventory.items[cRight] = item;
} else if (cRight >= ITEM_FAIRY && cRight <= ITEM_MASK_BUNNY) {
gSaveContext.inventory.ammo[cRight - ITEM_FAIRY] = item;
} else if (cRight == ITEM_MASK_GORON) {
gSaveContext.inventory.equipment = (item << 8) | (gSaveContext.inventory.equipment & 0x00FF);
} else if (cRight == ITEM_MASK_ZORA) {
gSaveContext.inventory.equipment = item | (gSaveContext.inventory.equipment & 0xFF00);
}
// ITEM_MASK_GERUDO and ITEM_MASK_TRUTH land in padding bytes
else if (cRight == ITEM_SOLD_OUT) {
gSaveContext.inventory.upgrades = (item << 24) | (gSaveContext.inventory.upgrades & 0x00FFFFFF);
} else if (cRight == ITEM_POCKET_EGG) {
gSaveContext.inventory.upgrades = (item << 16) | (gSaveContext.inventory.upgrades & 0xFF00FFFF);
} else if (cRight == ITEM_POCKET_CUCCO) {
gSaveContext.inventory.upgrades = (item << 8) | (gSaveContext.inventory.upgrades & 0xFFFF00FF);
} else if (cRight == ITEM_COJIRO) {
gSaveContext.inventory.upgrades = item | (gSaveContext.inventory.upgrades & 0xFFFFFF00);
} else if (cRight == ITEM_ODD_MUSHROOM) {
gSaveContext.inventory.questItems = (item << 24) | (gSaveContext.inventory.questItems & 0x00FFFFFF);
} else if (cRight == ITEM_ODD_POTION) {
gSaveContext.inventory.questItems = (item << 16) | (gSaveContext.inventory.questItems & 0xFF00FFFF);
} else if (cRight == ITEM_SAW) {
gSaveContext.inventory.questItems = (item << 8) | (gSaveContext.inventory.questItems & 0xFFFF00FF);
} else if (cRight == ITEM_SWORD_BROKEN) {
gSaveContext.inventory.questItems = item | (gSaveContext.inventory.questItems & 0xFFFFFF00);
} else if (cRight >= ITEM_PRESCRIPTION && cRight <= ITEM_BULLET_BAG_30) {
gSaveContext.inventory.dungeonItems[cRight - ITEM_PRESCRIPTION] = item;
} else if (cRight >= ITEM_BULLET_BAG_40 && cRight <= ITEM_SWORD_KNIFE) {
gSaveContext.inventory.dungeonKeys[cRight - ITEM_BULLET_BAG_40] = item;
} else if (cRight == ITEM_SONG_BOLERO) {
gSaveContext.inventory.defenseHearts = item;
} else if (cRight == ITEM_SONG_SERENADE) {
gSaveContext.inventory.gsTokens = (item << 8) | (gSaveContext.inventory.gsTokens & 0x00FF);
} else if (cRight == ITEM_SONG_REQUIEM) {
gSaveContext.inventory.gsTokens = item | (gSaveContext.inventory.gsTokens & 0xFF00);
}
// ITEM_SONG_NOCTURNE and ITEM_SONG_PRELUDE land in padding bytes
else if (cRight >= ITEM_SONG_LULLABY) {
// The rest of the items fall into the saved scene flags. Let's calculate the scene and which field it sets
u32 offset = cRight - ITEM_SONG_LULLABY;
u32 scene = offset / sizeof(SavedSceneFlags);
switch (offset % sizeof(SavedSceneFlags)) {
case 0:
gSaveContext.sceneFlags[scene].chest = (item << 24) | (gSaveContext.sceneFlags[scene].chest & 0x00FFFFFF);
break;
case 1:
gSaveContext.sceneFlags[scene].chest = (item << 16) | (gSaveContext.sceneFlags[scene].chest & 0xFF00FFFF);
break;
case 2:
gSaveContext.sceneFlags[scene].chest = (item << 8) | (gSaveContext.sceneFlags[scene].chest & 0xFFFF00FF);
break;
case 3:
gSaveContext.sceneFlags[scene].chest = item | (gSaveContext.sceneFlags[scene].chest & 0xFFFFFF00);
break;
case 4:
gSaveContext.sceneFlags[scene].swch = (item << 24) | (gSaveContext.sceneFlags[scene].swch & 0x00FFFFFF);
break;
case 5:
gSaveContext.sceneFlags[scene].swch = (item << 16) | (gSaveContext.sceneFlags[scene].swch & 0xFF00FFFF);
break;
case 6:
gSaveContext.sceneFlags[scene].swch = (item << 8) | (gSaveContext.sceneFlags[scene].swch & 0xFFFF00FF);
break;
case 7:
gSaveContext.sceneFlags[scene].swch = item | (gSaveContext.sceneFlags[scene].swch & 0xFFFFFF00);
break;
case 8:
gSaveContext.sceneFlags[scene].clear = (item << 24) | (gSaveContext.sceneFlags[scene].clear & 0x00FFFFFF);
break;
case 9:
gSaveContext.sceneFlags[scene].clear = (item << 16) | (gSaveContext.sceneFlags[scene].clear & 0xFF00FFFF);
break;
case 10:
gSaveContext.sceneFlags[scene].clear = (item << 8) | (gSaveContext.sceneFlags[scene].clear & 0xFFFF00FF);
break;
case 11:
gSaveContext.sceneFlags[scene].clear = item | (gSaveContext.sceneFlags[scene].clear & 0xFFFFFF00);
break;
case 12:
gSaveContext.sceneFlags[scene].collect = (item << 24) | (gSaveContext.sceneFlags[scene].collect & 0x00FFFFFF);
break;
case 13:
gSaveContext.sceneFlags[scene].collect = (item << 16) | (gSaveContext.sceneFlags[scene].collect & 0xFF00FFFF);
break;
case 14:
gSaveContext.sceneFlags[scene].collect = (item << 8) | (gSaveContext.sceneFlags[scene].collect & 0xFFFF00FF);
break;
case 15:
gSaveContext.sceneFlags[scene].collect = item | (gSaveContext.sceneFlags[scene].collect & 0xFFFFFF00);
break;
case 16:
gSaveContext.sceneFlags[scene].unk = (item << 24) | (gSaveContext.sceneFlags[scene].unk & 0x00FFFFFF);
break;
case 17:
gSaveContext.sceneFlags[scene].unk = (item << 16) | (gSaveContext.sceneFlags[scene].unk & 0xFF00FFFF);
break;
case 18:
gSaveContext.sceneFlags[scene].unk = (item << 8) | (gSaveContext.sceneFlags[scene].unk & 0xFFFF00FF);
break;
case 19:
gSaveContext.sceneFlags[scene].unk = item | (gSaveContext.sceneFlags[scene].unk & 0xFFFFFF00);
break;
case 20:
gSaveContext.sceneFlags[scene].rooms = (item << 24) | (gSaveContext.sceneFlags[scene].rooms & 0x00FFFFFF);
break;
case 21:
gSaveContext.sceneFlags[scene].rooms = (item << 16) | (gSaveContext.sceneFlags[scene].rooms & 0xFF00FFFF);
break;
case 22:
gSaveContext.sceneFlags[scene].rooms = (item << 8) | (gSaveContext.sceneFlags[scene].rooms & 0xFFFF00FF);
break;
case 23:
gSaveContext.sceneFlags[scene].rooms = item | (gSaveContext.sceneFlags[scene].rooms & 0xFFFFFF00);
break;
case 24:
gSaveContext.sceneFlags[scene].floors = (item << 24) | (gSaveContext.sceneFlags[scene].floors & 0x00FFFFFF);
break;
case 25:
gSaveContext.sceneFlags[scene].floors = (item << 16) | (gSaveContext.sceneFlags[scene].floors & 0xFF00FFFF);
break;
case 26:
gSaveContext.sceneFlags[scene].floors = (item << 8) | (gSaveContext.sceneFlags[scene].floors & 0xFFFF00FF);
break;
case 27:
gSaveContext.sceneFlags[scene].floors = item | (gSaveContext.sceneFlags[scene].floors & 0xFFFFFF00);
break;
}
}
}

void Inventory_UpdateBottleItem(PlayState* play, u8 item, u8 button) {
Expand All @@ -2717,10 +2986,9 @@ void Inventory_UpdateBottleItem(PlayState* play, u8 item, u8 button) {
item = ITEM_MILK_HALF;
}

if (CVarGetInteger(CVAR_ENHANCEMENT("RestoreRBAValues"),0)) {
byteSwapInventory();
gSaveContext.inventory.items[gSaveContext.equips.cButtonSlots[button - 1]] = item;
byteSwapInventory();
if (button == 0) {
// If bottle is on B, it triggers RBA, which we need to recreate
Inventory_DoRBA(gSaveContext.equips.buttonItems[3], item);
} else {
gSaveContext.inventory.items[gSaveContext.equips.cButtonSlots[button - 1]] = item;
}
Expand Down