diff --git a/NUSGet.py b/NUSGet.py
index 52dfd57..6d261f4 100644
--- a/NUSGet.py
+++ b/NUSGet.py
@@ -57,7 +57,9 @@ class MainWindow(QMainWindow, Ui_MainWindow):
self.ui.download_btn.clicked.connect(self.download_btn_pressed)
self.ui.pack_wad_chkbox.clicked.connect(self.pack_wad_chkbox_toggled)
# noinspection PyUnresolvedReferences
- self.ui.title_tree.header().setSectionResizeMode(QHeaderView.ResizeToContents)
+ self.ui.wii_title_tree.header().setSectionResizeMode(QHeaderView.ResizeToContents)
+ # noinspection PyUnresolvedReferences
+ self.ui.vwii_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 "
@@ -66,46 +68,51 @@ class MainWindow(QMainWindow, Ui_MainWindow):
f"ticket available, and can be decrypted and packed into a WAD. Titles with an"
f" X do not have a ticket, and only their encrypted contents can be saved.")
# Title tree building code.
- tree = self.ui.title_tree
- self.tree_categories = []
- global regions
- # Iterate over each category in the database file.
- for key in wii_database:
- new_category = QTreeWidgetItem()
- new_category.setText(0, key)
- # Iterate over each title in the current category.
- for title in wii_database[key]:
- new_title = QTreeWidgetItem()
- 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"]:
- new_region = QTreeWidgetItem()
- new_region.setText(0, region)
- for title_version in title["Versions"][region]:
- new_version = QTreeWidgetItem()
- new_version.setText(0, "v" + str(title_version))
- new_region.addChild(new_version)
- 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:
- new_title.setIcon(0, self.style().standardIcon(QStyle.StandardPixmap.SP_DialogApplyButton))
- else:
- new_title.setIcon(0, self.style().standardIcon(QStyle.StandardPixmap.SP_DialogCancelButton))
- new_category.addChild(new_title)
- self.tree_categories.append(new_category)
- tree.insertTopLevelItems(0, self.tree_categories)
- # Connect the double click signal for handling when titles are selected.
- tree.itemDoubleClicked.connect(self.onItemClicked)
+ wii_tree = self.ui.wii_title_tree
+ vwii_tree = self.ui.vwii_title_tree
+ self.trees = [[wii_tree, wii_database], [vwii_tree, vwii_database]]
+ for tree in self.trees:
+ self.tree_categories = []
+ global regions
+ # Iterate over each category in the database file.
+ for key in tree[1]:
+ new_category = QTreeWidgetItem()
+ new_category.setText(0, key)
+ # Iterate over each title in the current category.
+ for title in tree[1][key]:
+ new_title = QTreeWidgetItem()
+ 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"]:
+ new_region = QTreeWidgetItem()
+ new_region.setText(0, region)
+ for title_version in title["Versions"][region]:
+ new_version = QTreeWidgetItem()
+ new_version.setText(0, "v" + str(title_version))
+ new_region.addChild(new_version)
+ 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:
+ new_title.setIcon(0, self.style().standardIcon(QStyle.StandardPixmap.SP_DialogApplyButton))
+ else:
+ new_title.setIcon(0, self.style().standardIcon(QStyle.StandardPixmap.SP_DialogCancelButton))
+ new_category.addChild(new_title)
+ self.tree_categories.append(new_category)
+ tree[0].insertTopLevelItems(0, self.tree_categories)
+ # Connect the double click signal for handling when titles are selected.
+ tree[0].itemDoubleClicked.connect(self.onItemClicked)
@Slot(QTreeWidgetItem, int)
def onItemClicked(self, item, col):
if self.ui.download_btn.isEnabled() is True:
- # 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
- and item.parent().parent() not in self.tree_categories):
+ # Check to make sure that this is a version and nothing higher. If you've doubled clicked on anything other
+ # than a version, this returns an AttributeError and the click can be ignored.
+ try:
category = item.parent().parent().parent().text(0)
- for title in wii_database[category]:
+ 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
@@ -252,8 +259,8 @@ class MainWindow(QMainWindow, Ui_MainWindow):
# 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()
decrypt_contents_enabled = self.ui.create_dec_chkbox.isChecked()
- # Check whether we're going to be using the "fallback" (but faster) Wii U NUS or not.
- fallback_enabled = self.ui.use_wiiu_nus_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()
# Create a new libWiiPy Title.
title = libWiiPy.Title()
# Make a directory for this title if it doesn't exist.
@@ -269,9 +276,9 @@ class MainWindow(QMainWindow, Ui_MainWindow):
# 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(libWiiPy.download_tmd(tid, title_version, fallback_endpoint=fallback_enabled))
+ title.load_tmd(libWiiPy.download_tmd(tid, title_version, wiiu_endpoint=wiiu_nus_enabled))
else:
- title.load_tmd(libWiiPy.download_tmd(tid, fallback_endpoint=fallback_enabled))
+ title.load_tmd(libWiiPy.download_tmd(tid, wiiu_endpoint=wiiu_nus_enabled))
title_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:
@@ -292,7 +299,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
else:
progress_callback.emit(" - Downloading and parsing Ticket...")
try:
- title.load_ticket(libWiiPy.download_ticket(tid, fallback_endpoint=fallback_enabled))
+ title.load_ticket(libWiiPy.download_ticket(tid, wiiu_endpoint=wiiu_nus_enabled))
ticket_out = open(os.path.join(version_dir, "tik"), "wb")
ticket_out.write(title.ticket.dump())
ticket_out.close()
@@ -323,7 +330,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
str(len(title.tmd.content_records)) + " (" +
str(title.tmd.content_records[content].content_size) + " bytes)...")
content_list.append(libWiiPy.download_content(tid, title.tmd.content_records[content].content_id,
- fallback_endpoint=fallback_enabled))
+ wiiu_endpoint=wiiu_nus_enabled))
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:
@@ -353,7 +360,7 @@ class MainWindow(QMainWindow, Ui_MainWindow):
if pack_wad_enabled is True:
# Get the WAD certificate chain, courtesy of libWiiPy.
progress_callback.emit(" - Building certificate...")
- title.wad.set_cert_data(libWiiPy.download_cert(fallback_endpoint=fallback_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.
progress_callback.emit("Packing WAD...")
if self.ui.wad_file_entry.text() != "":
@@ -389,6 +396,8 @@ if __name__ == "__main__":
# 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"))
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)
# 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/vwii-database.json b/data/vwii-database.json
new file mode 100644
index 0000000..50edc43
--- /dev/null
+++ b/data/vwii-database.json
@@ -0,0 +1,71 @@
+{
+ "System": [
+ {
+ "Name": "BC-NAND",
+ "TID": "0000000700000200",
+ "Versions": {
+ "World": [6, 7]
+ },
+ "Ticket": true,
+ "WAD Name": "BC-NAND-NUS",
+ "Danger": "BC-NAND is required for the Wii U to be able to boot any vWii titles. DO NOT modify it, or your Wii U will no longer be able to load into the vWii without restoring it from Wii U mode."
+ },
+ {
+ "Name": "BC-WFS",
+ "TID": "0000000700000201",
+ "Versions": {
+ "World": [1]
+ },
+ "Ticket": true,
+ "WAD Name": "BC-WFS-NUS",
+ "Danger": "BC-WFS is required for the Wii U to be able to boot Dragon Quest X Online. While not generally essential, this is a system title and should not be modified."
+ },
+ {
+ "Name": "System Menu",
+ "TID": "0000000700000002",
+ "Versions": {
+ "USA/NTSC": [513, 545, 609],
+ "Europe/PAL": [514, 546, 610],
+ "Japan": [512, 544, 608]
+ },
+ "Ticket": true,
+ "WAD Name": "vWii-System-Menu",
+ "Danger": "The System Menu is a critical part of the vWii's operation, and should not be modified. If it is deleted, it will need to be restored from Wii U mode to be able to access the vWii again."
+ }
+ ],
+ "System Channels": [
+ {
+ "Name": "Region Select",
+ "TID": "0007000848414CXX",
+ "Versions": {
+ "USA/NTSC": [2],
+ "Europe/PAL": [2],
+ "Japan": [2]
+ },
+ "Ticket": true,
+ "WAD Name": "Region-Select-NUS",
+ "Danger": "\"Region Select\" is a hidden channel used during the Wii's initial setup. If this title is damaged or missing, you will not be able to set up your Wii again after a factory reset. This title will need to be reinstalled to fix your console."
+ },
+ {
+ "Name": "Return to Wii U Menu",
+ "TID": "00070002484356XX",
+ "Versions": {
+ "World": [0]
+ },
+ "Ticket": true,
+ "WAD Name": "Return-to-Wii-U-Menu-NUS",
+ "Danger": "\"Return to Wii U Menu\" is the channel launched from the vWii menu to reboot your console back into Wii U mode. While it generally should not be modified, modifying or removing it will not prevent the vWii from working."
+ },
+ {
+ "Name": "Wii Electronic Manual",
+ "TID": "00070002484355XX",
+ "Versions": {
+ "USA/NTSC": [0, 1, 2, 3, 5],
+ "Europe/PAL": [0, 1, 2, 3, 4, 5],
+ "Japan": [0, 1, 2, 3, 5]
+ },
+ "Ticket": true,
+ "WAD Name": "Wii-Electronic-Manual-NUS"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/qt/py/ui_MainMenu.py b/qt/py/ui_MainMenu.py
index e3e982a..ebaa4b0 100644
--- a/qt/py/ui_MainMenu.py
+++ b/qt/py/ui_MainMenu.py
@@ -17,8 +17,8 @@ from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
QPalette, QPixmap, QRadialGradient, QTransform)
from PySide6.QtWidgets import (QApplication, QCheckBox, QHBoxLayout, QHeaderView,
QLabel, QLineEdit, QMainWindow, QMenuBar,
- QPushButton, QSizePolicy, QTextBrowser, QTreeWidget,
- QTreeWidgetItem, QVBoxLayout, QWidget)
+ QPushButton, QSizePolicy, QTabWidget, QTextBrowser,
+ QTreeWidget, QTreeWidgetItem, QVBoxLayout, QWidget)
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
@@ -41,18 +41,46 @@ class Ui_MainWindow(object):
self.verticalLayout.addWidget(self.label_2)
- self.title_tree = QTreeWidget(self.centralwidget)
+ self.platform_tabs = QTabWidget(self.centralwidget)
+ self.platform_tabs.setObjectName(u"platform_tabs")
+ self.wii_tab = QWidget()
+ self.wii_tab.setObjectName(u"wii_tab")
+ self.verticalLayout_2 = QVBoxLayout(self.wii_tab)
+ self.verticalLayout_2.setObjectName(u"verticalLayout_2")
+ self.wii_title_tree = QTreeWidget(self.wii_tab)
__qtreewidgetitem = QTreeWidgetItem()
__qtreewidgetitem.setText(0, u"1");
- self.title_tree.setHeaderItem(__qtreewidgetitem)
- self.title_tree.setObjectName(u"title_tree")
- self.title_tree.setColumnCount(1)
- self.title_tree.header().setVisible(False)
- self.title_tree.header().setMinimumSectionSize(49)
- self.title_tree.header().setDefaultSectionSize(100)
- self.title_tree.header().setStretchLastSection(False)
+ self.wii_title_tree.setHeaderItem(__qtreewidgetitem)
+ self.wii_title_tree.setObjectName(u"wii_title_tree")
+ self.wii_title_tree.setColumnCount(1)
+ self.wii_title_tree.header().setVisible(False)
+ self.wii_title_tree.header().setMinimumSectionSize(49)
+ self.wii_title_tree.header().setDefaultSectionSize(100)
+ self.wii_title_tree.header().setStretchLastSection(False)
- self.verticalLayout.addWidget(self.title_tree)
+ self.verticalLayout_2.addWidget(self.wii_title_tree)
+
+ self.platform_tabs.addTab(self.wii_tab, "")
+ self.vwii_tab = QWidget()
+ self.vwii_tab.setObjectName(u"vwii_tab")
+ self.verticalLayout_4 = QVBoxLayout(self.vwii_tab)
+ self.verticalLayout_4.setObjectName(u"verticalLayout_4")
+ self.vwii_title_tree = QTreeWidget(self.vwii_tab)
+ __qtreewidgetitem1 = QTreeWidgetItem()
+ __qtreewidgetitem1.setText(0, u"1");
+ self.vwii_title_tree.setHeaderItem(__qtreewidgetitem1)
+ self.vwii_title_tree.setObjectName(u"vwii_title_tree")
+ self.vwii_title_tree.setColumnCount(1)
+ self.vwii_title_tree.header().setVisible(False)
+ self.vwii_title_tree.header().setMinimumSectionSize(49)
+ self.vwii_title_tree.header().setDefaultSectionSize(100)
+ self.vwii_title_tree.header().setStretchLastSection(False)
+
+ self.verticalLayout_4.addWidget(self.vwii_title_tree)
+
+ self.platform_tabs.addTab(self.vwii_tab, "")
+
+ self.verticalLayout.addWidget(self.platform_tabs)
self.horizontalLayout_3.addLayout(self.verticalLayout)
@@ -140,12 +168,17 @@ class Ui_MainWindow(object):
self.retranslateUi(MainWindow)
+ self.platform_tabs.setCurrentIndex(0)
+
+
QMetaObject.connectSlotsByName(MainWindow)
# setupUi
def retranslateUi(self, MainWindow):
MainWindow.setWindowTitle(QCoreApplication.translate("MainWindow", u"MainWindow", 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.vwii_tab), QCoreApplication.translate("MainWindow", u"vWii", 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))
@@ -162,6 +195,6 @@ class Ui_MainWindow(object):
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, NOT vWii titles)", None))
+ self.use_wiiu_nus_chkbox.setText(QCoreApplication.translate("MainWindow", u"Use the Wii U NUS (faster)", None))
# retranslateUi
diff --git a/qt/ui/MainMenu.ui b/qt/ui/MainMenu.ui
index 4e9f915..595e4d5 100644
--- a/qt/ui/MainMenu.ui
+++ b/qt/ui/MainMenu.ui
@@ -43,27 +43,72 @@
-
-
-
- 1
+
+
+ 0
-
- false
-
-
- 49
-
-
- 100
-
-
- false
-
-
-
- 1
-
-
+
+
+ Wii
+
+
+
-
+
+
+ 1
+
+
+ false
+
+
+ 49
+
+
+ 100
+
+
+ false
+
+
+
+ 1
+
+
+
+
+
+
+
+
+ vWii
+
+
+ -
+
+
+ 1
+
+
+ false
+
+
+ 49
+
+
+ 100
+
+
+ false
+
+
+
+ 1
+
+
+
+
+
+
@@ -176,7 +221,7 @@ p, li { white-space: pre-wrap; }
-
- Use the Wii U NUS (faster, NOT vWii titles)
+ Use the Wii U NUS (faster)
true