2024-07-02 10:52:24 +08:00
|
|
|
#include "ConfigManager.hpp"
|
|
|
|
|
|
|
|
#include "EncodingHelper.hpp"
|
|
|
|
#include "IOHelper.hpp"
|
2024-07-07 16:08:42 +08:00
|
|
|
#include <stdexcept>
|
2024-07-02 10:52:24 +08:00
|
|
|
|
|
|
|
namespace YYCC::ConfigManager {
|
|
|
|
|
|
|
|
#pragma region Core Manager
|
|
|
|
|
|
|
|
CoreManager::CoreManager(
|
|
|
|
const yycc_char8_t* cfg_file_path,
|
|
|
|
uint64_t version_identifier,
|
|
|
|
std::initializer_list<AbstractSetting*> settings) :
|
|
|
|
m_CfgFilePath(), m_VersionIdentifier(version_identifier), m_Settings() {
|
|
|
|
// assign cfg path
|
|
|
|
if (cfg_file_path != nullptr)
|
|
|
|
m_CfgFilePath = cfg_file_path;
|
|
|
|
// assign settings
|
|
|
|
for (auto* setting : settings) {
|
2024-07-07 16:08:42 +08:00
|
|
|
auto result = m_Settings.try_emplace(setting->GetName(), setting);
|
|
|
|
if (!result.second) {
|
|
|
|
// if not inserted because duplicated, raise exception
|
|
|
|
throw std::invalid_argument("Duplicated setting name");
|
|
|
|
}
|
2024-07-02 10:52:24 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CoreManager::Load() {
|
|
|
|
// reset all settings first
|
|
|
|
Reset();
|
|
|
|
|
|
|
|
// get file handle
|
|
|
|
auto fs = this->GetFileHandle(YYCC_U8("rb"));
|
|
|
|
if (fs.get() == nullptr) {
|
|
|
|
// if we fail to get, it means that we do not have corresponding cfg file.
|
|
|
|
// all settings should be reset to default value.
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// fetch version info
|
|
|
|
uint64_t version_info;
|
2024-07-03 10:14:17 +08:00
|
|
|
if (std::fread(&version_info, 1u, sizeof(version_info), fs.get()) != sizeof(version_info))
|
2024-07-02 10:52:24 +08:00
|
|
|
return false;
|
|
|
|
// check version
|
|
|
|
// if read version is greater than we expected,
|
|
|
|
// it means that this cfg file is created by the program higer than this.
|
|
|
|
// we should not read anything from it.
|
|
|
|
// however, for compaitibility reason, we allow read old cfg data.
|
|
|
|
if (version_info > m_VersionIdentifier)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
// fetch setting item from file
|
|
|
|
yycc_u8string name_cache;
|
|
|
|
while (true) {
|
|
|
|
// try fetch setting name
|
|
|
|
// fetch name length
|
|
|
|
size_t name_length;
|
2024-07-03 10:14:17 +08:00
|
|
|
if (std::fread(&name_length, 1u, sizeof(name_length), fs.get()) != sizeof(name_length)) {
|
2024-07-02 10:52:24 +08:00
|
|
|
// we also check whether reach EOF at there.
|
|
|
|
if (std::feof(fs.get())) break;
|
|
|
|
else return false;
|
|
|
|
}
|
|
|
|
// fetch name body
|
|
|
|
name_cache.resize(name_length);
|
2024-07-03 10:14:17 +08:00
|
|
|
if (std::fread(name_cache.data(), 1u, name_length, fs.get()) != name_length)
|
2024-07-02 10:52:24 +08:00
|
|
|
return false;
|
|
|
|
|
|
|
|
// get setting data length
|
|
|
|
size_t data_length;
|
2024-07-03 10:14:17 +08:00
|
|
|
if (std::fread(&data_length, 1u, sizeof(data_length), fs.get()) != sizeof(data_length))
|
2024-07-02 10:52:24 +08:00
|
|
|
return false;
|
|
|
|
|
|
|
|
// get matched setting first
|
|
|
|
const auto& found = m_Settings.find(name_cache);
|
|
|
|
if (found != m_Settings.end()) {
|
|
|
|
// found. read data for it
|
|
|
|
found->second->ResizeData(data_length);
|
2024-07-03 10:14:17 +08:00
|
|
|
if (std::fread(found->second->GetDataPtr(), 1u, data_length, fs.get()) != data_length)
|
2024-07-02 10:52:24 +08:00
|
|
|
return false;
|
|
|
|
// call user defined load function
|
|
|
|
// if fail to parse, reset to default value
|
|
|
|
if (!found->second->UserLoad())
|
|
|
|
found->second->UserReset();
|
|
|
|
} else {
|
|
|
|
// fail to find. skip this unknown setting
|
|
|
|
if (fseek(fs.get(), static_cast<long>(data_length), SEEK_CUR) != 0)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CoreManager::Save() {
|
|
|
|
// get file handle
|
|
|
|
auto fs = this->GetFileHandle(YYCC_U8("wb"));
|
|
|
|
// if we fail to get, return false.
|
|
|
|
if (fs == nullptr) return false;
|
|
|
|
|
|
|
|
// write config data
|
|
|
|
uint64_t version_info = m_VersionIdentifier;
|
2024-07-03 10:14:17 +08:00
|
|
|
if (std::fwrite(&version_info, 1u, sizeof(version_info), fs.get()) != sizeof(version_info))
|
2024-07-02 10:52:24 +08:00
|
|
|
return false;
|
|
|
|
|
|
|
|
// iterate all data for writing
|
|
|
|
for (const auto& pair : m_Settings) {
|
|
|
|
// do user defined save
|
|
|
|
// if failed, skip this setting
|
|
|
|
if (!pair.second->UserSave())
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// write setting name
|
|
|
|
// write name length
|
|
|
|
size_t name_length = pair.first.size();
|
2024-07-03 10:14:17 +08:00
|
|
|
if (std::fwrite(&name_length, 1u, sizeof(name_length), fs.get()) != sizeof(name_length))
|
2024-07-02 10:52:24 +08:00
|
|
|
return false;
|
|
|
|
// write name body
|
2024-07-03 10:14:17 +08:00
|
|
|
if (std::fwrite(pair.first.c_str(), 1u, name_length, fs.get()) != name_length)
|
2024-07-02 10:52:24 +08:00
|
|
|
return false;
|
|
|
|
|
|
|
|
// write setting daat
|
|
|
|
// write data length
|
|
|
|
size_t data_length = pair.second->GetDataSize();
|
2024-07-03 10:14:17 +08:00
|
|
|
if (std::fwrite(&data_length, 1u, sizeof(data_length), fs.get()) != sizeof(data_length))
|
2024-07-02 10:52:24 +08:00
|
|
|
return false;
|
|
|
|
// write data body
|
2024-07-03 10:14:17 +08:00
|
|
|
if (std::fwrite(pair.second->GetDataPtr(), 1u, data_length, fs.get()) != data_length)
|
2024-07-02 10:52:24 +08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// all settings done, return true
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CoreManager::Reset() {
|
|
|
|
for (const auto& pair : m_Settings) {
|
|
|
|
pair.second->UserReset();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
CoreManager::FileHandleGuard_t CoreManager::GetFileHandle(const yycc_char8_t* mode) const {
|
|
|
|
return CoreManager::FileHandleGuard_t(
|
|
|
|
IOHelper::UTF8FOpen(this->m_CfgFilePath.c_str(), mode),
|
|
|
|
[](FILE* fs) -> void {
|
|
|
|
if (fs != nullptr) std::fclose(fs);
|
|
|
|
}
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#pragma endregion
|
|
|
|
|
|
|
|
}
|