-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
{
m_inputs->ConfigureInputs(&m_model3->GetGame());
m_inputs->ConfigureInputs(m_model3->GetGame());
return false;
}
//

View file

@ -4,6 +4,7 @@
#include <algorithm>
#include <fstream>
#include <queue>
#include <set>
#include <cstdlib>
#include <iostream>
@ -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<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)
{
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<Node *> 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<std::string> 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());
}
}
}
}

View file

@ -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