/*
** THIS SOFTWARE IS SUBJECT TO COPYRIGHT PROTECTION AND IS OFFERED ONLY
** PURSUANT TO THE 3DFX GLIDE GENERAL PUBLIC LICENSE. THERE IS NO RIGHT
** TO USE THE GLIDE TRADEMARK WITHOUT PRIOR WRITTEN PERMISSION OF 3DFX
** INTERACTIVE, INC. A COPY OF THIS LICENSE MAY BE OBTAINED FROM THE 
** DISTRIBUTOR OR BY CONTACTING 3DFX INTERACTIVE INC(info@3dfx.com). 
** THIS PROGRAM IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER 
** EXPRESSED OR IMPLIED. SEE THE 3DFX GLIDE GENERAL PUBLIC LICENSE FOR A
** FULL TEXT OF THE NON-WARRANTY PROVISIONS.  
** 
** USE, DUPLICATION OR DISCLOSURE BY THE GOVERNMENT IS SUBJECT TO
** RESTRICTIONS AS SET FORTH IN SUBDIVISION (C)(1)(II) OF THE RIGHTS IN
** TECHNICAL DATA AND COMPUTER SOFTWARE CLAUSE AT DFARS 252.227-7013,
** AND/OR IN SIMILAR OR SUCCESSOR CLAUSES IN THE FAR, DOD OR NASA FAR
** SUPPLEMENT. UNPUBLISHED RIGHTS RESERVED UNDER THE COPYRIGHT LAWS OF
** THE UNITED STATES.  
** 
** COPYRIGHT 3DFX INTERACTIVE, INC. 1999, ALL RIGHTS RESERVED
**
**
** $Revision: 1.3 $ 
** $Date: 2000/11/16 20:28:54 $ 
**
*/

#include <stdlib.h>
#include <stdarg.h>
#ifndef __linux__
#include <conio.h>
#endif
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include <time.h>
#include <math.h>

#include <glide.h>
#include "tlib.h"
#include "plib.h"

#define MAX_HANDLES  32

//the following may need to go onto the heap...  currently need 10K, not bad
static tlTxMnTexture_t tlTxMnMngr[MAX_HANDLES];
static hTexId_t hFstOpnTexId = H_FSTTEXID;  //0 is the NULL handle
static GrChipID_t currTMU = GR_TMU0;
static FxBool bMultiRevOrder;
static FxBool bMultiOffsetFix;

/* static helper prototypes */
static FxU32 prGetLodSize( GrTexInfo *const disTexInfo, GrLOD_t disLOD );
static FxU32 prTexMultiMemRequired( FxU32 deMipMask, GrTexBaseRange_t deRange,
			    	    GrTexInfo* deTexInfo );
static void prGetLod( GrTexInfo *lpTexInfo, GrLOD_t deLod, void *data );
static void prPopLod( GrTexInfo *lpDeTexInfo, mipMapEnd_t deMipMapEnd );
static void prPushLod( GrTexInfo *lpDeTexInfo, mipMapEnd_t deMipMapEnd );
static FxU32 prGetTxMnAdd( hTexId_t deTexId, GrLOD_t deLod );
static int prGetLodWidth( GrLOD_t lod, GrAspectRatio_t aspect );
static int prGetLodHeight( GrLOD_t lod, GrAspectRatio_t aspect );
static FxU32 prGetMultiMemBump( const GrTexInfo *const deTexInfo, 
                                  GrTexBaseRange_t deTexBase,
                                  FxU32 deMipMask );


/*-------------------------------------------------------------------
  Function: plTxMnLoadTxMngr
  Date: 6/26/97
  Implementor(s): psw
  Library: 
  Description:
  Arguments:
  Return: 
  -------------------------------------------------------------------*/
hTexId_t
plTxMnLoadTxMngr( TlTexture* lpTexture )
{
  /* assumptions */
  assert( lpTexture );

  /* code */
  tlTxMnMngr[ hFstOpnTexId ].lpTlTextureInfo = lpTexture;
  tlTxMnMngr[ hFstOpnTexId ].origLargeLod = lpTexture->info.largeLod;
  tlTxMnMngr[ hFstOpnTexId ].origSmallLod = lpTexture->info.smallLod;
  SET_DOWNLOADABLE( tlTxMnMngr[hFstOpnTexId], FXFALSE);
  SET_MULTIBASE( tlTxMnMngr[hFstOpnTexId], FXFALSE);
  SET_TRILINEAR( tlTxMnMngr[hFstOpnTexId], FXFALSE);
  SET_BEENPOPD( tlTxMnMngr[hFstOpnTexId], FXFALSE);
  SET_DIRTY( tlTxMnMngr[hFstOpnTexId], FXFALSE);

  return (hFstOpnTexId++);
}
/*-------------------------------------------------------------------*/



/*-------------------------------------------------------------------
  Function: plTxMnSetDownload
  Date: 6/26/97
  Implementor(s): psw
  Library: 
  Description:
  Arguments:
  Return: 
  -------------------------------------------------------------------*/
static void
prTxMnSetBeenPopd( hTexId_t deTexId, FxBool bBeenPopd )
{
  SET_BEENPOPD( tlTxMnMngr[deTexId], bBeenPopd );
  SET_DIRTY( tlTxMnMngr[deTexId], FXTRUE );  /* just for messing with it */
}
/*-------------------------------------------------------------------*/



/*-------------------------------------------------------------------
  Function: plTxMnSetDownload
  Date: 6/26/97
  Implementor(s): psw
  Library: 
  Description:
  Arguments:
  Return: 
  -------------------------------------------------------------------*/
void 
plTxMnSetDownload( hTexId_t deTexId, FxBool bDownLoad )
{
  /* assumptions */
  assert( deTexId < hFstOpnTexId );

  /* code */
  SET_DOWNLOADABLE( tlTxMnMngr[deTexId], bDownLoad );
  SET_DIRTY( tlTxMnMngr[deTexId], FXTRUE );  /* just for messing with it */
}
/*-------------------------------------------------------------------*/



/*-------------------------------------------------------------------
  Function: plTxMnSetMultibase
  Date: 6/26/97
  Implementor(s): psw
  Library: 
  Description:
  Arguments:
  Return: 
  -------------------------------------------------------------------*/
void 
plTxMnSetMultibase( hTexId_t deTexId, FxBool bMultibase )
{
  /* assumptions */
  assert( deTexId < hFstOpnTexId );

  /* code */
  SET_MULTIBASE( tlTxMnMngr[deTexId], bMultibase );
  SET_DIRTY( tlTxMnMngr[deTexId], FXTRUE );  /* just for messing with it */
}
/*-------------------------------------------------------------------*/



/*-------------------------------------------------------------------
  Function: plTxMnGetMultibase
  Date: 6/26/97
  Implementor(s): psw
  Library: 
  Description:
  Arguments:
  Return: 
  -------------------------------------------------------------------*/
FxBool 
plTxMnGetMultibase( hTexId_t deTexId )
{
  assert( deTexId < hFstOpnTexId );
  return (IS_MULTIBASE(tlTxMnMngr[deTexId]) );
}
/*-------------------------------------------------------------------*/



/*-------------------------------------------------------------------
  Function: plTxMnSetMultiRevOrder
  Date: 7/6/97
  Implementor(s): psw
  Library: 
  Description:
    Sets the global bMultiRevOrder which is used by plTxMnDownloadAll
    to determine the order of the multibase segments in tmu mem
  Arguments:
    FxBool bOrder
  Return: 
    void
  -------------------------------------------------------------------*/
void 
plTxMnSetMultiRevOrder( FxBool bOrder )
{
  bMultiRevOrder = bOrder;
}
/*-------------------------------------------------------------------*/



/*-------------------------------------------------------------------
  Function: plTxMnGetMultiRevOrder
  Date: 7/6/97
  Implementor(s): psw
  Library: 
  Description:
    Returns the current status of the bMultiRevOrder static global
  Arguments:
    none
  Return:
    FxBool
  -------------------------------------------------------------------*/
FxBool 
plTxMnGetMultiRevOrder( )
{
  return ( bMultiRevOrder );
}
/*-------------------------------------------------------------------*/



/*-------------------------------------------------------------------
  Function: plTxMnSetMultiOffsetFix
  Date: 7/16/97
  Implementor(s): psw
  Library: 
  Description:
    Sets the global bMultiOffsetFix which is used by plTxMnTextureSource
    to determine to use the Multibase offset fix
  Arguments:
    FxBool bOrder
  Return: 
    void
  -------------------------------------------------------------------*/
void 
plTxMnSetMultiOffsetFix( FxBool bOffsetFix )
{
  bMultiOffsetFix = bOffsetFix;
}
/*-------------------------------------------------------------------*/



/*-------------------------------------------------------------------
  Function: plTxMnGetMultiOffsetFix
  Date: 7/16/97
  Implementor(s): psw
  Library: 
  Description:
    Returns the current status of the bMultiOffsetFix static global
  Arguments:
    none
  Return:
    FxBool
  -------------------------------------------------------------------*/
FxBool 
plTxMnGetMultiOffsetFix( )
{
  return ( bMultiOffsetFix );
}
/*-------------------------------------------------------------------*/



/*-------------------------------------------------------------------
  Function: plTxMnSetTrilinear
  Date: 6/26/97
  Implementor(s): psw
  Library: 
  Description:
  Arguments:
  Return: 
  -------------------------------------------------------------------*/
void 
plTxMnSetTrilinear( hTexId_t deTexId, FxBool bTrilinear )
{
  /* assumptions */
  assert( deTexId < hFstOpnTexId );

  /* trilinear && <2-lods are mutex */
  if ( (tlTxMnMngr[ deTexId ].lpTlTextureInfo->info.smallLod - 
        tlTxMnMngr[ deTexId ].lpTlTextureInfo->info.largeLod) >= 1)
  {
    SET_TRILINEAR( tlTxMnMngr[deTexId], bTrilinear );
    SET_DIRTY( tlTxMnMngr[deTexId], FXTRUE );  /* just for messing with it */
  }
}
/*-------------------------------------------------------------------*/



/*-------------------------------------------------------------------
  Function: plTxMnGetTrilinear
  Date: 6/26/97
  Implementor(s): psw
  Library: 
  Description:
  Arguments:
  Return: 
  -------------------------------------------------------------------*/
FxBool 
plTxMnGetTrilinear( hTexId_t deTexId )
{
  assert( deTexId < hFstOpnTexId );
  return (IS_TRILINEAR(tlTxMnMngr[deTexId]) );
}
/*-------------------------------------------------------------------*/



/*-------------------------------------------------------------------
  Function: plTxMnReset
  Date: 6/26/97
  Implementor(s): psw
  Library: 
  Description:
    Resets the respective texture back to its base state
  Arguments:
    hTexId_t deTexId - the id of the tlTxMnMngr texture to reset
  Return: 
    void
  -------------------------------------------------------------------*/
void 
plTxMnReset( hTexId_t deTexId )
{
  /* initializations */
  tlTxMnTexture_t *lpThisTxMnTexture;  
  GrTexInfo *lpThisTexInfo;  
  
  /* assumptions */
  assert( deTexId < hFstOpnTexId );

  /* initializations */
  lpThisTxMnTexture = &tlTxMnMngr[deTexId];
  lpThisTexInfo = &lpThisTxMnTexture->lpTlTextureInfo->info;

  /* code */
  SET_MULTIBASE( tlTxMnMngr[deTexId], FXFALSE );
  SET_TRILINEAR( tlTxMnMngr[deTexId], FXFALSE );
  bMultiRevOrder = FXFALSE;
  /* SET_DIRTY( tlTxMnMngr[deTexId], FXFALSE ); */

  while ( lpThisTexInfo->largeLod > lpThisTxMnTexture->origLargeLod )
    prPushLod( lpThisTexInfo, topOfMipMap );/* reset d'm data ptrs */
  
  while ( lpThisTexInfo->smallLod < lpThisTxMnTexture->origSmallLod )
    prPushLod (lpThisTexInfo, btmOfMipMap );/* reset d'm data ptrs */
}
/*-------------------------------------------------------------------*/



/*-------------------------------------------------------------------
  Function: plTxMnDownloadAll
  Date: 6/27/97
  Implementor(s): psw
  Library: 
  Description:
    Downloads all the textures be held by tlTexManMngr[]
    Will determine if the textures are to be downloaded 
    in (linear || multibase) && (BOTH || EVEN/ODD) mode based upon 
    their flags and do the right thang
  Arguments:
    None
  Return:
    void
  -------------------------------------------------------------------*/
void 
plTxMnDownloadAll( )
{
  /* definitions */
  hTexId_t         hCurrTexId;
  FxU32            currTexMemReq;  
  FxU32             currStartAdd;    
  tlTxMnTexture_t  *lpThisTxMnTexture;  
  GrTexInfo        *lpThisTexInfo;  
  TlTexture        *lpThisTlTex;

  /* assumptions */
  assert(hFstOpnTexId != 0);

  /* initializations */
  currStartAdd = grTexMinAddress(currTMU);

  /* code */
  for ( hCurrTexId = H_FSTTEXID; hCurrTexId < hFstOpnTexId; ++hCurrTexId )
  {    /* here we go */
    if (IS_DOWNLOADABLE(tlTxMnMngr[hCurrTexId]))
    {  /* download it */
      lpThisTxMnTexture = &tlTxMnMngr[hCurrTexId];
      lpThisTlTex = lpThisTxMnTexture->lpTlTextureInfo;
      lpThisTexInfo = &lpThisTlTex->info;
      if (!IS_MULTIBASE(*lpThisTxMnTexture))
      {  /* not multibased */
        if (!IS_TRILINEAR(*lpThisTxMnTexture))
        {  /* not trilinear mip'd */
          currTexMemReq = grTexTextureMemRequired(GR_MIPMAPLEVELMASK_BOTH, lpThisTexInfo);
          assert( currStartAdd + currTexMemReq < grTexMaxAddress(currTMU) );
          grTexDownloadMipMap( currTMU, currStartAdd, 
                                 GR_MIPMAPLEVELMASK_BOTH, lpThisTexInfo );
          lpThisTxMnTexture->u0.bothAdd = currStartAdd;
          currStartAdd += currTexMemReq;
        }  /* if !IS_TRILINEAR && !IS_MULTIBASE */
        else
        { /* else it is being rendered as trilinear mip'd */
          /* first do the even segment */
          currTexMemReq = grTexTextureMemRequired(GR_MIPMAPLEVELMASK_EVEN, lpThisTexInfo);
          assert( currStartAdd + currTexMemReq < grTexMaxAddress(currTMU) );
          grTexDownloadMipMap( currTMU, currStartAdd, 
                                 GR_MIPMAPLEVELMASK_EVEN, lpThisTexInfo );
          lpThisTxMnTexture->u0.evenAdd = currStartAdd;
          currStartAdd += currTexMemReq;
          /* next do the odd segment */
          currTexMemReq = grTexTextureMemRequired(GR_MIPMAPLEVELMASK_ODD, lpThisTexInfo);
          assert( currStartAdd + currTexMemReq < grTexMaxAddress(currTMU) );
          grTexDownloadMipMap( currTMU, currStartAdd, 
                                 GR_MIPMAPLEVELMASK_ODD, lpThisTexInfo );
          lpThisTxMnTexture->u1.oddAdd = currStartAdd;
          currStartAdd += currTexMemReq;
        } /* else !IS_MULTIBASE && IS_TRILINEAR */
      } /* if !IS_MULTIBASE */
      else /* else it is multibased */
      {  
        /* stack frame definitions */
        FxU32                   *lpxAdd;
        GrTexBaseRange_t   tTxBse;

        grTexMultibase(currTMU, FXTRUE);  /* turn on multibase */
        if (!IS_TRILINEAR(*lpThisTxMnTexture))
        { /* non trilinear mip'd multibase texture
          ** if bMultiRevOrder is True, get the addresses for the Multibase segs
          ** in reverse order, 32_TO_1 to 256, else get them in 256 to 32_TO_1 */

          for ( tTxBse = bMultiRevOrder ? GR_TEXBASE_32_TO_1 : GR_TEXBASE_256,
                  lpxAdd = bMultiRevOrder ? &lpThisTxMnTexture->u3.multiR3Add :
                  &lpThisTxMnTexture->u0.multiR0Add; 
                bMultiRevOrder ? lpxAdd >= &lpThisTxMnTexture->u0.multiR0Add :
                  lpxAdd <= &lpThisTxMnTexture->u3.multiR3Add;
                (bMultiRevOrder ? --tTxBse : ++tTxBse), 
                  (bMultiRevOrder ? --lpxAdd : ++lpxAdd) )
          { /* this loop just gets the texture addresses - the multibase 
            ** segments will be in reverse order, but in the same linear block */
            currTexMemReq = prTexMultiMemRequired( GR_MIPMAPLEVELMASK_BOTH,
                                                     tTxBse, lpThisTexInfo);
            if (currTexMemReq != 0)  /* if the LOD(s) !missing, ie. pop'd */
            {
              assert( currStartAdd + currTexMemReq < grTexMaxAddress(currTMU) );
              *lpxAdd = currStartAdd;
              currStartAdd += currTexMemReq; /* bump the current TMU address */
            }
          } /* for */
          for ( tTxBse = GR_TEXBASE_256, lpxAdd = &lpThisTxMnTexture->u0.multiR0Add;
                  lpxAdd <= &lpThisTxMnTexture->u3.multiR3Add;
                  ++tTxBse, ++lpxAdd )
          { /* this loop actually does the downloading - in the proper order...
            ** GR_TEXBASE_256 to GR_TEXBASE_32_TO_1 */

            /* stack frame definitions */
            GrTexInfo   tmpInfo;
            GrTexInfo    *lpTmpInfo = &tmpInfo;

            /* initializations */
            /* multibase requires that I munge the data for downloading */
            *lpTmpInfo = *lpThisTexInfo;
            if ((lpThisTexInfo->largeLod <= (FxI32) tTxBse ) && 
                (lpThisTexInfo->smallLod >= (FxI32) tTxBse ))
            { /* if the TexBase exists in the lod range */
              while ( lpTmpInfo->largeLod < (FxI32) tTxBse )
                prPopLod( lpTmpInfo, topOfMipMap ); /* munge the top */
  
              if ( tTxBse != GR_TEXBASE_32_TO_1 )
                lpTmpInfo->smallLod = lpTmpInfo->largeLod; /* munge the btm */

              grTexDownloadMipMap(currTMU, *lpxAdd,  /* download mung'd data */
                                    GR_MIPMAPLEVELMASK_BOTH, lpTmpInfo );
            }  /* if */
          }  /* for */
        } /* if !IS_TRILINEAR && IS_MULTIBASE */
        else
        { /* else it is a multibased trilinear mipmap, aka motherfucker
          ** if bMultiRevOrder is True, get the addresses for the Multibase segs
          ** in reverse order, 32_TO_1 to 256, else get them in 256 to 32_TO_1 */
          for (tTxBse = bMultiRevOrder ? GR_TEXBASE_32_TO_1+1 : GR_TEXBASE_256,
                 lpxAdd = bMultiRevOrder ? &lpThisTxMnTexture->u4.multiR3EvenAdd :
                 &lpThisTxMnTexture->u0.multiR0EvenAdd; 
               bMultiRevOrder ? lpxAdd >= &lpThisTxMnTexture->u0.multiR0EvenAdd :
                 lpxAdd <= &lpThisTxMnTexture->u4.multiR3EvenAdd;
               (bMultiRevOrder ? --tTxBse : ++tTxBse), 
                 (bMultiRevOrder ? --lpxAdd : ++lpxAdd) )
          { /* this loop just gets the texture addresses */
            if ( tTxBse % 2 ) /* is it odd? */
              currTexMemReq = prTexMultiMemRequired(GR_MIPMAPLEVELMASK_ODD,
                                                      tTxBse, lpThisTexInfo); /* 1,3 */
            else  /* even */
              currTexMemReq = prTexMultiMemRequired(GR_MIPMAPLEVELMASK_EVEN,
                                                     (tTxBse>3?3:tTxBse),
                                                     lpThisTexInfo);/* 0,2,3 */
                                        /* prTexMulti.. only knows about 0-3 */
            if (currTexMemReq != 0)  /* if the LOD(s) !missing, ie. pop'd */
            {
              assert( currStartAdd + currTexMemReq < grTexMaxAddress(currTMU) );
              *lpxAdd = currStartAdd;
              currStartAdd += currTexMemReq;/* bump the current TMU address */
            }
          } /* for */
          for (tTxBse = GR_TEXBASE_256,
                 lpxAdd = &lpThisTxMnTexture->u0.multiR0EvenAdd;
               lpxAdd <= &lpThisTxMnTexture->u4.multiR3EvenAdd;
               ++tTxBse, ++lpxAdd ) /* tTxBse will actually overrun... 
                                    ** overrun = GR_TEXBASE_32_TO_1 + 1
                                    ** (tTxBse>3?3:tTxBse) will fix this
                                    ** required for 32to1_ODD and 32to1_EVEN */
            /* actually (tTxBse>GR_TEXBASE_32_TO_1:GR_TEXBASE_32_TO_1?tTxBse) */
          { /* this loop actually does the downloading - in the proper order...
            ** GR_TEXBASE_256 to GR_TEXBASE_32_TO_1 */
  
            /* stack frame definitions */
            GrTexInfo   tmpInfo;
            GrTexInfo    *lpTmpInfo = &tmpInfo;

            /* initializations */
              /* multibase requires that I munge the data, hence the data copies */
            *lpTmpInfo = *lpThisTexInfo;

            if ((lpThisTexInfo->largeLod <= (FxI32) (tTxBse>3?3:tTxBse)) && 
                (lpThisTexInfo->smallLod >= (FxI32) (tTxBse>3?3:tTxBse)))
            { /* if an lod actually exits in the range */
              while ( lpTmpInfo->largeLod < (FxI32)(tTxBse>3?3:tTxBse) )
                prPopLod( lpTmpInfo, topOfMipMap ); /* munge the top */
  
              if ( (tTxBse>3?3:tTxBse) != GR_TEXBASE_32_TO_1 )
                lpTmpInfo->smallLod = lpTmpInfo->largeLod; /* munge the btm */

              if ( tTxBse % 2 ) /* is it odd? */
                grTexDownloadMipMap(currTMU, *lpxAdd,  /* download mung'd odd data */
                                      GR_MIPMAPLEVELMASK_ODD, lpTmpInfo );
              else
                grTexDownloadMipMap(currTMU, *lpxAdd,  /* download mung'd even data */
                                      GR_MIPMAPLEVELMASK_EVEN, lpTmpInfo );
            } /* if */
          }  /* for */
        } /* else IS_TRILINEAR && IS_MULTIBASE */
        grTexMultibase(currTMU, FXFALSE);  /* turn off multibase */
      } /* else IS_MULTIBASE */
      if ( lpThisTlTex->tableType != NO_TABLE )
        grTexDownloadTable(currTMU, lpThisTlTex->tableType, 
                             &lpThisTlTex->tableData );
    }  /* if IS_DOWNLOADABLE */
  } /* for ( i = 0; i < fstOpnTexId; ++i ) */
}  /* plTxMnDownloadAll */
/*-------------------------------------------------------------------*/



/*-------------------------------------------------------------------
  Function: plTxMnTexSource
  Date: 6/28/97
  Implementor(s): psw
  Library: 
  Description:
    Renders the texture associated with hTexId.  Will setup the rendering
    modes based upon the tlTxMnMngr[deTexId].flags fields, i.e., 
    (linear || multibase) && (BOTH || EVEN/ODD) modes.  Also uses
    the addresses saved by previous call to tlTxMnDownloadAll 
  Arguments:
    hTexId_t deTexId - handle to the texture
    FxU32 deMipMask - mipmap mask
  Return: 
    void
  -------------------------------------------------------------------*/
void
plTxMnTexSource( hTexId_t deTexId, FxU32 deMipMask )
{
  /* definitions */
  tlTxMnTexture_t   *lpThisTxMnTexture;  
  GrTexInfo         thisTexInfo,
                    *lpThisTexInfo;
  TlTexture         *lpThisTlTex;
  FxU32             *lpxAdd,
                    xAdd;
  GrTexBaseRange_t  tTexBase;

  /* assumptions */
  assert( deTexId < hFstOpnTexId );
  assert((deMipMask >= GR_TEXBASE_256) &&
         (deMipMask <= GR_TEXBASE_32_TO_1));

  /* initializations */
  lpThisTxMnTexture = &tlTxMnMngr[deTexId];
  lpThisTlTex = lpThisTxMnTexture->lpTlTextureInfo;
  lpThisTexInfo = &thisTexInfo;
  *lpThisTexInfo = lpThisTxMnTexture->lpTlTextureInfo->info;

/*
  if ((lpThisTlTex->tableType == GR_NCCTABLE_NCC0) || 
      (lpThisTlTex->tableType == GR_NCCTABLE_NCC1))
    grTexNCCTable( currTMU, lpThisTlTex->tableType );
*/
  if (!IS_MULTIBASE(*lpThisTxMnTexture))
  {  /* not multibased */
    if (!IS_TRILINEAR(*lpThisTxMnTexture))
      grTexSource( currTMU, lpThisTxMnTexture->u0.bothAdd, deMipMask, lpThisTexInfo );
    else  /* IS_TRILINEAR */
      if ( deMipMask == GR_MIPMAPLEVELMASK_EVEN ) /* first do the even segment */
        grTexSource( currTMU, lpThisTxMnTexture->u0.evenAdd, deMipMask, lpThisTexInfo );
      else        /* next do the odd segment */
        grTexSource( currTMU, lpThisTxMnTexture->u1.oddAdd, deMipMask, lpThisTexInfo );
  }/* if !IS_MULTIBASE */
  else
  {  /* else it is multibased */   
    grTexMultibase(currTMU, FXTRUE); /* turn on multibase */
    if (!IS_TRILINEAR(*lpThisTxMnTexture))
    {  /* really dependant on the unions being in the proper order */
      lpxAdd = &lpThisTxMnTexture->u0.multiR0Add;
      for  (tTexBase = GR_TEXBASE_256; tTexBase <= GR_TEXBASE_32_TO_1; 
              ++tTexBase, ++lpxAdd)  
      /* next i check that the current TexBase is in the lod range */
        if ((lpThisTexInfo->largeLod <= (FxI32) tTexBase) &&
            (lpThisTexInfo->smallLod >= (FxI32) tTexBase))
        {
          xAdd = *lpxAdd;
          if (bMultiOffsetFix)
            xAdd -= prGetMultiMemBump(lpThisTexInfo, tTexBase, deMipMask);
          grTexMultibaseAddress(currTMU, tTexBase, xAdd, deMipMask,
                                  lpThisTexInfo );
        }
    } /* if !IS_TRILINEAR */
    else 
    { /* else it is a multibased trilinear mipmap */
      if ( deMipMask == GR_MIPMAPLEVELMASK_EVEN ) /* do the even segments */
      {  
        lpxAdd = &lpThisTxMnTexture->u0.multiR0EvenAdd;/* set lpxAdd to 1st add */
        for  (tTexBase = GR_TEXBASE_256; tTexBase <= GR_TEXBASE_32_TO_1;
              tTexBase = ( tTexBase + 2 == GR_TEXBASE_32_TO_1 + 1 ) ? /* 32t1 is both even & odd */
              GR_TEXBASE_32_TO_1 : tTexBase + 2, lpxAdd += 2 ) /* even segs r at even indices */
        /* next i check that the current TexBase is in the lod range */
          if ((lpThisTexInfo->largeLod <= (FxI32) tTexBase) &&
              (lpThisTexInfo->smallLod >= (FxI32) tTexBase))
          {
            xAdd = *lpxAdd;
            if (bMultiOffsetFix)
              xAdd -= prGetMultiMemBump(lpThisTexInfo, tTexBase, deMipMask);
            grTexMultibaseAddress(currTMU, tTexBase, xAdd, deMipMask,
                                    lpThisTexInfo);
          }
      } /* if - even */
      else  /* else do the odd segments */
      {
        lpxAdd = &lpThisTxMnTexture->u1.multiR1OddAdd;
        for  (tTexBase = GR_TEXBASE_128; tTexBase <= GR_TEXBASE_32_TO_1; 
                tTexBase+=2, lpxAdd+=2 ) /* odd segs r at odd indices */
        /* next i check that the current TexBase is in the lod range */
          if ((lpThisTexInfo->largeLod <= (FxI32) tTexBase) &&
              (lpThisTexInfo->smallLod >= (FxI32) tTexBase))
          {
            xAdd = *lpxAdd;
            if (bMultiOffsetFix)
              xAdd -= prGetMultiMemBump(lpThisTexInfo, tTexBase, deMipMask);
            grTexMultibaseAddress( currTMU, tTexBase, xAdd, deMipMask,
                                     lpThisTexInfo );
          }
      } /* else - odd */
    } /* else IS_TRILINEAR && IS_MULTIBASE */
    grTexMultibase(currTMU, FXFALSE);  /* turn off multibase */
  } /* else IS_MULTIBASE */
}  /* plTxMnRender */
/*-------------------------------------------------------------------*/



/*-------------------------------------------------------------------
  Function: plTxMnGetInfo
  Date: 6/27/97
  Implementor(s): psw
  Library: 
  Description:
    simply fills in GrTexInfo struct with info about Texture
  Arguments:
    hTexId_t deTexHndl - the texture that you want to know about
    GrTexInfo *deGrTexInfo - the info struct you want filled
  Return: 
    void
  -------------------------------------------------------------------*/
void 
plTxMnGetInfo( hTexId_t deTexHndl, GrTexInfo *deGrTexInfo )
{

  /* assumptions */
  assert (deGrTexInfo);
  assert ( deTexHndl < hFstOpnTexId );

  *deGrTexInfo =  tlTxMnMngr[deTexHndl].lpTlTextureInfo->info;
}
/*-------------------------------------------------------------------*/



/*-------------------------------------------------------------------
  Function: plPopLod
  Date: 6/17/97
  Implementor(s): psw
  Library: 
  Description:
    Basically a wrapper for prPopLod  
  Arguments:
    hTexId_t thisTexId
    mipMapEnd_t deMipMapEnd
  Return: 
    none
  -------------------------------------------------------------------*/
void 
plPopLod( hTexId_t hCurrTexture, mipMapEnd_t deMipMapEnd )
{
  /* definitions  */
  GrTexInfo        *lpThisTexInfo;    
  tlTxMnTexture_t  *lpThisTxMnTexture;

  /* assumptions */
  assert( hCurrTexture < hFstOpnTexId );
 

  /* initializations */
  lpThisTxMnTexture = &tlTxMnMngr[hCurrTexture];
  lpThisTexInfo = &lpThisTxMnTexture->lpTlTextureInfo->info;

  /* code */
  if ( lpThisTexInfo->smallLod == lpThisTexInfo->largeLod )
    return;  /* no reason to go on, nothing to do */

  if ((IS_TRILINEAR(*lpThisTxMnTexture)) && 
       ((lpThisTexInfo->smallLod - lpThisTexInfo->largeLod) == 2 ) )
    return;  /* trilinear && <2-lods are mutex */

  prPopLod( lpThisTexInfo, deMipMapEnd );
} /* plPopLod */
/*-------------------------------------------------------------------*/



/*-------------------------------------------------------------------
  Function: plPushLod
  Date: 6/17/97
  Implementor(s): psw
  Library: 
  Description:
    Basically a wrapper for prPushLod  
  Arguments:
    hTexId_t thisTexId
    mipMapEnd_t deMipMapEnd
  Return: 
    none
  -------------------------------------------------------------------*/
void 
plPushLod( hTexId_t hCurrTexture, mipMapEnd_t deMipMapEnd )
{
  /* definitions  */
  GrTexInfo *lpThisTexInfo;/* info struct associated with curr tlTxMnMnger obj */
  tlTxMnTexture_t *lpThisTxMnTexture;

  /* assumptions */
  assert( hCurrTexture < hFstOpnTexId );
  
  /* initializations */
  lpThisTxMnTexture = &tlTxMnMngr[hCurrTexture];
  lpThisTexInfo = &lpThisTxMnTexture->lpTlTextureInfo->info;

  /* code */
  if ((deMipMapEnd == topOfMipMap) && 
      (lpThisTexInfo->largeLod == lpThisTxMnTexture->origLargeLod))
    return;  /* already at the top of the stack */
  
  if ((deMipMapEnd == btmOfMipMap) && 
      (lpThisTexInfo->smallLod == lpThisTxMnTexture->origSmallLod))
    return;  /* already at the bottom of the stack */

  prPushLod( lpThisTexInfo, deMipMapEnd);
} /* plPushLod */
/*-------------------------------------------------------------------*/



/*-------------------------------------------------------------------
  Function: plCreateTlTexture
  Date: 6/20/97
  Implementor(s): psw
  Library: 
  Description:
    creates a TlTexture
  Arguments:
    GrTexInfo*
    FxU16 []
  Return: 
    void
  -------------------------------------------------------------------*/
void 
plCreateTlTexture( TlTexture *deTlTexture, FxU16 daColors[] )
{
  /* definitions  */
  GrLOD_t    tCurrLod;        /* the current LOD */
  int        nBytePerPixel;   /* number of bytes per pixel */
  FxU8       *spLilTex;       /* 8bit LOD data ptr */
  FxU16      *mpBigTex;       /* 16bit LOD data ptr */
  void       *lpvCurrLod;     /* curr LOD data ptr */ 
  FxU32      xLodNumBytes;    /* number of bytes to write in curr LOD */
  GrTexInfo  *lpThisTexInfo;  /* ptr to GrTexInfo in TlTexture struct */

  /* assumptions */
  assert( deTlTexture );
  assert( deTlTexture->info.data );
  assert( deTlTexture->info.smallLod > deTlTexture->info.largeLod );

  /* no palettized */
  assert( (deTlTexture->info.format != GR_TEXFMT_YIQ_422)   ||
          (deTlTexture->info.format != GR_TEXFMT_P_8)       ||
          (deTlTexture->info.format != GR_TEXFMT_AYIQ_8422) ||
          (deTlTexture->info.format != GR_TEXFMT_AP_88) );

  /* initializations */
  lpThisTexInfo = &deTlTexture->info;

  /* code */
  if ((lpThisTexInfo->format == GR_TEXFMT_RGB_332)     ||
      (lpThisTexInfo->format == GR_TEXFMT_YIQ_422)     ||
      (lpThisTexInfo->format == GR_TEXFMT_ALPHA_8)     ||
      (lpThisTexInfo->format == GR_TEXFMT_INTENSITY_8) ||
      (lpThisTexInfo->format == GR_TEXFMT_P_8))
    nBytePerPixel = 1;  /* 8-bit formats */
  else
    nBytePerPixel = 2;  /* 16-bit formats */

  lpvCurrLod = lpThisTexInfo->data;

  if ((deTlTexture->info.format != GR_TEXFMT_YIQ_422)   ||
      (deTlTexture->info.format != GR_TEXFMT_P_8)       ||
      (deTlTexture->info.format != GR_TEXFMT_AYIQ_8422) ||
      (deTlTexture->info.format != GR_TEXFMT_AP_88) )
    deTlTexture->tableType = NO_TABLE; /* fuck me */
  
  for ( tCurrLod = lpThisTexInfo->largeLod; 
        tCurrLod <= (lpThisTexInfo->smallLod); ++tCurrLod )
  {
    if ( nBytePerPixel == 1 )
    {
      xLodNumBytes = prGetLodSize( lpThisTexInfo, tCurrLod );
      for (spLilTex = (FxU8*) lpvCurrLod; 
           spLilTex < (FxU8*) ((FxU32)lpvCurrLod + xLodNumBytes);
           ++spLilTex )  
        *spLilTex = (FxU8) daColors[tCurrLod];

      lpvCurrLod = (void*) spLilTex;
    }
    else
    {
      xLodNumBytes = prGetLodSize( lpThisTexInfo, tCurrLod );
      for (mpBigTex = (FxU16*) lpvCurrLod; 
           mpBigTex < (FxU16*) ((FxU32)lpvCurrLod + xLodNumBytes);
           ++mpBigTex )  
        *mpBigTex = daColors[tCurrLod];

      lpvCurrLod = (void*) mpBigTex;
    }
  }
}
/*-------------------------------------------------------------------*/



/*-------------------------------------------------------------------
  Function: plTxMnDwnldMipMapLod
  Date: 7/7/97
  Implementor(s): psw
  Library: 
  Description:
    downloads a mipmap lod, deLod, from a src TxMnTexture to a dst
    TxMnTexture.  
    ***multibase not yet supported, not clearly defined
  Arguments:
    hTexId_t dstTexId - destination TxMnTexture id
    hTexId_t srcTexId - source TxMnTexture id
    GrLOD_t deLod - the lod to download in srcTexId 
  Return: 
    void
  -------------------------------------------------------------------*/
void 
plTxMnDwnldMipMapLod( hTexId_t dstTexId, hTexId_t srcTexId, GrLOD_t deLod )
{
  /* definitions  */
  GrTexInfo        *lpDstTexInfo,
                   *lpSrcTexInfo;
  tlTxMnTexture_t  *lpDstTxMnTexture,
                   *lpSrcTxMnTexture;
  void             *lpvData;
  FxU32            thisAdd;
  GrLOD_t          thisLgLod;
  FxU32            thisMask;

  /* assumptions */
  assert( dstTexId < hFstOpnTexId );
  assert( srcTexId < hFstOpnTexId );
  assert( IS_DOWNLOADABLE(tlTxMnMngr[dstTexId]) );

  /* initializations */
  lpDstTxMnTexture = &tlTxMnMngr[dstTexId];
  lpSrcTxMnTexture = &tlTxMnMngr[srcTexId];
  lpDstTexInfo = &lpDstTxMnTexture->lpTlTextureInfo->info;
  lpSrcTexInfo = &lpSrcTxMnTexture->lpTlTextureInfo->info;

  /* just some error checking */
  if ( ((lpDstTexInfo->format) != (lpSrcTexInfo->format)) ||
       ((lpDstTexInfo->aspectRatio) != (lpSrcTexInfo->aspectRatio)) ||
       ((lpSrcTxMnTexture->origLargeLod) > deLod) ||
       ((lpSrcTxMnTexture->origSmallLod) < deLod) ||
       ((lpDstTxMnTexture->origLargeLod) > deLod) ||
       ((lpDstTxMnTexture->origSmallLod) < deLod) )
    return; /* nonfatal error, just return */

  /* code */
  /* get the data */
  lpvData = malloc(prGetLodSize(lpSrcTexInfo, deLod));
  assert(lpvData);
  prGetLod(lpSrcTexInfo, deLod, lpvData);  
  /* now i have an lod to download */

  /* get the address */
  thisAdd = prGetTxMnAdd( dstTexId, deLod );
  /* get the mask */
  if (!IS_TRILINEAR(*lpDstTxMnTexture))
    thisMask = GR_MIPMAPLEVELMASK_BOTH;
  else  /* it is trilinear */
    thisMask = deLod%2 ? GR_MIPMAPLEVELMASK_ODD : GR_MIPMAPLEVELMASK_EVEN;

  /* get the largeLod, SPECIAL case for multibase */  
  if (!IS_MULTIBASE(*lpDstTxMnTexture))
    thisLgLod = lpDstTexInfo->largeLod;
  else /* is is multibase */
    thisLgLod = deLod < GR_LOD_32 ? deLod : 
      lpDstTexInfo->largeLod > GR_LOD_32 ? 
      lpDstTexInfo->largeLod : GR_LOD_32; 

  grTexDownloadMipMapLevel( currTMU, thisAdd, deLod, thisLgLod,
                              lpDstTexInfo->aspectRatio, lpDstTexInfo->format, 
                              thisMask, lpvData );
  free(lpvData);
} /* plTxMnDwnldMipMapLod */
/*-------------------------------------------------------------------*/



/*-------------------------------------------------------------------
  Function: plTxMnDwnldMipMapLod
  Date: 7/7/97
  Implementor(s): psw
  Library: 
  Description:
    downloads a mipmap lod, deLod, from a src TxMnTexture to a dst
    TxMnTexture.  
  Arguments:
    hTexId_t dstTexId - destination TxMnTexture id
    hTexId_t srcTexId - source TxMnTexture id
    GrLOD_t deLod - the lod to download in srcTexId 
  Return: 
    void
  -------------------------------------------------------------------*/
void 
plTxMnDwnldMipMapLodPrtl( hTexId_t dstTexId, hTexId_t srcTexId, GrLOD_t deLod,
                          int nFstRow, int nLstRow )
{
  /* definitions  */
  GrTexInfo        *lpDstTexInfo,
                   *lpSrcTexInfo;
  tlTxMnTexture_t  *lpDstTxMnTexture,
                   *lpSrcTxMnTexture;
  void             *lpvData;
  FxU32            thisAdd;
  GrLOD_t          thisLgLod;
  FxU32            thisMask;

  /* assumptions */
  assert( dstTexId < hFstOpnTexId );
  assert( srcTexId < hFstOpnTexId );
  assert( IS_DOWNLOADABLE(tlTxMnMngr[dstTexId]) );
  assert( nFstRow <= nLstRow );

  /* initializations */
  lpDstTxMnTexture = &tlTxMnMngr[dstTexId];
  lpSrcTxMnTexture = &tlTxMnMngr[srcTexId];
  lpDstTexInfo = &lpDstTxMnTexture->lpTlTextureInfo->info;
  lpSrcTexInfo = &lpSrcTxMnTexture->lpTlTextureInfo->info;

  /* code */
  /* just some error checking */
  if ( ((lpDstTexInfo->format) != (lpSrcTexInfo->format))            ||
       ((lpDstTexInfo->aspectRatio) != (lpSrcTexInfo->aspectRatio))  ||
       ((lpSrcTxMnTexture->origLargeLod) > deLod)                    ||
       ((lpSrcTxMnTexture->origSmallLod) < deLod)                    ||
       ((lpDstTxMnTexture->origLargeLod) > deLod)                    ||
       ((lpDstTxMnTexture->origSmallLod) < deLod)                    ||
       (nFstRow > prGetLodHeight( deLod, lpDstTexInfo->aspectRatio)) )
    return; /* nonfatal error, just return */

  /* because i'm a nice guy */
//  if (nLstRow >= prGetLodHeight( deLod, lpDstTexInfo->aspectRatio))
//    nLstRow = (-1) + prGetLodHeight( deLod, lpDstTexInfo->aspectRatio);
  /* not so nice */

  /* get the data */
  lpvData = malloc(prGetLodSize(lpSrcTexInfo, deLod));
  assert(lpvData);
  prGetLod(lpSrcTexInfo, deLod, lpvData);
  /* now i have an lod to download */
 
  /* get the address */
  thisAdd = prGetTxMnAdd( dstTexId, deLod );

  /* get the mask */
  if (!IS_TRILINEAR(*lpDstTxMnTexture))
    thisMask = GR_MIPMAPLEVELMASK_BOTH;
  else  /* it is trilinear */
    thisMask = deLod%2 ? GR_MIPMAPLEVELMASK_ODD : GR_MIPMAPLEVELMASK_EVEN;

  /* get the largeLod, SPECIAL case for multibase */  
  if (!IS_MULTIBASE(*lpDstTxMnTexture))
    thisLgLod = lpDstTexInfo->largeLod;
  else /* is is multibase */
    thisLgLod = deLod < GR_LOD_32 ? deLod : 
      lpDstTexInfo->largeLod > GR_LOD_32 ? 
      lpDstTexInfo->largeLod : GR_LOD_32; 

  grTexDownloadMipMapLevelPartial( currTMU, thisAdd, deLod, thisLgLod,
                                     lpDstTexInfo->aspectRatio, lpDstTexInfo->format, 
                                     thisMask, lpvData, nFstRow, nLstRow );
   free(lpvData);
}
/*-------------------------------------------------------------------*/



/* static helpers */

/*-------------------------------------------------------------------
  Function: prGetTxMnAdd
  Date: 7/14/97
  Implementor(s): psw
  Library: 
  Description:
    returns the address of the texture seg in linear memory that contains
    the LOD being sought based on TxMn id 
  Arguments:
    hTexId_t deTexId - destination TxMnTexture id
    GrLOD_t deLod - the lod to locate the seg in
  Return: 
    FxU32 - address of the seg containing deLod
  -------------------------------------------------------------------*/
static FxU32
prGetTxMnAdd( hTexId_t deTexId, GrLOD_t deLod )
{
  /* definitions */
  tlTxMnTexture_t  *lpThisTxMnTxtre;
  FxU32            xAdd;

  /* initializations */
  lpThisTxMnTxtre = &tlTxMnMngr[deTexId];
 
  /* code */
  if (!IS_MULTIBASE(*lpThisTxMnTxtre))
  {  /* not multibased */
    if (!IS_TRILINEAR(*lpThisTxMnTxtre))
      xAdd = lpThisTxMnTxtre->u0.bothAdd;
    else  /* IS_TRILINEAR */
      xAdd = (deLod%2) ? lpThisTxMnTxtre->u1.oddAdd :
                         lpThisTxMnTxtre->u0.evenAdd ;
  } /* if !IS_MULTIBASE */
  else /* this is going to be fucking ugly! */
  {  /* else it is multibased */
    if (!IS_TRILINEAR(*lpThisTxMnTxtre))
    {
      switch ( deLod )
      {  /* some things are better done with a switch... */
        case GR_LOD_256:
          xAdd = (FxU32) &lpThisTxMnTxtre->u0.multiR0Add;
        break;          

        case GR_LOD_128:
          xAdd = (FxU32) &lpThisTxMnTxtre->u1.multiR1Add; 
        break;          

        case GR_LOD_64:
          xAdd = (FxU32) &lpThisTxMnTxtre->u2.multiR2Add; 
        break;          

        default:
          xAdd = (FxU32) &lpThisTxMnTxtre->u3.multiR3Add; 
        break;
      }
    } /* if !IS_TRILINEAR */
    else 
    {
      switch ( deLod )
      {  /* some things are better done with a switch... */
        case GR_LOD_256:
          xAdd = (FxU32) &lpThisTxMnTxtre->u0.multiR0EvenAdd;
        break;          

        case GR_LOD_128:
          xAdd = (FxU32) &lpThisTxMnTxtre->u1.multiR1OddAdd; 
        break;          

        case GR_LOD_64:
          xAdd = (FxU32) &lpThisTxMnTxtre->u2.multiR2EvenAdd; 
        break;          

        default: /* deLod <= 32_TO_1, key is could be < */
          xAdd = (deLod % 2) ? (FxU32) &lpThisTxMnTxtre->u3.multiR3OddAdd :
                               (FxU32) &lpThisTxMnTxtre->u4.multiR3EvenAdd;
        break;
      }
    }  /* else IS_TRILINEAR */
  } /* else IS_MULTIBASE */

  return (xAdd);
}
/*-------------------------------------------------------------------*/



/*-------------------------------------------------------------------
  Function: prGetLod
  Date: 7/7/97
  Implementor(s): psw
  Library: 
  Description:
    gets the lod specific data out of a GrTexInfo designated by 
    lpTexInfo, used primarily by plTxMnDwnldMipMapLod
  Arguments:
    GrTexInfo *lpTexInfo - destination TxMnTexture id
    GrLOD_t deLod - the lod get in deTexId 
    void *lpvData - pointer to data to propagate
  Return: 
    void
  -------------------------------------------------------------------*/
static void
prGetLod( GrTexInfo *lpTexInfo, GrLOD_t deLod, void *lpvData )
{
  /* definitions */
  GrTexInfo  thisTexInfo, 
             *lpThisTexInfo;
  GrLOD_t    lod;
  FxU32      i, xNumBytes;
  FxU8       *lpSrc,
             *lpDst;
  
  /* initializations */
  lpThisTexInfo = &thisTexInfo;
  *lpThisTexInfo = *lpTexInfo;  /* munge a copy */

  /* code */
  /* mod copy to get deLod at the top */
  for ( lod = lpThisTexInfo->largeLod; lod < deLod; ++lod )
    prPopLod( lpThisTexInfo, topOfMipMap );

  /* get the size of top lod  */
  xNumBytes = prGetLodSize( lpThisTexInfo, lpThisTexInfo->largeLod );

  /* read the top lod data into *lpvData */
  lpSrc = (FxU8*) lpThisTexInfo->data;
  lpDst = (FxU8*) lpvData;

  for ( i = 0; i < xNumBytes; ++i )
    *lpDst++ = *lpSrc++;
}
/*-------------------------------------------------------------------*/



/*-------------------------------------------------------------------
  Function: prPushLod
  Date: 7/7/97
  Implementor(s): psw
  Library: 
  Description:
    This routine actually modifies the GrTexInfo struct data to push
    data BACK on the top or bottom of the mipmap. pushes a single lod
  Arguments:
    GrTexInfo *lpDeTexInfo
    mipMapEnd_t deMipMapEnd
  Return: 
    none
  -------------------------------------------------------------------*/
static void 
prPushLod( GrTexInfo *lpDeTexInfo, mipMapEnd_t deMipMapEnd )
{
  if (deMipMapEnd == topOfMipMap)
    lpDeTexInfo->data = (void*)( (FxU32) lpDeTexInfo->data - 
                          prGetLodSize(lpDeTexInfo, --lpDeTexInfo->largeLod));
  else
    ++lpDeTexInfo->smallLod;
}
/*-------------------------------------------------------------------*/



/*-------------------------------------------------------------------
  Function: prPopLod
  Date: 7/7/97
  Implementor(s): psw
  Library: 
  Description:
    This routine actually modifies the GrTexInfo struct data to pop
    data off the top or bottom of the mipmap. pops off a single lod
  Arguments:
    GrTexInfo *lpDeTexInfo
    mipMapEnd_t deMipMapEnd
  Return: 
    none
  -------------------------------------------------------------------*/
static void 
prPopLod( GrTexInfo *lpDeTexInfo, mipMapEnd_t deMipMapEnd )
{
  if (deMipMapEnd == topOfMipMap)
    lpDeTexInfo->data = (void*)( (FxU32) lpDeTexInfo->data + 
                          prGetLodSize(lpDeTexInfo, lpDeTexInfo->largeLod++)); 
  else
    --lpDeTexInfo->smallLod;
}
/*-------------------------------------------------------------------*/



/*-------------------------------------------------------------------
  Function: prGetLodSize
  Date: 6/18/97
  Implementor(s): psw
  Library: 
  Description:
    returns the size, in number of bytes, of an LOD  based upon format, 
    LOD level and aspect ratio.  this routine is meant to be used on 
    the void* in the GrTexInfo struct.
  Arguments:
    GrTexInfo *const disTexInfo - pointer to the Texture Info
    GrLOD_t disLOD - the lod to get the size of
  Return: 
    FxU32
  -------------------------------------------------------------------*/
static FxU32 
prGetLodSize( GrTexInfo *const deTexInfo, GrLOD_t deLod )
{
  /* definitions */
  int    nPxlFctr;
  int    yAspctDvdr[] =  {3, 2, 1, 0, 1, 2, 3};  /* ie. GR_ASPECT_1x8 = 2^3 */
  int    yLodFctr[]   =  {8, 7, 6, 5, 4, 3, 2, 1, 0};  /* ie. GR_LOD_256 = 2^8 */
  FxU32  xNumBytes;
  GrAspectRatio_t  thisAR;
  GrLOD_t          thisLod;

  /* code */
  if ((deTexInfo->format == GR_TEXFMT_RGB_332)     ||
      (deTexInfo->format == GR_TEXFMT_YIQ_422)     ||
      (deTexInfo->format == GR_TEXFMT_ALPHA_8)     ||
      (deTexInfo->format == GR_TEXFMT_INTENSITY_8) ||
      (deTexInfo->format == GR_TEXFMT_P_8))
    nPxlFctr = 1;  /* 8-bit formats */
  else
    nPxlFctr = 2;  /* all else are 16-bit formats */
    
  thisAR = deTexInfo->aspectRatio;  /* thisAspectRatio */
  thisLod = deLod;                  /* thisLod */

  /* the general formula is xNumBytes=nBytesPerPixel*nPixels
  ** nPixels=maxPixels/aspectRatio
  ** maxPixels for GR_LOD_256=256*256 or 2^8*2^8, hence the following formula...*/
  if ( yAspctDvdr[thisAR] < yLodFctr[thisLod] )
    xNumBytes = nPxlFctr << yLodFctr[thisLod] << yLodFctr[thisLod] >> 
                  yAspctDvdr[thisAR];
  else
    xNumBytes = nPxlFctr << yLodFctr[thisLod];

  return xNumBytes;
}
/*-------------------------------------------------------------------*/



/*-------------------------------------------------------------------
  Function: prTexMultiMemRequired
  Date: 6/27/97
  Implementor(s): psw
  Library: 
  Description:
    calculates and returns the amount of memory required for the 
    individual multibase texture segments in BOTH, EVEN or ODD modes
    traps cases for missing LODs
  Arguments:
    FxU32 deMipMask (GR_MIPMAPLEVELMASK)
    GrTexBaseRange_t deRange
    GrAspectRatio_t deRatio
    GrTextureFormat_t deFormat
  Return: 
    FxU32 - memory required
  -------------------------------------------------------------------*/
static 
FxU32 prTexMultiMemRequired( FxU32 deMipMask, GrTexBaseRange_t deMultiSeg,
                             GrTexInfo* deTexInfo )
{
  /* definitions */
  GrTexInfo   tmpTexInfo, 
              *lpTmpTexInfo;  /* create a copy of the GrTexInfo */
  FxU32       thisSegMemReq = 0x0;
  GrLOD_t     lod;

  /* initializations */
  lpTmpTexInfo = &tmpTexInfo; 
  *lpTmpTexInfo = *deTexInfo;  /* copy passed data, then prepare to munge */

  /* code */
  if ((lpTmpTexInfo->largeLod > (FxI32) deMultiSeg) ||
      (lpTmpTexInfo->smallLod < (FxI32) deMultiSeg))
    return 0; /* the deMultiSeg !exist in the lod range of info */

  /* else it does */
  for (lod = lpTmpTexInfo->largeLod; lod < (FxI32) deMultiSeg; ++lod)
    prPopLod( lpTmpTexInfo, topOfMipMap ); /* munge the top */

  if ( deMultiSeg != GR_TEXBASE_32_TO_1)
    lpTmpTexInfo->smallLod = lpTmpTexInfo->largeLod; /* munge the btm */

  thisSegMemReq = grTexTextureMemRequired( deMipMask, lpTmpTexInfo );

  return thisSegMemReq;
}  /* prTexMultiMemRequired */
/*-------------------------------------------------------------------*/



/*-------------------------------------------------------------------*/
static int 
prGetLodWidth( GrLOD_t lod, GrAspectRatio_t aspect )
{
  int   yAspctWdvdr[] = {0, 0, 0, 0, 1, 2, 3};
  int   yLodFctr[] = {8, 7, 6, 5, 4, 3, 2, 1, 0};
  int   val;

  val = yLodFctr[lod] > yAspctWdvdr[aspect] ? 
          1 << yLodFctr[lod] >> yAspctWdvdr[aspect] : 1;

  return( val );
}
/*-------------------------------------------------------------------*/



/*-------------------------------------------------------------------*/
static int 
prGetLodHeight( GrLOD_t lod, GrAspectRatio_t aspect )
{
  int   yAspctHdvdr[] = {3, 2, 1, 0, 0, 0, 0};
  int   yLodFctr[] = {8, 7, 6, 5, 4, 3, 2, 1, 0};
  int   val;

  val = yLodFctr[lod] > yAspctHdvdr[aspect] ? 
          1 << yLodFctr[lod] >> yAspctHdvdr[aspect] : 1;
  
  return( val );
}
/*-------------------------------------------------------------------*/



/*-------------------------------------------------------------------*/
static FxU32
prGetMultiMemBump( const GrTexInfo *const deTexInfo, 
                     GrTexBaseRange_t deTexBase,
                     FxU32 deMipMask )
{ /* this fixes  some multibase fuckage */
  /* definitions */
  FxU32      xMemBump,
             xAllMemReq,
             xSegMemReq;
  GrTexInfo  tmpTexInfo,
             *lpTmpTexInfo;

  /* initializations */
  lpTmpTexInfo = &tmpTexInfo;
  *lpTmpTexInfo = *deTexInfo;

  /* code */
  lpTmpTexInfo->largeLod = GR_LOD_256;
  xAllMemReq = grTexTextureMemRequired( deMipMask, lpTmpTexInfo );

  lpTmpTexInfo->largeLod = deTexBase != GR_TEXBASE_32_TO_1 ? deTexBase :
                             lpTmpTexInfo->largeLod > (FxI32) deTexBase ? 
                             lpTmpTexInfo->largeLod : GR_LOD_32;
  xSegMemReq = grTexTextureMemRequired( deMipMask, lpTmpTexInfo );

  xMemBump = xAllMemReq - xSegMemReq;
  return (xMemBump);
}
