kjjkjjzyayufqza/Gundam-AGE-PSP
GitHub: kjjkjjzyayufqza/Gundam-AGE-PSP
一款用于研究并提取 PSP 游戏《高达 AGE》Level-5 引擎资源的工具集,支持将私有格式的模型与纹理转换为标准的 glTF、OBJ 和 PNG 文件。
Stars: 0 | Forks: 0
# Gundam AGE PSP 资源研究
Level-5 **Gundam AGE PSP** 资源的静态提取研究与工具。
## 本项目的作用
可复现的静态提取 pipeline,以及一个桌面查看器:
```
Already-unpacked PSP resource tree
-> XPCK archive inspection/extraction
-> Level-5 compressed payload decode
-> IMGP/XI textures converted to PNG
-> XMPR/PRM meshes exported to OBJ + MTL + glTF
-> JSON/Markdown index and validation reports
-> age_viewer (egui): browse, preview, batch export
```
当前状态:
- 角色/机动战士静态模型在 JSON/glTF 输出中保留了 UV、权重和 MBN 绑定骨骼元数据;
- 采样的地图档案导出为带纹理的静态网格;
- IMGP `.xi` 纹理导出默认采用 PSP 16 字节 x 8 行反交织 (deswizzle);
- 剩余的高优先级地图差距主要发生在大地图上的材质到纹理绑定缺失,而非全局纹理解码失败;
- 在 `outputs/manifests/` 下存在档案级别的模型/纹理索引;
- `age_viewer` 可浏览树结构、预览网格/纹理,并通过确认列表导出 glTF 或 OBJ 包(含纹理)。
## 目录
**入门指南**
- [环境要求](#requirements)
- [快速开始](#quick-start)
- [GUI 查看器 (`age_viewer`)](#gui-viewer-age_viewer)
- [单命令工具:`age_start.py`](#one-command-tool-age_startpy)
- [手动阶段命令](#manual-stage-commands)
- [工具布局](#tool-layout)
**参考**
- [仓库布局](#repository-layout)
- [高级资源架构](#high-level-resource-architecture)
- [数据分布](#data-distribution)
- [格式说明](#format-notes)
- [验证策略](#validation-strategy)
- [第三方参考](#third-party-references)
- [法律与资源处理](#legal-and-asset-handling)
## 环境要求
Python 3.11+:
```
python --version
python -m pip install pillow
```
可选:
- 用于 `age_viewer` 和格式库的 Rust 工具链(edition 2024)。
- 用于 HTML 查看器截图的 Chrome/Edge。
- .NET 9,仅当您重新构建可选的本地 XMTN 动画探针时。
输入必须是已在仓库外解包的本地 PSP 资源树,例如:
```
```
生成的输出应存放在 `outputs/` 目录下,该目录会被 Git 忽略。
## 快速开始
在仓库根目录下运行:
```
cd E:\research\Gundam-AGE-PSP
```
显示支持的命令:
```
python .\tools\age_start.py --help
```
构建模型/纹理索引列表:
```
python .\tools\age_start.py index "" `
--json .\outputs\manifests\age_asset_index.json `
--compact-json .\outputs\manifests\age_asset_index.compact.json `
--markdown .\outputs\manifests\AGE_ASSET_INDEX.md `
--pipeline-root .\outputs `
--exclude "*/map/*chr*.xc"
```
当前索引输出:
- `outputs\manifests\AGE_ASSET_INDEX.md`
- `outputs\manifests\age_asset_index.compact.json`
- `outputs\manifests\age_asset_index.json`
将一个档案导出为纹理、OBJ、MTL、glTF 和清单:
```
python .\tools\age_start.py asset `
"\map\e1101.xc" `
--out-dir .\outputs\pipeline\e1101 `
--name e1101 `
--overwrite
```
对于地图模型检查,请使用相同的 `asset` 输出目录,而不是创建一个并行的 `map_validation` 样本树。asset 清单将纹理、材质、OBJ 和 glTF 路径记录在同一处。
```
python .\tools\age_start.py asset `
"\map\e3108.xc" `
--out-dir .\outputs\pipeline\e3108 `
--name e3108 `
--overwrite
```
当档案需要人工审查网格到纹理的分配时,请将其记录在 `tools/data/mesh_texture_mappings.json` 中。asset pipeline 会在写入 MTL/OBJ/glTF 之前应用该文件,因此无需添加第二个导出 pipeline,即可将共享的源材质按网格进行拆分。
## 单命令工具:`age_start.py`
`tools/age_start.py` 是推荐的入口点。它遵循与 GBM workspace 相同的仓库风格:一个简单的面向用户的命令委托给专注的实现模块。
```
age_start.py
xpck extract -> age_xpck_tool
asset -> age_asset_pipeline
character -> age_asset_pipeline with animation opt-in
map survey -> research.age_map_survey
index -> age_asset_index
fx-index -> age_fx_index (game-native effect_config + XPCK members)
```
重要默认设置:
| 选项 | 默认值 | 含义 |
|---|---|---|
| `--texture-layout` | `psp-swizzled` | 对 `.xi` 数据进行 PSP 16 字节 x 8 行反交织 |
| `--triangulation` | `strip` | 将 XPVI 解码为三角形带 |
| 角色动画 | `none` | 默认为静态导出;姿势导出仍处于实验阶段 |
| 地图导出 | static | 无权重地图会跳过 MBN 绑定加载 |
## 手动阶段命令
在调查单一阶段时使用这些命令。
提取单个 XPCK:
```
python .\tools\age_start.py xpck extract `
"\map\e1101.xc" `
--out .\outputs\extract\e1101 `
--overwrite
```
从已提取的目录进行转换:
```
python .\tools\age_asset_pipeline.py from-dir `
.\outputs\extract\e1101 `
--out-dir .\outputs\pipeline\e1101_from_dir `
--name e1101 `
--overwrite
```
运行大型地图调查,无需保留每个提取的样本:
```
python .\tools\age_start.py map survey `
--input-root "\map" `
--out-root .\outputs\map_survey\all_non_chr `
--exclude "*chr*.xc" `
--cleanup-samples `
--overwrite
```
## 工具布局
Rust 查看器是根 crate。核心可重用的 Python 工具平铺在 `tools/` 中,以便于直接执行。
| 路径 | 用途 |
|---|---|
| `Cargo.toml`, `src/` | `age_viewer` GUI + 格式库:搜索、预览、glTF/OBJ 导出 |
| `docs/screenshots/` | 用于文档的 UI 截图 |
| `capture_ui.ps1` | 用于捕获查看器窗口的 Windows 助手 |
| `tests/real_resource_tree.rs` | 针对真实资源树的可选检查 |
| `tools/age_start.py` | Python pipeline 的主要面向用户入口点 |
| `tools/` | 核心档案、纹理、模型、材质、pipeline 和索引模块 |
| `tools/research/` | 验证、调查、编目、探针和预览助手 |
| `tools/tests/` | 单元测试 |
| `tools/StudioElevenAnimationProbe/` | 被忽略的本地可选 .NET 探针;不被追踪 |
详细的工具说明:[docs/TOOLING.md](docs/TOOLING.md)。
## GUI 查看器 (`age_viewer`)
用于浏览已解包资源树、预览模型和纹理,以及导出静态包的桌面工具。只读:无重新打包或回写功能。

*搜索(左侧)、wgpu 视口(中间)、档案检查器(右侧)。原生 egui 深色边框;支持结果列表中的多选和右键导出。*
### 构建与运行
需要 Rust 工具链(edition 2024)和 GPU 后端(Windows 上的 DirectX 12 或 Vulkan)。
```
cargo run --release
```
使用 `--release`。debug 构建索引树的速度大约慢六十倍,因为 Level-5 解码器是逐字节处理的。
库单元测试(无需游戏数据):
```
cargo test --lib
```
### CI / 发布二进制文件
GitHub Actions workflow [`.github/workflows/release.yml`](.github/workflows/release.yml):
| 触发条件 | 作用 |
|---|---|
| 推送到 `master` / `main`,拉取请求 | `cargo test --lib --release`, `cargo build --release --locked`, 上传 Windows x64 zip artifact |
| 标签 `v*`(例如 `v0.1.0`) | 相同的构建,然后发布一个包含 `age_viewer-windows-x64.zip` 的 GitHub Release |
| 手动 **workflow_dispatch** 且选择 *Create release* | 从当前分支草拟发布 |
从 Actions 标签页下载最新的 artifact,或从 Releases 页面下载已发布的资产。zip 文件仅包含 `age_viewer.exe`(不含游戏数据)。
```
# 在推送 commit 后发布一个 versioned release:
git tag v0.1.0
git push origin v0.1.0
```
### 工作流
1. **打开根目录** — `File > Open root...` 并选择已解包的 `psp` 文件夹(或子树,如 `psp/chr`)。该根目录会被记住,并在下次启动时重新索引。
2. **搜索** — 输入名称或路径片段;切换 `Has models` / `Has textures`;按 `Area`(顶级文件夹)过滤。
3. **预览** — 点击结果。左键拖拽旋转,右/中键拖拽平移,滚轮缩放。检查器显示档案信息、网格、绑定和纹理缩略图。
4. **多选** — 在当前结果顺序中按 `Shift+click` 选择范围。状态显示 `N selected`。
5. **上下文菜单**(右键点击某行):
- 导出为 glTF… / 导出为 OBJ…(选择项或单行)
- 打开(在查看器中加载)
- 打开包含文件夹(资源管理器,选中该文件)
- 选择所有匹配项 / 清除选择
6. **菜单导出** — `File > Export archive...`, `Export search results...` 或 `Export all indexed...`。
### 导出确认
每个导出路径都会打开一个文件夹选择器,然后在写入任何文件之前弹出一个**确认**对话框:
| 字段 | 含义 |
|---|---|
| Count | 将要导出多少个档案 |
| Scope | 单个档案、选择、搜索结果或所有已索引项 |
| Output root | 您选择的目标文件夹 |
| Format | glTF 2.0 (`.gltf` + `.bin`) 或 Wavefront OBJ (`.obj` + `.mtl`) |
| Textures | 每个包 `textures/` 下的可选 PNG |
| Export list | 可滚动的 **Source → package folder** 行以供审查 |
确认按钮文本为 `Export N items`。现有的包文件夹将被覆盖。批量运行会在输出根目录下写入 `age_viewer_export_report.json`。
### 包结构
文件夹名称遵循原始档案名称(批量导出时的相对路径,无扩展名):
```
/
chr/ms001000/ms001000_p000/
ms001000_p000.gltf # or .obj + .mtl
ms001000_p000.bin # glTF only
textures/
000.png
002.png
age_viewer_export_report.json # batch only
```
几何体是解码后的绑定姿势;未执行动画。当档案包含 MBN 骨骼和 XMPR 权重时,**glTF 导出将包含**:
- 来自 MBN 绑定变换的关节节点 (`joint_`)
- 每个带权重网格包含一个带有逆绑定矩阵的 `skin`
- `JOINTS_0` / `WEIGHTS_0`(当一个顶点具有四个以上的影响时,还包括 `_1`)
OBJ 导出依然是静态几何体 + MTL + 纹理(无蒙皮)。
### 捕获 UI 截图
```
cargo build --release
powershell -NoProfile -File .\capture_ui.ps1 `
-Out docs\screenshots\age_viewer_main.png `
-WaitSeconds 16 `
-LoadWaitSeconds 4
```
该脚本启动 release 二进制文件,点击搜索行以加载模型,使用 `PrintWindow` 捕获窗口(针对 wgpu 的完整内容),然后退出。
根目录/默认路径:`docs/screenshots/age_viewer_main.png`。
### 针对真实游戏数据进行验证
集成测试是可选的,因为它们需要游戏数据,而本仓库中不包含。通过 `AGE_PSP_ROOT` 环境变量或位于仓库根目录的 `age_psp_root.txt` 文件指向资源根目录,然后:
```
cargo test --release --test real_resource_tree -- --nocapture
```
如果两者都不存在,测试将报告被跳过并通过。`age_psp_root.txt` 是机器本地的,且被 git 忽略。
这些测试将 Rust 解码器与已验证的 Python 解码器进行锚定:每个网格的顶点和面数必须与 `tools/research/_age_viewer_parity.py` 完全匹配,并且会断言 UV 方向,因此回归问题不可能在不被察觉的情况下翻转每个模型。
## 仓库布局
Rust 查看器是根 crate。Python 研究 pipeline 位于 `tools/` 下。
```
Gundam-AGE-PSP/
Cargo.toml # age_viewer crate (GUI viewer + glTF/OBJ export)
src/ # Rust: formats, renderer, GUI
level5.rs # Level-5 compression decode
xpck.rs # XPCK archive directory
imgp.rs # IMGP .xi texture decode
xmpr.rs # XMPR .prm mesh decode
material.rs # RES.bin/CHRP00 + .txp material binding
scene.rs # archive -> previewable scene
gltf.rs # static glTF 2.0 export
obj.rs # Wavefront OBJ + MTL export
export_fmt.rs # format choice + package folder naming
index.rs # threaded resource-tree scan + search
render.rs # orbit camera
gpu_renderer.rs # wgpu offscreen renderer (per-mesh textures)
shaders/mesh.wgsl
theme.rs # stock egui dark chrome helpers
gui.rs, gui/ # egui shell, search, viewport, inspector, batch export
tests/
real_resource_tree.rs # opt-in checks against real game data
docs/screenshots/ # UI screenshots for README
capture_ui.ps1 # Windows UI screenshot helper
README.md
.gitignore
tools/
age_start.py
age_xpck_tool.py
age_imgp_tool.py
age_xmpr_tool.py
age_material_bind.py
age_gltf_tool.py
age_asset_pipeline.py
age_asset_index.py
age_fx_index.py
research/
age_map_report.py
age_map_survey.py
age_model_survey.py
age_static_model_catalog.py
age_obj_preview.py
tests/
docs/
RESOURCE_ARCHITECTURE.md
DATA_DISTRIBUTION.md
BINARY_FORMATS.md
TOOLING.md
THIRD_PARTY_REFERENCES.md
ASSET_EXTRACTION_RESEARCH.md
AGE_FX_NATIVE_INDEX_RESEARCH.md
AGE_FX_SESSION_2026-07-25.md
AGE_ITEM_HASH_INDEX.md
LEVEL5_ASSET_WORKFLOW.md
RESEARCH_LOG.md
outputs/ # ignored generated artifacts
# age_fx_ms/ = Gundam AGE-FX suit exports + AGE_FX_PARTS.md
external_tools/ # ignored local third-party clones
```
## 高级资源架构
```
flowchart LR
PSP["Unpacked PSP resource tree"] --> Areas["psp/chr, psp/map, psp/btl, psp/eff, ..."]
Areas --> XPCK["XPCK archives: .xc/.xb/.xa/.xv/.xk"]
XPCK --> Entries["Archive entries"]
Entries --> RES["RES.bin -> Level-5 decompress -> CHRP00 strings"]
Entries --> XI["IMGP .xi textures"]
Entries --> PRM["XMPR .prm meshes"]
Entries --> MBN[".mbn skeleton/bind data"]
Entries --> MAT[".mtr/.atr/.txp material params"]
Entries --> MTN[".mtn2 animation candidates"]
XI --> PNG["PNG"]
PRM --> OBJ["OBJ/MTL"]
PRM --> GLTF["glTF"]
RES --> Binding["material/texture binding"]
MAT --> Binding
Binding --> OBJ
Binding --> GLTF
```
更多细节:[docs/RESOURCE_ARCHITECTURE.md](docs/RESOURCE_ARCHITECTURE.md)。
## 数据分布
来自本地 PSP 树的当前索引:
| 指标 | 计数 |
|---|---:|
| 已索引的 XPCK 档案 | 4529 |
| 解析错误 | 0 |
| 包含 `.prm` 模型的档案 | 2364 |
| 包含 `.xi` 纹理的档案 | 2710 |
| 同时包含模型和纹理的档案 | 2343 |
| `.prm` 模型条目 | 23880 |
| `.xi` 纹理条目 | 9170 |
| 材质参数条目 | 46760 |
更多细节:[docs/DATA_DISTRIBUTION.md](docs/DATA_DISTRIBUTION.md)。
## 格式说明
| 格式 | 当前状态 |
|---|---|
| `XPCK` | 目录解析和提取工作正常 |
| Level-5 压缩 | 根据需要实现了无压缩、LZ10、Huffman4/8、RLE、zlib 路径 |
| `RES.bin` / `CHRP00` | 已解压并用于资源/材质字符串 |
| `IMGP` `.xi` | PNG 导出工作正常,默认使用 PSP 反交织 |
| `XMPR` `.prm` | OBJ/glTF 静态导出工作正常 |
| `XPVB` / `XPVI` | 针对采样的静态资产的顶点/索引解码工作正常 |
| `.mtr/.atr/.txp` | 材质绑定已部分映射 |
| `.mbn` | 角色绑定数据很有用;针对无权重地图已跳过 |
| `.mtn2` | 仅作为可选动画探针;完整动画导出尚未最终完成 |
更多细节:[docs/BINARY_FORMAT.md](docs/BINARY_FORMATS.md)。
## 验证策略
验证以证据为先:
- 解析真实的本地档案;
- 在每个阶段导出 JSON 清单;
- 在 `outputs/previews` 下渲染地图/模型预览图像;
- 将大型地图对照组与大型地图问题案例进行比较;
- 在 Markdown/JSON 报告中保留未解决材质的计数。
当前地图证据:
- 完整的非 `chr` 地图调查:`285` 个样本,`0` 次失败;
- `198` 个视觉上干净;
- `54` 个普通的未解决材质案例;
- `33` 个仅效果类未解决案例;
- 大型干净对照组包括 `t5201`, `t0901`, `e2104` 和 `b3003`;
- 优先问题地图包括 `e1101`, `b3205`, `b0101`, `b3104`, `t0201` 和 `e3108`。
## 第三方参考
第三方 GitHub 仓库不在本仓库中追踪。本地克隆(如果存在)位于被忽略的 `external_tools/` 下。
参考链接和使用说明:
- [Tiniifan/studio_eleven](https://github.com/Tiniifan/studio_eleven)
- [Tiniifan/StudioElevenLib](https://github.com/Tiniifan/StudioElevenLib)
- [Ploaj/Metanoia](https://github.com/Ploaj/Metanoia)
- [albe/openTri](https://github.com/albe/openTri)
- [Tiniifan/Pingouin](https://github.com/Tiniifan/Pingouin)
- [Tiniifan/Level5ResourceEditor](https://github.com/Tiniifan/Level5ResourceEditor)
- [Tiniifan/level5_material](https://github.com/Tiniifan/level5_material)
- [FanTranslatorsInternational/Kuriimu2](https://github.com/FanTranslatorsInternational/Kuriimu2)
更多细节:[docs/THIRD_PARTY_REFERENCES.md](docs/THIRD_PARTY_REFERENCES.md)。
## 法律与资源处理
本仓库用于互操作性和本地研究工具。它不授予 Gundam AGE PSP 资源的权利。
请勿提交:
- PSP 游戏档案或提取的档案;
- 生成的 PNG/OBJ/MTL/glTF/bin 文件;
- `outputs/`;
- `external_tools/` 下的本地第三方克隆;
- `tools/StudioElevenAnimationProbe/`;
- `target/` 中的 Rust 构建输出。
`.gitignore` 文件针对这些边界进行了配置。它在整个仓库范围内忽略游戏资产扩展名(`.xc`, `.prm`, `.xi`, `.mbn`, `.gltf`, `.obj` 等),而不仅仅是在 `outputs/` 下,因此提取的内容不会意外地从新位置被提交。
标签:3D模型导出, Python, Rust, 云资产清单, 信息收集, 可视化界面, 多人体追踪, 无后门, 格式转换, 游戏资源提取, 网络流量审计, 逆向工具, 逆向工程