存档文件通常是二进制格式;少数为json或xml但常加密或混淆,需用十六进制编辑器查看前几十字节判断:若开头为{或[且字段名可读,则可能是json。

存档文件是二进制还是 JSON?先看清楚再动手
绝大多数单机游戏存档不是标准文本,直接用 File.ReadAllText 读出来一堆乱码,说明它大概率是二进制格式。少数新游戏(比如《Stardew Valley》后期版本、《RimWorld》部分存档)会用 XML 或 JSON,但通常带加密或混淆——别急着写解析逻辑,先用十六进制编辑器(如 HxD、010 Editor)打开一个存档文件,观察前几十字节:
- 开头是
{或[,且能大致看清字段名 → 可能是 JSON,试试JsonSerializer.Deserialize - 开头是
<?xml或<savegame> → XML,用 <code>XDocument.Load - 开头像
PK\x03\x04→ ZIP 压缩包,解压后再看内部文件 - 全是不可读字符,长度不规则 → 大概率是自定义二进制结构,需要逆向或查社区文档
用 BinaryReader 解析二进制存档时字节序和对齐很关键
很多游戏(尤其是 Unity 引擎打包的)在 Windows 上用小端序(BitConverter.IsLittleEndian == true),但存档可能被其他平台生成,或开发者手动调用了 BigEndianBitConverter 类。更麻烦的是结构体对齐:C# 的 [StructLayout(LayoutKind.Sequential, Pack = 1)] 能强制按字节对齐,但如果你漏了 Pack = 1,BinaryReader.ReadSingle() 后面紧跟一个 int,实际读出来的值可能错位。
- 务必确认目标游戏的官方引擎/工具链文档里是否注明字节序(常见于 Unity 的
BinaryFormatter替代方案说明) - 用已知数值反推:比如你记得角色等级是 42,用十六进制编辑器找到附近字节,看它是
2A 00 00 00(小端)还是00 00 00 2A(大端) - 读结构体前先读长度头(常见
int表示后续数据块长度),避免越界读取导致整个解析崩掉
Unity 游戏存档常藏在 PlayerPrefs 或 LiteDB 里,别只盯着 .sav 文件
很多 Unity 游戏根本不用独立存档文件,而是把关键数据塞进注册表(Windows)、NSUserDefaults(macOS/iOS)或本地 SQLite 数据库。桌面版常见路径:%APPDATA%\prefs(其实是二进制 plist 或自定义格式),或者 %LOCALAPPDATA%\SavedGames\ 下的 .db 文件(可能是 LiteDB、SQLite 或加密的二进制 blob)。
- 先查进程内存:用 Cheat Engine 搜已知数值(如金币数),再看它存在哪块内存区域,导出后比对文件内容
- 运行游戏时用 Process Monitor 监控文件读写,过滤
ReadFile操作,重点看非 .sav 扩展名的文件 - LiteDB 的魔数是
LDB\x00,SQLite 是SQLite format 3\x00,用十六进制编辑器一眼可识别
解析失败时优先检查 CRC 校验和版本号字段
几乎所有商业游戏存档都会在头部放校验值(CRC32、Adler32 或简单异或)和版本号(int 或字符串)。你读出来的数据看似结构正确,但角色血量总是 0 或负数,大概率是版本不匹配导致字段偏移错乱,或者校验失败后游戏主动填了默认值(而你没跳过校验直接解析)。
- 先定位头部固定位置的版本字段(比如偏移 0x04 处的
int),对比你手头的文档或社区贴出的版本号是否一致 - CRC 字段常在末尾,长度 4 或 8 字节;如果校验失败,别硬解析,先确认你用的密钥或初始值是否正确(有些游戏用固定 seed 如 0xDEADBEEF)
- 某些游戏(如《Terraria》)存档里有明文标识符如
Terraria Save File V2,但后面紧跟着加密块 —— 别被明文骗了,得先解密再解析
真正麻烦的从来不是读取本身,而是搞清那个“看似随机”的 4 字节到底代表版本、校验、还是加密 salt —— 这部分没文档时,只能靠多组存档做差分分析。











