-Added support for specifying multiple sections simultaneously in INI

files, e.g., [ daytona2, dayto2pe ]
-Forgetfulness in 763
This commit is contained in:
SpinDizzy 2019-01-20 08:02:01 +00:00
parent 864bb36b5a
commit c171356f7d
3 changed files with 61 additions and 21 deletions

View file

@ -551,7 +551,7 @@ namespace Debugger
} }
else if (CheckToken(token, "caip", "configallinputs")) // configallinputs else if (CheckToken(token, "caip", "configallinputs")) // configallinputs
{ {
m_inputs->ConfigureInputs(&m_model3->GetGame()); m_inputs->ConfigureInputs(m_model3->GetGame());
return false; return false;
} }
// //

View file

@ -4,6 +4,7 @@
#include <algorithm> #include <algorithm>
#include <fstream> #include <fstream>
#include <queue> #include <queue>
#include <set>
#include <cstdlib> #include <cstdlib>
#include <iostream> #include <iostream>
@ -84,19 +85,23 @@ namespace Util
return line; 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('='); size_t idx_equals = line.find_first_of('=');
if (idx_equals == std::string::npos) if (idx_equals == std::string::npos)
{ {
if (!suppress_errors)
ErrorLog("%s:%d: Syntax error. No '=' found.", filename.c_str(), line_num); ErrorLog("%s:%d: Syntax error. No '=' found.", filename.c_str(), line_num);
return; return true;
} }
std::string lvalue(TrimWhiteSpace(std::string(line.begin(), line.begin() + idx_equals))); std::string lvalue(TrimWhiteSpace(std::string(line.begin(), line.begin() + idx_equals)));
if (lvalue.empty()) if (lvalue.empty())
{ {
if (!suppress_errors)
ErrorLog("%s:%d: Syntax error. Setting name missing before '='.", filename.c_str(), line_num); ErrorLog("%s:%d: Syntax error. Setting name missing before '='.", filename.c_str(), line_num);
return; return true;
} }
// Get rvalue, which is allowed to be empty and strip off quotes if they // 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. // exist. Only a single pair of quotes encasing the rvalue are permitted.
@ -107,11 +112,23 @@ namespace Util
rvalue = std::string(rvalue.begin() + idx_first_quote + 1, rvalue.begin() + idx_last_quote); rvalue = std::string(rvalue.begin() + idx_first_quote + 1, rvalue.begin() + idx_last_quote);
if (std::count(rvalue.begin(), rvalue.end(), '\"') != 0) if (std::count(rvalue.begin(), rvalue.end(), '\"') != 0)
{ {
if (!suppress_errors)
ErrorLog("%s:%d: Warning: Extraneous quotes present on line.", filename.c_str(), line_num); 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 // In INI files, we do not allow multiple settings with the same key. If
// a setting is specified multiple times, previous ones are overwritten. // a setting is specified multiple times, previous ones are overwritten.
current_section->Set(lvalue, rvalue); current_section->Set(lvalue, rvalue);
return parse_error;
}
static void ParseAssignment(const std::set<Node *> &current_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) bool FromINIFile(Node *config, const std::string &filename)
@ -126,7 +143,7 @@ namespace Util
*config = Node("Global"); // the root is also the "Global" section *config = Node("Global"); // the root is also the "Global" section
Node &global = *config; Node &global = *config;
Node *current_section = &global; std::set<Node *> current_sections{ &global }; // default when no section specified
size_t line_num = 1; size_t line_num = 1;
while (!file.eof()) while (!file.eof())
@ -145,22 +162,34 @@ namespace Util
if (!line.empty()) if (!line.empty())
{ {
if (*line.begin() == '[' && *line.rbegin() == ']') if (*line.begin() == '[' && *line.rbegin() == ']')
{
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<std::string> section_list = Util::Format(sections).Split(',');
for (const std::string &s: section_list)
{ {
// Check if section exists else create a new one // Check if section exists else create a new one
std::string section(TrimWhiteSpace(std::string(line.begin() + 1, line.begin() + line.length() - 1))); std::string section = TrimWhiteSpace(s);
Node *section_node = nullptr;
if (section.empty() || section == "Global") // empty section names (e.g., "[]") assigned to "[ Global ]" if (section.empty() || section == "Global") // empty section names (e.g., "[]") assigned to "[ Global ]"
current_section = &global; section_node = &global;
else if (!global.TryGet(section)) else if (!global.TryGet(section))
{ {
Node &new_section = global.Add(section); Node &new_section = global.Add(section);
current_section = &new_section; section_node = &new_section;
} }
else else
current_section = global.TryGet(section); section_node = global.TryGet(section);
// Add to current sections
current_sections.insert(section_node);
}
} }
else else
{ {
ParseAssignment(current_section, filename, line_num, line); ParseAssignment(current_sections, filename, line_num, line);
} }
} }

View file

@ -126,6 +126,17 @@
* Any setting not explicitly part of a section is assigned to the "Global" * 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". * 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 * TODO
* ---- * ----
* - TryGet() can be made quicker by attempting a direct lookup first. We never * - TryGet() can be made quicker by attempting a direct lookup first. We never