7 Commits

Author SHA1 Message Date
Ian Burgwin
9b7346c919 version 2.1b3 2021-03-08 18:16:58 -08:00
Ian Burgwin
38f5e2b0e6 custominstall: remove seek workaround for CDN contents (fixed in pyctr 0.4.6) 2021-03-08 18:08:32 -08:00
Ian Burgwin
f48e177604 bump pyctr -> 0.4.6, comtypes -> 1.1.8 2021-03-08 18:08:06 -08:00
Ian Burgwin
4ca2c59b5a custominstall: fix cdn content install 2021-02-24 15:19:48 -08:00
Ian Burgwin
7a68b23365 custominstall: create title.db and import.db if missing 2021-02-24 15:07:59 -08:00
Ian Burgwin
1dec5175ea add title.db.gz, add to standalone build 2021-02-24 14:17:17 -08:00
Ian Burgwin
4d223ed931 show warning if 300 titles are detected 2021-02-13 23:08:37 -08:00
6 changed files with 64 additions and 12 deletions

View File

@@ -190,7 +190,8 @@ class TitleReadFailResults(tk.Toplevel):
class InstallResults(tk.Toplevel): class InstallResults(tk.Toplevel):
def __init__(self, parent: tk.Tk = None, *, install_state: 'Dict[str, List[str]]', copied_3dsx: bool): def __init__(self, parent: tk.Tk = None, *, install_state: 'Dict[str, List[str]]', copied_3dsx: bool,
application_count: int):
super().__init__(parent) super().__init__(parent)
self.parent = parent self.parent = parent
@@ -223,6 +224,11 @@ class InstallResults(tk.Toplevel):
if install_state['installed'] and copied_3dsx: if install_state['installed'] and copied_3dsx:
message += '\n\ncustom-install-finalize has been copied to the SD card.' message += '\n\ncustom-install-finalize has been copied to the SD card.'
if application_count >= 300:
message += (f'\n\nWarning: {application_count} installed applications were detected.\n'
f'The HOME Menu will only show 300 icons.\n'
f'Some applications (not updates or DLC) will need to be deleted.')
message_label = ttk.Label(outer_container, text=message) message_label = ttk.Label(outer_container, text=message)
message_label.grid(row=0, column=0, sticky=tk.NSEW, padx=10, pady=10) message_label.grid(row=0, column=0, sticky=tk.NSEW, padx=10, pady=10)
@@ -681,9 +687,12 @@ class CustomInstallGUI(ttk.Frame):
def install(): def install():
try: try:
result, copied_3dsx = installer.start() result, copied_3dsx, application_count = installer.start()
if result: if result:
result_window = InstallResults(self.parent, install_state=result, copied_3dsx=copied_3dsx) result_window = InstallResults(self.parent,
install_state=result,
copied_3dsx=copied_3dsx,
application_count=application_count)
result_window.focus() result_window.focus()
elif result is None: elif result is None:
self.show_error("An error occurred when trying to run save3ds_fuse.\n" self.show_error("An error occurred when trying to run save3ds_fuse.\n"

View File

@@ -6,6 +6,8 @@
from argparse import ArgumentParser from argparse import ArgumentParser
from enum import Enum from enum import Enum
from glob import glob
import gzip
from os import makedirs, rename, scandir from os import makedirs, rename, scandir
from os.path import dirname, join, isdir, isfile from os.path import dirname, join, isdir, isfile
from random import randint from random import randint
@@ -43,7 +45,7 @@ if is_windows:
else: else:
from os import statvfs from os import statvfs
CI_VERSION = '2.1b2' CI_VERSION = '2.1b3'
# used to run the save3ds_fuse binary next to the script # used to run the save3ds_fuse binary next to the script
frozen = getattr(sys, 'frozen', False) frozen = getattr(sys, 'frozen', False)
@@ -279,7 +281,7 @@ class CustomInstall:
save3ds_fuse_path += '.exe' save3ds_fuse_path += '.exe'
if not isfile(save3ds_fuse_path): if not isfile(save3ds_fuse_path):
self.log("Couldn't find " + save3ds_fuse_path, 2) self.log("Couldn't find " + save3ds_fuse_path, 2)
return None, False return None, False, 0
crypto = self.crypto crypto = self.crypto
# TODO: Move a lot of these into their own methods # TODO: Move a lot of these into their own methods
@@ -290,6 +292,8 @@ class CustomInstall:
f'please remove extra directories') 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()}')
id1 = id1s[0]
sd_path = join(sd_path, id1)
if self.cifinish_out: if self.cifinish_out:
cifinish_path = self.cifinish_out cifinish_path = self.cifinish_out
@@ -304,7 +308,41 @@ class CustomInstall:
f'This could mean an issue with the SD card or the filesystem. Please check it for errors.\n' f'This could mean an issue with the SD card or the filesystem. Please check it for errors.\n'
f'It is also possible, though less likely, to be an issue with custom-install.\n' f'It is also possible, though less likely, to be an issue with custom-install.\n'
f'Exiting now to prevent possible issues. If you want to try again, delete cifinish.bin from the SD card and re-run custom-install.') f'Exiting now to prevent possible issues. If you want to try again, delete cifinish.bin from the SD card and re-run custom-install.')
return None, False return None, False, 0
db_path = join(sd_path, 'dbs')
titledb_path = join(db_path, 'title.db')
importdb_path = join(db_path, 'import.db')
if not isfile(titledb_path):
makedirs(db_path, exist_ok=True)
with gzip.open(join(script_dir, 'title.db.gz')) as f:
tdb = f.read()
self.log(f'Creating title.db...')
with open(titledb_path, 'wb') as o:
with self.crypto.create_ctr_io(Keyslot.SD, o, self.crypto.sd_path_to_iv('/dbs/title.db')) as e:
e.write(tdb)
cmac = crypto.create_cmac_object(Keyslot.CMACSDNAND)
cmac_data = [b'CTR-9DB0', 0x2.to_bytes(4, 'little'), tdb[0x100:0x200]]
cmac.update(sha256(b''.join(cmac_data)).digest())
e.seek(0)
e.write(cmac.digest())
self.log(f'Creating import.db...')
with open(importdb_path, 'wb') as o:
with self.crypto.create_ctr_io(Keyslot.SD, o, self.crypto.sd_path_to_iv('/dbs/import.db')) as e:
e.write(tdb)
cmac = crypto.create_cmac_object(Keyslot.CMACSDNAND)
cmac_data = [b'CTR-9DB0', 0x3.to_bytes(4, 'little'), tdb[0x100:0x200]]
cmac.update(sha256(b''.join(cmac_data)).digest())
e.seek(0)
e.write(cmac.digest())
del tdb
with TemporaryDirectory(suffix='-custom-install') as tempdir: with TemporaryDirectory(suffix='-custom-install') as tempdir:
# set up the common arguments for the two times we call save3ds_fuse # set up the common arguments for the two times we call save3ds_fuse
@@ -335,9 +373,7 @@ class CustomInstall:
self.log('Command line:') self.log('Command line:')
for l in pformat(out.args).split('\n'): for l in pformat(out.args).split('\n'):
self.log(l) self.log(l)
return None, False return None, False, 0
sd_path = join(sd_path, id1s[0])
if self.seeddb: if self.seeddb:
load_seeddb(self.seeddb) load_seeddb(self.seeddb)
@@ -585,7 +621,13 @@ class CustomInstall:
self.event.update_status(path, InstallStatus.Done) self.event.update_status(path, InstallStatus.Done)
copied = False copied = False
# launchable applications, not DLC or update data
application_count = len(glob(join(tempdir, '00040000*')))
if install_state['installed']: if install_state['installed']:
if application_count >= 300:
self.log(f'{application_count} installed applications were detected.', 1)
self.log('The HOME Menu will only show 300 icons.', 1)
self.log('Some applications (not updates or DLC) will need to be deleted.', 1)
finalize_3dsx_orig_path = join(script_dir, 'custom-install-finalize.3dsx') finalize_3dsx_orig_path = join(script_dir, 'custom-install-finalize.3dsx')
hb_dir = join(self.sd, '3ds') hb_dir = join(self.sd, '3ds')
finalize_3dsx_path = join(hb_dir, 'custom-install-finalize.3dsx') finalize_3dsx_path = join(hb_dir, 'custom-install-finalize.3dsx')
@@ -601,7 +643,7 @@ class CustomInstall:
if copied: if copied:
self.log('custom-install-finalize has been copied to the SD card.') self.log('custom-install-finalize has been copied to the SD card.')
return install_state, copied return install_state, copied, application_count
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())

View File

@@ -6,6 +6,7 @@ copy TaskbarLib.tlb build\custom-install-standalone
copy bin\win32\save3ds_fuse.exe build\custom-install-standalone\bin copy bin\win32\save3ds_fuse.exe build\custom-install-standalone\bin
copy bin\README build\custom-install-standalone\bin copy bin\README build\custom-install-standalone\bin
copy custom-install-finalize.3dsx build\custom-install-standalone copy custom-install-finalize.3dsx build\custom-install-standalone
copy title.db.gz build\custom-install-standalone
copy extras\windows-quickstart.txt build\custom-install-standalone copy extras\windows-quickstart.txt build\custom-install-standalone
copy LICENSE.md build\custom-install-standalone copy LICENSE.md build\custom-install-standalone
python -m zipfile -c dist\custom-install-standalone.zip build\custom-install-standalone python -m zipfile -c dist\custom-install-standalone.zip build\custom-install-standalone

View File

@@ -1,2 +1,2 @@
-r requirements.txt -r requirements.txt
comtypes==1.1.7 comtypes==1.1.8

View File

@@ -1,2 +1,2 @@
events==0.4 events==0.4
pyctr==0.4.5 pyctr==0.4.6

BIN
title.db.gz Normal file

Binary file not shown.