diff --git a/.gitignore b/.gitignore index 1df76208d..98fe2b637 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ __pycache__/ .idea/ cmake-build-debug venv/ +.cache/ # Project-specific ignores build/ @@ -410,6 +411,7 @@ oot.otr *.sav shipofharkinian.ini shipofharkinian.json +imgui.ini # Switch Stuff diff --git a/libultraship/libultraship/CMakeLists.txt b/libultraship/libultraship/CMakeLists.txt index b59993660..aec229bfb 100644 --- a/libultraship/libultraship/CMakeLists.txt +++ b/libultraship/libultraship/CMakeLists.txt @@ -334,6 +334,12 @@ set(Source_Files__Resources__mpq ) source_group("Source Files\\Resources\\mpq" FILES ${Source_Files__Resources__mpq}) +set(Source_Files__Crash_Handler + "CrashHandler.cpp" + "CrashHandler.h" +) +source_group("Source Files\\Crash Handler" FILES ${Source_Files__Crash_Handler}) + if (CMAKE_SYSTEM_NAME STREQUAL "Darwin") set(Source_Files__Darwin "OSXFolderManager.mm" @@ -388,6 +394,7 @@ set(ALL_FILES ${Source_Files__Resources__Factories} ${Source_Files__Resources__Files} ${Source_Files__Resources__mpq} + ${Source_Files__Crash_Handler} ${Source_Files__Darwin} ${Source_Files__NintendoSwitch} ${Source_Files__CafeOS} diff --git a/libultraship/libultraship/CrashHandler.cpp b/libultraship/libultraship/CrashHandler.cpp new file mode 100644 index 000000000..f8d66beab --- /dev/null +++ b/libultraship/libultraship/CrashHandler.cpp @@ -0,0 +1,172 @@ +#include "spdlog/spdlog.h" +#include "Utils/StringHelper.h" +#include "CrashHandler.h" + +#if defined(__linux__) +#include +#include +#include // for __cxa_demangle +#include // for dladdr +#include +#include + +extern "C" void DeinitOTR(void); + + +static void PrintRegisters(ucontext_t* ctx) { + char regbuffer[1024]; + SPDLOG_CRITICAL("Registers:"); +#if defined(__x86_64__) + snprintf(regbuffer, std::size(regbuffer), "0x%016llX", ctx->uc_mcontext.gregs[REG_RAX]); + SPDLOG_CRITICAL("RAX: {} ", regbuffer); + snprintf(regbuffer, std::size(regbuffer), "0x%016llX", ctx->uc_mcontext.gregs[REG_RDI]); + SPDLOG_CRITICAL("RDI: {} ", regbuffer); + snprintf(regbuffer, std::size(regbuffer), "0x%016llX", ctx->uc_mcontext.gregs[REG_RSI]); + SPDLOG_CRITICAL("RSI: {} ", regbuffer); + snprintf(regbuffer, std::size(regbuffer), "0x%016llX", ctx->uc_mcontext.gregs[REG_RDX]); + SPDLOG_CRITICAL("RDX: {} ", regbuffer); + snprintf(regbuffer, std::size(regbuffer), "0x%016llX", ctx->uc_mcontext.gregs[REG_RCX]); + SPDLOG_CRITICAL("RCX: {} ", regbuffer); + snprintf(regbuffer, std::size(regbuffer), "0x%016llX", ctx->uc_mcontext.gregs[REG_R8]); + SPDLOG_CRITICAL("R8 : {} ", regbuffer); + snprintf(regbuffer, std::size(regbuffer), "0x%016llX", ctx->uc_mcontext.gregs[REG_R9]); + SPDLOG_CRITICAL("R9 : {} ", regbuffer); + snprintf(regbuffer, std::size(regbuffer), "0x%016llX", ctx->uc_mcontext.gregs[REG_R10]); + SPDLOG_CRITICAL("R10: {} ", regbuffer); + snprintf(regbuffer, std::size(regbuffer), "0x%016llX", ctx->uc_mcontext.gregs[REG_R11]); + SPDLOG_CRITICAL("R11: {} ", regbuffer); + snprintf(regbuffer, std::size(regbuffer), "0x%016llX", ctx->uc_mcontext.gregs[REG_RSP]); + SPDLOG_CRITICAL("RSP: {} ", regbuffer); + snprintf(regbuffer, std::size(regbuffer), "0x%016llX", ctx->uc_mcontext.gregs[REG_RBX]); + SPDLOG_CRITICAL("RBX: {} ", regbuffer); + snprintf(regbuffer, std::size(regbuffer), "0x%016llX", ctx->uc_mcontext.gregs[REG_RBP]); + SPDLOG_CRITICAL("RBP: {} ", regbuffer); + snprintf(regbuffer, std::size(regbuffer), "0x%016llX", ctx->uc_mcontext.gregs[REG_R12]); + SPDLOG_CRITICAL("R12: {} ", regbuffer); + snprintf(regbuffer, std::size(regbuffer), "0x%016llX", ctx->uc_mcontext.gregs[REG_R13]); + SPDLOG_CRITICAL("R13: {} ", regbuffer); + snprintf(regbuffer, std::size(regbuffer), "0x%016llX", ctx->uc_mcontext.gregs[REG_R14]); + SPDLOG_CRITICAL("R14: {} ", regbuffer); + snprintf(regbuffer, std::size(regbuffer), "0x%016llX", ctx->uc_mcontext.gregs[REG_R15]); + SPDLOG_CRITICAL("R15: {} ", regbuffer); + snprintf(regbuffer, std::size(regbuffer), "0x%016llX", ctx->uc_mcontext.gregs[REG_RIP]); + SPDLOG_CRITICAL("RIP: {} ", regbuffer); + snprintf(regbuffer, std::size(regbuffer), "0x%016llX", ctx->uc_mcontext.gregs[REG_EFL]); + SPDLOG_CRITICAL("EFLAGS: {} ", regbuffer); +#else + snprintf(regbuffer, std::size(regbuffer),"0x%08lX", ctx->uc_mcontext.gregs[REG_EDI]); + SPDLOG_CRITICAL("EDI : {} ", regbuffer); + snprintf(regbuffer, std::size(regbuffer),"0x%08lX", ctx->uc_mcontext.gregs[REG_ESI]); + SPDLOG_CRITICAL("ESI : {} ", regbuffer); + snprintf(regbuffer, std::size(regbuffer),"0x%08lX", ctx->uc_mcontext.gregs[REG_EBP]); + SPDLOG_CRITICAL("EBP : {} ", regbuffer); + snprintf(regbuffer, std::size(regbuffer),"0x%08lX", ctx->uc_mcontext.gregs[REG_ESP]); + SPDLOG_CRITICAL("ESP : {} ", regbuffer); + snprintf(regbuffer, std::size(regbuffer),"0x%08lX", ctx->uc_mcontext.gregs[REG_EBX]); + SPDLOG_CRITICAL("EBX : {} ", regbuffer); + snprintf(regbuffer, std::size(regbuffer),"0x%08lX", ctx->uc_mcontext.gregs[REG_EDX]); + SPDLOG_CRITICAL("EDX : {} ", regbuffer); + snprintf(regbuffer, std::size(regbuffer),"0x%08lX", ctx->uc_mcontext.gregs[REG_ECX]); + SPDLOG_CRITICAL("ECX : {} ", regbuffer); + snprintf(regbuffer, std::size(regbuffer),"0x%08lX", ctx->uc_mcontext.gregs[REG_EAX]); + SPDLOG_CRITICAL("EAX : {} ", regbuffer); + snprintf(regbuffer, std::size(regbuffer),"0x%08lX", ctx->uc_mcontext.gregs[REG_EIP]); + SPDLOG_CRITICAL("EIP : {} ", regbuffer); + snprintf(regbuffer, std::size(regbuffer),"0x%08lX", ctx->uc_mcontext.gregs[REG_EFL]); + SPDLOG_CRITICAL("EFL : {} ", regbuffer); +#endif +} + +static void ErrorHandler(int sig, siginfo_t* sigInfo, void* data) +{ + std::array arr; + ucontext_t* ctx = static_cast(data); + constexpr size_t nMaxFrames = arr.size(); + size_t size = backtrace(arr.data(), nMaxFrames); + char** symbols = backtrace_symbols(arr.data(), nMaxFrames); + + SPDLOG_CRITICAL("(Signal: {})\n", sig); + + switch (sig) { + case SIGILL: + SPDLOG_CRITICAL("ILLEGAL INSTRUCTION"); + break; + case SIGABRT: + SPDLOG_CRITICAL("ABORT"); + break; + case SIGFPE: + SPDLOG_CRITICAL("ERRONEUS ARITHEMETIC OPERATION"); + break; + case SIGSEGV: + SPDLOG_CRITICAL("INVALID ACCESS TO STORAGE"); + break; + } + + PrintRegisters(ctx); + + SPDLOG_CRITICAL("Traceback:\n"); + for (size_t i = 1; i < size; i++) + { + Dl_info info; + int gotAddress = dladdr(arr[i], &info); + std::string functionName(symbols[i]); + + if (gotAddress != 0 && info.dli_sname != nullptr) + { + FILE* pipe; + int32_t status; + char* demangled = abi::__cxa_demangle(info.dli_sname, nullptr, nullptr, &status); + const char* nameFound = info.dli_sname; + + if (status == 0) + { + nameFound = demangled; + } + #if 0 + char command[256]; + char addrLine[128]; + snprintf(command, sizeof(command), "addr2line -e soh.elf %s + 0x%lX", nameFound, (uintptr_t)arr[i] - (uintptr_t)info.dli_saddr); + pipe = popen(command, "r"); + fgets(addrLine, 128, pipe); + #endif + + functionName = StringHelper::Sprintf("%s (+0x%X)", nameFound, + (char*)arr[i] - (char*)info.dli_saddr); + free(demangled); + } + + SPDLOG_CRITICAL("{} {}", i, functionName.c_str()); + } + + free(symbols); + DeinitOTR(); + exit(1); +} + +static void ShutdownHandler(int sig, siginfo_t* sigInfo, void* data) { + DeinitOTR(); + exit(1); +} + +extern "C" void SetupHandlerLinux() { + struct sigaction action; + struct sigaction shutdownAction; + + action.sa_flags = SA_SIGINFO; + action.sa_sigaction = ErrorHandler; + + sigaction(SIGILL, &action, nullptr); + sigaction(SIGABRT, &action, nullptr); + sigaction(SIGFPE, &action, nullptr); + sigaction(SIGSEGV, &action, nullptr); + + shutdownAction.sa_flags = SA_SIGINFO; + shutdownAction.sa_sigaction = ShutdownHandler; + sigaction(SIGINT, &shutdownAction, nullptr); + sigaction(SIGTERM, &shutdownAction, nullptr); + sigaction(SIGQUIT, &shutdownAction, nullptr); + sigaction(SIGKILL, &shutdownAction, nullptr); + + +} +#endif diff --git a/libultraship/libultraship/CrashHandler.h b/libultraship/libultraship/CrashHandler.h new file mode 100644 index 000000000..e068a4e38 --- /dev/null +++ b/libultraship/libultraship/CrashHandler.h @@ -0,0 +1,18 @@ +#ifndef CRASH_HANDLER_H +#define CRASH_HANDLER_H + +#ifdef __linux__ + +#ifdef __cplusplus +extern "C" { +#endif + +void SetupHandlerLinux(void); + +#ifdef __cplusplus +} +#endif + +#endif // __linux__ + +#endif // CRASH_HANDLER_H \ No newline at end of file diff --git a/soh/include/functions.h b/soh/include/functions.h index 9047b6d3f..203d4eea7 100644 --- a/soh/include/functions.h +++ b/soh/include/functions.h @@ -1661,7 +1661,7 @@ void PadMgr_HandleRetraceMsg(PadMgr* padmgr); void PadMgr_HandlePreNMI(PadMgr* padmgr); // This function must remain commented out, because it is called incorrectly in // fault.c (actual bug in game), and the compiler notices and won't compile it -// void PadMgr_RequestPadData(PadMgr* padmgr, Input* inputs, s32 mode); +void PadMgr_RequestPadData(PadMgr* padmgr, Input* inputs, s32 mode); void PadMgr_Init(PadMgr* padmgr, OSMesgQueue* siIntMsgQ, IrqMgr* irqMgr, OSId id, OSPri priority, void* stack); void Sched_SwapFrameBuffer(CfbInfo* cfbInfo); void func_800C84E4(SchedContext* sc, CfbInfo* cfbInfo); diff --git a/soh/include/segment_symbols.h b/soh/include/segment_symbols.h index 81fe34801..3b50b4eaa 100644 --- a/soh/include/segment_symbols.h +++ b/soh/include/segment_symbols.h @@ -3,20 +3,20 @@ #include "z64.h" -#define DECLARE_SEGMENT(name) \ - //extern u8 _##name##SegmentStart[]; \ +#define DECLARE_SEGMENT(name) + //extern u8 _##name##SegmentStart[]; //extern u8 _##name##SegmentEnd[]; -#define DECLARE_ROM_SEGMENT(name) \ - //extern u8 _##name##SegmentRomStart[]; \ +#define DECLARE_ROM_SEGMENT(name) + //extern u8 _##name##SegmentRomStart[]; //extern u8 _##name##SegmentRomEnd[]; #define DECLARE_BSS_SEGMENT(name) \ extern u8 _##name##SegmentBssStart[]; \ extern u8 _##name##SegmentBssEnd[]; -#define DECLARE_OVERLAY_SEGMENT(name) \ - //DECLARE_SEGMENT(ovl_##name) \ +#define DECLARE_OVERLAY_SEGMENT(name) + //DECLARE_SEGMENT(ovl_##name) //DECLARE_ROM_SEGMENT(ovl_##name) DECLARE_SEGMENT(boot) diff --git a/soh/soh/Enhancements/custom-message/CustomMessageTypes.h b/soh/soh/Enhancements/custom-message/CustomMessageTypes.h index dff85f415..1af49d6a6 100644 --- a/soh/soh/Enhancements/custom-message/CustomMessageTypes.h +++ b/soh/soh/Enhancements/custom-message/CustomMessageTypes.h @@ -1,6 +1,8 @@ #pragma once typedef enum { + TEXT_BUY_BOMBCHU_10_PROMPT = 0x8C, + TEXT_BUY_BOMBCHU_10_DESC = 0xBC, TEXT_GS_NO_FREEZE = 0xB4, TEXT_GS_FREEZE = 0xB5, TEXT_RANDOMIZER_CUSTOM_ITEM = 0xF8, diff --git a/soh/soh/Enhancements/randomizer/3drando/settings.cpp b/soh/soh/Enhancements/randomizer/3drando/settings.cpp index cb91e5334..dd9f6d2de 100644 --- a/soh/soh/Enhancements/randomizer/3drando/settings.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/settings.cpp @@ -2561,6 +2561,9 @@ namespace Settings { MapsAndCompasses.SetSelectedIndex(cvarSettings[RSK_STARTING_MAPS_COMPASSES]); + BombchusInLogic.SetSelectedIndex(cvarSettings[RSK_BOMBCHUS_IN_LOGIC]); + AmmoDrops.SetSelectedIndex(AMMODROPS_VANILLA); // Ensure logic knows bombchu drops aren't implemented yet + StartingConsumables.SetSelectedIndex(cvarSettings[RSK_STARTING_CONSUMABLES]); StartingMaxRupees.SetSelectedIndex(cvarSettings[RSK_FULL_WALLETS]); diff --git a/soh/soh/Enhancements/randomizer/randomizer.cpp b/soh/soh/Enhancements/randomizer/randomizer.cpp index de7997e9a..76936e6e9 100644 --- a/soh/soh/Enhancements/randomizer/randomizer.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer.cpp @@ -575,6 +575,7 @@ std::unordered_map SpoilerfileSettingNameToEn { "Shuffle Dungeon Items:Gerudo Fortress Keys", RSK_GERUDO_KEYS }, { "Shuffle Dungeon Items:Boss Keys", RSK_BOSS_KEYSANITY }, { "Shuffle Dungeon Items:Ganon's Boss Key", RSK_GANONS_BOSS_KEY }, + { "World Settings:Bombchus in Logic", RSK_BOMBCHUS_IN_LOGIC }, { "Misc Settings:Gossip Stone Hints", RSK_GOSSIP_STONE_HINTS }, { "Misc Settings:Hint Clarity", RSK_HINT_CLARITY }, { "Misc Settings:Hint Distribution", RSK_HINT_DISTRIBUTION }, @@ -816,6 +817,7 @@ void Randomizer::ParseRandomizerSettingsFile(const char* spoilerFileName) { case RSK_STARTING_KOKIRI_SWORD: case RSK_COMPLETE_MASK_QUEST: case RSK_ENABLE_GLITCH_CUTSCENES: + case RSK_BOMBCHUS_IN_LOGIC: if(it.value() == "Off") { gSaveContext.randoSettings[index].value = 0; } else if(it.value() == "On") { @@ -1392,7 +1394,13 @@ s16 Randomizer::GetItemFromGet(RandomizerGet randoGet, GetItemID ogItemId) { return GI_RUPEE_BLUE; case RG_PROGRESSIVE_BOMBCHUS: - return GI_BOMBCHUS_20; //todo progressive? + if (INV_CONTENT(ITEM_BOMBCHU) == ITEM_NONE) { + return GI_BOMBCHUS_20; + } + if (AMMO(ITEM_BOMBCHU) < 5) { + return GI_BOMBCHUS_10; + } + return GI_BOMBCHUS_5; case RG_PROGRESSIVE_MAGIC_METER: switch (gSaveContext.magicLevel) { @@ -1693,7 +1701,6 @@ bool Randomizer::IsItemVanilla(RandomizerGet randoGet) { case RG_BUY_BOMBS_535: case RG_BUY_RED_POTION_40: case RG_BUY_RED_POTION_50: - return true; case RG_FOREST_TEMPLE_SMALL_KEY: case RG_FIRE_TEMPLE_SMALL_KEY: case RG_WATER_TEMPLE_SMALL_KEY: @@ -3074,6 +3081,7 @@ void GenerateRandomizerImgui() { cvarSettings[RSK_SHUFFLE_COWS] = CVar_GetS32("gRandomizeShuffleCows", 0); cvarSettings[RSK_SHUFFLE_ADULT_TRADE] = CVar_GetS32("gRandomizeShuffleAdultTrade", 0); cvarSettings[RSK_SHUFFLE_MAGIC_BEANS] = CVar_GetS32("gRandomizeShuffleBeans", 0); + cvarSettings[RSK_BOMBCHUS_IN_LOGIC] = CVar_GetS32("gRandomizeBombchusInLogic", 0); cvarSettings[RSK_SKIP_CHILD_ZELDA] = CVar_GetS32("gRandomizeSkipChildZelda", 0); // if we skip child zelda, we start with zelda's letter, and malon starts @@ -3931,6 +3939,20 @@ void DrawRandoEditor(bool& open) { PaddedSeparator(); + // Bombchus in Logic + SohImGui::EnhancementCheckbox(Settings::BombchusInLogic.GetName().c_str(), "gRandomizeBombchusInLogic"); + InsertHelpHoverText( + "Bombchus are properly considered in logic.\n" + "\n" + "The first Bombchu pack will always be 20, and subsequent packs will be " + "5 or 10 based on how many you have.\n" + "Once found, they can be replenished at the Bombchu shop.\n" + "\n" + "Bombchu Bowling is opened by obtaining Bombchus." + ); + + PaddedSeparator(); + SohImGui::EnhancementCheckbox("WIP - Blue Fire Arrows", "gRandomizeBlueIceArrows", true, "In development"); InsertHelpHoverText( "Ice Arrows act like Blue Fire, making them able to melt red ice. " diff --git a/soh/soh/Enhancements/randomizer/randomizerTypes.h b/soh/soh/Enhancements/randomizer/randomizerTypes.h index 6d410df7c..d30e0cee4 100644 --- a/soh/soh/Enhancements/randomizer/randomizerTypes.h +++ b/soh/soh/Enhancements/randomizer/randomizerTypes.h @@ -1017,7 +1017,8 @@ typedef enum { RSK_ENABLE_GLITCH_CUTSCENES, RSK_SKULLS_SUNS_SONG, RSK_SHUFFLE_ADULT_TRADE, - RSK_SHUFFLE_MAGIC_BEANS + RSK_SHUFFLE_MAGIC_BEANS, + RSK_BOMBCHUS_IN_LOGIC } RandomizerSettingKey; typedef struct ScrubIdentity { diff --git a/soh/soh/OTRGlobals.cpp b/soh/soh/OTRGlobals.cpp index 7d7a86268..efb0e4464 100644 --- a/soh/soh/OTRGlobals.cpp +++ b/soh/soh/OTRGlobals.cpp @@ -1652,15 +1652,14 @@ extern "C" GetItemEntry Randomizer_GetItemFromKnownCheck(RandomizerCheck randomi return ItemTable_RetrieveEntry(getItemModIndex, itemID); } -extern "C" bool Randomizer_ObtainedFreestandingIceTrap(RandomizerCheck randomizerCheck, GetItemID ogId, Actor* actor) { - return gSaveContext.n64ddFlag && (actor->parent != NULL) && - Randomizer_GetItemFromKnownCheck(randomizerCheck, ogId).getItemId == RG_ICE_TRAP; -} - extern "C" bool Randomizer_ItemIsIceTrap(RandomizerCheck randomizerCheck, GetItemID ogId) { return gSaveContext.n64ddFlag && Randomizer_GetItemFromKnownCheck(randomizerCheck, ogId).getItemId == RG_ICE_TRAP; } +extern "C" bool Randomizer_ObtainedFreestandingIceTrap(RandomizerCheck randomizerCheck, GetItemID ogId, Actor* actor) { + return Randomizer_ItemIsIceTrap(randomizerCheck, ogId) && actor->parent != NULL; +} + extern "C" CustomMessageEntry Randomizer_GetCustomGetItemMessage(Player* player) { s16 giid; if (player->getItemEntry.objectId != OBJECT_INVALID) { @@ -1731,6 +1730,9 @@ extern "C" int CustomMessage_RetrieveIfExists(GlobalContext* globalCtx) { messageEntry = Randomizer_GetNaviMessage(); } else if (Randomizer_GetSettingValue(RSK_SHUFFLE_MAGIC_BEANS) && textId == TEXT_BEAN_SALESMAN) { messageEntry = CustomMessageManager::Instance->RetrieveMessage(Randomizer::merchantMessageTableID, TEXT_BEAN_SALESMAN); + } else if (Randomizer_GetSettingValue(RSK_BOMBCHUS_IN_LOGIC) && + (textId == TEXT_BUY_BOMBCHU_10_DESC || textId == TEXT_BUY_BOMBCHU_10_PROMPT)) { + messageEntry = CustomMessageManager::Instance->RetrieveMessage(customMessageTableID, textId); } } if (textId == TEXT_GS_NO_FREEZE || textId == TEXT_GS_FREEZE) { diff --git a/soh/soh/OTRGlobals.h b/soh/soh/OTRGlobals.h index f81afdaea..e1541f0a8 100644 --- a/soh/soh/OTRGlobals.h +++ b/soh/soh/OTRGlobals.h @@ -32,6 +32,8 @@ private: #endif #ifndef __cplusplus +void InitOTR(void); +void DeinitOTR(void); void VanillaItemTable_Init(); void OTRAudio_Init(); void InitAudio(); diff --git a/soh/soh/z_message_OTR.cpp b/soh/soh/z_message_OTR.cpp index c6be43964..5cfc99395 100644 --- a/soh/soh/z_message_OTR.cpp +++ b/soh/soh/z_message_OTR.cpp @@ -111,5 +111,25 @@ extern "C" void OTRMessage_Init() TEXTBOX_TYPE_BLUE, TEXTBOX_POS_BOTTOM, "You got a %rGold Skulltula Token%w!&You've collected %r\x19%w tokens&in total!", "Du erhälst ein %rGoldene&Skulltula-Symbol%w! Du hast&insgesamt %r\x19%w symbol gesammelt!", - "Vous obtenez un %rSymbole de&Skulltula d'or%w! Vous avez&collecté %r\x19\%w symboles en tout!" }); + "Vous obtenez un %rSymbole de&Skulltula d'or%w! Vous avez&collecté %r\x19\%w symboles en tout!" + } + ); + CustomMessageManager::Instance->CreateMessage( + customMessageTableID, TEXT_BUY_BOMBCHU_10_DESC, + { + TEXTBOX_TYPE_BLACK, TEXTBOX_POS_BOTTOM, + "\x08%rBombchu (10 pieces) 99 Rupees&%wThis looks like a toy mouse, but&it's actually a self-propelled time&bomb!\x09\x0A", + "\x08%rKrabbelmine 10 Stück 99 Rubine&%wDas ist eine praktische Zeitbombe,&die Du als Distanzwaffe&einsetzen kannst!\x09\x0A", + "\x08%rMissile 10 unités 99 Rubis&%wProfilée comme une souris&mécanique, cette arme est &destructrice!!!\x09\x0A", + } + ); + CustomMessageManager::Instance->CreateMessage( + customMessageTableID, TEXT_BUY_BOMBCHU_10_PROMPT, + { + TEXTBOX_TYPE_BLACK, TEXTBOX_POS_BOTTOM, + "\x08\Bombchu 10 pieces 99 Rupees\x09&&\x1B%gBuy&Don't buy%w", + "\x08Krabbelmine 10 Stück 99 Rubine\x09&&\x1B%gKaufen!&Nicht kaufen!%w", + "\x08Missiles 10 unités 99 Rubis\x09&&\x1B%gAcheter&Ne pas acheter%w", + } + ); } diff --git a/soh/src/code/fault.c b/soh/src/code/fault.c index c55a63a00..f7414766a 100644 --- a/soh/src/code/fault.c +++ b/soh/src/code/fault.c @@ -293,7 +293,7 @@ void Fault_Sleep(u32 duration) { void Fault_PadCallback(Input* input) { //! @bug This function is not called correctly and thus will crash from reading a bad pointer at 0x800C7E4C - PadMgr_RequestPadData(input, 0); + PadMgr_RequestPadData(&gPadMgr, input, 0); } void Fault_UpdatePadImpl() diff --git a/soh/src/code/main.c b/soh/src/code/main.c index ccfdc2019..8e9fe1776 100644 --- a/soh/src/code/main.c +++ b/soh/src/code/main.c @@ -3,6 +3,8 @@ #include #include "soh/OTRGlobals.h" +#include "../libultraship/CrashHandler.h" + s32 gScreenWidth = SCREEN_WIDTH; s32 gScreenHeight = SCREEN_HEIGHT; @@ -38,6 +40,10 @@ void Main_LogSystemHeap(void) { int main(int argc, char** argv) { +#ifdef __linux__ + SetupHandlerLinux(); +#endif + GameConsole_Init(); InitOTR(); BootCommands_Init(); @@ -64,14 +70,14 @@ void Main(void* arg) { Fault_Init(); SysCfb_Init(0); Heaps_Alloc(); - sysHeap = gSystemHeap; + sysHeap = (uintptr_t)gSystemHeap; fb = SysCfb_GetFbPtr(0); gSystemHeapSize = 1024 * 1024 * 4; // "System heap initalization" osSyncPrintf("システムヒープ初期化 %08x-%08x %08x\n", sysHeap, fb, gSystemHeapSize); - SystemHeap_Init(sysHeap, gSystemHeapSize); // initializes the system heap + SystemHeap_Init((void*)sysHeap, gSystemHeapSize); // initializes the system heap if (osMemSize >= 0x800000) { - debugHeap = SysCfb_GetFbEnd(); + debugHeap = (void*)SysCfb_GetFbEnd(); debugHeapSize = (0x80600000 - (uintptr_t)debugHeap); } else { debugHeapSize = 0x400; diff --git a/soh/src/code/z_sram.c b/soh/src/code/z_sram.c index c8befa586..c131f172f 100644 --- a/soh/src/code/z_sram.c +++ b/soh/src/code/z_sram.c @@ -860,6 +860,8 @@ void Sram_InitSave(FileChooseContext* fileChooseCtx) { if (getItem.modIndex == MOD_NONE) { if (getItem.itemId >= ITEM_MEDALLION_FOREST && getItem.itemId <= ITEM_ZORA_SAPPHIRE) { GiveLinkDungeonReward(getItem.getItemId); + } else if (getItem.itemId >= ITEM_SONG_MINUET && getItem.itemId <= ITEM_SONG_STORMS) { + GiveLinkSong(getItem.getItemId); } else if (giid == GI_RUPEE_GREEN || giid == GI_RUPEE_BLUE || giid == GI_RUPEE_RED || giid == GI_RUPEE_PURPLE || giid == GI_RUPEE_GOLD) { GiveLinkRupeesByGetItemId(giid); diff --git a/soh/src/overlays/actors/ovl_En_Bom_Bowl_Man/z_en_bom_bowl_man.c b/soh/src/overlays/actors/ovl_En_Bom_Bowl_Man/z_en_bom_bowl_man.c index dc22f9dab..6bb097d89 100644 --- a/soh/src/overlays/actors/ovl_En_Bom_Bowl_Man/z_en_bom_bowl_man.c +++ b/soh/src/overlays/actors/ovl_En_Bom_Bowl_Man/z_en_bom_bowl_man.c @@ -143,13 +143,15 @@ void EnBomBowMan_BlinkAwake(EnBomBowlMan* this, GlobalContext* globalCtx) { } } - // Check for Bomb Bag if Rando is enabled - // RANDOTODO: Check for bombchu pack instead of bomb bag if bombchus are in logic + // In randomizer, only check for bomb bag when bombchus aren't in logic + // and only check for bombchus when bombchus are in logic if (gSaveContext.n64ddFlag) { - if (INV_CONTENT(ITEM_BOMB) != ITEM_NONE) { - this->actor.textId = 0xBF; - } else { + u8 bombchusInLogic = Randomizer_GetSettingValue(RSK_BOMBCHUS_IN_LOGIC); + if ((!bombchusInLogic && INV_CONTENT(ITEM_BOMB) == ITEM_NONE) || + (bombchusInLogic && INV_CONTENT(ITEM_BOMBCHU) == ITEM_NONE)) { this->actor.textId = 0x7058; + } else { + this->actor.textId = 0xBF; } } } @@ -177,11 +179,17 @@ void EnBomBowMan_CheckBeatenDC(EnBomBowlMan* this, GlobalContext* globalCtx) { this->eyeMode = CHU_GIRL_EYES_AWAKE; this->blinkTimer = (s16)Rand_ZeroFloat(60.0f) + 20; - // Check for beaten Dodongo's Cavern if not rando'd - // check for bomb bag if rando'd - if ((!gSaveContext.n64ddFlag && - !((gSaveContext.eventChkInf[2] & 0x20) || BREG(2))) || - (gSaveContext.n64ddFlag && (INV_CONTENT(ITEM_BOMB) == ITEM_NONE))) { + bool bombchuBowlingClosed; + if (gSaveContext.n64ddFlag) { + // when rando'd, check if we have bombchus if chus are in logic + // and check if we have a bomb bag if chus aren't in logic + u8 explosive = Randomizer_GetSettingValue(RSK_BOMBCHUS_IN_LOGIC) ? ITEM_BOMBCHU : ITEM_BOMB; + bombchuBowlingClosed = (INV_CONTENT(explosive) == ITEM_NONE); + } else { + // if not rando'd, check if we have beaten Dodongo's Cavern + bombchuBowlingClosed = !((gSaveContext.eventChkInf[2] & 0x20) || BREG(2)); + } + if (bombchuBowlingClosed) { this->actionFunc = EnBomBowMan_WaitNotBeatenDC; } else { this->actor.textId = 0x18; diff --git a/soh/src/overlays/actors/ovl_En_GirlA/z_en_girla.c b/soh/src/overlays/actors/ovl_En_GirlA/z_en_girla.c index 5a1a36be7..cbc2e9749 100644 --- a/soh/src/overlays/actors/ovl_En_GirlA/z_en_girla.c +++ b/soh/src/overlays/actors/ovl_En_GirlA/z_en_girla.c @@ -633,8 +633,16 @@ s32 EnGirlA_CanBuy_Unk20(GlobalContext* globalCtx, EnGirlA* this) { } s32 EnGirlA_CanBuy_Bombchus(GlobalContext* globalCtx, EnGirlA* this) { - // When in rando, don't allow buying bombchus when the player doesn't have a bomb bag - if (AMMO(ITEM_BOMBCHU) >= 50 || (gSaveContext.n64ddFlag && CUR_CAPACITY(UPG_BOMB_BAG) == 0)) { + // When in rando, don't allow buying bombchus when the player doesn't have required explosives + // If bombchus are in logic, the player needs to have bombchus; otherwise they need a bomb bag + if (gSaveContext.n64ddFlag) { + u8 bombchusInLogic = Randomizer_GetSettingValue(RSK_BOMBCHUS_IN_LOGIC); + if ((!bombchusInLogic && CUR_CAPACITY(UPG_BOMB_BAG) == 0) || + (bombchusInLogic && INV_CONTENT(ITEM_BOMBCHU) == ITEM_NONE)) { + return CANBUY_RESULT_CANT_GET_NOW; + } + } + if (AMMO(ITEM_BOMBCHU) >= 50) { return CANBUY_RESULT_CANT_GET_NOW; } if (gSaveContext.rupees < this->basePrice) { @@ -861,6 +869,14 @@ void EnGirlA_BuyEvent_ZoraTunic(GlobalContext* globalCtx, EnGirlA* this) { } void EnGirlA_BuyEvent_ObtainBombchuPack(GlobalContext* globalCtx, EnGirlA* this) { + Rupees_ChangeBy(-this->basePrice); + + // Normally, buying a bombchu pack sets a flag indicating the pack is now sold out + // If they're in logic for rando, skip setting that flag so they can be purchased repeatedly + if (gSaveContext.n64ddFlag && Randomizer_GetSettingValue(RSK_BOMBCHUS_IN_LOGIC)) { + return; + } + switch (this->actor.params) { case SI_BOMBCHU_10_2: gSaveContext.itemGetInf[0] |= 0x40; @@ -887,7 +903,6 @@ void EnGirlA_BuyEvent_ObtainBombchuPack(GlobalContext* globalCtx, EnGirlA* this) gSaveContext.itemGetInf[0] |= 0x20; break; } - Rupees_ChangeBy(-this->basePrice); } void EnGirlA_Noop(EnGirlA* this, GlobalContext* globalCtx) { @@ -1053,7 +1068,13 @@ void EnGirlA_InitializeItemAction(EnGirlA* this, GlobalContext* globalCtx) { this->canBuyFunc = itemEntry->canBuyFunc; this->itemGiveFunc = itemEntry->itemGiveFunc; this->buyEventFunc = itemEntry->buyEventFunc; - this->basePrice = itemEntry->price; + // If chus are in logic, make the 10 pack affordable without a wallet upgrade + if (gSaveContext.n64ddFlag && Randomizer_GetSettingValue(RSK_BOMBCHUS_IN_LOGIC) && + this->getItemId == GI_BOMBCHUS_10) { + this->basePrice = 99; + } else { + this->basePrice = itemEntry->price; + } this->itemCount = itemEntry->count; this->hiliteFunc = itemEntry->hiliteFunc; this->giDrawId = itemEntry->giDrawId; diff --git a/soh/src/overlays/actors/ovl_En_Go/z_en_go.c b/soh/src/overlays/actors/ovl_En_Go/z_en_go.c index 58124ef23..4e803af0a 100644 --- a/soh/src/overlays/actors/ovl_En_Go/z_en_go.c +++ b/soh/src/overlays/actors/ovl_En_Go/z_en_go.c @@ -95,7 +95,7 @@ u16 EnGo_GetTextID(GlobalContext* globalCtx, Actor* thisx) { switch (thisx->params & 0xF0) { case 0x90: - if (gSaveContext.bgsFlag) { + if (!gSaveContext.n64ddFlag && gSaveContext.bgsFlag) { return 0x305E; } else if (INV_CONTENT(ITEM_TRADE_ADULT) >= ITEM_CLAIM_CHECK) { if (Environment_GetBgsDayCount() >= CVar_GetS32("gForgeTime", 3)) { @@ -859,7 +859,7 @@ void func_80A405CC(EnGo* this, GlobalContext* globalCtx) { void EnGo_BiggoronActionFunc(EnGo* this, GlobalContext* globalCtx) { if (((this->actor.params & 0xF0) == 0x90) && (this->unk_1E0.unk_00 == 2)) { - if (gSaveContext.bgsFlag) { + if (!gSaveContext.n64ddFlag && gSaveContext.bgsFlag) { this->unk_1E0.unk_00 = 0; } else { if (INV_CONTENT(ITEM_TRADE_ADULT) == ITEM_EYEDROPS) { diff --git a/soh/src/overlays/actors/ovl_En_Go2/z_en_go2.c b/soh/src/overlays/actors/ovl_En_Go2/z_en_go2.c index 9f6ff147e..c8b635902 100644 --- a/soh/src/overlays/actors/ovl_En_Go2/z_en_go2.c +++ b/soh/src/overlays/actors/ovl_En_Go2/z_en_go2.c @@ -595,7 +595,7 @@ s16 EnGo2_GetStateGoronCityLink(GlobalContext* globalCtx, EnGo2* this) { u16 EnGo2_GetTextIdGoronDmtBiggoron(GlobalContext* globalCtx, EnGo2* this) { Player* player = GET_PLAYER(globalCtx); - if (gSaveContext.bgsFlag) { + if (!gSaveContext.n64ddFlag && gSaveContext.bgsFlag) { player->exchangeItemId = EXCH_ITEM_CLAIM_CHECK; return 0x305E; } else if (INV_CONTENT(ITEM_TRADE_ADULT) >= ITEM_CLAIM_CHECK) { @@ -622,10 +622,6 @@ s16 EnGo2_GetStateGoronDmtBiggoron(GlobalContext* globalCtx, EnGo2* this) { } if(gSaveContext.n64ddFlag) { - if (INV_CONTENT(ITEM_CLAIM_CHECK) != ITEM_CLAIM_CHECK) { - return 0; - } - EnGo2_GetItemEntry(this, globalCtx, Randomizer_GetItemFromKnownCheck(RC_DMT_TRADE_CLAIM_CHECK, GI_SWORD_BGS)); Flags_SetTreasure(globalCtx, 0x1F); } else { @@ -1063,7 +1059,7 @@ void EnGo2_BiggoronSetTextId(EnGo2* this, GlobalContext* globalCtx, Player* play u16 textId; if ((this->actor.params & 0x1F) == GORON_DMT_BIGGORON) { - if (gSaveContext.bgsFlag) { + if ((!gSaveContext.n64ddFlag && gSaveContext.bgsFlag)) { if (func_8002F368(globalCtx) == EXCH_ITEM_CLAIM_CHECK) { this->actor.textId = 0x3003; } else { @@ -1071,16 +1067,20 @@ void EnGo2_BiggoronSetTextId(EnGo2* this, GlobalContext* globalCtx, Player* play } player->actor.textId = this->actor.textId; - } else if (!gSaveContext.bgsFlag && (INV_CONTENT(ITEM_TRADE_ADULT) == ITEM_CLAIM_CHECK)) { + } else if (INV_CONTENT(ITEM_TRADE_ADULT) == ITEM_CLAIM_CHECK) { if (func_8002F368(globalCtx) == EXCH_ITEM_CLAIM_CHECK) { - if (Environment_GetBgsDayCount() >= CVar_GetS32("gForgeTime", 3)) { + if (gSaveContext.n64ddFlag && Flags_GetTreasure(globalCtx, 0x1F)) { + textId = 0x3003; + } else if (Environment_GetBgsDayCount() >= CVar_GetS32("gForgeTime", 3)) { textId = 0x305E; } else { textId = 0x305D; } this->actor.textId = textId; } else { - if (Environment_GetBgsDayCount() >= CVar_GetS32("gForgeTime", 3)) { + if (gSaveContext.n64ddFlag && Flags_GetTreasure(globalCtx, 0x1F)) { + textId = 0x305E; + } else if (Environment_GetBgsDayCount() >= CVar_GetS32("gForgeTime", 3)) { textId = 0x3002; } else { textId = 0x305D; diff --git a/soh/src/overlays/actors/ovl_player_actor/z_player.c b/soh/src/overlays/actors/ovl_player_actor/z_player.c index 2631e39b9..9927ad617 100644 --- a/soh/src/overlays/actors/ovl_player_actor/z_player.c +++ b/soh/src/overlays/actors/ovl_player_actor/z_player.c @@ -6108,7 +6108,7 @@ s32 func_8083E5A8(Player* this, GlobalContext* globalCtx) { } GetItemEntry giEntry; - if (this->getItemEntry.objectId == OBJECT_INVALID) { + if (this->getItemEntry.objectId == OBJECT_INVALID || (this->getItemId != this->getItemEntry.getItemId)) { giEntry = ItemTable_Retrieve(this->getItemId); } else { giEntry = this->getItemEntry; @@ -6125,6 +6125,8 @@ s32 func_8083E5A8(Player* this, GlobalContext* globalCtx) { this->actor.colChkInfo.damage = 0; func_80837C0C(globalCtx, this, 3, 0.0f, 0.0f, 0, 20); Player_SetPendingFlag(this, globalCtx); + this->getItemId == GI_NONE; + this->getItemEntry = (GetItemEntry)GET_ITEM_NONE; return 1; } @@ -6165,7 +6167,7 @@ s32 func_8083E5A8(Player* this, GlobalContext* globalCtx) { } } else if (CHECK_BTN_ALL(sControlInput->press.button, BTN_A) && !(this->stateFlags1 & PLAYER_STATE1_11) && !(this->stateFlags2 & PLAYER_STATE2_10)) { - if (this->getItemId != GI_NONE && this->getItemEntry.objectId != OBJECT_INVALID) { + if (this->getItemId != GI_NONE) { GetItemEntry giEntry; if (this->getItemEntry.objectId == OBJECT_INVALID) { giEntry = ItemTable_Retrieve(-this->getItemId); @@ -9687,8 +9689,8 @@ void func_808473D4(GlobalContext* globalCtx, Player* this) { else if ((!(this->stateFlags1 & PLAYER_STATE1_11) || (heldActor == NULL)) && (interactRangeActor != NULL) && ((!sp1C && (this->getItemId == GI_NONE)) || - ((this->getItemId < 0 && this->getItemEntry.getItemId < 0) && !(this->stateFlags1 & PLAYER_STATE1_27)))) { - if (this->getItemId < 0 && this->getItemEntry.getItemId < 0) { + (this->getItemId < 0 && !(this->stateFlags1 & PLAYER_STATE1_27)))) { + if (this->getItemId < 0) { doAction = DO_ACTION_OPEN; } else if ((interactRangeActor->id == ACTOR_BG_TOKI_SWD) && LINK_IS_ADULT) { doAction = DO_ACTION_DROP; @@ -12853,6 +12855,8 @@ void func_8084E6D4(Player* this, GlobalContext* globalCtx) { } else { this->actor.colChkInfo.damage = 0; func_80837C0C(globalCtx, this, 3, 0.0f, 0.0f, 0, 20); + this->getItemId == GI_NONE; + this->getItemEntry = (GetItemEntry)GET_ITEM_NONE; } return; }