refactor(utils): 重构数据存储模块为新的存档系统
移除旧的 DataStore 类及相关文件,替换为新的 SaveStore 实现。新实现采用二进制序列化格式,提供更高效的存储策略接口,支持文件系统和 Switch 平台存档系统。改进事务处理机制和类型安全的数据访问方法。
This commit is contained in:
parent
137369c37c
commit
8e9fe8719f
|
|
@ -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 用户ID(Account 类型需要)
|
|
||||||
* @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 用户ID(无效时返回空ID)
|
|
||||||
*/
|
|
||||||
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 §ion, const std::string &key,
|
|
||||||
const std::string &defaultValue = "");
|
|
||||||
|
|
||||||
/// 获取整数值
|
|
||||||
int getInt(const std::string §ion, const std::string &key,
|
|
||||||
int defaultValue = 0);
|
|
||||||
|
|
||||||
/// 获取浮点数值
|
|
||||||
float getFloat(const std::string §ion, const std::string &key,
|
|
||||||
float defaultValue = 0.0f);
|
|
||||||
|
|
||||||
/// 获取布尔值
|
|
||||||
bool getBool(const std::string §ion, const std::string &key,
|
|
||||||
bool defaultValue = false);
|
|
||||||
|
|
||||||
/// 设置字符串值
|
|
||||||
void setString(const std::string §ion, const std::string &key,
|
|
||||||
const std::string &value);
|
|
||||||
|
|
||||||
/// 设置整数值
|
|
||||||
void setInt(const std::string §ion, const std::string &key, int value);
|
|
||||||
|
|
||||||
/// 设置浮点数值
|
|
||||||
void setFloat(const std::string §ion, const std::string &key,
|
|
||||||
float value);
|
|
||||||
|
|
||||||
/// 设置布尔值
|
|
||||||
void setBool(const std::string §ion, const std::string &key, bool value);
|
|
||||||
|
|
||||||
/// 删除键
|
|
||||||
void removeKey(const std::string §ion, const std::string &key);
|
|
||||||
|
|
||||||
/// 删除整个 section
|
|
||||||
void removeSection(const std::string §ion);
|
|
||||||
|
|
||||||
/// 检查键是否存在
|
|
||||||
bool hasKey(const std::string §ion, const std::string &key);
|
|
||||||
|
|
||||||
/// 检查 section 是否存在
|
|
||||||
bool hasSection(const std::string §ion);
|
|
||||||
|
|
||||||
/// 清除所有数据
|
|
||||||
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 §ion) 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
|
|
||||||
|
|
@ -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
|
|
@ -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 §ion,
|
|
||||||
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 §ion, 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 §ion, 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 §ion, const std::string &key,
|
|
||||||
bool defaultValue) {
|
|
||||||
return impl_->ini.GetBoolValue(section.c_str(), key.c_str(), defaultValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DataStore::setString(const std::string §ion, 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 §ion, 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 §ion, 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 §ion, 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 §ion, 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 §ion) {
|
|
||||||
impl_->ini.Delete(section.c_str(), nullptr);
|
|
||||||
dirty_ = true;
|
|
||||||
|
|
||||||
if (!inTransaction_ && !filename_.empty()) {
|
|
||||||
save("");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DataStore::hasKey(const std::string §ion, const std::string &key) {
|
|
||||||
return impl_->ini.GetValue(section.c_str(), key.c_str(), nullptr) != nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool DataStore::hasSection(const std::string §ion) {
|
|
||||||
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 §ion : sectionList) {
|
|
||||||
sections.emplace_back(section.pItem);
|
|
||||||
}
|
|
||||||
|
|
||||||
return sections;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<std::string>
|
|
||||||
DataStore::getAllKeys(const std::string §ion) 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
|
|
||||||
|
|
@ -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
|
||||||
Loading…
Reference in New Issue