JujuforceandClaude Opus 4.6 2121b43699 feat: pure Rust VHD merge, remove PowerShell dependency
Replace Set-VHD + Merge-VHD PowerShell cmdlets with a pure Rust
MergedVhdReader that overlays delta blocks on the base VHD using
the sector bitmap to decide which sectors come from which disk.
No actual merge to disk needed — reads directly from both files.

- No admin privileges or UAC prompts required
- No Hyper-V or PowerShell dependency
- Cross-platform (works on Windows and Unix)
- Remove #[cfg(windows)] from merge/extract path

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 16:40:18 +01:00
2026-03-22 16:39:52 +01:00
2026-01-21 09:26:00 +07:00
2026-01-21 09:26:00 +07:00
2024-07-21 23:59:02 +07:00

fsdecrypt

Decryptor and extractor for SEGA arcade filesystem containers (fscrypt format).

Handles AES-128-CBC encrypted container images used on SEGA Nu/ALLS arcade hardware, automatically parsing the embedded BootID header to identify the game and container type, then decrypting and extracting the contents.

Features

  • Decrypts OS, APP, and OPTION (DLC) containers
  • Extracts NTFS-based containers (OS/APP) including internal VHD images
  • Extracts ExFAT-based containers (OPTION/DLC packs)
  • Auto-merges delta update VHDs with their base on Windows (Hyper-V)
  • Preserves file timestamps during extraction
  • Built-in key database for 70+ game titles
  • Supports external key files for unlisted games
  • Progress bars with transfer speed and ETA

Installation

Requires Rust 1.56+ (edition 2021).

cargo build --release

The binary will be at target/release/fsdecrypt (or fsdecrypt.exe on Windows).

Usage

fsdecrypt [OPTIONS] <FILES>...

Arguments

  • <FILES>... - One or more encrypted container files (.app, .opt, etc.)

Options

  • --no-extract - Decrypt the container to a raw image file without extracting its contents
  • -h, --help - Print help
  • -V, --version - Print version

Examples

# Decrypt and extract an APP container
fsdecrypt ABCD_1.00.00_20240101120000_0.app

# Decrypt and extract an OPTION container
fsdecrypt ABCD_A001_20240101120000_0.opt

# Process multiple containers at once
fsdecrypt game_v1.app game_v2.app extras.opt

# Decrypt only, skip extraction
fsdecrypt --no-extract ABCD_1.00.00_20240101120000_0.app

Delta Updates

When a game ships incremental updates, you get a base .app (seq=0) and one or more delta .app files (seq>0). Pass them all together and fsdecrypt handles the rest:

# Base + delta update: fsdecrypt extracts both VHDs, then auto-merges
fsdecrypt ABCD_1.00.00_20240101120000_0.app ABCD_1.01.00_20240215143000_1_1.00.00.app

The merge workflow (Windows only, requires Hyper-V):

  1. Both containers are decrypted and their internal VHDs extracted
  2. Set-VHD links the delta (differencing) VHD to its parent
  3. Merge-VHD merges the delta into the base VHD
  4. A UAC prompt will appear since these cmdlets require elevation

If you only provide the delta without its base, fsdecrypt will extract the VHD and print a warning with instructions.

Output

By default, the tool extracts container contents directly:

Type Extracted contents
OS internal_{seq}.vhd
APP internal_{seq}.vhd (auto-merged if delta + base provided)
OPTION Directory with all DLC files

With --no-extract, a raw decrypted image is written instead:

Type Output filename
OS {os_id}_{version}_{timestamp}_{seq}.ntfs
APP {game_id}_{version}_{timestamp}_{seq}.ntfs
OPTION {game_id}_{option}_{timestamp}_{seq}.exfat

External Key Files

For games not in the built-in database, place a key file named {GAME_ID}.bin in the working directory:

  • 16 bytes: AES-128 key only (IV will be derived automatically)
  • 32 bytes: AES-128 key (first 16 bytes) + IV (last 16 bytes)

How It Works

  1. The first 128 bytes of the container are decrypted using a hardcoded master key to obtain the BootID header
  2. The BootID contains metadata: game ID, container type, block layout, and an IV mode flag
  3. The game-specific AES-128 key is looked up from the built-in database (or read from an external .bin file)
  4. If no IV is hardcoded or the container uses a custom IV, the IV is derived by trial-decrypting the first data page against the expected filesystem header (NTFS or ExFAT magic bytes)
  5. Each 4096-byte page is decrypted independently using AES-128-CBC, with a per-page IV computed by XORing the file IV with the page's file offset
  6. The decrypted stream is parsed as NTFS or ExFAT depending on container type, and contents are extracted

Container Types

Type ID Filesystem Description
OS 0x00 NTFS Operating system image
APP 0x01 NTFS Game application and assets
OPTION 0x02 ExFAT Downloadable content / option packs

License

BSD Zero Clause License (0BSD)

S
Description
No description provided
Readme 0BSD
10 MiB
Languages
Erlang 97.9%
Rust 2%