From 4a92229945c5de05d72fb61681a82e4c53e2ef61 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sat, 4 May 2024 23:33:18 +1000 Subject: [PATCH 1/4] Qt: Fix volume reset button in game properties --- pcsx2-qt/Settings/AudioSettingsWidget.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pcsx2-qt/Settings/AudioSettingsWidget.cpp b/pcsx2-qt/Settings/AudioSettingsWidget.cpp index e047217e261b4..5c9c977087bf9 100644 --- a/pcsx2-qt/Settings/AudioSettingsWidget.cpp +++ b/pcsx2-qt/Settings/AudioSettingsWidget.cpp @@ -486,9 +486,9 @@ void AudioSettingsWidget::resetVolume(bool fast_forward) if (m_dialog->isPerGameSettings()) { - m_dialog->removeSettingValue("Audio", key); + m_dialog->removeSettingValue("SPU2/Output", key); - const int value = m_dialog->getEffectiveIntValue("Audio", key, 100); + const int value = m_dialog->getEffectiveIntValue("SPU2/Output", key, 100); QSignalBlocker sb(slider); slider->setValue(value); label->setText(QStringLiteral("%1%2").arg(value).arg(tr("%"))); From d45f4c4d52844bb213604c28f5e09157c9e4841a Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sat, 4 May 2024 23:33:35 +1000 Subject: [PATCH 2/4] Qt: Clean and remove empty game settings --- 3rdparty/simpleini/include/SimpleIni.h | 16 ++++ common/FileSystem.cpp | 32 +++++++- common/FileSystem.h | 2 +- common/MemorySettingsInterface.cpp | 28 +++++++ common/MemorySettingsInterface.h | 4 + common/SettingsInterface.h | 3 + pcsx2-qt/QtHost.cpp | 36 +++++++++ pcsx2-qt/QtHost.h | 3 + pcsx2-qt/SettingWidgetBinder.h | 18 ++--- .../Settings/ControllerBindingWidgets.cpp | 2 +- .../ControllerGlobalSettingsWidget.cpp | 16 ++-- .../Settings/ControllerSettingWidgetBinder.h | 10 +-- .../Settings/ControllerSettingsWindow.cpp | 19 +++-- pcsx2-qt/Settings/ControllerSettingsWindow.h | 1 + pcsx2-qt/Settings/SettingsWindow.cpp | 29 +++---- pcsx2-qt/Settings/SettingsWindow.h | 1 + pcsx2/INISettingsInterface.cpp | 28 +++++++ pcsx2/INISettingsInterface.h | 3 + pcsx2/ImGui/FullscreenUI.cpp | 78 +++++++++++-------- pcsx2/LayeredSettingsInterface.cpp | 15 ++++ pcsx2/LayeredSettingsInterface.h | 4 + 21 files changed, 267 insertions(+), 81 deletions(-) diff --git a/3rdparty/simpleini/include/SimpleIni.h b/3rdparty/simpleini/include/SimpleIni.h index 72148df19e56b..262989af677b9 100644 --- a/3rdparty/simpleini/include/SimpleIni.h +++ b/3rdparty/simpleini/include/SimpleIni.h @@ -752,6 +752,11 @@ class CSimpleIniTempl /** @} @{ @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 names and can be iterated or searched as necessary. Note that the sort order of the returned strings is NOT DEFINED. You can sort @@ -2274,6 +2279,17 @@ CSimpleIniTempl::GetSectionSize( return nCount; } +template +size_t +CSimpleIniTempl::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 const typename CSimpleIniTempl::TKeyVal * CSimpleIniTempl::GetSection( diff --git a/common/FileSystem.cpp b/common/FileSystem.cpp index 467f14581700a..437f23e8dcb65 100644 --- a/common/FileSystem.cpp +++ b/common/FileSystem.cpp @@ -1725,17 +1725,29 @@ bool FileSystem::CreateDirectoryPath(const char* Path, bool Recursive, Error* er } } -bool FileSystem::DeleteFilePath(const char* path) +bool FileSystem::DeleteFilePath(const char* path, Error* error) { if (path[0] == '\0') + { + Error::SetStringView(error, "Path is empty."); return false; + } const std::wstring wpath = GetWin32Path(path); const DWORD fileAttributes = GetFileAttributesW(wpath.c_str()); 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 (DeleteFileW(wpath.c_str()) == TRUE); + return true; } bool FileSystem::RenamePath(const char* old_path, const char* new_path, Error* error) @@ -2213,16 +2225,28 @@ bool FileSystem::CreateDirectoryPath(const char* path, bool recursive, Error* er } } -bool FileSystem::DeleteFilePath(const char* path) +bool FileSystem::DeleteFilePath(const char* path, Error* error) { if (path[0] == '\0') + { + Error::SetStringView(error, "Path is empty."); return false; + } struct stat sysStatData; 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 (unlink(path) == 0); + return true; } bool FileSystem::RenamePath(const char* old_path, const char* new_path, Error* error) diff --git a/common/FileSystem.h b/common/FileSystem.h index f7672c5aa6bae..e77c46ab3d72d 100644 --- a/common/FileSystem.h +++ b/common/FileSystem.h @@ -87,7 +87,7 @@ namespace FileSystem bool DirectoryIsEmpty(const char* path); /// Delete file - bool DeleteFilePath(const char* path); + bool DeleteFilePath(const char* path, Error* error = nullptr); /// Rename file bool RenamePath(const char* OldPath, const char* NewPath, Error* error = nullptr); diff --git a/common/MemorySettingsInterface.cpp b/common/MemorySettingsInterface.cpp index 4483adc6272c6..17cdc74087084 100644 --- a/common/MemorySettingsInterface.cpp +++ b/common/MemorySettingsInterface.cpp @@ -20,6 +20,11 @@ void MemorySettingsInterface::Clear() m_sections.clear(); } +bool MemorySettingsInterface::IsEmpty() +{ + return m_sections.empty(); +} + bool MemorySettingsInterface::GetIntValue(const char* section, const char* key, s32* value) const { const auto sit = m_sections.find(section); @@ -312,3 +317,26 @@ void MemorySettingsInterface::ClearSection(const char* section) 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); + } +} diff --git a/common/MemorySettingsInterface.h b/common/MemorySettingsInterface.h index 1fac52e5970e2..eefafe22fcb31 100644 --- a/common/MemorySettingsInterface.h +++ b/common/MemorySettingsInterface.h @@ -16,6 +16,8 @@ class MemorySettingsInterface final : public SettingsInterface void Clear() override; + bool IsEmpty() 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 GetFloatValue(const char* section, const char* key, float* value) const override; @@ -37,6 +39,8 @@ class MemorySettingsInterface final : public SettingsInterface bool ContainsValue(const char* section, const char* key) const override; void DeleteValue(const char* section, const char* key) override; void ClearSection(const char* section) override; + void RemoveSection(const char* section) override; + void RemoveEmptySections() override; std::vector GetStringList(const char* section, const char* key) const override; void SetStringList(const char* section, const char* key, const std::vector& items) override; diff --git a/common/SettingsInterface.h b/common/SettingsInterface.h index b90c443c974bf..4792317962f4a 100644 --- a/common/SettingsInterface.h +++ b/common/SettingsInterface.h @@ -19,6 +19,7 @@ class SettingsInterface virtual bool Save(Error* error = nullptr) = 0; virtual void Clear() = 0; + virtual bool IsEmpty() = 0; virtual bool GetIntValue(const char* section, const char* key, int* value) const = 0; virtual bool GetUIntValue(const char* section, const char* key, uint* value) const = 0; @@ -46,6 +47,8 @@ class SettingsInterface virtual bool ContainsValue(const char* section, const char* key) const = 0; virtual void DeleteValue(const char* section, const char* key) = 0; virtual void ClearSection(const char* section) = 0; + virtual void RemoveSection(const char* section) = 0; + virtual void RemoveEmptySections() = 0; __fi int GetIntValue(const char* section, const char* key, int default_value = 0) const { diff --git a/pcsx2-qt/QtHost.cpp b/pcsx2-qt/QtHost.cpp index 24bfbc2b98554..aabf05e072179 100644 --- a/pcsx2-qt/QtHost.cpp +++ b/pcsx2-qt/QtHost.cpp @@ -1443,6 +1443,42 @@ std::string QtHost::GetRuntimeDownloadedResourceURL(std::string_view name) return fmt::format("{}/{}", RUNTIME_RESOURCES_URL, Path::URLEncode(name)); } +bool QtHost::SaveGameSettings(SettingsInterface* sif, bool delete_if_empty) +{ + INISettingsInterface* ini = static_cast(sif); + Error error; + + // if there's no keys, just toss the whole thing out + if (delete_if_empty && ini->IsEmpty()) + { + INFO_LOG("Removing empty gamesettings ini {}", Path::GetFileName(ini->GetFileName())); + if (FileSystem::FileExists(ini->GetFileName().c_str()) && + !FileSystem::DeleteFilePath(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; +} + std::optional QtHost::DownloadFile(QWidget* parent, const QString& title, std::string url, std::vector* data) { static constexpr u32 HTTP_POLL_INTERVAL = 10; diff --git a/pcsx2-qt/QtHost.h b/pcsx2-qt/QtHost.h index c8ce4c7a8e744..a79f2a8ecb356 100644 --- a/pcsx2-qt/QtHost.h +++ b/pcsx2-qt/QtHost.h @@ -283,6 +283,9 @@ namespace QtHost /// Returns the URL to a runtime-downloaded resource. std::string GetRuntimeDownloadedResourceURL(std::string_view name); + /// Saves a game settings interface. + bool SaveGameSettings(SettingsInterface* sif, bool delete_if_empty); + /// Downloads the specified URL to the provided path. bool DownloadFile(QWidget* parent, const QString& title, std::string url, const std::string& path); diff --git a/pcsx2-qt/SettingWidgetBinder.h b/pcsx2-qt/SettingWidgetBinder.h index 34a8f87bdef3f..8e91b1967d32a 100644 --- a/pcsx2-qt/SettingWidgetBinder.h +++ b/pcsx2-qt/SettingWidgetBinder.h @@ -682,7 +682,7 @@ namespace SettingWidgetBinder else sif->DeleteValue(section.c_str(), key.c_str()); - sif->Save(); + QtHost::SaveGameSettings(sif, true); g_emu_thread->reloadGameSettings(); }); } @@ -723,7 +723,7 @@ namespace SettingWidgetBinder else sif->DeleteValue(section.c_str(), key.c_str()); - sif->Save(); + QtHost::SaveGameSettings(sif, true); g_emu_thread->reloadGameSettings(); }); } @@ -797,7 +797,7 @@ namespace SettingWidgetBinder } } - sif->Save(); + QtHost::SaveGameSettings(sif, true); g_emu_thread->reloadGameSettings(); }); } @@ -845,7 +845,7 @@ namespace SettingWidgetBinder else sif->DeleteValue(section.c_str(), key.c_str()); - sif->Save(); + QtHost::SaveGameSettings(sif, true); g_emu_thread->reloadGameSettings(); }); } @@ -886,7 +886,7 @@ namespace SettingWidgetBinder else sif->DeleteValue(section.c_str(), key.c_str()); - sif->Save(); + QtHost::SaveGameSettings(sif, true); g_emu_thread->reloadGameSettings(); }); } @@ -927,7 +927,7 @@ namespace SettingWidgetBinder else sif->DeleteValue(section.c_str(), key.c_str()); - sif->Save(); + QtHost::SaveGameSettings(sif, true); g_emu_thread->reloadGameSettings(); }); } @@ -989,7 +989,7 @@ namespace SettingWidgetBinder sif->DeleteValue(section.c_str(), key.c_str()); } - sif->Save(); + QtHost::SaveGameSettings(sif, true); g_emu_thread->reloadGameSettings(); }); } @@ -1055,7 +1055,7 @@ namespace SettingWidgetBinder else sif->DeleteValue(section.c_str(), key.c_str()); - sif->Save(); + QtHost::SaveGameSettings(sif, true); g_emu_thread->reloadGameSettings(); }); } @@ -1120,7 +1120,7 @@ namespace SettingWidgetBinder else sif->DeleteValue(section.c_str(), key.c_str()); - sif->Save(); + QtHost::SaveGameSettings(sif, true); g_emu_thread->reloadGameSettings(); }); } diff --git a/pcsx2-qt/Settings/ControllerBindingWidgets.cpp b/pcsx2-qt/Settings/ControllerBindingWidgets.cpp index 6d545b6b9bc2a..50638b0164394 100644 --- a/pcsx2-qt/Settings/ControllerBindingWidgets.cpp +++ b/pcsx2-qt/Settings/ControllerBindingWidgets.cpp @@ -223,7 +223,7 @@ void ControllerBindingWidget::onClearBindingsClicked() else { Pad::ClearPortBindings(*m_dialog->getProfileSettingsInterface(), m_port_number); - m_dialog->getProfileSettingsInterface()->Save(); + QtHost::SaveGameSettings(m_dialog->getProfileSettingsInterface(), false); } // force a refresh after clearing diff --git a/pcsx2-qt/Settings/ControllerGlobalSettingsWidget.cpp b/pcsx2-qt/Settings/ControllerGlobalSettingsWidget.cpp index 2dbc357d961bd..4daf2b5d016ef 100644 --- a/pcsx2-qt/Settings/ControllerGlobalSettingsWidget.cpp +++ b/pcsx2-qt/Settings/ControllerGlobalSettingsWidget.cpp @@ -18,13 +18,13 @@ ControllerGlobalSettingsWidget::ControllerGlobalSettingsWidget(QWidget* parent, SettingsInterface* sif = dialog->getProfileSettingsInterface(); - SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enableSDLSource, "InputSources", "SDL", true); - SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enableSDLEnhancedMode, "InputSources", "SDLControllerEnhancedMode", false); + ControllerSettingWidgetBinder::BindWidgetToInputProfileBool(sif, m_ui.enableSDLSource, "InputSources", "SDL", true); + ControllerSettingWidgetBinder::BindWidgetToInputProfileBool(sif, m_ui.enableSDLEnhancedMode, "InputSources", "SDLControllerEnhancedMode", false); connect(m_ui.enableSDLSource, &QCheckBox::checkStateChanged, this, &ControllerGlobalSettingsWidget::updateSDLOptionsEnabled); connect(m_ui.ledSettings, &QToolButton::clicked, this, &ControllerGlobalSettingsWidget::ledSettingsClicked); #ifdef _WIN32 - SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enableSDLRawInput, "InputSources", "SDLRawInput", false); + ControllerSettingWidgetBinder::BindWidgetToInputProfileBool(sif, m_ui.enableSDLRawInput, "InputSources", "SDLRawInput", false); #else m_ui.sdlGridLayout->removeWidget(m_ui.enableSDLRawInput); m_ui.enableSDLRawInput->deleteLater(); @@ -32,8 +32,8 @@ ControllerGlobalSettingsWidget::ControllerGlobalSettingsWidget(QWidget* parent, #endif #ifdef __APPLE__ - SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enableSDLIOKitDriver, "InputSources", "SDLIOKitDriver", true); - SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enableSDLMFIDriver, "InputSources", "SDLMFIDriver", true); + ControllerSettingWidgetBinder::BindWidgetToInputProfileBool(sif, m_ui.enableSDLIOKitDriver, "InputSources", "SDLIOKitDriver", true); + ControllerSettingWidgetBinder::BindWidgetToInputProfileBool(sif, m_ui.enableSDLMFIDriver, "InputSources", "SDLMFIDriver", true); #else m_ui.sdlGridLayout->removeWidget(m_ui.enableSDLIOKitDriver); m_ui.enableSDLIOKitDriver->deleteLater(); @@ -50,8 +50,8 @@ ControllerGlobalSettingsWidget::ControllerGlobalSettingsWidget(QWidget* parent, ControllerSettingWidgetBinder::BindWidgetToInputProfileBool(sif, m_ui.multitapPort2, "Pad", "MultitapPort2", false); #ifdef _WIN32 - SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enableXInputSource, "InputSources", "XInput", false); - SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.enableDInputSource, "InputSources", "DInput", false); + ControllerSettingWidgetBinder::BindWidgetToInputProfileBool(sif, m_ui.enableXInputSource, "InputSources", "XInput", false); + ControllerSettingWidgetBinder::BindWidgetToInputProfileBool(sif, m_ui.enableDInputSource, "InputSources", "DInput", false); #else m_ui.mainLayout->removeWidget(m_ui.xinputGroup); m_ui.xinputGroup->deleteLater(); @@ -204,7 +204,7 @@ ControllerMappingSettingsDialog::ControllerMappingSettingsDialog(ControllerSetti m_ui.icon->setPixmap(QIcon::fromTheme(QStringLiteral("settings-3-line")).pixmap(32, 32)); - SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.ignoreInversion, "InputSources", "IgnoreInversion", false); + ControllerSettingWidgetBinder::BindWidgetToInputProfileBool(sif, m_ui.ignoreInversion, "InputSources", "IgnoreInversion", false); connect(m_ui.buttonBox->button(QDialogButtonBox::Close), &QPushButton::clicked, this, &QDialog::accept); } diff --git a/pcsx2-qt/Settings/ControllerSettingWidgetBinder.h b/pcsx2-qt/Settings/ControllerSettingWidgetBinder.h index 44faa30d3e76b..8457fd01637e3 100644 --- a/pcsx2-qt/Settings/ControllerSettingWidgetBinder.h +++ b/pcsx2-qt/Settings/ControllerSettingWidgetBinder.h @@ -39,7 +39,7 @@ namespace ControllerSettingWidgetBinder Accessor::connectValueChanged(widget, [sif, widget, section = std::move(section), key = std::move(key)]() { const bool new_value = Accessor::getBoolValue(widget); sif->SetBoolValue(section.c_str(), key.c_str(), new_value); - sif->Save(); + QtHost::SaveGameSettings(sif, false); g_emu_thread->reloadGameSettings(); }); } @@ -72,7 +72,7 @@ namespace ControllerSettingWidgetBinder Accessor::connectValueChanged(widget, [sif, widget, section = std::move(section), key = std::move(key), option_offset]() { const float new_value = Accessor::getIntValue(widget); sif->SetIntValue(section.c_str(), key.c_str(), new_value + option_offset); - sif->Save(); + QtHost::SaveGameSettings(sif, false); g_emu_thread->reloadGameSettings(); }); } @@ -105,7 +105,7 @@ namespace ControllerSettingWidgetBinder Accessor::connectValueChanged(widget, [sif, widget, section = std::move(section), key = std::move(key), multiplier]() { const float new_value = Accessor::getFloatValue(widget) / multiplier; sif->SetFloatValue(section.c_str(), key.c_str(), new_value); - sif->Save(); + QtHost::SaveGameSettings(sif, false); g_emu_thread->reloadGameSettings(); }); } @@ -138,7 +138,7 @@ namespace ControllerSettingWidgetBinder Accessor::connectValueChanged(widget, [sif, widget, section = std::move(section), key = std::move(key), range]() { const int new_value = Accessor::getIntValue(widget); sif->SetFloatValue(section.c_str(), key.c_str(), static_cast(new_value) / range); - sif->Save(); + QtHost::SaveGameSettings(sif, false); g_emu_thread->reloadGameSettings(); }); } @@ -176,7 +176,7 @@ namespace ControllerSettingWidgetBinder else sif->DeleteValue(section.c_str(), key.c_str()); - sif->Save(); + QtHost::SaveGameSettings(sif, false); g_emu_thread->reloadGameSettings(); }); } diff --git a/pcsx2-qt/Settings/ControllerSettingsWindow.cpp b/pcsx2-qt/Settings/ControllerSettingsWindow.cpp index 9416a9eca2f72..94157020d49f4 100644 --- a/pcsx2-qt/Settings/ControllerSettingsWindow.cpp +++ b/pcsx2-qt/Settings/ControllerSettingsWindow.cpp @@ -289,8 +289,7 @@ void ControllerSettingsWindow::setBoolValue(const char* section, const char* key if (m_profile_interface) { m_profile_interface->SetBoolValue(section, key, value); - m_profile_interface->Save(); - g_emu_thread->reloadGameSettings(); + saveAndReloadGameSettings(); } else { @@ -305,8 +304,7 @@ void ControllerSettingsWindow::setIntValue(const char* section, const char* key, if (m_profile_interface) { m_profile_interface->SetIntValue(section, key, value); - m_profile_interface->Save(); - g_emu_thread->reloadGameSettings(); + saveAndReloadGameSettings(); } else { @@ -321,8 +319,7 @@ void ControllerSettingsWindow::setStringValue(const char* section, const char* k if (m_profile_interface) { m_profile_interface->SetStringValue(section, key, value); - m_profile_interface->Save(); - g_emu_thread->reloadGameSettings(); + saveAndReloadGameSettings(); } else { @@ -337,8 +334,7 @@ void ControllerSettingsWindow::clearSettingValue(const char* section, const char if (m_profile_interface) { m_profile_interface->DeleteValue(section, key); - m_profile_interface->Save(); - g_emu_thread->reloadGameSettings(); + saveAndReloadGameSettings(); } else { @@ -348,6 +344,13 @@ void ControllerSettingsWindow::clearSettingValue(const char* section, const char } } +void ControllerSettingsWindow::saveAndReloadGameSettings() +{ + pxAssert(m_profile_interface); + QtHost::SaveGameSettings(m_profile_interface.get(), false); + g_emu_thread->reloadGameSettings(); +} + void ControllerSettingsWindow::createWidgets() { QSignalBlocker sb(m_ui.settingsContainer); diff --git a/pcsx2-qt/Settings/ControllerSettingsWindow.h b/pcsx2-qt/Settings/ControllerSettingsWindow.h index 6c36060b87efb..42672b6df760e 100644 --- a/pcsx2-qt/Settings/ControllerSettingsWindow.h +++ b/pcsx2-qt/Settings/ControllerSettingsWindow.h @@ -63,6 +63,7 @@ class ControllerSettingsWindow final : public QWidget void setIntValue(const char* section, const char* key, s32 value); void setStringValue(const char* section, const char* key, const char* value); void clearSettingValue(const char* section, const char* key); + void saveAndReloadGameSettings(); Q_SIGNALS: void inputProfileSwitched(); diff --git a/pcsx2-qt/Settings/SettingsWindow.cpp b/pcsx2-qt/Settings/SettingsWindow.cpp index c4df242482836..749d3adb15c47 100644 --- a/pcsx2-qt/Settings/SettingsWindow.cpp +++ b/pcsx2-qt/Settings/SettingsWindow.cpp @@ -300,8 +300,8 @@ void SettingsWindow::onCopyGlobalSettingsClicked() auto lock = Host::GetSettingsLock(); Pcsx2Config::CopyConfiguration(m_sif.get(), *Host::Internal::GetBaseSettingsLayer()); } - m_sif->Save(); - g_emu_thread->reloadGameSettings(); + saveAndReloadGameSettings(); + QMessageBox::information(reopen(), tr("PCSX2 Settings"), tr("Per-game configuration copied from global settings.")); } @@ -322,8 +322,7 @@ void SettingsWindow::onClearSettingsClicked() m_game_patch_settings_widget->disableAllPatches(); Pcsx2Config::ClearConfiguration(m_sif.get()); - m_sif->Save(); - g_emu_thread->reloadGameSettings(); + saveAndReloadGameSettings(); QMessageBox::information(reopen(), tr("PCSX2 Settings"), tr("Per-game configuration cleared.")); } @@ -560,8 +559,7 @@ void SettingsWindow::setBoolSettingValue(const char* section, const char* key, s if (m_sif) { value.has_value() ? m_sif->SetBoolValue(section, key, value.value()) : m_sif->DeleteValue(section, key); - m_sif->Save(); - g_emu_thread->reloadGameSettings(); + saveAndReloadGameSettings(); } else { @@ -576,8 +574,7 @@ void SettingsWindow::setIntSettingValue(const char* section, const char* key, st if (m_sif) { value.has_value() ? m_sif->SetIntValue(section, key, value.value()) : m_sif->DeleteValue(section, key); - m_sif->Save(); - g_emu_thread->reloadGameSettings(); + saveAndReloadGameSettings(); } else { @@ -592,8 +589,7 @@ void SettingsWindow::setFloatSettingValue(const char* section, const char* key, if (m_sif) { value.has_value() ? m_sif->SetFloatValue(section, key, value.value()) : m_sif->DeleteValue(section, key); - m_sif->Save(); - g_emu_thread->reloadGameSettings(); + saveAndReloadGameSettings(); } else { @@ -608,8 +604,7 @@ void SettingsWindow::setStringSettingValue(const char* section, const char* key, if (m_sif) { value.has_value() ? m_sif->SetStringValue(section, key, value.value()) : m_sif->DeleteValue(section, key); - m_sif->Save(); - g_emu_thread->reloadGameSettings(); + saveAndReloadGameSettings(); } else { @@ -632,8 +627,7 @@ void SettingsWindow::removeSettingValue(const char* section, const char* key) if (m_sif) { m_sif->DeleteValue(section, key); - m_sif->Save(); - g_emu_thread->reloadGameSettings(); + saveAndReloadGameSettings(); } else { @@ -643,6 +637,13 @@ void SettingsWindow::removeSettingValue(const char* section, const char* key) } } +void SettingsWindow::saveAndReloadGameSettings() +{ + pxAssert(m_sif); + QtHost::SaveGameSettings(m_sif.get(), true); + g_emu_thread->reloadGameSettings(); +} + void SettingsWindow::openGamePropertiesDialog(const GameList::Entry* game, const std::string_view& title, std::string serial, u32 disc_crc) { std::string filename = VMManager::GetGameSettingsPath(serial, disc_crc); diff --git a/pcsx2-qt/Settings/SettingsWindow.h b/pcsx2-qt/Settings/SettingsWindow.h index c2cb98500ab44..84485329b3071 100644 --- a/pcsx2-qt/Settings/SettingsWindow.h +++ b/pcsx2-qt/Settings/SettingsWindow.h @@ -93,6 +93,7 @@ class SettingsWindow final : public QWidget void setStringSettingValue(const char* section, const char* key, std::optional value); bool containsSettingValue(const char* section, const char* key) const; void removeSettingValue(const char* section, const char* key); + void saveAndReloadGameSettings(); Q_SIGNALS: void settingsResetToDefaults(); diff --git a/pcsx2/INISettingsInterface.cpp b/pcsx2/INISettingsInterface.cpp index 784c12ecfbaec..2184ac3cc093c 100644 --- a/pcsx2/INISettingsInterface.cpp +++ b/pcsx2/INISettingsInterface.cpp @@ -134,6 +134,11 @@ void INISettingsInterface::Clear() m_ini.Reset(); } +bool INISettingsInterface::IsEmpty() +{ + return (m_ini.GetKeyCount() == 0); +} + bool INISettingsInterface::GetIntValue(const char* section, const char* key, int* value) const { const char* str_value = m_ini.GetValue(section, key); @@ -278,6 +283,29 @@ void INISettingsInterface::ClearSection(const char* section) 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 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 INISettingsInterface::GetStringList(const char* section, const char* key) const { std::list entries; diff --git a/pcsx2/INISettingsInterface.h b/pcsx2/INISettingsInterface.h index d00ff5655f395..a28b8cefce995 100644 --- a/pcsx2/INISettingsInterface.h +++ b/pcsx2/INISettingsInterface.h @@ -23,6 +23,7 @@ class INISettingsInterface final : public SettingsInterface bool Save(Error* error = nullptr) override; void Clear() override; + bool IsEmpty() override; bool GetIntValue(const char* section, const char* key, int* value) const override; bool GetUIntValue(const char* section, const char* key, uint* value) const override; @@ -41,6 +42,8 @@ class INISettingsInterface final : public SettingsInterface bool ContainsValue(const char* section, const char* key) const override; void DeleteValue(const char* section, const char* key) override; void ClearSection(const char* section) override; + void RemoveSection(const char* section) override; + void RemoveEmptySections() override; std::vector GetStringList(const char* section, const char* key) const override; void SetStringList(const char* section, const char* key, const std::vector& items) override; diff --git a/pcsx2/ImGui/FullscreenUI.cpp b/pcsx2/ImGui/FullscreenUI.cpp index bd33953cd144a..284f959d942b1 100644 --- a/pcsx2/ImGui/FullscreenUI.cpp +++ b/pcsx2/ImGui/FullscreenUI.cpp @@ -25,6 +25,7 @@ #include "svnrev.h" #include "common/Console.h" +#include "common/Error.h" #include "common/FileSystem.h" #include "common/Image.h" #include "common/Path.h" @@ -860,7 +861,29 @@ void FullscreenUI::Render() { 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::DeleteFilePath(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 (VMManager::HasValidVM()) Host::RunOnCPUThread([]() { VMManager::ReloadGameSettings(); }); } @@ -6818,6 +6841,7 @@ void FullscreenUI::DrawAchievementsSettingsPage(std::unique_lock& se #if 0 // TRANSLATION-STRING-AREA-BEGIN +TRANSLATE_NOOP("FullscreenUI", "Error"); TRANSLATE_NOOP("FullscreenUI", "Could not find any CD/DVD-ROM devices. Please ensure you have a drive connected and sufficient permissions to access it."); TRANSLATE_NOOP("FullscreenUI", "WARNING: Your memory card is still writing data. Shutting down now will IRREVERSIBLY DESTROY YOUR MEMORY CARD. It is strongly recommended to resume your game and let it finish writing to your memory card.\n\nDo you wish to shutdown anyways and IRREVERSIBLY DESTROY YOUR MEMORY CARD?"); TRANSLATE_NOOP("FullscreenUI", "Use Global Setting"); @@ -6885,6 +6909,7 @@ TRANSLATE_NOOP("FullscreenUI", "Pauses the emulator when you minimize the window TRANSLATE_NOOP("FullscreenUI", "Pauses the emulator when you open the quick menu, and unpauses when you close it."); TRANSLATE_NOOP("FullscreenUI", "Determines whether a prompt will be displayed to confirm shutting down the emulator/game when the hotkey is pressed."); TRANSLATE_NOOP("FullscreenUI", "Automatically saves the emulator state when powering down or exiting. You can then resume directly from where you left off next time."); +TRANSLATE_NOOP("FullscreenUI", "Creates a backup copy of a save state if it already exists when the save is created. The backup copy has a .backup suffix"); TRANSLATE_NOOP("FullscreenUI", "Uses a light coloured theme instead of the default dark theme."); TRANSLATE_NOOP("FullscreenUI", "Game Display"); TRANSLATE_NOOP("FullscreenUI", "Automatically switches to fullscreen mode when a game is started."); @@ -6919,7 +6944,6 @@ TRANSLATE_NOOP("FullscreenUI", "Fast Forward Speed"); TRANSLATE_NOOP("FullscreenUI", "Sets the speed when using the fast forward hotkey."); TRANSLATE_NOOP("FullscreenUI", "Slow Motion Speed"); TRANSLATE_NOOP("FullscreenUI", "Sets the speed when using the slow motion hotkey."); -TRANSLATE_NOOP("FullscreenUI", "When disabled, the game will run as fast as possible."); TRANSLATE_NOOP("FullscreenUI", "System Settings"); TRANSLATE_NOOP("FullscreenUI", "EE Cycle Rate"); TRANSLATE_NOOP("FullscreenUI", "Underclocks or overclocks the emulated Emotion Engine CPU."); @@ -7108,18 +7132,26 @@ TRANSLATE_NOOP("FullscreenUI", "Disable Shader Cache"); TRANSLATE_NOOP("FullscreenUI", "Prevents the loading and saving of shaders/pipelines to disk."); TRANSLATE_NOOP("FullscreenUI", "Disable Vertex Shader Expand"); TRANSLATE_NOOP("FullscreenUI", "Falls back to the CPU for expanding sprites/lines."); -TRANSLATE_NOOP("FullscreenUI", "Runtime Settings"); -TRANSLATE_NOOP("FullscreenUI", "Applies a global volume modifier to all sound produced by the game."); -TRANSLATE_NOOP("FullscreenUI", "Mixing Settings"); +TRANSLATE_NOOP("FullscreenUI", "Audio Control"); +TRANSLATE_NOOP("FullscreenUI", "Output Volume"); +TRANSLATE_NOOP("FullscreenUI", "Controls the volume of the audio played on the host."); +TRANSLATE_NOOP("FullscreenUI", "Fast Forward Volume"); +TRANSLATE_NOOP("FullscreenUI", "Controls the volume of the audio played on the host when fast forwarding."); +TRANSLATE_NOOP("FullscreenUI", "Mute All Sound"); +TRANSLATE_NOOP("FullscreenUI", "Prevents the emulator from producing any audible sound."); +TRANSLATE_NOOP("FullscreenUI", "Backend Settings"); +TRANSLATE_NOOP("FullscreenUI", "Audio Backend"); +TRANSLATE_NOOP("FullscreenUI", "The audio backend determines how frames produced by the emulator are submitted to the host."); +TRANSLATE_NOOP("FullscreenUI", "Expansion"); +TRANSLATE_NOOP("FullscreenUI", "Determines how audio is expanded from stereo to surround for supported games."); +TRANSLATE_NOOP("FullscreenUI", "Synchronization"); TRANSLATE_NOOP("FullscreenUI", "Changes when SPU samples are generated relative to system emulation."); -TRANSLATE_NOOP("FullscreenUI", "Determines how the stereo output is transformed to greater speaker counts."); -TRANSLATE_NOOP("FullscreenUI", "Output Settings"); -TRANSLATE_NOOP("FullscreenUI", "Determines which API is used to play back audio samples on the host."); -TRANSLATE_NOOP("FullscreenUI", "Sets the average output latency when using the cubeb backend."); -TRANSLATE_NOOP("FullscreenUI", "%d ms (avg)"); -TRANSLATE_NOOP("FullscreenUI", "Timestretch Settings"); -TRANSLATE_NOOP("FullscreenUI", "Affects how the timestretcher operates when not running at 100% speed."); -TRANSLATE_NOOP("FullscreenUI", "%d ms"); +TRANSLATE_NOOP("FullscreenUI", "Buffer Size"); +TRANSLATE_NOOP("FullscreenUI", "Determines the amount of audio buffered before being pulled by the host API."); +TRANSLATE_NOOP("FullscreenUI", "Minimal Output Latency"); +TRANSLATE_NOOP("FullscreenUI", "When enabled, the minimum supported output latency will be used for the host API."); +TRANSLATE_NOOP("FullscreenUI", "Output Latency"); +TRANSLATE_NOOP("FullscreenUI", "Determines how much latency there is between the audio being picked up by the host API, and played through speakers."); TRANSLATE_NOOP("FullscreenUI", "Settings and Operations"); TRANSLATE_NOOP("FullscreenUI", "Creates a new memory card file or folder."); TRANSLATE_NOOP("FullscreenUI", "Simulates a larger memory card by filtering saves only to the current game."); @@ -7278,6 +7310,8 @@ TRANSLATE_NOOP("FullscreenUI", "Account"); TRANSLATE_NOOP("FullscreenUI", "Logs out of RetroAchievements."); TRANSLATE_NOOP("FullscreenUI", "Logs in to RetroAchievements."); TRANSLATE_NOOP("FullscreenUI", "Current Game"); +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", "{} is not a valid disc image."); TRANSLATE_NOOP("FullscreenUI", "{:%H:%M}"); TRANSLATE_NOOP("FullscreenUI", "{0}/{1}/{2}/{3}"); @@ -7484,16 +7518,6 @@ TRANSLATE_NOOP("FullscreenUI", "NxAGSS"); TRANSLATE_NOOP("FullscreenUI", "Uncompressed"); TRANSLATE_NOOP("FullscreenUI", "LZMA (xz)"); TRANSLATE_NOOP("FullscreenUI", "Zstandard (zst)"); -TRANSLATE_NOOP("FullscreenUI", "TimeStretch (Recommended)"); -TRANSLATE_NOOP("FullscreenUI", "Async Mix (Breaks some games!)"); -TRANSLATE_NOOP("FullscreenUI", "None (Audio can skip.)"); -TRANSLATE_NOOP("FullscreenUI", "Stereo (None, Default)"); -TRANSLATE_NOOP("FullscreenUI", "Quadraphonic"); -TRANSLATE_NOOP("FullscreenUI", "Surround 5.1"); -TRANSLATE_NOOP("FullscreenUI", "Surround 7.1"); -TRANSLATE_NOOP("FullscreenUI", "No Sound (Emulate SPU2 only)"); -TRANSLATE_NOOP("FullscreenUI", "Cubeb (Cross-platform)"); -TRANSLATE_NOOP("FullscreenUI", "XAudio2"); TRANSLATE_NOOP("FullscreenUI", "PS2 (8MB)"); TRANSLATE_NOOP("FullscreenUI", "PS2 (16MB)"); TRANSLATE_NOOP("FullscreenUI", "PS2 (32MB)"); @@ -7569,14 +7593,6 @@ TRANSLATE_NOOP("FullscreenUI", "Warn About Unsafe Settings"); TRANSLATE_NOOP("FullscreenUI", "Reset Settings"); TRANSLATE_NOOP("FullscreenUI", "Change Search Directory"); TRANSLATE_NOOP("FullscreenUI", "Fast Boot"); -TRANSLATE_NOOP("FullscreenUI", "Output Volume"); -TRANSLATE_NOOP("FullscreenUI", "Synchronization Mode"); -TRANSLATE_NOOP("FullscreenUI", "Expansion Mode"); -TRANSLATE_NOOP("FullscreenUI", "Output Module"); -TRANSLATE_NOOP("FullscreenUI", "Latency"); -TRANSLATE_NOOP("FullscreenUI", "Sequence Length"); -TRANSLATE_NOOP("FullscreenUI", "Seekwindow Size"); -TRANSLATE_NOOP("FullscreenUI", "Overlap"); TRANSLATE_NOOP("FullscreenUI", "Create Memory Card"); TRANSLATE_NOOP("FullscreenUI", "Memory Card Directory"); TRANSLATE_NOOP("FullscreenUI", "Folder Memory Card Filter"); diff --git a/pcsx2/LayeredSettingsInterface.cpp b/pcsx2/LayeredSettingsInterface.cpp index 1cc4476df3018..20bb4648e7a7f 100644 --- a/pcsx2/LayeredSettingsInterface.cpp +++ b/pcsx2/LayeredSettingsInterface.cpp @@ -22,6 +22,11 @@ void LayeredSettingsInterface::Clear() pxFailRel("Attempting to clear layered settings interface"); } +bool LayeredSettingsInterface::IsEmpty() +{ + return false; +} + bool LayeredSettingsInterface::GetIntValue(const char* section, const char* key, int* value) const { for (u32 layer = FIRST_LAYER; layer <= LAST_LAYER; layer++) @@ -173,6 +178,16 @@ void LayeredSettingsInterface::ClearSection(const char* section) pxFailRel("Attempt to call ClearSection() on layered settings interface"); } +void LayeredSettingsInterface::RemoveSection(const char* section) +{ + pxFailRel("Attempt to call RemoveSection() on layered settings interface"); +} + +void LayeredSettingsInterface::RemoveEmptySections() +{ + pxFailRel("Attempt to call RemoveEmptySections() on layered settings interface"); +} + std::vector LayeredSettingsInterface::GetStringList(const char* section, const char* key) const { std::vector ret; diff --git a/pcsx2/LayeredSettingsInterface.h b/pcsx2/LayeredSettingsInterface.h index b5399a9be05e5..2da1706684e81 100644 --- a/pcsx2/LayeredSettingsInterface.h +++ b/pcsx2/LayeredSettingsInterface.h @@ -29,6 +29,8 @@ class LayeredSettingsInterface final : public SettingsInterface void Clear() override; + bool IsEmpty() override; + bool GetIntValue(const char* section, const char* key, int* value) const override; bool GetUIntValue(const char* section, const char* key, uint* value) const override; bool GetFloatValue(const char* section, const char* key, float* value) const override; @@ -46,6 +48,8 @@ class LayeredSettingsInterface final : public SettingsInterface bool ContainsValue(const char* section, const char* key) const override; void DeleteValue(const char* section, const char* key) override; void ClearSection(const char* section) override; + void RemoveSection(const char* section) override; + void RemoveEmptySections() override; std::vector GetStringList(const char* section, const char* key) const override; void SetStringList(const char* section, const char* key, const std::vector& items) override; From 4ad784d755b67db5e51e33c730edf15de4715479 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sat, 4 May 2024 23:36:54 +1000 Subject: [PATCH 3/4] Config: Remove unused FrameLimitEnable field --- pcsx2/Config.h | 1 - pcsx2/Pcsx2Config.cpp | 1 - pcsx2/VMManager.cpp | 37 +++++++++++++++---------------------- 3 files changed, 15 insertions(+), 24 deletions(-) diff --git a/pcsx2/Config.h b/pcsx2/Config.h index c6094bb3267e5..9245f5921e68c 100644 --- a/pcsx2/Config.h +++ b/pcsx2/Config.h @@ -985,7 +985,6 @@ struct Pcsx2Config struct EmulationSpeedOptions { BITFIELD32() - bool FrameLimitEnable : 1; bool SyncToHostRefreshRate : 1; BITFIELD_END diff --git a/pcsx2/Pcsx2Config.cpp b/pcsx2/Pcsx2Config.cpp index 5ccd801a75040..7f2c93f7d8afe 100644 --- a/pcsx2/Pcsx2Config.cpp +++ b/pcsx2/Pcsx2Config.cpp @@ -1486,7 +1486,6 @@ Pcsx2Config::EmulationSpeedOptions::EmulationSpeedOptions() { bitset = 0; - FrameLimitEnable = true; SyncToHostRefreshRate = false; } diff --git a/pcsx2/VMManager.cpp b/pcsx2/VMManager.cpp index 1c98d52b6e7b2..50a56a4e9206e 100644 --- a/pcsx2/VMManager.cpp +++ b/pcsx2/VMManager.cpp @@ -133,7 +133,6 @@ namespace VMManager static void SaveSessionTime(const std::string& prev_serial); static void ReloadPINE(); - static LimiterModeType GetInitialLimiterMode(); static float GetTargetSpeedForLimiterMode(LimiterModeType mode); static void ResetFrameLimiter(); static double AdjustToHostRefreshRate(float frame_rate, float target_speed); @@ -1371,7 +1370,7 @@ bool VMManager::Initialize(VMBootParameters boot_params) } } - s_limiter_mode = GetInitialLimiterMode(); + s_limiter_mode = LimiterModeType::Nominal; s_target_speed = GetTargetSpeedForLimiterMode(s_limiter_mode); s_use_vsync_for_timing = false; @@ -1980,11 +1979,6 @@ float VMManager::GetTargetSpeed() return s_target_speed; } -LimiterModeType VMManager::GetInitialLimiterMode() -{ - return EmuConfig.EmulationSpeed.FrameLimitEnable ? LimiterModeType::Nominal : LimiterModeType::Unlimited; -} - double VMManager::AdjustToHostRefreshRate(float frame_rate, float target_speed) { if (!EmuConfig.EmulationSpeed.SyncToHostRefreshRate || target_speed != 1.0f) @@ -2015,27 +2009,26 @@ double VMManager::AdjustToHostRefreshRate(float frame_rate, float target_speed) float VMManager::GetTargetSpeedForLimiterMode(LimiterModeType mode) { - if (EmuConfig.EmulationSpeed.FrameLimitEnable && (!EmuConfig.EnableFastBootFastForward || !VMManager::Internal::IsFastBootInProgress())) + if (EmuConfig.EnableFastBootFastForward && VMManager::Internal::IsFastBootInProgress()) + return 0.0f; + + switch (s_limiter_mode) { - switch (s_limiter_mode) - { - case LimiterModeType::Nominal: - return EmuConfig.EmulationSpeed.NominalScalar; + case LimiterModeType::Nominal: + return EmuConfig.EmulationSpeed.NominalScalar; - case LimiterModeType::Slomo: - return EmuConfig.EmulationSpeed.SlomoScalar; + case LimiterModeType::Slomo: + return EmuConfig.EmulationSpeed.SlomoScalar; - case LimiterModeType::Turbo: - return EmuConfig.EmulationSpeed.TurboScalar; + case LimiterModeType::Turbo: + return EmuConfig.EmulationSpeed.TurboScalar; - case LimiterModeType::Unlimited: - return 0.0f; + case LimiterModeType::Unlimited: + return 0.0f; - jNO_DEFAULT - } + default: + ASSUME(false); } - - return 0.0f; } void VMManager::UpdateTargetSpeed() From ba7d65a5b8dda5cc3eb5033cf2dbd5b40d432d00 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Sat, 4 May 2024 23:38:18 +1000 Subject: [PATCH 4/4] HeapArray: Add missing field swap --- common/HeapArray.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/common/HeapArray.h b/common/HeapArray.h index eaf3df0f054bf..73f6254954b08 100644 --- a/common/HeapArray.h +++ b/common/HeapArray.h @@ -251,7 +251,11 @@ class DynamicHeapArray void fill(const_reference value) { std::fill(begin(), end(), value); } - void swap(this_type& move) { std::swap(m_data, move.m_data); } + void swap(this_type& move) + { + std::swap(m_data, move.m_data); + std::swap(m_size, move.m_size); + } void resize(size_t new_size) { internal_resize(new_size, m_data, m_size); }