System: Allow separate configuration for multi-disc games

This commit is contained in:
Stenzek 2024-12-16 01:22:48 +10:00
parent 23c221be01
commit b634eecd21
No known key found for this signature in database
11 changed files with 228 additions and 89 deletions

View File

@ -441,6 +441,7 @@ struct ALIGN_TO_CACHE_LINE UIState
// Settings // Settings
SettingsPage settings_page = SettingsPage::Interface; SettingsPage settings_page = SettingsPage::Interface;
std::unique_ptr<INISettingsInterface> game_settings_interface; std::unique_ptr<INISettingsInterface> game_settings_interface;
const GameDatabase::Entry* game_settings_db_entry;
std::unique_ptr<GameList::Entry> game_settings_entry; std::unique_ptr<GameList::Entry> game_settings_entry;
std::vector<std::pair<std::string, bool>> game_list_directories_cache; std::vector<std::pair<std::string, bool>> game_list_directories_cache;
GPUDevice::AdapterInfoList graphics_adapter_list_cache; GPUDevice::AdapterInfoList graphics_adapter_list_cache;
@ -870,8 +871,8 @@ void FullscreenUI::Render()
if (s_state.game_settings_interface->IsEmpty()) if (s_state.game_settings_interface->IsEmpty())
{ {
if (FileSystem::FileExists(s_state.game_settings_interface->GetFileName().c_str()) && if (FileSystem::FileExists(s_state.game_settings_interface->GetPath().c_str()) &&
!FileSystem::DeleteFile(s_state.game_settings_interface->GetFileName().c_str(), &error)) !FileSystem::DeleteFile(s_state.game_settings_interface->GetPath().c_str(), &error))
{ {
ImGuiFullscreen::OpenInfoMessageDialog( ImGuiFullscreen::OpenInfoMessageDialog(
FSUI_STR("Error"), fmt::format(FSUI_FSTR("An error occurred while deleting empty game settings:\n{}"), FSUI_STR("Error"), fmt::format(FSUI_FSTR("An error occurred while deleting empty game settings:\n{}"),
@ -2724,6 +2725,7 @@ void FullscreenUI::SwitchToSettings()
{ {
s_state.game_settings_entry.reset(); s_state.game_settings_entry.reset();
s_state.game_settings_interface.reset(); s_state.game_settings_interface.reset();
s_state.game_settings_db_entry = nullptr;
s_state.game_patch_list = {}; s_state.game_patch_list = {};
s_state.enabled_game_patch_cache = {}; s_state.enabled_game_patch_cache = {};
s_state.game_cheats_list = {}; s_state.game_cheats_list = {};
@ -2740,8 +2742,9 @@ void FullscreenUI::SwitchToSettings()
void FullscreenUI::SwitchToGameSettingsForSerial(std::string_view serial) void FullscreenUI::SwitchToGameSettingsForSerial(std::string_view serial)
{ {
s_state.game_settings_entry.reset(); s_state.game_settings_entry.reset();
s_state.game_settings_interface = std::make_unique<INISettingsInterface>(System::GetGameSettingsPath(serial)); s_state.game_settings_db_entry = GameDatabase::GetEntryForSerial(serial);
s_state.game_settings_interface->Load(); s_state.game_settings_interface =
System::GetGameSettingsInterface(s_state.game_settings_db_entry, serial, true, false);
PopulatePatchesAndCheatsList(serial); PopulatePatchesAndCheatsList(serial);
s_state.current_main_window = MainWindowType::Settings; s_state.current_main_window = MainWindowType::Settings;
s_state.settings_page = SettingsPage::Summary; s_state.settings_page = SettingsPage::Summary;
@ -2828,7 +2831,7 @@ void FullscreenUI::DoCopyGameSettings()
SetSettingsChanged(s_state.game_settings_interface.get()); SetSettingsChanged(s_state.game_settings_interface.get());
ShowToast("Game Settings Copied", fmt::format(FSUI_FSTR("Game settings initialized with global settings for '{}'."), ShowToast("Game Settings Copied", fmt::format(FSUI_FSTR("Game settings initialized with global settings for '{}'."),
Path::GetFileTitle(s_state.game_settings_interface->GetFileName()))); Path::GetFileTitle(s_state.game_settings_interface->GetPath())));
} }
void FullscreenUI::DoClearGameSettings() void FullscreenUI::DoClearGameSettings()
@ -2837,13 +2840,13 @@ void FullscreenUI::DoClearGameSettings()
return; return;
s_state.game_settings_interface->Clear(); s_state.game_settings_interface->Clear();
if (!s_state.game_settings_interface->GetFileName().empty()) if (!s_state.game_settings_interface->GetPath().empty())
FileSystem::DeleteFile(s_state.game_settings_interface->GetFileName().c_str()); FileSystem::DeleteFile(s_state.game_settings_interface->GetPath().c_str());
SetSettingsChanged(s_state.game_settings_interface.get()); SetSettingsChanged(s_state.game_settings_interface.get());
ShowToast("Game Settings Cleared", fmt::format(FSUI_FSTR("Game settings have been cleared for '{}'."), ShowToast("Game Settings Cleared", fmt::format(FSUI_FSTR("Game settings have been cleared for '{}'."),
Path::GetFileTitle(s_state.game_settings_interface->GetFileName()))); Path::GetFileTitle(s_state.game_settings_interface->GetPath())));
} }
void FullscreenUI::DrawSettingsWindow() void FullscreenUI::DrawSettingsWindow()
@ -3098,6 +3101,18 @@ void FullscreenUI::DrawSummarySettingsPage()
MenuHeading(FSUI_CSTR("Options")); MenuHeading(FSUI_CSTR("Options"));
if (s_state.game_settings_db_entry && !s_state.game_settings_db_entry->disc_set_serials.empty())
{
// only enable for first disc
const bool is_first_disc =
(s_state.game_settings_db_entry->serial == s_state.game_settings_db_entry->disc_set_serials.front());
DrawToggleSetting(
GetEditingSettingsInterface(), FSUI_ICONSTR(ICON_FA_COMPACT_DISC, "Use Separate Disc Settings"),
FSUI_CSTR(
"Uses separate game settings for each disc of multi-disc games. Can only be set on the first/main disc."),
"Main", "UseSeparateConfigForDiscSet", !is_first_disc, is_first_disc, false);
}
if (MenuButton(FSUI_ICONSTR(ICON_FA_COPY, "Copy Settings"), if (MenuButton(FSUI_ICONSTR(ICON_FA_COPY, "Copy Settings"),
FSUI_CSTR("Copies the current global settings to this game."))) FSUI_CSTR("Copies the current global settings to this game.")))
{ {

View File

@ -1171,20 +1171,6 @@ DiscRegion System::GetRegionForPsf(const char* path)
return psf.GetRegion(); return psf.GetRegion();
} }
std::string System::GetGameSettingsPath(std::string_view game_serial)
{
// multi-disc games => always use the first disc
const GameDatabase::Entry* entry = GameDatabase::GetEntryForSerial(game_serial);
const std::string_view serial_for_path =
(entry && !entry->disc_set_serials.empty()) ? entry->disc_set_serials.front() : game_serial;
return Path::Combine(EmuFolders::GameSettings, fmt::format("{}.ini", Path::SanitizeFileName(serial_for_path)));
}
std::string System::GetInputProfilePath(std::string_view name)
{
return Path::Combine(EmuFolders::InputProfiles, fmt::format("{}.ini", name));
}
bool System::RecreateGPU(GPURenderer renderer, bool force_recreate_device, bool update_display /* = true*/) bool System::RecreateGPU(GPURenderer renderer, bool force_recreate_device, bool update_display /* = true*/)
{ {
ClearMemorySaveStates(); ClearMemorySaveStates();
@ -1487,27 +1473,108 @@ void System::ReloadInputProfile(bool display_osd_messages)
ApplySettings(display_osd_messages); ApplySettings(display_osd_messages);
} }
bool System::UpdateGameSettingsLayer() std::string System::GetInputProfilePath(std::string_view name)
{ {
std::unique_ptr<INISettingsInterface> new_interface; return Path::Combine(EmuFolders::InputProfiles, fmt::format("{}.ini", name));
if (g_settings.apply_game_settings && !s_state.running_game_serial.empty()) }
std::string System::GetGameSettingsPath(std::string_view game_serial, bool ignore_disc_set)
{
// multi-disc games => always use the first disc
const GameDatabase::Entry* entry = ignore_disc_set ? nullptr : GameDatabase::GetEntryForSerial(game_serial);
const std::string_view serial_for_path =
(entry && !entry->disc_set_serials.empty()) ? entry->disc_set_serials.front() : game_serial;
return Path::Combine(EmuFolders::GameSettings, fmt::format("{}.ini", Path::SanitizeFileName(serial_for_path)));
}
std::unique_ptr<INISettingsInterface> System::GetGameSettingsInterface(const GameDatabase::Entry* dbentry,
std::string_view serial, bool create, bool quiet)
{
std::unique_ptr<INISettingsInterface> ret;
std::string path = GetGameSettingsPath(serial, false);
if (FileSystem::FileExists(path.c_str()))
{ {
std::string filename(GetGameSettingsPath(s_state.running_game_serial)); if (!quiet)
if (FileSystem::FileExists(filename.c_str())) INFO_COLOR_LOG(StrongCyan, "Loading game settings from '{}'...", Path::GetFileName(path));
Error error;
ret = std::make_unique<INISettingsInterface>(std::move(path));
if (ret->Load(&error))
{ {
INFO_LOG("Loading game settings from '{}'...", Path::GetFileName(filename)); // Check for separate disc configuration.
new_interface = std::make_unique<INISettingsInterface>(std::move(filename)); if (dbentry && !dbentry->disc_set_serials.empty() && dbentry->disc_set_serials.front() != serial)
if (!new_interface->Load())
{ {
ERROR_LOG("Failed to parse game settings ini '{}'", new_interface->GetFileName()); if (ret->GetBoolValue("Main", "UseSeparateConfigForDiscSet", false))
new_interface.reset(); {
if (!quiet)
{
INFO_COLOR_LOG(StrongCyan, "Using separate disc game settings serial {} for disc set {}", serial,
dbentry->disc_set_serials.front());
}
// Load the disc specific ini.
path = GetGameSettingsPath(serial, true);
if (FileSystem::FileExists(path.c_str()))
{
if (!ret->Load(std::move(path), &error))
{
if (!quiet)
{
ERROR_LOG("Failed to parse separate disc game settings ini '{}': {}", Path::GetFileName(ret->GetPath()),
error.GetDescription());
}
if (create)
ret->Clear();
else
ret.reset();
} }
} }
else else
{ {
INFO_LOG("No game settings found (tried '{}')", Path::GetFileName(filename)); if (!quiet)
INFO_COLOR_LOG(StrongCyan, "No separate disc game settings found (tried '{}')", Path::GetFileName(path));
ret.reset();
// return empty ini struct?
if (create)
ret = std::make_unique<INISettingsInterface>(std::move(path));
} }
} }
}
}
else
{
if (!quiet)
{
ERROR_LOG("Failed to parse game settings ini '{}': {}", Path::GetFileName(ret->GetPath()),
error.GetDescription());
}
if (!create)
ret.reset();
}
}
else
{
if (!quiet)
INFO_COLOR_LOG(StrongCyan, "No game settings found (tried '{}')", Path::GetFileName(path));
// return empty ini struct?
if (create)
ret = std::make_unique<INISettingsInterface>(std::move(path));
}
return ret;
}
bool System::UpdateGameSettingsLayer()
{
std::unique_ptr<INISettingsInterface> new_interface;
if (g_settings.apply_game_settings && !s_state.running_game_serial.empty())
new_interface = GetGameSettingsInterface(s_state.running_game_entry, s_state.running_game_serial, false, false);
std::string input_profile_name; std::string input_profile_name;
if (new_interface) if (new_interface)
@ -1543,7 +1610,7 @@ void System::UpdateInputSettingsLayer(std::string input_profile_name, std::uniqu
input_interface = std::make_unique<INISettingsInterface>(std::move(filename)); input_interface = std::make_unique<INISettingsInterface>(std::move(filename));
if (!input_interface->Load()) if (!input_interface->Load())
{ {
ERROR_LOG("Failed to parse input profile ini '{}'", Path::GetFileName(input_interface->GetFileName())); ERROR_LOG("Failed to parse input profile ini '{}'", Path::GetFileName(input_interface->GetPath()));
input_interface.reset(); input_interface.reset();
input_profile_name = {}; input_profile_name = {};
} }
@ -5543,22 +5610,14 @@ std::string System::GetGameMemoryCardPath(std::string_view serial, std::string_v
std::unique_ptr<INISettingsInterface> ini; std::unique_ptr<INISettingsInterface> ini;
if (!serial.empty()) if (!serial.empty())
{ {
std::string game_settings_path = GetGameSettingsPath(serial); ini = GetGameSettingsInterface(GameDatabase::GetEntryForSerial(serial), serial, false, true);
if (FileSystem::FileExists(game_settings_path.c_str())) if (ini && ini->ContainsValue(section, type_key))
{
ini = std::make_unique<INISettingsInterface>(std::move(game_settings_path));
if (!ini->Load())
{
ini.reset();
}
else if (ini->ContainsValue(section, type_key))
{ {
type = Settings::ParseMemoryCardTypeName( type = Settings::ParseMemoryCardTypeName(
ini->GetTinyStringValue(section, type_key, Settings::GetMemoryCardTypeName(global_type))) ini->GetTinyStringValue(section, type_key, Settings::GetMemoryCardTypeName(global_type)))
.value_or(global_type); .value_or(global_type);
} }
} }
}
else if (type == MemoryCardType::PerGame) else if (type == MemoryCardType::PerGame)
{ {
// always shared without serial // always shared without serial

View File

@ -28,6 +28,7 @@ enum class GPUVSyncMode : u8;
class Controller; class Controller;
class GPUTexture; class GPUTexture;
class INISettingsInterface;
class MediaCapture; class MediaCapture;
namespace BIOS { namespace BIOS {
@ -153,7 +154,12 @@ DiscRegion GetRegionForExe(const char* path);
DiscRegion GetRegionForPsf(const char* path); DiscRegion GetRegionForPsf(const char* path);
/// Returns the path for the game settings ini file for the specified serial. /// Returns the path for the game settings ini file for the specified serial.
std::string GetGameSettingsPath(std::string_view game_serial); std::string GetGameSettingsPath(std::string_view game_serial, bool ignore_disc_set);
/// Returns the loaded interface for the game settings ini file for the specified serial. If create is true, an empty
/// ini reader will be returned if the file does not exist. If quit is true, no log messages will be emitted.
std::unique_ptr<INISettingsInterface> GetGameSettingsInterface(const GameDatabase::Entry* dbentry,
std::string_view serial, bool create, bool quiet);
/// Returns the path for the input profile ini file with the specified name (may not exist). /// Returns the path for the input profile ini file with the specified name (may not exist).
std::string GetInputProfilePath(std::string_view name); std::string GetInputProfilePath(std::string_view name);

View File

@ -221,7 +221,7 @@ void ControllerSettingsWindow::onNewProfileClicked()
{ {
QMessageBox::critical( QMessageBox::critical(
this, tr("Error"), this, tr("Error"),
tr("Failed to save the new profile to '%1'.").arg(QString::fromStdString(temp_si.GetFileName()))); tr("Failed to save the new profile to '%1'.").arg(QString::fromStdString(temp_si.GetPath())));
return; return;
} }

View File

@ -86,6 +86,13 @@ GameSummaryWidget::~GameSummaryWidget() = default;
void GameSummaryWidget::reloadGameSettings() void GameSummaryWidget::reloadGameSettings()
{ {
if (m_ui.separateDiscSettings->isVisible() && m_ui.separateDiscSettings->isEnabled())
{
m_ui.separateDiscSettings->setCheckState(
m_dialog->getBoolValue("Main", "UseSeparateConfigForDiscSet", std::nullopt).value_or(false) ? Qt::Checked :
Qt::Unchecked);
}
if (m_dialog->getBoolValue("ControllerPorts", "UseGameSettingsForController", std::nullopt).value_or(false)) if (m_dialog->getBoolValue("ControllerPorts", "UseGameSettingsForController", std::nullopt).value_or(false))
{ {
const QSignalBlocker sb(m_ui.inputProfile); const QSignalBlocker sb(m_ui.inputProfile);
@ -214,6 +221,28 @@ void GameSummaryWidget::populateUi(const std::string& path, const std::string& s
m_ui.entryType->setCurrentIndex(static_cast<int>(gentry->type)); m_ui.entryType->setCurrentIndex(static_cast<int>(gentry->type));
} }
if (entry && !entry->disc_set_serials.empty())
{
if (serial == entry->disc_set_serials.front())
{
m_ui.separateDiscSettings->setCheckState(
m_dialog->getBoolValue("Main", "UseSeparateConfigForDiscSet", std::nullopt).value_or(false) ? Qt::Checked :
Qt::Unchecked);
connect(m_ui.separateDiscSettings, &QCheckBox::checkStateChanged, this,
&GameSummaryWidget::onSeparateDiscSettingsChanged);
}
else
{
// set disabled+checked if not first disc
m_ui.separateDiscSettings->setCheckState(Qt::Checked);
m_ui.separateDiscSettings->setEnabled(false);
}
}
else
{
m_ui.separateDiscSettings->setVisible(false);
}
m_ui.compatibilityComments->setVisible(!m_compatibility_comments.isEmpty()); m_ui.compatibilityComments->setVisible(!m_compatibility_comments.isEmpty());
m_ui.inputProfile->addItem(QIcon::fromTheme(QStringLiteral("global-line")), tr("Use Global Settings")); m_ui.inputProfile->addItem(QIcon::fromTheme(QStringLiteral("global-line")), tr("Use Global Settings"));
@ -228,6 +257,19 @@ void GameSummaryWidget::populateUi(const std::string& path, const std::string& s
updateWindowTitle(); updateWindowTitle();
} }
void GameSummaryWidget::onSeparateDiscSettingsChanged(Qt::CheckState state)
{
if (state == Qt::Checked)
m_dialog->setBoolSettingValue("Main", "UseSeparateConfigForDiscSet", true);
else
m_dialog->removeSettingValue("Main", "UseSeparateConfigForDiscSet");
}
void GameSummaryWidget::updateWindowTitle()
{
m_dialog->setGameTitle(m_ui.title->text().toStdString());
}
void GameSummaryWidget::populateCustomAttributes() void GameSummaryWidget::populateCustomAttributes()
{ {
auto lock = GameList::GetLock(); auto lock = GameList::GetLock();
@ -257,11 +299,6 @@ void GameSummaryWidget::populateCustomAttributes()
} }
} }
void GameSummaryWidget::updateWindowTitle()
{
m_dialog->setGameTitle(m_ui.title->text().toStdString());
}
void GameSummaryWidget::setCustomTitle(const std::string& text) void GameSummaryWidget::setCustomTitle(const std::string& text)
{ {
m_ui.restoreTitle->setEnabled(!text.empty()); m_ui.restoreTitle->setEnabled(!text.empty());

View File

@ -31,6 +31,7 @@ protected:
void showEvent(QShowEvent* event) override; void showEvent(QShowEvent* event) override;
private Q_SLOTS: private Q_SLOTS:
void onSeparateDiscSettingsChanged(Qt::CheckState state);
void onCustomLanguageChanged(int language); void onCustomLanguageChanged(int language);
void onCompatibilityCommentsClicked(); void onCompatibilityCommentsClicked();
void onInputProfileChanged(int index); void onInputProfileChanged(int index);

View File

@ -208,13 +208,6 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="5" column="1">
<widget class="QComboBox" name="entryType">
<property name="enabled">
<bool>false</bool>
</property>
</widget>
</item>
<item row="8" column="1"> <item row="8" column="1">
<layout class="QHBoxLayout" name="horizontalLayout" stretch="1,0"> <layout class="QHBoxLayout" name="horizontalLayout" stretch="1,0">
<item> <item>
@ -344,6 +337,27 @@
</item> </item>
</layout> </layout>
</item> </item>
<item row="5" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_4" stretch="1,0">
<item>
<widget class="QComboBox" name="entryType">
<property name="enabled">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="separateDiscSettings">
<property name="toolTip">
<string>Uses separate game settings for each disc of multi-disc games. Can only be set on the first/main disc.</string>
</property>
<property name="text">
<string>Use Separate Disc Settings</string>
</property>
</widget>
</item>
</layout>
</item>
</layout> </layout>
</widget> </widget>
<resources/> <resources/>

View File

@ -227,14 +227,14 @@ bool QtHost::SaveGameSettings(SettingsInterface* sif, bool delete_if_empty)
// if there's no keys, just toss the whole thing out // if there's no keys, just toss the whole thing out
if (delete_if_empty && ini->IsEmpty()) if (delete_if_empty && ini->IsEmpty())
{ {
INFO_LOG("Removing empty gamesettings ini {}", Path::GetFileName(ini->GetFileName())); INFO_LOG("Removing empty gamesettings ini {}", Path::GetFileName(ini->GetPath()));
// grab the settings lock while we're writing the file, that way the CPU thread doesn't try // grab the settings lock while we're writing the file, that way the CPU thread doesn't try
// to read it at the same time. // to read it at the same time.
const auto lock = Host::GetSettingsLock(); const auto lock = Host::GetSettingsLock();
if (FileSystem::FileExists(ini->GetFileName().c_str()) && if (FileSystem::FileExists(ini->GetPath().c_str()) &&
!FileSystem::DeleteFile(ini->GetFileName().c_str(), &error)) !FileSystem::DeleteFile(ini->GetPath().c_str(), &error))
{ {
Host::ReportErrorAsync( Host::ReportErrorAsync(
TRANSLATE_SV("QtHost", "Error"), TRANSLATE_SV("QtHost", "Error"),
@ -472,7 +472,7 @@ bool QtHost::InitializeConfig(std::string settings_filename)
QStringLiteral( QStringLiteral(
"Failed to save configuration to\n\n%1\n\nThe error was: %2\n\nPlease ensure this directory is writable. You " "Failed to save configuration to\n\n%1\n\nThe error was: %2\n\nPlease ensure this directory is writable. You "
"can also try portable mode by creating portable.txt in the same directory you installed DuckStation into.") "can also try portable mode by creating portable.txt in the same directory you installed DuckStation into.")
.arg(QString::fromStdString(s_base_settings_interface->GetFileName())) .arg(QString::fromStdString(s_base_settings_interface->GetPath()))
.arg(QString::fromStdString(error.GetDescription()))); .arg(QString::fromStdString(error.GetDescription())));
return false; return false;
} }

View File

@ -662,7 +662,10 @@ void SettingsWindow::setGameTitle(std::string title)
{ {
m_title = std::move(title); m_title = std::move(title);
const QString window_title = tr("%1 [%2]").arg(QString::fromStdString(m_title)).arg(QString::fromStdString(m_serial)); const QString window_title =
tr("%1 [%2]")
.arg(QString::fromStdString(m_title))
.arg(QtUtils::StringViewToQString(m_sif ? Path::GetFileName(m_sif->GetPath()) : std::string_view(m_serial)));
setWindowTitle(window_title); setWindowTitle(window_title);
} }
@ -695,13 +698,13 @@ SettingsWindow* SettingsWindow::openGamePropertiesDialog(const std::string& path
} }
std::string real_serial = dentry ? std::string(dentry->serial) : std::move(serial); std::string real_serial = dentry ? std::string(dentry->serial) : std::move(serial);
std::string ini_filename = System::GetGameSettingsPath(real_serial); std::unique_ptr<INISettingsInterface> sif = System::GetGameSettingsInterface(dentry, real_serial, true, false);
// check for an existing dialog with this crc // check for an existing dialog with this serial
for (SettingsWindow* dialog : s_open_game_properties_dialogs) for (SettingsWindow* dialog : s_open_game_properties_dialogs)
{ {
if (dialog->isPerGameSettings() && if (dialog->isPerGameSettings() &&
static_cast<INISettingsInterface*>(dialog->getSettingsInterface())->GetFileName() == ini_filename) static_cast<INISettingsInterface*>(dialog->getSettingsInterface())->GetPath() == sif->GetPath())
{ {
dialog->show(); dialog->show();
dialog->raise(); dialog->raise();
@ -713,10 +716,6 @@ SettingsWindow* SettingsWindow::openGamePropertiesDialog(const std::string& path
} }
} }
std::unique_ptr<INISettingsInterface> sif = std::make_unique<INISettingsInterface>(std::move(ini_filename));
if (FileSystem::FileExists(sif->GetFileName().c_str()))
sif->Load();
SettingsWindow* dialog = SettingsWindow* dialog =
new SettingsWindow(path, std::move(title), std::move(real_serial), hash, region, dentry, std::move(sif)); new SettingsWindow(path, std::move(title), std::move(real_serial), hash, region, dentry, std::move(sif));
dialog->show(); dialog->show();
@ -737,14 +736,14 @@ void SettingsWindow::closeGamePropertiesDialogs()
bool SettingsWindow::setGameSettingsBoolForSerial(const std::string& serial, const char* section, const char* key, bool SettingsWindow::setGameSettingsBoolForSerial(const std::string& serial, const char* section, const char* key,
bool value) bool value)
{ {
std::string ini_filename = System::GetGameSettingsPath(serial); if (serial.empty())
if (ini_filename.empty())
return false; return false;
INISettingsInterface sif(std::move(ini_filename)); std::unique_ptr<INISettingsInterface> sif =
if (FileSystem::FileExists(sif.GetFileName().c_str())) System::GetGameSettingsInterface(GameDatabase::GetEntryForSerial(serial), serial, true, false);
sif.Load(); if (!sif)
return false;
sif.SetBoolValue(section, key, value); sif->SetBoolValue(section, key, value);
return sif.Save(); return sif->Save();
} }

View File

@ -19,7 +19,7 @@ LOG_CHANNEL(Settings);
// we only allow one ini to be parsed at any point in time. // we only allow one ini to be parsed at any point in time.
static std::mutex s_ini_load_save_mutex; static std::mutex s_ini_load_save_mutex;
INISettingsInterface::INISettingsInterface(std::string filename) : m_filename(std::move(filename)), m_ini(true, true) INISettingsInterface::INISettingsInterface(std::string filename) : m_path(std::move(filename)), m_ini(true, true)
{ {
} }
@ -31,7 +31,7 @@ INISettingsInterface::~INISettingsInterface()
bool INISettingsInterface::Load(Error* error /* = nullptr */) bool INISettingsInterface::Load(Error* error /* = nullptr */)
{ {
if (m_filename.empty()) if (m_path.empty())
{ {
Error::SetStringView(error, "Filename is not set."); Error::SetStringView(error, "Filename is not set.");
return false; return false;
@ -39,7 +39,7 @@ bool INISettingsInterface::Load(Error* error /* = nullptr */)
std::unique_lock lock(s_ini_load_save_mutex); std::unique_lock lock(s_ini_load_save_mutex);
SI_Error err = SI_FAIL; SI_Error err = SI_FAIL;
auto fp = FileSystem::OpenManagedCFile(m_filename.c_str(), "rb", error); auto fp = FileSystem::OpenManagedCFile(m_path.c_str(), "rb", error);
if (fp) if (fp)
{ {
err = m_ini.LoadFile(fp.get()); err = m_ini.LoadFile(fp.get());
@ -50,16 +50,23 @@ bool INISettingsInterface::Load(Error* error /* = nullptr */)
return (err == SI_OK); return (err == SI_OK);
} }
bool INISettingsInterface::Load(std::string new_path, Error* error /*= nullptr*/)
{
m_path = std::move(new_path);
m_ini.Reset();
return Load(error);
}
bool INISettingsInterface::Save(Error* error /* = nullptr */) bool INISettingsInterface::Save(Error* error /* = nullptr */)
{ {
if (m_filename.empty()) if (m_path.empty())
{ {
Error::SetStringView(error, "Filename is not set."); Error::SetStringView(error, "Filename is not set.");
return false; return false;
} }
std::unique_lock lock(s_ini_load_save_mutex); std::unique_lock lock(s_ini_load_save_mutex);
FileSystem::AtomicRenamedFile fp = FileSystem::CreateAtomicRenamedFile(m_filename, error); FileSystem::AtomicRenamedFile fp = FileSystem::CreateAtomicRenamedFile(m_path, error);
SI_Error err = SI_FAIL; SI_Error err = SI_FAIL;
if (fp) if (fp)
{ {
@ -78,7 +85,7 @@ bool INISettingsInterface::Save(Error* error /* = nullptr */)
if (err != SI_OK) if (err != SI_OK)
{ {
WARNING_LOG("Failed to save settings to '{}'.", m_filename); WARNING_LOG("Failed to save settings to '{}'.", m_path);
return false; return false;
} }

View File

@ -13,12 +13,13 @@
class INISettingsInterface final : public SettingsInterface class INISettingsInterface final : public SettingsInterface
{ {
public: public:
INISettingsInterface(std::string filename); INISettingsInterface(std::string path);
~INISettingsInterface() override; ~INISettingsInterface() override;
const std::string& GetFileName() const { return m_filename; } const std::string& GetPath() const { return m_path; }
bool Load(Error* error = nullptr); bool Load(Error* error = nullptr);
bool Load(std::string new_path, Error* error = nullptr);
bool Save(Error* error = nullptr) override; bool Save(Error* error = nullptr) override;
void Clear() override; void Clear() override;
@ -61,7 +62,7 @@ public:
using SettingsInterface::GetUIntValue; using SettingsInterface::GetUIntValue;
private: private:
std::string m_filename; std::string m_path;
CSimpleIniA m_ini; CSimpleIniA m_ini;
bool m_dirty = false; bool m_dirty = false;
}; };