Initial commit

This commit is contained in:
2026-04-03 00:22:39 -05:00
commit eca1e8c458
945 changed files with 218160 additions and 0 deletions

256
ZUtil/ZIniReader.cpp Normal file
View File

@@ -0,0 +1,256 @@
#include <ZUtil/ZIniReader.hpp>
#include <ZSTL/ZBasicStringAlgo.hpp>
bool ZIniReader::Read(const char* data, size_t length)
{
/*
Algorithm:
1) Read a line, ignore blank lines and comment lines
2) If line is KVP
2.1) No section? -> Error
2.2) Section exists? -> Parse KVP
2.2.1) Takes form of K = V? -> Add KVP
2.2.2) Doesn't? -> Error
3) Else if section
3.1) If already seen section -> Error
3.2) Create section hashtable, insert it
4) Next line
*/
ClearData();
ErrorMessage.Clear();
ZHashMap<ZString, ZString>* currentSection = NULL;
size_t i = 0; //Current index into file data
size_t lineStart = 0; //Start of line
size_t lineLength;
uint32_t lineNumber = 1;
//Parse loop
while(i < length)
{
char c = data[i];
i++;
//Treat final character as EOL too
if(i < length)
{
//Split on \n or \r
if(c != 0x0A && c != 0x0D)
continue;
else //Found EOL character
{
//Line length does NOT include EOL character
lineLength = i - 1 - lineStart;
}
}
else //Final line, don't remove extra character
lineLength = i - lineStart;
//Line must be non-blank
if(lineLength > 0 && data[lineStart] != '\n')
{
//Get pointer to start of line
const char* line = &data[lineStart];
//Ignore comments
if(line[0] != ';' && line [0] != '#')
{
//Section name
if(line[0] == '[')
{
size_t j=1; //Start after '['
//Read until closing '] or EOL
while(j<lineLength && line[j] != ']')
{
if(!isalnum(line[j]) && line[j] != '_' && line[j] != ' ')
{
SetError("Header must contain alphanumeric, space, or _ only", lineNumber);
return false;
}
j++;
}
//Reached end of line but no terminator
if(j == lineLength)
{
SetError("Missing closing \']\'", lineNumber);
return false;
}
//Copy section name to ZString
ZString sectionName(line+1, j-1);
//Check for existence
if(StringSectionMap.ContainsKey(sectionName))
{
SetError("Section already exists", lineNumber);
return false;
}
//Construct new table
ZHashMap<ZString, ZString>* table = new(std::nothrow) ZHashMap<ZString, ZString>();
if(table != NULL)
{
//Add mapping
StringSectionMap.Put(sectionName, table);
Sections.PushBack(table);
SectionNames.PushBack(sectionName);
//Save this as the current section
currentSection = table;
}
else
{
SetError("Out of memory", lineNumber);
return false;
}
}
else if(!isspace(line[0])) //Not a space, not comment, not section -> KVP
{
//Did we find a section first?
if(currentSection != NULL)
{
size_t j=0; //length of key
//Scan until space, equals sign, or EOL
while(j<lineLength && line[j] != '=' && !isspace(line[j]))
{
if(!isalnum(line[j]) && line[j] != '_')
{
SetError("Key must contain alphanumeric or '_' only", lineNumber);
return false;
}
j++;
}
//Didn't find either a space or '='
if(j == lineLength)
{
SetError("Missing '=' after key name", lineNumber);
return false;
}
//Copy key to ZString
ZString key(line, j);
if(line[j] != '=')
{
//Skip remaining whitespace
while(j<lineLength && isspace(line[j]))
j++;
if(j == lineLength || line[j] != '=')
{
SetError("Missing '=' after key name", lineNumber);
return false;
}
}
//Now we definitely point to a '=' character. Ensure there is data after it
if(j+1 == lineLength)
{
SetError("Expected value after '='", lineNumber);
return false;
}
j++;
//Skip any whitespace after '='
while(j<lineLength && isspace(line[j]))
j++;
//Special case: if we hit EOL, then we have an empty value
if(j == lineLength)
{
currentSection->Put(key, "");
}
else
{
ZString val(&line[j], lineLength-j);
currentSection->Put(key, val);
}
}
else
{
SetError("Values must be in a section", lineNumber);
return false;
}
}
else
{
SetError("Invalid line. Must be a section, comment, or value", lineNumber);
return false;
}
}
} //if(non-blank line)
//Check for \r \n pattern
if(c == '\r') //we expect a \n, but verify
{
if(i < length && data[i] == '\n')
i++;
}
//Start on next line
lineStart = i;
lineNumber++;
}
return !Sections.Empty();
}
/*************************************************************************/
const ZHashMap<ZString, ZString>* ZIniReader::GetSection(const ZString& sectionName)
{
ZHashMap<ZString, ZHashMap<ZString, ZString>* >::Iterator it = StringSectionMap.Find(sectionName);
if(it.HasCurrent())
return it.GetValue();
return NULL;
}
/*************************************************************************/
void ZIniReader::SetError(const char* message, uint32_t line)
{
ZStringAlgo::BuildPrintf(ErrorMessage, "Line %u: %s", line, message);
//Clear all data
ClearData();
}
/*************************************************************************/
void ZIniReader::ClearData()
{
for(size_t i = 0; i < Sections.Size(); i++)
delete Sections[i];
Sections.Clear();
SectionNames.Clear();
StringSectionMap.Clear();
}
/*************************************************************************/
void ZIniReader::GetKVTree(ZKVTree& _kvtree)
{
for(size_t i = 0; i < Sections.Size(); i++)
{
ZHashMap<ZString, ZString>* hmap = Sections[i];
ZHashMap<ZString, ZString>::Iterator it = hmap->Begin();
for(; it != hmap->End(); ++it)
{
ZString key = SectionNames[i]+"."+it.GetKey();
ZString& value = it.GetValue();
_kvtree.Put(key, value);
}
}
}