mirror of
https://github.com/RetroDECK/Supermodel.git
synced 2024-11-25 23:25:40 +00:00
-Added support for specifying multiple sections simultaneously in INI
files, e.g., [ daytona2, dayto2pe ] -Forgetfulness in 763
This commit is contained in:
parent
864bb36b5a
commit
c171356f7d
|
@ -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;
|
||||
}
|
||||
//
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
if (!suppress_errors)
|
||||
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)));
|
||||
if (lvalue.empty())
|
||||
{
|
||||
if (!suppress_errors)
|
||||
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
|
||||
// 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);
|
||||
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);
|
||||
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 *> ¤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)
|
||||
|
@ -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())
|
||||
|
@ -145,22 +162,34 @@ namespace Util
|
|||
if (!line.empty())
|
||||
{
|
||||
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
|
||||
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 ]"
|
||||
current_section = &global;
|
||||
section_node = &global;
|
||||
else if (!global.TryGet(section))
|
||||
{
|
||||
Node &new_section = global.Add(section);
|
||||
current_section = &new_section;
|
||||
section_node = &new_section;
|
||||
}
|
||||
else
|
||||
current_section = global.TryGet(section);
|
||||
section_node = global.TryGet(section);
|
||||
|
||||
// Add to current sections
|
||||
current_sections.insert(section_node);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ParseAssignment(current_section, filename, line_num, line);
|
||||
ParseAssignment(current_sections, filename, line_num, line);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue