#include "SaiCPA.h"
#include "MmgSai.h"
#include "ErmSai.h"
#include "Sai_Enum.h"
#include "Sai_Pub.h"
#include "Sai_str.h"
#include "Sai_Priv.h"
#include "Sai_Save.h"
#include "cmp.h"
 
 
/* To avoid using compressed file in debug mode */
/* (uncompressed files are easier to view :-)*/
#ifdef _DEBUG
	#undef SAVEGAME_COMPRESSION
#else
	#define SAVEGAME_COMPRESSION
#endif
 
/**************************************************************************/
unsigned char SAI_fn_ucSaveAllGameTableValues(struct SAI_tdstInternalStructure_ *p_stInternalStructure,char *_szFileName,SAI_tdeFlags _eFlags)
{
 unsigned char bReturn = SAI_TRUE;
 SAI_tdxHandleToListEntry hListEntry = p_stInternalStructure -> p_stFirstEntry;
 FILE *p_stTempFile;
 
#ifdef SAVEGAME_COMPRESSION
 char szTempFile[_MAX_PATH];
 
 sprintf(szTempFile,"%s/TT.tmp",fn_szGetTempDataPath());
 
 DeleteFile(szTempFile);
 FIL_fn_bValidatePath(".",_szFileName);
 p_stTempFile = fopen(szTempFile,"wb");
#else /* SAVEGAME_COMPRESSION */
 FIL_fn_bValidatePath(".",_szFileName);
 p_stTempFile = fopen(_szFileName,"wb");
#endif /* SAVEGAME_COMPRESSION */
 
 if (p_stTempFile!=NULL)
 {
  SAI_fn_vClearBooleanBuffer();
  while (hListEntry)
  {
   if ((hListEntry->eFlags&(_eFlags)))  
    SAI_fn_ucSaveGameTableValue(hListEntry,p_stTempFile);
   hListEntry = hListEntry -> p_stNextEntry;
  }
  SAI_fn_vFlushBooleanBuffer( p_stTempFile );
  fclose(p_stTempFile);
 
#ifndef _DEBUG 
  CMP_fn_vCompressFileIn(szTempFile,_szFileName,D_CMP_DIFF);
#endif
 
 }
 else
  bReturn = SAI_FALSE;
 
#ifdef SAVEGAME_COMPRESSION 
 DeleteFile(szTempFile);
#endif /* SAVEGAME_COMPRESSION */
 
 return(bReturn);
}
/**************************************************************************/
unsigned char SAI_fn_ucSaveGameTableValue(SAI_tdxHandleToListEntry hListEntry,FILE *_p_stFile)
{
 unsigned char bReturn = SAI_TRUE;
 
 switch( hListEntry->eFlags & C_TypesFlags )
 {
  /* GuS : Save a boolean */
  /* (WARNING: all boolean entries must be at the end of the list, no other data should be saved AFTER booleans,*/
  /* More over, the list must be saved from the first element to the last one.)*/
  case SAI_eType1:
   if (hListEntry->eFlags&SAI_ePlayerSaveTableValue||hListEntry->eFlags&SAI_eLevelSaveTableValue)
    SAI_fn_vSaveBoolean( 
      hListEntry->uData.stBooleanData.ucCharData, 
      hListEntry->uData.stBooleanData.ucBitPosition, 
      _p_stFile );
   else if(hListEntry->eFlags&SAI_ePlayerSaveCurrentValue||hListEntry->eFlags&SAI_eLevelSaveCurrentValue)
    SAI_fn_vSaveBoolean( 
      *(unsigned char *)hListEntry->p_vDataPointer, 
      hListEntry->uData.stBooleanData.ucBitPosition, 
      _p_stFile );
   else
    M_SAIFatalError(E_uwSAI_InvalidSaveMode);
   break;
 
  case SAI_eType8:
   if (hListEntry->eFlags&SAI_ePlayerSaveTableValue||hListEntry->eFlags&SAI_eLevelSaveTableValue)
    fwrite(&hListEntry->uData.ucCharData,1,sizeof(unsigned char),_p_stFile);
   else if(hListEntry->eFlags&SAI_ePlayerSaveCurrentValue||hListEntry->eFlags&SAI_eLevelSaveCurrentValue)
    fwrite(hListEntry->p_vDataPointer,1,sizeof(unsigned char),_p_stFile);
   else
    M_SAIFatalError(E_uwSAI_InvalidSaveMode);
   break;
  case SAI_eType16:
   if (hListEntry->eFlags&SAI_ePlayerSaveTableValue||hListEntry->eFlags&SAI_eLevelSaveTableValue)
    fwrite(&hListEntry->uData.uwShortData,1,sizeof(unsigned short),_p_stFile);
   else if(hListEntry->eFlags&SAI_ePlayerSaveCurrentValue||hListEntry->eFlags&SAI_eLevelSaveCurrentValue)
    fwrite(hListEntry->p_vDataPointer,1,sizeof(unsigned short),_p_stFile);
   else
    M_SAIFatalError(E_uwSAI_InvalidSaveMode);
   break;
  case SAI_eType32:
   if (hListEntry->eFlags&SAI_ePlayerSaveTableValue||hListEntry->eFlags&SAI_eLevelSaveTableValue)
    fwrite(&hListEntry->uData.ulLongData,1,sizeof(unsigned long),_p_stFile);
   else if(hListEntry->eFlags&SAI_ePlayerSaveCurrentValue||hListEntry->eFlags&SAI_eLevelSaveCurrentValue)
    fwrite(hListEntry->p_vDataPointer,1,sizeof(unsigned long),_p_stFile);
   else
    M_SAIFatalError(E_uwSAI_InvalidSaveMode);
   break;
  case SAI_eType64:
   if (hListEntry->eFlags&SAI_ePlayerSaveTableValue||hListEntry->eFlags&SAI_eLevelSaveTableValue)
    fwrite(&hListEntry->uData.i64Long64Data,1,sizeof(tdLong64),_p_stFile);
   else if(hListEntry->eFlags&SAI_ePlayerSaveCurrentValue||hListEntry->eFlags&SAI_eLevelSaveCurrentValue)
    fwrite(hListEntry->p_vDataPointer,1,sizeof(tdLong64),_p_stFile);
   else
    M_SAIFatalError(E_uwSAI_InvalidSaveMode);
   break;
  case SAI_eTypePointer:
   if (hListEntry->eFlags&SAI_ePlayerSaveTableValue||hListEntry->eFlags&SAI_eLevelSaveTableValue)
   {
    fwrite(&hListEntry->uData.stPointerData.uwBlocAndModuleID,1,sizeof(unsigned short),_p_stFile);
    fwrite(&hListEntry->uData.stPointerData.p_vPointer,1,sizeof(void *),_p_stFile);
   }
   else if(hListEntry->eFlags&SAI_ePlayerSaveCurrentValue||hListEntry->eFlags&SAI_eLevelSaveCurrentValue)
   {
    void *p_vBeginBloc;
    unsigned short uwBlocId;
    void *p_vPointer = (void*)(*((void**)hListEntry->p_vDataPointer));
    if (p_vPointer!=NULL)
    {
     Mmg_fn_vWhereIs((void*)p_vPointer,&uwBlocId,&p_vBeginBloc);
     if (uwBlocId==0xffff)
      p_vPointer  = (void*)p_vPointer;
     else
      p_vPointer  = (void*)(((long)p_vPointer)-(long)p_vBeginBloc);
    }
    else
     uwBlocId = 0xffff;
 
    fwrite(&uwBlocId,1,sizeof(unsigned short),_p_stFile);
    fwrite(&p_vPointer,1,sizeof(void *),_p_stFile);
   }
   else
    M_SAIFatalError(E_uwSAI_InvalidSaveMode);
   break;
  case SAI_eTypeXX:
   if (hListEntry->eFlags&SAI_ePlayerSaveTableValue||hListEntry->eFlags&SAI_eLevelSaveTableValue)
    fwrite(hListEntry->uData.stArrayData.p_ucPointer,hListEntry->uData.stArrayData.ulArraySize,sizeof(unsigned char),_p_stFile);
   else if(hListEntry->eFlags&SAI_ePlayerSaveCurrentValue||hListEntry->eFlags&SAI_eLevelSaveCurrentValue)
    fwrite(hListEntry->p_vDataPointer,hListEntry->uData.stArrayData.ulArraySize,sizeof(unsigned char),_p_stFile);
   else
    M_SAIFatalError(E_uwSAI_InvalidSaveMode);
   break;
  default:
   bReturn = SAI_FALSE;
   break;
 }
 
 return(bReturn);
}
/**************************************************************************/
 
unsigned char SAI_fn_ucLoadGameTableValue( SAI_tdxHandleToListEntry hListEntry, FILE *_p_stFile )
{
 unsigned char bReturn = SAI_TRUE;
 if (!feof(_p_stFile))
 {
  switch( hListEntry->eFlags & C_TypesFlags )
  {
   case SAI_eType1:
    hListEntry->uData.stBooleanData.ucCharData = 
          (unsigned char)(SAI_fn_cLoadBoolean( _p_stFile ) << hListEntry->uData.stBooleanData.ucBitPosition);
    break;
   case SAI_eType8:
    fread(&hListEntry->uData.ucCharData,1,sizeof(hListEntry->uData.ucCharData),_p_stFile);
    break;
   case SAI_eType16:
    fread(&hListEntry->uData.uwShortData,1,sizeof(hListEntry->uData.uwShortData),_p_stFile);
    break;
   case SAI_eType32:
    fread(&hListEntry->uData.ulLongData,1,sizeof(hListEntry->uData.ulLongData),_p_stFile);
    break;
   case SAI_eType64:
    fread(&hListEntry->uData.i64Long64Data,1,sizeof(hListEntry->uData.i64Long64Data),_p_stFile);
    break;
   case SAI_eTypePointer:
    fread(&hListEntry->uData.stPointerData.uwBlocAndModuleID,1,sizeof(hListEntry->uData.stPointerData.uwBlocAndModuleID),_p_stFile);
    fread(&hListEntry->uData.stPointerData.p_vPointer,1,sizeof(hListEntry->uData.stPointerData.p_vPointer),_p_stFile);
    break;
   case SAI_eTypeXX:
    fread(hListEntry->uData.stArrayData.p_ucPointer,hListEntry->uData.stArrayData.ulArraySize,sizeof(*hListEntry->uData.stArrayData.p_ucPointer),_p_stFile);
    break;
   default:
    bReturn = SAI_FALSE;
    break;
  }
 }
 
 return(bReturn);
}
 
/**************************************************************************/
 
unsigned char SAI_fn_ucLoadAllGameTableValues( 
	struct SAI_tdstInternalStructure_ *p_stInternalStructure, 
	char *_szFileName, 
	SAI_tdeFlags _eFlags )
{
	unsigned char bReturn = SAI_FALSE;
	SAI_tdxHandleToListEntry hListEntry = p_stInternalStructure -> p_stFirstEntry;
	FILE *p_stTempFile;
#ifdef SAVEGAME_COMPRESSION 
	char szTempFile[_MAX_PATH];
 
	sprintf(szTempFile,"%s/TT.tmp",fn_szGetTempDataPath());
	DeleteFile(szTempFile);
 
	if (CMP_fn_bExpandFileIn(_szFileName,szTempFile,D_CMP_DIFF))
	{
		p_stTempFile = fopen(szTempFile,"rb");
#else  /* SAVEGAME_COMPRESSION */
		p_stTempFile = fopen(_szFileName,"rb");
#endif /* SAVEGAME_COMPRESSION */
		if (p_stTempFile!=NULL)
		{
			SAI_fn_vClearBooleanBuffer();
			while (hListEntry)
			{
				if( hListEntry->eFlags & _eFlags )
					SAI_fn_ucLoadGameTableValue( hListEntry, p_stTempFile );
				hListEntry = hListEntry -> p_stNextEntry;
			}
			fclose(p_stTempFile);
			bReturn = SAI_TRUE;
		}
 
#ifdef SAVEGAME_COMPRESSION 
	}
	DeleteFile(szTempFile);
#endif
 
 return(bReturn);
}
 
/**************************************************************************/
/* GuS*/
/* To optimize saving of boolean data, all booleans are grouped by 8 in a char.*/
/* To achieve this, saving is bufferised into one char witch is written to file*/
/* only when it is full.*/
static unsigned char g_ucBooleanBuffer = 0;
static unsigned char g_ucCurrentPosition = 0;     /* Next bit to be read or set (0..7)*/
 
void SAI_fn_vSaveBoolean( unsigned char _ucData, unsigned char _ucBitPosition, FILE *_p_stFile )
{
	g_ucBooleanBuffer |= ((_ucData >> _ucBitPosition) & (unsigned char)1 ) << g_ucCurrentPosition;
	g_ucCurrentPosition++;
 
	if( g_ucCurrentPosition > 7 )
		SAI_fn_vFlushBooleanBuffer( _p_stFile );
}
 
/**************************************************************************/
 
/* GuS*/
/* Function for reading boolean previously saved with SAI_fn_vSaveBoolean.*/
/* Return one boolean in the first (lowest) bit of a char.*/
/* Return -1 if end of file is reached.*/
char SAI_fn_cLoadBoolean( FILE *_p_stFile )
{
	if( g_ucCurrentPosition == 0 || g_ucCurrentPosition > 7 )
	{
		if( fread( &g_ucBooleanBuffer, 1, sizeof(unsigned char), _p_stFile ) == 0 ) return -1;
		g_ucCurrentPosition = 0;
	}
 
	return (unsigned char)((g_ucBooleanBuffer >> (g_ucCurrentPosition++)) & 1);
}
 
/**************************************************************************/
 
void SAI_fn_vClearBooleanBuffer()
{
	g_ucBooleanBuffer = 0;
	g_ucCurrentPosition = 0;
}
 
/**************************************************************************/
 
void SAI_fn_vFlushBooleanBuffer( FILE *_p_stFile )
{
	if( g_ucCurrentPosition )
	{
		fwrite( &g_ucBooleanBuffer, 1, sizeof(unsigned char), _p_stFile );
		g_ucBooleanBuffer = 0;
		g_ucCurrentPosition = 0;
	}
}
/**************************************************************************/
