diff --git a/NUSGet.py b/NUSGet.py index 3341714..6bd963f 100644 --- a/NUSGet.py +++ b/NUSGet.py @@ -7,6 +7,7 @@ import pathlib from importlib.metadata import version import libWiiPy +import libTWLPy from PySide6.QtWidgets import QApplication, QMainWindow, QMessageBox, QTreeWidgetItem, QHeaderView, QStyle from PySide6.QtCore import QRunnable, Slot, QThreadPool, Signal, QObject @@ -14,7 +15,8 @@ from PySide6.QtCore import QRunnable, Slot, QThreadPool, Signal, QObject from qt.py.ui_MainMenu import Ui_MainWindow -regions = {"World": ["41"], "USA/NTSC": ["45"], "Europe/PAL": ["50"], "Japan": ["4A"], "Korea": ["4B"]} +regions = {"World": ["41"], "USA/NTSC": ["45"], "Europe/PAL": ["50"], "Japan": ["4A"], "Korea": ["4B"], "China": ["43"], + "Australia/NZ": ["55"]} # Signals needed for the worker used for threading the downloads. @@ -54,11 +56,13 @@ class MainWindow(QMainWindow, Ui_MainWindow): self.log_text = "" self.threadpool = QThreadPool() self.ui.download_btn.clicked.connect(self.download_btn_pressed) - self.ui.pack_wad_chkbox.clicked.connect(self.pack_wad_chkbox_toggled) + self.ui.pack_archive_chkbox.clicked.connect(self.pack_wad_chkbox_toggled) # noinspection PyUnresolvedReferences self.ui.wii_title_tree.header().setSectionResizeMode(QHeaderView.ResizeToContents) # noinspection PyUnresolvedReferences self.ui.vwii_title_tree.header().setSectionResizeMode(QHeaderView.ResizeToContents) + # noinspection PyUnresolvedReferences + self.ui.dsi_title_tree.header().setSectionResizeMode(QHeaderView.ResizeToContents) # Basic intro text set to automatically show when the app loads. This may be changed in the future. libwiipy_version = "v" + version("libWiiPy") self.ui.log_text_browser.setText(f"NUSGet v1.0\nDeveloped by NinjaCheetah\nPowered by libWiiPy " @@ -69,11 +73,13 @@ class MainWindow(QMainWindow, Ui_MainWindow): # Add console entries to dropdown and attach on change signal. self.ui.console_select_dropdown.addItem("Wii") self.ui.console_select_dropdown.addItem("vWii") + self.ui.console_select_dropdown.addItem("DSi") self.ui.console_select_dropdown.currentIndexChanged.connect(self.selected_console_changed) # Title tree building code. wii_tree = self.ui.wii_title_tree vwii_tree = self.ui.vwii_title_tree - self.trees = [[wii_tree, wii_database], [vwii_tree, vwii_database]] + dsi_tree = self.ui.dsi_title_tree + self.trees = [[wii_tree, wii_database], [vwii_tree, vwii_database], [dsi_tree, dsi_database]] for tree in self.trees: self.tree_categories = [] global regions @@ -115,14 +121,17 @@ class MainWindow(QMainWindow, Ui_MainWindow): except AttributeError: return for tree in self.trees: - for title in tree[1][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"]): - selected_title = title - selected_version = item.text(0) - selected_region = item.parent().text(0) - self.ui.console_select_dropdown.setCurrentIndex(self.ui.platform_tabs.currentIndex()) - self.load_title_data(selected_title, selected_version, selected_region) + try: + for title in tree[1][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"]): + selected_title = title + selected_version = item.text(0) + selected_region = item.parent().text(0) + self.ui.console_select_dropdown.setCurrentIndex(self.ui.platform_tabs.currentIndex()) + self.load_title_data(selected_title, selected_version, selected_region) + except KeyError: + pass def update_log_text(self, new_text): # This function primarily exists to be the handler for the progress signal emitted by the worker thread. @@ -149,8 +158,11 @@ class MainWindow(QMainWindow, Ui_MainWindow): # 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: - wad_name = selected_title["WAD Name"] + "-v" + selected_version + ".wad" - self.ui.wad_file_entry.setText(wad_name) + if self.ui.console_select_dropdown.currentText() == "DSi": + archive_name = selected_title["TAD Name"] + "-v" + selected_version + ".tad" + else: + archive_name = selected_title["WAD Name"] + "-v" + selected_version + ".wad" + self.ui.archive_file_entry.setText(archive_name) except KeyError: pass # Same idea for the danger string, however this only exists for certain titles and will frequently be an error. @@ -170,7 +182,7 @@ class MainWindow(QMainWindow, Ui_MainWindow): 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_archive_chkbox.isChecked() is False and self.ui.keep_enc_chkbox.isChecked() is False and self.ui.create_dec_chkbox.isChecked() is False): msg_box = QMessageBox() msg_box.setIcon(QMessageBox.Icon.Critical) @@ -186,18 +198,21 @@ class MainWindow(QMainWindow, Ui_MainWindow): self.ui.tid_entry.setEnabled(False) self.ui.version_entry.setEnabled(False) self.ui.download_btn.setEnabled(False) - self.ui.pack_wad_chkbox.setEnabled(False) + self.ui.pack_archive_chkbox.setEnabled(False) self.ui.keep_enc_chkbox.setEnabled(False) self.ui.create_dec_chkbox.setEnabled(False) self.ui.use_local_chkbox.setEnabled(False) self.ui.use_wiiu_nus_chkbox.setEnabled(False) self.ui.pack_vwii_mode_chkbox.setEnabled(False) - self.ui.wad_file_entry.setEnabled(False) + self.ui.archive_file_entry.setEnabled(False) self.ui.console_select_dropdown.setEnabled(False) 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) + if self.ui.console_select_dropdown.currentText() == "DSi": + worker = Worker(self.run_nus_download_dsi) + else: + worker = Worker(self.run_nus_download_wii) worker.signals.result.connect(self.check_download_result) worker.signals.progress.connect(self.update_log_text) self.threadpool.start(worker) @@ -234,27 +249,27 @@ class MainWindow(QMainWindow, Ui_MainWindow): msg_box.setWindowTitle("Ticket Not Available") msg_box.setText("No Ticket is Available for the Requested Title!") msg_box.setInformativeText( - "A ticket could not be downloaded for the requested title, but you have selected " - "\"Pack WAD\" or \"Create Decrypted Contents\". These options are not available " - "for titles without a ticket. Only encrypted contents have been saved.") + "A ticket could not be downloaded for the requested title, but you have selected \"Pack installable " + " archive\" or \"Create decrypted contents\". These options are not available 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.version_entry.setEnabled(True) self.ui.download_btn.setEnabled(True) - self.ui.pack_wad_chkbox.setEnabled(True) + self.ui.pack_archive_chkbox.setEnabled(True) self.ui.keep_enc_chkbox.setEnabled(True) self.ui.create_dec_chkbox.setEnabled(True) self.ui.use_local_chkbox.setEnabled(True) self.ui.use_wiiu_nus_chkbox.setEnabled(True) self.ui.console_select_dropdown.setEnabled(True) - if self.ui.pack_wad_chkbox.isChecked() is True: - self.ui.wad_file_entry.setEnabled(True) + if self.ui.pack_archive_chkbox.isChecked() is True: + self.ui.archive_file_entry.setEnabled(True) # Call the dropdown callback because this will automagically handle setting console-specific settings based # on the currently selected console, and saves on duplicate code. self.selected_console_changed() - def run_nus_download(self, progress_callback): + def run_nus_download_wii(self, progress_callback): # Actual NUS download function that runs in a separate thread. tid = self.ui.tid_entry.text() # Immediately knock out any invalidly formatted Title IDs. @@ -267,7 +282,7 @@ class MainWindow(QMainWindow, Ui_MainWindow): except ValueError: title_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_archive_chkbox.isChecked() decrypt_contents_enabled = self.ui.create_dec_chkbox.isChecked() # Check whether we're going to be using the (faster) Wii U NUS or not. wiiu_nus_enabled = self.ui.use_wiiu_nus_chkbox.isChecked() @@ -384,8 +399,8 @@ class MainWindow(QMainWindow, Ui_MainWindow): title.wad.set_cert_data(libWiiPy.download_cert(wiiu_endpoint=wiiu_nus_enabled)) # 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...") - if self.ui.wad_file_entry.text() != "": - wad_file_name = self.ui.wad_file_entry.text() + if self.ui.archive_file_entry.text() != "": + wad_file_name = self.ui.archive_file_entry.text() if wad_file_name[-4:] != ".wad": wad_file_name = wad_file_name + ".wad" else: @@ -398,18 +413,146 @@ class MainWindow(QMainWindow, Ui_MainWindow): # 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_archive_chkbox.isChecked()) or + (not decrypt_contents_enabled and self.ui.create_dec_chkbox.isChecked())): + return 1 + return 0 + + def run_nus_download_dsi(self, progress_callback): + # Actual NUS download function that runs in a separate thread, but DSi flavored. + tid = self.ui.tid_entry.text() + # Immediately knock out any invalidly formatted Title IDs. + if len(tid) != 16: + 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: + title_version = int(self.ui.version_entry.text()) + except ValueError: + title_version = None + # Set variables for these two options so that their state can be compared against the user's choices later. + pack_tad_enabled = self.ui.pack_archive_chkbox.isChecked() + decrypt_contents_enabled = self.ui.create_dec_chkbox.isChecked() + # Create a new libTWLPy Title. + title = libTWLPy.Title() + # Make a directory for this title if it doesn't exist. + title_dir = pathlib.Path(os.path.join(out_folder, tid)) + if not title_dir.is_dir(): + title_dir.mkdir() + # Announce the title being downloaded, and the version if applicable. + if title_version is not None: + progress_callback.emit("Downloading title " + tid + " v" + str(title_version) + ", please wait...") + else: + progress_callback.emit("Downloading title " + tid + " vLatest, please wait...") + progress_callback.emit(" - Downloading and parsing TMD...") + # Download a specific TMD version if a version was specified, otherwise just download the latest TMD. + try: + if title_version is not None: + title.load_tmd(libTWLPy.download_tmd(tid, title_version)) + else: + title.load_tmd(libTWLPy.download_tmd(tid)) + title_version = title.tmd.title_version + # If libTWLPy returns an error, that means that either the TID or version doesn't exist, so return code -2. + except ValueError: + return -2 + # Make a directory for this version if it doesn't exist. + version_dir = pathlib.Path(os.path.join(title_dir, str(title_version))) + if not version_dir.is_dir(): + version_dir.mkdir() + # Write out the TMD to a file. + tmd_out = open(os.path.join(version_dir, "tmd." + str(title_version)), "wb") + tmd_out.write(title.tmd.dump()) + 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")): + progress_callback.emit(" - Parsing local copy of Ticket...") + local_ticket = open(os.path.join(version_dir, "tik"), "rb") + title.load_ticket(local_ticket.read()) + else: + progress_callback.emit(" - Downloading and parsing Ticket...") + try: + title.load_ticket(libTWLPy.download_ticket(tid)) + ticket_out = open(os.path.join(version_dir, "tik"), "wb") + ticket_out.write(title.ticket.dump()) + ticket_out.close() + except ValueError: + # If libTWLPy 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!") + pack_tad_enabled = False + decrypt_contents_enabled = False + # Load the content record from the TMD, and download the content it lists. DSi titles only have one content. + title.load_content_records() + content_file_name = hex(title.tmd.content_record.content_id)[2:] + while len(content_file_name) < 8: + content_file_name = "0" + content_file_name + # 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, + content_file_name)): + progress_callback.emit(" - Using local copy of content") + local_file = open(os.path.join(version_dir, content_file_name), "rb") + content = local_file.read() + else: + progress_callback.emit(" - Downloading content (Content ID: " + str(title.tmd.content_record.content_id) + + ", Size: " + str(title.tmd.content_record.content_size) + " bytes)...") + content = libTWLPy.download_content(tid, title.tmd.content_record.content_id) + 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: + enc_content_out = open(os.path.join(version_dir, content_file_name), "wb") + enc_content_out.write(content) + enc_content_out.close() + title.content.content = content + # If decrypt local contents is still true, decrypt each content and write out the decrypted file. + if decrypt_contents_enabled is True: + try: + progress_callback.emit(" - Decrypting content (Content ID: " + str(title.tmd.content_record.content_id) + + ")...") + dec_content = title.get_content() + content_file_name = hex(title.tmd.content_record.content_id)[2:] + while len(content_file_name) < 8: + content_file_name = "0" + content_file_name + content_file_name = content_file_name + ".app" + dec_content_out = open(os.path.join(version_dir, content_file_name), "wb") + dec_content_out.write(dec_content) + dec_content_out.close() + 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 + # If pack TAD is still true, pack the TMD, ticket, and content into a TAD. + if pack_tad_enabled is True: + # Get the TAD certificate chain, courtesy of libTWLPy. + progress_callback.emit(" - Building certificate...") + title.tad.set_cert_data(libTWLPy.download_cert()) + # Use a typed TAD name if there is one, and auto generate one based on the TID and version if there isn't. + progress_callback.emit("Packing TAD...") + if self.ui.archive_file_entry.text() != "": + tad_file_name = self.ui.archive_file_entry.text() + if tad_file_name[-4:] != ".tad": + tad_file_name = tad_file_name + ".tad" + else: + tad_file_name = tid + "-v" + str(title_version) + ".tad" + # Have libTWLPy dump the TAD, and write that data out. + file = open(os.path.join(version_dir, tad_file_name), "wb") + file.write(title.dump_tad()) + file.close() + 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 TAD 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_tad_enabled and self.ui.pack_archive_chkbox.isChecked()) or (not decrypt_contents_enabled and self.ui.create_dec_chkbox.isChecked())): return 1 return 0 def pack_wad_chkbox_toggled(self): - # Simple function to catch when the WAD checkbox is toggled and enable/disable the file name entry box + # Simple function to catch when the WAD/TAD checkbox is toggled and enable/disable the file name entry box # accordingly. - if self.ui.pack_wad_chkbox.isChecked() is True: - self.ui.wad_file_entry.setEnabled(True) + if self.ui.pack_archive_chkbox.isChecked() is True: + self.ui.archive_file_entry.setEnabled(True) else: - self.ui.wad_file_entry.setEnabled(False) + self.ui.archive_file_entry.setEnabled(False) def selected_console_changed(self): # Callback function to enable or disable console-specific settings based on the selected console. @@ -421,11 +564,13 @@ class MainWindow(QMainWindow, Ui_MainWindow): if __name__ == "__main__": app = QApplication(sys.argv) - # Load the database file, this will work for both the raw Python file and compiled standalone/onefile binaries. + # Load the database files, 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")) wii_database = json.load(database_file) database_file = open(os.path.join(os.path.dirname(__file__), "data/vwii-database.json")) vwii_database = json.load(database_file) + database_file = open(os.path.join(os.path.dirname(__file__), "data/dsi-database.json")) + dsi_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: diff --git a/data/dsi-database.json b/data/dsi-database.json new file mode 100644 index 0000000..f9b5d8d --- /dev/null +++ b/data/dsi-database.json @@ -0,0 +1,208 @@ +{ + "System": [ + { + "Name": "Nintendo DS Cartridge Whitelist", + "TID": "0003000F484E4341", + "Versions": { + "World": [256, 512] + }, + "Ticket": true, + "TAD Name": "Nintendo-DS-Cartridge-Whitelist-NUS" + }, + { + "Name": "Launcher (System Menu)", + "TID": "00030017484E41XX", + "Versions": { + "USA/NTSC": [512, 768, 1024, 1280, 1536, 1792], + "Europe/PAL": [512, 768, 1024, 1280, 1536, 1792], + "Japan": [256, 512, 768, 1024, 1280, 1536, 1792], + "Korea": [768, 1024, 1280, 1536, 1792], + "China": [768, 1024, 1280, 1536, 1792], + "Australia/NZ": [512, 768, 1024, 1280, 1536, 1792] + }, + "Ticket": true, + "TAD Name": "Launcher-NUS", + "Danger": "wip" + }, + { + "Name": "Version Data", + "TID": "0003000F484E4CXX", + "Versions": { + "USA/NTSC": [3, 4, 5, 6, 7, 8, 9], + "Europe/PAL": [3, 4, 5, 6, 7, 8, 9], + "Japan": [1, 2, 3, 4, 5, 6, 7, 8, 9], + "Korea": [5, 6, 7, 8, 9], + "China": [4, 5, 6, 7, 8, 9], + "Australia/NZ": [3, 4, 5, 6, 7, 8, 9] + }, + "Ticket": true, + "TAD Name": "Version-Data-NUS" + }, + { + "Name": "WiFi Firmware", + "TID": "0003000F484E4341", + "Versions": { + "World": [256, 512] + }, + "Ticket": true, + "TAD Name": "WiFi-Firmware-NUS", + "Danger": "wip" + } + ], + "System Apps": [ + { + "Name": "DS Download Play", + "TID": "00030005484E4441", + "Versions": { + "World": [256] + }, + "Ticket": true, + "TAD Name": "DS-Download-Play-NUS", + "Danger": "wip" + }, + { + "Name": "Nintendo 3DS Transfer Tool", + "TID": "00030015484E4FXX", + "Versions": { + "USA/NTSC": [0], + "Europe/PAL": [512, 768], + "Japan": [0], + "Korea": [256], + "Australia/NZ": [512, 768] + }, + "Ticket": false, + "TAD Name": "Nintendo-3DS-Transfer-Tool-NUS", + "Danger": "wip" + }, + { + "Name": "Nintendo DSi Camera", + "TID": "00030005484E49XX", + "Versions": { + "USA/NTSC": [768, 1024], + "Europe/PAL": [768, 1024], + "Japan": [256, 768, 1024], + "Australia/NZ": [768, 1024] + }, + "Ticket": true, + "TAD Name": "Nintendo-DSi-Camera-NUS" + }, + { + "Name": "Nintendo DSi Shop", + "TID": "00030015484E46XX", + "Versions": { + "USA/NTSC": [1536, 1792, 2048, 2304, 2560, 2816, 3072], + "Europe/PAL": [1536, 1792, 2048, 2304, 2560, 2816, 3072], + "Japan": [1024, 1280, 1536, 1792, 2048, 2304, 2560, 2816, 3072], + "Korea": [768], + "China": [768], + "Australia/NZ": [1536, 1792, 2048, 2304, 2560, 2816, 3072] + }, + "Ticket": true, + "TAD Name": "Nintendo-DSi-Shop-NUS", + "Danger": "wip" + }, + { + "Name": "Nintendo DSi Sound", + "TID": "00030005484E4BXX", + "Versions": { + "USA/NTSC": [256, 512], + "Europe/PAL": [256, 512], + "Japan": [256, 512], + "Australia/NZ": [256, 512] + }, + "Ticket": true, + "TAD Name": "Nintendo-DSi-Sound-NUS" + }, + { + "Name": "Nintendo Zone", + "TID": "00030005484E4AXX", + "Versions": { + "USA/NTSC": [512, 768], + "Europe/PAL": [512, 768], + "Japan": [512, 768], + "Australia/NZ": [512, 768] + }, + "Ticket": true, + "TAD Name": "Nintendo-DSi-Sound-NUS" + }, + { + "Name": "System Settings", + "TID": "00030015484E42XX", + "Versions": { + "USA/NTSC": [512, 768], + "Europe/PAL": [512, 768], + "Japan": [512, 768], + "Korea": [768], + "China": [768], + "Australia/NZ": [512, 768] + }, + "Ticket": true, + "TAD Name": "System-Settings-NUS" + } + ], + "DSiWare": [ + { + "Name": "A Little Bit of... Brain Training™: Maths Edition", + "TID": "000300044B4E5256", + "Versions": { + "Europe/PAL": [256] + }, + "Ticket": false + }, + { + "Name": "A Little Bit of... Dr. Mario™", + "TID": "000300044B443956", + "Versions": { + "Europe/PAL": [0] + }, + "Ticket": false + }, + { + "Name": "A Little Bit of... Magic Made Fun™: Deep Psyche", + "TID": "000300044B4D39XX", + "Versions": { + "Europe/PAL": [1] + }, + "Ticket": false + }, + { + "Name": "A Little Bit of... Magic Made Fun™: Funny Face", + "TID": "000300044B4D46XX", + "Versions": { + "Europe/PAL": [1], + "Japan": [0] + }, + "Ticket": false + }, + { + "Name": "A Little Bit of... Magic Made Fun™: Shuffle Games", + "TID": "000300044B4D53XX", + "Versions": { + "Europe/PAL": [1] + }, + "Ticket": false + }, + { + "Name": "Nintendo DSi Browser", + "TID": "00030004484E47XX", + "Versions": { + "USA/NTSC": [768], + "Europe/PAL": [768], + "Japan": [768] + }, + "Ticket": true, + "TAD Name": "Nintendo-DSi-Browser-NUS" + }, + { + "Name": "Flipnote Studio", + "TID": "000300044B4755XX", + "Versions": { + "USA/NTSC": [0], + "Europe/PAL": [0], + "Japan": [512] + }, + "Ticket": true, + "TAD Name": "Flipnote-Studio-NUS" + } + ] +} \ No newline at end of file diff --git a/qt/py/ui_MainMenu.py b/qt/py/ui_MainMenu.py index 3281336..5035940 100644 --- a/qt/py/ui_MainMenu.py +++ b/qt/py/ui_MainMenu.py @@ -87,6 +87,22 @@ class Ui_MainWindow(object): self.verticalLayout_4.addWidget(self.vwii_title_tree) self.platform_tabs.addTab(self.vwii_tab, "") + self.dsi_tab = QWidget() + self.dsi_tab.setObjectName(u"dsi_tab") + self.verticalLayout = QVBoxLayout(self.dsi_tab) + self.verticalLayout.setObjectName(u"verticalLayout") + self.dsi_title_tree = QTreeWidget(self.dsi_tab) + __qtreewidgetitem2 = QTreeWidgetItem() + __qtreewidgetitem2.setText(0, u"1"); + self.dsi_title_tree.setHeaderItem(__qtreewidgetitem2) + self.dsi_title_tree.setObjectName(u"dsi_title_tree") + self.dsi_title_tree.setHeaderHidden(True) + self.dsi_title_tree.header().setMinimumSectionSize(49) + self.dsi_title_tree.header().setStretchLastSection(False) + + self.verticalLayout.addWidget(self.dsi_title_tree) + + self.platform_tabs.addTab(self.dsi_tab, "") self.vertical_layout_trees.addWidget(self.platform_tabs) @@ -144,16 +160,16 @@ class Ui_MainWindow(object): self.verticalLayout_7.addWidget(self.label_3) - self.pack_wad_chkbox = QCheckBox(self.centralwidget) - self.pack_wad_chkbox.setObjectName(u"pack_wad_chkbox") + self.pack_archive_chkbox = QCheckBox(self.centralwidget) + self.pack_archive_chkbox.setObjectName(u"pack_archive_chkbox") - self.verticalLayout_7.addWidget(self.pack_wad_chkbox) + self.verticalLayout_7.addWidget(self.pack_archive_chkbox) - self.wad_file_entry = QLineEdit(self.centralwidget) - self.wad_file_entry.setObjectName(u"wad_file_entry") - self.wad_file_entry.setEnabled(False) + self.archive_file_entry = QLineEdit(self.centralwidget) + self.archive_file_entry.setObjectName(u"archive_file_entry") + self.archive_file_entry.setEnabled(False) - self.verticalLayout_7.addWidget(self.wad_file_entry) + self.verticalLayout_7.addWidget(self.archive_file_entry) self.keep_enc_chkbox = QCheckBox(self.centralwidget) self.keep_enc_chkbox.setObjectName(u"keep_enc_chkbox") @@ -221,7 +237,7 @@ class Ui_MainWindow(object): MainWindow.setCentralWidget(self.centralwidget) self.menubar = QMenuBar(MainWindow) self.menubar.setObjectName(u"menubar") - self.menubar.setGeometry(QRect(0, 0, 1010, 30)) + self.menubar.setGeometry(QRect(0, 0, 1010, 29)) MainWindow.setMenuBar(self.menubar) self.retranslateUi(MainWindow) @@ -238,6 +254,7 @@ class Ui_MainWindow(object): self.label_2.setText(QCoreApplication.translate("MainWindow", u"Available Titles", None)) self.platform_tabs.setTabText(self.platform_tabs.indexOf(self.wii_tab), QCoreApplication.translate("MainWindow", u"Wii", None)) self.platform_tabs.setTabText(self.platform_tabs.indexOf(self.vwii_tab), QCoreApplication.translate("MainWindow", u"vWii", None)) + self.platform_tabs.setTabText(self.platform_tabs.indexOf(self.dsi_tab), QCoreApplication.translate("MainWindow", u"DSi", None)) self.tid_entry.setText("") self.tid_entry.setPlaceholderText(QCoreApplication.translate("MainWindow", u"Title ID", None)) self.label.setText(QCoreApplication.translate("MainWindow", u"v", None)) @@ -246,19 +263,19 @@ class Ui_MainWindow(object): self.console_select_dropdown.setCurrentText("") self.download_btn.setText(QCoreApplication.translate("MainWindow", u"Start Download", None)) self.label_3.setText(QCoreApplication.translate("MainWindow", u"General Settings", None)) - self.pack_wad_chkbox.setText(QCoreApplication.translate("MainWindow", u"Pack installable archive (WAD/TAD)", None)) - self.wad_file_entry.setPlaceholderText(QCoreApplication.translate("MainWindow", u"File Name", None)) + self.pack_archive_chkbox.setText(QCoreApplication.translate("MainWindow", u"Pack installable archive (WAD/TAD)", None)) + self.archive_file_entry.setPlaceholderText(QCoreApplication.translate("MainWindow", u"File Name", None)) self.keep_enc_chkbox.setText(QCoreApplication.translate("MainWindow", u"Keep encrypted contents", None)) self.create_dec_chkbox.setText(QCoreApplication.translate("MainWindow", u"Create decrypted contents (*.app)", None)) self.use_local_chkbox.setText(QCoreApplication.translate("MainWindow", u"Use local files, if they exist", None)) - self.use_wiiu_nus_chkbox.setText(QCoreApplication.translate("MainWindow", u"Use the Wii U NUS (faster)", None)) + self.use_wiiu_nus_chkbox.setText(QCoreApplication.translate("MainWindow", u"Use the Wii U NUS (faster, only effects Wii/vWii)", None)) self.label_4.setText(QCoreApplication.translate("MainWindow", u"vWii Title Settings", None)) self.pack_vwii_mode_chkbox.setText(QCoreApplication.translate("MainWindow", u"Pack for vWii mode instead of Wii U mode", None)) self.log_text_browser.setMarkdown("") self.log_text_browser.setHtml(QCoreApplication.translate("MainWindow", u"\n" "\n" -"


", None)) +"\n" +"


", None)) # retranslateUi diff --git a/qt/ui/MainMenu.ui b/qt/ui/MainMenu.ui index 5dbafca..06f265d 100644 --- a/qt/ui/MainMenu.ui +++ b/qt/ui/MainMenu.ui @@ -127,6 +127,31 @@ + + + DSi + + + + + + true + + + 49 + + + false + + + + 1 + + + + + + @@ -221,14 +246,14 @@ - + Pack installable archive (WAD/TAD) - + false @@ -267,7 +292,7 @@ - Use the Wii U NUS (faster) + Use the Wii U NUS (faster, only effects Wii/vWii) true @@ -349,8 +374,8 @@ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Noto Sans'; font-size:10pt; font-weight:400; font-style:normal;"> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Sans Serif'; font-size:9pt;"><br /></p></body></html> +</style></head><body style=" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;"> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html> @@ -364,7 +389,7 @@ p, li { white-space: pre-wrap; } 0 0 1010 - 30 + 29 diff --git a/requirements.txt b/requirements.txt index 08e9491..af0df9f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,5 @@ pyside6 nuitka git+https://github.com/NinjaCheetah/libWiiPy +libTWLPy zstandard \ No newline at end of file