1 //
2 // /home/ms/source/sidplay/libsidplay/fformat/RCS/pp_.cpp,v
3 //
4 // For more info get ``depp.cpp'' and ``ppcl.cpp''.
5 
6 #include "pp_.h"
7 
8 
9 // exports
10 bool depp(ifstream& source, ubyte** destRef);
11 bool ppIsCompressed();
12 udword ppUncompressedLen();  // return the length of the uncompressed data
13 const char* ppErrorString;
14 
15 static const char PP_ID[] = "PP20";
16 
17 static const udword PP_BITS_FAST = 0x09090909;
18 static const udword PP_BITS_MEDIOCRE = 0x090a0a0a;
19 static const udword PP_BITS_GOOD = 0x090a0b0b;
20 static const udword PP_BITS_VERYGOOD = 0x090a0c0c;
21 static const udword PP_BITS_BEST = 0x090a0c0d;
22 
23 static const char text_packeddatacorrupt[]	= "PowerPacker: Packed data is corrupt";
24 static const char text_unrecognized[]	= "PowerPacker: Unrecognized compression method";
25 static const char text_uncompressed[] = "Not compressed with PowerPacker (PP20)";
26 static const char text_notenoughmemory[] = "Not enough free memory";
27 static const char text_fast[] = "PowerPacker: fast compression";
28 static const char text_mediocre[] = "PowerPacker: mediocre compression";
29 static const char text_good[] = "PowerPacker: good compression";
30 static const char text_verygood[] = "PowerPacker: very good compression";
31 static const char text_best[] = "PowerPacker: best compression";
32 
33 static ubyte* sourceBuf;
34 static ubyte* readPtr;
35 static ubyte* writePtr;
36 static ubyte* startPtr;
37 static udword current;       // compressed data longword
38 static int bits;             // number of bits in 'current' to evaluate
39 static ubyte efficiency[4];
40 static udword outputLen;
41 static bool isCompressed;
42 static bool globalError;
43 
44 
45 // Move four bytes to Motorola big-endian double-word.
bytesTOudword()46 static inline void bytesTOudword()
47 {
48 	readPtr -= 4;
49 	if ( readPtr < sourceBuf )
50 	{
51 		ppErrorString = text_packeddatacorrupt;
52 		globalError = true;
53 	}
54 	else
55 		current = readEndian(*readPtr,*(readPtr+1),*(readPtr+2),*(readPtr+3));
56 }
57 
ppFreeMem()58 static void ppFreeMem()
59 {
60 	if (sourceBuf != 0)      // should not be required
61 		delete[] sourceBuf;
62 	sourceBuf = 0;
63 }
64 
ppRead(int count)65 static inline udword ppRead(int count)
66 {
67 	udword data = 0;
68 	// read 'count' bits of packed data
69 	for (; count > 0; count--)
70 	{
71 		// equal to shift left
72 		data += data;
73 		// merge bit 0
74 		data = (data | (current&1));
75 		current = (current >> 1);
76 		if ((--bits) == 0)
77 		{
78 			bytesTOudword();
79 			bits = 32;
80 		}
81 	}
82 	return data;
83 }
84 
ppBytes()85 static inline void ppBytes()
86 {
87 	udword count, add;
88 	count = (add = ppRead(2));
89 	while (add == 3)
90 	{
91 		add = ppRead(2);
92 		count += add;
93 	}
94     count++;
95 	for (; count > 0 ; count--)
96 	{
97 		if (writePtr > startPtr)
98 		{
99 			*(--writePtr) = (ubyte)ppRead(8);
100 		}
101 		else
102 		{
103 			ppErrorString = text_packeddatacorrupt;
104 			globalError = true;
105 		}
106 	}
107 }
108 
ppSequence()109 static inline void ppSequence()
110 {
111 	udword offset, length, add;
112 	int offsetBitLen;
113 	length = ppRead( 2 );  // length -2
114 	offsetBitLen = (int)efficiency[length];
115 	length += 2;
116 	if ( length != 5 )
117 		offset = ppRead( offsetBitLen );
118 	else
119 	{
120 		if ( ppRead( 1 ) == 0 )
121 			offsetBitLen = 7;
122 		offset = ppRead( offsetBitLen );
123 		add = ppRead( 3 );
124 		length += add;
125 		while ( add == 7 )
126 		{
127 			add = ppRead(3);
128 			length += add;
129 		}
130 	}
131 	for ( ; length > 0 ; length-- )
132 	{
133 		if ( writePtr > startPtr )
134 		{
135 			--writePtr;
136 			*writePtr = *(writePtr+1+offset);
137 		}
138 		else
139 		{
140 			ppErrorString = text_packeddatacorrupt;
141 			globalError = true;
142 		}
143 	}
144 }
145 
ppUncompressedLen()146 udword ppUncompressedLen()
147 {
148 	return outputLen;
149 }
150 
ppIsCompressed()151 bool ppIsCompressed()
152 {
153 	return isCompressed;
154 }
155 
156 
157 // returns: false = file is not in (supported) PowerPacker format,
158 //                  or some error (=> ppErrorString) has occured
159 //          true  = successful return (size via ppUncompressedLen())
160 
depp(ifstream & source,ubyte ** destRef)161 bool depp( ifstream& source, ubyte** destRef )
162 {
163 	globalError = false;  // assume no error
164 	isCompressed = false;
165 	outputLen = 0;
166 
167 	// Check for header signature.
168 	source.seekg(0,ios::beg);
169 	char sig[5];
170 	source.read(sig,4);
171 	sig[4] = 0;
172 	if ( strcmp(sig,PP_ID) != 0 )
173 	{
174 		ppErrorString = text_uncompressed;
175 		return false;
176 	}
177 
178 	// Load efficiency table.
179 	source.read((char*)efficiency,4);
180 	udword eff = readEndian(efficiency[0],efficiency[1],efficiency[2],efficiency[3]);
181 	if (( eff != PP_BITS_FAST ) &&
182 		( eff != PP_BITS_MEDIOCRE ) &&
183 		( eff != PP_BITS_GOOD ) &&
184 		( eff != PP_BITS_VERYGOOD ) &&
185 		( eff != PP_BITS_BEST ))
186 	{
187 		ppErrorString = text_unrecognized;
188 		return false;
189 	}
190 
191 	// We set this flag here and not before previous block, because we hope
192 	// that every file with a valid signature and a valid efficiency table
193 	// actually is PP-compressed.
194 	isCompressed = true;
195 
196 	// Uncompressed size is stored at end of source file.
197 #if defined(HAVE_SEEKG_OFFSET)
198 	udword inputlen = (source.seekg(0,ios::end)).offset();
199 #else
200 	source.seekg( 0, ios::end );
201 	udword inputlen = (udword)source.tellg();
202 #endif
203 	source.seekg( 0, ios::beg );
204 
205 	// Get memory for source file.
206 	if (( sourceBuf = new ubyte[inputlen]) == 0 )
207 	{
208 		ppErrorString = text_notenoughmemory;
209 		return false;
210 	}
211 
212 	// For 16-bit system (like Windows 3.x) we would have to change
213 	// this to be able to load beyond the 64KB segment boundary.
214 	udword restfilelen = inputlen;
215 	while ( restfilelen > INT_MAX )  {
216 		source.read( (char*)sourceBuf + (inputlen - restfilelen), INT_MAX );
217 		restfilelen -= INT_MAX;
218 	}
219 	if ( restfilelen > 0 )
220 		source.read( (char*)sourceBuf + (inputlen - restfilelen), restfilelen );
221 
222 	// reset file pointer
223 	source.seekg( 0, ios::beg );
224 
225 	// backwards decompression
226 	readPtr = sourceBuf + inputlen -4;
227 
228 	// uncompressed length in bits 31-8 of last dword
229 	outputLen = readEndian(0,*readPtr,*(readPtr+1),*(readPtr+2));
230 
231 	// Free any previously existing destination buffer.
232 	if ( *destRef != 0 )
233 		delete[] *destRef;
234 
235 	// Allocate memory for output data.
236 	if (( *destRef = new ubyte[outputLen]) == 0 )
237 	{
238 		ppErrorString = text_notenoughmemory;
239 		return false;
240 	}
241 
242 	switch ( eff)
243 	{
244 	 case PP_BITS_FAST:
245 		ppErrorString = text_fast;
246 		break;
247 	 case PP_BITS_MEDIOCRE:
248 		ppErrorString = text_mediocre;
249 		break;
250 	 case PP_BITS_GOOD:
251 		ppErrorString = text_good;
252 		break;
253 	 case PP_BITS_VERYGOOD:
254 		ppErrorString = text_verygood;
255 		break;
256 	 case PP_BITS_BEST:
257 		ppErrorString = text_best;
258 		break;
259 	}
260 
261 	// put destptr to end of uncompressed data
262 	writePtr = *destRef + outputLen;
263 	// lowest dest. address for range-checks
264 	startPtr = *destRef;
265 
266 	// read number of unused bits in 1st data dword
267 	// from lowest bits 7-0 of last dword
268 	bits = 32 - *(sourceBuf + inputlen -1);
269 
270 	// decompress data
271 	bytesTOudword();
272 	if ( bits != 32 )
273 		current = ( current >> ( 32 - bits ));
274 	do
275 	{
276 		if ( ppRead( 1) == 0 )
277 			ppBytes();
278 		if ( writePtr > *destRef )
279 			ppSequence();
280 		if ( globalError )
281 		{
282 			ppFreeMem();
283 			return false;
284 		}
285 	} while ( writePtr > *destRef );
286 
287 	// Finished.
288 	ppFreeMem();
289 	return true;
290 }
291