Step 1. Add the JitPack repository to your build file
Add it in your root settings.gradle at the end of repositories:
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
mavenCentral()
maven { url 'https://jitpack.io' }
}
}
Add it in your settings.gradle.kts at the end of repositories:
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
mavenCentral()
maven { url = uri("https://jitpack.io") }
}
}
Add to pom.xml
<repositories>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories>
Add it in your build.sbt at the end of resolvers:
resolvers += "jitpack" at "https://jitpack.io"
Add it in your project.clj at the end of repositories:
:repositories [["jitpack" "https://jitpack.io"]]
Step 2. Add the dependency
dependencies {
implementation 'com.github.Maschell:JNUSLib:'
}
dependencies {
implementation("com.github.Maschell:JNUSLib:")
}
<dependency>
<groupId>com.github.Maschell</groupId>
<artifactId>JNUSLib</artifactId>
<version></version>
</dependency>
libraryDependencies += "com.github.Maschell" % "JNUSLib" % ""
:dependencies [[com.github.Maschell/JNUSLib ""]]
JNUSLib is a library written in Java to handle NUS Contents (.app, tmd, tik, cert files) from different sources. It can be easily used in other Applications and is directed to devolpers. It's based on JNUSTool, but is heavily rewritten and extented with even more features.
Loading NUS Contents from different sources. Currently implemented:
For files loaded from one of the sources, the following features can be used.
For WUD files, following additional operations are possible:
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
...
<!-- The core module -->
<dependency>
<groupId>de.Maschell.JNUSLib</groupId>
<artifactId>JNUSLib</artifactId>
<version>%version OR commit%</version>
</dependency>
Gor each different source type, you need to use a an own NUSTitleLoader.
byte[] commonKey = Utils.StringToByteArray("12345678901234567890123456789012");
// Loading a title with a public ticket
NUSTitle nusRemote = NUSTitleLoaderRemote.loadNUSTitle(0x0005000E12345678L, commonKey);
// Loading a title with an own ticket (key, titleid)
Ticket ticket = Ticket.createTicket(Utils.StringToByteArray("12345678901234567890123456789012"), 0x0005000E12345678L, commonKey);
NUSTitle nusRemoteWithTicket = NUSTitleLoaderRemote.loadNUSTitle(0x0005000E12345678L, commonKey);
// With ticket on disk
NUSTitle nusLocal = NUSTitleLoaderLocal.loadNUSTitle("path-to-app-files", commonKey);
// Loading a title with an own ticket (key, titleid)
Ticket ticket = Ticket.createTicket(Utils.StringToByteArray("12345678901234567890123456789012"), 0x0005000E12345678L, commonKey);
NUSTitle nusLocalWithTicket = NUSTitleLoaderLocal.loadNUSTitle("path-to-app-files", ticket);
// Loading a .woomy file
NUSTitle nusWoomy = NUSTitleLoaderWoomy.loadNUSTitle("testfile.woomy");
// WUD
// Loading a uncompressed WUD
WUDInfo wiWUD = WUDLoader.load("game.wud"); // needs a game.key next to the .wud
// Loading a compressed WUD (WUX)
WUDInfo wiWUX = WUDLoader.load("game.wux"); // needs a game.key next to the .wux
// Loading a uncompressed splitted WUD (2gb parts)
WUDInfo wiWUDSplitted = WUDLoader.load("game_part1.wud"); // needs a game.key next to the .wud
// Loading providing the disc key
WUDInfo wiWUXWithDisc = WUDLoader.load("game.wux", Utils.StringToByteArray("12345678901234567890123456789012")); // needs a game.key next to the .wux
// Loading a wud with no titley key (kiosk)
WUDInfo wiKiosk = WUDLoader.loadDev("game.wux");
// Get NUSTitles from WUDInfo
List<NUSTitle> titlesFromWUD = WUDLoader.getGamePartionsAsNUSTitles(wudInfo, commonKey);
/ Loading a wumad
WumadInfo wumadInfo = WumadLoader.load(new File("game.wud"));
// Get NUSTitles from wumad
List<NUSTitle> titlesFromWumad = WumadLoader.getGamePartionsAsNUSTitles(wumadInfo, commonKey);
Once the title is loaded, you can use one of the services to extract and decrypt files.
For the decryption you can use a FSTDataProvider in combinations with a FSTEntry. Example:
// Get a FSTDataProvider from NUSTitle
FSTDataProvider fstdataprovider = new FSTDataProviderNUSTitle(nustitle);
// When loading from a WUD, you can get the data of all partitions via
List<FSTDataProvider> partitionsFromWUD = WUDLoader.getPartitonsAsFSTDataProvider(wudInfo, commonKey);
// the includes all non-nustitles like the SI or UP partitions.
// When loading from a Wumad, you can get the data of all partitions via
List<FSTDataProvider> partitionsFromWumad = WumadLoader.getPartitonsAsFSTDataProvider(wumadInfo, commonKey);
FSTEntry fstRoot = fstdataprovider.getRoot();
FSTEntry appxml = FSTUtils.getFSTEntriesByRegEx(fstdataprovider.getRoot(), ".*app.xml").get(0); // get all .rpx files
// Get data as byte array
byte[] appxmlData = fstdataprovider.readFile(appxml);
// Get 1024 bytes from entry appxml from offset 0
byte[] appxmlChunk = fstdataprovider.readFile(appxml, 0, 1024);
// Get data as input stream
InputStream appxmlStream = fstdataprovider.readFileAsStream(appxml);
// Get 1024 bytes from entry appxml from offset 0 as input stream
InputStream appxmlStream = fstdataprovider.readFileAsStream(appxml, 0, 1024);
// Save data to output stream
FileOutputStream appxmlOut = new FileOutputStream(new File(appxml.getFilename()));
if (fstdataprovider.readFileToStream(appxmlOut, appxml)) {
System.out.println("Okay.");
}
Some wrapper functions can be found in the DecryptionService:
FSTDataProvider fstdataprovider = new FSTDataProviderNUSTitle(nustitle);
DecryptionService decrypt = DecryptionService.getInstance(fstdataprovider);
// Decrypt the whole FST into a folder called "DecryptedTitle" and skip existing
decrypt.decryptAllFSTEntriesTo("DecryptedTitle", true);
// Decrypt the code folder into a folder called "code_folder" and skip existing
decrypt.decryptFSTEntriesTo("/code/.*", "code_folder", true);
//Get the Service for the NUSTitle
ExtractionService extract = ExtractionService.getInstance(nusTitle);
//Saving all .app/.h3/tmd/tik/cert files into the folder "encryptedFiles"
extract.extractAll("encryptedFiles");
//Save all .h3 files into "contentHashes"
extract.extractAllEncrpytedContentFileHashes("contentHashes");
//Save all -.app files into "contents"
extract.extractAllEncryptedContentFilesWithoutHashesTo("contents");
//Save tmd, cert and ticket
extract.extractTMDTo("output");
extract.extractTicketTo("output");
extract.extractCertTo("output");
Example for compressing and verifing .wux files.
WUDImage imageUncompressed = new WUDImage(new File("game_part1.wud")); // Splitted and not splitted .wud possible here
Optional<File> compressedWUD = WUDService.compressWUDToWUX(imageUncompressed, "compressedImage", "game.wux", false);
if (compressedWUD.isPresent()) {
WUDImage imageCompressed = new WUDImage(compressedWUD.get());
// Verify compression
if (WUDService.compareWUDImage(imageUncompressed, imageCompressed)) {
System.out.println("Both images are the same");
} else {
System.err.println("The images are different");
}
//Turn it back into .wud
WUDService.decompressWUX(imageCompressed, "newdecompressed", "test.wud", false);
} else {
System.err.println("Failed to compress wud");
}
Call the method cleanup() for a NUSTitle to cleanup/close all opened ressources.
Maschell for creating the lib
Crediar for CDecrypt
All people who have contributed to vgmtoolbox
Exzap for the .wux file format
FIX94 for wudump
The creators of lombok for lombok