Qt: Clean and remove empty game settings

This commit is contained in:
Stenzek 2024-04-25 14:02:16 +10:00
parent d6ffdb0242
commit 1cdfca155d
No known key found for this signature in database
23 changed files with 247 additions and 47 deletions

View file

@ -797,6 +797,11 @@ public:
/** @} /** @}
@{ @name Accessing INI Data */ @{ @name Accessing INI Data */
/** Retrieve the number keys across all sections.
@return number of keys currently present.
*/
size_t GetKeyCount() const;
/** Retrieve all section names. The list is returned as an STL vector of /** Retrieve all section names. The list is returned as an STL vector of
names and can be iterated or searched as necessary. Note that the names and can be iterated or searched as necessary. Note that the
sort order of the returned strings is NOT DEFINED. You can sort sort order of the returned strings is NOT DEFINED. You can sort
@ -2440,6 +2445,17 @@ CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetSection(
return 0; return 0;
} }
template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
size_t
CSimpleIniTempl<SI_CHAR, SI_STRLESS, SI_CONVERTER>::GetKeyCount() const
{
size_t count = 0;
typename TSection::const_iterator i = m_data.begin();
for (; i != m_data.end(); ++i)
count += i->second.size();
return count;
}
template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER> template<class SI_CHAR, class SI_STRLESS, class SI_CONVERTER>
void void
CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetAllSections( CSimpleIniTempl<SI_CHAR,SI_STRLESS,SI_CONVERTER>::GetAllSections(

View file

@ -1736,17 +1736,29 @@ bool FileSystem::CreateDirectory(const char* Path, bool Recursive, Error* error)
} }
} }
bool FileSystem::DeleteFile(const char* path) bool FileSystem::DeleteFile(const char* path, Error* error)
{ {
if (path[0] == '\0') if (path[0] == '\0')
{
Error::SetStringView(error, "Path is empty.");
return false; return false;
}
const std::wstring wpath = GetWin32Path(path); const std::wstring wpath = GetWin32Path(path);
const DWORD fileAttributes = GetFileAttributesW(wpath.c_str()); const DWORD fileAttributes = GetFileAttributesW(wpath.c_str());
if (fileAttributes == INVALID_FILE_ATTRIBUTES || fileAttributes & FILE_ATTRIBUTE_DIRECTORY) if (fileAttributes == INVALID_FILE_ATTRIBUTES || fileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
Error::SetStringView(error, "File does not exist.");
return false;
}
if (!DeleteFileW(wpath.c_str()))
{
Error::SetWin32(error, "DeleteFileW() failed: ", GetLastError());
return false; return false;
}
return (DeleteFileW(wpath.c_str()) == TRUE); return true;
} }
bool FileSystem::RenamePath(const char* old_path, const char* new_path, Error* error) bool FileSystem::RenamePath(const char* old_path, const char* new_path, Error* error)
@ -2241,16 +2253,28 @@ bool FileSystem::CreateDirectory(const char* path, bool recursive, Error* error)
} }
} }
bool FileSystem::DeleteFile(const char* path) bool FileSystem::DeleteFile(const char* path, Error* error)
{ {
if (path[0] == '\0') if (path[0] == '\0')
{
Error::SetStringView(error, "Path is empty.");
return false; return false;
}
struct stat sysStatData; struct stat sysStatData;
if (stat(path, &sysStatData) != 0 || S_ISDIR(sysStatData.st_mode)) if (stat(path, &sysStatData) != 0 || S_ISDIR(sysStatData.st_mode))
{
Error::SetStringView(error, "File does not exist.");
return false;
}
if (unlink(path) != 0)
{
Error::SetErrno(error, "unlink() failed: ", errno);
return false; return false;
}
return (unlink(path) == 0); return true;
} }
bool FileSystem::RenamePath(const char* old_path, const char* new_path, Error* error) bool FileSystem::RenamePath(const char* old_path, const char* new_path, Error* error)

View file

@ -86,7 +86,7 @@ bool DirectoryExists(const char* path);
bool DirectoryIsEmpty(const char* path); bool DirectoryIsEmpty(const char* path);
/// Delete file /// Delete file
bool DeleteFile(const char* path); bool DeleteFile(const char* path, Error* error = nullptr);
/// Rename file /// Rename file
bool RenamePath(const char* OldPath, const char* NewPath, Error* error = nullptr); bool RenamePath(const char* OldPath, const char* NewPath, Error* error = nullptr);

View file

@ -19,6 +19,11 @@ void LayeredSettingsInterface::Clear()
Panic("Attempting to clear layered settings interface"); Panic("Attempting to clear layered settings interface");
} }
bool LayeredSettingsInterface::IsEmpty()
{
return false;
}
bool LayeredSettingsInterface::GetIntValue(const char* section, const char* key, s32* value) const bool LayeredSettingsInterface::GetIntValue(const char* section, const char* key, s32* value) const
{ {
for (u32 layer = FIRST_LAYER; layer <= LAST_LAYER; layer++) for (u32 layer = FIRST_LAYER; layer <= LAST_LAYER; layer++)
@ -170,6 +175,16 @@ void LayeredSettingsInterface::ClearSection(const char* section)
Panic("Attempt to call ClearSection() on layered settings interface"); Panic("Attempt to call ClearSection() on layered settings interface");
} }
void LayeredSettingsInterface::RemoveSection(const char* section)
{
Panic("Attempt to call RemoveSection() on layered settings interface");
}
void LayeredSettingsInterface::RemoveEmptySections()
{
Panic("Attempt to call RemoveEmptySections() on layered settings interface");
}
std::vector<std::string> LayeredSettingsInterface::GetStringList(const char* section, const char* key) const std::vector<std::string> LayeredSettingsInterface::GetStringList(const char* section, const char* key) const
{ {
std::vector<std::string> ret; std::vector<std::string> ret;

View file

@ -27,6 +27,8 @@ public:
void Clear() override; void Clear() override;
bool IsEmpty() override;
bool GetIntValue(const char* section, const char* key, s32* value) const override; bool GetIntValue(const char* section, const char* key, s32* value) const override;
bool GetUIntValue(const char* section, const char* key, u32* value) const override; bool GetUIntValue(const char* section, const char* key, u32* value) const override;
bool GetFloatValue(const char* section, const char* key, float* value) const override; bool GetFloatValue(const char* section, const char* key, float* value) const override;
@ -44,6 +46,8 @@ public:
bool ContainsValue(const char* section, const char* key) const override; bool ContainsValue(const char* section, const char* key) const override;
void DeleteValue(const char* section, const char* key) override; void DeleteValue(const char* section, const char* key) override;
void ClearSection(const char* section) override; void ClearSection(const char* section) override;
void RemoveSection(const char* section) override;
void RemoveEmptySections() override;
std::vector<std::string> GetStringList(const char* section, const char* key) const override; std::vector<std::string> GetStringList(const char* section, const char* key) const override;
void SetStringList(const char* section, const char* key, const std::vector<std::string>& items) override; void SetStringList(const char* section, const char* key, const std::vector<std::string>& items) override;

View file

@ -22,6 +22,11 @@ void MemorySettingsInterface::Clear()
m_sections.clear(); m_sections.clear();
} }
bool MemorySettingsInterface::IsEmpty()
{
return m_sections.empty();
}
bool MemorySettingsInterface::GetIntValue(const char* section, const char* key, s32* value) const bool MemorySettingsInterface::GetIntValue(const char* section, const char* key, s32* value) const
{ {
const auto sit = m_sections.find(section); const auto sit = m_sections.find(section);
@ -315,3 +320,26 @@ void MemorySettingsInterface::ClearSection(const char* section)
m_sections.erase(sit); m_sections.erase(sit);
} }
void MemorySettingsInterface::RemoveSection(const char* section)
{
auto sit = m_sections.find(section);
if (sit == m_sections.end())
return;
m_sections.erase(sit);
}
void MemorySettingsInterface::RemoveEmptySections()
{
for (auto sit = m_sections.begin(); sit != m_sections.end();)
{
if (sit->second.size() > 0)
{
++sit;
continue;
}
sit = m_sections.erase(sit);
}
}

View file

@ -16,6 +16,8 @@ public:
void Clear() override; void Clear() override;
bool IsEmpty() override;
bool GetIntValue(const char* section, const char* key, s32* value) const override; bool GetIntValue(const char* section, const char* key, s32* value) const override;
bool GetUIntValue(const char* section, const char* key, u32* value) const override; bool GetUIntValue(const char* section, const char* key, u32* value) const override;
bool GetFloatValue(const char* section, const char* key, float* value) const override; bool GetFloatValue(const char* section, const char* key, float* value) const override;
@ -37,6 +39,8 @@ public:
bool ContainsValue(const char* section, const char* key) const override; bool ContainsValue(const char* section, const char* key) const override;
void DeleteValue(const char* section, const char* key) override; void DeleteValue(const char* section, const char* key) override;
void ClearSection(const char* section) override; void ClearSection(const char* section) override;
void RemoveSection(const char* section) override;
void RemoveEmptySections();
std::vector<std::string> GetStringList(const char* section, const char* key) const override; std::vector<std::string> GetStringList(const char* section, const char* key) const override;
void SetStringList(const char* section, const char* key, const std::vector<std::string>& items) override; void SetStringList(const char* section, const char* key, const std::vector<std::string>& items) override;

View file

@ -19,6 +19,7 @@ public:
virtual bool Save(Error* error = nullptr) = 0; virtual bool Save(Error* error = nullptr) = 0;
virtual void Clear() = 0; virtual void Clear() = 0;
virtual bool IsEmpty() = 0;
virtual bool GetIntValue(const char* section, const char* key, s32* value) const = 0; virtual bool GetIntValue(const char* section, const char* key, s32* value) const = 0;
virtual bool GetUIntValue(const char* section, const char* key, u32* value) const = 0; virtual bool GetUIntValue(const char* section, const char* key, u32* value) const = 0;
@ -46,6 +47,8 @@ public:
virtual bool ContainsValue(const char* section, const char* key) const = 0; virtual bool ContainsValue(const char* section, const char* key) const = 0;
virtual void DeleteValue(const char* section, const char* key) = 0; virtual void DeleteValue(const char* section, const char* key) = 0;
virtual void ClearSection(const char* section) = 0; virtual void ClearSection(const char* section) = 0;
virtual void RemoveSection(const char* section) = 0;
virtual void RemoveEmptySections() = 0;
ALWAYS_INLINE s32 GetIntValue(const char* section, const char* key, s32 default_value = 0) const ALWAYS_INLINE s32 GetIntValue(const char* section, const char* key, s32 default_value = 0) const
{ {

View file

@ -836,7 +836,29 @@ void FullscreenUI::Render()
{ {
if (s_game_settings_interface) if (s_game_settings_interface)
{ {
s_game_settings_interface->Save(); Error error;
s_game_settings_interface->RemoveEmptySections();
if (s_game_settings_interface->IsEmpty())
{
if (FileSystem::FileExists(s_game_settings_interface->GetFileName().c_str()) &&
!FileSystem::DeleteFile(s_game_settings_interface->GetFileName().c_str(), &error))
{
ImGuiFullscreen::OpenInfoMessageDialog(
FSUI_STR("Error"), fmt::format(FSUI_FSTR("An error occurred while deleting empty game settings:\n{}"),
error.GetDescription()));
}
}
else
{
if (!s_game_settings_interface->Save(&error))
{
ImGuiFullscreen::OpenInfoMessageDialog(
FSUI_STR("Error"),
fmt::format(FSUI_FSTR("An error occurred while saving game settings:\n{}"), error.GetDescription()));
}
}
if (System::IsValid()) if (System::IsValid())
Host::RunOnCPUThread([]() { System::ReloadGameSettings(false); }); Host::RunOnCPUThread([]() { System::ReloadGameSettings(false); });
} }
@ -7052,6 +7074,8 @@ TRANSLATE_NOOP("FullscreenUI", "Advanced Settings");
TRANSLATE_NOOP("FullscreenUI", "All Time: {}"); TRANSLATE_NOOP("FullscreenUI", "All Time: {}");
TRANSLATE_NOOP("FullscreenUI", "Allow Booting Without SBI File"); TRANSLATE_NOOP("FullscreenUI", "Allow Booting Without SBI File");
TRANSLATE_NOOP("FullscreenUI", "Allows loading protected games without subchannel information."); TRANSLATE_NOOP("FullscreenUI", "Allows loading protected games without subchannel information.");
TRANSLATE_NOOP("FullscreenUI", "An error occurred while deleting empty game settings:\n{}");
TRANSLATE_NOOP("FullscreenUI", "An error occurred while saving game settings:\n{}");
TRANSLATE_NOOP("FullscreenUI", "Applies modern dithering techniques to further smooth out gradients when true color is enabled."); TRANSLATE_NOOP("FullscreenUI", "Applies modern dithering techniques to further smooth out gradients when true color is enabled.");
TRANSLATE_NOOP("FullscreenUI", "Apply Image Patches"); TRANSLATE_NOOP("FullscreenUI", "Apply Image Patches");
TRANSLATE_NOOP("FullscreenUI", "Apply Per-Game Settings"); TRANSLATE_NOOP("FullscreenUI", "Apply Per-Game Settings");
@ -7220,6 +7244,7 @@ TRANSLATE_NOOP("FullscreenUI", "Enhancements");
TRANSLATE_NOOP("FullscreenUI", "Ensures every frame generated is displayed for optimal pacing. Enable for variable refresh displays, such as GSync/FreeSync. Disable if you are having speed or sound issues."); TRANSLATE_NOOP("FullscreenUI", "Ensures every frame generated is displayed for optimal pacing. Enable for variable refresh displays, such as GSync/FreeSync. Disable if you are having speed or sound issues.");
TRANSLATE_NOOP("FullscreenUI", "Enter Value"); TRANSLATE_NOOP("FullscreenUI", "Enter Value");
TRANSLATE_NOOP("FullscreenUI", "Enter the name of the input profile you wish to create."); TRANSLATE_NOOP("FullscreenUI", "Enter the name of the input profile you wish to create.");
TRANSLATE_NOOP("FullscreenUI", "Error");
TRANSLATE_NOOP("FullscreenUI", "Execution Mode"); TRANSLATE_NOOP("FullscreenUI", "Execution Mode");
TRANSLATE_NOOP("FullscreenUI", "Exit"); TRANSLATE_NOOP("FullscreenUI", "Exit");
TRANSLATE_NOOP("FullscreenUI", "Exit And Save State"); TRANSLATE_NOOP("FullscreenUI", "Exit And Save State");
@ -7330,6 +7355,7 @@ TRANSLATE_NOOP("FullscreenUI", "Macro {} Buttons");
TRANSLATE_NOOP("FullscreenUI", "Macro {} Frequency"); TRANSLATE_NOOP("FullscreenUI", "Macro {} Frequency");
TRANSLATE_NOOP("FullscreenUI", "Macro {} Trigger"); TRANSLATE_NOOP("FullscreenUI", "Macro {} Trigger");
TRANSLATE_NOOP("FullscreenUI", "Makes games run closer to their console framerate, at a small cost to performance."); TRANSLATE_NOOP("FullscreenUI", "Makes games run closer to their console framerate, at a small cost to performance.");
TRANSLATE_NOOP("FullscreenUI", "Memory Card Busy");
TRANSLATE_NOOP("FullscreenUI", "Memory Card Directory"); TRANSLATE_NOOP("FullscreenUI", "Memory Card Directory");
TRANSLATE_NOOP("FullscreenUI", "Memory Card Port {}"); TRANSLATE_NOOP("FullscreenUI", "Memory Card Port {}");
TRANSLATE_NOOP("FullscreenUI", "Memory Card Settings"); TRANSLATE_NOOP("FullscreenUI", "Memory Card Settings");
@ -7351,6 +7377,7 @@ TRANSLATE_NOOP("FullscreenUI", "No input profiles available.");
TRANSLATE_NOOP("FullscreenUI", "No resume save state found."); TRANSLATE_NOOP("FullscreenUI", "No resume save state found.");
TRANSLATE_NOOP("FullscreenUI", "No save present in this slot."); TRANSLATE_NOOP("FullscreenUI", "No save present in this slot.");
TRANSLATE_NOOP("FullscreenUI", "No save states found."); TRANSLATE_NOOP("FullscreenUI", "No save states found.");
TRANSLATE_NOOP("FullscreenUI", "No, resume the game.");
TRANSLATE_NOOP("FullscreenUI", "None (Double Speed)"); TRANSLATE_NOOP("FullscreenUI", "None (Double Speed)");
TRANSLATE_NOOP("FullscreenUI", "None (Normal Speed)"); TRANSLATE_NOOP("FullscreenUI", "None (Normal Speed)");
TRANSLATE_NOOP("FullscreenUI", "Not Logged In"); TRANSLATE_NOOP("FullscreenUI", "Not Logged In");
@ -7599,6 +7626,7 @@ TRANSLATE_NOOP("FullscreenUI", "Uses perspective-correct interpolation for textu
TRANSLATE_NOOP("FullscreenUI", "Uses screen positions to resolve PGXP data. May improve visuals in some games."); TRANSLATE_NOOP("FullscreenUI", "Uses screen positions to resolve PGXP data. May improve visuals in some games.");
TRANSLATE_NOOP("FullscreenUI", "Value: {} | Default: {} | Minimum: {} | Maximum: {}"); TRANSLATE_NOOP("FullscreenUI", "Value: {} | Default: {} | Minimum: {} | Maximum: {}");
TRANSLATE_NOOP("FullscreenUI", "Vertical Sync (VSync)"); TRANSLATE_NOOP("FullscreenUI", "Vertical Sync (VSync)");
TRANSLATE_NOOP("FullscreenUI", "WARNING: Your game is still saving to the memory card. Continuing to {0} may IRREVERSIBLY DESTROY YOUR MEMORY CARD. We recommend resuming your game and waiting 5 seconds for it to finish saving.\n\nDo you want to {0} anyway?");
TRANSLATE_NOOP("FullscreenUI", "When enabled and logged in, DuckStation will scan for achievements on startup."); TRANSLATE_NOOP("FullscreenUI", "When enabled and logged in, DuckStation will scan for achievements on startup.");
TRANSLATE_NOOP("FullscreenUI", "When enabled, DuckStation will assume all achievements are locked and not send any unlock notifications to the server."); TRANSLATE_NOOP("FullscreenUI", "When enabled, DuckStation will assume all achievements are locked and not send any unlock notifications to the server.");
TRANSLATE_NOOP("FullscreenUI", "When enabled, DuckStation will list achievements from unofficial sets. These achievements are not tracked by RetroAchievements."); TRANSLATE_NOOP("FullscreenUI", "When enabled, DuckStation will list achievements from unofficial sets. These achievements are not tracked by RetroAchievements.");
@ -7611,8 +7639,12 @@ TRANSLATE_NOOP("FullscreenUI", "When this option is chosen, the clock speed set
TRANSLATE_NOOP("FullscreenUI", "Widescreen Hack"); TRANSLATE_NOOP("FullscreenUI", "Widescreen Hack");
TRANSLATE_NOOP("FullscreenUI", "Wireframe Rendering"); TRANSLATE_NOOP("FullscreenUI", "Wireframe Rendering");
TRANSLATE_NOOP("FullscreenUI", "Writes textures which can be replaced to the dump directory."); TRANSLATE_NOOP("FullscreenUI", "Writes textures which can be replaced to the dump directory.");
TRANSLATE_NOOP("FullscreenUI", "Yes, {} now and risk memory card corruption.");
TRANSLATE_NOOP("FullscreenUI", "\"Challenge\" mode for achievements, including leaderboard tracking. Disables save state, cheats, and slowdown functions."); TRANSLATE_NOOP("FullscreenUI", "\"Challenge\" mode for achievements, including leaderboard tracking. Disables save state, cheats, and slowdown functions.");
TRANSLATE_NOOP("FullscreenUI", "\"PlayStation\" and \"PSX\" are registered trademarks of Sony Interactive Entertainment Europe Limited. This software is not affiliated in any way with Sony Interactive Entertainment."); TRANSLATE_NOOP("FullscreenUI", "\"PlayStation\" and \"PSX\" are registered trademarks of Sony Interactive Entertainment Europe Limited. This software is not affiliated in any way with Sony Interactive Entertainment.");
TRANSLATE_NOOP("FullscreenUI", "change disc");
TRANSLATE_NOOP("FullscreenUI", "reset");
TRANSLATE_NOOP("FullscreenUI", "shut down");
TRANSLATE_NOOP("FullscreenUI", "{:%H:%M}"); TRANSLATE_NOOP("FullscreenUI", "{:%H:%M}");
TRANSLATE_NOOP("FullscreenUI", "{:%Y-%m-%d %H:%M:%S}"); TRANSLATE_NOOP("FullscreenUI", "{:%Y-%m-%d %H:%M:%S}");
TRANSLATE_NOOP("FullscreenUI", "{} Frames"); TRANSLATE_NOOP("FullscreenUI", "{} Frames");

View file

@ -336,7 +336,7 @@ void ControllerBindingWidget::doDeviceAutomaticBinding(const QString& device)
else else
{ {
result = InputManager::MapController(*m_dialog->getProfileSettingsInterface(), m_port_number, mapping); result = InputManager::MapController(*m_dialog->getProfileSettingsInterface(), m_port_number, mapping);
m_dialog->getProfileSettingsInterface()->Save(); QtHost::SaveGameSettings(m_dialog->getProfileSettingsInterface(), false);
g_emu_thread->reloadInputBindings(); g_emu_thread->reloadInputBindings();
} }

View file

@ -278,8 +278,7 @@ void ControllerSettingsWindow::setBoolValue(const char* section, const char* key
if (m_profile_interface) if (m_profile_interface)
{ {
m_profile_interface->SetBoolValue(section, key, value); m_profile_interface->SetBoolValue(section, key, value);
m_profile_interface->Save(); saveAndReloadGameSettings();
g_emu_thread->reloadGameSettings();
} }
else else
{ {
@ -294,8 +293,7 @@ void ControllerSettingsWindow::setIntValue(const char* section, const char* key,
if (m_profile_interface) if (m_profile_interface)
{ {
m_profile_interface->SetIntValue(section, key, value); m_profile_interface->SetIntValue(section, key, value);
m_profile_interface->Save(); saveAndReloadGameSettings();
g_emu_thread->reloadGameSettings();
} }
else else
{ {
@ -310,8 +308,7 @@ void ControllerSettingsWindow::setStringValue(const char* section, const char* k
if (m_profile_interface) if (m_profile_interface)
{ {
m_profile_interface->SetStringValue(section, key, value); m_profile_interface->SetStringValue(section, key, value);
m_profile_interface->Save(); saveAndReloadGameSettings();
g_emu_thread->reloadGameSettings();
} }
else else
{ {
@ -321,6 +318,13 @@ void ControllerSettingsWindow::setStringValue(const char* section, const char* k
} }
} }
void ControllerSettingsWindow::saveAndReloadGameSettings()
{
DebugAssert(m_profile_interface);
QtHost::SaveGameSettings(m_profile_interface.get(), false);
g_emu_thread->reloadGameSettings(false);
}
void ControllerSettingsWindow::clearSettingValue(const char* section, const char* key) void ControllerSettingsWindow::clearSettingValue(const char* section, const char* key)
{ {
if (m_profile_interface) if (m_profile_interface)

View file

@ -64,6 +64,7 @@ public:
void setIntValue(const char* section, const char* key, s32 value); void setIntValue(const char* section, const char* key, s32 value);
void setStringValue(const char* section, const char* key, const char* value); void setStringValue(const char* section, const char* key, const char* value);
void clearSettingValue(const char* section, const char* key); void clearSettingValue(const char* section, const char* key);
void saveAndReloadGameSettings();
Q_SIGNALS: Q_SIGNALS:
void inputProfileSwitched(); void inputProfileSwitched();

View file

@ -37,7 +37,7 @@ static void BindWidgetToInputProfileBool(SettingsInterface* sif, WidgetType* wid
Accessor::connectValueChanged(widget, [sif, widget, section = std::move(section), key = std::move(key)]() { Accessor::connectValueChanged(widget, [sif, widget, section = std::move(section), key = std::move(key)]() {
const bool new_value = Accessor::getBoolValue(widget); const bool new_value = Accessor::getBoolValue(widget);
sif->SetBoolValue(section.c_str(), key.c_str(), new_value); sif->SetBoolValue(section.c_str(), key.c_str(), new_value);
sif->Save(); QtHost::SaveGameSettings(sif, false);
g_emu_thread->reloadGameSettings(); g_emu_thread->reloadGameSettings();
}); });
} }
@ -70,7 +70,7 @@ static void BindWidgetToInputProfileFloat(SettingsInterface* sif, WidgetType* wi
Accessor::connectValueChanged(widget, [sif, widget, section = std::move(section), key = std::move(key)]() { Accessor::connectValueChanged(widget, [sif, widget, section = std::move(section), key = std::move(key)]() {
const float new_value = Accessor::getFloatValue(widget); const float new_value = Accessor::getFloatValue(widget);
sif->SetFloatValue(section.c_str(), key.c_str(), new_value); sif->SetFloatValue(section.c_str(), key.c_str(), new_value);
sif->Save(); QtHost::SaveGameSettings(sif, false);
g_emu_thread->reloadGameSettings(); g_emu_thread->reloadGameSettings();
}); });
} }
@ -103,7 +103,7 @@ static void BindWidgetToInputProfileNormalized(SettingsInterface* sif, WidgetTyp
Accessor::connectValueChanged(widget, [sif, widget, section = std::move(section), key = std::move(key), range]() { Accessor::connectValueChanged(widget, [sif, widget, section = std::move(section), key = std::move(key), range]() {
const int new_value = Accessor::getIntValue(widget); const int new_value = Accessor::getIntValue(widget);
sif->SetFloatValue(section.c_str(), key.c_str(), static_cast<float>(new_value) / range); sif->SetFloatValue(section.c_str(), key.c_str(), static_cast<float>(new_value) / range);
sif->Save(); QtHost::SaveGameSettings(sif, false);
g_emu_thread->reloadGameSettings(); g_emu_thread->reloadGameSettings();
}); });
} }
@ -142,7 +142,7 @@ static void BindWidgetToInputProfileString(SettingsInterface* sif, WidgetType* w
else else
sif->DeleteValue(section.c_str(), key.c_str()); sif->DeleteValue(section.c_str(), key.c_str());
sif->Save(); QtHost::SaveGameSettings(sif, false);
g_emu_thread->reloadGameSettings(); g_emu_thread->reloadGameSettings();
}); });
} }

View file

@ -234,7 +234,7 @@ void InputBindingDialog::saveListToSettings()
m_sif->SetStringList(m_section_name.c_str(), m_key_name.c_str(), m_bindings); m_sif->SetStringList(m_section_name.c_str(), m_key_name.c_str(), m_bindings);
else else
m_sif->DeleteValue(m_section_name.c_str(), m_key_name.c_str()); m_sif->DeleteValue(m_section_name.c_str(), m_key_name.c_str());
m_sif->Save(); QtHost::SaveGameSettings(m_sif, false);
g_emu_thread->reloadGameSettings(); g_emu_thread->reloadGameSettings();
} }
else else

View file

@ -218,7 +218,7 @@ void InputBindingWidget::setNewBinding()
if (m_sif) if (m_sif)
{ {
m_sif->SetStringValue(m_section_name.c_str(), m_key_name.c_str(), new_binding.c_str()); m_sif->SetStringValue(m_section_name.c_str(), m_key_name.c_str(), new_binding.c_str());
m_sif->Save(); QtHost::SaveGameSettings(m_sif, false);
g_emu_thread->reloadGameSettings(); g_emu_thread->reloadGameSettings();
} }
else else
@ -239,7 +239,7 @@ void InputBindingWidget::clearBinding()
if (m_sif) if (m_sif)
{ {
m_sif->DeleteValue(m_section_name.c_str(), m_key_name.c_str()); m_sif->DeleteValue(m_section_name.c_str(), m_key_name.c_str());
m_sif->Save(); QtHost::SaveGameSettings(m_sif, false);
g_emu_thread->reloadGameSettings(); g_emu_thread->reloadGameSettings();
} }
else else

View file

@ -41,15 +41,13 @@ void PostProcessingSettingsWidget::commitSettingsUpdate()
{ {
if (m_dialog->isPerGameSettings()) if (m_dialog->isPerGameSettings())
{ {
m_dialog->getSettingsInterface()->Save(); m_dialog->saveAndReloadGameSettings();
g_emu_thread->reloadGameSettings(false);
} }
else else
{ {
Host::CommitBaseSettingChanges(); Host::CommitBaseSettingChanges();
g_emu_thread->updatePostProcessingSettings();
} }
g_emu_thread->updatePostProcessingSettings();
} }
void PostProcessingSettingsWidget::connectUi() void PostProcessingSettingsWidget::connectUi()

View file

@ -171,6 +171,42 @@ INISettingsInterface* QtHost::GetBaseSettingsInterface()
return s_base_settings_interface.get(); return s_base_settings_interface.get();
} }
bool QtHost::SaveGameSettings(SettingsInterface* sif, bool delete_if_empty)
{
INISettingsInterface* ini = static_cast<INISettingsInterface*>(sif);
Error error;
// if there's no keys, just toss the whole thing out
if (delete_if_empty && ini->IsEmpty())
{
Log_InfoFmt("Removing empty gamesettings ini {}", Path::GetFileName(ini->GetFileName()));
if (FileSystem::FileExists(ini->GetFileName().c_str()) &&
!FileSystem::DeleteFile(ini->GetFileName().c_str(), &error))
{
Host::ReportErrorAsync(
TRANSLATE_SV("QtHost", "Error"),
fmt::format(TRANSLATE_FS("QtHost", "An error occurred while deleting empty game settings:\n{}"),
error.GetDescription()));
return false;
}
return true;
}
// clean unused sections, stops the file being bloated
sif->RemoveEmptySections();
if (!sif->Save(&error))
{
Host::ReportErrorAsync(
TRANSLATE_SV("QtHost", "Error"),
fmt::format(TRANSLATE_FS("QtHost", "An error occurred while saving game settings:\n{}"), error.GetDescription()));
return false;
}
return true;
}
QIcon QtHost::GetAppIcon() QIcon QtHost::GetAppIcon()
{ {
return QIcon(QStringLiteral(":/icons/duck.png")); return QIcon(QStringLiteral(":/icons/duck.png"));

View file

@ -280,6 +280,9 @@ QString GetResourcesBasePath();
/// Returns the base settings interface. Should lock before manipulating. /// Returns the base settings interface. Should lock before manipulating.
INISettingsInterface* GetBaseSettingsInterface(); INISettingsInterface* GetBaseSettingsInterface();
/// Saves a game settings interface.
bool SaveGameSettings(SettingsInterface* sif, bool delete_if_empty);
/// Downloads the specified URL to the provided path. /// Downloads the specified URL to the provided path.
bool DownloadFile(QWidget* parent, const QString& title, std::string url, const char* path); bool DownloadFile(QWidget* parent, const QString& title, std::string url, const char* path);

View file

@ -289,8 +289,7 @@ void SettingsWindow::onCopyGlobalSettingsClicked()
temp.Load(*Host::Internal::GetBaseSettingsLayer()); temp.Load(*Host::Internal::GetBaseSettingsLayer());
temp.Save(*m_sif.get(), true); temp.Save(*m_sif.get(), true);
} }
m_sif->Save(); saveAndReloadGameSettings();
g_emu_thread->reloadGameSettings();
reloadPages(); reloadPages();
@ -311,8 +310,7 @@ void SettingsWindow::onClearSettingsClicked()
} }
Settings::Clear(*m_sif.get()); Settings::Clear(*m_sif.get());
m_sif->Save(); saveAndReloadGameSettings();
g_emu_thread->reloadGameSettings();
reloadPages(); reloadPages();
@ -524,8 +522,7 @@ void SettingsWindow::setBoolSettingValue(const char* section, const char* key, s
if (m_sif) if (m_sif)
{ {
value.has_value() ? m_sif->SetBoolValue(section, key, value.value()) : m_sif->DeleteValue(section, key); value.has_value() ? m_sif->SetBoolValue(section, key, value.value()) : m_sif->DeleteValue(section, key);
m_sif->Save(); saveAndReloadGameSettings();
g_emu_thread->reloadGameSettings();
} }
else else
{ {
@ -541,8 +538,7 @@ void SettingsWindow::setIntSettingValue(const char* section, const char* key, st
if (m_sif) if (m_sif)
{ {
value.has_value() ? m_sif->SetIntValue(section, key, value.value()) : m_sif->DeleteValue(section, key); value.has_value() ? m_sif->SetIntValue(section, key, value.value()) : m_sif->DeleteValue(section, key);
m_sif->Save(); saveAndReloadGameSettings();
g_emu_thread->reloadGameSettings();
} }
else else
{ {
@ -558,8 +554,7 @@ void SettingsWindow::setFloatSettingValue(const char* section, const char* key,
if (m_sif) if (m_sif)
{ {
value.has_value() ? m_sif->SetFloatValue(section, key, value.value()) : m_sif->DeleteValue(section, key); value.has_value() ? m_sif->SetFloatValue(section, key, value.value()) : m_sif->DeleteValue(section, key);
m_sif->Save(); saveAndReloadGameSettings();
g_emu_thread->reloadGameSettings();
} }
else else
{ {
@ -575,8 +570,7 @@ void SettingsWindow::setStringSettingValue(const char* section, const char* key,
if (m_sif) if (m_sif)
{ {
value.has_value() ? m_sif->SetStringValue(section, key, value.value()) : m_sif->DeleteValue(section, key); value.has_value() ? m_sif->SetStringValue(section, key, value.value()) : m_sif->DeleteValue(section, key);
m_sif->Save(); saveAndReloadGameSettings();
g_emu_thread->reloadGameSettings();
} }
else else
{ {
@ -600,8 +594,7 @@ void SettingsWindow::removeSettingValue(const char* section, const char* key)
if (m_sif) if (m_sif)
{ {
m_sif->DeleteValue(section, key); m_sif->DeleteValue(section, key);
m_sif->Save(); saveAndReloadGameSettings();
g_emu_thread->reloadGameSettings();
} }
else else
{ {
@ -611,6 +604,13 @@ void SettingsWindow::removeSettingValue(const char* section, const char* key)
} }
} }
void SettingsWindow::saveAndReloadGameSettings()
{
DebugAssert(m_sif);
QtHost::SaveGameSettings(m_sif.get(), true);
g_emu_thread->reloadGameSettings(false);
}
void SettingsWindow::openGamePropertiesDialog(const std::string& path, const std::string& serial, DiscRegion region) void SettingsWindow::openGamePropertiesDialog(const std::string& path, const std::string& serial, DiscRegion region)
{ {
const GameDatabase::Entry* dentry = nullptr; const GameDatabase::Entry* dentry = nullptr;

View file

@ -88,6 +88,7 @@ public:
void setStringSettingValue(const char* section, const char* key, std::optional<const char*> value); void setStringSettingValue(const char* section, const char* key, std::optional<const char*> value);
bool containsSettingValue(const char* section, const char* key) const; bool containsSettingValue(const char* section, const char* key) const;
void removeSettingValue(const char* section, const char* key); void removeSettingValue(const char* section, const char* key);
void saveAndReloadGameSettings();
Q_SIGNALS: Q_SIGNALS:
void settingsResetToDefaults(); void settingsResetToDefaults();

View file

@ -710,7 +710,7 @@ static void BindWidgetToBoolSetting(SettingsInterface* sif, WidgetType* widget,
else else
sif->DeleteValue(section.c_str(), key.c_str()); sif->DeleteValue(section.c_str(), key.c_str());
sif->Save(); QtHost::SaveGameSettings(sif, true);
g_emu_thread->reloadGameSettings(); g_emu_thread->reloadGameSettings();
}); });
} }
@ -753,7 +753,7 @@ static void BindWidgetToIntSetting(SettingsInterface* sif, WidgetType* widget, s
else else
sif->DeleteValue(section.c_str(), key.c_str()); sif->DeleteValue(section.c_str(), key.c_str());
sif->Save(); QtHost::SaveGameSettings(sif, true);
g_emu_thread->reloadGameSettings(); g_emu_thread->reloadGameSettings();
}); });
} }
@ -828,7 +828,7 @@ static inline void BindWidgetAndLabelToIntSetting(SettingsInterface* sif, Widget
} }
} }
sif->Save(); QtHost::SaveGameSettings(sif, true);
g_emu_thread->reloadGameSettings(); g_emu_thread->reloadGameSettings();
}); });
} }
@ -876,7 +876,7 @@ static void BindWidgetToFloatSetting(SettingsInterface* sif, WidgetType* widget,
else else
sif->DeleteValue(section.c_str(), key.c_str()); sif->DeleteValue(section.c_str(), key.c_str());
sif->Save(); QtHost::SaveGameSettings(sif, true);
g_emu_thread->reloadGameSettings(); g_emu_thread->reloadGameSettings();
}); });
} }
@ -917,7 +917,7 @@ static void BindWidgetToNormalizedSetting(SettingsInterface* sif, WidgetType* wi
else else
sif->DeleteValue(section.c_str(), key.c_str()); sif->DeleteValue(section.c_str(), key.c_str());
sif->Save(); QtHost::SaveGameSettings(sif, true);
g_emu_thread->reloadGameSettings(); g_emu_thread->reloadGameSettings();
}); });
} }
@ -959,7 +959,7 @@ static void BindWidgetToStringSetting(SettingsInterface* sif, WidgetType* widget
else else
sif->DeleteValue(section.c_str(), key.c_str()); sif->DeleteValue(section.c_str(), key.c_str());
sif->Save(); QtHost::SaveGameSettings(sif, true);
g_emu_thread->reloadGameSettings(); g_emu_thread->reloadGameSettings();
}); });
} }
@ -1024,7 +1024,7 @@ static void BindWidgetToEnumSetting(SettingsInterface* sif, WidgetType* widget,
sif->DeleteValue(section.c_str(), key.c_str()); sif->DeleteValue(section.c_str(), key.c_str());
} }
sif->Save(); QtHost::SaveGameSettings(sif, true);
g_emu_thread->reloadGameSettings(); g_emu_thread->reloadGameSettings();
}); });
} }
@ -1092,7 +1092,7 @@ static void BindWidgetToEnumSetting(SettingsInterface* sif, WidgetType* widget,
else else
sif->DeleteValue(section.c_str(), key.c_str()); sif->DeleteValue(section.c_str(), key.c_str());
sif->Save(); QtHost::SaveGameSettings(sif, true);
g_emu_thread->reloadGameSettings(); g_emu_thread->reloadGameSettings();
}); });
} }
@ -1160,7 +1160,7 @@ static void BindWidgetToEnumSetting(SettingsInterface* sif, WidgetType* widget,
else else
sif->DeleteValue(section.c_str(), key.c_str()); sif->DeleteValue(section.c_str(), key.c_str());
sif->Save(); QtHost::SaveGameSettings(sif, true);
g_emu_thread->reloadGameSettings(); g_emu_thread->reloadGameSettings();
}); });
} }

View file

@ -135,6 +135,11 @@ void INISettingsInterface::Clear()
m_ini.Reset(); m_ini.Reset();
} }
bool INISettingsInterface::IsEmpty()
{
return (m_ini.GetKeyCount() == 0);
}
bool INISettingsInterface::GetIntValue(const char* section, const char* key, s32* value) const bool INISettingsInterface::GetIntValue(const char* section, const char* key, s32* value) const
{ {
const char* str_value = m_ini.GetValue(section, key); const char* str_value = m_ini.GetValue(section, key);
@ -279,6 +284,29 @@ void INISettingsInterface::ClearSection(const char* section)
m_ini.SetValue(section, nullptr, nullptr); m_ini.SetValue(section, nullptr, nullptr);
} }
void INISettingsInterface::RemoveSection(const char* section)
{
if (!m_ini.GetSection(section))
return;
m_dirty = true;
m_ini.Delete(section, nullptr);
}
void INISettingsInterface::RemoveEmptySections()
{
std::list<CSimpleIniA::Entry> entries;
m_ini.GetAllSections(entries);
for (const CSimpleIniA::Entry& entry : entries)
{
if (m_ini.GetSectionSize(entry.pItem) > 0)
continue;
m_dirty = true;
m_ini.Delete(entry.pItem, nullptr);
}
}
std::vector<std::string> INISettingsInterface::GetStringList(const char* section, const char* key) const std::vector<std::string> INISettingsInterface::GetStringList(const char* section, const char* key) const
{ {
std::list<CSimpleIniA::Entry> entries; std::list<CSimpleIniA::Entry> entries;

View file

@ -22,6 +22,7 @@ public:
bool Save(Error* error = nullptr) override; bool Save(Error* error = nullptr) override;
void Clear() override; void Clear() override;
bool IsEmpty() override;
bool GetIntValue(const char* section, const char* key, s32* value) const override; bool GetIntValue(const char* section, const char* key, s32* value) const override;
bool GetUIntValue(const char* section, const char* key, u32* value) const override; bool GetUIntValue(const char* section, const char* key, u32* value) const override;
@ -40,6 +41,8 @@ public:
bool ContainsValue(const char* section, const char* key) const override; bool ContainsValue(const char* section, const char* key) const override;
void DeleteValue(const char* section, const char* key) override; void DeleteValue(const char* section, const char* key) override;
void ClearSection(const char* section) override; void ClearSection(const char* section) override;
void RemoveSection(const char* section) override;
void RemoveEmptySections() override;
std::vector<std::string> GetStringList(const char* section, const char* key) const override; std::vector<std::string> GetStringList(const char* section, const char* key) const override;
void SetStringList(const char* section, const char* key, const std::vector<std::string>& items) override; void SetStringList(const char* section, const char* key, const std::vector<std::string>& items) override;