mirror of
https://github.com/Maschell/JNUSLib.git
synced 2024-11-29 11:24:20 +01:00
- 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:
parent
422c36dde3
commit
2913ccf8b2
@ -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());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -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;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -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());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user