mirror of
https://github.com/Maschell/JNUSLib.git
synced 2024-11-29 11:24:20 +01:00
The FST and Ticket attribute of the NUSTitle are now optionals
This commit is contained in:
parent
73440af121
commit
7c9fe334a8
@ -18,10 +18,12 @@ package de.mas.wiiu.jnus;
|
|||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.text.ParseException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
@ -34,15 +36,24 @@ import de.mas.wiiu.jnus.entities.fst.FST;
|
|||||||
import de.mas.wiiu.jnus.entities.fst.FSTEntry;
|
import de.mas.wiiu.jnus.entities.fst.FSTEntry;
|
||||||
import de.mas.wiiu.jnus.interfaces.NUSDataProvider;
|
import de.mas.wiiu.jnus.interfaces.NUSDataProvider;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
|
import lombok.NonNull;
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
|
||||||
public class NUSTitle {
|
public class NUSTitle {
|
||||||
@Getter @Setter private FST FST;
|
@Getter @Setter private Optional<FST> FST = Optional.empty();
|
||||||
@Getter @Setter private TMD TMD;
|
@Getter @Setter private Optional<Ticket> ticket;
|
||||||
@Getter @Setter private Ticket ticket;
|
|
||||||
|
@Getter private final TMD TMD;
|
||||||
|
|
||||||
@Getter @Setter private boolean skipExistingFiles = true;
|
@Getter @Setter private boolean skipExistingFiles = true;
|
||||||
@Getter @Setter private NUSDataProvider dataProvider = null;
|
@Getter private final NUSDataProvider dataProvider;
|
||||||
|
|
||||||
|
public NUSTitle(@NonNull NUSDataProvider dataProvider) throws ParseException, IOException {
|
||||||
|
byte[] tmdData = dataProvider.getRawTMD().orElseThrow(() -> new ParseException("No TMD data found", 0));
|
||||||
|
this.TMD = de.mas.wiiu.jnus.entities.TMD.parseTMD(tmdData);
|
||||||
|
this.dataProvider = dataProvider;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public List<FSTEntry> getAllFSTEntriesFlatByContentID(short ID) {
|
public List<FSTEntry> getAllFSTEntriesFlatByContentID(short ID) {
|
||||||
return getFSTEntriesFlatByContent(getTMD().getContentByID((int) ID));
|
return getFSTEntriesFlatByContent(getTMD().getContentByID((int) ID));
|
||||||
@ -65,7 +76,10 @@ public class NUSTitle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Stream<FSTEntry> getAllFSTEntriesAsStream() {
|
public Stream<FSTEntry> getAllFSTEntriesAsStream() {
|
||||||
return getAllFSTEntryChildrenAsStream(FST.getRoot());
|
if (!FST.isPresent()) {
|
||||||
|
return Stream.empty();
|
||||||
|
}
|
||||||
|
return getAllFSTEntryChildrenAsStream(FST.get().getRoot());
|
||||||
}
|
}
|
||||||
|
|
||||||
public Stream<FSTEntry> getAllFSTEntryChildrenAsStream(FSTEntry cur) {
|
public Stream<FSTEntry> getAllFSTEntryChildrenAsStream(FSTEntry cur) {
|
||||||
@ -84,7 +98,11 @@ public class NUSTitle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public List<FSTEntry> getFSTEntriesByRegEx(String regEx) {
|
public List<FSTEntry> getFSTEntriesByRegEx(String regEx) {
|
||||||
return getFSTEntriesByRegEx(regEx, FST.getRoot());
|
if (!FST.isPresent()) {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
return getFSTEntriesByRegEx(regEx, FST.get().getRoot());
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<FSTEntry> getFSTEntriesByRegEx(String regEx, FSTEntry entry) {
|
public List<FSTEntry> getFSTEntriesByRegEx(String regEx, FSTEntry entry) {
|
||||||
@ -92,7 +110,10 @@ public class NUSTitle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public List<FSTEntry> getFSTEntriesByRegEx(String regEx, boolean onlyInPackage) {
|
public List<FSTEntry> getFSTEntriesByRegEx(String regEx, boolean onlyInPackage) {
|
||||||
return getFSTEntriesByRegEx(regEx, FST.getRoot(), onlyInPackage);
|
if (!FST.isPresent()) {
|
||||||
|
return new ArrayList<>();
|
||||||
|
}
|
||||||
|
return getFSTEntriesByRegEx(regEx, FST.get().getRoot(), onlyInPackage);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<FSTEntry> getFSTEntriesByRegEx(String regEx, FSTEntry entry, boolean allowNotInPackage) {
|
public List<FSTEntry> getFSTEntriesByRegEx(String regEx, FSTEntry entry, boolean allowNotInPackage) {
|
||||||
@ -116,15 +137,20 @@ public class NUSTitle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void printFiles() {
|
public void printFiles() {
|
||||||
getFST().getRoot().printRecursive(0);
|
if (FST.isPresent()) {
|
||||||
|
FST.get().getRoot().printRecursive(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void printContentFSTInfos() {
|
public void printContentFSTInfos() {
|
||||||
for (Entry<Integer, ContentFSTInfo> e : getFST().getContentFSTInfos().entrySet()) {
|
if (FST.isPresent()) {
|
||||||
|
for (Entry<Integer, ContentFSTInfo> e : FST.get().getContentFSTInfos().entrySet()) {
|
||||||
System.out.println(String.format("%08X", e.getKey()) + ": " + e.getValue());
|
System.out.println(String.format("%08X", e.getKey()) + ": " + e.getValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public void printContentInfos() {
|
public void printContentInfos() {
|
||||||
for (Entry<Integer, Content> e : getTMD().getAllContents().entrySet()) {
|
for (Entry<Integer, Content> e : getTMD().getAllContents().entrySet()) {
|
||||||
|
|
||||||
|
@ -40,15 +40,9 @@ public class NUSTitleLoader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static NUSTitle loadNusTitle(NUSTitleConfig config, Supplier<NUSDataProvider> dataProviderFunction) throws IOException, ParseException {
|
public static NUSTitle loadNusTitle(NUSTitleConfig config, Supplier<NUSDataProvider> dataProviderFunction) throws IOException, ParseException {
|
||||||
NUSTitle result = new NUSTitle();
|
|
||||||
|
|
||||||
NUSDataProvider dataProvider = dataProviderFunction.get();
|
NUSDataProvider dataProvider = dataProviderFunction.get();
|
||||||
result.setDataProvider(dataProvider);
|
|
||||||
|
|
||||||
byte[] tmdData = dataProvider.getRawTMD().orElseThrow(() -> new ParseException("No TMD data found", 0));
|
NUSTitle result = new NUSTitle(dataProvider);
|
||||||
|
|
||||||
TMD tmd = TMD.parseTMD(tmdData);
|
|
||||||
result.setTMD(tmd);
|
|
||||||
|
|
||||||
if (config.isNoDecryption()) {
|
if (config.isNoDecryption()) {
|
||||||
return result;
|
return result;
|
||||||
@ -64,19 +58,20 @@ public class NUSTitleLoader {
|
|||||||
if (ticket == null) {
|
if (ticket == null) {
|
||||||
new ParseException("Failed to get ticket data", 0);
|
new ParseException("Failed to get ticket data", 0);
|
||||||
}
|
}
|
||||||
result.setTicket(ticket);
|
result.setTicket(Optional.of(ticket));
|
||||||
|
|
||||||
// If we have just content, we don't have a FST.
|
// If we have just content, we don't have a FST.
|
||||||
if (tmd.getAllContents().size() == 1) {
|
if (result.getTMD().getAllContents().size() == 1) {
|
||||||
// The only way to check if the key is right, is by trying to decrypt the whole thing.
|
// The only way to check if the key is right, is by trying to decrypt the whole thing.
|
||||||
FSTDataProvider dp = new FSTDataProviderNUSTitle(result);
|
FSTDataProvider dp = new FSTDataProviderNUSTitle(result);
|
||||||
for (FSTEntry children : dp.getRoot().getChildren()) {
|
for (FSTEntry children : dp.getRoot().getChildren()) {
|
||||||
dp.readFile(children);
|
dp.readFile(children);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
// If we have more than one content, the index 0 is the FST.
|
// If we have more than one content, the index 0 is the FST.
|
||||||
Content fstContent = tmd.getContentByIndex(0);
|
Content fstContent = result.getTMD().getContentByIndex(0);
|
||||||
|
|
||||||
InputStream fstContentEncryptedStream = dataProvider.getInputStreamFromContent(fstContent, 0, Optional.of(fstContent.getEncryptedFileSize()));
|
InputStream fstContentEncryptedStream = dataProvider.getInputStreamFromContent(fstContent, 0, Optional.of(fstContent.getEncryptedFileSize()));
|
||||||
|
|
||||||
@ -90,10 +85,10 @@ public class NUSTitleLoader {
|
|||||||
fstBytes = aesDecryption.decrypt(fstBytes);
|
fstBytes = aesDecryption.decrypt(fstBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<Integer, Content> contents = tmd.getAllContents();
|
Map<Integer, Content> contents = result.getTMD().getAllContents();
|
||||||
|
|
||||||
FST fst = FST.parseFST(fstBytes, contents);
|
FST fst = FST.parseFST(fstBytes, contents);
|
||||||
result.setFST(fst);
|
result.setFST(Optional.of(fst));
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,6 @@ import java.util.stream.Collectors;
|
|||||||
|
|
||||||
import de.mas.wiiu.jnus.NUSTitle;
|
import de.mas.wiiu.jnus.NUSTitle;
|
||||||
import de.mas.wiiu.jnus.entities.content.Content;
|
import de.mas.wiiu.jnus.entities.content.Content;
|
||||||
import de.mas.wiiu.jnus.entities.fst.FST;
|
|
||||||
import de.mas.wiiu.jnus.entities.fst.FSTEntry;
|
import de.mas.wiiu.jnus.entities.fst.FSTEntry;
|
||||||
import de.mas.wiiu.jnus.interfaces.FSTDataProvider;
|
import de.mas.wiiu.jnus.interfaces.FSTDataProvider;
|
||||||
import de.mas.wiiu.jnus.interfaces.HasNUSTitle;
|
import de.mas.wiiu.jnus.interfaces.HasNUSTitle;
|
||||||
@ -47,9 +46,8 @@ public class FSTDataProviderNUSTitle implements FSTDataProvider, HasNUSTitle {
|
|||||||
this.title = title;
|
this.title = title;
|
||||||
this.name = String.format("%016X", title.getTMD().getTitleID());
|
this.name = String.format("%016X", title.getTMD().getTitleID());
|
||||||
|
|
||||||
FST fst = title.getFST();
|
if (title.getFST().isPresent()) {
|
||||||
if (fst != null) {
|
rootEntry = title.getFST().get().getRoot();
|
||||||
rootEntry = title.getFST().getRoot();
|
|
||||||
} else if (title.getTMD().getContentCount() == 1) {
|
} else if (title.getTMD().getContentCount() == 1) {
|
||||||
// If the tmd has only one content file, it has not FST. We have to create our own FST.
|
// If the tmd has only one content file, it has not FST. We have to create our own FST.
|
||||||
Content c = title.getTMD().getAllContents().values().stream().collect(Collectors.toList()).get(0);
|
Content c = title.getTMD().getAllContents().values().stream().collect(Collectors.toList()).get(0);
|
||||||
@ -99,7 +97,15 @@ public class FSTDataProviderNUSTitle implements FSTDataProvider, HasNUSTitle {
|
|||||||
|
|
||||||
private boolean decryptFSTEntryToStream(FSTEntry entry, OutputStream outputStream, long fileOffsetBlock, long fileOffset, long fileSize)
|
private boolean decryptFSTEntryToStream(FSTEntry entry, OutputStream outputStream, long fileOffsetBlock, long fileOffset, long fileSize)
|
||||||
throws IOException, CheckSumWrongException {
|
throws IOException, CheckSumWrongException {
|
||||||
if (entry.isNotInPackage() || entry.getContent() == null) {
|
if (entry.isNotInPackage() || entry.getContent() == null || !title.getTicket().isPresent()) {
|
||||||
|
if (!title.getTicket().isPresent()) {
|
||||||
|
log.info("Decryption not possible because no ticket was set.");
|
||||||
|
}else if(entry.isNotInPackage()) {
|
||||||
|
log.info("Decryption not possible because the FSTEntry is not in this package");
|
||||||
|
}else if(entry.getContent() == null) {
|
||||||
|
// TODO: convert it to an Optional
|
||||||
|
log.info("Decryption not possible because the Content was null");
|
||||||
|
}
|
||||||
outputStream.close();
|
outputStream.close();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -111,7 +117,7 @@ public class FSTDataProviderNUSTitle implements FSTDataProvider, HasNUSTitle {
|
|||||||
InputStream in = dataProvider.getInputStreamFromContent(c, fileOffsetBlock);
|
InputStream in = dataProvider.getInputStreamFromContent(c, fileOffsetBlock);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
NUSDecryption nusdecryption = new NUSDecryption(title.getTicket());
|
NUSDecryption nusdecryption = new NUSDecryption(title.getTicket().get());
|
||||||
Optional<byte[]> h3HashedOpt = Optional.empty();
|
Optional<byte[]> h3HashedOpt = Optional.empty();
|
||||||
if (c.isHashed()) {
|
if (c.isHashed()) {
|
||||||
h3HashedOpt = dataProvider.getContentH3Hash(c);
|
h3HashedOpt = dataProvider.getContentH3Hash(c);
|
||||||
|
Loading…
Reference in New Issue
Block a user