《女神异闻录3 Reload》(Persona 3 Reload) Steam 版存档修改器。
纯离线文件读写,自带加解密,无需安装任何 MOD、不注入游戏进程。支持修改人格面具(Persona)的技能、等级、累计经验、五维,以及按数值 ID 直接改任意数据(金钱等)。
逆向成果与工具均为原创整理,已在实机存档上验证通过(Steam 版,UE4.27 GVAS)。
方式一:直接下载压缩包(推荐给只想用的人)
到 Releases 页面下载 P3R-Save-Editor-v1.1.zip,解压即用,里面已经包含全部脚本和使用说明。
方式二:克隆仓库(推荐给想改代码的人)
git clone https://github.com/anyiim/P3R-Save-Editor.git两种方式得到的内容一样,脚本只依赖 Python 标准库,不需要 pip install 任何东西。
| 功能 | 说明 |
|---|---|
| 列出 Persona 槽位 | 只读查看存档里全部人格的编号、等级、经验、五维、技能 |
| 修改 Persona 技能 | 最多 8 个技能,自动补/删技能槽 |
| 修改 Persona 等级 | 与累计经验配套写入 |
| 修改 Persona 累计经验 | 避免被游戏按经验反算回等级 |
| 修改 Persona 五维 | 力 / 魔 / 耐 / 速 / 运,每项 0~255 |
| 按 ID 改任意数值 | 金钱、日期、社群等级等,只需知道数值 ID |
| 完整性校验 | 对比改档前后,确认只有目标记录被改动 |
| 加解密 | 可单独导出解密后的存档,便于自行分析 |
- Python 3.8+(仅用标准库:
struct/argparse等) - Windows(存档路径默认在
%APPDATA%下)
非 Windows 系统理论上也能跑(脚本本身跨平台),但存档路径需要自己指定。
%APPDATA%\SEGA\P3R\Steam\<SteamID>\SaveData00X.sav
例如:
C:\Users\<你的用户名>\AppData\Roaming\SEGA\P3R\Steam\76561197960285355\SaveData004.sav
这一步不能省。 游戏运行时存档数据在内存里,你改完文件它一保存就会覆盖掉你的修改;更糟的情况是直接写坏存档。
cd C:\Users\<你的用户名>\AppData\Roaming\SEGA\P3R\Steam\<SteamID>
copy SaveData004.sav SaveData004.sav.bak脚本不会改你的原档,而是读原档、输出一个新文件:
python p3r_editor.py SaveData004.sav 新档.sav --persona-code 0134 --skills 117,792,793,875,897,834,885,856 --dumppython verify.py SaveData004.sav 新档.sav输出会列出被改动的数值 ID,并确认其余记录一动没动:
对比: SaveData004.sav -> 新档.sav
[OK] 加密往返自检 (encrypt(decrypt(x)) == x)
原始: 375720 字节 -> 修改后: 375720 字节 (差 +0)
[OK] 记录数: 7616 -> 7616 (+0)
删除的 ID (0 条): []
新增的 ID (0 条): []
数值变化的 ID (1 条): ['7257 (90244->9999999)']
[OK] 其他 7615 条记录数值未被触碰
[OK] 原始: 记录间距=[49] ID升序=True 终止符偏移=0x5bb9b
[OK] 修改后: 记录间距=[49] ID升序=True 终止符偏移=0x5bb9b
[结果] Persona 0134 (槽0, base=13090)
技能 : [117, 792, 793, 875, 897, 834, 885, 856]
原技能 : [117, 792, 793, 875, 897, 834, 885, 856]
等级 : 99 exp: 2000000
想让脚本额外复核某个指定人格,加 --persona-code;不加则自动复核第一个非空槽位。
把 新档.sav 复制回存档目录,改名成 SaveData004.sav 覆盖原文件即可。
下次启动如果 Steam 提示「云存档冲突」,选本地文件,否则云端旧档会把你的修改冲掉。
python p3r_editor.py <输入.sav> [输出.sav] [选项]
输出文件可省略 —— 配合 --list-persona 就能只读地查看存档,不写任何文件。
| 选项 | 说明 |
|---|---|
--list-persona |
只读,列出存档中全部 Persona 槽位(编号 / 等级 / 经验 / 五维),无需输出文件 |
--persona-code |
Persona 编号:完整编号如 0134(推荐),或旧简写如 3401 |
--skills |
最多 8 个技能 ID,逗号分隔;不足会自动补空槽,超出会报错 |
--level |
等级(需配合 --persona-code) |
--exp |
累计总经验(详见下方「关于累计经验」) |
--stats |
五维:力,魔,耐,速,运,每项 0~255 |
--set |
直接指定 数值ID=值,可重复使用 |
--plain |
强制输出未加密的 GVAS(默认跟随输入格式,见下) |
--encrypt |
强制输出加密存档 |
--dump |
写出后重新读回并打印结果 |
输出格式默认跟随输入:喂加密的
.sav进去就得到加密的存档;喂解密后的明文 GVAS 就得到明文。 需要强制指定时用--plain/--encrypt。
不确定编号时,先只读地列一遍,这个操作不会修改任何文件:
python p3r_editor.py SaveData004.sav --list-persona槽位 Persona 等级 累计经验 力/魔/耐/速/运
0 0134 99 2000000 99/99/99/99/99
技能: 117(空间杀法), 792(斩击强化), 793(高级斩击强化), ...
1 0040 3 136 3/4/3/4/3
技能: 155, 10
① 换成物理斩击流配装(8 个技能)
python p3r_editor.py 输入.sav 输出.sav --persona-code 0134 \
--skills 117,792,793,875,897,834,885,856② 等级 + 经验一起拉满
python p3r_editor.py 输入.sav 输出.sav --persona-code 0134 \
--level 99 --exp 2000000③ 五维拉满
python p3r_editor.py 输入.sav 输出.sav --persona-code 0134 \
--stats 99,99,99,99,99④ 改金钱(7257 = 金钱)
python p3r_editor.py 输入.sav 输出.sav --set 7257=9999999⑤ 组合使用
python p3r_editor.py 输入.sav 输出.sav \
--persona-code 0134 \
--skills 117,792,793,875,897,834,885,856 \
--level 99 --exp 2000000 \
--stats 99,99,99,99,99 \
--set 7257=9999999 \
--dump| 脚本 | 用法 |
|---|---|
verify.py |
python verify.py 原档.sav 新档.sav —— 改档前后完整性对比 |
p3r_crypt.py |
python p3r_crypt.py decrypt 存档.sav 输出.raw —— 单独解密存档,便于自行分析结构;info 子命令可只看格式与大小 |
槽位里存的数值是 (编号 << 16) | 0x0001 —— 也就是高 16 位才是完整编号,低 16 位是固定的 0x0001 标记。
| Persona | 完整编号 | 旧简写 |
|---|---|---|
| 灰姑娘 (Cinderella) | 0134 |
3401 |
推荐用完整编号(--persona-code 0134)。
旧的四位简写 3401 仍然能用,但它只保留了编号的低字节,所以 0040 和 0140 都会写成 4001 而撞车。
遇到这种情况脚本不会猜,而是直接报错并列出候选槽位,让你改用完整编号:
编号 4001 同时匹配到 2 个槽位, 无法确定改哪一个:
槽1 (base=13102) 完整编号 0040
槽3 (base=13126) 完整编号 0140
请改用完整编号, 例如: --persona-code 0040
其他 Persona 的编号不用查表 —— 用
--list-persona直接从存档里读出来,这是最可靠的方式。
| 技能(中文) | 英文名 | ID |
|---|---|---|
| 空间杀法 | Vorpal Blade | 117 |
| 斩击强化 | Slash Boost | 792 |
| 高级斩击强化 | High Slash Boost | 793 |
| 终极斩击强化 | Ultimate Slash Boost | 875 |
| 全体攻击强化 | Multi-Target Boost | 897 |
| 建言(暴击率 ×2) | Apt Pupil | 834 |
| 暴击大UP | Critical Rate Amp | 885 |
| 武道的资质(HP 消耗减半) | Arms Master | 856 |
| 技能 / 项目 | ID |
|---|---|
| 神经碎断 | 121 |
| 玛哈克加 | 60 |
| 金钱 | 7257 |
完整技能表可查 Persona 3 Reload 官方中文 Wiki 的「技能列表」,编号与英文技能数据表一致。 以上 ID 均已通过官方中文 Wiki 与实机存档双向验证(例如存档中原本带有的「终极斩击强化」实测就是 875)。
以下是从实战中逆向出来的完整格式,供想自行扩展功能的人参考。
Steam 版存档是加密的,但算法很简单,是 XOR + 半字节交换:
密钥:ae5zeitaix1joowooNgie3fahP5Ohph (32 字节,循环使用)
解密:
b = ciphertext ^ key
plain = ((b >> 4) & 0x03) | ((b & 0x03) << 4) | (b & 0xCC)
加密:把上面的步骤反过来
- 加密文件的魔数:
0x0B650015 - 解密后的魔数:
GVAS
解密后是标准 Unreal Engine 4.27 SaveGame:
- 存档类名:
/Script/xrd777.XRD777SaveGame - 游戏数据被拍平成一串名为
SaveDataArea的UInt32Property - 单条记录固定 49 字节:
| 偏移 | 长度 | 内容 |
|---|---|---|
| +0 | 17 | SaveDataArea\0(属性名) |
| +17 | 19 | UInt32Property\0(类型名) |
| +36 | 4 | 04 00 00 00(固定) |
| +40 | 4 | 数值 ID(小端 u32) |
| +44 | 1 | 00 |
| +45 | 4 | 值(小端 u32) |
- 记录按数值 ID 升序排列
value == 0的条目不写入- 文件末尾终止符:
05 00 00 00 "None\0"
base 序列为 13090 + 12 * k(k = 0…6,共 7 个槽位;空槽的 value 为 0,不会出现在文件里):
| 偏移 | 含义 |
|---|---|
| +0 | Persona 编号:(编号 << 16) | 0x0001,高 16 位是完整编号,低 16 位固定 0x0001 |
| +1 | 等级 |
| +2 | 累计总经验 |
| +3 … +6 | 技能(4 个 u32) |
| +7 | 力 / 魔 / 耐 / 速(4 个 u8 打包) |
| +8 | 运 |
槽位顺序 = 队伍里的排列顺序,会随游戏操作变化,所以别按槽位号记,认编号。
13090 + 12 * k之外也有长得很像的位置(例如base + 12 * 12),但那些是别的数据结构 —— 按固定区间扫这 7 个槽位才可靠,全局扫描会产生大量误报。
技能打包规则:每个 u32 里装 2 个技能,高 16 位在前,低 16 位在后。所以 4 个字段最多容纳 8 个技能。
存档里的 exp 存的是从 Lv1 起的累计总经验,不是当前等级的进度条。
证据是从多个存档里采样到的 (等级, 经验) 数据点:
(2, 67) (3, 136) (6, 912) (34, 135042)
(36, 159737) (50, 376259) (89, 1390631)
这些点严格随等级单调上升、横跨三个数量级——如果是「当前级进度值」,不同 Persona 不可能排得这么整齐。
所以等级和 exp 必须一起改。 只改等级,游戏下次结算经验时会按 exp 把你算回原来的等级。
Lv99 需要的累计经验用幂律模型外推约 1.82M ~ 1.84M,写 2000000 有约 9% 余量。
Q:会不会被封号? 不会。这是纯离线文件修改,不修改游戏本体、不联网、不涉及任何多人要素。和市面上其他 P3R 存档编辑器原理相同。当然,使用风险请自行承担。
Q:改完进游戏发现没变化? 三个常见原因:
- 游戏/Steam 在改档时没关,存档被覆盖了 —— 重来一次,先关游戏。
- 写回时文件名不对(必须是
SaveData004.sav)。 - Steam 云同步用云端旧档覆盖了本地 —— 冲突时选「本地文件」。
Q:五维改成 99 之后还能升级吗? 能升级,但属性不会再涨(已经是上限)。如果想要正常成长曲线,建议改成 40~60。
Q:改完存档损坏了 / 想反悔?
用第 3 步备份的 .bak 文件复制回去覆盖即可。所以备份真的很重要。
Q:技能 ID 写进去不生效? 确认 ID 是否正确。部分技能在存档里的编号与 Wiki 展示的编号存在换算关系,遇到不生效的情况可以对照存档中已有技能的数值反推。
Q:我不知道要改的 Persona 编号是多少?
跑 python p3r_editor.py 存档.sav --list-persona,它会只读地列出全部槽位的完整编号、等级、经验、五维和技能。这比查表可靠,因为它读的就是你这份存档。
Q:为什么编号 4001 会报「匹配到多个槽位」?
旧的四位写法只保留了编号的低字节,0040 和 0140 简化后都是 4001,无法区分。报错信息会列出每个候选的完整编号,挑你要的那个写进去即可。
Q:能直接写 --level 0 把等级清零吗?
游戏本身就不保存值为 0 的记录,所以写 0 等于删除那条记录。脚本会明确提示「值为 0: 已按游戏规则删除该条目」,不会静默处理。
Q:支持 Xbox Game Pass / PS 版吗? 不支持。目前只针对 Steam 版的加密格式,其他平台的存档结构不同。
P3R-Save-Editor/
├── README.md 本文档
├── p3r_editor.py 主工具(加解密 + 定位 + 修改)
├── verify.py 改档前后完整性对比
├── p3r_crypt.py 独立加解密工具
└── dist/
└── P3R-Save-Editor-v1.1.zip 打包好的压缩包(解压即用)
- 修复 Persona 定位可能撞车:编号的完整值在槽位的高 16 位,旧的四位简写只保留了低字节,
0040与0140简化后都是4001。现在优先按完整编号匹配,命中多个候选时明确报错并列出, 不再"猜"一个改掉。 - 新增
--list-persona:只读列出存档中全部人格的编号 / 等级 / 经验 / 五维 / 技能。 - 数值越界不再静默溢出:五维超过 255、技能 ID 超过 65535 现在直接报错 (此前会溢出并污染相邻字段,例如写「力=300」会读回 44)。
- 输出格式跟随输入:喂明文 GVAS 进去不再被擅自加密;需要强制指定时用
--plain/--encrypt。 - 写入 0 的行为明确提示:游戏不保存值为 0 的记录,因此写 0 等价于删除该条目,现在会打印提示。
verify.py不再硬编码人格编号,且可在任意目录调用(此前依赖当前工作目录)。p3r_crypt.py的输入校验由assert改为显式报错,避免python -O下被跳过。
初始版本。
本项目仅供个人学习与存档备份用途。请勿用于商业传播。
使用本工具修改存档造成的任何后果(包括但不限于存档损坏、游戏体验异常、账号风险)由使用者自行承担。修改前请务必备份原档。
本项目与 ATLUS / SEGA 无任何关联,《女神异闻录》系列相关商标与版权归其各自所有者所有。