gewenwu716/palworld-save-parser
GitHub: gewenwu716/palworld-save-parser
Palworld 存档解析器:用 Rust/WASM 在浏览器端提取游戏存档数据,并配合 TypeScript 引擎计算最优繁育路径。
Stars: 0 | Forks: 0
# PalForge 存档解析器
搭配一个 TypeScript 繁育引擎,可使用 BFS 寻路算法根据您实际的 Palbox 库存计算最优的多代繁育树。
## 此仓库包含的内容
```
├── parser/ # Rust save parser (core)
│ └── src/
│ ├── lib.rs # WASM exports + library entry
│ ├── main.rs # CLI entry point
│ ├── palsav.rs # SAV decompression (PlZ → zlib → GVAS)
│ ├── gvas.rs # UE5 GVAS header + property tree parser
│ ├── farchive.rs # FArchive binary reader (UE serialization)
│ ├── character.rs # Character data extraction from property tree
│ ├── types.rs # Shared data types (FGuid, PalEntry, etc.)
│ └── json_output.rs # Manual JSON serializer (no serde)
│
├── js/ # TypeScript packages
│ └── src/
│ ├── breeding-engine.ts # BFS pathfinding + passive convergence
│ ├── wasm-bridge.ts # Browser-side WASM integration helper
│ ├── sample-data.ts # 15-Pal sample dataset for testing
│ └── types.ts # Shared TypeScript types
│
├── data/ # Game data documentation
│ └── README.md # How to extract full dataset
│
└── .github/workflows/ # CI (Rust + JS tests)
```
## 快速开始
### CLI — 解析存档文件
```
cd parser
cargo build --release
./target/release/palworld-save-parser /path/to/Level.sav > pals.json
```
示例输出:
```
{
"total_characters": 52,
"pal_count": 48,
"player_count": 1,
"save_class": "/Script/Pal.PalWorldSaveGame",
"engine_version": "5.1.0-0 (++UE5+Release-5.1)",
"pals": [
{
"character_id": "Anubis",
"nickname": "",
"gender": "Male",
"level": 35,
"is_player": false,
"passive_skills": ["Artisan", "Work Slave", "Serious"]
}
],
"players": [...],
"warnings": []
}
```
### WASM — 在浏览器中解析
```
# 构建 WASM target
cd parser
cargo build --target wasm32-unknown-unknown --release
# 输出: target/wasm32-unknown-unknown/release/palworld_save_parser.wasm
```
然后在您的 Web 应用中:
```
import { initParser, parseSaveFile } from 'palforge-breeding-engine/wasm-bridge'
await initParser('/wasm/palworld_save_parser.wasm')
const result = await parseSaveFile(file) // File from
console.log(result.pals) // Array of extracted Pal entries
```
### JS — 计算繁育路径
```
import { createBreedingEngine, SAMPLE_DATA } from 'palforge-breeding-engine'
const engine = createBreedingEngine(SAMPLE_DATA)
// Find the shortest breeding path from your Pals to Anubis
const path = engine.findBreedingPath(
['Mozzarina', 'Chikipi', 'Lamball', 'Tombat'],
'Anubis'
)
console.log(`${path.steps.length} generations needed`)
for (const step of path.steps) {
console.log(`${step.parentA.name} + ${step.parentB.name} → ${step.child.name}`)
}
```
## 工作原理
### 存档格式
Palworld 存档是一个三层结构:
1. **PlZ 容器** — 自定义的 Pocketpair 包装器。在偏移量 8 处具有魔数 `PlZ`(如果存在 `CNK` 头部,则在偏移量 20 处)。支持单次压缩 (0x31) 和双次压缩 (0x32) zlib 压缩。
2. **GVAS payload** — Unreal Engine 5 的标准存档游戏格式。魔数 `0x53415647`。头部包含引擎版本、自定义版本的 GUID 以及存档游戏类名 (`/Script/Pal.PalWorldSaveGame`)。
3. **FArchive 属性树** — UE 的二进制序列化格式。类型化属性(StructProperty、MapProperty、ArrayProperty、FloatProperty 等)的递归树。角色数据存储在 `worldSaveData → CharacterSaveParameterMap → MapProperty entries` 中。
### 繁育算法
繁育引擎使用 BFS(广度优先搜索)和动态闭包:
1. 从玩家可用的 Pal 物种开始
2. 对于每一代,从当前可用的集合中计算所有可能的繁育组合
3. 新的后代被添加到可用集合中,形成反馈循环
4. 当达到目标或超过代数上限(默认为 5)时,搜索终止
5. 如果不存在纯库存路径,则会运行贪婪的捕获成本松弛阶段
### 架构决策
- **不使用 `serde`** — JSON 是手动序列化的。在 WASM 二进制文件大小中节省了约 100KB,但更容易出错。在 v2 中会考虑使用 `serde`。
- **不使用 `wasm-bindgen`** — 使用原生的 `extern "C"` 导出。导出表上有四个函数:`alloc`、`parse_sav`、`free_string`、`parser_version`。没有胶水代码,没有代码生成步骤。
- **纯 Rust zlib** — 使用 `miniz_oxide` 而不是系统的 libz。无需 Emscripten 即可编译为 WASM。
- **数据无关的引擎** — 繁育引擎接受 `EngineData` 作为参数,而不是导入静态数据集。使其可以使用任何 Pal 列表进行测试。
## 实际性能
| 文件大小 | 提取的 Pals 数量 | 解析时间(桌面端) | 解析时间(移动端) |
|-----------|---------------|---------------------|---------------------|
| 50 KB | 15 | < 20ms | < 50ms |
| 2 MB | 80 | ~80ms | ~200ms |
| 9 MB | 300+ | ~300ms | ~1.5s |
| 20 MB | 500+ | ~600ms | ~3s |
已在 Palworld v0.2.0.6 到 v1.0 的存档文件上进行了测试。
## 许可证
MIT — 详见 [LICENSE](./LICENSE)。
游戏数据格式是通过逆向公开的存档文件得到的。本项目与 Pocketpair 没有任何隶属关系。
标签:AI工具, Rust, TypeScript, WebAssembly, 可视化界面, 存档修改, 安全插件, 数据提取, 游戏工具, 网络流量审计, 解析器, 通知系统