show mid-install status per-title

This commit is contained in:
Ian Burgwin
2021-02-09 21:21:34 -08:00
parent 9c777adf26
commit 37112682a0
2 changed files with 49 additions and 15 deletions

View File

@@ -23,7 +23,7 @@ from pyctr.type.cdn import CDNError
from pyctr.type.cia import CIAError
from pyctr.type.tmd import TitleMetadataError
from custominstall import CustomInstall, CI_VERSION, load_cifinish, InvalidCIFinishError
from custominstall import CustomInstall, CI_VERSION, load_cifinish, InvalidCIFinishError, InstallStatus
if TYPE_CHECKING:
from os import PathLike
@@ -75,6 +75,15 @@ default_movable_sed_path = find_first_file([join(file_parent, 'movable.sed')])
if default_seeddb_path:
load_seeddb(default_seeddb_path)
statuses = {
InstallStatus.Waiting: 'Waiting',
InstallStatus.Starting: 'Starting',
InstallStatus.Writing: 'Writing',
InstallStatus.Finishing: 'Finishing',
InstallStatus.Done: 'Done',
InstallStatus.Failed: 'Failed',
}
class ConsoleFrame(ttk.Frame):
def __init__(self, parent: tk.BaseWidget = None, starting_lines: 'List[str]' = None):
@@ -418,14 +427,16 @@ class CustomInstallGUI(ttk.Frame):
self.treeview = ttk.Treeview(treeview_frame, yscrollcommand=treeview_scrollbar.set)
self.treeview.grid(row=0, column=0, sticky=tk.NSEW)
self.treeview.configure(columns=('filepath', 'titleid', 'titlename'), show='headings')
self.treeview.configure(columns=('filepath', 'titleid', 'titlename', 'status'), show='headings')
self.treeview.column('filepath', width=200, anchor=tk.W)
self.treeview.heading('filepath', text='File path')
self.treeview.column('titleid', width=50, anchor=tk.W)
self.treeview.column('titleid', width=70, anchor=tk.W)
self.treeview.heading('titleid', text='Title ID')
self.treeview.column('titlename', width=150, anchor=tk.W)
self.treeview.heading('titlename', text='Title name')
self.treeview.column('status', width=20, anchor=tk.W)
self.treeview.heading('status', text='Status')
treeview_scrollbar.configure(command=self.treeview.yview)
@@ -494,6 +505,9 @@ class CustomInstallGUI(ttk.Frame):
return False
return self.b9_loaded
def update_status(self, path: 'Union[PathLike, bytes, str]', status: InstallStatus):
self.treeview.set(path, 'status', statuses[status])
def add_cia(self, path):
if not self.check_b9_loaded():
# this shouldn't happen
@@ -516,7 +530,8 @@ class CustomInstallGUI(ttk.Frame):
title_name = reader.contents[0].exefs.icon.get_app_title().short_desc
except:
title_name = '(No title)'
self.treeview.insert('', tk.END, text=path, iid=path, values=(path, reader.tmd.title_id, title_name))
self.treeview.insert('', tk.END, text=path, iid=path,
values=(path, reader.tmd.title_id, title_name, statuses[InstallStatus.Waiting]))
self.readers[path] = reader
return True, ''
@@ -596,12 +611,13 @@ class CustomInstallGUI(ttk.Frame):
'Continue?'):
return
self.disable_buttons()
if not len(self.readers):
self.show_error('There are no titles added to install.')
return
for path in self.readers.keys():
self.update_status(path, InstallStatus.Waiting)
self.disable_buttons()
self.log('Starting install...')
if taskbar:
@@ -613,10 +629,10 @@ class CustomInstallGUI(ttk.Frame):
overwrite_saves=self.overwrite_saves_var.get() == 1)
# use the treeview which has been sorted alphabetically
#installer.readers = self.readers.values()
readers_final = []
for k in self.treeview.get_children():
readers_final.append(self.readers[self.treeview.set(k, 'filepath')])
filepath = self.treeview.set(k, 'filepath')
readers_final.append((self.readers[filepath], filepath))
installer.readers = readers_final
@@ -652,6 +668,7 @@ class CustomInstallGUI(ttk.Frame):
installer.event.update_percentage += ci_update_percentage
installer.event.on_error += ci_on_error
installer.event.on_cia_start += ci_on_cia_start
installer.event.update_status += self.update_status
if self.skip_contents_var.get() != 1:
total_size, free_space = installer.check_size()

View File

@@ -5,6 +5,7 @@
# You can find the full license text in LICENSE.md in the root of this project.
from argparse import ArgumentParser
from enum import Enum
from os import makedirs, rename, scandir
from os.path import dirname, join, isdir, isfile
from random import randint
@@ -21,7 +22,7 @@ import subprocess
if TYPE_CHECKING:
from os import PathLike
from typing import List, Union
from typing import List, Union, Tuple
from events import Events
@@ -74,6 +75,15 @@ class InvalidCIFinishError(Exception):
pass
class InstallStatus(Enum):
Waiting = 0
Starting = 1
Writing = 2
Finishing = 3
Done = 4
Failed = 5
def get_free_space(path: 'Union[PathLike, bytes, str]'):
if is_windows:
lpSectorsPerCluster = c_ulonglong(0)
@@ -199,7 +209,7 @@ class CustomInstall:
self.crypto = CryptoEngine(boot9=boot9)
self.crypto.setup_sd_key_from_file(movable)
self.seeddb = seeddb
self.readers: 'List[Union[CDNReader, CIAReader]]' = []
self.readers: 'List[Tuple[Union[CDNReader, CIAReader], Union[PathLike, bytes, str]]]' = []
self.sd = sd
self.skip_contents = skip_contents
self.overwrite_saves = overwrite_saves
@@ -249,12 +259,12 @@ class CustomInstall:
if reader.tmd.title_id.startswith('00048'): # DSiWare
self.log(f'Skipping {reader.tmd.title_id} - DSiWare is not supported')
continue
readers.append(reader)
readers.append((reader, path))
self.readers = readers
def check_size(self):
total_size = 0
for r in self.readers:
for r, _ in self.readers:
total_size += get_install_size(r)
free_space = get_free_space(self.sd)
@@ -335,9 +345,11 @@ class CustomInstall:
install_state = {'installed': [], 'failed': []}
# Now loop through all provided cia files
for idx, cia in enumerate(self.readers):
for idx, info in enumerate(self.readers):
cia, path = info
self.event.on_cia_start(idx)
self.event.update_status(path, InstallStatus.Starting)
temp_title_root = join(self.sd, f'ci-install-temp-{cia.tmd.title_id}-{randint(0, 0xFFFFFFFF):08x}')
makedirs(temp_title_root, exist_ok=True)
@@ -384,6 +396,7 @@ class CustomInstall:
temp_content_root = join(temp_title_root, 'content')
if not self.skip_contents:
self.event.update_status(path, InstallStatus.Writing)
makedirs(join(temp_content_root, 'cmd'), exist_ok=True)
if cia.tmd.save_size:
makedirs(join(temp_title_root, 'data'), exist_ok=True)
@@ -424,6 +437,7 @@ class CustomInstall:
install_state['failed'].append(display_title)
rename(temp_title_root, temp_title_root + '-corrupted')
do_continue = True
self.event.update_status(path, InstallStatus.Failed)
break
if do_continue:
@@ -535,6 +549,7 @@ class CustomInstall:
b'\0' * 0x2c
]
self.event.update_status(path, InstallStatus.Finishing)
if isdir(title_root):
self.log(f'Removing original install at {title_root}...')
rmtree(title_root)
@@ -564,8 +579,10 @@ class CustomInstall:
for l in pformat(out.args).split('\n'):
self.log(l)
install_state['failed'].append(display_title)
self.event.update_status(path, InstallStatus.Failed)
else:
install_state['installed'].append(display_title)
self.event.update_status(path, InstallStatus.Done)
copied = False
if install_state['installed']: