// // PKENGND.CPP // // Source file for ArchiveLib 2.0 // // Copyright (c) Greenleaf Software, Inc. 1994-1996 // All Rights Reserved // // CONTENTS // // ALPkDecompressor::operator new() // ALPkDecompressor::ALPkDecompressor() // newALPkDecompressor() // ALPkDecompressor::~ALPkDecompressor() // ALPkDecompressor::Decompress() // ALPkDecompressor::Clone() // // DESCRIPTION // // All of the C++ source to support the PKWare decompressor class. // // REVISION HISTORY // // February 14, 1996 2.0A : New release #include "arclib.h" #if !defined( AL_IBM ) #pragma hdrstop #endif #include "copyengn.h" #include "pkengn.h" #include "_openf.h" #include "zutil.h" #include "zlib.h" // // NAME // // ALPkDecompressor::operator new() // // PLATFORMS/ENVIRONMENTS // // Console Windows PM // C++ // // SHORT DESCRIPTION // // Memory allocator used when ArchiveLib resides in a 16 bit DLL. // // C++ SYNOPSIS // // #include "arclib.h" // #include "pkengn.h" // // void * ALPkDecompressor::operator new( size_t size ) // // C SYNOPSIS // // None. // // VB SYNOPSIS // // None. // // DELPHI SYNOPSIS // // None. // // ARGUMENTS // // size : The number of bytes that the compiler has decided will be // necessary to construct a new ALPkDecompressor object. // // DESCRIPTION // // When using a DLL, it is easy to get into a dangerous situation when // creating objects whose ctor and dtor are both in the DLL. The problem // arises because when you create an object using new, the memory for // the object will be allocated from the EXE. However, when you destroy // the object using delete, the memory is freed inside the DLL. Since // the DLL doesn't really own that memory, bad things can happen. // // But, you say, won't the space just go back to the Windows heap regardless // of who tries to free it? Maybe, but maybe not. If the DLL is using // a subsegment allocation scheme, it might do some sort of local free // before returning the space to the windows heap. That is the point where // you could conceivably cook your heap. // // By providing our own version of operator new inside this class, we // ensure that all memory allocation for the class will be done from // inside the DLL, not the EXE calling the DLL. // // Incidentally, I suspect that this function never gets called. If an // object of a derived archive class is being created, it should use // its own new operator, rendering this one useless. // // RETURNS // // A pointer to some memory that should have been pulled out of the // heap for the DLL. // // EXAMPLE // // SEE ALSO // // REVISION HISTORY // // February 14, 1996 2.0A : New release // #if defined( AL_BUILDING_DLL ) void AL_DLL_FAR * AL_PROTO ALPkDecompressor::operator new( size_t size ) /* Tag protected function */ { return ::new char[ size ]; } #endif // // NAME // // ALPkDecompressor::ALPkDecompressor() // // PLATFORMS/ENVIRONMENTS // // Console Windows PM // C++ C VB Delphi // // SHORT DESCRIPTION // // Constructor for the PkWare deflate decompressor engine. // // C++ SYNOPSIS // // #include "arclib.h" // #include "pkengn.h" // // ALPkDecompressor::ALPkDecompressor(); // // C SYNOPSIS // // #include "arclib.h" // #include "pkengn.h" // // hALDecompressor newALPkDecompressor( void ); // // VB SYNOPSIS // // Declare Function newALPkDecompressor Lib "AL20LW" As Long // // DELPHI SYNOPSIS // // function newALPkDecompressor : hALDecompressor; // // ARGUMENTS // // None. // // DESCRIPTION // // This constructor creates a new ALPkDecompressor. This decompressor by // itself is almost a no-cost object, with no derived class data members that // need to be initialized. The decompressor becomes a resource user when you // call the Decompress() member function. // // Create decompressor objects when you are initializing a toolkit, or when // decompressing files by hand, or when using an ALCompressedObject. // // RETURNS // // The constructor doesn't return anything to C++ programmers. C/VB/Delphi // programmers get a handle that points to the newly created object. // // EXAMPLE // // SEE ALSO // // REVISION HISTORY // // February 14, 1996 2.0A : New release // AL_PROTO ALPkDecompressor::ALPkDecompressor() /* Tag public function */ : ALDecompressor( AL_COMPRESSION_DEFLATE, "Inflate" ) { option = NORMAL; } #if !defined( AL_NO_C ) extern "C" AL_LINKAGE hALDecompressor AL_FUNCTION newALPkDecompressor( void ) /* Tag public function */ { return (hALDecompressor) new ALPkDecompressor; } #endif // // NAME // // ALPkDecompressor::~ALPkDecompressor() // // PLATFORMS/ENVIRONMENTS // // Console Windows PM // C++ // // SHORT DESCRIPTION // // The PKWare deflate Decompressor destructor. // // C++ SYNOPSIS // // #include "arclib.h" // #include "pkengn.h" // // ALPkDecompressor::~ALPkDecompressor() // // C SYNOPSIS // // None. C programmers need to call deleteALDecompressor(). // // VB SYNOPSIS // // None. VB programmers need to call deleteALDecompressor(). // // DELPHI SYNOPSIS // // None. Delphi programmers need to call deleteALDecompressor(). // // ARGUMENTS // // None. // // DESCRIPTION // // The destructor for objects of this class doesn't have to do // anything. In debug mode, we at least check for the validity // of the object. // // RETURNS // // Nothing. // // EXAMPLE // // SEE ALSO // // REVISION HISTORY // // February 14, 1996 2.0A : New release // AL_PROTO ALPkDecompressor::~ALPkDecompressor() /* Tag public function */ { AL_ASSERT( GoodTag(), "~ALPkDecompressor: Attempt to delete invalid object" ); } // // I've tried two different ways to feed data to the decompressor. // The "normal" way is to just give the engine a pointer to a buffer, // then work on filling that buffer by calling my own Read routines. // The hacked way ought to work better. When hacked, I use the buffer // in the storage object, instead of creating a standalong buffer. This // means the compressor has to be a friend of ALStorage, but that's // manageable. Right now, Hacked seems to be okay, and it saves some space // so that's what we're doing. // // Changing to unhacked is possible, but the buffer allocation should then // be done dynamically, as this static buffer's going to have trouble // when inside a DLL. // #define HACK_INPUT #define HACK_OUTPUT #if !defined( HACK_OUTPUT ) const int OUTBUF_LEN = 1024; static char outbuf[ OUTBUF_LEN ]; #endif #if !defined( HACK_INPUT ) const int INBUF_LEN = 1024; char inbuf[ INBUF_LEN ]; #endif // // NAME // // ALPkDecompressor::Decompress() // // PLATFORMS/ENVIRONMENTS // // Console Windows PM // C++ // // SHORT DESCRIPTION // // Decompress using the deflate algorithm. // // C++ SYNOPSIS // // #include "arclib.h" // #include "pkengn.h" // // int ALPkDecompressor::Decompress( ALStorage& input, // ALStorage& output ); // // C SYNOPSIS // // None, see the base class function ALDecompress(). // // VB SYNOPSIS // // None, see the base class function ALDecompress(). // // DELPHI SYNOPSIS // // None, see the base class function ALDecompress(). // // ARGUMENTS // // input : A reference to the input storage object. // // output : A reference to the output storage object. // // DESCRIPTION // // This is the virtual function that is called to decompress data. // This section of code is really just a front end to the real engine, // which is found in many source files in ..\CPP_ZLIB\*.CPP. // // It's kind of hard to see what's going on here with the #ifdefs, but // it really isn't too complicated. The inflate code wants a zstream // structure to work on. We set up zstream to contain buffer pointers // to both the input and output streams. We then sit in a loop calling // inflate() repeatedly. The inflate() code decompresses as much data // as it can, and returns. After each call, I flush all the data I can, // load as much new data as I can, then try it again. This repeats until // inflate() finally indicates that it's done. // // After decompression is done, we have to check for errors on // any of the other objects involved in the compression, and return the // cumulative result. // // This function will almost always be called indirectly, by means of // a virtual function call off the base class. That's why you won't // see any C, VB, or Delphi functions here. Those languages will only // be able to call the Decompress() routine by way of the base class. // // RETURNS // // AL_SUCCESS, or < AL_SUCCESS if something bad happens. // // EXAMPLE // // SEE ALSO // // REVISION HISTORY // // February 14, 1996 2.0A : New release // int AL_PROTO ALPkDecompressor::Decompress( ALStorage AL_DLL_FAR & input, /* Tag public function */ ALStorage AL_DLL_FAR & output, long length ) { ALOpenFiles files( input, output ); if ( input.mStatus < AL_SUCCESS ) return mStatus = input.mStatus; if ( output.mStatus < AL_SUCCESS ) return mStatus = output.mStatus; output.InitCrc32(); z_stream stream; int result; memset( &stream, 0, sizeof( z_stream ) ); stream.avail_in = 0; #if !defined( HACK_INPUT ) stream.next_in = (Byte ZL_FAR *) inbuf; #endif #if defined( HACK_OUTPUT ) output.FlushBuffer(); stream.avail_out = output.muBufferSize; stream.next_out = (Byte ZL_FAR *) output.mpcBuffer; #else stream.avail_out = OUTBUF_LEN; stream.next_out = (Byte ZL_FAR *) outbuf; #endif stream.zalloc = 0; stream.zfree = 0; inflateInit2( &stream, -MAX_WBITS ); for ( ; ; ) { if ( stream.avail_in == 0 ) { #if !defined( HACK_INPUT ) stream.avail_in = input.ReadBuffer( (unsigned char *) inbuf, INBUF_LEN ); stream.next_in = (unsigned char *) inbuf; #else if ( input.muReadIndex >= input.muBufferValidData ) { if ( input.LoadBuffer( input.mlFilePointer ) < 0 ) stream.avail_in = 0; else stream.avail_in = input.muBufferValidData; } else stream.avail_in = input.muBufferValidData - input.muReadIndex; // // The next few lines are designed to prevent overshoot of the input // data stream. // if ( length != -1 ) { if ( (long) stream.avail_in > length ) stream.avail_in = (unsigned short) length; length -= stream.avail_in; } stream.next_in = input.mpcBuffer + input.muReadIndex; input.muReadIndex += stream.avail_in; #endif } result = inflate( &stream, Z_NO_FLUSH ); #if defined( HACK_OUTPUT ) if ( stream.avail_out != output.muBufferSize ) { output.muWriteIndex = output.muBufferSize - stream.avail_out; output.FlushBuffer(); stream.next_out = output.mpcBuffer; stream.avail_out = output.muBufferSize; output.muWriteIndex = 0; #else if ( stream.avail_out != OUTBUF_LEN ) { output.WriteBuffer( (unsigned char *) outbuf, OUTBUF_LEN - stream.avail_out ); stream.avail_out = OUTBUF_LEN; stream.next_out = (Byte ZL_FAR *) outbuf; #endif } else break; if ( result != Z_OK ) break; } inflateEnd( &stream ); if ( input.mStatus < AL_SUCCESS ) return mStatus = input.mStatus; if ( output.mStatus < AL_SUCCESS ) return mStatus = output.mStatus; return mStatus; } // // NAME // // ALPkDecompressor::Clone() // // PLATFORMS/ENVIRONMENTS // // Console Windows PM // C++ // // SHORT DESCRIPTION // // Create a copy of an existing Decompressor // // C++ SYNOPSIS // // #include "arclib.h" // #include "pkengn.h" // // ALPkDecompressor::Clone( int engine_type ); // // C SYNOPSIS // // No C equivalent. // // VB SYNOPSIS // // No VB equivalent. // // DELPHI SYNOPSIS // // No Delphi equivalent. // // ARGUMENTS // // engine_type : This argument indicates what sort of engine the // caller wants to create. A value of either // AL_COMPRESSION_DEFAULT, AL_COMPRESSION_DEFLATE, // AL_DEFLATE_COPY, or AL_COPY will cause this function to // create a clone. Any other // value (for example, AL_GREENLEAF), will return a 0, // indicating that this object doesn't know how to // perform that sort of compression. // // DESCRIPTION // // Although this is a public function, it isn't really of any use // to end users. Clone() is a virtual function for the base class // ALDecompressor, and can be called to create a duplicate of an // existing decompression engine. // // Why is this useful? It is useful because it allows us to use // what is the equivalent of a virtual constructor. We can pass a // pointer to a PKWare engine to the archiving code, and it can then in // turn stuff copies of that engine into an ALEntryList without // having any idea what sort of decompression engine it is actually creating. // // RETURNS // // A copy of a newly created decompression engine. When this routine is // called, it will usually be called via a virtual function from a pointer // to a base class object, which means the resulting pointer will be // treated as an ALDecompressor * by the code, not an ALPkDecompressor. // // If this routine doesn't know how to create an engine of the correct type, // it returns a 0. When performing decompression from an archive, the // Clone() functions might be called with an unknown compression type. For // example, if you try to unzip a PkWAre archive, but are using a // Greenleaf-only toolkit. // // EXAMPLE // // SEE ALSO // // REVISION HISTORY // // November 13, 1995 2.00A : First release. // ALDecompressor AL_DLL_FAR * AL_PROTO ALPkDecompressor::Clone( int engine_type ) const /* Tag public function */ { _option pk_option; ALPkDecompressor *decompressor; switch ( engine_type ) { case AL_COMPRESSION_COPY : case AL_COMPRESSION_DEFLATE_COPY : return new ALCopyDecompressor(); case AL_COMPRESSION_DEFLATE + 3 : pk_option = SUPER_FAST; break; case AL_COMPRESSION_DEFLATE + 2 : pk_option = FAST; break; case AL_COMPRESSION_DEFLATE + 1 : pk_option = MAXIMUM; break; case AL_COMPRESSION_DEFLATE + 0 : /* Normal */ case AL_COMPRESSION_DEFAULT : pk_option = NORMAL; break; default : return 0; } decompressor = new ALPkDecompressor; if ( decompressor ) decompressor->option = pk_option; return decompressor; }