示例#1
0
def processContainer(readContainer, writeContainer, compressionLevel, threads,
                     stusReport, id, pleaseNoPrint):
    for nspf in readContainer:
        if isinstance(
                nspf,
                Nca.Nca) and nspf.header.contentType == Type.Content.DATA:
            Print.info('skipping delta fragment', pleaseNoPrint)
            continue

        if isinstance(nspf, Nca.Nca) and (
                nspf.header.contentType == Type.Content.PROGRAM
                or nspf.header.contentType == Type.Content.PUBLICDATA):
            if isNcaPacked(nspf, ncaHeaderSize):

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

                with writeContainer.add(newFileName, nspf.size,
                                        pleaseNoPrint) as f:
                    start = f.tell()

                    nspf.seek(0)
                    f.write(nspf.read(ncaHeaderSize))

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

                    if len(sections) == 0:
                        raise Exception(
                            "NCA can't be decrypted. Outdated keys.txt?")

                    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)

                    blockID = 0
                    chunkRelativeBlockID = 0
                    startChunkBlockID = 0
                    blocksHeaderFilePos = f.tell()
                    compressedblockSizeList = []

                    decompressedBytes = ncaHeaderSize

                    stusReport[id] = [0, 0, nspf.size]

                    partitions = []
                    for section in sections:
                        #Print.info('offset: %x\t\tsize: %x\t\ttype: %d\t\tiv%s' % (section.offset, section.size, section.cryptoType, str(hx(section.cryptoCounter))), pleaseNoPrint)
                        partitions.append(
                            nspf.partition(offset=section.offset,
                                           size=section.size,
                                           n=None,
                                           cryptoType=section.cryptoType,
                                           cryptoKey=section.cryptoKey,
                                           cryptoCounter=bytearray(
                                               section.cryptoCounter),
                                           autoOpen=True))

                    partNr = 0
                    stusReport[id] = [nspf.tell(), f.tell(), nspf.size]
                    if threads > 1:
                        cctx = ZstdCompressor(level=compressionLevel,
                                              threads=threads)
                    else:
                        cctx = ZstdCompressor(level=compressionLevel)
                    compressor = cctx.stream_writer(f)
                    while True:

                        buffer = partitions[partNr].read(CHUNK_SZ)
                        while (len(buffer) < CHUNK_SZ
                               and partNr < len(partitions) - 1):
                            partitions[partNr].close()
                            partitions[partNr] = None
                            partNr += 1
                            buffer += partitions[partNr].read(CHUNK_SZ -
                                                              len(buffer))
                        if len(buffer) == 0:
                            break
                        compressor.write(buffer)

                        decompressedBytes += len(buffer)
                        stusReport[id] = [nspf.tell(), f.tell(), nspf.size]
                    partitions[partNr].close()
                    partitions[partNr] = None

                    compressor.flush(FLUSH_FRAME)
                    compressor.flush(COMPRESSOBJ_FLUSH_FINISH)
                    stusReport[id] = [nspf.tell(), f.tell(), nspf.size]

                    written = f.tell() - start
                    Print.info(
                        'Compressed {0}% {1} -> {2}  - {3}'.format(
                            written * 100 / nspf.size, decompressedBytes,
                            written, nspf._path), pleaseNoPrint)
                    writeContainer.resize(newFileName, written)
                    continue
            else:
                Print.info('not packed!', pleaseNoPrint)

        with writeContainer.add(nspf._path, nspf.size, pleaseNoPrint) as f:
            nspf.seek(0)
            while not nspf.eof():
                buffer = nspf.read(CHUNK_SZ)
                f.write(buffer)
示例#2
0
def solidCompress(filePath, compressionLevel=18, outputDir=None, threads=-1):
    ncaHeaderSize = 0x4000
    filePath = str(Path(filePath).resolve())
    container = factory(filePath)
    container.open(filePath, 'rb')
    CHUNK_SZ = 0x1000000
    nszPath = str(
        Path(filePath[0:-1] + 'z' if outputDir == None else Path(outputDir).
             joinpath(Path(filePath[0:-1] + 'z').name)).resolve(strict=False))

    for nspf in container:
        if isinstance(nspf, Ticket.Ticket):
            nspf.getRightsId()
            break  # No need to go for other objects

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

    try:
        for nspf in container:
            if isinstance(
                    nspf,
                    Nca.Nca) and nspf.header.contentType == Type.Content.DATA:
                Print.info('skipping delta fragment')
                continue
            if isinstance(nspf, Nca.Nca) and (
                    nspf.header.contentType == Type.Content.PROGRAM
                    or nspf.header.contentType == Type.Content.PUBLICDATA):
                if isNcaPacked(nspf, ncaHeaderSize):
                    newFileName = nspf._path[0:-1] + 'z'
                    f = newNsp.add(newFileName, nspf.size)
                    start = f.tell()
                    nspf.seek(0)
                    f.write(nspf.read(ncaHeaderSize))
                    sections = []

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

                    if len(sections) == 0:
                        raise Exception(
                            "NCA can't be decrypted. Outdated keys.txt?")
                    header = b'NCZSECTN'
                    header += len(sections).to_bytes(8, 'little')

                    for fs in sections:
                        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)
                    decompressedBytes = ncaHeaderSize

                    with tqdm(total=nspf.size, unit_scale=True,
                              unit="B") as bar:
                        partitions = [
                            nspf.partition(
                                offset=section.offset,
                                size=section.size,
                                n=None,
                                cryptoType=section.cryptoType,
                                cryptoKey=section.cryptoKey,
                                cryptoCounter=bytearray(section.cryptoCounter),
                                autoOpen=True) for section in sections
                        ]
                        partNr = 0
                        bar.update(f.tell())
                        cctx = ZstdCompressor(
                            level=compressionLevel, threads=threads
                        ) if threads > 1 else ZstdCompressor(
                            level=compressionLevel)
                        compressor = cctx.stream_writer(f)

                        while True:
                            buffer = partitions[partNr].read(CHUNK_SZ)

                            while (len(buffer) < CHUNK_SZ
                                   and partNr < len(partitions) - 1):
                                partitions[partNr].close()
                                partitions[partNr] = None
                                partNr += 1
                                buffer += partitions[partNr].read(CHUNK_SZ -
                                                                  len(buffer))

                            if len(buffer) == 0:
                                break
                            compressor.write(buffer)
                            decompressedBytes += len(buffer)
                            bar.update(len(buffer))

                        partitions[partNr].close()
                        partitions[partNr] = None

                    compressor.flush(FLUSH_FRAME)
                    compressor.flush(COMPRESSOBJ_FLUSH_FINISH)
                    written = f.tell() - start
                    print('compressed %d%% %d -> %d  - %s' %
                          (int(written * 100 / nspf.size), decompressedBytes,
                           written, nspf._path))
                    newNsp.resize(newFileName, written)
                    continue
                else:
                    print('not packed!')
            f = newNsp.add(nspf._path, nspf.size)
            nspf.seek(0)
            while not nspf.eof():
                buffer = nspf.read(CHUNK_SZ)
                f.write(buffer)
    except KeyboardInterrupt:
        remove(nszPath)
        raise KeyboardInterrupt
    except BaseException:
        Print.error(format_exc())
        remove(nszPath)
    finally:
        newNsp.close()
        container.close()
    return nszPath
示例#3
0
def blockCompress(filePath,
                  compressionLevel=18,
                  blockSizeExponent=20,
                  outputDir=None,
                  threads=32):
    CHUNK_SZ = 0x100000
    ncaHeaderSize = 0x4000
    if blockSizeExponent < 14 or blockSizeExponent > 32:
        raise ValueError("Block size must be between 14 and 32")
    blockSize = 2**blockSizeExponent
    filePath = str(Path(filePath).resolve(strict=False))
    container = factory(filePath)
    container.open(filePath, 'rb')
    nszPath = str(
        Path(filePath[0:-1] + 'z' if outputDir == None else str(
            Path(outputDir).joinpath(Path(filePath[0:-1] +
                                          'z').name))).resolve(strict=False))
    Print.info('compressing (level %d) %s -> %s' %
               (compressionLevel, filePath, nszPath))
    newNsp = Pfs0.Pfs0Stream(nszPath)
    try:
        manager = Manager()
        results = manager.list()
        readyForWork = Counter(0)
        pleaseKillYourself = Counter(0)
        TasksPerChunk = 209715200 // blockSize
        for i in range(TasksPerChunk):
            results.append(b"")
        work = manager.Queue(threads)
        pool = []

        for i in range(threads):
            p = Process(target=compressBlockTask,
                        args=(work, results, readyForWork, pleaseKillYourself))
            p.start()
            pool.append(p)

        for nspf in container:
            if isinstance(
                    nspf,
                    Nca.Nca) and nspf.header.contentType == Type.Content.DATA:
                Print.info('skipping delta fragment')
                continue
            if isinstance(nspf, Nca.Nca) and (
                    nspf.header.contentType == Type.Content.PROGRAM
                    or nspf.header.contentType == Type.Content.PUBLICDATA):
                if isNcaPacked(nspf, ncaHeaderSize):
                    newFileName = nspf._path[0:-1] + 'z'
                    f = newNsp.add(newFileName, nspf.size)
                    start = f.tell()
                    nspf.seek(0)
                    f.write(nspf.read(ncaHeaderSize))
                    sections = []

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

                    if len(sections) == 0:
                        raise Exception(
                            "NCA can't be decrypted. Outdated keys.txt?")
                    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)
                    blockID = 0
                    chunkRelativeBlockID = 0
                    startChunkBlockID = 0
                    blocksHeaderFilePos = f.tell()
                    bytesToCompress = nspf.size - ncaHeaderSize
                    blocksToCompress = bytesToCompress // blockSize + (
                        bytesToCompress % blockSize > 0)
                    compressedblockSizeList = [0] * blocksToCompress
                    header = b'NCZBLOCK'  #Magic
                    header += b'\x02'  #Version
                    header += b'\x01'  #Type
                    header += b'\x00'  #Unused
                    header += blockSizeExponent.to_bytes(
                        1, 'little')  #blockSizeExponent in bits: 2^x
                    header += blocksToCompress.to_bytes(
                        4, 'little')  #Amount of Blocks
                    header += bytesToCompress.to_bytes(
                        8, 'little')  #Decompressed Size
                    header += b'\x00' * (blocksToCompress * 4)
                    f.write(header)
                    decompressedBytes = ncaHeaderSize
                    with tqdm(total=nspf.size, unit_scale=True,
                              unit="B") as bar:
                        partitions = [
                            nspf.partition(
                                offset=section.offset,
                                size=section.size,
                                n=None,
                                cryptoType=section.cryptoType,
                                cryptoKey=section.cryptoKey,
                                cryptoCounter=bytearray(section.cryptoCounter),
                                autoOpen=True) for section in sections
                        ]
                        partNr = 0
                        bar.update(f.tell())
                        while True:
                            buffer = partitions[partNr].read(blockSize)
                            while (len(buffer) < blockSize
                                   and partNr < len(partitions) - 1):
                                partitions[partNr].close()
                                partitions[partNr] = None
                                partNr += 1
                                buffer += partitions[partNr].read(blockSize -
                                                                  len(buffer))
                            if chunkRelativeBlockID >= TasksPerChunk or len(
                                    buffer) == 0:
                                while readyForWork.value() < threads:
                                    sleep(0.02)

                                for i in range(
                                        min(
                                            TasksPerChunk, blocksToCompress -
                                            startChunkBlockID)):
                                    compressedblockSizeList[startChunkBlockID +
                                                            i] = len(
                                                                results[i])
                                    f.write(results[i])
                                    results[i] = b""

                                if len(buffer) == 0:
                                    pleaseKillYourself.increment()

                                    for i in range(readyForWork.value()):
                                        work.put(None)

                                    while readyForWork.value() > 0:
                                        sleep(0.02)
                                    break
                                chunkRelativeBlockID = 0
                                startChunkBlockID = blockID
                            work.put([
                                buffer, compressionLevel,
                                compressedblockSizeList, chunkRelativeBlockID
                            ])
                            blockID += 1
                            chunkRelativeBlockID += 1
                            decompressedBytes += len(buffer)
                            bar.update(len(buffer))
                    partitions[partNr].close()
                    partitions[partNr] = None
                    f.seek(blocksHeaderFilePos + 24)
                    header = b""

                    for compressedblockSize in compressedblockSizeList:
                        header += compressedblockSize.to_bytes(4, 'little')

                    f.write(header)
                    f.seek(0, 2)  #Seek to end of file.
                    written = f.tell() - start
                    print('compressed %d%% %d -> %d  - %s' %
                          (int(written * 100 / nspf.size), decompressedBytes,
                           written, nspf._path))
                    newNsp.resize(newFileName, written)
                    continue
                else:
                    print('not packed!')
            f = newNsp.add(nspf._path, nspf.size)
            nspf.seek(0)
            while not nspf.eof():
                buffer = nspf.read(CHUNK_SZ)
                f.write(buffer)
    except KeyboardInterrupt:
        remove(nszPath)
        raise KeyboardInterrupt
    except BaseException:
        Print.error(format_exc())
        remove(nszPath)
    finally:
        newNsp.close()
        container.close()
    return nszPath
示例#4
0
def blockCompressContainer(readContainer, writeContainer, compressionLevel,
                           blockSizeExponent, threads):
    CHUNK_SZ = 0x100000
    ncaHeaderSize = 0x4000
    if blockSizeExponent < 14 or blockSizeExponent > 32:
        raise ValueError("Block size must be between 14 and 32")
    blockSize = 2**blockSizeExponent
    manager = Manager()
    results = manager.list()
    readyForWork = Counter(0)
    pleaseKillYourself = Counter(0)
    TasksPerChunk = 209715200 // blockSize
    for i in range(TasksPerChunk):
        results.append(b"")
    pool = []
    work = manager.Queue(threads)

    for i in range(threads):
        p = Process(target=compressBlockTask,
                    args=(work, results, readyForWork, pleaseKillYourself))
        p.start()
        pool.append(p)

    for nspf in readContainer:
        if isinstance(
                nspf,
                Nca.Nca) and nspf.header.contentType == Type.Content.DATA:
            Print.info('skipping delta fragment')
            continue
        if isinstance(nspf, Nca.Nca) and (
                nspf.header.contentType == Type.Content.PROGRAM
                or nspf.header.contentType == Type.Content.PUBLICDATA):
            if isNcaPacked(nspf, ncaHeaderSize):
                newFileName = nspf._path[0:-1] + 'z'
                f = writeContainer.add(newFileName, nspf.size)
                startPos = f.tell()
                nspf.seek(0)
                f.write(nspf.read(ncaHeaderSize))
                sections = []

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

                if len(sections) == 0:
                    for p in pool:
                        #Process.terminate() might corrupt the datastructure but we do't care
                        p.terminate()
                    raise Exception(
                        "NCA can't be decrypted. Outdated keys.txt?")
                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)
                blockID = 0
                chunkRelativeBlockID = 0
                startChunkBlockID = 0
                blocksHeaderFilePos = f.tell()
                bytesToCompress = nspf.size - ncaHeaderSize
                blocksToCompress = bytesToCompress // blockSize + (
                    bytesToCompress % blockSize > 0)
                compressedblockSizeList = [0] * blocksToCompress
                header = b'NCZBLOCK'  #Magic
                header += b'\x02'  #Version
                header += b'\x01'  #Type
                header += b'\x00'  #Unused
                header += blockSizeExponent.to_bytes(
                    1, 'little')  #blockSizeExponent in bits: 2^x
                header += blocksToCompress.to_bytes(
                    4, 'little')  #Amount of Blocks
                header += bytesToCompress.to_bytes(
                    8, 'little')  #Decompressed Size
                header += b'\x00' * (blocksToCompress * 4)
                f.write(header)
                decompressedBytes = ncaHeaderSize
                compressedBytes = f.tell()
                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]'
                bar = enlighten.Counter(total=nspf.size // 1048576,
                                        desc='Compressing',
                                        unit='MiB',
                                        color='cyan',
                                        bar_format=BAR_FMT)
                subBars = bar.add_subcounter('green', all_fields=True)
                partitions = [
                    nspf.partition(offset=section.offset,
                                   size=section.size,
                                   n=None,
                                   cryptoType=section.cryptoType,
                                   cryptoKey=section.cryptoKey,
                                   cryptoCounter=bytearray(
                                       section.cryptoCounter),
                                   autoOpen=True) for section in sections
                ]
                partNr = 0
                bar.count = nspf.tell() // 1048576
                subBars.count = f.tell() // 1048576
                bar.refresh()
                while True:
                    buffer = partitions[partNr].read(blockSize)
                    while (len(buffer) < blockSize
                           and partNr < len(partitions) - 1):
                        partitions[partNr].close()
                        partitions[partNr] = None
                        partNr += 1
                        buffer += partitions[partNr].read(blockSize -
                                                          len(buffer))
                    if chunkRelativeBlockID >= TasksPerChunk or len(
                            buffer) == 0:
                        while readyForWork.value() < threads:
                            sleep(0.02)

                        for i in range(
                                min(TasksPerChunk,
                                    blocksToCompress - startChunkBlockID)):
                            lenResult = len(results[i])
                            compressedBytes += lenResult
                            compressedblockSizeList[startChunkBlockID +
                                                    i] = lenResult
                            f.write(results[i])
                            results[i] = b""

                        if len(buffer) == 0:
                            sleep(0.02)
                            pleaseKillYourself.increment()

                            for i in range(readyForWork.value()):
                                work.put(None)

                            while readyForWork.value() > 0:
                                sleep(0.02)
                            break
                        chunkRelativeBlockID = 0
                        startChunkBlockID = blockID
                    work.put([
                        buffer, compressionLevel, compressedblockSizeList,
                        chunkRelativeBlockID
                    ])
                    blockID += 1
                    chunkRelativeBlockID += 1
                    decompressedBytes += len(buffer)
                    bar.count = decompressedBytes // 1048576
                    subBars.count = compressedBytes // 1048576
                    bar.refresh()
                partitions[partNr].close()
                partitions[partNr] = None
                endPos = f.tell()
                bar.count = decompressedBytes // 1048576
                subBars.count = compressedBytes // 1048576
                bar.close()
                written = endPos - startPos
                f.seek(blocksHeaderFilePos + 24)
                header = b""

                for compressedblockSize in compressedblockSizeList:
                    header += compressedblockSize.to_bytes(4, 'little')

                f.write(header)
                f.seek(endPos)  #Seek to end of file.
                Print.info('compressed %d%% %d -> %d  - %s' %
                           (int(written * 100 / nspf.size), decompressedBytes,
                            written, nspf._path))
                writeContainer.resize(newFileName, written)
                continue
            else:
                Print.info('not packed!')
        f = writeContainer.add(nspf._path, nspf.size)
        nspf.seek(0)
        while not nspf.eof():
            buffer = nspf.read(CHUNK_SZ)
            f.write(buffer)