64DD disk image splitting and decompilation helpers
Project description
leosplit
A binary splitting tool for 64DD .ndd images, built to assist decompilation and modding projects.
leosplit is intended to fill a role similar to Splat for 64DD disk images:
identify loadable binaries, record their disk/LBA and RDRAM mapping metadata,
then extract those ranges into standalone files for tools like Ghidra.
Features
- 64DD Load Table Detection: Finds LBA ranges paired with N64 RDRAM load/entry addresses
- Mario Artist 64DD Metadata Fallback: Extracts repeated disk metadata labels only when no load tables are found
- DOL Fallback: Automatically detects and parses GameCube DOL executable files if native metadata is not found
- Multiple Output Formats: JSON (default) or YAML output
- File Identification: Extracts cartridge file/module names and metadata
- MFS Binary Extraction: Traverses 64DD MFS directory entries and carves exact byte ranges from
.nddimages - Manifest Binary Extraction: Still supports generated manifests for load-table based workflows
- LeoSplit Assembly Workspace: Emits raw bins, MIPS assembly listings, symbol hints, rebuild metadata, and build scaffolding
- Exact Image Rebuilds: Reconstructs an
.nddfrom split bins and compares SHA-1 against the original
Usage
Install for local development:
python -m pip install -e .
Generate a manifest:
leosplit-manifest input.ndd -o manifest.json
Extract files directly from a 64DD MFS image:
leosplit-extract input.ndd -o extracted
List detected MFS entries without extracting:
leosplit-extract input.ndd --list
Extract binaries from a manifest:
leosplit-extract input.ndd manifest.json -o extracted
Create a Splat-like assembly workspace from the manifest:
leosplit-asm input.ndd manifest.json -o split --overwrite --verbose
LeoSplit infers the YAML project name and basename from known 64DD disk codes, manifest metadata, embedded title strings, or finally the image filename. It also emits a compiler guess with a detection reason. Override any of these when you know better:
leosplit-asm NUD-DSCJ-JPN.ndd simcity64.json -o split-simcity --overwrite \
--name "SimCity 64" --basename simcity64 --compiler IDO
Restrict disassembly when you know a specific code span:
leosplit-asm input.ndd manifest.json -o split --overwrite \
--code-range 3:0x80280000-0x802C0000
This writes:
split/bin/*.bin: exact carved segment bytessplit/asm/*.s: big-endian MIPS assembly listings using manifest VRAM addressessplit/symbols/*.sym: entry labels, branch/jump labels, and rough data boundary hintssplit/macro.inc: assembler compatibility macrossplit/<basename>.ld: a generated MIPS linker script scaffoldsplit/Makefile: rebuild and compare targetssplit/leosplit_workspace.json: machine-readable rebuild metadatasplit/leosplit.yaml: a human-readable segment skeleton for the project
Rebuild the image from the workspace bins:
leosplit-build split -o split/build/rebuilt.ndd --base input.ndd --compare input.ndd
Or from inside the generated workspace:
cd split
make compare
The manifest includes:
file_id: Unique identifier for each file entryfile_name: Extracted cartridge metadata or file namelba_start: Logical block address (sector-based offset)lba_length: Length in sectorsload_address: N64 RDRAM load address (if available)entry_point: N64 program entry point (if available)
Output Formats
- JSON (default):
leosplit-manifest input.ndd -o manifest.json - YAML:
leosplit-manifest input.ndd --format yaml
The extractor accepts either generated JSON or generated YAML:
leosplit-extract NUD-DMTJ-JPN1.ndd talentstudio.json -o extracted
leosplit-extract NUD-DMTJ-JPN1.ndd talentstudio.yaml -o extracted --overwrite
Example
# Generate JSON manifest
leosplit-manifest NUD-DMTJ-JPN1.ndd -o manifest.json
# Output YAML to stdout
leosplit-manifest input.ndd --format yaml
# Verbose output with parsing details
leosplit-manifest input.ndd --verbose
# Extract files and print offsets/load addresses
leosplit-extract input.ndd manifest.json -o extracted --verbose
# Build a decompilation workspace with asm and symbol hints
leosplit-asm input.ndd manifest.json -o split --overwrite --verbose
# Rebuild and compare the image from split bins
leosplit-build split -o split/build/rebuilt.ndd --base input.ndd --compare input.ndd
Extractor output files are named with the manifest ID and sanitized file name,
for example extracted/03_NICHIYOUBI.bin.
How It Works
-
Primary Method: Scans for 64DD load table records
- Looks for
lba_start,lba_end,ram_start,ram_end, and entry/init addresses - Keeps clustered records to avoid treating random data as files
- Uses nearby ASCII labels when available, otherwise names entries by table offset
- Looks for
-
Fallback Method: If no metadata found, searches for embedded DOL (GameCube executable) headers
- Validates DOL header structure
- Extracts load address and entry point from RDRAM addresses
-
Direct MFS Extraction: Reads 64DD MFS directory entries
- Uses the real zone-dependent
.nddLBA map for full 64DD images - Applies each entry's start LBA, intra-block offset, and byte-exact file size
- Writes carved files using their MFS name/type metadata
- Uses the real zone-dependent
-
Manifest Extraction: Reads each manifest entry
- Uses the real
.nddLBA map for full 64DD images, or fixed sectors for test images - Reads
lba_lengthblocks unless a byte-exactfile_sizeis present - Writes the result as a standalone
.bin
- Uses the real
-
Assembly Workspace Generation: Uses manifest load metadata as segment hints
- Treats each carved file as a loaded N64 MIPS segment
- Infers project name/basename from disk code, embedded strings, manifest data, or filename
- Detects obvious compiler markers, otherwise records the N64/N64DD IDO default assumption
- Disassembles words using big-endian MIPS decoding
- Accepts explicit code ranges by file ID, manifest name, or generated segment name
- Labels entry points and local branch/jump targets
- Emits comments for possible data boundaries such as string regions and long zero runs
-
Workspace Rebuilds: Uses generated workspace metadata
- Patches
bin/*.binback into a base image at the original ROM offsets - Rejects segment size mismatches by default
- Can compare rebuilt output against the original image by SHA-1
- Generates
macro.inc,<basename>.ld, andMakefileso the workspace can grow into a self-contained decomp project
- Patches
Testing
python -m pytest
Sample Output
{
"source_file": "NUD-DMTJ-JPN1.ndd",
"sector_size": 2048,
"file_count": 17,
"files": [
{
"file_id": 1,
"file_name": "keyword_pmotion2",
"lba_start": 43,
"lba_length": 1,
"load_address": "0x80218980",
"entry_point": "0x802189D0"
}
]
}
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file leosplit-1.2.0a1.tar.gz.
File metadata
- Download URL: leosplit-1.2.0a1.tar.gz
- Upload date:
- Size: 29.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
29f94e5f6d4275734a8a0493969cfddf8f07230b7d1dce3b830856d6ed525cce
|
|
| MD5 |
55d13e864f9e1e63056df978b40924c3
|
|
| BLAKE2b-256 |
ea4d5e714882bf33c6f2ce2937ece6eb44f15aa1ca6a2d5bff5fb95bcaebf6a2
|
File details
Details for the file leosplit-1.2.0a1-py3-none-any.whl.
File metadata
- Download URL: leosplit-1.2.0a1-py3-none-any.whl
- Upload date:
- Size: 25.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1f51bf5445b7302bcff484063561d51066b1a6f5072ca8ebd52e434f215519db
|
|
| MD5 |
c6e74b45063688bf9a206b9209efc414
|
|
| BLAKE2b-256 |
8ca0504dd8f5f56d4321f75dc77cf8bae5f5ed80c192d8cd42d737dd82c42a12
|