Skip to content

Instantly share code, notes, and snippets.

@scurest
Created May 11, 2019 13:31
Show Gist options
  • Save scurest/635f2ab5fe0651bf0cc10113e50cc6e2 to your computer and use it in GitHub Desktop.
Save scurest/635f2ab5fe0651bf0cc10113e50cc6e2 to your computer and use it in GitHub Desktop.
Use ndstool and apicula to extract model files from an NDS ROM with the original filesystem structure.
#!/usr/bin/env python3
# Use ndstool to dump the FS off an NDS ROM, then use apicula to recursively
# search all files for models, etc.
import sys, os, subprocess, random, shutil
# !!! IMPORTANT !!!
# Change these to the paths to ndstool and apicula on your system.
NDSTOOL_CMD = 'ndstool'
APICULA_CMD = 'apicula'
def main(rom_path, out_dir):
if os.path.exists(out_dir):
raise Exception('Output directory already exists!')
# Dir for ndstool to extract data files to
data_dir = os.path.join(out_dir, 'tmp.d.' + hex(random.getrandbits(96))[2:])
# Temp dir for apicula to put extracted files in
extract_dir = os.path.join(out_dir, 'tmp.x.' + hex(random.getrandbits(96))[2:])
def extract(path):
try:
rm(extract_dir)
subprocess.run(
[APICULA_CMD, 'x', os.path.join(data_dir, path), '-o', extract_dir],
check=True,
)
if not os.path.isdir(extract_dir):
return
files = os.listdir(extract_dir)
if not files:
return
if len(files) == 1:
# If one file is extracted, use the original filename plus the
# extension of the extracted file
extension = files[0][files[0].rfind('.')+1:]
out_path = os.path.join(out_dir, path + '.' + extension)
os.makedirs(os.path.dirname(out_path), exist_ok=True)
shutil.move(os.path.join(extract_dir, files[0]), out_path)
else:
shutil.move(extract_dir, os.path.join(out_dir, path + '.d'))
except Exception as e:
print(f'error extracting {path}: {e}')
try:
os.makedirs(out_dir, exist_ok=True)
subprocess.run(
[NDSTOOL_CMD, '-x', rom_path, '-d', data_dir],
check=True,
)
for dirpath, __dirnames, filenames in os.walk(data_dir):
for filename in filenames:
path = os.path.join(dirpath, filename)
relpath = os.path.relpath(path, data_dir)
extract(relpath)
finally:
rm(data_dir)
rm(extract_dir)
def rm(path):
try:
shutil.rmtree(path)
except Exception:
pass
if len(sys.argv) != 3:
print('Usage: extract_nds_models.py PATH_TO_ROM OUTPUT_DIR')
else:
main(sys.argv[1], sys.argv[2])
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment