Repository: AnalogMan151/splitNSP Branch: master Commit: d5cf9157cc0d Files: 3 Total size: 8.0 KB Directory structure: gitextract_apmmmq6c/ ├── LICENSE ├── README.md └── splitNSP.py ================================================ FILE CONTENTS ================================================ ================================================ FILE: LICENSE ================================================ This is free and unencumbered software released into the public domain. Anyone is free to copy, modify, publish, use, compile, sell, or distribute this software, either in source code form or as a compiled binary, for any purpose, commercial or non-commercial, and by any means. In jurisdictions that recognize copyright laws, the author or authors of this software dedicate any and all copyright interest in the software to the public domain. We make this dedication for the benefit of the public at large and to the detriment of our heirs and successors. We intend this dedication to be an overt act of relinquishment in perpetuity of all present and future rights to this software under copyright law. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. For more information, please refer to ================================================ FILE: README.md ================================================ # splitNSP As some have become aware, it's been found out that the official Nintendo SDK contains a PowerShell script for splitting NSP files into 4GiB chunks so that they can be installed from FAT32 filesystems. Seeing as the official script cannot be shared, I re-wrote it in Python3 (which makes it useable on more than just Windows) as well as added in an additional feature. To run it you'll need Python3 installed. Once installed, call the script from Terminal or Command Prompt with the following: ```python3 splitNSP.py filename.nsp``` By default this will make a copy of the NSP and split it up into parts. Once created, you'll need to open the folder's properties and check the Archive flag. This is easily done on Windows, I'm still working on a way to do it for macOS since file flags aren't saved when copying to FAT32. You can also set the archive flag on a folder directly on the Switch using a homebrew such as NX-Shell. You can also activate quick mode with this command: ```python3 splitNSP.py -q filename.nsp``` This will not make a copy of the NSP and instead will split the original. This is useful if you're running low on space as it only requires that you have 4GiB of temporary space to run it. It's also much faster. Once the folder is made and the archive flag is set copy it to your SD card (sdmc:/tinfoil/nsp/ if using tinfoil) and install it like any other NSP. If you have any issues feel free to submit an issue and I'll try my best to work it out. ================================================ FILE: splitNSP.py ================================================ #!/usr/bin/env python3 # Author: AnalogMan # Modified Date: 2018-10-08 # Purpose: Splits Nintendo Switch NSP files into parts for installation on FAT32 import os import argparse import shutil from datetime import datetime startTime = datetime.now() splitSize = 0xFFFF0000 # 4,294,901,760 bytes chunkSize = 0x8000 # 32,768 bytes def splitQuick(filepath): fileSize = os.path.getsize(filepath) info = shutil.disk_usage(os.path.dirname(os.path.abspath(filepath))) if info.free < splitSize: print('Not enough temporary space. Needs 4GiB of free space\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') return print('Splitting NSP into {0} parts...\n'.format(splitNum + 1)) # Create directory, delete if already exists dir = filepath[:-4] + '_split.nsp' if os.path.exists(dir): shutil.rmtree(dir) os.makedirs(dir) # Move input file to directory and rename it to first part filename = os.path.basename(filepath) shutil.move(filepath, os.path.join(dir, '00')) filepath = os.path.join(dir, '00') # Calculate size of final part to copy first finalSplitSize = fileSize - (splitSize * splitNum) # Copy final part and trim from main file with open(filepath, 'r+b') as nspFile: nspFile.seek(finalSplitSize * -1, os.SEEK_END) outFile = os.path.join(dir, '{:02}'.format(splitNum)) partSize = 0 print('Starting part {:02}'.format(splitNum)) with open(outFile, 'wb') as splitFile: while partSize < finalSplitSize: splitFile.write(nspFile.read(chunkSize)) partSize += chunkSize nspFile.seek(finalSplitSize * -1, os.SEEK_END) nspFile.truncate() print('Part {:02} complete'.format(splitNum)) # Loop through additional parts and trim with open(filepath, 'r+b') as nspFile: for i in range(splitNum - 1): nspFile.seek(splitSize * -1, os.SEEK_END) outFile = os.path.join(dir, '{:02}'.format(splitNum - (i + 1))) partSize = 0 print('Starting part {:02}'.format(splitNum - (i + 1))) with open(outFile, 'wb') as splitFile: while partSize < splitSize: splitFile.write(nspFile.read(chunkSize)) partSize += chunkSize nspFile.seek(splitSize * -1, os.SEEK_END) nspFile.truncate() print('Part {:02} complete'.format(splitNum - (i + 1))) # Print assurance statement for user print('Starting part 00\nPart 00 complete') print('\nNSP successfully split!\n') def splitCopy(filepath, output_dir=""): 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') 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') return print('Splitting NSP into {0} parts...\n'.format(splitNum + 1)) # Create directory, delete if already exists if output_dir == "": dir = filepath[:-4] + '_split.nsp' else: if output_dir[-4:] != '.nsp': output_dir+= ".nsp" dir = output_dir if os.path.exists(dir): shutil.rmtree(dir) os.makedirs(dir) remainingSize = fileSize # Open source file and begin writing to output files stoping at splitSize with open(filepath, 'rb') as nspFile: for i in range(splitNum + 1): partSize = 0 print('Starting part {:02}'.format(i)) outFile = os.path.join(dir, '{:02}'.format(i)) with open(outFile, 'wb') as splitFile: if remainingSize > splitSize: while partSize < splitSize: splitFile.write(nspFile.read(chunkSize)) partSize += chunkSize remainingSize -= splitSize else: while partSize < remainingSize: splitFile.write(nspFile.read(chunkSize)) partSize += chunkSize print('Part {:02} complete'.format(i)) print('\nNSP successfully split!\n') def main(): print('\n========== NSP 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') 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="", help="Set alternative output dir") # Check passed arguments args = parser.parse_args() filepath = args.filepath # Check if required files exist if os.path.isfile(filepath) == False: print('NSP cannot be found\n') return 1 # Split NSP file if args.quick: splitQuick(filepath) else: splitCopy(filepath, args.output_dir) if __name__ == "__main__": main()