mirror of
https://github.com/NinjaCheetah/NUSGet.git
synced 2025-04-25 23:21:02 -04:00
Added DSi title support and began filling out database
This commit is contained in:
parent
32af83f476
commit
93e21023ea
211
NUSGet.py
211
NUSGet.py
@ -7,6 +7,7 @@ import pathlib
|
|||||||
from importlib.metadata import version
|
from importlib.metadata import version
|
||||||
|
|
||||||
import libWiiPy
|
import libWiiPy
|
||||||
|
import libTWLPy
|
||||||
|
|
||||||
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
|
||||||
@ -14,7 +15,8 @@ from PySide6.QtCore import QRunnable, Slot, QThreadPool, Signal, QObject
|
|||||||
from qt.py.ui_MainMenu import Ui_MainWindow
|
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.
|
# Signals needed for the worker used for threading the downloads.
|
||||||
@ -54,11 +56,13 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|||||||
self.log_text = ""
|
self.log_text = ""
|
||||||
self.threadpool = QThreadPool()
|
self.threadpool = QThreadPool()
|
||||||
self.ui.download_btn.clicked.connect(self.download_btn_pressed)
|
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
|
# noinspection PyUnresolvedReferences
|
||||||
self.ui.wii_title_tree.header().setSectionResizeMode(QHeaderView.ResizeToContents)
|
self.ui.wii_title_tree.header().setSectionResizeMode(QHeaderView.ResizeToContents)
|
||||||
# noinspection PyUnresolvedReferences
|
# noinspection PyUnresolvedReferences
|
||||||
self.ui.vwii_title_tree.header().setSectionResizeMode(QHeaderView.ResizeToContents)
|
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.
|
# Basic intro text set to automatically show when the app loads. This may be changed in the future.
|
||||||
libwiipy_version = "v" + version("libWiiPy")
|
libwiipy_version = "v" + version("libWiiPy")
|
||||||
self.ui.log_text_browser.setText(f"NUSGet v1.0\nDeveloped by NinjaCheetah\nPowered by 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.
|
# Add console entries to dropdown and attach on change signal.
|
||||||
self.ui.console_select_dropdown.addItem("Wii")
|
self.ui.console_select_dropdown.addItem("Wii")
|
||||||
self.ui.console_select_dropdown.addItem("vWii")
|
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)
|
self.ui.console_select_dropdown.currentIndexChanged.connect(self.selected_console_changed)
|
||||||
# Title tree building code.
|
# Title tree building code.
|
||||||
wii_tree = self.ui.wii_title_tree
|
wii_tree = self.ui.wii_title_tree
|
||||||
vwii_tree = self.ui.vwii_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:
|
for tree in self.trees:
|
||||||
self.tree_categories = []
|
self.tree_categories = []
|
||||||
global regions
|
global regions
|
||||||
@ -115,14 +121,17 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|||||||
except AttributeError:
|
except AttributeError:
|
||||||
return
|
return
|
||||||
for tree in self.trees:
|
for tree in self.trees:
|
||||||
for title in tree[1][category]:
|
try:
|
||||||
# Check to see if the current title matches the selected one, and if it does, pass that info on.
|
for title in tree[1][category]:
|
||||||
if item.parent().parent().text(0) == (title["TID"] + " - " + title["Name"]):
|
# Check to see if the current title matches the selected one, and if it does, pass that info on.
|
||||||
selected_title = title
|
if item.parent().parent().text(0) == (title["TID"] + " - " + title["Name"]):
|
||||||
selected_version = item.text(0)
|
selected_title = title
|
||||||
selected_region = item.parent().text(0)
|
selected_version = item.text(0)
|
||||||
self.ui.console_select_dropdown.setCurrentIndex(self.ui.platform_tabs.currentIndex())
|
selected_region = item.parent().text(0)
|
||||||
self.load_title_data(selected_title, selected_version, selected_region)
|
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):
|
def update_log_text(self, new_text):
|
||||||
# This function primarily exists to be the handler for the progress signal emitted by the worker thread.
|
# 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
|
# 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.
|
# 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"
|
if self.ui.console_select_dropdown.currentText() == "DSi":
|
||||||
self.ui.wad_file_entry.setText(wad_name)
|
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:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
# Same idea for the danger string, however this only exists for certain titles and will frequently be an error.
|
# 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):
|
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.
|
# 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):
|
self.ui.create_dec_chkbox.isChecked() is False):
|
||||||
msg_box = QMessageBox()
|
msg_box = QMessageBox()
|
||||||
msg_box.setIcon(QMessageBox.Icon.Critical)
|
msg_box.setIcon(QMessageBox.Icon.Critical)
|
||||||
@ -186,18 +198,21 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|||||||
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)
|
||||||
self.ui.pack_wad_chkbox.setEnabled(False)
|
self.ui.pack_archive_chkbox.setEnabled(False)
|
||||||
self.ui.keep_enc_chkbox.setEnabled(False)
|
self.ui.keep_enc_chkbox.setEnabled(False)
|
||||||
self.ui.create_dec_chkbox.setEnabled(False)
|
self.ui.create_dec_chkbox.setEnabled(False)
|
||||||
self.ui.use_local_chkbox.setEnabled(False)
|
self.ui.use_local_chkbox.setEnabled(False)
|
||||||
self.ui.use_wiiu_nus_chkbox.setEnabled(False)
|
self.ui.use_wiiu_nus_chkbox.setEnabled(False)
|
||||||
self.ui.pack_vwii_mode_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.ui.console_select_dropdown.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.
|
# 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.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)
|
||||||
@ -234,27 +249,27 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|||||||
msg_box.setWindowTitle("Ticket Not Available")
|
msg_box.setWindowTitle("Ticket Not Available")
|
||||||
msg_box.setText("No Ticket is Available for the Requested Title!")
|
msg_box.setText("No Ticket is Available for the Requested Title!")
|
||||||
msg_box.setInformativeText(
|
msg_box.setInformativeText(
|
||||||
"A ticket could not be downloaded for the requested title, but you have selected "
|
"A ticket could not be downloaded for the requested title, but you have selected \"Pack installable "
|
||||||
"\"Pack WAD\" or \"Create Decrypted Contents\". These options are not available "
|
" archive\" or \"Create decrypted contents\". These options are not available for titles without a "
|
||||||
"for titles without a ticket. Only encrypted contents have been saved.")
|
" ticket. Only encrypted contents have been saved.")
|
||||||
msg_box.exec()
|
msg_box.exec()
|
||||||
# Now that the thread has closed, unlock the UI to allow for the next download.
|
# 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)
|
||||||
self.ui.pack_wad_chkbox.setEnabled(True)
|
self.ui.pack_archive_chkbox.setEnabled(True)
|
||||||
self.ui.keep_enc_chkbox.setEnabled(True)
|
self.ui.keep_enc_chkbox.setEnabled(True)
|
||||||
self.ui.create_dec_chkbox.setEnabled(True)
|
self.ui.create_dec_chkbox.setEnabled(True)
|
||||||
self.ui.use_local_chkbox.setEnabled(True)
|
self.ui.use_local_chkbox.setEnabled(True)
|
||||||
self.ui.use_wiiu_nus_chkbox.setEnabled(True)
|
self.ui.use_wiiu_nus_chkbox.setEnabled(True)
|
||||||
self.ui.console_select_dropdown.setEnabled(True)
|
self.ui.console_select_dropdown.setEnabled(True)
|
||||||
if self.ui.pack_wad_chkbox.isChecked() is True:
|
if self.ui.pack_archive_chkbox.isChecked() is True:
|
||||||
self.ui.wad_file_entry.setEnabled(True)
|
self.ui.archive_file_entry.setEnabled(True)
|
||||||
# Call the dropdown callback because this will automagically handle setting console-specific settings based
|
# Call the dropdown callback because this will automagically handle setting console-specific settings based
|
||||||
# on the currently selected console, and saves on duplicate code.
|
# on the currently selected console, and saves on duplicate code.
|
||||||
self.selected_console_changed()
|
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.
|
# 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.
|
# Immediately knock out any invalidly formatted Title IDs.
|
||||||
@ -267,7 +282,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
|
|||||||
except ValueError:
|
except ValueError:
|
||||||
title_version = None
|
title_version = None
|
||||||
# Set variables for these two options so that their state can be compared against the user's choices later.
|
# 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()
|
decrypt_contents_enabled = self.ui.create_dec_chkbox.isChecked()
|
||||||
# Check whether we're going to be using the (faster) Wii U NUS or not.
|
# 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()
|
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))
|
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.
|
# 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.archive_file_entry.text() != "":
|
||||||
wad_file_name = self.ui.wad_file_entry.text()
|
wad_file_name = self.ui.archive_file_entry.text()
|
||||||
if wad_file_name[-4:] != ".wad":
|
if wad_file_name[-4:] != ".wad":
|
||||||
wad_file_name = wad_file_name + ".wad"
|
wad_file_name = wad_file_name + ".wad"
|
||||||
else:
|
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
|
# 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
|
# 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.
|
# 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())):
|
(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
|
# Simple function to catch when the WAD/TAD checkbox is toggled and enable/disable the file name entry box
|
||||||
# accordingly.
|
# accordingly.
|
||||||
if self.ui.pack_wad_chkbox.isChecked() is True:
|
if self.ui.pack_archive_chkbox.isChecked() is True:
|
||||||
self.ui.wad_file_entry.setEnabled(True)
|
self.ui.archive_file_entry.setEnabled(True)
|
||||||
else:
|
else:
|
||||||
self.ui.wad_file_entry.setEnabled(False)
|
self.ui.archive_file_entry.setEnabled(False)
|
||||||
|
|
||||||
def selected_console_changed(self):
|
def selected_console_changed(self):
|
||||||
# Callback function to enable or disable console-specific settings based on the selected console.
|
# 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__":
|
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.
|
# 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"))
|
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)
|
||||||
database_file = open(os.path.join(os.path.dirname(__file__), "data/vwii-database.json"))
|
database_file = open(os.path.join(os.path.dirname(__file__), "data/vwii-database.json"))
|
||||||
vwii_database = json.load(database_file)
|
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
|
# 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.
|
# path here is for compatibility with macOS .app bundles, which require the use of absolute paths.
|
||||||
try:
|
try:
|
||||||
|
208
data/dsi-database.json
Normal file
208
data/dsi-database.json
Normal file
@ -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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@ -87,6 +87,22 @@ class Ui_MainWindow(object):
|
|||||||
self.verticalLayout_4.addWidget(self.vwii_title_tree)
|
self.verticalLayout_4.addWidget(self.vwii_title_tree)
|
||||||
|
|
||||||
self.platform_tabs.addTab(self.vwii_tab, "")
|
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)
|
self.vertical_layout_trees.addWidget(self.platform_tabs)
|
||||||
|
|
||||||
@ -144,16 +160,16 @@ class Ui_MainWindow(object):
|
|||||||
|
|
||||||
self.verticalLayout_7.addWidget(self.label_3)
|
self.verticalLayout_7.addWidget(self.label_3)
|
||||||
|
|
||||||
self.pack_wad_chkbox = QCheckBox(self.centralwidget)
|
self.pack_archive_chkbox = QCheckBox(self.centralwidget)
|
||||||
self.pack_wad_chkbox.setObjectName(u"pack_wad_chkbox")
|
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.archive_file_entry = QLineEdit(self.centralwidget)
|
||||||
self.wad_file_entry.setObjectName(u"wad_file_entry")
|
self.archive_file_entry.setObjectName(u"archive_file_entry")
|
||||||
self.wad_file_entry.setEnabled(False)
|
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 = QCheckBox(self.centralwidget)
|
||||||
self.keep_enc_chkbox.setObjectName(u"keep_enc_chkbox")
|
self.keep_enc_chkbox.setObjectName(u"keep_enc_chkbox")
|
||||||
@ -221,7 +237,7 @@ class Ui_MainWindow(object):
|
|||||||
MainWindow.setCentralWidget(self.centralwidget)
|
MainWindow.setCentralWidget(self.centralwidget)
|
||||||
self.menubar = QMenuBar(MainWindow)
|
self.menubar = QMenuBar(MainWindow)
|
||||||
self.menubar.setObjectName(u"menubar")
|
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)
|
MainWindow.setMenuBar(self.menubar)
|
||||||
|
|
||||||
self.retranslateUi(MainWindow)
|
self.retranslateUi(MainWindow)
|
||||||
@ -238,6 +254,7 @@ class Ui_MainWindow(object):
|
|||||||
self.label_2.setText(QCoreApplication.translate("MainWindow", u"Available Titles", None))
|
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.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.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.setText("")
|
||||||
self.tid_entry.setPlaceholderText(QCoreApplication.translate("MainWindow", u"Title ID", None))
|
self.tid_entry.setPlaceholderText(QCoreApplication.translate("MainWindow", u"Title ID", None))
|
||||||
self.label.setText(QCoreApplication.translate("MainWindow", u"v", None))
|
self.label.setText(QCoreApplication.translate("MainWindow", u"v", None))
|
||||||
@ -246,19 +263,19 @@ class Ui_MainWindow(object):
|
|||||||
self.console_select_dropdown.setCurrentText("")
|
self.console_select_dropdown.setCurrentText("")
|
||||||
self.download_btn.setText(QCoreApplication.translate("MainWindow", u"Start Download", None))
|
self.download_btn.setText(QCoreApplication.translate("MainWindow", u"Start Download", None))
|
||||||
self.label_3.setText(QCoreApplication.translate("MainWindow", u"General Settings", 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.pack_archive_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.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.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.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_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.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.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.setMarkdown("")
|
||||||
self.log_text_browser.setHtml(QCoreApplication.translate("MainWindow", u"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
|
self.log_text_browser.setHtml(QCoreApplication.translate("MainWindow", u"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
|
||||||
"<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n"
|
"<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n"
|
||||||
"p, li { white-space: pre-wrap; }\n"
|
"p, li { white-space: pre-wrap; }\n"
|
||||||
"</style></head><body style=\" font-family:'Noto Sans'; font-size:10pt; font-weight:400; font-style:normal;\">\n"
|
"</style></head><body style=\" font-family:'Sans Serif'; font-size:9pt; font-weight:400; font-style:normal;\">\n"
|
||||||
"<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>", None))
|
"<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>", None))
|
||||||
# retranslateUi
|
# retranslateUi
|
||||||
|
|
||||||
|
@ -127,6 +127,31 @@
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
|
<widget class="QWidget" name="dsi_tab">
|
||||||
|
<attribute name="title">
|
||||||
|
<string>DSi</string>
|
||||||
|
</attribute>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QTreeWidget" name="dsi_title_tree">
|
||||||
|
<property name="headerHidden">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
<attribute name="headerMinimumSectionSize">
|
||||||
|
<number>49</number>
|
||||||
|
</attribute>
|
||||||
|
<attribute name="headerStretchLastSection">
|
||||||
|
<bool>false</bool>
|
||||||
|
</attribute>
|
||||||
|
<column>
|
||||||
|
<property name="text">
|
||||||
|
<string notr="true">1</string>
|
||||||
|
</property>
|
||||||
|
</column>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
@ -221,14 +246,14 @@
|
|||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QCheckBox" name="pack_wad_chkbox">
|
<widget class="QCheckBox" name="pack_archive_chkbox">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Pack installable archive (WAD/TAD)</string>
|
<string>Pack installable archive (WAD/TAD)</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLineEdit" name="wad_file_entry">
|
<widget class="QLineEdit" name="archive_file_entry">
|
||||||
<property name="enabled">
|
<property name="enabled">
|
||||||
<bool>false</bool>
|
<bool>false</bool>
|
||||||
</property>
|
</property>
|
||||||
@ -267,7 +292,7 @@
|
|||||||
<item>
|
<item>
|
||||||
<widget class="QCheckBox" name="use_wiiu_nus_chkbox">
|
<widget class="QCheckBox" name="use_wiiu_nus_chkbox">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Use the Wii U NUS (faster)</string>
|
<string>Use the Wii U NUS (faster, only effects Wii/vWii)</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="checked">
|
<property name="checked">
|
||||||
<bool>true</bool>
|
<bool>true</bool>
|
||||||
@ -349,8 +374,8 @@
|
|||||||
<string><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
|
<string><!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">
|
<html><head><meta name="qrichtext" content="1" /><style type="text/css">
|
||||||
p, li { white-space: pre-wrap; }
|
p, li { white-space: pre-wrap; }
|
||||||
</style></head><body style=" font-family:'Noto Sans'; font-size:10pt; font-weight:400; font-style:normal;">
|
</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; font-family:'Sans Serif'; font-size:9pt;"><br /></p></body></html></string>
|
<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></string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -364,7 +389,7 @@ p, li { white-space: pre-wrap; }
|
|||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>1010</width>
|
<width>1010</width>
|
||||||
<height>30</height>
|
<height>29</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
pyside6
|
pyside6
|
||||||
nuitka
|
nuitka
|
||||||
git+https://github.com/NinjaCheetah/libWiiPy
|
git+https://github.com/NinjaCheetah/libWiiPy
|
||||||
|
libTWLPy
|
||||||
zstandard
|
zstandard
|
Loading…
x
Reference in New Issue
Block a user