mirror of
https://gitea.tendokyu.moe/beerpsi/fsdecrypt.git
synced 2026-10-07 06:07:56 +03:00
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
104 lines
3.4 KiB
Markdown
104 lines
3.4 KiB
Markdown
# 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)
|
|
- 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](https://www.rust-lang.org/tools/install) 1.56+ (edition 2021).
|
|
|
|
```bash
|
|
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
|
|
|
|
```bash
|
|
# Decrypt and extract an APP container
|
|
fsdecrypt SDXX_1.00.00_20240101120000_0.app
|
|
|
|
# Decrypt and extract an OPTION container
|
|
fsdecrypt SDXX_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 SDXX_1.00.00_20240101120000_0.app
|
|
```
|
|
|
|
### Output
|
|
|
|
By default, the tool extracts container contents directly:
|
|
|
|
| Type | Extracted contents |
|
|
|--------|--------------------|
|
|
| OS | `internal_{seq}.vhd` |
|
|
| APP | `internal_{seq}.vhd` |
|
|
| 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](LICENSE) (0BSD)
|