From 4d2fb9b9ba857239187f3cffaa0739abac818874 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Sat, 27 May 2017 12:32:45 +0200 Subject: [PATCH 1/7] Core: Replace BOOT_MIOS with an explicit "is MIOS" flag I didn't know better back then, but the boot type is only supposed to be used for the actual boot params. It shouldn't be used or changed after booting. --- Source/Core/Core/ConfigManager.h | 2 +- Source/Core/Core/HW/EXI/EXI_DeviceIPL.cpp | 4 +--- Source/Core/Core/IOS/MIOS.cpp | 2 +- Source/Core/Core/State.cpp | 3 +-- 4 files changed, 4 insertions(+), 7 deletions(-) diff --git a/Source/Core/Core/ConfigManager.h b/Source/Core/Core/ConfigManager.h index 33e6009ec626..5102d400bae2 100644 --- a/Source/Core/Core/ConfigManager.h +++ b/Source/Core/Core/ConfigManager.h @@ -129,6 +129,7 @@ struct SConfig : NonCopyable bool bOverrideGCLanguage = false; bool bWii = false; + bool m_is_mios = false; // Interface settings bool bConfirmStop = false; @@ -195,7 +196,6 @@ struct SConfig : NonCopyable BOOT_ELF, BOOT_DOL, BOOT_WII_NAND, - BOOT_MIOS, BOOT_BS2, BOOT_DFF }; diff --git a/Source/Core/Core/HW/EXI/EXI_DeviceIPL.cpp b/Source/Core/Core/HW/EXI/EXI_DeviceIPL.cpp index a4c9c3ad1b6e..22544118c55a 100644 --- a/Source/Core/Core/HW/EXI/EXI_DeviceIPL.cpp +++ b/Source/Core/Core/HW/EXI/EXI_DeviceIPL.cpp @@ -236,9 +236,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/IOS/MIOS.cpp b/Source/Core/Core/IOS/MIOS.cpp index ceb51f6f756b..57f9451d43de 100644 --- a/Source/Core/Core/IOS/MIOS.cpp +++ b/Source/Core/Core/IOS/MIOS.cpp @@ -168,7 +168,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/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) From 22992ae41e8f979149f5a1ec895519397fce490a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Sat, 27 May 2017 15:43:40 +0200 Subject: [PATCH 2/7] Boot: Clean up the boot code * Move out boot parameters to a separate struct, which is not part of SConfig/ConfigManager because there is no reason for it to be there. * Move out file name parsing and constructing the appropriate params from paths to a separate function that does that, and only that. * For every different boot type we support, add a proper struct with only the required parameters, with descriptive names and use std::variant to only store what we need. * Clean up the bHLE_BS2 stuff which made no sense sometimes. Now instead of using bHLE_BS2 for two different things, both for storing the user config setting and as a runtime boot parameter, we simply replace the Disc boot params with BootParameters::IPL. * Const correctness so it's clear what can or cannot update the config. * Drop unused parameters and unneeded checks. * Make a few checks a lot more concise. (Looking at you, extension checks for disc images.) * Remove a mildly terrible workaround where we needed to pass an empty string in order to boot the GC IPL without any game inserted. (Not required anymore thanks to std::variant and std::optional.) The motivation for this are multiple: cleaning up and being able to add support for booting an installed NAND title. Without this change, it'd be pretty much impossible to implement that. Also, using std::visit with std::variant makes the compiler do additional type checks: now we're guaranteed that the boot code will handle all boot types and no invalid boot type will be possible. --- Source/Android/jni/MainAndroid.cpp | 3 +- Source/Core/Core/Boot/Boot.cpp | 417 ++++++++++++---------- Source/Core/Core/Boot/Boot.h | 59 ++- Source/Core/Core/Boot/Boot_WiiWAD.cpp | 8 + Source/Core/Core/BootManager.cpp | 26 +- Source/Core/Core/BootManager.h | 5 +- Source/Core/Core/ConfigManager.cpp | 213 ++++------- Source/Core/Core/ConfigManager.h | 28 +- Source/Core/Core/Core.cpp | 48 ++- Source/Core/Core/Core.h | 5 +- Source/Core/Core/HW/EXI/EXI_DeviceIPL.cpp | 3 +- Source/Core/Core/IOS/ES/Views.cpp | 2 +- Source/Core/Core/Movie.cpp | 28 +- Source/Core/Core/Movie.h | 6 +- Source/Core/DolphinNoGUI/MainNoGUI.cpp | 3 +- Source/Core/DolphinQt2/MainWindow.cpp | 3 +- Source/Core/DolphinWX/Frame.h | 5 +- Source/Core/DolphinWX/FrameTools.cpp | 14 +- 18 files changed, 452 insertions(+), 424 deletions(-) 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..43296eb78486 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" @@ -22,6 +25,7 @@ #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 +43,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" || extension == ".dol") + { + return std::make_unique( + Executable{path, extension == ".elf" ? Executable::Type::ELF : Executable::Type::DOL}); + } + + 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 +176,24 @@ void CBoot::UpdateDebugger_MapLoaded() Host_NotifyMapLoaded(); } +// 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) { - 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().GetGameID(); if (writable_map_file) - *writable_map_file = File::GetUserPath(D_MAPS_IDX) + title_id_str + ".map"; + *writable_map_file = File::GetUserPath(D_MAPS_IDX) + game_id + ".map"; if (title_id) - *title_id = title_id_str; + *title_id = game_id; 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 +311,187 @@ 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()(const BootParameters::Disc& disc) const { - PanicAlertT("Warning - starting ISO in wrong console mode!"); - } - - _StartupPara.bWii = volume->GetVolumeType() == DiscIO::Platform::WII_DISC; + NOTICE_LOG(BOOT, "Booting from disc: %s", disc.path.c_str()); + const DiscIO::Volume* volume = SetDisc(DiscIO::CreateVolumeFromFilename(disc.path)); - // We HLE the bootrom if requested or if LLEing it fails - if (_StartupPara.bHLE_BS2 || !Load_BS2(_StartupPara.m_strBootROM)) - EmulatedBS2(_StartupPara.bWii, volume); + if (!volume) + return false; - PatchEngine::LoadPatches(); + if (!EmulatedBS2(config.bWii, volume)) + return false; - // Scan for common HLE functions - if (_StartupPara.bHLE_BS2 && !_StartupPara.bEnableDebugging) - { - PPCAnalyst::FindFunctions(0x80004000, 0x811fffff, &g_symbolDB); - SignatureDB db(SignatureDB::HandlerType::DSY); - if (db.Load(File::GetSysDirectory() + TOTALDB)) + // Scan for common HLE functions + if (!config.bEnableDebugging) { - db.Apply(&g_symbolDB); - HLE::PatchFunctions(); - db.Clear(); + PPCAnalyst::FindFunctions(0x80004000, 0x811fffff, &g_symbolDB); + SignatureDB db(SignatureDB::HandlerType::DSY); + if (db.Load(File::GetSysDirectory() + TOTALDB)) + { + db.Apply(&g_symbolDB); + HLE::PatchFunctions(); + db.Clear(); + } } - } - // Try to load the symbol map if there is one, and then scan it for - // and eventually replace code - if (LoadMapFromFilename()) - HLE::PatchFunctions(); - - break; - } - - case SConfig::BOOT_DOL: - { - CDolLoader dolLoader(_StartupPara.m_strFilename); - if (!dolLoader.IsValid()) - return false; + // Try to load the symbol map if there is one, and then scan it for + // and eventually replace code + if (LoadMapFromFilename()) + HLE::PatchFunctions(); - // 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!"); + 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::Executable& executable) 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 executable: %s", executable.path.c_str()); - // Poor man's bootup - if (dolWii) - { - HID4.SBE = 1; - SetupMSR(); - SetupBAT(dolWii); + // TODO: needs more cleanup. + if (executable.type == BootParameters::Executable::Type::DOL) + { + CDolLoader dolLoader(executable.path); + if (!dolLoader.IsValid()) + return false; + + const DiscIO::Volume* volume = nullptr; + 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)); + } + + // 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); + } + + Load_FST(config.bWii, volume); + dolLoader.Load(); + PC = dolLoader.GetEntryPoint(); + + if (LoadMapFromFilename()) + HLE::PatchFunctions(); + } + + if (executable.type == BootParameters::Executable::Type::ELF) + { + const DiscIO::Volume* volume = SetDefaultDisc(); + + // Poor man's bootup + if (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); + } + + Load_FST(config.bWii, volume); + if (!Boot_ELF(executable.path)) + return false; + + // Note: Boot_ELF calls HLE::PatchFunctions() + + UpdateDebugger_MapLoaded(); + Dolphin_Debugger::AddAutoBreakpoints(); + } - // 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); + return true; } - else + + bool operator()(const BootParameters::NAND& nand) const { - EmulatedBS2_GC(volume, true); + NOTICE_LOG(BOOT, "Booting from NAND: %s", nand.content_path.c_str()); + SetDefaultDisc(); + return Boot_WiiWAD(nand.content_path); } - Load_FST(dolWii, volume); - dolLoader.Load(); - PC = dolLoader.GetEntryPoint(); + bool operator()(const BootParameters::IPL& ipl) const + { + 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(); - - // 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)); + private: + const SConfig& config; + }; - 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; } diff --git a/Source/Core/Core/Boot/Boot.h b/Source/Core/Core/Boot/Boot.h index 42c71b199f51..742bb056e52a 100644 --- a/Source/Core/Core/Boot/Boot.h +++ b/Source/Core/Core/Boot/Boot.h @@ -5,15 +5,13 @@ #pragma once #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,10 +21,57 @@ struct RegionSetting const std::string code; }; +struct BootParameters +{ + struct Disc + { + std::string path; + std::unique_ptr volume; + }; + + struct Executable + { + enum class Type + { + DOL, + ELF, + }; + std::string path; + Type type; + }; + + 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 BootUp(std::unique_ptr boot); static bool IsElfWii(const std::string& filename); // Tries to find a map file for the current game by looking first in the 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/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/ConfigManager.cpp b/Source/Core/Core/ConfigManager.cpp index 400bbfc581e5..342cb605279c 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" @@ -881,165 +882,95 @@ 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); + SetGameMetadata(SConfig* config_, DiscIO::Region* region_) : config(config_), region(region_) {} + bool operator()(const BootParameters::Disc& disc) const + { + 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; + } - switch (_BootBS2) + bool operator()(const BootParameters::Executable& executable) const { - case BOOT_DEFAULT: + if (executable.type == BootParameters::Executable::Type::DOL) + config->bWii = CDolLoader{executable.path}.IsWii(); + if (executable.type == BootParameters::Executable::Type::ELF) + config->bWii = CBoot::IsElfWii(executable.path); + + // 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; + return true; + } + + bool operator()(const BootParameters::NAND& nand) 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()); + const auto& loader = DiscIO::NANDContentManager::Access().GetNANDLoader(nand.content_path); + if (!loader.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()); + const IOS::ES::TMDReader& tmd = loader.GetTMD(); - // Check if we have a Wii disc - bWii = pVolume->GetVolumeType() == DiscIO::Platform::WII_DISC; + config->SetRunningGameMetadata(tmd); + config->bWii = true; + *region = tmd.GetRegion(); + return true; + } - 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; + bool operator()(const BootParameters::IPL& ipl) const + { + config->bWii = false; + *region = ipl.region; + return true; + } - std::unique_ptr ddfFile(FifoDataFile::Load(m_strFilename, true)); + bool operator()(const BootParameters::DFF& dff) const + { + std::unique_ptr dff_file(FifoDataFile::Load(dff.dff_path, true)); + if (!dff_file) + 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(); + config->bWii = dff_file->GetIsWii(); + *region = DiscIO::Region::NTSC_U; + return true; + } - if (!IOS::ES::IsChannel(tmd.GetTitleId())) - { - PanicAlertT("This WAD is not bootable."); - return false; - } +private: + SConfig* config; + DiscIO::Region* region; +}; - SetRegion(tmd.GetRegion(), &set_region_dir); - SetRunningGameMetadata(tmd); +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; - bWii = true; - m_BootType = BOOT_WII_NAND; - } - else - { - PanicAlertT("Could not recognize ISO file %s", m_strFilename.c_str()); - return false; - } - } - 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; + // 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 5102d400bae2..3fd85fdfb9a9 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 @@ -182,25 +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_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,9 @@ struct SConfig : NonCopyable std::string m_perfDir; + // 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 +216,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 +374,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/HW/EXI/EXI_DeviceIPL.cpp b/Source/Core/Core/HW/EXI/EXI_DeviceIPL.cpp index 22544118c55a..528ed351f534 100644 --- a/Source/Core/Core/HW/EXI/EXI_DeviceIPL.cpp +++ b/Source/Core/Core/HW/EXI/EXI_DeviceIPL.cpp @@ -98,7 +98,8 @@ 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) + // The Wii doesn't have a copy of the IPL, only fonts. + if (SConfig::GetInstance().bWii || SConfig::GetInstance().bHLE_BS2) { // Copy header if (DiscIO::IsNTSC(SConfig::GetInstance().m_region)) 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/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/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/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)) From 065261dbadab5793d245faed996b4cd26ff58321 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Sun, 28 May 2017 17:12:38 +0200 Subject: [PATCH 3/7] Boot: Unify the ELF and DOL code paths They're essentially the same. To achieve this, this commit unifies DolReader and ElfReader into a common interface for boot executable readers, so the only remaining difference between ELF and DOL is how which volume is inserted. --- Source/Core/Core/Boot/Boot.cpp | 109 +++++++++--------- Source/Core/Core/Boot/Boot.h | 30 +++-- Source/Core/Core/Boot/Boot_ELF.cpp | 89 -------------- .../Core/Boot/{Boot_DOL.cpp => DolReader.cpp} | 41 ++++--- .../Core/Boot/{Boot_DOL.h => DolReader.h} | 22 ++-- Source/Core/Core/Boot/ElfReader.cpp | 49 +++++++- Source/Core/Core/Boot/ElfReader.h | 42 ++++--- Source/Core/Core/CMakeLists.txt | 3 +- Source/Core/Core/ConfigManager.cpp | 9 +- Source/Core/Core/Core.vcxproj | 7 +- Source/Core/Core/Core.vcxproj.filters | 9 +- Source/Core/Core/IOS/IOS.cpp | 7 +- Source/Core/Core/IOS/MIOS.cpp | 3 +- 13 files changed, 191 insertions(+), 229 deletions(-) delete mode 100644 Source/Core/Core/Boot/Boot_ELF.cpp rename Source/Core/Core/Boot/{Boot_DOL.cpp => DolReader.cpp} (75%) rename Source/Core/Core/Boot/{Boot_DOL.h => DolReader.h} (63%) diff --git a/Source/Core/Core/Boot/Boot.cpp b/Source/Core/Core/Boot/Boot.cpp index 43296eb78486..65394fd05fe6 100644 --- a/Source/Core/Core/Boot/Boot.cpp +++ b/Source/Core/Core/Boot/Boot.cpp @@ -21,7 +21,8 @@ #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" @@ -86,11 +87,11 @@ std::unique_ptr BootParameters::GenerateFromFile(const std::stri return std::make_unique(Disc{path, std::move(volume)}); } - if (extension == ".elf" || extension == ".dol") - { - return std::make_unique( - Executable{path, extension == ".elf" ? Executable::Type::ELF : Executable::Type::DOL}); - } + 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}); @@ -371,14 +372,13 @@ bool CBoot::BootUp(std::unique_ptr boot) { NOTICE_LOG(BOOT, "Booting from executable: %s", executable.path.c_str()); - // TODO: needs more cleanup. - if (executable.type == BootParameters::Executable::Type::DOL) - { - CDolLoader dolLoader(executable.path); - if (!dolLoader.IsValid()) - return false; + if (!executable.reader->IsValid()) + return false; - const DiscIO::Volume* volume = nullptr; + const DiscIO::Volume* volume = nullptr; + // VolumeDirectory only works with DOLs. + if (StringEndsWith(executable.path, ".dol")) + { if (!config.m_strDVDRoot.empty()) { NOTICE_LOG(BOOT, "Setting DVDRoot %s", config.m_strDVDRoot.c_str()); @@ -390,57 +390,41 @@ bool CBoot::BootUp(std::unique_ptr boot) NOTICE_LOG(BOOT, "Loading default ISO %s", config.m_strDefaultISO.c_str()); volume = SetDisc(DiscIO::CreateVolumeFromFilename(config.m_strDefaultISO)); } - - // 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); - } - - Load_FST(config.bWii, volume); - dolLoader.Load(); - PC = dolLoader.GetEntryPoint(); - - if (LoadMapFromFilename()) - HLE::PatchFunctions(); } - - if (executable.type == BootParameters::Executable::Type::ELF) + else { - const DiscIO::Volume* volume = SetDefaultDisc(); + volume = SetDefaultDisc(); + } - // Poor man's bootup - if (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); - } + if (!executable.reader->LoadIntoMemory()) + { + PanicAlertT("Failed to load the executable to memory."); + return false; + } - Load_FST(config.bWii, volume); - if (!Boot_ELF(executable.path)) - return false; + // 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); + } - // Note: Boot_ELF calls HLE::PatchFunctions() + Load_FST(config.bWii, volume); + PC = executable.reader->GetEntryPoint(); + if (executable.reader->LoadSymbols() || LoadMapFromFilename()) + { UpdateDebugger_MapLoaded(); - Dolphin_Debugger::AddAutoBreakpoints(); + HLE::PatchFunctions(); } - return true; } @@ -495,3 +479,16 @@ bool CBoot::BootUp(std::unique_ptr boot) 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 742bb056e52a..562c067685a4 100644 --- a/Source/Core/Core/Boot/Boot.h +++ b/Source/Core/Core/Boot/Boot.h @@ -5,9 +5,11 @@ #pragma once #include +#include #include #include #include +#include #include "Common/CommonTypes.h" #include "DiscIO/Enums.h" @@ -21,6 +23,8 @@ struct RegionSetting const std::string code; }; +class BootExecutableReader; + struct BootParameters { struct Disc @@ -31,13 +35,8 @@ struct BootParameters struct Executable { - enum class Type - { - DOL, - ELF, - }; std::string path; - Type type; + std::unique_ptr reader; }; struct NAND @@ -72,7 +71,6 @@ class CBoot { public: static bool BootUp(std::unique_ptr boot); - static bool IsElfWii(const std::string& filename); // Tries to find a map file for the current game by looking first in the // local user directory, then in the shared user directory. @@ -97,7 +95,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(); @@ -111,3 +108,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_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/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 342cb605279c..b94f06e82095 100644 --- a/Source/Core/Core/ConfigManager.cpp +++ b/Source/Core/Core/ConfigManager.cpp @@ -23,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" @@ -896,10 +895,10 @@ struct SetGameMetadata bool operator()(const BootParameters::Executable& executable) const { - if (executable.type == BootParameters::Executable::Type::DOL) - config->bWii = CDolLoader{executable.path}.IsWii(); - if (executable.type == BootParameters::Executable::Type::ELF) - config->bWii = CBoot::IsElfWii(executable.path); + if (!executable.reader->IsValid()) + return false; + + config->bWii = executable.reader->IsWii(); // 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, 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/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 57f9451d43de..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."); From 6f6a18b1b0390a8def05f34b14ffcb99d7baa365 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Sun, 28 May 2017 17:15:30 +0200 Subject: [PATCH 4/7] Boot: Remove unneeded manual HLE function scan Not needed. And the symbols would get overwritten by the symbol map load that is just below. --- Source/Core/Core/Boot/Boot.cpp | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/Source/Core/Core/Boot/Boot.cpp b/Source/Core/Core/Boot/Boot.cpp index 65394fd05fe6..63d249d62543 100644 --- a/Source/Core/Core/Boot/Boot.cpp +++ b/Source/Core/Core/Boot/Boot.cpp @@ -347,19 +347,6 @@ bool CBoot::BootUp(std::unique_ptr boot) if (!EmulatedBS2(config.bWii, volume)) return false; - // Scan for common HLE functions - if (!config.bEnableDebugging) - { - PPCAnalyst::FindFunctions(0x80004000, 0x811fffff, &g_symbolDB); - SignatureDB db(SignatureDB::HandlerType::DSY); - if (db.Load(File::GetSysDirectory() + TOTALDB)) - { - db.Apply(&g_symbolDB); - HLE::PatchFunctions(); - db.Clear(); - } - } - // Try to load the symbol map if there is one, and then scan it for // and eventually replace code if (LoadMapFromFilename()) From d0169fe8db0f11c7837f704241ac7e3f4b327724 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Tue, 30 May 2017 11:16:20 +0200 Subject: [PATCH 5/7] Boot: Re-use the disc volume Saves some disc IO. --- Source/Core/Core/Boot/Boot.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Source/Core/Core/Boot/Boot.cpp b/Source/Core/Core/Boot/Boot.cpp index 63d249d62543..aec784cec3f4 100644 --- a/Source/Core/Core/Boot/Boot.cpp +++ b/Source/Core/Core/Boot/Boot.cpp @@ -336,10 +336,10 @@ bool CBoot::BootUp(std::unique_ptr boot) struct BootTitle { BootTitle() : config(SConfig::GetInstance()) {} - bool operator()(const BootParameters::Disc& disc) const + bool operator()(BootParameters::Disc& disc) const { NOTICE_LOG(BOOT, "Booting from disc: %s", disc.path.c_str()); - const DiscIO::Volume* volume = SetDisc(DiscIO::CreateVolumeFromFilename(disc.path)); + const DiscIO::Volume* volume = SetDisc(std::move(disc.volume)); if (!volume) return false; From 9a3fb858f31e159ab76bee4cb8944489997a44fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Mon, 5 Jun 2017 00:08:58 +0200 Subject: [PATCH 6/7] EXI: Always try to load IPL in GameCube mode I don't see any reason to disable loading the IPL if bHLE_BS2 is disabled. bHLE_BS2 should only cause us not to run the IPL, but not skip loading it in the first place. More importantly, without always loading it, this causes issues when trying to launch only the GC IPL while having bHLE_BS2 = false. --- Source/Core/Core/HW/EXI/EXI_DeviceIPL.cpp | 29 +++++++++++++---------- Source/Core/Core/HW/EXI/EXI_DeviceIPL.h | 2 +- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/Source/Core/Core/HW/EXI/EXI_DeviceIPL.cpp b/Source/Core/Core/HW/EXI/EXI_DeviceIPL.cpp index 528ed351f534..0cf6bbfc3897 100644 --- a/Source/Core/Core/HW/EXI/EXI_DeviceIPL.cpp +++ b/Source/Core/Core/HW/EXI/EXI_DeviceIPL.cpp @@ -98,9 +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)); - // The Wii doesn't have a copy of the IPL, only fonts. - if (SConfig::GetInstance().bWii || 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)); @@ -111,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)); @@ -158,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) 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); }; From d50b4406a66f1d9e09ad095df38fae9d57a3a729 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=A9o=20Lam?= Date: Mon, 5 Jun 2017 13:55:54 +0200 Subject: [PATCH 7/7] Boot: Restore symbol map loading --- Source/Core/Core/Boot/Boot.cpp | 8 ++------ Source/Core/Core/Boot/Boot.h | 5 +---- Source/Core/Core/ConfigManager.cpp | 17 +++++++++++++++++ Source/Core/Core/ConfigManager.h | 1 + .../DolphinWX/Debugger/CodeWindowFunctions.cpp | 5 +++-- 5 files changed, 24 insertions(+), 12 deletions(-) diff --git a/Source/Core/Core/Boot/Boot.cpp b/Source/Core/Core/Boot/Boot.cpp index aec784cec3f4..e428129226cd 100644 --- a/Source/Core/Core/Boot/Boot.cpp +++ b/Source/Core/Core/Boot/Boot.cpp @@ -178,17 +178,13 @@ void CBoot::UpdateDebugger_MapLoaded() } // 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) +bool CBoot::FindMapFile(std::string* existing_map_file, std::string* writable_map_file) { - const std::string game_id = SConfig::GetInstance().GetGameID(); + const std::string& game_id = SConfig::GetInstance().m_debugger_game_id; if (writable_map_file) *writable_map_file = File::GetUserPath(D_MAPS_IDX) + game_id + ".map"; - if (title_id) - *title_id = game_id; - bool found = false; static const std::string maps_directories[] = {File::GetUserPath(D_MAPS_IDX), File::GetSysDirectory() + MAPS_DIR DIR_SEP}; diff --git a/Source/Core/Core/Boot/Boot.h b/Source/Core/Core/Boot/Boot.h index 562c067685a4..a178edaf7542 100644 --- a/Source/Core/Core/Boot/Boot.h +++ b/Source/Core/Core/Boot/Boot.h @@ -81,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: diff --git a/Source/Core/Core/ConfigManager.cpp b/Source/Core/Core/ConfigManager.cpp index b94f06e82095..b63702b6d2da 100644 --- a/Source/Core/Core/ConfigManager.cpp +++ b/Source/Core/Core/ConfigManager.cpp @@ -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; @@ -906,6 +920,9 @@ struct SetGameMetadata // 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; + + // Strip the .elf/.dol file extension and directories before the name + SplitPath(executable.path, nullptr, &config->m_debugger_game_id, nullptr); return true; } diff --git a/Source/Core/Core/ConfigManager.h b/Source/Core/Core/ConfigManager.h index 3fd85fdfb9a9..69947125fc3c 100644 --- a/Source/Core/Core/ConfigManager.h +++ b/Source/Core/Core/ConfigManager.h @@ -202,6 +202,7 @@ 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; 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: