//
// STORAGE.CPP
//
//  Source file for ArchiveLib 2.0
//
//  Copyright (c) Greenleaf Software, Inc. 1994-1996
//  All Rights Reserved
//
// CONTENTS
//
//  ALStorage::operator new()
//  ALStorage::ALStorage()
//  ALStorage::~ALStorage()
//  deleteALStorage()
//  ALStorage::Open()
//  ALStorageOpen()
//  ALStorage::Create()
//  ALStorageCreate()
//  ALStorage::Close()
//  ALStorageClose()
//  ALStorage::ReadBuffer()
//  ALStorageReadBuffer()
//  ALStorageReadBufferVB32()
//  ALStorage::WriteBuffer()
//  ALStorageWriteBuffer()
//  ALStorageWriteBufferVB32()
//  ALStorage::WriteString()
//  ALStorageWriteString()
//  ALStorage::ReadString()
//  ALStorage::Tell()
//  ALStorageTell()
//  ALStorage::WriteStorageObjectData()
//  ALStorage::ReadStorageObjectData()
//  ALStorage::ReadCopyright()
//  ALStorageReadCopyright()
//  ALStorageReadCopyrightVB()
//
// DESCRIPTION
//
//  This file contains all of the source code for the member functions
//  of ALStorage.  AlStorage has pure virtual functions, so you can't
//  ever instantiate one of these guys.
//
// REVISION HISTORY
//
//  May 26, 1994  1.0A  : First release
//
//  July 7, 1994  1.0B  : Minor bug fixes
//
//   February 14, 1996  2.0A : New release.
//

#include "arclib.h"
#if !defined( AL_IBM )
#pragma hdrstop
#endif

#include "_vbutil.h"
#include <string.h>

//
// NAME
//
//  ALStorage::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"
//
//  void * ALStorage::operator new( size_t size )
//
// C SYNOPSIS
//
//  None, internal C++ member function.
//
// VB SYNOPSIS
//
//  None, internal C++ member function.
//
// DELPHI SYNOPSIS
//
//  None, internal C++ member function.
//
// ARGUMENTS
//
//  size  :  The number of bytes that the compiler has decided will be
//           necessary to construct a new ALStorage 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.
//
//  Note that this function will probably never be called.  You can't
//  instantiate an ALStorage object, because it has some pure virtual
//  functions.  So only derived classes get instantiated, and they
//  really ought to have their own new operators.

// RETURNS
//
//  A pointer to some memory that should have been pulled out of the
//  heap for the DLL.
//
// EXAMPLE
//
// SEE ALSO
//
// REVISION HISTORY
//
//   May 24, 1994  1.0A  : First release
//
//   February 14, 1996  2.0A : New release.
//

#if defined( AL_BUILDING_DLL )

void AL_DLL_FAR * AL_PROTO
ALStorage::operator new( size_t size )  /* Tag protected function */
{
    return ::new char[ size ];
}

#endif

//
// NAME
//
//  ALStorage::ALStorage()
//
// PLATFORMS/ENVIRONMENTS
//
//  Console  Windows  PM
//  C++
//
// SHORT DESCRIPTION
//
//  The ALStorage constructor.
//
// C++ SYNOPSIS
//
//  #include "arclib.h"
//
//  ALStorage::ALStorage( const char *file_name,
//                        size_t size,
//                        const enum ALStorageType object_type,
//                        ALCase name_case = AL_MIXED );
//
// C SYNOPSIS
//
//  None, this function is not available to C programmers.
//
// VB SYNOPSIS
//
//  None, this function is not available to VB programmers.
//
// DELPHI SYNOPSIS
//
//  None, this function is not available to Delphi programmers.
//
// ARGUMENTS
//
//  file_name     :  The name to assign to the mName data member of the
//                   newly created storage object.
//
//  size          :  The size of the I/O buffer that is going to be used
//                   for the storage object.  ALFile uses 4096 as a default.
//
//  object_type   :  The type of object, as defined in ALDEFS.H.  Good
//                   values include AL_FILE_OBJECT and AL_MEMORY_OBJECT.
//
//  name_case     :  The case sensitivity of the object name.  For objects
//                   such as ALFile, AL_MIXED is a no-no.  Those objects
//                   need to be forced to convert names to all upper
//                   or all lower, because the operating system considers
//                   file names to be case insensitive.
//
// DESCRIPTION
//
//  The constructor for ALStorage gets called from the constructor of
//  derived classes.  It has to initialize all sorts of data members.
//  First, in the initializer list, it sets up the mName data member,
//  as well as muBufferSize and miStorageObjectType.  The latter two
//  data members are set to be const so I can make them public, which
//  means we have to initialize them in the initializer list.
//
//  In the body of the constructor, we initialize a bunch of data members,
//  none of which mean anything at this point.
//
// RETURNS
//
//  Nothing, it is a constructor.
//
// EXAMPLE
//
//  N/A, not part of the public API.
//
// SEE ALSO
//
//  ALFile::ALFile(), ALMemory::ALMemory()
//
// REVISION HISTORY
//
//   February 14, 1996  2.0A : New release.
//

AL_PROTO
ALStorage::ALStorage( const char AL_DLL_FAR *file_name,  /* Tag public function */
                      size_t size,
                      const enum ALStorageType object_type,
                      ALCase name_case /* = AL_MIXED */ )
    : mName( file_name, name_case ),
      miStorageObjectType( object_type ),
      muBufferSize( size )
{
    mpcBuffer = 0;
    muBufferValidData = 0;
    muWriteIndex = 0;
    muReadIndex = 0;
    mlFilePointer = 0;
    miUpdateCrcFlag = 0;
    mlCrc32 = 0xffffffffL;
    mlSize = -1L;
    mpMonitor = 0;
    miCreated = 0;
    if ( mName.GetName() == 0 )
        mStatus.SetError( AL_CANT_OPEN_BUFFER,
                          "Allocation of buffer failed in "
                          "ALStorage constructor" );
}

//
// NAME
//
//  ALStorage::~ALStorage()
//
// PLATFORMS/ENVIRONMENTS
//
//  Console  Windows  PM
//  C++  C  VB  Delphi
//
// SHORT DESCRIPTION
//
//  The ALStorage destructor.
//
// C++ SYNOPSIS
//
//  #include "arclib.h"
//
//  ALStorage::~ALStorage();
//
// C SYNOPSIS
//
//  #include "arclib.h"
//
//  void deleteALStorage( hALStorage this_object );
//
// VB SYNOPSIS
//
//  Declare Sub deleteALStorage Lib "AL20LW" (ByVal this_object&)
//
// DELPHI SYNOPSIS
//
//  procedure deleteALStorage( this_object : hALStorage );
//
// ARGUMENTS
//
//  this_object  : A handle for (pointer to) the storage object that
//                 is going to be destroyed.  Note that the C++ version
//                 of this function doesn't have this argument, since it
//                 has implicit access to 'this'.
//
// DESCRIPTION
//
//  In debug mode, we first check to make sure we are destroying the
//  right type of object.
//
//  The only thing left to do is free up the I/O buffer if it is still
//  allocated.  This piece of work probably isn't necessary.  Since this
//  is a virtual destructor, we will be called after the destructors
//  for the derived class.  Any derived class that is doing its job
//  will make sure that it calls Close() before destroying itself.  If
//  it doesn't, it will probably be leaving unfinished business behind
//  that we aren't going to be able to deal with here.  Even so, we will
//  be diligent in our attention to detail.
//
//
// RETURNS
//
//  Nothing.
//
// EXAMPLE
//
// SEE ALSO
//
// REVISION HISTORY
//
//   February 14, 1996  2.0A : New release.
//

AL_PROTO
ALStorage::~ALStorage()  /* Tag public function */
{
    AL_ASSERT( GoodTag(), "~ALStorage: attempting to delete invalid object" );
    if ( mpcBuffer )
        Close();
}

#if !defined( AL_NO_C )

extern "C" AL_LINKAGE void AL_FUNCTION
deleteALStorage( hALStorage this_object )  /* Tag public function */
{
    AL_ASSERT_OBJECT( this_object, ALStorage, "deleteALStorage" );
    delete (ALStorage *) this_object;
}

#endif

//
// NAME
//
//  ALStorage::Open()
//
// PLATFORMS/ENVIRONMENTS
//
//  Console  Windows  PM
//  C++  C  VB  Delphi
//
// SHORT DESCRIPTION
//
//  Open a storage object.
//
// C++ SYNOPSIS
//
//  #include "arclib.h"
//
//  int ALStorage::Open()
//
// C SYNOPSIS
//
//  #include "arclib.h"
//
//  int ALStorageOpen( hALStorage this_object )
//
// VB SYNOPSIS
//
//  Declare Function ALStorageOpen Lib "AL20LW"
//    (ByVal this_object&) As Integer
//
// DELPHI SYNOPSIS
//
//  function ALStorageOpen( this_object : hALStorage ) : Integer;
//
// ARGUMENTS
//
//  this_object  :  A reference or pointer to the ALStorage object that
//                  is going to be opened.  Note that the C++
//                  version of this call doesn't have an explicit argument
//                  here, since it has access to 'this' implicitly.
//
// DESCRIPTION
//
//  Any derived class needs to have its own Open() function.  However,
//  the derived class can also call this Open() function in the base
//  class to do some odds and ends for it.  The most important thing it
//  does is allocate the I/O buffer, which is what makes ALStorage a
//  relatively fast way to read and write data.  Although the buffer
//  is in place, there is no data in it, so this guy also sets up the
//  indices and pointers to reflect that.
//
//  Upon exit, all you need to to is start reading or writing, and the
//  whole thing should be ready to go.
//
// RETURNS
//
//  AL_SUCCESS, or AL_CANT_OPEN_BUFFER on memory allocation failure.
//  If the object was already in an error state, it is very possible to
//  get some other error code < 0.
//
// EXAMPLE
//
// SEE ALSO
//
//  ALStorage::Create(), ALStorage::Close()
//
// REVISION HISTORY
//
//   February 14, 1996  2.0A : New release.
//

int AL_PROTO
ALStorage::Open()  /* Tag public function */
{
    if ( mStatus < AL_SUCCESS )
        return mStatus;
    if ( muBufferSize != 0 )
        mpcBuffer = new unsigned char[ muBufferSize ];
    muBufferValidData = 0;
    muWriteIndex = 0;
    muReadIndex = 0;
    mlFilePointer = 0;
    miUpdateCrcFlag = 0;
    mlCrc32 = 0xffffffffL;
    if ( mpcBuffer == 0 )
        return mStatus.SetError( AL_CANT_OPEN_BUFFER,
                                 "Allocation of buffer failed in Open()" );
    return AL_SUCCESS;
}

#if !defined( AL_NO_C )

extern "C" AL_LINKAGE int AL_FUNCTION
ALStorageOpen( hALStorage this_object )  /* Tag public function */
{
    AL_ASSERT_OBJECT( this_object, ALStorage, "ALStorageOpen" );
    return ( (ALStorage *) this_object )->Open();
}

#endif

//
// NAME
//
//  ALStorage::Create();
//
// PLATFORMS/ENVIRONMENTS
//
//  Console  Windows  PM
//  C++  C  VB  Delphi
//
// SHORT DESCRIPTION
//
//  Create a new storage object.
//
// C++ SYNOPSIS
//
//  #include "arclib.h"
//
//  int ALStorage::Create( long desired_size = - 1 );
//
// C SYNOPSIS
//
//  #include "arclib.h"
//
//  int ALStorageCreate( hALStorage this_object, long desired_size );
//
// VB SYNOPSIS
//
//  Declare Function ALStorageCreate Lib "AL20LW"
//    (ByVal this_object&, ByVal desired_size&) As Integer
//
// DELPHI SYNOPSIS
//
//  function ALStorageCreate( this_object : hALStorage;
//                            desired_size : Long ) : Integer;
//
// ARGUMENTS
//
//  this_object  :  A reference or pointer to the ALStorage object that
//                  is going to be created.  Note that the C++
//                  version of this call doesn't have an explicit argument
//                  here, since it has access to 'this' implicitly.
//
//  desired_size :  If you know in advance how much space a storage object
//                  is going to occupy, you can pass that number as the
//                  desired_size argument.  Some storage types, such as
//                  ALMemory, can make good use of this information.  Others
//                  such as ALFile, could care less.  In any case, the base
//                  class, ALStorage, ignores the info.  If you don't know
//                  how many bytes the storage object is going to need,
//                  pass a -1L as the argument.
//
// DESCRIPTION
//
//  This function is nearly identical to ALStorage::Open().
//
//  Any derived class needs to have its own Create() function.  However,
//  the derived class can also call this Create() function in the base
//  class to do some odds and ends for it.  The most important thing it
//  does is allocate the I/O buffer, which is what makes ALStorage a
//  relatively fast way to read and write data.  Although the buffer
//  is in place, there is no data in it, so this guy also sets up the
//  indices and pointers to reflect that.
//
// RETURNS
//
//  AL_SUCCESS, or AL_CANT_OPEN_BUFFER on memory allocation failure.
//  If the object was already in an error state, it is very possible to
//  get some other error code < 0.
//
// EXAMPLE
//
// SEE ALSO
//
//  ALStorage::Delete(), ALStorage::Open()
//
// REVISION HISTORY
//
//   February 14, 1996  2.0A : New release.
//

int AL_PROTO
ALStorage::Create( long /* desired_size = - 1 */ )  /* Tag public function */
{
    if ( mStatus < AL_SUCCESS )
        return mStatus;
    mpcBuffer = new unsigned char[ muBufferSize ];
    muBufferValidData = 0;
    muWriteIndex = 0;
    muReadIndex = 0;
    mlFilePointer = 0;
    miUpdateCrcFlag = 0;
    mlSize = 0; //If the file has been opened previously, mlSize might be non-zero
    mlCrc32 = 0xffffffffL;
    miCreated = 1;
    if ( mpcBuffer == 0 )
        return mStatus.SetError( AL_CANT_OPEN_BUFFER,
                                 "Allocation of buffer failed in Open()" );
    return AL_SUCCESS;
}

#if !defined( AL_NO_C )

extern "C" AL_LINKAGE int AL_FUNCTION
ALStorageCreate( hALStorage this_object, /* Tag public function */
                 long desired_size )
{
    AL_ASSERT_OBJECT( this_object, ALStorage, "ALStorageCreate" );
    if ( desired_size == AL_DEFAULT )
        desired_size = -1;
    return ( (ALStorage *) this_object )->Create( desired_size );
}

#endif

//
// NAME
//
//  ALStorage::Close()
//
// PLATFORMS/ENVIRONMENTS
//
//  Console  Windows  PM
//  C++  C  VB  Delphi
//
// SHORT DESCRIPTION
//
//  Close an ALStorage object.
//
// C++ SYNOPSIS
//
//  #include "arclib.h"
//
//  int ALStorage::Close();
//
// C SYNOPSIS
//
//  #include "arclib.h"
//
//  int ALStorageClose( hALStorage this_object );
//
// VB SYNOPSIS
//
//  Declare Function ALStorageClose Lib "AL20LW"
//    (ByVal this_object&) As Integer
//
// DELPHI SYNOPSIS
//
//  function ALStorageClose( this_object : hALStorage ) : Integer;
//
// ARGUMENTS
//
//  this_object  :  A reference or pointer to the ALStorage object that
//                  is going to be closed.  Note that the C++
//                  version of this call doesn't have an explicit argument
//                  here, since it has access to 'this' implicitly.
//
// DESCRIPTION
//
//  Just like with Open(), most derived classes will have their own
//  versions of Close().  They can call this version to delete the I/O
//  buffer if they feel like it is too hard to do themselves.
//
// RETURNS
//
//  The current integer status of the object.  Hopefully this will be
//  AL_SUCCESS, but it could well be a value < AL_SUCCESS.
//
// EXAMPLE
//
// SEE ALSO
//
//  ALFile::Create(), ALFile::Open()
//
// REVISION HISTORY
//
//   February 14, 1996  2.0A : New release.
//

int AL_PROTO
ALStorage::Close()  /* Tag public function */
{
    if ( mpcBuffer ) {
        delete[] mpcBuffer;
        mpcBuffer = 0;
    }
    return mStatus;
}

#if !defined( AL_NO_C )

extern "C" AL_LINKAGE int AL_FUNCTION
ALStorageClose( hALStorage this_object )  /* Tag public function */
{
    AL_ASSERT_OBJECT( this_object, ALStorage, "ALStorageClose" );
    return ( (ALStorage *) this_object )->Close();
}

#endif

//
// NAME
//
//  ALStorage::ReadBuffer()
//
// PLATFORMS/ENVIRONMENTS
//
//  Console  Windows  PM
//  C++  C  VB  Delphi
//
// SHORT DESCRIPTION
//
//  Read in a buffer full of data.
//
// C++ SYNOPSIS
//
//  #include "arclib.h"
//
//  size_t ALStorage::ReadBuffer( unsigned char *buf, size_t length );
//
// C SYNOPSIS
//
//  #include "arclib.h"
//
//  size_t ALStorageReadBuffer( hALStorage this_object,
//                              unsigned char *buffer,
//                              size_t length );
//
// VB SYNOPSIS
//
//  Declare Function ALStorageReadBuffer Lib "AL20LW"
//    (ByVal this_object&, ByVal buffer$, ByVal length%) As Integer
//
// DELPHI SYNOPSIS
//
//  function ALStorageReadBuffer( this_object : hALStorage;
//                                buffer : PChar;
//                                length : Integer ) : Integer;
//
// ARGUMENTS
//
//  this_object  :  A reference or pointer to the ALStorage object that
//                  is going to be read.  Note that the C++
//                  version of this call doesn't have an explicit argument
//                  here, since it has access to 'this' implicitly.
//
//  buf    :  The buffer that is going to receive input characters.
//
//  length :  The number of bytes you want to read.
//
// DESCRIPTION
//
//  We could write a simple version of this function by just calling
//  ReadChar() over and over, but it would be nice to do things
//  a little more efficiently.  Since we have this nice big buffer
//  full of data ready to read, it makes sense to copy big chunks of
//  it in one fell swoop.  That is what this guy does.  It sits in a loop
//  doing a memcpy() followed by LoadBuffer() until all of the data
//  that has been asked for got moved.  As data is read in, we have to
//  update the data member muReadIndex.  Other data members will get
//  updated by LoadBuffer().
//
// RETURNS
//
//  The number of bytes read in, always.  If this function generates an
//  error, it will be found in the mStatus member.
//
// EXAMPLE
//
// SEE ALSO
//
//  ALStorage::ReadChar(), ALStorage::WriteBuffer()
//
// REVISION HISTORY
//
//   February 14, 1996  2.0A : New release.
//

size_t AL_PROTO
ALStorage::ReadBuffer( unsigned char AL_DLL_FAR *buf,  /* Tag public function */
                       size_t length )
{
    size_t bytes_left_to_read = length;
    size_t buffer_bytes_available;

    while ( bytes_left_to_read ) {
        buffer_bytes_available = muBufferValidData - muReadIndex;
        if ( buffer_bytes_available == 0 ) {
            if ( LoadBuffer( mlFilePointer ) < 0 )
                return length - bytes_left_to_read;
            buffer_bytes_available = muBufferValidData;
        }
        if ( bytes_left_to_read <= buffer_bytes_available ) {
            memcpy( buf, mpcBuffer + muReadIndex, bytes_left_to_read );
            muReadIndex += bytes_left_to_read;
            return length;
        } else {
            memcpy( buf, mpcBuffer + muReadIndex, buffer_bytes_available );
            buf += buffer_bytes_available;
            bytes_left_to_read -= buffer_bytes_available;
            muReadIndex += buffer_bytes_available;
            if ( LoadBuffer( mlFilePointer ) < 0 )
                return length - bytes_left_to_read;
        }
    }
    return length;
}

#if !defined( AL_NO_C )

extern "C" AL_LINKAGE size_t AL_FUNCTION
ALStorageReadBuffer( hALStorage this_object,  /* Tag public function */
                     unsigned char AL_DLL_FAR *buffer,
                     size_t length )
{
    AL_ASSERT_OBJECT( this_object, ALStorage, "ALStorageReadBuffer" );
    AL_ASSERT( buffer != 0, "Passed a null buffer to ALStorageReadBuffer" );
    return ( (ALStorage * ) this_object )->ReadBuffer( buffer, length );
}

#endif

#if defined( AL_VB32 )
extern "C" AL_LINKAGE size_t AL_FUNCTION
ALStorageReadBufferVB32( hALStorage this_object,  /* Tag public function */
                         LPSAFEARRAY *ppsa,
                         size_t length )
{
    unsigned char *p;
    size_t result;

    AL_ASSERT_OBJECT( this_object, ALStorage, "ALStorageReadBufferVB32" );
    AL_ASSERT( SafeArrayGetDim( *ppsa ) > 0, "Array with < 1 dimensions in ALStorageWriteBufferVB32" );
//
// This doesn't work, don't know why
//
//    int j = SafeArrayGetElemsize( *ppsa );
//    wsprintf( buffer, "Array dim = %d  j = %d  length = %d", i, j, length );
//    char buffer[ 81 ];
//    MessageBox( 0, buffer, buffer, MB_OK );
//    AL_ASSERT( j >= (int) length, "Buffer too small in ALStorageReadBufferVB32" );
//    int j =
    SafeArrayAccessData( *ppsa, (void **) &p );
    result = ( (ALStorage * ) this_object )->ReadBuffer( p, length );
    SafeArrayUnaccessData( *ppsa );
    return result;
}

#endif


//
// NAME
//
//  ALStorage::WriteBuffer()
//
// PLATFORMS/ENVIRONMENTS
//
//  Console  Windows  PM
//  C++  C  VB  Delphi
//
// SHORT DESCRIPTION
//
//  Write a buffer to a storage object.
//
// C++ SYNOPSIS
//
//  #include "arclib.h"
//
//  size_t ALStorage::WriteBuffer( const unsigned char *buf,
//                                 size_t length );
//
// C SYNOPSIS
//
//  #include "arclib.h"
//
//  size_t ALStorageWriteBuffer( hALStorage this_object,
//                               unsigned char *buffer,
//                               size_t length );
//
// VB SYNOPSIS
//
//  Declare Function ALStorageWriteBuffer Lib "AL20LW"
//    (ByVal this_object&, ByVal buffer$, ByVal length%) As Integer
//
// DELPHI SYNOPSIS
//
//  function ALStorageWriteBuffer( this_object : hALStorage;
//                                 buffer : PChar;
//                                 length : Integer ) : Integer;
//
// ARGUMENTS
//
//  this_object  :  A reference or pointer to the ALStorage object that
//                  is going to be written to.  Note that the C++
//                  version of this call doesn't have an explicit argument
//                  here, since it has access to 'this' implicitly.
//
//  buf    :  The buffer that is contains the output data.
//
//  length :  The number of bytes you want to write.
//
// DESCRIPTION
//
//  We could write a simple version of this function by just calling
//  WriteChar() over and over, but it would be nice to do things
//  a little more efficiently.  Since we have this nice big buffer
//  just waiting for data, it makes sense to copy big chunks to
//  it in one fell swoop.  That is what this guy does.  It sits in a loop
//  doing a memcpy() followed by FlushBuffer() until all of the data
//  that was ready to go has been sent. As data is written, we have to
//  update the data member muWriteIndex.  Other data members will get
//  updated by FlushBuffer().
//
// RETURNS
//
//  The number of bytes written, always.  If this function generates an
//  error, it will be found in the mStatus member.
//
// EXAMPLE
//
// SEE ALSO
//
//  ALStorage::WriteChar(), ALStorage::ReadBuffer()
//
// REVISION HISTORY
//
//   February 14, 1996  2.0A : New release.
//

size_t AL_PROTO
ALStorage::WriteBuffer( const unsigned char AL_DLL_FAR *buf,  /* Tag public function */
                        size_t length )
{
    size_t buffer_bytes_free;
    size_t write_bytes_left = length;

    if ( mStatus < 0 )
        return 0;
    while ( write_bytes_left > 0 ) {
        buffer_bytes_free = muBufferSize - muWriteIndex;
        if ( buffer_bytes_free == 0 ) {
            if ( FlushBuffer() < 0 )
                return length - write_bytes_left;
            buffer_bytes_free = muBufferSize;
        }
        if ( write_bytes_left <= buffer_bytes_free ) {
            memcpy( mpcBuffer + muWriteIndex, buf, write_bytes_left );
            muWriteIndex += write_bytes_left;
            return length;
        } else {
            memcpy( mpcBuffer + muWriteIndex, buf, buffer_bytes_free );
            muWriteIndex += buffer_bytes_free;
            buf += buffer_bytes_free;
            write_bytes_left -= buffer_bytes_free;
            if ( FlushBuffer() < 0 )
                return length - write_bytes_left;
        }
    }
    return length;
}

#if !defined( AL_NO_C )

extern "C" AL_LINKAGE size_t AL_FUNCTION
ALStorageWriteBuffer( hALStorage this_object,  /* Tag public function */
                      unsigned char AL_DLL_FAR *buffer,
                      size_t length )

{
    AL_ASSERT_OBJECT( this_object, ALStorage, "ALStorageWriteBuffer" );
    AL_ASSERT( buffer != 0, "Passed a null buffer to ALStorageWriteBuffer" );
    return ( (ALStorage * ) this_object )->WriteBuffer( buffer, length );
}

#endif

#if defined( AL_VB32 )

extern "C" AL_LINKAGE size_t AL_FUNCTION
ALStorageWriteBufferVB32( hALStorage this_object,  /* Tag public function */
                          LPSAFEARRAY *ppsa,
                          size_t length )

{
    unsigned char *p;
    size_t result;

    AL_ASSERT_OBJECT( this_object, ALStorage, "ALStorageWriteBufferVB32" );
    AL_ASSERT( SafeArrayGetDim( *ppsa ) > 0, "Array with < 1 dimensions in ALStorageWriteBufferVB32" );
//    i = SafeArrayGetElemsize( *ppsa );
//    AL_ASSERT( i >= (int)length, "Buffer too small in ALStorageWriteBufferVB32" );
//    int j =
    SafeArrayAccessData( *ppsa, (void **) &p );
    result = ( (ALStorage * ) this_object )->WriteBuffer( p, length );
    SafeArrayUnaccessData( *ppsa );
    return result;
}

#endif

//
// NAME
//
//  ALStorage::WriteString()
//
// PLATFORMS/ENVIRONMENTS
//
//  Console  Windows  PM
//  C++  C  VB  Delphi
//
// SHORT DESCRIPTION
//
//  Write a null terminated string to a storage object.
//
// C++ SYNOPSIS
//
//  #include "arclib.h"
//
//  int ALStorage::WriteString( const char AL_DLL_FAR *string_data );
//
// C SYNOPSIS
//
//  #include "arclib.h"
//
//  int ALStorageWriteString( hALStorage this_object,
//                            char *string );
//
// VB SYNOPSIS
//
//  Declare Function ALStorageWriteString Lib "AL20LW"
//    (ByVal this_object&, ByVal string_data$) As Integer
//
// DELPHI SYNOPSIS
//
//  function ALStorageWriteString( this_object : hALStorage;
//                                 string_data : PChar ) : Integer;
//
// ARGUMENTS
//
//  this_object  :  A reference or pointer to the ALStorage object that
//                  is going to have a string written to it.  Note that the C++
//                  version of this call doesn't have an explicit argument
//                  here, since it has access to 'this' implicitly.
//
//  string_data  :  A C-style null terminated string to be written out in
//                  our portable format.
//
// DESCRIPTION
//
//  We write random length data to archive directories using this special
//  format, which is a 16 bit int describing the length of the data,
//  followed by the data itself.  All of the storage objects and compression
//  engines write their own private data out using this format.  This
//  means that even if another class doesn't understand the content of data
//  stored in this format, at least it knows how to read it in so as to
//  move past it.
//
//  This function won't write just any random data, it is specifically
//  oriented towards C strings.  This means it is mostly used to write
//  file names and comments.  Their are a few places where classes
//  write private data that isn't kept in C strings, they just manually
//  write the length with WriteGlShort(), followed by the data.
//
// RETURNS
//
//  AL_SUCCESS if things work, or an error code < AL_SUCCESS if an error
//  occurs writing the data out.
//
// EXAMPLE
//
// SEE ALSO
//
//  ALStorage::ReadString()
//
// REVISION HISTORY
//
//   February 14, 1996  2.0A : New release.
//

int AL_PROTO
ALStorage::WriteString( const char AL_DLL_FAR *string_data )  /* Tag public function */
{
    short unsigned int len;
    if ( string_data != 0 )
        len = (short unsigned int) strlen( string_data );
    else
        len = 0;
    WriteGlShort( len );
    if ( len )
        WriteBuffer( (unsigned char *) string_data, len );
    return mStatus;
}

#if !defined( AL_NO_C )

extern "C" AL_LINKAGE int AL_FUNCTION
ALStorageWriteString( hALStorage this_object,  /* Tag public function */
                      char AL_DLL_FAR *string )
{
    AL_ASSERT_OBJECT( this_object, ALStorage, "ALStorageWriteString" );
    AL_ASSERT( string != 0, "Passing null string in ALStorageWriteString()" );
    return ( (ALStorage *) this_object )->WriteString( string );
}


#endif

//
// NAME
//
//  ALStorage::ReadString()
//
// PLATFORMS/ENVIRONMENTS
//
//  Console  Windows  PM
//  C++
//
// SHORT DESCRIPTION
//
//  Read a string from a storage object.
//
// C++ SYNOPSIS
//
//  #include "arclib.h"
//
//  int ALStorage::ReadString( ALName &name,
//                             short int length = -1 );
//
// C SYNOPSIS
//
//  None, we have problems with this in C/VB/Delphi.
//
// VB SYNOPSIS
//
//  None, we have problems with this in C/VB/Delphi.
//
// DELPHI SYNOPSIS
//
//  None, we have problems with this in C/VB/Delphi.
//
// ARGUMENTS
//
//  name  :  A reference to the storage object that is going to be loaded
//           up with a string.  Note that using an ALName object is a good
//           thing here, because we are going to have to dynamically allocate
//           data based on how long the input string is.  If we returned a
//           char *, we would have problems, because the pointer would be
//           allocated inside the DLL, but the user would have to free it
//           outside the DLL.  With an ALName, the data will be freed inside
//           the DLL when the ALName object is destroyed.
//
//           We can't pass around this ALName object to C/VB/Delphi, so they
//           don't get access to this function.  Sorry.
//
//  length : Normally, you read the length in from the storage object,
//           and then read in the data.  But sometimes, you know the
//           length in advance, and don't want to read it from the
//           storage object.  In that case, pass the length in this arg.
//           O/w, pass -1.
//
// DESCRIPTION
//
//  This function is used internally by ArchiveLib.  It is used to read
//  random length blocks of data out of archives (or other storage objects).
//
// RETURNS
//
//  AL_SUCCESS, or not.
//
// EXAMPLE
//
// SEE ALSO
//
//  ALStorage::WriteString()
//
// REVISION HISTORY
//
//   February 14, 1996  2.0A : New release.
//

int AL_PROTO
ALStorage::ReadString( ALName AL_DLL_FAR &name,  /* Tag public function */
                       short int length /* = -1 */ )
{
    if ( length == -1 ) {
        if ( ReadGlShort( length ) < 0 )
        return mStatus.SetError( AL_READ_ERROR,
                                 "Error reading string length"
                                 "in ReadString() for object %s",
                                  mName.GetSafeName() );
    }
    char *new_string = new char[ length + 1 ];
    if ( new_string ) {
        ReadBuffer( (unsigned char *) new_string, length );
        new_string[ length ] = '\0';
        name = new_string;
        delete[] new_string;
        return AL_SUCCESS;
    }
    return mStatus.SetError( AL_CANT_ALLOCATE_MEMORY,
                             "Error allocating buffer space in call "
                             "to ReadString() for object %s",
                              mName.GetSafeName() );
}

//
// NAME
//
//  ALStorage::Tell()
//
// PLATFORMS/ENVIRONMENTS
//
//  Console  Windows  PM
//  C++  C  VB  Delphi
//
// SHORT DESCRIPTION
//
//  Return the current offset in the storage object.
//
// C++ SYNOPSIS
//
//  #include "arclib.h"
//
//  long ALStorage::Tell();
//
// C SYNOPSIS
//
//  #include "arclib.h"
//
//  long ALStorageTell( hALStorage this_object );
//
// VB SYNOPSIS
//
//  Declare Function ALStorageTell Lib "AL20LW"
//    (ByVal this_object&) As Long
//
// DELPHI SYNOPSIS
//
//  function ALStorageTell( this_object : hALStorage ) : LongInt;
//
// ARGUMENTS
//
//  this_object  :  A reference or pointer to the ALStorage object that
//                  is going to have its offset returned.  Note that the C++
//                  version of this call doesn't have an explicit argument
//                  here, since it has access to 'this' implicitly.
//
// DESCRIPTION
//
//  Because we are using buffered I/O here, figuring out the current
//  position of the read write pointer is just a tiny bit more complicated
//  than just checking a pointer.  We have to find the physical location of
//  the file pointer, then add in any offset created by the presence of
//  data in the I/O buffer.
//
// RETURNS
//
//  A long integer indicating the current position of the read/write
//  pointer for the file.
//
// EXAMPLE
//
// SEE ALSO
//
//  ALStorage::Seek(), ALStorage::WriteBuffer()
//
// REVISION HISTORY
//
//   February 14, 1996  2.0A : New release.
//

long AL_PROTO
ALStorage::Tell()  /* Tag public function */
{
    if ( muWriteIndex )
        return mlFilePointer + muWriteIndex;
    else
        return mlFilePointer - muBufferValidData + muReadIndex;

}

#if !defined( AL_NO_C )

extern "C" AL_LINKAGE long AL_FUNCTION
ALStorageTell( hALStorage this_object )  /* Tag public function */
{
    AL_ASSERT_OBJECT( this_object, ALStorage, "ALStorageTell" );
    return ( (ALStorage *) this_object )->Tell();
}

#endif

//
// NAME
//
//  ALStorage::WriteStorageObjectData()
//
// PLATFORMS/ENVIRONMENTS
//
//  Console  Windows  PM
//  C++
//
// SHORT DESCRIPTION
//
//  Write out class-specific storage object data.
//
// C++ SYNOPSIS
//
//  #include "arclib.h"
//
//  int ALStorage::WriteStorageObjectData( ALStorage * archive );
//
// C SYNOPSIS
//
//  None, this is an internal callback virtual function.
//
// VB SYNOPSIS
//
//  None, this is an internal callback virtual function.
//
// DELPHI SYNOPSIS
//
//  None, this is an internal callback virtual function.
//
// ARGUMENTS
//
//  archive : A pointer to the storage object where we are going to
//            write the private data.
//
// DESCRIPTION
//
//  All storage objects have the ability to create a private data block
//  that will be stored along with the directory when creating an archive.
//  None of the classes predefined in ArchiveLib use this data block, which
//  means they use this function instead of providing their own virtual
//  substitute. This function writes a private data block of exactly 0
//  bytes in length.  Our internal storage format means that a block
//  of 0 bytes length takes 2 bytes to store.
//
// RETURNS
//
//  AL_SUCCESS if things went okay, otherwise an error code < AL_SUCCESS.
//
// EXAMPLE
//
// SEE ALSO
//
// REVISION HISTORY
//
//   February 14, 1996  2.0A : New release.
//

int AL_PROTO
ALStorage::WriteStorageObjectData( ALStorage AL_DLL_FAR * archive )  /* Tag public function */
{
    return archive->WriteGlShort( 0 );
}

//
// NAME
//
//  ALStorage::ReadStorageObjectData()
//
// PLATFORMS/ENVIRONMENTS
//
//  Console  Windows  PM
//  C++
//
// SHORT DESCRIPTION
//
//  Read in class specific data from an archive.
//
// C++ SYNOPSIS
//
//  #include "arclib.h"
//
//  int ALStorage::ReadStorageObjectData( ALStorage * archive );
//
// C SYNOPSIS
//
//  None, this is an internal callback virtual function.
//
// VB SYNOPSIS
//
//  None, this is an internal callback virtual function.
//
// DELPHI SYNOPSIS
//
//  None, this is an internal callback virtual function.
//
// ARGUMENTS
//
//  archive : A pointer to the storage object where we are going to
//            read in the private data..
//
// DESCRIPTION
//
//  All storage objects have the ability to create a private data block
//  that will be stored along with the directory when creating an archive.
//  None of the classes predefined in ArchiveLib use this data block, which
//  means they use this function instead of providing their own virtual
//  substitute. This function reads a private data block of exactly 0
//  bytes in length.  Our internal storage format means that a block
//  of 0 bytes length takes 2 bytes to store.
//
//  In debug mode, we get really bent out of shape if this data block
//  doesn't look exactly like we expect it to.
//
// RETURNS
//
//  AL_SUCCESS if things went okay, otherwise an error code < AL_SUCCESS.
//
// EXAMPLE
//
// SEE ALSO
//
//  ALStorage::WriteStorageObjectData()
//
// REVISION HISTORY
//
//   February 14, 1996  2.0A : New release.
//

int AL_PROTO
ALStorage::ReadStorageObjectData( ALStorage AL_DLL_FAR * archive )  /* Tag public function */
{
    short int temp;
    int status = archive->ReadGlShort( temp );
    AL_ASSERT( temp == 0, "ReadStorageObjectData: stored data is not null" );
    return status;
}

//
// NAME
//
//  ALStorage::ReadCopyright()
//
// PLATFORMS/ENVIRONMENTS
//
//  Console  Windows  PM
//  C++  C  VB  Delphi
//
// SHORT DESCRIPTION
//
//  Read a copyright string for human display.
//
// C++ SYNOPSIS
//
//  #include "arclib.h"
//
//  char * ALStorage::ReadCopyright();
//
// C SYNOPSIS
//
//  #include "arclib.h"
//
//  char * ALReadCopyright( void );
//
// VB SYNOPSIS
//
//  Declare Function ALReadCopyright Lib "AL20FW"
//    Alias "ALReadCopyrightVB"
//    As String
//
// DELPHI SYNOPSIS
//
//  function ALReadCopyright : PChar;
//
// ARGUMENTS
//
//  None.  Note that we don't have the usual this_object argument, because
//  this is a static function.  It's only in the ALStorage class to avoid
//  name space clutter.
//
// DESCRIPTION
//
//  It is a good idea for us to have a copyright notice embedded in the
//  library.  Hopefully, this notice will show up in any executables linked
//  using this library, or in the DLL they link to.
//
// RETURNS
//
//  A pointer to a copyright notice.
//
// EXAMPLE
//
// SEE ALSO
//
// REVISION HISTORY
//
//   February 14, 1996  2.0A : New release.
//

char *_al_copyright =  "Copyright (c) 1994-1996 Greenleaf Software, Inc.\n"
                       "All Rights Reserved.\n";

char AL_DLL_FAR * AL_PROTO
ALStorage::ReadCopyright()  /* Tag public function */
{
    return _al_copyright;
}

#if !defined( AL_NO_C )

extern "C" AL_LINKAGE char AL_DLL_FAR * AL_FUNCTION
ALReadCopyright( void ) /* Tag public function */
{
    return _al_copyright;
}

#if defined( AL_VB )

extern "C" AL_LINKAGE long AL_FUNCTION
ALReadCopyrightVB( void )  /* Tag public function */
{
    return ALCreateVBString( _al_copyright,
                            (unsigned short int) _fstrlen( _al_copyright ) );
}

#elif defined( AL_VB32 )

extern "C" AL_LINKAGE BSTR AL_FUNCTION
ALReadCopyrightVB( void )  /* Tag public function */
{
    return SysAllocStringByteLen( _al_copyright, strlen( _al_copyright ) );
}

#endif /* #if defined( AL_VB ) */

#endif /* #if !defined( AL_NO_C ) */