print('No id1 directory could be found in', id0, file=sys.stderr)
    sys.exit(2)

id1 = id1_list[0]

dbs_folder = id1 / 'dbs'
dbs_folder.mkdir(exist_ok=True)

title_db_path = dbs_folder / 'title.db'
import_db_path = dbs_folder / 'import.db'

with gzip.open('title.db.gz') as gzfh:
    db_data = gzfh.read()

with title_db_path.open('wb') as fh:
    with crypto.create_ctr_io(Keyslot.SD, fh, CryptoEngine.sd_path_to_iv('/dbs/title.db')) as cfh:
        cmac = crypto.create_cmac_object(Keyslot.CMACSDNAND)
        cmac_data = [b'CTR-9DB0', 0x2.to_bytes(4, 'little'), db_data[0x100:0x200]]
        cmac.update(sha256(b''.join(cmac_data)).digest())

        cfh.write(cmac.digest())
        cfh.write(db_data[0x10:])

with import_db_path.open('wb') as fh:
    with crypto.create_ctr_io(Keyslot.SD, fh, CryptoEngine.sd_path_to_iv('/dbs/import.db')) as cfh:
        cmac = crypto.create_cmac_object(Keyslot.CMACSDNAND)
        cmac_data = [b'CTR-9DB0', 0x3.to_bytes(4, 'little'), db_data[0x100:0x200]]
        cmac.update(sha256(b''.join(cmac_data)).digest())

        cfh.write(cmac.digest())
        cfh.write(db_data[0x10:])
Beispiel #2
0
    print('Please remove the rest.', file=sys.stderr)
    sys.exit(1)
elif len(id1_list) < 1:
    print('No id1 directory could be found in', id0, file=sys.stderr)
    sys.exit(2)

id1 = id1_list[0]
title_dir = id1 / 'title'

for tmd_path in title_dir.rglob('*.tmd'):
    tmd_id = int(tmd_path.name[0:8], 16)
    tmd_path_for_cid = '/' + '/'.join(tmd_path.parts[len(id1.parts):])

    with tmd_path.open('rb') as tmd_fh:
        with crypto.create_ctr_io(
                Keyslot.SD, tmd_fh,
                crypto.sd_path_to_iv(tmd_path_for_cid)) as tmd_cfh:
            try:
                tmd = TitleMetadataReader.load(tmd_cfh)
            except Exception as e:
                print(f'Failed to parse tmd at {tmd_path}')
                traceback.print_exc()

    print('Parsing', tmd.title_id)

    if tmd.title_id.startswith('0004008c'):
        # DLC puts contents into different folders, the first content always goes in the first one
        content0_path = tmd_path.parent / '00000000' / (
            tmd.chunk_records[0].id + '.app')
        has_manual = False
    else: