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.IOException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
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.interfaces.NUSDataProvider;
import lombok.Getter;
import lombok.NonNull;
import lombok.Setter;
public class NUSTitle {
@Getter @Setter private FST FST;
@Getter @Setter private TMD TMD;
@Getter @Setter private Ticket ticket;
@Getter @Setter private Optional<FST> FST = Optional.empty();
@Getter @Setter private Optional<Ticket> ticket;
@Getter private final TMD TMD;
@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) {
return getFSTEntriesFlatByContent(getTMD().getContentByID((int) ID));
@ -65,7 +76,10 @@ public class NUSTitle {
}
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) {
@ -84,7 +98,11 @@ public class NUSTitle {
}
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) {
@ -92,7 +110,10 @@ public class NUSTitle {
}
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) {
@ -116,13 +137,18 @@ public class NUSTitle {
}
public void printFiles() {
getFST().getRoot().printRecursive(0);
if (FST.isPresent()) {
FST.get().getRoot().printRecursive(0);
}
}
public void printContentFSTInfos() {
for (Entry<Integer, ContentFSTInfo> e : getFST().getContentFSTInfos().entrySet()) {
System.out.println(String.format("%08X", e.getKey()) + ": " + e.getValue());
if (FST.isPresent()) {
for (Entry<Integer, ContentFSTInfo> e : FST.get().getContentFSTInfos().entrySet()) {
System.out.println(String.format("%08X", e.getKey()) + ": " + e.getValue());
}
}
}
public void printContentInfos() {

View File

@ -40,15 +40,9 @@ public class NUSTitleLoader {
}
public static NUSTitle loadNusTitle(NUSTitleConfig config, Supplier<NUSDataProvider> dataProviderFunction) throws IOException, ParseException {
NUSTitle result = new NUSTitle();
NUSDataProvider dataProvider = dataProviderFunction.get();
result.setDataProvider(dataProvider);
byte[] tmdData = dataProvider.getRawTMD().orElseThrow(() -> new ParseException("No TMD data found", 0));
TMD tmd = TMD.parseTMD(tmdData);
result.setTMD(tmd);
NUSTitle result = new NUSTitle(dataProvider);
if (config.isNoDecryption()) {
return result;
@ -64,19 +58,20 @@ public class NUSTitleLoader {
if (ticket == null) {
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 (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.
FSTDataProvider dp = new FSTDataProviderNUSTitle(result);
for (FSTEntry children : dp.getRoot().getChildren()) {
dp.readFile(children);
}
return result;
}
// 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()));
@ -90,10 +85,10 @@ public class NUSTitleLoader {
fstBytes = aesDecryption.decrypt(fstBytes);
}
Map<Integer, Content> contents = tmd.getAllContents();
Map<Integer, Content> contents = result.getTMD().getAllContents();
FST fst = FST.parseFST(fstBytes, contents);
result.setFST(fst);
result.setFST(Optional.of(fst));
return result;
}

View File

@ -25,7 +25,6 @@ import java.util.stream.Collectors;
import de.mas.wiiu.jnus.NUSTitle;
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.interfaces.FSTDataProvider;
import de.mas.wiiu.jnus.interfaces.HasNUSTitle;
@ -47,9 +46,8 @@ public class FSTDataProviderNUSTitle implements FSTDataProvider, HasNUSTitle {
this.title = title;
this.name = String.format("%016X", title.getTMD().getTitleID());
FST fst = title.getFST();
if (fst != null) {
rootEntry = title.getFST().getRoot();
if (title.getFST().isPresent()) {
rootEntry = title.getFST().get().getRoot();
} 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.
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)
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();
return false;
}
@ -111,7 +117,7 @@ public class FSTDataProviderNUSTitle implements FSTDataProvider, HasNUSTitle {
InputStream in = dataProvider.getInputStreamFromContent(c, fileOffsetBlock);
try {
NUSDecryption nusdecryption = new NUSDecryption(title.getTicket());
NUSDecryption nusdecryption = new NUSDecryption(title.getTicket().get());
Optional<byte[]> h3HashedOpt = Optional.empty();
if (c.isHashed()) {
h3HashedOpt = dataProvider.getContentH3Hash(c);