Sylpheed RE agent c53da9ea2a re: the asteroid-field tables (AsteroidGroup_00N) decoded
New doc docs/re/structures/asteroid-fields.md, indexed.

Half the chain was already in the corpus: stage-definition-table and
stage-mission-tables record the per-phase AsteroidDefinition naming a .tbl,
and unit-datasheet-static partitions Generic and finds 10 per pack whose only
field is {EnumAsteroidGroup}, "an asteroid-group file". Neither says what is
inside them.

The table: 10 objects per pack x 6 packs = 60, 384 AsteroidGroup_* records,
4 or 7 groups per object. Every record carries exactly three fields, all 384,
no partials - AsteroidModelName (28 distinct; mob_n050..n056 x24 each,
mob_n040..n043 x18), AsteroidFrameName (64 distinct, Frame_S<NN>[_pN]_
Asteroid_NN, the same names regn-map-grid reports the Route tables showing),
and EnumAsteroid (37 distinct, 1..588, 6633 per pack summed).

Enum<Thing> is a COUNT; Enumerate<Thing> names a TABLE. EnumAsteroidGroup
equals its object's group count in 54 of 60 and EnumAsteroid is a plain
integer - the opposite of the Enumerate* join rule. The -erate is the
difference. Count-style fields still are not safe: the 6 failures are ONE
object repeated per language pack, declaring 4 while holding 7.

The tenth object is unreferenced and it is the one that miscounts. Joining by
frame prefix, nine objects match the nine AsteroidDefinition values one to
one; the tenth carries Frame_Alpha_S01_Asteroid, Frame_Alpha_S01_Haze and
Frame_TAlpha_S01_Asteroid and is named by nothing.

S28 has an asteroid VOLUME but no asteroid DEFINITION: mcol-collision lists
nine _AsteroidVolume_wp meshes (…, S28_p1); the nine AsteroidDefinition values
are those eight plus test_s8p1. Eight shared, one different each way.

Not settled: what EnumAsteroid counts of; why S28_p1 has a volume and no
definition. The name->object join did not reproduce by hash under name_hash,
mod 0x00FFF9D7, or a 24-bit mask - the join used is by frame prefix.

All fifteen artefacts byte-identical.
2026-08-28 01:32:52 +00:00

Project Sylpheed: Arc of Deception — Reborn

A clean-room, open-source reimplementation of Project Sylpheed: Arc of Deception (Xbox 360, 2006).

Built with Rust and the Bevy game engine. Runs natively on Windows, macOS, and Linux, and in the browser via WebAssembly.

Legal note: This project contains no original game code or assets. You must own a legitimate copy of Project Sylpheed to use this engine. Assets remain the intellectual property of SETA Corporation / Square Enix.


Current Status: Milestone 1 — Asset Explorer

  • XISO disc image reading via xdvdfs
  • Xbox 360 texture de-tiling (Morton / Z-order)
  • XPR2 texture container parsing
  • Bevy custom AssetLoader for .xpr textures
  • Orbit camera viewer
  • CLI tools: extract, list, sniff, texture info, texture export
  • GitHub Actions CI (Windows + macOS + Linux + WASM)
  • WASM / web build target
  • Mesh format (reverse engineering in progress)
  • Audio format (XMA → PCM pipeline)
  • Mission data format

Repository Structure

sylpheed-reborn/
└── sylpheed-viewer/        ← Milestone 1: asset explorer (Rust/Bevy workspace)
    ├── Cargo.toml          ← Workspace root
    ├── crates/
    │   ├── sylpheed-formats/   # Format parsers — no Bevy dependency
    │   │   ├── xiso.rs         # XISO / XDVDFS disc image reader
    │   │   ├── texture.rs      # XPR2 container + DXT de-tiling (Morton)
    │   │   ├── vfs.rs          # Virtual filesystem + magic-byte sniffer
    │   │   ├── mesh.rs         # Mesh parser (stub — RE in progress)
    │   │   └── audio.rs        # Audio parser (stub — XMA TODO)
    │   │
    │   ├── sylpheed-viewer/    # Bevy application
    │   │   ├── lib.rs          # App setup + WASM entry point
    │   │   ├── main.rs         # Native binary entry point
    │   │   ├── asset_loader.rs # Custom Bevy AssetLoaders
    │   │   ├── camera.rs       # Orbit camera (LMB orbit, RMB pan, scroll zoom)
    │   │   └── ui.rs           # egui file browser + RE notes panel
    │   │
    │   └── sylpheed-cli/       # Command-line tools
    │       └── main.rs         # extract / list / sniff / texture info commands
    │
    ├── assets/             ← Extracted game files (gitignored)
    ├── .github/workflows/
    │   └── ci.yml          # CI: Windows + macOS + Linux + WASM
    ├── Trunk.toml          # WASM build configuration
    └── justfile            # All build recipes

Getting Started

Prerequisites

# Install Rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

# Install just (task runner)
cargo install just

# For web builds only
cargo install trunk
rustup target add wasm32-unknown-unknown

Linux — Bevy system dependencies

sudo apt-get install -y \
  libasound2-dev libudev-dev libwayland-dev \
  libxkbcommon-dev libx11-dev libxi-dev pkg-config

Step 1 — Extract your disc

cd sylpheed-viewer

# Extract using the CLI tool
just extract /path/to/project_sylpheed.iso

# Or manually with xdvdfs
cargo install xdvdfs-cli
xdvdfs unpack project_sylpheed.iso ./assets/

Step 2 — Run the viewer

cd sylpheed-viewer

just run          # Native viewer
just web          # WASM dev server → http://localhost:8080

The asset viewer opens an orbit camera scene. Use the left panel to browse extracted game files. Click a .xpr file to preview it as a texture.


Build Commands

All commands run from the sylpheed-viewer/ directory.

just run              # Run the native viewer (debug)
just run-dev          # Run with hot-reloading
just build-native     # Build native release binary
just web              # WASM dev server at localhost:8080
just build-web        # WASM release build → ./dist/

just extract game.iso # Extract an ISO to ./assets/
just sniff            # Identify file formats in ./assets/
just sniff-unknown    # Show only unrecognised formats (RE focus)

just test             # Run all tests
just ci               # Full CI: fmt + lint + test + WASM check

Technology Stack

Layer Choice Reason
Language Rust Memory safety, performance, cross-platform
Game engine Bevy 0.15 ECS-first, WASM-native, data-driven
XISO reading xdvdfs Pure Rust, reads Xbox 360 disc images
Binary parsing binrw Derive-macro based, ideal for RE work
Web bundler Trunk Bevy's standard WASM build tool
CLI clap Asset extraction and inspection tools
Debug UI bevy_egui In-viewer asset browser and RE notes panel

Key Architectural Decisions

sylpheed-formats has zero Bevy dependency. All binary format parsers live here and are testable with plain cargo test. Bevy integration is a thin layer on top in sylpheed-viewer.

WASM is a first-class target. XISO reading is gated behind #[cfg(not(target_arch = "wasm32"))] since the browser can't read local files. On the web, assets must be pre-extracted and served over HTTP.

Modding is designed in from the start. The virtual filesystem (vfs.rs) is the single choke point for all asset reads — a mod loader only needs to intercept that one layer to override any file.


Reverse Engineering Notes

Known file formats

Path pattern Format Status Notes
*.XPR, *.XPR2 XPR2 texture Parsing DXT1/3/5, DXN, Morton de-tiling done
DEFAULT.XEX Xbox EXE 2 🔬 Study Main executable — load in Ghidra (PPC BE)
*.XWB, *.XSB XACT audio TODO Wave/sound banks; audio is XMA codec
*.pak, *.p00 SETA archive Unknown Paired header/data format
mesh files Unknown TODO Run just sniff-unknown to find candidates

RE workflow

# 1. Extract and map the disc
just extract game.iso
just sniff-unknown   # shows hex magic bytes of unknown files

# 2. Hex-inspect candidates
#    Groups of 12 bytes at offsets: likely f32 XYZ vertices
#    Groups of 6 bytes:             likely u16 triangle indices

# 3. Cross-reference in Ghidra
#    Load DEFAULT.XEX with PowerPC Big-Endian processor
#    Search string refs to file extensions → find load functions

# 4. Implement parser
#    Add binrw #[derive(BinRead)] struct in sylpheed-formats/src/mesh.rs
#    cargo test -p sylpheed-formats

Next steps (Milestone 2)

  1. Mesh format — fingerprint with sniff-unknown, implement mesh.rs
  2. Audio — parse XWB headers, batch-convert XMA to WAV via ffmpeg
  3. Mission data — format unknown; starts after mesh/texture loading works
  4. Flight model — document by playing the original; implement as Bevy system

Constraints

  1. Never copy decompiled or disassembled game code — reimplement behavior through observation only.
  2. Assets stay gitignoredassets/ and *.iso are excluded. Never commit game files.
  3. Keep sylpheed-formats Bevy-free — parsers must be testable without a GPU.
  4. WASM must always compile — CI enforces cargo check --target wasm32-unknown-unknown.
  5. justfile is the source of truth for build commands — add new recipes there.

Milestone Roadmap

Milestone Goal Status
1 Asset Explorer 🚧 In Progress
2 Flying Tech Demo Planned
3 Combat Prototype Planned
4 Mission 1 Playable Planned
5 Full Game (all 16 missions) Planned
6 Mod SDK Planned

Useful References

Description
No description provided
Readme MIT 820 MiB
Languages
Rust 57.6%
Python 27.7%
Shell 10.6%
GDScript 3.5%
Dockerfile 0.4%
Other 0.1%