From c171356f7d52d485835cf3e849e8455480b4df1d Mon Sep 17 00:00:00 2001 From: SpinDizzy Date: Sun, 20 Jan 2019 08:02:01 +0000 Subject: [PATCH] -Added support for specifying multiple sections simultaneously in INI files, e.g., [ daytona2, dayto2pe ] -Forgetfulness in 763 --- Src/Debugger/SupermodelDebugger.cpp | 2 +- Src/Util/ConfigBuilders.cpp | 69 ++++++++++++++++++++--------- Src/Util/NewConfig.cpp | 11 +++++ 3 files changed, 61 insertions(+), 21 deletions(-) diff --git a/Src/Debugger/SupermodelDebugger.cpp b/Src/Debugger/SupermodelDebugger.cpp index 01ae992..6f3155d 100644 --- a/Src/Debugger/SupermodelDebugger.cpp +++ b/Src/Debugger/SupermodelDebugger.cpp @@ -551,7 +551,7 @@ namespace Debugger } else if (CheckToken(token, "caip", "configallinputs")) // configallinputs { - m_inputs->ConfigureInputs(&m_model3->GetGame()); + m_inputs->ConfigureInputs(m_model3->GetGame()); return false; } // diff --git a/Src/Util/ConfigBuilders.cpp b/Src/Util/ConfigBuilders.cpp index 9931c2c..007f0e5 100644 --- a/Src/Util/ConfigBuilders.cpp +++ b/Src/Util/ConfigBuilders.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -84,19 +85,23 @@ namespace Util return line; } - static void ParseAssignment(Node *current_section, const std::string &filename, size_t line_num, const std::string &line) + static bool ParseAssignment(Node *current_section, const std::string &filename, size_t line_num, const std::string &line, bool suppress_errors) { + bool parse_error = false; + size_t idx_equals = line.find_first_of('='); if (idx_equals == std::string::npos) { - ErrorLog("%s:%d: Syntax error. No '=' found.", filename.c_str(), line_num); - return; + if (!suppress_errors) + ErrorLog("%s:%d: Syntax error. No '=' found.", filename.c_str(), line_num); + return true; } std::string lvalue(TrimWhiteSpace(std::string(line.begin(), line.begin() + idx_equals))); if (lvalue.empty()) { - ErrorLog("%s:%d: Syntax error. Setting name missing before '='.", filename.c_str(), line_num); - return; + if (!suppress_errors) + ErrorLog("%s:%d: Syntax error. Setting name missing before '='.", filename.c_str(), line_num); + return true; } // Get rvalue, which is allowed to be empty and strip off quotes if they // exist. Only a single pair of quotes encasing the rvalue are permitted. @@ -107,13 +112,25 @@ namespace Util rvalue = std::string(rvalue.begin() + idx_first_quote + 1, rvalue.begin() + idx_last_quote); if (std::count(rvalue.begin(), rvalue.end(), '\"') != 0) { - ErrorLog("%s:%d: Warning: Extraneous quotes present on line.", filename.c_str(), line_num); + if (!suppress_errors) + ErrorLog("%s:%d: Warning: Extraneous quotes present on line.", filename.c_str(), line_num); + parse_error = true; } // In INI files, we do not allow multiple settings with the same key. If // a setting is specified multiple times, previous ones are overwritten. current_section->Set(lvalue, rvalue); + return parse_error; } - + + static void ParseAssignment(const std::set ¤t_sections, const std::string &filename, size_t line_num, const std::string &line) + { + bool suppress_errors = false; + for (auto section: current_sections) + { + suppress_errors |= ParseAssignment(section, filename, line_num, line, suppress_errors); + } + } + bool FromINIFile(Node *config, const std::string &filename) { std::ifstream file; @@ -126,7 +143,7 @@ namespace Util *config = Node("Global"); // the root is also the "Global" section Node &global = *config; - Node *current_section = &global; + std::set current_sections{ &global }; // default when no section specified size_t line_num = 1; while (!file.eof()) @@ -146,21 +163,33 @@ namespace Util { if (*line.begin() == '[' && *line.rbegin() == ']') { - // Check if section exists else create a new one - std::string section(TrimWhiteSpace(std::string(line.begin() + 1, line.begin() + line.length() - 1))); - if (section.empty() || section == "Global") // empty section names (e.g., "[]") assigned to "[ Global ]" - current_section = &global; - else if (!global.TryGet(section)) + current_sections.clear(); + std::string sections(line.begin() + 1, line.begin() + line.length() - 1); + + // Handle multiple sections [ a,b,c ] by splitting on ',' and processing individually + std::vector section_list = Util::Format(sections).Split(','); + for (const std::string &s: section_list) { - Node &new_section = global.Add(section); - current_section = &new_section; + // Check if section exists else create a new one + std::string section = TrimWhiteSpace(s); + Node *section_node = nullptr; + if (section.empty() || section == "Global") // empty section names (e.g., "[]") assigned to "[ Global ]" + section_node = &global; + else if (!global.TryGet(section)) + { + Node &new_section = global.Add(section); + section_node = &new_section; + } + else + section_node = global.TryGet(section); + + // Add to current sections + current_sections.insert(section_node); } - else - current_section = global.TryGet(section); } else { - ParseAssignment(current_section, filename, line_num, line); + ParseAssignment(current_sections, filename, line_num, line); } } @@ -169,7 +198,7 @@ namespace Util return false; } - + /* * Produces a new INI section by merging two existing sections. * @@ -232,6 +261,6 @@ namespace Util } printf("Configuration successfully saved to '%s'.\n", filename.c_str()); InfoLog("Configuration successfully saved to '%s'.", filename.c_str()); - } + } } } diff --git a/Src/Util/NewConfig.cpp b/Src/Util/NewConfig.cpp index 12dccb2..0cda175 100644 --- a/Src/Util/NewConfig.cpp +++ b/Src/Util/NewConfig.cpp @@ -126,6 +126,17 @@ * Any setting not explicitly part of a section is assigned to the "Global" * section. For example, Setting0 and Setting1 above are both part of "Global". * + * Multiple sections can be specified like so: + * + * [ Section1, Section2, Section3 ] + * SettingX = foo + * [ Section4, , Section5 ] + * SettingX = bar + * + * In this example, SettingX will be set to "foo" in Section1, Section2, and + * Section3. It will be set to "bar" in Section4, Section5, and the "Global" + * section because of the unnamed element. + * * TODO * ---- * - TryGet() can be made quicker by attempting a direct lookup first. We never