/************************************************************************
*                                                                       *
*                               HUGEARR.DLL                             *
*                                                                       *
*               Huge array support for Microsoft Visual Basic           *
*                                                                       *
*                             By Mike Warning                           *
*									*
*   Modifications by Jonathan Zuck.					*
*   Modifications by Stephen Schmidt.					*
*   Modifications by End User Computing, Ltd.				*
*                                                                       *
************************************************************************/

#include <memory.h>
/* Define NOCOMM so that MSC compiler does not generate messages using Warning Level 4. */
#define NOCOMM
#include <windows.h>


struct ArrayDesc  {
    HANDLE  handle;   /* handle to global memory array */
    int     recsize;  /* record size of array */
    long    ubound;   /* upper bound of array */
    int     perseg;   /* #elements per segment */
    int     redim;    /* whether or not GlobalRealloc was successful */
    long    bsize;    /* actual size in bytes of memory block allocated for array element storage */
};

typedef struct ArrayDesc *pDescrip;
typedef double currency;  /* currency and double are the same size and will be treated the same */

/* All of the C comments which begin with "VBI:" or "VBB:" are placed into the files HUGEARR.VBI
   and HUGEARR.BAS. */
/* VBI: ' An alternate set of definitions with more consistent names that you may want */
/* VBI: ' to consider using instead of those in HUGEARR.BAS. */
/* VBI:  Global Const HA_OK              = 0 */
/* VBI:  Global Const HA_OUTOFMEMORY    = -1 */
/* VBI:  Global Const HA_TOMANYARRAYS   = -2 */
/* VBI:  Global Const HA_BADELEMENTSIZE = -3  ' no longer possible; left for backward compatibility. */
/* VBI:  Global Const HA_SUBSCRIPT      = -4 */
/* VBI:  Global Const HA_BADARRAY       = -5 */
/* VBI:  Global Const HA_FILEOPENERROR  = -7 */
/* VBI:  Global Const HA_FILEWRITEERROR = -8 */
/* VBI:  Global Const HA_FILEREADERROR  = -9 */

/* VBB: ' The original set of definitions. */
/* VBB:  Global Const HA_OK              = 0 */
/* VBB:  Global Const HA_OUTOFMEMORY    = -1 */
/* VBB:  Global Const HA_TOMANYARRAYS   = -2 */
/* VBB:  Global Const HA_BADELEMENTSIZE = -3  ' no longer possible; left for backward compatibility. */
/* VBB:  Global Const HA_SUBSCRIPT      = -4 */
/* VBB:  Global Const HA_BADARRAY       = -5 */
/* VBB:  Global Const HA_FILEOPENERROR  = -7 */
/* VBB:  Global Const HA_FILEWRITEERROR = -8 */
/* VBB:  Global Const HA_FILEREADERROR  = -9 */

#define HA_OK              0
#define HA_OUTOFMEMORY    -1
#define HA_TOMANYARRAYS   -2
#define HA_BADELEMENTSIZE -3  /* no longer possible; left for backward compatibility */
#define HA_SUBSCRIPT      -4
#define HA_BADARRAY       -5
#define HA_FILEOPENERROR  -7
#define HA_FILEWRITEERROR -8
#define HA_FILEREADERROR  -9


HANDLE hLocalMem;  /* handle to local memory */
int    NumArrays;  /* total number of arrays */

/************************************************************************
* LibMain -                                                             *
*   Standard DLL constructor.  Allocates all of local heap to store     *
* array descriptors and then set the total number of arrays possible.   *
************************************************************************/
int FAR pascal _export
LibMain(HANDLE hModule, WORD wDataSeg, WORD cbHeapSize, LPSTR lpszCmdLine)
	{
	if (cbHeapSize > 0)
		UnlockData(0);

	/* Allocate memory for array descrips. */
	hLocalMem = LocalAlloc(LMEM_MOVEABLE | LMEM_ZEROINIT, LocalCompact((WORD) 65500));
	if (hLocalMem == NULL)
		/* Something happened, bomb out */
		return 0;

	/* calc total number of arrays */
	NumArrays = (int) (LocalSize(hLocalMem) / sizeof(struct ArrayDesc));

	return 1;
	}

/************************************************************************
* WEP -                                                                 *
*   Standard DLL destructor.  Free up local memory and quit.            *
************************************************************************/
int FAR pascal _export
WEP(int bSystemExit)
	{
	LocalFree(hLocalMem);
	return 1;
	}

/********************************************************************
* GetFreeArray -                                                    *
*   Searches the array descriptor table looking for a free entry.   *
* It returns the index into the table if an entry is free or        *
* HA_TOMANYARRAYS otherwise.  pArray is the pointer to the start of *
* the table.                                                        *
********************************************************************/
int
GetFreeArray(pDescrip pArray)
	{
	int i = 0;

	/* loop until found or out of entries */
	while (i < NumArrays && pArray -> handle != NULL)
		{
		++pArray;
		++i;
		}

	if (i == NumArrays)
		/* didn't find a spot */
		return HA_TOMANYARRAYS;

	/* found one, return index to it */
	return i;
	}

#define HugeElementOffset(ELEMNO, PERSEG, RECSIZE)	\
	  ELEMNO / (long) PERSEG * 0x10000L		\
	+ ELEMNO % (long) PERSEG * (long) RECSIZE

/********************************************************************
*   HugeAlloc -                                                     *
* Allocates (or reallocates) global memory for an array.  The size  *
* of the array is recsize * (ubound + 1) bytes.  The information    *
* for the array is stored in the array descriptor pointed to by     *
* 'pArray'.                                                         *
********************************************************************/
int
HugeAlloc(pDescrip pArray, int recsize, long ubound, BOOL realloc)
	{
	int    perseg;  /* #elements per segment */
	long   newsiz;  /* actual size of array */
	HANDLE handle;  /* temp handle for alloc */

	/* ubound = #elements - 1 */
	++ubound;

	/* #elements per segment. */
	perseg = (int) (0x10000L / (long) recsize);
	/* size of array in bytes */
        newsiz = HugeElementOffset(ubound, perseg, recsize);

	if (!realloc)
		/* allocate a new array */
		handle = GlobalAlloc(GMEM_MOVEABLE || GMEM_ZEROINIT, newsiz);
	else
#ifndef REDIMUAES
		/* attempt to reallocate the existing array */
		/* TO HUGEARR USERS RECEIVING UAE'S DURING CALLS TO HugeRedim:  Define REDIMUAES somehow then recompile. */
		if ((handle = GlobalReAlloc(pArray -> handle, newsiz, GMEM_MOVEABLE || GMEM_ZEROINIT)) != NULL)
			pArray -> redim = TRUE;
		else
#endif
			/* existing array could not be reallocated--attempt to create a new one */
			if ((handle = GlobalAlloc(GMEM_MOVEABLE || GMEM_ZEROINIT, newsiz)) != NULL)
				/* new array to replace the existing one was allocated okay */
				{
				BYTE _huge *FmBegin;
				BYTE _huge *ToBegin;
				long        numcopy;  /* minimum of #elements in old & new array */
				long        curelem;  /* current element during copy */
				long        ElemOff;  /* offset of current elements for copy */

				/* get pointers to the beginning of the existing and new arrays */
				FmBegin = (BYTE _huge *) GlobalLock(pArray -> handle);
				ToBegin = (BYTE _huge *) GlobalLock(handle);
				/* size of original array is pArray->ubound + 1; size of new one is ubound */
				numcopy  = (ubound < pArray->ubound + 1L) ? ubound : pArray->ubound + 1L;

				/* copy each element from old array to new array, up to the min. # of elements in both arrays. */
				for (curelem = 0; curelem < numcopy; curelem++)
					{
					/* byte offset of current numbered element from beginning of memory block */
					ElemOff = HugeElementOffset(curelem, perseg, recsize);

					_fmemcpy(ToBegin + ElemOff, FmBegin + ElemOff, recsize);
					}

				GlobalUnlock(handle);
				GlobalUnlock(pArray -> handle);
				/* free old array */
				GlobalFree(pArray -> handle);
				pArray -> redim = FALSE;
				}

	if (handle == NULL)
		/* out of memory */
		return HA_OUTOFMEMORY;

	pArray -> bsize = GlobalSize(handle);
	/* save new handle */
	pArray -> handle = handle;
	/* save element size */
	pArray -> recsize = recsize;
	/* ubound = #elements - 1 */
	pArray -> ubound = ubound - 1;
	pArray -> perseg = perseg;

	return HA_OK;
	}

/************************************************************************
* HugeDim -                                                             *
*   Dimensions a new array.  The size of the array is                   *
* (recsize * ubound+1). Recsize is the size in bytes of each element in *
* the array and ubound is the upper bound of the array. All arrays have *
* a lower bound of 0.                                                   *
************************************************************************/
/* VBI:  Declare Function MSHugeDim% Lib "hugearr.dll" Alias "HugeDim" (ByVal recsize%, ByVal limit&) */
/* VBB:  Declare Function HugeDim% Lib "hugearr.dll" (ByVal recsize%, ByVal limit&) */
int FAR pascal _export
HugeDim(int recsize, long ubound)
	{
	int      hArray;  /* handle to array to dimension */
	pDescrip pArray;  /* pointer to array descriptor */
	int      ret;     /* return value from HugeAlloc */

	pArray = (pDescrip) LocalLock(hLocalMem);

	/* find a free array */
	if ((hArray = GetFreeArray(pArray)) == HA_TOMANYARRAYS)
		{
		/* couldn't find one, return error.*/
		LocalUnlock(hLocalMem);
		return HA_TOMANYARRAYS;
		}

	/* allocate new array */
	ret = HugeAlloc(pArray + hArray, recsize, ubound, FALSE);
	LocalUnlock(hLocalMem);

        /* if an error occured during alloc */
	if (ret < 0)
		/* return the error, else */
		return ret;
	else
		/* return the handle to the array */
		/* VB users think handles start at 1 */
		return hArray + 1;
	}

#define DecCheckHandle 					\
	/* VB users think hArray begins at 1 */		\
	if (--hArray < 0 || hArray >= NumArrays)	\
		/* illegal array handle */		\
		return HA_BADARRAY;

#define CheckNotAllocYet				\
	if (pArray -> handle == NULL)			\
		{					\
		/* array hasn't been allocated */	\
		LocalUnlock(hLocalMem);			\
		return HA_BADARRAY;			\
		}

#define CheckSubscript(MAX)				\
	if (pArray -> ubound < MAX || element < 0)	\
		{					\
		/* subscript out of range */		\
		LocalUnlock(hLocalMem);			\
		return HA_SUBSCRIPT;			\
		}

/************************************************************************
* HugeRedim -                                                           *
*   Redimenions the given array to have the new 'ubound'.  The old      *
* recsize is kept.  All data in the array is preserved and any new data *
* (created by expanding the array) is initialized to 0.                 *
************************************************************************/
/* VBI:  Declare Function MSHugeRedim% Lib "hugearr.dll" Alias "HugeRedim" (ByVal hArray%, ByVal limit&) */
/* VBB:  Declare Function HugeRedim% Lib "hugearr.dll" (ByVal hArray%, ByVal limit&) */
int FAR pascal _export
HugeRedim(int hArray, long ubound)
	{
	pDescrip pArray;  /* pointer to array desciptor */
	register ret;     /* return code of HugeAlloc */

	DecCheckHandle

	pArray = (pDescrip) LocalLock(hLocalMem) + hArray;

	if (pArray -> handle != NULL)
		/* reallocate array */
		ret = HugeAlloc(pArray, pArray -> recsize, ubound, TRUE);
	else
		/* array has never been allocated */
		ret = HA_BADARRAY;

	LocalUnlock(hLocalMem);
	return ret;
	}

/********************************************************************
* GetHugeEl -                                                       *
*   Retrieves an element of the array storing it into the buffer    *
* pointed to by 'buffer'.  hArray is the index into the descriptor  *
* table of the array, element is the element to get.                *
*                                                                   *
* NOTE: there is absolutely no type checking done on the buffer.    *
*       It is up to the programmer to make sure it points to the    *
*       correct data type.                                          *
********************************************************************/
/* VBI:  Declare Function MSHugeGet% Lib "hugearr.dll" Alias "GetHugeEl" (ByVal Index%, ByVal el&, buffer As Any) */
/* VBB:  Declare Function GetHugeEl% Lib "hugearr.dll" (ByVal Index%, ByVal el&, buffer As Any) */
int FAR pascal _export
GetHugeEl(int hArray, long element, BYTE FAR *buffer)
	{
	BYTE _huge *ptr;    /* pointer to array element */
	pDescrip   pArray;  /* pointer to array descriptor */

	DecCheckHandle

	/* point to proper descriptor */
	pArray = (pDescrip) LocalLock(hLocalMem) + hArray;

	CheckNotAllocYet
	CheckSubscript(element)

	/* calculate pointer to element */
	ptr = (BYTE _huge *) GlobalLock(pArray -> handle);

	/* add offset of element */
	ptr += HugeElementOffset(element, pArray->perseg, pArray->recsize);

	/* copy data */
	_fmemcpy(buffer, ptr, pArray -> recsize);

	GlobalUnlock(pArray -> handle);
	LocalUnlock(hLocalMem);
	return HA_OK;
	}

/* Same as GetHugeEl, except that it can copy multiple elements at one time. */
/* VBI:  Declare Function MSHugeGetNum% Lib "hugearr.dll" Alias "GetHugeNEl" (ByVal Index%, ByVal el&, ByVal nelem%, buffer As Any) */
/* VBB:  Declare Function GetHugeNEl% Lib "hugearr.dll" (ByVal Index%, ByVal el&, ByVal nelem%, buffer As Any) */
int FAR pascal _export
GetHugeNEl(int hArray, long element, int nelem, BYTE FAR *buffer)
	{
	BYTE _huge *ToBegin;
	BYTE _huge *ToElem;   /* pointer to array element */
	pDescrip   pArray;    /* pointer to array descriptor */

	DecCheckHandle

	/* point to proper descriptor */
	pArray = (pDescrip) LocalLock(hLocalMem) + hArray;

	CheckNotAllocYet
	CheckSubscript(element + nelem - 1)

	/* calculate pointer to element */
	ToBegin = (BYTE _huge *) GlobalLock(pArray -> handle);

	while (nelem-- > 0)
		{
		ToElem = ToBegin + HugeElementOffset(element, pArray->perseg, pArray->recsize);

		/* copy one element of data */
		_fmemcpy(buffer, ToElem, pArray -> recsize);

		/* increment pointer to VB array to point to next element. */
		buffer += pArray -> recsize;
		element ++;
		}

	GlobalUnlock(pArray -> handle);
	LocalUnlock(hLocalMem);
	return HA_OK;
	}

/********************************************************************
* SetHugeEl -                                                       *
*   Sets the value of an array element.  This routine is exactly    *
* the same as 'GetHugeEl' except that the memory copy is resversed. *
********************************************************************/
/* VBI:  Declare Function MSHugeSet% Lib "hugearr.dll" Alias "SetHugeEl" (ByVal Index%, ByVal el&, buffer As Any) */
/* VBB:  Declare Function SetHugeEl% Lib "hugearr.dll" (ByVal Index%, ByVal el&, buffer As Any) */
int FAR pascal _export
SetHugeEl(int hArray, long element, BYTE FAR *buffer)
	{
	BYTE _huge *ptr;    /* pointer to array element */
	pDescrip   pArray;  /* pointer to array descriptor */

	DecCheckHandle

	/* point to proper descriptor */
	pArray = (pDescrip) LocalLock(hLocalMem) + hArray;

	CheckNotAllocYet
	CheckSubscript(element)

	/* calculate pointer to element */
	ptr = (BYTE _huge *) GlobalLock(pArray -> handle);

	/* add offset of element */
	ptr += HugeElementOffset(element, pArray->perseg, pArray->recsize);

	/* copy data */
	_fmemcpy(ptr, buffer, pArray -> recsize);

	GlobalUnlock(pArray -> handle);
	LocalUnlock(hLocalMem);
	return HA_OK;
	}

/* Same as SetHugeEl, except that it can copy multiple elements at one time. */
/* VBI:  Declare Function MSHugeSetNum% Lib "hugearr.dll" Alias "SetHugeNEl" (ByVal Index%, ByVal el&, ByVal nelem%, buffer As Any) */
/* VBB:  Declare Function SetHugeNEl% Lib "hugearr.dll" (ByVal Index%, ByVal el&, ByVal nelem%, buffer As Any) */
int FAR pascal _export
SetHugeNEl(int hArray, long element, int nelem, BYTE FAR *buffer)
	{
	BYTE _huge *ToBegin;
	BYTE _huge *ToElem;   /* pointer to array element */
	pDescrip   pArray;    /* pointer to array descriptor */

	DecCheckHandle

	/* point to proper descriptor */
	pArray = (pDescrip) LocalLock(hLocalMem) + hArray;

	CheckNotAllocYet
	CheckSubscript(element + nelem - 1)

	/* calculate pointer to element */
	ToBegin = (BYTE _huge *) GlobalLock(pArray -> handle);

	while (nelem-- > 0)
		{
		ToElem = ToBegin + HugeElementOffset(element, pArray->perseg, pArray->recsize);

		/* copy one element of data */
		_fmemcpy(ToElem, buffer, pArray -> recsize);

		/* increment pointer to VB array to point to next element. */
		buffer += pArray -> recsize;
		element ++;
		}

	GlobalUnlock(pArray -> handle);
	LocalUnlock(hLocalMem);
	return HA_OK;
	}

/********************************************************************
* HugeErase -                                                       *
*   Deletes an array and marks it as free in the descriptor table.  *
* 'hArray' is the array to erase.                                   *
********************************************************************/
/* VBI:  Declare Function MSHugeErase% Lib "hugearr.dll" Alias "HugeErase" (ByVal hArray%) */
/* VBB:  Declare Function HugeErase% Lib "hugearr.dll" (ByVal hArray%) */
int FAR pascal _export
HugeErase(int hArray)
	{
	pDescrip pArray;  /* pointer to array descriptor */

	DecCheckHandle

	pArray = (pDescrip) LocalLock(hLocalMem) + hArray;

	CheckNotAllocYet

	/* free the memory */
	GlobalFree(pArray -> handle);
	pArray -> handle = NULL;

	LocalUnlock(hLocalMem);
	return HA_OK;
	}

/********************************************************************
* NumHugeArrays -                                                   *
*   Returns the number of free entries in the array descriptor table*
********************************************************************/
/* VBI:  Declare Function MSHugeNumArrays% Lib "hugearr.dll" Alias "NumHugeArrays" () */
/* VBB:  Declare Function NumHugeArrays% Lib "hugearr.dll" () */
int FAR pascal _export
NumHugeArrays(void)
	{
	pDescrip pArray;  /* pointer to current descriptor */
	int num, i;       /* number free so far */

	pArray = (pDescrip) LocalLock(hLocalMem);
	for (i = 0, num = 0; i < NumArrays; i++, pArray++)
		if (pArray -> handle == NULL)
			++num;

	LocalUnlock(hLocalMem);
	return num;
	}


/********************************************************************
* HugeUbound -                                                      *
*   Returns the upper bound of a given array                        *
********************************************************************/
/* VBI:  Declare Function MSHugeUbound& Lib "hugearr.dll" Alias "HugeUBound" (ByVal hArray%) */
/* VBB:  Declare Function HugeUbound& Lib "hugearr.dll" (ByVal hArray%) */
long FAR pascal _export
HugeUbound(int hArray)
	{
	pDescrip pArray;  /* pointer to array descriptor */
	long ubound;      /* upper bound of array */

	DecCheckHandle

	pArray = (pDescrip) LocalLock(hLocalMem) + hArray;

	CheckNotAllocYet

	ubound = pArray -> ubound;
	LocalUnlock(hLocalMem);

	return ubound;
	}


/********************************************************************
* HugeInt -                                                         *
*   Same as GetHugeEl except that it explicitly returns an integer. *
* Use this function when you are in an expression such as:          *
*           i% = 5 * HugeInt(4, 5)                                  *
*                                                                   *
* NOTE: Because the user could store anything in the array element, *
*       they will not know if the value returned is a real value or *
*       if an error occured.  Use GetHugeEl if the error code must  *
*       be checked.                                                 *
********************************************************************/
/* VBI:  Declare Function MSHugeGetInt% Lib "hugearr.dll" Alias "HugeInt" (ByVal hArray%, ByVal el&) */
/* VBB:  Declare Function HugeInt% Lib "hugearr.dll" (ByVal hArray%, ByVal el&) */
int FAR pascal _export
HugeInt(int hArray, long element)
	{
	int retval;

	GetHugeEl(hArray, element, (BYTE FAR *) &retval);
	return retval;
	}

/********************************************************************
* HugeLong -                                                        *
*   Returns an element of a long integer array.  (re. HugeInt)      *
********************************************************************/
/* VBI:  Declare Function MSHugeGetLong& Lib "hugearr.dll" Alias "HugeLong" (ByVal hArray%, ByVal el&) */
/* VBB:  Declare Function HugeLong& Lib "hugearr.dll" (ByVal hArray%, ByVal el&) */
long FAR pascal _export
HugeLong(int hArray, long element)
	{
	long    retval;

	GetHugeEl(hArray, element, (BYTE FAR *) &retval);
	return retval;
	}

/********************************************************************
* HugeSingle -                                                      *
*   Returns an element of a single precesion array.  (re. HugeInt)  *
********************************************************************/
/* VBI:  Declare Function MSHugeGetSingle! Lib "hugearr.dll" Alias "HugeSingle" (ByVal hArray%, ByVal el&) */
/* VBB:  Declare Function HugeSingle! Lib "hugearr.dll" (ByVal hArray%, ByVal el&) */
float FAR pascal _export
HugeSingle(int hArray, long element)
	{
	float retval;

	GetHugeEl(hArray, element, (BYTE FAR *) &retval);
	return retval;
	}

/********************************************************************
* HugeDouble -                                                      *
*   Returns an element of a double precesion array.  (re. HugeInt)  *
********************************************************************/
/* VBI:  Declare Function MSHugeGetDouble# Lib "hugearr.dll" Alias "HugeDouble" (ByVal hArray%, ByVal el&) */
/* VBB:  Declare Function HugeDouble# Lib "hugearr.dll" (ByVal hArray%, ByVal el&) */
double FAR pascal _export
HugeDouble(int hArray, long element)
	{
	double  retval;

	GetHugeEl(hArray, element, (BYTE FAR *) &retval);
	return retval;
	}

/********************************************************************
* HugeCurrency -                                                    *
*   Returns an element of a currency array.  (re. HugeInt)          *
********************************************************************/
/* VBI:  Declare Function MSHugeGetCurrency@ Lib "hugearr.dll" Alias "HugeCurrency" (ByVal hArray%, ByVal el&) */
/* VBB:  Declare Function HugeCurrency@ Lib "hugearr.dll" (ByVal hArray%, ByVal el&) */
currency FAR pascal _export
HugeCurrency(int hArray, long element)
	{
	currency retval;

	GetHugeEl(hArray, element, (BYTE FAR *) &retval);
	return retval;
	}


#ifdef DEBUG
/* Functions left from when I was debugging my own HugeRedim problems. --SJS */
/* Return 1 if the last HugeRedim successfully called GlobalRealloc, or
   return 0 if it had to call HugeAlloc and copy over elements. */
int FAR pascal _export
HugeRedimOk(int hArray)
	{
	pDescrip pArray;  /* pointer to array descriptor */
	int      RtnVal;

	DecCheckHandle

	pArray = (pDescrip) LocalLock(hLocalMem) + hArray;

	RtnVal = pArray -> redim;

	LocalUnlock(hLocalMem);
	return RtnVal;
	}

/* Return actual size in bytes of the global memory block that was
   allocated for storing array elements */
long FAR pascal _export
HugeSize(int hArray)
	{
	pDescrip pArray;  /* pointer to array descriptor */
	long     RtnVal;

	DecCheckHandle

	pArray = (pDescrip) LocalLock(hLocalMem) + hArray;

	RtnVal = pArray -> bsize;

	LocalUnlock(hLocalMem);
	return RtnVal;
	}
#endif

/********************************************************************
*   HugeSave - Saves the specified number of elements to a file     *
*              Returns the number of items saved.                   *
*                                                                   *
*   NB!   HugeSave does not Erase the Huge Array.                   *
********************************************************************/
/* VBI:  Declare Function MSHugeSave& Lib "hugearr.dll" Alias "HugeSave" (ByVal hArray%, ByVal NEl&, ByVal RecLen%, ByVal Fn$) */
/* VBB:  Declare Function HugeSave& Lib "hugearr.dll" (ByVal hArray%, ByVal NEl&, ByVal RecLen%, ByVal Fn$) */
long FAR pascal _export
HugeSave(int hArray, long nelements, int OutLen, LPSTR FileSpec)
	{
	int        hSaveFile;  /* handle to the save file */
	BYTE _huge *FmBegin;   /* pointer for first element */
	BYTE _huge *FmElem;    /* pointer to array element */
	long       element;    /* element in the huge array */
	pDescrip   pArray;     /* pointer to array descriptor */

	DecCheckHandle

	pArray = (pDescrip) LocalLock(hLocalMem) + hArray;

	CheckNotAllocYet

	if (pArray -> ubound + 1 < nelements || nelements < 0)
		{
		/* subscript out of range */
		LocalUnlock(hLocalMem);
		return HA_SUBSCRIPT;
		}

	/* Open the file */
	hSaveFile = _lopen(FileSpec, OF_WRITE);
	if (hSaveFile < 0)
		{
		LocalUnlock(hLocalMem);
		return HA_FILEOPENERROR;
		}

	/* calculate pointer to element */
	FmBegin = (BYTE _huge *) GlobalLock(pArray -> handle);

	for(element = 0L; element < nelements; element++)
		{
		FmElem = FmBegin + HugeElementOffset(element, pArray->perseg, pArray->recsize);

		/* Write out the record */
		if(_lwrite(hSaveFile, FmElem, OutLen) < 0)
			{
			element = HA_FILEWRITEERROR;
			break;
			}
		}

	/* Wrap up and return */
	GlobalUnlock(pArray -> handle);
	LocalUnlock(hLocalMem);
	_lclose(hSaveFile);
	return element;
	}

/********************************************************************
*   HugeLoad - Loads a Huge Array from the specified file           *
*              Returns the number of elements loaded                *
*                                                                   *
*   NB!  The array must have been allocated and be of sufficient    *
*   NB!  size to hold the array.                                    *
********************************************************************/
/* VBI:  Declare Function MSHugeLoad& Lib "hugearr.dll" Alias "HugeLoad" (ByVal hArray%, ByVal RecLen%, ByVal Fn$) */
/* VBB:  Declare Function HugeLoad& Lib "hugearr.dll" (ByVal hArray%, ByVal RecLen%, ByVal Fn$) */
long FAR pascal _export
HugeLoad(int hArray, int InLen, LPSTR FileSpec)
	{
	int        BytesRead;  /* number of bytes read from the file */
	int        hLoadFile;  /* handle to the save file */
	BYTE _huge *ToBegin;   /* pointer for first element */
	BYTE _huge *ToElem;    /* pointer to array element */
	long       element;    /* element in the huge array */
	pDescrip   pArray;     /* pointer to array descriptor */

	DecCheckHandle

	pArray = (pDescrip) LocalLock(hLocalMem) + hArray;

	CheckNotAllocYet

	/* Open the file */
	if((hLoadFile = _lopen(FileSpec, OF_READ)) < 0)
		{
		LocalUnlock(hLocalMem);
		return HA_FILEOPENERROR;
		}

	ToBegin = (BYTE _huge *) GlobalLock(pArray -> handle);

	for(element = 0L; element <= pArray -> ubound ; element++)
		{
		/* calculate pointer to element */
		ToElem = ToBegin + HugeElementOffset(element, pArray->perseg, pArray->recsize);

		/* Read the record */
		BytesRead = _lread(hLoadFile, ToElem, InLen);

		if (BytesRead < 0)
			element = HA_FILEREADERROR;
		if (BytesRead <= 0)
			break;
		}

	/* Wrap up and return */
	GlobalUnlock(pArray -> handle);
	LocalUnlock(hLocalMem);
	_lclose(hLoadFile);
	return element;
	}
