✨ 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
|
||||
|
|
9
.vscode/settings.json
vendored
9
.vscode/settings.json
vendored
|
@ -4,5 +4,10 @@
|
|||
"decrypt.h": "c",
|
||||
"stdint.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.
|
||||
char* getBytes(unsigned long numBytes) {
|
||||
char* output = malloc(numBytes);
|
||||
if (output == NULL) {
|
||||
fprintf(stderr, "Could not allocate %lu bytes.\n", numBytes);
|
||||
exit(1);
|
||||
}
|
||||
char* bufferPosition = output;
|
||||
void getBytes(char* buffer, unsigned long numBytes) {
|
||||
char* bufferPosition = buffer;
|
||||
unsigned long remainingBytes = numBytes;
|
||||
|
||||
//As long as we still need to read bytes
|
||||
|
@ -54,6 +49,4 @@ char* getBytes(unsigned long numBytes) {
|
|||
openNextFile();
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
|
|
@ -2,4 +2,4 @@
|
|||
|
||||
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);
|
87
src/main.c
87
src/main.c
|
@ -3,13 +3,15 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
//Import our code
|
||||
#include "decrypt.h"
|
||||
#include "fileReader.h"
|
||||
#include "inflate.h"
|
||||
#include "utils/min.h"
|
||||
|
||||
#define BUFFER_SIZE 0xffffUL
|
||||
#define BUFFER_SIZE 512UL * 1024UL //512 KiB
|
||||
#define ENCRYPTION_HEADER_LENGTH 12UL
|
||||
#define LOCAL_FILE_HEADER_MAGIC (uint32_t)0x04034b50
|
||||
|
||||
|
@ -45,55 +47,78 @@ int main(int argc, unsigned char *argv[]) {
|
|||
|
||||
//-------------------------------------------------
|
||||
|
||||
//Initialize file reader
|
||||
initFileReader(archiveName, archiveOffset);
|
||||
|
||||
//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);
|
||||
char* compressedChunk = malloc(BUFFER_SIZE);
|
||||
if (compressedChunk == NULL) {
|
||||
fprintf(stderr, "Could not allocate %lu bytes for compressed buffer.\n", BUFFER_SIZE);
|
||||
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
|
||||
if (isEncrypted) {
|
||||
char* encrHeader = getBytes(ENCRYPTION_HEADER_LENGTH);
|
||||
decrypt(encrHeader, ENCRYPTION_HEADER_LENGTH);
|
||||
free(encrHeader);
|
||||
char* uncompressedChunk = malloc(BUFFER_SIZE);
|
||||
if (uncompressedChunk == NULL) {
|
||||
fprintf(stderr, "Could not allocate %lu bytes for uncompressed buffer.\n", BUFFER_SIZE);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
//-------------------------------------------------
|
||||
|
||||
//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
|
||||
unsigned long remainingBytes = fileLength;
|
||||
bool needToRead = true;
|
||||
unsigned long chunkSize;
|
||||
while (remainingBytes > 0) {
|
||||
const unsigned long chunkSize = min(BUFFER_SIZE, remainingBytes);
|
||||
char* chunk = getBytes(chunkSize);
|
||||
remainingBytes -= chunkSize;
|
||||
if (needToRead) {
|
||||
chunkSize = min(BUFFER_SIZE, remainingBytes);
|
||||
getBytes(compressedChunk, chunkSize);
|
||||
remainingBytes -= chunkSize;
|
||||
|
||||
//Decrypt file if it is encrypted
|
||||
if (isEncrypted) {
|
||||
//TODO: For highest performance, we need to move if condition outside of while loop
|
||||
decrypt(chunk, chunkSize);
|
||||
//Decrypt file if it is encrypted
|
||||
if (isEncrypted) {
|
||||
//TODO: For highest performance, we need to move if condition outside of while loop
|
||||
decrypt(compressedChunk, chunkSize);
|
||||
}
|
||||
}
|
||||
|
||||
//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
|
||||
//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;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue