Skip to content

Commit

Permalink
add support for extraction of IMPLODED flagged blocks
Browse files Browse the repository at this point in the history
  • Loading branch information
Frotty committed Jul 15, 2018
1 parent d7402d8 commit 51231ce
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 11 deletions.
70 changes: 62 additions & 8 deletions src/main/java/systems/crigges/jmpq3/MpqFile.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ public class MpqFile {
public static final int ADJUSTED_ENCRYPTED = 0x00020000;
public static final int EXISTS = 0x80000000;
public static final int DELETED = 0x02000000;
public static final int IMPLODED = 0x00000100;

private ByteBuffer buf;
private Block block;
Expand Down Expand Up @@ -83,6 +84,52 @@ public void extractToOutputStream(OutputStream writer) throws IOException {
writer.close();
return;
}
if (extractImplodedBlock(writer)) return;
if (extractSingleUnitBlock(writer)) return;
if (block.hasFlag(COMPRESSED)) {
extractCompressedBlock(writer);
} else {
check(writer);
}
}

private void extractCompressedBlock(OutputStream writer) throws IOException {
buf.position(0);
byte[] sot = new byte[sectorCount * 4];
buf.get(sot);
if (isEncrypted) {
new MPQEncryption(baseKey - 1, true).processSingle(ByteBuffer.wrap(sot));
}
ByteBuffer sotBuffer = ByteBuffer.wrap(sot).order(ByteOrder.LITTLE_ENDIAN);
int start = sotBuffer.getInt();
int end = sotBuffer.getInt();
int finalSize = 0;
for (int i = 0; i < sectorCount - 1; i++) {
buf.position(0 + start);
byte[] arr = getSectorAsByteArray(buf, end - start);
if (isEncrypted) {
new MPQEncryption(baseKey + i, true).processSingle(ByteBuffer.wrap(arr));
}
if (block.getNormalSize() - finalSize <= sectorSize) {
arr = decompressSector(arr, end - start, block.getNormalSize() - finalSize);
} else {
arr = decompressSector(arr, end - start, sectorSize);
}
writer.write(arr);

finalSize += sectorSize;
start = end;
try {
end = sotBuffer.getInt();
} catch (BufferUnderflowException e) {
break;
}
}
writer.flush();
writer.close();
}

private boolean extractSingleUnitBlock(OutputStream writer) throws IOException {
if (block.hasFlag(SINGLE_UNIT)) {
if (block.hasFlag(COMPRESSED)) {
buf.position(0);
Expand All @@ -97,17 +144,20 @@ public void extractToOutputStream(OutputStream writer) throws IOException {
} else {
check(writer);
}
return;
return true;
}
if (block.hasFlag(COMPRESSED)) {
ByteBuffer sotBuffer = null;
return false;
}

private boolean extractImplodedBlock(OutputStream writer) throws IOException {
if (block.hasFlag(IMPLODED)) {
buf.position(0);
byte[] sot = new byte[sectorCount * 4];
buf.get(sot);
if (isEncrypted) {
new MPQEncryption(baseKey - 1, true).processSingle(ByteBuffer.wrap(sot));
}
sotBuffer = ByteBuffer.wrap(sot).order(ByteOrder.LITTLE_ENDIAN);
ByteBuffer sotBuffer = ByteBuffer.wrap(sot).order(ByteOrder.LITTLE_ENDIAN);
int start = sotBuffer.getInt();
int end = sotBuffer.getInt();
int finalSize = 0;
Expand All @@ -118,9 +168,9 @@ public void extractToOutputStream(OutputStream writer) throws IOException {
new MPQEncryption(baseKey + i, true).processSingle(ByteBuffer.wrap(arr));
}
if (block.getNormalSize() - finalSize <= sectorSize) {
arr = decompressSector(arr, end - start, block.getNormalSize() - finalSize);
arr = decompressImplodedSector(arr, end - start, block.getNormalSize() - finalSize);
} else {
arr = decompressSector(arr, end - start, sectorSize);
arr = decompressImplodedSector(arr, end - start, sectorSize);
}
writer.write(arr);

Expand All @@ -134,9 +184,9 @@ public void extractToOutputStream(OutputStream writer) throws IOException {
}
writer.flush();
writer.close();
} else {
check(writer);
return true;
}
return false;
}

private void check(OutputStream writer) throws IOException {
Expand Down Expand Up @@ -348,6 +398,10 @@ private byte[] decompressSector(byte[] sector, int normalSize, int uncompressedS
return CompressionUtil.decompress(sector, normalSize, uncompressedSize);
}

private byte[] decompressImplodedSector(byte[] sector, int normalSize, int uncompressedSize) throws JMpqException {
return CompressionUtil.explode(sector, normalSize, uncompressedSize);
}

@Override
public String toString() {
return "MpqFile [sectorSize=" + sectorSize + ", compressedSize=" + compressedSize + ", normalSize=" + normalSize + ", flags=" + flags + ", name=" + name
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public static byte[] decompress(byte[] sector, int compressedSize, int uncompres
throw new JMpqException("Unsupported compression Bzip2");
} else if (isImploded) {
byte[] output = new byte[uncompressedSize];
Exploder.pkexplode(sector, output);
Exploder.pkexplode(sector, output, 1);
out.put(output);
out.position(0);
flip = !flip;
Expand Down Expand Up @@ -97,4 +97,18 @@ public static byte[] decompress(byte[] sector, int compressedSize, int uncompres
return (flip ? out : in).array();
}
}

public static byte[] explode(byte[] sector, int compressedSize, int uncompressedSize) throws JMpqException {
if (compressedSize == uncompressedSize) {
return sector;
} else {
ByteBuffer out = ByteBuffer.wrap(new byte[uncompressedSize]);

byte[] output = new byte[uncompressedSize];
Exploder.pkexplode(sector, output, 0);
out.put(output);
out.position(0);
return out.array();
}
}
}
4 changes: 2 additions & 2 deletions src/main/java/systems/crigges/jmpq3/compression/Exploder.java
Original file line number Diff line number Diff line change
Expand Up @@ -150,15 +150,15 @@ private static long TRUNCATE_VALUE(long value, int bits) {
0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08
};

public static int pkexplode(byte[] pInBuffer, byte[] pOutBuffer) {
public static int pkexplode(byte[] pInBuffer, byte[] pOutBuffer, int inPos) {
// Compressed data cannot be less than 4 bytes;
// this is not possible in any case whatsoever
if (pInBuffer.length < 4)
throw new IllegalArgumentException("PK_ERR_INCOMPLETE_INPUT: Incomplete input");

int pOutPos = 0;
// This is 1 because in an mpq-sector, the first byte is the compression type flag
int pInPos = 1;
int pInPos = inPos;

// Get header from compressed data
byte nLitSize = pInBuffer[pInPos++];
Expand Down

0 comments on commit 51231ce

Please sign in to comment.