- Removed the NUSTitlerLoaderWUD

- Instead of just parsing the GI partitions, parse now all non-GM partition as "WUDDataPartition".
- Simplified and optimized the WUDInfoParser
This commit is contained in:
Maschell 2019-04-10 18:52:01 +02:00
parent 422c36dde3
commit 2913ccf8b2
7 changed files with 71 additions and 388 deletions

View File

@ -1,115 +0,0 @@
/****************************************************************************
* Copyright (C) 2016-2018 Maschell
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
****************************************************************************/
package de.mas.wiiu.jnus;
import java.io.File;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.List;
import de.mas.wiiu.jnus.implementations.NUSDataProvider;
import de.mas.wiiu.jnus.implementations.NUSDataProviderWUD;
import de.mas.wiiu.jnus.implementations.NUSDataProviderWUDGI;
import de.mas.wiiu.jnus.implementations.wud.WUDImage;
import de.mas.wiiu.jnus.implementations.wud.parser.WUDInfo;
import de.mas.wiiu.jnus.implementations.wud.parser.WUDInfoParser;
import lombok.val;
import lombok.extern.java.Log;
@Log
public final class NUSTitleLoaderWUD extends NUSTitleLoader {
private NUSTitleLoaderWUD() {
super();
}
public static List<NUSTitle> loadNUSTitle(String WUDPath) throws Exception {
return loadNUSTitle(WUDPath, (byte[]) null);
}
public static List<NUSTitle> loadNUSTitle(String WUDPath, File key) throws Exception {
byte[] data = Files.readAllBytes(key.toPath());
if (data == null) {
log.warning("Failed to read the key file.");
return new ArrayList<>();
}
return loadNUSTitle(WUDPath, data);
}
public static List<NUSTitle> loadNUSTitleDev(String WUDPath) throws Exception {
return loadNUSTitle(WUDPath, null, true);
}
public static List<NUSTitle> loadNUSTitle(String WUDPath, byte[] titleKey) throws Exception {
return loadNUSTitle(WUDPath, titleKey, false);
}
public static List<NUSTitle> loadNUSTitle(String WUDPath, byte[] titleKey, boolean forceNoKey) throws Exception {
byte[] usedTitleKey = titleKey;
File wudFile = new File(WUDPath);
if (!wudFile.exists()) {
log.warning(WUDPath + " does not exist.");
System.exit(1);
}
WUDImage image = new WUDImage(wudFile);
if (usedTitleKey == null && !forceNoKey) {
File keyFile = new File(wudFile.getParentFile().getPath() + File.separator + Settings.WUD_KEY_FILENAME);
if (!keyFile.exists()) {
log.warning(keyFile.getAbsolutePath() + " does not exist and no title key was provided.");
return new ArrayList<>();
}
usedTitleKey = Files.readAllBytes(keyFile.toPath());
}
WUDInfo wudInfo = WUDInfoParser.createAndLoad(image.getWUDDiscReader(), usedTitleKey);
if (wudInfo == null) {
log.warning("Failed to parse any WUDInfo");
return new ArrayList<>();
}
List<NUSTitle> result = new ArrayList<>();
for (val gamePartition : wudInfo.getGamePartitions()) {
NUSTitleConfig config = new NUSTitleConfig();
NUSTitleLoader loader = new NUSTitleLoaderWUD();
config.setWUDGamePartition(gamePartition);
config.setWUDInfo(wudInfo);
result.add(loader.loadNusTitle(config));
}
for (val giPartitionTitle : wudInfo.getGIPartitionTitles()) {
NUSTitleConfig config = new NUSTitleConfig();
NUSTitleLoader loader = new NUSTitleLoaderWUD();
config.setWUDGIPartitionTitle(giPartitionTitle);
config.setWUDInfo(wudInfo);
result.add(loader.loadNusTitle(config));
}
return result;
}
@Override
protected NUSDataProvider getDataProvider(NUSTitle title, NUSTitleConfig config) {
if (config.getWUDGIPartitionTitle() != null) {
return new NUSDataProviderWUDGI(title, config.getWUDGIPartitionTitle(), config.getWUDInfo().getWUDDiscReader(), config.getWUDInfo().getTitleKey());
}
return new NUSDataProviderWUD(title, config.getWUDGamePartition(), config.getWUDInfo().getWUDDiscReader());
}
}

View File

@ -1,99 +0,0 @@
/****************************************************************************
* Copyright (C) 2016-2018 Maschell
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
****************************************************************************/
package de.mas.wiiu.jnus.implementations;
import java.io.IOException;
import java.io.InputStream;
import java.util.Optional;
import de.mas.wiiu.jnus.NUSTitle;
import de.mas.wiiu.jnus.Settings;
import de.mas.wiiu.jnus.entities.TMD;
import de.mas.wiiu.jnus.entities.content.Content;
import de.mas.wiiu.jnus.implementations.wud.parser.WUDGIPartitionTitle;
import de.mas.wiiu.jnus.implementations.wud.reader.WUDDiscReader;
import lombok.Getter;
public class NUSDataProviderWUDGI extends NUSDataProvider {
@Getter private final WUDGIPartitionTitle giPartitionTitle;
@Getter private final WUDDiscReader discReader;
private final byte[] titleKey;
private final TMD tmd;
public NUSDataProviderWUDGI(NUSTitle title, WUDGIPartitionTitle giPartitionTitle, WUDDiscReader discReader, byte[] titleKey) {
super(title);
this.giPartitionTitle = giPartitionTitle;
this.discReader = discReader;
this.titleKey = titleKey;
this.tmd = TMD.parseTMD(getRawTMD());
}
@Override
public InputStream getInputStreamFromContent(Content content, long fileOffsetBlock, Optional<Long> size) throws IOException {
InputStream in = getGiPartitionTitle().getFileAsStream(content.getFilename(), getDiscReader(), fileOffsetBlock, titleKey);
return in;
}
@Override
public byte[] getChunkFromContent(Content content, long offset, int size) throws IOException {
return getGiPartitionTitle().getFileAsByte(content.getFilename(), getDiscReader(), offset, size, titleKey);
}
@Override
public byte[] getContentH3Hash(Content content) throws IOException {
return getGiPartitionTitle().getFileAsByte(String.format("%08X.h3", content.getID()), getDiscReader(), titleKey);
}
public TMD getTMD() {
return tmd;
}
@Override
public byte[] getRawTMD() {
try {
return getGiPartitionTitle().getFileAsByte(Settings.TMD_FILENAME, getDiscReader(), titleKey);
} catch (IOException e) {
return new byte[0];
}
}
@Override
public byte[] getRawTicket() {
try {
return getGiPartitionTitle().getFileAsByte(Settings.TICKET_FILENAME, getDiscReader(), titleKey);
} catch (IOException e) {
return new byte[0];
}
}
@Override
public byte[] getRawCert() throws IOException {
try {
return getGiPartitionTitle().getFileAsByte(Settings.CERT_FILENAME, getDiscReader(), titleKey);
} catch (IOException e) {
return new byte[0];
}
}
@Override
public void cleanup() {
// We don't need it
}
}

View File

@ -0,0 +1,14 @@
package de.mas.wiiu.jnus.implementations.wud.parser;
import de.mas.wiiu.jnus.entities.fst.FST;
import lombok.Getter;
public class WUDDataPartition extends WUDPartition {
@Getter private final FST FST;
public WUDDataPartition(String partitionName, long partitionOffset, FST curFST) {
super(partitionName, partitionOffset);
this.FST = curFST;
}
}

View File

@ -1,22 +0,0 @@
package de.mas.wiiu.jnus.implementations.wud.parser;
import java.util.ArrayList;
import java.util.List;
import de.mas.wiiu.jnus.entities.fst.FST;
import lombok.Getter;
import lombok.val;
import lombok.extern.java.Log;
@Log
public class WUDGIPartition extends WUDPartition {
@Getter private final List<WUDGIPartitionTitle> titles = new ArrayList<>();
public WUDGIPartition(String partitionName, long partitionOffset, FST fst) {
super(partitionName, partitionOffset);
for (val curDir : fst.getRoot().getDirChildren()) {
titles.add(new WUDGIPartitionTitle(fst, curDir, partitionOffset));
}
}
}

View File

@ -1,71 +0,0 @@
package de.mas.wiiu.jnus.implementations.wud.parser;
import java.io.IOException;
import java.io.InputStream;
import de.mas.wiiu.jnus.Settings;
import de.mas.wiiu.jnus.entities.content.ContentFSTInfo;
import de.mas.wiiu.jnus.entities.fst.FST;
import de.mas.wiiu.jnus.entities.fst.FSTEntry;
import de.mas.wiiu.jnus.implementations.wud.reader.WUDDiscReader;
import de.mas.wiiu.jnus.utils.StreamUtils;
import lombok.Getter;
public class WUDGIPartitionTitle {
private final FST fst;
private final FSTEntry rootEntry;
@Getter private final long partitionOffset;
public WUDGIPartitionTitle(FST fst, FSTEntry rootEntry, long partitionOffset) {
this.fst = fst;
this.rootEntry = rootEntry;
this.partitionOffset = partitionOffset;
}
public byte[] getFileAsByte(String filename, WUDDiscReader discReader, byte[] titleKey) throws IOException {
FSTEntry entry = getEntryByFilename(rootEntry, filename);
return StreamUtils.getBytesFromStream(getFileAsStream(filename, discReader, 0, titleKey), (int) entry.getFileSize());
}
public InputStream getFileAsStream(String filename, WUDDiscReader discReader, long offsetInFile, byte[] titleKey) throws IOException {
FSTEntry entry = getEntryByFilename(rootEntry, filename);
ContentFSTInfo info = fst.getContentFSTInfos().get((int) entry.getContentFSTID());
return getFileAsStream(info.getOffset(), entry.getFileOffset() + offsetInFile, entry.getFileSize(), discReader, titleKey);
}
public InputStream getFileAsStream(long contentOffset, long fileoffset, long size, WUDDiscReader discReader, byte[] titleKey) throws IOException {
return discReader.readDecryptedToInputStream(getAbsoluteReadOffset() + contentOffset, fileoffset, size, titleKey, null, false);
}
public byte[] getFileAsData(long contentOffset, long fileoffset, long size, WUDDiscReader discReader, byte[] titleKey) throws IOException {
return discReader.readDecryptedToByteArray(getAbsoluteReadOffset() + contentOffset, fileoffset, (int) size, titleKey, null, false);
}
public byte[] getFileAsByte(String filename, WUDDiscReader discReader, long offsetInFile, int size, byte[] titleKey) throws IOException {
FSTEntry entry = getEntryByFilename(rootEntry, filename);
ContentFSTInfo info = fst.getContentFSTInfos().get((int) entry.getContentFSTID());
return getFileAsData(info.getOffset(), entry.getFileOffset() + offsetInFile, size, discReader, titleKey);
}
private long getAbsoluteReadOffset() {
return (long) Settings.WIIU_DECRYPTED_AREA_OFFSET + getPartitionOffset();
}
private static FSTEntry getEntryByFilename(FSTEntry root, String filename) {
for (FSTEntry cur : root.getFileChildren()) {
if (cur.getFilename().equalsIgnoreCase(filename)) {
return cur;
}
}
for (FSTEntry cur : root.getDirChildren()) {
FSTEntry dir_result = getEntryByFilename(cur, filename);
if (dir_result != null) {
return dir_result;
}
}
return null;
}
}

View File

@ -16,36 +16,25 @@
****************************************************************************/ ****************************************************************************/
package de.mas.wiiu.jnus.implementations.wud.parser; package de.mas.wiiu.jnus.implementations.wud.parser;
import java.util.HashMap; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import de.mas.wiiu.jnus.implementations.wud.reader.WUDDiscReader; import de.mas.wiiu.jnus.implementations.wud.reader.WUDDiscReader;
import lombok.AccessLevel;
import lombok.Data; import lombok.Data;
import lombok.Getter; import lombok.Getter;
import lombok.Setter;
@Data @Data
public class WUDInfo { public class WUDInfo {
private final byte[] titleKey; @Getter private final byte[] titleKey;
private final WUDDiscReader WUDDiscReader; private final WUDDiscReader WUDDiscReader;
private final Map<String, WUDPartition> partitions = new HashMap<>(); private final List<WUDPartition> partitions = new ArrayList<>();
@Getter(AccessLevel.PRIVATE) @Setter(AccessLevel.PROTECTED) private String gamePartitionName;
public void addPartion(String partitionName, WUDGamePartition partition) {
getPartitions().put(partitionName, partition);
}
public List<WUDGamePartition> getGamePartitions() { public List<WUDGamePartition> getGamePartitions() {
return partitions.values().stream().filter(p -> p instanceof WUDGamePartition).map(p -> (WUDGamePartition) p).collect(Collectors.toList()); return partitions.stream().filter(p -> p instanceof WUDGamePartition).map(p -> (WUDGamePartition) p).collect(Collectors.toList());
} }
public List<WUDGIPartitionTitle> getGIPartitionTitles() { public List<WUDDataPartition> getDataPartitions() {
return partitions.values().stream().filter(p -> p instanceof WUDGIPartition).flatMap(p -> ((WUDGIPartition) p).getTitles().stream()) return partitions.stream().filter(p -> p instanceof WUDDataPartition).map(p -> ((WUDDataPartition) p)).collect(Collectors.toList());
.collect(Collectors.toList());
} }
} }

View File

@ -21,11 +21,14 @@ import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.ByteOrder; import java.nio.ByteOrder;
import java.text.ParseException;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import de.mas.wiiu.jnus.FSTUtils;
import de.mas.wiiu.jnus.Settings; import de.mas.wiiu.jnus.Settings;
import de.mas.wiiu.jnus.entities.content.ContentFSTInfo; import de.mas.wiiu.jnus.entities.content.ContentFSTInfo;
import de.mas.wiiu.jnus.entities.fst.FST; import de.mas.wiiu.jnus.entities.fst.FST;
@ -52,7 +55,7 @@ public final class WUDInfoParser {
// //
} }
public static WUDInfo createAndLoad(WUDDiscReader discReader, byte[] titleKey) throws IOException { public static WUDInfo createAndLoad(WUDDiscReader discReader, byte[] titleKey) throws IOException, ParseException {
WUDInfo result = new WUDInfo(titleKey, discReader); WUDInfo result = new WUDInfo(titleKey, discReader);
byte[] PartitionTocBlock; byte[] PartitionTocBlock;
@ -66,15 +69,14 @@ public final class WUDInfoParser {
// verify DiscKey before proceeding // verify DiscKey before proceeding
if (!Arrays.equals(Arrays.copyOfRange(PartitionTocBlock, 0, 4), DECRYPTED_AREA_SIGNATURE)) { if (!Arrays.equals(Arrays.copyOfRange(PartitionTocBlock, 0, 4), DECRYPTED_AREA_SIGNATURE)) {
// log.info("Decryption of PartitionTocBlock failed"); // log.info("Decryption of PartitionTocBlock failed");
throw new IOException("Decryption of PartitionTocBlock failed"); throw new ParseException("Decryption of PartitionTocBlock failed", 0);
} }
result.getPartitions().clear(); result.getPartitions().addAll(parsePartitions(result, PartitionTocBlock));
result.getPartitions().putAll(readGamePartitions(result, PartitionTocBlock));
return result; return result;
} }
private static Map<String, WUDPartition> readGamePartitions(WUDInfo wudInfo, byte[] partitionTocBlock) throws IOException { private static Collection<WUDPartition> parsePartitions(WUDInfo wudInfo, byte[] partitionTocBlock) throws IOException, ParseException {
ByteBuffer buffer = ByteBuffer.allocate(partitionTocBlock.length); ByteBuffer buffer = ByteBuffer.allocate(partitionTocBlock.length);
buffer.order(ByteOrder.BIG_ENDIAN); buffer.order(ByteOrder.BIG_ENDIAN);
@ -83,8 +85,7 @@ public final class WUDInfoParser {
int partitionCount = (int) ByteUtils.getUnsingedIntFromBytes(partitionTocBlock, 0x1C, ByteOrder.BIG_ENDIAN); int partitionCount = (int) ByteUtils.getUnsingedIntFromBytes(partitionTocBlock, 0x1C, ByteOrder.BIG_ENDIAN);
Map<String, WUDPartition> internalPartitions = new HashMap<>(); Map<String, Long> internalPartitions = new HashMap<>();
Map<String, WUDPartition> gamePartitions = new HashMap<>();
// populate partition information from decrypted TOC // populate partition information from decrypted TOC
for (int i = 0; i < partitionCount; i++) { for (int i = 0; i < partitionCount; i++) {
@ -104,21 +105,14 @@ public final class WUDInfoParser {
long partitionOffset = ((tmp * (long) 0x8000) - 0x10000); long partitionOffset = ((tmp * (long) 0x8000) - 0x10000);
WUDPartition partition = new WUDPartition(partitionName, partitionOffset); internalPartitions.put(partitionName, partitionOffset);
byte[] header = wudInfo.getWUDDiscReader().readEncryptedToByteArray(partition.getPartitionOffset() + 0x10000, 0, 0x8000);
WUDPartitionHeader partitionHeader = WUDPartitionHeader.parseHeader(header);
partition.setPartitionHeader(partitionHeader);
internalPartitions.put(partitionName, partition);
} }
val siPartitionOpt = internalPartitions.entrySet().stream().filter(e -> e.getKey().startsWith("SI")).findFirst(); val siPartitionOpt = internalPartitions.entrySet().stream().filter(e -> e.getKey().startsWith("SI")).findFirst();
val siPartitionPair = siPartitionOpt.orElseThrow(() -> new RuntimeException("SI partition not foud.")); val siPartitionPair = siPartitionOpt.orElseThrow(() -> new ParseException("SI partition not found.", 0));
// siPartition // siPartition
long siPartitionOffset = siPartitionPair.getValue().getPartitionOffset(); long siPartitionOffset = siPartitionPair.getValue();
val siPartition = siPartitionPair.getValue();
byte[] fileTableBlock; byte[] fileTableBlock;
@ -131,66 +125,73 @@ public final class WUDInfoParser {
if (!Arrays.equals(Arrays.copyOfRange(fileTableBlock, 0, 4), PARTITION_FILE_TABLE_SIGNATURE)) { if (!Arrays.equals(Arrays.copyOfRange(fileTableBlock, 0, 4), PARTITION_FILE_TABLE_SIGNATURE)) {
log.info("FST Decrpytion failed"); log.info("FST Decrpytion failed");
throw new RuntimeException("Failed to decrypt the FST of the SI partition."); throw new ParseException("Failed to decrypt the FST of the SI partition.", 0);
} }
FST siFST = FST.parseFST(fileTableBlock, null); FST siFST = FST.parseFST(fileTableBlock, null);
Map<String, WUDPartition> partitionsResult = new HashMap<>();
for (val dirChilden : siFST.getRoot().getDirChildren()) { for (val dirChilden : siFST.getRoot().getDirChildren()) {
// The SI partition contains the tmd, cert and tik for every GM partition. // The SI partition contains the tmd, cert and tik for every GM partition.
byte[] rawTIK = getFSTEntryAsByte(dirChilden.getFullPath() + File.separator + WUD_TICKET_FILENAME, siPartition, siFST, wudInfo.getWUDDiscReader(), byte[] rawTIK = getFSTEntryAsByte(dirChilden.getFullPath() + File.separator + WUD_TICKET_FILENAME, siPartitionOffset, siFST,
wudInfo.getTitleKey()); wudInfo.getWUDDiscReader(), wudInfo.getTitleKey());
byte[] rawTMD = getFSTEntryAsByte(dirChilden.getFullPath() + File.separator + WUD_TMD_FILENAME, siPartition, siFST, wudInfo.getWUDDiscReader(), byte[] rawTMD = getFSTEntryAsByte(dirChilden.getFullPath() + File.separator + WUD_TMD_FILENAME, siPartitionOffset, siFST,
wudInfo.getTitleKey()); wudInfo.getWUDDiscReader(), wudInfo.getTitleKey());
byte[] rawCert = getFSTEntryAsByte(dirChilden.getFullPath() + File.separator + WUD_CERT_FILENAME, siPartition, siFST, wudInfo.getWUDDiscReader(), byte[] rawCert = getFSTEntryAsByte(dirChilden.getFullPath() + File.separator + WUD_CERT_FILENAME, siPartitionOffset, siFST,
wudInfo.getTitleKey()); wudInfo.getWUDDiscReader(), wudInfo.getTitleKey());
String partitionName = "GM" + Utils.ByteArrayToString(Arrays.copyOfRange(rawTIK, 0x1DC, 0x1DC + 0x08)); String partitionName = "GM" + Utils.ByteArrayToString(Arrays.copyOfRange(rawTIK, 0x1DC, 0x1DC + 0x08));
val curPartitionOpt = internalPartitions.entrySet().stream().filter(e -> e.getKey().startsWith(partitionName)).findFirst(); val curPartitionOpt = internalPartitions.entrySet().stream().filter(e -> e.getKey().startsWith(partitionName)).findFirst();
val curPartitionPair = curPartitionOpt.orElseThrow(() -> new RuntimeException("partition not foud.")); val curPartitionPair = curPartitionOpt.orElseThrow(() -> new ParseException("partition not foud.", 0));
long curPartitionOffset = curPartitionPair.getValue();
WUDGamePartition curPartition = new WUDGamePartition(curPartitionPair.getKey(), curPartitionPair.getValue().getPartitionOffset(), rawTMD, rawCert, WUDGamePartition curPartition = new WUDGamePartition(curPartitionPair.getKey(), curPartitionOffset, rawTMD, rawCert, rawTIK);
rawTIK); byte[] header = wudInfo.getWUDDiscReader().readEncryptedToByteArray(curPartition.getPartitionOffset() + 0x10000, 0, 0x8000);
curPartition.setPartitionHeader(curPartitionPair.getValue().getPartitionHeader()); WUDPartitionHeader partitionHeader = WUDPartitionHeader.parseHeader(header);
gamePartitions.put(curPartitionPair.getKey(), curPartition); curPartition.setPartitionHeader(partitionHeader);
partitionsResult.put(curPartitionPair.getKey(), curPartition);
} }
val giPartitions = internalPartitions.entrySet().stream().filter(e -> e.getKey().startsWith("GI")).collect(Collectors.toList()); val dataPartitions = internalPartitions.entrySet().stream().filter(e -> !e.getKey().startsWith("GM")).collect(Collectors.toList());
for (val giPartition : giPartitions) { for (val dataPartition : dataPartitions) {
String curPartionName = giPartition.getKey(); String curPartionName = dataPartition.getKey();
WUDPartition curPartition = giPartition.getValue(); long partitionOffset = dataPartition.getValue();
byte[] curFileTableBlock = wudInfo.getWUDDiscReader().readDecryptedToByteArray( byte[] curFileTableBlock;
Settings.WIIU_DECRYPTED_AREA_OFFSET + curPartition.getPartitionOffset(), 0, 0x8000, wudInfo.getTitleKey(), null, true); if (wudInfo.getTitleKey() == null) {
curFileTableBlock = wudInfo.getWUDDiscReader().readEncryptedToByteArray(Settings.WIIU_DECRYPTED_AREA_OFFSET + partitionOffset, 0, 0x8000);
} else {
curFileTableBlock = wudInfo.getWUDDiscReader().readDecryptedToByteArray(Settings.WIIU_DECRYPTED_AREA_OFFSET + partitionOffset, 0, 0x8000,
wudInfo.getTitleKey(), null, true);
}
if (!Arrays.equals(Arrays.copyOfRange(curFileTableBlock, 0, 4), WUDInfoParser.PARTITION_FILE_TABLE_SIGNATURE)) { if (!Arrays.equals(Arrays.copyOfRange(curFileTableBlock, 0, 4), WUDInfoParser.PARTITION_FILE_TABLE_SIGNATURE)) {
log.info("FST Decrpytion failed"); log.info("FST Decrpytion failed");
throw new RuntimeException("Failed to decrypt the FST of the SI partition."); throw new ParseException("Failed to decrypt the FST of the " + curPartionName + " partition.", 0);
} }
FST curFST = FST.parseFST(curFileTableBlock, null); FST curFST = FST.parseFST(curFileTableBlock, null);
WUDGIPartition curNewPartition = new WUDGIPartition(curPartionName, curPartition.getPartitionOffset(), curFST); WUDDataPartition curDataPartition = new WUDDataPartition(curPartionName, partitionOffset, curFST);
curPartition.setPartitionHeader(curPartition.getPartitionHeader());
gamePartitions.put(curPartionName, curNewPartition); byte[] header = wudInfo.getWUDDiscReader().readEncryptedToByteArray(curDataPartition.getPartitionOffset() + 0x10000, 0, 0x8000);
WUDPartitionHeader partitionHeader = WUDPartitionHeader.parseHeader(header);
curDataPartition.setPartitionHeader(partitionHeader);
partitionsResult.put(curPartionName, curDataPartition);
} }
return gamePartitions; return partitionsResult.values();
} }
private static byte[] getFSTEntryAsByte(String filePath, WUDPartition partition, FST fst, WUDDiscReader discReader, byte[] key) throws IOException { private static byte[] getFSTEntryAsByte(String filePath, long partitionOffset, FST fst, WUDDiscReader discReader, byte[] key) throws IOException {
FSTEntry entry = getEntryByFullPath(fst.getRoot(), filePath); FSTEntry entry = FSTUtils.getEntryByFullPath(fst.getRoot(), filePath).orElseThrow(() -> new FileNotFoundException(filePath + " was not found."));
if(entry == null) {
String errormsg = "FSTEntry with name \"" + filePath + "\" not found.";
log.warning(errormsg);
throw new FileNotFoundException(errormsg);
}
ContentFSTInfo info = fst.getContentFSTInfos().get((int) entry.getContentFSTID()); ContentFSTInfo info = fst.getContentFSTInfos().get((int) entry.getContentFSTID());
if (key == null) { if (key == null) {
return discReader.readEncryptedToByteArray(Settings.WIIU_DECRYPTED_AREA_OFFSET + (long) partition.getPartitionOffset() + (long) info.getOffset(), return discReader.readEncryptedToByteArray(((long) Settings.WIIU_DECRYPTED_AREA_OFFSET) + partitionOffset + (long) info.getOffset(),
entry.getFileOffset(), (int) entry.getFileSize()); entry.getFileOffset(), (int) entry.getFileSize());
} }
@ -199,22 +200,8 @@ public final class WUDInfoParser {
byteBuffer.position(0x08); byteBuffer.position(0x08);
byte[] IV = byteBuffer.putLong(entry.getFileOffset() >> 16).array(); byte[] IV = byteBuffer.putLong(entry.getFileOffset() >> 16).array();
return discReader.readDecryptedToByteArray(Settings.WIIU_DECRYPTED_AREA_OFFSET + (long) partition.getPartitionOffset() + (long) info.getOffset(), return discReader.readDecryptedToByteArray(((long) Settings.WIIU_DECRYPTED_AREA_OFFSET) + partitionOffset + info.getOffset(), entry.getFileOffset(),
entry.getFileOffset(), (int) entry.getFileSize(), key, IV, false); (int) entry.getFileSize(), key, IV, false);
} }
private static FSTEntry getEntryByFullPath(FSTEntry root, String filePath) {
for (FSTEntry cur : root.getFileChildren()) {
if (cur.getFullPath().equals(filePath)) {
return cur;
}
}
for (FSTEntry cur : root.getDirChildren()) {
FSTEntry dir_result = getEntryByFullPath(cur, filePath);
if (dir_result != null) {
return dir_result;
}
}
return null;
}
} }