Sudachi存档编辑器:从零搭建SaveData修改工具

发布时间:2026/10/24 8:05:29
Sudachi存档编辑器:从零搭建SaveData修改工具
Sudachi存档编辑器从零搭建SaveData修改工具【免费下载链接】sudachiSudachi is a Nintendo Switch emulator for Android, Linux, macOS and Windows, written in C项目地址: https://gitcode.com/GitHub_Trending/suda/sudachiSudachi 是一款用 C 编写的跨平台 Nintendo Switch 模拟器它通过SaveDataFactory统一管理游戏存档的存放与访问。如果你遇到过换了账号就找不到存档或存档坏了打不开的情况问题大概率出在路径映射和大小元数据上。这篇文章带你顺着源码把存档编辑器从零搭起来。一条存档路径是怎么拼出来的Sudachi 的存档不放在扁平结构里而是按空间 → 类型 → 标识三级目录组织。顶层由SaveDataSpaceId存档所在空间决定System 对应/system/User 对应/user/Temporary 对应/temp/。平时要动的用户存档都在/user/save/下。关键在于user_id参数它是一个 128 位 UUID可以理解为由两个 64 位整数组成的 128 位大整数代表玩家唯一身份。生成路径时拆成user_id[1]和user_id[0]各自格式化为 16 位十六进制字符串再拼起来。GetFullPath位于 savedata_factory.cpp的核心分支按存档类型决定最终路径// src/core/file_sys/savedata_factory.cpp 中 GetFullPath 的核心分支 switch (type) { case SaveDataType::System: return fmt::format({}save/{:016X}/{:016X}{:016X}, out, save_id, user_id[1], user_id[0]); case SaveDataType::Account: case SaveDataType::Device: return fmt::format({}save/{:016X}/{:016X}{:016X}/{:016X}, out, 0, user_id[1], user_id[0], title_id); case SaveDataType::Temporary: return fmt::format({}{:016X}/{:016X}{:016X}/{:016X}, out, 0, user_id[1], user_id[0], title_id); case SaveDataType::Cache: return fmt::format({}save/cache/{:016X}, out, title_id); }从这段代码能看出用户存档的典型形态/user/save/0/32位user_id/16位title_id/。工具里定位某个玩家的存档根目录直接用GetUserGameSaveDataRoot的逻辑即可0前缀 拼接后的 user_id 就是根路径再往下按 title_id 找具体游戏。五种存档类型与自动创建规则SaveDataType区分存档归属的枚举共 5 个成员路径特征和权限各不相同System系统级存档路径里带save_id和 user_id需系统权限Account玩家维度存档路径含 32 位 user_id 和 title_idDevice设备共享存档路径只有 title_idTemporary会话期临时存档注意它的 switch 分支里没有save/这一段Cache缓存存档统一放在save/cache/下自动创建的判断在ShouldSaveDataBeAutomaticallyCreated里Cache 和 Temporary 无条件自动创建Account 和 Device 只在program_id 0且system_save_data_id 0时才会自动建目录。做修改工具时记住这条规则不要替系统存档补建目录那是受保护的数据。解析 .sudachi_save_size 大小文件每个存档目录里有个隐藏文件.sudachi_save_size由GetSaveDataSizeFileName()定义并非某些资料里写的save_size.dat。结构定义见 fs_save_data_types.h// 结构共 0x10 字节normal 普通区大小journal 日志区大小 struct SaveDataSize { u64 normal; u64 journal; }; // ReadSaveDataSize 的核心文件不存在或不足 16 字节时返回 {0, 0} if (size_file-ReadObject(out) ! sizeof(SaveDataSize)) { return {0, 0}; }ReadObject是 vfs.h 里的模板接口把 16 个字节直接拷进平凡可拷贝的结构体。所以解析这个文件就是读 16 字节、拆成两个小端 64 位整数文件缺失或短于 16 字节代表没有大小记录工具要走{0, 0}分支而不是崩溃。改之前先备份大小文件动存档目录里任何字节前先把大小文件复制一份。VFS 层提供现成的CopyFile// 用 VfsFilesystem::CopyFile 把大小文件备份一份防止改坏 journal 字段 vfs-CopyFile(save_dir /.sudachi_save_size, save_dir /.sudachi_save_size.bak);写回时照WriteSaveDataSize的组合拳CreateFileResize(sizeof(SaveDataSize))WriteObject顺序不能乱。可视化界面选型Qt 还是 PythonSudachi 的桌面前端本身就基于 Qt5/Qt6见src/sudachi/CMakeLists.txt的构建脚本所以做跨平台编辑器选 Qt 阻力最小QTreeView 做目录导航QHexEdit 做十六进制编辑再挂一个小面板做 u64/字符串/十六进制互转。如果更熟悉 Python用 PyQt 加pathlib遍历目录也行核心仍是上面那套路径拼接逻辑界面只是壳。四个兼容性与安全上的坑新旧两套路径并存新版 Sudachi 支持 future 格式GetFutureSaveDataPath用户存档会放在/user/save/account/uuid/title_id/0。GetFullPath会优先检测新位置、不存在才回退旧路径你的扫描逻辑同样要两种格式都试。title_id 为 0 是占位Account/Device 类型下title_id 0表示当前进程的 title ID工具拼路径前必须用真实 program ID 替换。temp 目录每次启动会被清空SaveDataFactory构造时直接调用DeleteSubdirectoryRecursive(temp)别把临时存档当持久修改目标。别碰系统存档空间在/system/下路径规则也不同误写可能直接破坏模拟器状态。测试验证从路径单测到启动回归先给路径生成写单元测试把 5 种类型 × 新旧空间格式作为输入组合用EXPECT_EQ断言生成的字符串测试文件可放进src/tests/现有结构。然后做集成测试准备一份从模拟器导出的真实存档目录验证工具能正确读出.sudachi_save_size修改若干字节后写回再重读最后启动游戏确认存档可正常加载。下一步先实现GetFullPath与GetUserGameSaveDataRoot的双格式逻辑把路径单测跑绿接上 QTreeView QHexEdit打通浏览 → 定位 → 编辑 → 写回主流程给大小文件加自动.bak备份并对/system/下目录禁写需要处理加密卷存档时再顺着src/core/file_sys/system_archive/的 NCA/XTS 相关代码扩展解密能力。【免费下载链接】sudachiSudachi is a Nintendo Switch emulator for Android, Linux, macOS and Windows, written in C项目地址: https://gitcode.com/GitHub_Trending/suda/sudachi创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考