//
// Asura_SaveBlock.h
//
// Created by Kevin Lea - 12/06/2001 11:22:15
// 
// Copyright (C) 2001 Rebellion, All Rights Reserved.
//

#ifdef ASURA_USE_PRAGMA_ONCE
    #pragma once
#endif

#ifndef __ASURA_SAVEBLOCK_H__
#define __ASURA_SAVEBLOCK_H__

// I N C L U D E S ///////////////////////////////////////////////////////////

#if defined(ASURA_TOOLS) && defined(ASURA_MAIN)
#include <windows.h>
#endif

#ifdef ASURA_SAVEBLOCK_USE_ZLIB
#include "zlib/zlib.h"
#endif

// D E F I N E S /////////////////////////////////////////////////////////////
// turn ASURA_SAVEBLOCK_ALLOWACCESSPOINTER off to help convert code to using 
// Asura_SaveBlock_Base and thus allow use of Asura_CompressedSaveBlock.
#define ASURA_SAVEBLOCK_ALLOWACCESSPOINTER 1	

#ifdef ASURA_SAVEBLOCK_USE_ZLIB
#if !defined ASURA_SAVEBLOCK_ZLIB_DEFAULT_WINDOW_BITS
#define ASURA_SAVEBLOCK_ZLIB_DEFAULT_WINDOW_BITS (13)
#endif
#endif


// C L A S S E S /////////////////////////////////////////////////////////////
class Asura_SaveBlock_Base
{
public:
	
	// C O N S T R U C T O R S   /   D E S T R U C T O R S ///////////////////
	virtual ~Asura_SaveBlock_Base( void ) { }

	// M E T H O D S /////////////////////////////////////////////////////////
	virtual bool AddData( const void* pSrc, size_t uSize ) = 0;								// returns whether all data was added.
	virtual bool AddPatchData( const void* pData, u_int uNumberOfBytes, u_int uPos ) = 0;	// writes 4 bytes at the specified position.
	virtual size_t ReadData( void* pDest, size_t uSize ) const = 0;							// returns number of bytes that were read.
	virtual size_t SkipData( size_t uSize ) const = 0;										// returns number of bytes that were skipped.
	virtual size_t GetBytesRead() const = 0;
	virtual size_t GetBytesWritten() const = 0;

	virtual void RestartRead() const = 0;							// resets read pointer to the beginning.

	virtual void Clear( bool bWriteFileID = true ) = 0;

	// added GetSpaceUsed() to help with transition from ProtoSaveBlock
	virtual u_int GetSpaceUsed( void ) const;

	// adds an empty chunk (4 ints) which acts as file terminator
	virtual bool AddAsuraTerminator( void );
};

// Asura_ProtoSaveBlock is the non-compressible non-streamable type which uses
// start and current pointers to write the data out to a big fixed block. It
// has been kept around to support legacy code. (MH - 20.05.2008)
class Asura_ProtoSaveBlock : public Asura_SaveBlock_Base
{

public:

    // C O N S T R U C T O R S   /   D E S T R U C T O R S ///////////////////
    Asura_ProtoSaveBlock();
    virtual ~Asura_ProtoSaveBlock( void ) { }

    // M E T H O D S /////////////////////////////////////////////////////////
	virtual bool AddData( const void* pSrc, size_t uSize );								// returns whether all data was added.
	virtual bool AddPatchData( const void* pData, u_int uNumberOfBytes, u_int uPos );	// writes 4 bytes at the specified position.
	virtual size_t ReadData( void* pDest, size_t uSize ) const;							// returns number of bytes that were read.
	virtual size_t SkipData( size_t uSize ) const;										// returns number of bytes that were skipped.
	virtual size_t GetBytesRead() const;												// read counter
	virtual size_t GetBytesWritten() const;												// write counter
	virtual void RestartRead() const;													// resets the read pointer to the beginning.

#ifdef ASURA_SAVEBLOCK_ALLOWACCESSPOINTER
	virtual void* GetAccessPointer( int iNumBytesRequired ) = 0;
#endif

	const void* GetCurrentPosition( void ) const { return m_pcCurrentPos; }

    u_int GetSize( void ) const { return m_uSpaceUsed; }

    const char* GetStartPtr( void ) const { return m_pcStart; }

#if defined(ASURA_TOOLS) || !defined(ASURA_MAIN)
	// (MH - 16.06.2008) this SaveFile function doesn't work on PS3 and possibly other platforms.
	// It uses fopen etc. which require e.g. host0: to be inserted into the filename.
	// It's used some small utility apps, so I'm also allowing it for non-ASURA_MAIN configs.
	bool SaveFile( const char* szFilename, bool bUseCompression ) const;
#endif

#if defined(ASURA_TOOLS) && defined(ASURA_MAIN)
	// Writes the buffer to file given by handle hFile, without the Asura file ID.
	// Returns number of bytes written if successful or -1 if failed.
	int WriteToFile( HANDLE hFile );
#endif
    
	virtual void Clear( bool bWriteFileID = true );

	void Truncate(u_int uLength);

	u_int GetSpaceUsed( void ) const { return m_uSpaceUsed; }
	u_int GetSpaceLeft() const { return m_uSpaceLeft; }

protected:

    // S T A T E /////////////////////////////////////////////////////////////

    char* m_pcStart;
    char* m_pcCurrentPos;
	mutable char* m_pcCurrentReadPos;
    u_int m_uSize;
    u_int m_uSpaceLeft;
    u_int m_uSpaceUsed;
	mutable size_t m_uBytesRead;
	mutable size_t m_uBytesLeftToRead;

	friend class Asura_Chunk_SaveBlock_Stream;
};
// End class Asura_ProtoSaveBlock.

class Asura_SaveBlock : public Asura_ProtoSaveBlock
{

public:

    // C O N S T R U C T O R S   /   D E S T R U C T O R S ///////////////////

    Asura_SaveBlock( void );
	Asura_SaveBlock( const u_int uSmallestSize );
    virtual ~Asura_SaveBlock( void );

    // M E T H O D S /////////////////////////////////////////////////////////

#ifdef ASURA_SAVEBLOCK_ALLOWACCESSPOINTER
    virtual void* GetAccessPointer( int iNumBytesRequired );
#endif

private:
	u_int m_uSmallestSize;						// smallest block size to allocate
};
// End class Asura_SaveBlock.


/*************************************************************
 *
 * Class Name    : Asura_StaticSaveBlock
 *
 * Author        : Richard May
 *
 * Description   : Maintains a single big block of memory with
 *					constant size to store checkpoint saved.
 *
 *************************************************************/
class Asura_StaticSaveBlock : public Asura_ProtoSaveBlock
{

public:
	/* Constructors/destructor. */
	Asura_StaticSaveBlock	( int iBufferSize );
	virtual ~Asura_StaticSaveBlock	();

public:
	/* Public interface functions. */

#ifdef ASURA_SAVEBLOCK_ALLOWACCESSPOINTER
	virtual void* GetAccessPointer( int iNumBytesRequired );
#endif

	virtual bool AddData( const void* pSrc, size_t uSize );	// returns whether all data was added.

protected:
	/* Data. */

};
/* end of class Asura_StaticSaveBlock */


 /*************************************************************
 *
 * Class Name    : Asura_CompressedSaveBlock
 *
 * Author        : Mike Healey (22.05.2008)
 *
 * Description   : Maintains a block of memory containing 
 *					compressed checkpoint data. Data is added
 *					to a smallish buffer which is compressed
 *					into a larger block when it becomes full,
 *					or when the Flush() function is called.
 *
 *************************************************************/
class Asura_CompressedSaveBlock : public Asura_SaveBlock_Base
{

public:
	// Constructors/destructor.
	Asura_CompressedSaveBlock( u_int uCompressedSize );
	Asura_CompressedSaveBlock();
	~Asura_CompressedSaveBlock();

public:
	// Public interface functions.
    // M E T H O D S /////////////////////////////////////////////////////////
	virtual bool AddData( const void* pSrc, size_t uSize );								// returns whether all data was added.
	virtual bool AddPatchData( const void* pData, u_int uNumberOfBytes, u_int uPos );	// writes 4 bytes at the specified position.
	virtual size_t ReadData( void* pDest, size_t uSize ) const;							// returns number of bytes that were read.
	virtual size_t SkipData( size_t uSize ) const;										// returns number of bytes that were skipped.
	virtual size_t GetBytesRead() const;												// read counter
	virtual size_t GetBytesWritten() const;												// write counter
	virtual void RestartRead() const;													// resets the read pointer to the beginning.
	void SetCompressedDataPointer( char* pxDataBuffer, u_int uSize );					// Allows us to set the compressed block to external buffer
	void UnsetCompressedDataPointer();													// Allows us to unset the compressed block from external buffer

	// Flush() compresses and moves the data from the uncompressed buffer into the compressed buffer
	bool Flush( void );

	virtual void Clear( bool bWriteFileID = true );

	virtual bool AddAsuraTerminator( void );

#ifdef ASURA_SAVEBLOCK_USE_ZLIB
	static void SetMemLevel(u_int uMemLevel);
	static void SetWindowBits(u_int uWindowBits);

	u_int GetDeflatedSize() const { return m_uWritePosInCompressedBuffer; }
#endif
private:

	// when data that has already been compressed needs to be modified, we store a patch.
	// Patches are checked applied when reading data back from the compressed to the uncompressed buffer.
	struct SaveBlockPatch
	{
		u_int m_uPos;
		u_int m_uNumBytes; // number of bytes stored in the array below, from 1 to 4
		char m_acValue[4];
	};

	// internal read/write functions
	void WriteDataToUncompressedBuffer( const void* pSrc, size_t uSize );
	void ReadDataFromUncompressedBuffer( void* pDest, size_t uSize ) const;
	void SkipDataInUncompressedBuffer( size_t uSize ) const;
	// Decompress() - function to fill up the uncompressed buffer from the compressed one while reading
	void Decompress() const;

#ifdef ASURA_SAVEBLOCK_USE_ZLIB
	bool BeginZlibCompression();
	bool BeginZlibDecompression() const;
	bool EndZlibCompression();
	bool EndZlibDecompression() const;
#endif

	// Data.
	bool m_bOwnsData;
	char* m_pcCompressedData;
	char* m_pcUncompressedBuffer;
	u_int m_uCompressedSize;
	SaveBlockPatch* m_pxPatches;
	u_int m_uMaxPatches;
	u_int m_uPatchesUsed;

	u_int m_uWritePosInUncompressedBuffer;
	u_int m_uWritePosInCompressedBuffer;
	u_int m_uBytesCompressed;
	
	mutable u_int m_uReadPosInUncompressedBuffer;
	mutable u_int m_uReadPosInCompressedBuffer;
	mutable u_int m_uBytesDecompressed;
	mutable u_int m_uNextPatch;

	u_int m_uBytesWritten;
	mutable u_int m_uBytesRead;

#ifdef ASURA_SAVEBLOCK_USE_ZLIB
	mutable z_stream m_xZlibStream;
	static u_int s_uZlibMemLevel;
	static u_int s_uZLibWindowBits;
#endif
};
/* end of class Asura_StaticSaveBlock */




#endif//__ASURA_SAVEBLOCK_H__
