custominstall: style updates

This commit is contained in:
Ian Burgwin
2020-01-24 01:59:29 -08:00
parent 8d3e09cc29
commit 48e1636588

View File

@@ -33,13 +33,16 @@ TITLE_ALIGN_SIZE = 0x8000
# size to read at a time when copying files # size to read at a time when copying files
READ_SIZE = 0x200000 READ_SIZE = 0x200000
# Placeholder for SDPathErrors # Placeholder for SDPathErrors
class SDPathError(Exception): pass class SDPathError(Exception):
pass
class CustomInstall(): class CustomInstall():
def __init__(self, boot9, movable, cias, sd, skip_contents=False): def __init__(self, boot9, movable, cias, sd, skip_contents=False):
self.event = Events() self.event = Events()
self.log_lines = [] # Stores all info messages for user to view self.log_lines = [] # Stores all info messages for user to view
self.crypto = CryptoEngine(boot9=boot9) self.crypto = CryptoEngine(boot9=boot9)
self.crypto.setup_sd_key_from_file(movable) self.crypto.setup_sd_key_from_file(movable)
@@ -67,7 +70,8 @@ class CustomInstall():
[sd_path, id1s] = self.get_sd_path() [sd_path, id1s] = self.get_sd_path()
try: try:
if len(id1s) > 1: if len(id1s) > 1:
raise SDPathError(f'There are multiple id1 directories for id0 {crypto.id0.hex()}, please remove extra directories') raise SDPathError(f'There are multiple id1 directories for id0 {crypto.id0.hex()}, '
f'please remove extra directories')
elif len(id1s) == 0: elif len(id1s) == 0:
raise SDPathError(f'Could not find a suitable id1 directory for id0 {crypto.id0.hex()}') raise SDPathError(f'Could not find a suitable id1 directory for id0 {crypto.id0.hex()}')
except SDPathError: except SDPathError:
@@ -148,22 +152,23 @@ class CustomInstall():
# write the tmd # write the tmd
enc_path = content_root_cmd + '/' + tmd_filename enc_path = content_root_cmd + '/' + tmd_filename
self.log(f'Writing {enc_path}...') self.log(f'Writing {enc_path}...')
with cia.open_raw_section(CIASection.TitleMetadata) as s, open(join(content_root, tmd_filename), 'wb') as o: with cia.open_raw_section(CIASection.TitleMetadata) as s:
self.copy_with_progress(s, o, cia.sections[CIASection.TitleMetadata].size, enc_path) with open(join(content_root, tmd_filename), 'wb') as o:
self.copy_with_progress(s, o, cia.sections[CIASection.TitleMetadata].size, enc_path)
# write each content # write each content
for c in cia.content_info: for co in cia.content_info:
content_filename = c.id + '.app' content_filename = co.id + '.app'
if is_dlc: if is_dlc:
dir_index = format((c.cindex // 256), '08x') dir_index = format((co.cindex // 256), '08x')
enc_path = content_root_cmd + f'/{dir_index}/{content_filename}' enc_path = content_root_cmd + f'/{dir_index}/{content_filename}'
out_path = join(content_root, dir_index, content_filename) out_path = join(content_root, dir_index, content_filename)
else: else:
enc_path = content_root_cmd + '/' + content_filename enc_path = content_root_cmd + '/' + content_filename
out_path = join(content_root, content_filename) out_path = join(content_root, content_filename)
self.log(f'Writing {enc_path}...') self.log(f'Writing {enc_path}...')
with cia.open_raw_section(c.cindex) as s, open(out_path, 'wb') as o: with cia.open_raw_section(co.cindex) as s, open(out_path, 'wb') as o:
self.copy_with_progress(s, o, c.size, enc_path) self.copy_with_progress(s, o, co.size, enc_path)
# generate a blank save # generate a blank save
if cia.tmd.save_size: if cia.tmd.save_size:
@@ -193,9 +198,9 @@ class CustomInstall():
id_bytes = bytes.fromhex(record.id)[::-1] id_bytes = bytes.fromhex(record.id)[::-1]
cmac_data += record.cindex.to_bytes(4, 'little') + id_bytes cmac_data += record.cindex.to_bytes(4, 'little') + id_bytes
c = crypto.create_cmac_object(Keyslot.CMACSDNAND) cmac_ncch = crypto.create_cmac_object(Keyslot.CMACSDNAND)
c.update(sha256(cmac_data).digest()) cmac_ncch.update(sha256(cmac_data).digest())
content_ids[record.cindex] = (id_bytes, c.digest()) content_ids[record.cindex] = (id_bytes, cmac_ncch.digest())
# add content IDs up to the last one # add content IDs up to the last one
ids_by_index = [CMD_MISSING] * (highest_index + 1) ids_by_index = [CMD_MISSING] * (highest_index + 1)
@@ -213,12 +218,12 @@ class CustomInstall():
installed_ids.sort(key=lambda x: int.from_bytes(x, 'little')) installed_ids.sort(key=lambda x: int.from_bytes(x, 'little'))
final = (cmd_id.to_bytes(4, 'little') final = (cmd_id.to_bytes(4, 'little')
+ len(ids_by_index).to_bytes(4, 'little') + len(ids_by_index).to_bytes(4, 'little')
+ len(installed_ids).to_bytes(4, 'little') + len(installed_ids).to_bytes(4, 'little')
+ (1).to_bytes(4, 'little')) + (1).to_bytes(4, 'little'))
c = crypto.create_cmac_object(Keyslot.CMACSDNAND) cmac_cmd_header = crypto.create_cmac_object(Keyslot.CMACSDNAND)
c.update(final) cmac_cmd_header.update(final)
final += c.digest() final += cmac_cmd_header.digest()
final += b''.join(ids_by_index) final += b''.join(ids_by_index)
final += b''.join(installed_ids) final += b''.join(installed_ids)
@@ -265,9 +270,6 @@ class CustomInstall():
title_info_entries[cia.tmd.title_id] = b''.join(title_info_entry_data) title_info_entries[cia.tmd.title_id] = b''.join(title_info_entry_data)
with cia.open_raw_section(CIASection.Ticket) as t:
ticket_data = t.read()
finalize_entry_data = [ finalize_entry_data = [
# magic # magic
b'TITLE\0', b'TITLE\0',
@@ -318,14 +320,13 @@ class CustomInstall():
self.log('FINAL STEP:\nRun custom-install-finalize through homebrew launcher.') self.log('FINAL STEP:\nRun custom-install-finalize through homebrew launcher.')
self.log('This will install a ticket and seed if required.') self.log('This will install a ticket and seed if required.')
def get_sd_path(self): def get_sd_path(self):
sd_path = join(self.sd, 'Nintendo 3DS', self.crypto.id0.hex()) sd_path = join(self.sd, 'Nintendo 3DS', self.crypto.id0.hex())
id1s = [] id1s = []
for d in scandir(sd_path): for d in scandir(sd_path):
if d.is_dir() and len(d.name) == 32: if d.is_dir() and len(d.name) == 32:
try: try:
#id1_tmp = bytes.fromhex(d.name) # id1_tmp = bytes.fromhex(d.name)
pass pass
except ValueError: except ValueError:
continue continue
@@ -333,7 +334,6 @@ class CustomInstall():
id1s.append(d.name) id1s.append(d.name)
return [sd_path, id1s] return [sd_path, id1s]
def log(self, message, mtype=0, errorname=None): def log(self, message, mtype=0, errorname=None):
"""Logs an Message with a type. Format is similar to python errors """Logs an Message with a type. Format is similar to python errors
@@ -344,15 +344,15 @@ class CustomInstall():
optionally, errorname can be a custom name as a string to identify errors easily optionally, errorname can be a custom name as a string to identify errors easily
""" """
if errorname == None: if errorname:
errorname += ": "
else:
# No errorname provided # No errorname provided
errorname = "" errorname = ""
else:
errorname += ": "
types = [ types = [
"", # Type 0 "", # Type 0
"Warning: ", # Type 1 "Warning: ", # Type 1
"Error: " # Type 2 "Error: " # Type 2
] ]
# Example: "Warning: UninformativeError: An error occured, try again."" # Example: "Warning: UninformativeError: An error occured, try again.""
msg_with_type = types[mtype] + errorname + str(message) msg_with_type = types[mtype] + errorname + str(message)
@@ -360,6 +360,7 @@ class CustomInstall():
self.event.on_log_msg(msg_with_type) self.event.on_log_msg(msg_with_type)
return msg_with_type return msg_with_type
if __name__ == "__main__": if __name__ == "__main__":
parser = ArgumentParser(description='Manually install a CIA to the SD card for a Nintendo 3DS system.') parser = ArgumentParser(description='Manually install a CIA to the SD card for a Nintendo 3DS system.')
parser.add_argument('cia', help='CIA files', nargs='+') parser.add_argument('cia', help='CIA files', nargs='+')
@@ -368,14 +369,13 @@ if __name__ == "__main__":
parser.add_argument('--sd', help='path to SD root') parser.add_argument('--sd', help='path to SD root')
parser.add_argument('--skip-contents', help="don't add contents, only add title info entry", action='store_true') parser.add_argument('--skip-contents', help="don't add contents, only add title info entry", action='store_true')
args = parser.parse_args() args = parser.parse_args()
installer = CustomInstall(boot9=args.boot9, installer = CustomInstall(boot9=args.boot9,
cias=args.cia, cias=args.cia,
movable=args.movable, movable=args.movable,
sd=args.sd, sd=args.sd,
skip_contents=(args.skip_contents or False)) skip_contents=(args.skip_contents or False))
def log_handle(msg): def log_handle(msg):
print(msg) print(msg)