Index: mptrack/Settings.cpp =================================================================== --- mptrack/Settings.cpp (revision 24333) +++ mptrack/Settings.cpp (working copy) @@ -140,6 +140,16 @@ backend->RemoveSection(section); } +bool SettingsContainer::BackendsCanWriteMultipleSettings() const +{ + return backend->CanWriteMultipleSettings(); +} + +void SettingsContainer::BackendsWriteMultipleSettings(const std::map &settings) +{ + backend->WriteMultipleSettings(settings); +} + SettingValue SettingsContainer::ReadSetting(const SettingPath &path, const SettingValue &def) const { ASSERT(theApp.InGuiThread()); @@ -242,6 +252,20 @@ { ASSERT(theApp.InGuiThread()); ASSERT(!CMainFrame::GetMainFrame() || (CMainFrame::GetMainFrame() && !CMainFrame::GetMainFrame()->InNotifyHandler())); // This is a slow path, use CachedSetting for stuff that is accessed in notify handler. + if(BackendsCanWriteMultipleSettings()) + { + std::map settings; + for(auto &[path, value] : map) + { + if(value.IsDirty()) + { + settings.insert(std::make_pair(path, value.GetRefValue())); + value.Clean(); + } + } + BackendsWriteMultipleSettings(settings); + return; + } for(auto &[path, value] : map) { if(value.IsDirty()) @@ -407,7 +431,12 @@ IniFileSettingsBackend::IniFileSettingsBackend(const mpt::PathString &filename) : filename(filename) { - return; +#if defined(UNICODE) + if(mpt::osinfo::windows::Version::Current().IsAtLeast(mpt::osinfo::windows::Version::WinXP)) + { + ConvertToUnicode(); + } +#endif } IniFileSettingsBackend::~IniFileSettingsBackend() @@ -493,10 +522,143 @@ RemoveSectionRaw(section); } +std::set IniFileSettingsBackend::ReadSections() const +{ + std::set result; + const std::vector sectionsstr = [&]() + { + std::vector buf; + buf.resize(1024); + while(true) + { + DWORD bufused = ::GetPrivateProfileSectionNames(buf.data(), mpt::saturate_cast(buf.size()), filename.AsNative().c_str()); + if(bufused >= (buf.size() - 2)) + { + buf.resize(mpt::exponential_grow(buf.size())); + continue; + } + buf.resize(bufused); + break; + }; + return buf; + }(); + const std::vector sections = mpt::split(mpt::winstring(sectionsstr.data(), sectionsstr.size()), mpt::winstring(_T("\0"), 1)); + for(const auto §ion : sections) + { + result.insert(section); + } + return result; +} +std::map IniFileSettingsBackend::ReadSection(const mpt::winstring §ion) const +{ + std::map result; + const std::vector keyvalsstr = [&]() + { + std::vector buf; + buf.resize(1024); + while(true) + { + DWORD bufused = ::GetPrivateProfileSection(mpt::ToWin(section).c_str(), buf.data(), mpt::saturate_cast(buf.size()), filename.AsNative().c_str()); + if(bufused >= (buf.size() - 2)) + { + buf.resize(mpt::exponential_grow(buf.size())); + continue; + } + buf.resize(bufused); + break; + }; + return buf; + }(); + const std::vector keyvals = mpt::split(mpt::winstring(keyvalsstr.data(), keyvalsstr.size()), mpt::winstring(_T("\0"), 1)); + for(const auto &keyval : keyvals) + { + const auto equalpos = keyval.find(_T("=")); + if(equalpos == mpt::winstring::npos) + { + continue; + } + if(equalpos == 0) + { + continue; + } + result.insert(std::make_pair(keyval.substr(0, equalpos), keyval.substr(equalpos + 1))); + } + return result; +} +mpt::winstring IniFileSettingsBackend::FormatValueAsIni(const SettingValue &value) +{ + switch(value.GetType()) + { + case SettingTypeBool: + return mpt::tfmt::val(value.as()); + break; + case SettingTypeInt: + return mpt::tfmt::val(value.as()); + break; + case SettingTypeFloat: + return mpt::tfmt::val(value.as()); + break; + case SettingTypeString: + return mpt::ToWin(value.as()); + break; + case SettingTypeBinary: + { + std::vector data = value.as>(); + uint8 checksum = 0; + for(const std::byte x : data) { + checksum += mpt::byte_cast(x); + } + return mpt::ToWin(mpt::encode_hex(mpt::as_span(data)) + mpt::ufmt::HEX0<2>(checksum)); + } + break; + case SettingTypeNone: + default: + return mpt::ustring(); + break; + } +} +void IniFileSettingsBackend::WriteSection(const mpt::winstring §ion, const std::map keyvalues) +{ + mpt::winstring keyvals; + for(const auto &[key, val] : keyvalues) + { + keyvals.append(key); + keyvals.append(_T("=")); + keyvals.append(val); + keyvals.append(mpt::winstring(_T("\0"), 1)); + } + keyvals.append(mpt::winstring(_T("\0"), 1)); + ::WritePrivateProfileSection(section.c_str(), keyvals.c_str(), filename.AsNative().c_str()); +} +void IniFileSettingsBackend::WriteMultipleSettings(const std::map &settings) +{ + std::map> sectionssettings; + for(const auto &[path, value] : settings) + { + sectionssettings[path.GetRefSection()][path] = value; + } + for(const auto &[section, sectionsettings] : sectionssettings) + { + std::map workingsectionsettings = ReadSection(mpt::ToWin(section)); + for(const auto &[path, value] : sectionsettings) + { + workingsectionsettings[mpt::ToWin(path.GetRefKey())] = FormatValueAsIni(value); + } + WriteSection(mpt::ToWin(section), workingsectionsettings); + } +} + +bool IniFileSettingsBackend::CanWriteMultipleSettings() const +{ + return true; +} + + + IniFileSettingsContainer::IniFileSettingsContainer(const mpt::PathString &filename) : IniFileSettingsBackend(filename) , SettingsContainer(this) Index: mptrack/Settings.h =================================================================== --- mptrack/Settings.h (revision 24333) +++ mptrack/Settings.h (working copy) @@ -408,6 +408,8 @@ virtual void WriteSetting(const SettingPath &path, const SettingValue &val) = 0; virtual void RemoveSetting(const SettingPath &path) = 0; virtual void RemoveSection(const mpt::ustring §ion) = 0; + virtual bool CanWriteMultipleSettings() const = 0; + virtual void WriteMultipleSettings(const std::map &settings) = 0; protected: virtual ~ISettingsBackend() = default; }; @@ -448,6 +450,8 @@ void BackendsWriteSetting(const SettingPath &path, const SettingValue &val); void BackendsRemoveSetting(const SettingPath &path); void BackendsRemoveSection(const mpt::ustring §ion); + bool BackendsCanWriteMultipleSettings() const; + void BackendsWriteMultipleSettings(const std::map &settings); void NotifyListeners(const SettingPath &path); SettingValue ReadSetting(const SettingPath &path, const SettingValue &def) const; bool IsDefaultSetting(const SettingPath &path) const; @@ -711,6 +715,14 @@ virtual void WriteSetting(const SettingPath &path, const SettingValue &val) override; virtual void RemoveSetting(const SettingPath &path) override; virtual void RemoveSection(const mpt::ustring §ion) override; +private: + std::set ReadSections() const; + std::map ReadSection(const mpt::winstring §ion) const; + static mpt::winstring FormatValueAsIni(const SettingValue &value); + void WriteSection(const mpt::winstring §ion, const std::map keyvalues); +public: + virtual bool CanWriteMultipleSettings() const override; + virtual void WriteMultipleSettings(const std::map &settings) override; const mpt::PathString& GetFilename() const { return filename; } };