19 Commits

Author SHA1 Message Date
ab28a7bf1a Merge remote-tracking branch 'origin/main' 2025-02-18 21:31:06 -05:00
0bb87bf75f Merge remote-tracking branch 'upstream/main'
# Conflicts:
#	README.md
#	modules/download_wii.py
2025-02-18 21:27:16 -05:00
d1570711ac Minor update required for libWiiPy v0.6.0 compatibility 2025-02-18 20:11:12 -05:00
449a680d32 Add safeguards to system Qt library loading on Linux
Also made minor update for libWiiPy v0.6.0 compatibility
2025-02-18 20:10:27 -05:00
rougets
35989d038a Add French translations (#23) 2025-01-20 13:31:41 -05:00
bf313eedf3 Update README.md 2025-01-16 21:18:52 -05:00
92bfeb2374 Merge remote-tracking branch 'upstream/main' 2025-01-13 14:24:38 -05:00
86da2d62b0 Merge remote-tracking branch 'upstream/main' 2025-01-05 01:31:20 -05:00
1e88c22f7c Merge remote-tracking branch 'upstream/main' 2024-12-23 18:04:18 -05:00
c4ed6e6ee6 Merge remote-tracking branch 'origin/main' 2024-12-23 12:56:45 -05:00
b337be8c08 Merge remote-tracking branch 'upstream/main' 2024-12-23 12:56:27 -05:00
76911db12d Merge remote-tracking branch 'upstream/main' 2024-12-22 21:53:12 -05:00
a361a45314 Merge remote-tracking branch 'upstream/main' 2024-12-22 17:16:37 -05:00
724c7e554b Merge remote-tracking branch 'upstream/main' 2024-12-21 20:08:03 -05:00
469cd96392 Merge remote-tracking branch 'upstream/main'
# Conflicts:
#	README.md
2024-12-19 20:34:19 -05:00
398654609b Merge changes from upstream 2024-12-18 16:43:35 -05:00
78f98b2c73 Fix minor issue with Ticket forging 2024-12-13 23:17:20 -05:00
5872ca4676 Merge major improvements from upstream 2024-12-13 23:09:23 -05:00
147e72c8c9 Added Title Key generation code 2024-12-13 16:56:15 -05:00
8 changed files with 202 additions and 83 deletions

View File

@@ -502,11 +502,21 @@ if __name__ == "__main__":
# it looks nice, but fallback on kvantum if it isn't, since kvantum is likely to exist. If all else fails, fusion. # it looks nice, but fallback on kvantum if it isn't, since kvantum is likely to exist. If all else fails, fusion.
if platform.system() == "Linux": if platform.system() == "Linux":
if os.path.isdir("/usr/lib/qt6/plugins"): if os.path.isdir("/usr/lib/qt6/plugins"):
app.addLibraryPath("/usr/lib/qt6/plugins") import subprocess
if "Breeze" in QStyleFactory.keys(): try:
app.setStyle("Breeze") # This CANNOT be the best way to get the system Qt version, but it's what I came up with for now.
elif "kvantum" in QStyleFactory.keys(): result = subprocess.run(['/usr/lib/qt6/bin/qtdiag'], stdout=subprocess.PIPE)
app.setStyle("kvantum") result_str = result.stdout.decode("utf-8").split("\n")[0]
sys_qt_ver = result_str.split(" ")[1].split(".")
pyside_qt_ver = version("PySide6").split(".")
if sys_qt_ver[0:2] == pyside_qt_ver[0:2]:
app.addLibraryPath("/usr/lib/qt6/plugins")
if "Breeze" in QStyleFactory.keys():
app.setStyle("Breeze")
elif "kvantum" in QStyleFactory.keys():
app.setStyle("kvantum")
except Exception as e:
print(e)
# Load qtbase translations, and then apps-specific translations. # Load qtbase translations, and then apps-specific translations.
path = QLibraryInfo.path(QLibraryInfo.LibraryPath.TranslationsPath) path = QLibraryInfo.path(QLibraryInfo.LibraryPath.TranslationsPath)

View File

@@ -1,9 +1,16 @@
# NUSGet # NUSGet After Dark
A modern and supercharged NUS downloader built with Python and Qt6. Powered by libWiiPy and libTWLPy. A modern and supercharged NUS downloader built with Python and Qt6. Powered by libWiiPy and libTWLPy. Fork with features not acceptable for prod.
<div align="center">
<img src="https://github.com/user-attachments/assets/156eb949-93aa-4453-b7a0-99b784ec0c8c" alt="The icon for NUSGet" width=256 height=256>
<h1>NUSGet</h1>
<p>A modern and supercharged NUS downloader built with Python and Qt6.</p>
<p>Powered by libWiiPy and libTWLPy.</p>
<a href="https://github.com/NinjaCheetah/NUSGet/actions/workflows/python-build.yml">
<img src="https://github.com/NinjaCheetah/NUSGet/actions/workflows/python-build.yml/badge.svg">
</a>
</div>
[![Python application](https://github.com/NinjaCheetah/NUSGet/actions/workflows/python-build.yml/badge.svg)](https://github.com/NinjaCheetah/NUSGet/actions/workflows/python-build.yml) ![Linux Screenshot](https://github.com/user-attachments/assets/f9e0e6c4-6a04-4c2b-bffd-7a1dc58671ff)
The name is a play on NuGet, the .NET package manager. Thank you [@Janni9009](https://github.com/Janni9009) for the name idea!
## Features ## Features
NUSGet allows you to download any content from the Nintendo Update Servers. Free content (content with a Ticket freely available on the servers) can be decrypted or packed directly into an installable archive (WAD/TAD). NUSGet allows you to download any content from the Nintendo Update Servers. Free content (content with a Ticket freely available on the servers) can be decrypted or packed directly into an installable archive (WAD/TAD).
@@ -12,11 +19,12 @@ NUSGet also offers the ability to create vWii WADs that can be installed from wi
The following features are available for all supported consoles: The following features are available for all supported consoles:
- Downloading encrypted contents (files like `00000000`, `00000001`, etc.) directly from the update servers for any title. - Downloading encrypted contents (files like `00000000`, `00000001`, etc.) directly from the update servers for any title.
- Creating decrypted contents (*.app files) from the encrypted contents on the servers. Only supported for free titles. - Creating decrypted contents (*.app files) from the encrypted contents on the servers.
- Scripting support, allowing you to perform batch downloads of any titles for the Wii, vWii, or DSi in one script. (See `example-script.json` for an example of the scripting format.) - Scripting support, allowing you to perform batch downloads of any titles for the Wii, vWii, or DSi in one script. (See `example-script.json` for an example of the scripting format.)
**For Wii and vWii titles only:** **For Wii and vWii titles only:**
- "Pack installable archive (WAD/TAD)": Pack the encrypted contents, TMD, and Ticket into a WAD file that can be installed on a Wii or in Dolphin Emulator. Only supported for free titles. - "Pack installable archive (WAD/TAD)": Pack the encrypted contents, TMD, and Ticket into a WAD file that can be installed on a Wii or in Dolphin Emulator.
- Forging Tickets for titles without a common Ticket available on the NUS by using the Title Key algorithm to derive the key needed to decrypt the title.
**For vWii titles only:** **For vWii titles only:**
- "Re-encrypt title using the Wii Common Key": Re-encrypt the Title Key in a vWii title's Ticket before packing the WAD, so that the WAD can be installed via a normal WAD manager on the vWii, and can be extracted with legacy tools. **This also creates WADs that can be installed directly in Dolphin, allowing for running the vWii System Menu in Dolphin without a vWii NAND dump!** - "Re-encrypt title using the Wii Common Key": Re-encrypt the Title Key in a vWii title's Ticket before packing the WAD, so that the WAD can be installed via a normal WAD manager on the vWii, and can be extracted with legacy tools. **This also creates WADs that can be installed directly in Dolphin, allowing for running the vWii System Menu in Dolphin without a vWii NAND dump!**
@@ -73,7 +81,7 @@ A huge thanks to all the wonderful translators who've helped make NUSGet availab
If your language isn't present or is out of date, and you'd like to contribute, you can check out [TRANSLATING.md](https://github.com/NinjaCheetah/NUSGet/blob/main/TRANSLATING.md) for directions on how to translate NUSGet. If your language isn't present or is out of date, and you'd like to contribute, you can check out [TRANSLATING.md](https://github.com/NinjaCheetah/NUSGet/blob/main/TRANSLATING.md) for directions on how to translate NUSGet.
## Why this and not NUSD? ## Additional Thanks
NUS Downloader (Nintendo Update Server Downloader), is an old tool for downloading titles from the Nintendo Update Servers for the Wii and DSi. Originally released in 2009, and effectively last updated in 2011, it stills works today, however it definitely shows its age, and is in need of a refresh. One of the major shortcomings of NUSD is that it only supports Windows, as most of the tools for the Wii from that era are written in C# and use the .NET Framework, especially since they tend to rely on the C# library libWiiSharp. NUSD also has far more limited support for DSi titles, and no support whatsoever for vWii titles. The name is a play on NuGet, the .NET package manager. Thank you [@Janni9009](https://github.com/Janni9009) for the name idea!
With my introduction of [libWiiPy](https://github.com/NinjaCheetah/libWiiPy), there's now a work-in-progress Python library designed to eventually have feature parity with libWiiSharp. At this point in time, the library is featured enough that every piece of libWiiSharp that NUSD relied on is now available in libWiiPy, so I decided to put that to use and create a replacement for it. NUSGet offers nearly all the same features as NUSD (currently there is no support for the DSi servers or for scripting), but is built on top of a modern library with a modern graphical framework, that being Qt6. A major benefit of this rewrite is that its fully cross-platform, and is natively compiled for Windows, Linux, and macOS. Thanks to all those who contributed to libWiiSharp and NUSD, without which this project would not exist.

View File

@@ -2,7 +2,6 @@
# Copyright 2024-2025 NinjaCheetah # Copyright 2024-2025 NinjaCheetah
import pathlib import pathlib
from typing import List, Tuple
import libTWLPy import libTWLPy

View File

@@ -3,8 +3,9 @@
import pathlib import pathlib
from typing import List, Tuple from typing import List, Tuple
from .tkey import find_tkey
import libWiiPy import libWiiPy
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, def run_nus_download_wii(out_folder: pathlib.Path, tid: str, version: str, pack_wad_chkbox: bool, keep_enc_chkbox: bool,
@@ -52,6 +53,7 @@ def run_nus_download_wii(out_folder: pathlib.Path, tid: str, version: str, pack_
# Write out the TMD to a file. # Write out the TMD to a file.
version_dir.joinpath(f"tmd.{title_version}").write_bytes(title.tmd.dump()) 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. # 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(): if use_local_chkbox and version_dir.joinpath("tik").exists():
progress_callback.emit(" - Parsing local copy of Ticket...") progress_callback.emit(" - Parsing local copy of Ticket...")
title.load_ticket(version_dir.joinpath("tik").read_bytes()) title.load_ticket(version_dir.joinpath("tik").read_bytes())
@@ -61,11 +63,10 @@ def run_nus_download_wii(out_folder: pathlib.Path, tid: str, version: str, pack_
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))
version_dir.joinpath("tik").write_bytes(title.ticket.dump()) version_dir.joinpath("tik").write_bytes(title.ticket.dump())
except ValueError: except ValueError:
# If libWiiPy returns an error, then no ticket is available. Log this, and disable options requiring a # If libWiiPy returns an error, then no ticket is available. Try to forge a ticket after we download the
# ticket so that they aren't attempted later. # content.
progress_callback.emit(" - No Ticket is available!") progress_callback.emit(" - No Ticket is available! Will try forging a Ticket.")
pack_wad_enabled = False forge_ticket = True
decrypt_contents_enabled = False
# Load the content records from the TMD, and begin iterating over the records. # Load the content records from the TMD, and begin iterating over the records.
title.load_content_records() title.load_content_records()
content_list = [] content_list = []
@@ -87,6 +88,39 @@ def run_nus_download_wii(out_folder: pathlib.Path, tid: str, version: str, pack_
if keep_enc_chkbox is True: if keep_enc_chkbox is True:
version_dir.joinpath(content_file_name).write_bytes(content_list[content]) version_dir.joinpath(content_file_name).write_bytes(content_list[content])
title.content.content_list = content_list title.content.content_list = content_list
# Try to forge a Ticket, if a common one wasn't available.
if forge_ticket is True:
progress_callback.emit(" - Attempting to forge Ticket...")
try:
title_key = find_tkey(tid, title.content.content_list[0], title.tmd.content_records[0])
title_key_enc = libWiiPy.title.encrypt_title_key(title_key, 0, tid)
ticket = libWiiPy.title.Ticket()
ticket.common_key_index = 0
ticket.console_id = 0
ticket.content_access_permissions = b'\xff' * 64
ticket.ecdh_data = b'\x00' * 60
ticket.permit_mask = b'\x00' * 4
ticket.permitted_titles = b'\x00' * 4
ticket.signature = b'\x00' * 256
ticket.signature_issuer = "Root-CA00000001-XS00000003" + ("\x00" * 38)
ticket.signature_type = b'\x00\x01' * 2
ticket.ticket_id = b'\x00' * 8
ticket.ticket_version = 0
ticket.title_export_allowed = 0
ticket.title_id = tid.encode()
ticket.title_key_enc = title_key_enc
ticket.title_limits_list = [_TitleLimit(0, 0) for _ in range(0, 8)]
ticket.title_version = 0
ticket.unknown1 = b'\xff' * 2
ticket.unknown2 = b'\x00' * 48
ticket.fakesign()
title.ticket = ticket
version_dir.joinpath("tik").write_bytes(title.ticket.dump())
progress_callback.emit(" - Successfully forged Ticket!")
except Exception:
progress_callback.emit(" - Ticket could not be forged!")
pack_wad_enabled = False
decrypt_contents_enabled = False
# If decrypt local contents is still true, decrypt each content and write out the decrypted file. # If decrypt local contents is still true, decrypt each content and write out the decrypted file.
if decrypt_contents_enabled is True: if decrypt_contents_enabled is True:
try: try:
@@ -112,7 +146,7 @@ def run_nus_download_wii(out_folder: pathlib.Path, tid: str, version: str, pack_
title.ticket.title_key_enc = title_key_common title.ticket.title_key_enc = title_key_common
# Get the WAD certificate chain, courtesy of libWiiPy. # Get the WAD certificate chain, courtesy of libWiiPy.
progress_callback.emit(" - Building certificate...") progress_callback.emit(" - Building certificate...")
title.wad.set_cert_data(libWiiPy.title.download_cert(wiiu_endpoint=wiiu_nus_enabled)) 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. # 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 wad_file_name != "" and wad_file_name is not None: if wad_file_name != "" and wad_file_name is not None:

53
modules/tkey.py Normal file
View File

@@ -0,0 +1,53 @@
# "tkey-gen.py", licensed under the MIT license
# Copyright 2024 NinjaCheetah
import binascii
import hashlib
import libWiiPy
from libWiiPy.types import _ContentRecord
def _secret(start, length):
ret = b''
add = start + length
for _ in range(length):
unsigned_start = start & 0xFF # Compensates for how Python handles negative values vs PHP.
ret += bytes.fromhex(f"{unsigned_start:02x}"[-2:])
nxt = start + add
add = start
start = nxt
return ret
def _mungetid(tid):
# Remove leading zeroes from the TID.
while tid.startswith("00"):
tid = tid[2:]
if tid == "":
tid = "00"
# In PHP, the last character just gets dropped if you make a hex string from an odd-length input, so this
# replicates that functionality.
if len(tid) % 2 != 0:
tid = tid[:-1]
return bytes.fromhex(tid)
def _derive_key(tid, passwd):
key_secret = _secret(-3, 10)
salt = hashlib.md5(key_secret + _mungetid(tid)).digest()
# Had to reduce the length here from 32 to 16 when converting to get the same length keys.
return hashlib.pbkdf2_hmac("sha1", passwd.encode(), salt, 20, 16).hex()
def find_tkey(tid: str, banner_enc: bytes, content_record: _ContentRecord) -> bytes:
# Find a working Title Key by generating a key with a password, then decrypting content 0 and comparing it to the
# expected hash. If the hash matches, then we generated the correct key.
passwds = ["nintendo", "mypass"]
for passwd in passwds:
key = binascii.unhexlify(_derive_key(tid, passwd).encode())
banner_dec = libWiiPy.title.decrypt_content(banner_enc, key, content_record.index, content_record.content_size)
banner_dec_hash = hashlib.sha1(banner_dec).hexdigest()
content_record_hash = content_record.content_hash.decode()
if banner_dec_hash == content_record_hash:
return key
raise Exception("Valid Title Key could not be generated")

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 41 KiB

View File

@@ -15,277 +15,286 @@ Select a title from the list on the left, or enter a Title ID to begin.
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. 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.
Titles will be downloaded to a folder named &quot;NUSGet Downloads&quot; inside your downloads folder.</source> Titles will be downloaded to a folder named &quot;NUSGet Downloads&quot; inside your downloads folder.</source>
<translation type="unfinished"></translation> <translation>NUSGet v{nusget_version}
Développé par NinjaCheetah
Alimenté par libWiiPy {libwiipy_version}
Support DSi fourni par libTWLPy {libtwlpy_version}
Choisissez un titre depuis la liste à gauche, ou saisissez un ID de titre pour commencer.
Les titres marqués d&apos;une coche sont gratuits et ont un billet disponible, et peuvent être décryptés et/ou empaquetés dans un fichier WAD ou TAD. Les titres marqués d&apos;une croix n&apos;ont pas de billets, et seul leur contenu crypté peut être enregistré.
Les titres seront téléchargés dans un dossier &quot;NUSGet Downloads&quot;, à l&apos;intérieur de votre dossier de téléchargements.</translation>
</message> </message>
<message> <message>
<location filename="../../NUSGet.py" line="192"/> <location filename="../../NUSGet.py" line="192"/>
<source>NUSGet Update Available</source> <source>NUSGet Update Available</source>
<translation type="unfinished"></translation> <translation>Mise à jour NUSGet disponible</translation>
</message> </message>
<message> <message>
<location filename="../../NUSGet.py" line="193"/> <location filename="../../NUSGet.py" line="193"/>
<source>There&apos;s a newer version of NUSGet available!</source> <source>There&apos;s a newer version of NUSGet available!</source>
<translation type="unfinished"></translation> <translation>Une nouvelle version de NUSGet est disponible !</translation>
</message> </message>
<message> <message>
<location filename="../../NUSGet.py" line="286"/> <location filename="../../NUSGet.py" line="286"/>
<source>No Output Selected</source> <source>No Output Selected</source>
<translation type="unfinished"></translation> <translation>Aucun format sélectionné</translation>
</message> </message>
<message> <message>
<location filename="../../NUSGet.py" line="287"/> <location filename="../../NUSGet.py" line="287"/>
<source>You have not selected any format to output the data in!</source> <source>You have not selected any format to output the data in!</source>
<translation type="unfinished"></translation> <translation>Veuillez sélectionner un format de sortie pour les données !</translation>
</message> </message>
<message> <message>
<location filename="../../NUSGet.py" line="289"/> <location filename="../../NUSGet.py" line="289"/>
<source>Please select at least one option for how you would like the download to be saved.</source> <source>Please select at least one option for how you would like the download to be saved.</source>
<translation type="unfinished"></translation> <translation>Veuillez sélectionner au moins une option de téléchargement.</translation>
</message> </message>
<message> <message>
<location filename="../../NUSGet.py" line="318"/> <location filename="../../NUSGet.py" line="318"/>
<source>Invalid Title ID</source> <source>Invalid Title ID</source>
<translation type="unfinished"></translation> <translation>ID de titre invalide</translation>
</message> </message>
<message> <message>
<location filename="../../NUSGet.py" line="319"/> <location filename="../../NUSGet.py" line="319"/>
<source>The Title ID you have entered is not in a valid format!</source> <source>The Title ID you have entered is not in a valid format!</source>
<translation type="unfinished"></translation> <translation>L&apos;ID de titre que vous avez saisi a un format invalide !</translation>
</message> </message>
<message> <message>
<location filename="../../NUSGet.py" line="321"/> <location filename="../../NUSGet.py" line="321"/>
<source>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.</source> <source>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.</source>
<translation type="unfinished"></translation> <translation>Les ID de titre doivent être composés de 16 caractères alphanumériques. Veuillez saisir un ID formaté correctement, ou sélectionnez-en un depuis le menu de gauche.</translation>
</message> </message>
<message> <message>
<location filename="../../NUSGet.py" line="323"/> <location filename="../../NUSGet.py" line="323"/>
<source>Title ID/Version Not Found</source> <source>Title ID/Version Not Found</source>
<translation type="unfinished"></translation> <translation>ID de titre / Version introuvable</translation>
</message> </message>
<message> <message>
<location filename="../../NUSGet.py" line="324"/> <location filename="../../NUSGet.py" line="324"/>
<source>No title with the provided Title ID or version could be found!</source> <source>No title with the provided Title ID or version could be found!</source>
<translation type="unfinished"></translation> <translation>Aucun titre trouvé pour l&apos;ID ou la version fourni !</translation>
</message> </message>
<message> <message>
<location filename="../../NUSGet.py" line="326"/> <location filename="../../NUSGet.py" line="326"/>
<source>Please make sure that you have entered a valid Title ID, or selected one from the title database, and that the provided version exists for the title you are attempting to download.</source> <source>Please make sure that you have entered a valid Title ID, or selected one from the title database, and that the provided version exists for the title you are attempting to download.</source>
<translation type="unfinished"></translation> <translation>Veuillez vous assurez que vous avez saisi un ID valide, ou sélectionnez-en un depuis la base de données, et que la version fournie existe pour le titre que vous souhaitez télécharger.</translation>
</message> </message>
<message> <message>
<location filename="../../NUSGet.py" line="328"/> <location filename="../../NUSGet.py" line="328"/>
<source>Content Decryption Failed</source> <source>Content Decryption Failed</source>
<translation type="unfinished"></translation> <translation>Échec du décryptage</translation>
</message> </message>
<message> <message>
<location filename="../../NUSGet.py" line="329"/> <location filename="../../NUSGet.py" line="329"/>
<source>Content decryption was not successful! Decrypted contents could not be created.</source> <source>Content decryption was not successful! Decrypted contents could not be created.</source>
<translation type="unfinished"></translation> <translation>Le décryptage du contenu a échoué ! Le contenu décrypté ne peut être créé.</translation>
</message> </message>
<message> <message>
<location filename="../../NUSGet.py" line="332"/> <location filename="../../NUSGet.py" line="332"/>
<source>Your TMD or Ticket may be damaged, or they may not correspond with the content being decrypted. If you have checked &quot;Use local files, if they exist&quot;, try disabling that option before trying the download again to fix potential issues with local data.</source> <source>Your TMD or Ticket may be damaged, or they may not correspond with the content being decrypted. If you have checked &quot;Use local files, if they exist&quot;, try disabling that option before trying the download again to fix potential issues with local data.</source>
<translation type="unfinished"></translation> <translation>Vos métadonnées (TMD) ou le billet sont probablement endommagés, ou ils ne correspondent pas au contenu décrypté. Si vous avez coché &quot;Utiliser des fichiers locaux, s&apos;ils existent&quot;, essayez de désactiver cette option avant d&apos;essayer à nouveau pour résoudre les éventuelles erreurs avec les données locales.</translation>
</message> </message>
<message> <message>
<location filename="../../NUSGet.py" line="335"/> <location filename="../../NUSGet.py" line="335"/>
<source>Ticket Not Available</source> <source>Ticket Not Available</source>
<translation type="unfinished"></translation> <translation>Billet indisponible</translation>
</message> </message>
<message> <message>
<location filename="../../NUSGet.py" line="336"/> <location filename="../../NUSGet.py" line="336"/>
<source>No Ticket is Available for the Requested Title!</source> <source>No Ticket is Available for the Requested Title!</source>
<translation type="unfinished"></translation> <translation>Aucun billet disponible pour le titre demandé !</translation>
</message> </message>
<message> <message>
<location filename="../../NUSGet.py" line="339"/> <location filename="../../NUSGet.py" line="339"/>
<source>A ticket could not be downloaded for the requested title, but you have selected &quot;Pack installable archive&quot; or &quot;Create decrypted contents&quot;. These options are not available for titles without a ticket. Only encrypted contents have been saved.</source> <source>A ticket could not be downloaded for the requested title, but you have selected &quot;Pack installable archive&quot; or &quot;Create decrypted contents&quot;. These options are not available for titles without a ticket. Only encrypted contents have been saved.</source>
<translation type="unfinished"></translation> <translation>Un billet ne peut être téléchargé pour le titre demandé, mais vous avez sélectionné &quot;Empaqueter une archive d&apos;installation&quot; ou &quot;Décrypter le contenu&quot;. Ces options sont indisponibles pour les titres sans billet. Seul le contenu crypté a é enregistré.</translation>
</message> </message>
<message> <message>
<location filename="../../NUSGet.py" line="341"/> <location filename="../../NUSGet.py" line="341"/>
<source>Unknown Error</source> <source>Unknown Error</source>
<translation type="unfinished"></translation> <translation>Erreur inconnue</translation>
</message> </message>
<message> <message>
<location filename="../../NUSGet.py" line="342"/> <location filename="../../NUSGet.py" line="342"/>
<source>An Unknown Error has Occurred!</source> <source>An Unknown Error has Occurred!</source>
<translation type="unfinished"></translation> <translation>Une erreur inconnue est survenue !</translation>
</message> </message>
<message> <message>
<location filename="../../NUSGet.py" line="344"/> <location filename="../../NUSGet.py" line="344"/>
<source>Please try again. If this issue persists, please open a new issue on GitHub detailing what you were trying to do when this error occurred.</source> <source>Please try again. If this issue persists, please open a new issue on GitHub detailing what you were trying to do when this error occurred.</source>
<translation type="unfinished"></translation> <translation>Veuillez essayer à nouveau. Si le problème persiste, déclarez un problème sur GitHub en décrivant les actions qui ont provoqué l&apos;erreur.</translation>
</message> </message>
<message> <message>
<location filename="../../NUSGet.py" line="363"/> <location filename="../../NUSGet.py" line="363"/>
<source>Script Issues Occurred</source> <source>Script Issues Occurred</source>
<translation type="unfinished"></translation> <translation>Erreurs survenues dans le script</translation>
</message> </message>
<message> <message>
<location filename="../../NUSGet.py" line="364"/> <location filename="../../NUSGet.py" line="364"/>
<source>Some issues occurred while running the download script.</source> <source>Some issues occurred while running the download script.</source>
<translation type="unfinished"></translation> <translation>Des erreurs sont survenues pendant l&apos;exécution du script de téléchargement.</translation>
</message> </message>
<message> <message>
<location filename="../../NUSGet.py" line="366"/> <location filename="../../NUSGet.py" line="366"/>
<source>Check the log for more details about what issues were encountered.</source> <source>Check the log for more details about what issues were encountered.</source>
<translation type="unfinished"></translation> <translation>Vérifiez le journal pour plus de détails à propos des erreurs rencontrées.</translation>
</message> </message>
<message> <message>
<location filename="../../NUSGet.py" line="373"/> <location filename="../../NUSGet.py" line="373"/>
<source>The following titles could not be downloaded due to an error. Please ensure that the Title ID and version listed in the script are valid.</source> <source>The following titles could not be downloaded due to an error. Please ensure that the Title ID and version listed in the script are valid.</source>
<translation type="unfinished"></translation> <translation>Le téléchargement des titres suivants a échoué. Assurez-vous que les ID de titre et version du script soient valides.</translation>
</message> </message>
<message> <message>
<location filename="../../NUSGet.py" line="383"/> <location filename="../../NUSGet.py" line="383"/>
<source>You enabled &quot;Create decrypted contents&quot; or &quot;Pack installable archive&quot;, but the following titles in the script do not have tickets available. If enabled, encrypted contents were still downloaded.</source> <source>You enabled &quot;Create decrypted contents&quot; or &quot;Pack installable archive&quot;, but the following titles in the script do not have tickets available. If enabled, encrypted contents were still downloaded.</source>
<translation type="unfinished"></translation> <translation>Vous avez activé &quot;Décrypter le contenu&quot; ou &quot;Empaqueter une archive d&apos;installation&quot;, mais les billets des titres suivants sont indisponibles. Si activé(s), le contenu crypté a é téléchargé.</translation>
</message> </message>
<message> <message>
<location filename="../../NUSGet.py" line="402"/> <location filename="../../NUSGet.py" line="402"/>
<source>Script Download Failed</source> <source>Script Download Failed</source>
<translation type="unfinished"></translation> <translation>Échec du script de téléchargement</translation>
</message> </message>
<message> <message>
<location filename="../../NUSGet.py" line="403"/> <location filename="../../NUSGet.py" line="403"/>
<source>Open NUS Script</source> <source>Open NUS Script</source>
<translation type="unfinished"></translation> <translation>Ouvrir un script NUS</translation>
</message> </message>
<message> <message>
<location filename="../../NUSGet.py" line="404"/> <location filename="../../NUSGet.py" line="404"/>
<source>NUS Scripts (*.nus *.json)</source> <source>NUS Scripts (*.nus *.json)</source>
<translation type="unfinished"></translation> <translation>Scripts NUS (*.nus *.json)</translation>
</message> </message>
<message> <message>
<location filename="../../NUSGet.py" line="414"/> <location filename="../../NUSGet.py" line="414"/>
<source>An error occurred while parsing the script file!</source> <source>An error occurred while parsing the script file!</source>
<translation type="unfinished"></translation> <translation>Une erreur est survenue pendant la lecture du script !</translation>
</message> </message>
<message> <message>
<location filename="../../NUSGet.py" line="415"/> <location filename="../../NUSGet.py" line="415"/>
<source>Error encountered at line {e.lineno}, column {e.colno}. Please double-check the script and try again.</source> <source>Error encountered at line {e.lineno}, column {e.colno}. Please double-check the script and try again.</source>
<translation type="unfinished"></translation> <translation>Erreur recontrée ligne {e.lineno}, colonne {e.colno}. Vérifiez le script et réessayez.</translation>
</message> </message>
<message> <message>
<location filename="../../NUSGet.py" line="424"/> <location filename="../../NUSGet.py" line="424"/>
<source>An error occurred while parsing Title IDs!</source> <source>An error occurred while parsing Title IDs!</source>
<translation type="unfinished"></translation> <translation>Une erreur est survenue à la lecture d&apos;un ID de titre !</translation>
</message> </message>
<message> <message>
<location filename="../../NUSGet.py" line="425"/> <location filename="../../NUSGet.py" line="425"/>
<source>The title at index {script_data.index(title)} does not have a Title ID!</source> <source>The title at index {script_data.index(title)} does not have a Title ID!</source>
<translation type="unfinished"></translation> <translation>Le titre à l&apos;index {script_data.index(title)} n&apos;a pas d&apos;ID !</translation>
</message> </message>
<message> <message>
<location filename="../../qt/ui/MainMenu.ui" line="26"/> <location filename="../../qt/ui/MainMenu.ui" line="26"/>
<source>MainWindow</source> <source>MainWindow</source>
<translation type="unfinished"></translation> <translation>FenetrePrincipale</translation>
</message> </message>
<message> <message>
<location filename="../../qt/ui/MainMenu.ui" line="46"/> <location filename="../../qt/ui/MainMenu.ui" line="46"/>
<source>Search</source> <source>Search</source>
<translation type="unfinished"></translation> <translation>Rechercher</translation>
</message> </message>
<message> <message>
<location filename="../../qt/ui/MainMenu.ui" line="59"/> <location filename="../../qt/ui/MainMenu.ui" line="59"/>
<source>Clear</source> <source>Clear</source>
<translation type="unfinished"></translation> <translation>Effacer</translation>
</message> </message>
<message> <message>
<location filename="../../qt/ui/MainMenu.ui" line="90"/> <location filename="../../qt/ui/MainMenu.ui" line="90"/>
<source>Wii</source> <source>Wii</source>
<translation type="unfinished"></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../../qt/ui/MainMenu.ui" line="100"/> <location filename="../../qt/ui/MainMenu.ui" line="100"/>
<source>vWii</source> <source>vWii</source>
<translation type="unfinished"></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../../qt/ui/MainMenu.ui" line="110"/> <location filename="../../qt/ui/MainMenu.ui" line="110"/>
<source>DSi</source> <source>DSi</source>
<translation type="unfinished"></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../../qt/ui/MainMenu.ui" line="135"/> <location filename="../../qt/ui/MainMenu.ui" line="135"/>
<source>Title ID</source> <source>Title ID</source>
<translation type="unfinished"></translation> <translation>ID du titre</translation>
</message> </message>
<message> <message>
<location filename="../../qt/ui/MainMenu.ui" line="142"/> <location filename="../../qt/ui/MainMenu.ui" line="142"/>
<source>v</source> <source>v</source>
<translation type="unfinished"></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../../qt/ui/MainMenu.ui" line="155"/> <location filename="../../qt/ui/MainMenu.ui" line="155"/>
<source>Version</source> <source>Version</source>
<translation type="unfinished"></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../../qt/ui/MainMenu.ui" line="162"/> <location filename="../../qt/ui/MainMenu.ui" line="162"/>
<source>Console:</source> <source>Console:</source>
<translation type="unfinished"></translation> <translation>Console :</translation>
</message> </message>
<message> <message>
<location filename="../../qt/ui/MainMenu.ui" line="198"/> <location filename="../../qt/ui/MainMenu.ui" line="198"/>
<source>Start Download</source> <source>Start Download</source>
<translation type="unfinished"></translation> <translation>Télécharger</translation>
</message> </message>
<message> <message>
<location filename="../../qt/ui/MainMenu.ui" line="211"/> <location filename="../../qt/ui/MainMenu.ui" line="211"/>
<source>Run Script</source> <source>Run Script</source>
<translation type="unfinished"></translation> <translation>Exécuter le script</translation>
</message> </message>
<message> <message>
<location filename="../../qt/ui/MainMenu.ui" line="238"/> <location filename="../../qt/ui/MainMenu.ui" line="238"/>
<source>General Settings</source> <source>General Settings</source>
<translation type="unfinished"></translation> <translation>Configuration</translation>
</message> </message>
<message> <message>
<location filename="../../qt/ui/MainMenu.ui" line="269"/> <location filename="../../qt/ui/MainMenu.ui" line="269"/>
<source>Pack installable archive (WAD/TAD)</source> <source>Pack installable archive (WAD/TAD)</source>
<translation type="unfinished"></translation> <translation>Empaqueter une archive d&apos;installation (WAD / TAD)</translation>
</message> </message>
<message> <message>
<location filename="../../qt/ui/MainMenu.ui" line="284"/> <location filename="../../qt/ui/MainMenu.ui" line="284"/>
<source>File Name</source> <source>File Name</source>
<translation type="unfinished"></translation> <translation>Nom du fichier</translation>
</message> </message>
<message> <message>
<location filename="../../qt/ui/MainMenu.ui" line="318"/> <location filename="../../qt/ui/MainMenu.ui" line="318"/>
<source>Keep encrypted contents</source> <source>Keep encrypted contents</source>
<translation type="unfinished"></translation> <translation>Conserver le contenu crypté</translation>
</message> </message>
<message> <message>
<location filename="../../qt/ui/MainMenu.ui" line="354"/> <location filename="../../qt/ui/MainMenu.ui" line="354"/>
<source>Create decrypted contents (*.app)</source> <source>Create decrypted contents (*.app)</source>
<translation type="unfinished"></translation> <translation>Décrypter le contenu (*.app)</translation>
</message> </message>
<message> <message>
<location filename="../../qt/ui/MainMenu.ui" line="393"/> <location filename="../../qt/ui/MainMenu.ui" line="393"/>
<source>Use local files, if they exist</source> <source>Use local files, if they exist</source>
<translation type="unfinished"></translation> <translation>Utiliser des fichiers locaux, s&apos;ils existent</translation>
</message> </message>
<message> <message>
<location filename="../../qt/ui/MainMenu.ui" line="438"/> <location filename="../../qt/ui/MainMenu.ui" line="438"/>
<source>Use the Wii U NUS (faster, only effects Wii/vWii)</source> <source>Use the Wii U NUS (faster, only effects Wii/vWii)</source>
<translation type="unfinished"></translation> <translation>Utiliser le NUS Wii U (plus rapide, n&apos;affecte que Wii / vWii)</translation>
</message> </message>
<message> <message>
<location filename="../../qt/ui/MainMenu.ui" line="480"/> <location filename="../../qt/ui/MainMenu.ui" line="480"/>
<source>Apply patches to IOS (Applies to WADs only)</source> <source>Apply patches to IOS (Applies to WADs only)</source>
<translation type="unfinished"></translation> <translation>Appliquer des modifications aux IOS (WAD uniquement)</translation>
</message> </message>
<message> <message>
<location filename="../../qt/ui/MainMenu.ui" line="536"/> <location filename="../../qt/ui/MainMenu.ui" line="536"/>
<source>vWii Title Settings</source> <source>vWii Title Settings</source>
<translation type="unfinished"></translation> <translation>Titres vWii</translation>
</message> </message>
<message> <message>
<location filename="../../qt/ui/MainMenu.ui" line="570"/> <location filename="../../qt/ui/MainMenu.ui" line="570"/>
<source>Re-encrypt title using the Wii Common Key</source> <source>Re-encrypt title using the Wii Common Key</source>
<translation type="unfinished"></translation> <translation>Encrypter le titre avec la clé commune Wii</translation>
</message> </message>
<message> <message>
<location filename="../../qt/ui/MainMenu.ui" line="627"/> <location filename="../../qt/ui/MainMenu.ui" line="627"/>
@@ -297,28 +306,34 @@ li.unchecked::marker { content: &quot;\2610&quot;; }
li.checked::marker { content: &quot;\2612&quot;; } li.checked::marker { content: &quot;\2612&quot;; }
&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:&apos;Noto Sans&apos;; font-size:10pt; font-weight:400; font-style:normal;&quot;&gt; &lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:&apos;Noto Sans&apos;; font-size:10pt; font-weight:400; font-style:normal;&quot;&gt;
&lt;p style=&quot;-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:&apos;Sans Serif&apos;; font-size:9pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source> &lt;p style=&quot;-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:&apos;Sans Serif&apos;; font-size:9pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
<translation type="unfinished"></translation> <translation></translation>
</message> </message>
<message> <message>
<location filename="../../modules/core.py" line="52"/> <location filename="../../modules/core.py" line="52"/>
<source> <source>
Could not check for updates.</source> Could not check for updates.</source>
<translation type="unfinished"></translation> <translation>
Impossible de vérifier les mises à jour.</translation>
</message> </message>
<message> <message>
<location filename="../../modules/core.py" line="60"/> <location filename="../../modules/core.py" line="60"/>
<source> <source>
There&apos;s a newer version of NUSGet available!</source> There&apos;s a newer version of NUSGet available!</source>
<translation type="unfinished"></translation> <translation>
Une nouvelle version de NUSGet est disponible !</translation>
</message> </message>
<message> <message>
<location filename="../../modules/core.py" line="62"/> <location filename="../../modules/core.py" line="62"/>
<source> <source>
You&apos;re running the latest release of NUSGet.</source> You&apos;re running the latest release of NUSGet.</source>
<translation type="unfinished"></translation> <translation>
Vous utilisez la dernière version de NUSGet.</translation>
</message> </message>
</context> </context>
</TS> </TS>