mirror of
https://github.com/Maschell/JNUSLib.git
synced 2024-11-26 18:04:17 +01:00
The NUSTitleLoaderWUD can now handle multiple GM partition and can mount contents of a GI partition. Added support for handling Kiosk discs where the SI parition is NOT encrypted with a title key.
This commit is contained in:
parent
bfbbafc269
commit
7e07765fa1
@ -18,12 +18,17 @@ package de.mas.wiiu.jnus;
|
|||||||
|
|
||||||
import de.mas.wiiu.jnus.entities.Ticket;
|
import de.mas.wiiu.jnus.entities.Ticket;
|
||||||
import de.mas.wiiu.jnus.implementations.woomy.WoomyInfo;
|
import de.mas.wiiu.jnus.implementations.woomy.WoomyInfo;
|
||||||
|
import de.mas.wiiu.jnus.implementations.wud.parser.WUDGIPartitionTitle;
|
||||||
|
import de.mas.wiiu.jnus.implementations.wud.parser.WUDGamePartition;
|
||||||
import de.mas.wiiu.jnus.implementations.wud.parser.WUDInfo;
|
import de.mas.wiiu.jnus.implementations.wud.parser.WUDInfo;
|
||||||
|
import de.mas.wiiu.jnus.implementations.wud.reader.WUDDiscReader;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
public class NUSTitleConfig {
|
public class NUSTitleConfig {
|
||||||
private String inputPath;
|
private String inputPath;
|
||||||
|
private WUDGamePartition WUDGamePartition = null;
|
||||||
|
private WUDGIPartitionTitle WUDGIPartitionTitle = null;
|
||||||
private WUDInfo WUDInfo;
|
private WUDInfo WUDInfo;
|
||||||
private Ticket ticket;
|
private Ticket ticket;
|
||||||
|
|
||||||
|
@ -18,12 +18,16 @@ package de.mas.wiiu.jnus;
|
|||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.nio.file.Files;
|
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.NUSDataProvider;
|
||||||
import de.mas.wiiu.jnus.implementations.NUSDataProviderWUD;
|
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.WUDImage;
|
||||||
import de.mas.wiiu.jnus.implementations.wud.parser.WUDInfo;
|
import de.mas.wiiu.jnus.implementations.wud.parser.WUDInfo;
|
||||||
import de.mas.wiiu.jnus.implementations.wud.parser.WUDInfoParser;
|
import de.mas.wiiu.jnus.implementations.wud.parser.WUDInfoParser;
|
||||||
|
import lombok.val;
|
||||||
import lombok.extern.java.Log;
|
import lombok.extern.java.Log;
|
||||||
|
|
||||||
@Log
|
@Log
|
||||||
@ -33,13 +37,28 @@ public final class NUSTitleLoaderWUD extends NUSTitleLoader {
|
|||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static NUSTitle loadNUSTitle(String WUDPath) throws Exception {
|
public static List<NUSTitle> loadNUSTitle(String WUDPath) throws Exception {
|
||||||
return loadNUSTitle(WUDPath, null);
|
return loadNUSTitle(WUDPath, (byte[]) null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static NUSTitle loadNUSTitle(String WUDPath, byte[] titleKey) throws Exception {
|
public static List<NUSTitle> loadNUSTitle(String WUDPath, File key) throws Exception {
|
||||||
NUSTitleLoader loader = new NUSTitleLoaderWUD();
|
byte[] data = Files.readAllBytes(key.toPath());
|
||||||
NUSTitleConfig config = new NUSTitleConfig();
|
if (data == null) {
|
||||||
|
System.out.println("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;
|
byte[] usedTitleKey = titleKey;
|
||||||
File wudFile = new File(WUDPath);
|
File wudFile = new File(WUDPath);
|
||||||
if (!wudFile.exists()) {
|
if (!wudFile.exists()) {
|
||||||
@ -48,26 +67,49 @@ public final class NUSTitleLoaderWUD extends NUSTitleLoader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
WUDImage image = new WUDImage(wudFile);
|
WUDImage image = new WUDImage(wudFile);
|
||||||
if (usedTitleKey == null) {
|
if (usedTitleKey == null && !forceNoKey) {
|
||||||
File keyFile = new File(wudFile.getParentFile().getPath() + File.separator + Settings.WUD_KEY_FILENAME);
|
File keyFile = new File(wudFile.getParentFile().getPath() + File.separator + Settings.WUD_KEY_FILENAME);
|
||||||
if (!keyFile.exists()) {
|
if (!keyFile.exists()) {
|
||||||
log.info(keyFile.getAbsolutePath() + " does not exist and no title key was provided.");
|
log.info(keyFile.getAbsolutePath() + " does not exist and no title key was provided.");
|
||||||
return null;
|
return new ArrayList<>();
|
||||||
}
|
}
|
||||||
usedTitleKey = Files.readAllBytes(keyFile.toPath());
|
usedTitleKey = Files.readAllBytes(keyFile.toPath());
|
||||||
}
|
}
|
||||||
WUDInfo wudInfo = WUDInfoParser.createAndLoad(image.getWUDDiscReader(), usedTitleKey);
|
WUDInfo wudInfo = WUDInfoParser.createAndLoad(image.getWUDDiscReader(), usedTitleKey);
|
||||||
|
|
||||||
if (wudInfo == null) {
|
if (wudInfo == null) {
|
||||||
return null;
|
System.out.println("WTF. ERROR.");
|
||||||
|
return new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
config.setWUDInfo(wudInfo);
|
List<NUSTitle> result = new ArrayList<>();
|
||||||
|
|
||||||
return loader.loadNusTitle(config);
|
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
|
@Override
|
||||||
protected NUSDataProvider getDataProvider(NUSTitle title, NUSTitleConfig config) {
|
protected NUSDataProvider getDataProvider(NUSTitle title, NUSTitleConfig config) {
|
||||||
return new NUSDataProviderWUD(title, config.getWUDInfo());
|
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());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -32,13 +32,15 @@ import lombok.extern.java.Log;
|
|||||||
|
|
||||||
@Log
|
@Log
|
||||||
public class NUSDataProviderWUD extends NUSDataProvider {
|
public class NUSDataProviderWUD extends NUSDataProvider {
|
||||||
@Getter private final WUDInfo WUDInfo;
|
@Getter private final WUDGamePartition gamePartition;
|
||||||
|
@Getter private final WUDDiscReader discReader;
|
||||||
|
|
||||||
private final TMD tmd;
|
private final TMD tmd;
|
||||||
|
|
||||||
public NUSDataProviderWUD(NUSTitle title, WUDInfo wudinfo) {
|
public NUSDataProviderWUD(NUSTitle title, WUDGamePartition gamePartition, WUDDiscReader discReader) {
|
||||||
super(title);
|
super(title);
|
||||||
this.WUDInfo = wudinfo;
|
this.gamePartition = gamePartition;
|
||||||
|
this.discReader = discReader;
|
||||||
this.tmd = TMD.parseTMD(getRawTMD());
|
this.tmd = TMD.parseTMD(getRawTMD());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,18 +98,10 @@ public class NUSDataProviderWUD extends NUSDataProvider {
|
|||||||
return getGamePartition().getRawCert();
|
return getGamePartition().getRawCert();
|
||||||
}
|
}
|
||||||
|
|
||||||
public WUDGamePartition getGamePartition() {
|
|
||||||
return getWUDInfo().getGamePartition();
|
|
||||||
}
|
|
||||||
|
|
||||||
public WUDPartitionHeader getGamePartitionHeader() {
|
public WUDPartitionHeader getGamePartitionHeader() {
|
||||||
return getGamePartition().getPartitionHeader();
|
return getGamePartition().getPartitionHeader();
|
||||||
}
|
}
|
||||||
|
|
||||||
public WUDDiscReader getDiscReader() {
|
|
||||||
return getWUDInfo().getWUDDiscReader();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void cleanup() {
|
public void cleanup() {
|
||||||
// We don't need it
|
// We don't need it
|
||||||
@ -115,6 +109,6 @@ public class NUSDataProviderWUD extends NUSDataProvider {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "NUSDataProviderWUD [WUDInfo=" + WUDInfo + "]";
|
return "NUSDataProviderWUD [WUDGamePartition=" + gamePartition + "]";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,96 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
* 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 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;
|
||||||
|
import lombok.extern.java.Log;
|
||||||
|
|
||||||
|
@Log
|
||||||
|
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) throws IOException {
|
||||||
|
InputStream in = getGiPartitionTitle().getFileAsStream(content.getFilename(), getDiscReader(), titleKey);
|
||||||
|
in.skip(fileOffsetBlock);
|
||||||
|
return in;
|
||||||
|
}
|
||||||
|
|
||||||
|
@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
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -58,7 +58,7 @@ public class WUDImage {
|
|||||||
WUDImageCompressedInfo compressedInfo = new WUDImageCompressedInfo(wuxheader);
|
WUDImageCompressedInfo compressedInfo = new WUDImageCompressedInfo(wuxheader);
|
||||||
|
|
||||||
if (compressedInfo.isWUX()) {
|
if (compressedInfo.isWUX()) {
|
||||||
log.info("Image is compressed");
|
log.fine("Image is compressed");
|
||||||
this.isCompressed = true;
|
this.isCompressed = true;
|
||||||
this.isSplitted = false;
|
this.isSplitted = false;
|
||||||
Map<Integer, Long> indexTable = new HashMap<>();
|
Map<Integer, Long> indexTable = new HashMap<>();
|
||||||
|
@ -0,0 +1,22 @@
|
|||||||
|
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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,59 @@
|
|||||||
|
package de.mas.wiiu.jnus.implementations.wud.parser;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
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, titleKey), (int) entry.getFileSize());
|
||||||
|
}
|
||||||
|
|
||||||
|
public InputStream getFileAsStream(String filename, WUDDiscReader discReader, byte[] titleKey) throws IOException {
|
||||||
|
FSTEntry entry = getEntryByFilename(rootEntry, filename);
|
||||||
|
ContentFSTInfo info = fst.getContentFSTInfos().get((int) entry.getContentFSTID());
|
||||||
|
|
||||||
|
return discReader.readDecryptedToInputStream(getAbsoluteReadOffset() + (long) info.getOffset(), entry.getFileOffset(), (int) entry.getFileSize(),
|
||||||
|
titleKey, null, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
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,9 +16,12 @@
|
|||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
package de.mas.wiiu.jnus.implementations.wud.parser;
|
package de.mas.wiiu.jnus.implementations.wud.parser;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
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.AccessLevel;
|
||||||
@ -35,25 +38,16 @@ public class WUDInfo {
|
|||||||
|
|
||||||
@Getter(AccessLevel.PRIVATE) @Setter(AccessLevel.PROTECTED) private String gamePartitionName;
|
@Getter(AccessLevel.PRIVATE) @Setter(AccessLevel.PROTECTED) private String gamePartitionName;
|
||||||
|
|
||||||
private WUDGamePartition cachedGamePartition = null;
|
|
||||||
|
|
||||||
public void addPartion(String partitionName, WUDGamePartition partition) {
|
public void addPartion(String partitionName, WUDGamePartition partition) {
|
||||||
getPartitions().put(partitionName, partition);
|
getPartitions().put(partitionName, partition);
|
||||||
}
|
}
|
||||||
|
|
||||||
public WUDGamePartition getGamePartition() {
|
public List<WUDGamePartition> getGamePartitions() {
|
||||||
if (cachedGamePartition == null) {
|
return partitions.values().stream().filter(p -> p instanceof WUDGamePartition).map(p -> (WUDGamePartition) p).collect(Collectors.toList());
|
||||||
cachedGamePartition = findGamePartition();
|
|
||||||
}
|
|
||||||
return cachedGamePartition;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private WUDGamePartition findGamePartition() {
|
public List<WUDGIPartitionTitle> getGIPartitionTitles() {
|
||||||
for (Entry<String, WUDPartition> e : getPartitions().entrySet()) {
|
return partitions.values().stream().filter(p -> p instanceof WUDGIPartition).flatMap(p -> ((WUDGIPartition) p).getTitles().stream())
|
||||||
if (e.getKey().equals(getGamePartitionName())) {
|
.collect(Collectors.toList());
|
||||||
return (WUDGamePartition) e.getValue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,20 +16,25 @@
|
|||||||
****************************************************************************/
|
****************************************************************************/
|
||||||
package de.mas.wiiu.jnus.implementations.wud.parser;
|
package de.mas.wiiu.jnus.implementations.wud.parser;
|
||||||
|
|
||||||
|
import java.io.FileOutputStream;
|
||||||
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.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import de.mas.wiiu.jnus.Settings;
|
import de.mas.wiiu.jnus.Settings;
|
||||||
|
import de.mas.wiiu.jnus.entities.TMD;
|
||||||
|
import de.mas.wiiu.jnus.entities.Ticket;
|
||||||
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;
|
||||||
import de.mas.wiiu.jnus.entities.fst.FSTEntry;
|
import de.mas.wiiu.jnus.entities.fst.FSTEntry;
|
||||||
import de.mas.wiiu.jnus.implementations.wud.reader.WUDDiscReader;
|
import de.mas.wiiu.jnus.implementations.wud.reader.WUDDiscReader;
|
||||||
import de.mas.wiiu.jnus.utils.ByteUtils;
|
import de.mas.wiiu.jnus.utils.ByteUtils;
|
||||||
import de.mas.wiiu.jnus.utils.Utils;
|
import de.mas.wiiu.jnus.utils.Utils;
|
||||||
|
import lombok.val;
|
||||||
import lombok.extern.java.Log;
|
import lombok.extern.java.Log;
|
||||||
|
|
||||||
@Log
|
@Log
|
||||||
@ -51,22 +56,26 @@ public final class WUDInfoParser {
|
|||||||
public static WUDInfo createAndLoad(WUDDiscReader discReader, byte[] titleKey) throws IOException {
|
public static WUDInfo createAndLoad(WUDDiscReader discReader, byte[] titleKey) throws IOException {
|
||||||
WUDInfo result = new WUDInfo(titleKey, discReader);
|
WUDInfo result = new WUDInfo(titleKey, discReader);
|
||||||
|
|
||||||
byte[] PartitionTocBlock = discReader.readDecryptedToByteArray(Settings.WIIU_DECRYPTED_AREA_OFFSET, 0, 0x8000, titleKey, null);
|
byte[] PartitionTocBlock;
|
||||||
|
if (titleKey == null) {
|
||||||
|
PartitionTocBlock = discReader.readEncryptedToByteArray(Settings.WIIU_DECRYPTED_AREA_OFFSET, 0, 0x8000);
|
||||||
|
} else {
|
||||||
|
PartitionTocBlock = discReader.readDecryptedToByteArray(Settings.WIIU_DECRYPTED_AREA_OFFSET, 0, 0x8000, titleKey, null, true);
|
||||||
|
}
|
||||||
|
//
|
||||||
|
|
||||||
// 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");
|
||||||
return null;
|
throw new RuntimeException("Decryption of PartitionTocBlock failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<String, WUDPartition> partitions = readPartitions(result, PartitionTocBlock);
|
|
||||||
result.getPartitions().clear();
|
result.getPartitions().clear();
|
||||||
result.getPartitions().putAll(partitions);
|
result.getPartitions().putAll(readGamePartitions(result, PartitionTocBlock));
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Map<String, WUDPartition> readPartitions(WUDInfo wudInfo, byte[] partitionTocBlock) throws IOException {
|
private static Map<String, WUDPartition> readGamePartitions(WUDInfo wudInfo, byte[] partitionTocBlock) throws IOException {
|
||||||
ByteBuffer buffer = ByteBuffer.allocate(partitionTocBlock.length);
|
ByteBuffer buffer = ByteBuffer.allocate(partitionTocBlock.length);
|
||||||
|
|
||||||
buffer.order(ByteOrder.BIG_ENDIAN);
|
buffer.order(ByteOrder.BIG_ENDIAN);
|
||||||
@ -75,16 +84,11 @@ 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> partitions = new HashMap<>();
|
Map<String, WUDPartition> internalPartitions = new HashMap<>();
|
||||||
|
Map<String, WUDPartition> gamePartitions = new HashMap<>();
|
||||||
|
|
||||||
byte[] gamePartitionTMD = new byte[0];
|
|
||||||
byte[] gamePartitionTicket = new byte[0];
|
|
||||||
byte[] gamePartitionCert = new byte[0];
|
|
||||||
|
|
||||||
String realGamePartitionName = null;
|
|
||||||
// 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++) {
|
||||||
|
|
||||||
int offset = (PARTITION_TOC_OFFSET + (i * PARTITION_TOC_ENTRY_SIZE));
|
int offset = (PARTITION_TOC_OFFSET + (i * PARTITION_TOC_ENTRY_SIZE));
|
||||||
byte[] partitionIdentifier = Arrays.copyOfRange(partitionTocBlock, offset, offset + 0x19);
|
byte[] partitionIdentifier = Arrays.copyOfRange(partitionTocBlock, offset, offset + 0x19);
|
||||||
int j = 0;
|
int j = 0;
|
||||||
@ -103,61 +107,106 @@ public final class WUDInfoParser {
|
|||||||
|
|
||||||
WUDPartition partition = new WUDPartition(partitionName, partitionOffset);
|
WUDPartition partition = new WUDPartition(partitionName, partitionOffset);
|
||||||
|
|
||||||
if (partitionName.startsWith("SI")) {
|
|
||||||
byte[] fileTableBlock = wudInfo.getWUDDiscReader().readDecryptedToByteArray(Settings.WIIU_DECRYPTED_AREA_OFFSET + partitionOffset, 0, 0x8000,
|
|
||||||
wudInfo.getTitleKey(), null);
|
|
||||||
if (!Arrays.equals(Arrays.copyOfRange(fileTableBlock, 0, 4), PARTITION_FILE_TABLE_SIGNATURE)) {
|
|
||||||
log.info("FST Decrpytion failed");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
FST fst = FST.parseFST(fileTableBlock, null);
|
|
||||||
|
|
||||||
byte[] rawTIK = getFSTEntryAsByte(WUD_TICKET_FILENAME, partition, fst, wudInfo.getWUDDiscReader(), wudInfo.getTitleKey());
|
|
||||||
byte[] rawTMD = getFSTEntryAsByte(WUD_TMD_FILENAME, partition, fst, wudInfo.getWUDDiscReader(), wudInfo.getTitleKey());
|
|
||||||
byte[] rawCert = getFSTEntryAsByte(WUD_CERT_FILENAME, partition, fst, wudInfo.getWUDDiscReader(), wudInfo.getTitleKey());
|
|
||||||
|
|
||||||
gamePartitionTMD = rawTMD;
|
|
||||||
gamePartitionTicket = rawTIK;
|
|
||||||
gamePartitionCert = rawCert;
|
|
||||||
|
|
||||||
// We want to use the real game partition
|
|
||||||
realGamePartitionName = partitionName = "GM" + Utils.ByteArrayToString(Arrays.copyOfRange(rawTIK, 0x1DC, 0x1DC + 0x08));
|
|
||||||
} else if (partitionName.startsWith(realGamePartitionName)) {
|
|
||||||
wudInfo.setGamePartitionName(partitionName);
|
|
||||||
partition = new WUDGamePartition(partitionName, partitionOffset, gamePartitionTMD, gamePartitionCert, gamePartitionTicket);
|
|
||||||
}
|
|
||||||
byte[] header = wudInfo.getWUDDiscReader().readEncryptedToByteArray(partition.getPartitionOffset() + 0x10000, 0, 0x8000);
|
byte[] header = wudInfo.getWUDDiscReader().readEncryptedToByteArray(partition.getPartitionOffset() + 0x10000, 0, 0x8000);
|
||||||
WUDPartitionHeader partitionHeader = WUDPartitionHeader.parseHeader(header);
|
WUDPartitionHeader partitionHeader = WUDPartitionHeader.parseHeader(header);
|
||||||
partition.setPartitionHeader(partitionHeader);
|
partition.setPartitionHeader(partitionHeader);
|
||||||
|
|
||||||
partitions.put(partitionName, partition);
|
internalPartitions.put(partitionName, partition);
|
||||||
}
|
}
|
||||||
|
|
||||||
return partitions;
|
val siPartitionOpt = internalPartitions.entrySet().stream().filter(e -> e.getKey().startsWith("SI")).findFirst();
|
||||||
|
val siPartitionPair = siPartitionOpt.orElseThrow(() -> new RuntimeException("SI partition not foud."));
|
||||||
|
|
||||||
|
// siPartition
|
||||||
|
long siPartitionOffset = siPartitionPair.getValue().getPartitionOffset();
|
||||||
|
val siPartition = siPartitionPair.getValue();
|
||||||
|
|
||||||
|
byte[] fileTableBlock;
|
||||||
|
|
||||||
|
if (wudInfo.getTitleKey() == null) {
|
||||||
|
fileTableBlock = wudInfo.getWUDDiscReader().readEncryptedToByteArray(Settings.WIIU_DECRYPTED_AREA_OFFSET + siPartitionOffset, 0, 0x8000);
|
||||||
|
} else {
|
||||||
|
fileTableBlock = wudInfo.getWUDDiscReader().readDecryptedToByteArray(Settings.WIIU_DECRYPTED_AREA_OFFSET + siPartitionOffset, 0, 0x8000,
|
||||||
|
wudInfo.getTitleKey(), null, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Arrays.equals(Arrays.copyOfRange(fileTableBlock, 0, 4), PARTITION_FILE_TABLE_SIGNATURE)) {
|
||||||
|
log.info("FST Decrpytion failed");
|
||||||
|
throw new RuntimeException("Failed to decrypt the FST of the SI partition.");
|
||||||
|
}
|
||||||
|
|
||||||
|
FST siFST = FST.parseFST(fileTableBlock, null);
|
||||||
|
|
||||||
|
for (val dirChilden : siFST.getRoot().getDirChildren()) {
|
||||||
|
// The SI partition contains the tmd, cert and tik for every GM partition.
|
||||||
|
byte[] rawTIK = getFSTEntryAsByte(dirChilden.getFullPath() + "\\" + WUD_TICKET_FILENAME, siPartition, siFST, wudInfo.getWUDDiscReader(),
|
||||||
|
wudInfo.getTitleKey());
|
||||||
|
byte[] rawTMD = getFSTEntryAsByte(dirChilden.getFullPath() + "\\" + WUD_TMD_FILENAME, siPartition, siFST, wudInfo.getWUDDiscReader(),
|
||||||
|
wudInfo.getTitleKey());
|
||||||
|
byte[] rawCert = getFSTEntryAsByte(dirChilden.getFullPath() + "\\" + WUD_CERT_FILENAME, siPartition, siFST, wudInfo.getWUDDiscReader(),
|
||||||
|
wudInfo.getTitleKey());
|
||||||
|
|
||||||
|
String partitionName = "GM" + Utils.ByteArrayToString(Arrays.copyOfRange(rawTIK, 0x1DC, 0x1DC + 0x08));
|
||||||
|
|
||||||
|
val curPartitionOpt = internalPartitions.entrySet().stream().filter(e -> e.getKey().startsWith(partitionName)).findFirst();
|
||||||
|
val curPartitionPair = curPartitionOpt.orElseThrow(() -> new RuntimeException("partition not foud."));
|
||||||
|
|
||||||
|
WUDGamePartition curPartition = new WUDGamePartition(curPartitionPair.getKey(), curPartitionPair.getValue().getPartitionOffset(), rawTMD, rawCert,
|
||||||
|
rawTIK);
|
||||||
|
curPartition.setPartitionHeader(curPartitionPair.getValue().getPartitionHeader());
|
||||||
|
gamePartitions.put(curPartitionPair.getKey(), curPartition);
|
||||||
|
}
|
||||||
|
|
||||||
|
val giPartitions = internalPartitions.entrySet().stream().filter(e -> e.getKey().startsWith("GI")).collect(Collectors.toList());
|
||||||
|
for (val giPartition : giPartitions) {
|
||||||
|
String curPartionName = giPartition.getKey();
|
||||||
|
WUDPartition curPartition = giPartition.getValue();
|
||||||
|
|
||||||
|
byte[] curFileTableBlock = wudInfo.getWUDDiscReader().readDecryptedToByteArray(
|
||||||
|
Settings.WIIU_DECRYPTED_AREA_OFFSET + curPartition.getPartitionOffset(), 0, 0x8000, wudInfo.getTitleKey(), null, true);
|
||||||
|
if (!Arrays.equals(Arrays.copyOfRange(curFileTableBlock, 0, 4), WUDInfoParser.PARTITION_FILE_TABLE_SIGNATURE)) {
|
||||||
|
log.info("FST Decrpytion failed");
|
||||||
|
throw new RuntimeException("Failed to decrypt the FST of the SI partition.");
|
||||||
|
}
|
||||||
|
|
||||||
|
FST curFST = FST.parseFST(curFileTableBlock, null);
|
||||||
|
|
||||||
|
WUDGIPartition curNewPartition = new WUDGIPartition(curPartionName, curPartition.getPartitionOffset(), curFST);
|
||||||
|
curPartition.setPartitionHeader(curPartition.getPartitionHeader());
|
||||||
|
|
||||||
|
gamePartitions.put(curPartionName, curNewPartition);
|
||||||
|
}
|
||||||
|
|
||||||
|
return gamePartitions;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static byte[] getFSTEntryAsByte(String filename, WUDPartition partition, FST fst, WUDDiscReader discReader, byte[] key) throws IOException {
|
private static byte[] getFSTEntryAsByte(String filePath, WUDPartition partition, FST fst, WUDDiscReader discReader, byte[] key) throws IOException {
|
||||||
FSTEntry entry = getEntryByName(fst.getRoot(), filename);
|
FSTEntry entry = getEntryByFullPath(fst.getRoot(), filePath);
|
||||||
|
|
||||||
ContentFSTInfo info = fst.getContentFSTInfos().get((int) entry.getContentFSTID());
|
ContentFSTInfo info = fst.getContentFSTInfos().get((int) entry.getContentFSTID());
|
||||||
|
|
||||||
|
if (key == null) {
|
||||||
|
return discReader.readEncryptedToByteArray(Settings.WIIU_DECRYPTED_AREA_OFFSET + (long) partition.getPartitionOffset() + (long) info.getOffset(),
|
||||||
|
entry.getFileOffset(), (int) entry.getFileSize());
|
||||||
|
}
|
||||||
|
|
||||||
// Calculating the IV
|
// Calculating the IV
|
||||||
ByteBuffer byteBuffer = ByteBuffer.allocate(0x10);
|
ByteBuffer byteBuffer = ByteBuffer.allocate(0x10);
|
||||||
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(Settings.WIIU_DECRYPTED_AREA_OFFSET + (long) partition.getPartitionOffset() + (long) info.getOffset(),
|
||||||
entry.getFileOffset(), (int) entry.getFileSize(), key, IV);
|
entry.getFileOffset(), (int) entry.getFileSize(), key, IV, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static FSTEntry getEntryByName(FSTEntry root, String name) {
|
private static FSTEntry getEntryByFullPath(FSTEntry root, String filePath) {
|
||||||
for (FSTEntry cur : root.getFileChildren()) {
|
for (FSTEntry cur : root.getFileChildren()) {
|
||||||
if (cur.getFilename().equals(name)) {
|
if (cur.getFullPath().equals(filePath)) {
|
||||||
return cur;
|
return cur;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (FSTEntry cur : root.getDirChildren()) {
|
for (FSTEntry cur : root.getDirChildren()) {
|
||||||
FSTEntry dir_result = getEntryByName(cur, name);
|
FSTEntry dir_result = getEntryByFullPath(cur, filePath);
|
||||||
if (dir_result != null) {
|
if (dir_result != null) {
|
||||||
return dir_result;
|
return dir_result;
|
||||||
}
|
}
|
||||||
|
@ -24,9 +24,11 @@ import java.io.OutputStream;
|
|||||||
import java.io.PipedInputStream;
|
import java.io.PipedInputStream;
|
||||||
import java.io.PipedOutputStream;
|
import java.io.PipedOutputStream;
|
||||||
import java.io.RandomAccessFile;
|
import java.io.RandomAccessFile;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
import de.mas.wiiu.jnus.implementations.wud.WUDImage;
|
import de.mas.wiiu.jnus.implementations.wud.WUDImage;
|
||||||
|
import de.mas.wiiu.jnus.utils.Utils;
|
||||||
import de.mas.wiiu.jnus.utils.cryptography.AESDecryption;
|
import de.mas.wiiu.jnus.utils.cryptography.AESDecryption;
|
||||||
import lombok.Getter;
|
import lombok.Getter;
|
||||||
import lombok.extern.java.Log;
|
import lombok.extern.java.Log;
|
||||||
@ -60,13 +62,13 @@ public abstract class WUDDiscReader {
|
|||||||
return out.toByteArray();
|
return out.toByteArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
public InputStream readDecryptedToInputStream(long offset, long fileoffset, long size, byte[] key, byte[] iv) throws IOException {
|
public InputStream readDecryptedToInputStream(long offset, long fileoffset, long size, byte[] key, byte[] IV, boolean useFixedIV) throws IOException {
|
||||||
PipedInputStream in = new PipedInputStream();
|
PipedInputStream in = new PipedInputStream();
|
||||||
PipedOutputStream out = new PipedOutputStream(in);
|
PipedOutputStream out = new PipedOutputStream(in);
|
||||||
|
|
||||||
new Thread(() -> {
|
new Thread(() -> {
|
||||||
try {
|
try {
|
||||||
readDecryptedToOutputStream(out, offset, fileoffset, size, key, iv);
|
readDecryptedToOutputStream(out, offset, fileoffset, size, key, IV, useFixedIV);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
@ -75,10 +77,10 @@ public abstract class WUDDiscReader {
|
|||||||
return in;
|
return in;
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] readDecryptedToByteArray(long offset, long fileoffset, long size, byte[] key, byte[] iv) throws IOException {
|
public byte[] readDecryptedToByteArray(long offset, long fileoffset, long size, byte[] key, byte[] IV, boolean useFixedIV) throws IOException {
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
|
||||||
readDecryptedToOutputStream(out, offset, fileoffset, size, key, iv);
|
readDecryptedToOutputStream(out, offset, fileoffset, size, key, IV, useFixedIV);
|
||||||
return out.toByteArray();
|
return out.toByteArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,12 +107,16 @@ public abstract class WUDDiscReader {
|
|||||||
return decryptedChunk;
|
return decryptedChunk;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void readDecryptedToOutputStream(OutputStream outputStream, long clusterOffset, long fileOffset, long size, byte[] key, byte[] IV)
|
public void readDecryptedToOutputStream(OutputStream outputStream, long clusterOffset, long fileOffset, long size, byte[] key, byte[] IV,
|
||||||
throws IOException {
|
boolean useFixedIV) throws IOException {
|
||||||
byte[] usedIV = IV;
|
byte[] usedIV = null;
|
||||||
if (usedIV == null) {
|
if (useFixedIV) {
|
||||||
usedIV = new byte[0x10];
|
usedIV = IV;
|
||||||
|
if (IV == null) {
|
||||||
|
usedIV = new byte[0x10];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
long usedSize = size;
|
long usedSize = size;
|
||||||
long usedFileOffset = fileOffset;
|
long usedFileOffset = fileOffset;
|
||||||
byte[] buffer;
|
byte[] buffer;
|
||||||
@ -130,11 +136,26 @@ public abstract class WUDDiscReader {
|
|||||||
readOffset = clusterOffset + (blockNumber * BLOCK_SIZE);
|
readOffset = clusterOffset + (blockNumber * BLOCK_SIZE);
|
||||||
// (long)WiiUDisc.WIIU_DECRYPTED_AREA_OFFSET + volumeOffset + clusterOffset + (blockStructure.getBlockNumber() * 0x8000);
|
// (long)WiiUDisc.WIIU_DECRYPTED_AREA_OFFSET + volumeOffset + clusterOffset + (blockStructure.getBlockNumber() * 0x8000);
|
||||||
|
|
||||||
|
if (!useFixedIV) {
|
||||||
|
ByteBuffer byteBuffer = ByteBuffer.allocate(0x10);
|
||||||
|
byteBuffer.position(0x08);
|
||||||
|
usedIV = byteBuffer.putLong(usedFileOffset >> 16).array();
|
||||||
|
}
|
||||||
|
|
||||||
buffer = readDecryptedChunk(readOffset, key, usedIV);
|
buffer = readDecryptedChunk(readOffset, key, usedIV);
|
||||||
maxCopySize = BLOCK_SIZE - blockOffset;
|
maxCopySize = BLOCK_SIZE - blockOffset;
|
||||||
copySize = (usedSize > maxCopySize) ? maxCopySize : usedSize;
|
copySize = (usedSize > maxCopySize) ? maxCopySize : usedSize;
|
||||||
|
|
||||||
outputStream.write(Arrays.copyOfRange(buffer, (int) blockOffset, (int) copySize));
|
try {
|
||||||
|
outputStream.write(Arrays.copyOfRange(buffer, (int) blockOffset, (int) (blockOffset + copySize)));
|
||||||
|
} catch (IOException e) {
|
||||||
|
if (e.getMessage().equals("Pipe closed")) {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
totalread += copySize;
|
totalread += copySize;
|
||||||
|
|
||||||
// update counters
|
// update counters
|
||||||
@ -145,6 +166,12 @@ public abstract class WUDDiscReader {
|
|||||||
outputStream.close();
|
outputStream.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new RandomAccessFileStream
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* @throws FileNotFoundException
|
||||||
|
*/
|
||||||
public RandomAccessFile getRandomAccessFileStream() throws FileNotFoundException {
|
public RandomAccessFile getRandomAccessFileStream() throws FileNotFoundException {
|
||||||
if (getImage() == null || getImage().getFileHandle() == null) {
|
if (getImage() == null || getImage().getFileHandle() == null) {
|
||||||
log.warning("No image or image filehandle set.");
|
log.warning("No image or image filehandle set.");
|
||||||
|
Loading…
Reference in New Issue
Block a user