// // GLARC.CPP // // Source file for ArchiveLib 2.0 // // Copyright (c) Greenleaf Software, Inc. 1994-1996 // All Rights Reserved // // CONTENTS // // ALGlArchive::operator new() // ALGlArchive::ALGlArchive() // newALGlArchiveFromStorage() // ALGlArchive::~ALGlArchive() // ALGlArchive::PreWriteDir() // ALGlArchive::PostWriteDir() // ALGlArchive::WriteDirEntry() // ALGlArchive::ReadDirectory() // ALGlArchive::PreCreate() // // DESCRIPTION // // This file contains all of the source code for the base class, // ALGlArchive. The details on how things get inserted and extracted from an // archive will all be found here. // // REVISION HISTORY // // May 23, 1994 1.0A : First release // // July 7, 1994 1.0B : Minor change to handle a snag with the sun // compilers. // // February 14, 1996 2.0A : New Release // #include "arclib.h" #if !defined( AL_IBM ) #pragma hdrstop #endif #include "glarc.h" #include "_openf.h" // // NAME // // ALGlArchive::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 "glarc.h" // // void * ALGlArchive::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 ALGlArchive 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. // // This function will be called whenever you dynamically create a new // ALGlArchive object (using the new operator.) // // 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 ALGlArchive::operator new( size_t size ) /* Tag internal function */ { return ::new char[ size ]; } #endif // // NAME // // ALGlArchive::ALGlArchive() // // PLATFORMS/ENVIRONMENTS // // Console Windows PM // C++ C VB Delphi // // SHORT DESCRIPTION // // The Greenleaf Archive constructors. // // C++ SYNOPSIS // // #include "arclib.h" // #include "glarc.h" // // ALGlArchive::ALGlArchive( ALStorage &storage_object ); // // C SYNOPSIS // // #include "arclib.h" // #include "glarc.h" // // hALArchive newALGlArchiveFromStorage( hALStorage storage ); // // VB SYNOPSIS // // Declare Function newALGlArchiveFromStorage Lib "AL20LW" // (ByVal storage&) As Long // // DELPHI SYNOPSIS // // function newALGlArchiveFromStorage( storage : hALStorage ) : hALArchive; // // ARGUMENTS // // storage_object : A pointer to the storage object that will/does // hold the archive. // // // DESCRIPTION // // This function creates a new ALGlArchive object, using a storage object // as the underlying physical container. GlArchives are siblings to // Pk archives. While they both use the same base class, and support many // functions in common, they are essentially incompatible. // // You call this constructor to manipulate any archive, including ones // that haven't even been created yet. // // Recall that the base class, ALArchive, has a single argument to *its* // constructor that determines whether or not it destroys the underlying // storage object in its destructor. Since we are using user-specified // storage in this case, we pass a 0, indicating that we don't want the // destructor to destroy the underlying storage object. // // RETURNS // // When called from C/VB/Delphi, the translation function returns a handle // to a newly created archive object. When called using the new operator // in C++, this function returns a pointer to a newly created archive // object. // // EXAMPLE // // SEE ALSO // // REVISION HISTORY // // February 14, 1996 2.0A : New Release // AL_PROTO ALGlArchive::ALGlArchive( ALStorage AL_DLL_FAR &storage_object ) /* Tag public function */ : ALArchive( &storage_object, 0) { miVersion = 0x100; } #if !defined( AL_NO_C ) extern "C" AL_LINKAGE hALArchive AL_FUNCTION newALGlArchiveFromStorage( hALStorage storage ) /* Tag public function */ { AL_ASSERT( ( (ALStorage *) storage)->GoodTag(), "storage argument is not a valid ALStorageObject" ); ALArchive *archive; archive = new ALGlArchive( *(ALStorage *) storage ); return (hALArchive) archive; } #endif // // NAME // // ALGlArchive::~ALGlArchive() // // PLATFORMS/ENVIRONMENTS // // Console Windows PM // C++ // // SHORT DESCRIPTION // // Destructor for the Greenleaf Archive. // // C++ SYNOPSIS // // #include "arclib.h" // #include "glarc.h" // // ALGlArchive::~ALGlArchive() // // C SYNOPSIS // // None, use deleteALArchive(); // // VB SYNOPSIS // // None, use deleteALArchive(); // // DELPHI SYNOPSIS // // None, use deleteALArchive(); // // ARGUMENTS // // None. // // DESCRIPTION // // This is a virtual destructor for the derived class ALGlArchive. It // has absolutely nothing to do, since all of the important work is done // by the base class destructor, ALArchive::~ALArchive(). Note that // there is no translation function for C/VB/Delphi to call this function // directly. Instead, they will get here by way of a virtual function // call to the base class destructor. // // RETURNS // // Nothing. // // EXAMPLE // // SEE ALSO // // REVISION HISTORY // // February 14, 1996 2.0A : New Release // AL_PROTO ALGlArchive::~ALGlArchive() /* Tag public function */ { AL_ASSERT( GoodTag(), "~ALGlArchive(): Attempting to delete invalid ALGlArchive" ); } // // NAME // // ALGlArchive::PreWriteDir() // // PLATFORMS/ENVIRONMENTS // // Console Windows PM // C++ // // SHORT DESCRIPTION // // Write out the archive data that precedes the directory. // // C++ SYNOPSIS // // #include "arclib.h" // #include "glarc.h" // // void ALGlArchive::PreWriteDir() // // C SYNOPSIS // // None, this is an internal function. // // VB SYNOPSIS // // None, this is an internal function. // // DELPHI SYNOPSIS // // None, this is an internal function. // // ARGUMENTS // // None. // // DESCRIPTION // // The base class, ALArchive, is responsible for all the work done when // creating an archive. However, since Greenleaf and PKWare archives // are fairly different, the base class relies on a few virtual functions // to perform archive specific chores. // // PreWriteDir() is one of those functions. It is called after the // file pointer has been positioned to write the directory out, but // before any directory entries have been written. // // Greenleaf archives store three items directly ahead of the // directory: the archive version, any customized archive data, and // the archive comment. This routine just writes all those out, then // returns to the calling routine. The caller is presumably the // write directory code in ALArchive, and it should continue by writing // out all of its directory entries. // // RETURNS // // Nothing. // // EXAMPLE // // SEE ALSO // // REVISION HISTORY // // February 14, 1996 2.0A : New Release // void AL_PROTO ALGlArchive::PreWriteDir() /* Tag protected function */ { mpArchiveStorageObject->WriteGlShort( miVersion ); WriteArchiveData(); mpArchiveStorageObject->WriteString( mComment ); } // // NAME // // ALGlArchive::PostWriteDir() // // PLATFORMS/ENVIRONMENTS // // Console Windows PM // C++ // // SHORT DESCRIPTION // // Write out the archive data that follows the directory. // // C++ SYNOPSIS // // #include "arclib.h" // #include "glarc.h" // // void ALGlArchive::PostWriteDir() // // C SYNOPSIS // // None, this is an internal function. // // VB SYNOPSIS // // None, this is an internal function. // // DELPHI SYNOPSIS // // None, this is an internal function. // // ARGUMENTS // // None. // // DESCRIPTION // // The base class, ALArchive, is responsible for all the work done when // creating an archive. However, since Greenleaf and PKWare archives // are fairly different, the base class relies on a few virtual functions // to perform archive specific chores. // // PostWriteDir() is one of those functions. It is called after the // directory entires for the archive have all been written out. // // Greenleaf archives store three items directly ahead of the // directory: the archive version, any customized archive data, and // the archive comment. This routine just writes all those out, then // returns to the calling routine. The caller is presumably the // write directory code in ALArchive, and it should continue by writing // out all of its directory entries. // // RETURNS // // Nothing. // // EXAMPLE // // SEE ALSO // // REVISION HISTORY // // February 14, 1996 2.0A : New Release // void AL_PROTO ALGlArchive::PostWriteDir() /* Tag protected function */ { long keeper = mpArchiveStorageObject->Tell(); mpArchiveStorageObject->WriteString( "" ); mpArchiveStorageObject->Seek( 0L ); mpArchiveStorageObject->WriteGlLong( mlDirectoryOffset ); mpArchiveStorageObject->Seek( keeper ); } // // NAME // // ALGlArchive::WriteDirEntry() // // PLATFORMS/ENVIRONMENTS // // Console Windows PM // C++ // // SHORT DESCRIPTION // // Write an individual object entry to the archive directory. // // C++ SYNOPSIS // // #include "arclib.h" // #include "glarc.h" // // void ALGlArchive::WriteDirEntry( ALEntry &entry ); // // C SYNOPSIS // // None, internal support function. // // VB SYNOPSIS // // None, internal support function. // // DELPHI SYNOPSIS // // None, internal support function. // // ARGUMENTS // // entry : A reference to an entry. This should correspond to an actual // object that is present in the archive. // // DESCRIPTION // // The base class function, ALArchive::WriteDirectory(), sits in a loop // writing out directory entries, one after the other. Since it is in // the base class, you can't expect it to know the exact format for an // entry. So instead, it relies on this virtual function in derived // classes to do the job. // // This function takes care of writing out a directory entry in the format // Greenleaf archives use. // // RETURNS // // Nothing. // // EXAMPLE // // SEE ALSO // // REVISION HISTORY // // February 14, 1996 2.0A : New Release // void AL_PROTO ALGlArchive::WriteDirEntry( ALEntry AL_DLL_FAR &entry ) /* Tag protected function */ { mpArchiveStorageObject->WriteString( entry.mpStorageObject->mName.GetSafeName() ); if ( entry.mpCompressor ) { mpArchiveStorageObject->WriteChar( entry.mpCompressor->miCompressionType ); entry.mpCompressor->WriteEngineData( mpArchiveStorageObject ); } else if ( entry.mpDecompressor ) { mpArchiveStorageObject->WriteChar( entry.mpDecompressor->miCompressionType ); entry.mpDecompressor->WriteEngineData( mpArchiveStorageObject ); } else { mStatus.SetError( AL_UNKNOWN_COMPRESSION_TYPE, "No compressor or decompressor for archive" " entry %s", entry.mpStorageObject->mName.GetSafeName() ); return; } mpArchiveStorageObject->WriteChar( entry.mpStorageObject->miStorageObjectType ); entry.mpStorageObject->WriteStorageObjectData( mpArchiveStorageObject ); mpArchiveStorageObject->WriteGlLong( entry.mpStorageObject->GetSize() ); mpArchiveStorageObject->WriteGlLong( entry.GetCompressedSize() ); mpArchiveStorageObject->WriteGlLong( entry.GetCrc32() ); mpArchiveStorageObject->WriteGlLong( entry.mlCompressedObjectPosition ); mpArchiveStorageObject->WriteString( entry.GetComment() ); mpArchiveStorageObject->WriteGlLong( entry.mpStorageObject->mTimeDate.GetUnixTime() ); mpArchiveStorageObject->WriteGlShort( entry.mpStorageObject->mAttributes.PackedAttributes() ); if ( mpArchiveStorageObject->mStatus < 0 ) mStatus = mpArchiveStorageObject->mStatus; } // // NAME // // ALGlArchive::ReadDirectory() // // PLATFORMS/ENVIRONMENTS // // Console Windows PM // C++ // // SHORT DESCRIPTION // // Read in the directory for a GL format archive. // // C++ SYNOPSIS // // #include "arclib.h" // #include "glarc.h" // // int ALGlArchive::ReadDirectory( ALEntryList &list ) // // C SYNOPSIS // // None, internal function. // // VB SYNOPSIS // // None, internal function. // // DELPHI SYNOPSIS // // None, internal function. // // ARGUMENTS // // list : An ALEntryList object that is going to receive all the // new ALEntry objects created from reading the directory. // // DESCRIPTION // // During the process of creating ArchiveLib 2.0, the WriteDirectory() // function was moved up into the base class. The addition of three // virtual helper functions takes care of all the specialized aspects // needed to make it happen. // // Well, I haven't gotten around to that for the ReadDirectory function. // Maybe in the final release of 2.0, but more likely in the release // of ArchiveLib 3.0. It's not a critical issue, it just helps break // the code down into smaller pieces, and is somewhat more efficient by // sharing common code. // // RETURNS // // AL_SUCCESS or < AL_SUCCESS if things don't work. // // EXAMPLE // // SEE ALSO // // REVISION HISTORY // // February 14, 1996 2.0A : New Release // int AL_PROTO ALGlArchive::ReadDirectory( ALEntryList AL_DLL_FAR &list ) /* Tag protected function */ { list.mrMonitor.ArchiveOperation( AL_START_DIRECTORY_READ, this, 0 ); ALOpenInputFile archive( *mpArchiveStorageObject ); if ( mpArchiveStorageObject->mStatus < 0 ) return mStatus = mpArchiveStorageObject->mStatus; // // First I seek to the start of the directory (offset found at 0), and // read in the version. This function only supports the directory // structure defined in version 0x100. // mpArchiveStorageObject->Seek( 0 ); mpArchiveStorageObject->ReadGlLong( mlDirectoryOffset ); mpArchiveStorageObject->Seek( mlDirectoryOffset ); mpArchiveStorageObject->ReadGlShort( miVersion ); if ( miVersion != 0x100 ) return mStatus.SetError( AL_INVALID_ARCHIVE, "%s is not a valid archive file", mpArchiveStorageObject->mName.GetSafeName() ); // // Read in any customized archive data defined by a derived class. // ReadArchiveData(); // // Read in the comment, deleting the old one if necessary. // mpArchiveStorageObject->ReadString( mComment ); // // Now, the big loop. I have to read in each entry, one at a time, and // add it to the list. If I broke this out into a separate routine it // would make the whole thing a lot more manageable. // for ( ; ; ) { if ( mpArchiveStorageObject->mStatus < 0 ) return mStatus = mpArchiveStorageObject->mStatus; ALName name; mpArchiveStorageObject->ReadString( name ); // // The directory ends with a blank name. // if ( strlen( name ) == 0 ) break; // // Derived classes are responsible for providing a version of // CreateCompressionEngine() that will convert the engine_type // integer into a created compression engine. The derived class is // then also responsible for reading in the engine data from the archive. // int engine_type = mpArchiveStorageObject->ReadChar(); ALCompressor *compressor = 0; ALDecompressor *decompressor = list.mToolKit.CreateDecompressor( engine_type ); if ( decompressor ) decompressor->ReadEngineData( mpArchiveStorageObject ); else { compressor = list.mToolKit.CreateCompressor( engine_type ); if ( compressor ) compressor->ReadEngineData( mpArchiveStorageObject ); else { ALName temp; mpArchiveStorageObject->ReadString( temp ); // // I used to treat this as an error, now I don't. The error will come about // when you actually try to decompress the object. // #if 0 return mStatus.SetError( AL_CANT_CREATE_ENGINE, "Failure creating compression engine for object %s", (const char *) name ); #endif } } // // Now we go through a nearly identical process to create the storage object. // The derived class is responsible for writing a CreateStorageObject() // function that converts an object_type integer to a created storage // object. The derived class also has to read in the storage object // data. // int object_type = mpArchiveStorageObject->ReadChar(); ALStorage *storage_object = list.mToolKit.CreateStorageObject( name, object_type ); if ( storage_object ) storage_object->ReadStorageObjectData( mpArchiveStorageObject ); else { ALName temp; mpArchiveStorageObject->ReadString( temp ); // // I used to treat this as an error, now I don't. The error will come about // when you actually try to compress the object. // #if 0 return mStatus.SetError( AL_CANT_CREATE_STORAGE_OBJECT, "Failure creating storage object for object %s", (const char *) name ); #endif } // // The rest of the stuff in the entry is pretty straightforward. // mpArchiveStorageObject->ReadGlLong( storage_object->mlSize ); ALEntry *job = new ALEntry( list, storage_object, compressor, decompressor ); mpArchiveStorageObject->ReadGlLong( job->mlCompressedSize ); mpArchiveStorageObject->ReadGlLong( job->mlCrc32 ); mpArchiveStorageObject->ReadGlLong( job->mlCompressedObjectPosition ); ALName comment; mpArchiveStorageObject->ReadString( comment ); job->SetComment( comment ); long unix_time; mpArchiveStorageObject->ReadGlLong( unix_time ); storage_object->mTimeDate.SetTimeDate( unix_time ); short int packed_attributes; mpArchiveStorageObject->ReadGlShort( packed_attributes ); storage_object->mAttributes.SetFromPackedAttributes( packed_attributes ); } list.mrMonitor.ArchiveOperation( AL_END_DIRECTORY_READ, this, 0 ); return mStatus; } // // NAME // // ALEntryList::ClearError() // // PLATFORMS/ENVIRONMENTS // // Console Windows PM // C++ // // SHORT DESCRIPTION // // Reset the error status for an entry list. // // C++ SYNOPSIS // // #include "arclib.h" // #include "glarc.h" // // void ALGlArchive::PreCreate() // // C SYNOPSIS // // None, internal support function. // // VB SYNOPSIS // // None, internal support function. // // DELPHI SYNOPSIS // // None, internal support function. // // ARGUMENTS // // None. // // DESCRIPTION // // The Create() function is found in the base class. It takes care of // sticking a bunch of compressed objects into an archive. It gets // ready to do this, then class this function first. // // Since this is a virtual function, the derived class can put any // specialized behavior here. For Greenleaf archives, the only // specialized behavior needed is to save enough space for a long // in the file. After the archive is created, the long at offset 0 // will get a pointer to the start of the directory. // // RETURNS // // Nothing. // // EXAMPLE // // SEE ALSO // // REVISION HISTORY // // February 14, 1996 2.0A : New Release // void AL_PROTO ALGlArchive::PreCreate() /* Tag protected function */ { // // The first four bytes in the archive are a long that points to the // first byte of the directory. I don't know where the directory is // going to be, so I just reserve space at this time with a dummy value. // mpArchiveStorageObject->WriteGlLong( 0x12345678L ); }