summaryrefslogtreecommitdiffstats
path: root/fbreader/src/formats/pdb/HuffDecompressor.cpp
blob: 9b6f285635fbfb95e57319688dd71838647f83d2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
/*
 * Copyright (C) 2004-2012 Geometer Plus <[email protected]>
 *
 * This program 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 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 * 02110-1301, USA.
 */

#include <cstring>

#include <ZLInputStream.h>

#include "PdbReader.h"
#include "BitReader.h"
#include "HuffDecompressor.h"

HuffDecompressor::HuffDecompressor(ZLInputStream& stream, 
                        const std::vector<unsigned long>::const_iterator beginIt, 
                        const std::vector<unsigned long>::const_iterator endIt,
						const unsigned long endHuffDataOffset, const unsigned long extraFlags) : myExtraFlags(extraFlags), myErrorCode(ERROR_NONE) {
	

	const unsigned long huffHeaderOffset = *beginIt;
	const unsigned long huffRecordsNumber = endIt - beginIt;
	const unsigned long huffDataOffset = *(beginIt + 1);
	
	stream.seek(huffHeaderOffset, true);
	stream.seek(16, false);
	unsigned long cacheTableOffset, baseTableOffset;
	PdbUtil::readUnsignedLongBE(stream, cacheTableOffset);
	PdbUtil::readUnsignedLongBE(stream, baseTableOffset); 
	

	myCacheTable = new unsigned long[256];
	stream.seek(huffHeaderOffset + cacheTableOffset, true);
	for (std::size_t i = 0; i < 256; ++i) {
		PdbUtil::readUnsignedLongLE(stream, myCacheTable[i]); //LE
	}
	
	myBaseTable = new unsigned long[64]; 
	stream.seek(huffHeaderOffset + baseTableOffset, true);
	for (std::size_t i = 0; i < 64; ++i) {
		PdbUtil::readUnsignedLongLE(stream, myBaseTable[i]); //LE
	}
	
	stream.seek(huffDataOffset + 12, true);
	PdbUtil::readUnsignedLongBE(stream, myEntryBits);	
	
	std::size_t huffDataSize = endHuffDataOffset - huffDataOffset;
	myData = new unsigned char[huffDataSize];
	stream.seek(huffDataOffset, true);
	if (huffDataSize == stream.read((char*)myData, huffDataSize)) {	
		myDicts = new unsigned char* [huffRecordsNumber - 1];
		for(std::size_t i = 0; i < huffRecordsNumber - 1;  ++i) {	
			std::size_t shift = *(beginIt + i + 1) - huffDataOffset;
			myDicts[i] = myData + shift;
		}
	} else {
		myErrorCode = ERROR_CORRUPTED_FILE;
	}
	
	myTargetBuffer = 0;
	myTargetBufferEnd = 0;
	myTargetBufferPtr = 0;
}

HuffDecompressor::~HuffDecompressor() {
	delete[] myCacheTable;
	delete[] myBaseTable;
	delete[] myData;
	delete[] myDicts;
}

bool HuffDecompressor::error() const {
	return myErrorCode == ERROR_CORRUPTED_FILE;
}

std::size_t HuffDecompressor::decompress(ZLInputStream &stream, char *targetBuffer, std::size_t compressedSize, std::size_t maxUncompressedSize) {
	if ((compressedSize == 0) || (myErrorCode == ERROR_CORRUPTED_FILE)) {
		return 0;
	}
	if (targetBuffer != 0) { 
		unsigned char *sourceBuffer = new unsigned char[compressedSize];
    	myTargetBuffer = targetBuffer;
		myTargetBufferEnd = targetBuffer + maxUncompressedSize;
    	myTargetBufferPtr = targetBuffer;
		if (stream.read((char*)sourceBuffer, compressedSize) == compressedSize) {
			std::size_t trailSize = sizeOfTrailingEntries(sourceBuffer, compressedSize); 
        	if (trailSize < compressedSize) { 
				bitsDecompress(BitReader(sourceBuffer, compressedSize - trailSize));
			} else {
				myErrorCode = ERROR_CORRUPTED_FILE;
			}
		}
		delete[] sourceBuffer;
	} else {
		myTargetBuffer = 0;
		myTargetBufferEnd = 0;
		myTargetBufferPtr = 0;
	}

	return myTargetBufferPtr - myTargetBuffer;
}

void HuffDecompressor::bitsDecompress(BitReader bits, std::size_t depth) {
	if (depth > 32) {
		myErrorCode = ERROR_CORRUPTED_FILE;
		return;
	}	

	while (bits.left()) {
    	const unsigned long dw = (unsigned long)bits.peek(32);
	    const unsigned long v = myCacheTable[dw >> 24];
  	 	unsigned long codelen = v & 0x1F;
		//if ((codelen == 0) || (codelen > 32)) {
		//	return false;
		//} 
		unsigned long code = dw >> (32 - codelen);
    	unsigned long r = (v >> 8);
    	if (!(v & 0x80)) {
        	while (code < myBaseTable[(codelen - 1) * 2]) {
            	codelen += 1;
            	code = dw >> (32 - codelen);
			}
        	r = myBaseTable[(codelen - 1) * 2 + 1];
		}
    	r -= code;
		//if (codelen == 0) {
		//	return false;
		//} 
		if (!bits.eat(codelen)) {
			return;
		}
    	const unsigned long dicno = r >> myEntryBits;
    	const unsigned long off1 = 16 + (r - (dicno << myEntryBits)) * 2;
    	const unsigned char* dict = myDicts[dicno];							//TODO need index check 
    	const unsigned long off2 = 16 + dict[off1] * 256 + dict[off1 + 1];	//TODO need index check 
    	const unsigned long blen = dict[off2] * 256 + dict[off2 + 1];		//TODO need index check 
    	const unsigned char* slice = dict + off2 + 2;
		const unsigned long sliceSize = blen & 0x7fff;
    	if (blen & 0x8000) {
        	if (myTargetBufferPtr + sliceSize < myTargetBufferEnd) {
        		std::memcpy(myTargetBufferPtr, slice, sliceSize);
        		myTargetBufferPtr += sliceSize;
        	} else {
        		return;
        	}
    	} else {
			bitsDecompress(BitReader(slice, sliceSize), depth + 1);
		}
	}
}

std::size_t HuffDecompressor::sizeOfTrailingEntries(unsigned char* data, std::size_t size) const {
	std::size_t num = 0;
	std::size_t flags = myExtraFlags >> 1;
	while (flags) {
    	if (flags & 1) {
			if (num < size) {
        		num += readVariableWidthIntegerBE(data, size - num);
			}
		}
    	flags >>= 1;
	}
	return num;
}


std::size_t HuffDecompressor::readVariableWidthIntegerBE(unsigned char* ptr, std::size_t psize) const {
    unsigned char bitsSaved = 0;
	std::size_t result = 0;
    while (true) {
        const unsigned char oneByte = ptr[psize - 1];
        result |= (oneByte & 0x7F) << bitsSaved;
        bitsSaved += 7;
        psize -= 1;
        if (((oneByte & 0x80) != 0) || (bitsSaved >= 28) || (psize == 0)) {
            return result;
		}
	}
}