mirror of
https://github.com/Maschell/JNUSLib.git
synced 2024-06-16 05:58:48 +02:00
Add support for reading wudmad archive files
This commit is contained in:
parent
75a280ece5
commit
07430cc2c7
|
@ -102,7 +102,7 @@ public final class WUDService {
|
|||
WUDImageCompressedInfo info = new WUDImageCompressedInfo(WUDImageCompressedInfo.SECTOR_SIZE, 0, toReadFilesize);
|
||||
|
||||
byte[] header = info.getHeaderAsBytes();
|
||||
log.info("Writing header + " + header.length);
|
||||
log.info("Writing header");
|
||||
fileOutput.write(header);
|
||||
|
||||
int sectorTableEntryCount = (int) ((toReadFilesize + WUDImageCompressedInfo.SECTOR_SIZE - 1) / (long) WUDImageCompressedInfo.SECTOR_SIZE);
|
||||
|
|
|
@ -27,6 +27,7 @@ import de.mas.wiiu.jnus.implementations.wud.reader.WUDDiscReader;
|
|||
import de.mas.wiiu.jnus.implementations.wud.reader.WUDDiscReaderCompressed;
|
||||
import de.mas.wiiu.jnus.implementations.wud.reader.WUDDiscReaderSplitted;
|
||||
import de.mas.wiiu.jnus.implementations.wud.reader.WUDDiscReaderUncompressed;
|
||||
import de.mas.wiiu.jnus.implementations.wud.reader.WUDDiscReaderWumadArchive;
|
||||
import de.mas.wiiu.jnus.utils.ByteUtils;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
@ -41,6 +42,7 @@ public class WUDImage {
|
|||
|
||||
@Getter private final boolean isCompressed;
|
||||
@Getter private final boolean isSplitted;
|
||||
@Getter private final boolean isWUMADArchiveFormat;
|
||||
|
||||
private long inputFileSize = 0L;
|
||||
@Getter private final WUDDiscReader WUDDiscReader;
|
||||
|
@ -57,6 +59,9 @@ public class WUDImage {
|
|||
|
||||
RandomAccessFile fileStream = new RandomAccessFile(file, "r");
|
||||
fileStream.seek(0);
|
||||
|
||||
this.fileHandle = file;
|
||||
|
||||
byte[] wuxheader = new byte[WUDImageCompressedInfo.WUX_HEADER_SIZE];
|
||||
fileStream.read(wuxheader);
|
||||
WUDImageCompressedInfo compressedInfo = new WUDImageCompressedInfo(wuxheader);
|
||||
|
@ -65,6 +70,7 @@ public class WUDImage {
|
|||
log.fine("Image is compressed");
|
||||
this.isCompressed = true;
|
||||
this.isSplitted = false;
|
||||
this.isWUMADArchiveFormat = false;
|
||||
Map<Integer, Long> indexTable = new HashMap<>();
|
||||
long offsetIndexTable = compressedInfo.getOffsetIndexTable();
|
||||
fileStream.seek(offsetIndexTable);
|
||||
|
@ -80,6 +86,12 @@ public class WUDImage {
|
|||
setCompressedInfo(compressedInfo);
|
||||
} else {
|
||||
this.isCompressed = false;
|
||||
int magic0 = ByteUtils.getIntFromBytes(wuxheader, 0x00, ByteOrder.BIG_ENDIAN);
|
||||
if (magic0 == 0x57696920) {
|
||||
this.isWUMADArchiveFormat = true;
|
||||
}else {
|
||||
this.isWUMADArchiveFormat = false;
|
||||
}
|
||||
if (file.getName().equals(String.format(WUDDiscReaderSplitted.WUD_SPLITTED_DEFAULT_FILEPATTERN, 1))
|
||||
&& (file.length() == WUDDiscReaderSplitted.WUD_SPLITTED_FILE_SIZE)) {
|
||||
this.isSplitted = true;
|
||||
|
@ -93,12 +105,13 @@ public class WUDImage {
|
|||
this.WUDDiscReader = new WUDDiscReaderCompressed(this, readOffset);
|
||||
} else if (isSplitted()) {
|
||||
this.WUDDiscReader = new WUDDiscReaderSplitted(this, readOffset);
|
||||
} else if ((isWUMADArchiveFormat())) {
|
||||
this.WUDDiscReader = new WUDDiscReaderWumadArchive(this, readOffset);
|
||||
} else {
|
||||
this.WUDDiscReader = new WUDDiscReaderUncompressed(this, readOffset);
|
||||
}
|
||||
|
||||
fileStream.close();
|
||||
this.fileHandle = file;
|
||||
|
||||
}
|
||||
|
||||
public long getWUDFileSize() {
|
||||
|
@ -107,6 +120,8 @@ public class WUDImage {
|
|||
inputFileSize = calculateSplittedFileSize();
|
||||
} else if (isCompressed()) {
|
||||
inputFileSize = getCompressedInfo().getUncompressedSize();
|
||||
} else if (isWUMADArchiveFormat()){
|
||||
inputFileSize = WUDImage.WUD_FILESIZE;
|
||||
} else {
|
||||
inputFileSize = getFileHandle().length();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,176 @@
|
|||
/****************************************************************************
|
||||
* Copyright (C) 2016-2019 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.wud.reader;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.ByteOrder;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Optional;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import de.mas.wiiu.jnus.implementations.wud.WUDImage;
|
||||
import de.mas.wiiu.jnus.utils.ByteUtils;
|
||||
import de.mas.wiiu.jnus.utils.StreamUtils;
|
||||
import lombok.val;
|
||||
|
||||
public class WUDDiscReaderWumadArchive extends WUDDiscReader {
|
||||
private final Map<Long, WUMADAOffsetInfo> offsetMap = new TreeMap<>();
|
||||
private final int WUMADA_HEADER_SIZE = 0x10000;
|
||||
|
||||
public WUDDiscReaderWumadArchive(WUDImage image, long baseOffset) throws IOException {
|
||||
super(image, baseOffset);
|
||||
|
||||
FileInputStream input = new FileInputStream(getImage().getFileHandle());
|
||||
StreamUtils.skipExactly(input, 0 + this.getBaseOffset());
|
||||
|
||||
byte[] rawHeader = StreamUtils.getBytesFromStream(input, WUMADA_HEADER_SIZE);
|
||||
|
||||
int fileTableOffset = 0x1000;
|
||||
int sizeSectorSize = 0x8000;
|
||||
int offsetSectorSize = 0x800;
|
||||
|
||||
long curOffsetInFile = fileTableOffset;
|
||||
long curOffset = WUMADA_HEADER_SIZE;
|
||||
|
||||
// Mapping.
|
||||
while (true) {
|
||||
long curValue = ByteUtils.getUnsingedIntFromBytes(rawHeader, (int) curOffsetInFile, ByteOrder.LITTLE_ENDIAN);
|
||||
if (curValue == 0 && curOffsetInFile != fileTableOffset) {
|
||||
break;
|
||||
}
|
||||
curOffsetInFile += 4;
|
||||
long size = ByteUtils.getUnsingedIntFromBytes(rawHeader, (int) curOffsetInFile, ByteOrder.LITTLE_ENDIAN);
|
||||
curOffsetInFile += 4;
|
||||
|
||||
offsetMap.put(curValue * offsetSectorSize, new WUMADAOffsetInfo(curOffset, size * sizeSectorSize, curValue * offsetSectorSize, false));
|
||||
curOffset += size * sizeSectorSize;
|
||||
}
|
||||
|
||||
// Fill in empty regions
|
||||
long calculatedOffset = 0;
|
||||
Map<Long, WUMADAOffsetInfo> offsetMapEmptyRegions = new TreeMap<>();
|
||||
long lastEntry = 0;
|
||||
for (val curEntry : offsetMap.entrySet()) {
|
||||
long offsetOnDisc = curEntry.getKey();
|
||||
if (offsetOnDisc != calculatedOffset) {
|
||||
offsetMapEmptyRegions.put(calculatedOffset, new WUMADAOffsetInfo(0, offsetOnDisc - calculatedOffset, calculatedOffset, true));
|
||||
calculatedOffset += offsetOnDisc - calculatedOffset;
|
||||
}
|
||||
calculatedOffset += curEntry.getValue().getSize();
|
||||
lastEntry = calculatedOffset;
|
||||
}
|
||||
if (lastEntry < WUDImage.WUD_FILESIZE) {
|
||||
offsetMap.put(lastEntry, new WUMADAOffsetInfo(0, WUDImage.WUD_FILESIZE - lastEntry, lastEntry, true));
|
||||
}
|
||||
offsetMap.putAll(offsetMapEmptyRegions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long readEncryptedToStream(OutputStream outputStream, long offset, long size) throws IOException {
|
||||
FileInputStream input = new FileInputStream(getImage().getFileHandle());
|
||||
|
||||
WUMADAOffsetInfo curOffsetInfo = getOffsetInfoForOffset(offset).orElseThrow(() -> new IOException("Invalid read"));
|
||||
|
||||
long targetOffsetOfSectionStart = curOffsetInfo.getTargetOffset();
|
||||
long inSectionOffset = offset - targetOffsetOfSectionStart;
|
||||
|
||||
if (inSectionOffset > curOffsetInfo.getSize()) {
|
||||
throw new IOException("offset > size");
|
||||
}
|
||||
|
||||
long realOffset = inSectionOffset + curOffsetInfo.getOffset();
|
||||
|
||||
StreamUtils.skipExactly(input, realOffset + this.getBaseOffset());
|
||||
|
||||
int bufferSize = 0x8000;
|
||||
byte[] buffer = new byte[bufferSize];
|
||||
long totalread = 0;
|
||||
long readInSection = 0;
|
||||
long maximumToRead = curOffsetInfo.getSize() - inSectionOffset;
|
||||
do {
|
||||
int read = 0;
|
||||
if (curOffsetInfo.isEmptyContent()) {
|
||||
Arrays.fill(buffer, (byte) 0);
|
||||
read = bufferSize;
|
||||
} else {
|
||||
read = input.read(buffer);
|
||||
if (read < 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
readInSection += read;
|
||||
// System.out.println(readInSection + " read in section " + maximumToRead);
|
||||
|
||||
if (readInSection >= maximumToRead) {
|
||||
|
||||
read -= (int) (readInSection - maximumToRead);
|
||||
long offsetRead = offset + totalread + read;
|
||||
if (offsetRead < offset + size) {
|
||||
curOffsetInfo = getOffsetInfoForOffset(offsetRead).orElseThrow(() -> new IOException("Invalid read for offset " + offsetRead));
|
||||
maximumToRead = curOffsetInfo.getSize();
|
||||
readInSection = 0;
|
||||
|
||||
input.close();
|
||||
input = new FileInputStream(getImage().getFileHandle());
|
||||
StreamUtils.skipExactly(input, curOffsetInfo.getOffset() + this.getBaseOffset());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (totalread + read > size) {
|
||||
read = (int) (size - totalread);
|
||||
}
|
||||
try {
|
||||
outputStream.write(Arrays.copyOfRange(buffer, 0, read));
|
||||
} catch (IOException e) {
|
||||
if (e.getMessage().equals("Pipe closed")) {
|
||||
break;
|
||||
} else {
|
||||
input.close();
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
totalread += read;
|
||||
} while (totalread < size);
|
||||
input.close();
|
||||
outputStream.close();
|
||||
return totalread;
|
||||
}
|
||||
|
||||
private Optional<WUMADAOffsetInfo> getOffsetInfoForOffset(long offset) {
|
||||
Optional<WUMADAOffsetInfo> curOffsetInfo = Optional.empty();
|
||||
for (Entry<Long, WUMADAOffsetInfo> test : offsetMap.entrySet()) {
|
||||
val start = test.getValue().getTargetOffset();
|
||||
val end = start + test.getValue().getSize();
|
||||
|
||||
if (offset < start) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (offset < end) {
|
||||
curOffsetInfo = Optional.of(test.getValue());
|
||||
break;
|
||||
}
|
||||
}
|
||||
return curOffsetInfo;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package de.mas.wiiu.jnus.implementations.wud.reader;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class WUMADAOffsetInfo {
|
||||
private final long offset;
|
||||
private final long size;
|
||||
private final long targetOffset;
|
||||
private final boolean emptyContent;
|
||||
}
|
Loading…
Reference in New Issue
Block a user