campo-sirio/arch/cmpobj.cpp

362 lines
11 KiB
C++
Executable File

//
// 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;
}