Example #1
0
def __decompressNsz(filePath, outputDir, write, raiseVerificationException,
                    statusReportInfo):
    fileHashes = []  # FileExistingChecks.ExtractHashes(filePath)
    container = factory(filePath)
    container.open(str(filePath), 'rb')

    for f in container:
        if isCompressedGameFile(f._path):
            fileHashes.append(f._path.split('.')[0])

    if write:
        filename = changeExtension(filePath, '.nsp')
        outPath = filename if outputDir is None else os.path.join(
            outputDir, os.path.basename(filename))
        Print.info('Decompressing %s -> %s' % (filePath, outPath))

        if Config.dryRun:
            return outPath

        with Pfs0Stream(outPath) as nsp:
            __decompressContainer(container, nsp, fileHashes, write,
                                  raiseVerificationException, statusReportInfo)
        return outPath
    else:
        __decompressContainer(container, None, fileHashes, write,
                              raiseVerificationException, statusReportInfo)

    container.close()
    return None
Example #2
0
    def repack(self):
        newNsp = Pfs0Stream(self._path[:-4] + '.nsp')

        for nspF in self.hfs0['secure']:
            f = newNsp.add(nspF._path, nspF.size)
            nspF.rewind()
            i = 0

            pageSize = 0x10000

            while True:
                buf = nspF.read(pageSize)
                if len(buf) == 0:
                    break
                i += len(buf)
                f.write(buf)

        newNsp.close()
Example #3
0
def compress(filePath, compressionLevel=19, outputDir=None):
	filePath = os.path.abspath(filePath)

	CHUNK_SZ = 0x1000000

	if outputDir is None:
		nszPath = filePath[0:-1] + 'z'
	else:
		nszPath = os.path.join(outputDir, os.path.basename(filePath[0:-1] + 'z'))

	nszPath = os.path.abspath(nszPath)

	Print.info('compressing (level %d) %s -> %s' % (compressionLevel, filePath, nszPath))

	if Config.dryRun:
		return None

	container = Fs.factory(filePath)

	container.open(filePath, 'rb')

	newNsp = Pfs0Stream(nszPath)

	for nspf in container:
		if isinstance(nspf, Fs.Nca) and ((nspf.header.contentType == Fs.Type.Content.PROGRAM or nspf.header.contentType == Fs.Type.Content.PUBLICDATA) or int(nspf.header.titleId, 16) <= 0x0100000000001000):
			if nspf.size > ncaHeaderSize * 2:
				cctx = zstandard.ZstdCompressor(level=compressionLevel)

				newFileName = nspf._path[0:-1] + 'z'

				f = newNsp.add(newFileName, nspf.size)

				start = f.tell()

				nspf.seek(0)
				h = nspf.read(ncaHeaderSize)
				#crypto = aes128.AESXTS(uhx(Keys.get('header_key')))
				#d = crypto.decrypt(h)

				# if d[0x200:0x204] == b'NCA3':
				#	d = d[0:0x200] + b'NCZ3' + d[0x204:]
				#	h = crypto.encrypt(d)
				# else:
				#	raise IOError('unknown NCA magic')

				# self.partition(0x0, 0xC00, self.header, Fs.Type.Crypto.XTS, uhx(Keys.get('header_key')))
				f.write(h)
				written = ncaHeaderSize

				compressor = cctx.stream_writer(f)

				sections = []
				sectionsTmp = []
				for fs in sortedFs(nspf):
					sectionsTmp += fs.getEncryptionSections()

				currentOffset = ncaHeaderSize
				for fs in sectionsTmp:
					if fs.offset < ncaHeaderSize:
						if fs.offset + fs.size < ncaHeaderSize:
							currentOffset = fs.offset + fs.size
							continue
						else:
							fs.size -= ncaHeaderSize - fs.offset
							fs.offset = ncaHeaderSize
					elif fs.offset > currentOffset:
						sections.append(BaseFs.EncryptedSection(currentOffset, fs.offset - currentOffset, Fs.Type.Crypto.NONE, None, None))
					elif fs.offset < currentOffset:
						raise IOError("misaligned nca partitions")

					sections.append(fs)
					currentOffset = fs.offset + fs.size

				header = b'NCZSECTN'
				header += len(sections).to_bytes(8, 'little')

				i = 0
				for fs in sections:
					i += 1
					header += fs.offset.to_bytes(8, 'little')
					header += fs.size.to_bytes(8, 'little')
					header += fs.cryptoType.to_bytes(8, 'little')
					header += b'\x00' * 8
					header += fs.cryptoKey
					header += fs.cryptoCounter

				f.write(header)
				written += len(header)

				bar = Status.create(nspf.size, desc=os.path.basename(nszPath), unit='B')

				decompressedBytes = ncaHeaderSize
				bar.add(ncaHeaderSize)

				for section in sections:
					#print('offset: %x\t\tsize: %x\t\ttype: %d\t\tiv%s' % (section.offset, section.size, section.cryptoType, str(hx(section.cryptoCounter))))
					o = nspf.partition(offset=section.offset, size=section.size, n=None, cryptoType=section.cryptoType,
									   cryptoKey=section.cryptoKey, cryptoCounter=bytearray(section.cryptoCounter), autoOpen=True)

					while not o.eof():
						buffer = o.read(CHUNK_SZ)

						if len(buffer) == 0:
							raise IOError('read failed')

						written += compressor.write(buffer)

						decompressedBytes += len(buffer)
						bar.add(len(buffer))

					o.close()

				compressor.flush(zstandard.FLUSH_FRAME)
				bar.close()

				Print.info('%d written vs %d tell' % (written, f.tell() - start))
				written = f.tell() - start
				Print.info('compressed %d%% %d -> %d  - %s' % (int(written * 100 / nspf.size), decompressedBytes, written, nspf._path))
				newNsp.resize(newFileName, written)

				continue

		f = newNsp.add(nspf._path, nspf.size)
		nspf.seek(0)
		while not nspf.eof():
			buffer = nspf.read(CHUNK_SZ)
			f.write(buffer)

	newNsp.close()
	return nszPath