//
// GRENENGN.CPP
//
//  Source file for ArchiveLib 1.0
//
//  Copyright (c) Greenleaf Software, Inc. 1994
//  All Rights Reserved
//
// CONTENTS
//
//  ALGreenleafEngine::operator new()
//  ALGreenleafEngine::ALGreenleafEngine()
//  ALGreenleafEngine::~ALGreenleafEngine()
//  ALGreenleafEngine::Compress()
//  ALGreenleafEngine::Decompress()
//  ALGreenleafEngine::WriteEngineData()
//  ALGreenleafEngine::ReadEngineData()
//
// DESCRIPTION
//
//  This file contains the front end to the Greenleaf compression engine.
//  This contains everything but the actual low level compression
//  and expansion code, which can be found in _RE.CPP and _RC.CPP.  Those
//  two source files are shrouded though, so you won't get a tremendous
//  amount of detail!
//
// REVISION HISTORY
//
//  May 26, 1994  1.0A  : First release
//
//

#include "arclib.h"
#pragma hdrstop

#include "grenengn.h"
#include "_openf.h"
#include "_r.h"


//
// void * ALGreenleafEngine::operator new( size_t size )
//
// ARGUMENTS:
//
//  size  :  The number of bytes needed to create a new 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 26, 1994  1.0A  : First release
//

#if defined( AL_BUILDING_DLL )
void AL_DLL_FAR * AL_PROTO ALGreenleafEngine::operator new( size_t size )
{
    return ::new char[ size ];
}
#endif

//
// ALGreenleafEngine::
// ALGreenleafEngine( short int compression_level = AL_GREENLEAF_LEVEL_2,
//                    short int fail_uncompressible = 0 )
//
// ARGUMENTS:
//
//  compression_level   : This is one of the enumerated types found in ALDEFS.H,
//                        namely AL_GREENLEAF_LEVEL_0 through 
//                        AL_GREENLEAF_LEVEL_4.  Level 4 gives the most
//                        compression, but takes up the most memory as well.
//
//  fail_uncompressible : This flag is used to indicate the disposition
//                        of an uncompressible file.  If this flag is set,
//                        the compression of an incompressible file will
//                        be interrupted, and the file will be recompressed
//                        using a straight copy.  Note that this requires
//                        a Seek() operation!  Note also that this feature
//                        is NOT YET IMPLEMENTED!!!
//
// RETURNS
//
//  Nothing, a constructor.
//
// DESCRIPTION
//
//  The constructor for the Greenleaf engine has a pretty simple life.  All
//  it has to do is call the base class constructor, then define a couple of
//  data members.  This is a lightweight object until the compression
//  or expansion routines are invoked, at which time the memory requirements
//  go through the roof.
//
// REVISION HISTORY
//
//   May 26, 1994  1.0A  : First release
//

AL_PROTO ALGreenleafEngine::
ALGreenleafEngine( short int compression_level /* = AL_GREENLEAF_LEVEL_2 */,
                   short int fail_uncompressible /* = 0 */ )
    :  ALCompressionEngine( AL_COMPRESSION_GREENLEAF, "Greenleaf" )
{
    miCompressionLevel = compression_level;
    miFailUncompressible = fail_uncompressible;
}

//
// ALGreenleafEngine::~ALGreenleafEngine()
//
// ARGUMENTS:
//
//  None.
//
// RETURNS
//
//  Nothing.
//
// DESCRIPTION
//
//  The destructor for objects of this class doesn't have to do
//  anything.  In debug mode, we at least check for the validity
//  of the object.
//
// REVISION HISTORY
//
//   May 26, 1994  1.0A  : First release
//

AL_PROTO ALGreenleafEngine::~ALGreenleafEngine()
{
    AL_ASSERT( GoodTag(), "~ALGreenleafEngine: attempt to delete invalid object" );
}

//
// int ALGreenleafEngine::Compress( ALStorage &input,
//                                  ALStorage &output )
//
// ARGUMENTS:
//
//  input   :  A reference to the storage object that will be compressed.
//
//  output  :  A reference to the storage object that will receive the
//             compressed data.
//
// RETURNS
//
//  
//  AL_SUCCESS in the event of a success, an error code < AL_SUCCESS
//  if a failure occurred.
//
// DESCRIPTION
//
//  This is the virtual function that is called to compress data.  The
//  This section of code is really just a front end to the real engine,
//  which is found in _RC.CPP.  The first thing we do here
//  is create an RCompress object, which allocates all of the
//  storage we need to perform the compression.  In a tight memory
//  situation, that may well fail, so we check its status before moving
//  on.  If it succeeded, we can call the low level compression function
//  to do the real work.
//
//  After the compress function returns, we have to check for errors on
//  any of the other objects involved in the compression, and return the
//  cumulative result.
//
// REVISION HISTORY
//
//   May 26, 1994  1.0A  : First release
//

int AL_PROTO ALGreenleafEngine::Compress( ALStorage AL_DLL_FAR &input,
                                          ALStorage AL_DLL_FAR &output )
{
    ALOpenFiles files( input, output );

    input.InitCrc32();
    RCompress rc( input,
                  output,
                  miCompressionLevel + 10,
                  miFailUncompressible );

    if ( rc.mStatus < 0 )
        return mStatus = rc.mStatus;
    else
        rc.Compress();
    if ( rc.mStatus < 0 )
        return mStatus = rc.mStatus;
    else if ( input.mStatus < 0 )
        return mStatus = input.mStatus;
    else if ( output.mStatus < 0 )
        return mStatus = output.mStatus;
    return mStatus;
}

//
// int ALGreenleafEngine::Decompress( ALStorage &input,
//                                    ALStorage &output,
//                                    long compressed_length )
//
// ARGUMENTS:
//
//  input             :  A reference to the storage object that will be 
//                       expanded.
//
//  output            :  A reference to the storage object that will receive 
//                       the expanded data.
//
//  compressed_length : A long value indicating how long the compressed
//                      object is.  This helps to tell the decompressor
//                      when to quit.
// RETURNS
//
//  
//  AL_SUCCESS in the event of a success, an error code < AL_SUCCESS
//  if a failure occurred.
//
// DESCRIPTION
//
//  This is the virtual function that is called to expand a compressed
//  object. This section of code is really just a front end to the real 
//  engine, which is found in _RE.CPP.  The first thing we do here
//  is create an RExpand object, which allocates all of the
//  storage we need to perform the decompression.  In a tight memory
//  situation, that may well fail, so we check its status before moving
//  on.  If it succeeded, we can call the low level expansion function
//  to do the real work.
//
//  After the expand function returns, we have to check for errors on
//  any of the other objects involved in the expansion, and return the
//  cumulative result.
//
// REVISION HISTORY
//
//   May 26, 1994  1.0A  : First release
//

int AL_PROTO ALGreenleafEngine::Decompress( ALStorage AL_DLL_FAR &input,
                                            ALStorage AL_DLL_FAR &output,
                                            long compressed_length )
{
    ALOpenFiles files( input, output );

    output.InitCrc32();
    RExpand re( input, output, compressed_length, miCompressionLevel + 10 );

    if ( re.mStatus < 0 )
        return mStatus = re.mStatus;
    else
        re.Expand();
    if ( re.mStatus < 0 )
        return mStatus = re.mStatus;
    else if ( input.mStatus < 0 )
        return mStatus = input.mStatus;
    else if ( output.mStatus < 0 )
        return mStatus = output.mStatus;
    return mStatus;
}

//
// int ALGreenleafEngine::WriteEngineData( ALStorage * archive )
//
// ARGUMENTS:
//
//  A pointer to the storage area where the data is to be written.
//
// RETURNS
//
//  AL_SUCCESS if the data was written properly, else an error code
//  less than AL_SUCCESS.
//
// DESCRIPTION
//
//  Every compression engine used in ArchiveLib gets the opportunity
//  to store data it needs to save in order to characterize its compression
//  process.  The Greenleaf compression engine only needs to save a single
//  integer, which contains the compression level used.  This is the
//  function that does so.
//
//  Data like this is stored in string format, which consists of a single
//  short integer describing the number of bytes in the string, followed
//  by the string.  We store in this portable format so that even a program
//  that doesn't know about compression engines would be able to read in
//  archive directory data.
//
// REVISION HISTORY
//
//   May 26, 1994  1.0A  : First release
//

int AL_PROTO ALGreenleafEngine::
WriteEngineData( ALStorage AL_DLL_FAR * archive )
{
    archive->WritePortableShort( 2 );
    return archive->WritePortableShort( miCompressionLevel );
}

//
// int ALGreenleafEngine::ReadEngineData( ALStorage * archive )
//
// ARGUMENTS:
//
//  A pointer to the storage area where the data is to be read.
//
// RETURNS
//
//  AL_SUCCESS if the data was read properly, else an error code
//  less than AL_SUCCESS.
//
// DESCRIPTION
//
//  Every compression engine used in ArchiveLib gets the opportunity
//  to store data it needs to save in order to characterize its compression
//  process.  The Greenleaf compression engine only needs to save a single
//  integer, which contains the compression level used.  
//
//  During the creation of the compression engine, this function gets called
//  in order to load the engine's private data.  All we do is read in
//  the compression level, along with a little error checking.
//
//  Data like this is stored in string format, which consists of a single
//  short integer describing the number of bytes in the string, followed
//  by the string.  We store in this portable format so that even a program
//  that doesn't know about compression engines would be able to read in
//  archive directory data.
//
// REVISION HISTORY
//
//   May 26, 1994  1.0A  : First release
//

int AL_PROTO ALGreenleafEngine::ReadEngineData( ALStorage AL_DLL_FAR * archive )
{
    short temp;
    archive->ReadPortableShort( temp );
    AL_ASSERT( temp == 2, "ReadEngineData: engine data size is not 2, it should be" );
    return archive->ReadPortableShort( miCompressionLevel );
}