AndrewAltimit/legend-of-legaia-re

GitHub: AndrewAltimit/legend-of-legaia-re

针对PSX游戏《Legend of Legaia》的全链路逆向工程项目,涵盖基于Ghidra的格式文档、Rust资产提取管线和wgpu净室引擎重实现。

Stars: 0 | Forks: 0

# legend-of-legaia-re 针对 PSX 游戏 **Legend of Legaia** (1998, Sony, NA SCUS-94254) 的逆向工程:基于 Ghidra 追踪的格式文档、涵盖光盘上每种资产的 Rust 提取器,以及使用 wgpu 渲染(可选 WASM)的净室引擎重实现。 在同一个 Cargo workspace 下包含两条协同的推进路线: 1. **资产保存 + 格式文档。** 提取光盘上的每种资产,将每种格式的文档溯源至 Ghidra 函数,构建可双向转换的解析器(`.bin` → PNG / WAV / OBJ / JSON)。 2. **引擎重实现。** 引擎的净室 Rust 移植版 —— 通过 wgpu 渲染,通过现有的 XA + VAB 解码器处理音频,可选 WASM 目标。采用与 [ScummVM](https://www.scummvm.org/)、[OpenRCT2](https://github.com/openrct2/OpenRCT2)、[OpenMW](https://github.com/OpenMW/openmw)、[OpenLara](https://github.com/XProger/OpenLara) 相同的法律模式 —— 自带光盘镜像;工具包负责剩下的工作。 仓库名称中的 `-re` 包含双重含义:**r**everse-**e**ngineering(逆向工程)和 **r**e-implementation(重实现)。 **状态:** 本地研究项目。请勿期望 API 稳定性。 **许可证:** 根据您的选择,在 [Unlicense](LICENSE)(公共领域贡献)或 [MIT License](LICENSE-MIT) 下双重授权。故意不提供 Apache-2.0 —— 本项目旨在在您所在司法管辖区的法律允许范围内尽可能接近公共领域,不附带任何专利报复条款:复制它,分叉它,出售它,为其改进申请专利,只是不要阻止其他人做同样的事情。这些许可证*仅*适用于本仓库中的代码和文档。**Sony 的知识产权 —— 游戏可执行文件、资产数据、ROM 内容 —— 不被分发,也不受这些许可证的保护。** 您需要自带光盘镜像。`extracted/` 和 `ghidra/projects/` 目录已被 gitignore。CI 在无光盘数据的情况下运行。 ## 文档 `docs/` 下提交的文档按主题优先组织,作为技术参考: - **[`docs/overview.md`](docs/overview.md)** —— 简短介绍 + 架构层次说明。 - **[`docs/formats/`](docs/formats/overview.md)** —— 针对各种格式的字节级规范(PROT、LZS、TIM、TMD、VAB、MES、ANM、MDT、场景包、特效、覆盖等)。 - **[`docs/subsystems/`](docs/subsystems/)** —— 引擎工作原理:[启动](docs/subsystems/boot.md)、[资产加载器](docs/subsystems/asset-loader.md)、[脚本 VM](docs/subsystems/script-vm.md)、[角色 VM](docs/subsystems/actor-vm.md)、[特效 VM](docs/subsystems/effect-vm.md)、[招式 VM](docs/subsystems/move-vm.md)、[运动 VM](docs/subsystems/motion-vm.md)、[渲染器](docs/subsystems/renderer.md)、[音频](docs/subsystems/audio.md)、[过场动画](docs/subsystems/cutscene.md)、[战斗](docs/subsystems/battle.md)、[战斗动作 SM](docs/subsystems/battle-action.md)、[战斗公式](docs/subsystems/battle-formulas.md)、[引擎重实现](docs/subsystems/engine.md)。 - **[`docs/tooling/`](docs/tooling/)** —— 如何使用本仓库:[提取命令行工具](docs/tooling/extraction.md)、[Ghidra 设置](docs/tooling/ghidra.md)、[覆盖捕获](docs/tooling/overlay-capture.md)。 - **[`docs/reference/`](docs/reference/)** —— [Ghidra 追踪的关键函数](docs/reference/functions.md)、[RAM 映射 + 全局变量](docs/reference/memory-map.md)、[TCRF 区域数据](docs/reference/builds.md)。 有关 workspace 约定和格式陷阱(特别是 MIPS LUI+ADDIU 指令对),请先阅读 [`CLAUDE.md`](CLAUDE.md)。 ## 快速开始 ### 前置条件 - Rust 工具链(`cargo`,2024 edition)。 - Legend of Legaia (USA) 光盘镜像,格式为 `.bin` + `.cue` (Mode2/2352)。 - (可选)Docker + docker-compose,用于无头 Ghidra 运行。 - (可选)mednafen + 目标场景的保存状态,用于运行时覆盖捕获。 ### 构建 ``` cargo build --release ``` 二进制文件位于 `target/release/`。运行 ` --help` 获取完整的子命令列表。 如果您打算提交代码,请运行一次 hook 安装程序 —— 它会将 `core.hooksPath` 指向 `scripts/git-hooks/`,以便在每次提交前运行 `cargo fmt --check` 和 `cargo clippy -D warnings`(与 CI 匹配)。当没有暂存 Rust 文件时,该 hook 会自动跳过。 ``` scripts/install-hooks.sh ``` ### 运行整个流程 ``` ./target/release/legaia-extract "/path/to/Legend of Legaia (USA).bin" --out extracted ``` 校验 → 光盘 → PROT → 分类 → 流式子资产提取 → TIM → PNG。使用 `--skip-png` 跳过最慢的步骤,或使用 `--skip-verify` 跳过 SHA-256 哈希校验。传递 `-v` 可查看逐文件输出。 ### 分阶段命令行工具 有关独立运行各个阶段的说明,请参见 [`docs/tooling/extraction.md`](docs/tooling/extraction.md)。校验光盘镜像: ``` ./target/release/disc-extract verify "/path/to/Legend of Legaia (USA).bin" ``` | 光盘 | SHA-256 (Mode2/2352 .bin) | |---|---| | Legend of Legaia (USA), SCUS-94254 | `e6120a5d70716dd2f026a2da32d0171d52651971b52c4347a68541299f75258c` | 要按轨道进行规范性校验,请与 [Redump](http://redump.org/disc/425/) 交叉核对。 ### 浏览资产 运行流程后: ``` # 3D mesh + textures ./target/release/asset-viewer tmd extracted/tmd_scan/0866_battle_data \ --shape character --sort-by-size --bundle battle # A VAB sample ./target/release/asset-viewer vab extracted/PROT/0865_battle_data.BIN --offset 0x... --sample 0 # PROT entry browser ./target/release/asset-viewer prot extracted/PROT.DAT --cdname extracted/CDNAME.TXT # Headless engine driver — boots a CDNAME scene straight off PROT bytes # (no `tim_scan/` or `tmd_scan/` filesystem intermediate). Prints what the # scene-host resolved: TIMs uploaded to VRAM, TMDs parsed, MES presence, # SEQ / VAB / event-script counts. ./target/release/legaia-engine info --scene town01 ./target/release/legaia-engine list-scenes # Run the engine for N frames against a scene — ticks the World, drives # the camera, drains BGM events into the audio director (if available), # logs scene transitions. Headless smoke check that the boot-loop wiring # (engine-shell::BootSession) actually moves state forward. ./target/release/legaia-engine play --scene town01 --frames 600 --no-audio # Open a windowed wgpu session rendering scene TMDs + HUD; accepts keyboard # input; exits cleanly on window close. 60 Hz fixed tick, uncapped render. ./target/release/legaia-engine play-window --scene town01 # Decode a raw PSX STR file (MDEC video) and play it back in a window with # synced XA audio. ./target/release/legaia-engine play-str /path/to/cutscene.str # Edit input key bindings (persisted to TOML via engine-core::input::Mapping) ./target/release/legaia-engine config set --binding cross=Z # Save / load the world's empty default party to a slot file. Engines # drive the same flow at runtime through `engine-core::menu_runtime`. ./target/release/legaia-engine save --slot 0 --save-dir saves ./target/release/legaia-engine load --slot 0 --save-dir saves # Field scene runner — drives the field-VM against a real CDNAME scene's # event-script records, with dialog rendering wired into the same window ./target/release/asset-viewer field town01 # Battle scene driver — boots the battle bundle, ticks the battle-action # state machine, shows action state + per-slot liveness in the HUD ./target/release/asset-viewer battle-scene --queued-action 3 # SEQ playback — drives the SsAPI-shape sequencer + a VAB through cpal, # producing live audio ./target/release/asset-viewer seq path/to.seq path/to.vab # Standalone MES dialog viewer — typewriter-paced text rendering through # the extracted dialog font ./target/release/asset-viewer dialog path/to.mes # ANM keyframe inspector — per-record header + per-bone keyframe table ./target/release/anm keyframes path/to.anm --record 0 # Field-pack slot clusters — group the 97 schema slots by size to surface # semantic record kinds (5 × 0x2088 = the scene's TIM blobs, 21 × 0x218 = # the NPC-slot array, etc.) ./target/release/asset field-pack extracted/PROT/0005_town01.BIN --groups # PSX memory-card reader — list active save blocks, parse a character # record, JSON-dump a five-slot party ./target/release/save-tool dir ~/.mednafen/sav/Legend*.0.mcr ./target/release/save-tool roundtrip /path/to/character.bin ``` ### 静态分析(Docker 中的 Ghidra) ``` docker compose build ghidra # one-time, sets UID/GID matching the host user docker compose up -d ghidra docker compose exec ghidra /ghidra/support/analyzeHeadless \ /projects legaia -process SCUS_942.54 \ -noanalysis -postScript find_streaming_consumers.py ``` 每个函数的反编译 + 反汇编转储将生成在 `ghidra/scripts/funcs/.txt` 中。有关完整的脚本目录和注意事项,请参见 [`docs/tooling/ghidra.md`](docs/tooling/ghidra.md)。 ### 捕获和分析运行时覆盖 大部分游戏逻辑(场景/战斗/菜单状态机、对话渲染器、调试标志写入器)位于加载到 `0x801C0000+` 的 RAM 覆盖中,而**不在** `SCUS_942.54` 中。在 mednafen 中保存目标场景的状态并运行: ``` scripts/analyze-overlay.sh \ ~/.mednafen/mcs/Legend*Legaia*.mc0 \ --label level_up ``` 该流程会解压缩 gzip 压缩的保存状态,切出覆盖窗口,将其重新导入 Ghidra,并输出一个 CSV 文件,其中包含对已知 SCUS 资产加载器的每个 `jal` 调用及其常量跟踪参数。请参见 [`docs/tooling/overlay-capture.md`](docs/tooling/overlay-capture.md)。 ### 依赖光盘的测试 ``` LEGAIA_DISC_BIN="/path/to/Legend of Legaia (USA).bin" cargo test --workspace --release ``` 一些集成测试涉及真实的光盘/提取目录: - `crates/iso/tests/disc_pipeline.rs` —— 光盘遍历、文件计数、关键文件的 SHA-256。 - `crates/extract/tests/validation_suite.rs` —— 完整的流程断言。 - `crates/engine-core/tests/scene_chain_e2e.rs` —— 加载每个 CDNAME 场景,遍历 MES + SEQ + TMD 资产,根据每个场景的 `block_start + 6 + id` 数学计算来验证 BGM 解析器。 - `crates/engine-core/tests/battle_real_data_chain.rs` —— 定位正式版特效包并驱动战斗 SM。 - `crates/engine-audio/tests/real_bgm_chain.rs` —— 将真实的 `music_01` SEQ + VAB 对推送到序列器和 SPU 混音器。 - `crates/save/tests/real_card_roundtrip.rs` —— 遍历真实的 PSX 记忆卡镜像(mednafen `.mcr`)并验证存档块布局。 如果未设置 `LEGAIA_DISC_BIN`,则每个依赖光盘的测试都会跳过并顺利通过 —— 这是故意的,以便 CI 可以在不分发 Sony 数据的情况下运行。 ## 仓库布局 ``` legend-of-legaia-re/ ├── Cargo.toml # workspace root ├── docker-compose.yml # ghidra service (UID/GID-matched user) ├── docker/ghidra.Dockerfile # wraps blacktop/ghidra:latest with host-UID mapping ├── crates/ │ ├── iso/ # PSX disc reader + ISO9660 walker │ ├── prot/ # PROT.DAT TOC + CDNAME + standalone TIM-pack │ ├── lzs/ # Legaia LZS decoder (FUN_8001a55c) │ ├── asset/ # Asset dispatcher, streaming, scene-bundle + format detectors, per-entry categorize classifier │ ├── tim/ # PSX TIM parser + PNG exporter │ ├── tmd/ # Legaia TMD parser + primitive walker + OBJ export │ ├── vab/ # VAB sound bank extractor + SPU-ADPCM decoder │ ├── xa/ # XA-ADPCM decoder + WAV exporter │ ├── mdt/ # Move table (Tactical Arts) parser │ ├── mes/ # MES dialog container parser │ ├── anm/ # ANM animation container parser │ ├── seq/ # PsyQ SEQ parser + CLI inspector │ ├── save/ # Per-character record (0x414B) parse + write │ ├── font/ # Dialog font extraction + atlas / layout API │ ├── extract/ # Top-level pipeline driver │ ├── mdec/ # PSX MDEC clean-room decoder (BS v2 bitstream → RGBA8); STR sector assembler │ ├── engine-core/ # World, scene host, scene resources (VRAM pre-pass), camera, menu runtime, save round-trip │ ├── engine-render/ # winit + wgpu, software PSX VRAM emulation, text overlay │ ├── engine-audio/ # cpal mixer + clean-room SPU + SEQ sequencer │ ├── engine-vm/ # Actor / field / effect / move / motion VMs + battle SM + action validator + formulas │ ├── engine-shell/ # `legaia-engine` top-level driver + BootSession + AudioBgmDirector; play-window renders shop + inn + level-up overlays │ ├── asset-viewer/ # Combined viewer: TIM, TMD, stage, VAB, SEQ, dialog, field, battle, PROT │ └── web-viewer/ # WASM target — disc browser running in the browser ├── docs/ # Topic-first technical reference (see "Documentation") ├── ghidra/ │ ├── projects/ # Ghidra project DB (gitignored) │ └── scripts/ # Jython analysis scripts + per-function dumps ├── scripts/ # Host-side helpers (function-coverage, overlay capture) ├── site/ # Project landing site (mirrors docs/) └── extracted/ # Build outputs (gitignored) ``` ## 致谢 - [**The Cutting Room Floor**](https://tcrf.net/Legend_of_Legaia) —— 开发者署名(Prokion / Contrail)、调试标志地址、14 个已知构建版本的目录。 - [Sam Ste 的 PROT.DAT 解包器](https://github.com/SamSteProjects/LegendOfLegaia_.Dat_unpacker) —— 早期的 Python 概念验证,指明了正确的 TOC 槽位和 TIM 包启发式方法。 - PSX 场景分析社区 —— Sony PsyQ 文档、Martin Korth 的 [PSX-SPX](https://problemkaputt.de/psx-spx.htm) 以及数十年来积累的 TIM/TMD/SPU 文档。 - 提供本仓库所遵循之法律模式的参考项目:ScummVM、OpenRCT2、OpenMW、OpenLara。 本项目不分发 Sony 的知识产权。您需要自带光盘镜像。工具部分由人工指导下的 AI 代理共同完成。
标签:3D渲染, BYO-disc, Ghidra, Legend of Legaia, OpenMW, PlayStation, PSX, ROM破解, Rust, ScummVM, SDL3, WASM, WebAssembly, wgpu, 云资产清单, 信息收集, 可视化界面, 开源游戏, 数据解析, 格式文档, 游戏开发, 游戏引擎重制, 游戏提取器, 索尼, 网络流量审计, 解析器, 请求拦截, 逆向工程, 通知系统, 非法分发, 音频解码