/******************* ( Animation Construction Kit 3D ) ***********************/
/*			    Utility Routines				     */
/* CopyRight (c) 1993	   Author: Lary Myers				     */
/*****************************************************************************/

#include <stdlib.h>
#include <stdio.h>
#include <dos.h>
#include <mem.h>
#include <alloc.h>
#include <io.h>
#include <fcntl.h>
#include <time.h>
#include <string.h>
#include <sys\stat.h>
#include "ack3d.h"
#include "ackeng.h"
#include "ackext.h"
#include "xmslib.h"

#define XMS_LOWVALUE	0x0280
#define XMS_HIVALUE	0xA000

/****************************************************************************
** Return the square root of a long value				   **
****************************************************************************/
long long_sqrt(long v)
{
    int		i;
    unsigned	long result,tmp;
    unsigned	long low,high;

if (v <= 1L) return((unsigned)v);

low = v;
high = 0L;
result = 0;

for (i = 0; i < 16; i++)
    {
    result += result;
    high = (high << 2) | ((low >>30) & 0x3);
    low <<= 2;

    tmp = result + result + 1;
    if (high >= tmp)
	{
	result++;
	high -= tmp;
	}
    }

if (v - (result * result) >= (result - 1))
    result++;

return(result);
}


/****************************************************************************
** The application can use this routine to read in a 768 byte palette	   **
** file and immediately set it in the video. PalName is the name of the	   **
** palette file to load.						   **
**									   **
****************************************************************************/
int AckLoadAndSetPalette(char *PalName)
{
    int	    handle,ErrCode;
    char    *buf;

buf = malloc(800);
if (buf == NULL)
    return(ERR_NOMEMORY);

ErrCode = 0;
handle = open(PalName,O_RDWR|O_BINARY);
if (handle > 0)
    {
    read(handle,buf,768);
    close(handle);
    AckSetPalette(buf);
    }
else
    ErrCode = ERR_BADFILE;

free(buf);
return(ErrCode);
}

/****************************************************************************
** The application calls this routine to get the last object hit (from the **
** AckMovePOV() or AckMoveObjectPOV() routines.				   **
**									   **
****************************************************************************/
int AckGetObjectHit(void)
{

return(LastObjectHit);
}

/****************************************************************************
** The application calls this routine to get the last wall hit (from the   **
** AckMovePOV() or AckMoveObjectPOV() routines.				   **
**									   **
****************************************************************************/
int AckGetWallHit(void)
{

return(LastMapPosn);
}


/****************************************************************************
** The application calls this routine to make an object invisible. The	   **
** object and object bitmaps are NOT deleted. Currently the active flag	   **
** is set to zero however, future versions may do more in the routine so   **
** it would be a good idea for the application to call here instead of	   **
** setting the active flag to zero on its own.				   **
**									   **
****************************************************************************/
int AckDeleteObject(ACKENG *ae,int ObjIndex)
{
    int	    MapPosn;


if (!ae->ObjList[ObjIndex].Active)
    return(-1);

ae->ObjList[ObjIndex].Active = 0;

return(0);
}

/****************************************************************************
** The purpose of this routine is to replace an existing bitmap with a	   **
** new one. The old bitmap is freed and the new one is put into place in   **
** the specified bitmap array.						   **
**									   **
** index is the number of the bitmap array to replace			   **
** Maps is the bitmap array						   **
** NewBitmap is a pointer to the new bitmap to use			   **
**									   **
****************************************************************************/
int AckSetNewBitmap(int index,UCHAR far **Maps,UCHAR far *NewBitmap)
{
    int		    i;
    UCHAR   far	    *bPtr;

bPtr = Maps[index];
if (FP_SEG(bPtr) < XMS_HIVALUE && FP_SEG(bPtr) > XMS_LOWVALUE)
    {
    if (bPtr == NewBitmap)
	return(0);

    free(bPtr);
    Maps[index] = NewBitmap;
    return(0);
    }

#if USE_XMS
    XMSput((XMSHANDLE)bPtr,NewBitmap,BITMAP_SIZE);

    for (i = 0; i < MAX_XARRAY; i++)
	{
	if (bPtr == (UCHAR far *)xArray[i].xHandle)
	    {
	    memmove(xArray[i].Bmp,NewBitmap,BITMAP_SIZE);
	    }
	}
#endif

return(0);
}


/****************************************************************************
** This routine should be called for all wall and object bitmaps when using**
** XMS memory. It will copy the bitmap from XMS to a real memory array	   **
** buffer which is then passed back to the caller. It checks for a buffer  **
** that has the smallest usage count (an empty one would have zero), to	   **
** copy the XMS memory into. XMS handles have been observed to have a seg  **
** value of > A000H or < 100H (which hopefully will be true all the time!) **
****************************************************************************/
UCHAR far *AckGetBitmapPtr(int index,UCHAR far **Maps)
{
    int	    i,min,Lasti;
    UCHAR   far *bPtr;

bPtr = Maps[index];

#if USE_XMS
    if (FP_SEG(bPtr) < XMS_HIVALUE && FP_SEG(bPtr) > XMS_LOWVALUE)
	return(bPtr);

    min = 32000;
    Lasti = -1;

    for (i = 0; i < MAX_XARRAY; i++)
	{
	if (bPtr == (UCHAR far *)xArray[i].xHandle)
	    {
	    xArray[i].count++;
	    return(xArray[i].Bmp);
	    }

	if (xArray[i].count < min)
	    {
	    min = xArray[i].count;
	    Lasti = i;
	    }
	}

    if (Lasti == -1)
	{
	XMSget(BitmapXferPtr,(XMSHANDLE)bPtr);
	return(BitmapXferPtr);
	}

    XMSget(xArray[Lasti].Bmp,(XMSHANDLE)bPtr);
    xArray[Lasti].count = 1;
    xArray[Lasti].xHandle = (ULONG)bPtr;

    return(xArray[Lasti].Bmp);
#else
    return(bPtr);
#endif
}


#define LOCK_MAX    20

typedef struct {
    XMSHANDLE	xHandle;
    UCHAR   far *Bmp;
} LOCKLIST;


	LOCKLIST     LockTable[LOCK_MAX+1];


/****************************************************************************
** This routine loads an XMS bitmap into memory for use by the app. If the **
** bitmap is not in XMS then the bitmap pointer itself is returned.	   **
**									   **
****************************************************************************/
UCHAR far *AckLockPtr(UCHAR far *bPtr)
{
    int	    i,uFlag;
    UCHAR   far *bmp;


#if USE_XMS
    uFlag = 0;

    if (FP_SEG(bPtr) < XMS_HIVALUE && FP_SEG(bPtr) > XMS_LOWVALUE)
	return(bPtr);

    bmp = malloc(BITMAP_SIZE);
    if (bmp == NULL)
	return(bmp);

    XMSget(bmp,(XMSHANDLE)bPtr);

    for (i = 0; i < LOCK_MAX; i++)
	{
	if (LockTable[i].Bmp == NULL)
	    {
	    LockTable[i].Bmp = bmp;
	    LockTable[i].xHandle = (XMSHANDLE)bPtr;
	    uFlag = 1;
	    break;
	    }
	}

    if (!uFlag)
	{
	LockTable[0].Bmp = bmp;
	LockTable[0].xHandle = (XMSHANDLE)bPtr;
	}
    return(bmp);
#else
    return(bPtr);
#endif

}


/****************************************************************************
** This routine frees up a previously locked pointer. If the pointer is	   **
** not in XMS then no action is taken.					   **
**									   **
****************************************************************************/
int AckUnlockPtr(UCHAR far *bPtr)
{
    int	    i,j;


#if USE_XMS

    for (i = 0; i < LOCK_MAX; i++)
	{
	if (LockTable[i].Bmp == bPtr)
	    {
	    XMSput(LockTable[i].xHandle,bPtr,BITMAP_SIZE);

	    for (j = 0; j < MAX_XARRAY; j++)
		{
		if (xArray[j].xHandle == LockTable[i].xHandle)
		    {
		    memmove(xArray[j].Bmp,bPtr,BITMAP_SIZE);
		    break;
		    }
		}

	    free(LockTable[i].Bmp);
	    LockTable[i].Bmp = NULL;
	    return(0);
	    }
	}

    return(-1);
#else
    return(0);
#endif
}


/****************************************************************************
** This routine creates a new bitmap buffer (in real or XMS memory) and	   **
** copies the supplied bitmap into it. The return pointer is either an	   **
** XMS handle or a pointer to the real memory.				   **
**									   **
****************************************************************************/
UCHAR far *AckCopyNewBitmap(UCHAR far *bPtr)
{
    UCHAR   far	    *bmp;
#if USE_XMS
    XMSHANDLE	    xPtr;

    if (UseXMS > 0)
	{
	xPtr = XMSalloc(BITMAP_SIZE);
	if (xPtr != XMSHNULL)
	    {
	    if (FP_SEG(bPtr) > XMS_HIVALUE || FP_SEG(bPtr) < XMS_LOWVALUE)
		XMSget(BitmapXferPtr,(XMSHANDLE)bPtr);
	    else
		memmove(BitmapXferPtr,bPtr,BITMAP_SIZE);

	    XMSput(xPtr,BitmapXferPtr,BITMAP_SIZE);
	    return((UCHAR far *)xPtr);
	    }
	else
	    {
	    bmp = malloc(BITMAP_SIZE);
	    if (bmp)
		memmove(bmp,bPtr,BITMAP_SIZE);
	    return(bmp);
	    }

	}
    else
	{
	bmp = malloc(BITMAP_SIZE);
	if (bmp)
	    memmove(bmp,bPtr,BITMAP_SIZE);
	return(bmp);
	}
#else
    bmp = malloc(BITMAP_SIZE);
    if (bmp)
	memmove(bmp,bPtr,BITMAP_SIZE);
    return(bmp);
#endif

}

/****************************************************************************
** This routine frees the memory used by a bitmap in either real or XMS	   **
** memory.								   **
**									   **
****************************************************************************/
int AckFreeBitmap(UCHAR far *Bmp)
{
	int	i;

#if USE_XMS

    if (FP_SEG(Bmp) > XMS_HIVALUE || FP_SEG(Bmp) < XMS_LOWVALUE)
	{
	for (i = 0; i < MAX_XARRAY; i++)
	    {
	    if (xArray[i].xHandle == (XMSHANDLE)Bmp)
		{
		xArray[i].count = 0;
		xArray[i].Bmp = XMSHNULL;
		break;
		}

	    }

	XMSfree((XMSHANDLE)Bmp);

	}
    else
	free(Bmp);

#else
    free(Bmp);
#endif

return(0);
}

