diff --git a/Source/Android/jni/MainAndroid.cpp b/Source/Android/jni/MainAndroid.cpp index 9d3dff94c468..350b78b361f9 100644 --- a/Source/Android/jni/MainAndroid.cpp +++ b/Source/Android/jni/MainAndroid.cpp @@ -25,6 +25,7 @@ #include "Common/Logging/LogManager.h" #include "Common/MsgHandler.h" +#include "Core/Boot/Boot.h" #include "Core/BootManager.h" #include "Core/ConfigManager.h" #include "Core/Core.h" @@ -794,7 +795,7 @@ JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_Run(JNIEnv* // No use running the loop when booting fails s_have_wm_user_stop = false; - if (BootManager::BootCore(s_filename.c_str(), SConfig::BOOT_DEFAULT)) + if (BootManager::BootCore(BootParameters::GenerateFromFile(s_filename))) { static constexpr int TIMEOUT = 10000; static constexpr int WAIT_STEP = 25; diff --git a/Source/Core/Core/Boot/Boot.cpp b/Source/Core/Core/Boot/Boot.cpp index 9ca576721b87..e428129226cd 100644 --- a/Source/Core/Core/Boot/Boot.cpp +++ b/Source/Core/Core/Boot/Boot.cpp @@ -4,13 +4,16 @@ #include "Core/Boot/Boot.h" +#include #include #include +#include #include #include #include "Common/Align.h" +#include "Common/CDUtils.h" #include "Common/CommonPaths.h" #include "Common/CommonTypes.h" #include "Common/FileUtil.h" @@ -18,10 +21,12 @@ #include "Common/MsgHandler.h" #include "Common/StringUtil.h" -#include "Core/Boot/Boot_DOL.h" +#include "Core/Boot/DolReader.h" +#include "Core/Boot/ElfReader.h" #include "Core/ConfigManager.h" #include "Core/Core.h" #include "Core/Debugger/Debugger_SymbolMap.h" +#include "Core/FifoPlayer/FifoPlayer.h" #include "Core/HLE/HLE.h" #include "Core/HW/DVD/DVDInterface.h" #include "Core/HW/EXI/EXI_DeviceIPL.h" @@ -39,6 +44,76 @@ #include "DiscIO/NANDContentLoader.h" #include "DiscIO/Volume.h" +BootParameters::BootParameters(Parameters&& parameters_) : parameters(std::move(parameters_)) +{ +} + +std::unique_ptr BootParameters::GenerateFromFile(const std::string& path) +{ + const bool is_drive = cdio_is_cdrom(path); + // Check if the file exist, we may have gotten it from a --elf command line + // that gave an incorrect file name + if (!is_drive && !File::Exists(path)) + { + PanicAlertT("The specified file \"%s\" does not exist", path.c_str()); + return {}; + } + + std::string extension; + SplitPath(path, nullptr, nullptr, &extension); + std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower); + + static const std::unordered_set disc_image_extensions = { + {".gcm", ".iso", ".tgc", ".wbfs", ".ciso", ".gcz"}}; + if (disc_image_extensions.find(extension) != disc_image_extensions.end() || is_drive) + { + auto volume = DiscIO::CreateVolumeFromFilename(path); + if (!volume) + { + if (is_drive) + { + PanicAlertT("Could not read \"%s\". " + "There is no disc in the drive or it is not a GameCube/Wii backup. " + "Please note that Dolphin cannot play games directly from the original " + "GameCube and Wii discs.", + path.c_str()); + } + else + { + PanicAlertT("\"%s\" is an invalid GCM/ISO file, or is not a GC/Wii ISO.", path.c_str()); + } + return {}; + } + return std::make_unique(Disc{path, std::move(volume)}); + } + + if (extension == ".elf") + return std::make_unique(Executable{path, std::make_unique(path)}); + + if (extension == ".dol") + return std::make_unique(Executable{path, std::make_unique(path)}); + + if (extension == ".dff") + return std::make_unique(DFF{path}); + + if (DiscIO::NANDContentManager::Access().GetNANDLoader(path).IsValid()) + return std::make_unique(NAND{path}); + + PanicAlertT("Could not recognize file %s", path.c_str()); + return {}; +} + +BootParameters::IPL::IPL(DiscIO::Region region_) : region(region_) +{ + const std::string directory = SConfig::GetInstance().GetDirectoryForRegion(region); + path = SConfig::GetInstance().GetBootROMPath(directory); +} + +BootParameters::IPL::IPL(DiscIO::Region region_, Disc&& disc_) : IPL(region_) +{ + disc = std::move(disc_); +} + // Inserts a disc into the emulated disc drive and returns a pointer to it. // The returned pointer must only be used while we are still booting, // because DVDThread can do whatever it wants to the disc after that. @@ -102,57 +177,20 @@ void CBoot::UpdateDebugger_MapLoaded() Host_NotifyMapLoaded(); } -bool CBoot::FindMapFile(std::string* existing_map_file, std::string* writable_map_file, - std::string* title_id) +// Get map file paths for the active title. +bool CBoot::FindMapFile(std::string* existing_map_file, std::string* writable_map_file) { - std::string title_id_str; - size_t name_begin_index; - - SConfig& _StartupPara = SConfig::GetInstance(); - switch (_StartupPara.m_BootType) - { - case SConfig::BOOT_WII_NAND: - { - const DiscIO::NANDContentLoader& Loader = - DiscIO::NANDContentManager::Access().GetNANDLoader(_StartupPara.m_strFilename); - if (Loader.IsValid()) - { - u64 TitleID = Loader.GetTMD().GetTitleId(); - title_id_str = StringFromFormat("%08X_%08X", (u32)(TitleID >> 32) & 0xFFFFFFFF, - (u32)TitleID & 0xFFFFFFFF); - } - break; - } - - case SConfig::BOOT_ELF: - case SConfig::BOOT_DOL: - // Strip the .elf/.dol file extension and directories before the name - name_begin_index = _StartupPara.m_strFilename.find_last_of("/") + 1; - if ((_StartupPara.m_strFilename.find_last_of("\\") + 1) > name_begin_index) - { - name_begin_index = _StartupPara.m_strFilename.find_last_of("\\") + 1; - } - title_id_str = _StartupPara.m_strFilename.substr( - name_begin_index, _StartupPara.m_strFilename.size() - 4 - name_begin_index); - break; - - default: - title_id_str = _StartupPara.GetGameID(); - break; - } + const std::string& game_id = SConfig::GetInstance().m_debugger_game_id; if (writable_map_file) - *writable_map_file = File::GetUserPath(D_MAPS_IDX) + title_id_str + ".map"; - - if (title_id) - *title_id = title_id_str; + *writable_map_file = File::GetUserPath(D_MAPS_IDX) + game_id + ".map"; bool found = false; static const std::string maps_directories[] = {File::GetUserPath(D_MAPS_IDX), File::GetSysDirectory() + MAPS_DIR DIR_SEP}; for (size_t i = 0; !found && i < ArraySize(maps_directories); ++i) { - std::string path = maps_directories[i] + title_id_str + ".map"; + std::string path = maps_directories[i] + game_id + ".map"; if (File::Exists(path)) { found = true; @@ -270,199 +308,170 @@ bool CBoot::Load_BS2(const std::string& boot_rom_filename) return true; } -// Third boot step after BootManager and Core. See Call schedule in BootManager.cpp -bool CBoot::BootUp() +static const DiscIO::Volume* SetDefaultDisc() { - SConfig& _StartupPara = SConfig::GetInstance(); + const SConfig& config = SConfig::GetInstance(); + // load default image or create virtual drive from directory + if (!config.m_strDVDRoot.empty()) + return SetDisc(DiscIO::CreateVolumeFromDirectory(config.m_strDVDRoot, config.bWii)); + if (!config.m_strDefaultISO.empty()) + return SetDisc(DiscIO::CreateVolumeFromFilename(config.m_strDefaultISO)); + return nullptr; +} - if (_StartupPara.m_BootType == SConfig::BOOT_BS2) - NOTICE_LOG(BOOT, "Booting %s", _StartupPara.m_strBootROM.c_str()); - else - NOTICE_LOG(BOOT, "Booting %s", _StartupPara.m_strFilename.c_str()); +// Third boot step after BootManager and Core. See Call schedule in BootManager.cpp +bool CBoot::BootUp(std::unique_ptr boot) +{ + SConfig& config = SConfig::GetInstance(); g_symbolDB.Clear(); // PAL Wii uses NTSC framerate and linecount in 60Hz modes - VideoInterface::Preset(DiscIO::IsNTSC(_StartupPara.m_region) || - (_StartupPara.bWii && _StartupPara.bPAL60)); + VideoInterface::Preset(DiscIO::IsNTSC(config.m_region) || (config.bWii && config.bPAL60)); - switch (_StartupPara.m_BootType) + struct BootTitle { - case SConfig::BOOT_ISO: - { - const DiscIO::Volume* volume = - SetDisc(DiscIO::CreateVolumeFromFilename(_StartupPara.m_strFilename)); - - if (!volume) - return false; - - if ((volume->GetVolumeType() == DiscIO::Platform::WII_DISC) != _StartupPara.bWii) + BootTitle() : config(SConfig::GetInstance()) {} + bool operator()(BootParameters::Disc& disc) const { - PanicAlertT("Warning - starting ISO in wrong console mode!"); - } + NOTICE_LOG(BOOT, "Booting from disc: %s", disc.path.c_str()); + const DiscIO::Volume* volume = SetDisc(std::move(disc.volume)); + + if (!volume) + return false; - _StartupPara.bWii = volume->GetVolumeType() == DiscIO::Platform::WII_DISC; + if (!EmulatedBS2(config.bWii, volume)) + return false; - // We HLE the bootrom if requested or if LLEing it fails - if (_StartupPara.bHLE_BS2 || !Load_BS2(_StartupPara.m_strBootROM)) - EmulatedBS2(_StartupPara.bWii, volume); + // Try to load the symbol map if there is one, and then scan it for + // and eventually replace code + if (LoadMapFromFilename()) + HLE::PatchFunctions(); - PatchEngine::LoadPatches(); + return true; + } - // Scan for common HLE functions - if (_StartupPara.bHLE_BS2 && !_StartupPara.bEnableDebugging) + bool operator()(const BootParameters::Executable& executable) const { - PPCAnalyst::FindFunctions(0x80004000, 0x811fffff, &g_symbolDB); - SignatureDB db(SignatureDB::HandlerType::DSY); - if (db.Load(File::GetSysDirectory() + TOTALDB)) + NOTICE_LOG(BOOT, "Booting from executable: %s", executable.path.c_str()); + + if (!executable.reader->IsValid()) + return false; + + const DiscIO::Volume* volume = nullptr; + // VolumeDirectory only works with DOLs. + if (StringEndsWith(executable.path, ".dol")) { - db.Apply(&g_symbolDB); - HLE::PatchFunctions(); - db.Clear(); + if (!config.m_strDVDRoot.empty()) + { + NOTICE_LOG(BOOT, "Setting DVDRoot %s", config.m_strDVDRoot.c_str()); + volume = SetDisc(DiscIO::CreateVolumeFromDirectory( + config.m_strDVDRoot, config.bWii, config.m_strApploader, executable.path)); + } + else if (!config.m_strDefaultISO.empty()) + { + NOTICE_LOG(BOOT, "Loading default ISO %s", config.m_strDefaultISO.c_str()); + volume = SetDisc(DiscIO::CreateVolumeFromFilename(config.m_strDefaultISO)); + } + } + else + { + volume = SetDefaultDisc(); } - } - // Try to load the symbol map if there is one, and then scan it for - // and eventually replace code - if (LoadMapFromFilename()) - HLE::PatchFunctions(); + if (!executable.reader->LoadIntoMemory()) + { + PanicAlertT("Failed to load the executable to memory."); + return false; + } - break; - } + // Poor man's bootup + if (config.bWii) + { + HID4.SBE = 1; + SetupMSR(); + SetupBAT(config.bWii); + // Because there is no TMD to get the requested system (IOS) version from, + // we default to IOS58, which is the version used by the Homebrew Channel. + SetupWiiMemory(volume, 0x000000010000003a); + } + else + { + EmulatedBS2_GC(volume, true); + } - case SConfig::BOOT_DOL: - { - CDolLoader dolLoader(_StartupPara.m_strFilename); - if (!dolLoader.IsValid()) - return false; + Load_FST(config.bWii, volume); + PC = executable.reader->GetEntryPoint(); - // Check if we have gotten a Wii file or not - bool dolWii = dolLoader.IsWii(); - if (dolWii != _StartupPara.bWii) - { - PanicAlertT("Warning - starting DOL in wrong console mode!"); + if (executable.reader->LoadSymbols() || LoadMapFromFilename()) + { + UpdateDebugger_MapLoaded(); + HLE::PatchFunctions(); + } + return true; } - const DiscIO::Volume* volume = nullptr; - if (!_StartupPara.m_strDVDRoot.empty()) - { - NOTICE_LOG(BOOT, "Setting DVDRoot %s", _StartupPara.m_strDVDRoot.c_str()); - volume = SetDisc(DiscIO::CreateVolumeFromDirectory(_StartupPara.m_strDVDRoot, dolWii, - _StartupPara.m_strApploader, - _StartupPara.m_strFilename)); - } - else if (!_StartupPara.m_strDefaultISO.empty()) + bool operator()(const BootParameters::NAND& nand) const { - NOTICE_LOG(BOOT, "Loading default ISO %s", _StartupPara.m_strDefaultISO.c_str()); - volume = SetDisc(DiscIO::CreateVolumeFromFilename(_StartupPara.m_strDefaultISO)); + NOTICE_LOG(BOOT, "Booting from NAND: %s", nand.content_path.c_str()); + SetDefaultDisc(); + return Boot_WiiWAD(nand.content_path); } - // Poor man's bootup - if (dolWii) - { - HID4.SBE = 1; - SetupMSR(); - SetupBAT(dolWii); - - // Because there is no TMD to get the requested system (IOS) version from, - // we default to IOS58, which is the version used by the Homebrew Channel. - SetupWiiMemory(volume, 0x000000010000003a); - } - else + bool operator()(const BootParameters::IPL& ipl) const { - EmulatedBS2_GC(volume, true); - } - - Load_FST(dolWii, volume); - dolLoader.Load(); - PC = dolLoader.GetEntryPoint(); + NOTICE_LOG(BOOT, "Booting GC IPL: %s", ipl.path.c_str()); + if (!File::Exists(ipl.path)) + { + if (ipl.disc) + PanicAlertT("Cannot start the game, because the GC IPL could not be found."); + else + PanicAlertT("Cannot find the GC IPL."); + return false; + } - if (LoadMapFromFilename()) - HLE::PatchFunctions(); + if (!Load_BS2(ipl.path)) + return false; - break; - } + if (ipl.disc) + { + NOTICE_LOG(BOOT, "Inserting disc: %s", ipl.disc->path.c_str()); + SetDisc(DiscIO::CreateVolumeFromFilename(ipl.disc->path)); + } - case SConfig::BOOT_ELF: - { - const DiscIO::Volume* volume = nullptr; + if (LoadMapFromFilename()) + HLE::PatchFunctions(); - // load image or create virtual drive from directory - if (!_StartupPara.m_strDVDRoot.empty()) - { - NOTICE_LOG(BOOT, "Setting DVDRoot %s", _StartupPara.m_strDVDRoot.c_str()); - volume = - SetDisc(DiscIO::CreateVolumeFromDirectory(_StartupPara.m_strDVDRoot, _StartupPara.bWii)); - } - else if (!_StartupPara.m_strDefaultISO.empty()) - { - NOTICE_LOG(BOOT, "Loading default ISO %s", _StartupPara.m_strDefaultISO.c_str()); - volume = SetDisc(DiscIO::CreateVolumeFromFilename(_StartupPara.m_strDefaultISO)); + return true; } - // Poor man's bootup - if (_StartupPara.bWii) + bool operator()(const BootParameters::DFF& dff) const { - // Because there is no TMD to get the requested system (IOS) version from, - // we default to IOS58, which is the version used by the Homebrew Channel. - SetupWiiMemory(volume, 0x000000010000003a); + NOTICE_LOG(BOOT, "Booting DFF: %s", dff.dff_path.c_str()); + return FifoPlayer::GetInstance().Open(dff.dff_path); } - else - { - EmulatedBS2_GC(volume, true); - } - - Load_FST(_StartupPara.bWii, volume); - if (!Boot_ELF(_StartupPara.m_strFilename)) - return false; - - // Note: Boot_ELF calls HLE::PatchFunctions() - - UpdateDebugger_MapLoaded(); - Dolphin_Debugger::AddAutoBreakpoints(); - break; - } - - case SConfig::BOOT_WII_NAND: - Boot_WiiWAD(_StartupPara.m_strFilename); - PatchEngine::LoadPatches(); + private: + const SConfig& config; + }; - // Not bootstrapped yet, can't translate memory addresses. Thus, prevents Symbol Map usage. - // if (LoadMapFromFilename()) - // HLE::PatchFunctions(); - - // load default image or create virtual drive from directory - if (!_StartupPara.m_strDVDRoot.empty()) - SetDisc(DiscIO::CreateVolumeFromDirectory(_StartupPara.m_strDVDRoot, true)); - else if (!_StartupPara.m_strDefaultISO.empty()) - SetDisc(DiscIO::CreateVolumeFromFilename(_StartupPara.m_strDefaultISO)); - - break; - - // Bootstrap 2 (AKA: Initial Program Loader, "BIOS") - case SConfig::BOOT_BS2: - { - if (!Load_BS2(_StartupPara.m_strBootROM)) - return false; - - if (LoadMapFromFilename()) - HLE::PatchFunctions(); - - break; - } - - case SConfig::BOOT_DFF: - // do nothing - break; - - default: - { - PanicAlertT("Tried to load an unknown file type."); + if (!std::visit(BootTitle(), boot->parameters)) return false; - } - } + PatchEngine::LoadPatches(); HLE::PatchFixedFunctions(); return true; } + +BootExecutableReader::BootExecutableReader(const std::string& file_name) +{ + m_bytes.resize(File::GetSize(file_name)); + File::IOFile file{file_name, "rb"}; + file.ReadBytes(m_bytes.data(), m_bytes.size()); +} + +BootExecutableReader::BootExecutableReader(const std::vector& bytes) : m_bytes(bytes) +{ +} + +BootExecutableReader::~BootExecutableReader() = default; diff --git a/Source/Core/Core/Boot/Boot.h b/Source/Core/Core/Boot/Boot.h index 42c71b199f51..a178edaf7542 100644 --- a/Source/Core/Core/Boot/Boot.h +++ b/Source/Core/Core/Boot/Boot.h @@ -5,15 +5,15 @@ #pragma once #include +#include +#include #include +#include +#include #include "Common/CommonTypes.h" - -namespace DiscIO -{ -class Volume; -struct Partition; -} +#include "DiscIO/Enums.h" +#include "DiscIO/Volume.h" struct RegionSetting { @@ -23,11 +23,54 @@ struct RegionSetting const std::string code; }; +class BootExecutableReader; + +struct BootParameters +{ + struct Disc + { + std::string path; + std::unique_ptr volume; + }; + + struct Executable + { + std::string path; + std::unique_ptr reader; + }; + + struct NAND + { + std::string content_path; + }; + + struct IPL + { + explicit IPL(DiscIO::Region region_); + IPL(DiscIO::Region region_, Disc&& disc_); + std::string path; + DiscIO::Region region; + // It is possible to boot the IPL with a disc inserted (with "skip IPL" disabled). + std::optional disc; + }; + + struct DFF + { + std::string dff_path; + }; + + static std::unique_ptr GenerateFromFile(const std::string& path); + + using Parameters = std::variant; + BootParameters(Parameters&& parameters_); + + Parameters parameters; +}; + class CBoot { public: - static bool BootUp(); - static bool IsElfWii(const std::string& filename); + static bool BootUp(std::unique_ptr boot); // Tries to find a map file for the current game by looking first in the // local user directory, then in the shared user directory. @@ -38,11 +81,8 @@ class CBoot // If writable_map_file is not nullptr, it is set to the path to where a map // file should be saved. // - // If title_id is not nullptr, it is set to the title id - // // Returns true if a map file exists, false if none could be found. - static bool FindMapFile(std::string* existing_map_file, std::string* writable_map_file, - std::string* title_id = nullptr); + static bool FindMapFile(std::string* existing_map_file, std::string* writable_map_file); static bool LoadMapFromFilename(); private: @@ -52,7 +92,6 @@ class CBoot static void UpdateDebugger_MapLoaded(); - static bool Boot_ELF(const std::string& filename); static bool Boot_WiiWAD(const std::string& filename); static void SetupMSR(); @@ -66,3 +105,20 @@ class CBoot static bool SetupWiiMemory(const DiscIO::Volume* volume, u64 ios_title_id); }; + +class BootExecutableReader +{ +public: + BootExecutableReader(const std::string& file_name); + BootExecutableReader(const std::vector& buffer); + virtual ~BootExecutableReader(); + + virtual u32 GetEntryPoint() const = 0; + virtual bool IsValid() const = 0; + virtual bool IsWii() const = 0; + virtual bool LoadIntoMemory(bool only_in_mem1 = false) const = 0; + virtual bool LoadSymbols() const = 0; + +protected: + std::vector m_bytes; +}; diff --git a/Source/Core/Core/Boot/Boot_ELF.cpp b/Source/Core/Core/Boot/Boot_ELF.cpp deleted file mode 100644 index 1cef0937ba54..000000000000 --- a/Source/Core/Core/Boot/Boot_ELF.cpp +++ /dev/null @@ -1,89 +0,0 @@ -// Copyright 2008 Dolphin Emulator Project -// Licensed under GPLv2+ -// Refer to the license.txt file included. - -#include - -#include "Common/FileUtil.h" -#include "Common/Swap.h" -#include "Core/Boot/Boot.h" -#include "Core/Boot/ElfReader.h" -#include "Core/HLE/HLE.h" -#include "Core/PowerPC/PowerPC.h" - -bool CBoot::IsElfWii(const std::string& filename) -{ - /* We already check if filename existed before we called this function, so - there is no need for another check, just read the file right away */ - - size_t filesize = File::GetSize(filename); - auto elf = std::make_unique(filesize); - - { - File::IOFile f(filename, "rb"); - f.ReadBytes(elf.get(), filesize); - } - - // Use the same method as the DOL loader uses: search for mfspr from HID4, - // which should only be used in Wii ELFs. - // - // Likely to have some false positives/negatives, patches implementing a - // better heuristic are welcome. - - // Swap these once, instead of swapping every word in the file. - u32 HID4_pattern = Common::swap32(0x7c13fba6); - u32 HID4_mask = Common::swap32(0xfc1fffff); - ElfReader reader(elf.get()); - - for (int i = 0; i < reader.GetNumSegments(); ++i) - { - if (reader.IsCodeSegment(i)) - { - u32* code = (u32*)reader.GetSegmentPtr(i); - for (u32 j = 0; j < reader.GetSegmentSize(i) / sizeof(u32); ++j) - { - if ((code[j] & HID4_mask) == HID4_pattern) - return true; - } - } - } - - return false; -} - -bool CBoot::Boot_ELF(const std::string& filename) -{ - // Read ELF from file - size_t filesize = File::GetSize(filename); - auto elf = std::make_unique(filesize); - - { - File::IOFile f(filename, "rb"); - f.ReadBytes(elf.get(), filesize); - } - - // Load ELF into GameCube Memory - ElfReader reader(elf.get()); - if (!reader.LoadIntoMemory()) - return false; - - const bool is_wii = IsElfWii(filename); - if (is_wii) - HID4.SBE = 1; - SetupMSR(); - SetupBAT(is_wii); - - if (!reader.LoadSymbols()) - { - if (LoadMapFromFilename()) - HLE::PatchFunctions(); - } - else - { - HLE::PatchFunctions(); - } - - PC = reader.GetEntryPoint(); - - return true; -} diff --git a/Source/Core/Core/Boot/Boot_WiiWAD.cpp b/Source/Core/Core/Boot/Boot_WiiWAD.cpp index 54501a163831..889496ccb2c9 100644 --- a/Source/Core/Core/Boot/Boot_WiiWAD.cpp +++ b/Source/Core/Core/Boot/Boot_WiiWAD.cpp @@ -11,6 +11,7 @@ #include "Common/CommonPaths.h" #include "Common/CommonTypes.h" #include "Common/FileUtil.h" +#include "Common/MsgHandler.h" #include "Common/NandPaths.h" #include "Core/Boot/Boot.h" @@ -78,6 +79,13 @@ bool CBoot::Boot_WiiWAD(const std::string& _pFilename) return false; u64 titleID = ContentLoader.GetTMD().GetTitleId(); + + if (!IOS::ES::IsChannel(titleID)) + { + PanicAlertT("This WAD is not bootable."); + return false; + } + // create data directory File::CreateFullPath(Common::GetTitleDataPath(titleID, Common::FROM_SESSION_ROOT)); diff --git a/Source/Core/Core/Boot/Boot_DOL.cpp b/Source/Core/Core/Boot/DolReader.cpp similarity index 75% rename from Source/Core/Core/Boot/Boot_DOL.cpp rename to Source/Core/Core/Boot/DolReader.cpp index b86169744da0..7f17696240b8 100644 --- a/Source/Core/Core/Boot/Boot_DOL.cpp +++ b/Source/Core/Core/Boot/DolReader.cpp @@ -2,7 +2,7 @@ // Licensed under GPLv2+ // Refer to the license.txt file included. -#include "Core/Boot/Boot_DOL.h" +#include "Core/Boot/DolReader.h" #include #include @@ -12,29 +12,19 @@ #include "Common/Swap.h" #include "Core/HW/Memmap.h" -CDolLoader::CDolLoader(const std::vector& buffer) +DolReader::DolReader(const std::vector& buffer) : BootExecutableReader(buffer) { m_is_valid = Initialize(buffer); } -CDolLoader::CDolLoader(const std::string& filename) +DolReader::DolReader(const std::string& filename) : BootExecutableReader(filename) { - const u64 size = File::GetSize(filename); - std::vector temp_buffer(size); - - { - File::IOFile pStream(filename, "rb"); - pStream.ReadBytes(temp_buffer.data(), temp_buffer.size()); - } - - m_is_valid = Initialize(temp_buffer); + m_is_valid = Initialize(m_bytes); } -CDolLoader::~CDolLoader() -{ -} +DolReader::~DolReader() = default; -bool CDolLoader::Initialize(const std::vector& buffer) +bool DolReader::Initialize(const std::vector& buffer) { if (buffer.size() < sizeof(SDolHeader)) return false; @@ -97,17 +87,30 @@ bool CDolLoader::Initialize(const std::vector& buffer) return true; } -void CDolLoader::Load() const +bool DolReader::LoadIntoMemory(bool only_in_mem1) const { + if (!m_is_valid) + return false; + // load all text (code) sections for (size_t i = 0; i < m_text_sections.size(); ++i) - if (!m_text_sections[i].empty()) + if (!m_text_sections[i].empty() && + !(only_in_mem1 && + m_dolheader.textAddress[i] + m_text_sections[i].size() >= Memory::REALRAM_SIZE)) + { Memory::CopyToEmu(m_dolheader.textAddress[i], m_text_sections[i].data(), m_text_sections[i].size()); + } // load all data sections for (size_t i = 0; i < m_data_sections.size(); ++i) - if (!m_data_sections[i].empty()) + if (!m_data_sections[i].empty() && + !(only_in_mem1 && + m_dolheader.dataAddress[i] + m_data_sections[i].size() >= Memory::REALRAM_SIZE)) + { Memory::CopyToEmu(m_dolheader.dataAddress[i], m_data_sections[i].data(), m_data_sections[i].size()); + } + + return true; } diff --git a/Source/Core/Core/Boot/Boot_DOL.h b/Source/Core/Core/Boot/DolReader.h similarity index 63% rename from Source/Core/Core/Boot/Boot_DOL.h rename to Source/Core/Core/Boot/DolReader.h index f415e35705d3..43b1c0f354c0 100644 --- a/Source/Core/Core/Boot/Boot_DOL.h +++ b/Source/Core/Core/Boot/DolReader.h @@ -8,20 +8,20 @@ #include #include "Common/CommonTypes.h" +#include "Core/Boot/Boot.h" -class CDolLoader +class DolReader final : public BootExecutableReader { public: - CDolLoader(const std::string& filename); - CDolLoader(const std::vector& buffer); - ~CDolLoader(); - - bool IsValid() const { return m_is_valid; } - bool IsWii() const { return m_is_wii; } - u32 GetEntryPoint() const { return m_dolheader.entryPoint; } - // Load into emulated memory - void Load() const; - + DolReader(const std::string& filename); + DolReader(const std::vector& buffer); + ~DolReader(); + + bool IsValid() const override { return m_is_valid; } + bool IsWii() const override { return m_is_wii; } + u32 GetEntryPoint() const override { return m_dolheader.entryPoint; } + bool LoadIntoMemory(bool only_in_mem1 = false) const override; + bool LoadSymbols() const override { return false; } private: enum { diff --git a/Source/Core/Core/Boot/ElfReader.cpp b/Source/Core/Core/Boot/ElfReader.cpp index 91eeba7487b0..4f9297b06deb 100644 --- a/Source/Core/Core/Boot/ElfReader.cpp +++ b/Source/Core/Core/Boot/ElfReader.cpp @@ -66,7 +66,17 @@ static void byteswapSection(Elf32_Shdr& sec) bswap(sec.sh_type); } -ElfReader::ElfReader(void* ptr) +ElfReader::ElfReader(const std::vector& buffer) : BootExecutableReader(buffer) +{ + Initialize(m_bytes.data()); +} + +ElfReader::ElfReader(const std::string& filename) : BootExecutableReader(filename) +{ + Initialize(m_bytes.data()); +} + +void ElfReader::Initialize(u8* ptr) { base = (char*)ptr; base32 = (u32*)ptr; @@ -86,6 +96,8 @@ ElfReader::ElfReader(void* ptr) byteswapSection(sections[i]); } entryPoint = header->e_entry; + + bRelocate = (header->e_type != ET_EXEC); } const char* ElfReader::GetSectionName(int section) const @@ -103,13 +115,10 @@ const char* ElfReader::GetSectionName(int section) const } // This is just a simple elf loader, good enough to load elfs generated by devkitPPC -bool ElfReader::LoadIntoMemory(bool only_in_mem1) +bool ElfReader::LoadIntoMemory(bool only_in_mem1) const { INFO_LOG(MASTER_LOG, "String section: %i", header->e_shstrndx); - // Should we relocate? - bRelocate = (header->e_type != ET_EXEC); - if (bRelocate) { PanicAlert("Error: Dolphin doesn't know how to load a relocatable elf."); @@ -160,7 +169,7 @@ SectionID ElfReader::GetSectionByName(const char* name, int firstSection) const return -1; } -bool ElfReader::LoadSymbols() +bool ElfReader::LoadSymbols() const { bool hasSymbols = false; SectionID sec = GetSectionByName(".symtab"); @@ -205,3 +214,31 @@ bool ElfReader::LoadSymbols() g_symbolDB.Index(); return hasSymbols; } + +bool ElfReader::IsWii() const +{ + // Use the same method as the DOL loader uses: search for mfspr from HID4, + // which should only be used in Wii ELFs. + // + // Likely to have some false positives/negatives, patches implementing a + // better heuristic are welcome. + + // Swap these once, instead of swapping every word in the file. + u32 HID4_pattern = Common::swap32(0x7c13fba6); + u32 HID4_mask = Common::swap32(0xfc1fffff); + + for (int i = 0; i < GetNumSegments(); ++i) + { + if (IsCodeSegment(i)) + { + u32* code = (u32*)GetSegmentPtr(i); + for (u32 j = 0; j < GetSegmentSize(i) / sizeof(u32); ++j) + { + if ((code[j] & HID4_mask) == HID4_pattern) + return true; + } + } + } + + return false; +} diff --git a/Source/Core/Core/Boot/ElfReader.h b/Source/Core/Core/Boot/ElfReader.h index e3d7c257a8c9..014d0667c3ec 100644 --- a/Source/Core/Core/Boot/ElfReader.h +++ b/Source/Core/Core/Boot/ElfReader.h @@ -5,6 +5,7 @@ #pragma once #include "Common/CommonTypes.h" +#include "Core/Boot/Boot.h" #include "Core/Boot/ElfTypes.h" enum KnownElfTypes @@ -17,31 +18,23 @@ enum KnownElfTypes typedef int SectionID; -class ElfReader +class ElfReader final : public BootExecutableReader { -private: - char* base; - u32* base32; - - Elf32_Ehdr* header; - Elf32_Phdr* segments; - Elf32_Shdr* sections; - - u32* sectionAddrs; - bool bRelocate; - u32 entryPoint; - public: - explicit ElfReader(void* ptr); + ElfReader(const std::string& filename); + ElfReader(const std::vector& buffer); ~ElfReader() {} u32 Read32(int off) const { return base32[off >> 2]; } // Quick accessors ElfType GetType() const { return (ElfType)(header->e_type); } ElfMachine GetMachine() const { return (ElfMachine)(header->e_machine); } - u32 GetEntryPoint() const { return entryPoint; } + u32 GetEntryPoint() const override { return entryPoint; } u32 GetFlags() const { return (u32)(header->e_flags); } - bool LoadIntoMemory(bool only_in_mem1 = false); - bool LoadSymbols(); + bool LoadIntoMemory(bool only_in_mem1 = false) const override; + bool LoadSymbols() const override; + // TODO: actually check for validity. + bool IsValid() const override { return true; } + bool IsWii() const override; int GetNumSegments() const { return (int)(header->e_phnum); } int GetNumSections() const { return (int)(header->e_shnum); } @@ -57,11 +50,24 @@ class ElfReader return nullptr; } bool IsCodeSegment(int segment) const { return segments[segment].p_flags & PF_X; } - const u8* GetSegmentPtr(int segment) { return GetPtr(segments[segment].p_offset); } + const u8* GetSegmentPtr(int segment) const { return GetPtr(segments[segment].p_offset); } int GetSegmentSize(int segment) const { return segments[segment].p_filesz; } u32 GetSectionAddr(SectionID section) const { return sectionAddrs[section]; } int GetSectionSize(SectionID section) const { return sections[section].sh_size; } SectionID GetSectionByName(const char* name, int firstSection = 0) const; //-1 for not found bool DidRelocate() const { return bRelocate; } +private: + void Initialize(u8* bytes); + + char* base; + u32* base32; + + Elf32_Ehdr* header; + Elf32_Phdr* segments; + Elf32_Shdr* sections; + + u32* sectionAddrs; + bool bRelocate; + u32 entryPoint; }; diff --git a/Source/Core/Core/BootManager.cpp b/Source/Core/Core/BootManager.cpp index 6262fd94dd46..7738e0897c49 100644 --- a/Source/Core/Core/BootManager.cpp +++ b/Source/Core/Core/BootManager.cpp @@ -31,6 +31,7 @@ #include "Common/MsgHandler.h" #include "Common/StringUtil.h" +#include "Core/Boot/Boot.h" #include "Core/Config/Config.h" #include "Core/ConfigLoaders/GameConfigLoader.h" #include "Core/ConfigLoaders/NetPlayConfigLoader.h" @@ -222,23 +223,23 @@ static GPUDeterminismMode ParseGPUDeterminismMode(const std::string& mode) } // Boot the ISO or file -bool BootCore(const std::string& filename, SConfig::EBootBS2 type) +bool BootCore(std::unique_ptr boot) { + if (!boot) + return false; + SConfig& StartUp = SConfig::GetInstance(); - StartUp.m_BootType = SConfig::BOOT_ISO; - StartUp.m_strFilename = filename; StartUp.bRunCompareClient = false; StartUp.bRunCompareServer = false; config_cache.SaveConfig(StartUp); - // If for example the ISO file is bad we return here - if (!StartUp.AutoSetup(type)) + if (!StartUp.SetPathsAndGameMetadata(*boot)) return false; // Load game specific settings - if (type == SConfig::BOOT_DEFAULT) + if (!std::holds_alternative(boot->parameters)) { std::string game_id = SConfig::GetInstance().GetGameID(); u16 revision = SConfig::GetInstance().GetRevision(); @@ -400,15 +401,14 @@ bool BootCore(const std::string& filename, SConfig::EBootBS2 type) if (StartUp.bWii) StartUp.SaveSettingsToSysconf(); - // Run the game - // Init the core - if (!Core::Init()) + const bool load_ipl = !StartUp.bWii && !StartUp.bHLE_BS2 && + std::holds_alternative(boot->parameters); + if (load_ipl) { - PanicAlertT("Couldn't init the core.\nCheck your configuration."); - return false; + return Core::Init(std::make_unique(BootParameters::IPL{ + StartUp.m_region, std::move(std::get(boot->parameters))})); } - - return true; + return Core::Init(std::move(boot)); } void Stop() diff --git a/Source/Core/Core/BootManager.h b/Source/Core/Core/BootManager.h index bb7b7cae9976..a068247e505f 100644 --- a/Source/Core/Core/BootManager.h +++ b/Source/Core/Core/BootManager.h @@ -4,13 +4,16 @@ #pragma once +#include #include #include "Core/ConfigManager.h" +struct BootParameters; + namespace BootManager { -bool BootCore(const std::string& filename, SConfig::EBootBS2 type); +bool BootCore(std::unique_ptr parameters); // Stop the emulation core and restore the configuration. void Stop(); diff --git a/Source/Core/Core/CMakeLists.txt b/Source/Core/Core/CMakeLists.txt index e3684b891dbf..b29f42fef8f1 100644 --- a/Source/Core/Core/CMakeLists.txt +++ b/Source/Core/Core/CMakeLists.txt @@ -21,9 +21,8 @@ set(SRCS WiiRoot.cpp Boot/Boot_BS2Emu.cpp Boot/Boot.cpp - Boot/Boot_DOL.cpp - Boot/Boot_ELF.cpp Boot/Boot_WiiWAD.cpp + Boot/DolReader.cpp Boot/ElfReader.cpp Config/Config.cpp Config/GraphicsSettings.cpp diff --git a/Source/Core/Core/ConfigManager.cpp b/Source/Core/Core/ConfigManager.cpp index 400bbfc581e5..b63702b6d2da 100644 --- a/Source/Core/Core/ConfigManager.cpp +++ b/Source/Core/Core/ConfigManager.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include "AudioCommon/AudioCommon.h" @@ -22,7 +23,6 @@ #include "Core/Analytics.h" #include "Core/Boot/Boot.h" -#include "Core/Boot/Boot_DOL.h" #include "Core/Config/Config.h" #include "Core/ConfigManager.h" #include "Core/Core.h" @@ -753,6 +753,20 @@ void SConfig::SetRunningGameMetadata(const std::string& game_id, u64 title_id, u m_title_id = title_id; m_revision = revision; + if (game_id.length() == 6) + { + m_debugger_game_id = game_id; + } + else if (title_id != 0) + { + m_debugger_game_id = + StringFromFormat("%08X_%08X", static_cast(title_id >> 32), static_cast(title_id)); + } + else + { + m_debugger_game_id.clear(); + } + if (!was_changed) return; @@ -881,165 +895,98 @@ std::string SConfig::GetBootROMPath(const std::string& region_directory) const return path; } -// Sets m_region to the region parameter, or to PAL if the region parameter -// is invalid. Set directory_name to the value returned by GetDirectoryForRegion -// for m_region. Returns false if the region parameter is invalid. -bool SConfig::SetRegion(DiscIO::Region region, std::string* directory_name) +struct SetGameMetadata { - const char* retrieved_region_dir = GetDirectoryForRegion(region); - m_region = retrieved_region_dir ? region : DiscIO::Region::PAL; - *directory_name = retrieved_region_dir ? retrieved_region_dir : EUR_DIR; - return !!retrieved_region_dir; -} - -bool SConfig::AutoSetup(EBootBS2 _BootBS2) -{ - std::string set_region_dir(EUR_DIR); - - switch (_BootBS2) + SetGameMetadata(SConfig* config_, DiscIO::Region* region_) : config(config_), region(region_) {} + bool operator()(const BootParameters::Disc& disc) const { - case BOOT_DEFAULT: + config->SetRunningGameMetadata(*disc.volume, disc.volume->GetGamePartition()); + config->bWii = disc.volume->GetVolumeType() == DiscIO::Platform::WII_DISC; + config->m_disc_booted_from_game_list = true; + *region = disc.volume->GetRegion(); + return true; + } + + bool operator()(const BootParameters::Executable& executable) const { - bool bootDrive = cdio_is_cdrom(m_strFilename); - // Check if the file exist, we may have gotten it from a --elf command line - // that gave an incorrect file name - if (!bootDrive && !File::Exists(m_strFilename)) - { - PanicAlertT("The specified file \"%s\" does not exist", m_strFilename.c_str()); + if (!executable.reader->IsValid()) return false; - } - std::string Extension; - SplitPath(m_strFilename, nullptr, nullptr, &Extension); - if (!strcasecmp(Extension.c_str(), ".gcm") || !strcasecmp(Extension.c_str(), ".iso") || - !strcasecmp(Extension.c_str(), ".tgc") || !strcasecmp(Extension.c_str(), ".wbfs") || - !strcasecmp(Extension.c_str(), ".ciso") || !strcasecmp(Extension.c_str(), ".gcz") || - bootDrive) - { - m_BootType = BOOT_ISO; - std::unique_ptr pVolume(DiscIO::CreateVolumeFromFilename(m_strFilename)); - if (pVolume == nullptr) - { - if (bootDrive) - PanicAlertT("Could not read \"%s\". " - "There is no disc in the drive or it is not a GameCube/Wii backup. " - "Please note that Dolphin cannot play games directly from the original " - "GameCube and Wii discs.", - m_strFilename.c_str()); - else - PanicAlertT("\"%s\" is an invalid GCM/ISO file, or is not a GC/Wii ISO.", - m_strFilename.c_str()); - return false; - } - SetRunningGameMetadata(*pVolume, pVolume->GetGamePartition()); + config->bWii = executable.reader->IsWii(); - // Check if we have a Wii disc - bWii = pVolume->GetVolumeType() == DiscIO::Platform::WII_DISC; + // TODO: Right now GC homebrew boots in NTSC and Wii homebrew in PAL. + // This is intentional so that Wii homebrew can boot in both 50Hz and 60Hz, + // without forcing all GC homebrew to 50Hz. + // In the future, it probably makes sense to add a Region setting for homebrew somewhere in + // the emulator config. + *region = config->bWii ? DiscIO::Region::PAL : DiscIO::Region::NTSC_U; - if (!SetRegion(pVolume->GetRegion(), &set_region_dir)) - if (!PanicYesNoT("Your GCM/ISO file seems to be invalid (invalid country)." - "\nContinue with PAL region?")) - return false; - } - else if (!strcasecmp(Extension.c_str(), ".elf")) - { - bWii = CBoot::IsElfWii(m_strFilename); - // TODO: Right now GC homebrew boots in NTSC and Wii homebrew in PAL. - // This is intentional so that Wii homebrew can boot in both 50Hz and 60Hz, without forcing - // all GC homebrew to 50Hz. - // In the future, it probably makes sense to add a Region setting for homebrew somewhere in - // the emulator config. - SetRegion(bWii ? DiscIO::Region::PAL : DiscIO::Region::NTSC_U, &set_region_dir); - m_BootType = BOOT_ELF; - } - else if (!strcasecmp(Extension.c_str(), ".dol")) - { - CDolLoader dolfile(m_strFilename); - bWii = dolfile.IsWii(); - // TODO: See the ELF code above. - SetRegion(bWii ? DiscIO::Region::PAL : DiscIO::Region::NTSC_U, &set_region_dir); - m_BootType = BOOT_DOL; - } - else if (!strcasecmp(Extension.c_str(), ".dff")) - { - bWii = true; - SetRegion(DiscIO::Region::NTSC_U, &set_region_dir); - m_BootType = BOOT_DFF; + // Strip the .elf/.dol file extension and directories before the name + SplitPath(executable.path, nullptr, &config->m_debugger_game_id, nullptr); + return true; + } - std::unique_ptr ddfFile(FifoDataFile::Load(m_strFilename, true)); + bool operator()(const BootParameters::NAND& nand) const + { + const auto& loader = DiscIO::NANDContentManager::Access().GetNANDLoader(nand.content_path); + if (!loader.IsValid()) + return false; - if (ddfFile) - { - bWii = ddfFile->GetIsWii(); - } - } - else if (DiscIO::NANDContentManager::Access().GetNANDLoader(m_strFilename).IsValid()) - { - const DiscIO::NANDContentLoader& content_loader = - DiscIO::NANDContentManager::Access().GetNANDLoader(m_strFilename); - const IOS::ES::TMDReader& tmd = content_loader.GetTMD(); + const IOS::ES::TMDReader& tmd = loader.GetTMD(); - if (!IOS::ES::IsChannel(tmd.GetTitleId())) - { - PanicAlertT("This WAD is not bootable."); - return false; - } + config->SetRunningGameMetadata(tmd); + config->bWii = true; + *region = tmd.GetRegion(); + return true; + } - SetRegion(tmd.GetRegion(), &set_region_dir); - SetRunningGameMetadata(tmd); + bool operator()(const BootParameters::IPL& ipl) const + { + config->bWii = false; + *region = ipl.region; + return true; + } - bWii = true; - m_BootType = BOOT_WII_NAND; - } - else - { - PanicAlertT("Could not recognize ISO file %s", m_strFilename.c_str()); + bool operator()(const BootParameters::DFF& dff) const + { + std::unique_ptr dff_file(FifoDataFile::Load(dff.dff_path, true)); + if (!dff_file) return false; - } + + config->bWii = dff_file->GetIsWii(); + *region = DiscIO::Region::NTSC_U; + return true; } - break; - - case BOOT_BS2_USA: - SetRegion(DiscIO::Region::NTSC_U, &set_region_dir); - m_strFilename.clear(); - m_BootType = SConfig::BOOT_BS2; - break; - - case BOOT_BS2_JAP: - SetRegion(DiscIO::Region::NTSC_J, &set_region_dir); - m_strFilename.clear(); - m_BootType = SConfig::BOOT_BS2; - break; - - case BOOT_BS2_EUR: - SetRegion(DiscIO::Region::PAL, &set_region_dir); - m_strFilename.clear(); - m_BootType = SConfig::BOOT_BS2; - break; + +private: + SConfig* config; + DiscIO::Region* region; +}; + +bool SConfig::SetPathsAndGameMetadata(const BootParameters& boot) +{ + m_is_mios = false; + m_disc_booted_from_game_list = false; + DiscIO::Region region; + if (!std::visit(SetGameMetadata(this, ®ion), boot.parameters)) + return false; + + // Set up region + const char* retrieved_region_dir = GetDirectoryForRegion(region); + m_region = retrieved_region_dir ? region : DiscIO::Region::PAL; + const std::string set_region_dir = retrieved_region_dir ? retrieved_region_dir : EUR_DIR; + if (!retrieved_region_dir && + !PanicYesNoT("Your GCM/ISO file seems to be invalid (invalid country)." + "\nContinue with PAL region?")) + { + return false; } - // Setup paths + // Set up paths CheckMemcardPath(SConfig::GetInstance().m_strMemoryCardA, set_region_dir, true); CheckMemcardPath(SConfig::GetInstance().m_strMemoryCardB, set_region_dir, false); m_strSRAM = File::GetUserPath(F_GCSRAM_IDX); - if (!bWii) - { - if (!bHLE_BS2) - { - m_strBootROM = GetBootROMPath(set_region_dir); - - if (!File::Exists(m_strBootROM)) - { - WARN_LOG(BOOT, "Bootrom file %s not found - using HLE.", m_strBootROM.c_str()); - bHLE_BS2 = true; - } - } - } - else if (bWii && !bHLE_BS2) - { - WARN_LOG(BOOT, "GC bootrom file will not be loaded for Wii mode."); - bHLE_BS2 = true; - } + m_strBootROM = GetBootROMPath(set_region_dir); return true; } diff --git a/Source/Core/Core/ConfigManager.h b/Source/Core/Core/ConfigManager.h index 33e6009ec626..69947125fc3c 100644 --- a/Source/Core/Core/ConfigManager.h +++ b/Source/Core/Core/ConfigManager.h @@ -51,6 +51,8 @@ enum GPUDeterminismMode GPU_DETERMINISM_FAKE_COMPLETION, }; +struct BootParameters; + struct SConfig : NonCopyable { // Wii Devices @@ -129,6 +131,7 @@ struct SConfig : NonCopyable bool bOverrideGCLanguage = false; bool bWii = false; + bool m_is_mios = false; // Interface settings bool bConfirmStop = false; @@ -181,26 +184,6 @@ struct SConfig : NonCopyable bool bEnableCustomRTC; u32 m_customRTCValue; - enum EBootBS2 - { - BOOT_DEFAULT, - BOOT_BS2_JAP, - BOOT_BS2_USA, - BOOT_BS2_EUR, - }; - - enum EBootType - { - BOOT_ISO, - BOOT_ELF, - BOOT_DOL, - BOOT_WII_NAND, - BOOT_MIOS, - BOOT_BS2, - BOOT_DFF - }; - - EBootType m_BootType; DiscIO::Region m_region; std::string m_strVideoBackend; @@ -210,7 +193,6 @@ struct SConfig : NonCopyable GPUDeterminismMode m_GPUDeterminismMode; // files - std::string m_strFilename; std::string m_strBootROM; std::string m_strSRAM; std::string m_strDefaultISO; @@ -220,6 +202,10 @@ struct SConfig : NonCopyable std::string m_perfDir; + std::string m_debugger_game_id; + // TODO: remove this as soon as the ticket view hack in IOS/ES/Views is dropped. + bool m_disc_booted_from_game_list = false; + const std::string& GetGameID() const { return m_game_id; } const std::string& GetTitleDescription() const { return m_title_description; } u64 GetTitleID() const { return m_title_id; } @@ -231,7 +217,7 @@ struct SConfig : NonCopyable void LoadDefaults(); static const char* GetDirectoryForRegion(DiscIO::Region region); std::string GetBootROMPath(const std::string& region_directory) const; - bool AutoSetup(EBootBS2 _BootBS2); + bool SetPathsAndGameMetadata(const BootParameters& boot); void CheckMemcardPath(std::string& memcardPath, const std::string& gameRegion, bool isSlotA); DiscIO::Language GetCurrentLanguage(bool wii) const; @@ -389,7 +375,6 @@ struct SConfig : NonCopyable void SetRunningGameMetadata(const std::string& game_id, u64 title_id, u16 revision, Core::TitleDatabase::TitleType type); - bool SetRegion(DiscIO::Region region, std::string* directory_name); static SConfig* m_Instance; diff --git a/Source/Core/Core/Core.cpp b/Source/Core/Core/Core.cpp index 122289c5b068..bdddfbadb406 100644 --- a/Source/Core/Core/Core.cpp +++ b/Source/Core/Core/Core.cpp @@ -10,6 +10,7 @@ #include #include #include +#include #ifdef _WIN32 #include @@ -122,7 +123,7 @@ static void InitIsCPUKey() } #endif -static void EmuThread(); +static void EmuThread(std::unique_ptr boot); bool GetIsThrottlerTempDisabled() { @@ -221,7 +222,7 @@ bool WantsDeterminism() // This is called from the GUI thread. See the booting call schedule in // BootManager.cpp -bool Init() +bool Init(std::unique_ptr boot) { if (s_emu_thread.joinable()) { @@ -248,7 +249,7 @@ bool Init() s_window_handle = Host_GetRenderHandle(); // Start the emu thread - s_emu_thread = std::thread(EmuThread); + s_emu_thread = std::thread(EmuThread, std::move(boot)); return true; } @@ -412,21 +413,18 @@ static void FifoPlayerThread() } // Enter CPU run loop. When we leave it - we are done. - if (FifoPlayer::GetInstance().Open(_CoreParameter.m_strFilename)) + if (auto cpu_core = FifoPlayer::GetInstance().GetCPUCore()) { - if (auto cpu_core = FifoPlayer::GetInstance().GetCPUCore()) - { - PowerPC::InjectExternalCPUCore(cpu_core.get()); - s_is_started = true; + PowerPC::InjectExternalCPUCore(cpu_core.get()); + s_is_started = true; - CPUSetInitialExecutionState(); - CPU::Run(); + CPUSetInitialExecutionState(); + CPU::Run(); - s_is_started = false; - PowerPC::InjectExternalCPUCore(nullptr); - } - FifoPlayer::GetInstance().Close(); + s_is_started = false; + PowerPC::InjectExternalCPUCore(nullptr); } + FifoPlayer::GetInstance().Close(); // If we did not enter the CPU Run Loop above then run a fake one instead. // We need to be IsRunningAndStarted() for DolphinWX to stop us. @@ -450,7 +448,7 @@ static void FifoPlayerThread() // Initialize and create emulation thread // Call browser: Init():s_emu_thread(). // See the BootManager.cpp file description for a complete call schedule. -static void EmuThread() +static void EmuThread(std::unique_ptr boot) { const SConfig& core_parameter = SConfig::GetInstance(); s_is_booting.Set(); @@ -474,12 +472,11 @@ static void EmuThread() DisplayMessage("WARNING: running at non-native CPU clock! Game may not be stable.", 8000); DisplayMessage(cpu_info.brand_string, 8000); DisplayMessage(cpu_info.Summarize(), 8000); - DisplayMessage(core_parameter.m_strFilename, 3000); // For a time this acts as the CPU thread... DeclareAsCPUThread(); - Movie::Init(); + Movie::Init(*boot); Common::ScopeGuard movie_guard{Movie::Shutdown}; HW::Init(); @@ -560,7 +557,15 @@ static void EmuThread() // Load GCM/DOL/ELF whatever ... we boot with the interpreter core PowerPC::SetMode(PowerPC::CoreMode::Interpreter); - CBoot::BootUp(); + // Determine the CPU thread function + void (*cpuThreadFunc)(); + if (std::holds_alternative(boot->parameters)) + cpuThreadFunc = FifoPlayerThread; + else + cpuThreadFunc = CpuThread; + + if (!CBoot::BootUp(std::move(boot))) + return; // This adds the SyncGPU handler to CoreTiming, so now CoreTiming::Advance might block. Fifo::Prepare(); @@ -583,13 +588,6 @@ static void EmuThread() Host_UpdateDisasmDialog(); Host_UpdateMainFrame(); - // Determine the CPU thread function - void (*cpuThreadFunc)(void); - if (core_parameter.m_BootType == SConfig::BOOT_DFF) - cpuThreadFunc = FifoPlayerThread; - else - cpuThreadFunc = CpuThread; - // ENTER THE VIDEO THREAD LOOP if (core_parameter.bCPUThread) { diff --git a/Source/Core/Core/Core.h b/Source/Core/Core/Core.h index 89bdedb25c33..78b5f5d6c766 100644 --- a/Source/Core/Core/Core.h +++ b/Source/Core/Core/Core.h @@ -11,11 +11,14 @@ #pragma once #include +#include #include #include #include "Common/CommonTypes.h" +struct BootParameters; + namespace Core { bool GetIsThrottlerTempDisabled(); @@ -31,7 +34,7 @@ enum class State Stopping }; -bool Init(); +bool Init(std::unique_ptr boot); void Stop(); void Shutdown(); diff --git a/Source/Core/Core/Core.vcxproj b/Source/Core/Core/Core.vcxproj index 6616031f964f..7fccbc5fed76 100644 --- a/Source/Core/Core/Core.vcxproj +++ b/Source/Core/Core/Core.vcxproj @@ -52,9 +52,8 @@ - - + @@ -308,7 +307,7 @@ - + @@ -583,4 +582,4 @@ - \ No newline at end of file + diff --git a/Source/Core/Core/Core.vcxproj.filters b/Source/Core/Core/Core.vcxproj.filters index 27485da41f64..a0a74fc073d3 100644 --- a/Source/Core/Core/Core.vcxproj.filters +++ b/Source/Core/Core/Core.vcxproj.filters @@ -182,13 +182,10 @@ Boot - - Boot - - + Boot - + Boot @@ -888,7 +885,7 @@ Boot - + Boot diff --git a/Source/Core/Core/HW/EXI/EXI_DeviceIPL.cpp b/Source/Core/Core/HW/EXI/EXI_DeviceIPL.cpp index a4c9c3ad1b6e..0cf6bbfc3897 100644 --- a/Source/Core/Core/HW/EXI/EXI_DeviceIPL.cpp +++ b/Source/Core/Core/HW/EXI/EXI_DeviceIPL.cpp @@ -98,8 +98,18 @@ CEXIIPL::CEXIIPL() : m_uPosition(0), m_uAddress(0), m_uRWOffset(0), m_FontsLoade // Create the IPL m_pIPL = static_cast(Common::AllocateMemoryPages(ROM_SIZE)); - if (SConfig::GetInstance().bHLE_BS2) + // Load whole ROM dump + // Note: The Wii doesn't have a copy of the IPL, only fonts. + if (!SConfig::GetInstance().bWii && LoadFileToIPL(SConfig::GetInstance().m_strBootROM, 0)) { + // Descramble the encrypted section (contains BS1 and BS2) + Descrambler(m_pIPL + 0x100, 0x1afe00); + INFO_LOG(BOOT, "Loaded bootrom: %s", m_pIPL); // yay for null-terminated strings ;p + } + else + { + // If we are in Wii mode or if loading the GC IPL fails, we should still try to load fonts. + // Copy header if (DiscIO::IsNTSC(SConfig::GetInstance().m_region)) memcpy(m_pIPL, iplverNTSC, sizeof(iplverNTSC)); @@ -110,14 +120,6 @@ CEXIIPL::CEXIIPL() : m_uPosition(0), m_uAddress(0), m_uRWOffset(0), m_FontsLoade LoadFontFile((File::GetSysDirectory() + GC_SYS_DIR + DIR_SEP + FONT_SHIFT_JIS), 0x1aff00); LoadFontFile((File::GetSysDirectory() + GC_SYS_DIR + DIR_SEP + FONT_WINDOWS_1252), 0x1fcf00); } - else - { - // Load whole ROM dump - LoadFileToIPL(SConfig::GetInstance().m_strBootROM, 0); - // Descramble the encrypted section (contains BS1 and BS2) - Descrambler(m_pIPL + 0x100, 0x1afe00); - INFO_LOG(BOOT, "Loaded bootrom: %s", m_pIPL); // yay for null-terminated strings ;p - } // Clear RTC memset(m_RTC, 0, sizeof(m_RTC)); @@ -157,17 +159,19 @@ void CEXIIPL::DoState(PointerWrap& p) p.Do(m_FontsLoaded); } -void CEXIIPL::LoadFileToIPL(const std::string& filename, u32 offset) +bool CEXIIPL::LoadFileToIPL(const std::string& filename, u32 offset) { File::IOFile stream(filename, "rb"); if (!stream) - return; + return false; u64 filesize = stream.GetSize(); - stream.ReadBytes(m_pIPL + offset, filesize); + if (!stream.ReadBytes(m_pIPL + offset, filesize)) + return false; m_FontsLoaded = true; + return true; } std::string CEXIIPL::FindIPLDump(const std::string& path_prefix) @@ -236,9 +240,7 @@ void CEXIIPL::SetCS(int _iCS) void CEXIIPL::UpdateRTC() { u32 epoch = - (SConfig::GetInstance().bWii || SConfig::GetInstance().m_BootType == SConfig::BOOT_MIOS) ? - WII_EPOCH : - GC_EPOCH; + (SConfig::GetInstance().bWii || SConfig::GetInstance().m_is_mios) ? WII_EPOCH : GC_EPOCH; u32 rtc = Common::swap32(GetEmulatedTime(epoch)); std::memcpy(m_RTC, &rtc, sizeof(u32)); } diff --git a/Source/Core/Core/HW/EXI/EXI_DeviceIPL.h b/Source/Core/Core/HW/EXI/EXI_DeviceIPL.h index 031001e6f67d..ea2d7093e2b4 100644 --- a/Source/Core/Core/HW/EXI/EXI_DeviceIPL.h +++ b/Source/Core/Core/HW/EXI/EXI_DeviceIPL.h @@ -74,7 +74,7 @@ class CEXIIPL : public IEXIDevice void TransferByte(u8& _uByte) override; bool IsWriteCommand() const { return !!(m_uAddress & (1 << 31)); } u32 CommandRegion() const { return (m_uAddress & ~(1 << 31)) >> 8; } - void LoadFileToIPL(const std::string& filename, u32 offset); + bool LoadFileToIPL(const std::string& filename, u32 offset); void LoadFontFile(const std::string& filename, u32 offset); std::string FindIPLDump(const std::string& path_prefix); }; diff --git a/Source/Core/Core/IOS/ES/Views.cpp b/Source/Core/Core/IOS/ES/Views.cpp index 6bdd730c2079..9d1369ca796e 100644 --- a/Source/Core/Core/IOS/ES/Views.cpp +++ b/Source/Core/Core/IOS/ES/Views.cpp @@ -38,7 +38,7 @@ static bool ShouldReturnFakeViewsForIOSes(u64 title_id, const TitleContext& cont const bool ios = IsTitleType(title_id, IOS::ES::TitleType::System) && title_id != TITLEID_SYSMENU; const bool disc_title = context.active && IOS::ES::IsDiscTitle(context.tmd.GetTitleId()); return Core::WantsDeterminism() || - (ios && SConfig::GetInstance().m_BootType == SConfig::BOOT_ISO && disc_title); + (ios && SConfig::GetInstance().m_disc_booted_from_game_list && disc_title); } IPCCommandResult ES::GetTicketViewCount(const IOCtlVRequest& request) diff --git a/Source/Core/Core/IOS/IOS.cpp b/Source/Core/Core/IOS/IOS.cpp index abbc7b2444d9..56cb56ae101d 100644 --- a/Source/Core/Core/IOS/IOS.cpp +++ b/Source/Core/Core/IOS/IOS.cpp @@ -18,7 +18,7 @@ #include "Common/ChunkFile.h" #include "Common/CommonTypes.h" #include "Common/Logging/Log.h" -#include "Core/Boot/Boot_DOL.h" +#include "Core/Boot/DolReader.h" #include "Core/ConfigManager.h" #include "Core/Core.h" #include "Core/CoreTiming.h" @@ -282,14 +282,15 @@ bool Kernel::BootstrapPPC(const DiscIO::NANDContentLoader& content_loader) if (!content) return false; - const auto dol_loader = std::make_unique(content->m_Data->Get()); + const auto dol_loader = std::make_unique(content->m_Data->Get()); if (!dol_loader->IsValid()) return false; if (!SetupMemory(m_title_id, MemorySetupType::Full)) return false; - dol_loader->Load(); + if (!dol_loader->LoadIntoMemory()) + return false; // NAND titles start with address translation off at 0x3400 (via the PPC bootstub) // The state of other CPU registers (like the BAT registers) doesn't matter much diff --git a/Source/Core/Core/IOS/MIOS.cpp b/Source/Core/Core/IOS/MIOS.cpp index ceb51f6f756b..47a4fd5f6dff 100644 --- a/Source/Core/Core/IOS/MIOS.cpp +++ b/Source/Core/Core/IOS/MIOS.cpp @@ -134,8 +134,7 @@ bool Load() return false; } - std::vector elf_bytes = mios.GetElf(); - ElfReader elf{elf_bytes.data()}; + ElfReader elf{mios.GetElf()}; if (!elf.LoadIntoMemory(true)) { PanicAlertT("Failed to load MIOS ELF into memory."); @@ -168,7 +167,7 @@ bool Load() Memory::Write_U32(0x00000000, ADDRESS_INIT_SEMAPHORE); NOTICE_LOG(IOS, "IPL ready."); - SConfig::GetInstance().m_BootType = SConfig::BOOT_MIOS; + SConfig::GetInstance().m_is_mios = true; DVDInterface::UpdateRunningGameMetadata(); return true; } diff --git a/Source/Core/Core/Movie.cpp b/Source/Core/Core/Movie.cpp index 207a047079b8..608638c39a04 100644 --- a/Source/Core/Core/Movie.cpp +++ b/Source/Core/Core/Movie.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include #include "Common/Assert.h" @@ -23,6 +24,7 @@ #include "Common/NandPaths.h" #include "Common/StringUtil.h" #include "Common/Timer.h" +#include "Core/Boot/Boot.h" #include "Core/ConfigManager.h" #include "Core/Core.h" #include "Core/CoreTiming.h" @@ -97,6 +99,8 @@ static std::string s_InputDisplay[8]; static GCManipFunction s_gc_manip_func; static WiiManipFunction s_wii_manip_func; +static std::string s_current_file_name; + // NOTE: Host / CPU Thread static void EnsureTmpInputSize(size_t bound) { @@ -216,11 +220,19 @@ void FrameUpdate() s_bPolled = false; } +static void CheckMD5(); +static void GetMD5(); + // called when game is booting up, even if no movie is active, // but potentially after BeginRecordingInput or PlayInput has been called. // NOTE: EmuThread -void Init() +void Init(const BootParameters& boot) { + if (std::holds_alternative(boot.parameters)) + s_current_file_name = std::get(boot.parameters).path; + else + s_current_file_name.clear(); + s_bPolled = false; s_bFrameStep = false; s_bSaveConfig = false; @@ -1551,8 +1563,11 @@ void GetSettings() static const mbedtls_md_info_t* s_md5_info = mbedtls_md_info_from_type(MBEDTLS_MD_MD5); // NOTE: Entrypoint for own thread -void CheckMD5() +static void CheckMD5() { + if (s_current_file_name.empty()) + return; + for (int i = 0, n = 0; i < 16; ++i) { if (tmpHeader.md5[i] != 0) @@ -1564,7 +1579,7 @@ void CheckMD5() Core::DisplayMessage("Verifying checksum...", 2000); unsigned char gameMD5[16]; - mbedtls_md_file(s_md5_info, SConfig::GetInstance().m_strFilename.c_str(), gameMD5); + mbedtls_md_file(s_md5_info, s_current_file_name.c_str(), gameMD5); if (memcmp(gameMD5, s_MD5, 16) == 0) Core::DisplayMessage("Checksum of current game matches the recorded game.", 2000); @@ -1573,11 +1588,14 @@ void CheckMD5() } // NOTE: Entrypoint for own thread -void GetMD5() +static void GetMD5() { + if (s_current_file_name.empty()) + return; + Core::DisplayMessage("Calculating checksum of game file...", 2000); memset(s_MD5, 0, sizeof(s_MD5)); - mbedtls_md_file(s_md5_info, SConfig::GetInstance().m_strFilename.c_str(), s_MD5); + mbedtls_md_file(s_md5_info, s_current_file_name.c_str(), s_MD5); Core::DisplayMessage("Finished calculating checksum.", 2000); } diff --git a/Source/Core/Core/Movie.h b/Source/Core/Core/Movie.h index 84326dcdb6cc..d56e61eaadc2 100644 --- a/Source/Core/Core/Movie.h +++ b/Source/Core/Core/Movie.h @@ -9,6 +9,8 @@ #include "Common/CommonTypes.h" +struct BootParameters; + struct GCPadStatus; class PointerWrap; struct wiimote_key; @@ -110,7 +112,7 @@ static_assert(sizeof(DTMHeader) == 256, "DTMHeader should be 256 bytes"); void FrameUpdate(); void InputUpdate(); -void Init(); +void Init(const BootParameters& boot); void SetPolledDevice(); @@ -171,8 +173,6 @@ bool PlayWiimote(int wiimote, u8* data, const struct WiimoteEmu::ReportFeatures& void EndPlayInput(bool cont); void SaveRecording(const std::string& filename); void DoState(PointerWrap& p); -void CheckMD5(); -void GetMD5(); void Shutdown(); void CheckPadStatus(GCPadStatus* PadStatus, int controllerID); void CheckWiimoteStatus(int wiimote, u8* data, const struct WiimoteEmu::ReportFeatures& rptf, diff --git a/Source/Core/Core/State.cpp b/Source/Core/Core/State.cpp index a3fffacea6cc..01cbd1d2fe0f 100644 --- a/Source/Core/Core/State.cpp +++ b/Source/Core/Core/State.cpp @@ -156,8 +156,7 @@ static std::string DoState(PointerWrap& p) return version_created_by; } - bool is_wii = - SConfig::GetInstance().bWii || SConfig::GetInstance().m_BootType == SConfig::BOOT_MIOS; + bool is_wii = SConfig::GetInstance().bWii || SConfig::GetInstance().m_is_mios; const bool is_wii_currently = is_wii; p.Do(is_wii); if (is_wii != is_wii_currently) diff --git a/Source/Core/DolphinNoGUI/MainNoGUI.cpp b/Source/Core/DolphinNoGUI/MainNoGUI.cpp index 528f2aa0e01c..988518724a9e 100644 --- a/Source/Core/DolphinNoGUI/MainNoGUI.cpp +++ b/Source/Core/DolphinNoGUI/MainNoGUI.cpp @@ -18,6 +18,7 @@ #include "Common/MsgHandler.h" #include "Core/Analytics.h" +#include "Core/Boot/Boot.h" #include "Core/BootManager.h" #include "Core/ConfigManager.h" #include "Core/Core.h" @@ -422,7 +423,7 @@ int main(int argc, char* argv[]) DolphinAnalytics::Instance()->ReportDolphinStart("nogui"); - if (!BootManager::BootCore(boot_filename, SConfig::BOOT_DEFAULT)) + if (!BootManager::BootCore(BootParameters::GenerateFromFile(boot_filename))) { fprintf(stderr, "Could not boot %s\n", boot_filename.c_str()); return 1; diff --git a/Source/Core/DolphinQt2/MainWindow.cpp b/Source/Core/DolphinQt2/MainWindow.cpp index a8049826f299..0c8b14ef8cda 100644 --- a/Source/Core/DolphinQt2/MainWindow.cpp +++ b/Source/Core/DolphinQt2/MainWindow.cpp @@ -9,6 +9,7 @@ #include "Common/Common.h" +#include "Core/Boot/Boot.h" #include "Core/BootManager.h" #include "Core/ConfigManager.h" #include "Core/Core.h" @@ -287,7 +288,7 @@ void MainWindow::StartGame(const QString& path) return; } // Boot up, show an error if it fails to load the game. - if (!BootManager::BootCore(path.toStdString(), SConfig::BOOT_DEFAULT)) + if (!BootManager::BootCore(BootParameters::GenerateFromFile(path.toStdString()))) { QMessageBox::critical(this, tr("Error"), tr("Failed to init core"), QMessageBox::Ok); return; diff --git a/Source/Core/DolphinWX/Debugger/CodeWindowFunctions.cpp b/Source/Core/DolphinWX/Debugger/CodeWindowFunctions.cpp index ffca4d027326..2452e85b3844 100644 --- a/Source/Core/DolphinWX/Debugger/CodeWindowFunctions.cpp +++ b/Source/Core/DolphinWX/Debugger/CodeWindowFunctions.cpp @@ -173,8 +173,9 @@ void CCodeWindow::OnSymbolsMenu(wxCommandEvent& event) if (!Core::IsRunning()) return; - std::string existing_map_file, writable_map_file, title_id_str; - bool map_exists = CBoot::FindMapFile(&existing_map_file, &writable_map_file, &title_id_str); + const std::string& title_id_str = SConfig::GetInstance().m_debugger_game_id; + std::string existing_map_file, writable_map_file; + bool map_exists = CBoot::FindMapFile(&existing_map_file, &writable_map_file); switch (event.GetId()) { case IDM_CLEAR_SYMBOLS: diff --git a/Source/Core/DolphinWX/Frame.h b/Source/Core/DolphinWX/Frame.h index a0635321f8f2..4d5a7282dd83 100644 --- a/Source/Core/DolphinWX/Frame.h +++ b/Source/Core/DolphinWX/Frame.h @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -28,6 +29,8 @@ #include #endif +struct BootParameters; + // Class declarations class CGameListCtrl; class CCodeWindow; @@ -184,7 +187,7 @@ class CFrame : public CRenderFrame void InitializeTASDialogs(); void InitializeCoreCallbacks(); - void StartGame(const std::string& filename, SConfig::EBootBS2 type = SConfig::BOOT_DEFAULT); + void StartGame(std::unique_ptr boot); void SetDebuggerStartupParameters() const; // Utility diff --git a/Source/Core/DolphinWX/FrameTools.cpp b/Source/Core/DolphinWX/FrameTools.cpp index 22f0096cb61b..8ac6fd7e0802 100644 --- a/Source/Core/DolphinWX/FrameTools.cpp +++ b/Source/Core/DolphinWX/FrameTools.cpp @@ -30,6 +30,7 @@ #include "Common/NandPaths.h" #include "Common/StringUtil.h" +#include "Core/Boot/Boot.h" #include "Core/BootManager.h" #include "Core/ConfigManager.h" #include "Core/Core.h" @@ -53,6 +54,7 @@ #include "Core/PowerPC/PowerPC.h" #include "Core/State.h" +#include "DiscIO/Enums.h" #include "DiscIO/NANDContentLoader.h" #include "DiscIO/NANDImporter.h" #include "DiscIO/VolumeWad.h" @@ -315,7 +317,7 @@ void CFrame::BootGame(const std::string& filename) } if (!bootfile.empty()) { - StartGame(bootfile); + StartGame(BootParameters::GenerateFromFile(bootfile)); } } @@ -627,7 +629,7 @@ void CFrame::ToggleDisplayMode(bool bFullscreen) } // Prepare the GUI to start the game. -void CFrame::StartGame(const std::string& filename, SConfig::EBootBS2 type) +void CFrame::StartGame(std::unique_ptr boot) { if (m_is_game_loading) return; @@ -705,7 +707,7 @@ void CFrame::StartGame(const std::string& filename, SConfig::EBootBS2 type) SetDebuggerStartupParameters(); - if (!BootManager::BootCore(filename, type)) + if (!BootManager::BootCore(std::move(boot))) { DoFullscreen(false); @@ -1169,17 +1171,17 @@ void CFrame::OnMemcard(wxCommandEvent& WXUNUSED(event)) void CFrame::OnLoadGameCubeIPLJAP(wxCommandEvent&) { - StartGame("", SConfig::BOOT_BS2_JAP); + StartGame(std::make_unique(BootParameters::IPL{DiscIO::Region::NTSC_J})); } void CFrame::OnLoadGameCubeIPLUSA(wxCommandEvent&) { - StartGame("", SConfig::BOOT_BS2_USA); + StartGame(std::make_unique(BootParameters::IPL{DiscIO::Region::NTSC_U})); } void CFrame::OnLoadGameCubeIPLEUR(wxCommandEvent&) { - StartGame("", SConfig::BOOT_BS2_EUR); + StartGame(std::make_unique(BootParameters::IPL{DiscIO::Region::PAL})); } void CFrame::OnExportAllSaves(wxCommandEvent& WXUNUSED(event))