Files
UnrealEngine/Engine/Source/Programs/UnrealBuildAccelerator/Common/Private/UbaConfig.cpp
2025-05-18 13:04:45 +08:00

327 lines
7.7 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "UbaConfig.h"
#include "UbaFileAccessor.h"
namespace uba
{
bool ConfigTable::GetValueAsString(const tchar*& out, const tchar* key) const
{
auto findIt = m_values.find(key);
if (findIt == m_values.end())
return m_parent ? m_parent->GetValueAsString(out, key) : false;
out = findIt->second.string.c_str();
return true;
}
bool ConfigTable::GetValueAsString(TString& out, const tchar* key) const
{
const tchar* str;
if (!GetValueAsString(str, key))
return false;
out = str;
return true;
}
bool ConfigTable::GetValueAsU32(u32& out, const tchar* key) const
{
auto findIt = m_values.find(key);
if (findIt == m_values.end())
return m_parent ? m_parent->GetValueAsU32(out, key) : false;
StringBuffer<> buf(findIt->second.string);
return buf.Parse(out);
}
bool ConfigTable::GetValueAsU64(u64& out, const tchar* key) const
{
auto findIt = m_values.find(key);
if (findIt == m_values.end())
return m_parent ? m_parent->GetValueAsU64(out, key) : false;
StringBuffer<> buf(findIt->second.string);
return buf.Parse(out);
}
bool ConfigTable::GetValueAsInt(int& out, const tchar* key) const
{
auto findIt = m_values.find(key);
if (findIt == m_values.end())
return m_parent ? m_parent->GetValueAsInt(out, key) : false;
#if PLATFORM_WINDOWS
out = (int)wcstol(findIt->second.string.c_str(), 0, 10);
#else
out = atoi(findIt->second.string.c_str());
#endif
return true;
}
bool ConfigTable::GetValueAsBool(bool& out, const tchar* key) const
{
auto findIt = m_values.find(key);
if (findIt == m_values.end())
return m_parent ? m_parent->GetValueAsBool(out, key) : false;
const tchar* value = findIt->second.string.c_str();
if (Equals(value, TC("true")) || Equals(value, TC("1")))
{
out = true;
return true;
}
if (Equals(value, TC("false")) || Equals(value, TC("0")))
{
out = false;
return true;
}
return false;
}
const ConfigTable* ConfigTable::GetTable(const tchar* name) const
{
if (!name || !*name)
return this;
auto findIt = m_tables.find(name);
if (findIt == m_tables.end())
return nullptr;
return &findIt->second;
}
ConfigTable& ConfigTable::AddTable(const tchar* name)
{
if (!name || !*name)
return *this;
ConfigTable& table = m_tables.try_emplace(name).first->second;
table.m_parent = this;
return table;
}
void ConfigTable::AddValue(const tchar* key, int value)
{
tchar buf[256];
TSprintf_s(buf, sizeof_array(buf), TC("%i"), value);
m_values[key] = Value{ValueType_Value, buf};
}
void ConfigTable::AddValue(const tchar* key, u32 value)
{
tchar buf[256];
TSprintf_s(buf, sizeof_array(buf), TC("%u"), value);
m_values[key] = Value{ValueType_Value, buf};
}
void ConfigTable::AddValue(const tchar* key, u64 value)
{
tchar buf[256];
TSprintf_s(buf, sizeof_array(buf), TC("%llu"), value);
m_values[key] = Value{ValueType_Value, buf};
}
void ConfigTable::AddValue(const tchar* key, bool value)
{
m_values[key] = Value{ValueType_Value, value ? TC("true") : TC("false")};
}
void ConfigTable::AddValue(const tchar* key, const tchar* str)
{
m_values[key] = Value{ValueType_String, str};
}
bool ConfigTable::SaveToText(Logger& logger, Vector<char>& outText) const
{
char line[1024];
for (auto& kv : m_values)
{
const char* quote = kv.second.type == ValueType_String ? "\"" : "";
#if PLATFORM_WINDOWS
int written = sprintf_s(line, sizeof_array(line), "%S = %s%S%s\r\n", kv.first.c_str(), quote, kv.second.string.c_str(), quote);
#else
int written = snprintf(line, sizeof_array(line), "%s = %s%s%s\r\n", kv.first.c_str(), quote, kv.second.string.c_str(), quote);
#endif
u64 size = outText.size();
outText.resize(size + written);
memcpy(outText.data() + size, line, written);
}
for (auto& kv : m_tables)
{
#if PLATFORM_WINDOWS
int written = sprintf_s(line, sizeof_array(line), "[%S]\r\n", kv.first.c_str());
#else
int written = snprintf(line, sizeof_array(line), "[%s]\r\n", kv.first.c_str());
#endif
u64 size = outText.size();
outText.resize(size + written);
memcpy(outText.data() + size, line, written);
kv.second.SaveToText(logger, outText);
}
return true;
}
bool Config::LoadFromFile(Logger& logger, const tchar* configFile)
{
m_isLoaded = true;
#if PLATFORM_WINDOWS
StringBuffer<> tempPath;
if (configFile[1] != ':')
{
if (GetCurrentDirectoryW(tempPath) && FileExists(logger, tempPath.EnsureEndsWithSlash().Append(configFile).data))
{
configFile = tempPath.data;
}
else if (GetDirectoryOfCurrentModule(logger, tempPath.Clear()) && FileExists(logger, tempPath.EnsureEndsWithSlash().Append(configFile).data))
{
configFile = tempPath.data;
}
else
return false;
}
#endif
FileAccessor fa(logger, configFile);
if (!fa.OpenMemoryRead(0, false))
return false;
logger.Info(TC(" Loading config from %s"), configFile);
return LoadFromText(logger, (const char*)fa.GetData(), fa.GetSize());
}
bool ConfigTable::LoadFromText(Logger& logger, const char* text, u64 textLen)
{
const char* i = text;
const char* e = i + textLen;
auto consumeEmpty = [&]() -> char
{
while (i != e)
{
if (*i != ' ' && *i != '\t'&& *i != '\r')
return *i;
++i;
}
return 0;
};
auto consumeIdentifier = [&](StringBufferBase& out) -> char
{
while (i != e)
{
tchar c = *i;
bool validChar = (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || c == '-';
if (!validChar)
return *i;
out.Append(*i);
++i;
}
return 0;
};
auto consumeLine = [&](StringBufferBase& out, char untilChar) -> char
{
bool append = true;
while (i != e)
{
if (*i == '\n')
return *i;
append &= (*i != untilChar && *i != '\r');
if (append)
out.Append(*i);
++i;
}
return 0;
};
ConfigTable* activeTable = this;
while (true)
{
char token = consumeEmpty();
if (token == 0)
break;
if (token == '\n')
{
++i;
}
else if (token == '#')
{
StringBuffer<1024> comment;
consumeLine(comment, 0);
}
else if (token == '[')
{
++i;
StringBuffer<128> tableName;
token = consumeIdentifier(tableName);
if (token != ']')
return logger.Error(TC("No end token after group name %s"), tableName.data);
++i;
token = consumeEmpty();
if (token == 0)
break;
if (token != '\n')
return logger.Error(TC("Unexpected token %c after group %s"), tableName.data);
++i;
activeTable = &m_tables.try_emplace(tableName.data).first->second;
activeTable->m_parent = this;
}
else
{
StringBuffer<128> key;
consumeIdentifier(key);
token = consumeEmpty();
if (token != '=')
return logger.Error(TC("Unexpected equals sign after key name %s"), key);
++i;
token = consumeEmpty();
ValueType type = ValueType_Value;
StringBuffer<1024> value;
if (token == '\"')
{
++i;
token = consumeLine(value, '\"');
type = ValueType_String;
}
else
{
token = consumeLine(value, ' ');
}
activeTable->m_values[key.data] = Value{type, value.data};
if (token == 0)
break;
++i;
}
}
return true;
}
bool Config::IsLoaded() const
{
return m_isLoaded;
}
bool Config::SaveToFile(Logger& logger, const tchar* configFile) const
{
StringBuffer<> dir;
dir.AppendDir(configFile);
if (!DirectoryCache().CreateDirectory(logger, dir.data))
return false;
FileAccessor fa(logger, configFile);
if (!fa.CreateWrite())
return false;
Vector<char> text;
if (!SaveToText(logger, text))
return false;
if (!fa.Write(text.data(), text.size()))
return false;
return fa.Close();
}
}