mirror of
https://github.com/Maschell/JWUDTool.git
synced 2024-06-02 07:18:46 +02:00
Compare commits
10 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
6a6afff21d | ||
|
1443db9cdf | ||
|
b19809c3b6 | ||
|
51cc20eaf9 | ||
|
92978d4a69 | ||
|
27aad40261 | ||
|
1e4a56cd6f | ||
|
f172151030 | ||
|
f51b9b7282 | ||
|
1c8673b416 |
37
.classpath
37
.classpath
|
@ -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
24
.github/workflows/maven.yml
vendored
Normal 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
3
.gitignore
vendored
|
@ -1,2 +1,5 @@
|
|||
/bin/
|
||||
/target/
|
||||
.classpath
|
||||
.project
|
||||
.settings/
|
||||
|
|
23
.project
23
.project
|
@ -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>
|
12
README.md
12
README.md
|
@ -1,4 +1,4 @@
|
|||
# JWUDTool 0.2
|
||||
# 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.
|
||||
|
@ -9,11 +9,11 @@ The usage should be pretty self explaining.
|
|||
|
||||
* Compressing .wud and splitted wud files into .wux
|
||||
* Decompressing a .wux back to .wud
|
||||
* Extracting from the GI or GM partition
|
||||
* Extracting titles from the GI or GM partition
|
||||
* Extracting .app/-h3/.tmd/.cert/.tik files from a .wud/.wux or splitted .wud
|
||||
* Extracting 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
|
||||
* 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
|
||||
|
@ -39,7 +39,7 @@ usage:
|
|||
-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
|
||||
```
|
||||
|
@ -112,7 +112,7 @@ java -jar JWUDTool.jar -in "game_part1.wud" -decryptFile /content/Sound/.*
|
|||
```
|
||||
|
||||
## Compiling
|
||||
`clean assembly:single package`
|
||||
`mvn clean package`
|
||||
|
||||
## Credits
|
||||
Maschell
|
||||
|
|
68
pom.xml
68
pom.xml
|
@ -4,40 +4,70 @@
|
|||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>de.mas</groupId>
|
||||
<artifactId>jwudtool</artifactId>
|
||||
<version>0.2a</version>
|
||||
<version>0.5</version>
|
||||
|
||||
<properties>
|
||||
<maven.compiler.source>1.8</maven.compiler.source>
|
||||
<maven.compiler.target>1.8</maven.compiler.target>
|
||||
</properties>
|
||||
<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-assembly-plugin</artifactId>
|
||||
<version>3.1.0</version>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<version>3.7.0</version>
|
||||
<configuration>
|
||||
<descriptorRefs>
|
||||
<descriptorRef>jar-with-dependencies</descriptorRef>
|
||||
</descriptorRefs>
|
||||
<archive>
|
||||
<manifest>
|
||||
<mainClass>de.mas.jwudtool.Main</mainClass>
|
||||
</manifest>
|
||||
</archive>
|
||||
<source>1.8</source>
|
||||
<target>1.8</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>make-assembly</id> <!-- this is used for inheritance merges -->
|
||||
<phase>package</phase> <!-- bind to the packaging phase -->
|
||||
<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>single</goal>
|
||||
<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 -->
|
||||
|
@ -49,7 +79,7 @@
|
|||
<dependency>
|
||||
<groupId>com.github.Maschell</groupId>
|
||||
<artifactId>JNUSLib</artifactId>
|
||||
<version>8124bb2</version>
|
||||
<version>822cf2d</version>
|
||||
</dependency>
|
||||
|
||||
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
|
||||
|
|
|
@ -4,6 +4,9 @@ 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;
|
||||
|
@ -17,10 +20,11 @@ import org.apache.commons.cli.UnrecognizedOptionException;
|
|||
import de.mas.wiiu.jnus.DecryptionService;
|
||||
import de.mas.wiiu.jnus.ExtractionService;
|
||||
import de.mas.wiiu.jnus.NUSTitle;
|
||||
import de.mas.wiiu.jnus.NUSTitleLoaderWUD;
|
||||
import de.mas.wiiu.jnus.Settings;
|
||||
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;
|
||||
|
||||
|
@ -39,9 +43,12 @@ public class Main {
|
|||
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.2a - Maschell");
|
||||
System.out.println("JWUDTool 0.4 - Maschell");
|
||||
System.out.println();
|
||||
Options options = getOptions();
|
||||
|
||||
|
@ -51,7 +58,7 @@ public class Main {
|
|||
}
|
||||
|
||||
CommandLineParser parser = new DefaultParser();
|
||||
CommandLine cmd = null;
|
||||
CommandLine cmd;
|
||||
try {
|
||||
cmd = parser.parse(options, args);
|
||||
} catch (MissingArgumentException e) {
|
||||
|
@ -69,13 +76,18 @@ public class Main {
|
|||
boolean devMode = false;
|
||||
byte[] titlekey = null;
|
||||
|
||||
readKey();
|
||||
|
||||
if (cmd.hasOption(OPTION_HELP)) {
|
||||
showHelp(options);
|
||||
return;
|
||||
}
|
||||
|
||||
if (cmd.hasOption(OPTION_DEVMODE)) {
|
||||
devMode = true;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
@ -87,7 +99,7 @@ public class Main {
|
|||
String commonKey = cmd.getOptionValue(OPTION_COMMON_KEY);
|
||||
byte[] key = Utils.StringToByteArray(commonKey);
|
||||
if (key.length == 0x10) {
|
||||
Settings.commonKey = key;
|
||||
Main.commonKey = key;
|
||||
System.out.println("Commonkey was set to: " + Utils.ByteArrayToString(key));
|
||||
}
|
||||
}
|
||||
|
@ -106,10 +118,6 @@ public class Main {
|
|||
overwrite = true;
|
||||
}
|
||||
|
||||
if (cmd.hasOption(OPTION_DEVMODE)) {
|
||||
devMode = true;
|
||||
}
|
||||
|
||||
if (cmd.hasOption(OPTION_COMPRESS)) {
|
||||
boolean verify = true;
|
||||
System.out.println("Compressing: " + input);
|
||||
|
@ -163,6 +171,7 @@ public class Main {
|
|||
return;
|
||||
}
|
||||
}
|
||||
System.out.println("Done!");
|
||||
}
|
||||
|
||||
private static void extract(String input, String output, boolean devMode, boolean overwrite, byte[] titlekey, String arg) throws Exception {
|
||||
|
@ -175,36 +184,36 @@ public class Main {
|
|||
boolean extractHashes = false;
|
||||
|
||||
switch (arg) {
|
||||
case "all":
|
||||
extractAll = true;
|
||||
break;
|
||||
case "content":
|
||||
extractContent = true;
|
||||
break;
|
||||
case "ticket":
|
||||
extractTicket = true;
|
||||
break;
|
||||
case "hashes":
|
||||
extractHashes = true;
|
||||
break;
|
||||
default:
|
||||
System.out.println("Argument not found:" + arg);
|
||||
return;
|
||||
case "all":
|
||||
extractAll = true;
|
||||
break;
|
||||
case "content":
|
||||
extractContent = true;
|
||||
break;
|
||||
case "ticket":
|
||||
extractTicket = true;
|
||||
break;
|
||||
case "hashes":
|
||||
extractHashes = true;
|
||||
break;
|
||||
default:
|
||||
System.out.println("Argument not found:" + arg);
|
||||
return;
|
||||
}
|
||||
|
||||
assert input != null;
|
||||
File inputFile = new File(input);
|
||||
|
||||
System.out.println("Extracting: " + inputFile.getAbsolutePath());
|
||||
|
||||
List<NUSTitle> titles = null;
|
||||
WiiUDisc wudInfo;
|
||||
if (!devMode) {
|
||||
titles = NUSTitleLoaderWUD.loadNUSTitle(inputFile.getAbsolutePath(), titlekey);
|
||||
wudInfo = WUDLoader.load(inputFile.getAbsolutePath(), titlekey);
|
||||
} else {
|
||||
titles = NUSTitleLoaderWUD.loadNUSTitleDev(inputFile.getAbsolutePath());
|
||||
}
|
||||
if (titles == null || titles.isEmpty()) {
|
||||
return;
|
||||
wudInfo = WUDLoader.loadDev(inputFile.getAbsolutePath());
|
||||
}
|
||||
|
||||
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;
|
||||
|
@ -236,39 +245,47 @@ public class Main {
|
|||
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");
|
||||
return;
|
||||
}
|
||||
File inputFile = new File(input);
|
||||
|
||||
System.out.println("Decrypting: " + inputFile.getAbsolutePath());
|
||||
|
||||
List<NUSTitle> titles = null;
|
||||
WiiUDisc wudInfo;
|
||||
if (!devMode) {
|
||||
titles = NUSTitleLoaderWUD.loadNUSTitle(inputFile.getAbsolutePath(), titlekey);
|
||||
wudInfo = WUDLoader.load(inputFile.getAbsolutePath(), titlekey);
|
||||
} else {
|
||||
titles = NUSTitleLoaderWUD.loadNUSTitleDev(inputFile.getAbsolutePath());
|
||||
wudInfo = WUDLoader.loadDev(inputFile.getAbsolutePath());
|
||||
}
|
||||
|
||||
if (titles == null || titles.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
System.out.println("Found " + titles.size() + " titles on the Disc.");
|
||||
|
||||
for (val title : titles) {
|
||||
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 Title " + String.format("%016X", title.getTMD().getTitleID()));
|
||||
System.out.println("Decrypting files in partition " + partitionName);
|
||||
if (newOutput == null) {
|
||||
newOutput = String.format("%016X", title.getTMD().getTitleID());
|
||||
newOutput = partitionName;
|
||||
} else {
|
||||
newOutput += File.separator + String.format("%016X", title.getTMD().getTitleID());
|
||||
newOutput += File.separator + partitionName;
|
||||
}
|
||||
|
||||
File outputFolder = new File(newOutput);
|
||||
|
||||
System.out.println("To the folder: " + outputFolder.getAbsolutePath());
|
||||
title.setSkipExistingFiles(!overwrite);
|
||||
DecryptionService decryption = DecryptionService.getInstance(title);
|
||||
DecryptionService decryption = DecryptionService.getInstance(dp);
|
||||
|
||||
decryption.decryptFSTEntriesTo(regex, outputFolder.getAbsolutePath());
|
||||
decryption.decryptFSTEntriesTo(regex, outputFolder.getAbsolutePath(), !overwrite);
|
||||
}
|
||||
System.out.println("Decryption done");
|
||||
}
|
||||
|
@ -302,22 +319,22 @@ public class Main {
|
|||
}
|
||||
System.out.println("Parsing WUD image.");
|
||||
WUDImage image = new WUDImage(inputImage);
|
||||
File outputFile = null;
|
||||
Optional<File> outputFile;
|
||||
if (!decompress) {
|
||||
outputFile = WUDService.compressWUDToWUX(image, output, overwrite);
|
||||
if (outputFile != null) {
|
||||
if (outputFile.isPresent()) {
|
||||
System.out.println("Compression successful!");
|
||||
}
|
||||
} else {
|
||||
outputFile = WUDService.decompressWUX(image, output, overwrite);
|
||||
if (outputFile != null) {
|
||||
if (outputFile.isPresent()) {
|
||||
System.out.println("Decompression successful!");
|
||||
}
|
||||
}
|
||||
|
||||
if (verify) {
|
||||
if (outputFile != null) {
|
||||
WUDImage image2 = new WUDImage(outputFile);
|
||||
if (outputFile.isPresent()) {
|
||||
WUDImage image2 = new WUDImage(outputFile.get());
|
||||
if (WUDService.compareWUDImage(image, image2)) {
|
||||
System.out.println("Compressed files is valid.");
|
||||
} else {
|
||||
|
@ -329,13 +346,18 @@ public class Main {
|
|||
}
|
||||
}
|
||||
|
||||
private static void readKey() throws IOException {
|
||||
File file = new File("common.key");
|
||||
private static Optional<byte[]> readKey(File file) {
|
||||
if (file.isFile()) {
|
||||
byte[] key = Files.readAllBytes(file.toPath());
|
||||
Settings.commonKey = key;
|
||||
System.out.println("Commonkey was set to: " + Utils.ByteArrayToString(key));
|
||||
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() {
|
||||
|
|
Loading…
Reference in New Issue
Block a user