Add inflation of files

This commit is contained in:
C-3PO 2018-07-31 13:18:18 +02:00
parent ad8d88a066
commit 614aa91c94
Signed by: c3po
GPG key ID: 62993C4BB4D86F24
7 changed files with 175 additions and 44 deletions

2
.gitignore vendored
View file

@ -1,2 +1,2 @@
.vscode .vscode/
patcher-installer patcher-installer

View file

@ -4,5 +4,10 @@
"decrypt.h": "c", "decrypt.h": "c",
"stdint.h": "c", "stdint.h": "c",
"decryptutilities.h": "c" "decryptutilities.h": "c"
} },
"cSpell.words": [
"init",
"miniz",
"tinfl"
]
} }

View file

@ -32,13 +32,8 @@ void initFileReader(char path[], unsigned long offset) {
//Reads the given amount of bytes from the file and returns them. Automatically opens next file if EOF is reached. //Reads the given amount of bytes from the file and returns them. Automatically opens next file if EOF is reached.
char* getBytes(unsigned long numBytes) { void getBytes(char* buffer, unsigned long numBytes) {
char* output = malloc(numBytes); char* bufferPosition = buffer;
if (output == NULL) {
fprintf(stderr, "Could not allocate %lu bytes.\n", numBytes);
exit(1);
}
char* bufferPosition = output;
unsigned long remainingBytes = numBytes; unsigned long remainingBytes = numBytes;
//As long as we still need to read bytes //As long as we still need to read bytes
@ -54,6 +49,4 @@ char* getBytes(unsigned long numBytes) {
openNextFile(); openNextFile();
} }
} }
return output;
} }

View file

@ -2,4 +2,4 @@
void initFileReader(char path[], unsigned long offset); void initFileReader(char path[], unsigned long offset);
char* getBytes(unsigned long numBytes); void getBytes(char* buffer, unsigned long numBytes);

98
src/inflate.c Normal file
View file

@ -0,0 +1,98 @@
//Based on https://github.com/richgel999/miniz/blob/master/examples/example5.c (Public domain, April 11 2012, Rich Geldreich, richgel99@gmail.com)
#include <stdbool.h>
#include <stdio.h>
//Include miniz, disabling what we don't need
#define MINIZ_NO_STDIO
#define MINIZ_NO_ARCHIVE_APIS
#define MINIZ_NO_TIME
#define MINIZ_NO_ZLIB_APIS
#define MINIZ_NO_MALLOC
#include "../lib/miniz/miniz.h"
#include "inflate.h"
//The compressed and uncompressed buffer arrays
char* comprBuffer;
char* uncomprBuffer;
//Position of the inflater in the buffer arrays
char* comprBufferNext;
char* uncomprBufferNext;
//Buffer lengths
unsigned long uncomprBufferSize;
unsigned long remainingInput;
unsigned long spaceInOutput;
//Internal variables used by miniz
tinfl_decompressor inflator;
tdefl_status status;
size_t in_bytes, out_bytes;
void inflateInit(char* comprBufferIn, char* uncomprBufferIn, unsigned long uncomprBufferSizeIn) {
comprBuffer = comprBufferIn;
uncomprBuffer = uncomprBufferIn;
uncomprBufferSize = uncomprBufferSizeIn;
tinfl_init(&inflator);
}
struct InflateOutput inflateInflate(unsigned long numInputBytes, bool hasMoreBytes) {
if (numInputBytes == 0) {
//continue in input buffer where we were before
} else {
//go back to beginning of input buffer
remainingInput = numInputBytes;
comprBufferNext = comprBuffer;
}
//output buffer has been emptied
spaceInOutput = uncomprBufferSize;
uncomprBufferNext = uncomprBuffer;
struct InflateOutput out;
while (remainingInput > 0 && spaceInOutput > 0) {
in_bytes = remainingInput;
out_bytes = spaceInOutput;
//Call decompression
status = tinfl_decompress(
&inflator,
(const mz_uint8 *)comprBufferNext,
&in_bytes,
uncomprBuffer,
(mz_uint8 *)uncomprBufferNext,
&out_bytes,
(hasMoreBytes ? TINFL_FLAG_HAS_MORE_INPUT : 0)
);
//Adjust buffer positions by the amount of bytes processed
comprBufferNext += in_bytes;
remainingInput -= in_bytes;
uncomprBufferNext += out_bytes;
spaceInOutput -= out_bytes;
//Check for errors
if (status <= TINFL_STATUS_DONE) {
if (status == TINFL_STATUS_DONE) {
// Decompression completed successfully.
break;
} else {
// Decompression failed.
fprintf(stderr, "tinfl_decompress() failed with status %i!\n", status);
exit(1);
}
}
//If output buffer is filled
if (spaceInOutput == 0) {
break;
}
}
//Return output
if (remainingInput == 0) {
out.needMoreInput = hasMoreBytes;
}
out.numBytesWrittenToOutput = uncomprBufferSize - spaceInOutput;
return out;
}

10
src/inflate.h Normal file
View file

@ -0,0 +1,10 @@
#pragma once
void inflateInit(char* comprBuffer, char* uncomprBuffer, unsigned long uncomprBufferSize);
struct InflateOutput {
bool needMoreInput;
unsigned long numBytesWrittenToOutput;
};
struct InflateOutput inflateInflate(unsigned long numInputBytes, bool hasMoreBytes);

View file

@ -3,13 +3,15 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <unistd.h>
//Import our code //Import our code
#include "decrypt.h" #include "decrypt.h"
#include "fileReader.h" #include "fileReader.h"
#include "inflate.h"
#include "utils/min.h" #include "utils/min.h"
#define BUFFER_SIZE 0xffffUL #define BUFFER_SIZE 512UL * 1024UL //512 KiB
#define ENCRYPTION_HEADER_LENGTH 12UL #define ENCRYPTION_HEADER_LENGTH 12UL
#define LOCAL_FILE_HEADER_MAGIC (uint32_t)0x04034b50 #define LOCAL_FILE_HEADER_MAGIC (uint32_t)0x04034b50
@ -45,55 +47,78 @@ int main(int argc, unsigned char *argv[]) {
//------------------------------------------------- //-------------------------------------------------
//Initialize file reader char* compressedChunk = malloc(BUFFER_SIZE);
initFileReader(archiveName, archiveOffset); if (compressedChunk == NULL) {
fprintf(stderr, "Could not allocate %lu bytes for compressed buffer.\n", BUFFER_SIZE);
//Skip local file header (30 bytes + additional length)
char* fileHeader = getBytes(30UL);
//Check that header is correct
const uint32_t magic = getUint32(fileHeader);
if (magic != LOCAL_FILE_HEADER_MAGIC) {
fprintf(stderr, "Wrong magic in local file header, expected %#010x but found %#010x.", LOCAL_FILE_HEADER_MAGIC, magic);
exit(1); exit(1);
} }
free(fileHeader);
//Read additional length
const unsigned long additionalLength = getUint16(fileHeader + 26) + getUint16(fileHeader + 28);
char* additionalBytes = getBytes(additionalLength);
free(additionalBytes);
//If file is encrypted, skip 12-byte encryption header char* uncompressedChunk = malloc(BUFFER_SIZE);
if (isEncrypted) { if (uncompressedChunk == NULL) {
char* encrHeader = getBytes(ENCRYPTION_HEADER_LENGTH); fprintf(stderr, "Could not allocate %lu bytes for uncompressed buffer.\n", BUFFER_SIZE);
decrypt(encrHeader, ENCRYPTION_HEADER_LENGTH); exit(1);
free(encrHeader);
} }
//------------------------------------------------- //-------------------------------------------------
//Initialize file reader
initFileReader(archiveName, archiveOffset);
//Skip local file header (30 bytes + additional length)
getBytes(compressedChunk, 30UL);
//Check that header is correct
const uint32_t magic = getUint32(compressedChunk);
if (magic != LOCAL_FILE_HEADER_MAGIC) {
fprintf(stderr, "Wrong magic in local file header, expected %#010x but found %#010x.", LOCAL_FILE_HEADER_MAGIC, magic);
exit(1);
}
//Read additional length
const unsigned long additionalLength = getUint16(compressedChunk + 26) + getUint16(compressedChunk + 28);
getBytes(compressedChunk, additionalLength);
//If file is encrypted, skip 12-byte encryption header
if (isEncrypted) {
getBytes(compressedChunk, ENCRYPTION_HEADER_LENGTH);
decrypt(compressedChunk, ENCRYPTION_HEADER_LENGTH);
}
//-------------------------------------------------
struct InflateOutput inflateResult;
inflateInit(compressedChunk, uncompressedChunk, BUFFER_SIZE);
//Read actual file //Read actual file
unsigned long remainingBytes = fileLength; unsigned long remainingBytes = fileLength;
bool needToRead = true;
unsigned long chunkSize;
while (remainingBytes > 0) { while (remainingBytes > 0) {
const unsigned long chunkSize = min(BUFFER_SIZE, remainingBytes); if (needToRead) {
char* chunk = getBytes(chunkSize); chunkSize = min(BUFFER_SIZE, remainingBytes);
getBytes(compressedChunk, chunkSize);
remainingBytes -= chunkSize; remainingBytes -= chunkSize;
//Decrypt file if it is encrypted //Decrypt file if it is encrypted
if (isEncrypted) { if (isEncrypted) {
//TODO: For highest performance, we need to move if condition outside of while loop //TODO: For highest performance, we need to move if condition outside of while loop
decrypt(chunk, chunkSize); decrypt(compressedChunk, chunkSize);
}
} }
//Decompress file //Decompress file
//TODO //bytes are contained in uncompressedChunk from [0, inflateResult.numBytesWrittenToOutput - 1]
inflateResult = inflateInflate(needToRead ? chunkSize : 0, remainingBytes > 0);
needToRead = inflateResult.needMoreInput;
write(1, uncompressedChunk, inflateResult.numBytesWrittenToOutput);
//Optionally perform xdelta3 //Optionally perform xdelta3
//TODO //TODO
//release memory
//TODO: need to malloc once outside of while loop, and then reuse it instead of a new malloc() each loop
free(chunk);
} }
//release memory
free(compressedChunk);
free(uncompressedChunk);
return 0; return 0;
} }