// // /home/ms/source/sidplay/libsidplay/fformat/RCS/pp_.cpp,v // // For more info get ``depp.cpp'' and ``ppcl.cpp''. #include "pp_.h" // exports bool depp(ifstream& source, ubyte** destRef); bool ppIsCompressed(); udword ppUncompressedLen(); // return the length of the uncompressed data const char* ppErrorString; static const char PP_ID[] = "PP20"; static const udword PP_BITS_FAST = 0x09090909; static const udword PP_BITS_MEDIOCRE = 0x090a0a0a; static const udword PP_BITS_GOOD = 0x090a0b0b; static const udword PP_BITS_VERYGOOD = 0x090a0c0c; static const udword PP_BITS_BEST = 0x090a0c0d; static const char text_packeddatacorrupt[] = "PowerPacker: Packed data is corrupt"; static const char text_unrecognized[] = "PowerPacker: Unrecognized compression method"; static const char text_uncompressed[] = "Not compressed with PowerPacker (PP20)"; static const char text_notenoughmemory[] = "Not enough free memory"; static const char text_fast[] = "PowerPacker: fast compression"; static const char text_mediocre[] = "PowerPacker: mediocre compression"; static const char text_good[] = "PowerPacker: good compression"; static const char text_verygood[] = "PowerPacker: very good compression"; static const char text_best[] = "PowerPacker: best compression"; static ubyte* sourceBuf; static ubyte* readPtr; static ubyte* writePtr; static ubyte* startPtr; static udword current; // compressed data longword static int bits; // number of bits in 'current' to evaluate static ubyte efficiency[4]; static udword outputLen; static bool isCompressed; static bool globalError; // Move four bytes to Motorola big-endian double-word. static inline void bytesTOudword() { readPtr -= 4; if ( readPtr < sourceBuf ) { ppErrorString = text_packeddatacorrupt; globalError = true; } else current = readEndian(*readPtr,*(readPtr+1),*(readPtr+2),*(readPtr+3)); } static void ppFreeMem() { if (sourceBuf != 0) // should not be required delete[] sourceBuf; sourceBuf = 0; } static inline udword ppRead(int count) { udword data = 0; // read 'count' bits of packed data for (; count > 0; count--) { // equal to shift left data += data; // merge bit 0 data = (data | (current&1)); current = (current >> 1); if ((--bits) == 0) { bytesTOudword(); bits = 32; } } return data; } static inline void ppBytes() { udword count, add; count = (add = ppRead(2)); while (add == 3) { add = ppRead(2); count += add; } count++; for (; count > 0 ; count--) { if (writePtr > startPtr) { *(--writePtr) = (ubyte)ppRead(8); } else { ppErrorString = text_packeddatacorrupt; globalError = true; } } } static inline void ppSequence() { udword offset, length, add; int offsetBitLen; length = ppRead( 2 ); // length -2 offsetBitLen = (int)efficiency[length]; length += 2; if ( length != 5 ) offset = ppRead( offsetBitLen ); else { if ( ppRead( 1 ) == 0 ) offsetBitLen = 7; offset = ppRead( offsetBitLen ); add = ppRead( 3 ); length += add; while ( add == 7 ) { add = ppRead(3); length += add; } } for ( ; length > 0 ; length-- ) { if ( writePtr > startPtr ) { --writePtr; *writePtr = *(writePtr+1+offset); } else { ppErrorString = text_packeddatacorrupt; globalError = true; } } } udword ppUncompressedLen() { return outputLen; } bool ppIsCompressed() { return isCompressed; } // returns: false = file is not in (supported) PowerPacker format, // or some error (=> ppErrorString) has occured // true = successful return (size via ppUncompressedLen()) bool depp( ifstream& source, ubyte** destRef ) { globalError = false; // assume no error isCompressed = false; outputLen = 0; // Check for header signature. source.seekg(0,ios::beg); char sig[5]; source.read(sig,4); sig[4] = 0; if ( strcmp(sig,PP_ID) != 0 ) { ppErrorString = text_uncompressed; return false; } // Load efficiency table. source.read((char*)efficiency,4); udword eff = readEndian(efficiency[0],efficiency[1],efficiency[2],efficiency[3]); if (( eff != PP_BITS_FAST ) && ( eff != PP_BITS_MEDIOCRE ) && ( eff != PP_BITS_GOOD ) && ( eff != PP_BITS_VERYGOOD ) && ( eff != PP_BITS_BEST )) { ppErrorString = text_unrecognized; return false; } // We set this flag here and not before previous block, because we hope // that every file with a valid signature and a valid efficiency table // actually is PP-compressed. isCompressed = true; // Uncompressed size is stored at end of source file. #if defined(HAVE_SEEKG_OFFSET) udword inputlen = (source.seekg(0,ios::end)).offset(); #else source.seekg( 0, ios::end ); udword inputlen = (udword)source.tellg(); #endif source.seekg( 0, ios::beg ); // Get memory for source file. if (( sourceBuf = new ubyte[inputlen]) == 0 ) { ppErrorString = text_notenoughmemory; return false; } // For 16-bit system (like Windows 3.x) we would have to change // this to be able to load beyond the 64KB segment boundary. udword restfilelen = inputlen; while ( restfilelen > INT_MAX ) { source.read( (char*)sourceBuf + (inputlen - restfilelen), INT_MAX ); restfilelen -= INT_MAX; } if ( restfilelen > 0 ) source.read( (char*)sourceBuf + (inputlen - restfilelen), restfilelen ); // reset file pointer source.seekg( 0, ios::beg ); // backwards decompression readPtr = sourceBuf + inputlen -4; // uncompressed length in bits 31-8 of last dword outputLen = readEndian(0,*readPtr,*(readPtr+1),*(readPtr+2)); // Free any previously existing destination buffer. if ( *destRef != 0 ) delete[] *destRef; // Allocate memory for output data. if (( *destRef = new ubyte[outputLen]) == 0 ) { ppErrorString = text_notenoughmemory; return false; } switch ( eff) { case PP_BITS_FAST: ppErrorString = text_fast; break; case PP_BITS_MEDIOCRE: ppErrorString = text_mediocre; break; case PP_BITS_GOOD: ppErrorString = text_good; break; case PP_BITS_VERYGOOD: ppErrorString = text_verygood; break; case PP_BITS_BEST: ppErrorString = text_best; break; } // put destptr to end of uncompressed data writePtr = *destRef + outputLen; // lowest dest. address for range-checks startPtr = *destRef; // read number of unused bits in 1st data dword // from lowest bits 7-0 of last dword bits = 32 - *(sourceBuf + inputlen -1); // decompress data bytesTOudword(); if ( bits != 32 ) current = ( current >> ( 32 - bits )); do { if ( ppRead( 1) == 0 ) ppBytes(); if ( writePtr > *destRef ) ppSequence(); if ( globalError ) { ppFreeMem(); return false; } } while ( writePtr > *destRef ); // Finished. ppFreeMem(); return true; }