✨ Add inflation of files
This commit is contained in:
parent
ad8d88a066
commit
614aa91c94
7 changed files with 175 additions and 44 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -1,2 +1,2 @@
|
||||||
.vscode
|
.vscode/
|
||||||
patcher-installer
|
patcher-installer
|
||||||
|
|
7
.vscode/settings.json
vendored
7
.vscode/settings.json
vendored
|
@ -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"
|
||||||
|
]
|
||||||
}
|
}
|
|
@ -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;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
98
src/inflate.c
Normal 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
10
src/inflate.h
Normal 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);
|
79
src/main.c
79
src/main.c
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue