Added comments to all the source code

This commit is contained in:
Campbell 2024-04-29 18:25:13 -04:00
parent 25e62c4d9a
commit 3eff8d08fe
Signed by: NinjaCheetah
GPG Key ID: B547958AF96ED344

151
NUSGet.py
View File

@ -10,7 +10,6 @@ import libWiiPy
from PySide6.QtWidgets import QApplication, QMainWindow, QMessageBox, QTreeWidgetItem, QHeaderView, QStyle from PySide6.QtWidgets import QApplication, QMainWindow, QMessageBox, QTreeWidgetItem, QHeaderView, QStyle
from PySide6.QtCore import QRunnable, Slot, QThreadPool, Signal, QObject from PySide6.QtCore import QRunnable, Slot, QThreadPool, Signal, QObject
from PySide6.QtGui import QIcon
from qt.py.ui_MainMenu import Ui_MainWindow from qt.py.ui_MainMenu import Ui_MainWindow
@ -18,11 +17,13 @@ regions = [["World", "World", "41"], ["USA", "USA/NTSC", "45"], ["JAP", "Japan",
["KOR", "Korea", "4B"]] ["KOR", "Korea", "4B"]]
# Signals needed for the worker used for threading the downloads.
class WorkerSignals(QObject): class WorkerSignals(QObject):
result = Signal(int) result = Signal(int)
progress = Signal(str) progress = Signal(str)
# Worker class used to thread the downloads.
class Worker(QRunnable): class Worker(QRunnable):
def __init__(self, fn, **kwargs): def __init__(self, fn, **kwargs):
super(Worker, self).__init__() super(Worker, self).__init__()
@ -34,6 +35,9 @@ class Worker(QRunnable):
@Slot() @Slot()
def run(self): def run(self):
# All possible errors *should* be caught by the code and will safely return specific error codes. In the
# unlikely event that an unexpected error happens, it can only possibly be a ValueError, so handle that and
# return code 1.
try: try:
result = self.fn(**self.kwargs) result = self.fn(**self.kwargs)
except ValueError: except ValueError:
@ -53,27 +57,29 @@ class MainWindow(QMainWindow, Ui_MainWindow):
self.ui.pack_wad_chkbox.clicked.connect(self.pack_wad_chkbox_toggled) self.ui.pack_wad_chkbox.clicked.connect(self.pack_wad_chkbox_toggled)
# noinspection PyUnresolvedReferences # noinspection PyUnresolvedReferences
self.ui.title_tree.header().setSectionResizeMode(QHeaderView.ResizeToContents) self.ui.title_tree.header().setSectionResizeMode(QHeaderView.ResizeToContents)
# Basic intro text set to automatically show when the app loads. This may be changed in the future.
self.ui.log_text_browser.setText("NUSGet v1.0\nDeveloped by NinjaCheetah\nPowered by libWiiPy\n\n" self.ui.log_text_browser.setText("NUSGet v1.0\nDeveloped by NinjaCheetah\nPowered by libWiiPy\n\n"
"Select a title from the list on the left, or enter a Title ID to begin.\n\n" "Select a title from the list on the left, or enter a Title ID to begin.\n\n"
"Titles marked with a checkmark are free and have a ticket available, and can" "Titles marked with a checkmark are free and have a ticket available, and can"
" be decrypted and packed into a WAD. Titles with an X do not have a ticket," " be decrypted and packed into a WAD. Titles with an X do not have a ticket,"
" and only their encrypted contents can be saved.") " and only their encrypted contents can be saved.")
# Tree building code.
tree = self.ui.title_tree tree = self.ui.title_tree
self.tree_categories = [] self.tree_categories = []
global regions global regions
# Iterate over each category in the database file.
for key in wii_database: for key in wii_database:
new_category = QTreeWidgetItem() new_category = QTreeWidgetItem()
new_category.setText(0, key) new_category.setText(0, key)
# Iterate over each title in the current category.
for title in wii_database[key]: for title in wii_database[key]:
new_title = QTreeWidgetItem() new_title = QTreeWidgetItem()
new_title.setText(0, title["TID"] + " - " + title["Name"]) new_title.setText(0, title["TID"] + " - " + title["Name"])
# Build the list of regions and what versions are offered for each region.
for region in title["Versions"]: for region in title["Versions"]:
new_region = QTreeWidgetItem() new_region = QTreeWidgetItem()
region_title = "" region_title = ""
# This part is probably done poorly and should be improved.
if region == "World": if region == "World":
region_title = "World" region_title = "World"
else: else:
@ -86,14 +92,15 @@ class MainWindow(QMainWindow, Ui_MainWindow):
new_version.setText(0, "v" + str(version)) new_version.setText(0, "v" + str(version))
new_region.addChild(new_version) new_region.addChild(new_version)
new_title.addChild(new_region) new_title.addChild(new_region)
# Set an indicator icon to show if a ticket is offered for this title or not.
if title["Ticket"] is True: if title["Ticket"] is True:
new_title.setIcon(0, self.style().standardIcon(QStyle.StandardPixmap.SP_DialogApplyButton)) new_title.setIcon(0, self.style().standardIcon(QStyle.StandardPixmap.SP_DialogApplyButton))
else: else:
new_title.setIcon(0, self.style().standardIcon(QStyle.StandardPixmap.SP_DialogCancelButton)) new_title.setIcon(0, self.style().standardIcon(QStyle.StandardPixmap.SP_DialogCancelButton))
new_category.addChild(new_title) new_category.addChild(new_title)
self.tree_categories.append(new_category) self.tree_categories.append(new_category)
tree.insertTopLevelItems(0, self.tree_categories) tree.insertTopLevelItems(0, self.tree_categories)
# Connect the double click signal for handling when titles are selected.
tree.itemDoubleClicked.connect(self.onItemClicked) tree.itemDoubleClicked.connect(self.onItemClicked)
@Slot(QTreeWidgetItem, int) @Slot(QTreeWidgetItem, int)
@ -103,10 +110,13 @@ class MainWindow(QMainWindow, Ui_MainWindow):
region_names = [] region_names = []
for region in regions: for region in regions:
region_names.append(region[1]) region_names.append(region[1])
# This is checking to make sure all category names, title names, and region names are not handled as
# valid choices. item.parent().parent().parent().text(0) is terrifying, I know.
if ((item.parent() is not None) and item.parent() not in self.tree_categories if ((item.parent() is not None) and item.parent() not in self.tree_categories
and item.parent().parent() not in self.tree_categories): and item.parent().parent() not in self.tree_categories):
category = item.parent().parent().parent().text(0) category = item.parent().parent().parent().text(0)
for title in wii_database[category]: for title in wii_database[category]:
# Check to see if the current title matches the selected one, and if it does, pass that info on.
if item.parent().parent().text(0) == (title["TID"] + " - " + title["Name"]): if item.parent().parent().text(0) == (title["TID"] + " - " + title["Name"]):
selected_title = title selected_title = title
selected_version = item.text(0) selected_version = item.text(0)
@ -114,6 +124,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
self.load_title_data(selected_title, selected_version, selected_region) self.load_title_data(selected_title, selected_version, selected_region)
def update_log_text(self, new_text): def update_log_text(self, new_text):
# This function primarily exists to be the handler for the progress signal emitted by the worker thread.
self.log_text += new_text + "\n" self.log_text += new_text + "\n"
self.ui.log_text_browser.setText(self.log_text) self.ui.log_text_browser.setText(self.log_text)
# Always auto-scroll to the bottom of the log. # Always auto-scroll to the bottom of the log.
@ -121,36 +132,47 @@ class MainWindow(QMainWindow, Ui_MainWindow):
scrollBar.setValue(scrollBar.maximum()) scrollBar.setValue(scrollBar.maximum())
def load_title_data(self, selected_title, selected_version, selected_region=None): def load_title_data(self, selected_title, selected_version, selected_region=None):
# Use the information passed from the double click callback to prepare a title for downloading.
selected_version = selected_version[1:] selected_version = selected_version[1:]
# If the last two characters are "XX", then this title has multiple regions, and each region uses its own
# two-digit code. Use the region info passed to load the correct code.
if selected_title["TID"][-2:] == "XX": if selected_title["TID"][-2:] == "XX":
global regions global regions
region_code = "" region_code = ""
# Similarly to previous region-related code, this can definitely be improved.
for region in regions: for region in regions:
if region[1] == selected_region: if region[1] == selected_region:
region_code = region[2] region_code = region[2]
tid = selected_title["TID"][:-2] + region_code tid = selected_title["TID"][:-2] + region_code
else: else:
tid = selected_title["TID"] tid = selected_title["TID"]
# Load the TID and version into the entry boxes.
self.ui.tid_entry.setText(tid) self.ui.tid_entry.setText(tid)
self.ui.version_entry.setText(selected_version) self.ui.version_entry.setText(selected_version)
# Load the WAD name, assuming it exists. This shouldn't ever be able to fail as the database has a WAD name
# for every single title, regardless of whether it can be packed or not.
try: try:
wad_name = selected_title["WAD Name"] + "-v" + selected_version + ".wad" wad_name = selected_title["WAD Name"] + "-v" + selected_version + ".wad"
self.ui.wad_file_entry.setText(wad_name) self.ui.wad_file_entry.setText(wad_name)
except KeyError: except KeyError:
pass pass
# Same idea for the danger string, however this only exists for certain titles and will frequently be an error.
danger_text = "" danger_text = ""
try: try:
danger_text = selected_title["Danger"] danger_text = selected_title["Danger"]
except KeyError: except KeyError:
pass pass
# Add warning text to the log if the selected title has no ticket.
if selected_title["Ticket"] is False: if selected_title["Ticket"] is False:
danger_text = danger_text + ("Note: This Title does not have a Ticket available, so it cannot be " danger_text = danger_text + ("Note: This Title does not have a Ticket available, so it cannot be "
"packed into a WAD or decrypted.") "packed into a WAD or decrypted.")
# Print log info about the selected title and version.
self.log_text = (tid + " - " + selected_title["Name"] + "\n" + "Version: " + selected_version + "\n\n" + self.log_text = (tid + " - " + selected_title["Name"] + "\n" + "Version: " + selected_version + "\n\n" +
danger_text + "\n") danger_text + "\n")
self.ui.log_text_browser.setText(self.log_text) self.ui.log_text_browser.setText(self.log_text)
def download_btn_pressed(self): def download_btn_pressed(self):
# Throw an error and make a message box appear if you haven't selected any options to output the title.
if (self.ui.pack_wad_chkbox.isChecked() is False and self.ui.keep_enc_chkbox.isChecked() is False and if (self.ui.pack_wad_chkbox.isChecked() is False and self.ui.keep_enc_chkbox.isChecked() is False and
self.ui.create_dec_chkbox.isChecked() is False): self.ui.create_dec_chkbox.isChecked() is False):
msgBox = QMessageBox() msgBox = QMessageBox()
@ -163,7 +185,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
"saved.") "saved.")
msgBox.exec() msgBox.exec()
return return
# Lock the UI prior to the download beginning to avoid spawning multiple threads or changing info part way in.
self.ui.tid_entry.setEnabled(False) self.ui.tid_entry.setEnabled(False)
self.ui.version_entry.setEnabled(False) self.ui.version_entry.setEnabled(False)
self.ui.download_btn.setEnabled(False) self.ui.download_btn.setEnabled(False)
@ -174,47 +196,49 @@ class MainWindow(QMainWindow, Ui_MainWindow):
self.ui.wad_file_entry.setEnabled(False) self.ui.wad_file_entry.setEnabled(False)
self.log_text = "" self.log_text = ""
self.ui.log_text_browser.setText(self.log_text) self.ui.log_text_browser.setText(self.log_text)
# Create a new worker object to handle the download in a new thread.
worker = Worker(self.run_nus_download) worker = Worker(self.run_nus_download)
worker.signals.result.connect(self.check_download_result) worker.signals.result.connect(self.check_download_result)
worker.signals.progress.connect(self.update_log_text) worker.signals.progress.connect(self.update_log_text)
self.threadpool.start(worker) self.threadpool.start(worker)
def check_download_result(self, result): def check_download_result(self, result):
msgBox = QMessageBox() # Handle all possible error codes returned from the download thread.
msgBox.setIcon(QMessageBox.Icon.Critical) msg_box = QMessageBox()
msgBox.setStandardButtons(QMessageBox.StandardButton.Ok) msg_box.setIcon(QMessageBox.Icon.Critical)
msgBox.setDefaultButton(QMessageBox.StandardButton.Ok) msg_box.setStandardButtons(QMessageBox.StandardButton.Ok)
msg_box.setDefaultButton(QMessageBox.StandardButton.Ok)
if result == -1: if result == -1:
msgBox.setWindowTitle("Invalid Title ID") msg_box.setWindowTitle("Invalid Title ID")
msgBox.setText("The Title ID you have entered is not in a valid format!") msg_box.setText("The Title ID you have entered is not in a valid format!")
msgBox.setInformativeText("Title IDs must be 16 digit strings of numbers and letters. Please enter a " msg_box.setInformativeText("Title IDs must be 16 digit strings of numbers and letters. Please enter a "
"correctly formatted Title ID, or select one from the menu on the left.") "correctly formatted Title ID, or select one from the menu on the left.")
msgBox.exec() msg_box.exec()
elif result == -2: elif result == -2:
msgBox.setWindowTitle("Title ID/Version Not Found") msg_box.setWindowTitle("Title ID/Version Not Found")
msgBox.setText("No title with the provided Title ID or version could be found!") msg_box.setText("No title with the provided Title ID or version could be found!")
msgBox.setInformativeText("Please make sure that you have entered a valid Title ID, or selected one from " msg_box.setInformativeText("Please make sure that you have entered a valid Title ID, or selected one from "
" the title database, and that the provided version exists for the title you are" " the title database, and that the provided version exists for the title you are"
" attempting to download.") " attempting to download.")
msgBox.exec() msg_box.exec()
elif result == -3: elif result == -3:
msgBox.setWindowTitle("Content Decryption Failed") msg_box.setWindowTitle("Content Decryption Failed")
msgBox.setText("Content decryption was not successful! Decrypted contents could not be created.") msg_box.setText("Content decryption was not successful! Decrypted contents could not be created.")
msgBox.setInformativeText("Your TMD or Ticket may be damaged, or they may not correspond with the content " msg_box.setInformativeText("Your TMD or Ticket may be damaged, or they may not correspond with the content "
"being decrypted. If you have checked \"Use local files, if they exist\", try " "being decrypted. If you have checked \"Use local files, if they exist\", try "
"disabling that option before trying the download again to fix potential issues " "disabling that option before trying the download again to fix potential issues "
"with local data.") "with local data.")
msgBox.exec() msg_box.exec()
elif result == 1: elif result == 1:
msgBox.setIcon(QMessageBox.Icon.Warning) msg_box.setIcon(QMessageBox.Icon.Warning)
msgBox.setWindowTitle("Ticket Not Available") msg_box.setWindowTitle("Ticket Not Available")
msgBox.setText("No Ticket is Available for the Requested Title!") msg_box.setText("No Ticket is Available for the Requested Title!")
msgBox.setInformativeText("A ticket could not be downloaded for the requested title, but you have selected " msg_box.setInformativeText(
"\"Pack WAD\" or \"Create Decrypted Contents\". These options are not available " "A ticket could not be downloaded for the requested title, but you have selected "
"for titles without a ticket. Only encrypted contents have been saved.") "\"Pack WAD\" or \"Create Decrypted Contents\". These options are not available "
msgBox.exec() "for titles without a ticket. Only encrypted contents have been saved.")
msg_box.exec()
# Now that the thread has closed, unlock the UI to allow for the next download.
self.ui.tid_entry.setEnabled(True) self.ui.tid_entry.setEnabled(True)
self.ui.version_entry.setEnabled(True) self.ui.version_entry.setEnabled(True)
self.ui.download_btn.setEnabled(True) self.ui.download_btn.setEnabled(True)
@ -226,46 +250,51 @@ class MainWindow(QMainWindow, Ui_MainWindow):
self.ui.wad_file_entry.setEnabled(True) self.ui.wad_file_entry.setEnabled(True)
def run_nus_download(self, progress_callback): def run_nus_download(self, progress_callback):
# Actual NUS download function that runs in a separate thread.
tid = self.ui.tid_entry.text() tid = self.ui.tid_entry.text()
# Immediately knock out any invalidly formatted Title IDs.
if len(tid) != 16: if len(tid) != 16:
return -1 return -1
# An error here is acceptable, because it may just mean the box is empty. Or the version string is nonsense.
# Either way, just fall back on downloading the latest version of the title.
try: try:
version = int(self.ui.version_entry.text()) version = int(self.ui.version_entry.text())
except ValueError: except ValueError:
version = None version = None
# Set variables for these two options so that their state can be compared against the user's choices later.
pack_wad_enabled = self.ui.pack_wad_chkbox.isChecked() pack_wad_enabled = self.ui.pack_wad_chkbox.isChecked()
decrypt_contents_enabled = self.ui.create_dec_chkbox.isChecked() decrypt_contents_enabled = self.ui.create_dec_chkbox.isChecked()
# Create a new libWiiPy Title.
title = libWiiPy.Title() title = libWiiPy.Title()
# Make a directory for this title if it doesn't exist.
title_dir = pathlib.Path(os.path.join(out_folder, tid)) title_dir = pathlib.Path(os.path.join(out_folder, tid))
if not title_dir.is_dir(): if not title_dir.is_dir():
title_dir.mkdir() title_dir.mkdir()
# Announce the title being downloaded, and the version if applicable.
if version is not None: if version is not None:
progress_callback.emit("Downloading title " + tid + " v" + str(version) + ", please wait...") progress_callback.emit("Downloading title " + tid + " v" + str(version) + ", please wait...")
else: else:
progress_callback.emit("Downloading title " + tid + " vLatest, please wait...") progress_callback.emit("Downloading title " + tid + " vLatest, please wait...")
progress_callback.emit(" - Downloading and parsing TMD...") progress_callback.emit(" - Downloading and parsing TMD...")
# Download a specific TMD version if a version was specified, otherwise just download the latest TMD.
try: try:
if version is not None: if version is not None:
title.load_tmd(libWiiPy.download_tmd(tid, version)) title.load_tmd(libWiiPy.download_tmd(tid, version))
else: else:
title.load_tmd(libWiiPy.download_tmd(tid)) title.load_tmd(libWiiPy.download_tmd(tid))
version = title.tmd.title_version version = title.tmd.title_version
# If libWiiPy returns an error, that means that either the TID or version doesn't exist, so return code -2.
except ValueError: except ValueError:
return -2 return -2
# Make a directory for this version if it doesn't exist.
version_dir = pathlib.Path(os.path.join(title_dir, str(version))) version_dir = pathlib.Path(os.path.join(title_dir, str(version)))
if not version_dir.is_dir(): if not version_dir.is_dir():
version_dir.mkdir() version_dir.mkdir()
# Write out the TMD to a file.
tmd_out = open(os.path.join(version_dir, "tmd." + str(version)), "wb") tmd_out = open(os.path.join(version_dir, "tmd." + str(version)), "wb")
tmd_out.write(title.tmd.dump()) tmd_out.write(title.tmd.dump())
tmd_out.close() tmd_out.close()
# Use a local ticket, if one exists and "use local files" is enabled.
if self.ui.use_local_chkbox.isChecked() is True and os.path.exists(os.path.join(version_dir, "tik")): if self.ui.use_local_chkbox.isChecked() is True and os.path.exists(os.path.join(version_dir, "tik")):
progress_callback.emit(" - Parsing local copy of Ticket...") progress_callback.emit(" - Parsing local copy of Ticket...")
local_ticket = open(os.path.join(version_dir, "tik"), "rb") local_ticket = open(os.path.join(version_dir, "tik"), "rb")
@ -278,17 +307,22 @@ class MainWindow(QMainWindow, Ui_MainWindow):
ticket_out.write(title.ticket.dump()) ticket_out.write(title.ticket.dump())
ticket_out.close() ticket_out.close()
except ValueError: except ValueError:
# If libWiiPy returns an error, then no ticket is available. Log this, and disable options requiring a
# ticket so that they aren't attempted later.
progress_callback.emit(" - No Ticket is available!") progress_callback.emit(" - No Ticket is available!")
pack_wad_enabled = False pack_wad_enabled = False
decrypt_contents_enabled = False decrypt_contents_enabled = False
# Load the content records from the TMD, and begin iterating over the records.
title.load_content_records() title.load_content_records()
content_list = [] content_list = []
for content in range(len(title.tmd.content_records)): for content in range(len(title.tmd.content_records)):
# Generate the correct file name by converting the content ID into hex, minus the 0x, and then appending
# that to the end of 000000. I refuse to believe there isn't a better way to do this here and in libWiiPy.
content_id_hex = hex(title.tmd.content_records[content].content_id)[2:] content_id_hex = hex(title.tmd.content_records[content].content_id)[2:]
if len(content_id_hex) < 2: if len(content_id_hex) < 2:
content_id_hex = "0" + content_id_hex content_id_hex = "0" + content_id_hex
content_file_name = "000000" + content_id_hex content_file_name = "000000" + content_id_hex
# Check for a local copy of the current content if "use local files" is enabled, and use it.
if self.ui.use_local_chkbox.isChecked() is True and os.path.exists(os.path.join(version_dir, if self.ui.use_local_chkbox.isChecked() is True and os.path.exists(os.path.join(version_dir,
content_file_name)): content_file_name)):
progress_callback.emit(" - Using local copy of content " + str(content + 1) + " of " + progress_callback.emit(" - Using local copy of content " + str(content + 1) + " of " +
@ -301,6 +335,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
str(title.tmd.content_records[content].content_size) + " bytes)...") str(title.tmd.content_records[content].content_size) + " bytes)...")
content_list.append(libWiiPy.download_content(tid, title.tmd.content_records[content].content_id)) content_list.append(libWiiPy.download_content(tid, title.tmd.content_records[content].content_id))
progress_callback.emit(" - Done!") progress_callback.emit(" - Done!")
# If keep encrypted contents is on, write out each content after its downloaded.
if self.ui.keep_enc_chkbox.isChecked() is True: if self.ui.keep_enc_chkbox.isChecked() is True:
content_id_hex = hex(title.tmd.content_records[content].content_id)[2:] content_id_hex = hex(title.tmd.content_records[content].content_id)[2:]
if len(content_id_hex) < 2: if len(content_id_hex) < 2:
@ -310,7 +345,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
enc_content_out.write(content_list[content]) enc_content_out.write(content_list[content])
enc_content_out.close() enc_content_out.close()
title.content.content_list = content_list title.content.content_list = content_list
# If decrypt local contents is still true, decrypt each content and write out the decrypted file.
if decrypt_contents_enabled is True: if decrypt_contents_enabled is True:
try: try:
for content in range(len(title.tmd.content_records)): for content in range(len(title.tmd.content_records)):
@ -325,12 +360,15 @@ class MainWindow(QMainWindow, Ui_MainWindow):
dec_content_out.write(dec_content) dec_content_out.write(dec_content)
dec_content_out.close() dec_content_out.close()
except ValueError: except ValueError:
# If libWiiPy throws an error during decryption, return code -3. This should only be possible if using
# local encrypted contents that have been altered at present.
return -3 return -3
# If pack WAD is still true, pack the TMD, ticket, and contents all into a WAD.
if pack_wad_enabled is True: if pack_wad_enabled is True:
# Get the WAD certificate chain, courtesy of libWiiPy.
progress_callback.emit(" - Building certificate...") progress_callback.emit(" - Building certificate...")
title.wad.set_cert_data(libWiiPy.download_cert()) title.wad.set_cert_data(libWiiPy.download_cert())
# Use a typed WAD name if there is one, and auto generate one based on the TID and version if there isn't.
progress_callback.emit("Packing WAD...") progress_callback.emit("Packing WAD...")
if self.ui.wad_file_entry.text() != "": if self.ui.wad_file_entry.text() != "":
wad_file_name = self.ui.wad_file_entry.text() wad_file_name = self.ui.wad_file_entry.text()
@ -338,17 +376,22 @@ class MainWindow(QMainWindow, Ui_MainWindow):
wad_file_name = wad_file_name + ".wad" wad_file_name = wad_file_name + ".wad"
else: else:
wad_file_name = tid + "-v" + str(version) + ".wad" wad_file_name = tid + "-v" + str(version) + ".wad"
# Have libWiiPy dump the WAD, and write that data out.
file = open(os.path.join(version_dir, wad_file_name), "wb") file = open(os.path.join(version_dir, wad_file_name), "wb")
file.write(title.dump_wad()) file.write(title.dump_wad())
file.close() file.close()
progress_callback.emit("Download complete!") progress_callback.emit("Download complete!")
# This is where the variables come in. If the state of these variables doesn't match the user's choice by this
# point, it means that they enabled decryption or WAD packing for a title that doesn't have a ticket. Return
# code 1 so that a warning popup is shown informing them of this.
if ((not pack_wad_enabled and self.ui.pack_wad_chkbox.isChecked()) or if ((not pack_wad_enabled and self.ui.pack_wad_chkbox.isChecked()) or
(not decrypt_contents_enabled and self.ui.create_dec_chkbox.isChecked())): (not decrypt_contents_enabled and self.ui.create_dec_chkbox.isChecked())):
return 1 return 1
return 0 return 0
def pack_wad_chkbox_toggled(self): def pack_wad_chkbox_toggled(self):
# Simple function to catch when the WAD checkbox is toggled and enable/disable the file name entry box
# accordingly.
if self.ui.pack_wad_chkbox.isChecked() is True: if self.ui.pack_wad_chkbox.isChecked() is True:
self.ui.wad_file_entry.setEnabled(True) self.ui.wad_file_entry.setEnabled(True)
else: else:
@ -357,15 +400,17 @@ class MainWindow(QMainWindow, Ui_MainWindow):
if __name__ == "__main__": if __name__ == "__main__":
app = QApplication(sys.argv) app = QApplication(sys.argv)
# Load the database file, this will work for both the raw Python file and compiled standalone/onefile binaries.
database_file = open(os.path.join(os.path.dirname(__file__), "data/wii-database.json")) database_file = open(os.path.join(os.path.dirname(__file__), "data/wii-database.json"))
wii_database = json.load(database_file) wii_database = json.load(database_file)
# If this is a compiled build, the path needs to be obtained differently than if it isn't. The use of an absolute
# path here is for compatibility with macOS .app bundles, which require the use of absolute paths.
try: try:
# noinspection PyUnresolvedReferences # noinspection PyUnresolvedReferences
out_folder = os.path.join(__compiled__.containing_dir, "titles") out_folder = os.path.join(__compiled__.containing_dir, "titles")
except NameError: except NameError:
out_folder = os.path.join(os.path.dirname(sys.argv[0]), "titles") out_folder = os.path.join(os.path.dirname(sys.argv[0]), "titles")
# Create the titles directory if it doesn't exist. In the future, this directory will probably be elsewhere.
if not os.path.isdir(out_folder): if not os.path.isdir(out_folder):
os.mkdir(out_folder) os.mkdir(out_folder)