refactor(utils): 重构数据存储模块为新的存档系统

移除旧的 DataStore 类及相关文件,替换为新的 SaveStore 实现。新实现采用二进制序列化格式,提供更高效的存储策略接口,支持文件系统和 Switch 平台存档系统。改进事务处理机制和类型安全的数据访问方法。
This commit is contained in:
ChestnutYueyue 2026-02-23 03:21:29 +08:00
parent 137369c37c
commit 8e9fe8719f
5 changed files with 584 additions and 4328 deletions

View File

@ -1,216 +0,0 @@
#pragma once
#include <extra2d/core/types.h>
#include <string>
#include <vector>
namespace extra2d {
// ============================================================================
// 存档类型枚举
// ============================================================================
enum class SaveDataType {
Account, // 用户存档(与特定用户关联)
Common, // 公共存档(所有用户共享)
Cache, // 缓存数据(可删除)
Device, // 设备存档
Temporary, // 临时数据
};
// ============================================================================
// 用户ID结构封装 Switch AccountUid
// ============================================================================
struct UserId {
uint64_t uid[2] = {0, 0};
bool isValid() const { return uid[0] != 0 || uid[1] != 0; }
bool operator==(const UserId &other) const {
return uid[0] == other.uid[0] && uid[1] == other.uid[1];
}
bool operator!=(const UserId &other) const { return !(*this == other); }
};
// ============================================================================
// DataStore 类 - 数据持久化(支持 Switch 存档系统)
// ============================================================================
class DataStore {
public:
DataStore();
~DataStore();
// ------------------------------------------------------------------------
// 文件操作
// ------------------------------------------------------------------------
/// 加载 INI 文件
bool load(const std::string &filename);
/// 保存到 INI 文件
bool save(const std::string &filename);
/// 获取当前文件名
const std::string &getFilename() const { return filename_; }
// ------------------------------------------------------------------------
// Switch 存档系统支持
// ------------------------------------------------------------------------
/**
* @brief Switch
* @param type
* @param userId IDAccount
* @param mountName "save"
* @return
*/
bool mountSaveData(SaveDataType type = SaveDataType::Account,
const UserId &userId = UserId(),
const std::string &mountName = "save");
/**
* @brief
* @param mountName
*/
void unmountSaveData(const std::string &mountName = "save");
/**
* @brief
* @param mountName
* @return
*/
bool commitSaveData(const std::string &mountName = "save");
/**
* @brief
*/
bool isSaveDataMounted() const { return saveDataMounted_; }
/**
* @brief
*/
std::string getSaveDataPath(const std::string &path = "") const;
// ------------------------------------------------------------------------
// 用户账户管理
// ------------------------------------------------------------------------
/**
* @brief ID
* @return IDID
*/
static UserId getCurrentUserId();
/**
* @brief ID
*/
void setDefaultUserId(const UserId &userId) { defaultUserId_ = userId; }
/**
* @brief ID
*/
UserId getDefaultUserId() const { return defaultUserId_; }
// ------------------------------------------------------------------------
// 数据读写
// ------------------------------------------------------------------------
/// 获取字符串值
std::string getString(const std::string &section, const std::string &key,
const std::string &defaultValue = "");
/// 获取整数值
int getInt(const std::string &section, const std::string &key,
int defaultValue = 0);
/// 获取浮点数值
float getFloat(const std::string &section, const std::string &key,
float defaultValue = 0.0f);
/// 获取布尔值
bool getBool(const std::string &section, const std::string &key,
bool defaultValue = false);
/// 设置字符串值
void setString(const std::string &section, const std::string &key,
const std::string &value);
/// 设置整数值
void setInt(const std::string &section, const std::string &key, int value);
/// 设置浮点数值
void setFloat(const std::string &section, const std::string &key,
float value);
/// 设置布尔值
void setBool(const std::string &section, const std::string &key, bool value);
/// 删除键
void removeKey(const std::string &section, const std::string &key);
/// 删除整个 section
void removeSection(const std::string &section);
/// 检查键是否存在
bool hasKey(const std::string &section, const std::string &key);
/// 检查 section 是否存在
bool hasSection(const std::string &section);
/// 清除所有数据
void clear();
// ------------------------------------------------------------------------
// 事务支持
// ------------------------------------------------------------------------
/**
* @brief
*/
void beginTransaction();
/**
* @brief
* @return
*/
bool commit();
/**
* @brief
*/
void rollback();
/**
* @brief
*/
bool isInTransaction() const { return inTransaction_; }
// ------------------------------------------------------------------------
// 工具方法
// ------------------------------------------------------------------------
/// 获取所有 section 名称
std::vector<std::string> getAllSections() const;
/// 获取指定 section 的所有 key
std::vector<std::string> getAllKeys(const std::string &section) const;
/// 从存档加载(自动处理挂载路径)
bool loadFromSave(const std::string &path);
/// 保存到存档(自动处理挂载路径和提交)
bool saveToSave(const std::string &path);
private:
class Impl;
Unique<Impl> impl_;
std::string filename_;
std::string mountName_;
UserId defaultUserId_;
bool saveDataMounted_ = false;
bool inTransaction_ = false;
bool dirty_ = false;
// 内部辅助方法
bool internalSave(const std::string &filename);
};
} // namespace extra2d

View File

@ -0,0 +1,125 @@
#pragma once
#include <extra2d/core/types.h>
#include <memory>
#include <string>
#include <unordered_map>
#include <vector>
namespace extra2d {
// ============================================================================
// 存储策略接口
// ============================================================================
class ISaveStrategy {
public:
virtual ~ISaveStrategy() = default;
// 读取原始数据
virtual bool read(const std::string& path, std::vector<u8>& data) = 0;
// 写入原始数据
virtual bool write(const std::string& path, const std::vector<u8>& data) = 0;
// 提交更改Switch存档需要
virtual bool commit() { return true; }
// 是否可用
virtual bool valid() const = 0;
};
// ============================================================================
// 文件存储策略
// ============================================================================
class FileSaveStrategy : public ISaveStrategy {
public:
bool read(const std::string& path, std::vector<u8>& data) override;
bool write(const std::string& path, const std::vector<u8>& data) override;
bool valid() const override { return true; }
};
// ============================================================================
// Switch存档策略仅Switch平台
// ============================================================================
#ifdef __SWITCH__
class SwitchSaveStrategy : public ISaveStrategy {
public:
SwitchSaveStrategy();
~SwitchSaveStrategy();
bool mount(const std::string& mountName);
void unmount();
bool read(const std::string& path, std::vector<u8>& data) override;
bool write(const std::string& path, const std::vector<u8>& data) override;
bool commit() override;
bool valid() const override { return mounted_; }
private:
std::string mountName_;
bool mounted_ = false;
};
#endif
// ============================================================================
// 存档数据(内存表示)
// ============================================================================
struct SaveData {
std::unordered_map<std::string, std::unordered_map<std::string, std::string>> sections;
void set(const std::string& section, const std::string& key, const std::string& value);
std::string get(const std::string& section, const std::string& key, const std::string& defaultValue = "");
bool has(const std::string& section, const std::string& key);
void remove(const std::string& section, const std::string& key);
void clear();
// 序列化/反序列化
std::vector<u8> serialize() const;
bool deserialize(const std::vector<u8>& data);
};
// ============================================================================
// 存档管理器
// ============================================================================
class SaveStore {
public:
SaveStore();
~SaveStore();
// 设置存储策略
void setStrategy(std::unique_ptr<ISaveStrategy> strategy);
// 加载/保存
bool load(const std::string& path);
bool save(const std::string& path = "");
// 数据操作
void set(const std::string& section, const std::string& key, const std::string& value);
void setInt(const std::string& section, const std::string& key, int value);
void setFloat(const std::string& section, const std::string& key, float value);
void setBool(const std::string& section, const std::string& key, bool value);
std::string get(const std::string& section, const std::string& key, const std::string& defaultValue = "");
int getInt(const std::string& section, const std::string& key, int defaultValue = 0);
float getFloat(const std::string& section, const std::string& key, float defaultValue = 0.0f);
bool getBool(const std::string& section, const std::string& key, bool defaultValue = false);
bool has(const std::string& section, const std::string& key);
void remove(const std::string& section, const std::string& key);
void clear();
// 事务
void begin();
bool commit();
void rollback();
private:
SaveData data_;
SaveData backup_; // 事务备份
std::unique_ptr<ISaveStrategy> strategy_;
std::string path_;
bool inTransaction_ = false;
bool dirty_ = false;
};
} // namespace extra2d

File diff suppressed because it is too large Load Diff

View File

@ -1,446 +0,0 @@
#include <extra2d/services/logger_service.h>
#include <extra2d/utils/data.h>
#include <simpleini/SimpleIni.h>
// Switch 平台特定头文件
#ifdef __SWITCH__
#include <switch.h>
#include <switch/services/fs.h>
#endif
namespace extra2d {
class DataStore::Impl {
public:
CSimpleIniA ini;
};
DataStore::DataStore() : impl_(ptr::unique<Impl>()) {}
DataStore::~DataStore() {
// 如果在事务中,尝试提交
if (inTransaction_ && dirty_) {
commit();
}
// 如果存档已挂载,卸载
if (saveDataMounted_) {
unmountSaveData(mountName_);
}
}
// ============================================================================
// 文件操作
// ============================================================================
bool DataStore::load(const std::string &filename) {
filename_ = filename;
SI_Error rc = impl_->ini.LoadFile(filename.c_str());
dirty_ = false;
return rc >= 0;
}
bool DataStore::save(const std::string &filename) {
// 如果在事务中,只标记为脏,不实际写入
if (inTransaction_) {
dirty_ = true;
return true;
}
const std::string &targetFile = filename.empty() ? filename_ : filename;
if (targetFile.empty()) {
E2D_ERROR(CAT_APP, "DataStore::save: 没有指定文件名");
return false;
}
return internalSave(targetFile);
}
bool DataStore::internalSave(const std::string &filename) {
SI_Error rc = impl_->ini.SaveFile(filename.c_str());
if (rc < 0) {
E2D_ERROR(CAT_DATA, "DataStore::save: 保存文件失败: {}", filename);
return false;
}
dirty_ = false;
return true;
}
// ============================================================================
// Switch 存档系统支持
// ============================================================================
#ifdef __SWITCH__
bool DataStore::mountSaveData(SaveDataType type, const UserId &userId,
const std::string &mountName) {
// 如果已经挂载,先卸载
if (saveDataMounted_) {
unmountSaveData(mountName_);
}
Result rc = 0;
AccountUid uid = {userId.uid[0], userId.uid[1]};
// 如果没有提供用户ID尝试获取当前用户
if (type == SaveDataType::Account && !userId.isValid()) {
UserId currentUid = getCurrentUserId();
uid.uid[0] = currentUid.uid[0];
uid.uid[1] = currentUid.uid[1];
if (uid.uid[0] == 0 && uid.uid[1] == 0) {
E2D_ERROR(CAT_DATA, "DataStore::mountSaveData: 无法获取当前用户ID");
return false;
}
}
// 使用 fsdevMountSaveData 挂载
// 注意这里使用当前应用程序ID (0 表示当前应用)
u64 applicationId = 0;
rc = fsdevMountSaveData(mountName.c_str(), applicationId, uid);
if (R_FAILED(rc)) {
E2D_ERROR(CAT_DATA, "DataStore::mountSaveData: 挂载失败: 0x{:X}", rc);
return false;
}
mountName_ = mountName;
saveDataMounted_ = true;
defaultUserId_ = UserId{uid.uid[0], uid.uid[1]};
E2D_INFO(CAT_DATA, "DataStore::mountSaveData: 成功挂载存档: {}", mountName);
return true;
}
void DataStore::unmountSaveData(const std::string &mountName) {
if (!saveDataMounted_) {
return;
}
// 先提交更改
if (dirty_) {
commitSaveData(mountName_);
}
fsdevUnmountDevice(mountName.c_str());
saveDataMounted_ = false;
mountName_.clear();
E2D_INFO("DataStore::unmountSaveData: 已卸载存档");
}
bool DataStore::commitSaveData(const std::string &mountName) {
if (!saveDataMounted_) {
E2D_WARN(CAT_DATA, "DataStore::commitSaveData: 存档未挂载");
return false;
}
Result rc = fsdevCommitDevice(mountName.c_str());
if (R_FAILED(rc)) {
E2D_ERROR("DataStore::commitSaveData: 提交失败: 0x{:X}", rc);
return false;
}
E2D_DEBUG("DataStore::commitSaveData: 提交成功");
return true;
}
std::string DataStore::getSaveDataPath(const std::string &path) const {
if (!saveDataMounted_) {
return path;
}
return mountName_ + ":/" + path;
}
UserId DataStore::getCurrentUserId() {
UserId result;
Result rc = accountInitialize(AccountServiceType_Application);
if (R_FAILED(rc)) {
E2D_ERROR(CAT_DATA,
"DataStore::getCurrentUserId: accountInitialize 失败: 0x{:X}",
rc);
return result;
}
AccountUid uid;
rc = accountGetPreselectedUser(&uid);
accountExit();
if (R_SUCCEEDED(rc)) {
result.uid[0] = uid.uid[0];
result.uid[1] = uid.uid[1];
E2D_DEBUG("DataStore::getCurrentUserId: 获取成功: 0x{:X}{:X}",
result.uid[1], result.uid[0]);
} else {
E2D_ERROR(CAT_DATA, "DataStore::getCurrentUserId: 获取失败: 0x{:X}", rc);
}
return result;
}
#else
// 非 Switch 平台的存根实现
bool DataStore::mountSaveData(SaveDataType type, const UserId &userId,
const std::string &mountName) {
(void)type;
(void)userId;
(void)mountName;
E2D_WARN(CAT_DATA, "非 Switch 平台,存档功能不可用");
return false;
}
void DataStore::unmountSaveData(const std::string &mountName) {
(void)mountName;
saveDataMounted_ = false;
}
bool DataStore::commitSaveData(const std::string &mountName) {
(void)mountName;
return true;
}
std::string DataStore::getSaveDataPath(const std::string &path) const {
return path;
}
UserId DataStore::getCurrentUserId() { return UserId(); }
#endif
// ============================================================================
// 数据读写
// ============================================================================
std::string DataStore::getString(const std::string &section,
const std::string &key,
const std::string &defaultValue) {
const char *value =
impl_->ini.GetValue(section.c_str(), key.c_str(), defaultValue.c_str());
return value ? value : defaultValue;
}
int DataStore::getInt(const std::string &section, const std::string &key,
int defaultValue) {
return static_cast<int>(
impl_->ini.GetLongValue(section.c_str(), key.c_str(), defaultValue));
}
float DataStore::getFloat(const std::string &section, const std::string &key,
float defaultValue) {
const char *value =
impl_->ini.GetValue(section.c_str(), key.c_str(), nullptr);
if (value) {
try {
return std::stof(value);
} catch (...) {
return defaultValue;
}
}
return defaultValue;
}
bool DataStore::getBool(const std::string &section, const std::string &key,
bool defaultValue) {
return impl_->ini.GetBoolValue(section.c_str(), key.c_str(), defaultValue);
}
void DataStore::setString(const std::string &section, const std::string &key,
const std::string &value) {
impl_->ini.SetValue(section.c_str(), key.c_str(), value.c_str());
dirty_ = true;
// 不在事务中时自动保存
if (!inTransaction_ && !filename_.empty()) {
save("");
}
}
void DataStore::setInt(const std::string &section, const std::string &key,
int value) {
impl_->ini.SetLongValue(section.c_str(), key.c_str(), value);
dirty_ = true;
if (!inTransaction_ && !filename_.empty()) {
save("");
}
}
void DataStore::setFloat(const std::string &section, const std::string &key,
float value) {
impl_->ini.SetValue(section.c_str(), key.c_str(),
std::to_string(value).c_str());
dirty_ = true;
if (!inTransaction_ && !filename_.empty()) {
save("");
}
}
void DataStore::setBool(const std::string &section, const std::string &key,
bool value) {
impl_->ini.SetBoolValue(section.c_str(), key.c_str(), value);
dirty_ = true;
if (!inTransaction_ && !filename_.empty()) {
save("");
}
}
void DataStore::removeKey(const std::string &section, const std::string &key) {
impl_->ini.Delete(section.c_str(), key.c_str());
dirty_ = true;
if (!inTransaction_ && !filename_.empty()) {
save("");
}
}
void DataStore::removeSection(const std::string &section) {
impl_->ini.Delete(section.c_str(), nullptr);
dirty_ = true;
if (!inTransaction_ && !filename_.empty()) {
save("");
}
}
bool DataStore::hasKey(const std::string &section, const std::string &key) {
return impl_->ini.GetValue(section.c_str(), key.c_str(), nullptr) != nullptr;
}
bool DataStore::hasSection(const std::string &section) {
return impl_->ini.GetSection(section.c_str()) != nullptr;
}
void DataStore::clear() {
impl_->ini.Reset();
dirty_ = true;
if (!inTransaction_ && !filename_.empty()) {
save("");
}
}
// ============================================================================
// 事务支持
// ============================================================================
void DataStore::beginTransaction() {
if (inTransaction_) {
E2D_WARN(CAT_DATA, "DataStore::beginTransaction: 已经处于事务中");
return;
}
inTransaction_ = true;
dirty_ = false;
E2D_DEBUG(CAT_DATA, "DataStore::beginTransaction: 事务开始");
}
bool DataStore::commit() {
if (!inTransaction_) {
E2D_WARN(CAT_DATA, "DataStore::commit: 不在事务中");
return false;
}
// 如果有文件名,写入文件
bool result = true;
if (!filename_.empty() && dirty_) {
result = internalSave(filename_);
// 如果挂载了存档,提交更改
if (result && saveDataMounted_) {
result = commitSaveData(mountName_);
}
}
inTransaction_ = false;
E2D_DEBUG(CAT_DATA, "DataStore::commit: 事务提交 {}",
result ? "成功" : "失败");
return result;
}
void DataStore::rollback() {
if (!inTransaction_) {
E2D_WARN(CAT_DATA, "DataStore::rollback: 不在事务中");
return;
}
// 重新加载文件来恢复数据
if (!filename_.empty()) {
impl_->ini.Reset();
SI_Error rc = impl_->ini.LoadFile(filename_.c_str());
if (rc < 0) {
E2D_ERROR(CAT_DATA, "DataStore::rollback: 重新加载文件失败: {}",
filename_);
}
} else {
// 如果没有文件名,清空数据
impl_->ini.Reset();
}
inTransaction_ = false;
dirty_ = false;
E2D_DEBUG(CAT_DATA, "DataStore::rollback: 事务已回滚");
}
// ============================================================================
// 工具方法
// ============================================================================
std::vector<std::string> DataStore::getAllSections() const {
std::vector<std::string> sections;
CSimpleIniA::TNamesDepend sectionList;
impl_->ini.GetAllSections(sectionList);
for (const auto &section : sectionList) {
sections.emplace_back(section.pItem);
}
return sections;
}
std::vector<std::string>
DataStore::getAllKeys(const std::string &section) const {
std::vector<std::string> keys;
CSimpleIniA::TNamesDepend keyList;
impl_->ini.GetAllKeys(section.c_str(), keyList);
for (const auto &key : keyList) {
keys.emplace_back(key.pItem);
}
return keys;
}
bool DataStore::loadFromSave(const std::string &path) {
if (!saveDataMounted_) {
E2D_ERROR(CAT_DATA, "DataStore::loadFromSave: 存档未挂载");
return false;
}
std::string fullPath = getSaveDataPath(path);
return load(fullPath);
}
bool DataStore::saveToSave(const std::string &path) {
if (!saveDataMounted_) {
E2D_ERROR(CAT_DATA, "DataStore::saveToSave: 存档未挂载");
return false;
}
std::string fullPath = getSaveDataPath(path);
bool result = save(fullPath);
// 自动提交
if (result) {
result = commitSaveData(mountName_);
}
return result;
}
} // namespace extra2d

View File

@ -0,0 +1,459 @@
#include <extra2d/utils/save_store.h>
#include <extra2d/services/logger_service.h>
#include <cstring>
#include <fstream>
// Switch 平台特定头文件
#ifdef __SWITCH__
#include <switch.h>
#include <switch/services/fs.h>
#endif
namespace extra2d {
// ============================================================================
// 二进制序列化辅助函数
// ============================================================================
static void writeU32(std::vector<u8>& data, u32 value) {
data.push_back((value >> 0) & 0xFF);
data.push_back((value >> 8) & 0xFF);
data.push_back((value >> 16) & 0xFF);
data.push_back((value >> 24) & 0xFF);
}
static u32 readU32(const u8* data, size_t& offset) {
u32 value = data[offset] | (data[offset + 1] << 8) |
(data[offset + 2] << 16) | (data[offset + 3] << 24);
offset += 4;
return value;
}
static void writeString(std::vector<u8>& data, const std::string& str) {
writeU32(data, static_cast<u32>(str.size()));
data.insert(data.end(), str.begin(), str.end());
}
static std::string readString(const u8* data, size_t& offset) {
u32 len = readU32(data, offset);
std::string str(reinterpret_cast<const char*>(data + offset), len);
offset += len;
return str;
}
// ============================================================================
// FileSaveStrategy 实现
// ============================================================================
bool FileSaveStrategy::read(const std::string& path, std::vector<u8>& data) {
std::ifstream file(path, std::ios::binary | std::ios::ate);
if (!file.is_open()) {
return false;
}
std::streamsize size = file.tellg();
file.seekg(0, std::ios::beg);
data.resize(size);
if (!file.read(reinterpret_cast<char*>(data.data()), size)) {
return false;
}
return true;
}
bool FileSaveStrategy::write(const std::string& path, const std::vector<u8>& data) {
std::ofstream file(path, std::ios::binary);
if (!file.is_open()) {
return false;
}
return file.write(reinterpret_cast<const char*>(data.data()), data.size()).good();
}
// ============================================================================
// SwitchSaveStrategy 实现仅Switch平台
// ============================================================================
#ifdef __SWITCH__
SwitchSaveStrategy::SwitchSaveStrategy() = default;
SwitchSaveStrategy::~SwitchSaveStrategy() {
if (mounted_) {
unmount();
}
}
bool SwitchSaveStrategy::mount(const std::string& mountName) {
if (mounted_) {
unmount();
}
// 获取当前用户ID
AccountUid uid;
Result rc = accountInitialize(AccountServiceType_Application);
if (R_FAILED(rc)) {
E2D_ERROR(CAT_DATA, "SwitchSaveStrategy::mount: accountInitialize 失败: 0x{:X}", rc);
return false;
}
rc = accountGetPreselectedUser(&uid);
accountExit();
if (R_FAILED(rc)) {
E2D_ERROR(CAT_DATA, "SwitchSaveStrategy::mount: 获取用户ID失败: 0x{:X}", rc);
return false;
}
// 挂载存档
rc = fsdevMountSaveData(mountName.c_str(), 0, uid);
if (R_FAILED(rc)) {
E2D_ERROR(CAT_DATA, "SwitchSaveStrategy::mount: 挂载失败: 0x{:X}", rc);
return false;
}
mountName_ = mountName;
mounted_ = true;
E2D_INFO(CAT_DATA, "SwitchSaveStrategy::mount: 成功挂载存档: {}", mountName);
return true;
}
void SwitchSaveStrategy::unmount() {
if (!mounted_) {
return;
}
fsdevUnmountDevice(mountName_.c_str());
mounted_ = false;
mountName_.clear();
E2D_INFO(CAT_DATA, "SwitchSaveStrategy::unmount: 已卸载存档");
}
bool SwitchSaveStrategy::read(const std::string& path, std::vector<u8>& data) {
if (!mounted_) {
return false;
}
std::string fullPath = mountName_ + ":/" + path;
return FileSaveStrategy().read(fullPath, data);
}
bool SwitchSaveStrategy::write(const std::string& path, const std::vector<u8>& data) {
if (!mounted_) {
return false;
}
std::string fullPath = mountName_ + ":/" + path;
return FileSaveStrategy().write(fullPath, data);
}
bool SwitchSaveStrategy::commit() {
if (!mounted_) {
return false;
}
Result rc = fsdevCommitDevice(mountName_.c_str());
if (R_FAILED(rc)) {
E2D_ERROR(CAT_DATA, "SwitchSaveStrategy::commit: 提交失败: 0x{:X}", rc);
return false;
}
E2D_DEBUG(CAT_DATA, "SwitchSaveStrategy::commit: 提交成功");
return true;
}
#endif
// ============================================================================
// SaveData 实现
// ============================================================================
void SaveData::set(const std::string& section, const std::string& key, const std::string& value) {
sections[section][key] = value;
}
std::string SaveData::get(const std::string& section, const std::string& key, const std::string& defaultValue) {
auto secIt = sections.find(section);
if (secIt == sections.end()) {
return defaultValue;
}
auto keyIt = secIt->second.find(key);
if (keyIt == secIt->second.end()) {
return defaultValue;
}
return keyIt->second;
}
bool SaveData::has(const std::string& section, const std::string& key) {
auto secIt = sections.find(section);
if (secIt == sections.end()) {
return false;
}
return secIt->second.find(key) != secIt->second.end();
}
void SaveData::remove(const std::string& section, const std::string& key) {
auto secIt = sections.find(section);
if (secIt != sections.end()) {
secIt->second.erase(key);
if (secIt->second.empty()) {
sections.erase(secIt);
}
}
}
void SaveData::clear() {
sections.clear();
}
// 二进制格式:
// [魔数: 4字节 "E2SD"]
// [版本: 4字节]
// [section数量: 4字节]
// [section名长度: 4字节] [section名: N字节]
// [key-value对数量: 4字节]
// [key长度: 4字节] [key: N字节] [value长度: 4字节] [value: N字节]
static const char MAGIC[4] = {'E', '2', 'S', 'D'};
static const u32 VERSION = 1;
std::vector<u8> SaveData::serialize() const {
std::vector<u8> data;
// 魔数
data.insert(data.end(), MAGIC, MAGIC + 4);
// 版本
writeU32(data, VERSION);
// section数量
writeU32(data, static_cast<u32>(sections.size()));
// 写入每个section
for (const auto& [sectionName, keys] : sections) {
writeString(data, sectionName);
writeU32(data, static_cast<u32>(keys.size()));
for (const auto& [key, value] : keys) {
writeString(data, key);
writeString(data, value);
}
}
return data;
}
bool SaveData::deserialize(const std::vector<u8>& data) {
if (data.size() < 12) {
return false;
}
size_t offset = 0;
// 检查魔数
if (std::memcmp(data.data(), MAGIC, 4) != 0) {
return false;
}
offset += 4;
// 检查版本
u32 version = readU32(data.data(), offset);
if (version != VERSION) {
return false;
}
// 读取section数量
u32 sectionCount = readU32(data.data(), offset);
sections.clear();
for (u32 i = 0; i < sectionCount; ++i) {
std::string sectionName = readString(data.data(), offset);
u32 keyCount = readU32(data.data(), offset);
for (u32 j = 0; j < keyCount; ++j) {
std::string key = readString(data.data(), offset);
std::string value = readString(data.data(), offset);
sections[sectionName][key] = value;
}
}
return true;
}
// ============================================================================
// SaveStore 实现
// ============================================================================
SaveStore::SaveStore() = default;
SaveStore::~SaveStore() {
if (inTransaction_ && dirty_) {
commit();
}
}
void SaveStore::setStrategy(std::unique_ptr<ISaveStrategy> strategy) {
strategy_ = std::move(strategy);
}
bool SaveStore::load(const std::string& path) {
if (!strategy_ || !strategy_->valid()) {
E2D_ERROR(CAT_DATA, "SaveStore::load: 存储策略未设置或无效");
return false;
}
std::vector<u8> rawData;
if (!strategy_->read(path, rawData)) {
return false;
}
if (!data_.deserialize(rawData)) {
E2D_ERROR(CAT_DATA, "SaveStore::load: 反序列化失败");
return false;
}
path_ = path;
dirty_ = false;
return true;
}
bool SaveStore::save(const std::string& path) {
if (!strategy_ || !strategy_->valid()) {
E2D_ERROR(CAT_DATA, "SaveStore::save: 存储策略未设置或无效");
return false;
}
if (inTransaction_) {
dirty_ = true;
return true;
}
const std::string& targetPath = path.empty() ? path_ : path;
if (targetPath.empty()) {
E2D_ERROR(CAT_DATA, "SaveStore::save: 未指定路径");
return false;
}
std::vector<u8> rawData = data_.serialize();
if (!strategy_->write(targetPath, rawData)) {
E2D_ERROR(CAT_DATA, "SaveStore::save: 写入失败");
return false;
}
if (!strategy_->commit()) {
E2D_ERROR(CAT_DATA, "SaveStore::save: 提交失败");
return false;
}
dirty_ = false;
return true;
}
void SaveStore::set(const std::string& section, const std::string& key, const std::string& value) {
data_.set(section, key, value);
dirty_ = true;
}
void SaveStore::setInt(const std::string& section, const std::string& key, int value) {
set(section, key, std::to_string(value));
}
void SaveStore::setFloat(const std::string& section, const std::string& key, float value) {
set(section, key, std::to_string(value));
}
void SaveStore::setBool(const std::string& section, const std::string& key, bool value) {
set(section, key, value ? "1" : "0");
}
std::string SaveStore::get(const std::string& section, const std::string& key, const std::string& defaultValue) {
return data_.get(section, key, defaultValue);
}
int SaveStore::getInt(const std::string& section, const std::string& key, int defaultValue) {
std::string value = get(section, key);
if (value.empty()) {
return defaultValue;
}
try {
return std::stoi(value);
} catch (...) {
return defaultValue;
}
}
float SaveStore::getFloat(const std::string& section, const std::string& key, float defaultValue) {
std::string value = get(section, key);
if (value.empty()) {
return defaultValue;
}
try {
return std::stof(value);
} catch (...) {
return defaultValue;
}
}
bool SaveStore::getBool(const std::string& section, const std::string& key, bool defaultValue) {
std::string value = get(section, key);
if (value.empty()) {
return defaultValue;
}
return value == "1" || value == "true" || value == "yes";
}
bool SaveStore::has(const std::string& section, const std::string& key) {
return data_.has(section, key);
}
void SaveStore::remove(const std::string& section, const std::string& key) {
data_.remove(section, key);
dirty_ = true;
}
void SaveStore::clear() {
data_.clear();
dirty_ = true;
}
void SaveStore::begin() {
if (inTransaction_) {
E2D_WARN(CAT_DATA, "SaveStore::begin: 已经处于事务中");
return;
}
backup_ = data_;
inTransaction_ = true;
dirty_ = false;
}
bool SaveStore::commit() {
if (!inTransaction_) {
E2D_WARN(CAT_DATA, "SaveStore::commit: 不在事务中");
return false;
}
bool result = true;
if (dirty_ && !path_.empty()) {
result = save(path_);
}
inTransaction_ = false;
backup_.clear();
return result;
}
void SaveStore::rollback() {
if (!inTransaction_) {
E2D_WARN(CAT_DATA, "SaveStore::rollback: 不在事务中");
return;
}
data_ = backup_;
inTransaction_ = false;
dirty_ = false;
}
} // namespace extra2d