1190 lines
38 KiB
C
1190 lines
38 KiB
C
/* This file is part of REWise.
|
|
*
|
|
* REWise is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* REWise is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <errno.h>
|
|
|
|
#include "inflate.h"
|
|
#include "errors.h"
|
|
#include "print.h"
|
|
|
|
|
|
static HuffmanNode * FixedLiteralTree = NULL;
|
|
static HuffmanNode * FixedDistanceTree = NULL;
|
|
|
|
// Constant defined in the PKZIP APPNOTE (5.5.3 Expanding Huffman Codes)
|
|
// Also here https://www.rfc-editor.org/rfc/rfc1951#page-13
|
|
static unsigned char CODE_LENGTH_ORDER[19] = {
|
|
0x10, 0x11, 0x12, 0x00, 0x08, 0x07, 0x09, 0x06, 0x0A, 0x05, 0x0B, 0x04,
|
|
0x0C, 0x03, 0x0D, 0x02, 0x0E, 0x01, 0x0F
|
|
};
|
|
|
|
// DEFLATE static dictionary (Bit reduction)
|
|
static int LENGTH_CODE_VALUE_OFFSET[286] = {
|
|
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
|
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
|
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
|
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
|
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
|
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
|
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
|
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
|
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
|
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
|
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
|
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
|
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
|
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
|
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
|
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
|
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
|
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
|
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
|
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
|
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
|
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
|
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
|
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
|
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
|
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
|
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
|
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
|
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
|
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
|
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
|
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
|
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
|
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
|
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
|
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
|
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
|
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
|
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
|
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
|
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
|
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
|
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
|
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
|
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
|
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
|
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
|
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
|
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
|
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
|
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
|
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
|
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
|
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
|
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
|
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
|
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
|
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
|
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
|
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
|
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
|
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
|
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
|
0x00000000, 0x00000000, 0x00000000, 0x00000000,
|
|
0x00000000, 0x00000003, 0x00000004, 0x00000005,
|
|
0x00000006, 0x00000007, 0x00000008, 0x00000009,
|
|
0x0000000A, 0x0000000B, 0x0000000D, 0x0000000F,
|
|
0x00000011, 0x00000013, 0x00000017, 0x0000001B,
|
|
0x0000001F, 0x00000023, 0x0000002B, 0x00000033,
|
|
0x0000003B, 0x00000043, 0x00000053, 0x00000063,
|
|
0x00000073, 0x00000083, 0x000000A3, 0x000000C3,
|
|
0x000000E3, 0x00000102
|
|
|
|
};
|
|
|
|
static unsigned char LENGTH_CODE_VALUE_EXTRA_BITS[286] = {
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x01, 0x01, 0x01, 0x01, 0x02, 0x02, 0x02,
|
|
0x02, 0x03, 0x03, 0x03, 0x03, 0x04, 0x04, 0x04,
|
|
0x04, 0x05, 0x05, 0x05, 0x05, 0x00
|
|
};
|
|
|
|
static int DISTANCE_CODE_VALUE_OFFSET[30] = {
|
|
0x00000001, 0x00000002, 0x00000003, 0x00000004,
|
|
0x00000005, 0x00000007, 0x00000009, 0x0000000D,
|
|
0x00000011, 0x00000019, 0x00000021, 0x00000031,
|
|
0x00000041, 0x00000061, 0x00000081, 0x000000C1,
|
|
0x00000101, 0x00000181, 0x00000201, 0x00000301,
|
|
0x00000401, 0x00000601, 0x00000801, 0x00000C01,
|
|
0x00001001, 0x00001801, 0x00002001, 0x00003001,
|
|
0x00004001, 0x00006001
|
|
};
|
|
|
|
static unsigned char DISTANCE_CODE_VALUE_EXTRA_BITS[30] = {
|
|
0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x02,
|
|
0x03, 0x03, 0x04, 0x04, 0x05, 0x05, 0x06, 0x06,
|
|
0x07, 0x07, 0x08, 0x08, 0x09, 0x09, 0x0A, 0x0A,
|
|
0x0B, 0x0B, 0x0C, 0x0C, 0x0D, 0x0D
|
|
};
|
|
|
|
|
|
/* newHuffmanNode() - Create new Huffman node. This allocates memory and inits
|
|
* the new node.
|
|
*
|
|
* @returns: Pointer to the new Huffman node or NULL on error. */
|
|
HuffmanNode * newHuffmanNode(void) {
|
|
HuffmanNode * newNode = NULL;
|
|
newNode = malloc(sizeof(HuffmanNode));
|
|
|
|
if (newNode == NULL) {
|
|
printError("newHuffmanNode errno: %s\n", strerror(errno));
|
|
return NULL;
|
|
}
|
|
|
|
newNode->value = 0xffff;
|
|
newNode->childeren[HUFFMAN_LEFT] = NULL;
|
|
newNode->childeren[HUFFMAN_RIGHT] = NULL;
|
|
newNode->leafes[HUFFMAN_LEFT] = false;
|
|
newNode->leafes[HUFFMAN_RIGHT] = false;
|
|
|
|
return newNode;
|
|
}
|
|
|
|
|
|
/* HuffmanAddCode() - Adds new code to a Huffman tree.
|
|
*
|
|
* @param node : Node to add the code to.
|
|
* @param codeLength: The traverse count to the node we want to set the value
|
|
* to (count from given 'node').
|
|
* @param codeValue : The value to set.
|
|
*
|
|
* @returns: 'true' on success, 'false' on error. */
|
|
bool HuffmanAddCode(HuffmanNode * node, unsigned char codeLength, int codeValue) {
|
|
bool result = false;
|
|
|
|
if (codeLength == 0) {
|
|
printError("HuffmanAddCode codeLength == 0\n");
|
|
return result;
|
|
}
|
|
|
|
// Left child of current node is not a end node
|
|
if (node->leafes[HUFFMAN_LEFT] == false) {
|
|
// Add new left node
|
|
if (node->childeren[HUFFMAN_LEFT] == NULL) {
|
|
HuffmanNode * newNode = newHuffmanNode();
|
|
if (newNode == NULL) {
|
|
return false;
|
|
}
|
|
node->childeren[HUFFMAN_LEFT] = newNode;
|
|
}
|
|
|
|
// Left (new) child is a end node
|
|
if (codeLength == 1) {
|
|
node->leafes[HUFFMAN_LEFT] = true;
|
|
node->childeren[HUFFMAN_LEFT]->value = codeValue;
|
|
result = true;
|
|
}
|
|
else {
|
|
result = HuffmanAddCode(node->childeren[HUFFMAN_LEFT], codeLength - 1, codeValue);
|
|
}
|
|
}
|
|
|
|
if (result == false) {
|
|
// Right child of current node is not a end node
|
|
if (node->leafes[HUFFMAN_RIGHT] == false) {
|
|
// Set left child as a end node ???
|
|
node->leafes[HUFFMAN_LEFT] = true;
|
|
|
|
// Add new right node
|
|
if (node->childeren[HUFFMAN_RIGHT] == NULL) {
|
|
HuffmanNode * newNode = newHuffmanNode();
|
|
if (newNode == NULL) {
|
|
return false;
|
|
}
|
|
node->childeren[HUFFMAN_RIGHT] = newNode;
|
|
}
|
|
|
|
// The new right child is a end node
|
|
if (codeLength == 1) {
|
|
node->leafes[HUFFMAN_RIGHT] = true;
|
|
node->childeren[HUFFMAN_RIGHT]->value = codeValue;
|
|
result = true;
|
|
}
|
|
else {
|
|
result = HuffmanAddCode(node->childeren[HUFFMAN_RIGHT], codeLength - 1, codeValue);
|
|
}
|
|
}
|
|
else {
|
|
node->leafes[HUFFMAN_RIGHT] = true;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
/* huffmanFreeTree() - Free a Huffman tree.
|
|
*
|
|
* @param node: Root node of the Huffman tree. */
|
|
void huffmanFreeTree(HuffmanNode * node) {
|
|
if (node->childeren[HUFFMAN_LEFT] != NULL) {
|
|
huffmanFreeTree(node->childeren[HUFFMAN_LEFT]);
|
|
node->childeren[HUFFMAN_LEFT] = NULL;
|
|
}
|
|
|
|
if (node->childeren[HUFFMAN_RIGHT] != NULL) {
|
|
huffmanFreeTree(node->childeren[HUFFMAN_RIGHT]);
|
|
node->childeren[HUFFMAN_RIGHT] = NULL;
|
|
}
|
|
|
|
free(node);
|
|
}
|
|
|
|
|
|
/* huffmanInitFixedTrees() - Build fixed DEFLATE Huffman tree.
|
|
* NOTE: huffmanFreeFixedTrees() should be called when this has returned true!
|
|
**/
|
|
bool huffmanInitFixedTrees(void) {
|
|
HuffmanNode * literalTree = newHuffmanNode();
|
|
if (literalTree == NULL) {
|
|
return false;
|
|
}
|
|
|
|
// 256 - 279
|
|
for (uint16_t value=256; value < 280; value++) {
|
|
if (HuffmanAddCode(literalTree, 7, value) == false) {
|
|
huffmanFreeTree(literalTree);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// 0 - 143
|
|
for (uint16_t value=0; value < 144; value++) {
|
|
if (HuffmanAddCode(literalTree, 8, value) == false) {
|
|
huffmanFreeTree(literalTree);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// 280 - 287
|
|
for (uint16_t value=280; value < 288; value++) {
|
|
if (HuffmanAddCode(literalTree, 8, value) == false) {
|
|
huffmanFreeTree(literalTree);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// 144 - 255
|
|
for (uint16_t value=144; value < 256; value++) {
|
|
if (HuffmanAddCode(literalTree, 9, value) == false) {
|
|
huffmanFreeTree(literalTree);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
HuffmanNode * distanceTree = newHuffmanNode();
|
|
if (distanceTree == NULL) {
|
|
huffmanFreeTree(literalTree);
|
|
return false;
|
|
}
|
|
|
|
// 0 - 31
|
|
for (unsigned char value=0; value < 32; value++) {
|
|
if (HuffmanAddCode(distanceTree, 5, value) == false) {
|
|
huffmanFreeTree(literalTree);
|
|
huffmanFreeTree(distanceTree);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
FixedLiteralTree = literalTree;
|
|
FixedDistanceTree = distanceTree;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
void huffmanFreeFixedTrees(void) {
|
|
huffmanFreeTree(FixedLiteralTree);
|
|
huffmanFreeTree(FixedDistanceTree);
|
|
FixedLiteralTree = NULL;
|
|
FixedDistanceTree = NULL;
|
|
}
|
|
|
|
|
|
// forward deceleration
|
|
int inflateReadBits(InflateObject * inflateObj, uint8_t bitCount);
|
|
|
|
|
|
// return value larger then 0x11e is error
|
|
/* huffmanReadValue() - Keep reading 1 bit until we arrive at a leaf of the
|
|
* given 'tree' and return that value.
|
|
*
|
|
* @param inflateObj: Inflate descriptor.
|
|
* @param tree : The Huffman tree for resolving the value.
|
|
*
|
|
* @returns: The real value, or 0xffffffff on error. */
|
|
int huffmanReadValue(InflateObject * inflateObj, HuffmanNode * tree) {
|
|
HuffmanNode * node = tree;
|
|
// loop until a leaf node has reached
|
|
while (node->value == 0xffff) {
|
|
int direction = inflateReadBits(inflateObj, 1);
|
|
if (direction > 1) {
|
|
printError("huffmanReadValue inflateReadBits failed\n");
|
|
return 0xffffffff; // error
|
|
}
|
|
if (node->childeren[direction] == NULL) {
|
|
printError("huffmanReadValue the tree is incomplete or invalid bit path "
|
|
"requested.\n");
|
|
return 0xffffffff; // error
|
|
}
|
|
node = node->childeren[direction];
|
|
}
|
|
return node->value;
|
|
}
|
|
|
|
|
|
/* inflateInit() - This should be called on every new InflateObject to init it.
|
|
*
|
|
* @param inflateObj: Inflate descriptor.
|
|
* @param inputFile : Valid and open FILE to the input PE file (installer exe),
|
|
* this may NOT be NULL!
|
|
**/
|
|
void inflateInit(InflateObject * inflateObj, FILE * inputFile) {
|
|
memset(inflateObj->window , 0x00, sizeof(unsigned char) * WINDOW_SIZE);
|
|
memset(inflateObj->chunkBuff, 0x00, sizeof(unsigned char) * CHUNK_SIZE);
|
|
inflateObj->bitOffset = 8; // trigger read new byte
|
|
inflateObj->windowPosition = 0;
|
|
inflateObj->chunkBuffPosition = CHUNK_SIZE; // set to max, to trigger new read
|
|
inflateObj->chunkBuffSize = CHUNK_SIZE;
|
|
inflateObj->inputFile = inputFile;
|
|
inflateObj->outputFile = NULL;
|
|
inflateObj->outputSize = 0;
|
|
inflateObj->crc32 = CRC32_NEW;
|
|
|
|
long inputFileOffset = ftell(inputFile); // backup input positions
|
|
fseek(inputFile, 0L, SEEK_END); // seek to end
|
|
inflateObj->inputFileSize = ftell(inputFile); // store file size
|
|
fseek(inputFile, inputFileOffset, SEEK_SET); // restore input position
|
|
}
|
|
|
|
|
|
/* inflateNew() - Prepare the 'InflateObject' for a new file to extract. This
|
|
* should be called before trying to extract a file.
|
|
*
|
|
* @param inflateObj: Inflate descriptor.
|
|
* @param outputFile: Should be a valid and open FILE to actually inflate to a
|
|
* output file. This may be NULL to not write the inflated
|
|
* data to a file. */
|
|
void inflateNew(InflateObject * inflateObj, FILE * outputFile) {
|
|
inflateObj->bitOffset = 8; // trigger read new byte
|
|
inflateObj->windowPosition = 0;
|
|
inflateObj->chunkBuffPosition = CHUNK_SIZE; // set to max, to trigger new read
|
|
inflateObj->chunkBuffSize = CHUNK_SIZE;
|
|
inflateObj->outputFile = outputFile;
|
|
inflateObj->outputSize = 0;
|
|
inflateObj->crc32 = CRC32_NEW;
|
|
}
|
|
|
|
|
|
/* inflateRead() - Read a byte from the inflate chunk buffer.
|
|
*
|
|
* @returns: A value greater then 0xff on error, else it will return the value.
|
|
**/
|
|
uint16_t inflateRead(InflateObject * inflateObj) {
|
|
// Read new chunk
|
|
if (inflateObj->chunkBuffPosition >= inflateObj->chunkBuffSize) {
|
|
// End Of File
|
|
if (ftell(inflateObj->inputFile) == inflateObj->inputFileSize) {
|
|
printError("inflateRead EOF.\n");
|
|
return 0xffff;
|
|
}
|
|
|
|
// Last chunk of the inputFile is smaller then 16k, adjust the
|
|
// inputBuffSize
|
|
if (inflateObj->chunkBuffPosition > (inflateObj->inputFileSize - ftell(inflateObj->inputFile))) {
|
|
inflateObj->chunkBuffSize = inflateObj->inputFileSize - ftell(inflateObj->inputFile);
|
|
}
|
|
|
|
// Read next chunk
|
|
REWError status = readBytesInto(inflateObj->inputFile, inflateObj->chunkBuff, inflateObj->chunkBuffSize);
|
|
if (status != REWERR_OK) {
|
|
// failed to read next chunk
|
|
printError("inflateRead failed to read next chunk.\n");
|
|
return 0xffff;
|
|
}
|
|
inflateObj->chunkBuffPosition = 0;
|
|
}
|
|
|
|
uint16_t result = (uint16_t)inflateObj->chunkBuff[inflateObj->chunkBuffPosition];
|
|
inflateObj->chunkBuffPosition++;
|
|
return result;
|
|
}
|
|
|
|
|
|
/* inflateReadBits() - Reads 'bitCount' of bits from the chunk buffer.
|
|
*
|
|
* @param inflateObj: Inflate descriptor.
|
|
* @param bitCount : The amount of bits to read. (TODO define maximum bitCount)
|
|
*
|
|
* @returns: The value on success, 0xffffffff on error. */
|
|
int inflateReadBits(InflateObject * inflateObj, uint8_t bitCount) {
|
|
int result = 0;
|
|
int resultOffset = 0;
|
|
|
|
while (bitCount > 0) {
|
|
// Read new byte into buffer
|
|
if (inflateObj->bitOffset == 8) {
|
|
uint16_t readResult = inflateRead(inflateObj);
|
|
if (readResult > 0xff) {
|
|
// inflateRead error
|
|
return 0xffffffff;
|
|
}
|
|
inflateObj->bitBuff = (unsigned char)readResult;
|
|
inflateObj->bitOffset = 0;
|
|
}
|
|
|
|
int mask = 1 << inflateObj->bitOffset;
|
|
if ((inflateObj->bitBuff & mask)) {
|
|
result |= 1 << resultOffset;
|
|
}
|
|
|
|
inflateObj->bitOffset++;
|
|
bitCount--;
|
|
resultOffset++;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
/* huffmanReadDynamicTrees() - Read and build dynamic Huffman trees.
|
|
*
|
|
* @param inflateObj: Inflate descriptor.
|
|
* @param hlit : Literal/Length codes
|
|
* @param hdist : Distance codes
|
|
* @param hclen : Code Length codes
|
|
* @param literalTree : Literal tree destination on success, this should be
|
|
* freed with huffmanFreeTree() on success.
|
|
* @param distanceTree: Distance tree destination on success, this should be
|
|
* freed with huffmanFreeTree() on success.
|
|
*
|
|
* @returns: 'true' on success, 'false' on error. */
|
|
bool huffmanReadDynamicTrees(InflateObject * inflateObj, int hlit, int hdist,
|
|
int hclen, HuffmanNode ** literalTree,
|
|
HuffmanNode ** distanceTree) {
|
|
bool result;
|
|
int repeatAmount;
|
|
unsigned char repeatValue;
|
|
unsigned char maxCodeLength, maxCodeLength2;
|
|
unsigned char codeLengths[19];
|
|
unsigned char codeLengthAlphabet[318];
|
|
unsigned char codeLength;
|
|
int codeValue;
|
|
int readBitsResult;
|
|
|
|
memset(codeLengths, 0x00, sizeof(unsigned char) * 19);
|
|
memset(codeLengthAlphabet, 0x00, sizeof(unsigned char) * 318);
|
|
maxCodeLength = 0;
|
|
|
|
// Read codeLengths
|
|
for (uint16_t i=0; i < hclen; i++) {
|
|
readBitsResult = inflateReadBits(inflateObj, 3);
|
|
if (readBitsResult == 0xffffffff) {
|
|
// inflateReadBits error
|
|
printError("Failed to read dynamic trees.\n");
|
|
return false;
|
|
}
|
|
codeLength = (unsigned char)readBitsResult;
|
|
|
|
if (codeLength > maxCodeLength) {
|
|
maxCodeLength = codeLength;
|
|
}
|
|
|
|
codeLengths[CODE_LENGTH_ORDER[i]] = codeLength;
|
|
}
|
|
|
|
// Build codeLengthTree
|
|
HuffmanNode * codeLengthTree = newHuffmanNode();
|
|
if (codeLengthTree == NULL) {
|
|
printError("huffmanReadDynamicTrees failed to build dynamic code length "
|
|
"tree.\n");
|
|
printError("huffmanReadDynamicTrees errno: %s\n", strerror(errno));
|
|
return false;
|
|
}
|
|
result = false;
|
|
// 1 - 16
|
|
for (codeLength = 0x01; codeLength < 0x10; codeLength++) {
|
|
// 0 - 18
|
|
for (codeValue = 0x00; codeValue < 0x13; codeValue++) {
|
|
if (codeLength == codeLengths[codeValue]) {
|
|
result = HuffmanAddCode(codeLengthTree, codeLength, codeValue);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (result == false) {
|
|
huffmanFreeTree(codeLengthTree);
|
|
printError("huffmanReadDynamicTrees failed to build dynamic code length "
|
|
"tree.\n");
|
|
return false;
|
|
}
|
|
|
|
if (maxCodeLength == 0) {
|
|
maxCodeLength++;
|
|
}
|
|
|
|
result = !HuffmanAddCode(codeLengthTree, maxCodeLength, 0);
|
|
if (result == false) {
|
|
huffmanFreeTree(codeLengthTree);
|
|
printError("huffmanReadDynamicTrees ailed to build dynamic code length "
|
|
"tree. It has nodes without end-node.\n");
|
|
return false;
|
|
}
|
|
|
|
// Build alphabet
|
|
repeatAmount = 0;
|
|
repeatValue = 0;
|
|
maxCodeLength = 0;
|
|
maxCodeLength2 = 0;
|
|
for (codeValue=0; codeValue < (int)(hlit + hdist); codeValue++) {
|
|
if (repeatAmount == 0) {
|
|
// read and decode codeLength
|
|
codeLength = (unsigned char)huffmanReadValue(inflateObj, codeLengthTree);
|
|
|
|
if (codeLength < 0x10) {
|
|
codeLengthAlphabet[codeValue] = codeLength;
|
|
|
|
// Update maxBits/maxBits2
|
|
if (codeValue < hlit) {
|
|
if (codeLength > maxCodeLength) {
|
|
maxCodeLength = codeLength;
|
|
}
|
|
}
|
|
else {
|
|
if (codeLength > maxCodeLength2) {
|
|
maxCodeLength2 = codeLength;
|
|
}
|
|
}
|
|
}
|
|
else if (codeLength == 0x10) {
|
|
readBitsResult = inflateReadBits(inflateObj, 2);
|
|
if (readBitsResult == 0xffffffff) {
|
|
printError("Failed to read dynamic trees.\n");
|
|
return false;
|
|
}
|
|
repeatAmount = 0x02 + readBitsResult;
|
|
repeatValue = codeLengthAlphabet[codeValue - 0x01];
|
|
codeLengthAlphabet[codeValue] = repeatValue;
|
|
}
|
|
else if (codeLength == 0x11) {
|
|
readBitsResult = inflateReadBits(inflateObj, 3);
|
|
if (readBitsResult == 0xffffffff) {
|
|
printError("Failed to read dynamic trees.\n");
|
|
return false;
|
|
}
|
|
repeatAmount = 0x02 + readBitsResult;
|
|
repeatValue = 0;
|
|
codeLengthAlphabet[codeValue] = repeatValue;
|
|
}
|
|
else if (codeLength == 0x12) {
|
|
readBitsResult = inflateReadBits(inflateObj, 7);
|
|
if (readBitsResult == 0xffffffff) {
|
|
printError("Failed to read dynamic trees.\n");
|
|
return false;
|
|
}
|
|
repeatAmount = 0x0A + readBitsResult;
|
|
repeatValue = 0;
|
|
codeLengthAlphabet[codeValue] = repeatValue;
|
|
}
|
|
}
|
|
else {
|
|
codeLengthAlphabet[codeValue] = repeatValue;
|
|
repeatAmount--;
|
|
}
|
|
}
|
|
|
|
// free the codeLengthTree, we don't need it anymore.
|
|
huffmanFreeTree(codeLengthTree);
|
|
|
|
// build literal tree
|
|
HuffmanNode * litTree = newHuffmanNode();
|
|
if (litTree == NULL) {
|
|
printError("huffmanReadDynamicTrees failed to allocate literalTree.\n");
|
|
printError("huffmanReadDynamicTrees errno: %s\n", strerror(errno));
|
|
return false;
|
|
}
|
|
for (codeLength=0x01; codeLength < 0x10; codeLength++) {
|
|
for (codeValue=0; codeValue < hlit; codeValue++) {
|
|
if (codeLength == codeLengthAlphabet[codeValue]) {
|
|
result = HuffmanAddCode(litTree, codeLength, codeValue);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (result == true) {
|
|
if (maxCodeLength == 0) {
|
|
maxCodeLength++;
|
|
}
|
|
result = !HuffmanAddCode(litTree, maxCodeLength, 0);
|
|
if (result == false) {
|
|
huffmanFreeTree(litTree);
|
|
printError("huffmanReadDynamicTrees failed to build dynamic literal "
|
|
"tree (1). It has a open branch without leaf?\n");
|
|
return false;
|
|
}
|
|
}
|
|
else {
|
|
huffmanFreeTree(litTree);
|
|
printError("huffmanReadDynamicTrees failed to build dynamic literal tree "
|
|
"(2).\n");
|
|
return false;
|
|
}
|
|
|
|
// build distance tree
|
|
HuffmanNode * distTree = newHuffmanNode();
|
|
if (distTree == NULL) {
|
|
printError("huffmanReadDynamicTrees failed to allocate distanceTree.\n");
|
|
printError("huffmanReadDynamicTrees errno: %s\n", strerror(errno));
|
|
return false;
|
|
}
|
|
for (codeLength=0x01; codeLength < 0x10; codeLength++) {
|
|
for (codeValue=0; codeValue < hdist; codeValue++) {
|
|
if (codeLength == codeLengthAlphabet[codeValue + hlit]) {
|
|
result = HuffmanAddCode(distTree, codeLength, codeValue);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (result == true) {
|
|
if (maxCodeLength2 == 0) {
|
|
maxCodeLength2++;
|
|
}
|
|
result = !HuffmanAddCode(distTree, maxCodeLength2, 0);
|
|
if (result == false) {
|
|
huffmanFreeTree(litTree);
|
|
huffmanFreeTree(distTree);
|
|
printError("huffmanReadDynamicTrees failed to build dynamic distance "
|
|
"tree (1).\n");
|
|
return false;
|
|
}
|
|
}
|
|
else {
|
|
huffmanFreeTree(litTree);
|
|
huffmanFreeTree(distTree);
|
|
printError("huffmanReadDynamicTrees failed to build dynamic distance tree "
|
|
"(2).\n");
|
|
return false;
|
|
}
|
|
|
|
*literalTree = litTree;
|
|
*distanceTree = distTree;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
#ifdef REWISE_DEV_DEBUG
|
|
void inflateInitStaticTables(void) {
|
|
int lengthOffset = 3;
|
|
unsigned char lengthExtraBits = 0;
|
|
int distanceOffset = 1;
|
|
unsigned char distanceExtraBits = 0;
|
|
|
|
LENGTH_CODE_VALUE_OFFSET[285] = 258; // 258 = 0x102
|
|
LENGTH_CODE_VALUE_EXTRA_BITS[285] = 0;
|
|
|
|
for (unsigned char pos=0; pos < 30; pos++) {
|
|
// increment number of extra bits for length code table every 4th value
|
|
if (pos >= 0x08 && !(pos & 0x03)) {
|
|
lengthExtraBits++;
|
|
}
|
|
|
|
// increment number of extra bits for distance code table every 2nd value
|
|
if (pos >= 0x04 && !(pos & 0x01)) {
|
|
distanceExtraBits++;
|
|
}
|
|
|
|
// for pos<0x1c put value entry into length code table
|
|
if (pos < 0x1c) {
|
|
LENGTH_CODE_VALUE_OFFSET[pos + 0x101] = lengthOffset;
|
|
LENGTH_CODE_VALUE_EXTRA_BITS[pos + 0x101] = lengthExtraBits;
|
|
}
|
|
|
|
// put value entry into distance code table
|
|
DISTANCE_CODE_VALUE_OFFSET[pos] = distanceOffset;
|
|
DISTANCE_CODE_VALUE_EXTRA_BITS[pos] = distanceExtraBits;
|
|
|
|
// increment length and distance code values
|
|
lengthOffset += (1 << lengthExtraBits);
|
|
distanceOffset += (1 << distanceExtraBits);
|
|
}
|
|
}
|
|
|
|
|
|
void inflatePrintStaticTables(void) {
|
|
uint8_t colCount;
|
|
|
|
colCount = 4;
|
|
printf("LENGTH_CODE_VALUE_OFFSET\n");
|
|
for (uint32_t i=0; i<286; i++) {
|
|
printf("0x%08X", LENGTH_CODE_VALUE_OFFSET[i]);
|
|
uint8_t colMod = (i+1) % colCount;
|
|
if (i == 285) { printf("\n"); }
|
|
else if (colMod) { printf(", "); }
|
|
else { printf(",\n"); }
|
|
}
|
|
|
|
colCount = 8;
|
|
printf("\n");
|
|
printf("LENGTH_CODE_VALUE_EXTRA_BITS\n");
|
|
for (uint32_t i=0; i<286; i++) {
|
|
printf("0x%02X", LENGTH_CODE_VALUE_EXTRA_BITS[i]);
|
|
if (i == 285) { printf("\n"); }
|
|
else if ((i+1) % colCount) { printf(", "); }
|
|
else { printf(",\n"); }
|
|
}
|
|
|
|
colCount = 4;
|
|
printf("\n");
|
|
printf("DISTANCE_CODE_VALUE_OFFSET\n");
|
|
for (uint32_t i=0; i<30; i++) {
|
|
printf("0x%08X", DISTANCE_CODE_VALUE_OFFSET[i]);
|
|
if (i == 29) { printf("\n"); }
|
|
else if ((i+1) % colCount) { printf(", "); }
|
|
else { printf(",\n"); }
|
|
}
|
|
|
|
colCount = 8;
|
|
printf("\n");
|
|
printf("DISTANCE_CODE_VALUE_EXTRA_BITS\n");
|
|
for (uint32_t i=0; i<30; i++) {
|
|
printf("0x%02X", DISTANCE_CODE_VALUE_EXTRA_BITS[i]);
|
|
if (i == 29) { printf("\n"); }
|
|
else if ((i+1) % colCount) { printf(", "); }
|
|
else { printf(",\n"); }
|
|
}
|
|
}
|
|
#endif
|
|
|
|
|
|
/* inflateWriteOutput() - Writes the content of the sliding window to the
|
|
* 'InflateObject->outputFile' when it is not 'NULL'.
|
|
*
|
|
* It will also keep track of the total number of bytes
|
|
* written to this file and update the CRC32.
|
|
*
|
|
* This does not reset the sliding window position, the
|
|
* caller is responsible for that when this returns
|
|
* 'true'.
|
|
*
|
|
* @param inflateObj: Inflate descriptor.
|
|
* @param size : Amount of bytes in the sliding window to write.
|
|
*
|
|
* @returns: 'true' on success, 'false' on error. */
|
|
bool inflateWriteOutput(InflateObject * inflateObj, uint32_t size) {
|
|
// Write to output file
|
|
if (inflateObj->outputFile != NULL) {
|
|
size_t noWritten = fwrite(inflateObj->window, sizeof(unsigned char),
|
|
(size_t)size, inflateObj->outputFile);
|
|
if (noWritten != (size_t)size) {
|
|
// Failed to write to file, out of disk space or something got corrupted.
|
|
printError("Failed to write to file, only wrote %ld of %ld bytes. "
|
|
"Out of disk space?\n", noWritten, size);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
inflateObj->outputSize += (long)size;
|
|
|
|
// Update CRC32
|
|
inflateObj->crc32 = crc32Update(inflateObj->crc32, inflateObj->window, size);
|
|
return true;
|
|
}
|
|
|
|
|
|
/* inflateOutputByte() - Appends the given 'byte' to the sliding window, when
|
|
* the sliding window is full it will output the sliding
|
|
* window to 'inflateObj->outputFile' (when it isn't
|
|
* 'NULL') and resets the sliding window.
|
|
*
|
|
* @param inflateObj: Inflate descriptor.
|
|
* @param byte : The 'byte' to output.
|
|
*
|
|
* @returns: 'true' on success, 'false' on error. */
|
|
bool inflateOutputByte(InflateObject * inflateObj, unsigned char byte) {
|
|
inflateObj->window[inflateObj->windowPosition] = byte;
|
|
inflateObj->windowPosition++;
|
|
|
|
if (inflateObj->windowPosition == WINDOW_SIZE) {
|
|
if (inflateWriteOutput(inflateObj, WINDOW_SIZE) == false) {
|
|
return false;
|
|
}
|
|
inflateObj->windowPosition = 0;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
/* inflateCopyBytes() - Copy 'codeLength' amount of bytes (with 'codeDistance'
|
|
* offset) from the sliding window to the end of the
|
|
* sliding window.
|
|
* @param inflateObj : Inflate descriptor.
|
|
* @param codeDistance: Offset from the end of the current sliding window.
|
|
* @param codeLength : Amount of bytes to copy and append to the end of the
|
|
* current sliding window.
|
|
*
|
|
* @returns: 'true' on success, 'false' on error. */
|
|
bool inflateCopyBytes(InflateObject * inflateObj, int codeDistance,
|
|
int codeLength)
|
|
{
|
|
while (codeLength > 0) {
|
|
unsigned char byte = inflateObj->window[
|
|
(inflateObj->windowPosition + WINDOW_SIZE - codeDistance) & 0x7fff];
|
|
if (inflateOutputByte(inflateObj, byte) == false) {
|
|
return false;
|
|
}
|
|
codeLength--;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
/* inflateNext() - Inflate next file.
|
|
*
|
|
* @returns: 'true' on success, 'false' on error. */
|
|
bool inflateNext(InflateObject * inflateObj) {
|
|
unsigned char lastBlock;
|
|
unsigned char blockType;
|
|
int hclen;
|
|
int hdist;
|
|
int hlit;
|
|
|
|
// TODO just for DEBUG
|
|
inflateObj->deflatedStart = ftell(inflateObj->inputFile);
|
|
printDebug("deflatedStart: %08lX\n", inflateObj->deflatedStart);
|
|
|
|
lastBlock = 0;
|
|
while (lastBlock == 0) {
|
|
// read lastBlock
|
|
int readBitsResult = inflateReadBits(inflateObj, 1);
|
|
if (readBitsResult == 0xffffffff) {
|
|
return false;
|
|
}
|
|
lastBlock = (unsigned char)readBitsResult;
|
|
|
|
// read blockType
|
|
readBitsResult = inflateReadBits(inflateObj, 2);
|
|
if (readBitsResult == 0xffffffff) {
|
|
return false;
|
|
}
|
|
blockType = (unsigned char)readBitsResult;
|
|
|
|
if (blockType == BTYPE_UNCOMPRESSED) {
|
|
// Make sure we start on a fresh byte (aligned)
|
|
inflateObj->bitOffset = 8;
|
|
|
|
hclen = inflateRead(inflateObj) + (inflateRead(inflateObj) << 8);
|
|
hdist = inflateRead(inflateObj) + (inflateRead(inflateObj) << 8);
|
|
|
|
if ((hclen ^ hdist) != 0xffff) {
|
|
printError("Code-length or code-distance invalid! 0x%04X hclen: 0x%02X hdist: 0x%02X\n", (hclen ^ hdist), hclen, hdist);
|
|
return false;
|
|
}
|
|
|
|
while (hclen > 0) {
|
|
// read hlit
|
|
uint16_t readResultHlit = inflateRead(inflateObj);
|
|
if (readResultHlit > 255) {
|
|
return false;
|
|
}
|
|
//hlit = (unsigned char)readResult;
|
|
if (inflateOutputByte(inflateObj, (unsigned char)readResultHlit) == false) {
|
|
return false;
|
|
}
|
|
hclen--;
|
|
}
|
|
}
|
|
else
|
|
if (blockType == BTYPE_FIXED) {
|
|
hlit = 0;
|
|
while (hlit != 0x100) {
|
|
hlit = huffmanReadValue(inflateObj, FixedLiteralTree);
|
|
|
|
if (hlit < 0x100) {
|
|
if (inflateOutputByte(inflateObj, (unsigned char)hlit) == false) {
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
if (hlit == 0x100) {
|
|
break;
|
|
}
|
|
else
|
|
if (hlit < 0x11e) {
|
|
// Read code length extra bits
|
|
readBitsResult = inflateReadBits(inflateObj,
|
|
LENGTH_CODE_VALUE_EXTRA_BITS[hlit]);
|
|
if (readBitsResult == 0xffffffff) {
|
|
// inflateReadBits error
|
|
printError("Failed to read fixed length extra bits.\n");
|
|
return false;
|
|
}
|
|
int codeLength = LENGTH_CODE_VALUE_OFFSET[hlit] + readBitsResult;
|
|
int codeDistance = huffmanReadValue(inflateObj, FixedDistanceTree);
|
|
|
|
if (codeDistance > 0x1d) {
|
|
printError("Unexpected code distance 0x%08X\n", codeDistance);
|
|
return false;
|
|
}
|
|
|
|
// Read code distance extra bits
|
|
readBitsResult = inflateReadBits(inflateObj,
|
|
DISTANCE_CODE_VALUE_EXTRA_BITS[codeDistance]);
|
|
if (readBitsResult == 0xffffffff) {
|
|
printError("Failed to read fixed distance extra bits.\n");
|
|
return false;
|
|
}
|
|
codeDistance = DISTANCE_CODE_VALUE_OFFSET[codeDistance] + readBitsResult;
|
|
if (inflateCopyBytes(inflateObj, codeDistance, codeLength) == false) {
|
|
return false;
|
|
}
|
|
}
|
|
else {
|
|
printError("Unexpected literal 0x%08X\n", hlit);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
if (blockType == BTYPE_DYNAMIC) {
|
|
// Read hlit
|
|
readBitsResult = inflateReadBits(inflateObj, 5);
|
|
if (readBitsResult == 0xffffffff) {
|
|
printError("Failed to read dynamic hlit bits.\n");
|
|
return false;
|
|
}
|
|
hlit = readBitsResult + 0x101;
|
|
|
|
// Read hdist
|
|
readBitsResult = inflateReadBits(inflateObj, 5);
|
|
if (readBitsResult == 0xffffffff) {
|
|
printError("Failed to read dynamic hdist bits.\n");
|
|
return false;
|
|
}
|
|
hdist = readBitsResult + 0x01;
|
|
|
|
// Read hclen
|
|
readBitsResult = inflateReadBits(inflateObj, 4);
|
|
if (readBitsResult == 0xffffffff) {
|
|
printError("Failed to read dynamic hclen bits.\n");
|
|
return false;
|
|
}
|
|
hclen = readBitsResult + 0x04;
|
|
|
|
HuffmanNode * literalTree = NULL;
|
|
HuffmanNode * distanceTree = NULL;
|
|
|
|
if (huffmanReadDynamicTrees(inflateObj, hlit, hdist, hclen, &literalTree,
|
|
&distanceTree) == false) {
|
|
printError("Failed to build dynamic trees\n");
|
|
return false;
|
|
}
|
|
|
|
while (hlit != 0x100) {
|
|
hlit = huffmanReadValue(inflateObj, literalTree);
|
|
|
|
if (hlit < 0x100) {
|
|
if (inflateOutputByte(inflateObj, (unsigned char)hlit) == false) {
|
|
huffmanFreeTree(literalTree);
|
|
huffmanFreeTree(distanceTree);
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
if (hlit == 0x100) {
|
|
break;
|
|
}
|
|
else
|
|
if (hlit < 0x11e) {
|
|
// Read code value extra bits
|
|
readBitsResult = inflateReadBits(inflateObj, LENGTH_CODE_VALUE_EXTRA_BITS[hlit]);
|
|
if (readBitsResult == 0xffffffff) {
|
|
huffmanFreeTree(literalTree);
|
|
huffmanFreeTree(distanceTree);
|
|
printError("Failed to read dynamic code value extra bits.\n");
|
|
return false;
|
|
}
|
|
int codeLength = LENGTH_CODE_VALUE_OFFSET[hlit] + readBitsResult;
|
|
int codeDistance = huffmanReadValue(inflateObj, distanceTree);
|
|
|
|
// Read distance value extra bits
|
|
readBitsResult = inflateReadBits(inflateObj, DISTANCE_CODE_VALUE_EXTRA_BITS[codeDistance]);
|
|
if (readBitsResult == 0xffffffff) {
|
|
huffmanFreeTree(literalTree);
|
|
huffmanFreeTree(distanceTree);
|
|
printError("Failed to read dynamic distance value extra bits.\n");
|
|
return false;
|
|
}
|
|
|
|
codeDistance = DISTANCE_CODE_VALUE_OFFSET[codeDistance] + readBitsResult;
|
|
if (inflateCopyBytes(inflateObj, codeDistance, codeLength) == false) {
|
|
huffmanFreeTree(literalTree);
|
|
huffmanFreeTree(distanceTree);
|
|
return false;
|
|
}
|
|
}
|
|
else {
|
|
huffmanFreeTree(literalTree);
|
|
huffmanFreeTree(distanceTree);
|
|
printError("Unexpected literal 0x%08X\n", hlit);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// free dynamic trees
|
|
huffmanFreeTree(literalTree);
|
|
huffmanFreeTree(distanceTree);
|
|
}
|
|
else {
|
|
printError("Unknown block type! '%X'\n", blockType);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Write leftover (smaller then WINDOW_SIZE)
|
|
if (inflateObj->windowPosition > 0) {
|
|
if (inflateWriteOutput(inflateObj, inflateObj->windowPosition) == false) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
/* inflateExtractNextFile() - Inflate helper that opens/creates the output file
|
|
* when 'outputFilePath' is not 'NULL'. This also
|
|
* reads and checks the CRC32. The input file offset
|
|
* should be EOF or the beginning of the next
|
|
* deflated file data after this returns 'true'.
|
|
*
|
|
* @param inflateObj : Inflate descriptor.
|
|
* @param outputFilePath: File path to extract the data to, this may be 'NULL'
|
|
* to not extract to a file (for just verify crc32 or to
|
|
* skip a file).
|
|
*
|
|
* @returns: 'true' on success, 'false' on error. */
|
|
bool inflateExtractNextFile(InflateObject * inflateObj,
|
|
const char * outputFilePath) {
|
|
FILE * outputFp;
|
|
bool result;
|
|
uint32_t newCrc;
|
|
|
|
if (outputFilePath != NULL) {
|
|
outputFp = fopen(outputFilePath, "wb");
|
|
if (outputFp == NULL) {
|
|
printError("Failed to open output file '%s'\n", outputFilePath);
|
|
return false;
|
|
}
|
|
}
|
|
else {
|
|
outputFp = NULL;
|
|
}
|
|
|
|
inflateNew(inflateObj, outputFp);
|
|
result = inflateNext(inflateObj);
|
|
|
|
// close output file when there is any
|
|
if (outputFp != NULL) {
|
|
fclose(outputFp);
|
|
}
|
|
|
|
if (result == false) {
|
|
return false;
|
|
}
|
|
inflateObj->crc32 = crc32Finalize(inflateObj->crc32);
|
|
|
|
// Seek to the end of the deflate data since we probably overshot due to the
|
|
// chunk buffer.
|
|
if (fseek(inflateObj->inputFile, ftell(inflateObj->inputFile) -
|
|
inflateObj->chunkBuffSize +
|
|
inflateObj->chunkBuffPosition, SEEK_SET) != 0)
|
|
{
|
|
printError("Failed to seek back to deflate end.\n");
|
|
return false;
|
|
}
|
|
|
|
if (readUInt32(inflateObj->inputFile, &newCrc) != REWERR_OK) {
|
|
printError("Failed to read CRC32\n");
|
|
return false;
|
|
}
|
|
|
|
if (inflateObj->crc32 != newCrc) {
|
|
// deflated data should be 8 byte aligned?
|
|
// NOTE: largest offset I have seen is 1. Probably because of the bits read.
|
|
uint8_t attempt;
|
|
for (attempt=0; attempt<8; attempt++) {
|
|
if (fseek(inflateObj->inputFile, -3l, SEEK_CUR) != 0) {
|
|
printError("Failed to seek to next crc attempt.\n");
|
|
return false;
|
|
}
|
|
if (readUInt32(inflateObj->inputFile, &newCrc) != REWERR_OK) {
|
|
printError("Failed to read CRC32 attempt\n");
|
|
return false;
|
|
}
|
|
if (inflateObj->crc32 == newCrc) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (inflateObj->crc32 != newCrc) {
|
|
printError("CRC32 mismatch\n");
|
|
return false;
|
|
}
|
|
|
|
printDebug("crc32 attempt %d\n", attempt);
|
|
}
|
|
|
|
return true;
|
|
}
|