Files
UnleashedRecomp-hedge-dev/UnleashedRecomp/mod/ini_file.inl
Skyth (Asilkan) a397a90551 Mod loader implementation. (#66)
* Initial mod loader implementation.

* Allow iterating in mod directories.

* Initial append archive implementation.

* Avoid calling function wrappers when loading append ARs.

For some reason they cause issues. Should investigate later.

* UMM merge archive support.

* Load merge archives without archive lists.

* Less thread locals.

I shouldn't worry about string allocations this much when the game itself spams them...

* Check for read-only UMM archives.

TODO: Skip merging as it's currently just doing duplicate loads.

* Skip loading merge archives if they are read-only.

* Merge only archives.

* Implement decompression.

* Fix append ARLs not loading.

* Initial save file redirection implementation.

* Slightly refactor resolved path usage.

* Implement save file redirection fallback.

* Set a default save file path if none is provided.

* Check for enabled option & replace backward slashes with forward ones in mod save file paths.

* Convert back slashes to forward ones when iterating directories.

* Make CSB limit dynamic.

* Cache append/merge archive lookups.

* Close stream after reading compressed ARL.

* Fix UMM/HMM ARL file path inconsistency.
2024-12-31 20:20:07 +03:00

206 lines
6.1 KiB
C++

inline size_t IniFile::hashStr(const std::string_view& str)
{
return XXH3_64bits(str.data(), str.size());
}
inline bool IniFile::isWhitespace(char value)
{
return value == ' ' || value == '\t';
}
inline bool IniFile::isNewLine(char value)
{
return value == '\n' || value == '\r';
}
inline bool IniFile::read(const std::filesystem::path& filePath)
{
std::ifstream file(filePath, std::ios::binary);
if (!file.good())
return false;
file.seekg(0, std::ios::end);
const size_t dataSize = static_cast<size_t>(file.tellg());
const auto data = std::make_unique<char[]>(dataSize + 1);
data[dataSize] = '\0';
file.seekg(0, std::ios::beg);
file.read(data.get(), dataSize);
file.close();
Section* section = nullptr;
const char* dataPtr = data.get();
while (dataPtr < data.get() + dataSize)
{
if (*dataPtr == ';')
{
while (*dataPtr != '\0' && !isNewLine(*dataPtr))
++dataPtr;
}
else if (*dataPtr == '[')
{
++dataPtr;
const char* endPtr = dataPtr;
while (*endPtr != '\0' && !isNewLine(*endPtr) && *endPtr != ']')
++endPtr;
if (*endPtr != ']')
return false;
std::string sectionName(dataPtr, endPtr - dataPtr);
section = &m_sections[hashStr(sectionName)];
section->name = std::move(sectionName);
dataPtr = endPtr + 1;
}
else if (!isWhitespace(*dataPtr) && !isNewLine(*dataPtr))
{
if (section == nullptr)
return false;
const char* endPtr;
if (*dataPtr == '"')
{
++dataPtr;
endPtr = dataPtr;
while (*endPtr != '\0' && !isNewLine(*endPtr) && *endPtr != '"')
++endPtr;
if (*endPtr != '"')
return false;
}
else
{
endPtr = dataPtr;
while (*endPtr != '\0' && !isNewLine(*endPtr) && !isWhitespace(*endPtr) && *endPtr != '=')
++endPtr;
if (!isNewLine(*endPtr) && !isWhitespace(*endPtr) && *endPtr != '=')
return false;
}
std::string propertyName(dataPtr, endPtr - dataPtr);
auto& property = section->properties[hashStr(propertyName)];
property.name = std::move(propertyName);
dataPtr = endPtr;
while (*dataPtr != '\0' && !isNewLine(*dataPtr) && *dataPtr != '=')
++dataPtr;
if (*dataPtr == '=')
{
++dataPtr;
while (*dataPtr != '\0' && isWhitespace(*dataPtr))
++dataPtr;
if (*dataPtr == '"')
{
++dataPtr;
endPtr = dataPtr;
while (*endPtr != '\0' && !isNewLine(*endPtr) && *endPtr != '"')
++endPtr;
if (*endPtr != '"')
return false;
}
else
{
endPtr = dataPtr;
while (*endPtr != '\0' && !isNewLine(*endPtr) && !isWhitespace(*endPtr))
++endPtr;
}
property.value = std::string(dataPtr, endPtr - dataPtr);
dataPtr = endPtr + 1;
}
}
else
{
++dataPtr;
}
}
return true;
}
inline std::string IniFile::getString(const std::string_view& sectionName, const std::string_view& propertyName, std::string defaultValue) const
{
const auto sectionPair = m_sections.find(hashStr(sectionName));
if (sectionPair != m_sections.end())
{
const auto propertyPair = sectionPair->second.properties.find(hashStr(propertyName));
if (propertyPair != sectionPair->second.properties.end())
return propertyPair->second.value;
}
return defaultValue;
}
inline bool IniFile::getBool(const std::string_view& sectionName, const std::string_view& propertyName, bool defaultValue) const
{
const auto sectionPair = m_sections.find(hashStr(sectionName));
if (sectionPair != m_sections.end())
{
const auto propertyPair = sectionPair->second.properties.find(hashStr(propertyName));
if (propertyPair != sectionPair->second.properties.end() && !propertyPair->second.value.empty())
{
const char firstChar = propertyPair->second.value[0];
return firstChar == 't' || firstChar == 'T' || firstChar == 'y' || firstChar == 'Y' || firstChar == '1';
}
}
return defaultValue;
}
inline bool IniFile::contains(const std::string_view& sectionName) const
{
return m_sections.contains(hashStr(sectionName));
}
template <typename T>
T IniFile::get(const std::string_view& sectionName, const std::string_view& propertyName, T defaultValue) const
{
const auto sectionPair = m_sections.find(hashStr(sectionName));
if (sectionPair != m_sections.end())
{
const auto propertyPair = sectionPair->second.properties.find(hashStr(propertyName));
if (propertyPair != sectionPair->second.properties.end())
{
T value{};
const auto result = std::from_chars(propertyPair->second.value.data(),
propertyPair->second.value.data() + propertyPair->second.value.size(), value);
if (result.ec == std::errc{})
return value;
}
}
return defaultValue;
}
template<typename T>
inline void IniFile::enumerate(const T& function) const
{
for (const auto& [_, section] : m_sections)
{
for (auto& property : section.properties)
function(section.name, property.second.name, property.second.value);
}
}
template <typename T>
void IniFile::enumerate(const std::string_view& sectionName, const T& function) const
{
const auto sectionPair = m_sections.find(hashStr(sectionName));
if (sectionPair != m_sections.end())
{
for (const auto& property : sectionPair->second.properties)
function(property.second.name, property.second.value);
}
}