mirror of
https://github.com/ihaveamac/custom-install.git
synced 2026-01-21 14:06:02 +00:00
Compare commits
18 Commits
finalize-1
...
module-new
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
379da26a4e | ||
|
|
ed7fc99ff1 | ||
|
|
3a5f554b58 | ||
|
|
ba5c5f19a7 | ||
|
|
c344ce3e7b | ||
|
|
13f706a0dc | ||
|
|
3c99c7a9d9 | ||
|
|
238b7400e0 | ||
|
|
2319819bfa | ||
|
|
cb52b38ea7 | ||
|
|
3dcee32145 | ||
|
|
647f21d32b | ||
|
|
58237a0ebe | ||
|
|
9f69a2195c | ||
|
|
91e0fa24ad | ||
|
|
b3365c47bd | ||
|
|
a515ca7e61 | ||
|
|
167a80ff11 |
@@ -4,11 +4,13 @@
|
|||||||
Experimental script to automate the process of a manual title install for Nintendo 3DS. Originally created late June 2019.
|
Experimental script to automate the process of a manual title install for Nintendo 3DS. Originally created late June 2019.
|
||||||
|
|
||||||
## Summary
|
## Summary
|
||||||
|
Note for Windows users: Enabling "Add Python 3.X to PATH" is **NOT** required! Python is installed with the `py` launcher by default.
|
||||||
|
|
||||||
1. [Dump boot9.bin and movable.sed](https://ihaveamac.github.io/dump.html) from a 3DS system.
|
1. [Dump boot9.bin and movable.sed](https://ihaveamac.github.io/dump.html) from a 3DS system.
|
||||||
2. Install the packages:
|
2. Download the repo ([zip link](https://github.com/ihaveamac/custom-install/archive/module-new-gui.zip) or `git clone`)
|
||||||
|
3. Install the packages:
|
||||||
* Windows: `py -3 -m pip install --user -r requirements.txt`
|
* Windows: `py -3 -m pip install --user -r requirements.txt`
|
||||||
* macOS/Linux: `python3 -m pip install --user -r requirements.txt`
|
* macOS/Linux: `python3 -m pip install --user -r requirements.txt`
|
||||||
3. Download the repo ([zip link](https://github.com/ihaveamac/custom-install/archive/master.zip) or `git clone`)
|
|
||||||
4. Run `custominstall.py` with boot9.bin, movable.sed, path to the SD root, and CIA files to install (see Usage section).
|
4. Run `custominstall.py` with boot9.bin, movable.sed, path to the SD root, and CIA files to install (see Usage section).
|
||||||
5. Download and use [custom-install-finalize](https://github.com/ihaveamac/custom-install/releases) on the 3DS system to finish the install.
|
5. Download and use [custom-install-finalize](https://github.com/ihaveamac/custom-install/releases) on the 3DS system to finish the install.
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ if TYPE_CHECKING:
|
|||||||
|
|
||||||
from events import Events
|
from events import Events
|
||||||
|
|
||||||
from pyctr.crypto import CryptoEngine, Keyslot
|
from pyctr.crypto import CryptoEngine, Keyslot, load_seeddb
|
||||||
from pyctr.type.cia import CIAReader, CIASection
|
from pyctr.type.cia import CIAReader, CIASection
|
||||||
from pyctr.type.ncch import NCCHSection
|
from pyctr.type.ncch import NCCHSection
|
||||||
from pyctr.util import roundup
|
from pyctr.util import roundup
|
||||||
@@ -134,7 +134,7 @@ class CustomInstall:
|
|||||||
|
|
||||||
cia: CIAReader
|
cia: CIAReader
|
||||||
|
|
||||||
def __init__(self, boot9, seeddb, movable, cias, sd, skip_contents=False):
|
def __init__(self, boot9, seeddb, movable, cias, sd, cifinish_out=None, 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
|
||||||
|
|
||||||
@@ -144,6 +144,7 @@ class CustomInstall:
|
|||||||
self.cias = cias
|
self.cias = cias
|
||||||
self.sd = sd
|
self.sd = sd
|
||||||
self.skip_contents = skip_contents
|
self.skip_contents = skip_contents
|
||||||
|
self.cifinish_out = cifinish_out
|
||||||
self.movable = movable
|
self.movable = movable
|
||||||
|
|
||||||
def copy_with_progress(self, src: BinaryIO, dst: BinaryIO, size: int, path: str):
|
def copy_with_progress(self, src: BinaryIO, dst: BinaryIO, size: int, path: str):
|
||||||
@@ -162,26 +163,33 @@ class CustomInstall:
|
|||||||
# TODO: Move a lot of these into their own methods
|
# TODO: Move a lot of these into their own methods
|
||||||
self.log("Finding path to install to...")
|
self.log("Finding path to install to...")
|
||||||
[sd_path, id1s] = self.get_sd_path()
|
[sd_path, id1s] = self.get_sd_path()
|
||||||
try:
|
|
||||||
if len(id1s) > 1:
|
if len(id1s) > 1:
|
||||||
raise SDPathError(f'There are multiple id1 directories for id0 {crypto.id0.hex()}, '
|
raise SDPathError(f'There are multiple id1 directories for id0 {crypto.id0.hex()}, '
|
||||||
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()}')
|
||||||
except SDPathError:
|
|
||||||
self.log("")
|
|
||||||
|
|
||||||
|
if self.cifinish_out:
|
||||||
|
cifinish_path = self.cifinish_out
|
||||||
|
else:
|
||||||
cifinish_path = join(self.sd, 'cifinish.bin')
|
cifinish_path = join(self.sd, 'cifinish.bin')
|
||||||
sd_path = join(sd_path, id1s[0])
|
sd_path = join(sd_path, id1s[0])
|
||||||
title_info_entries = {}
|
title_info_entries = {}
|
||||||
cifinish_data = load_cifinish(cifinish_path)
|
cifinish_data = load_cifinish(cifinish_path)
|
||||||
|
|
||||||
|
load_seeddb(self.seeddb)
|
||||||
|
|
||||||
# Now loop through all provided cia files
|
# Now loop through all provided cia files
|
||||||
|
|
||||||
for c in self.cias:
|
for c in self.cias:
|
||||||
self.log('Reading ' + c)
|
self.log('Reading ' + c)
|
||||||
|
|
||||||
cia = CIAReader(c, seeddb=self.seeddb)
|
try:
|
||||||
|
cia = CIAReader(c)
|
||||||
|
except Exception as e:
|
||||||
|
self.log(f'Failed to load file: {type(e).__name__}: {e}')
|
||||||
|
continue
|
||||||
|
|
||||||
self.cia = cia
|
self.cia = cia
|
||||||
|
|
||||||
tid_parts = (cia.tmd.title_id[0:8], cia.tmd.title_id[8:16])
|
tid_parts = (cia.tmd.title_id[0:8], cia.tmd.title_id[8:16])
|
||||||
@@ -229,20 +237,20 @@ class CustomInstall:
|
|||||||
title_root_cmd = f'/title/{"/".join(tid_parts)}'
|
title_root_cmd = f'/title/{"/".join(tid_parts)}'
|
||||||
content_root_cmd = title_root_cmd + '/content'
|
content_root_cmd = title_root_cmd + '/content'
|
||||||
|
|
||||||
|
if not self.skip_contents:
|
||||||
makedirs(join(content_root, 'cmd'), exist_ok=True)
|
makedirs(join(content_root, 'cmd'), exist_ok=True)
|
||||||
if cia.tmd.save_size:
|
if cia.tmd.save_size:
|
||||||
makedirs(join(title_root, 'data'), exist_ok=True)
|
makedirs(join(title_root, 'data'), exist_ok=True)
|
||||||
if is_dlc:
|
if is_dlc:
|
||||||
# create the separate directories for every 256 contents
|
# create the separate directories for every 256 contents
|
||||||
for x in range(((len(cia.content_info) - 1) // 256) + 1):
|
for x in range(((len(cia.content_info) - 1) // 256) + 1):
|
||||||
makedirs(join(content_root, f'{x:08x}'))
|
makedirs(join(content_root, f'{x:08x}'), exist_ok=True)
|
||||||
|
|
||||||
# maybe this will be changed in the future
|
# maybe this will be changed in the future
|
||||||
tmd_id = 0
|
tmd_id = 0
|
||||||
|
|
||||||
tmd_filename = f'{tmd_id:08x}.tmd'
|
tmd_filename = f'{tmd_id:08x}.tmd'
|
||||||
|
|
||||||
if not self.skip_contents:
|
|
||||||
# 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}...')
|
||||||
@@ -367,10 +375,12 @@ 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)
|
||||||
|
|
||||||
cifinish_data[int(cia.tmd.title_id, 16)] = {'seed': (cia.contents[0].seed if cia.contents[0].flags.uses_seed else None)}
|
cifinish_data[int(cia.tmd.title_id, 16)] = {'seed': (get_seed(cia.contents[0].program_id) if cia.contents[0].flags.uses_seed else None)}
|
||||||
|
|
||||||
|
# This is saved regardless if any titles were installed, so the file can be upgraded just in case.
|
||||||
save_cifinish(cifinish_path, cifinish_data)
|
save_cifinish(cifinish_path, cifinish_data)
|
||||||
|
|
||||||
|
if title_info_entries:
|
||||||
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
|
||||||
save3ds_fuse_common_args = [
|
save3ds_fuse_common_args = [
|
||||||
@@ -398,6 +408,9 @@ 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.')
|
||||||
|
|
||||||
|
else:
|
||||||
|
self.log('Did not install any titles.', 2)
|
||||||
|
|
||||||
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 = []
|
||||||
@@ -448,6 +461,7 @@ if __name__ == "__main__":
|
|||||||
parser.add_argument('-s', '--seeddb', help='seeddb file')
|
parser.add_argument('-s', '--seeddb', help='seeddb file')
|
||||||
parser.add_argument('--sd', help='path to SD root', required=True)
|
parser.add_argument('--sd', help='path to SD root', required=True)
|
||||||
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')
|
||||||
|
parser.add_argument('--cifinish-out', help='path for cifinish.bin file, defaults to (SD root)/cifinish.bin')
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
@@ -456,6 +470,7 @@ if __name__ == "__main__":
|
|||||||
cias=args.cia,
|
cias=args.cia,
|
||||||
movable=args.movable,
|
movable=args.movable,
|
||||||
sd=args.sd,
|
sd=args.sd,
|
||||||
|
cifinish_out=args.cifinish_out,
|
||||||
skip_contents=(args.skip_contents or False))
|
skip_contents=(args.skip_contents or False))
|
||||||
|
|
||||||
def log_handle(msg, end='\n'):
|
def log_handle(msg, end='\n'):
|
||||||
|
|||||||
@@ -85,6 +85,8 @@ int load_cifinish(char* path, struct finish_db_entry_final **entries)
|
|||||||
struct finish_db_entry_v2 v2;
|
struct finish_db_entry_v2 v2;
|
||||||
struct finish_db_entry_v3 v3;
|
struct finish_db_entry_v3 v3;
|
||||||
|
|
||||||
|
struct finish_db_entry_final *tmp;
|
||||||
|
|
||||||
int i;
|
int i;
|
||||||
size_t read;
|
size_t read;
|
||||||
|
|
||||||
@@ -114,6 +116,12 @@ int load_cifinish(char* path, struct finish_db_entry_final **entries)
|
|||||||
}
|
}
|
||||||
|
|
||||||
*entries = calloc(header.title_count, sizeof(struct finish_db_entry_final));
|
*entries = calloc(header.title_count, sizeof(struct finish_db_entry_final));
|
||||||
|
if (!*entries) {
|
||||||
|
printf("Couldn't allocate memory.\n");
|
||||||
|
printf("This should never happen.\n");
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
tmp = *entries;
|
||||||
|
|
||||||
if (header.version == 1)
|
if (header.version == 1)
|
||||||
{
|
{
|
||||||
@@ -133,9 +141,9 @@ int load_cifinish(char* path, struct finish_db_entry_final **entries)
|
|||||||
printf(" Is the file corrupt?\n");
|
printf(" Is the file corrupt?\n");
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
entries[i]->has_seed = v1.has_seed;
|
tmp[i].has_seed = v1.has_seed;
|
||||||
entries[i]->title_id = v1.title_id;
|
tmp[i].title_id = v1.title_id;
|
||||||
memcpy(entries[i]->seed, v1.seed, 16);
|
memcpy(tmp[i].seed, v1.seed, 16);
|
||||||
}
|
}
|
||||||
} else if (header.version == 2) {
|
} else if (header.version == 2) {
|
||||||
for (i = 0; i < header.title_count; i++)
|
for (i = 0; i < header.title_count; i++)
|
||||||
@@ -154,9 +162,9 @@ int load_cifinish(char* path, struct finish_db_entry_final **entries)
|
|||||||
printf(" Is the file corrupt?\n");
|
printf(" Is the file corrupt?\n");
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
entries[i]->has_seed = v2.has_seed;
|
tmp[i].has_seed = v2.has_seed;
|
||||||
entries[i]->title_id = v2.title_id;
|
tmp[i].title_id = v2.title_id;
|
||||||
memcpy(entries[i]->seed, v2.seed, 16);
|
memcpy(tmp[i].seed, v2.seed, 16);
|
||||||
}
|
}
|
||||||
} else if (header.version == 3) {
|
} else if (header.version == 3) {
|
||||||
for (i = 0; i < header.title_count; i++)
|
for (i = 0; i < header.title_count; i++)
|
||||||
@@ -175,9 +183,9 @@ int load_cifinish(char* path, struct finish_db_entry_final **entries)
|
|||||||
printf(" Is the file corrupt?\n");
|
printf(" Is the file corrupt?\n");
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
entries[i]->has_seed = v3.has_seed;
|
tmp[i].has_seed = v3.has_seed;
|
||||||
entries[i]->title_id = v3.title_id;
|
tmp[i].title_id = v3.title_id;
|
||||||
memcpy(entries[i]->seed, v3.seed, 16);
|
memcpy(tmp[i].seed, v3.seed, 16);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -267,7 +275,7 @@ int main(int argc, char* argv[])
|
|||||||
gfxInitDefault();
|
gfxInitDefault();
|
||||||
consoleInit(GFX_TOP, NULL);
|
consoleInit(GFX_TOP, NULL);
|
||||||
|
|
||||||
printf("custom-install-finalize v1.3\n");
|
printf("custom-install-finalize v1.4\n");
|
||||||
|
|
||||||
finalize_install();
|
finalize_install();
|
||||||
// print this at the end in case it gets pushed off the screen
|
// print this at the end in case it gets pushed off the screen
|
||||||
|
|||||||
57
gui.py
57
gui.py
@@ -12,12 +12,6 @@ import tkinter.filedialog as tkfiledialog
|
|||||||
import style
|
import style
|
||||||
|
|
||||||
|
|
||||||
# BUTTON_COLOR =
|
|
||||||
# BUTTON_HIGHLIGHT_COLOR =
|
|
||||||
# BUTTON_FONT =
|
|
||||||
|
|
||||||
# Custom button
|
|
||||||
|
|
||||||
class themedFrame(tk.Frame):
|
class themedFrame(tk.Frame):
|
||||||
def __init__(self, frame, **kw):
|
def __init__(self, frame, **kw):
|
||||||
tk.Frame.__init__(self, frame, **kw)
|
tk.Frame.__init__(self, frame, **kw)
|
||||||
@@ -304,9 +298,13 @@ class gui(tk.Tk):
|
|||||||
self.boot9_box.place(relwidth=1, height=20, x=0, y=60)
|
self.boot9_box.place(relwidth=1, height=20, x=0, y=60)
|
||||||
CreateToolTip(self.boot9_box.xtainer, "Select the path to boot9.bin, this can be dumped from a 3ds")
|
CreateToolTip(self.boot9_box.xtainer, "Select the path to boot9.bin, this can be dumped from a 3ds")
|
||||||
|
|
||||||
|
self.seeddb_box = LabeledPathEntry(outer_frame, "Path to seeddb file -", filetypes=[('seeddb file', '*.bin')])
|
||||||
|
self.seeddb_box.place(relwidth=1, height=20, x=0, y=90)
|
||||||
|
CreateToolTip(self.seeddb_box.xtainer, "Select the path to seeddb.bin, this can retrieved from online")
|
||||||
|
|
||||||
# -------------------------------------------------
|
# -------------------------------------------------
|
||||||
cia_container = themedFrame(outer_frame, borderwidth=0, highlightthickness=0)
|
cia_container = themedFrame(outer_frame, borderwidth=0, highlightthickness=0)
|
||||||
cia_container.place(y=90, relwidth=1, height=115)
|
cia_container.place(y=120, relwidth=1, height=190)
|
||||||
|
|
||||||
cia_label = tk.Label(cia_container, text="cia paths - ", foreground=style.LABEL_COLOR,
|
cia_label = tk.Label(cia_container, text="cia paths - ", foreground=style.LABEL_COLOR,
|
||||||
background=style.BACKGROUND_COLOR)
|
background=style.BACKGROUND_COLOR)
|
||||||
@@ -318,31 +316,31 @@ class gui(tk.Tk):
|
|||||||
"Select the cias you wish to install to the sd card. The `add folder` button will add all cias in the selected folder, but will not check subdirs. The `remove cia` button will remove the currently selected file from the listbox.")
|
"Select the cias you wish to install to the sd card. The `add folder` button will add all cias in the selected folder, but will not check subdirs. The `remove cia` button will remove the currently selected file from the listbox.")
|
||||||
|
|
||||||
add_cia_button = Button(cia_container, self.add_cia, text="add cia", font=style.monospace)
|
add_cia_button = Button(cia_container, self.add_cia, text="add cia", font=style.monospace)
|
||||||
add_cia_button.place(relx=0, relwidth=0.333, height=20, y=92, width=- 6)
|
add_cia_button.place(relx=0, relwidth=0.333, height=20, y=95, width=- 6)
|
||||||
|
|
||||||
add_cia_folder_button = Button(cia_container, self.add_cia_folder, text="add folder", font=style.monospace)
|
add_cia_folder_button = Button(cia_container, self.add_cia_folder, text="add folder", font=style.monospace)
|
||||||
add_cia_folder_button.place(relx=0.333, relwidth=0.333, height=20, y=92, x=+ 3, width=- 6)
|
add_cia_folder_button.place(relx=0.333, relwidth=0.333, height=20, y=95, x=+ 3, width=- 6)
|
||||||
|
|
||||||
remove_cia_button = Button(cia_container, self.remove_cia, text="remove cia", font=style.monospace)
|
remove_cia_button = Button(cia_container, self.remove_cia, text="remove cia", font=style.monospace)
|
||||||
remove_cia_button.place(relx=0.666, relwidth=0.333, height=20, y=92, x=+ 6, width=- 6)
|
remove_cia_button.place(relx=0.666, relwidth=0.333, height=20, y=95, x=+ 6, width=- 6)
|
||||||
# -------------------------------------------------
|
# -------------------------------------------------
|
||||||
|
|
||||||
self.skip_contents = tk.IntVar()
|
self.skip_contents = tk.IntVar()
|
||||||
skip_contents_checkbutton = tk.Checkbutton(outer_frame, text="Skip contents? (only add title info)",
|
skip_contents_checkbutton = tk.Checkbutton(outer_frame, text="Skip contents? (only add title info)",
|
||||||
variable=self.skip_contents, background=style.BACKGROUND_COLOR,
|
variable=self.skip_contents, background=style.BACKGROUND_COLOR,
|
||||||
foreground=style.LABEL_COLOR, borderwidth=0, highlightthickness=0)
|
foreground=style.LABEL_COLOR, borderwidth=0, highlightthickness=0)
|
||||||
skip_contents_checkbutton.place(relwidth=1, y=205, height=20)
|
skip_contents_checkbutton.place(relwidth=1, y=239, height=20)
|
||||||
|
|
||||||
console_label = tk.Label(outer_frame, text="Console:", background="black", foreground="white",
|
console_label = tk.Label(outer_frame, text="Console:", background="black", foreground="white",
|
||||||
font=style.boldmonospace, borderwidth=0, highlightthickness=0)
|
font=style.boldmonospace, borderwidth=0, highlightthickness=0)
|
||||||
console_label.place(relwidth=1, height=20, y=230)
|
console_label.place(relwidth=1, height=20, y=260)
|
||||||
self.console = ScrolledText(outer_frame, background="black", foreground="white", highlightthickness=0)
|
self.console = ScrolledText(outer_frame, background="black", foreground="white", highlightthickness=0)
|
||||||
self.console.place(relwidth=1, relheight=1, y=250, height=- 272)
|
self.console.place(relwidth=1, relheight=1, y=280, height=- 272)
|
||||||
run_button = Button(outer_frame, self.run, text="run", font=style.boldmonospace)
|
run_button = Button(outer_frame, self.run, text="run", font=style.boldmonospace)
|
||||||
run_button.place(relwidth=1, rely=1, y=- 22)
|
run_button.place(relwidth=1, rely=1, y=- 22)
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
argstring = ""
|
args_extra = []
|
||||||
|
|
||||||
self.output_to_console("-----------------------\nStarting...\n")
|
self.output_to_console("-----------------------\nStarting...\n")
|
||||||
|
|
||||||
@@ -350,32 +348,38 @@ class gui(tk.Tk):
|
|||||||
if not boot9:
|
if not boot9:
|
||||||
self.output_to_console(
|
self.output_to_console(
|
||||||
"Warning - boot9 not selected, if it's not set externally you may run into problems.\n")
|
"Warning - boot9 not selected, if it's not set externally you may run into problems.\n")
|
||||||
argstring += f"-b {boot9} "
|
else:
|
||||||
|
args_extra.extend(['-b', boot9])
|
||||||
|
|
||||||
sed = self.sed_box.get()
|
sed = self.sed_box.get()
|
||||||
if not sed:
|
if not sed:
|
||||||
self.output_to_console("Failed to run - No movable.sed selected.\n")
|
self.output_to_console("Failed to run - No movable.sed selected.\n")
|
||||||
return
|
return
|
||||||
argstring += f"-m {sed} "
|
args_extra.extend(['-m', sed])
|
||||||
|
|
||||||
sd = self.sd_box.get().strip()
|
sd = self.sd_box.get().strip()
|
||||||
if not sd:
|
if not sd:
|
||||||
self.output_to_console("Failed to run - SD path not selected.\n")
|
self.output_to_console("Failed to run - SD path not selected.\n")
|
||||||
return
|
return
|
||||||
argstring += f"--sd {sd} "
|
args_extra.extend(['--sd', sd])
|
||||||
|
|
||||||
|
seed = self.seeddb_box.get().strip()
|
||||||
|
if not seed:
|
||||||
|
self.output_to_console("Optional Seeddb not given - Certain CIAs May Require This!\n")
|
||||||
|
args_extra.extend(['--seeddb', seed])
|
||||||
|
|
||||||
cias = []
|
cias = []
|
||||||
for i in range(0, self.cia_box.size()):
|
for i in range(0, self.cia_box.size()):
|
||||||
cias.append(self.cia_box.get(i).strip())
|
cias.append(self.cia_box.get(i).strip())
|
||||||
for cia in cias:
|
for cia in cias:
|
||||||
argstring += f" {cia}"
|
args_extra.append(cia)
|
||||||
|
|
||||||
if self.skip_contents.get():
|
if self.skip_contents.get():
|
||||||
argstring += "--skip-contents "
|
args_extra.append('--skip-contents')
|
||||||
|
|
||||||
print(f"Running custom-install.py with args {args}\n")
|
print(f"Running custom-install.py with args {args_extra}\n")
|
||||||
|
|
||||||
self.threader.do_async(execute_script, [argstring, self.output_to_console])
|
self.threader.do_async(execute_script, [args_extra, self.output_to_console])
|
||||||
|
|
||||||
def output_to_console(self, outstring):
|
def output_to_console(self, outstring):
|
||||||
self.console.insert('end', outstring)
|
self.console.insert('end', outstring)
|
||||||
@@ -393,7 +397,7 @@ class gui(tk.Tk):
|
|||||||
(os.path.isfile(os.path.join(cia_dir_to_add, f)) and f.endswith(".cia"))]
|
(os.path.isfile(os.path.join(cia_dir_to_add, f)) and f.endswith(".cia"))]
|
||||||
if cias_to_add:
|
if cias_to_add:
|
||||||
for cia_to_add in cias_to_add:
|
for cia_to_add in cias_to_add:
|
||||||
self.cia_box.insert('end', cia_to_add)
|
self.cia_box.insert('end', os.path.join(cia_dir_to_add, cia_to_add))
|
||||||
|
|
||||||
def remove_cia(self):
|
def remove_cia(self):
|
||||||
index = self.cia_box.curselection()
|
index = self.cia_box.curselection()
|
||||||
@@ -410,12 +414,11 @@ class gui(tk.Tk):
|
|||||||
self.cia_box.select_set(0)
|
self.cia_box.select_set(0)
|
||||||
|
|
||||||
|
|
||||||
def execute_script(argstring, printer):
|
def execute_script(args_extra, printer):
|
||||||
"""Wrapper function to pipe install script output to a printer"""
|
"""Wrapper function to pipe install script output to a printer"""
|
||||||
try:
|
|
||||||
args = [sys.executable, '-u', os.path.join(os.path.dirname(__file__), "custominstall.py")]
|
args = [sys.executable, '-u', os.path.join(os.path.dirname(__file__), "custominstall.py")]
|
||||||
for arg in argstring.split():
|
try:
|
||||||
args.append(arg.strip())
|
args.extend(args_extra)
|
||||||
p = subprocess.Popen(args,
|
p = subprocess.Popen(args,
|
||||||
stdout=subprocess.PIPE,
|
stdout=subprocess.PIPE,
|
||||||
stderr=subprocess.STDOUT,
|
stderr=subprocess.STDOUT,
|
||||||
@@ -427,7 +430,7 @@ def execute_script(argstring, printer):
|
|||||||
printer(line)
|
printer(line)
|
||||||
p.wait()
|
p.wait()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
printer(f"Error while executing script with args - {argstring} | Exception - {e}\n")
|
printer(f"Error while executing script with args - {args} | Exception - {e}\n")
|
||||||
|
|
||||||
|
|
||||||
t = threader_object()
|
t = threader_object()
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
pycryptodomex<=3.9.4
|
pycryptodomex<=3.9.4
|
||||||
events==0.3
|
events==0.3
|
||||||
pyctr==0.1.0
|
pyctr==0.4.1
|
||||||
|
|||||||
Reference in New Issue
Block a user