diff --git a/src/de/mas/wiiu/jnus/NUSTitle.java b/src/de/mas/wiiu/jnus/NUSTitle.java index b6a60a5..6d67e26 100644 --- a/src/de/mas/wiiu/jnus/NUSTitle.java +++ b/src/de/mas/wiiu/jnus/NUSTitle.java @@ -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 = Optional.empty(); + @Getter @Setter private Optional 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 getAllFSTEntriesFlatByContentID(short ID) { return getFSTEntriesFlatByContent(getTMD().getContentByID((int) ID)); @@ -65,7 +76,10 @@ public class NUSTitle { } public Stream getAllFSTEntriesAsStream() { - return getAllFSTEntryChildrenAsStream(FST.getRoot()); + if (!FST.isPresent()) { + return Stream.empty(); + } + return getAllFSTEntryChildrenAsStream(FST.get().getRoot()); } public Stream getAllFSTEntryChildrenAsStream(FSTEntry cur) { @@ -84,7 +98,11 @@ public class NUSTitle { } public List getFSTEntriesByRegEx(String regEx) { - return getFSTEntriesByRegEx(regEx, FST.getRoot()); + if (!FST.isPresent()) { + return new ArrayList<>(); + } + + return getFSTEntriesByRegEx(regEx, FST.get().getRoot()); } public List getFSTEntriesByRegEx(String regEx, FSTEntry entry) { @@ -92,7 +110,10 @@ public class NUSTitle { } public List 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 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 e : getFST().getContentFSTInfos().entrySet()) { - System.out.println(String.format("%08X", e.getKey()) + ": " + e.getValue()); + if (FST.isPresent()) { + for (Entry e : FST.get().getContentFSTInfos().entrySet()) { + System.out.println(String.format("%08X", e.getKey()) + ": " + e.getValue()); + } } + } public void printContentInfos() { diff --git a/src/de/mas/wiiu/jnus/NUSTitleLoader.java b/src/de/mas/wiiu/jnus/NUSTitleLoader.java index 9ed5a20..03fb1e4 100644 --- a/src/de/mas/wiiu/jnus/NUSTitleLoader.java +++ b/src/de/mas/wiiu/jnus/NUSTitleLoader.java @@ -40,15 +40,9 @@ public class NUSTitleLoader { } public static NUSTitle loadNusTitle(NUSTitleConfig config, Supplier 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 contents = tmd.getAllContents(); + Map contents = result.getTMD().getAllContents(); FST fst = FST.parseFST(fstBytes, contents); - result.setFST(fst); + result.setFST(Optional.of(fst)); return result; } diff --git a/src/de/mas/wiiu/jnus/implementations/FSTDataProviderNUSTitle.java b/src/de/mas/wiiu/jnus/implementations/FSTDataProviderNUSTitle.java index 94e8871..ed34724 100644 --- a/src/de/mas/wiiu/jnus/implementations/FSTDataProviderNUSTitle.java +++ b/src/de/mas/wiiu/jnus/implementations/FSTDataProviderNUSTitle.java @@ -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 h3HashedOpt = Optional.empty(); if (c.isHashed()) { h3HashedOpt = dataProvider.getContentH3Hash(c);