Пример #1
0
def blockCompressXci(filePath, compressionLevel, blockSizeExponent, outputDir,
                     threads):
    filePath = filePath.resolve()
    container = factory(filePath)
    container.open(str(filePath), 'rb')
    secureIn = container.hfs0['secure']
    xczPath = outputDir.joinpath(filePath.stem + '.xcz')

    Print.info('Block compressing (level {0}) {1} -> {2}'.format(
        compressionLevel, filePath, xczPath))

    try:
        with Xci.XciStream(
                str(xczPath), originalXciPath=filePath
        ) as xci:  # need filepath to copy XCI container settings
            with Hfs0.Hfs0Stream(xci.hfs0.add('secure', 0),
                                 xci.f.tell()) as secureOut:
                blockCompressContainer(secureIn, secureOut, compressionLevel,
                                       blockSizeExponent, threads)

            xci.hfs0.resize('secure', secureOut.actualSize)
    except BaseException as ex:
        if not ex is KeyboardInterrupt:
            Print.error(format_exc())
        if xczPath.is_file():
            xczPath.unlink()

    container.close()
    return xczPath
Пример #2
0
def solidCompressTask(in_queue, statusReport, readyForWork, pleaseNoPrint,
                      pleaseKillYourself, id):
    while True:
        readyForWork.increment()
        item = in_queue.get()
        readyForWork.decrement()
        if pleaseKillYourself.value() > 0:
            break
        try:
            filePath, compressionLevel, outputDir, threadsToUse, verifyArg = item
            outFile = solidCompress(filePath, compressionLevel, outputDir,
                                    threadsToUse, statusReport, id,
                                    pleaseNoPrint)
            if verifyArg:
                Print.info("[VERIFY NSZ] {0}".format(outFile))
                try:
                    verify(outFile, True, [statusReport, id], pleaseNoPrint)
                except VerificationException:
                    Print.error("[BAD VERIFY] {0}".format(outFile))
                    Print.error("[DELETE NSZ] {0}".format(outFile))
                    remove(outFile)
        except KeyboardInterrupt:
            Print.info('Keyboard exception')
        except BaseException as e:
            Print.info('nut exception: {0}'.format(str(e)))
            raise
Пример #3
0
def AllowedToWriteOutfile(filePath, targetFileExtension, targetDict, removeOld,
                          overwrite, parseCnmt):
    (filesAtTarget, alreadyExists) = targetDict
    extractedIdVersion = ExtractTitleIDAndVersion(filePath, parseCnmt)
    if extractedIdVersion == None:
        if parseCnmt:
            Print.error(
                'Failed to extract TitleID/Version from booth filename "{0}" and Cnmt - Outdated keys.txt?'
                .format(Path(filePath).name))
        else:
            Print.error(
                'Failed to extract TitleID/Version from filename "{0}". Use -p to extract from Cnmt.'
                .format(Path(filePath).name))
        return fileNameCheck(filePath, targetFileExtension, filesAtTarget,
                             removeOld, overwrite)
    (titleIDExtracted, versionExtracted) = extractedIdVersion
    titleIDEntry = alreadyExists.get(titleIDExtracted)

    if titleIDEntry != None:
        DuplicateEntriesToDelete = []
        OutdatedEntriesToDelete = []
        exitFlag = False
        for versionEntry in titleIDEntry.keys():
            if versionEntry == versionExtracted:
                if overwrite:
                    DuplicateEntriesToDelete.append(versionEntry)
                else:
                    Print.info('{0} with the same ID and version already exists in the output directory.\n'\
                    'If you want to overwrite it use the -w parameter!'.format(titleIDEntry[versionEntry]))
                    return False
            elif versionEntry < versionExtracted:
                if removeOld:
                    if versionEntry == 0:
                        raise ValueError(
                            "rm-old-version: A titleID containing updates should never have any version v0 with the same titleID!"
                        )
                    OutdatedEntriesToDelete.append(versionEntry)
            else:  #versionEntry > versionExtracted
                if removeOld:
                    exitFlag = True
        if exitFlag:
            Print.info('{0} with a the same ID and newer version already exists in the output directory.\n'\
            'If you want to process it do not use --rm-old-version!'.format(titleIDEntry[versionEntry]))
            return False

        for versionEntry in DuplicateEntriesToDelete:
            for delFilePath in titleIDEntry[versionEntry]:
                Print.info('Delete duplicate: {0}'.format(delFilePath))
                remove(delFilePath)
                del filesAtTarget[Path(delFilePath).name.lower()]
            del titleIDEntry[versionEntry]
        for versionEntry in OutdatedEntriesToDelete:
            for delFilePath in titleIDEntry[versionEntry]:
                Print.info('Delete outdated version: {0}'.format(delFilePath))
                remove(delFilePath)
                del filesAtTarget[Path(delFilePath).name.lower()]
            del titleIDEntry[versionEntry]

    return fileNameCheck(filePath, targetFileExtension, filesAtTarget,
                         removeOld, overwrite)
Пример #4
0
def decompress(filePath, outputDir, statusReportInfo, pleaseNoPrint = None):
	if isNspNsz(filePath):
		__decompressNsz(filePath, outputDir, True, False, statusReportInfo, pleaseNoPrint)
	elif isXciXcz(filePath):
		__decompressXcz(filePath, outputDir, True, False, statusReportInfo, pleaseNoPrint)
	elif isCompressedGameFile(filePath):
		filename = changeExtension(filePath, '.nca')
		outPath = filename if outputDir == None else str(Path(outputDir).joinpath(filename))
		Print.info('Decompressing %s -> %s' % (filePath, outPath), pleaseNoPrint)
		try:
			inFile = factory(filePath)
			inFile.open(str(filePath), 'rb')
			with open(outPath, 'wb') as outFile:
				written, hexHash = __decompressNcz(inFile, outFile, statusReportInfo, pleaseNoPrint)
				fileNameHash = Path(filePath).stem.lower()
				if hexHash[:32] == fileNameHash:
					Print.info('[VERIFIED]   {0}'.format(filename), pleaseNoPrint)
				else:
					Print.info('[MISMATCH]   Filename startes with {0} but {1} was expected - hash verified failed!'.format(fileNameHash, hexHash[:32]), pleaseNoPrint)
		except BaseException as ex:
			if not ex is KeyboardInterrupt:
				Print.error(format_exc())
			if Path(outPath).is_file():
				Path(outPath).unlink()
		finally:
			inFile.close()
	else:
		raise NotImplementedError("Can't decompress {0} as that file format isn't implemented!".format(filePath))
Пример #5
0
    def verify(self):
        success = True
        for f in self:
            if not isinstance(f, Nca):
                continue
            hash = str(f.sha256())

            if hash[0:16] != str(f._path)[0:16]:
                Print.error('BAD HASH %s = %s' %
                            (str(f._path), str(f.sha256())))
                success = False

        return success
Пример #6
0
def load(fileName):
    try:
        global keyAreaKeys
        global titleKeks
        global loadedKeysFile
        loadedKeysFile = fileName

        with open(fileName, encoding="utf8") as f:
            for line in f.readlines():
                r = re.match('\s*([a-z0-9_]+)\s*=\s*([A-F0-9]+)\s*', line,
                             re.I)
                if r:
                    keys[r.group(1)] = r.group(2)

        aes_kek_generation_source = getKey('aes_kek_generation_source')
        aes_key_generation_source = getKey('aes_key_generation_source')
        titlekek_source = getKey('titlekek_source')
        key_area_key_application_source = getKey(
            'key_area_key_application_source')
        key_area_key_ocean_source = getKey('key_area_key_ocean_source')
        key_area_key_system_source = getKey('key_area_key_system_source')

        keyAreaKeys = []
        for i in range(32):
            keyAreaKeys.append([None, None, None])

        for i in range(32):
            if not existsMasterKey(i):
                continue
            masterKey = getMasterKey(i)
            crypto = aes128.AESECB(masterKey)
            titleKeks.append(crypto.decrypt(titlekek_source).hex())
            keyAreaKeys[i][0] = generateKek(key_area_key_application_source,
                                            masterKey,
                                            aes_kek_generation_source,
                                            aes_key_generation_source)
            keyAreaKeys[i][1] = generateKek(key_area_key_ocean_source,
                                            masterKey,
                                            aes_kek_generation_source,
                                            aes_key_generation_source)
            keyAreaKeys[i][2] = generateKek(key_area_key_system_source,
                                            masterKey,
                                            aes_kek_generation_source,
                                            aes_key_generation_source)
    except BaseException as e:
        Print.error(format_exc())
        Print.error(str(e))
Пример #7
0
def blockCompressNsp(filePath, compressionLevel , blockSizeExponent, outputDir, threads):
	filePath = filePath.resolve()
	container = factory(filePath)
	container.open(str(filePath), 'rb')
	nszPath = outputDir.joinpath(filePath.stem + '.nsz')

	Print.info('Block compressing (level {0}) {1} -> {2}'.format(compressionLevel, filePath, nszPath))
	
	try:
		with Pfs0.Pfs0Stream(str(nszPath)) as nsp:
			blockCompressContainer(container, nsp, compressionLevel, blockSizeExponent, threads)
	except BaseException as ex:
		if not ex is KeyboardInterrupt:
			Print.error(format_exc())
		if nszPath.is_file():
			nszPath.unlink()

	container.close()
	return nszPath
Пример #8
0
def CreateTargetDict(targetFolder, args, extension, filesAtTarget = {}, alreadyExists = {}):
	for filePath in expandFiles(targetFolder):
		try:
			filePath_str = str(filePath)
			if (isGame(filePath) or filePath.suffix == ".nspz" or filePath.suffix == ".nsx") and (extension == None or filePath.suffix == extension):
				print(filePath)
				Print.infoNoNewline('Extract TitleID/Version: {0} '.format(filePath.name))
				filesAtTarget[filePath.name.lower()] = filePath_str
				extractedIdVersion = ExtractTitleIDAndVersion(filePath, args)
				if extractedIdVersion == None:
					if args.parseCnmt or args.alwaysParseCnmt:
						Print.error('Failed to extract TitleID/Version from booth filename "{0}" and Cnmt - Outdated keys.txt?'.format(Path(filePath).name))
					else:
						Print.error('Failed to extract TitleID/Version from filename "{0}". Use -p to extract from Cnmt.'.format(Path(filePath).name))
					continue
				titleID, version = extractedIdVersion
				titleIDEntry = alreadyExists.get(titleID)
				if titleIDEntry == None:
					titleIDEntry = {version: [filePath_str]}
				elif not version in titleIDEntry:
					titleIDEntry[version] = [filePath_str]
				else:
					titleIDEntry[version].append(filePath_str)
				alreadyExists[titleID] = titleIDEntry
				Print.info('=> {0} {1}'.format(titleID, version))
		except BaseException as e:
			Print.info("")
			print_exc()
			Print.error('Error: ' + str(e))
	return(filesAtTarget, alreadyExists)
Пример #9
0
def solidCompressNsp(filePath, compressionLevel, outputDir, threads,
                     stusReport, id, pleaseNoPrint):
    filePath = filePath.resolve()
    container = factory(filePath)
    container.open(str(filePath), 'rb')
    nszPath = outputDir.joinpath(filePath.stem + '.nsz')

    Print.info(
        'Solid compressing (level {0}) {1} -> {2}'.format(
            compressionLevel, filePath, nszPath), pleaseNoPrint)

    try:
        with Pfs0.Pfs0Stream(str(nszPath)) as nsp:
            processContainer(container, nsp, compressionLevel, threads,
                             stusReport, id, pleaseNoPrint)
    except BaseException as ex:
        if not ex is KeyboardInterrupt:
            Print.error(format_exc())
        if nszPath.is_file():
            nszPath.unlink()

    container.close()
    return nszPath
Пример #10
0
def CreateTargetDict(targetFolder, parseCnmt, extension):
    filesAtTarget = {}
    alreadyExists = {}
    for file in scandir(str(targetFolder)):
        try:
            filePath = Path(targetFolder).joinpath(file.name)
            filePath_str = str(filePath)
            if filePath.suffix == extension:
                Print.infoNoNewline('Extract TitleID/Version: {0} '.format(
                    file.name))
                filesAtTarget[file.name.lower()] = filePath_str
                extractedIdVersion = ExtractTitleIDAndVersion(file, parseCnmt)
                if extractedIdVersion == None:
                    if parseCnmt:
                        Print.error(
                            'Failed to extract TitleID/Version from booth filename "{0}" and Cnmt - Outdated keys.txt?'
                            .format(Path(filePath).name))
                    else:
                        Print.error(
                            'Failed to extract TitleID/Version from filename "{0}". Use -p to extract from Cnmt.'
                            .format(Path(filePath).name))
                    continue
                titleID, version = extractedIdVersion
                titleIDEntry = alreadyExists.get(titleID)
                if titleIDEntry == None:
                    titleIDEntry = {version: [filePath_str]}
                elif not version in titleIDEntry:
                    titleIDEntry[version] = [filePath_str]
                else:
                    titleIDEntry[version].append(filePath_str)
                alreadyExists[titleID] = titleIDEntry
                Print.info('=> {0} {1}'.format(titleID, version))
        except BaseException as e:
            Print.info("")
            print_exc()
            Print.error('Error: ' + str(e))
    return (filesAtTarget, alreadyExists)
Пример #11
0
def main():
	global err
	try:
		if len(argv) > 1:
			args = ParseArguments.parse()
		else:
			try:
				from nsz.gui.NSZ_GUI import GUI
			except ImportError:
				Print.error("Failed to import the GUI - is it installed?")
				return
			args = GUI().run()
			if args == None:
				Print.info("Done!")
				return
		
		if args.output:
			outfolderToPharse = args.output
			if not outfolderToPharse.endswith('/') and not outfolderToPharse.endswith('\\'):
				outfolderToPharse += "/"
			if not Path(outfolderToPharse).is_dir():
				Print.error('Error: Output directory "{0}" does not exist!'.format(args.output))
				return
		outfolder = Path(outfolderToPharse).resolve() if args.output else Path('.').resolve()
		
		Print.info('')
		Print.info('             NSZ v3.1   ,;:;;,')
		Print.info('                       ;;;;;')
		Print.info('               .=\',    ;:;;:,')
		Print.info('              /_\', "=. \';:;:;')
		Print.info('              @=:__,  \,;:;:\'')
		Print.info('                _(\.=  ;:;;\'')
		Print.info('               `"_(  _/="`')
		Print.info('                `"\'')
		Print.info('')
		
		barManager = enlighten.get_manager()
		poolManager = Manager()
		statusReport = poolManager.list()
		readyForWork = Counter(0)
		pleaseNoPrint = Counter(0)
		pleaseKillYourself = Counter(0)
		pool = []
		work = poolManager.Queue()
		amountOfTastkQueued = Counter(0)
		
		if args.titlekeys:
			extractTitlekeys(args.file)
		
		if args.extract:
			for f_str in args.file:
				for filePath in expandFiles(Path(f_str)):
					filePath_str = str(filePath)
					Print.info('Extracting "{0}" to {1}'.format(filePath_str, outfolder))
					dir = outfolder.joinpath(filePath.stem)
					container = factory(filePath)
					container.open(filePath_str, 'rb')
					if isXciXcz(filePath):
						for hfs0 in container.hfs0:
							secureIn = hfs0
							secureIn.unpack(dir.joinpath(hfs0._path), args.extractregex)
					else:
						container.unpack(dir, args.extractregex)
					container.close()

		if args.create:
			Print.info('Creating "{0}"'.format(args.create))
			nsp = Nsp.Nsp(None, None)
			nsp.path = args.create
			nsp.pack(args.file)

		if args.C:
			targetDictNsz = CreateTargetDict(outfolder, args.parseCnmt, ".nsz")
			targetDictXcz = CreateTargetDict(outfolder, args.parseCnmt, ".xcz")
			sourceFileToDelete = []
			for f_str in args.file:
				for filePath in expandFiles(Path(f_str)):
					if not isUncompressedGame(filePath):
						continue
					try:
						if filePath.suffix == '.nsp':
							if not AllowedToWriteOutfile(filePath, ".nsz", targetDictNsz, args.rm_old_version, args.overwrite, args.parseCnmt):
								continue
						elif filePath.suffix == '.xci':
							if not AllowedToWriteOutfile(filePath, ".xcz", targetDictXcz, args.rm_old_version, args.overwrite, args.parseCnmt):
								continue
						compress(filePath, outfolder, args, work, amountOfTastkQueued)
						if args.rm_source:
							sourceFileToDelete.append(filePath)
					except KeyboardInterrupt:
						raise
					except BaseException as e:
						Print.error('Error when compressing file: %s' % filePath)
						err.append({"filename":filePath,"error":format_exc() })
						print_exc()
			
			bars = []
			compressedSubBars = []
			BAR_FMT = u'{desc}{desc_pad}{percentage:3.0f}%|{bar}| {count:{len_total}d}/{total:d} {unit} [{elapsed}<{eta}, {rate:.2f}{unit_pad}{unit}/s]'
			parallelTasks = min(args.multi, amountOfTastkQueued.value())
			if parallelTasks < 0:
				parallelTasks = 4
			for i in range(parallelTasks):
				statusReport.append([0, 0, 100])
				p = Process(target=solidCompressTask, args=(work, statusReport, readyForWork, pleaseNoPrint, pleaseKillYourself, i))
				p.start()
				pool.append(p)
			for i in range(parallelTasks):
				bar = barManager.counter(total=100, desc='Compressing', unit='MiB', color='cyan', bar_format=BAR_FMT)
				compressedSubBars.append(bar.add_subcounter('green'))
				bars.append(bar)
			#Ensures that all threads are started and compleaded before being requested to quit
			while readyForWork.value() < parallelTasks:
				sleep(0.2)
				if pleaseNoPrint.value() > 0:
					continue
				pleaseNoPrint.increment()
				for i in range(parallelTasks):
					compressedRead, compressedWritten, total = statusReport[i]
					if bars[i].total != total:
						bars[i].total = total//1048576
					bars[i].count = compressedRead//1048576
					compressedSubBars[i].count = compressedWritten//1048576
					bars[i].refresh()
				pleaseNoPrint.decrement()
			pleaseKillYourself.increment()
			for i in range(readyForWork.value()):
				work.put(None)
			
			while readyForWork.value() > 0:
				sleep(0.02)
			
			for i in range(parallelTasks):
				bars[i].close(clear=True)
			#barManager.stop() #We aren’t using stop because of it printing garbage to the console.

			for filePath in sourceFileToDelete:
				delete_source_file(filePath)

		if args.D:
			targetDictNsz = CreateTargetDict(outfolder, args.parseCnmt, ".nsp")
			targetDictXcz = CreateTargetDict(outfolder, args.parseCnmt, ".xci")
			for f_str in args.file:
				for filePath in expandFiles(Path(f_str)):
					if not isCompressedGame(filePath) and not isCompressedGameFile(filePath):
						continue
					try:
						if filePath.suffix == '.nsz':
							if not AllowedToWriteOutfile(filePath, ".nsp", targetDictNsz, args.rm_old_version, args.overwrite, args.parseCnmt):
								continue
						elif filePath.suffix == '.xcz':
							if not AllowedToWriteOutfile(filePath, ".xci", targetDictXcz, args.rm_old_version, args.overwrite, args.parseCnmt):
								continue
						elif filePath.suffix == '.ncz':
							outfile = Path(changeExtension(outfolder.joinpath(filePath.name), ".nca"))
							if not args.overwrite and outfile.is_file():
								Print.info('{0} with the same file name already exists in the output directory.\n'\
								'If you want to overwrite it use the -w parameter!'.format(outfile.name))
								continue
						decompress(filePath, outfolder)
						if args.rm_source:
							delete_source_file(filePath)
					except KeyboardInterrupt:
						raise
					except BaseException as e:
						Print.error('Error when decompressing file: {0}'.format(filePath))
						err.append({"filename":filePath, "error":format_exc()})
						print_exc()

		if args.info:
			for f_str in args.file:
				for filePath in expandFiles(Path(f_str)):
					filePath_str = str(filePath)
					Print.info(filePath_str)
					f = factory(filePath)
					f.open(filePath_str, 'r+b')
					f.printInfo(args.depth+1)
					f.close()

		if args.verify and not args.C and not args.D:
			for f_str in args.file:
				for filePath in expandFiles(Path(f_str)):
					try:
						if isGame(filePath):
							Print.info("[VERIFY {0}] {1}".format(getExtensionName(filePath), filePath.name))
							verify(filePath, False)
					except KeyboardInterrupt:
						raise
					except BaseException as e:
						Print.error('Error when verifying file: {0}'.format(filePath))
						err.append({"filename":filePath,"error":format_exc()})
						print_exc()

		if len(argv) == 1:
			pass
	except KeyboardInterrupt:
		Print.info('Keyboard exception')
	except BaseException as e:
		Print.info('nut exception: {0}'.format(str(e)))
		raise
	if err:
		Print.info('\n\033[93m\033[1mSummary of errors which occurred while processing files:')
		
		for e in err:
			Print.info('\033[0mError while processing {0}'.format(e["filename"]))
			Print.info(e["error"])

	Print.info('Done!')
	_exit(0)
Пример #12
0
			if not existsMasterKey(i):
				continue
			masterKey = getMasterKey(i)
			crypto = aes128.AESECB(masterKey)
			titleKeks.append(crypto.decrypt(titlekek_source).hex())
			keyAreaKeys[i][0] = generateKek(key_area_key_application_source, masterKey, aes_kek_generation_source, aes_key_generation_source)
			keyAreaKeys[i][1] = generateKek(key_area_key_ocean_source, masterKey, aes_kek_generation_source, aes_key_generation_source)
			keyAreaKeys[i][2] = generateKek(key_area_key_system_source, masterKey, aes_kek_generation_source, aes_key_generation_source)
	except BaseException as e:
		Print.error(format_exc())
		Print.error(str(e))



keyScriptPath = Path(sys.argv[0])
#While loop to get rid of things like C:\\Python37\\Scripts\\nsz.exe\\__main__.py
while not keyScriptPath.is_dir():
	keyScriptPath = keyScriptPath.parents[0]
keypath = keyScriptPath.joinpath('keys.txt')
dumpedKeys = Path.home().joinpath(".switch", "prod.keys")
if keypath.is_file():
	load(str(keypath))
elif dumpedKeys.is_file():
	load(str(dumpedKeys))
else:
	errorMsg = "{0} or {1} not found!\nPlease dump your keys using https://github.com/shchmue/Lockpick_RCM/releases".format(str(keypath), str(dumpedKeys))
	Print.error(errorMsg)
	input("Press Enter to exit...")
	sys.exit(1)