-
Notifications
You must be signed in to change notification settings - Fork 3
/
fix-titledb.py
67 lines (51 loc) · 2.3 KB
/
fix-titledb.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#!/usr/bin/env python3
# This file is a part of rebuild-title-database.
#
# Copyright (c) 2020 Ian Burgwin
# This file is licensed under The MIT License (MIT).
# You can find the full license text in LICENSE.md in the root of this project.
from argparse import ArgumentParser
import gzip
from hashlib import sha256
from pathlib import Path
import sys
from pyctr.crypto import CryptoEngine, Keyslot
parser = ArgumentParser(description='Copy a clean version of title.db and import.db.')
parser.add_argument('-b', '--boot9', help='boot9')
parser.add_argument('-m', '--movable', help='movable.sed', required=True)
parser.add_argument('-s', '--sd', help='SD card (containing "Nintendo 3DS")')
args = parser.parse_args()
crypto = CryptoEngine(boot9=args.boot9)
crypto.setup_sd_key_from_file(args.movable)
id0 = Path(args.sd) / 'Nintendo 3DS' / crypto.id0.hex()
# Only continue if there is one id1 directory.
# If there isn't, the user needs to remove the unwanted ones.
id1_list = [x for x in id0.iterdir() if len(x.parts[-1]) == 32]
if len(id1_list) > 1:
print('There are multiple id1 directories in', id0, file=sys.stderr)
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]
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:])