Sylpheed RE agent 4f1f5ac0a2 re: the unreached code -- five mechanisms eliminated, nothing in the file names it
Inverting the question -- read what an unreached routine DOES, rather than testing
another candidate mechanism -- sharpened this a lot without solving it.

The metric was partly inflated.  Of 24901 unreached instructions in 564 runs, 458 runs
are length 1, and 111 of those are a lone `ret` stranded after an end_coroutine, with
the next routine being a properly seeded entry.  Stage 02's first two "unreached
routines" are exactly that.  But those are only 458 instructions: the real gap is
24443 in 106 runs, the largest 1526 (Stage 26), 1496 and 1481 (Stage 29), 1069, 904.

And it is live mission logic.  Stage 26's 1526-instruction run stages arguments and
calls builtin57(ADN110, 1) and set_group_speed(ADN110, 2, 0) -- named units, named
built-ins.

New this iteration: kill_coroutine (built-in 5) is confirmed as a code-offset carrier,
which is what the corpus said -- its local[0] resolves 150/150 onto the instruction
stream against a 34.0% control, values 13740..221048.  And, like the others, it points
at zero unreached run-starts.

That is now five mechanisms.  Across 2757 code references -- start_coroutine 1765, the
phase timeline 675, built-in 19's trigger handlers 79, kill_coroutine 150, and the
0x1883 records 88 -- not ONE lands on an unreached run-start.  A categorical absence,
not a near miss.

Not a tool artefact: 1765 of 1765 start_coroutine sites resolve with 0 missed, 0
entries land outside their phase region, and 0 are discarded by the on-stream filter.

Combined with the earlier result that an unreached routine's offset appears nowhere in
the file as a word in any encoding, these routines are referenced by nothing inside the
.ssb.  Either they are unused in this build, or they are entered from outside it.  A
1526-instruction routine driving a named squadron sits badly with "unused", but that
discomfort is not evidence and the two readings are recorded without choosing.

No static test I have devised can decide it; a runtime probe logging sub_822737C8's
third argument through a mission would.

All artefacts regenerate byte-identical; documentation only.
2026-08-27 07:46:56 +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%