The FST and Ticket attribute of the NUSTitle are now optionals

This commit is contained in:
Maschell 2019-04-18 11:18:32 +02:00
parent 73440af121
commit 7c9fe334a8
3 changed files with 55 additions and 28 deletions

View File

@ -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,13 +137,18 @@ 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()) {
System.out.println(String.format("%08X", e.getKey()) + ": " + e.getValue()); for (Entry<Integer, ContentFSTInfo> e : FST.get().getContentFSTInfos().entrySet()) {
System.out.println(String.format("%08X", e.getKey()) + ": " + e.getValue());
}
} }
} }
public void printContentInfos() { public void printContentInfos() {

View File

@ -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;
} }

View File

@ -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);