#include "stdafx.h"
#include "fcpriv.h"
#include "fspriv.h"
#include "forage.h"

static int cbRead;	   // Number of bytes already read from qBuffer.
int  cbSystemFile;	  // Total number of bytes in |SYSTEM file buffer.
PBYTE  pSysBufRead;  /* Points to point in |SYSTEM file buffer just after stuff
					   we've already read.  */

static BOOL STDCALL FCheckSystem(QHHDR qhhdr, UINT* pwErr);

#define PtrFromGh(x) (x)
#define PszFromGh(x) (x)
QRGWSMAG qrgwsmag;

/***************************************************************************
 *
 -	Name: FReadBufferQch
 -
 *	Purpose: To read from the System buffer and advance the count of what's
 *			 been read.  If qchToRead == NULL, then we just seek forward.
 *
 *	Arguments: LPSTR  qchToRead - Buffer to read System buffer into.
 *			   LONG cbToRead  - Number of bytes to read.
 *
 *	Returns: BOOL - TRUE if read successful.
 *
 *	Globals Used: pSysBufRead - The static pointer to where we are in the
 *								|SYSTEM buffer.
 *
 *	+++
 *
 *	Notes: Trivial function, but making it a function makes things easier
 *		   elsewhere.
 *
 ***************************************************************************/

BOOL STDCALL FReadBufferQch(PBYTE qchToRead, int cbToRead)
{
	if ((cbRead + cbToRead) > cbSystemFile)
		return FALSE;
	if (qchToRead != NULL)
		MoveMemory(qchToRead, pSysBufRead, cbToRead);
	pSysBufRead += cbToRead;
	cbRead += cbToRead;
	return TRUE;
}

/***************************************************************************

	FUNCTION:	FSkipReadBufferQch

	PURPOSE:	Skip over the specified amount of data

	PARAMETERS:
		cbToRead

	RETURNS:

	COMMENTS:

	MODIFICATION DATES:
		24-Feb-1993 [ralphw]

***************************************************************************/

BOOL STDCALL FSkipReadBufferQch(int cbToRead)
{
	if ((cbRead + cbToRead) > cbSystemFile)
		return FALSE;
	pSysBufRead += cbToRead;
	cbRead += cbToRead;
	return TRUE;
}

/***************************************************************************
 *
 -	Name: FReadSystemFile
 -
 *	Purpose:
 *	Reads in the tagged data from the |SYSTEM file
 *
 *	Arguments:
 *	  hfs		- handle to file system to read
 *	  pdb		- near pointer to DB struct to fill
 *	  pwErr 	- pointer to place error word
 *
 *	Returns: True if valid version number, system file
 *			 pdb is changed
 *
 *	Globals Used: pSysBufRead, cbRead, cbSystemFile
 *
 *	History:
 *		24-Feb-1993 [ralphw] Added fTitleOnly for when we build a master
 *			keyword list.
 *
 ***************************************************************************/

#define QHFSYSTEM "|SYSTEM" 		// system file
#define RcGetFSError() (rcFSError)

extern QRGWSMAG qrgwsmag;

BOOL STDCALL FReadSystemFile (
		HFS 	hfs,
		PDB 	pdb,
		UINT*	pwErr,
		BOOL	fTitleOnly
) {
	HF	  hf;
	TAG   tagRead;
	WORD  cbData;
	LPBYTE qBuffer;
	LPSTR  lpszTempBuf;
	BOOL  fIconTag = FALSE; // set to TRUE if the icon is present in file.
	UINT iWsmag   = 0;		// count of smags seen

	*pwErr = wERRS_BADFILE; // REVIEW - is this a reasonable default????

	// Initialize fields of DE to default values:

	PDB_ADDRCONTENTS(pdb) = addrNil;

	// Open the |SYSTEM subsystem.

	if ((hf = HfOpenHfs((QFSHR) hfs, QHFSYSTEM, FS_OPEN_READ_ONLY)) == NULL) {
		if (RcGetFSError() == RC_OutOfMemory)
			*pwErr = wERRS_OOM;
		else
			*pwErr = wERRS_BADFILE;   // this is good enough
	  	return FALSE;
	}

	// Get the size of the |SYSTEM file, and read it into a buffer.

	cbSystemFile = LcbSizeHf(hf);
	CMem mem(cbSystemFile);
	qBuffer = mem.pb;

	if (!qBuffer) {
		*pwErr = RC_OutOfMemory;
		goto error_quit;
	}

	if (!LcbReadHf(hf, qBuffer, cbSystemFile)) {
		if (RcGetFSError() == RC_OutOfMemory)
			*pwErr = RC_OutOfMemory;
		else
			*pwErr = RC_BadVersion;
		goto error_close;
	}
	pSysBufRead = qBuffer;
	cbRead = 0;

	Ensure(RcCloseHf(hf), RC_Success);

	if (!FCheckSystem(&pdb->hhdr, pwErr))
		goto error_return;

	// If this is a 3.0 file, just read in the title like we used to.

	if (PDB_HHDR(pdb).wVersionNo == wVersion3_0) {
		if (!FReadBufferQch((unsigned char *) PDB_RGCHTITLE(pdb), (cbSystemFile - cbRead))) {
			*pwErr = RC_BadVersion;
			goto error_return;
		}
		goto ok_return;
	}

	// Loop through all tags, reading the data and putting it someplace.

	kwlcid.langid = 0;

	for (;;)
		{
		//
		// TAG's are 16 bit values.  This code is endian dependent.
		//
		*((int *)&tagRead) = 0;
		if (!FReadBufferQch((LPBYTE) &tagRead, sizeof(WORD)))
			break;	// Out of tags.

		ASSERT ((tagRead > tagFirst) && (tagRead < tagLast));

		if (!FReadBufferQch((LPBYTE) &cbData, sizeof(WORD)))
			goto error_return;

		if (fTitleOnly)
			{
			if (tagRead == tagTitle)
				{
				lpszTempBuf = PDB_RGCHTITLE(pdb);

				if (!FReadBufferQch((unsigned char *) lpszTempBuf, cbData))
					goto error_return;
				else
					goto ok_return; 	  // we don't care about any other tags
				}
			else
				{
				if (!FSkipReadBufferQch(cbData))
					goto error_return;
				}
			continue;
			}

		// The valye of tagRead decides where we will read the data into.

		switch (tagRead)
		{
		case tagTitle:
			lpszTempBuf = PDB_RGCHTITLE(pdb);
			break;

		case tagCopyright:
			lpszTempBuf = PDB_RGCHCOPYRIGHT(pdb);
			break;

		case tagContents:
			lpszTempBuf = (LPSTR) &PDB_ADDRCONTENTS(pdb);
			break;

		case tagCitation:

			/*
			 * Citation tag: additional text to be appended to the end of
			 * copy-to-clipboard. Just stick in some global memory referenced
			 * by the DB, where it can be picked up when needed by the copy
			 * code.
			 */

			if (PDB_HCITATION(pdb))
				lcFree(PDB_HCITATION(pdb));

			PDB_HCITATION(pdb) = (LPSTR) lcMalloc(cbData);

			if (!FReadBufferQch((unsigned char *) PtrFromGh(PDB_HCITATION(pdb)), cbData))
				goto error_return;

			ASSERT ((WORD)lstrlen((const char *) pSysBufRead) < cbData);

			lpszTempBuf = NULL;
			cbData = 0;
			break;

		case tagConfig:
			lpszTempBuf = NULL;
			break;

		case tagIcon:
			lpszTempBuf = NULL;
			break;

		case tagWindow:

			// window tag. We collect all the wsmag structures into a single
			// block of memory, and hang that sucker off the de.

			if (!PDB_HRGWSMAG(pdb)) {

				/*
				 * Block has not yet been allocated. We always allocate
				 * the maximum size block, just because managing it as
				 * variable size is more of a pain than it's worth. When we
				 * go to multiple secondary windows and the number increases,
				 * this will no longer be true.
				 */

				ASSERT (iWsmag == 0);

				// REVIEW: could probably just used LMEM_FIXED

				PDB_HRGWSMAG(pdb) = (HWSMAG) lcMalloc(sizeof(RGWSMAG));
				if (!PDB_HRGWSMAG(pdb)) {
					*pwErr = wERRS_OOM;
					goto error_return;
				}
			}
			else {

				// Increase the size to allow for the new window array

				PDB_HRGWSMAG(pdb) = (HWSMAG) lcReAlloc(PDB_HRGWSMAG(pdb), 
					sizeof(RGWSMAG) + sizeof(WSMAG) *
					(((QRGWSMAG) PtrFromGh(PDB_HRGWSMAG(pdb)))->cWsmag + 1));
				if (!PDB_HRGWSMAG(pdb)) {
					*pwErr = wERRS_OOM;
					goto error_return;
				}
			}

			qrgwsmag = (QRGWSMAG) PtrFromGh(PDB_HRGWSMAG(pdb));

			// Increment the count of structures in the block, point at the
			// appropriate new slot, and copy in the new structure.

			qrgwsmag->rgwsmag[iWsmag++] = *(QWSMAG) pSysBufRead;
			qrgwsmag->cWsmag = iWsmag;

			lpszTempBuf = NULL;
			break;

		// The following are new to 4.0

		case tagLCID:	  // Locale Identifier and CompareStringA flags
			lpszTempBuf = (LPSTR) &kwlcid;
			break;

		case tagCHARSET:  // default charset to use
			lpszTempBuf = NULL;
			break;

		case tagCNT:
			lpszTempBuf = NULL;
			break;

		case tagPopupColor:
			lpszTempBuf = NULL;
			break;

		case tagDefFont:
			lpszTempBuf = NULL;
			break;

		case tagIndexSep:
			lpszTempBuf = NULL;
			break;

		default:

			// Unimplemented tag. Ignore it.

			ASSERT(FALSE);

			lpszTempBuf = NULL;
			break;
		}

		if (!FReadBufferQch((unsigned char *)lpszTempBuf, cbData))
			goto error_return;
		}

ok_return:

	if (kwlcid.langid)
		lcid = MAKELCID(kwlcid.langid, SORT_DEFAULT);
	return TRUE;

error_close:
	Ensure(RcCloseHf(hf), RC_Success);
error_return:
error_quit:
//
// BUGBUG want an error report here?
	//if (*pwErr == RC_Error || *pwErr == RC_BadVersion) {
	//	char szName[_MAX_PATH];
	//	lstrcpy(szName, PszFromGh(PDB_FM(pdb)));
	//	ErrorVarArgs(*pwErr, wERRA_RETURN, szName);
	//}
	return FALSE;
}

/***************
 *
 - FCheckSystem
 -
 * purpose
 *	 Verifies that the file system can be displayed by this version
 *	 of the software.
 *
 * arguments
 *	 QHHDR qhhdr	- far pointer to help header structure
 *
 * return value
 *	 TRUE iff valid
 *	 return system help header in qhhdr
 *
 * globals used
 *	 pSysBufRead - The static pointer to where we are in the |SYSTEM buffer
 *
 **************/

static BOOL STDCALL FCheckSystem(QHHDR qhhdr, UINT* pwErr)
{

  /* (kevynct)
   * We read Help 3.0, 3.1 (3.5) or 4.0 files. But certain in-between
   * versions of Help 3.5 files are no longer supported. The format number
   * in the header now indicates a sub-version of a supported version, and
   * so is not checked here.
   */

  // Read in the first field of the HHDR, the Magic number.

  if ((!FReadBufferQch((LPBYTE) &qhhdr->wMagic, sizeof(WORD)))
		|| (qhhdr->wMagic != MagicWord)) {
			*pwErr = RC_BadVersion;
			return FALSE;
  }

  // Read in the rest of the fields, except for those that are new.

  if ((! FReadBufferQch((LPBYTE) &qhhdr->wVersionNo, sizeof(WORD)))
	|| (! FReadBufferQch((LPBYTE) &qhhdr->wVersionFmt, sizeof(WORD)))
	|| (! FReadBufferQch((LPBYTE) &qhhdr->lDateCreated, sizeof(LONG)))
	|| (! FReadBufferQch((LPBYTE) &qhhdr->wFlags, sizeof(WORD)))) {
	  *pwErr = RC_BadVersion;
	  return FALSE;
  }

  /*
   * WARNING: Version dependency: Fix for Help 3.5 bug 488. The Help 3.0
   * and 3.1 compilers do not initialize the wFlags bits. Only the fDebug
   * bit is used.
   */

  if (qhhdr->wVersionNo == wVersion3_0) {
	qhhdr->wFlags &= fDEBUG;
  }

  if ((qhhdr->wMagic != MagicWord)
	  || ((qhhdr->wVersionNo < wVersion3_5)
		&& qhhdr->wVersionNo != wVersion3_0)
	  ) {
	*pwErr = RC_BadVersion;
	return FALSE;
  }

  if (qhhdr->wVersionNo > VersionNo)
	{
	*pwErr = RC_BadVersion;
	goto error_return;
	}

#if 0
  if ((qhhdr->wFlags & fDEBUG) != fVerDebug) {
	*pwErr = wERRS_DEBUGMISMATCH;
	goto error_return;
  }
#endif

  return TRUE;

error_return:
//
//	BUGBUG error reporting.
//	PostErrorMessage(*pwErr);
  return FALSE;
}