// // CMPOBJ.CPP // // Source file for ArchiveLib 1.0 // // Copyright (c) Greenleaf Software, Inc. 1994 // All Rights Reserved // // CONTENTS // // ALCompressedObject::operator new() // ALCompressedObject::ALCompressedObject() // ALCompressedObject::~ALCompressedObject() // ALCompressedObject::Insert() // ALCompressedObject::Extract() // ALCompressedObject::WriteHeaderData() // ALCompressedObject::ReadHeaderData() // // DESCRIPTION // // This file contains all the support code for the ALCompressedObject // class. This class is sort of a poor-man's archive, with just one // file, no flexibility, and super-low overhead. // // REVISION HISTORY // // May 23, 1994 1.0A : First release // // #include "arclib.h" #pragma hdrstop #include "_openf.h" // // void * ALCompressedObject::operator new( size_t size ) // // ARGUMENTS: // // size : The number of bytes needed to create a new ALCompressedObject // object. // // RETURNS // // A pointer to the newly allocated storage area, or 0 if no storage // was available. // // 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. // // REVISION HISTORY // // May 23, 1994 1.0A : First release // #if defined( AL_BUILDING_DLL ) void AL_DLL_FAR * AL_PROTO ALCompressedObject::operator new( size_t size ) { return ::new char[ size ]; } #endif // // ALCompressedObject:: // ALCompressedObject( ALStorage AL_DLL_FAR & storage_object, // ALCompressionEngine AL_DLL_FAR & compression_engine ) // // ARGUMENTS: // // storage_object : A reference to the storage object that is going // to get the compressed data. // // compression_engine : A reference to the compression engine that will // be used to insert an object or extract an object. // // RETURNS // // A constructor, you don't get a return. // // DESCRIPTION // // A compressed object is a storage object that gets a single compressed // object packed into it. You get to call Insert() or Extract(), to // put the object in or take it out. Compressed objects don't get all // the fancy options that Archives do. For example, you have to know in // advance what sort of compression engine and storage object you are // going to use to put things in and take things out. You don't get to // store comments or time date stamps, or anything like that. // // The one piece of flexibility you do get the ALCompressedObject is // the ability to derive a new class from this base, then use the // new class to write some custom data out to the object. // // This constructor stores references to the object being used to hold the // compressed data, and the engine being used to pack and unpack it. // // REVISION HISTORY // // May 23, 1994 1.0A : First release // AL_PROTO ALCompressedObject:: ALCompressedObject( ALStorage AL_DLL_FAR & storage_object, ALCompressionEngine AL_DLL_FAR & compression_engine ) { mpCompressionEngine = &compression_engine; mpStorageObject = &storage_object; } // // ALCompressedObject::~ALCompressedObject() // // ARGUMENTS: // // None, destructor. // // RETURNS // // None, destructor. // // DESCRIPTION // // This destructor has nothing important to do. The debug version // checks the object type for validity, but that's it. // // REVISION HISTORY // // May 23, 1994 1.0A : First release // AL_PROTO ALCompressedObject::~ALCompressedObject() { AL_ASSERT_OBJECT( this, ALCompressedObject, "~ALCompressedObject" ); } // // int ALCompressedObject::Insert( ALStorage &input_object ) // // ARGUMENTS: // // input_object : A storage object that is going to be inserted into // the compressed object. // // RETURNS // // AL_SUCCESS if everything worked properly, or < AL_SUCCESS if an // error was encountered. // // DESCRIPTION // // The compressed object has this format: // // long uncompressed_size // long compressed_size // DWORD crc_32 // Any data from derived classes // unsigned char data[] // // Writing all this out is pretty straightforward, although you might // note that it is going to require at least one seek() back to the // start of the compressed object after the compression is done. // // REVISION HISTORY // // May 23, 1994 1.0A : First release // int AL_PROTO ALCompressedObject::Insert( ALStorage AL_DLL_FAR &input_object ) { AL_ASSERT_OBJECT( this, ALCompressedObject, "Insert" ); AL_ASSERT_OBJECT( &input_object, ALStorage, "Insert" ); if ( mStatus < AL_SUCCESS ) return mStatus; // // Here is where we open the input and the output. // ALOpenFiles files( input_object, *mpStorageObject ); // // We first write out the uncompressed size, which we already know. We // then save the current position, and write placeholder longs out for // what will become the compressed size and the CRC-32. // mpStorageObject->WritePortableLong( input_object.GetSize() ); long saved_pos = mpStorageObject->Tell(); mpStorageObject->WritePortableLong( 0xfedcba98L ); //Temporary mpStorageObject->WritePortableLong( 0x01234567L ); //Temporary // // If a derived class has any header data to write out, this is where it // will be performed. The base class writes 0 bytes here. // WriteHeaderData(); long start = mpStorageObject->Tell(); // // Next, perform the compression. Once that is done we can calculate // the compressed size. The CRC-32 will have been calculated on the fly // as the compression was performed. // mpCompressionEngine->Compress( input_object, *mpStorageObject ); long compressed_size = mpStorageObject->Tell() - start; if ( mpCompressionEngine->mStatus < 0 ) return mStatus = mpCompressionEngine->mStatus; // // Go back to the spot we remembered, and write out the compressed // size and the CRC. At that point, the compressed object is complete. // mpStorageObject->Seek( saved_pos ); mpStorageObject->WritePortableLong( compressed_size ); mpStorageObject->WritePortableLong( ~input_object.GetCrc32() ); if ( mpStorageObject->mStatus < 0 ) return mStatus = mpStorageObject->mStatus; return AL_SUCCESS; } // // int ALCompressedObject::Extract( ALStorage &output_object ) // // ARGUMENTS: // // output_object : The storage object that is going to receive the // extracted data from the compressed object. // // RETURNS // // AL_SUCCESS, or < AL_SUCCESS if an error occurs. // // DESCRIPTION // // Extracting the data to a new storage object is easy. We read in // all the data so that we can do a little error checking along the // way, but that's all. // // REVISION HISTORY // // May 23, 1994 1.0A : First release // int AL_PROTO ALCompressedObject::Extract( ALStorage AL_DLL_FAR &output_object ) { long compressed_length; long crc32; AL_ASSERT_OBJECT( this, ALCompressedObject, "Extract" ); AL_ASSERT_OBJECT( &output_object, ALStorage, "Extract" ); if ( mStatus < AL_SUCCESS ) return mStatus; // // Open the input and output files. // ALOpenFiles files( *mpStorageObject, output_object ); // // Now read in all the data stored at the start of the object, // including any header data created by derived classes. If we are // using the base class, there won't be any additional data bytes there. // mpStorageObject->ReadPortableLong( output_object.mlSize ); mpStorageObject->ReadPortableLong( compressed_length ); mpStorageObject->ReadPortableLong( crc32 ); ReadHeaderData(); if ( mpStorageObject->mStatus < 0 ) return mStatus = mpStorageObject->mStatus; // // Extract the data and store it in the storage object specified // as an argument. // if ( mpCompressionEngine->Decompress( *mpStorageObject, output_object, compressed_length ) < 0 ) return mStatus = mpCompressionEngine->mStatus; // // A little error checking leads to an error return if things didn't // go well, or AL_SUCCESS if things did. // if ( mpStorageObject->mStatus < 0 ) return mStatus = mpStorageObject->mStatus; if ( crc32 != ~output_object.GetCrc32() ) return mStatus.SetError( AL_CRC_ERROR, "CRC32 differs between %s and %s", mpStorageObject->mName.GetName(), output_object.mName.GetName() ); return AL_SUCCESS; } // // int ALCompressedObject::WriteHeaderData() // // ARGUMENTS: // // None. // // RETURNS // // AL_SUCCESS, always. // // DESCRIPTION // // Derived classes can override this function and use it to add // additional data bytes to the header of a compressed object. Note // that this data does not have to be written out in any particular // format, we have no portability concerns here. It is up to the // derived class to insure that the data is written in an internally // consistent format so that ReadHeaderData() can always position the // file pointer to the correct start of data. // // REVISION HISTORY // // May 23, 1994 1.0A : First release // int AL_PROTO ALCompressedObject::WriteHeaderData() { return AL_SUCCESS; } // // int ALCompressedObject::ReadHeaderData() // // ARGUMENTS: // // None. // // RETURNS // // AL_SUCCESS, always. // // DESCRIPTION // // Derived classes can override this function and use it to read // additional data bytes from the header of a compressed object. Note // that this data does not have to be written out in any particular // format, we have no portability concerns here. It is up to the // derived class to insure that the data is written in an internally // consistent format so that ReadHeaderData() can always position the // file pointer to the correct start of data. // // REVISION HISTORY // // May 23, 1994 1.0A : First release // int AL_PROTO ALCompressedObject::ReadHeaderData() { return AL_SUCCESS; }