mirror of
https://github.com/cemu-project/Cemu.git
synced 2025-07-15 11:18:29 +12:00
Add all the files
This commit is contained in:
parent
e3db07a16a
commit
d60742f52b
1445 changed files with 430238 additions and 0 deletions
263
src/util/IniParser/IniParser.cpp
Normal file
263
src/util/IniParser/IniParser.cpp
Normal file
|
@ -0,0 +1,263 @@
|
|||
#include "util/IniParser/IniParser.h"
|
||||
|
||||
IniParser::IniParser(std::span<char> iniContents, std::string_view name) : m_name(name)
|
||||
{
|
||||
// we dont support utf8 but still skip the byte order mark in case the user saved the document with the wrong encoding
|
||||
if (iniContents.size() >= 3 && (uint8)iniContents[0] == 0xEF && (uint8)iniContents[1] == 0xBB && (uint8)iniContents[2] == 0xBF)
|
||||
iniContents = iniContents.subspan(3);
|
||||
|
||||
m_iniFileData.assign(iniContents.begin(), iniContents.end());
|
||||
m_isValid = parse();
|
||||
}
|
||||
|
||||
bool IniParser::ReadNextLine(std::string_view& lineString)
|
||||
{
|
||||
if (m_parseOffset >= m_iniFileData.size())
|
||||
return false;
|
||||
// skip \r and \n
|
||||
for (; m_parseOffset < m_iniFileData.size(); m_parseOffset++)
|
||||
{
|
||||
char c = m_iniFileData[m_parseOffset];
|
||||
if (c == '\r' || c == '\n')
|
||||
continue;
|
||||
break;
|
||||
}
|
||||
if (m_parseOffset >= m_iniFileData.size())
|
||||
return false;
|
||||
size_t lineStart = m_parseOffset;
|
||||
// parse until end of line/file
|
||||
for (; m_parseOffset < m_iniFileData.size(); m_parseOffset++)
|
||||
{
|
||||
char c = m_iniFileData[m_parseOffset];
|
||||
if (c == '\r' || c == '\n')
|
||||
break;
|
||||
}
|
||||
size_t lineEnd = m_parseOffset;
|
||||
lineString = { m_iniFileData.data() + lineStart, lineEnd - lineStart };
|
||||
return true;
|
||||
}
|
||||
|
||||
void IniParser::TrimWhitespaces(std::string_view& str)
|
||||
{
|
||||
while (!str.empty())
|
||||
{
|
||||
char c = str[0];
|
||||
if (c != ' ' && c != '\t')
|
||||
break;
|
||||
str.remove_prefix(1);
|
||||
}
|
||||
while (!str.empty())
|
||||
{
|
||||
char c = str.back();
|
||||
if (c != ' ' && c != '\t')
|
||||
break;
|
||||
str.remove_suffix(1);
|
||||
}
|
||||
}
|
||||
|
||||
bool IniParser::parse()
|
||||
{
|
||||
sint32 lineNumber = 0;
|
||||
std::string_view lineView;
|
||||
while (ReadNextLine(lineView))
|
||||
{
|
||||
lineNumber++;
|
||||
// skip whitespaces
|
||||
while (!lineView.empty())
|
||||
{
|
||||
char c = lineView[0];
|
||||
if (c != ' ' && c != '\t')
|
||||
break;
|
||||
lineView.remove_prefix(1);
|
||||
}
|
||||
if (lineView.empty())
|
||||
continue;
|
||||
// cut off comments (starting with # or ;)
|
||||
bool isInQuote = false;
|
||||
for (size_t i = 0; i < lineView.size(); i++)
|
||||
{
|
||||
if (lineView[i] == '\"')
|
||||
isInQuote = !isInQuote;
|
||||
if ((lineView[i] == '#' || lineView[i] == ';') && !isInQuote)
|
||||
{
|
||||
lineView.remove_suffix(lineView.size() - i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(lineView.empty())
|
||||
continue;
|
||||
// handle section headers
|
||||
if (lineView[0] == '[')
|
||||
{
|
||||
isInQuote = false;
|
||||
bool endsWithBracket = false;
|
||||
for (size_t i = 1; i < lineView.size(); i++)
|
||||
{
|
||||
if (lineView[i] == '\"')
|
||||
isInQuote = !isInQuote;
|
||||
if (lineView[i] == ']')
|
||||
{
|
||||
lineView.remove_suffix(lineView.size() - i);
|
||||
lineView.remove_prefix(1);
|
||||
endsWithBracket = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!endsWithBracket)
|
||||
PrintWarning(lineNumber, "Section doesn't end with a ]", lineView);
|
||||
StartSection(lineView, lineNumber);
|
||||
continue;
|
||||
}
|
||||
// otherwise try to parse it as an option in the form name = value
|
||||
// find and split at = character
|
||||
std::string_view option_name;
|
||||
std::string_view option_value;
|
||||
bool invalidName = true;
|
||||
for (size_t i = 0; i < lineView.size(); i++)
|
||||
{
|
||||
if (lineView[i] == '=')
|
||||
{
|
||||
option_name = lineView.substr(0, i);
|
||||
option_value = lineView.substr(i+1);
|
||||
invalidName = false;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
if (invalidName)
|
||||
{
|
||||
TrimWhitespaces(lineView);
|
||||
if (!lineView.empty())
|
||||
PrintWarning(lineNumber, "Not a valid section header or name-value pair", lineView);
|
||||
continue;
|
||||
}
|
||||
// validate
|
||||
TrimWhitespaces(option_name);
|
||||
TrimWhitespaces(option_value);
|
||||
if (option_name.empty())
|
||||
{
|
||||
PrintWarning(lineNumber, "Empty option name is not allowed", lineView);
|
||||
continue;
|
||||
}
|
||||
bool invalidCharacter = false;
|
||||
for (auto& _c : option_name)
|
||||
{
|
||||
uint8 c = (uint8)_c;
|
||||
if (c == ']' || c == '[')
|
||||
{
|
||||
PrintWarning(lineNumber, "Option name may not contain [ or ]", lineView);
|
||||
invalidCharacter = true;
|
||||
break;
|
||||
}
|
||||
else if (c < 32 || c > 128 || c == ' ')
|
||||
{
|
||||
PrintWarning(lineNumber, "Option name may only contain ANSI characters and no spaces", lineView);
|
||||
invalidCharacter = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(invalidCharacter)
|
||||
continue;
|
||||
// remove quotes from value
|
||||
if (!option_value.empty() && option_value.front() == '\"')
|
||||
{
|
||||
option_value.remove_prefix(1);
|
||||
if (option_value.size() >= 2 && option_value.back() == '\"')
|
||||
{
|
||||
option_value.remove_suffix(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
PrintWarning(lineNumber, "Option value starts with a quote character \" but does not end with one", lineView);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (m_sectionList.empty())
|
||||
{
|
||||
// no current section
|
||||
PrintWarning(lineNumber, "Option defined without first defining a section", lineView);
|
||||
continue;
|
||||
}
|
||||
// convert name to lower case
|
||||
m_sectionList.back().m_optionPairs.emplace_back(option_name, option_value);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void IniParser::StartSection(std::string_view sectionName, size_t lineNumber)
|
||||
{
|
||||
m_sectionList.emplace_back(sectionName, lineNumber);
|
||||
}
|
||||
|
||||
bool IniParser::NextSection()
|
||||
{
|
||||
if (m_currentSectionIndex == std::numeric_limits<size_t>::max())
|
||||
{
|
||||
m_currentSectionIndex = 0;
|
||||
return m_currentSectionIndex < m_sectionList.size();
|
||||
}
|
||||
if (m_currentSectionIndex >= m_sectionList.size())
|
||||
return false;
|
||||
m_currentSectionIndex++;
|
||||
return m_currentSectionIndex < m_sectionList.size();
|
||||
}
|
||||
|
||||
std::string_view IniParser::GetCurrentSectionName()
|
||||
{
|
||||
if (m_currentSectionIndex == std::numeric_limits<size_t>::max() || m_currentSectionIndex >= m_sectionList.size())
|
||||
return "";
|
||||
return m_sectionList[m_currentSectionIndex].m_sectionName;
|
||||
}
|
||||
|
||||
size_t IniParser::GetCurrentSectionLineNumber()
|
||||
{
|
||||
if (m_currentSectionIndex == std::numeric_limits<size_t>::max() || m_currentSectionIndex >= m_sectionList.size())
|
||||
return 0;
|
||||
return m_sectionList[m_currentSectionIndex].m_lineNumber;
|
||||
}
|
||||
|
||||
std::optional<std::string_view> IniParser::FindOption(std::string_view optionName)
|
||||
{
|
||||
if (m_currentSectionIndex == std::numeric_limits<size_t>::max() || m_currentSectionIndex >= m_sectionList.size())
|
||||
return std::nullopt;
|
||||
auto& optionPairsList = m_sectionList[m_currentSectionIndex].m_optionPairs;
|
||||
for (auto& itr : optionPairsList)
|
||||
{
|
||||
auto& itrOptionName = itr.first;
|
||||
// case insensitive ANSI string comparison
|
||||
if(itrOptionName.size() != optionName.size())
|
||||
continue;
|
||||
bool isMatch = true;
|
||||
for (size_t i = 0; i < itrOptionName.size(); i++)
|
||||
{
|
||||
char c0 = itrOptionName[i];
|
||||
char c1 = optionName[i];
|
||||
if (c0 >= 'A' && c0 <= 'Z')
|
||||
c0 -= ('A' - 'a');
|
||||
if (c1 >= 'A' && c1 <= 'Z')
|
||||
c1 -= ('A' - 'a');
|
||||
if (c0 != c1)
|
||||
{
|
||||
isMatch = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!isMatch)
|
||||
continue;
|
||||
return itr.second;
|
||||
}
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::span<std::pair<std::string_view, std::string_view>> IniParser::GetAllOptions()
|
||||
{
|
||||
if (m_currentSectionIndex == std::numeric_limits<size_t>::max() || m_currentSectionIndex >= m_sectionList.size())
|
||||
return {};
|
||||
return m_sectionList[m_currentSectionIndex].m_optionPairs;
|
||||
}
|
||||
|
||||
void IniParser::PrintWarning(int lineNumber, std::string_view msg, std::string_view lineView)
|
||||
{
|
||||
// INI logging is silenced
|
||||
// cemuLog_force("File: {} Line {}: {}", m_name, lineNumber, msg);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue