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