mirror of
https://github.com/ihaveamac/custom-install.git
synced 2025-12-06 06:41:45 +00:00
ci-gui: ensure boot9 is loaded before allowing titles to be added, show better error message for missing seeddb, load seeddb before starting and any time a new one is selected
This commit is contained in:
62
ci-gui.py
62
ci-gui.py
@@ -16,6 +16,7 @@ import tkinter.filedialog as fd
|
|||||||
import tkinter.messagebox as mb
|
import tkinter.messagebox as mb
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
from pyctr.crypto import MissingSeedError, CryptoEngine, load_seeddb
|
||||||
from pyctr.crypto.engine import b9_paths
|
from pyctr.crypto.engine import b9_paths
|
||||||
from pyctr.util import config_dirs
|
from pyctr.util import config_dirs
|
||||||
from pyctr.type.cdn import CDNError
|
from pyctr.type.cdn import CDNError
|
||||||
@@ -25,7 +26,8 @@ from pyctr.type.tmd import TitleMetadataError
|
|||||||
from custominstall import CustomInstall, CI_VERSION, load_cifinish, InvalidCIFinishError
|
from custominstall import CustomInstall, CI_VERSION, load_cifinish, InvalidCIFinishError
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from typing import Dict, List
|
from os import PathLike
|
||||||
|
from typing import Dict, List, Union
|
||||||
|
|
||||||
is_windows = platform == 'win32'
|
is_windows = platform == 'win32'
|
||||||
taskbar = None
|
taskbar = None
|
||||||
@@ -70,6 +72,9 @@ default_b9_path = find_first_file(b9_paths)
|
|||||||
default_seeddb_path = find_first_file(seeddb_paths)
|
default_seeddb_path = find_first_file(seeddb_paths)
|
||||||
default_movable_sed_path = find_first_file([join(file_parent, 'movable.sed')])
|
default_movable_sed_path = find_first_file([join(file_parent, 'movable.sed')])
|
||||||
|
|
||||||
|
if default_seeddb_path:
|
||||||
|
load_seeddb(default_seeddb_path)
|
||||||
|
|
||||||
|
|
||||||
class ConsoleFrame(ttk.Frame):
|
class ConsoleFrame(ttk.Frame):
|
||||||
def __init__(self, parent: tk.BaseWidget = None, starting_lines: 'List[str]' = None):
|
def __init__(self, parent: tk.BaseWidget = None, starting_lines: 'List[str]' = None):
|
||||||
@@ -235,6 +240,7 @@ class InstallResults(tk.Toplevel):
|
|||||||
|
|
||||||
class CustomInstallGUI(ttk.Frame):
|
class CustomInstallGUI(ttk.Frame):
|
||||||
console = None
|
console = None
|
||||||
|
b9_loaded = False
|
||||||
|
|
||||||
def __init__(self, parent: tk.Tk = None):
|
def __init__(self, parent: tk.Tk = None):
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
@@ -304,13 +310,14 @@ class CustomInstallGUI(ttk.Frame):
|
|||||||
self.file_picker_textboxes['sd'] = sd_selected
|
self.file_picker_textboxes['sd'] = sd_selected
|
||||||
|
|
||||||
# This feels so wrong.
|
# This feels so wrong.
|
||||||
def create_required_file_picker(type_name, types, default, row):
|
def create_required_file_picker(type_name, types, default, row, callback=lambda filename: None):
|
||||||
def internal_callback():
|
def internal_callback():
|
||||||
f = fd.askopenfilename(parent=parent, title='Select ' + type_name, filetypes=types,
|
f = fd.askopenfilename(parent=parent, title='Select ' + type_name, filetypes=types,
|
||||||
initialdir=file_parent)
|
initialdir=file_parent)
|
||||||
if f:
|
if f:
|
||||||
selected.delete('1.0', tk.END)
|
selected.delete('1.0', tk.END)
|
||||||
selected.insert(tk.END, f)
|
selected.insert(tk.END, f)
|
||||||
|
callback(f)
|
||||||
|
|
||||||
type_label = ttk.Label(file_pickers, text=type_name)
|
type_label = ttk.Label(file_pickers, text=type_name)
|
||||||
type_label.grid(row=row, column=0)
|
type_label.grid(row=row, column=0)
|
||||||
@@ -325,8 +332,15 @@ class CustomInstallGUI(ttk.Frame):
|
|||||||
|
|
||||||
self.file_picker_textboxes[type_name] = selected
|
self.file_picker_textboxes[type_name] = selected
|
||||||
|
|
||||||
create_required_file_picker('boot9', [('boot9 file', '*.bin')], default_b9_path, 1)
|
def b9_callback(path: 'Union[PathLike, bytes, str]'):
|
||||||
create_required_file_picker('seeddb', [('seeddb file', '*.bin')], default_seeddb_path, 2)
|
self.check_b9_loaded()
|
||||||
|
self.enable_buttons()
|
||||||
|
|
||||||
|
def seeddb_callback(path: 'Union[PathLike, bytes, str]'):
|
||||||
|
load_seeddb(path)
|
||||||
|
|
||||||
|
create_required_file_picker('boot9', [('boot9 file', '*.bin')], default_b9_path, 1, b9_callback)
|
||||||
|
create_required_file_picker('seeddb', [('seeddb file', '*.bin')], default_seeddb_path, 2, seeddb_callback)
|
||||||
create_required_file_picker('movable.sed', [('movable.sed file', '*.sed')], default_movable_sed_path, 3)
|
create_required_file_picker('movable.sed', [('movable.sed file', '*.sed')], default_movable_sed_path, 3)
|
||||||
|
|
||||||
# ---------------------------------------------------------------- #
|
# ---------------------------------------------------------------- #
|
||||||
@@ -454,7 +468,13 @@ class CustomInstallGUI(ttk.Frame):
|
|||||||
|
|
||||||
self.log('Ready.')
|
self.log('Ready.')
|
||||||
|
|
||||||
self.disable_during_install = (add_cias, add_dirs, remove_selected, start, *self.file_picker_textboxes.values())
|
self.require_boot9 = (add_cias, add_cdn, add_dirs, remove_selected, start)
|
||||||
|
|
||||||
|
self.disable_buttons()
|
||||||
|
self.check_b9_loaded()
|
||||||
|
self.enable_buttons()
|
||||||
|
if not self.b9_loaded:
|
||||||
|
self.log('Note: boot9 was not auto-detected. Please choose it before adding any titles.')
|
||||||
|
|
||||||
def sort_treeview(self):
|
def sort_treeview(self):
|
||||||
l = [(self.treeview.set(k, 'titlename'), k) for k in self.treeview.get_children()]
|
l = [(self.treeview.set(k, 'titlename'), k) for k in self.treeview.get_children()]
|
||||||
@@ -464,7 +484,20 @@ class CustomInstallGUI(ttk.Frame):
|
|||||||
for idx, pair in enumerate(l):
|
for idx, pair in enumerate(l):
|
||||||
self.treeview.move(pair[1], '', idx)
|
self.treeview.move(pair[1], '', idx)
|
||||||
|
|
||||||
|
def check_b9_loaded(self):
|
||||||
|
if not self.b9_loaded:
|
||||||
|
boot9 = self.file_picker_textboxes['boot9'].get('1.0', tk.END).strip()
|
||||||
|
try:
|
||||||
|
tmp_crypto = CryptoEngine(boot9=boot9)
|
||||||
|
self.b9_loaded = tmp_crypto.b9_keys_set
|
||||||
|
except:
|
||||||
|
return False
|
||||||
|
return self.b9_loaded
|
||||||
|
|
||||||
def add_cia(self, path):
|
def add_cia(self, path):
|
||||||
|
if not self.check_b9_loaded():
|
||||||
|
# this shouldn't happen
|
||||||
|
return False, 'Please choose boot9 first'
|
||||||
path = abspath(path)
|
path = abspath(path)
|
||||||
if path in self.readers:
|
if path in self.readers:
|
||||||
return False, 'File already in list'
|
return False, 'File already in list'
|
||||||
@@ -472,6 +505,8 @@ class CustomInstallGUI(ttk.Frame):
|
|||||||
reader = CustomInstall.get_reader(path)
|
reader = CustomInstall.get_reader(path)
|
||||||
except (CIAError, CDNError, TitleMetadataError):
|
except (CIAError, CDNError, TitleMetadataError):
|
||||||
return False, 'Failed to read as a CIA or CDN title, probably corrupt'
|
return False, 'Failed to read as a CIA or CDN title, probably corrupt'
|
||||||
|
except MissingSeedError:
|
||||||
|
return False, 'Latest seeddb.bin is required, check the README for details'
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return False, f'Exception occurred: {type(e).__name__}: {e}'
|
return False, f'Exception occurred: {type(e).__name__}: {e}'
|
||||||
|
|
||||||
@@ -532,25 +567,26 @@ class CustomInstallGUI(ttk.Frame):
|
|||||||
mb.showinfo('Info', message, parent=self.parent)
|
mb.showinfo('Info', message, parent=self.parent)
|
||||||
|
|
||||||
def disable_buttons(self):
|
def disable_buttons(self):
|
||||||
for b in self.disable_during_install:
|
for b in self.require_boot9:
|
||||||
|
b.config(state=tk.DISABLED)
|
||||||
|
for b in self.file_picker_textboxes.values():
|
||||||
b.config(state=tk.DISABLED)
|
b.config(state=tk.DISABLED)
|
||||||
|
|
||||||
def enable_buttons(self):
|
def enable_buttons(self):
|
||||||
for b in self.disable_during_install:
|
if self.b9_loaded:
|
||||||
|
for b in self.require_boot9:
|
||||||
|
b.config(state=tk.NORMAL)
|
||||||
|
for b in self.file_picker_textboxes.values():
|
||||||
b.config(state=tk.NORMAL)
|
b.config(state=tk.NORMAL)
|
||||||
|
|
||||||
def start_install(self):
|
def start_install(self):
|
||||||
sd_root = self.file_picker_textboxes['sd'].get('1.0', tk.END).strip()
|
sd_root = self.file_picker_textboxes['sd'].get('1.0', tk.END).strip()
|
||||||
boot9 = self.file_picker_textboxes['boot9'].get('1.0', tk.END).strip()
|
|
||||||
seeddb = self.file_picker_textboxes['seeddb'].get('1.0', tk.END).strip()
|
seeddb = self.file_picker_textboxes['seeddb'].get('1.0', tk.END).strip()
|
||||||
movable_sed = self.file_picker_textboxes['movable.sed'].get('1.0', tk.END).strip()
|
movable_sed = self.file_picker_textboxes['movable.sed'].get('1.0', tk.END).strip()
|
||||||
|
|
||||||
if not sd_root:
|
if not sd_root:
|
||||||
self.show_error('SD root is not specified.')
|
self.show_error('SD root is not specified.')
|
||||||
return
|
return
|
||||||
if not boot9:
|
|
||||||
self.show_error('boot9 is not specified.')
|
|
||||||
return
|
|
||||||
if not movable_sed:
|
if not movable_sed:
|
||||||
self.show_error('movable.sed is not specified.')
|
self.show_error('movable.sed is not specified.')
|
||||||
return
|
return
|
||||||
@@ -571,9 +607,7 @@ class CustomInstallGUI(ttk.Frame):
|
|||||||
if taskbar:
|
if taskbar:
|
||||||
taskbar.SetProgressState(self.hwnd, tbl.TBPF_NORMAL)
|
taskbar.SetProgressState(self.hwnd, tbl.TBPF_NORMAL)
|
||||||
|
|
||||||
installer = CustomInstall(boot9=boot9,
|
installer = CustomInstall(movable=movable_sed,
|
||||||
seeddb=seeddb,
|
|
||||||
movable=movable_sed,
|
|
||||||
sd=sd_root,
|
sd=sd_root,
|
||||||
skip_contents=self.skip_contents_var.get() == 1,
|
skip_contents=self.skip_contents_var.get() == 1,
|
||||||
overwrite_saves=self.overwrite_saves_var.get() == 1)
|
overwrite_saves=self.overwrite_saves_var.get() == 1)
|
||||||
|
|||||||
Reference in New Issue
Block a user