From 0195ea75d4adcafd457b6e5c7d40b1c2f9320f03 Mon Sep 17 00:00:00 2001 From: Ian Burgwin Date: Sat, 1 Feb 2020 21:58:41 -0800 Subject: [PATCH 01/18] README: remove mention of --seeddb This option does not actually exist on this branch. It will be in module-rewrite eventually. --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 6a98224..997d0eb 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,6 @@ boot9 is needed: A [SeedDB](https://github.com/ihaveamac/3DS-rom-tools/wiki/SeedDB-list) is needed for newer games (2015+) that use seeds. SeedDB is checked in order of: -* `--seeddb` argument (if set) * `SEEDDB_PATH` environment variable (if set) * `%APPDATA%\3ds\seeddb.bin` (Windows-specific) * `~/Library/Application Support/3ds/seeddb.bin` (macOS-specific) From c0e1d45054476a25dd12917f783fa2b28982f4d3 Mon Sep 17 00:00:00 2001 From: LyfeOnEdge Date: Wed, 25 Mar 2020 19:01:55 -0700 Subject: [PATCH 02/18] Add gui.py --- .gitignore | 2 + gui.py | 403 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 405 insertions(+) create mode 100644 .gitignore create mode 100644 gui.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fd20fdd --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ + +*.pyc diff --git a/gui.py b/gui.py new file mode 100644 index 0000000..30c1077 --- /dev/null +++ b/gui.py @@ -0,0 +1,403 @@ +#A gui for custom-install.py +#By LyfeOnEdge +import os, sys, platform, subprocess, threading +import tkinter as tk +import tkinter.filedialog as tkfiledialog + +STANDARD_OFFSET = 10 #Offset to place everything +BUTTONSIZE = 30 +monospace = ("Monospace",10) +boldmonospace = ("Monospace",10,"bold") + +# Custom button +class Button(tk.Label): + """Cross-platform button""" + def __init__(self,frame,callback,**kw): + self.callback = callback + self.background = "#aaaaaa" + self.selected = False + tk.Label.__init__(self, frame, **kw) + self.configure(anchor="center") + self.configure(background=self.background) + self.configure(highlightthickness=1) + self.configure(font = monospace) + self.configure(highlightbackground = "#999999") + self.bind('', self.on_click) + + # Use callback when our makeshift "button" clicked + def on_click(self, event=None): + self.configure(background="#dddddd") + if not self.selected: + self.after(100, self.on_click_color_change) + if self.callback: + self.callback() + + # Function to set the button's image + def setimage(self, image): + self.configure(image=image) + + # Function to set the button's text + def settext(self, text): + self.configure(text=text) + + def deselect(self): + self.selected = False + self.configure(background=self.background) + + def on_click_color_change(self): + if not self.selected: + self.configure(background=self.background) + +class PathEntry(tk.Entry): + """Tkinter entry widget with a button to set the file path using tkinter's file dialog""" + def __init__(self, frame, dir = False, filetypes = None, *args, **kw): + self.dir = dir + self.filetypes = filetypes + container = tk.Frame(frame) + self.button = Button(container, self.set_path, text = "...") + self.button.place(relheight = 1, relx = 1, x = - BUTTONSIZE, width = BUTTONSIZE) + tk.Entry.__init__(self, container, *args, **kw) + self.text_var = tk.StringVar() + self.configure(textvariable = self.text_var) + super().place(relwidth = 1, relheight = 1, width = - BUTTONSIZE) + self.container = container + + def clear(self): + self.text_var.set("") + + def set(self, string): + self.text_var.set(string) + + def get_var(self): + return self.text_var + + def get(self): + return self.text_var.get() + + def place(self, **kw): + self.container.place(**kw) + + def set_path(self): + if not self.dir: + self.set(tkfiledialog.askopenfilename(filetypes = self.filetypes)) + else: + self.set(tkfiledialog.askdirectory()) + +class LabeledPathEntry(PathEntry): + """Gives the PathEntry class a label""" + def __init__(self, frame, text, *args, **kw): + self.xtainer = tk.Frame(frame) + label = tk.Label(self.xtainer, text = text) + label.place(width = label.winfo_reqwidth(), relheight = 1) + PathEntry.__init__(self, self.xtainer, *args, **kw) + PathEntry.place(self, relwidth = 1, relheight = 1, width = - (label.winfo_reqwidth() + 5), x = label.winfo_reqwidth() + 5) + + def place(self, **kw): + self.xtainer.place(**kw) + + +class AutoScroll(object): + def __init__(self, master): + try: + vsb = tk.Scrollbar(master, orient='vertical', command=self.yview) + except: + pass + hsb = tk.Scrollbar(master, orient='horizontal', command=self.xview) + + try: + self.configure(yscrollcommand=self._autoscroll(vsb)) + except: + pass + self.configure(xscrollcommand=self._autoscroll(hsb)) + + self.grid(column=0, row=0, sticky='nsew') + try: + vsb.grid(column=1, row=0, sticky='ns') + except: + pass + hsb.grid(column=0, row=1, sticky='ew') + + master.grid_columnconfigure(0, weight=1) + master.grid_rowconfigure(0, weight=1) + + methods = tk.Pack.__dict__.keys() | tk.Grid.__dict__.keys() \ + | tk.Place.__dict__.keys() + + for m in methods: + if m[0] != '_' and m not in ('config', 'configure'): + setattr(self, m, getattr(master, m)) + + + @staticmethod + def _autoscroll(sbar): + '''Hide and show scrollbar as needed.''' + def wrapped(first, last): + first, last = float(first), float(last) + if first <= 0 and last >= 1: + sbar.grid_remove() + else: + sbar.grid() + sbar.set(first, last) + return wrapped + + def __str__(self): + return str(self.master) + + +def _create_container(func): + '''Creates a tk Frame with a given master, and use this new frame to + place the scrollbars and the widget.''' + def wrapped(cls, master, **kw): + container = tk.Frame(master) + container.bind('', lambda e: _bound_to_mousewheel(e, container)) + container.bind( + '', lambda e: _unbound_to_mousewheel(e, container)) + return func(cls, container, **kw) + return wrapped + + +def _bound_to_mousewheel(event, widget): + child = widget.winfo_children()[0] + if platform.system() == 'Windows' or platform.system() == 'Darwin': + child.bind_all('', lambda e: _on_mousewheel(e, child)) + child.bind_all('', + lambda e: _on_shiftmouse(e, child)) + else: + child.bind_all('', lambda e: _on_mousewheel(e, child)) + child.bind_all('', lambda e: _on_mousewheel(e, child)) + child.bind_all('', lambda e: _on_shiftmouse(e, child)) + child.bind_all('', lambda e: _on_shiftmouse(e, child)) + + +def _unbound_to_mousewheel(event, widget): + if platform.system() == 'Windows' or platform.system() == 'Darwin': + widget.unbind_all('') + widget.unbind_all('') + else: + widget.unbind_all('') + widget.unbind_all('') + widget.unbind_all('') + widget.unbind_all('') + + +def _on_mousewheel(event, widget): + if platform.system() == 'Windows': + widget.yview_scroll(-1 * int(event.delta / 120), 'units') + elif platform.system() == 'Darwin': + widget.yview_scroll(-1 * int(event.delta), 'units') + else: + if event.num == 4: + widget.yview_scroll(-1, 'units') + elif event.num == 5: + widget.yview_scroll(1, 'units') + + +class ScrolledText(AutoScroll, tk.Text): + @_create_container + def __init__(self, master, **kw): + tk.Text.__init__(self, master, **kw) + AutoScroll.__init__(self, master) + +# from https://stackoverflow.com/questions/3221956/how-do-i-display-tooltips-in-tkinter +class CreateToolTip(object): + """ + create a tooltip for a given widget + """ + def __init__(self, widget, text='widget info'): + self.waittime = 500 #miliseconds + self.wraplength = 180 #pixels + self.widget = widget + self.text = text + self.widget.bind("", self.enter) + self.widget.bind("", self.leave) + self.widget.bind("", self.leave) + self.id = None + self.tw = None + + def enter(self, event=None): + self.schedule() + + def leave(self, event=None): + self.unschedule() + self.hidetip() + + def schedule(self): + self.unschedule() + self.id = self.widget.after(self.waittime, self.showtip) + + def unschedule(self): + id = self.id + self.id = None + if id: + self.widget.after_cancel(id) + + def showtip(self, event=None): + x = y = 0 + x, y, cx, cy = self.widget.bbox("insert") + x += self.widget.winfo_rootx() + 25 + y += self.widget.winfo_rooty() + 20 + # creates a toplevel window + self.tw = tk.Toplevel(self.widget) + # Leaves only the label and removes the app window + self.tw.wm_overrideredirect(True) + self.tw.wm_geometry("+%d+%d" % (x, y)) + label = tk.Label(self.tw, text=self.text, justify='left', + background="#ffffff", relief='solid', borderwidth=1, + wraplength = self.wraplength) + label.pack(ipadx=1) + + def hidetip(self): + tw = self.tw + self.tw= None + if tw: + tw.destroy() + + +class threader_object: + """an object to be declared outside of tk root so + things can be called asyncronously (you cannot start + a new thread from within a tkinter callback so you + must call it from an object that exists outside)""" + def do_async(self, func, arglist = []): + threading.Thread(target = func, args = arglist).start() + + +class gui(tk.Tk): + def __init__(self, threader): + self.threader = threader + tk.Tk.__init__(self) + self.minsize(300, 400) + self.title("custom-install gui") + + outer_frame = tk.Frame(self) + outer_frame.place(relwidth = 1, relheight = 1, x = + STANDARD_OFFSET, width = - 2 * STANDARD_OFFSET, y = + STANDARD_OFFSET, height = - 2 * STANDARD_OFFSET) + + self.sd_box = LabeledPathEntry(outer_frame, "Path to SD root -", dir = True) + self.sd_box.place(relwidth = 1, height = 20, x = 0) + + self.sed_box = LabeledPathEntry(outer_frame, "Path to movable.sed file -", filetypes = [('sed file', '*.sed')]) + self.sed_box.place(relwidth = 1, height = 20, x = 0, y = 30) + + self.boot9_box = LabeledPathEntry(outer_frame, "Path to boot9 file -", filetypes = [('boot9 file', '*.bin')]) + self.boot9_box.place(relwidth = 1, height = 20, x = 0, y = 60) + + #------------------------------------------------- + cia_container = tk.Frame(outer_frame, borderwidth = 0, highlightthickness = 0) + cia_container.place(y = 90, relwidth = 1, height = 115) + + cia_label = tk.Label(cia_container, text = "cia paths - ") + cia_label.place(relwidth = 1, height = 20) + self.cia_box = tk.Listbox(cia_container, highlightthickness = 0) + self.cia_box.place(relwidth = 1, height = 70, y = 20) + + add_cia_button = Button(cia_container, self.add_cia, text = "add cia", font = monospace) + add_cia_button.place(relx = 0, relwidth = 0.333, height = 20, y = 92, width = - 6) + + add_cia_folder_button = Button(cia_container, self.add_cia_folder, text = "add folder", font = monospace) + add_cia_folder_button.place(relx = 0.333, relwidth = 0.333, height = 20, y = 92, x = + 3, width = - 6) + + remove_cia_button = Button(cia_container, self.remove_cia, text = "remove cia", font = monospace) + remove_cia_button.place(relx = 0.666, relwidth = 0.333, height = 20, y = 92, x = + 6, width = - 6) + #------------------------------------------------- + + self.skip_contents = tk.IntVar() + skip_contents_checkbutton = tk.Checkbutton(outer_frame, text="Skip contents? (only add title info)", variable=self.skip_contents) + skip_contents_checkbutton.place(relwidth = 1, y = 205, height = 20) + + console_label = tk.Label(outer_frame, text = "Console:", background = "black", foreground = "white", font = boldmonospace, borderwidth = 0, highlightthickness = 0) + console_label.place(relwidth = 1, height = 20, y = 230) + self.console = ScrolledText(outer_frame, background = "black", foreground = "white", highlightthickness = 0) + self.console.place(relwidth = 1, relheight = 1, y = 250, height = - 272) + run_button = Button(outer_frame, self.run, text = "run", font = boldmonospace) + run_button.place(relwidth = 1, rely = 1, y = - 22) + + def run(self): + argstring = "" + + self.output_to_console("-----------------------\nStarting...\n") + + boot9 = self.boot9_box.get() + if not boot9: + self.output_to_console("Warning - boot9 not selected, if it's not set externally you may run into problems.\n") + argstring += f"-b {boot9} " + + sed = self.sed_box.get() + if not sed: + self.output_to_console("Failed to run - No movable.sed selected.\n") + return + argstring += f"-m {sed} " + + sd = self.sd_box.get().strip() + if not sd: + self.output_to_console("Failed to run - SD path not selected.\n") + return + argstring += f"--sd {sd} " + + cias = [] + for i in range(0, self.cia_box.size()): + cias.append(self.cia_box.get(i).strip()) + for cia in cias: + argstring += f" {cia}" + + if self.skip_contents.get(): + argstring += "--skip-contents " + + print(f"Running custom-install.py with args {args}\n") + + self.threader.do_async(execute_script, [argstring, self.output_to_console]) + + def output_to_console(self, outstring): + self.console.insert('end', outstring) + self.console.see('end') + + def add_cia(self): + cia_to_add = tkfiledialog.askopenfilename(filetypes = [('cia file', '*.cia')]) + if cia_to_add: + self.cia_box.insert('end', cia_to_add) + + def add_cia_folder(self): + cia_dir_to_add = tkfiledialog.askdirectory() + if cia_dir_to_add: + cias_to_add = [f for f in os.listdir(cia_dir_to_add) if (os.path.isfile(os.path.join(cia_dir_to_add, f)) and f.endswith(".cia"))] + if cias_to_add: + for cia_to_add in cias_to_add: + self.cia_box.insert('end', cia_to_add) + + def remove_cia(self): + index = self.cia_box.curselection() + if index: + self.cia_box.delete(index) + if self.cia_box.size(): + self.cia_box.select_clear(0, 'end') + if self.cia_box.size() > 1: + try: + self.cia_box.select_set(index) + except: + pass + else: + self.cia_box.select_set(0) + + +def execute_script(argstring, printer): + """Wrapper function to pipe install script output to a printer""" + try: + args = [sys.executable, '-u', os.path.join(os.path.dirname(__file__), "custom-install.py")] + for arg in argstring.split(): + args.append(arg.strip()) + p = subprocess.Popen(args, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + bufsize=1, + ) + + with p.stdout: + for line in iter(p.stdout.readline, b''): + printer(line) + p.wait() + except Exception as e: + printer(f"Error while executing script with args - {argstring} | Exception - {e}\n") + + +t = threader_object() +window = gui(t) +window.mainloop() From f21b63f9dd3c9add2dd6f570cbbb8554b895ddda Mon Sep 17 00:00:00 2001 From: LyfeOnEdge Date: Wed, 25 Mar 2020 19:21:22 -0700 Subject: [PATCH 03/18] document gui --- README.md | 5 +++++ docu/main.png | Bin 0 -> 18974 bytes 2 files changed, 5 insertions(+) create mode 100644 docu/main.png diff --git a/README.md b/README.md index 997d0eb..dea3eab 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,11 @@ python3 custom-install.py -b boot9.bin -m movable.sed --sd /Volumes/GM9SD file.c python3 custom-install.py -b boot9.bin -m movable.sed --sd /media/GM9SD file.cia file2.cia ``` +## GUI +GUI wrapper to easily manage your apps. +[GUI](https://raw.githubusercontent.com/LyfeOnEdge/custom-install/master/docu/main.png) +GUI by LyfeOnEdge, developed on the brewtools discord - https://www.brewtools.dev + ## License/Credits `pyctr/` is from [ninfs `d994c78`](https://github.com/ihaveamac/ninfs/tree/d994c78acf5ff3840df1ef5a6aabdc12ca98e806/ninfs/pyctr). diff --git a/docu/main.png b/docu/main.png new file mode 100644 index 0000000000000000000000000000000000000000..ebb1139c1be50310e5f0db89722e79b4aabb1b6a GIT binary patch literal 18974 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

kIA?KBWFgi-<$U@VQUTB!we)W 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 literal 0 HcmV?d00001 From ff196a667dca3150aed60d90ff614eb568fefb8f Mon Sep 17 00:00:00 2001 From: LyfeOnEdge Date: Wed, 25 Mar 2020 19:22:33 -0700 Subject: [PATCH 04/18] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index dea3eab..e03261f 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ python3 custom-install.py -b boot9.bin -m movable.sed --sd /media/GM9SD file.cia ## GUI GUI wrapper to easily manage your apps. -[GUI](https://raw.githubusercontent.com/LyfeOnEdge/custom-install/master/docu/main.png) +![GUI](https://raw.githubusercontent.com/LyfeOnEdge/custom-install/master/docu/main.png) GUI by LyfeOnEdge, developed on the brewtools discord - https://www.brewtools.dev ## License/Credits From 12d59cad5db06c7c40cec941952de9b3a0461145 Mon Sep 17 00:00:00 2001 From: LyfeOnEdge Date: Wed, 25 Mar 2020 19:23:20 -0700 Subject: [PATCH 05/18] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index e03261f..de1be31 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,9 @@ python3 custom-install.py -b boot9.bin -m movable.sed --sd /media/GM9SD file.cia ## GUI GUI wrapper to easily manage your apps. + ![GUI](https://raw.githubusercontent.com/LyfeOnEdge/custom-install/master/docu/main.png) + GUI by LyfeOnEdge, developed on the brewtools discord - https://www.brewtools.dev ## License/Credits From 8ee248c793f1953550acf4a79be8487406fe96ae Mon Sep 17 00:00:00 2001 From: LyfeOnEdge Date: Wed, 25 Mar 2020 19:35:02 -0700 Subject: [PATCH 06/18] Update README.md --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index de1be31..1f761ff 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,11 @@ GUI wrapper to easily manage your apps. ![GUI](https://raw.githubusercontent.com/LyfeOnEdge/custom-install/master/docu/main.png) GUI by LyfeOnEdge, developed on the brewtools discord - https://www.brewtools.dev +Special thanks to CrafterPika and archbox for testing. + +### Setup +- Ubuntu/Debian: `sudo apt install python3-tk` +- Manjaro/Arch: `sudo pacman -S tk` ## License/Credits `pyctr/` is from [ninfs `d994c78`](https://github.com/ihaveamac/ninfs/tree/d994c78acf5ff3840df1ef5a6aabdc12ca98e806/ninfs/pyctr). From 29e17bec6db546c31573ba1d5077a6855530e628 Mon Sep 17 00:00:00 2001 From: LyfeOnEdge Date: Wed, 25 Mar 2020 19:35:22 -0700 Subject: [PATCH 07/18] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 1f761ff..77aaec7 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,7 @@ GUI wrapper to easily manage your apps. ![GUI](https://raw.githubusercontent.com/LyfeOnEdge/custom-install/master/docu/main.png) GUI by LyfeOnEdge, developed on the brewtools discord - https://www.brewtools.dev + Special thanks to CrafterPika and archbox for testing. ### Setup From 6922c0d209883c102577a16b357745720d130d63 Mon Sep 17 00:00:00 2001 From: LyfeOnEdge Date: Wed, 25 Mar 2020 19:40:09 -0700 Subject: [PATCH 08/18] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 77aaec7..b59a61d 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,8 @@ Special thanks to CrafterPika and archbox for testing. ### Setup - Ubuntu/Debian: `sudo apt install python3-tk` - Manjaro/Arch: `sudo pacman -S tk` +- Mac: Sometimes the default tkinter libs that ship with mac don't work, you can get them on the python site. - `https://www.python.org/downloads/mac-osx/` +- Windows: Install python - `Remember to install tcl/tk when doing a custom installation` ## License/Credits `pyctr/` is from [ninfs `d994c78`](https://github.com/ihaveamac/ninfs/tree/d994c78acf5ff3840df1ef5a6aabdc12ca98e806/ninfs/pyctr). From a08654160a2dae217a29739b135ad95fe36f45f5 Mon Sep 17 00:00:00 2001 From: LyfeOnEdge Date: Wed, 25 Mar 2020 19:40:43 -0700 Subject: [PATCH 09/18] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b59a61d..31ae998 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,7 @@ GUI by LyfeOnEdge, developed on the brewtools discord - https://www.brewtools.de Special thanks to CrafterPika and archbox for testing. -### Setup +### GUI Setup - Ubuntu/Debian: `sudo apt install python3-tk` - Manjaro/Arch: `sudo pacman -S tk` - Mac: Sometimes the default tkinter libs that ship with mac don't work, you can get them on the python site. - `https://www.python.org/downloads/mac-osx/` From e8787a2d9a63f0bf439fd76660843e1b88b46929 Mon Sep 17 00:00:00 2001 From: LyfeOnEdge Date: Wed, 25 Mar 2020 19:41:08 -0700 Subject: [PATCH 10/18] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 31ae998..b66881f 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ Special thanks to CrafterPika and archbox for testing. ### GUI Setup - Ubuntu/Debian: `sudo apt install python3-tk` - Manjaro/Arch: `sudo pacman -S tk` -- Mac: Sometimes the default tkinter libs that ship with mac don't work, you can get them on the python site. - `https://www.python.org/downloads/mac-osx/` +- Mac: Sometimes the default tkinter libs that ship with mac don't work, you can get them on the python site - `https://www.python.org/downloads/mac-osx/` - Windows: Install python - `Remember to install tcl/tk when doing a custom installation` ## License/Credits From b799e3af1ae0c1c4053c7d2eeb2010da822713c5 Mon Sep 17 00:00:00 2001 From: LyfeOnEdge Date: Thu, 26 Mar 2020 17:56:06 -0700 Subject: [PATCH 11/18] add style.py and tooltips --- gui.py | 139 +++++++++++++++++++++++++++---------------------------- style.py | 17 +++++++ 2 files changed, 86 insertions(+), 70 deletions(-) create mode 100644 style.py diff --git a/gui.py b/gui.py index 30c1077..ac5be20 100644 --- a/gui.py +++ b/gui.py @@ -3,13 +3,25 @@ import os, sys, platform, subprocess, threading import tkinter as tk import tkinter.filedialog as tkfiledialog +import style -STANDARD_OFFSET = 10 #Offset to place everything -BUTTONSIZE = 30 -monospace = ("Monospace",10) -boldmonospace = ("Monospace",10,"bold") +# BUTTON_COLOR = +# BUTTON_HIGHLIGHT_COLOR = +# BUTTON_FONT = # Custom button + +class themedFrame(tk.Frame): + def __init__(self, frame, **kw): + tk.Frame.__init__(self, frame, **kw) + if not (kw.get("background") or kw.get("bg")): + self.configure(bg = style.BACKGROUND_COLOR) + if not kw.get("borderwidth"): + self.configure(borderwidth = 0) + if not kw.get("highlightthickness"): + self.configure(highlightthickness = 0) + + class Button(tk.Label): """Cross-platform button""" def __init__(self,frame,callback,**kw): @@ -20,7 +32,8 @@ class Button(tk.Label): self.configure(anchor="center") self.configure(background=self.background) self.configure(highlightthickness=1) - self.configure(font = monospace) + if not "font" in kw.keys(): + self.configure(font = style.BUTTON_FONT) self.configure(highlightbackground = "#999999") self.bind('', self.on_click) @@ -53,13 +66,15 @@ class PathEntry(tk.Entry): def __init__(self, frame, dir = False, filetypes = None, *args, **kw): self.dir = dir self.filetypes = filetypes - container = tk.Frame(frame) + container = themedFrame(frame) self.button = Button(container, self.set_path, text = "...") - self.button.place(relheight = 1, relx = 1, x = - BUTTONSIZE, width = BUTTONSIZE) + self.button.place(relheight = 1, relx = 1, x = - style.BUTTONSIZE, width = style.BUTTONSIZE) tk.Entry.__init__(self, container, *args, **kw) self.text_var = tk.StringVar() self.configure(textvariable = self.text_var) - super().place(relwidth = 1, relheight = 1, width = - BUTTONSIZE) + self.configure(background = style.ENTRY_COLOR) + self.configure(foreground = style.ENTRY_FOREGROUND) + super().place(relwidth = 1, relheight = 1, width = - style.BUTTONSIZE) self.container = container def clear(self): @@ -86,7 +101,7 @@ class PathEntry(tk.Entry): class LabeledPathEntry(PathEntry): """Gives the PathEntry class a label""" def __init__(self, frame, text, *args, **kw): - self.xtainer = tk.Frame(frame) + self.xtainer = themedFrame(frame) label = tk.Label(self.xtainer, text = text) label.place(width = label.winfo_reqwidth(), relheight = 1) PathEntry.__init__(self, self.xtainer, *args, **kw) @@ -148,7 +163,7 @@ def _create_container(func): '''Creates a tk Frame with a given master, and use this new frame to place the scrollbars and the widget.''' def wrapped(cls, master, **kw): - container = tk.Frame(master) + container = themedFrame(master) container.bind('', lambda e: _bound_to_mousewheel(e, container)) container.bind( '', lambda e: _unbound_to_mousewheel(e, container)) @@ -199,58 +214,38 @@ class ScrolledText(AutoScroll, tk.Text): AutoScroll.__init__(self, master) # from https://stackoverflow.com/questions/3221956/how-do-i-display-tooltips-in-tkinter + + class CreateToolTip(object): - """ - create a tooltip for a given widget - """ - def __init__(self, widget, text='widget info'): - self.waittime = 500 #miliseconds - self.wraplength = 180 #pixels - self.widget = widget - self.text = text - self.widget.bind("", self.enter) - self.widget.bind("", self.leave) - self.widget.bind("", self.leave) - self.id = None - self.tw = None + ''' + create a tooltip for a given widget + ''' + def __init__(self, widget, text='widget info'): + self.widget = widget + self.text = text + self.widget.bind("", self.enter) + self.widget.bind("", self.close) - def enter(self, event=None): - self.schedule() + def enter(self, event=None): + x = y = 0 + x, y, cx, cy = self.widget.bbox("insert") + x += self.widget.winfo_rootx() + y += self.widget.winfo_rooty() + 20 + # creates a toplevel window + self.tw = tk.Toplevel(self.widget) + # Leaves only the label and removes the app window + self.tw.wm_overrideredirect(True) + self.tw.wm_geometry("+%d+%d" % (x, y)) + label = tk.Label(self.tw, text=self.text, justify='left', + background='gray', foreground = "white", + relief='solid', borderwidth=2, + font=("times", "12", "normal"), + wraplength = self.widget.winfo_width()) + label.pack(ipadx=1) - def leave(self, event=None): - self.unschedule() - self.hidetip() - - def schedule(self): - self.unschedule() - self.id = self.widget.after(self.waittime, self.showtip) - - def unschedule(self): - id = self.id - self.id = None - if id: - self.widget.after_cancel(id) - - def showtip(self, event=None): - x = y = 0 - x, y, cx, cy = self.widget.bbox("insert") - x += self.widget.winfo_rootx() + 25 - y += self.widget.winfo_rooty() + 20 - # creates a toplevel window - self.tw = tk.Toplevel(self.widget) - # Leaves only the label and removes the app window - self.tw.wm_overrideredirect(True) - self.tw.wm_geometry("+%d+%d" % (x, y)) - label = tk.Label(self.tw, text=self.text, justify='left', - background="#ffffff", relief='solid', borderwidth=1, - wraplength = self.wraplength) - label.pack(ipadx=1) - - def hidetip(self): - tw = self.tw - self.tw= None - if tw: - tw.destroy() + def close(self, event=None): + if self.tw: + self.tw.destroy() class threader_object: @@ -269,34 +264,38 @@ class gui(tk.Tk): self.minsize(300, 400) self.title("custom-install gui") - outer_frame = tk.Frame(self) - outer_frame.place(relwidth = 1, relheight = 1, x = + STANDARD_OFFSET, width = - 2 * STANDARD_OFFSET, y = + STANDARD_OFFSET, height = - 2 * STANDARD_OFFSET) + outer_frame = themedFrame(self) + outer_frame.place(relwidth = 1, relheight = 1, x = + style.STANDARD_OFFSET, width = - 2 * style.STANDARD_OFFSET, y = + style.STANDARD_OFFSET, height = - 2 * style.STANDARD_OFFSET) self.sd_box = LabeledPathEntry(outer_frame, "Path to SD root -", dir = True) self.sd_box.place(relwidth = 1, height = 20, x = 0) + CreateToolTip(self.sd_box.xtainer, "Select the root of the sd card you wish to install the cias to.") self.sed_box = LabeledPathEntry(outer_frame, "Path to movable.sed file -", filetypes = [('sed file', '*.sed')]) self.sed_box.place(relwidth = 1, height = 20, x = 0, y = 30) - + CreateToolTip(self.sed_box.xtainer, "Select movable.sed file, this can be dumped from a 3ds") + self.boot9_box = LabeledPathEntry(outer_frame, "Path to boot9 file -", filetypes = [('boot9 file', '*.bin')]) 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") #------------------------------------------------- - cia_container = tk.Frame(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_label = tk.Label(cia_container, text = "cia paths - ") cia_label.place(relwidth = 1, height = 20) - self.cia_box = tk.Listbox(cia_container, highlightthickness = 0) + self.cia_box = tk.Listbox(cia_container, highlightthickness = 0, bg = style.ENTRY_COLOR, foreground = style.ENTRY_FOREGROUND) self.cia_box.place(relwidth = 1, height = 70, y = 20) + CreateToolTip(cia_label, "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 = 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_folder_button = Button(cia_container, self.add_cia_folder, text = "add folder", font = 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) - remove_cia_button = Button(cia_container, self.remove_cia, text = "remove cia", font = 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) #------------------------------------------------- @@ -304,11 +303,11 @@ class gui(tk.Tk): skip_contents_checkbutton = tk.Checkbutton(outer_frame, text="Skip contents? (only add title info)", variable=self.skip_contents) skip_contents_checkbutton.place(relwidth = 1, y = 205, height = 20) - console_label = tk.Label(outer_frame, text = "Console:", background = "black", foreground = "white", font = boldmonospace, borderwidth = 0, highlightthickness = 0) + console_label = tk.Label(outer_frame, text = "Console:", background = "black", foreground = "white", font = style.boldmonospace, borderwidth = 0, highlightthickness = 0) console_label.place(relwidth = 1, height = 20, y = 230) self.console = ScrolledText(outer_frame, background = "black", foreground = "white", highlightthickness = 0) self.console.place(relwidth = 1, relheight = 1, y = 250, height = - 272) - run_button = Button(outer_frame, self.run, text = "run", font = boldmonospace) + run_button = Button(outer_frame, self.run, text = "run", font = style.boldmonospace) run_button.place(relwidth = 1, rely = 1, y = - 22) def run(self): diff --git a/style.py b/style.py new file mode 100644 index 0000000..db2c65c --- /dev/null +++ b/style.py @@ -0,0 +1,17 @@ +STANDARD_OFFSET = 10 #Offset to place everything +BUTTONSIZE = 30 + + +monospace = ("Monospace",10) +boldmonospace = ("Monospace",10,"bold") + +BUTTON_FONT = monospace + +BACKGROUND_COLOR = "#d9d9d9" +BUTTON_COLOR = "#aaaaaa" +ENTRY_COLOR = "white" +ENTRY_FOREGROUND = "black" + + + + From 14e5692cacc02181ecb62525d1688c573068751f Mon Sep 17 00:00:00 2001 From: LyfeOnEdge Date: Thu, 26 Mar 2020 18:16:40 -0700 Subject: [PATCH 12/18] update color scheme and fix some elements' coloration --- gui.py | 15 ++++++++++----- style.py | 5 +++-- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/gui.py b/gui.py index ac5be20..5be923f 100644 --- a/gui.py +++ b/gui.py @@ -74,6 +74,9 @@ class PathEntry(tk.Entry): self.configure(textvariable = self.text_var) self.configure(background = style.ENTRY_COLOR) self.configure(foreground = style.ENTRY_FOREGROUND) + self.configure(borderwidth = 0) + self.configure(highlightthickness = 2) + self.configure(highlightbackground = style.BUTTON_COLOR) super().place(relwidth = 1, relheight = 1, width = - style.BUTTONSIZE) self.container = container @@ -102,7 +105,7 @@ class LabeledPathEntry(PathEntry): """Gives the PathEntry class a label""" def __init__(self, frame, text, *args, **kw): self.xtainer = themedFrame(frame) - label = tk.Label(self.xtainer, text = text) + label = tk.Label(self.xtainer, text = text, background = style.BACKGROUND_COLOR, foreground = style.LABEL_COLOR) label.place(width = label.winfo_reqwidth(), relheight = 1) PathEntry.__init__(self, self.xtainer, *args, **kw) PathEntry.place(self, relwidth = 1, relheight = 1, width = - (label.winfo_reqwidth() + 5), x = label.winfo_reqwidth() + 5) @@ -237,7 +240,7 @@ class CreateToolTip(object): self.tw.wm_overrideredirect(True) self.tw.wm_geometry("+%d+%d" % (x, y)) label = tk.Label(self.tw, text=self.text, justify='left', - background='gray', foreground = "white", + background='gray', foreground = style.LABEL_COLOR, relief='solid', borderwidth=2, font=("times", "12", "normal"), wraplength = self.widget.winfo_width()) @@ -263,8 +266,10 @@ class gui(tk.Tk): tk.Tk.__init__(self) self.minsize(300, 400) self.title("custom-install gui") + self.f = themedFrame(self) + self.f.place(relwidth = 1, relheight = 1) - outer_frame = themedFrame(self) + outer_frame = themedFrame(self.f) outer_frame.place(relwidth = 1, relheight = 1, x = + style.STANDARD_OFFSET, width = - 2 * style.STANDARD_OFFSET, y = + style.STANDARD_OFFSET, height = - 2 * style.STANDARD_OFFSET) self.sd_box = LabeledPathEntry(outer_frame, "Path to SD root -", dir = True) @@ -283,7 +288,7 @@ class gui(tk.Tk): cia_container = themedFrame(outer_frame, borderwidth = 0, highlightthickness = 0) cia_container.place(y = 90, relwidth = 1, height = 115) - cia_label = tk.Label(cia_container, text = "cia paths - ") + cia_label = tk.Label(cia_container, text = "cia paths - ", foreground = style.LABEL_COLOR, background = style.BACKGROUND_COLOR) cia_label.place(relwidth = 1, height = 20) self.cia_box = tk.Listbox(cia_container, highlightthickness = 0, bg = style.ENTRY_COLOR, foreground = style.ENTRY_FOREGROUND) self.cia_box.place(relwidth = 1, height = 70, y = 20) @@ -300,7 +305,7 @@ class gui(tk.Tk): #------------------------------------------------- self.skip_contents = tk.IntVar() - skip_contents_checkbutton = tk.Checkbutton(outer_frame, text="Skip contents? (only add title info)", variable=self.skip_contents) + skip_contents_checkbutton = tk.Checkbutton(outer_frame, text="Skip contents? (only add title info)", variable=self.skip_contents, background = style.BACKGROUND_COLOR, foreground = style.LABEL_COLOR, borderwidth = 0, highlightthickness = 0) skip_contents_checkbutton.place(relwidth = 1, y = 205, height = 20) console_label = tk.Label(outer_frame, text = "Console:", background = "black", foreground = "white", font = style.boldmonospace, borderwidth = 0, highlightthickness = 0) diff --git a/style.py b/style.py index db2c65c..66d806d 100644 --- a/style.py +++ b/style.py @@ -7,11 +7,12 @@ boldmonospace = ("Monospace",10,"bold") BUTTON_FONT = monospace -BACKGROUND_COLOR = "#d9d9d9" +BACKGROUND_COLOR = "#20232a" BUTTON_COLOR = "#aaaaaa" -ENTRY_COLOR = "white" +ENTRY_COLOR = "#373940" ENTRY_FOREGROUND = "black" +LABEL_COLOR = "#61dafb" From 13d0cbd796c8f02e6904b909cc7df587b1c970ec Mon Sep 17 00:00:00 2001 From: LyfeOnEdge Date: Thu, 26 Mar 2020 18:43:37 -0700 Subject: [PATCH 13/18] Update main.png --- docu/main.png | Bin 18974 -> 18808 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/docu/main.png b/docu/main.png index ebb1139c1be50310e5f0db89722e79b4aabb1b6a..44cf65e45af8735d3c93e3d6579d8e0b78b08882 100644 GIT binary patch literal 18808 zcmeFZWmF_hx-ATiySq1yH4QZGPDA7F?(VJy4UN0IySuwK?pnCJyVd2LIW}|W++M1RaInERAxp*u6*{h_l^valMzFL$AN&KVX!gI=HrY?RVX`u8Hs$Eyr!SGG@#Ayn%sR_U5#Mmh!ELi}m z&_93JFDo8D@yU`xUP7&cSIk;`2|$gk`D82r9u0?kq9LFSAqSq@&dcNBWVYecp48+D zQ=xK_Lwz?cPZm&2f>o{YjWTbz{$MJGocZSZx)D62h>|m(ez*YajH}-A!Ai~SP}q2H z58qK854)_vrU6?BmTHO`9zO!QLno@Ot&NV3&U&j;IA|UGen$^>!T1n=El0cU!2c6O z5Dpgh*S)>HC96gcWi41T`uNh)B`dZuY=nY>g3zum5SET~YP0FV{yF$H5smQ9W-X7m zx3?!GG$fCll2U-Qb8`?)04mN8dHEQ0Lc-VVrXVr)uM>((duCvP{Y~aK%{BQVx}ak? zGLgQoG&J^>8##R#0@dU<-1xqo0`a%60jNn#X{EUWU#7-lo`{eHT+_y}l|UA7Wc>sB z%JX_(=_l(X5A;$r$_BQlk~Lt-@Z$^`JhnFBbi^R!KDMNS*F}pvw@cCee~`7bzjsv> zkLGZ}36P28!A=8liS=@=F=%^QZx~8OYpAWL?G4>Kxl>HUqUPo^AhS`obKCON@%kX7 z4lN&PM6+e~TsD|&u4xQoa^N<0SU zPquDbS+q73f&=rfiq^Wh-SuwmDs6H~o>=Y0Tk6zP`nFsv$$X4g_cix#pE){{ic4R$ zKLJA180a*HP`B5AI$s|4Cd>NlH(i)Af6)NCo#clcoF8QE)CAe&#Sw zlWab2=2h9Mnhc4=49(enM!Sdza(FEgN!kPh>iKp5?q^$V!|x`K=LW z5t?vos4M+4rwekSgfD#^IU>6-x(+AuZhmpp>m8vqV#b+&lX;(Y>WJ~}9t%vml1O5f zu}Yy{ZzbiD`;03~6ux`Z9)cZzAm|Y&#%OWK>J(`fsE|I`X{)=}a?$I4S449=eR|NR zPyWOtF1%=(bbZb%i1~hr=&uV5z@sC36TQAo);z*K3#w$v8p8lwzX!Og0X%f48g`}! z2;4BgQ;iuMVo3h^5?u-M2#;~SqvBgaGJMuq_?9^&Hu@l6{ApzYF_R^-he&gp=MN5p zHBAZ(XlSI%F@(qsf>+~3(C4#eQpc#Ku2r9$qL|d8A9vOxJG)1rKJXRi;w)-=0teJ* zeMSy@bu-WwB4NM6_x`Z);06j3jxQGISU3{Xi1bJ+vW+C0+d$BE=NtA^xw&g2xgNc` z36lS+{4jZ}!`+5VcH3^tg0iuY)TqBMry(HYfexeU(>)sYV0hK?JLSGnanEJRums5G z09Sm*e0DnN8IYD>(pp-OSgG?#4(*q(11Ae1q4wlSMRG4p(ra%xiX1prB)w zJ+&{XR-}nL52sWX^2>mhPtabA_t|(%{DJALN@$1=GF6TX`Ov3PE05g=Z4Hm-3d!o| z;c5X}k#})8e4IO;v`GO`U6kil*lhlVm=Y=}`f*c?uzV=7#7s~3IXr8Wz+W@F0Xy2| z6*sY4MWo-EoTG!Hb>OAtHTQJpprXXx_N#UWB-HOhLmjB>!@ObT!2W~lMK)2eg{|&f z+F3}Q$v-bE+d24Z{5On+^xS^pJ0VHy0poP<>72Ag<-E?9Dj2vjj;>G*PTV&nfWW;J z6&5ZcH6@O!Y*HWnH!VU^5UF&>W8H&~2DxlaD z1Snm7xQDWTNdLhzOP>6$+PIT34y>Lx%mBMKSsRb2TQfUeM4a@sbbHSOP37%jIUy@? zBrCQMTd+EpwN6^}+HCfurR6Cyt3AE;wKq0KC&y&n92GBFjFh-@bJmti>R3CwTt6RT zQ8h)y9aAmBbEGs{Q?0PdS7VhMAE8ivFY%rKDPCeoFy>(ZL7*_wlZP`&uRhprPTtH_ zD_+sTXsrda(n_;*&3-+ri>F3S^^~N$Y%D+R)>PnaG@pf4G61ubWCr!4r>_n!lrG30 zhPfuE2e=4B=K(FHIsQDJwp{JbOu%Nloka6i?a9w#iT!e~8oxuZ8Pu405hUTnyjG4Q)jC728!t!AW`IzN_ zoo5(}bpFZhcEdpj#HnGWABtBMxhvK7K5ZqXShT*|foVD=4(Sq5b2AxUmxp=jxc8*O zwr65PX`-`N$F%B~OFyv-OVf{Ua?)?sM=}zR8#{iP0wq2z2`5_CZ&(G}oT$gkOkcZ( z_W!sz=&8>yH@iE2+-Yx*_w>YZ&JBnm#u_ZXuVC`fBK=O&;;vsY^eKvhQ{h84tESH9 zzACc+s~_bdVjMOtF_GiX_CRO4pN~&p_71P^7ZemMNUQpx$2F#Mgh^A$Ta*!|o;FRn z$7C;LcZ;AZ@ZNh4!fiHEXrI0Qos-?`g_r8LY|7F^UzFr2{5brf?|#SwsSZMj)?J6xLbz1-tC zI77Gc+Ux!R`=mEup$x7oB*Y_6EmCpcaHEb96>BDMezBOQ2_0B>z<@ypPJF;mI23cH zB#Xnmd0K-rIH6?YzkzUvXyY>RV=6R|^3A5oVeI0TQSjltN&oq+>mUPeeRJ27NfL}J zh0YlZ^tPj;)4~|wr`VOZG5eIvvd*&1>CZh_wRO?Jx$5EhotNsH$sYNDnHR*w%jMAu z-&ni5>sE?;Y&kl+kfymH9p#x3acM!Ybz8XB;$qZ^j*iUYs>7;uncMzG*7eiF!;HJx zm&K0ycpr?BX;07GqIhA+P7`gkC6YF_^yl(f1V`i;%M$^+hZ?VTJJR6+Y4iEp^u*f2 z=wt)Iv#EH?LZ5MwSveD{;{68agDl3zf=Yv{jRz)cJ?ceS-I2T5vXW}|sI@bv7Rzu_ z9TU49E|QyNooowoPy$SeR|$fz`JI6jz}s`3r4C+quUR?eLFf;IVEUWssPTAN)KxE4 zSKWaTnHyW5Bx|%)^`*#8d8cHRBt=l zZH=I8qqDsxcZSP*TWBRq?6URb!9@a@XxqE)$&HBZUZz_KG~l}XuY>)!k#$52E)=@K zIY=K5Q$krldsOX+>X0g5wC>)qJNJoNOM2=%p!8!T;;e_XC?}so)~LoG8;(SyY^L2X z3mx=;$TFD4deSD#C$?8R6H1}w6qvh-${>xsR?WI6?`K@f3GImuP4u5jw^V?$;l9Ao zajVMagjfpzxAI2jwJK0ow52yG0irzhvdny?*Xkj32N3yfQZUk|guvji>lpCk?jGN=|st#?D*_q;fLLkHUZ7bY9pIXPqV*f$W zSKhKY1>8F&v(rZY22U1{H&PyLrOeUA?_z%~8VL?hrj_`LDFeGz`vvSbD!t?9) ziD@vR90Ku#u8GOf>IZbygl8kU#$9@Ayp?W-Gl5YQlf;k{Q?cn5JjCZ=R$2reD!*6}X_ky)rzGUL&14foU6dh&T z_w%w6usXtuJtVT;=4J@brUv7fqSX*pGO9XAMih8uJI7$ZiqaKa&%Wg;T@#mw$F9bs~_0`LTfm#2(EF=A0zndE7Ac=%wzU|=OwH}S7u+OE&L_!?WZfltU|qq00}Zh z9dEo)62ZxKS*u$QWZ5VFYsoxVBHj#ShSX&~A8?8U=BZ>!_ZAds?grs5Qyh~ZZZitZ z*GHn?{v=e@9o*^!l4U7kL6hT6v7i-PHW>zi+5V{Rnj6c?wDG9y8rH>*l{+kC*4lG=v*Uh-OnwB%&u}chaWR`W2T-$Z_tE>BQ<2#PE&~SMU#kZ%+y9`h$-Ln2k2clC$^PSJty|U6zzn z&VRHC0b7i*Q9#Y?3}IYIkkv)7_Znw_6@=9f@uwCHM@==>w>T6=n!I>xuVn zF4W9Ir_jcVd^4bgLee*KkNZWD)#N z;81-?x_4y8Rii5Vi?eI?xRg4sqKwjOL7{#k#+TmTtSi5#ENtK@EeYSX{pG==!_q=p z@^+R5H5e!VRYWdr#wC9YN`~4@dEk9fyLxbV~|3?`h0OCK<;2f_EAVdK1nGh z(kl!G{|;8JO*3>?^MaeGE7{??xrj4;U$yf=$_XA;6)KO0=nmd8_6D(kNa`g`#`z_g zYmcXqDj$RvhNp8K`x>4#rAUW5BYy2L8?I&Z#Dh=~x9$!R=cMky^aD2<|Fh83&LCwX{$_Rv?q@azbh8%l;Za1^u z(1a}T#8}IhCA4tBI<9BSYgwv>KAf-!)ooN{<5H;F)+rQhG2DP51bZ-5zd3;y)?AlU z$nHCOVgf4!yCs6|D+HTFA*{C*4OsEs3;_F!wBco4u)fL=|0FkO(TgyP-v%$lg3Ad_ z9bl*a_txP5qvEwBD%n@}n>+Ie?cb6*hHr@HckBlo4LpAxq2uvs{+qTjx;W&E!98W`D)!70tGU+wiXdF!KzM{xN7>#$g{rg@gxSk@ zRljrPmj1Plc5wPV?;GIf=JCKRoL!j`NYdK7pI;Q>9*h?*r8UsSdlejO-k>+VF|3?| zne+WX{zcYXp%)flh$l#JPPz3`|E%1DXB-S&*z>kR6@s|dy&i)iucj3`e=1XV#vhlY zHM5j}0kT5^>D?kR)a}%r6?*_uk@3Y=73<`ggY2BD3@1mhKu+-IJ!H|hNKfieN<5sc zh&B z!g%B+(N(s>OT{4~$cMgdjUQL5D`DCTqkGHztt4xVNP9=J?|9sB2(hrp?%H?ZL-Ge-;tZ0zVr`L#c}M}u<?B-5 za}PyqFV%2p@ID})iD?UNnaWmMztZCVBF-6)M#e(udc@m``0jfU z+Y>sYF+7>iJP#M|A@X+S>5n4-iRn#WQ?2z3D5m}9_9o&izTgq#br&cSWPR|5*?;+Y z1ijgr#V>Q7H+SvlzNGhZ4GZ4M#^mLLw6+?%r8TRdyWa`92ok*xn1%NxF-!Us_5kK7 ze`M(I-i`gZPH&RIF{f}C6^Gk0))e2s?F@DlTB?>4b&ucdtU}< z3TG3#739D&rKc?j)kP_nEgzpaw~3=T`1Z=Zi#2Guhf1j2hufi^~)r`UvC1(Ew*j*Nr;42gCybBTZx5x z@oN`s^Xlh()KjZf+)9If@@x2*gq|(ggpRS#)hZtsR-WXwT+=g)AOK#B=dkl5o+FLU65|xhVwwCPQLMo9bOcXoZ5qUF#F`HsbZ)5T+z=1+**+? z0(#7n^Knyc2BC~s$c=AQUx9K>)TW3Q?rPi4RgGZ1y`Pao(4^-Q`P&_$jw`Ui6>3ooI&oG^M3Ibf5Rr*Rzo3 z3$x8m!-Fi98xUE~I7C^Ivlh-xO%YCgWd)E2=e7~l7SxgH$Ze69U;N=tHxhioz2%!; zCEiOgcJ6`D0%4|UZcatIioeY834W?OC?rUR&&>(PFL7;dv>^8hYoZNi(2A>b{LYZ= z3M9J}{k7xdGO92erT+!KYTwkAj)DbRmi++9@1&it@LMX@mh}iZ z^Q)Lr3mmU)(0O2~*z6dNVncPjgblv6t4uuGo|}6ch#gI0Yo7e`3Hpz5^$}b`LsF#m z(vqnn_f!_Eu&J4963=8*-b{q*fvJ)cD;dqY%rV8p#=GTf_AbHJRn{`oPI!W+cNu2r zsD2x3aUsFnqJzh8%9EcCK!PJxPmqx*f=*rcZjeDc^DbcHB<_*>@)`nR4;gibOy?%6 zSaJqIsE(5nESB@=t|08k+vA8L2Xw{8c^`FmKuqU&)_$K&Umud4rTCxfwyPPAsT-&G zwL7$kzR>At>Bb(lKIcvxuC*upiz2(HGLB~-s%@P|DQm`g)bBi8msmc>Ps1S#7NXk}xY#@+nZJsH zi8-T!1=!02l|o%BzLW?+u^b8kcL+LQDn7+0=A1C4Ka#~XyMI-iaf#+M{aRAO_F`Pp zV$=a5lpd7X51k|#Qb`u|ue*Uei4I(AXEcOGOK4NY;pzbA zSSdLsz?!x3_FoVQmh3-j>-l5rSH|_GspsD#cV`0I822qpNwZsf$;rQG^KwlOJ?iWN z#;TeTB1rG%51kmwe-R1C1vNHd z7g8U{ONnXg_Xn7QvXA-0DDW2}@j(+bfBS$_|C6HbuV85V=S+|7tdbfZ=!3&IFd~W% z99Nw5m0iSI4+*Um-8WZoSUUlv{%-F2r=>j-+~RXcaz_Y#)9=^1Bz2O1!dVFahR2Kt zoC;<@zfqBvA#T4I9pkUu*~2I)g~2(l(AOr8?LKCSo$=p^!;!y67(-e&rv+Th9;-r` z#!pXKlp5BZi-h}xV)O3U&0@;cIx`OOX$6yYwVJM2*md2De->u1cHI@m#7;6`ZJV`v zncSsF(yCWR+jf|}L=oNi*X+YGpsq}z*PL;S=rga1o!PZf=twHY<3&k5pflhs4qLx8 z*6%-oS`l%Tdf*l?spa)ziBhD`{1Qw_l8Lb(y?YQY9Km$9{$c3RHB4~X6L#{163x+5 z*Q*y|I?+_F`5prqQ2Q%Zf&J4EK+H-^_OLnBa?m%m;c(255o^*-BRuPe(S}<6kC#7> zZcJi}1=D3#$yusV-y&*VG#K!2)qf z0xLta9G#3E1}VfYBW3ojjVoN|smVX>+6JDw0moW4;F4# zGw*>~&IO@Q4>eZF-oe$2s;ITb)ZB>$N?i)A`;f?1O&a= zn^2Py!aNb>==wB}@iUO+roN^a0oXE$qShxC%x|beJQI;0T!n-=IX_UYQB=%pl+AWk z8du$Z4&JUNSiKtxvEQU0=PCY!E%IvtD%ZXFFR!osTXlg<zk&Ju(ZlZt{?zw4*}7DPGwKz5)e z6@=mVYLK+UAW_x-V1#;!$}`BhY%&W4SP-FnmX3LN1g`ZBFcW#gW7G#?iO7pzr%vz(wvfK{ad7 z_=1$5IX;@qb@QwM+#B{?IA}(yNbus|;Ic2>a?g=&%U>#gBIU<^&ld^isOY9$Bn9SY z)S0zKJW15;*u{^8{w>?J#ep=NntI>rI!SixvCu%kzw_<81`3IUfE0q$xdtZG+^@Yu z%eMecMv4p$U|$U$HN$=X+H8%QT(8Df2_fZ;-O)16e?ci#E-8b;FxBbAhGol`L=!!{ zpV2cit}!Dr_K1p2>=V7ebX;LeXTG4UCGkXA(Dl{Zb8^yDT&bKroDt>(H1z=Mi2O(> zrjB2KEB+mnAk&ytdWF4Xz$kkeY9QMgtU`RD;4GpSVo40-hpRP*hSRkYZiqLSkSS_E zoeLT~u`r;D>OU(wSiVyDRG6!9$P&WG&CI zGd45_I@G&0hKRYg+DM>E8$!@X%Jdo(@w|+dS@Y7VA>qYirP!Z1(!RIy?w&hMz`?WL zp;9y=Kf1cuHCvAKCOhPAGq57n4baS^=F0y!{77evhJd;a9`P0L<+W|#Ixz42t^RoA z`>XhhC{d0fCF)8ch4kka3|1RRY1wZSZ*t>V7Sc&*{W=>Vl;CKOYrHAJ&4xzh>roa} zJf5><+OkN*Z$Y!y$DNOjM?&2d3^P~L>+$q}s9PzjyY_1k>E5E(r=c;@R$Wh?Uwu~= z+`ju!JYqM+2v0L;m81V0$CF6^isSFlJfa_Qd=ilVqR30FRufvwoLBR=I8N&IZLLd7 zxC`ao-!=p0bUr?ZHTrEmj*xdLv{*H9<|M-$GQnqbU6rpWe^8}Ku72AwyrXc|yGMa* zbz5d(4DJh+hG2tYX2%h0c~NN;>EjmrA(JEDmFs}s>%O=Ppk_9NTNEx3DysIN$m>;X zBJbc=ffnNt&965HMl25FXgaNkzXjx>=0(81k@LpvKS$%&y!1|j@K@}|ti4-XKO|`3 z`82X6nOd(Ix5TCM{Rlka;NsPe*6t%{|DLV3o-f3}4U{E4TfP>&U#PvDEOgC|gP+f> zijR;suUkK=gg9dUM&%Eqs<9LUR^E2{`NKXca&_=e8XqUVfK%i52jCQbkoW=Bl7H6` z{;$mc|FJ=6UA&&g*wW9%HVrlx>4_N;bVg4MZ5ssh)6`%9DV%_Pq(V3lK*`XOZ;xFS zfcCyGTG^cEzVl7DnXrW@kwcQM|MF~9>)q8Iqn8ogxANylX8w{%aooD@_ViVa={~x~ zj%ldT%9_YDHds=GBnb>ihzx9kCs;Q59pC&2x)^VY1_APx>boEx zGBJ*Lwxq`<`J@~a&i3}k2zz6){~dLF4V@L)_Q$^HA*X)xKyK@#={B4~=#TPF{k4Yi z$Dzx=naWfCxo3xPH6dafLvVJ9Quzba9sdMt#ozF&Z?A+?uj=}Fy%1{XT^KF`4<5fM z!K_LBYV9+?;yd;cJxX!BiCf^SCS4Vk&J$|{wYb!`%YcIq;LpPEeZ)L8G zmK6+{tlg3Yso3;D$G<)(I@qLtaOFhH6g|x)RKu(8zB4=oyC(Wv@?C{!m13W!2B_(_ zFJ}n9Say+9aj_f%QS$CCb0GWc1r92I$P3V^Me39s@v2CLP)*z;&wrALy|If578&J> z=Sp%d9m{tdssDR0P!wEyAUF~WLVeg$_|;T76kQ!yeg~wkFbS^wx9re<|34_~!Qo1& zPgC>M4V2?g?Uj@+*FRT%JTi$GDD=L5{VfxBc=L;!H7PgStbsb6SdNxjV;m}8Rj^?m zRVlGAU#c}?RYnzks(~H+VVun3@Zr?mgfcMp2Bx>M!nWpYx}^yITVFca*1_>E6E03( zA#ra1LZZo8`{_hkz~IhVjKK4C1TKQ4yfTsv8l#Jcr3MeMUf9)xm;y3uNnj*4u6mhn zGSEU%j%B5_BEAB4XCfj0TYB$Ak!~NV8+niYyjN3N?f1g;;zvp8KC2R-K|g;KWOf7-~>dX_Gf$gD4oG$UEf;Utm|3`M!SUXJ@r$@mHXDwknu$GYeA5Gb})l4`p{ z(!j8P;&vv=1|9dDzB1O;Sdwe0ZP&cL`6Au#$Nf#t{JT;~2!T0aDEay6X#ESj3vYm3 z^zDC2py?mEw^B>1672MMrGb{1dD0;;f8pK0#1A?dpL1JZ$NNpXD3L~3?|+np{C`PU z{$JbwLuNO_Dw0Dk#N+|#_>J0o^pQb+aP=}5;GN&M_$TNU z(yyP>98V|6&o=Dx-w3kEf!Xx2JW4hTojIP-LD$q#F2UPew&oLdAI=k4Z2&8de7v?9S9PZ@DkRNrtNdYoi^8eEL-b ze(dV#k9PeXh(uiRISw_bh_ zM&kDXL5ol%8N{w)_dIJ@JdJvcQFx2j`+5AVZgf9{TUq#-(S7f7r%pr3!aH+oPV7_U z!iI+%P}#DH$fP^u@Z0!u(X>*tQY62DSsU5Br!uHmZP{56Q)cip^4cEc1VK-aky6m4YzuRVx zU#b((s0wblvp7;A^*)SIt@yk#REpZjHu&=Ec&0zKjbVZKIRZWTp$9HJ3cbF^B6DT4 zgv@6FdS|4EhMS(y*G^d=>~@ydG%6R4&LWL}cySEL#9`U59A-ysZ8y z))x``%#&ic`|s=<<$OOYSHdSAy8hHH{^mNujma>cQxLnkM{&Ci<5}@2tnT_A2(*DS z8J(HmZDgvu^hBINjuSzyXJP+sE;ERNCP+j||G1%cl2aebE{o*Z8 zjoc%{qb=hIx!U-B;&c&5(PJJqlVlcp% zezsfuF-ikTf^l6Y(W%3-idp1aBd+dOttJPzV53V-W|UdP7DL52S@{tT0azqn{XpFy zCPyQ&)Fo|R&pXiX>7Xr@9H=g~D6XBbqrs-{|))Gu>jX{u&SHLid zRR5w|c*nAh%z4Q|efY`=F5(hAYdvUEWW|R!1N4qHv3n$2eILQ_v@s0~F*hygKq7CQ zUAw?(Jo~zTOq-UQZLr#r;Lo>)OIT4MOfY}y@cuDERD$&)C?3AtFi!c(YN$OiYt1{) zF7bq1KLfGmwlm6sB?T8#WLncq0l~ze?ek32Af@|4py^)!u`(ed!59YUT+B+1v?oTp z<<5`5Y;P(_|Lm^o5x^q6kTHt4{jXPWD{s7dOQvSa4bBW=?2Ey+^K-=1Lic779 zGp8yqQ+nA>8V2S}DmcojOj>+|)hN$HKxioEp~btmsn{>x+h&;h0!>Xo5sY5Ls9kpC zN;aEQaA>Z!6qt_uRlPZytd$fo!+9s+QY%3oyq>{i1)?qq$0sxbhjWZ4I|{xHL+-Ubt^SF;PEwzXq}Bj%W3{SaDX9YM+s} zbXWKv7*ap|;Pp*)vdx{!J{Vsy^4qI&d}*-;$HZ@zu! z3wGo7v1aRZuV&P*h_SdMHJOIrl;7j^q>8YU^q_-}pUe&m4<>h>${wp~USy zrV&wM85n5ELjVcZ23s;#5CO+?U z`Y#|C2PTHBU$G8KV#f#5zX@QTtM_hD9?8f;%FE*jm){9=+nd56A{?X|@o_Z7f>z{Y zttM%wsJ`h>yENyT5==<7ZnCwu^D`$cpf2SRJVbE*fyt6J8UyfNBAs_f__x-)R4LA4 z=|1x@Mb@L>T=(YbZk}(XW@c#QjN93@La>l6Ud8(c1NE90f5I809-~%~^1yKs zp@VFPzMNfn&AdlUPFsUTyIBGF_53@Y=u19OnUL0BWbcP4|2LT($pJl|?0oerA=TMN zbN(9xi3~s7=K$0QX_;RiYg>r^OUr*K7McG$d#l8+{a?iMMgC+;n{T{)k0^W|FE=am zxIi&>-^Pts39SQsmG1T15kmDK1ntS^_Kh-xnAy%E5DJ`DhZ`68J$Imc}ITy#Bfw+grHiXCfoIUW9lgd#(ZHerp!-*(FT;#z8ec&Wtu_rVLWSz zp*=TScQdx=yX-RM(&l^40HZ2;NMt{1@U1)eeRpKWttm&N|u9XD| z-`pL~$8lcx*u96PE{L>qgxLX^B0KK!e?w0N^=|`t@!4IWPR~#*$O3J3Md|G;{KTj} z0kL8mE;%FjS$ZDMhwFXL%|nA^KLgdAT?z)QS!_Q%+jUF~KH3b*IEEx;H%yqT{=%e3 z1lM)&gq_j}9!3=E#w0WCLt591IEz)4I6cN#zF=XU?_=jY7wz0l$6?^Smr-9yEswv= z$nqC_I|6&sl##Al#ZgI}KFxVJ{7#xc&LBg<+!uviRYhtvD(CV|vILi@>qEC+W2UiU zjnDo=TEE|CG!}ZIp^GQb*)@&bV_EpWfW;W0UpH`5Rv+G*B80j z5`mG@YpAA$BreI4x2>FrAr9w?be#Kl9xbob;7}ZAD}9jfKUxe74ubJ^XaBW=Z<+6ozR#wgy&po;o~xn@2U-J*cN1k<_ zt~qogB5`eTw+!zB5T?RC>$l9Su;rEBV)Xqdd$TWUCJ{GEZMyUI8MKPO$~L0P ztQWn06;=NFs@{VWZPHJ zITxOlIhz7%T2g?4Vn}9kF6@!JEPm={WY8mHSl4iyTBmGlG=`MTYepLs^txw*%}DCn#t5o zoQ!Ddz7|_{x?r_><4Mc@%Yk4>X}XUZdCC|InwEZbRy1AUVe$70!|@^CpDCObSI}vR z8qXu%pQ6HP%YE{=dp*Wp*-&DuT+E5lUmh@a} zXwJ0Im9=$|)>>_|pkakwf`1ra%p<2eM`Qk;}fy) zu=-0@pa5bnmGl!ynpj)q3nzy;m%4g`w`)l<`!`Z^0JvU`)k#qPspXHQZWrY!?Ai}X~BGa)9^tf$)ly}jT>x$s}v3!H2Qk2KJ^^oggXy9_VJu;Nt~u@)eA z(DDnuQ5{&hakU{`FGkn&C2f{*XTg8h{Zx78Z~P{4JzA>C{VzR~rQ5=106P5+seAxS zSozLTeCDJ7PF7{E7H31jnY1l6vbCw_akDLFeMNU_Qp7+Nr!@T*SbIR(zLiNhKt}&0 zu!Y5X18ajVvp^&CAGH2G^p6HJf zNzqZA+At-u&>{8T7#c8yL*UJ(V18#0uZfCFZ^7u%_G=+>+1i$Zg6Fl8TIUv1FEN%E z7U5cY2@I#`9!=hRr}J3Tdv9A)mxjshUyj^e+T(poiZQvQb0-nbixRWJROTFKXH7T{ zK=+00sGh)OLFW~eQe|hKfijwvTCcIa`7xgmy9_cr=Wi+T_)7IU>qTr?q3#zir#^;A z#D@Dbjez2ewZGgAE{8Ic`2m|4e|IW#00YZxaPY41q{C`|1Ga#ii#FG_mgP&?}Fk|=%!%Y z+oG?=XkzN6Pb4ozCe8y-kj5oQ)MM&8$%ZaUKEao=M{@pBLc- zKt27FZoi8ODtj0zSu>}#VOw4Ci6cW53HmugsWl>eQDXRebM)7#18hl7B7PkqT>Ov%?wut)Qsxi?Jo>BiO z-#}1+P!xQ|sqDrOb6*;U$Tu^WfrUs7JEUm^fCBzjKREoPHU8)wPJ| zkY;6viep6vRrX-X64=-?GrBtQyg(&C;2Z&8&G}B*cviuh5(5z12pA%cmu|lzl$)f@ zn`R=cP26q$X;f&xG~a+%>@^vopEr%&mJr_D?${+hlvz5PoMqyHtuV-a-6eM5_le!< zD)dKppBQ5sS?C%&r>vSb-fpY+II@qtsHl`f_^|;H6`wqE0VEmLR-|r96 zkXnBoTGD08JS^fPZ!HO>@J7MQs)U&rxc8LJC1H*EP=>ZEY5S8e={z^y)6r>>y`&TV z(STd+*pG;aKx{1AqbL%UE+&#qpd$QrpiL!KY4Z=Udc&ADs0l?oX{#S4q<>toC$`al zj8jounip6x1KGa?Y(=feK#Ql?5_7o4fVFy~hk_ZGG4nqz5ik40!x&q`N3x2VP-*Ig~fxtb$orMmG_t?lhUJQO(187DpKjh`;h zCFNeeF!A$y4D-d|(bo~=-k;C+ZJmIXX~CXZ6;)+&XN@Q)0%I~NU{WXc}lzQX}c zKvB>H6i00{&v~w9*L!EDZ?s)GiJvF(dJHeUTKgIFfdpOpHUW@Mw}Hh;$|%*URhjd; zyWi-ARU~}8A~4v4b_yZFW-;V~7O23gEX7cBL2>?RUta0)2Vk0NyEtBtNFvaEobA=2 z^!y)=`v;UgqQ{J32nwyVNeuWig@f-%k1k}X7U#Z)q0P(P&^+#)3|9UYrZ4*|7P8ST(;4_JYB&KuJ|Du-_v9fA_Ij%BSjk9=Xc=~`9|W#17uITDp-%YpUKJ<5 z?;OlY-Y?f~UsHe|UbnZrw;Z&Trbvv$1_h%a6dB8ro}FuUzbm5D$-jCq-q#_ee@~{S z`)M$%IyuFEITsOd+u?pw4b^b#x}Ne*9)u(siViS6PVh0cVa<%WUVi!`HMVzRscyYJm3gBke1ewpoP9~&?>B&G?zY{^UG%$3Y!#l zCi#>6=-xTOVkn>uQC(hvv4HCjRkfqH8h)cdItb*^4wh#xeh@3?!ML^K^_k~|=ec_V zSJIU%xyMJ{JRE7rk#ykR~q-607U_batbI0H2Aca&zlUjdcsqx*jwNRRF zmgqrx{t)zaW>Q^~SU5DDElt7Xr(DVj8P`_1s3QDnLqvCD$so67L%gpuJF=Um|9g@5~yh zZdx>xzV$L|rNg|Xle0-^;78N(u)o6Au0w)oUu2fb1;0mviUEc}28=0lxYM9p=D<7d*FEjb>)S$xOEQ0mh51o6_ za{T^*#q1jXZ0RnSjrT2G$INUVD1E|maIMG&P2$jCqPqCubt&NM``hWs0Y>{$z56~_ zlvv91xHbnK+?!2~YTgy`iI;R)yb-YMVV1nxD{WZ8*&l4bZL=r^ zmS&F9vQ&ALbb5P+M1p)eus1qXtcNdmvdq+Sj+LuQc@=Wi5iFJJ+$t;EI6hNo-u>J| zq*LF>&O}pWDRT(qbN!Kp&u%@JNPkY)pJME#%8>~TL8{ryhZLy21-AJOmK01A64jUP z6Kpp&IHVr!&ITk61H0AD1 z1krA(T7MZA+qcGzC-aK&3GqMY{J&0+@g5tlnXTXExx-rCq5F<-U9-=ue!f(^Ai>Y_!F zx4-jTO;@jTny6g;^>zBIQjISs9W2f;OtihYan-K+nRzlZl|8-Gg1eURw_BSVMH%nk z7qFSDTx;KJ$K3a?t{hxaq8YAZ)FG*`NzJIJ$xHw2&j-)v%qiY+M`?P-kK;-~evMq8 zR8@aXORK?E;ryZw`GqxHNSpWe&xyg zCA&%6F7V*bsm4Kz^}4;z`d0nh*juH2Z^3>62Ofd628;bo-XUeT*Y4V7u$FoEy$cHq z;{!PVw;4Jdx))}e=@aY%Q~loCIHs^xWxbf literal 18974 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

kIA?KBWFgi-<$U@VQUTB!we)W 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 From 61b27f33ed5e01a3eb1d6742290b4a0e913a395a Mon Sep 17 00:00:00 2001 From: "Andrew (LyfeOnEdge) (ArcticGentoo)" Date: Thu, 26 Mar 2020 19:13:11 -0700 Subject: [PATCH 14/18] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index b66881f..ceef044 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ +(https://img.shields.io/github/downloads/LyfeOnEdge/custom-install/total.svg)]() [![LatestVer] + # custom-install Experimental script to automate the process of a manual title install for Nintendo 3DS. Originally created late June 2019. From 625f1f9db58ad0e513eaa5d042510d334f29547d Mon Sep 17 00:00:00 2001 From: "Andrew (LyfeOnEdge) (ArcticGentoo)" Date: Thu, 26 Mar 2020 19:13:58 -0700 Subject: [PATCH 15/18] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ceef044..5aa38bc 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -(https://img.shields.io/github/downloads/LyfeOnEdge/custom-install/total.svg)]() [![LatestVer] +[![Releases](https://img.shields.io/github/downloads/LyfeOnEdge/appstore-workbench/total.svg)] # custom-install Experimental script to automate the process of a manual title install for Nintendo 3DS. Originally created late June 2019. From d5a4cbd8f88da4bacdb92f368fd5cd7ef3251cba Mon Sep 17 00:00:00 2001 From: "Andrew (LyfeOnEdge) (ArcticGentoo)" Date: Thu, 26 Mar 2020 19:14:08 -0700 Subject: [PATCH 16/18] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5aa38bc..a2988e0 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![Releases](https://img.shields.io/github/downloads/LyfeOnEdge/appstore-workbench/total.svg)] +![Releases](https://img.shields.io/github/downloads/LyfeOnEdge/appstore-workbench/total.svg) # custom-install Experimental script to automate the process of a manual title install for Nintendo 3DS. Originally created late June 2019. From 8f4b3d11346a937c46ea2271220dd3a5a4660c2f Mon Sep 17 00:00:00 2001 From: "Andrew (LyfeOnEdge) (ArcticGentoo)" Date: Thu, 26 Mar 2020 19:14:21 -0700 Subject: [PATCH 17/18] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a2988e0..c70d314 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -![Releases](https://img.shields.io/github/downloads/LyfeOnEdge/appstore-workbench/total.svg) +![Releases](https://img.shields.io/github/downloads/LyfeOnEdge/custom-install/total.svg) # custom-install Experimental script to automate the process of a manual title install for Nintendo 3DS. Originally created late June 2019. From 11cbbcdf1ef664be694fea1d24b7b0ce68727cde Mon Sep 17 00:00:00 2001 From: "Andrew (LyfeOnEdge) (ArcticGentoo)" Date: Thu, 26 Mar 2020 19:15:09 -0700 Subject: [PATCH 18/18] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c70d314..cbbfb0a 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -![Releases](https://img.shields.io/github/downloads/LyfeOnEdge/custom-install/total.svg) +[![License](https://img.shields.io/badge/License-MIT-blue.svg)]() ![Releases](https://img.shields.io/github/downloads/LyfeOnEdge/custom-install/total.svg) # custom-install Experimental script to automate the process of a manual title install for Nintendo 3DS. Originally created late June 2019.