From 6e5c567809b2cea6d40d22cd28a3a084b63ec33a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 31 Aug 2025 14:10:35 +0200 Subject: [PATCH] Fix some ini file misbehaviors --- Common/Data/Format/IniFile.cpp | 48 +++++++++------------------------- Common/StringUtils.cpp | 26 ++++++++++++++++-- Common/StringUtils.h | 4 ++- Core/Config.cpp | 4 ++- Core/ConfigSettings.cpp | 9 ++++++- 5 files changed, 51 insertions(+), 40 deletions(-) diff --git a/Common/Data/Format/IniFile.cpp b/Common/Data/Format/IniFile.cpp index a32d92edc2..349022a5cb 100644 --- a/Common/Data/Format/IniFile.cpp +++ b/Common/Data/Format/IniFile.cpp @@ -300,23 +300,19 @@ void Section::Set(std::string_view key, bool newValue, bool defaultValue) Delete(key); } -void Section::Set(std::string_view key, const std::vector& newValues) -{ +void Section::Set(std::string_view key, const std::vector &newValues) { std::string temp; // Join the strings with , for (const auto &value : newValues) { - temp += value + ","; + temp += value; + temp.push_back(','); } // remove last , if (!temp.empty()) - temp.resize(temp.length() - 1); + temp.pop_back(); Set(key, temp.c_str()); } -void Section::AddComment(const std::string &comment) { - lines_.emplace_back(ParsedIniLine::CommentOnly("# " + comment)); -} - bool Section::Get(std::string_view key, std::vector *values, const std::vector *defaultValues) const { std::string temp; bool retval = Get(key, &temp, 0); @@ -326,23 +322,8 @@ bool Section::Get(std::string_view key, std::vector *values, const } return false; } - // ignore starting , if any - size_t subStart = temp.find_first_not_of(','); - size_t subEnd; - // split by , - while (subStart != std::string::npos) { - // Find next , - subEnd = temp.find_first_of(',', subStart); - if (subStart != subEnd) { - // take from first char until next , - values->push_back(StripSpaces(temp.substr(subStart, subEnd - subStart))); - } - - // Find the next non , char - subStart = temp.find_first_not_of(',', subEnd); - } - + SplitString(temp, ',', *values, true); return true; } @@ -408,6 +389,10 @@ bool Section::Exists(std::string_view key) const { return false; } +void Section::AddComment(const std::string &comment) { + lines_.emplace_back(ParsedIniLine::CommentOnly("# " + comment)); +} + std::map Section::ToMap() const { std::map outMap; for (auto &line : lines_) { @@ -502,8 +487,7 @@ void IniFile::SortSections() std::sort(sections.begin(), sections.end()); } -bool IniFile::Load(const Path &path) -{ +bool IniFile::Load(const Path &path) { sections.clear(); sections.push_back(std::make_unique
("")); // first section consists of the comments before the first real section @@ -531,15 +515,10 @@ bool IniFile::LoadFromVFS(VFSInterface &vfs, const std::string &filename) { } bool IniFile::Load(std::istream &in) { - // Maximum number of letters in a line - static const int MAX_BYTES = 1024*32; - char *templine = new char[MAX_BYTES]; // avoid using up massive stack space - - while (!(in.eof() || in.fail())) - { - in.getline(templine, MAX_BYTES); - std::string_view line = templine; + std::string linebuf; + while (std::getline(in, linebuf)) { + std::string_view line = StripSpaces(std::string_view(linebuf)); // Remove UTF-8 byte order marks. if (line.substr(0, 3) == "\xEF\xBB\xBF") { line = line.substr(3); @@ -575,7 +554,6 @@ bool IniFile::Load(std::istream &in) { } } - delete[] templine; return true; } diff --git a/Common/StringUtils.cpp b/Common/StringUtils.cpp index 8ea4e4d02c..6efd728ba1 100644 --- a/Common/StringUtils.cpp +++ b/Common/StringUtils.cpp @@ -16,7 +16,7 @@ // http://code.google.com/p/dolphin-emu/ #include - +#include #include "ppsspp_config.h" #ifdef _WIN32 @@ -418,7 +418,7 @@ void SplitString(std::string_view str, const char delim, std::vector &output) { +void SplitString(std::string_view str, const char delim, std::vector &output, bool trimOutput) { size_t next = 0; size_t pos = 0; while (pos < str.length()) { @@ -427,6 +427,9 @@ void SplitString(std::string_view str, const char delim, std::vector &v) { + std::unordered_set seen; + std::vector result; + result.reserve(v.size()); // minimize reallocations + for (const auto &s : v) { + if (seen.insert(s).second) { + // insert returns {iterator, bool} + // bool == true if it was newly inserted (didn't already exist) + result.push_back(s); + } + } + v.swap(result); +} diff --git a/Common/StringUtils.h b/Common/StringUtils.h index 05caafb1a9..92cad2bef9 100644 --- a/Common/StringUtils.h +++ b/Common/StringUtils.h @@ -111,7 +111,7 @@ int CountChar(std::string_view haystack, char needle); // NOTE: str must live at least as long as all uses of output. void SplitString(std::string_view str, const char delim, std::vector &output); // Try to avoid this when possible, in favor of the string_view version. -void SplitString(std::string_view str, const char delim, std::vector &output); +void SplitString(std::string_view str, const char delim, std::vector &output, bool trimOutput = false); void GetQuotedStrings(std::string_view str, std::vector &output); @@ -153,6 +153,8 @@ inline void CharArrayFromFormat(char (& out)[Count], const char* format, ...) // "C:/Windows/winhelp.exe" to "C:/Windows/", "winhelp", ".exe" bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _pFilename, std::string* _pExtension); +void MakeUnique(std::vector &vec); + // Replaces %1, %2, %3 in format with arg1, arg2, arg3. // Much safer than snprintf and friends. // For mixes of strings and ints, manually convert the ints to strings. diff --git a/Core/Config.cpp b/Core/Config.cpp index 7c6b4d4fe6..bf813c3bcd 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -548,7 +548,9 @@ int Config::NextValidBackend() { #endif // They've all failed. Let them try the default - or on Android, OpenGL. - sFailedGPUBackends += ",ALL"; + if (sFailedGPUBackends.find(",ALL") == std::string::npos) { + sFailedGPUBackends += ",ALL"; + } ERROR_LOG(Log::Loader, "All graphics backends failed"); #if PPSSPP_PLATFORM(ANDROID) return (int)GPUBackend::OPENGL; diff --git a/Core/ConfigSettings.cpp b/Core/ConfigSettings.cpp index 5c17b58d5b..f6aed72bb0 100644 --- a/Core/ConfigSettings.cpp +++ b/Core/ConfigSettings.cpp @@ -1,6 +1,7 @@ #include "Common/Data/Format/IniFile.h" #include "Common/Net/URL.h" #include "Common/Log.h" +#include "Common/StringUtils.h" #include "Core/ConfigSettings.h" #include "Core/ConfigValues.h" @@ -38,8 +39,14 @@ bool ConfigSetting::Get(const Section *section) const { case TYPE_STRING: return section->Get(iniKey_, ptr_.s, cb_.s ? cb_.s().c_str() : default_.s); case TYPE_STRING_VECTOR: + { // No support for callbacks for these yet. that's not an issue. - return section->Get(iniKey_, ptr_.v, default_.v); + bool success = section->Get(iniKey_, ptr_.v, default_.v); + if (success) { + MakeUnique(*ptr_.v); + } + return success; + } case TYPE_TOUCH_POS: { ConfigTouchPos defaultTouchPos = cb_.touchPos ? cb_.touchPos() : default_.touchPos;