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.
This commit is contained in:
Léo Lam 2017-05-27 15:43:40 +02:00
parent 4d2fb9b9ba
commit 22992ae41e
18 changed files with 468 additions and 440 deletions

View File

@ -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;

View File

@ -4,13 +4,16 @@
#include "Core/Boot/Boot.h"
#include <algorithm>
#include <optional>
#include <string>
#include <unordered_set>
#include <vector>
#include <zlib.h>
#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> 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<std::string> 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<BootParameters>(Disc{path, std::move(volume)});
}
if (extension == ".elf" || extension == ".dol")
{
return std::make_unique<BootParameters>(
Executable{path, extension == ".elf" ? Executable::Type::ELF : Executable::Type::DOL});
}
if (extension == ".dff")
return std::make_unique<BootParameters>(DFF{path});
if (DiscIO::NANDContentManager::Access().GetNANDLoader(path).IsValid())
return std::make_unique<BootParameters>(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,47 +311,43 @@ 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<BootParameters> 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:
BootTitle() : config(SConfig::GetInstance()) {}
bool operator()(const BootParameters::Disc& disc) const
{
const DiscIO::Volume* volume =
SetDisc(DiscIO::CreateVolumeFromFilename(_StartupPara.m_strFilename));
NOTICE_LOG(BOOT, "Booting from disc: %s", disc.path.c_str());
const DiscIO::Volume* volume = SetDisc(DiscIO::CreateVolumeFromFilename(disc.path));
if (!volume)
return false;
if ((volume->GetVolumeType() == DiscIO::Platform::WII_DISC) != _StartupPara.bWii)
{
PanicAlertT("Warning - starting ISO in wrong console mode!");
}
_StartupPara.bWii = volume->GetVolumeType() == DiscIO::Platform::WII_DISC;
// We HLE the bootrom if requested or if LLEing it fails
if (_StartupPara.bHLE_BS2 || !Load_BS2(_StartupPara.m_strBootROM))
EmulatedBS2(_StartupPara.bWii, volume);
PatchEngine::LoadPatches();
if (!EmulatedBS2(config.bWii, volume))
return false;
// Scan for common HLE functions
if (_StartupPara.bHLE_BS2 && !_StartupPara.bEnableDebugging)
if (!config.bEnableDebugging)
{
PPCAnalyst::FindFunctions(0x80004000, 0x811fffff, &g_symbolDB);
SignatureDB db(SignatureDB::HandlerType::DSY);
@ -327,42 +364,39 @@ bool CBoot::BootUp()
if (LoadMapFromFilename())
HLE::PatchFunctions();
break;
return true;
}
case SConfig::BOOT_DOL:
bool operator()(const BootParameters::Executable& executable) const
{
CDolLoader dolLoader(_StartupPara.m_strFilename);
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;
// 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!");
}
const DiscIO::Volume* volume = nullptr;
if (!_StartupPara.m_strDVDRoot.empty())
if (!config.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));
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 (!_StartupPara.m_strDefaultISO.empty())
else if (!config.m_strDefaultISO.empty())
{
NOTICE_LOG(BOOT, "Loading default ISO %s", _StartupPara.m_strDefaultISO.c_str());
volume = SetDisc(DiscIO::CreateVolumeFromFilename(_StartupPara.m_strDefaultISO));
NOTICE_LOG(BOOT, "Loading default ISO %s", config.m_strDefaultISO.c_str());
volume = SetDisc(DiscIO::CreateVolumeFromFilename(config.m_strDefaultISO));
}
// Poor man's bootup
if (dolWii)
if (config.bWii)
{
HID4.SBE = 1;
SetupMSR();
SetupBAT(dolWii);
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.
@ -373,35 +407,20 @@ bool CBoot::BootUp()
EmulatedBS2_GC(volume, true);
}
Load_FST(dolWii, volume);
Load_FST(config.bWii, volume);
dolLoader.Load();
PC = dolLoader.GetEntryPoint();
if (LoadMapFromFilename())
HLE::PatchFunctions();
break;
}
case SConfig::BOOT_ELF:
if (executable.type == BootParameters::Executable::Type::ELF)
{
const DiscIO::Volume* volume = nullptr;
// 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));
}
const DiscIO::Volume* volume = SetDefaultDisc();
// Poor man's bootup
if (_StartupPara.bWii)
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.
@ -412,57 +431,67 @@ bool CBoot::BootUp()
EmulatedBS2_GC(volume, true);
}
Load_FST(_StartupPara.bWii, volume);
if (!Boot_ELF(_StartupPara.m_strFilename))
Load_FST(config.bWii, volume);
if (!Boot_ELF(executable.path))
return false;
// Note: Boot_ELF calls HLE::PatchFunctions()
UpdateDebugger_MapLoaded();
Dolphin_Debugger::AddAutoBreakpoints();
break;
}
case SConfig::BOOT_WII_NAND:
Boot_WiiWAD(_StartupPara.m_strFilename);
return true;
}
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));
break;
// Bootstrap 2 (AKA: Initial Program Loader, "BIOS")
case SConfig::BOOT_BS2:
bool operator()(const BootParameters::NAND& nand) const
{
if (!Load_BS2(_StartupPara.m_strBootROM))
NOTICE_LOG(BOOT, "Booting from NAND: %s", nand.content_path.c_str());
SetDefaultDisc();
return Boot_WiiWAD(nand.content_path);
}
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 (!Load_BS2(ipl.path))
return false;
if (ipl.disc)
{
NOTICE_LOG(BOOT, "Inserting disc: %s", ipl.disc->path.c_str());
SetDisc(DiscIO::CreateVolumeFromFilename(ipl.disc->path));
}
if (LoadMapFromFilename())
HLE::PatchFunctions();
break;
return true;
}
case SConfig::BOOT_DFF:
// do nothing
break;
default:
bool operator()(const BootParameters::DFF& dff) const
{
PanicAlertT("Tried to load an unknown file type.");
return false;
}
NOTICE_LOG(BOOT, "Booting DFF: %s", dff.dff_path.c_str());
return FifoPlayer::GetInstance().Open(dff.dff_path);
}
private:
const SConfig& config;
};
if (!std::visit(BootTitle(), boot->parameters))
return false;
PatchEngine::LoadPatches();
HLE::PatchFixedFunctions();
return true;
}

View File

@ -5,15 +5,13 @@
#pragma once
#include <cstdlib>
#include <optional>
#include <string>
#include <variant>
#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<DiscIO::Volume> 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> disc;
};
struct DFF
{
std::string dff_path;
};
static std::unique_ptr<BootParameters> GenerateFromFile(const std::string& path);
using Parameters = std::variant<Disc, Executable, NAND, IPL, DFF>;
BootParameters(Parameters&& parameters_);
Parameters parameters;
};
class CBoot
{
public:
static bool BootUp();
static bool BootUp(std::unique_ptr<BootParameters> boot);
static bool IsElfWii(const std::string& filename);
// Tries to find a map file for the current game by looking first in the

View File

@ -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));

View File

@ -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<BootParameters> 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<BootParameters::IPL>(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<BootParameters::Disc>(boot->parameters);
if (load_ipl)
{
PanicAlertT("Couldn't init the core.\nCheck your configuration.");
return false;
return Core::Init(std::make_unique<BootParameters>(BootParameters::IPL{
StartUp.m_region, std::move(std::get<BootParameters::Disc>(boot->parameters))}));
}
return true;
return Core::Init(std::move(boot));
}
void Stop()

View File

@ -4,13 +4,16 @@
#pragma once
#include <memory>
#include <string>
#include "Core/ConfigManager.h"
struct BootParameters;
namespace BootManager
{
bool BootCore(const std::string& filename, SConfig::EBootBS2 type);
bool BootCore(std::unique_ptr<BootParameters> parameters);
// Stop the emulation core and restore the configuration.
void Stop();

View File

@ -6,6 +6,7 @@
#include <climits>
#include <memory>
#include <optional>
#include <variant>
#include "AudioCommon/AudioCommon.h"
@ -881,166 +882,96 @@ 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;
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;
}
bool SConfig::AutoSetup(EBootBS2 _BootBS2)
bool operator()(const BootParameters::Executable& executable) const
{
std::string set_region_dir(EUR_DIR);
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);
switch (_BootBS2)
{
case BOOT_DEFAULT:
{
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());
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<DiscIO::Volume> 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());
// Check if we have a Wii disc
bWii = pVolume->GetVolumeType() == DiscIO::Platform::WII_DISC;
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.
// 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;
*region = config->bWii ? DiscIO::Region::PAL : DiscIO::Region::NTSC_U;
return true;
}
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;
std::unique_ptr<FifoDataFile> ddfFile(FifoDataFile::Load(m_strFilename, true));
if (ddfFile)
bool operator()(const BootParameters::NAND& nand) const
{
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();
if (!IOS::ES::IsChannel(tmd.GetTitleId()))
{
PanicAlertT("This WAD is not bootable.");
const auto& loader = DiscIO::NANDContentManager::Access().GetNANDLoader(nand.content_path);
if (!loader.IsValid())
return false;
const IOS::ES::TMDReader& tmd = loader.GetTMD();
config->SetRunningGameMetadata(tmd);
config->bWii = true;
*region = tmd.GetRegion();
return true;
}
SetRegion(tmd.GetRegion(), &set_region_dir);
SetRunningGameMetadata(tmd);
bWii = true;
m_BootType = BOOT_WII_NAND;
}
else
bool operator()(const BootParameters::IPL& ipl) const
{
PanicAlertT("Could not recognize ISO file %s", m_strFilename.c_str());
config->bWii = false;
*region = ipl.region;
return true;
}
bool operator()(const BootParameters::DFF& dff) const
{
std::unique_ptr<FifoDataFile> 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;
private:
SConfig* config;
DiscIO::Region* region;
};
case BOOT_BS2_JAP:
SetRegion(DiscIO::Region::NTSC_J, &set_region_dir);
m_strFilename.clear();
m_BootType = SConfig::BOOT_BS2;
break;
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, &region), boot.parameters))
return false;
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;
}
// 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;
}
return true;
}

View File

@ -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 @@ private:
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;

View File

@ -10,6 +10,7 @@
#include <mutex>
#include <queue>
#include <utility>
#include <variant>
#ifdef _WIN32
#include <windows.h>
@ -122,7 +123,7 @@ static void InitIsCPUKey()
}
#endif
static void EmuThread();
static void EmuThread(std::unique_ptr<BootParameters> 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<BootParameters> 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,8 +413,6 @@ 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())
{
PowerPC::InjectExternalCPUCore(cpu_core.get());
@ -426,7 +425,6 @@ static void FifoPlayerThread()
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<BootParameters> 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<BootParameters::DFF>(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)
{

View File

@ -11,11 +11,14 @@
#pragma once
#include <functional>
#include <memory>
#include <string>
#include <vector>
#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<BootParameters> boot);
void Stop();
void Shutdown();

View File

@ -98,7 +98,8 @@ CEXIIPL::CEXIIPL() : m_uPosition(0), m_uAddress(0), m_uRWOffset(0), m_FontsLoade
// Create the IPL
m_pIPL = static_cast<u8*>(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))

View File

@ -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)

View File

@ -13,6 +13,7 @@
#include <mutex>
#include <thread>
#include <utility>
#include <variant>
#include <vector>
#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<BootParameters::Disc>(boot.parameters))
s_current_file_name = std::get<BootParameters::Disc>(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);
}

View File

@ -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,

View File

@ -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;

View File

@ -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;

View File

@ -6,6 +6,7 @@
#include <array>
#include <cstddef>
#include <memory>
#include <mutex>
#include <string>
#include <vector>
@ -28,6 +29,8 @@
#include <IOKit/pwr_mgt/IOPMLib.h>
#endif
struct BootParameters;
// Class declarations
class CGameListCtrl;
class CCodeWindow;
@ -184,7 +187,7 @@ private:
void InitializeTASDialogs();
void InitializeCoreCallbacks();
void StartGame(const std::string& filename, SConfig::EBootBS2 type = SConfig::BOOT_DEFAULT);
void StartGame(std::unique_ptr<BootParameters> boot);
void SetDebuggerStartupParameters() const;
// Utility

View File

@ -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<BootParameters> 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>(BootParameters::IPL{DiscIO::Region::NTSC_J}));
}
void CFrame::OnLoadGameCubeIPLUSA(wxCommandEvent&)
{
StartGame("", SConfig::BOOT_BS2_USA);
StartGame(std::make_unique<BootParameters>(BootParameters::IPL{DiscIO::Region::NTSC_U}));
}
void CFrame::OnLoadGameCubeIPLEUR(wxCommandEvent&)
{
StartGame("", SConfig::BOOT_BS2_EUR);
StartGame(std::make_unique<BootParameters>(BootParameters::IPL{DiscIO::Region::PAL}));
}
void CFrame::OnExportAllSaves(wxCommandEvent& WXUNUSED(event))