diff --git a/NUSGet.py b/NUSGet.py index 888c8e0..f8620a4 100644 --- a/NUSGet.py +++ b/NUSGet.py @@ -30,12 +30,13 @@ from qt.py.ui_AboutDialog import AboutNUSGet from qt.py.ui_MainMenu import Ui_MainWindow from modules.core import * +from modules.theme import is_dark_theme from modules.tree import NUSGetTreeModel, TIDFilterProxyModel from modules.download_batch import run_nus_download_batch from modules.download_wii import run_nus_download_wii from modules.download_dsi import run_nus_download_dsi -nusget_version = "1.4.0" +nusget_version = "1.4.1" regions = {"World": ["41"], "USA/NTSC": ["45"], "Europe/PAL": ["50"], "Japan": ["4A"], "Korea": ["4B"], "China": ["43"], "Australia/NZ": ["55"]} @@ -44,7 +45,7 @@ regions = {"World": ["41"], "USA/NTSC": ["45"], "Europe/PAL": ["50"], "Japan": [ # Signals needed for the worker used for threading the downloads. class WorkerSignals(QObject): result = Signal(object) - progress = Signal(str) + progress = Signal(int, int, str) # Worker class used to thread the downloads. @@ -129,8 +130,13 @@ class MainWindow(QMainWindow, Ui_MainWindow): self.ui.menuHelp.setWindowFlags(self.ui.menuHelp.windowFlags() | Qt.FramelessWindowHint) self.ui.menuHelp.setWindowFlags(self.ui.menuHelp.windowFlags() | Qt.NoDropShadowWindowHint) self.ui.menuHelp.setAttribute(Qt.WA_TranslucentBackground) - # Load the custom information icon. - icon = QIcon(os.path.join(os.path.dirname(__file__), "resources", "information.svg")) + # Save some light/dark theme values for later, including the appropriately colored info icon. + if is_dark_theme(): + bg_color = "#2b2b2b" + icon = QIcon(os.path.join(os.path.dirname(__file__), "resources", "information_white.svg")) + else: + bg_color = "#e3e3e3" + icon = QIcon(os.path.join(os.path.dirname(__file__), "resources", "information_black.svg")) self.ui.actionAbout.setIcon(icon) self.ui.actionAbout_Qt.setIcon(icon) # Title tree loading code. Now powered by Models:tm: @@ -157,7 +163,7 @@ class MainWindow(QMainWindow, Ui_MainWindow): self.trees[tree].setStyleSheet(self.trees[tree].styleSheet() + f""" QTreeView QScrollBar::sub-line:vertical {{ border: 0; - background: #2b2b2b; + background: {bg_color}; height: {self.trees[tree].header().sizeHint().height()}px; }}""") # Prevent resizing. @@ -224,6 +230,18 @@ class MainWindow(QMainWindow, Ui_MainWindow): return self.ui.patch_ios_checkbox.setEnabled(False) + def download_progress_update(self, done, total, log_text): + if done == 0 and total == 0: + self.ui.progress_bar.setRange(0, 0) + elif done == -1 and total == -1: + pass + else: + self.ui.progress_bar.setRange(0, total) + self.ui.progress_bar.setValue(done) + # Pass the text on to the log text updater, if it was provided. + if log_text: + self.update_log_text(log_text) + def update_log_text(self, new_text): # This method primarily exists to be the handler for the progress signal emitted by the worker thread. self.log_text += new_text + "\n" @@ -379,7 +397,7 @@ class MainWindow(QMainWindow, Ui_MainWindow): self.ui.pack_vwii_mode_checkbox.isChecked(), self.ui.patch_ios_checkbox.isChecked(), self.ui.archive_file_entry.text()) worker.signals.result.connect(self.check_download_result) - worker.signals.progress.connect(self.update_log_text) + worker.signals.progress.connect(self.download_progress_update) self.threadpool.start(worker) def check_download_result(self, result): @@ -546,7 +564,7 @@ class MainWindow(QMainWindow, Ui_MainWindow): self.ui.use_wiiu_nus_checkbox.isChecked(), self.ui.use_local_checkbox.isChecked(), self.ui.pack_vwii_mode_checkbox.isChecked(), self.ui.patch_ios_checkbox.isChecked()) worker.signals.result.connect(self.check_batch_result) - worker.signals.progress.connect(self.update_log_text) + worker.signals.progress.connect(self.download_progress_update) self.threadpool.start(worker) def choose_output_dir(self): @@ -650,7 +668,12 @@ if __name__ == "__main__": # Load Fusion because that's objectively the best base theme, and then load the fancy stylesheet on top to make # NUSGet look nice and pretty. app.setStyle("fusion") - stylesheet = open(os.path.join(os.path.dirname(__file__), "resources", "style.qss")).read() + theme_sheet = "style_dark.qss" + if is_dark_theme(): + theme_sheet = "style_dark.qss" + else: + theme_sheet = "style_light.qss" + stylesheet = open(os.path.join(os.path.dirname(__file__), "resources", theme_sheet)).read() image_path_prefix = pathlib.Path(os.path.join(os.path.dirname(__file__), "resources")).resolve().as_posix() stylesheet = stylesheet.replace("{IMAGE_PREFIX}", image_path_prefix) app.setStyleSheet(stylesheet) diff --git a/modules/core.py b/modules/core.py index e579075..d149293 100644 --- a/modules/core.py +++ b/modules/core.py @@ -54,6 +54,7 @@ def connect_label_to_checkbox(label, checkbox): checkbox.toggle() label.mousePressEvent = toggle_checkbox + def connect_is_enabled_to_checkbox(items, chkbox): for item in items: if chkbox.isChecked(): @@ -61,6 +62,7 @@ def connect_is_enabled_to_checkbox(items, chkbox): else: item.setEnabled(False) + def check_nusget_updates(app, current_version: str, progress_callback=None) -> str | None: # Simple function to make a request to the GitHub API and then check if the latest available version is newer. gh_api_request = requests.get(url="https://api.github.com/repos/NinjaCheetah/NUSGet/releases/latest", stream=True) @@ -80,6 +82,7 @@ def check_nusget_updates(app, current_version: str, progress_callback=None) -> s progress_callback.emit(app.translate("MainWindow", "\n\nYou're running the latest release of NUSGet.")) return None + def get_config_file() -> pathlib.Path: config_dir = pathlib.Path(os.path.join( os.environ.get('APPDATA') or @@ -90,11 +93,13 @@ def get_config_file() -> pathlib.Path: config_dir.mkdir(exist_ok=True) return config_dir.joinpath("config.json") + def save_config(config_data: dict) -> None: config_file = get_config_file() print(f"writing data: {config_data}") open(config_file, "w").write(json.dumps(config_data)) + def update_setting(config_data: dict, setting: str, value: any) -> None: config_data[setting] = value save_config(config_data) diff --git a/modules/download_batch.py b/modules/download_batch.py index bc86208..e76ace4 100644 --- a/modules/download_batch.py +++ b/modules/download_batch.py @@ -53,5 +53,5 @@ def run_nus_download_batch(out_folder: pathlib.Path, titles: List[BatchTitleData # failed title. result = 1 failed_titles.append(title.tid) - progress_callback.emit(f"Batch download finished.") + progress_callback.emit(0, 1, f"Batch download finished.") return BatchResults(result, warning_titles, failed_titles) diff --git a/modules/download_dsi.py b/modules/download_dsi.py index c0d8c98..8c2bd66 100644 --- a/modules/download_dsi.py +++ b/modules/download_dsi.py @@ -29,10 +29,10 @@ def run_nus_download_dsi(out_folder: pathlib.Path, tid: str, version: str, pack_ title_dir.mkdir(exist_ok=True) # Announce the title being downloaded, and the version if applicable. if title_version is not None: - progress_callback.emit(f"Downloading title {tid} v{title_version}, please wait...") + progress_callback.emit(0, 0, f"Downloading title {tid} v{title_version}, please wait...") else: - progress_callback.emit(f"Downloading title {tid} vLatest, please wait...") - progress_callback.emit(" - Downloading and parsing TMD...") + progress_callback.emit(0, 0, f"Downloading title {tid} vLatest, please wait...") + progress_callback.emit(-1, -1, " - 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: @@ -50,17 +50,17 @@ def run_nus_download_dsi(out_folder: pathlib.Path, tid: str, version: str, pack_ version_dir.joinpath(f"tmd.{title_version}").write_bytes(title.tmd.dump()) # Use a local ticket, if one exists and "use local files" is enabled. if use_local_chkbox and version_dir.joinpath("tik").exists(): - progress_callback.emit(" - Parsing local copy of Ticket...") + progress_callback.emit(-1, -1, " - Parsing local copy of Ticket...") title.load_ticket(version_dir.joinpath("tik").read_bytes()) else: - progress_callback.emit(" - Downloading and parsing Ticket...") + progress_callback.emit(-1, -1, " - Downloading and parsing Ticket...") try: title.load_ticket(libTWLPy.download_ticket(tid)) version_dir.joinpath("tik").write_bytes(title.ticket.dump()) 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!") + progress_callback.emit(-1, -1, " - 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. @@ -68,13 +68,13 @@ def run_nus_download_dsi(out_folder: pathlib.Path, tid: str, version: str, pack_ content_file_name = f"{title.tmd.content_record.content_id:08X}" # Check for a local copy of the current content if "use local files" is enabled, and use it. if use_local_chkbox and version_dir.joinpath(content_file_name).exists(): - progress_callback.emit(" - Using local copy of content") + progress_callback.emit(-1, -1, " - Using local copy of content") content = version_dir.joinpath(content_file_name).read_bytes() else: - progress_callback.emit(f" - Downloading content (Content ID: {title.tmd.content_record.content_id}, Size: " + progress_callback.emit(-1, -1, f" - Downloading content (Content ID: {title.tmd.content_record.content_id}, Size: " f"{title.tmd.content_record.content_size} bytes)...") content = libTWLPy.download_content(tid, title.tmd.content_record.content_id) - progress_callback.emit(" - Done!") + progress_callback.emit(-1, -1, " - Done!") # If keep encrypted contents is on, write out the content after its downloaded. if keep_enc_chkbox is True: version_dir.joinpath(content_file_name).write_bytes(content) @@ -82,7 +82,7 @@ def run_nus_download_dsi(out_folder: pathlib.Path, tid: str, version: str, pack_ # If decrypt local contents is still true, decrypt the content and write out the decrypted file. if decrypt_contents_enabled is True: try: - progress_callback.emit(f" - Decrypting content (Content ID: {title.tmd.content_record.content_id})...") + progress_callback.emit(-1, -1, f" - Decrypting content (Content ID: {title.tmd.content_record.content_id})...") dec_content = title.get_content() content_file_name = f"{title.tmd.content_record.content_id:08X}.app" version_dir.joinpath(content_file_name).write_bytes(dec_content) @@ -93,10 +93,10 @@ def run_nus_download_dsi(out_folder: pathlib.Path, tid: str, version: str, pack_ # 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...") + progress_callback.emit(-1, -1, " - 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...") + progress_callback.emit(-1, -1, "Packing TAD...") if tad_file_name != "" and tad_file_name is not None: # Batch downloads may insert -vLatest, so if it did we can fill in the real number now. tad_file_name = tad_file_name.replace("-vLatest", f"-v{title_version}") @@ -104,9 +104,13 @@ def run_nus_download_dsi(out_folder: pathlib.Path, tid: str, version: str, pack_ tad_file_name += ".tad" else: tad_file_name = f"{tid}-v{title_version}.tad" + # Certain special characters are prone to breaking things, so strip them from the file name before actually + # opening the file for writing. On some platforms (like macOS), invalid characters get replaced automatically, + # but on Windows the file will just fail to be written out at all. + tad_file_name = tad_file_name.translate({ord(c): None for c in '/\\:*"?<>|'}) # Have libTWLPy dump the TAD, and write that data out. version_dir.joinpath(tad_file_name).write_bytes(title.dump_tad()) - progress_callback.emit("Download complete!") + progress_callback.emit(0, 1, "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. diff --git a/modules/download_wii.py b/modules/download_wii.py index 5b51b78..76ae8f6 100644 --- a/modules/download_wii.py +++ b/modules/download_wii.py @@ -11,6 +11,8 @@ from libWiiPy.title.ticket import _TitleLimit def run_nus_download_wii(out_folder: pathlib.Path, tid: str, version: str, pack_wad_chkbox: bool, keep_enc_chkbox: bool, decrypt_contents_chkbox: bool, wiiu_nus_chkbox: bool, use_local_chkbox: bool, repack_vwii_chkbox: bool, patch_ios: bool, wad_file_name: str, progress_callback=None): + def progress_update(done, total): + progress_callback.emit(done, total, None) # Actual NUS download function that runs in a separate thread. # Immediately knock out any invalidly formatted Title IDs. if len(tid) != 16: @@ -33,16 +35,16 @@ def run_nus_download_wii(out_folder: pathlib.Path, tid: str, version: str, pack_ title_dir.mkdir(exist_ok=True) # Announce the title being downloaded, and the version if applicable. if title_version is not None: - progress_callback.emit(f"Downloading title {tid} v{title_version}, please wait...") + progress_callback.emit(0, 0, f"Downloading title {tid} v{title_version}, please wait...") else: - progress_callback.emit(f"Downloading title {tid} vLatest, please wait...") - progress_callback.emit(" - Downloading and parsing TMD...") + progress_callback.emit(-1, -1, f"Downloading title {tid} vLatest, please wait...") + progress_callback.emit(-1, -1, " - 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(libWiiPy.title.download_tmd(tid, title_version, wiiu_endpoint=wiiu_nus_enabled)) + title.load_tmd(libWiiPy.title.download_tmd(tid, title_version, wiiu_endpoint=wiiu_nus_enabled, progress=progress_update)) else: - title.load_tmd(libWiiPy.title.download_tmd(tid, wiiu_endpoint=wiiu_nus_enabled)) + title.load_tmd(libWiiPy.title.download_tmd(tid, wiiu_endpoint=wiiu_nus_enabled, progress=progress_update)) 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: @@ -55,17 +57,17 @@ def run_nus_download_wii(out_folder: pathlib.Path, tid: str, version: str, pack_ # Use a local ticket, if one exists and "use local files" is enabled. forge_ticket = False if use_local_chkbox and version_dir.joinpath("tik").exists(): - progress_callback.emit(" - Parsing local copy of Ticket...") + progress_callback.emit(-1, -1, " - Parsing local copy of Ticket...") title.load_ticket(version_dir.joinpath("tik").read_bytes()) else: - progress_callback.emit(" - Downloading and parsing Ticket...") + progress_callback.emit(-1, -1, " - Downloading and parsing Ticket...") try: - title.load_ticket(libWiiPy.title.download_ticket(tid, wiiu_endpoint=wiiu_nus_enabled)) + title.load_ticket(libWiiPy.title.download_ticket(tid, wiiu_endpoint=wiiu_nus_enabled, progress=progress_update)) version_dir.joinpath("tik").write_bytes(title.ticket.dump()) except ValueError: # If libWiiPy returns an error, then no ticket is available. Try to forge a ticket after we download the # content. - progress_callback.emit(" - No Ticket is available! Will try forging a Ticket.") + progress_callback.emit(0, 0, " - No Ticket is available! Will try forging a Ticket.") forge_ticket = True # Load the content records from the TMD, and begin iterating over the records. title.load_content_records() @@ -75,15 +77,15 @@ def run_nus_download_wii(out_folder: pathlib.Path, tid: str, version: str, pack_ content_file_name = f"{title.tmd.content_records[content].content_id:08X}" # Check for a local copy of the current content if "use local files" is enabled, and use it. if use_local_chkbox is True and version_dir.joinpath(content_file_name).exists(): - progress_callback.emit(f" - Using local copy of content {content + 1} of {len(title.tmd.content_records)}") + progress_callback.emit(-1, -1, f" - Using local copy of content {content + 1} of {len(title.tmd.content_records)}") content_list.append(version_dir.joinpath(content_file_name).read_bytes()) else: - progress_callback.emit(f" - Downloading content {content + 1} of {len(title.tmd.content_records)} " + progress_callback.emit(0, 0, f" - Downloading content {content + 1} of {len(title.tmd.content_records)} " f"(Content ID: {title.tmd.content_records[content].content_id}, Size: " f"{title.tmd.content_records[content].content_size} bytes)...") content_list.append(libWiiPy.title.download_content(tid, title.tmd.content_records[content].content_id, - wiiu_endpoint=wiiu_nus_enabled)) - progress_callback.emit(" - Done!") + wiiu_endpoint=wiiu_nus_enabled, progress=progress_update)) + progress_callback.emit(-1, -1, " - Done!") # If keep encrypted contents is on, write out each content after its downloaded. if keep_enc_chkbox is True: version_dir.joinpath(content_file_name).write_bytes(content_list[content]) @@ -125,7 +127,7 @@ def run_nus_download_wii(out_folder: pathlib.Path, tid: str, version: str, pack_ if decrypt_contents_enabled is True: try: for content in range(len(title.tmd.content_records)): - progress_callback.emit(f" - Decrypting content {content + 1} of {len(title.tmd.content_records)} " + progress_callback.emit(-1, -1, f" - Decrypting content {content + 1} of {len(title.tmd.content_records)} " f"(Content ID: {title.tmd.content_records[content].content_id})...") dec_content = title.get_content_by_index(content) content_file_name = f"{title.tmd.content_records[content].content_id:08X}.app" @@ -140,15 +142,15 @@ def run_nus_download_wii(out_folder: pathlib.Path, tid: str, version: str, pack_ # re-encrypted with the common key instead of the vWii key, so that the title can be installed from within # vWii mode. (vWii mode does not have access to the vWii key, only Wii U mode has that.) if repack_vwii_chkbox is True and (tid[3] == "7" or tid[7] == "7"): - progress_callback.emit(" - Re-encrypting Title Key with the common key...") + progress_callback.emit(-1, -1, " - Re-encrypting Title Key with the common key...") title_key_common = libWiiPy.title.encrypt_title_key(title.ticket.get_title_key(), 0, title.tmd.title_id) title.ticket.common_key_index = 0 title.ticket.title_key_enc = title_key_common # Get the WAD certificate chain, courtesy of libWiiPy. - progress_callback.emit(" - Building certificate...") + progress_callback.emit(-1, -1, " - Building certificate...") title.load_cert_chain(libWiiPy.title.download_cert_chain(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...") + progress_callback.emit(-1, -1, " - Packing WAD...") if wad_file_name != "" and wad_file_name is not None: # Batch downloads may insert -vLatest, so if it did we can fill in the real number now. wad_file_name = wad_file_name.replace("-vLatest", f"-v{title_version}") @@ -158,20 +160,24 @@ def run_nus_download_wii(out_folder: pathlib.Path, tid: str, version: str, pack_ wad_file_name = f"{tid}-v{title_version}.wad" # If enabled (after we make sure it's an IOS), apply all main IOS patches. if patch_ios and (tid[:8] == "00000001" and int(tid[-2:], 16) > 2): - progress_callback.emit(" - Patching IOS...") + progress_callback.emit(-1, -1, " - Patching IOS...") ios_patcher = libWiiPy.title.IOSPatcher() ios_patcher.load(title) patch_count = ios_patcher.patch_all() if patch_count > 0: - progress_callback.emit(f" - Applied {patch_count} patches!") + progress_callback.emit(-1, -1, f" - Applied {patch_count} patches!") else: - progress_callback.emit(" - No patches could be applied! Is this a stub IOS?") + progress_callback.emit(-1, -1, " - No patches could be applied! Is this a stub IOS?") title = ios_patcher.dump() # Append "-PATCHED" to the end of the WAD file name to make it clear that it was modified. wad_file_name = wad_file_name[:-4] + "-PATCHED" + wad_file_name[-4:] + # Certain special characters are prone to breaking things, so strip them from the file name before actually + # opening the file for writing. On some platforms (like macOS), invalid characters get replaced automatically, + # but on Windows the file will just fail to be written out at all. + wad_file_name = wad_file_name.translate({ord(c): None for c in '/\\:*"?<>|'}) # Have libWiiPy dump the WAD, and write that data out. version_dir.joinpath(wad_file_name).write_bytes(title.dump_wad()) - progress_callback.emit("Download complete!") + progress_callback.emit(0, 1, "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. diff --git a/modules/theme.py b/modules/theme.py new file mode 100644 index 0000000..b210fac --- /dev/null +++ b/modules/theme.py @@ -0,0 +1,64 @@ +# "modules/theme.py", licensed under the MIT license +# Copyright 2024-2025 NinjaCheetah & Contributors + +import os +import platform +import subprocess + +def is_dark_theme_windows(): + # This has to be here so that Python doesn't try to import it on non-Windows. + import winreg + try: + registry = winreg.ConnectRegistry(None, winreg.HKEY_CURRENT_USER) + key = winreg.OpenKey(registry, r"Software\Microsoft\Windows\CurrentVersion\Themes\Personalize") + # This value is "AppsUseLightTheme" so a "1" is light and a "0" is dark. Side note: I hate the Windows registry. + value, _ = winreg.QueryValueEx(key, "AppsUseLightTheme") + return value == 0 + except Exception: + return False + +def is_dark_theme_macos(): + # macOS is weird. If the dark theme is on, then `defaults read -g AppleInterfaceStyle` returns "Dark". If the light + # theme is on, then trying to read this key fails and returns an error instead. + try: + result = subprocess.run( + ["defaults", "read", "-g", "AppleInterfaceStyle"], + capture_output=True, text=True + ) + return "Dark" in result.stdout + except Exception: + return False + +def is_dark_theme_linux(): + try: + import subprocess + result = subprocess.run( + ["gsettings", "get", "org.gnome.desktop.interface", "gtk-theme"], + capture_output=True, text=True + ) + # Looking for *not* "Light", because I want any theme that isn't light to be dark. An example of this is my own + # KDE Plasma setup on my desktop, where I use the "Breeze" GTK theme and want dark NUSGet to be used in that + # case. + return not "light" in result.stdout.lower() + except Exception: + return False + +def is_dark_theme(): + # First, check for an environment variable overriding the theme, and use that if it exists. + try: + if os.environ["THEME"].lower() == "light": + return False + elif os.environ["THEME"].lower() == "dark": + return True + else: + print(f"Unknown theme specified: \"{os.environ['THEME']}\"") + except KeyError: + pass + # If the theme wasn't overridden, then check the current system theme. + system = platform.system() + if system == "Windows": + return is_dark_theme_windows() + elif system == "Darwin": + return is_dark_theme_macos() + else: + return is_dark_theme_linux() diff --git a/qt/py/ui_AboutDialog.py b/qt/py/ui_AboutDialog.py index 90f6bf2..679213b 100644 --- a/qt/py/ui_AboutDialog.py +++ b/qt/py/ui_AboutDialog.py @@ -17,56 +17,6 @@ class AboutNUSGet(QDialog): self.setFixedWidth(450) self.setFixedHeight(500) - # Set background color to match main app - self.setStyleSheet(""" - Credits { - background-color: #222222; - color: #ffffff; - } - QLabel { - color: #ffffff; - } - QLabel[class="title"] { - font-size: 20px; - font-weight: bold; - color: #ffffff; - } - QLabel[class="version"] { - font-size: 13px; - color: #aaaaaa; - } - QLabel[class="copyright"] { - font-size: 12px; - color: #888888; - } - QLabel[class="header"] { - font-size: 14px; - font-weight: bold; - border-bottom: 1px solid #444444; - padding-bottom: 4px; - margin-top: 8px; - } - QPushButton { - outline: 0; - show-decoration-selected: 1; - background-color: transparent; - border: 1px solid rgba(70, 70, 70, 1); - border-radius: 8px; - padding: 6px 10px; - margin: 4px 0px; - font-size: 13px; - font-weight: 500; - color: #ffffff; - } - QPushButton:hover { - background-color: rgba(60, 60, 60, 1); - border-color: #9c4ae8; - } - QPushButton:pressed { - background-color: rgba(26, 115, 232, 0.15); - border: 1px solid #6c1ae8; - }""") - # Create main layout self.layout = QVBoxLayout() self.layout.setSpacing(4) diff --git a/qt/py/ui_MainMenu.py b/qt/py/ui_MainMenu.py index e2a4915..09a3bc8 100644 --- a/qt/py/ui_MainMenu.py +++ b/qt/py/ui_MainMenu.py @@ -18,9 +18,9 @@ from PySide6.QtGui import (QAction, QBrush, QColor, QConicalGradient, QTransform) from PySide6.QtWidgets import (QApplication, QComboBox, QHBoxLayout, QHeaderView, QLabel, QLayout, QLineEdit, QMainWindow, - QMenu, QMenuBar, QPushButton, QSizePolicy, - QSpacerItem, QTabWidget, QTextBrowser, QTreeView, - QVBoxLayout, QWidget) + QMenu, QMenuBar, QProgressBar, QPushButton, + QSizePolicy, QSpacerItem, QTabWidget, QTextBrowser, + QTreeView, QVBoxLayout, QWidget) from qt.py.ui_WrapCheckboxWidget import WrapCheckboxWidget @@ -308,17 +308,25 @@ class Ui_MainWindow(object): self.log_text_browser = QTextBrowser(self.centralwidget) self.log_text_browser.setObjectName(u"log_text_browser") - self.log_text_browser.setMinimumSize(QSize(0, 247)) + self.log_text_browser.setMinimumSize(QSize(0, 222)) self.vertical_layout_controls.addWidget(self.log_text_browser) + self.progress_bar = QProgressBar(self.centralwidget) + self.progress_bar.setObjectName(u"progress_bar") + self.progress_bar.setMinimumSize(QSize(0, 25)) + self.progress_bar.setMaximumSize(QSize(16777215, 30)) + self.progress_bar.setValue(0) + + self.vertical_layout_controls.addWidget(self.progress_bar) + self.horizontalLayout_3.addLayout(self.vertical_layout_controls) MainWindow.setCentralWidget(self.centralwidget) self.menubar = QMenuBar(MainWindow) self.menubar.setObjectName(u"menubar") - self.menubar.setGeometry(QRect(0, 0, 1010, 21)) + self.menubar.setGeometry(QRect(0, 0, 1010, 30)) self.menuHelp = QMenu(self.menubar) self.menuHelp.setObjectName(u"menuHelp") MainWindow.setMenuBar(self.menubar) @@ -367,7 +375,7 @@ class Ui_MainWindow(object): "hr { height: 1px; border-width: 0; }\n" "li.unchecked::marker { content: \"\\2610\"; }\n" "li.checked::marker { content: \"\\2612\"; }\n" -"\n" +"\n" "


", None)) self.menuHelp.setTitle(QCoreApplication.translate("MainWindow", u"Help", None)) # retranslateUi diff --git a/qt/ui/MainMenu.ui b/qt/ui/MainMenu.ui index bf613a5..40bef00 100755 --- a/qt/ui/MainMenu.ui +++ b/qt/ui/MainMenu.ui @@ -422,7 +422,7 @@ 0 - 247 + 222 @@ -435,11 +435,30 @@ p, li { white-space: pre-wrap; } hr { height: 1px; border-width: 0; } li.unchecked::marker { content: "\2610"; } li.checked::marker { content: "\2612"; } -</style></head><body style=" font-family:'.AppleSystemUIFont'; font-size:13pt; font-weight:400; font-style:normal;"> +</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> + + + + + 0 + 25 + + + + + 16777215 + 30 + + + + 0 + + + @@ -450,7 +469,7 @@ li.checked::marker { content: "\2612"; } 0 0 1010 - 21 + 30 diff --git a/requirements.txt b/requirements.txt index 2066228..3446304 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ pyside6 nuitka~=2.6.0 -libWiiPy +git+https://github.com/NinjaCheetah/libWiiPy libTWLPy zstandard requests -imageio \ No newline at end of file +imageio diff --git a/resources/down_arrow_black.svg b/resources/down_arrow_black.svg new file mode 100644 index 0000000..2f1a769 --- /dev/null +++ b/resources/down_arrow_black.svg @@ -0,0 +1,59 @@ + + + + + + + + + + diff --git a/resources/down_arrow.svg b/resources/down_arrow_white.svg similarity index 100% rename from resources/down_arrow.svg rename to resources/down_arrow_white.svg diff --git a/resources/information_black.svg b/resources/information_black.svg new file mode 100644 index 0000000..4a3b2ef --- /dev/null +++ b/resources/information_black.svg @@ -0,0 +1,46 @@ + + + + + + + + + + diff --git a/resources/information.svg b/resources/information_white.svg similarity index 100% rename from resources/information.svg rename to resources/information_white.svg diff --git a/resources/right_arrow_black.svg b/resources/right_arrow_black.svg new file mode 100644 index 0000000..34720f0 --- /dev/null +++ b/resources/right_arrow_black.svg @@ -0,0 +1,59 @@ + + + + + + + + + + diff --git a/resources/right_arrow_white.svg b/resources/right_arrow_white.svg new file mode 100644 index 0000000..4ab9fbd --- /dev/null +++ b/resources/right_arrow_white.svg @@ -0,0 +1,59 @@ + + + + + + + + + + diff --git a/resources/style.qss b/resources/style_dark.qss similarity index 85% rename from resources/style.qss rename to resources/style_dark.qss index 7229318..43827bd 100644 --- a/resources/style.qss +++ b/resources/style_dark.qss @@ -10,6 +10,33 @@ QMainWindow QLabel { color: #ffffff; } +QDialog QLabel { + color: #ffffff; +} + +QDialog QLabel[class="title"] { + font-size: 20px; + font-weight: bold; +} + +QDialog QLabel[class="version"] { + font-size: 13px; + color: #aaaaaa; +} + +QDialog QLabel[class="copyright"] { + font-size: 12px; + color: #888888; +} + +QDialog QLabel[class="header"] { + font-size: 14px; + font-weight: bold; + border-bottom: 1px solid #444444; + padding-bottom: 4px; + margin-top: 8px; +} + QMenuBar { background-color: #2b2b2b; } @@ -145,6 +172,7 @@ QTreeView { } QTreeView QHeaderView::section { + color: white; background-color: #2b2b2b; border: 0; font-weight: 500; @@ -170,7 +198,18 @@ QTreeView QScrollBar:vertical { margin-top: 16px; } +QTreeView::branch:has-children:!has-siblings:closed, +QTreeView::branch:closed:has-children:has-siblings { + image: url("{IMAGE_PREFIX}/right_arrow_white.svg"); +} + +QTreeView::branch:open:has-children:!has-siblings, +QTreeView::branch:open:has-children:has-siblings { + image: url("{IMAGE_PREFIX}/down_arrow_white.svg"); +} + QTextBrowser { + color: white; background-color: #1a1a1a; selection-background-color: #6c1ae8; } @@ -242,7 +281,7 @@ QComboBox::drop-down { } QComboBox::down-arrow { - image: url("{IMAGE_PREFIX}/down_arrow.svg"); + image: url("{IMAGE_PREFIX}/down_arrow_white.svg"); } QComboBox QAbstractItemView { @@ -326,6 +365,28 @@ QScrollBar::sub-line:horizontal { subcontrol-origin: margin; } +QMessageBox QLabel { + color: white; +} + +QProgressBar { + border: 1px solid rgba(70, 70, 70, 1); + border-radius: 8px; + background-color: #1a1a1a; + text-align: center; + color: white; + padding-left: 1px; +} + +QProgressBar::chunk { + background-color: qlineargradient( + x1: 0, y1: 0, x2: 1, y2: 0, + stop: 0 #1a73e8, stop: 1 #5596f4 + ); + border-radius: 5px; + margin: 0.5px; +} + WrapCheckboxWidget { show-decoration-selected: 1; outline: 0; diff --git a/resources/style_light.qss b/resources/style_light.qss new file mode 100644 index 0000000..b13d52e --- /dev/null +++ b/resources/style_light.qss @@ -0,0 +1,444 @@ +/* "resources/style.qss" from NUSGet by NinjaCheetah & Contributors */ +/* Much of this QSS was written by Alex (https://github.com/Humanoidear) */ +/* from WiiLink for the fancy new WiiLink Patcher GUI. Used with permission. */ + +QMainWindow, QDialog { + background-color: #ffffff; +} + +QMainWindow QLabel { + color: #000000; +} + +QDialog QLabel { + color: #000000; +} + +QDialog QLabel[class="title"] { + font-size: 20px; + font-weight: bold; +} + +QDialog QLabel[class="version"] { + font-size: 13px; + color: #777777; +} + +QDialog QLabel[class="copyright"] { + font-size: 12px; + color: #444444; +} + +QDialog QLabel[class="header"] { + font-size: 14px; + font-weight: bold; + border-bottom: 1px solid #111111; + padding-bottom: 4px; + margin-top: 8px; +} + +QMenuBar { + background-color: #e3e3e3; + color: #000000; +} + +QMenuBar::item:selected { + background-color: rgb(195, 195, 195); + color: #000000; +} + +QMenuBar::item:pressed { + background-color: #1a73e8; + color: #ffffff; +} + +QMenu { + background-color: #ffffff; + border: 1px solid rgb(163, 163, 163); + border-radius: 8px; + padding: 6px 2px; + margin: 4px 0; + color: #000000; +} + +QMenu::item { + padding: 6px 2px; + margin: 2px; + border-radius: 4px; + background-color: transparent; +} + +QMenu::item:selected { + background-color: #1a73e8; + color: #ffffff; +} + +QMenu::icon { + padding: 4px; +} + +QRadioButton { + background-color: transparent; + border: 1px solid rgb(163, 163, 163); + border-radius: 8px; + padding: 8px 10px; + font-size: 13px; + font-weight: 500; + color: #ffffff; +} + +QRadioButton:hover { + background-color: rgba(60, 60, 60, 1); + border-color: #4a86e8; +} + +QRadioButton:checked { + background-color: rgba(26, 115, 232, 0.08); + border: 1px solid #1a73e8; + color: #1a73e8; +} + +QRadioButton::indicator { + width: 18px; + height: 18px; + border-radius: 5px; + border: 1px solid #5f6368; + margin-right: 8px; + subcontrol-position: left center; +} + +QRadioButton::indicator:checked { + background-color: #1a73e8; + border: 1px solid #1a73e8; + image: url("{IMAGE_PREFIX}/rounded_square.svg"); +} + +QRadioButton::indicator:hover { + border-color: #1a73e8; +} + +QLineEdit { + background-color: transparent; + border: 1px solid rgb(163, 163, 163); + border-radius: 8px; + padding: 6px 10px; + margin: 4px 0px; + font-size: 13px; + color: #000000; + selection-background-color: #1a73e8; +} + +QLineEdit:focus { + border-color: #1a73e8; +} + +QLineEdit:disabled { + background-color: rgba(182, 182, 182, 0.5); + border: 1px solid rgba(100, 100, 100, 0.3); + color: rgba(143, 143, 143, 0.3); +} + +QTabWidget::pane { + border: 1px solid rgb(163, 163, 163); + border-top-right-radius: 8px; + border-bottom-right-radius: 8px; + border-bottom-left-radius: 8px; + background-color: #e3e3e3; + top: -1px; +} + +QTabBar::tab { + background-color: transparent; + border-top: 1px solid rgb(163, 163, 163); + border-left: 1px solid rgb(163, 163, 163); + border-right: 1px solid rgb(163, 163, 163); + border-top-left-radius: 6px; + border-top-right-radius: 6px; + padding: 6px 10px; + font-size: 13px; + font-weight: 500; + color: #000000; +} + +QTabBar::tab:selected, QTabBar::tab:hover { + background-color: #e3e3e3; +} + +QTreeView { + show-decoration-selected: 1; + outline: 0; + background-color: #ffffff; + border: 0; + border-radius: 8px; +} + +QTreeView QHeaderView::section { + color: #000000; + background-color: #e3e3e3; + border: 0; + font-weight: 500; +} + +QTreeView::item { + color: #000000; +} + +QTreeView::item:hover { + background-color: rgb(195, 195, 195); +} + +QTreeView::item:focus { + background-color: rgba(26, 115, 232, 0.08); +} + +QTreeView::item:selected { + background-color: rgb(127, 182, 255); +} + +QTreeView::branch:selected { + background-color: rgb(127, 182, 255); +} + +QTreeView QScrollBar:vertical { + margin-top: 16px; +} + +QTreeView::branch:has-children:!has-siblings:closed, +QTreeView::branch:closed:has-children:has-siblings { + image: url("{IMAGE_PREFIX}/right_arrow_black.svg"); +} + +QTreeView::branch:open:has-children:!has-siblings, +QTreeView::branch:open:has-children:has-siblings { + image: url("{IMAGE_PREFIX}/down_arrow_black.svg"); +} + +QTextBrowser { + color: #000000; + background-color: #ececec; + selection-background-color: #1a73e8; + selection-color: #ffffff; +} + +QPushButton { + outline: 0; + show-decoration-selected: 1; + background-color: transparent; + border: 1px solid rgb(163, 163, 163); + border-radius: 8px; + padding: 6px 10px; + margin: 4px 0px; + font-size: 13px; + font-weight: 500; + color: #000000; +} + +QPushButton:hover { + background-color: rgb(195, 195, 195); + border-color: #4a86e8; +} + +QPushButton:focus { + background-color: rgb(195, 195, 195); + border-color: #4a86e8; +} + +QPushButton:pressed { + background-color: rgba(26, 115, 232, 0.15); + border: 1px solid #1a73e8; +} + +QPushButton:disabled { + background-color: rgba(182, 182, 182, 0.5); + border: 1px solid rgba(100, 100, 100, 0.3); + color: rgba(143, 143, 143, 0.3); +} + +QComboBox { + background-color: transparent; + combobox-popup: 0; + border: 1px solid rgb(163, 163, 163); + border-radius: 8px; + padding: 6px 10px; + margin: 4px 0px; + font-size: 13px; + font-weight: 500; + color: #000000; +} + +QComboBox:on { + background-color: rgba(26, 115, 232, 0.15); + border: 1px solid #1a73e8; +} + +QComboBox:hover { + background-color: rgb(195, 195, 195); + border-color: #4a86e8; +} + +QComboBox:focus { + background-color: rgb(195, 195, 195); + border-color: #4a86e8; +} + +QComboBox::drop-down { + border: 0; + width: 24px; +} + +QComboBox::down-arrow { + image: url("{IMAGE_PREFIX}/down_arrow_black.svg"); +} + +QComboBox QAbstractItemView { + background-color: #ffffff; + border: 1px solid rgb(163, 163, 163); + border-radius: 8px; + padding: 4px; + outline: none; +} + +QComboBox QAbstractItemView::item { + height: 25px; + border-radius: 4px; + padding: 4px 8px; + margin: 2px 0px; + color: #000000; +} + +QComboBox QAbstractItemView::item:hover { + background-color: #1a73e8; + color: #ffffff; +} + +QScrollBar:vertical { + border: 0; + border-radius: 8px; + padding: 2px 0 2px 0; + background-color: #f1f1f1; +} + +QScrollBar::handle:vertical { + background-color: #e3e3e3; + margin: 0 2px 0 2px; + width: 10px; + border: 1px solid rgb(163, 163, 163); + border-radius: 4px; +} + +QScrollBar::handle:vertical:hover { + background-color: rgba(26, 115, 232, 0.4); +} + +QScrollBar::add-line:vertical { + height: 0; + subcontrol-position: bottom; + subcontrol-origin: margin; +} + +QScrollBar::sub-line:vertical { + height: 0; + subcontrol-position: top; + subcontrol-origin: margin; +} + +QScrollBar:horizontal { + border: 0; + border-radius: 8px; + padding: 2px 0 2px 0; + background-color: #f1f1f1; +} + +QScrollBar::handle:horizontal { + background-color: #e3e3e3; + border: 1px solid rgb(163, 163, 163); + margin: 0px 2px 0px 2px; + border: 1px solid rgb(163, 163, 163); + border-radius: 4px; +} + +QScrollBar::handle:horizontal:hover { + background-color: rgba(26, 115, 232, 0.4); +} + +QScrollBar::add-line:horizontal { + height: 0; + subcontrol-position: bottom; + subcontrol-origin: margin; +} + +QScrollBar::sub-line:horizontal { + height: 0; + subcontrol-position: top; + subcontrol-origin: margin; +} + +QMessageBox QLabel { + color: #000000; +} + +QProgressBar { + border: 1px solid rgb(163, 163, 163); + border-radius: 8px; + background-color: #ececec; + text-align: center; + padding: 1px; + color: black; +} + +QProgressBar::chunk { + background-color: qlineargradient( + x1: 0, y1: 0, x2: 1, y2: 0, + stop: 0 #1a73e8, stop: 1 #5596f4 + ); + border-radius: 5px; + margin: 0.5px; +} + +WrapCheckboxWidget { + show-decoration-selected: 1; + outline: 0; + background-color: transparent; + border: 1px solid rgb(163, 163, 163); + border-radius: 8px; + padding: 12px 10px; + font-size: 13px; + font-weight: 500; + color: #000000; +} + +WrapCheckboxWidget:hover { + background-color: rgb(195, 195, 195); + border-color: #4a86e8; +} + +WrapCheckboxWidget:disabled { + background-color: rgba(182, 182, 182, 0.5); + border: 1px solid rgba(100, 100, 100, 0.3); + color: rgba(255, 255, 255, 0.3); +} + +WrapCheckboxWidget QLabel:disabled { + color: rgba(143, 143, 143, 0.3); +} + +WrapCheckboxWidget QCheckBox::indicator { + width: 16px; + height: 16px; + border-radius: 4px; + border: 1px solid #5f6368; +} + +WrapCheckboxWidget QCheckBox::indicator:checked { + background-color: #1a73e8; + border: 1px solid #1a73e8; + image: url("{IMAGE_PREFIX}/check.svg"); +} + +WrapCheckboxWidget QCheckBox::indicator:hover { + border-color: #1a73e8; +} + +WrapCheckboxWidget QCheckBox:checked { + color: #1a73e8; +} diff --git a/resources/translations/nusget_de.ts b/resources/translations/nusget_de.ts index c9e419e..781a3db 100644 --- a/resources/translations/nusget_de.ts +++ b/resources/translations/nusget_de.ts @@ -439,17 +439,17 @@ Sie nutzen bereits die neuste Version von NUSGet. App-Einstellungen - + Output Path Downloads-Ordner - + Select... Auswählen... - + <!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" /><meta charset="utf-8" /><style type="text/css"> p, li { white-space: pre-wrap; } @@ -468,17 +468,17 @@ li.checked::marker { content: "\2612"; } <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> - + Help Hilfe - + About NUSGet - Über NUSGet + Über NUSGet - + About Qt Über Qt diff --git a/resources/translations/nusget_es.ts b/resources/translations/nusget_es.ts index 4c4c2b1..34151b4 100644 --- a/resources/translations/nusget_es.ts +++ b/resources/translations/nusget_es.ts @@ -147,7 +147,7 @@ Configuración general - + <!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" /><meta charset="utf-8" /><style type="text/css"> p, li { white-space: pre-wrap; } @@ -159,7 +159,7 @@ li.checked::marker { content: "\2612"; } - + About NUSGet Acerca de NUSGet @@ -214,22 +214,22 @@ li.checked::marker { content: "\2612"; } Usar ruta de descarga personalizada - + Select... Seleccionar... - + Help Ayuda - + About Qt Acerca de Qt - + Output Path Ruta de descarga diff --git a/resources/translations/nusget_fr.ts b/resources/translations/nusget_fr.ts index 78e8b28..6dbf8b6 100644 --- a/resources/translations/nusget_fr.ts +++ b/resources/translations/nusget_fr.ts @@ -410,7 +410,7 @@ Les titres seront téléchargés dans un dossier "NUSGet Downloads", Configuration - + <!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" /><meta charset="utf-8" /><style type="text/css"> p, li { white-space: pre-wrap; } @@ -422,7 +422,7 @@ li.checked::marker { content: "\2612"; } - + About NUSGet À propos de NUSGet @@ -486,22 +486,22 @@ li.checked::marker { content: "\2612"; } Utiliser un dossier de téléchargement différent - + Select... Choisir - + Help Aide - + About Qt À propos de Qt - + Output Path Dossier de téléchargement diff --git a/resources/translations/nusget_it.ts b/resources/translations/nusget_it.ts index 94600c2..0d294ef 100644 --- a/resources/translations/nusget_it.ts +++ b/resources/translations/nusget_it.ts @@ -6,72 +6,72 @@ About NUSGet - + Info su NUSGet NUSGet - + NUSGet Version {nusget_version} - + Versione {nusget_version} Using libWiiPy {libwiipy_version} & libTWLPy {libtwlpy_version} - + Versione libWiiPy {libwiipy_version} & libTWLPy {libtwlpy_version} © 2024-2025 NinjaCheetah & Contributors - + © 2024-2025 NinjaCheetah & Contributori View Project on GitHub - + Vedi il progetto su GitHub Translations - + Traduzioni French (Français): <a href=https://github.com/rougets style='color: #4a86e8; text-decoration: none;'><b>rougets</b></a> - + Francese (Français): <a href=https://github.com/rougets style='color: #4a86e8; text-decoration: none;'><b>rougets</b></a> German (Deutsch): <a href=https://github.com/yeah-its-gloria style='color: #4a86e8; text-decoration: none;'><b>yeah-its-gloria</b></a> - + Tedesco (Deutsch): <a href=https://github.com/yeah-its-gloria style='color: #4a86e8; text-decoration: none;'><b>yeah-its-gloria</b></a> Italian (Italiano): <a href=https://github.com/LNLenost style='color: #4a86e8; text-decoration: none;'><b>LNLenost</b></a> - + Italiano: <a href=https://github.com/LNLenost style='color: #4a86e8; text-decoration: none;'><b>LNLenost</b></a> Korean (한국어): <a href=https://github.com/DDinghoya style='color: #4a86e8; text-decoration: none;'><b>DDinghoya</b></a> - + Coreano (한국어): <a href=https://github.com/DDinghoya style='color: #4a86e8; text-decoration: none;'><b>DDinghoya</b></a> Norwegian (Norsk): <a href=https://github.com/rolfiee style='color: #4a86e8; text-decoration: none;'><b>rolfiee</b></a> - + Norvegese (Norsk): <a href=https://github.com/rolfiee style='color: #4a86e8; text-decoration: none;'><b>rolfiee</b></a> Romanian (Română): <a href=https://github.com/NotImplementedLife style='color: #4a86e8; text-decoration: none;'><b>NotImplementedLife</b></a> - + Rumeno (Română): <a href=https://github.com/NotImplementedLife style='color: #4a86e8; text-decoration: none;'><b>NotImplementedLife</b></a> Spanish (Español): <a href=https://github.com/DarkMatterCore style='color: #4a86e8; text-decoration: none;'><b>DarkMatterCore</b></a> - + Spagnolo (Español): <a href=https://github.com/DarkMatterCore style='color: #4a86e8; text-decoration: none;'><b>DarkMatterCore</b></a> @@ -146,9 +146,9 @@ Impostazioni generali - + About NUSGet - + Info su NUSGet @@ -192,25 +192,25 @@ App Settings - + Impostazioni app Check for updates on startup - + Controlla aggiornamenti all'avvio Use a custom download directory - + Usa una cartella di download personalizzata - + Select... - + Seleziona... - + <!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" /><meta charset="utf-8" /><style type="text/css"> p, li { white-space: pre-wrap; } @@ -219,22 +219,29 @@ li.unchecked::marker { content: "\2610"; } li.checked::marker { content: "\2612"; } </style></head><body style=" font-family:'.AppleSystemUIFont'; font-size:13pt; 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> - + <!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" /><meta charset="utf-8" /><style type="text/css"> +p, li { white-space: pre-wrap; } +hr { height: 1px; border-width: 0; } +li.unchecked::marker { content: "\2610"; } +li.checked::marker { content: "\2612"; } +</style></head><body style=" font-family:'.AppleSystemUIFont'; font-size:13pt; 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> - + Help - + Aiuto - + About Qt - + Info su Qt - + Output Path - + Cartella output <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> @@ -295,17 +302,21 @@ I titoli verranno scaricati nella cartella "NUSGet Downloads" all&apos Titles marked with a checkmark are free and have a ticket available, and can be decrypted and/or packed into a WAD or TAD. Titles with an X do not have a ticket, and only their encrypted contents can be saved. By default, titles will be downloaded to a folder named "NUSGet Downloads" inside your downloads folder. - + Scegli un titolo dalla lista a sinistra o inserisci un ID Titolo per iniziare. + +I titoli marcati da una spunta sono disponibili e hanno un ticket libero, e possono essere decriptati e/o convertiti in WAD o TAD. I titoli con una X non hanno un ticket e solo il contenuto criptato può essere salvato. + +Per impostazione predefinita, i titoli verranno scaricati nella cartella "NUSGet Downloads" all'interno della cartella Download. Use the Wii U NUS (faster, only affects Wii/vWii) - + Usa il NUS di Wii U (più veloce, influisce solo su Wii/vWii) <b>There's a newer version of NUSGet available!</b> - + <b>È disponibile una nuova versione di NUSGet!</b> @@ -326,17 +337,17 @@ By default, titles will be downloaded to a folder named "NUSGet Downloads&q Invalid Download Directory - + Cartella di download non valida The specified download directory does not exist! - + La cartella di download specificata non esiste! Please make sure the specified download directory exists, and that you have permission to access it. - + Assicurati che la cartella di download specificata esista e che tu abbia i permessi per accedervi. @@ -346,52 +357,52 @@ By default, titles will be downloaded to a folder named "NUSGet Downloads&q <b>The Title ID you have entered is not in a valid format!</b> + <b>L'ID Titolo che hai inserito non è in un formato valido!</b> + + + + 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. <b>No title with the provided Title ID or version could be found!</b> - + <b>Non è stato trovato alcun titolo con l'ID Titolo o la versione forniti!</b> <b>Content decryption was not successful! Decrypted contents could not be created.</b> - + <b>La decriptazione dei contenuti non è riuscita! Non è stato possibile creare i contenuti decriptati.</b> <b>No Ticket is Available for the Requested Title!</b> - + <b>Nessun ticket disponibile per il titolo richiesto!</b> <b>An Unknown Error has Occurred!</b> - + <b>Si è verificato un errore sconosciuto!</b> <b>Some issues occurred while running the download script.</b> - + <b>Si sono verificati alcuni problemi durante l'esecuzione dello script di download.</b> <b>An error occurred while parsing the script file!</b> - + <b>Si è verificato un errore durante l'analisi del file script!</b> <b>An error occurred while parsing Title IDs!</b> - + <b>Si è verificato un errore durante l'analisi degli ID Titolo!</b> The Title ID you have entered is not in a valid format! L' ID Titolo che hai inserito non è in un formato valido! - - - 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. - Gli ID Titolo sono un codice di 16 caratteri tra numeri e lettere. Per favore inserisci in ID Titolo formattato correttamente, o scegline uno dal menù a sinistra. - Title ID/Version Not Found @@ -470,7 +481,7 @@ By default, titles will be downloaded to a folder named "NUSGet Downloads&q You enabled "Create decrypted contents" or "Pack installable archive", but the following titles in the script do not have tickets available. If enabled, encrypted contents were still downloaded. - You enabled "Create decrypted contents" or "Pack installable archive", but the following titles in the script do not have tickets available. If enabled, encrypted contents were still downloaded. + Hai abilitato "Crea contenuto decriptato" o "Archivio installabile", ma i seguenti titoli nello script non hanno ticket disponibili. Se abilitati, i contenuti criptati sono stati comunque scaricati. @@ -494,7 +505,7 @@ By default, titles will be downloaded to a folder named "NUSGet Downloads&q Error encountered at line {lineno}, column {colno}. Please double-check the script and try again. - Error encountered at line {lineno}, column {colno}. Please double-check the script and try again. + Errore riscontrato alla riga {lineno}, colonna {colno}. Controlla nuovamente lo script e riprova. An error occurred while parsing Title IDs! @@ -503,22 +514,22 @@ By default, titles will be downloaded to a folder named "NUSGet Downloads&q The title at index {index} does not have a Title ID! - The title at index {index} does not have a Title ID! + Il titolo all'indice {index} non ha un ID Titolo! Open Directory - + Apri cartella <b>The specified download directory does not exist!</b> - + <b>La cartella di download specificata non esiste!</b> Please make sure the download directory you want to use exists, and that you have permission to access it. - + Assicurati che la cartella di download che desideri utilizzare esista e che tu abbia i permessi per accedervi. Open NUS script diff --git a/resources/translations/nusget_ko.ts b/resources/translations/nusget_ko.ts index ee40843..34c666e 100644 --- a/resources/translations/nusget_ko.ts +++ b/resources/translations/nusget_ko.ts @@ -146,9 +146,9 @@ 일반 설정 - + About NUSGet - NUSGet 정보 + NUSGet 정보 @@ -205,12 +205,12 @@ 커스텀 다운로드 디렉터리 사용 - + Select... 선택... - + <!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" /><meta charset="utf-8" /><style type="text/css"> p, li { white-space: pre-wrap; } @@ -222,7 +222,7 @@ li.checked::marker { content: "\2612"; } - + Help 도움말 @@ -231,12 +231,12 @@ li.checked::marker { content: "\2612"; } 정보 - + About Qt Qt 정보 - + Output Path 출력 경로 diff --git a/resources/translations/nusget_nb.ts b/resources/translations/nusget_nb.ts index 480f799..a286499 100644 --- a/resources/translations/nusget_nb.ts +++ b/resources/translations/nusget_nb.ts @@ -146,7 +146,7 @@ Generelle Instillinger - + About NUSGet Om NUSGet @@ -205,12 +205,12 @@ Bruke en egendefinert nedlastingsmappe - + Select... Velg... - + <!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" /><meta charset="utf-8" /><style type="text/css"> p, li { white-space: pre-wrap; } @@ -229,17 +229,17 @@ li.checked::marker { content: "\2612"; } <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> - + Help Hjelp - + About Qt Om Qt - + Output Path Utgangsbane diff --git a/resources/translations/nusget_no.ts b/resources/translations/nusget_no.ts index 480f799..a286499 100644 --- a/resources/translations/nusget_no.ts +++ b/resources/translations/nusget_no.ts @@ -146,7 +146,7 @@ Generelle Instillinger - + About NUSGet Om NUSGet @@ -205,12 +205,12 @@ Bruke en egendefinert nedlastingsmappe - + Select... Velg... - + <!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" /><meta charset="utf-8" /><style type="text/css"> p, li { white-space: pre-wrap; } @@ -229,17 +229,17 @@ li.checked::marker { content: "\2612"; } <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> - + Help Hjelp - + About Qt Om Qt - + Output Path Utgangsbane diff --git a/resources/translations/nusget_ro.ts b/resources/translations/nusget_ro.ts index bc37607..063cf09 100644 --- a/resources/translations/nusget_ro.ts +++ b/resources/translations/nusget_ro.ts @@ -447,7 +447,7 @@ Implicit, titlurile vor fi descărcate într-un folder numit „NUSGet Downloads Setări Generale - + <!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" /><meta charset="utf-8" /><style type="text/css"> p, li { white-space: pre-wrap; } @@ -459,7 +459,7 @@ li.checked::marker { content: "\2612"; } - + About NUSGet Despre NUSGet @@ -523,22 +523,22 @@ li.checked::marker { content: "\2612"; } Folosiți un director de descărcare propriu - + Select... Selectează... - + Help Ajutor - + About Qt Despre Qt - + Output Path Cale de ieșire