Files
dolphin/Source/Core/Core/ConfigManager.cpp
T

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

481 lines
13 KiB
C++
Raw Normal View History

// Copyright 2008 Dolphin Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
2008-12-08 05:30:24 +00:00
2017-06-08 00:53:59 -04:00
#include "Core/ConfigManager.h"
2021-01-21 10:47:07 -05:00
#include <algorithm>
2016-08-14 19:54:01 +00:00
#include <climits>
#include <memory>
#include <optional>
2017-06-06 23:36:16 -04:00
#include <sstream>
2021-01-21 10:47:07 -05:00
#include <string>
#include <string_view>
2017-05-27 15:43:40 +02:00
#include <variant>
#include <fmt/format.h>
#include "AudioCommon/AudioCommon.h"
2017-07-16 16:31:12 +02:00
#include "Common/Assert.h"
#include "Common/CDUtils.h"
2014-02-17 05:18:15 -05:00
#include "Common/CommonPaths.h"
2014-09-07 20:06:58 -05:00
#include "Common/CommonTypes.h"
2017-05-13 22:29:55 +01:00
#include "Common/Config/Config.h"
2014-02-17 05:18:15 -05:00
#include "Common/FileUtil.h"
#include "Common/IniFile.h"
#include "Common/Logging/Log.h"
#include "Common/MsgHandler.h"
2017-01-06 21:59:02 +01:00
#include "Common/NandPaths.h"
#include "Common/StringUtil.h"
#include "Common/Version.h"
#include "Core/Boot/Boot.h"
2017-07-05 10:27:13 +02:00
#include "Core/CommonTitles.h"
#include "Core/Config/DefaultLocale.h"
2020-11-28 15:09:37 -05:00
#include "Core/Config/MainSettings.h"
2017-08-01 22:37:42 +08:00
#include "Core/Config/SYSCONFSettings.h"
2017-07-11 13:10:11 +02:00
#include "Core/ConfigLoaders/GameConfigLoader.h"
#include "Core/Core.h"
#include "Core/DolphinAnalytics.h"
#include "Core/FifoPlayer/FifoDataFile.h"
#include "Core/HLE/HLE.h"
2017-03-24 19:17:52 +01:00
#include "Core/HW/DVD/DVDInterface.h"
#include "Core/HW/EXI/EXI_Device.h"
#include "Core/HW/SI/SI.h"
#include "Core/HW/SI/SI_Device.h"
#include "Core/Host.h"
2017-07-05 10:27:13 +02:00
#include "Core/IOS/ES/ES.h"
#include "Core/IOS/ES/Formats.h"
#include "Core/PatchEngine.h"
#include "Core/PowerPC/PPCSymbolDB.h"
#include "Core/PowerPC/PowerPC.h"
#include "Core/TitleDatabase.h"
#include "VideoCommon/HiresTextures.h"
2016-07-06 20:33:05 +02:00
#include "DiscIO/Enums.h"
#include "DiscIO/Volume.h"
2019-07-14 18:00:14 +02:00
#include "DiscIO/VolumeWad.h"
2009-02-28 23:21:51 +00:00
SConfig* SConfig::m_Instance;
2008-12-08 05:30:24 +00:00
SConfig::SConfig()
{
LoadDefaults();
// Make sure we have log manager
LoadSettings();
}
2008-12-08 05:30:24 +00:00
void SConfig::Init()
{
m_Instance = new SConfig;
}
void SConfig::Shutdown()
{
delete m_Instance;
2014-03-09 21:14:26 +01:00
m_Instance = nullptr;
}
2008-12-08 05:30:24 +00:00
SConfig::~SConfig()
{
SaveSettings();
}
2008-12-08 05:30:24 +00:00
void SConfig::SaveSettings()
{
2020-11-18 06:01:15 -05:00
NOTICE_LOG_FMT(BOOT, "Saving settings to {}", File::GetUserPath(F_DOLPHINCONFIG_IDX));
2017-05-13 22:29:55 +01:00
Config::Save();
}
2008-12-08 05:30:24 +00:00
void SConfig::LoadSettings()
{
2020-11-18 06:01:15 -05:00
INFO_LOG_FMT(BOOT, "Loading Settings from {}", File::GetUserPath(F_DOLPHINCONFIG_IDX));
Config::Load();
2014-06-16 01:12:43 -04:00
}
void SConfig::ResetRunningGameMetadata()
{
SetRunningGameMetadata("00000000", "", 0, 0, DiscIO::Region::Unknown);
}
void SConfig::SetRunningGameMetadata(const DiscIO::Volume& volume,
const DiscIO::Partition& partition)
{
if (partition == volume.GetGamePartition())
{
SetRunningGameMetadata(volume.GetGameID(), volume.GetGameTDBID(),
volume.GetTitleID().value_or(0), volume.GetRevision().value_or(0),
volume.GetRegion());
}
else
{
SetRunningGameMetadata(volume.GetGameID(partition), volume.GetGameTDBID(),
volume.GetTitleID(partition).value_or(0),
volume.GetRevision(partition).value_or(0), volume.GetRegion());
}
}
void SConfig::SetRunningGameMetadata(const IOS::ES::TMDReader& tmd, DiscIO::Platform platform)
{
const u64 tmd_title_id = tmd.GetTitleId();
// If we're launching a disc game, we want to read the revision from
// the disc header instead of the TMD. They can differ.
// (IOS HLE ES calls us with a TMDReader rather than a volume when launching
// a disc game, because ES has no reason to be accessing the disc directly.)
if (platform == DiscIO::Platform::WiiWAD ||
!DVDInterface::UpdateRunningGameMetadata(tmd_title_id))
{
2016-09-24 14:43:03 +02:00
// If not launching a disc game, just read everything from the TMD.
SetRunningGameMetadata(tmd.GetGameID(), tmd.GetGameTDBID(), tmd_title_id, tmd.GetTitleVersion(),
tmd.GetRegion());
}
}
2021-01-21 10:47:07 -05:00
void SConfig::SetRunningGameMetadata(const std::string& game_id)
{
SetRunningGameMetadata(game_id, "", 0, 0, DiscIO::Region::Unknown);
}
void SConfig::SetRunningGameMetadata(const std::string& game_id, const std::string& gametdb_id,
u64 title_id, u16 revision, DiscIO::Region region)
{
const bool was_changed = m_game_id != game_id || m_gametdb_id != gametdb_id ||
m_title_id != title_id || m_revision != revision;
m_game_id = game_id;
m_gametdb_id = gametdb_id;
m_title_id = title_id;
m_revision = revision;
2017-06-05 13:55:54 +02:00
if (game_id.length() == 6)
{
m_debugger_game_id = game_id;
}
else if (title_id != 0)
{
m_debugger_game_id =
fmt::format("{:08X}_{:08X}", static_cast<u32>(title_id >> 32), static_cast<u32>(title_id));
2017-06-05 13:55:54 +02:00
}
else
{
m_debugger_game_id.clear();
}
if (!was_changed)
return;
if (game_id == "00000000")
{
m_title_name.clear();
m_title_description.clear();
return;
}
const Core::TitleDatabase title_database;
const DiscIO::Language language = GetLanguageAdjustedForRegion(bWii, region);
m_title_name = title_database.GetTitleName(m_gametdb_id, language);
m_title_description = title_database.Describe(m_gametdb_id, language);
2020-11-18 06:01:15 -05:00
NOTICE_LOG_FMT(CORE, "Active title: {}", m_title_description);
Host_TitleChanged();
2017-07-31 13:39:35 +08:00
Config::AddLayer(ConfigLoaders::GenerateGlobalGameConfigLoader(game_id, revision));
Config::AddLayer(ConfigLoaders::GenerateLocalGameConfigLoader(game_id, revision));
if (Core::IsRunning())
DolphinAnalytics::Instance().ReportGameStart();
}
void SConfig::OnNewTitleLoad()
{
if (!Core::IsRunning())
return;
if (!g_symbolDB.IsEmpty())
{
g_symbolDB.Clear();
Host_NotifyMapLoaded();
}
CBoot::LoadMapFromFilename();
HLE::Reload();
PatchEngine::Reload();
HiresTexture::Update();
}
void SConfig::LoadDefaults()
{
bAutomaticStart = false;
bBootToPause = false;
bWii = false;
2016-06-18 02:43:59 +02:00
ResetRunningGameMetadata();
}
2016-06-25 11:24:43 -04:00
2021-01-21 10:47:07 -05:00
// Static method to make a simple game ID for elf/dol files
std::string SConfig::MakeGameID(std::string_view file_name)
{
size_t lastdot = file_name.find_last_of(".");
if (lastdot == std::string::npos)
return "ID-" + std::string(file_name);
return "ID-" + std::string(file_name.substr(0, lastdot));
}
2017-07-16 16:31:12 +02:00
// The reason we need this function is because some memory card code
// expects to get a non-NTSC-K region even if we're emulating an NTSC-K Wii.
DiscIO::Region SConfig::ToGameCubeRegion(DiscIO::Region region)
{
if (region != DiscIO::Region::NTSC_K)
return region;
// GameCube has no NTSC-K region. No choice of replacement value is completely
// non-arbitrary, but let's go with NTSC-J since Korean GameCubes are NTSC-J.
return DiscIO::Region::NTSC_J;
}
2016-12-23 21:53:36 +01:00
const char* SConfig::GetDirectoryForRegion(DiscIO::Region region)
{
if (region == DiscIO::Region::Unknown)
region = ToGameCubeRegion(GetFallbackRegion());
switch (region)
{
case DiscIO::Region::NTSC_J:
return JAP_DIR;
case DiscIO::Region::NTSC_U:
return USA_DIR;
case DiscIO::Region::PAL:
return EUR_DIR;
case DiscIO::Region::NTSC_K:
2018-03-14 20:34:35 -04:00
ASSERT_MSG(BOOT, false, "NTSC-K is not a valid GameCube region");
return JAP_DIR; // See ToGameCubeRegion
default:
ASSERT_MSG(BOOT, false, "Default case should not be reached");
return EUR_DIR;
}
}
2017-05-21 22:21:34 +01:00
std::string SConfig::GetBootROMPath(const std::string& region_directory) const
{
const std::string path =
File::GetUserPath(D_GCUSER_IDX) + DIR_SEP + region_directory + DIR_SEP GC_IPL;
if (!File::Exists(path))
return File::GetSysDirectory() + GC_SYS_DIR + DIR_SEP + region_directory + DIR_SEP GC_IPL;
return path;
}
2017-05-27 15:43:40 +02:00
struct SetGameMetadata
{
2017-05-27 15:43:40 +02:00
SetGameMetadata(SConfig* config_, DiscIO::Region* region_) : config(config_), region(region_) {}
bool operator()(const BootParameters::Disc& disc) const
{
*region = disc.volume->GetRegion();
config->bWii = disc.volume->GetVolumeType() == DiscIO::Platform::WiiDisc;
2017-05-27 15:43:40 +02:00
config->m_disc_booted_from_game_list = true;
config->SetRunningGameMetadata(*disc.volume, disc.volume->GetGamePartition());
2017-05-27 15:43:40 +02:00
return true;
}
bool operator()(const BootParameters::Executable& executable) const
{
2017-05-28 17:12:38 +02:00
if (!executable.reader->IsValid())
return false;
*region = DiscIO::Region::Unknown;
config->bWii = executable.reader->IsWii();
2017-06-05 13:55:54 +02:00
// Strip the .elf/.dol file extension and directories before the name
SplitPath(executable.path, nullptr, &config->m_debugger_game_id, nullptr);
2020-09-13 16:40:08 +02:00
2021-01-21 10:47:07 -05:00
// Set DOL/ELF game ID appropriately
std::string executable_path = executable.path;
constexpr char BACKSLASH = '\\';
constexpr char FORWARDSLASH = '/';
std::replace(executable_path.begin(), executable_path.end(), BACKSLASH, FORWARDSLASH);
config->SetRunningGameMetadata(SConfig::MakeGameID(PathToFileName(executable_path)));
2020-09-13 16:40:08 +02:00
Host_TitleChanged();
2017-05-27 15:43:40 +02:00
return true;
}
2019-07-14 18:00:14 +02:00
bool operator()(const DiscIO::VolumeWAD& wad) const
2017-05-27 15:43:40 +02:00
{
2019-07-14 18:00:14 +02:00
if (!wad.GetTMD().IsValid())
2017-10-01 15:14:53 +02:00
{
2020-11-18 06:01:15 -05:00
PanicAlertFmtT("This WAD is not valid.");
2017-05-27 15:43:40 +02:00
return false;
2017-10-01 15:14:53 +02:00
}
if (!IOS::ES::IsChannel(wad.GetTMD().GetTitleId()))
{
2020-11-18 06:01:15 -05:00
PanicAlertFmtT("This WAD is not bootable.");
2017-10-01 15:14:53 +02:00
return false;
}
2017-05-27 15:43:40 +02:00
2017-10-01 15:14:53 +02:00
const IOS::ES::TMDReader& tmd = wad.GetTMD();
2017-05-27 15:43:40 +02:00
*region = tmd.GetRegion();
config->bWii = true;
config->SetRunningGameMetadata(tmd, DiscIO::Platform::WiiWAD);
2017-05-27 15:43:40 +02:00
return true;
}
bool operator()(const BootParameters::NANDTitle& nand_title) const
{
IOS::HLE::Kernel ios;
const IOS::ES::TMDReader tmd = ios.GetES()->FindInstalledTMD(nand_title.id);
if (!tmd.IsValid() || !IOS::ES::IsChannel(nand_title.id))
{
2020-11-18 06:01:15 -05:00
PanicAlertFmtT("This title cannot be booted.");
return false;
}
*region = tmd.GetRegion();
config->bWii = true;
config->SetRunningGameMetadata(tmd, DiscIO::Platform::WiiWAD);
return true;
}
2017-05-27 15:43:40 +02:00
bool operator()(const BootParameters::IPL& ipl) const
{
*region = ipl.region;
config->bWii = false;
2020-09-13 16:40:08 +02:00
Host_TitleChanged();
2017-05-27 15:43:40 +02:00
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;
*region = DiscIO::Region::NTSC_U;
config->bWii = dff_file->GetIsWii();
2020-09-13 16:40:08 +02:00
Host_TitleChanged();
2017-05-27 15:43:40 +02:00
return true;
}
private:
SConfig* config;
DiscIO::Region* region;
};
bool SConfig::SetPathsAndGameMetadata(const BootParameters& boot)
{
m_is_mios = false;
m_disc_booted_from_game_list = false;
2017-07-05 10:27:13 +02:00
if (!std::visit(SetGameMetadata(this, &m_region), boot.parameters))
2017-05-27 15:43:40 +02:00
return false;
if (m_region == DiscIO::Region::Unknown)
m_region = GetFallbackRegion();
2017-07-05 10:27:13 +02:00
2017-05-27 15:43:40 +02:00
// Set up paths
2017-07-05 10:27:13 +02:00
const std::string region_dir = GetDirectoryForRegion(ToGameCubeRegion(m_region));
m_strSRAM = File::GetUserPath(F_GCSRAM_IDX);
2017-07-05 10:27:13 +02:00
m_strBootROM = GetBootROMPath(region_dir);
return true;
}
DiscIO::Region SConfig::GetFallbackRegion()
{
2020-11-28 15:09:37 -05:00
return Config::Get(Config::MAIN_FALLBACK_REGION);
}
2016-07-06 20:33:05 +02:00
DiscIO::Language SConfig::GetCurrentLanguage(bool wii) const
{
DiscIO::Language language;
if (wii)
language = static_cast<DiscIO::Language>(Config::Get(Config::SYSCONF_LANGUAGE));
else
language = DiscIO::FromGameCubeLanguage(Config::Get(Config::MAIN_GC_LANGUAGE));
// Get rid of invalid values (probably doesn't matter, but might as well do it)
if (language > DiscIO::Language::Unknown || language < DiscIO::Language::Japanese)
language = DiscIO::Language::Unknown;
return language;
}
DiscIO::Language SConfig::GetLanguageAdjustedForRegion(bool wii, DiscIO::Region region) const
{
const DiscIO::Language language = GetCurrentLanguage(wii);
if (!wii && region == DiscIO::Region::NTSC_K)
region = DiscIO::Region::NTSC_J; // NTSC-K only exists on Wii, so use a fallback
if (!wii && region == DiscIO::Region::NTSC_J && language == DiscIO::Language::English)
return DiscIO::Language::Japanese; // English and Japanese both use the value 0 in GC SRAM
if (!Config::Get(Config::MAIN_OVERRIDE_REGION_SETTINGS))
{
if (region == DiscIO::Region::NTSC_J)
return DiscIO::Language::Japanese;
if (region == DiscIO::Region::NTSC_U && language != DiscIO::Language::English &&
(!wii || (language != DiscIO::Language::French && language != DiscIO::Language::Spanish)))
{
return DiscIO::Language::English;
}
if (region == DiscIO::Region::PAL &&
(language < DiscIO::Language::English || language > DiscIO::Language::Dutch))
{
return DiscIO::Language::English;
}
if (region == DiscIO::Region::NTSC_K)
return DiscIO::Language::Korean;
}
return language;
}
IniFile SConfig::LoadDefaultGameIni() const
{
return LoadDefaultGameIni(GetGameID(), m_revision);
}
IniFile SConfig::LoadLocalGameIni() const
{
return LoadLocalGameIni(GetGameID(), m_revision);
}
IniFile SConfig::LoadGameIni() const
{
return LoadGameIni(GetGameID(), m_revision);
}
IniFile SConfig::LoadDefaultGameIni(const std::string& id, std::optional<u16> revision)
{
IniFile game_ini;
2017-07-11 13:10:11 +02:00
for (const std::string& filename : ConfigLoaders::GetGameIniFilenames(id, revision))
game_ini.Load(File::GetSysDirectory() + GAMESETTINGS_DIR DIR_SEP + filename, true);
return game_ini;
}
IniFile SConfig::LoadLocalGameIni(const std::string& id, std::optional<u16> revision)
{
IniFile game_ini;
2017-07-11 13:10:11 +02:00
for (const std::string& filename : ConfigLoaders::GetGameIniFilenames(id, revision))
game_ini.Load(File::GetUserPath(D_GAMESETTINGS_IDX) + filename, true);
return game_ini;
}
IniFile SConfig::LoadGameIni(const std::string& id, std::optional<u16> revision)
{
IniFile game_ini;
2017-07-11 13:10:11 +02:00
for (const std::string& filename : ConfigLoaders::GetGameIniFilenames(id, revision))
game_ini.Load(File::GetSysDirectory() + GAMESETTINGS_DIR DIR_SEP + filename, true);
2017-07-11 13:10:11 +02:00
for (const std::string& filename : ConfigLoaders::GetGameIniFilenames(id, revision))
game_ini.Load(File::GetUserPath(D_GAMESETTINGS_IDX) + filename, true);
return game_ini;
}