#include #include #include #include #include #include //Import our code #include "utils/min.h" #include "decrypt.h" #include "errorAndExit.h" #include "fileReader.h" #include "inflate.h" #include "parseArguments.h" #include "rateLimiter.h" #include "xdelta3.h" //The size of the buffers where the compressed and uncompressed data is stored #define BUFFER_SIZE 512UL * 1024UL //512 KiB //If a file is encrypted, there are 12 random bytes before the start of the file contents #define ENCRYPTION_HEADER_LENGTH 12UL //Each local file header in a .zip file has the magic bytes "50 4b 03 04" at the beginning #define LOCAL_FILE_HEADER_MAGIC (uint32_t)0x04034b50 //Convenience functions for reading integers from a char array uint16_t getUint16(uint8_t* buffer) { return ((uint16_t)buffer[0]) | \ ((uint16_t)buffer[1]) << 8; } uint32_t getUint32(uint8_t* buffer) { return ((uint32_t)buffer[0]) | \ ((uint32_t)buffer[1]) << 8 | \ ((uint32_t)buffer[2]) << 16 | \ ((uint32_t)buffer[3]) << 24; } int main(int argc, char *argv[]) { //Parse command line arguments struct arguments state = parseArguments(argc, argv); //------------------------------------------------- uint8_t* compressedChunk = malloc(BUFFER_SIZE); if (compressedChunk == NULL) { fprintf(stderr, "Could not allocate %lu bytes for compressed buffer.\n", BUFFER_SIZE); errorAndExit(); } uint8_t* uncompressedChunk = malloc(BUFFER_SIZE); if (uncompressedChunk == NULL) { fprintf(stderr, "Could not allocate %lu bytes for uncompressed buffer.\n", BUFFER_SIZE); errorAndExit(); } memset(uncompressedChunk, (uint8_t)0, BUFFER_SIZE); //------------------------------------------------- //Initialize file reader initFileReader(state.diskName, state.diskOffset); //Skip local file header (30 bytes + additional length) getBytes(compressedChunk, 30UL, false); //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.\n", LOCAL_FILE_HEADER_MAGIC, magic); errorAndExit(); } //Read additional length const unsigned long additionalLength = getUint16(compressedChunk + 26) + getUint16(compressedChunk + 28); if (additionalLength > 0UL) { getBytes(NULL, additionalLength, false); } unsigned long remainingBytes = state.fileSize; //If file is encrypted, skip 12-byte encryption header if (state.isEncrypted) { getBytes(compressedChunk, ENCRYPTION_HEADER_LENGTH, false); decrypt(compressedChunk, ENCRYPTION_HEADER_LENGTH); remainingBytes -= 12; } //Initialize xdelta3 if (state.prevFile) { xdelta3Init(state.prevFile); } //If a target path is specified, create a file write stream FILE* targetFile; if (state.target) { targetFile = fopen(state.target, "wb"); if (!targetFile) { fprintf(stderr, "Could not open the target file \"%s\" for writing: %s\n", state.target, strerror(errno)); errorAndExit(); } } //------------------------------------------------- struct InflateOutput inflateResult; inflateInit(compressedChunk, uncompressedChunk, BUFFER_SIZE); //Read actual file bool needToRead = true; bool hasReachedEnd = false; unsigned long chunkSize; unsigned long uncompressedPosition = 0UL; while (remainingBytes > 0 || !hasReachedEnd) { if (needToRead && remainingBytes > 0) { chunkSize = min(BUFFER_SIZE, remainingBytes); getBytes(compressedChunk, chunkSize, chunkSize == remainingBytes); remainingBytes -= chunkSize; //Decrypt file if it is encrypted if (state.isEncrypted) { decrypt(compressedChunk, chunkSize); } } //Decompress file //bytes are contained in uncompressedChunk from [0, inflateResult.numBytesWrittenToOutput - 1] inflateResult = inflateInflate(needToRead ? chunkSize : 0, remainingBytes > 0); needToRead = inflateResult.needMoreInput; hasReachedEnd = inflateResult.hasReachedEnd; //important: we must not modify uncompressedChunk since miniz may use it as dictionary and read from it during the next invocation of inflateInflate() uint8_t* dataBuffer = uncompressedChunk + uncompressedPosition; unsigned long numBytes = inflateResult.numBytesWrittenToOutput; //Optionally perform xdelta3 if (state.prevFile) { xdelta3AddInput(dataBuffer, numBytes, remainingBytes == 0 && hasReachedEnd, targetFile); } else { //otherwise write to target file or stdout consumeCapacity(numBytes); if (state.target) { fwrite(dataBuffer, 1, numBytes, targetFile); } else { write(1, dataBuffer, numBytes); } } uncompressedPosition += inflateResult.numBytesWrittenToOutput; while (uncompressedPosition >= BUFFER_SIZE) { uncompressedPosition -= BUFFER_SIZE; } } //release memory free(compressedChunk); free(uncompressedChunk); return 0; }