1457 lines
		
	
	
		
			48 KiB
		
	
	
	
		
			C++
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			1457 lines
		
	
	
		
			48 KiB
		
	
	
	
		
			C++
		
	
	
		
			Executable File
		
	
	
	
	
| //
 | |
| // ARCHIVEB.CPP
 | |
| //
 | |
| //  Source file for ArchiveLib 1.0
 | |
| //
 | |
| //  Copyright (c) Greenleaf Software, Inc. 1994
 | |
| //  All Rights Reserved
 | |
| //
 | |
| // CONTENTS
 | |
| //
 | |
| //  ALArchiveBase::operator new()
 | |
| //  ALArchiveBase::ALArchiveBase()
 | |
| //  ALArchiveBase::~ALArchiveBase()
 | |
| //  ALArchiveBase::SetComment()
 | |
| //  ALArchiveBase::WriteDirectory()
 | |
| //  ALArchiveBase::ScanStatus()
 | |
| //  ALArchiveBase::Extract()
 | |
| //  ALArchiveBase::AddJobs()
 | |
| //  ALArchiveBase::AddDirectoryEntries()
 | |
| //  ALArchiveBase::CalculateJobSize()
 | |
| //  ALArchiveBase::CalculateCompressedJobSize()
 | |
| //  ALArchiveBase::Create(ALEntryList&)
 | |
| //  ALArchiveBase::CopyJobs()
 | |
| //  ALArchiveBase::Create( ALArchiveBase&,ALEntryList&)
 | |
| //  ALArchiveBase::Append(ALEntryList&)
 | |
| //  ALArchiveBase::Append(ALArchiveBase&,ALEntryList&)
 | |
| //  ALArchiveBase::ReadDirectory()
 | |
| //  ALArchiveBase::WriteArchiveData()
 | |
| //  ALArchiveBase::ReadArchiveData()
 | |
| //  ALArchiveBase::Delete(ALEntryList&,ALArchiveBase&)
 | |
| //  ALArchiveBase::FillListBox()
 | |
| //  
 | |
| //
 | |
| // DESCRIPTION
 | |
| //
 | |
| //  This file contains all of the source code for the base class, 
 | |
| //  ALArchiveBase.  Classes derived from ALArchiveBase don't actually
 | |
| //  do much work, they just bind different sorts of storage objects and
 | |
| //  compression engines to an application, so this is where all the action
 | |
| //  is.  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
 | |
| //
 | |
| //
 | |
| 
 | |
| #include "arclib.h"
 | |
| #pragma hdrstop
 | |
| 
 | |
| #include "_openf.h"
 | |
| 
 | |
| //
 | |
| // void * ALArchiveBase::operator new( size_t size )
 | |
| //
 | |
| // ARGUMENTS:
 | |
| //
 | |
| //  size  :  The number of bytes needed to create a new ALArchiveBase 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 enter 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 ALArchiveBase::operator new( size_t size )
 | |
| {
 | |
|   return ::new char[ size ];
 | |
| }
 | |
| #endif
 | |
| 
 | |
| //
 | |
| // ALArchiveBase::ALArchiveBase( ALStorage *storage_object,
 | |
| //                               short int delete_in_dtor )
 | |
| //
 | |
| // ARGUMENTS:
 | |
| //
 | |
| //  storage_object   : A pointer to the storage object that will/does 
 | |
| //                     hold the archive.
 | |
| //
 | |
| //  delete_in_dtor   : This flags whether the ALArchiveBase object should call
 | |
| //                     the destructor for the storage object when the
 | |
| //                     ALArchiveBase object is created.
 | |
| //
 | |
| // RETURNS
 | |
| //
 | |
| //  Nothing, it is a constructor.
 | |
| //
 | |
| // DESCRIPTION
 | |
| //
 | |
| //  This is the ALArchiveBase constructor.  It is a public member function,
 | |
| //  but in practice it should only be called by the constructors for
 | |
| //  class derived from ALArchiveBase.  Since there are pure functions
 | |
| //  in this class, you can't construct an object of this type anyway,
 | |
| //  no matter how hard you try.
 | |
| //
 | |
| //  Despite the complexity of this class, and the vast array of member
 | |
| //  functions it contains, the constructor doesn't have much to do.  It
 | |
| //  just sets up the contents of a few data members, and that's that.
 | |
| //
 | |
| // REVISION HISTORY
 | |
| //
 | |
| //   May 23, 1994  1.0A  : First release
 | |
| //
 | |
| 
 | |
| AL_PROTO ALArchiveBase::ALArchiveBase( ALStorage AL_DLL_FAR *storage_object,
 | |
|                                       short int delete_in_dtor )
 | |
| : miDeleteStorageObject( delete_in_dtor )
 | |
| {
 | |
|   mpArchiveStorageObject = storage_object;
 | |
|   mszComment = 0;
 | |
|   mlDirectoryOffset = -1L;
 | |
|   miVersion = -1;
 | |
| }
 | |
| 
 | |
| //
 | |
| // ALArchiveBase::~ALArchiveBase()
 | |
| //
 | |
| // ARGUMENTS:
 | |
| //
 | |
| //  None.
 | |
| //
 | |
| // RETURNS
 | |
| //
 | |
| //  None, destructors don't get any.
 | |
| //
 | |
| // DESCRIPTION
 | |
| //
 | |
| //  The destructor for ALArchiveBase has a few pieces of busy work to
 | |
| //  complete.  First, it might have a comment to delete.  Second, it
 | |
| //  might have to delete its storage object, but only if it was told
 | |
| //  to in the constructor.
 | |
| //
 | |
| // REVISION HISTORY
 | |
| //
 | |
| //   May 23, 1994  1.0A  : First release
 | |
| //
 | |
| 
 | |
| AL_PROTO ALArchiveBase::~ALArchiveBase()
 | |
| {
 | |
|   AL_ASSERT( GoodTag(), "~Archive(): Attempting to delete invalid ALArchiveBase" );
 | |
|   if ( mszComment )
 | |
|     delete[] mszComment;
 | |
|   if ( mpArchiveStorageObject && miDeleteStorageObject )
 | |
|     delete mpArchiveStorageObject;
 | |
|   AL_ASSERT( GoodTag(), "~Archive::Attempting to delete invalid ALArchiveBase" );
 | |
| }
 | |
| 
 | |
| //
 | |
| // int ALArchiveBase::SetComment( char * comment )
 | |
| //
 | |
| // ARGUMENTS:
 | |
| //
 | |
| //  comment :  The new comment that is going to be attached to the archive.
 | |
| //
 | |
| // RETURNS
 | |
| //
 | |
| //  AL_SUCCESS, if things went well, AL_CANT_ALLOCATE_MEMORY if allocation
 | |
| //  of the character array failed.
 | |
| //
 | |
| // DESCRIPTION
 | |
| //
 | |
| //  The archive object has a comment member, that is blank when first 
 | |
| //  constructed.  It can be set to something interesting either by
 | |
| //  reading in a new comment along with the archive directory, or by
 | |
| //  setting it using this function. 
 | |
| //
 | |
| // REVISION HISTORY
 | |
| //
 | |
| //   May 23, 1994  1.0A  : First release
 | |
| //
 | |
| 
 | |
| int AL_PROTO ALArchiveBase::SetComment( char AL_DLL_FAR * comment )
 | |
| {
 | |
|   if ( mszComment )
 | |
|     delete[] mszComment;
 | |
|   if ( comment == 0 )
 | |
|     mszComment = 0;
 | |
|   else {
 | |
|     mszComment = new char[ strlen( comment ) + 1 ];
 | |
|     if ( mszComment )
 | |
|       strcpy( mszComment, comment );
 | |
|     else
 | |
|       return mStatus.SetError( AL_CANT_ALLOCATE_MEMORY,
 | |
|                               "Failed to allocate memory for "
 | |
|                               "comment in archive %s",
 | |
|                               mpArchiveStorageObject->mName.GetName() );
 | |
|   }
 | |
|   return mStatus;
 | |
| }
 | |
| 
 | |
| //
 | |
| // int ALArchiveBase::WriteDirectory( ALEntryList &list )
 | |
| //
 | |
| // ARGUMENTS:
 | |
| //
 | |
| //  list  :  The ALEntryList object that contains the Archive's
 | |
| //           up to date directory.
 | |
| //
 | |
| // RETURNS
 | |
| //
 | |
| //  The integer stored in mStatus, which ought to be AL_SUCCESS if everything
 | |
| //  went okay, or some int < AL_SUCCESS on error.
 | |
| //
 | |
| // DESCRIPTION
 | |
| //
 | |
| //  This is the public function the user can call to rewrite the directory
 | |
| //  for an archive. It is also called internally be several of the functions
 | |
| //  the update archive contents.  Probably the main reason to call this under
 | |
| //  normal circumstances would be after modifying the comment field of an 
 | |
| //  archive.
 | |
| //
 | |
| // REVISION HISTORY
 | |
| //
 | |
| //   May 23, 1994  1.0A  : First release
 | |
| //
 | |
| 
 | |
| //
 | |
| // Don't call ArchiveOperation() here, because this might
 | |
| // just be a component of a directory write (during an append, eg.)
 | |
| // 
 | |
| int AL_PROTO ALArchiveBase::WriteDirectory( ALEntryList AL_DLL_FAR &list )
 | |
| {
 | |
|   ALOpenInputFile archive( *mpArchiveStorageObject );
 | |
| 
 | |
|   mpArchiveStorageObject->Seek( mlDirectoryOffset );
 | |
|   mpArchiveStorageObject->WritePortableShort( miVersion );
 | |
|   WriteArchiveData();
 | |
|   mpArchiveStorageObject->WriteString( mszComment );
 | |
| 
 | |
|   AddDirectoryEntries( list );
 | |
|   return mStatus;
 | |
| }
 | |
| 
 | |
| // PRIVATE MEMBER FUNCTION
 | |
| //
 | |
| // void ALArchiveBase::ScanStatus( ALEntryList &list )
 | |
| //
 | |
| // ARGUMENTS:
 | |
| //
 | |
| //  list  :  The list of entries that have just been processed.
 | |
| //
 | |
| // RETURNS
 | |
| //
 | |
| //  None.  This function sort of has a return, it will update
 | |
| //  the member mStatus with an error code if one is found.
 | |
| //
 | |
| // DESCRIPTION
 | |
| //
 | |
| //
 | |
| // After an archive operation, I use this function to update the
 | |
| // status member of the archive.  If the archive doesn't already
 | |
| // have an error, I check through all the storage objects and
 | |
| // compression engines to see if any of them hosed up.  Any error
 | |
| // of any sort by any of them is copied into the archive status.
 | |
| // The whole point of this is to ensure that if
 | |
| // ALArchiveBase.mStatus == AL_SUCCESS, it means everything worked.
 | |
| //
 | |
| // REVISION HISTORY
 | |
| //
 | |
| //   May 23, 1994  1.0A  : First release
 | |
| //
 | |
| 
 | |
| void AL_PROTO ALArchiveBase::ScanStatus( ALEntryList AL_DLL_FAR &list )
 | |
| {
 | |
|   if ( mStatus < AL_SUCCESS )
 | |
|     return;
 | |
|   ALEntry *job = list.GetFirstEntry();
 | |
|   while ( job ) {
 | |
|     if ( job->mpStorageObject->mStatus < AL_SUCCESS ) {
 | |
|       mStatus.SetError( job->mpStorageObject->mStatus,
 | |
|                        "%s: %s",
 | |
|                        job->mpStorageObject->mName.GetSafeName(),
 | |
|                        job->mpStorageObject->mStatus.GetStatusDetail() );
 | |
|       return;
 | |
|     }
 | |
|     job = job->GetNextEntry();
 | |
|   }
 | |
| }
 | |
| 
 | |
| //
 | |
| // int ALArchiveBase::Extract( ALEntryList &list )
 | |
| //
 | |
| // ARGUMENTS:
 | |
| //
 | |
| //  list   :  A list of storage objects to be extracted (if marked.)
 | |
| //
 | |
| // RETURNS
 | |
| //
 | |
| //  AL_SUCCESS if all went well, or < AL_SUCCESS if the process
 | |
| //  went sour at any point.
 | |
| //
 | |
| // DESCRIPTION
 | |
| //
 | |
| //  This function is one of the high level functions that can be called
 | |
| //  from a user program.  It has several important things it needs to do
 | |
| //  in order to extract the appropriate objects from an archive:
 | |
| //
 | |
| //  o  Flag any duplicates.  We don't extract anything twice, that will
 | |
| //     be considered an error.
 | |
| //
 | |
| //  o  Open the Archive storage object.
 | |
| //
 | |
| //  o  Calculate the total number of bytes to be processed in the entire
 | |
| //     job, and give this information to the monitor, who might care if
 | |
| //     he is in AL_MONITOR_JOB mode.
 | |
| //
 | |
| //  o  Iterate through the list, performing the following actions for
 | |
| //     each object marked for extraction:
 | |
| //
 | |
| //     o  Update the monitor with information about the object destined
 | |
| //        for immediate extraction.
 | |
| //
 | |
| //     o  Locate the compressed object in the archive.
 | |
| //
 | |
| //     o  Decompress the object, and check for CRC errors.
 | |
| //
 | |
| //     o  Update the monitor.
 | |
| //
 | |
| //  o  After all objects have been extracted, update the monitor again.
 | |
| //
 | |
| //  o  Scan for extraction errors, then return the result.
 | |
| //
 | |
| // REVISION HISTORY
 | |
| //
 | |
| //   May 23, 1994  1.0A  : First release
 | |
| //
 | |
| 
 | |
| int AL_PROTO ALArchiveBase::Extract( ALEntryList AL_DLL_FAR &list )
 | |
| {
 | |
|   //
 | |
|   // Open the input storage object, if not already open.  Let the monitor
 | |
|   // know about it.
 | |
|   //
 | |
|   ALOpenInputFile archive( *mpArchiveStorageObject );
 | |
|   list.mrMonitor.ArchiveOperation( AL_ARCHIVE_OPEN, this, 0 );
 | |
|   //
 | |
|   // Get rid of any duplicate entries, and set up the monitor sizes.
 | |
|   //
 | |
|   list.UnmarkDuplicates( list, "Duplicate entry in list passed to Extract()" );
 | |
|   list.mrMonitor.mlJobSoFar = 0L;
 | |
|   if ( list.mrMonitor.miMonitorType == AL_MONITOR_JOB )
 | |
|     list.mrMonitor.mlJobSize = CalculateCompressedJobSize( list );
 | |
|   //
 | |
|   // This loop iterates through the entire ALEntryList.  We only care about
 | |
|   // ALEntry objects that have their mark set.
 | |
|   //
 | |
|   ALEntry *job = list.GetFirstEntry();
 | |
|   while ( job ) {
 | |
|     if ( job->miMark ) {
 | |
|       //
 | |
|       // Go to the correct input position in this, and set up the monitor for
 | |
|       // this particular object.
 | |
|       //
 | |
|       list.mrMonitor.ArchiveOperation( AL_EXTRACTION_OPEN, this, job );
 | |
|       mpArchiveStorageObject->Seek( job->mlCompressedObjectPosition );
 | |
|       list.mrMonitor.mlObjectStart = job->mlCompressedObjectPosition;
 | |
|       list.mrMonitor.mlObjectSize = job->mlCompressedSize;
 | |
|       mpArchiveStorageObject->mpMonitor = &list.mrMonitor;
 | |
|       //
 | |
|       // Extract it, then check the CRC.
 | |
|       //
 | |
|       job->mpCompressionEngine->Decompress( *mpArchiveStorageObject,
 | |
|                                            *job->mpStorageObject,
 | |
|                                            job->mlCompressedSize );
 | |
|       if ( job->mpStorageObject->GetCrc32() != job->GetCrc32() )
 | |
|         job->mpStorageObject->mStatus.SetError(
 | |
|           AL_CRC_ERROR,
 | |
|           "CRC32 was supposed to be %08lx, was %08lx",
 | |
|           job->GetCrc32(),
 | |
|           job->mpStorageObject->GetCrc32() );
 | |
|       //
 | |
|       // Update the monitor data, and yield some time. Note that I turn off
 | |
|       // the monitor at this point so it doesn't jump around while I seek to the
 | |
|       // next position in the archive.
 | |
|       //
 | |
|       list.mrMonitor.mlJobSoFar  += job->mlCompressedSize;
 | |
|       mpArchiveStorageObject->YieldTime();
 | |
|       mpArchiveStorageObject->mpMonitor = 0;
 | |
|       list.mrMonitor.ArchiveOperation( AL_EXTRACTION_CLOSE, this, job );
 | |
|       job->mpStorageObject->mpMonitor = 0;
 | |
|     }
 | |
|     job = job->GetNextEntry();
 | |
|   }
 | |
|   //
 | |
|   // Update the monitor, then scan the list for status errors.
 | |
|   //
 | |
|   list.mrMonitor.ArchiveOperation( AL_ARCHIVE_CLOSE, this, 0 );
 | |
|   ScanStatus( list );
 | |
|   return mStatus;
 | |
| }
 | |
| 
 | |
| // PRIVATE MEMBER FUNCTION
 | |
| //
 | |
| // int ALArchiveBase::AddJobs( ALEntryList &list )
 | |
| //
 | |
| // ARGUMENTS:
 | |
| //
 | |
| //  list  :  A list of marked objects to be added to the archive.
 | |
| //
 | |
| // RETURNS
 | |
| //
 | |
| //  AL_SUCCESS if things are going well, < AL_SUCCESS in case of error.
 | |
| //
 | |
| // DESCRIPTION
 | |
| //
 | |
| //  This is a helper function that is called by both Create() and
 | |
| //  Append().  There is enough code here to justify breaking this
 | |
| //  out into a separate module.
 | |
| //
 | |
| //  All this guy does is sit in a loop, look for marked entries in the
 | |
| //  list, and compress each one into the archive.  Before it adds each object
 | |
| //  to the archive, it has to set up the monitor so that progress on the
 | |
| //  selected object will be monitored properly.  It has to dink with the
 | |
| //  monitor once again when the object has been compressed.  It relies on 
 | |
| //  the calling function to have set up the total job size and other info
 | |
| //  that the monitor might need.  It also has to set up some of the
 | |
| //  data in the ALEntry object for each job, as not all of this information
 | |
| //  is available until *after* the job has been compressed.  For example, 
 | |
| //  the storage object's CRC32 gets calculated as a byproduct of the
 | |
| //  compression process.
 | |
| //
 | |
| // REVISION HISTORY
 | |
| //
 | |
| //   May 23, 1994  1.0A  : First release
 | |
| //
 | |
| 
 | |
| int AL_PROTO ALArchiveBase::AddJobs( ALEntryList AL_DLL_FAR &list )
 | |
| {
 | |
|   list.mrMonitor.mlObjectStart = 0L; // This will be true for all input jobs
 | |
|   //
 | |
|   // This loop iterates through all of the entries in the list, picking off
 | |
|   // only the marked entries.
 | |
|   //
 | |
|   ALEntry *job = list.GetFirstEntry();
 | |
|   while ( job ) {
 | |
|     if ( job->miMark ) {
 | |
|       //
 | |
|       // We need to keep track of the position in the archive where the compressed
 | |
|       // data is going to go.
 | |
|       //
 | |
|       job->mlCompressedObjectPosition = mpArchiveStorageObject->Tell();
 | |
|       //
 | |
|       // Attach the monitor to the storage object that is going to be inserted
 | |
|       // in the archive.
 | |
|       //
 | |
|       list.mrMonitor.ArchiveOperation( AL_INSERTION_OPEN, this, job );
 | |
|       list.mrMonitor.mlObjectSize = -1L; // This means we ask for it in ALMonitor, after the object is opened
 | |
|       job->mpStorageObject->mpMonitor = &list.mrMonitor;
 | |
|       //
 | |
|       // Compress the object into the archive.  Then store the resulting CRC
 | |
|       // the compressed size in the ALEntry object.
 | |
|       //
 | |
|       job->mpCompressionEngine->Compress( *job->mpStorageObject,
 | |
|                                          *mpArchiveStorageObject );
 | |
|       job->mlCrc32 = job->mpStorageObject->GetCrc32();
 | |
|       job->mpStorageObject->mpMonitor = 0;
 | |
|       if ( job->mpCompressionEngine->mStatus < 0 )
 | |
|         return mStatus = job->mpCompressionEngine->mStatus;
 | |
|       job->mlCompressedSize = mpArchiveStorageObject->Tell() -
 | |
|         job->mlCompressedObjectPosition;
 | |
|       //
 | |
|       // Update the monitor
 | |
|       //
 | |
|       list.mrMonitor.mlJobSoFar += job->mpStorageObject->GetSize();
 | |
|       list.mrMonitor.ArchiveOperation( AL_INSERTION_CLOSE, this, job );
 | |
|     }
 | |
|     job = job->GetNextEntry();
 | |
|     if ( mStatus < 0 )
 | |
|       break;
 | |
|   }
 | |
|   return mStatus;
 | |
| }
 | |
| 
 | |
| // PRIVATE MEMBER FUNCTION
 | |
| //
 | |
| // int ALArchiveBase::AddDirectoryEntries( ALEntryList &list )
 | |
| //
 | |
| // ARGUMENTS:
 | |
| //
 | |
| //  list  :  The list of ALEntry objects to be written to the directory.
 | |
| //
 | |
| // RETURNS
 | |
| //
 | |
| //  AL_SUCCESS if everything goes well, < AL_SUCCESS otherwise.
 | |
| //
 | |
| // DESCRIPTION
 | |
| //
 | |
| //  This function writes all the entries in the list to the Archive
 | |
| //  directory.  It doesn't do a seek() to the start of the directory,
 | |
| //  so the calling routine needs to be absolutely sure that it is in
 | |
| //  the write spot when it invokes this.
 | |
| //
 | |
| //  This routine leaves the output pointer of the storage object pointing
 | |
| //  at just the right spot to write some more entries.  That means you can
 | |
| //  call this function repeatedly as new entries are added to the list.
 | |
| //  The function also terminates the directory properly, so that if you
 | |
| //  don't add any more directory entries, the archive is still ready
 | |
| //  for primetime.
 | |
| //
 | |
| //  This function is called by WriteDirectory(), and both versions of
 | |
| //  Append().
 | |
| //
 | |
| //  Writing directory entries is a real simple linear task.  The source
 | |
| //  code here should explain it all.
 | |
| //
 | |
| // REVISION HISTORY
 | |
| //
 | |
| //   May 23, 1994  1.0A  : First release
 | |
| //
 | |
| 
 | |
| //
 | |
| // No call to ArchiveOperation here, either.  The setup and everything
 | |
| // else has to be done by the calling routine.
 | |
| //
 | |
| int AL_PROTO ALArchiveBase::AddDirectoryEntries( ALEntryList AL_DLL_FAR &list )
 | |
| {
 | |
|   ALEntry *job = list.GetFirstEntry();
 | |
|   while ( job ) {
 | |
|     if ( job->miMark ) {
 | |
|       mpArchiveStorageObject->WriteString( job->mpStorageObject->mName.GetSafeName() );
 | |
|       mpArchiveStorageObject->WriteChar( job->mpCompressionEngine->miCompressionType );
 | |
|       job->mpCompressionEngine->WriteEngineData( mpArchiveStorageObject );
 | |
|       mpArchiveStorageObject->WriteChar( job->mpStorageObject->miStorageObjectType );
 | |
|       job->mpStorageObject->WriteStorageObjectData( mpArchiveStorageObject );
 | |
| 
 | |
|       mpArchiveStorageObject->WritePortableLong( job->mpStorageObject->GetSize() );
 | |
|       mpArchiveStorageObject->WritePortableLong( job->GetCompressedSize() );
 | |
|       mpArchiveStorageObject->WritePortableLong( job->GetCrc32() );
 | |
|       mpArchiveStorageObject->WritePortableLong( job->mlCompressedObjectPosition );
 | |
|       mpArchiveStorageObject->WriteString( job->GetComment() );
 | |
|       mpArchiveStorageObject->WritePortableLong( job->mpStorageObject->mTimeDate.GetUnixTime() );
 | |
|       mpArchiveStorageObject->WritePortableShort( job->mpStorageObject->mAttributes.PackedAttributes() );
 | |
|       if ( mpArchiveStorageObject->mStatus < 0 )
 | |
|         return mStatus = mpArchiveStorageObject->mStatus;
 | |
|     }
 | |
|     job = job->GetNextEntry();
 | |
|   }
 | |
|   //
 | |
|   // I write out the end of directory string here.  But then I back up the
 | |
|   // file pointer so new entries can be appended without causing any trouble
 | |
|   // The end of the directory is denoted by an entry with an empty name.
 | |
|   //
 | |
|   mpArchiveStorageObject->WriteString( "" );
 | |
|   mpArchiveStorageObject->Seek( mpArchiveStorageObject->Tell() - 2 );
 | |
| 
 | |
|   return AL_SUCCESS;
 | |
| }
 | |
| 
 | |
| // PRIVATE MEMBER FUNCTION
 | |
| //
 | |
| // long ALArchiveBase::CalculateJobSize( ALEntryList &list )
 | |
| //
 | |
| // ARGUMENTS:
 | |
| //
 | |
| //  list  :  The list of entries in the job.
 | |
| //
 | |
| // RETURNS
 | |
| //
 | |
| //  This function is used to calculate the total number of bytes that
 | |
| //  are going to have to be moved when performing a Create() or Append()
 | |
| //  operation.  We need that info in order to set up a monitor properly
 | |
| //  when its mode is AL_MONITOR_JOB.  Naturally, we don't really care
 | |
| //  about the total size when the monitor is in AL_MONITOR_OBJECTS mode.
 | |
| //  Anyway, it returns the total size of all the objects.
 | |
| //
 | |
| // DESCRIPTION
 | |
| //
 | |
| //  If a monitor is running in AL_MONITOR_JOB mode, we need to add up
 | |
| //  the sizes of all the storage objects we are going to process, so
 | |
| //  that we can accurately track our progress from 0 to 100%.  In many
 | |
| //  cases, the sizes of all the files will not yet be known, which means
 | |
| //  this routine will have to open the files up and check the values.
 | |
| //  That is why we only call this routine when we have to.
 | |
| //
 | |
| // REVISION HISTORY
 | |
| //
 | |
| //   May 23, 1994  1.0A  : First release
 | |
| //
 | |
| 
 | |
| long AL_PROTO ALArchiveBase::CalculateJobSize( ALEntryList AL_DLL_FAR &list )
 | |
| {
 | |
|   long total = 0;
 | |
|   ALEntry *job = list.GetFirstEntry();
 | |
|   while ( job ) {
 | |
|     if ( job->miMark ) {
 | |
|       long obj_size;
 | |
|       if ( ( obj_size = job->mpStorageObject->GetSize() ) == -1 ) {
 | |
|         job->mpStorageObject->Open();
 | |
|         obj_size = job->mpStorageObject->GetSize();
 | |
|         job->mpStorageObject->Close();
 | |
|         if ( obj_size == -1 )
 | |
|           return -1;
 | |
|       }
 | |
|       total += obj_size;
 | |
|     }
 | |
|     job = job->GetNextEntry();
 | |
|   }
 | |
|   return total;
 | |
| }
 | |
| 
 | |
| // PRIVATE MEMBER FUNCTION
 | |
| //
 | |
| // long ALArchiveBase::CalculateCompressedJobSize( ALEntryList &list )
 | |
| //
 | |
| // ARGUMENTS:
 | |
| //
 | |
| //  list  :  The list of compressed jobs to be processed.
 | |
| //
 | |
| // RETURNS
 | |
| //
 | |
| //  The total size of a bunch of compressed objects, not the uncompressed
 | |
| //  size.
 | |
| //
 | |
| // DESCRIPTION
 | |
| //
 | |
| //  When we are monitoring an Extract() command, the monitor object
 | |
| //  gets attached to the Archive, not to the objects that are getting
 | |
| //  sucked out of it.  This means that progress is being measured
 | |
| //  against the compressed objects, not the true size objects.  So
 | |
| //  before I start the extract, I call this function to see just how
 | |
| //  much compressed space is taken up by the compressed objects in
 | |
| //  the archive.
 | |
| //
 | |
| // REVISION HISTORY
 | |
| //
 | |
| //   May 23, 1994  1.0A  : First release
 | |
| //
 | |
| 
 | |
| long AL_PROTO ALArchiveBase::CalculateCompressedJobSize( ALEntryList AL_DLL_FAR &list )
 | |
| {
 | |
|   long total = 0;
 | |
|   ALEntry *job = list.GetFirstEntry();
 | |
|   while ( job ) {
 | |
|     if ( job->miMark ) {
 | |
|       if ( job->mlCompressedSize  == -1 )
 | |
|         return -1;
 | |
|       else
 | |
|         total += job->mlCompressedSize;
 | |
|     }
 | |
|     job = job->GetNextEntry();
 | |
|   }
 | |
|   return total;
 | |
| }
 | |
| 
 | |
| //
 | |
| // int ALArchiveBase::Create( ALEntryList &list )
 | |
| //
 | |
| // ARGUMENTS:
 | |
| //
 | |
| //  list  :  A list of ALEntry objects describing what is going to
 | |
| //           be stuffed into the archive.
 | |
| //
 | |
| // RETURNS
 | |
| //
 | |
| //  AL_SUCCESS if things went well, <AL_SUCCESS if things sucked.
 | |
| //
 | |
| // DESCRIPTION
 | |
| //
 | |
| //  This is one of two public Create() functions.  This is the one you
 | |
| //  call if you have a bunch of storage objects just sitting around
 | |
| //  and you want to put them into an archive.  It works by simply
 | |
| //  walking through the list, and adding each object to the archive
 | |
| //  storage object by stuffing it through the compression engine.
 | |
| //
 | |
| //  This routine has to first create the archive object by opening the
 | |
| //  associated storage object an reserving a long for a pointer to
 | |
| //  the directory (which will have to be written later, since we don't
 | |
| //  have any idea where it is going to be at this time).
 | |
| // 
 | |
| //  After creating the archive storage object, we go through and remove any
 | |
| //  duplicated entries in the input list.  If the monitor we will use
 | |
| //  for this operation is in AL_MONITOR_JOB mode, we then have to 
 | |
| //  calculate the total job size by scanning all the input files 
 | |
| //  (this is really done by CalculateJobSize().)  Finally, we call
 | |
| //  AddJobs() to do the real work.  Once that is done, we can call
 | |
| //  WriteDirectory() to finish up.  Note that there are a few calls
 | |
| //  to ArchiveOperation scattered at key points throughout the process.
 | |
| //
 | |
| // REVISION HISTORY
 | |
| //
 | |
| //   May 23, 1994  1.0A  : First release
 | |
| //
 | |
| 
 | |
| int AL_PROTO ALArchiveBase::Create( ALEntryList AL_DLL_FAR &list )
 | |
| {
 | |
|   //
 | |
|   // Miscellaneous: open the archive, set the archive version, initialize
 | |
|   // the monitor.  If the storage object is broken, quite now!
 | |
|   //
 | |
|   ALOpenOutputFile archive( *mpArchiveStorageObject );
 | |
|   miVersion = 0x100;
 | |
|   list.mrMonitor.ArchiveOperation( AL_ARCHIVE_OPEN, this, 0 );
 | |
|   if ( mpArchiveStorageObject->mStatus < 0 )
 | |
|     return mStatus = mpArchiveStorageObject->mStatus;
 | |
|   //
 | |
|   // We don't want to create an archive with duplicate entries, so we check here.
 | |
|   //
 | |
|   list.UnmarkDuplicates( list, 
 | |
|                         "Duplicate entry in list passed to Create()" );
 | |
|   //
 | |
|   // 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->WritePortableLong( 0x12345678L );
 | |
|   //
 | |
|   // Set up the monitor.
 | |
|   //
 | |
|   list.mrMonitor.mlJobSoFar = 0L;
 | |
|   if ( list.mrMonitor.miMonitorType == AL_MONITOR_JOB )
 | |
|     list.mrMonitor.mlJobSize = CalculateJobSize( list );
 | |
|   //
 | |
|   // AddJobs() takes care of actually adding the jobs to the archive.
 | |
|   //
 | |
|   AddJobs( list );
 | |
|   //
 | |
|   // All the jobs are written, now I can figure out where the
 | |
|   // directory is in the storage object.  I copy it, then write
 | |
|   // it out to the storage object at position 0.
 | |
|   //
 | |
|   mlDirectoryOffset = mpArchiveStorageObject->Tell();
 | |
|   mpArchiveStorageObject->Seek( 0L );
 | |
|   mpArchiveStorageObject->WritePortableLong( mlDirectoryOffset );
 | |
|   //
 | |
|   // Return without writing the directory if there is an error in the
 | |
|   // archive storage object.
 | |
|   //
 | |
|   if ( mpArchiveStorageObject->mStatus < 0 ) {
 | |
|     list.mrMonitor.ArchiveOperation( AL_ARCHIVE_CLOSE, this, 0 );
 | |
|     return mStatus = mpArchiveStorageObject->mStatus;
 | |
|   }
 | |
|   //
 | |
|   // Finally, write out the directory to the storage object.
 | |
|   //
 | |
|   list.mrMonitor.ArchiveOperation( AL_START_DIRECTORY_WRITE, this, 0 );
 | |
|   WriteDirectory( list );
 | |
|   //
 | |
|   // Update the monitor, check for errors, and blow.
 | |
|   //
 | |
|   list.mrMonitor.ArchiveOperation( AL_END_DIRECTORY_WRITE, this, 0 );
 | |
|   list.mrMonitor.ArchiveOperation( AL_ARCHIVE_CLOSE, this, 0 );
 | |
|   ScanStatus( list );
 | |
|   return mStatus;
 | |
| }
 | |
| 
 | |
| // PRIVATE MEMBER FUNCTIONS
 | |
| //
 | |
| // int ALArchiveBase::CopyJobs( ALArchiveBase & source_archive,
 | |
| //                              ALEntryList & source_list )
 | |
| //
 | |
| // ARGUMENTS:
 | |
| //
 | |
| //  source_archive  :  The source for all the ALEntry objects that are
 | |
| //                     going to get copied to this.
 | |
| //
 | |
| //  source_list     :  The list of ALEntry objects that are going to be copied.
 | |
| //
 | |
| // RETURNS
 | |
| //
 | |
| //  An mStatus value, either AL_SUCCESS or < AL_SUCCESS.
 | |
| //
 | |
| // DESCRIPTION
 | |
| //
 | |
| //  This private member function is used by the Create() and Append() member
 | |
| //  functions.  Each of these two public functions has two versions, one
 | |
| //  which compresses freestanding storage objects into an archive, and
 | |
| //  another which copies jobs out of one archive and into this.  The
 | |
| //  second versions of the two functions use CopyJobs() to get the
 | |
| //  compressed objects out of one archive and put it into this.
 | |
| //
 | |
| //  The actual operation of this guy is pretty simple.  It would be almost
 | |
| //  trivial without having to take the monitor into account.  Basically,
 | |
| //  it just has to work its way through the list of entries.  For each
 | |
| //  marked entry, we just seek to the correct position in the input file,
 | |
| //  the copy the correct number of bytes to this.
 | |
| //
 | |
| //  One thing kind of funny here is that the ALEntryList starts off with
 | |
| //  offsets for the objects within the source archive.  But after copying them
 | |
| //  over, we change the offset field in the ALEntry object to reflect the new
 | |
| //  position in this.  This means that after this function has completed,
 | |
| //  you ALEntryList object is no longer associated with source_archive, it
 | |
| //  is instead associated with this.
 | |
| //
 | |
| // REVISION HISTORY
 | |
| //
 | |
| //   May 23, 1994  1.0A  : First release
 | |
| //
 | |
| 
 | |
| int AL_PROTO ALArchiveBase::CopyJobs( ALArchiveBase AL_DLL_FAR &source_archive,
 | |
|                                      ALEntryList AL_DLL_FAR &source_list )
 | |
| {
 | |
|   //
 | |
|   // Open the storage object attached to the input archive.  The storage object
 | |
|   // attached to this is already open.
 | |
|   //
 | |
|   ALOpenInputFile input( *(source_archive.mpArchiveStorageObject) );
 | |
|   //
 | |
|   // Iterate through the list of entries in the list, selecting only the
 | |
|   // marked entries.
 | |
|   //
 | |
|   ALEntry *job = source_list.GetFirstEntry();
 | |
|   while ( job ) {
 | |
|     if ( job->miMark ) {
 | |
|       //
 | |
|       // Seek the compressed object in the source archive, then update the monitor
 | |
|       // to work properly during the copy operation.
 | |
|       //
 | |
|       source_archive.mpArchiveStorageObject->Seek( job->mlCompressedObjectPosition );
 | |
|       source_list.mrMonitor.mlObjectStart = job->mlCompressedObjectPosition;
 | |
|       source_list.mrMonitor.mlObjectSize = job->mlCompressedSize;
 | |
|       source_list.mrMonitor.ArchiveOperation( AL_COPY_OPEN, this, job );
 | |
|       source_archive.mpArchiveStorageObject->mpMonitor = &source_list.mrMonitor;
 | |
|       //
 | |
|       // Save the new position in the destination archive, then copy the 
 | |
|       // whole thing across.
 | |
|       //
 | |
|       job->mlCompressedObjectPosition = mpArchiveStorageObject->Tell();
 | |
|       for ( long i = 0 ; i < job->mlCompressedSize ; i++ ) {
 | |
|         int c = source_archive.mpArchiveStorageObject->ReadChar();
 | |
|         mpArchiveStorageObject->WriteChar( c );
 | |
|       }
 | |
|       //
 | |
|       // Update the monitor now that the copy is complete.
 | |
|       //
 | |
|       source_list.mrMonitor.ArchiveOperation( AL_COPY_CLOSE, this, job );
 | |
|       source_archive.mpArchiveStorageObject->YieldTime();
 | |
|       source_list.mrMonitor.mlJobSoFar += job->mlCompressedSize;
 | |
|       source_archive.mpArchiveStorageObject->mpMonitor = 0;
 | |
|       if ( source_archive.mpArchiveStorageObject->mStatus < 0 )
 | |
|         return mStatus = source_archive.mpArchiveStorageObject->mStatus;
 | |
|       if ( mpArchiveStorageObject->mStatus < 0 )
 | |
|         return mStatus = mpArchiveStorageObject->mStatus;
 | |
|     }
 | |
|     job = job->GetNextEntry();
 | |
|     if ( mStatus < 0 )
 | |
|       break;
 | |
|   }
 | |
|   return mStatus;
 | |
| }
 | |
| 
 | |
| //
 | |
| // int ALArchiveBase::Create( ALArchiveBase &source_archive,
 | |
| //                            ALEntryList &source_list )
 | |
| //
 | |
| // ARGUMENTS:
 | |
| //
 | |
| //  source_archive  : The archive that contains the compressed objects
 | |
| //                    we are using to create this.
 | |
| //
 | |
| //  source_list     : The ALEntryList that contains the marked ALEntry
 | |
| //                    objects that are going to be inserted in this.
 | |
| //
 | |
| // RETURNS
 | |
| //
 | |
| //  AL_SUCCESS if things went well, < AL_SUCCESS to flag an error.
 | |
| //
 | |
| // DESCRIPTION
 | |
| //
 | |
| //  This is the second version of Create().  Instead of creating a new
 | |
| //  archive by using a bunch of freestanding objects, this guy just
 | |
| //  sucks existing compressed objects out of one archive and copies
 | |
| //  them directly into another.  The actual copying gets done in 
 | |
| //  CopyJobs().
 | |
| //
 | |
| //
 | |
| // REVISION HISTORY
 | |
| //
 | |
| //   May 23, 1994  1.0A  : First release
 | |
| //
 | |
| 
 | |
| int AL_PROTO ALArchiveBase::Create( ALArchiveBase AL_DLL_FAR &source_archive,
 | |
|                                    ALEntryList AL_DLL_FAR &source_list )
 | |
| {
 | |
|   //
 | |
|   // Open the source archive, set the version, and blow if for some reason 
 | |
|   // the storage object I am writing to isn't working right.
 | |
|   //
 | |
|   ALOpenOutputFile archive( *mpArchiveStorageObject );
 | |
|   miVersion = 0x100;
 | |
|   if ( mpArchiveStorageObject->mStatus < 0 )
 | |
|     return mStatus = mpArchiveStorageObject->mStatus;
 | |
|   //
 | |
|   // I don't want to create an archive with duplicates, that would be bad.
 | |
|   //
 | |
|   source_list.UnmarkDuplicates( source_list, "Duplicate entry in list passed to Create()" );
 | |
|   //
 | |
|   // At this point, just for fun, I am going to calculate the total
 | |
|   // compressed size of the jobs I am copying.  Hey, it looks like I
 | |
|   // could substitute a call to CalculateCompressedSize() here!
 | |
|   //
 | |
|   source_list.mrMonitor.ArchiveOperation( AL_ARCHIVE_OPEN, this, 0 );
 | |
|   source_list.mrMonitor.mlJobSoFar = 0L;
 | |
|   source_list.mrMonitor.mlJobSize = 0L;
 | |
|   for ( ALEntry *job = source_list.GetFirstEntry();
 | |
|        job != 0;
 | |
|        job = job->GetNextEntry() ) {
 | |
|     if ( job->GetMark() )
 | |
|       source_list.mrMonitor.mlJobSize += job->mlCompressedSize;
 | |
|   }
 | |
|   //
 | |
|   // Since I am creating a new archive, I write a long out as a place
 | |
|   // holder for the directory pointer.  When I am done copying jobs,
 | |
|   // I'll come back here and write a pointer to the directory.
 | |
|   //
 | |
|   mpArchiveStorageObject->WritePortableLong( 0x12345678L );
 | |
|   //
 | |
|   // Now copy the data.
 | |
|   //
 | |
|   CopyJobs( source_archive, source_list );
 | |
|   //
 | |
|   // Write out the directory offset, then the directory itself.
 | |
|   //
 | |
|   mlDirectoryOffset = mpArchiveStorageObject->Tell();
 | |
|   mpArchiveStorageObject->Seek( 0L );
 | |
|   mpArchiveStorageObject->WritePortableLong( mlDirectoryOffset );
 | |
|   if ( mpArchiveStorageObject->mStatus < 0 ) {
 | |
|     source_list.mrMonitor.ArchiveOperation( AL_ARCHIVE_CLOSE, this, 0 );
 | |
|     return mStatus = mpArchiveStorageObject->mStatus;
 | |
|   }
 | |
|   source_list.mrMonitor.ArchiveOperation( AL_START_DIRECTORY_WRITE,this, 0 );
 | |
|   WriteDirectory( source_list );
 | |
|   source_list.mrMonitor.ArchiveOperation( AL_END_DIRECTORY_WRITE, this, 0 );
 | |
|   source_list.mrMonitor.ArchiveOperation( AL_ARCHIVE_CLOSE, this, 0 );
 | |
|   //
 | |
|   // Update the error status, and then we are done.
 | |
|   //
 | |
|   ScanStatus( source_list );
 | |
|   return mStatus;
 | |
| }
 | |
| 
 | |
| //
 | |
| // int ALArchiveBase::Append( ALEntryList &list )
 | |
| //
 | |
| // ARGUMENTS:
 | |
| //
 | |
| //  list  : A list of objects to append to this.
 | |
| //
 | |
| // RETURNS
 | |
| //
 | |
| //  AL_SUCCESS if things go okay, < AL_SUCCESS if they didn't.
 | |
| //
 | |
| // DESCRIPTION
 | |
| //
 | |
| //  This routine is one of the public functions.  It is called to add
 | |
| //  a list of standalone objects to an existing archive, this.
 | |
| //  To accomplish this, we have to read in the existing directory, then
 | |
| //  add the new batch of objects to the archive.  Finally, I write out
 | |
| //  the old directory, then the directory for the new batch of objects.
 | |
| //
 | |
| //  There is another version of Append() that takes as input a list of
 | |
| //  entries that are in another archive.  
 | |
| //
 | |
| // REVISION HISTORY
 | |
| //
 | |
| //   May 23, 1994  1.0A  : First release
 | |
| //
 | |
| 
 | |
| int AL_PROTO ALArchiveBase::Append( ALEntryList AL_DLL_FAR &list )
 | |
| {
 | |
|   ALEntryList old_list;
 | |
|   //
 | |
|   // Open the storage object for this.
 | |
|   //
 | |
|   ALOpenInputFile archive( *mpArchiveStorageObject );
 | |
|   //
 | |
|   // I read in the current directory for this archive.  I am going to
 | |
|   // write over the directory with new stuff, so I will have to write it
 | |
|   // back out later.
 | |
|   //
 | |
|   ReadDirectory( old_list );
 | |
|   if ( mStatus < 0 )
 | |
|     return mStatus;
 | |
|   //
 | |
|   // The list of new objects I am going to add needs to be scanned for
 | |
|   // duplicates.  First I clear duplicate entries from the list itself.
 | |
|   // Then I clear any duplicates between the current list and the
 | |
|   // stuff already in the archive.
 | |
|   //
 | |
|   list.UnmarkDuplicates( list, "Duplicate entry in list passed to Append()" );
 | |
|   list.UnmarkDuplicates( old_list, "Duplicate entry in list passed to Append()" );
 | |
|   //
 | |
|   // I get the monitor set up, for the batch of entries I am about to do.
 | |
|   //
 | |
|   list.mrMonitor.ArchiveOperation( AL_ARCHIVE_OPEN, this, 0 );
 | |
|   list.mrMonitor.mlJobSoFar = 0L;
 | |
|   if ( list.mrMonitor.miMonitorType == AL_MONITOR_JOB )
 | |
|     list.mrMonitor.mlJobSize = CalculateJobSize( list );
 | |
|   //
 | |
|   // The new entries start at the position currently occupied by the
 | |
|   // directory.  I seek to that point, then call AddJobs() to do the
 | |
|   // dirty work.
 | |
|   //
 | |
|   mpArchiveStorageObject->Seek( mlDirectoryOffset );
 | |
|   AddJobs( list );
 | |
|   if ( mStatus < 0 ) {
 | |
|     list.mrMonitor.ArchiveOperation( AL_ARCHIVE_CLOSE, this, 0 );
 | |
|     return mStatus;
 | |
|   }
 | |
|   //
 | |
|   // Now that all the new stuff is in the archive, I can figure
 | |
|   // out where the directory belongs, and write it out to position
 | |
|   // 0 in the archive.
 | |
|   //
 | |
|   mlDirectoryOffset = mpArchiveStorageObject->Tell();
 | |
|   mpArchiveStorageObject->Seek( 0L );
 | |
|   mpArchiveStorageObject->WritePortableLong( mlDirectoryOffset );
 | |
|   if ( mpArchiveStorageObject->mStatus < 0 ) {
 | |
|     list.mrMonitor.ArchiveOperation( AL_ARCHIVE_CLOSE, this, 0 );
 | |
|     return mStatus = mpArchiveStorageObject->mStatus;
 | |
|   }
 | |
|   //
 | |
|   // Now I write the old directory out, and then add in the new
 | |
|   // directory entries.
 | |
|   //
 | |
|   list.mrMonitor.ArchiveOperation( AL_START_DIRECTORY_WRITE, this, 0 );
 | |
|   WriteDirectory( old_list );
 | |
|   AddDirectoryEntries( list );
 | |
|   //
 | |
|   // Update the monitor, check for errors, then leave.
 | |
|   //
 | |
|   list.mrMonitor.ArchiveOperation( AL_END_DIRECTORY_WRITE, this, 0 );
 | |
|   list.mrMonitor.ArchiveOperation( AL_ARCHIVE_CLOSE, this, 0 );
 | |
|   ScanStatus( list );
 | |
|   return mStatus;
 | |
| }
 | |
| 
 | |
| //
 | |
| // int ALArchiveBase::Append( ALArchiveBase &source_archive,
 | |
| //                            ALEntryList &source_list )
 | |
| //
 | |
| // ARGUMENTS:
 | |
| //
 | |
| //  source_archive   : The archive where the objects that we are going
 | |
| //                     use can be found.
 | |
| //
 | |
| //  source_list      : The ALEntryList that has a batch of marked entries.
 | |
| //
 | |
| // RETURNS
 | |
| //
 | |
| //  AL_SUCCESS if things work, < AL_SUCCESS if they don't.
 | |
| //
 | |
| // DESCRIPTION
 | |
| //
 | |
| //  This append function works just like the previous one, except it
 | |
| //  is appending jobs that have already been compressed and can be found
 | |
| //  in a different archive.  It has to go through exactly the same process,
 | |
| //  which consists of reading the current directory in from this, appending
 | |
| //  the new compressed objects to this, then writing out the old directory
 | |
| //  and the new list.
 | |
| //
 | |
| // REVISION HISTORY
 | |
| //
 | |
| //   May 23, 1994  1.0A  : First release
 | |
| //
 | |
| 
 | |
| int AL_PROTO ALArchiveBase::Append( ALArchiveBase AL_DLL_FAR &source_archive,
 | |
|                                    ALEntryList AL_DLL_FAR &source_list )
 | |
| {
 | |
|   ALEntryList old_list;
 | |
|   //
 | |
|   // Open the storage object associated with this.
 | |
|   //
 | |
|   ALOpenInputFile archive( *mpArchiveStorageObject );
 | |
| 
 | |
|   source_list.mrMonitor.ArchiveOperation( AL_ARCHIVE_OPEN, this, 0 );
 | |
|   //
 | |
|   // I have to read the current directory into memory, because as soon as
 | |
|   // I start to write objects out to this, I am going to obliterate
 | |
|   // the directory.
 | |
|   //
 | |
|   ReadDirectory( old_list );
 | |
|   if ( mStatus < 0 ) {
 | |
|     source_list.mrMonitor.ArchiveOperation( AL_ARCHIVE_CLOSE, this, 0 );
 | |
|     return mStatus;
 | |
|   }
 | |
|   //
 | |
|   // We don't want to create an archive that has duplicate entries, that would
 | |
|   // be a bad thing.  So I first comb all the duplicate entries out of the list
 | |
|   // of objects to append.  I then compare that list for duplicates against
 | |
|   // the list of objects already in the library, and comb out any matches
 | |
|   // there as well.
 | |
|   //
 | |
|   source_list.UnmarkDuplicates( source_list, "Duplicate entry in list passed to Append()" );
 | |
|   source_list.UnmarkDuplicates( old_list, "Duplicate entry in list passed to Append()" );
 | |
|   //
 | |
|   // I am going to start writing new stuff at the location where the
 | |
|   // the directory starts right now.
 | |
|   //
 | |
|   mpArchiveStorageObject->Seek( mlDirectoryOffset );
 | |
|   //
 | |
|   // Before starting to copy jobs, I have to set up the monitor.  
 | |
|   // This includes calculating the total number of compressed bytes
 | |
|   // in all the marked jobs.  I could do this a lot easier by calling
 | |
|   // the CalculateCompressedBytes() function, but it's too late to change now.
 | |
|   //
 | |
|   source_list.mrMonitor.mlJobSoFar = 0L;
 | |
|   source_list.mrMonitor.mlJobSize = 0L;
 | |
|   for ( ALEntry *job = source_list.GetFirstEntry();
 | |
|        job != 0;
 | |
|        job = job->GetNextEntry() ) {
 | |
|     if ( job->GetMark() )
 | |
|       source_list.mrMonitor.mlJobSize += job->mlCompressedSize;
 | |
|   }
 | |
|   //
 | |
|   // CopyJobs() does the hard work for me.
 | |
|   //
 | |
|   CopyJobs( source_archive, source_list );
 | |
|   if ( mStatus < 0 ) {
 | |
|     source_list.mrMonitor.ArchiveOperation( AL_ARCHIVE_CLOSE, this, 0 );
 | |
|     return mStatus;
 | |
|   }
 | |
|   //
 | |
|   // The jobs are now in, I just have to update the directory with
 | |
|   // the old files and the new files.
 | |
|   //
 | |
|   mlDirectoryOffset = mpArchiveStorageObject->Tell();
 | |
|   mpArchiveStorageObject->Seek( 0L );
 | |
|   mpArchiveStorageObject->WritePortableLong( mlDirectoryOffset );
 | |
|   if ( mpArchiveStorageObject->mStatus < 0 ) {
 | |
|     source_list.mrMonitor.ArchiveOperation( AL_ARCHIVE_CLOSE, this, 0 );
 | |
|     return mStatus = mpArchiveStorageObject->mStatus;
 | |
|   }
 | |
|   source_list.mrMonitor.ArchiveOperation( AL_START_DIRECTORY_WRITE, this, 0 );
 | |
|   WriteDirectory( old_list );
 | |
|   AddDirectoryEntries( source_list );
 | |
|   //
 | |
|   // Wrap it up.
 | |
|   //
 | |
|   source_list.mrMonitor.ArchiveOperation( AL_END_DIRECTORY_WRITE, this, 0 );
 | |
|   source_list.mrMonitor.ArchiveOperation( AL_ARCHIVE_CLOSE, this, 0 );
 | |
|   ScanStatus( source_list );
 | |
|   return mStatus;
 | |
| }
 | |
| 
 | |
| //
 | |
| // int ALArchiveBase::ReadDirectory( ALEntryList &list )
 | |
| //
 | |
| // ARGUMENTS:
 | |
| //
 | |
| //  list : The target for the directory listing.
 | |
| //
 | |
| // RETURNS
 | |
| //
 | |
| //  AL_SUCCESS or < AL_SUCCESS if things don't work.
 | |
| //
 | |
| // DESCRIPTION
 | |
| //
 | |
| //  This function reads the directory from archive this and places the
 | |
| //  results in the list parameter.  I have to apologize for the fact
 | |
| //  that it is so long.  The only thing I can say in my defense is that
 | |
| //  even though it is really long, it is also really simple, not tricky
 | |
| //  bits here.
 | |
| //
 | |
| // REVISION HISTORY
 | |
| //
 | |
| //   May 23, 1994  1.0A  : First release
 | |
| //
 | |
| 
 | |
| int AL_PROTO ALArchiveBase::ReadDirectory( ALEntryList AL_DLL_FAR &list )
 | |
| {
 | |
|   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->ReadPortableLong( mlDirectoryOffset );
 | |
|   mpArchiveStorageObject->Seek( mlDirectoryOffset );
 | |
|   mpArchiveStorageObject->ReadPortableShort( 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.
 | |
|   //
 | |
|   if ( mszComment )
 | |
|     delete[] mszComment;
 | |
|   mszComment = mpArchiveStorageObject->ReadString();
 | |
|   //
 | |
|   // 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;
 | |
|     char *name = mpArchiveStorageObject->ReadString();
 | |
|     if ( name == 0 )
 | |
|       break;
 | |
|     //
 | |
|     // The directory ends with a blank name.
 | |
|     //
 | |
|     if ( strlen( name ) == 0 ) {
 | |
|       delete[] name;
 | |
|       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();
 | |
|     ALCompressionEngine *engine = CreateCompressionEngine( engine_type );
 | |
|     if ( engine )
 | |
|       engine->ReadEngineData( mpArchiveStorageObject );
 | |
|     else {
 | |
|       char *temp = mpArchiveStorageObject->ReadString();
 | |
|       if ( temp )
 | |
|         delete[] temp;
 | |
|       return mStatus.SetError( AL_CANT_CREATE_ENGINE,
 | |
|                               "Failure creating compression engine for object %s",
 | |
|                               name );
 | |
|     }
 | |
|     //
 | |
|     // 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 = CreateStorageObject( name, object_type );
 | |
|     delete[] name; // Don't need it any more
 | |
|     name = 0;
 | |
|     if ( storage_object )
 | |
|       storage_object->ReadStorageObjectData( mpArchiveStorageObject );
 | |
|     else {
 | |
|       char *temp = mpArchiveStorageObject->ReadString();
 | |
|       if ( temp )
 | |
|         delete[] temp;
 | |
|       return mStatus.SetError( AL_CANT_CREATE_STORAGE_OBJECT,
 | |
|                               "Failure creating storage object for object %s",
 | |
|                               name );
 | |
|     }
 | |
|     //
 | |
|     // The rest of the stuff in the entry is pretty straightforward.
 | |
|     //
 | |
|     mpArchiveStorageObject->ReadPortableLong( storage_object->mlSize );
 | |
|     ALEntry *job = new ALEntry( list, storage_object, engine );
 | |
|     mpArchiveStorageObject->ReadPortableLong( job->mlCompressedSize );
 | |
|     mpArchiveStorageObject->ReadPortableLong( job->mlCrc32 );
 | |
|     mpArchiveStorageObject->ReadPortableLong( job->mlCompressedObjectPosition );
 | |
|     char *comment = mpArchiveStorageObject->ReadString();
 | |
|     job->SetComment( comment );
 | |
|     if ( comment )
 | |
|       delete[] comment;
 | |
|     long unix_time;
 | |
|     mpArchiveStorageObject->ReadPortableLong( unix_time );
 | |
|     storage_object->mTimeDate.SetTimeDate( unix_time );
 | |
|     short int packed_attributes;
 | |
|     mpArchiveStorageObject->ReadPortableShort( packed_attributes );
 | |
|     storage_object->mAttributes.SetFromPackedAttributes( packed_attributes );
 | |
|   }
 | |
|   list.mrMonitor.ArchiveOperation( AL_END_DIRECTORY_READ, this, 0 );
 | |
|   return mStatus;
 | |
| }
 | |
| 
 | |
| //
 | |
| // int ALArchiveBase::WriteArchiveData()
 | |
| //
 | |
| // ARGUMENTS:
 | |
| //
 | |
| //  None.
 | |
| //
 | |
| // RETURNS
 | |
| //
 | |
| //  AL_SUCCESS if everything writes out okay, or < AL_SUCCESS for trouble.
 | |
| //
 | |
| // DESCRIPTION
 | |
| //
 | |
| //  Derived classes can write out customized archive data, for whatever
 | |
| //  reasons they deem necessary.  Our base class has nothing that it
 | |
| //  needs to save, so it just writes out a zero length string, which takes
 | |
| //  two bytes to save.  Instead of using WriteString like I ought to, for
 | |
| //  some reason I write the 0 out directly.
 | |
| //
 | |
| // REVISION HISTORY
 | |
| //
 | |
| //   May 23, 1994  1.0A  : First release
 | |
| //
 | |
| 
 | |
| int AL_PROTO ALArchiveBase::WriteArchiveData()
 | |
| {
 | |
|   return mpArchiveStorageObject->WritePortableShort( 0 );
 | |
| }
 | |
| 
 | |
| //
 | |
| // int ALArchiveBase::ReadArchiveData()
 | |
| //
 | |
| // ARGUMENTS:
 | |
| //
 | |
| //  None.
 | |
| //
 | |
| // RETURNS
 | |
| //
 | |
| //  AL_SUCCESS if things went well, < AL_SUCCESS it things go sour.
 | |
| //
 | |
| // DESCRIPTION
 | |
| //
 | |
| //  The base class doesn't store anything in the archive specific
 | |
| //  data area.  That means that when I am reading the archive specific
 | |
| //  data in, I should see a zero length string, which is the same thing
 | |
| //  as a single short of value 0.  I read it in and verify it here.
 | |
| //
 | |
| //  Note that derived classes are free to override this function, but
 | |
| //  nothing we ship with ArchiveLib does so.
 | |
| //
 | |
| // REVISION HISTORY
 | |
| //
 | |
| //   May 23, 1994  1.0A  : First release
 | |
| //
 | |
| 
 | |
| int AL_PROTO ALArchiveBase::ReadArchiveData()
 | |
| {
 | |
|   short temp;
 | |
|   mpArchiveStorageObject->ReadPortableShort( temp );
 | |
|   AL_ASSERT( temp == 0, "ReadArchiveData(): archive data != 0" );
 | |
|   return mpArchiveStorageObject->mStatus;
 | |
| }
 | |
| 
 | |
| //
 | |
| // int ALArchiveBase::Delete( ALEntryList &list,
 | |
| //                            ALArchiveBase &destination )
 | |
| //
 | |
| // ARGUMENTS:
 | |
| //
 | |
| //  list        : A list of the objects to delete from the archive.
 | |
| //
 | |
| //  destination : The destination archive, which is the result after
 | |
| //                deleting all the objects from this.
 | |
| //
 | |
| // RETURNS
 | |
| //
 | |
| //  AL_SUCCESS if things went well, < AL_SUCCESS otherwise.
 | |
| //
 | |
| // DESCRIPTION
 | |
| //
 | |
| //  Delete is really more like copy.  It doesn't actually delete objects
 | |
| //  out of an existing archive.  Instead it deletes by excluding the
 | |
| //  specified objects from a copy command, copying only those objects
 | |
| //  that aren't in the delete list.  The resulting archive looks as if
 | |
| //  it is one that has had objects deleted from it.
 | |
| //
 | |
| //  After deleting, we do some renaming to make it look like the delete
 | |
| //  operation did what you really expected.  As a result, the original
 | |
| //  archive (this) has been renamed to a backup, and the new archive
 | |
| //  now has the original name of this.
 | |
| //
 | |
| // REVISION HISTORY
 | |
| //
 | |
| //   May 23, 1994  1.0A  : First release
 | |
| //
 | |
| 
 | |
| int AL_PROTO ALArchiveBase::Delete( ALEntryList AL_DLL_FAR &list,
 | |
|                                    ALArchiveBase AL_DLL_FAR &destination )
 | |
| {
 | |
|   destination.SetComment( mszComment );
 | |
|   list.ToggleMarks();
 | |
|   destination.Create( *this, list );
 | |
|   list.ToggleMarks();
 | |
|   ALName temp = mpArchiveStorageObject->mName;
 | |
|   mpArchiveStorageObject->RenameToBackup();
 | |
|   destination.mpArchiveStorageObject->Rename( (const char*)temp );
 | |
|   if ( destination.mStatus < 0 )
 | |
|     return mStatus = destination.mStatus;
 | |
|   return mStatus;
 | |
| }
 | |
| 
 | |
| //
 | |
| // int ALArchiveBase::FillListBox( HWND hDlg, int list_box = -1 )
 | |
| //
 | |
| // ARGUMENTS:
 | |
| //
 | |
| //  hDlg       : The handle of the dialog box that has a list box in it.
 | |
| //               If the value of list_box is set to -1, it means that
 | |
| //               hDlg doesn't refer to a dialog box, instead it refers
 | |
| //               to the actual list box itself.
 | |
| //
 | |
| //  list_box   : This is set to the id of the list box control found in 
 | |
| //               the hDlg dialog box.  If this value is set to -1, it
 | |
| //               means the hDlg parameter is the handle of the list box.
 | |
| //
 | |
| // RETURNS
 | |
| //
 | |
| //  The count of marked items stuffed into the list box.
 | |
| //
 | |
| // DESCRIPTION
 | |
| //
 | |
| //  This is a quicky useful function to read the names of all the
 | |
| //  storage objects out of this, then stuffing them all into a list
 | |
| //  box.
 | |
| //
 | |
| // REVISION HISTORY
 | |
| //
 | |
| //   May 23, 1994  1.0A  : First release
 | |
| //
 | |
| 
 | |
| #if defined( AL_WINDOWS_GUI )
 | |
| int AL_PROTO ALArchiveBase::FillListBox( HWND hDlg, int list_box /* = -1 */ )
 | |
| {
 | |
|   ALEntryList list;
 | |
|   HWND window;
 | |
|   ReadDirectory( list );
 | |
|   if ( list_box != -1 )
 | |
|     window = GetDlgItem( hDlg, (short int) list_box );
 | |
|   else
 | |
|     window = hDlg;
 | |
|   int count;
 | |
|   if ( ( count = list.FillListBox( window ) ) == 0 ) {
 | |
|     if ( mStatus < 0 ) {
 | |
|       SendMessage( window, LB_RESETCONTENT, 0, 0 );
 | |
|       SendMessage( window,
 | |
|                   LB_ADDSTRING,
 | |
|                   0,
 | |
|                   (LPARAM)( (LPSTR) "Error!" ) );
 | |
|     }
 | |
|   }
 | |
|   return count;
 | |
| }
 | |
| #endif
 |