Compare commits

...

15 Commits
0.1b ... master

Author SHA1 Message Date
Maschell
6a6afff21d Improve dev common key detection, fix splitted file support on non-windows machines 2022-02-01 23:27:30 +01:00
Maschell
1443db9cdf Remove circleci 2020-12-20 15:15:50 +01:00
Maschell
b19809c3b6
Create maven.yml 2020-12-20 15:14:03 +01:00
Maschell
51cc20eaf9 Use latest JNUSLib version, fix support for multiple partitions with the same name, version bump to 0.4 2020-12-20 15:12:46 +01:00
Maschell
92978d4a69
Merge pull request #7 from agilly1989/master
Fixed the -titlekey argument
2020-12-20 12:39:19 +01:00
agilly1989
27aad40261
Fixed the -titlekey argument
the correct arg is titleKey as per Main.java:40
2020-12-20 00:11:11 +10:00
Maschell
1e4a56cd6f Update README 2019-04-26 21:17:57 +02:00
Maschell
f172151030 Add nightly builds - version bump to 0.3 2019-04-26 21:16:29 +02:00
Maschell
f51b9b7282 Adjust .gitignore 2019-04-26 21:12:36 +02:00
Maschell
1c8673b416 Using the newest JNUSLib as a backend:
- Support for extracting and decrypting all partitions
- Common key can be read from ~/.wii/common.key
2019-04-26 20:54:34 +02:00
Maschell
2341b55271 bump version im pom 2019-02-11 17:33:23 +01:00
Maschell
a3d298e63d Bump version. 2019-02-11 17:32:47 +01:00
Maschell
d0b2629278 Update to use the latest JNUSLIB version.
This fixed WUD parsing on *nix and more.
2019-02-11 17:32:27 +01:00
Maschell
0529ab5c5d - Update to use the latest JNUSLIB version.
- Use jitpack.io instead of having a .jar
- Building now via maven
- Added support to handle kiosk disc.
- Support for handling multiple GM partition, and support for the GM partition.
- Added an option to decompress a .wux back to .wud
- Fix serveral bugs
2018-12-06 20:16:05 +01:00
Maschell
053eaac3bf Fixing formatting, added examples 2017-05-15 11:15:14 +02:00
8 changed files with 460 additions and 269 deletions

View File

@ -1,37 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" output="target/classes" path="src/main/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry excluding="**" kind="src" output="target/classes" path="src/main/resources">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="src" output="target/test-classes" path="src/test/java">
<attributes>
<attribute name="optional" value="true"/>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry excluding="**" kind="src" output="target/test-classes" path="src/test/resources">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.m2e.MAVEN2_CLASSPATH_CONTAINER">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="lib" path="G:/Programmieren/JWUDTool/libs/jnuslib.jar"/>
<classpathentry kind="output" path="target/classes"/>
</classpath>

24
.github/workflows/maven.yml vendored Normal file
View File

@ -0,0 +1,24 @@
# This workflow will build a Java project with Maven
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven
name: Java CI with Maven
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up JDK 1.8
uses: actions/setup-java@v1
with:
java-version: 1.8
- name: Build with Maven
run: mvn -B package --file pom.xml

3
.gitignore vendored
View File

@ -1,2 +1,5 @@
/bin/
/target/
.classpath
.project
.settings/

View File

@ -1,23 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>jwudtool</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.m2e.core.maven2Builder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.m2e.core.maven2Nature</nature>
</natures>
</projectDescription>

View File

@ -1,20 +1,22 @@
#JWUDTool 0.1
# JWUDTool
Here is just a simple program that uses the (http://gbatemp.net/threads/jnuslib-java-nus-library.452954/).
The usage should be pretty self explaining.
**STILL EXPERIMENTAL. Bugs may occur, please report them!**
##Features
## Features
* Compressing .wud and splitted wud files into .wux
* Decompressing a .wux back to .wud
* Extracting titles from the GI or GM partition
* Extracting .app/-h3/.tmd/.cert/.tik files from a .wud/.wux or splitted .wud
* Exctracting just the contents/hashes/ticket.
* Decrypting the full game partition from a .wud/.wux or splitted .wud
* Decrypting specific files the game partition from a .wud/.wux or splitted .wud
* Extracting just the contents/hashes/ticket.
* Decrypting the full partitions from a .wud/.wux or splitted .wud
* Decrypting specific files any partition from a .wud/.wux or splitted .wud
* Verify a image / Compare two images (for example a .wud with .wux to make sure its legit)
##Usage
## Usage
Optional:
- Copy the common.key into the folder next to the .jar or provide the key via the command line
@ -24,7 +26,9 @@ Optional:
usage:
-commonkey <WiiU common key> Optional. HexString. Will be used if no "common.key" in the
folder of this .jar is found
-dev Required when using discs without a titlekey.
-compress Compresses the input to a .wux file.
-decompress Decompresses the input to a .wud file.
-decrypt Decrypts full the game partition of the given wud.
-decryptFile <regular expression> Decrypts files of the game partition that match the regular
expression of the given wud.
@ -32,18 +36,85 @@ usage:
(Arguments optional)
-help shows this text
-in <input file> Input file. Can be a .wux, .wud or a game_part1.wud
-noVerify Disables verification after compressing
-noVerify Disables verification after (de)compressing
-out <output path> The path where the result will be saved
-overwrite Optional. Overwrites existing files
-titlekey <WUD title key> Optional. HexString. Will be used if no "game.key" in the
-titleKey <WUD title key> Optional. HexString. Will be used if no "game.key" in the
folder of the wud image is found
-verify <wudimage1|wudimage2> Compares two WUD images to find differences
```
##Compiling
Add the "jnuslib.jar" into the library path and load the other dependicies throuugh maven.
# Examples
## Getting .app files from an Wii U Image:
### Extract .app etc. from a WUD:
Get .app files from "game.wud" to the folder "extracted" with game.key in the same folder
```
java -jar JWUDTool.jar -in "game.wud" -out "extracted" -extract all
```
##Credits
### Extract .app etc. from a WUX (compressed WUD):
Get .app files from "game.wux" to the folder "extracted" with game.key in the same folder
```
java -jar JWUDTool.jar -in "game.wux" -out "extracted" -extract all
```
### Extract .app etc. from a splitted WUD (dump with wudump):
Get .app files from "game_part1.wud" to the folder "extracted" with game.key in the same folder
```
java -jar JWUDTool.jar -in "game_part1.wud" -out "extracted" -extract all
```
## Compressing into .wux examples:
### Compress a .wud to .wux:[/B]
Compress a "game.wud" to "game.wux"
```
java -jar JWUDTool.jar -in "game.wud" -compress
```
### Compress a splitted game_part1.wud to .wux:
Compress a "game_part1.wud" from a wudump dump to "game.wux"
```
java -jar JWUDTool.jar -in "game_part1.wud" -compress
```
## Decryption game files examples:
### Decrypt a WUD image to game files
Input can be a .wud, game_part1.wud or a .wux. This decrypted the full game partition.
Given a game.key and common.key in the same folder.
```
java -jar JWUDTool.jar -in "game.wud" -decrypt //WUD
java -jar JWUDTool.jar -in "game.wux" -decrypt //WUX
java -jar JWUDTool.jar -in "game_part1.wud" -decrypt //game_part1
```
### Decrypt a single file from an WUD
Input can be a .wud, game_part1.wud or a .wux. This decrypted the full game partition.
Given a game.key and common.key in the same folder.
Extracting the code/app.xml file.
```
java -jar JWUDTool.jar -in "game.wud" -decryptFile /code/app.xml
java -jar JWUDTool.jar -in "game.wux" -decryptFile /code/app.xml
java -jar JWUDTool.jar -in "game_part1.wud" -decryptFile /code/app.xml
```
Extracting all .bfstm files.
```
java -jar JWUDTool.jar -in "game.wud" -decryptFile /.*.bfstm
java -jar JWUDTool.jar -in "game.wux" -decryptFile /.*.bfstm
java -jar JWUDTool.jar -in "game_part1.wud" -decryptFile /.*.bfstm
```
Extracting the folder /content/Sound
```
java -jar JWUDTool.jar -in "game.wud" -decryptFile /content/Sound/.*
java -jar JWUDTool.jar -in "game.wux" -decryptFile /content/Sound/.*
java -jar JWUDTool.jar -in "game_part1.wud" -decryptFile /content/Sound/.*
```
## Compiling
`mvn clean package`
## Credits
Maschell
Thanks to:

Binary file not shown.

124
pom.xml
View File

@ -1,27 +1,101 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>de.mas</groupId>
<artifactId>jwudtool</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.12</version>
<scope>provided</scope>
</dependency>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>de.mas</groupId>
<artifactId>jwudtool</artifactId>
<version>0.5</version>
<profiles>
<profile>
<id>normal-build</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<jar_dir>./target</jar_dir>
</properties>
</profile>
<profile>
<id>ci-build</id>
<activation>
<property>
<name>ci-build</name>
<value>true</value>
</property>
</activation>
<properties>
<jar_dir>./ci</jar_dir>
</properties>
</profile>
</profiles>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<id>make-assembly</id>
<configuration>
<archive>
<manifest>
<mainClass>de.mas.jwudtool.Main</mainClass>
</manifest>
</archive>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<!-- Stick jar in root dir, if you want -->
<outputDirectory>${jar_dir}</outputDirectory>
<finalName>JWUDTool-${project.version}-nightly</finalName>
</configuration>
<phase>package</phase>
<goals>
<goal>attached</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>jitpack.io</id> <!-- JitPack allows github repo to be used as a maven repo -->
<url>https://jitpack.io</url> <!-- For documentation: http://jitpack.io/ -->
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>com.github.Maschell</groupId>
<artifactId>JNUSLib</artifactId>
<version>822cf2d</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.4</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>commons-cli</groupId>
<artifactId>commons-cli</artifactId>
<version>1.4</version>
</dependency>
</dependencies>
<dependency>
<groupId>commons-cli</groupId>
<artifactId>commons-cli</artifactId>
<version>1.3.1</version>
</dependency>
</dependencies>
</project>

View File

@ -3,6 +3,10 @@ package de.mas.jwudtool;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.TreeSet;
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
@ -13,139 +17,173 @@ import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;
import org.apache.commons.cli.UnrecognizedOptionException;
import de.mas.jnus.lib.DecryptionService;
import de.mas.jnus.lib.ExtractionService;
import de.mas.jnus.lib.NUSTitle;
import de.mas.jnus.lib.NUSTitleLoaderWUD;
import de.mas.jnus.lib.Settings;
import de.mas.jnus.lib.WUDService;
import de.mas.jnus.lib.implementations.wud.WUDImage;
import de.mas.jnus.lib.utils.Utils;
import de.mas.wiiu.jnus.DecryptionService;
import de.mas.wiiu.jnus.ExtractionService;
import de.mas.wiiu.jnus.NUSTitle;
import de.mas.wiiu.jnus.WUDLoader;
import de.mas.wiiu.jnus.WUDService;
import de.mas.wiiu.jnus.implementations.wud.WUDImage;
import de.mas.wiiu.jnus.implementations.wud.WiiUDisc;
import de.mas.wiiu.jnus.interfaces.FSTDataProvider;
import de.mas.wiiu.jnus.utils.Utils;
import lombok.val;
public class Main {
private final static String OPTION_IN = "in";
private final static String OPTION_HELP = "help";
private static final String OPTION_OUT = "out";
private static final String OPTION_DECOMPRESS = "decompress";
private static final String OPTION_COMPRESS = "compress";
private static final String OPTION_COMMON_KEY = "commonkey";
private static final String OPTION_NO_VERIFY = "noVerify";
private static final String OPTION_VERIFY = "verify";
private static final String OPTION_OVERWRITE = "overwrite";
private static final String OPTION_DECRYPT = "decrypt";
private static final String OPTION_TITLEKEY = "titleKey";
private static final String OPTION_DECRYPT_FILE = "decryptFile";
private static final String OPTION_EXTRACT = "extract";
private static final String OPTION_DEVMODE = "dev";
private static byte[] commonKey;
private static final String HOMEPATH = System.getProperty("user.home") + File.separator + ".wiiu";
public static void main(String[] args) throws Exception {
System.out.println("JWUDTool 0.1b - Maschell");
System.out.println("JWUDTool 0.4 - Maschell");
System.out.println();
Options options = getOptions();
if (args.length == 0) {
showHelp(options);
return;
}
CommandLineParser parser = new DefaultParser();
CommandLine cmd = null;
try{
cmd = parser.parse(options, args);
}catch(MissingArgumentException e){
CommandLine cmd;
try {
cmd = parser.parse(options, args);
} catch (MissingArgumentException e) {
System.out.println(e.getMessage());
return ;
}catch(UnrecognizedOptionException e){
return;
} catch (UnrecognizedOptionException e) {
System.out.println(e.getMessage());
showHelp(options);
return;
}
String input = null;
String output = null;
boolean overwrite = false;
boolean devMode = false;
byte[] titlekey = null;
readKey();
if(cmd.hasOption("help")){
if (cmd.hasOption(OPTION_HELP)) {
showHelp(options);
return;
}
if(cmd.hasOption("in")){
input = cmd.getOptionValue("in");
if (cmd.hasOption(OPTION_DEVMODE)) {
devMode = true;
}
if(cmd.hasOption("out")){
output = cmd.getOptionValue("out");
readKey(new File(HOMEPATH + File.separator + (devMode ? "devcommon.key" : "common.key"))).ifPresent(key -> Main.commonKey = key);
readKey(new File("common.key")).ifPresent(key -> Main.commonKey = key);
if (cmd.hasOption(OPTION_IN)) {
input = cmd.getOptionValue(OPTION_IN);
}
if(cmd.hasOption("commonkey")){
String commonKey = cmd.getOptionValue("commonkey");
if (cmd.hasOption(OPTION_OUT)) {
output = cmd.getOptionValue(OPTION_OUT);
}
if (cmd.hasOption(OPTION_COMMON_KEY)) {
String commonKey = cmd.getOptionValue(OPTION_COMMON_KEY);
byte[] key = Utils.StringToByteArray(commonKey);
if(key.length == 0x10){
Settings.commonKey = key;
if (key.length == 0x10) {
Main.commonKey = key;
System.out.println("Commonkey was set to: " + Utils.ByteArrayToString(key));
}
}
if(cmd.hasOption("titlekey")){
String titlekey_string = cmd.getOptionValue("titlekey");
if (cmd.hasOption(OPTION_TITLEKEY)) {
String titlekey_string = cmd.getOptionValue(OPTION_TITLEKEY);
titlekey = Utils.StringToByteArray(titlekey_string);
if(titlekey.length != 0x10){
titlekey = null;
}else{
if (titlekey.length != 0x10) {
titlekey = null;
} else {
System.out.println("Titlekey was set to: " + Utils.ByteArrayToString(titlekey));
}
}
if(cmd.hasOption("overwrite")){
if (cmd.hasOption(OPTION_OVERWRITE)) {
overwrite = true;
}
if(cmd.hasOption("compress")){
if (cmd.hasOption(OPTION_COMPRESS)) {
boolean verify = true;
System.out.println("Compressing: " + input);
if(cmd.hasOption("noVerify")){
if (cmd.hasOption(OPTION_NO_VERIFY)) {
System.out.println("Verification disabled.");
verify = false;
}
compressWUD(input,output,verify,overwrite);
compressDecompressWUD(input, output, verify, overwrite, false);
return;
}else if(cmd.hasOption("verify")){
} else if (cmd.hasOption(OPTION_DECOMPRESS)) {
boolean verify = true;
System.out.println("Decompressing: " + input);
if (cmd.hasOption(OPTION_NO_VERIFY)) {
System.out.println("Verification disabled.");
verify = false;
}
compressDecompressWUD(input, output, verify, overwrite, true);
return;
} else if (cmd.hasOption(OPTION_VERIFY)) {
System.out.println("Comparing images.");
String[] verifyArgs = cmd.getOptionValues("verify");
String[] verifyArgs = cmd.getOptionValues(OPTION_VERIFY);
File input1 = new File(verifyArgs[0]);
File input2 = new File(verifyArgs[1]);
verifyImages(input1,input2);
verifyImages(input1, input2);
return;
}else{
if(cmd.hasOption("decrypt")){
} else {
if (cmd.hasOption(OPTION_DECRYPT)) {
System.out.println("Decrypting full game partition.");
decrypt(input,output,overwrite,titlekey);
decrypt(input, output, devMode, overwrite, titlekey);
return;
}else if(cmd.hasOption("decryptFile")){
String regex = cmd.getOptionValue("decryptFile");
System.out.println("Decrypting files matching \"" +regex + "\"");
decryptFile(input,output,regex,overwrite,titlekey);
} else if (cmd.hasOption(OPTION_DECRYPT_FILE)) {
String regex = cmd.getOptionValue(OPTION_DECRYPT_FILE);
System.out.println("Decrypting files matching \"" + regex + "\"");
decryptFile(input, output, regex, devMode, overwrite, titlekey);
return;
}else if(cmd.hasOption("extract")){
} else if (cmd.hasOption(OPTION_EXTRACT)) {
System.out.println("Extracting WUD");
String arg = cmd.getOptionValue("extract");
if(arg == null){
String arg = cmd.getOptionValue(OPTION_EXTRACT);
if (arg == null) {
arg = "all";
}
extract(input,output,overwrite,titlekey,arg);
extract(input, output, devMode, overwrite, titlekey, arg);
return;
}
}
System.out.println("Done!");
}
private static void extract(String input, String output, boolean overwrite, byte[] titlekey, String arg) throws Exception {
if(input == null){
private static void extract(String input, String output, boolean devMode, boolean overwrite, byte[] titlekey, String arg) throws Exception {
if (input == null) {
System.out.println("You need to provide an input file");
}
boolean extractAll = false;
boolean extractContent = false;
boolean extractTicket = false;
boolean extractHashes = false;
switch(arg){
switch (arg) {
case "all":
extractAll = true;
break;
@ -161,152 +199,193 @@ public class Main {
default:
System.out.println("Argument not found:" + arg);
return;
}
assert input != null;
File inputFile = new File(input);
System.out.println("Extracting: " + inputFile.getAbsolutePath());
NUSTitle title = NUSTitleLoaderWUD.loadNUSTitle(inputFile.getAbsolutePath(),titlekey);
if(title == null){
return;
WiiUDisc wudInfo;
if (!devMode) {
wudInfo = WUDLoader.load(inputFile.getAbsolutePath(), titlekey);
} else {
wudInfo = WUDLoader.loadDev(inputFile.getAbsolutePath());
}
if(output == null){
output = String.format("%016X", title.getTMD().getTitleID());
}else{
output += File.separator + String.format("%016X", title.getTMD().getTitleID());
List<NUSTitle> titles = WUDLoader.getGamePartionsAsNUSTitles(wudInfo, Main.commonKey);
System.out.println("Found " + titles.size() + " titles on the Disc.");
for (val title : titles) {
String newOutput = output;
System.out.println("Extract files of Title " + String.format("%016X", title.getTMD().getTitleID()));
if (newOutput == null) {
newOutput = String.format("%016X", title.getTMD().getTitleID());
} else {
newOutput += File.separator + String.format("%016X", title.getTMD().getTitleID());
}
File outputFolder = new File(newOutput);
System.out.println("To the folder: " + outputFolder.getAbsolutePath());
ExtractionService extractionService = ExtractionService.getInstance(title);
if (extractAll) {
extractionService.extractAll(outputFolder.getAbsolutePath());
} else if (extractTicket) {
extractionService.extractTicketTo(outputFolder.getAbsolutePath());
} else if (extractContent) {
extractionService.extractAllEncryptedContentFilesWithoutHashesTo(outputFolder.getAbsolutePath());
} else if (extractHashes) {
extractionService.extractAllEncrpytedContentFileHashes(outputFolder.getAbsolutePath());
}
System.out.println("Extraction done!");
}
File outputFolder = new File(output);
System.out.println("To the folder: " + outputFolder.getAbsolutePath());
ExtractionService extractionService = ExtractionService.getInstance(title);
if(extractAll){
extractionService.extractAll(outputFolder.getAbsolutePath());
}else if(extractTicket){
extractionService.extractTicketTo(outputFolder.getAbsolutePath());
}else if(extractContent){
extractionService.extractAllEncryptedContentFilesWithoutHashesTo(outputFolder.getAbsolutePath());
}else if(extractHashes){
extractionService.extractAllEncrpytedContentFileHashes(outputFolder.getAbsolutePath());
}
System.out.println("Extraction done!");
}
private static void decryptFile(String input, String output, String regex, boolean overwrite, byte[] titlekey) throws Exception {
if(input == null){
private static void decryptFile(String input, String output, String regex, boolean devMode, boolean overwrite, byte[] titlekey) throws Exception {
if (input == null) {
System.out.println("You need to provide an input file");
}
File inputFile = new File(input);
System.out.println("Decrypting: " + inputFile.getAbsolutePath());
NUSTitle title = NUSTitleLoaderWUD.loadNUSTitle(inputFile.getAbsolutePath(),titlekey);
if(title == null){
return;
}
if(output == null){
output = String.format("%016X", title.getTMD().getTitleID());
}else{
output += File.separator + String.format("%016X", title.getTMD().getTitleID());
File inputFile = new File(input);
System.out.println("Decrypting: " + inputFile.getAbsolutePath());
WiiUDisc wudInfo;
if (!devMode) {
wudInfo = WUDLoader.load(inputFile.getAbsolutePath(), titlekey);
} else {
wudInfo = WUDLoader.loadDev(inputFile.getAbsolutePath());
}
List<FSTDataProvider> partitions = WUDLoader.getPartitonsAsFSTDataProvider(wudInfo, Main.commonKey);
System.out.println("Found " + partitions.size() + " titles on the Disc.");
Set<String> paritionNames = new TreeSet<>();
for (val dp : partitions) {
String partitionName = dp.getName();
int i = 0;
while (paritionNames.contains(partitionName)) {
partitionName = dp.getName() + "_" + i++;
}
paritionNames.add(partitionName);
String newOutput = output;
System.out.println("Decrypting files in partition " + partitionName);
if (newOutput == null) {
newOutput = partitionName;
} else {
newOutput += File.separator + partitionName;
}
File outputFolder = new File(newOutput);
System.out.println("To the folder: " + outputFolder.getAbsolutePath());
DecryptionService decryption = DecryptionService.getInstance(dp);
decryption.decryptFSTEntriesTo(regex, outputFolder.getAbsolutePath(), !overwrite);
}
File outputFolder = new File(output);
System.out.println("To the folder: " + outputFolder.getAbsolutePath());
title.setSkipExistingFiles(!overwrite);
DecryptionService decryption = DecryptionService.getInstance(title);
decryption.decryptFSTEntriesTo(regex,outputFolder.getAbsolutePath());
System.out.println("Decryption done");
}
private static void decrypt(String input,String output, boolean overwrite,byte[] titlekey) throws Exception {
decryptFile(input,output,".*",overwrite,titlekey);
private static void decrypt(String input, String output, boolean devMode, boolean overwrite, byte[] titlekey) throws Exception {
decryptFile(input, output, ".*", devMode, overwrite, titlekey);
}
private static void verifyImages(File input1, File input2) throws IOException {
System.out.println("Input 1: " +input1.getAbsolutePath());
System.out.println("Input 1: " + input1.getAbsolutePath());
WUDImage image1 = new WUDImage(input1);
System.out.println("Input 2: " +input2.getAbsolutePath());
System.out.println("Input 2: " + input2.getAbsolutePath());
WUDImage image2 = new WUDImage(input2);
if(WUDService.compareWUDImage(image1, image2)){
if (WUDService.compareWUDImage(image1, image2)) {
System.out.println("Both images have the same data");
}else{
} else {
System.out.println("The images are different!");
}
}
}
private static void compressWUD(String input,String output, boolean verify, boolean overwrite) throws IOException {
if(input == null){
System.out.println("-in null");
private static void compressDecompressWUD(String input, String output, boolean verify, boolean overwrite, boolean decompress) throws IOException {
if (input == null) {
System.out.println("-" + OPTION_IN + " was null");
return;
}
File inputImage = new File(input);
if(inputImage.isDirectory() || !inputImage.exists()){
if (inputImage.isDirectory() || !inputImage.exists()) {
System.out.println(inputImage.getAbsolutePath() + " is no file or does not exist");
return;
}
System.out.println("Parsing WUD image.");
WUDImage image = new WUDImage(inputImage);
File compressedImage = WUDService.compressWUDToWUX(image, output,overwrite);
if(compressedImage != null){
System.out.println("Compression successful!");
Optional<File> outputFile;
if (!decompress) {
outputFile = WUDService.compressWUDToWUX(image, output, overwrite);
if (outputFile.isPresent()) {
System.out.println("Compression successful!");
}
} else {
outputFile = WUDService.decompressWUX(image, output, overwrite);
if (outputFile.isPresent()) {
System.out.println("Decompression successful!");
}
}
if(verify){
if(compressedImage != null){
WUDImage image2 = new WUDImage(compressedImage);
if(WUDService.compareWUDImage(image, image2)){
if (verify) {
if (outputFile.isPresent()) {
WUDImage image2 = new WUDImage(outputFile.get());
if (WUDService.compareWUDImage(image, image2)) {
System.out.println("Compressed files is valid.");
}else{
System.out.println("Warning! Compressed file in INVALID!");
} else {
System.out.println("Warning! (De)Compressed file is INVALID!");
}
}
}else{
} else {
System.out.println("Verfication skipped");
}
}
private static void readKey() throws IOException {
File file = new File("common.key");
if(file.isFile()){
byte[] key = Files.readAllBytes(file.toPath());
Settings.commonKey = key;
System.out.println("Commonkey was set to: " + Utils.ByteArrayToString(key));
private static Optional<byte[]> readKey(File file) {
if (file.isFile()) {
byte[] key;
try {
key = Files.readAllBytes(file.toPath());
if (key.length == 16) {
return Optional.of(key);
}
} catch (IOException ignored) {
}
}
return Optional.empty();
}
private static Options getOptions() { // TODO: schöner machen?
private static Options getOptions() {
Options options = new Options();
options.addOption(Option.builder("in").argName("input file").hasArg().desc("Input file. Can be a .wux, .wud or a game_part1.wud").build());
options.addOption(Option.builder("out").argName("output path").hasArg().desc("The path where the result will be saved").build());
options.addOption(Option.builder("compress").desc("Compresses the input to a .wux file.").build());
options.addOption(Option.builder("noVerify").desc("Disables verification after compressing").build());
options.addOption(Option.builder("verify").argName("wudimage1|wudimage2").hasArg().numberOfArgs(2).desc("Compares two WUD images to find differences").build());
options.addOption(Option.builder("overwrite").desc("Optional. Overwrites existing files").build());
options.addOption(Option.builder("commonkey").argName("WiiU common key").hasArg().desc("Optional. HexString. Will be used if no \"common.key\" in the folder of this .jar is found").build());
options.addOption(Option.builder("decrypt").desc("Decrypts full the game partition of the given wud.").build());
options.addOption(Option.builder("titlekey").argName("WUD title key").hasArg().desc("Optional. HexString. Will be used if no \"game.key\" in the folder of the wud image is found").build());
options.addOption(Option.builder("decryptFile").argName("regular expression").hasArg().desc("Decrypts full the game partition of the given wud.").build());
options.addOption(Option.builder("extract").argName("all|content|ticket|hashes").hasArg().optionalArg(true).desc("Extracts files from the game partition of the given wud (Arguments optional)").build());
options.addOption("help", false, "shows this text");
options.addOption(Option.builder(OPTION_IN).argName("input file").hasArg().desc("Input file. Can be a .wux, .wud or a game_part1.wud").build());
options.addOption(Option.builder(OPTION_OUT).argName("output path").hasArg().desc("The path where the result will be saved").build());
options.addOption(Option.builder(OPTION_DEVMODE).argName("dev mode").desc("Allows you to handle Kiosk Discs").build());
options.addOption(Option.builder(OPTION_COMPRESS).desc("Compresses the input to a .wux file.").build());
options.addOption(Option.builder(OPTION_DECOMPRESS).desc("Decompresses the input back to a .wud file.").build());
options.addOption(Option.builder(OPTION_NO_VERIFY).desc("Disables verification after compressing").build());
options.addOption(Option.builder(OPTION_VERIFY).argName("wudimage1|wudimage2").hasArg().numberOfArgs(2)
.desc("Compares two WUD images to find differences").build());
options.addOption(Option.builder(OPTION_OVERWRITE).desc("Optional. Overwrites existing files").build());
options.addOption(Option.builder(OPTION_COMMON_KEY).argName("WiiU common key").hasArg()
.desc("Optional. HexString. Will be used if no \"common.key\" in the folder of this .jar is found").build());
options.addOption(Option.builder(OPTION_DECRYPT).desc("Decrypts full the game partition of the given wud.").build());
options.addOption(Option.builder(OPTION_TITLEKEY).argName("WUD title key").hasArg()
.desc("Optional. HexString. Will be used if no \"game.key\" in the folder of the wud image is found").build());
options.addOption(
Option.builder(OPTION_DECRYPT_FILE).argName("regular expression").hasArg().desc("Decrypts full the game partition of the given wud.").build());
options.addOption(Option.builder(OPTION_EXTRACT).argName("all|content|ticket|hashes").hasArg().optionalArg(true)
.desc("Extracts files from the game partition of the given wud (Arguments optional)").build());
options.addOption(OPTION_HELP, false, "shows this text");
return options;
}
private static void showHelp(Options options) {
HelpFormatter formatter = new HelpFormatter();
formatter.setWidth(100);