Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 49 additions & 23 deletions splitNSP.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/usr/bin/env python3
# Author: AnalogMan
# Modified Date: 2018-10-08
# Purpose: Splits Nintendo Switch NSP files into parts for installation on FAT32
# Purpose: Splits Nintendo Switch NSP or NSZ files into parts for installation on FAT32

import os
import argparse
Expand All @@ -18,16 +18,26 @@ def splitQuick(filepath):
if info.free < splitSize:
print('Not enough temporary space. Needs 4GiB of free space\n')
return

# Identify extension and create directory name accordingly
extension = os.path.splitext(filepath)[1].lower()
extension_name = 'NSP'
# Fallback to .nsp if neither .nsp nor .nsz
if extension not in ('.nsp', '.nsz'):
extension = '.nsp'
if extension == '.nsz':
extension_name = 'NSZ'

print('Calculating number of splits...\n')
splitNum = int(fileSize/splitSize)
if splitNum == 0:
print('This NSP is under 4GiB and does not need to be split.\n')
print('This ' + extension_name + ' is under 4GiB and does not need to be split.\n')
return

print('Splitting NSP into {0} parts...\n'.format(splitNum + 1))

print('Splitting ' + extension_name + ' into {0} parts...\n'.format(splitNum + 1))
dir = filepath[:-4] + '_split' + extension

# Create directory, delete if already exists
dir = filepath[:-4] + '_split.nsp'
if os.path.exists(dir):
shutil.rmtree(dir)
os.makedirs(dir)
Expand Down Expand Up @@ -62,7 +72,7 @@ def splitQuick(filepath):
partSize = 0
print('Starting part {:02}'.format(splitNum - (i + 1)))
with open(outFile, 'wb') as splitFile:
while partSize < splitSize:
while partSize < splitSize:
splitFile.write(nspFile.read(chunkSize))
partSize += chunkSize
nspFile.seek(splitSize * -1, os.SEEK_END)
Expand All @@ -72,29 +82,45 @@ def splitQuick(filepath):
# Print assurance statement for user
print('Starting part 00\nPart 00 complete')

print('\nNSP successfully split!\n')
print('\n' + extension_name + ' successfully split!\n')

def splitCopy(filepath, output_dir=""):
# Identify extension and create extension name accordingly
extension = os.path.splitext(filepath)[1].lower()
extension_name = 'NSP'
# Fallback to .nsp if neither .nsp nor .nsz
if extension not in ('.nsp', '.nsz'):
extension = '.nsp'
if extension == '.nsz':
extension_name = 'NSZ'

fileSize = os.path.getsize(filepath)
info = shutil.disk_usage(os.path.dirname(os.path.abspath(filepath)))
if info.free < fileSize*2:
print('Not enough free space to run. Will require twice the space as the NSP file\n')
print('Not enough free space to run. Will require twice the space as the ' + extension_name + ' file\n')
return
print('Calculating number of splits...\n')
splitNum = int(fileSize/splitSize)
if splitNum == 0:
print('This NSP is under 4GiB and does not need to be split.\n')
print('This ' + extension_name + ' file is under 4GiB and does not need to be split.\n')
return

print('Splitting NSP into {0} parts...\n'.format(splitNum + 1))
print('Splitting ' + extension_name + ' file into {0} parts...\n'.format(splitNum + 1))

# Identify extension and handle directory naming
extension = os.path.splitext(filepath)[1].lower()
if extension not in ('.nsp', '.nsz'):
extension = '.nsp'

# Create directory, delete if already exists
if output_dir == "":
dir = filepath[:-4] + '_split.nsp'
dir = filepath[:-4] + '_split' + extension
else:
if output_dir[-4:] != '.nsp':
output_dir+= ".nsp"
# If the user provided an output directory that doesn't match .nsp or .nsz, default to .nsp
if not output_dir.lower().endswith(('.nsp', '.nsz')):
output_dir += '.nsp'
dir = output_dir

if os.path.exists(dir):
shutil.rmtree(dir)
os.makedirs(dir)
Expand All @@ -107,7 +133,7 @@ def splitCopy(filepath, output_dir=""):
partSize = 0
print('Starting part {:02}'.format(i))
outFile = os.path.join(dir, '{:02}'.format(i))
with open(outFile, 'wb') as splitFile:
with open(outFile, 'wb') as splitFile:
if remainingSize > splitSize:
while partSize < splitSize:
splitFile.write(nspFile.read(chunkSize))
Expand All @@ -118,14 +144,14 @@ def splitCopy(filepath, output_dir=""):
splitFile.write(nspFile.read(chunkSize))
partSize += chunkSize
print('Part {:02} complete'.format(i))
print('\nNSP successfully split!\n')
print('\n' + extension_name + ' successfully split!\n')

def main():
print('\n========== NSP Splitter ==========\n')
print('\n========== NSP/NSZ Splitter ==========\n')

# Arg parser for program options
parser = argparse.ArgumentParser(description='Split NSP files into FAT32 compatible sizes')
parser.add_argument('filepath', help='Path to NSP file')
parser = argparse.ArgumentParser(description='Split NSP or NSZ files into FAT32 compatible sizes')
parser.add_argument('filepath', help='Path to NSP or NSZ file')
group = parser.add_mutually_exclusive_group()
group.add_argument('-q', '--quick', action='store_true', help='Splits file in-place without creating a copy. Only requires 4GiB free space to run')
group.add_argument('-o', '--output-dir', type=str, default="",
Expand All @@ -136,16 +162,16 @@ def main():

filepath = args.filepath

# Check if required files exist
if os.path.isfile(filepath) == False:
print('NSP cannot be found\n')
# Check if required file exists
if not os.path.isfile(filepath):
print('File cannot be found\n')
return 1

# Split NSP file
# Split file
if args.quick:
splitQuick(filepath)
else:
splitCopy(filepath, args.output_dir)

if __name__ == "__main__":
main()
main()