//-------------------------------------------------------------------------
// Desc:	Read a record from the database.
// Tabs:	3
//
//		Copyright (c) 1991-2006 Novell, Inc. All Rights Reserved.
//
//		This program is free software; you can redistribute it and/or
//		modify it under the terms of version 2 of the GNU General Public
//		License as published by the Free Software Foundation.
//
//		This program is distributed in the hope that it will be useful,
//		but WITHOUT ANY WARRANTY; without even the implied warranty of
//		MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
//		GNU General Public License for more details.
//
//		You should have received a copy of the GNU General Public License
//		along with this program; if not, contact Novell, Inc.
//
//		To contact Novell about this file by physical or electronic mail,
//		you may find current contact information at www.novell.com
//
// $Id: fsrecget.cpp 12329 2006-01-20 17:49:30 -0700 (Fri, 20 Jan 2006) ahodgkinson $
//-------------------------------------------------------------------------

#include "flaimsys.h"

#define NUM_FIELDS_IN_ARRAY	16

typedef struct Field_State
{
	FLMBYTE *		pElement;			// Points to the element within block
	FLMUINT			uiFieldType;		// Storage field type
	FLMUINT			uiFieldLen;			// Length of storage if known
	FLMUINT			uiPosInElm;			// Position within the element
	FLMUINT			uiTagNum;			// Field tag/data dictionary number
	FLMUINT			uiLevel;				// Current level number
	FLMUINT			uiEncId;				// EncDef ID if encrypted
	FLMUINT			uiEncFieldLen; 	// Encrypted field length
} FSTATE;

typedef struct DATAPIECE
{
	FLMBYTE *		pData;				// Points to data within the block.
	FLMUINT			uiLength;			// Length of this data piece
	DATAPIECE *		pNext;				// Next data piece or NULL
} DATAPIECE;

typedef struct Temporary_Field
{
	FLMUINT			uiLevel;
	FLMUINT			uiFieldID;
	FLMUINT			uiFieldType;
	FLMUINT			uiFieldLen;
	FLMUINT			uiEncId;
	FLMUINT			uiEncFieldLen;
	DATAPIECE		DataPiece;
} TEMPFIELD;

typedef struct FLDGROUP
{
	TEMPFIELD		pFields[ NUM_FIELDS_IN_ARRAY];
	FLDGROUP *		pNext;
} FLDGROUP;

typedef struct LOCKED_BLOCK
{
	SCACHE *			pSCache;
	LOCKED_BLOCK *	pNext;
} LOCKED_BLOCK;

FSTATIC RCODE FSGetFldOverhead(
	FDB *				pDb,
	FSTATE * 		fState);

/****************************************************************************
Desc: Retrieves a record given a DRN
****************************************************************************/
RCODE FSReadRecord(
	FDB *				pDb,
	LFILE *			pLFile,
	FLMUINT			uiDrn,
	FlmRecord **	ppRecord,
	FLMUINT *		puiRecTransId,
	FLMBOOL *		pbMostCurrent)
{
	RCODE				rc = FERR_OK;
	BTSK				stackBuf[BH_MAX_LEVELS];
	BTSK *			pStack = NULL;
	FLMBYTE			pKeyBuf[DIN_KEY_SIZ];
	FLMBYTE			pDrnBuf[DIN_KEY_SIZ];

	FSInitStackCache( &stackBuf[0], BH_MAX_LEVELS);

	pStack = stackBuf;
	pStack->pKeyBuf = pKeyBuf;

	// Search the B-TREE for the record

	f_UINT32ToBigEndian( (FLMUINT32)uiDrn, pDrnBuf);
	if (RC_OK( rc = FSBtSearch( pDb, pLFile, &pStack, pDrnBuf, 4, 0)))
	{
		rc = RC_SET( FERR_NOT_FOUND);
		
		if ((pStack->uiCmpStatus == BT_EQ_KEY) && (uiDrn != DRN_LAST_MARKER))
		{
			rc = FSReadElement( pDb, &pDb->TempPool, pLFile, uiDrn, pStack, TRUE,
									 ppRecord, puiRecTransId, pbMostCurrent);
		}
	}

	FSReleaseStackCache( stackBuf, BH_MAX_LEVELS, FALSE);
	return (rc);
}

/****************************************************************************
Desc: Low-level routine to retrieve and build an internal tree record.
****************************************************************************/
RCODE FSReadElement(
	FDB *					pDb,
	F_Pool *				pPool,
	LFILE *				pLFile,
	FLMUINT				uiDrn,
	BTSK *				pStack,
	FLMBOOL				bOkToPreallocSpace,
	FlmRecord **		ppRecord,
	FLMUINT *			puiRecTransId,
	FLMBOOL *			pbMostCurrent)
{
	RCODE					rc = FERR_OK;
	FlmRecord *			pRecord = NULL;
	FLMBYTE *			pCurElm;
	void *				pvPoolMark = pPool->poolMark();
	FLMUINT				uiElmRecLen;
	FLMUINT				uiFieldLen;
	FLMUINT				uiLowestTransId;
	FLMUINT				uiFieldCount;
	FLMUINT				uiTrueDataSpace;
	FLMUINT				uiFieldPos;
	FLMBOOL				bMostCurrent;
	TEMPFIELD *			pField;
	FLDGROUP *			pFldGroup = NULL;
	FLDGROUP*			pFirstFldGroup = NULL;
	DATAPIECE *			pDataPiece;
	LOCKED_BLOCK *		pLockedBlock = NULL;
	FSTATE				fState;
	FLMUINT				uiEncFieldLen;

	// Initialize variables

	fState.uiLevel = 0;
	uiFieldCount = 0;
	uiTrueDataSpace = 0;
	uiFieldPos = NUM_FIELDS_IN_ARRAY;

	// Check to make sure we are positioned at the first element.

	pCurElm = CURRENT_ELM( pStack);
	if (!BBE_IS_FIRST( pCurElm))
	{
		rc = RC_SET( FERR_DATA_ERROR);
		goto Exit;
	}

	uiLowestTransId = FB2UD( &pStack->pBlk[BH_TRANS_ID]);
	bMostCurrent = (pStack->pSCache->uiHighTransID == 0xFFFFFFFF) 
											? TRUE 
											: FALSE;

	// Loop on each element in the record

	for (;;)
	{

		// Setup all variables to process the current element

		uiElmRecLen = BBE_GET_RL( pCurElm);
		if (uiElmRecLen == 0)
		{
			rc = RC_SET( FERR_EOF_HIT);
			break;
		}

		pCurElm += BBE_REC_OFS( pCurElm);
		fState.uiPosInElm = 0;

		// Loop on each field within this element.

		while (fState.uiPosInElm < uiElmRecLen)
		{
			fState.pElement = pCurElm;
			if (RC_BAD( rc = FSGetFldOverhead( pDb, &fState)))
			{
				goto Exit;
			}

			uiFieldLen = fState.uiFieldLen;
			uiEncFieldLen = fState.uiEncFieldLen;

			// Old record info data - skip past for now

			if (fState.uiTagNum == 0)
			{
				fState.uiPosInElm += (uiEncFieldLen ? uiEncFieldLen : uiFieldLen);
				continue;
			}

			if (!pRecord)
			{

				// Create a new data record or use the existing data record.

				if (*ppRecord)
				{
					if ((*ppRecord)->isReadOnly())
					{
						(*ppRecord)->Release();
						*ppRecord = NULL;

						if ((pRecord = f_new FlmRecord) == NULL)
						{
							rc = RC_SET( FERR_MEM);
							goto Exit;
						}
					}
					else
					{

						// Reuse the existing FlmRecord object.

						pRecord = *ppRecord;
						*ppRecord = NULL;
						pRecord->clear();
					}
				}
				else
				{
					if ((pRecord = f_new FlmRecord) == NULL)
					{
						rc = RC_SET( FERR_MEM);
						goto Exit;
					}
				}

				pRecord->setContainerID( pLFile->uiLfNum);
				pRecord->setID( uiDrn);
				if (pLFile->bMakeFieldIdTable)
				{
					pRecord->enableFieldIdTable();
				}
			}

			// Check if out of fields in the tempoary field group.

			if (uiFieldPos >= NUM_FIELDS_IN_ARRAY)
			{
				FLDGROUP *		pTempFldGroup;

				uiFieldPos = 0;

				// Allocate the first field group from the pool.
				
				if( RC_BAD( rc = pPool->poolAlloc( 
					sizeof( FLDGROUP), (void **)&pTempFldGroup)))
				{
					goto Exit;
				}

				pTempFldGroup->pNext = NULL;
				if (pFldGroup)
				{
					pFldGroup->pNext = pTempFldGroup;
				}
				else
				{
					pFirstFldGroup = pTempFldGroup;
				}

				pFldGroup = pTempFldGroup;
			}

			uiFieldCount++;
			pField = &pFldGroup->pFields[uiFieldPos++];
			pField->uiLevel = fState.uiLevel;
			pField->uiFieldID = fState.uiTagNum;
			pField->uiFieldType = fState.uiFieldType;
			pField->uiFieldLen = fState.uiFieldLen;
			pField->uiEncId = fState.uiEncId;
			pField->uiEncFieldLen = fState.uiEncFieldLen;
			pDataPiece = &pField->DataPiece;

			if (uiFieldLen || uiEncFieldLen)
			{
				FLMUINT	uiDataPos = 0;

				if (fState.uiEncFieldLen)
				{
					uiTrueDataSpace += FLM_ENC_FLD_OVERHEAD;

					// Binary data needs to account for alignment issues.

					if (fState.uiFieldType == FLM_BINARY_TYPE)
					{

						// Adjust for the decrypted data.

						uiTrueDataSpace = ((uiTrueDataSpace + FLM_ALLOC_ALIGN) & 
												 (~(FLM_ALLOC_ALIGN) & 0x7FFFFFFF));
					}

					uiTrueDataSpace += fState.uiFieldLen + fState.uiEncFieldLen;

					// Store the encrypted field length rather than the
					// decrypted field length This will allow the gathering of
					// the encrypted or decrypted field data to use the same
					// code.

					uiFieldLen = uiEncFieldLen;
				}
				else if (fState.uiFieldLen > 4)
				{

					// Binary data needs to account for alignment issues.

					if (fState.uiFieldType == FLM_BINARY_TYPE)
					{
						if (fState.uiFieldLen >= 0xFF)
						{

							// Align so that the data is aligned - not the length

							uiTrueDataSpace += sizeof(FLMUINT32);
							uiTrueDataSpace = ((uiTrueDataSpace + FLM_ALLOC_ALIGN) &
														(~(FLM_ALLOC_ALIGN) & 0x7FFFFFFF));
							uiTrueDataSpace -= sizeof(FLMUINT32);
						}
						else
						{
							uiTrueDataSpace = ((uiTrueDataSpace + FLM_ALLOC_ALIGN) &
														(~(FLM_ALLOC_ALIGN) & 0x7FFFFFFF));
						}
					}

					uiTrueDataSpace += fState.uiFieldLen;

					// Field values with lengths greater than 255 bytes are
					// stored length-preceded. A single byte flags field
					// precedes the length.

					if (fState.uiFieldLen >= 0xFF)
					{
						uiTrueDataSpace += sizeof(FLMUINT32) + 1;
					}
				}

				// Value may start in the next element.

				while (uiDataPos < uiFieldLen)
				{

					// Need to read next element for the value portion?

					if (fState.uiPosInElm >= uiElmRecLen)
					{
						if (BBE_IS_LAST( CURRENT_ELM( pStack)))
						{
							rc = RC_SET( FERR_DATA_ERROR);
							goto Exit;
						}

						// If we are going to the next block, lock down this
						// block because data pointers are pointing to it.

						if (RC_BAD( FSBlkNextElm( pStack)))
						{
							LOCKED_BLOCK *	pLastLockedBlock = pLockedBlock;

							if( RC_BAD( rc = pPool->poolAlloc( sizeof( LOCKED_BLOCK),
								(void **)&pLockedBlock)))
							{
								goto Exit;
							}
							
							ScaHoldCache( pStack->pSCache);
							pLockedBlock->pSCache = pStack->pSCache;
							pLockedBlock->pNext = pLastLockedBlock;

							if (RC_BAD( rc = FSBtNextElm( pDb, pLFile, pStack)))
							{
								rc = (rc == FERR_BT_END_OF_DATA) 
												? RC_SET( FERR_DATA_ERROR) 
												: rc;
								goto Exit;
							}

							if (uiLowestTransId > FB2UD( &pStack->pBlk[BH_TRANS_ID]))
							{
								uiLowestTransId = FB2UD( &pStack->pBlk[BH_TRANS_ID]);
							}

							if (!bMostCurrent)
							{
								bMostCurrent = 
									(pStack->pSCache->uiHighTransID == 0xFFFFFFFF) 
												? TRUE 
												: FALSE;
							}
						}

						pCurElm = CURRENT_ELM( pStack);
						uiElmRecLen = BBE_GET_RL( pCurElm);
						pCurElm += BBE_REC_OFS( pCurElm);
						fState.uiPosInElm = 0;
					}

					// Compare number of bytes left if value <= # bytes left
					// in element

					if (uiFieldLen - uiDataPos <= uiElmRecLen - fState.uiPosInElm)
					{
						FLMUINT	uiDelta = uiFieldLen - uiDataPos;

						pDataPiece->pData = &pCurElm[fState.uiPosInElm];
						pDataPiece->uiLength = uiDelta;
						fState.uiPosInElm += uiDelta;
						pDataPiece->pNext = NULL;
						break;
					}
					else
					{

						// Take what is there and get next element to grab some
						// more.

						FLMUINT			uiBytesToMove = uiElmRecLen - fState.uiPosInElm;
						DATAPIECE *		pNextDataPiece;

						pDataPiece->pData = &pCurElm[fState.uiPosInElm];
						pDataPiece->uiLength = uiBytesToMove;
						fState.uiPosInElm += uiBytesToMove;
						uiDataPos += uiBytesToMove;

						if( RC_BAD( rc = pPool->poolAlloc( sizeof( DATAPIECE),
							(void **)&pNextDataPiece)))
						{
							goto Exit;
						}

						pDataPiece->pNext = pNextDataPiece;
						pDataPiece = pNextDataPiece;
					}
				}
			}
		}

		// Done?

		if (BBE_IS_LAST( CURRENT_ELM( pStack)))
		{
			break;
		}

		// Position to next element

		if (RC_BAD( FSBlkNextElm( pStack)))
		{
			LOCKED_BLOCK *		pLastLockedBlock = pLockedBlock;
			
			if( RC_BAD( rc = pPool->poolAlloc( sizeof( LOCKED_BLOCK),
				(void **)&pLockedBlock)))
			{
				goto Exit;
			}

			ScaHoldCache( pStack->pSCache);
			pLockedBlock->pSCache = pStack->pSCache;
			pLockedBlock->pNext = pLastLockedBlock;

			if (RC_BAD( rc = FSBtNextElm( pDb, pLFile, pStack)))
			{
				if (rc == FERR_BT_END_OF_DATA)
				{
					rc = RC_SET( FERR_DATA_ERROR);
				}

				goto Exit;
			}

			if (uiLowestTransId > FB2UD( &pStack->pBlk[BH_TRANS_ID]))
			{
				uiLowestTransId = FB2UD( &pStack->pBlk[BH_TRANS_ID]);
			}

			if (!bMostCurrent)
			{
				bMostCurrent = (pStack->pSCache->uiHighTransID == 0xFFFFFFFF) 
												? TRUE 
												: FALSE;
			}
		}

		// Corruption Check.

		pCurElm = CURRENT_ELM( pStack);
		if (BBE_IS_FIRST( pCurElm))
		{
			rc = RC_SET( FERR_DATA_ERROR);
			goto Exit;
		}
	}

	if (pRecord)
	{
		void *		pvField;

		if (bOkToPreallocSpace)
		{
			if (RC_BAD( rc = pRecord->preallocSpace( 
				uiFieldCount, uiTrueDataSpace)))
			{
				goto Exit;
			}
		}

		pFldGroup = pFirstFldGroup;

		for (uiFieldPos = 0; uiFieldCount--; uiFieldPos++)
		{
			if (uiFieldPos >= NUM_FIELDS_IN_ARRAY)
			{
				uiFieldPos = 0;
				if ((pFldGroup = pFldGroup->pNext) == NULL)
				{
					break;
				}
			}

			pField = &pFldGroup->pFields[uiFieldPos];

			if (RC_BAD( rc = pRecord->insertLast( pField->uiLevel,
						  pField->uiFieldID, pField->uiFieldType, &pvField)))
			{
				goto Exit;
			}

			if (pField->uiFieldLen)
			{
				FLMBYTE *		pDataPtr;
				FLMBYTE *		pEncDataPtr;

				pDataPiece = &pField->DataPiece;
				if (RC_BAD( rc = pRecord->allocStorageSpace( pvField,
							  pField->uiFieldType, pField->uiFieldLen,
							  pField->uiEncFieldLen, pField->uiEncId,
							  (pField->uiEncId ? FLD_HAVE_ENCRYPTED_DATA : 0),
							  &pDataPtr, &pEncDataPtr)))
				{
					goto Exit;
				}

				do
				{
					if (pField->uiEncId)
					{
						f_memcpy( pEncDataPtr, pDataPiece->pData, pDataPiece->uiLength);
						pEncDataPtr += pDataPiece->uiLength;
					}
					else
					{
						f_memcpy( pDataPtr, pDataPiece->pData, pDataPiece->uiLength);
						pDataPtr += pDataPiece->uiLength;
					}

					pDataPiece = pDataPiece->pNext;
				} while (pDataPiece);

				// If the field is encrypted, we must decrypt it here.

				if (pField->uiEncId && !pDb->pFile->bInLimitedMode)
				{
					if (RC_BAD( rc = flmDecryptField( pDb->pDict, pRecord, pvField,
								  pField->uiEncId, &pDb->TempPool)))
					{
						goto Exit;
					}
				}
			}
		}
	}

	if (puiRecTransId)
	{
		*puiRecTransId = uiLowestTransId;
	}

	if (pbMostCurrent)
	{
		*pbMostCurrent = bMostCurrent;
	}

	if (*ppRecord)
	{
		flmAssert( 0);
		(*ppRecord)->Release();
	}
	
	pRecord->sortFieldIdTable();

	*ppRecord = pRecord;
	pRecord = NULL;

Exit:

	// Release all locked down blocks except the current block.

	while (pLockedBlock)
	{
		ScaReleaseCache( pLockedBlock->pSCache, FALSE);
		pLockedBlock = pLockedBlock->pNext;
	}

	pPool->poolReset( pvPoolMark);

	if (pRecord)
	{
		pRecord->Release();
	}

	// You are now positioned to the last element in the record

	return (rc);
}

/****************************************************************************
Desc: Read field overhead (level, field drn, and length information. This
		isolates the complexity of the storage formats.
****************************************************************************/
FSTATIC RCODE FSGetFldOverhead(
	FDB *				pDb,
	FSTATE *			fState)
{
	RCODE				rc = FERR_OK;
	FLMBYTE *		pFieldOvhd = &fState->pElement[fState->uiPosInElm];
	FLMBYTE *		pElement = fState->pElement;
	FLMBYTE *		pTmp;
	FLMBOOL			bDoesntHaveFieldDef = TRUE;
	FLMUINT			uiFieldLen;
	FLMUINT			uiFieldType = 0;
	FLMUINT			uiTagNum;
	FLMBYTE			ucBaseFlags;
	FLMUINT			uiEncId = 0;
	FLMUINT			uiEncFieldLen = 0;

	if (FOP_IS_STANDARD( pFieldOvhd))
	{
		if (FSTA_LEVEL( pFieldOvhd))
		{
			fState->uiLevel++;
		}

		uiFieldLen = FSTA_FLD_LEN( pFieldOvhd);
		uiTagNum = FSTA_FLD_NUM( pFieldOvhd);
		pFieldOvhd += FSTA_OVHD;
	}
	else if (FOP_IS_OPEN( pFieldOvhd))
	{
		if (FOPE_LEVEL( pFieldOvhd))
		{
			fState->uiLevel++;
		}

		ucBaseFlags = (FLMBYTE) (FOP_GET_FLD_FLAGS( pFieldOvhd++));
		uiTagNum = (FLMUINT) * pFieldOvhd++;

		if (FOP_2BYTE_FLDNUM( ucBaseFlags))
		{
			uiTagNum += ((FLMUINT) * pFieldOvhd++) << 8;
		}

		uiFieldLen = (FLMUINT) * pFieldOvhd++;
		if (FOP_2BYTE_FLDLEN( ucBaseFlags))
		{
			uiFieldLen += ((FLMUINT) * pFieldOvhd++) << 8;
		}
	}
	else if (FOP_IS_NO_VALUE( pFieldOvhd))
	{
		if (FNOV_LEVEL( pFieldOvhd))
		{
			fState->uiLevel++;
		}

		ucBaseFlags = (FLMBYTE) (FOP_GET_FLD_FLAGS( pFieldOvhd++));
		uiTagNum = (FLMUINT) * pFieldOvhd++;
		if (FOP_2BYTE_FLDNUM( ucBaseFlags))
		{
			uiTagNum += ((FLMUINT) * pFieldOvhd++) << 8;
		}

		uiFieldLen = uiFieldType = 0;
	}
	else if (FOP_IS_SET_LEVEL( pFieldOvhd))
	{

		// SET THE LEVEL Must be continuous with the next field

		fState->uiLevel -= FSLEV_GET( pFieldOvhd++);
		fState->uiPosInElm = (FLMUINT) (pFieldOvhd - pElement);

		rc = FSGetFldOverhead( pDb, fState);
		goto Exit;
	}
	else if (FOP_IS_TAGGED( pFieldOvhd))
	{
		bDoesntHaveFieldDef = FALSE;

		if (FTAG_LEVEL( pFieldOvhd))
		{
			fState->uiLevel++;
		}

		ucBaseFlags = (FLMBYTE) (FOP_GET_FLD_FLAGS( pFieldOvhd));
		pFieldOvhd++;
		uiFieldType = (FLMUINT) (FTAG_GET_FLD_TYPE( *pFieldOvhd));
		pFieldOvhd++;
		uiTagNum = (FLMUINT) * pFieldOvhd++;

		if (FOP_2BYTE_FLDNUM( ucBaseFlags))
		{
			uiTagNum += ((FLMUINT) * pFieldOvhd++) << 8;
		}

		// When storing the unregistered fields we cleared the high bit to
		// save on storage for VER11. The problem is that if a tag that is
		// not in the unregistered range (FLAIM TAGS) cannot be represented.
		// SO, we will XOR the high bit so 0x0111 is stored as 0x8111 and
		// 0x8222 is stored as 0x0222.

		uiTagNum ^= 0x8000;
		uiFieldLen = (FLMUINT) * pFieldOvhd++;
		if (FOP_2BYTE_FLDLEN( ucBaseFlags))
		{
			uiFieldLen += ((FLMUINT) * pFieldOvhd++) << 8;
		}
	}
	else if (FOP_IS_RECORD_INFO( pFieldOvhd))
	{
		bDoesntHaveFieldDef = FALSE;
		ucBaseFlags = *pFieldOvhd++;
		uiFieldLen = *pFieldOvhd++;

		if (FOP_2BYTE_FLDLEN( ucBaseFlags))
		{
			uiFieldLen += ((FLMUINT) * pFieldOvhd++) << 8;
		}

		uiTagNum = 0;
	}
	else if (FOP_IS_ENCRYPTED( pFieldOvhd))
	{
		FLMBOOL	bTagSz;
		FLMBOOL	bLenSz;
		FLMBOOL	bENumSz;
		FLMBOOL	bELenSz;

		if (pDb->pFile->FileHdr.uiVersionNum < FLM_FILE_FORMAT_VER_4_60)
		{
			rc = RC_SET( FERR_DATA_ERROR);
			goto Exit;
		}

		bDoesntHaveFieldDef = FALSE;

		if (FENC_LEVEL( pFieldOvhd))
		{
			fState->uiLevel++;
		}

		uiFieldType = (FLMUINT) (FENC_FLD_TYPE( pFieldOvhd));
		bTagSz = FENC_TAG_SZ( pFieldOvhd);
		bLenSz = FENC_LEN_SZ( pFieldOvhd);
		bENumSz = FENC_ETAG_SZ( pFieldOvhd);
		bELenSz = FENC_ELEN_SZ( pFieldOvhd);

		pFieldOvhd += 2;

		uiTagNum = (FLMUINT) * pFieldOvhd++;
		if (bTagSz)
		{
			uiTagNum += ((FLMUINT) * pFieldOvhd++) << 8;
		}

		uiFieldLen = (FLMUINT) * pFieldOvhd++;
		if (bLenSz)
		{
			uiFieldLen += ((FLMUINT) * pFieldOvhd++) << 8;
		}

		uiEncId = (FLMUINT) * pFieldOvhd++;
		if (bENumSz)
		{
			uiEncId += ((FLMUINT) * pFieldOvhd++) << 8;
		}

		uiEncFieldLen = (FLMUINT) * pFieldOvhd++;
		if (bELenSz)
		{
			uiEncFieldLen += ((FLMUINT) * pFieldOvhd++) << 8;
		}
	}
	else if (FOP_IS_LARGE( pFieldOvhd))
	{
		if (pDb->pFile->FileHdr.uiVersionNum < FLM_FILE_FORMAT_VER_4_61)
		{
			rc = RC_SET( FERR_DATA_ERROR);
			goto Exit;
		}

		bDoesntHaveFieldDef = FALSE;
		pTmp = pFieldOvhd;

		if (FLARGE_LEVEL( pFieldOvhd))
		{
			fState->uiLevel++;
		}

		pTmp++;

		uiFieldType = FLARGE_FLD_TYPE( pFieldOvhd);
		pTmp++;

		uiTagNum = FLARGE_TAG_NUM( pFieldOvhd);
		pTmp += 2;

		if ((uiFieldLen = FLARGE_DATA_LEN( pFieldOvhd)) <= 0x0000FFFF)
		{
			flmAssert( 0);
			rc = RC_SET( FERR_DATA_ERROR);
			goto Exit;
		}

		pTmp += 4;

		if (FLARGE_ENCRYPTED( pFieldOvhd))
		{
			uiEncId = FLARGE_ETAG_NUM( pFieldOvhd);
			pTmp += 2;

			uiEncFieldLen = FLARGE_EDATA_LEN( pFieldOvhd);
			pTmp += 4;
		}

		pFieldOvhd = pTmp;
	}
	else
	{
		flmAssert( 0);
		rc = RC_SET( FERR_DATA_ERROR);
		goto Exit;
	}

	if (bDoesntHaveFieldDef)
	{

		// Get the field's storage type.

		if (RC_BAD( fdictGetField( pDb->pDict, uiTagNum, 
				&uiFieldType, NULL, NULL)))
		{
			rc = RC_SET( FERR_DATA_ERROR);
			goto Exit;
		}
	}

	// Set the fState return values

	fState->uiFieldType = uiFieldType;
	fState->uiFieldLen = uiFieldLen;
	fState->uiPosInElm = (FLMUINT) (pFieldOvhd - pElement);
	fState->uiTagNum = uiTagNum;
	fState->uiEncId = uiEncId;
	fState->uiEncFieldLen = uiEncFieldLen;

Exit:

	return (rc);
}
