Skip to content

Instantly share code, notes, and snippets.

@garoxas
Last active December 30, 2023 15:51
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save garoxas/b41b7acca5c4c833fe446c22951cf005 to your computer and use it in GitHub Desktop.
Save garoxas/b41b7acca5c4c833fe446c22951cf005 to your computer and use it in GitHub Desktop.
Extract XML from NSP
#!/usr/bin/env python
import argparse
import os
import struct
import sys
import xml.etree.ElementTree as etree
parser = argparse.ArgumentParser()
parser.add_argument('file', help='NSP file')
parser.add_argument('--output', '-o', help='Output directory')
parser.add_argument('--verbose', '-v', help='Display verbose log', action='store_true')
args = parser.parse_args()
# https://switchbrew.org/index.php?title=NCA_Format#PFS0
with open(args.file, 'rb') as fin:
magic = fin.read(4)
if magic != b'PFS0'[:4]:
print('Not a valid NSP file', file=sys.stderr)
sys.exit(1)
number_of_files = struct.unpack('i', fin.read(4))[0]
size_of_the_string_table = struct.unpack('i', fin.read(4))[0]
struct.unpack('i', fin.read(4))[0] # Zero/Reserved
string_table_offset = 0x10 + (0x18 * number_of_files)
data_offset = string_table_offset + size_of_the_string_table
if args.verbose:
print('Number of files: {}'.format(number_of_files), file=sys.stderr)
print('String Table offset: {:08X}'.format(string_table_offset), file=sys.stderr)
print('Data offset: {:08X}'.format(data_offset), file=sys.stderr)
print('', file=sys.stderr)
fin.seek(0x10)
files = []
for i in range(0, number_of_files):
offset_of_file_in_data = struct.unpack('q', fin.read(8))[0]
size_of_file_in_data = struct.unpack('q', fin.read(8))[0]
offset_of_filename_in_string_table = struct.unpack('i', fin.read(4))[0]
struct.unpack('i', fin.read(4))[0] # Zero/Reserved
files.append((offset_of_file_in_data, size_of_file_in_data, offset_of_filename_in_string_table, ))
for f in files:
fin.seek(string_table_offset + f[2])
filename = ''
while True:
c = fin.read(1)
if c == b'\x00':
break
filename += c.decode('utf-8')
if args.verbose:
print('Filename: {}'.format(filename), file=sys.stderr)
print('Offset of file in Data: {:08X}'.format(data_offset + f[0]), file=sys.stderr)
print('Size of file in Data: {:08X}'.format(f[1]), file=sys.stderr)
print('', file=sys.stderr)
fin.seek(data_offset + f[0])
xml = fin.read(f[1])
if args.output:
if not os.path.exists(args.output):
os.makedirs(args.output)
filename = args.output + '/' + filename
with open(filename, 'wb') as fout:
fout.write(xml)
print(filename)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment