Source file: /~heha/hsn/borg.zip/SAVEFILE.CPP

/************************************************************************
*		    savefile.cpp					*
* File I/O for saving to and reading from database files only.		*
* Added in version 2.11. Compression is implemented based on rle, but	*
* it is nibble based (2*nibbles=byte) rather than byte based. I decided *
* on this after closer examination of the database files. It results in *
* around a 30% reduction to the database file size.			*
************************************************************************/

#include <windows.h>

#include "savefile.h"
#include "dasm.h"
#include "debug.h"

/************************************************************************
* Constructor - sets up some variables used in the compression and save *
************************************************************************/
savefile::savefile() {
 sfile=INVALID_HANDLE_VALUE;
 rbufflen=0;
 rbuffptr=0;
 rbhigh=true;
 rlecount=0;
 rlemode=false;
 rlestart=true;
}

/************************************************************************
* Destructor - closes the database file if it is still open		*
************************************************************************/
savefile::~savefile() {
 sclose();
}

/************************************************************************
* sopen									*
* - opens the database file and returns true on success			*
************************************************************************/
bool savefile::sopen(LPCTSTR lpFileName,DWORD dwDesiredAccess,DWORD dwShareMode,
	 DWORD dwCreationDistribution,DWORD dwFlagsAndAttributes, bool compr) {
 compressed=compr;
 okay=false;

 sfile=CreateFile(lpFileName,dwDesiredAccess,dwShareMode,NULL,
	    dwCreationDistribution,dwFlagsAndAttributes,NULL);

 okay= sfile!=INVALID_HANDLE_VALUE && GetFileType(sfile)==FILE_TYPE_DISK;

 if (!okay) {
  MessageBox(MainWnd,"File open failed!",lpFileName,MB_OK|MB_ICONEXCLAMATION);
  sclose();
 }
 return okay;
}

/************************************************************************
* sclose								*
* - closes the database file if still open				*
************************************************************************/
void savefile::sclose(void) {
 if (sfile!=INVALID_HANDLE_VALUE) CloseHandle(sfile);
}

/************************************************************************
* getnibble								*
* - This function sets n to the next nibble from the file, it uses	*
*   buffering and reads from the file as required			*
************************************************************************/
bool savefile::getnibble(byte *n)
{  bool rval;
   if(rbuffptr<rbufflen)
   {  if(rbhigh)
      {	 (*n)=(byte)(rbuff[rbuffptr]>>4);
	 rbhigh=false;
	 return true;
      }
      (*n)=(byte)(rbuff[rbuffptr]&0x0f);
      rbhigh=true;
      rbuffptr++;
      return true;
   }
   else
   {  rval=(ReadFile(sfile,rbuff,RBUFF_MAXLEN,&rbufflen,NULL)!=0);
      rbhigh=true;
      rbuffptr=0;
      if((rval)&&(rbufflen))
	return getnibble(n);
      return false;
   }
}

/************************************************************************
* getrlenibble								*
* - this function sets n to the next nibble from the file, taking into	*
*   account rle encoding. So this returns the next uncompressed nibble	*
* - rle encoding is:							*
*   rle_code count nibble						*
*   count is number-1 (so it can encode from 2 to 16 nibbles)		*
*   or rle_code 0 signifies a nibble equal to rle_code			*
* - note that rle_code is a constant specified in the savefile.h which	*
*   indicates an rle encoding, and is currently 0x0f. Do not set this	*
*   constant to 0 as this would be inefficient....			*
************************************************************************/
bool savefile::getrlenibble(byte *n)
{  if(rlemode)
   {  rlecount--;
      if(!rlecount)
	 rlemode=false;
      (*n)=(byte)(rlebyte);
      return true;
   }
   if(!getnibble(&rlebyte))
      return false;
   if(rlebyte==rle_code)
   {  if(!getnibble(&rlebyte))
	 return false;
      if(rlebyte)
      {	 rlecount=rlebyte;
	 rlemode=true;
	 if(!getnibble(&rlebyte))
	    return false;
	 (*n)=rlebyte;
	 return true;
      }
      (*n)=rle_code;
      return true;
   }
   (*n)=rlebyte;
   return true;
}

/************************************************************************
* putnibble								*
* - The opposite function to getnibble, it writes one nibble to the	*
*   file using buffering						*
************************************************************************/
bool savefile::putnibble(byte n)
{  bool rval;
   dword num;
   if(rbuffptr<RBUFF_MAXLEN)
   {  if(rbhigh)
      {	 rbuff[rbuffptr]=(byte)((n&0x0f)<<4);
	 rbhigh=false;
	 rbufflen++;
	 return true;
      }
      rbuff[rbuffptr]+=(byte)(n&0x0f);
      rbhigh=true;
      rbuffptr++;
      return true;
   }
   else
   {  rval=(WriteFile(sfile,rbuff,RBUFF_MAXLEN,&num,NULL)!=0);
      rbhigh=true;
      rbuffptr=0;
      rbufflen=0;
      if(rval)
	 return putnibble(n);
      return false;
   }
}

/************************************************************************
* flushnibble								*
* - A necessity of buffered writing, this flushes the remainder of the	*
*   buffer, writing it out to file					*
************************************************************************/
bool savefile::flushnibble(void)
{  dword num;
   if(rbufflen)
      return (WriteFile(sfile,rbuff,rbufflen,&num,NULL)!=0);
   return true;
}

/************************************************************************
* putrlenibble								*
* - This is the opposite function to getrlenibble. It writes nibbles to *
*   file whilst performing the compression. The rle encoding happens	*
*   here and when nibbles are ready to be written the putnibble		*
*   function is called							*
************************************************************************/
bool savefile::putrlenibble(byte n)
{  if(rlestart)
   {  rlestart=false;
      rlebyte=n;
      return true;
   }
   if(rlemode)
   {  if((rlebyte==n)&&(rlecount<0x0f))
      {	 rlecount++;
	 return true;
      }
      if(!putnibble(rle_code))
	 return false;
      if(!putnibble(rlecount))
	 return false;
      if(!putnibble(rlebyte))
	 return false;
      rlemode=false;
      rlebyte=n;
      return true;
   }
   if(rlebyte==n)
   {  rlemode=true;
      rlecount=1;
      return true;
   }
   if(!putnibble(rlebyte))
      return false;
   if(rlebyte==rle_code)
      if(!putnibble(0))
	 return false;
   rlebyte=n;
   return true;
}

/************************************************************************
* flushrlenibble							*
* - This flushes any partial rle at the end of a file and forces it to	*
*   the putnibble function						*
************************************************************************/
bool savefile::flushrlenibble(void)
{  if(rlemode)
   {  if(!putnibble(rle_code))
	 return false;
      if(!putnibble(rlecount))
	 return false;
      if(!putnibble(rlebyte))
	 return false;
   }
   else
   {  if(!putnibble(rlebyte))
	 return false;
      if(rlebyte==rle_code)
	 if(!putnibble(0))
	    return false;
   }
   return true;
}

/************************************************************************
* flushfilewrite							*
* - The function to flush writing which should be called at the end of	*
*   the save. It flushes any partial encoding and then flushes the	*
*   buffered write							*
************************************************************************/
bool savefile::flushfilewrite(void)
{  if(!flushrlenibble())
      return false;
   return flushnibble();
}

/************************************************************************
* sread									*
* - This is the external call for reading from a file. Its a similar	*
*   format to ReadFile, and uses the rle compression routines		*
************************************************************************/
bool savefile::sread(LPVOID lpBuffer,DWORD nNumberOfBytesToRead)
{  byte n;
   dword num;
   if (compressed) {
     for(num=0;num<nNumberOfBytesToRead;num++)
     {  if(!getrlenibble(&n)) return false;
        ((byte *)lpBuffer)[num]=(byte)(n<<4);
        if(!getrlenibble(&n)) return false;
        ((byte *)lpBuffer)[num]+=n;
     }
     return true;
   } else {
     return ReadFile(sfile,lpBuffer,nNumberOfBytesToRead,&num,NULL) && num==nNumberOfBytesToRead;
   }
}

// GUI version of sread() above
bool savefile::Read(LPVOID lpBuffer,DWORD nNumberOfBytesToWrite) {
 ZeroMemory(lpBuffer,nNumberOfBytesToWrite); //give zeroes in case of failure
 if (!okay) return false;	// silently
 okay=sread(lpBuffer,nNumberOfBytesToWrite);
 if (!okay) {
  MessageBox(MainWnd,"Error reading file, maybe file corrupt",NULL,MB_OK|MB_ICONEXCLAMATION);
 }
 return okay;
}

/************************************************************************
* swrite								*
* - This is the external call for writing to a file. Its a similar	*
*   format to WriteFile, and uses the rle compression routines		*
************************************************************************/
bool savefile::swrite(LPCVOID lpBuffer,DWORD nNumberOfBytesToWrite) {
 dword num;
 if (compressed) {
  for (num=0;num<nNumberOfBytesToWrite;num++) {
   if (!putrlenibble((byte)(((byte *)lpBuffer)[num]>>4))) return false;
   if (!putrlenibble((byte)(((byte *)lpBuffer)[num]&0x0f))) return false;
  }
  return true;
 }else{
  return WriteFile(sfile,lpBuffer,nNumberOfBytesToWrite,&num,NULL) && num==nNumberOfBytesToWrite;
 }
}

// GUI version of swrite() above
bool savefile::Write(LPCVOID lpBuffer,DWORD nNumberOfBytesToWrite) {
 if (!okay) return false;
 okay=swrite(lpBuffer,nNumberOfBytesToWrite);
 if (!okay) {
  MessageBox(MainWnd,"Error writing file, maybe disk full",NULL,MB_OK|MB_ICONEXCLAMATION);
 }
 return okay;
}
Detected encoding: ASCII (7 bit)2