Exemple #1
0
def xci_to_nsz(filepath,
               buffer=65536,
               outfile=None,
               keepupd=False,
               level=17,
               threads=0,
               pos=False,
               nthreads=False):
    isthreaded = False
    if pos != False:
        isthreaded = True
    elif str(pos) == '0':
        isthreaded = True
    else:
        pos = 0
    pos = int(pos)
    try:
        exchangefile.deletefile()
    except:
        pass
    f = squirrelXCI(filepath)
    for nspF in f.hfs0:
        if str(nspF._path) == "secure":
            for ticket in nspF:
                if str(ticket._path).endswith('.tik'):
                    if isthreaded == False:
                        print('- Titlerights: ' + ticket.rightsId)
                    tk = (str(hex(ticket.getTitleKeyBlock()))[2:]).upper()
                    if isthreaded == False:
                        print('- Titlekey: ' + tk)
                    exchangefile.add(ticket.rightsId, tk)
    f.flush()
    f.close()
    files_list = sq_tools.ret_xci_offsets(filepath)
    files = list()
    filesizes = list()
    if isthreaded == True and nthreads != False:
        tqlist = list()
        for i in range(nthreads):
            tq = tqdm(total=0,
                      unit='B',
                      unit_scale=True,
                      leave=False,
                      position=i)
            tqlist.append(tq)
    fplist = list()
    for k in range(len(files_list)):
        entry = files_list[k]
        fplist.append(entry[0])
    for i in range(len(files_list)):
        entry = files_list[i]
        cnmtfile = entry[0]
        if cnmtfile.endswith('.cnmt.nca'):
            f = squirrelXCI(filepath)
            titleid, titleversion, base_ID, keygeneration, rightsId, RSV, RGV, ctype, metasdkversion, exesdkversion, hasHtmlManual, Installedsize, DeltaSize, ncadata = f.get_data_from_cnmt(
                cnmtfile)
            f.flush()
            f.close()
            for j in range(len(ncadata)):
                row = ncadata[j]
                # print(row)
                if row['NCAtype'] != 'Meta':
                    test1 = str(row['NcaId']) + '.nca'
                    test2 = str(row['NcaId']) + '.ncz'
                    if test1 in fplist or test2 in fplist:
                        # print(str(row['NcaId'])+'.nca')
                        files.append(str(row['NcaId']) + '.nca')
                        filesizes.append(int(row['Size']))
                else:
                    # print(str(row['NcaId'])+'.cnmt.nca')
                    files.append(str(row['NcaId']) + '.cnmt.nca')
                    filesizes.append(int(row['Size']))
    for k in range(len(files_list)):
        entry = files_list[k]
        fp = entry[0]
        sz = int(entry[3])
        if fp.endswith('xml'):
            files.append(fp)
            filesizes.append(sz)
    for k in range(len(files_list)):
        entry = files_list[k]
        fp = entry[0]
        sz = int(entry[3])
        if fp.endswith('.tik'):
            files.append(fp)
            filesizes.append(sz)
    for k in range(len(files_list)):
        entry = files_list[k]
        fp = entry[0]
        sz = int(entry[3])
        if fp.endswith('.cert'):
            files.append(fp)
            filesizes.append(sz)
    nspheader = sq_tools.gen_nsp_header(files, filesizes)
    properheadsize = len(nspheader)

    compressionLevel = int(level)
    CHUNK_SZ = buffer
    if outfile == None:
        nszPath = filepath[0:-1] + 'z'
    else:
        nszPath = outfile
    nszPath = os.path.abspath(nszPath)

    tsize = properheadsize
    for sz in filesizes:
        tsize += sz
    if isthreaded == True:
        from colorama import Fore
        colors = Fore.__dict__
        k = 0
        l = pos
        for col in colors:
            if l > len(colors):
                l = l - len(colors)
            color = colors[col]
            if k == (l + 1):
                break
            else:
                k += 1
        t = tqdm(total=tsize,
                 unit='B',
                 unit_scale=True,
                 leave=False,
                 position=pos,
                 bar_format="{l_bar}%s{bar}%s{r_bar}" % (color, Fore.RESET))
    else:
        t = tqdm(total=tsize,
                 unit='B',
                 unit_scale=True,
                 leave=False,
                 position=0)

    if isthreaded == False:
        t.write('Compressing with level %d and %d threads' %
                (compressionLevel, threads))
        t.write('\n  %s -> %s \n' % (filepath, nszPath))

    newNsp = nutFs.Pfs0.Pfs0Stream(nszPath,
                                   headsize=properheadsize,
                                   mode='wb+')

    xcicontainer = Xci(filepath)
    # f.compressed_supertrim(buffer,outfile,keepupd,level,threads)
    for nspF in xcicontainer.hfs0:
        if str(nspF._path) == "secure":
            for nca in nspF:
                if isinstance(nca, nutFs.Nca.Nca) and (
                        nca.header.contentType == nutFs.Type.Content.PROGRAM
                        or nca.header.contentType
                        == nutFs.Type.Content.PUBLIC_DATA):
                    if isNcaPacked(nca):
                        cctx = zstandard.ZstdCompressor(level=compressionLevel,
                                                        threads=threads)

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

                        f = newNsp.add(newFileName, nca.size, t, isthreaded)

                        start = f.tell()

                        nca.seek(0)
                        data = nca.read(ncaHeaderSize)
                        f.write(data)
                        nca.seek(ncaHeaderSize)

                        written = ncaHeaderSize

                        compressor = cctx.stream_writer(f)

                        sections = get_sections(nca)

                        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)
                        timestamp = time.time()
                        decompressedBytes = ncaHeaderSize
                        totsize = 0
                        for fs in sections:
                            totsize += fs.size

                        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 = nca.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)
                                t.update(len(buffer))
                                if len(buffer) == 0:
                                    raise IOError('read failed')

                                written += compressor.write(buffer)

                                decompressedBytes += len(buffer)

                        compressor.flush(zstandard.FLUSH_FRAME)

                        elapsed = time.time() - timestamp
                        minutes = elapsed / 60
                        seconds = elapsed % 60

                        speed = 0 if elapsed == 0 else (nca.size / elapsed)

                        written = f.tell() - start
                        if isthreaded == False:
                            t.write(
                                '\n  * Compressed at %d%% from %s to %s  - %s'
                                % (int(written * 100 / nca.size),
                                   str(sq_tools.getSize(decompressedBytes)),
                                   str(sq_tools.getSize(written)), nca._path))
                            t.write(
                                '  * Compressed in %02d:%02d at speed: %.1f MB/s\n'
                                % (minutes, seconds, speed / 1000000.0))
                        newNsp.resize(newFileName, written)
                        continue
                    else:
                        if isthreaded == False:
                            t.write('not packed!')

                f = newNsp.add(nca._path, nca.size, t, isthreaded)

                nca.seek(0)
                while not nca.eof():
                    buffer = nca.read(CHUNK_SZ)
                    t.update(len(buffer))
                    f.write(buffer)
    t.close()
    newNsp.close()
    try:
        exchangefile.deletefile()
    except:
        pass
    if isthreaded == True and nthreads != False:
        for i in range(nthreads):
            tqlist[i].close()
    return nszPath
    def open(self,
             file=None,
             mode='rb',
             cryptoType=-1,
             cryptoKey=-1,
             cryptoCounter=-1):
        super(NcaHeader, self).open(file, mode, cryptoType, cryptoKey,
                                    cryptoCounter)
        self.rewind()
        self.signature1 = self.read(0x100)
        self.signature2 = self.read(0x100)
        self.magic = self.read(0x4)
        self.isGameCard = self.readInt8()
        self.contentType = self.readInt8()

        try:
            self.contentType = nutFs.Type.Content(self.contentType)
        except:
            pass

        self.cryptoType = self.readInt8()
        self.keyIndex = self.readInt8()
        self.size = self.readInt64()
        self.titleId = hx(self.read(8)[::-1]).decode('utf-8').upper()
        self.contentIndex = self.readInt32()
        self.sdkVersion = self.readInt32()
        self.cryptoType2 = self.readInt8()

        self.read(0xF)  # padding

        self.rightsId = hx(self.read(0x10))

        if self.magic not in [b'NCA3', b'NCA2']:
            raise Exception('Failed to decrypt NCA header: ' + str(self.magic))

        self.sectionHashes = []

        for i in range(4):
            self.sectionTables.append(SectionTableEntry(self.read(0x10)))

        for i in range(4):
            self.sectionHashes.append(self.sectionTables[i])

        if self.cryptoType > self.cryptoType2:
            self.masterKeyRev = self.cryptoType
        else:
            self.masterKeyRev = self.cryptoType2

        self.masterKey = self.masterKeyRev - 1

        if self.masterKey < 0:
            self.masterKey = 0

        self.encKeyBlock = self.getKeyBlock()
        #for i in range(4):
        #	offset = i * 0x10
        #	key = encKeyBlock[offset:offset+0x10]
        #	Print.info('enc %d: %s' % (i, hx(key)))

        #crypto = aes128.AESECB(Keys.keyAreaKey(self.masterKey, 0))
        self.keyBlock = Keys.unwrapAesWrappedTitlekey(self.encKeyBlock,
                                                      self.masterKey)
        self.keys = []
        for i in range(4):
            offset = i * 0x10
            key = self.keyBlock[offset:offset + 0x10]
            #Print.info('dec %d: %s' % (i, hx(key)))
            self.keys.append(key)

        if self.hasTitleRights():
            titleRightsTitleId = self.rightsId.decode()[0:16].upper()
            try:
                if titleRightsTitleId in Titles.keys() and Titles.get(
                        titleRightsTitleId).key:
                    self.titleKeyDec = Keys.decryptTitleKey(
                        uhx(Titles.get(titleRightsTitleId).key),
                        self.masterKey)
                else:
                    Print.info('could not find title key!')
            except:
                rightsID_ = self.rightsId.decode()[0:32].upper()
                tk = exchangefile.retrievekey(rightsID_)
                if tk == None:
                    raise Exception(
                        'Non valid titlekey detected for rightsid: ' +
                        rightsID_)
                self.titleKeyDec = Keys.decryptTitleKey(
                    uhx(tk), self.masterKey)
        else:
            self.titleKeyDec = self.key()

        return True
Exemple #3
0
def supertrim_xci(filepath,
                  buffer=65536,
                  outfile=None,
                  keepupd=False,
                  level=17,
                  threads=0):
    try:
        exchangefile.deletefile()
    except:
        pass
    f = squirrelXCI(filepath)
    for nspF in f.hfs0:
        if str(nspF._path) == "secure":
            for ticket in nspF:
                if str(ticket._path).endswith('.tik'):
                    print('- Titlerights: ' + ticket.rightsId)
                    tk = (str(hex(ticket.getTitleKeyBlock()))[2:]).upper()
                    print('- Titlekey: ' + tk)
                    exchangefile.add(ticket.rightsId, tk)
    f.flush()
    f.close()
    files_list = sq_tools.ret_xci_offsets(filepath)
    files = list()
    filesizes = list()
    fplist = list()
    for k in range(len(files_list)):
        entry = files_list[k]
        fplist.append(entry[0])
    for i in range(len(files_list)):
        entry = files_list[i]
        cnmtfile = entry[0]
        if cnmtfile.endswith('.cnmt.nca'):
            f = squirrelXCI(filepath)
            titleid, titleversion, base_ID, keygeneration, rightsId, RSV, RGV, ctype, metasdkversion, exesdkversion, hasHtmlManual, Installedsize, DeltaSize, ncadata = f.get_data_from_cnmt(
                cnmtfile)
            f.flush()
            f.close()
            for j in range(len(ncadata)):
                row = ncadata[j]
                # print(row)
                if row['NCAtype'] != 'Meta':
                    test1 = str(row['NcaId']) + '.nca'
                    test2 = str(row['NcaId']) + '.ncz'
                    if test1 in fplist or test2 in fplist:
                        # print(str(row['NcaId'])+'.nca')
                        files.append(str(row['NcaId']) + '.nca')
                        filesizes.append(int(row['Size']))
                else:
                    # print(str(row['NcaId'])+'.cnmt.nca')
                    files.append(str(row['NcaId']) + '.cnmt.nca')
                    filesizes.append(int(row['Size']))
            for k in range(len(files_list)):
                entry = files_list[k]
                fp = entry[0]
                sz = int(entry[3])
                if fp.endswith('xml'):
                    files.append(fp)
                    filesizes.append(sz)
            for k in range(len(files_list)):
                entry = files_list[k]
                fp = entry[0]
                sz = int(entry[3])
                if fp.endswith('.tik'):
                    files.append(fp)
                    filesizes.append(sz)
            for k in range(len(files_list)):
                entry = files_list[k]
                fp = entry[0]
                sz = int(entry[3])
                if fp.endswith('.cert'):
                    files.append(fp)
                    filesizes.append(sz)
    sec_hashlist = list()
    f = squirrelXCI(filepath)
    try:
        for file in files:
            sha, size, gamecard = f.file_hash(file)
            # print(sha)
            if sha != False:
                sec_hashlist.append(sha)
    except BaseException as e:
        Print.error('Exception: ' + str(e))
    f.flush()
    f.close()
    xci_header, game_info, sig_padding, xci_certificate, root_header, upd_header, norm_header, sec_header, rootSize, upd_multiplier, norm_multiplier, sec_multiplier = sq_tools.get_xciheader(
        files, filesizes, sec_hashlist)
    compressionLevel = int(level)
    CHUNK_SZ = buffer
    if outfile == None:
        ofile = filepath[0:-1] + 'z'
    else:
        ofile = outfile
    ofile = os.path.abspath(ofile)
    outheader = xci_header
    outheader += game_info
    outheader += sig_padding
    outheader += xci_certificate
    outheader += root_header
    outheader += upd_header
    outheader += norm_header
    outheader += sec_header
    properheadsize = len(outheader)

    compressionLevel = int(level)
    CHUNK_SZ = buffer
    if outfile == None:
        nszPath = filepath[0:-1] + 'z'
    else:
        nszPath = outfile
    nszPath = os.path.abspath(nszPath)

    Print.info('Compressing with level %d and %d threads' %
               (compressionLevel, threads))
    Print.info('\n  %s -> %s \n' % (filepath, nszPath))

    newNsp = nutFs.Pfs0.Pfs0Stream(nszPath,
                                   headsize=properheadsize,
                                   mode='wb+')

    xcicontainer = Xci(filepath)
    # f.compressed_supertrim(buffer,outfile,keepupd,level,threads)
    files2 = list()
    filesizes2 = list()
    for file in files:
        for nspF in xcicontainer.hfs0:
            if str(nspF._path) == "secure":
                for nca in nspF:
                    if nca._path == file:
                        if isinstance(nca, nutFs.Nca.Nca) and (
                                nca.header.contentType
                                == nutFs.Type.Content.PROGRAM
                                or nca.header.contentType
                                == nutFs.Type.Content.PUBLIC_DATA):
                            if isNcaPacked(nca):
                                cctx = zstandard.ZstdCompressor(
                                    level=compressionLevel, threads=threads)

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

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

                                start = f.tell()

                                nca.seek(0)
                                data = nca.read(ncaHeaderSize)
                                f.write(data)
                                nca.seek(ncaHeaderSize)

                                written = ncaHeaderSize

                                compressor = cctx.stream_writer(f)

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

                                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)
                                timestamp = time.time()
                                decompressedBytes = ncaHeaderSize
                                totsize = 0
                                for fs in sortedFs(nca):
                                    totsize += fs.size
                                t = tqdm(total=totsize,
                                         unit='B',
                                         unit_scale=True,
                                         leave=False)

                                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 = nca.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)
                                        t.update(len(buffer))
                                        if len(buffer) == 0:
                                            raise IOError('read failed')

                                        written += compressor.write(buffer)

                                        decompressedBytes += len(buffer)
                                t.close()
                                compressor.flush(zstandard.FLUSH_FRAME)

                                elapsed = time.time() - timestamp
                                minutes = elapsed / 60
                                seconds = elapsed % 60

                                speed = 0 if elapsed == 0 else (nca.size /
                                                                elapsed)

                                written = f.tell() - start
                                print(
                                    '\n  * Compressed at %d%% from %s to %s  - %s'
                                    %
                                    (int(written * 100 / nca.size),
                                     str(sq_tools.getSize(decompressedBytes)),
                                     str(sq_tools.getSize(written)),
                                     nca._path))
                                print(
                                    '  * Compressed in %02d:%02d at speed: %.1f MB/s\n'
                                    % (minutes, seconds, speed / 1000000.0))
                                newNsp.resize(newFileName, written)
                                files2.append(newFileName)
                                filesizes2.append(written)
                                continue
                            else:
                                print('not packed!')

                        f = newNsp.add(nca._path, nca.size)
                        files2.append(nca._path)
                        filesizes2.append(nca.size)
                        nca.seek(0)
                        t = tqdm(total=nca.size,
                                 unit='B',
                                 unit_scale=True,
                                 leave=False)
                        while not nca.eof():
                            buffer = nca.read(CHUNK_SZ)
                            t.update(len(buffer))
                            f.write(buffer)
                        t.close()
    newNsp.close()

    xci_header, game_info, sig_padding, xci_certificate, root_header, upd_header, norm_header, sec_header, rootSize, upd_multiplier, norm_multiplier, sec_multiplier = sq_tools.get_xciheader(
        files2, filesizes2, sec_hashlist)
    outheader = xci_header
    outheader += game_info
    outheader += sig_padding
    outheader += xci_certificate
    outheader += root_header
    outheader += upd_header
    outheader += norm_header
    outheader += sec_header
    with open(nszPath, 'rb+') as o:
        o.seek(0)
        o.write(outheader)
    try:
        exchangefile.deletefile()
    except:
        pass
    return nszPath