/* Emacs style mode select   -*- C++ -*-
 *-----------------------------------------------------------------------------
 *
 *
 *  PrBoom a Doom port merged with LxDoom and LSDLDoom
 *  based on BOOM, a modified and improved DOOM engine
 *  Copyright (C) 1999 by
 *  id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman
 *  Copyright (C) 1999-2000 by
 *  Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze
 *
 *  This program is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU General Public License
 *  as published by the Free Software Foundation; either version 2
 *  of the License, or (at your option) any later version.
 *
 *  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, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 *  02111-1307, USA.
 *
 * DESCRIPTION:
 *      Preparation of data for rendering,
 *      generation of lookups, caching, retrieval by name.
 *
 *-----------------------------------------------------------------------------*/

#include "doomstat.h"
#include "w_wad.h"
#include "r_main.h"
#include "r_sky.h"
#include "i_system.h"
#include "m_swap.h"
#include "p_tick.h"
//#include "lprintf.h"  // jff 08/03/98 - declaration of lprintf
#include "rockmacros.h"
//
// Graphics.
// DOOM graphics for walls and sprites
// is stored in vertical runs of opaque pixels (posts).
// A column is composed of zero or more posts,
// a patch or sprite is composed of zero or more columns.
//

//
// Texture definition.
// Each texture is composed of one or more patches,
// with patches being lumps stored in the WAD.
// The lumps are referenced by number, and patched
// into the rectangular texture space using origin
// and possibly other attributes.
//

typedef struct
{
   short originx;
   short originy;
   short patch;
   short stepdir;         // unused in Doom but might be used in Phase 2 Boom
   short colormap;        // unused in Doom but might be used in Phase 2 Boom
}
PACKEDATTR mappatch_t;

typedef struct
{
   char       name[8];
   boolean    masked;
   short      width;
   short      height;
   char       pad[4];       // unused in Doom but might be used in Boom Phase 2
   short      patchcount;
   mappatch_t patches[1];
}
PACKEDATTR maptexture_t;

// A maptexturedef_t describes a rectangular texture, which is composed
// of one or more mappatch_t structures that arrange graphic patches.

// killough 4/17/98: make firstcolormaplump,lastcolormaplump external
int firstcolormaplump, lastcolormaplump;      // killough 4/17/98

int       firstflat, lastflat, numflats;
int       firstspritelump, lastspritelump, numspritelumps;
int       numtextures;
static texture_t **textures;
fixed_t   *textureheight; //needed for texture pegging (and TFE fix - killough)
int       *flattranslation;             // for global animation
int       *texturetranslation;
// needed for pre-rendering
fixed_t   *spritewidth, *spriteoffset, *spritetopoffset;

//
// MAPTEXTURE_T CACHING
// When a texture is first needed,
//  it counts the number of composite columns
//  required in the texture and allocates space
//  for a column directory and any new columns.
// The directory will simply point inside other patches
//  if there is only one patch in a given column,
//  but any columns with multiple patches
//  will have new column_ts generated.
//

//
// R_DrawColumnInCache
// Clip and draw a column
//  from a patch into a cached post.
//
// Rewritten by Lee Killough for performance and to fix Medusa bug
//

void R_DrawColumnInCache(const column_t *patch, byte *cache,
                         int originy, int cacheheight, byte *marks)
{
   while (patch->topdelta != 0xff)
   {
      int count = patch->length;
      int position = originy + patch->topdelta;

      if (position < 0)
      {
         count += position;
         position = 0;
      }

      if (position + count > cacheheight)
         count = cacheheight - position;

      if (count > 0)
      {
         memcpy (cache + position, (byte *)patch + 3, count);

         // killough 4/9/98: remember which cells in column have been drawn,
         // so that column can later be converted into a series of posts, to
         // fix the Medusa bug.

         memset (marks + position, 0xff, count);
      }

      patch = (column_t *)((byte *) patch + patch->length + 4);
   }
}

//
// R_GenerateComposite
// Using the texture definition,
//  the composite texture is created from the patches,
//  and each column is cached.
//
// Rewritten by Lee Killough for performance and to fix Medusa bug

void R_GenerateComposite(int texnum)
{
   texture_t *texture = textures[texnum];
   byte *block = Z_Malloc(texture->compositesize, PU_STATIC,
                          (void **)&texture->composite);
   // Composite the columns together.
   texpatch_t *patch = texture->patches;
   short *collump = texture->columnlump;
   unsigned *colofs = texture->columnofs; // killough 4/9/98: make 32-bit
   int i = texture->patchcount;
   // killough 4/9/98: marks to identify transparent regions in merged textures
   byte *marks = calloc(texture->width, texture->height), *source;

   for (; --i >=0; patch++)
   {
      const patch_t *realpatch = W_CacheLumpNum(patch->patch); // cph
      int x1 = patch->originx, x2 = x1 + SHORT(realpatch->width);
      const int *cofs = realpatch->columnofs-x1;
      if (x1<0)
         x1 = 0;
      if (x2 > texture->width)
         x2 = texture->width;
      for (; x1<x2 ; x1++)
         if (collump[x1] == -1)      // Column has multiple patches?
            // killough 1/25/98, 4/9/98: Fix medusa bug.
            R_DrawColumnInCache((column_t*)((byte*)realpatch+LONG(cofs[x1])),
                                block+colofs[x1],patch->originy,texture->height,
                                marks + x1 * texture->height);

      W_UnlockLumpNum(patch->patch); // cph - unlock the patch lump
   }

   // killough 4/9/98: Next, convert multipatched columns into true columns,
   // to fix Medusa bug while still allowing for transparent regions.

   source = malloc(texture->height);       // temporary column
   for (i=0; i < texture->width; i++)
      if (collump[i] == -1)                 // process only multipatched columns
      {
         column_t *col = (column_t *)(block + colofs[i] - 3);  // cached column
         const byte *mark = marks + i * texture->height;
         int j = 0;

         // save column in temporary so we can shuffle it around
         memcpy(source, (byte *) col + 3, texture->height);

         for (;;)  // reconstruct the column by scanning transparency marks
         {
            while (j < texture->height && !mark[j]) // skip transparent cells
               j++;
            if (j >= texture->height)           // if at end of column
            {
               col->topdelta = -1;             // end-of-column marker
               break;
            }
            col->topdelta = j;                  // starting offset of post
            for (col->length=0; j < texture->height && mark[j]; j++)
               col->length++;                    // count opaque cells
            // copy opaque cells from the temporary back into the column
            memcpy((byte *) col + 3, source + col->topdelta, col->length);
            col = (column_t *)((byte *) col + col->length + 4); // next post
         }
      }
   free(source);         // free temporary column
   free(marks);          // free transparency marks

   // Now that the texture has been built in column cache,
   // it is purgable from zone memory.

   Z_ChangeTag(block, PU_CACHE);
}

//
// R_GenerateLookup
//
// Rewritten by Lee Killough for performance and to fix Medusa bug
//

static void R_GenerateLookup(int texnum, int *const errors)
{
   texture_t *texture = textures[texnum];

   // killough 4/9/98: make column offsets 32-bit;
   // clean up malloc-ing to use sizeof
   // CPhipps - moved allocing here
   short *collump = texture->columnlump =
                       Z_Malloc(texture->width*sizeof(*texture->columnlump), PU_STATIC,0);
   unsigned *colofs = texture->columnofs =
                         Z_Malloc(texture->width*sizeof(*texture->columnofs), PU_STATIC,0);

   // killough 4/9/98: keep count of posts in addition to patches.
   // Part of fix for medusa bug for multipatched 2s normals.

   struct {
      unsigned short patches, posts;
   }
   *count = calloc(sizeof *count, texture->width);

   {
      int i = texture->patchcount;
      const texpatch_t *patch = texture->patches;

      while (--i >= 0)
      {
         int pat = patch->patch;
         const patch_t *realpatch = W_CacheLumpNum(pat);
         int x1 = patch++->originx, x2 = x1 + SHORT(realpatch->width), x = x1;
         const int *cofs = realpatch->columnofs-x1;

         if (x2 > texture->width)
            x2 = texture->width;
         if (x1 < 0)
            x = 0;
         for ( ; x<x2 ; x++)
         {
            // killough 4/9/98: keep a count of the number of posts in column,
            // to fix Medusa bug while allowing for transparent multipatches.

            const column_t *col = (column_t*)((byte*)realpatch+LONG(cofs[x]));
            for (;col->topdelta != 0xff; count[x].posts++)
               col = (column_t *)((byte *) col + col->length + 4);
            count[x].patches++;
            collump[x] = pat;
            colofs[x] = LONG(cofs[x])+3;
         }

         W_UnlockLumpNum(pat);
      }
   }

   // Composited texture not created yet.
   texture->composite = NULL;

   // Now count the number of columns
   //  that are covered by more than one patch.
   // Fill in the lump / offset, so columns
   //  with only a single patch are all done.

   {
      int x = texture->width;
      int height = texture->height;
      int csize = 0;

      while (--x >= 0)
      {
         if (!count[x].patches)          // killough 4/9/98
         {
            //jff 8/3/98 use logical output routine
            printf("\nR_GenerateLookup: Column %d is without a patch in texture %s",
                   x, texture->name);
            if (errors)
               ++*errors;
            else
               I_Error("R_GenerateLookup: Failed");
         }
         if (count[x].patches > 1)       // killough 4/9/98
         {
            // killough 1/25/98, 4/9/98:
            //
            // Fix Medusa bug, by adding room for column header
            // and trailer bytes for each post in merged column.
            // For now, just allocate conservatively 4 bytes
            // per post per patch per column, since we don't
            // yet know how many posts the merged column will
            // require, and it's bounded above by this limit.

            collump[x] = -1;              // mark lump as multipatched
            colofs[x] = csize + 3;        // three header bytes in a column
            csize += 4*count[x].posts+1;  // 1 stop byte plus 4 bytes per post
         }
         csize += height;                  // height bytes of texture data
      }
      texture->compositesize = csize;
   }
   free(count);                    // killough 4/9/98
}

//
// R_GetColumn
//

const byte *R_GetColumn(int tex, int col)
{
   const texture_t *texture = textures[tex];
   if (!texture->columnlump)
      R_GenerateLookup(tex, NULL);
   {
      int lump = texture->columnlump[col &= texture->widthmask];
      int ofs  = texture->columnofs[col]; // cph - WARNING: must be after the above line
      // cph - remember the last lump, so we can unlock it if no longer needed,
      //  or reuse it if possible to reduce lump locking/unlocking
      static int lastlump = -1;
      static const byte* lastlumpdata;

      if ((lump<=0) && (lastlump<=0))
         lump = lastlump; // cph - force equal

      if (lump != lastlump)
      {
         // cph - must change the cached lump
         if (lastlump>0)
            W_UnlockLumpNum(lastlump);

         if ((lastlump = lump) > 0)
            lastlumpdata = W_CacheLumpNum(lump);
#ifdef RANGECHECK

         else
            lastlumpdata = NULL;
#endif

      }

      if (lump > 0)
         return lastlumpdata + ofs;

      if (!texture->composite)
         R_GenerateComposite(tex);

      return texture->composite + ofs;
   }
}

//
// R_InitTextures
// Initializes the texture list
//  with the textures from the world map.
//

void R_InitTextures (void)
{
   maptexture_t *mtexture;
   texture_t    *texture;
   mappatch_t   *mpatch;
   texpatch_t   *patch;
   int  i, j;
   int         maptex_lump[2] = {-1, -1};
   const int  *maptex;
   const int  *maptex1, *maptex2;
   char name[9];
   int names_lump; // cph - new wad lump handling
   const unsigned char *names; // cph -
   const char *name_p;// const*'s
   int  *patchlookup;
   int  totalwidth;
   int  nummappatches;
   int  offset;
   int  maxoff, maxoff2;
   int  numtextures1, numtextures2;
   const int *directory;
   int  errors = 0;

   // Load the patch names from pnames.lmp.
   name[8] = 0;
   names = W_CacheLumpNum(names_lump = W_GetNumForName("PNAMES"));
   nummappatches = (names[3]<<24)|(names[2]<<16)|(names[1]<<8)|names[0];
   name_p = names+4;
   patchlookup = malloc(nummappatches*sizeof(*patchlookup));  // killough

   for (i=0 ; i<nummappatches ; i++)
   {
      strncpy (name,name_p+i*8, 8);
      patchlookup[i] = W_CheckNumForName(name);
      if (patchlookup[i] == -1)
      {
         // killough 4/17/98:
         // Some wads use sprites as wall patches, so repeat check and
         // look for sprites this time, but only if there were no wall
         // patches found. This is the same as allowing for both, except
         // that wall patches always win over sprites, even when they
         // appear first in a wad. This is a kludgy solution to the wad
         // lump namespace problem.

         patchlookup[i] = (W_CheckNumForName)(name, ns_sprites);

         if (patchlookup[i] == -1 && devparm)
            //jff 8/3/98 use logical output routine
            printf("\nWarning: patch %.8s, index %d does not exist",name,i);
      }
   }
   W_UnlockLumpNum(names_lump); // cph - release the lump

   // Load the map texture definitions from textures.lmp.
   // The data is contained in one or two lumps,
   //  TEXTURE1 for shareware, plus TEXTURE2 for commercial.

   maptex = maptex1 = W_CacheLumpNum(maptex_lump[0] = W_GetNumForName("TEXTURE1"));
   numtextures1 = LONG(*maptex);
   maxoff = W_LumpLength(maptex_lump[0]);
   directory = maptex+1;

   if (W_CheckNumForName("TEXTURE2") != -1)
   {
      maptex2 = W_CacheLumpNum(maptex_lump[1] = W_GetNumForName("TEXTURE2"));
      numtextures2 = LONG(*maptex2);
      maxoff2 = W_LumpLength(maptex_lump[1]);
   }
   else
   {
      maptex2 = NULL;
      numtextures2 = 0;
      maxoff2 = 0;
   }
   numtextures = numtextures1 + numtextures2;

   // killough 4/9/98: make column offsets 32-bit;
   // clean up malloc-ing to use sizeof

   textures = Z_Malloc(numtextures*sizeof*textures, PU_STATIC, 0);
   textureheight = Z_Malloc(numtextures*sizeof*textureheight, PU_STATIC, 0);

   totalwidth = 0;

   for (i=0 ; i<numtextures ; i++, directory++)
   {
      if (i == numtextures1)
      {
         // Start looking in second texture file.
         maptex = maptex2;
         maxoff = maxoff2;
         directory = maptex+1;
      }

      offset = LONG(*directory);

      if (offset > maxoff)
         I_Error("R_InitTextures: Bad texture directory");

      mtexture = (maptexture_t *) ( (byte *)maptex + offset);

      texture = textures[i] =
                   Z_Malloc(sizeof(texture_t) +
                            sizeof(texpatch_t)*(SHORT(mtexture->patchcount)-1),
                            PU_STATIC, 0);

      texture->width = SHORT(mtexture->width);
      texture->height = SHORT(mtexture->height);
      texture->patchcount = SHORT(mtexture->patchcount);

      /* Mattias EngdegÄrd emailed me of the following explenation of
       * why memcpy doesnt work on some systems:
       * "I suppose it is the mad unaligned allocation
       * going on (and which gcc in some way manages to cope with
       * through the __attribute__ ((packed))), and which it forgets
       * when optimizing memcpy (to a single word move) since it appears
       * to be aligned. Technically a gcc bug, but I can't blame it when
       * it's stressed with that amount of
       * non-standard nonsense."
      * So in short the unaligned struct confuses gcc's optimizer so
      * i took the memcpy out alltogether to avoid future problems-Jess
       */
      /* The above was #ifndef SPARC, but i got a mail from
       * Putera Joseph F NPRI <PuteraJF@Npt.NUWC.Navy.Mil> containing:
       *   I had to use the memcpy function on a sparc machine.  The
       *   other one would give me a core dump.
       * cph - I find it hard to believe that sparc memcpy is broken,
       * but I don't believe the pointers to memcpy have to be aligned
       * either. Use fast memcpy on other machines anyway.
       */
      /*
        proff - I took this out, because Oli Kraus (olikraus@yahoo.com) told
        me the memcpy produced a buserror. Since this function isn't time-
        critical I'm using the for loop now.
      */
      /*
      #ifndef GCC
            memcpy(texture->name, mtexture->name, sizeof(texture->name));
      #else
      */
      {
         unsigned int j;
         for(j=0;j<sizeof(texture->name);j++)
            texture->name[j]=mtexture->name[j];
      }
      /* #endif */

      mpatch = mtexture->patches;
      patch = texture->patches;

      for (j=0 ; j<texture->patchcount ; j++, mpatch++, patch++)
      {
         patch->originx = SHORT(mpatch->originx);
         patch->originy = SHORT(mpatch->originy);
         patch->patch = patchlookup[SHORT(mpatch->patch)];
         if (patch->patch == -1)
         {
            //jff 8/3/98 use logical output routine
            printf("\nR_InitTextures: Missing patch %d in texture %s",
                   SHORT(mpatch->patch), texture->name); // killough 4/17/98
            ++errors;
         }
      }

      texture->columnofs = NULL;
      texture->columnlump = NULL;

      for (j=1; j*2 <= texture->width; j<<=1)
         ;
      texture->widthmask = j-1;
      textureheight[i] = texture->height<<FRACBITS;

      totalwidth += texture->width;
   }

   free(patchlookup);         // killough

   for (i=0; i<2; i++) // cph - release the TEXTUREx lumps
      if (maptex_lump[i] != -1)
         W_UnlockLumpNum(maptex_lump[i]);

   if (errors)
      I_Error("R_InitTextures: %d errors", errors);

   // Precalculate whatever possible.
   if (devparm) // cph - If in development mode, generate now so all errors are found at once
      for (i=0 ; i<numtextures ; i++)
         R_GenerateLookup(i, &errors);

   if (errors)
      I_Error("R_InitTextures: %d errors", errors);

   // Create translation table for global animation.
   // killough 4/9/98: make column offsets 32-bit;
   // clean up malloc-ing to use sizeof

   texturetranslation =
      Z_Malloc((numtextures+1)*sizeof*texturetranslation, PU_STATIC, 0);

   for (i=0 ; i<numtextures ; i++)
      texturetranslation[i] = i;

   // killough 1/31/98: Initialize texture hash table
   for (i = 0; i<numtextures; i++)
      textures[i]->index = -1;
   while (--i >= 0)
   {
      int j = W_LumpNameHash(textures[i]->name) % (unsigned) numtextures;
      textures[i]->next = textures[j]->index;   // Prepend to chain
      textures[j]->index = i;
   }
}

//
// R_InitFlats
//
void R_InitFlats(void)
{
   int i;

   firstflat = W_GetNumForName("F_START") + 1;
   lastflat  = W_GetNumForName("F_END") - 1;
   numflats  = lastflat - firstflat + 1;

   // Create translation table for global animation.
   // killough 4/9/98: make column offsets 32-bit;
   // clean up malloc-ing to use sizeof

   flattranslation =
      Z_Malloc((numflats+1)*sizeof(*flattranslation), PU_STATIC, 0);

   for (i=0 ; i<numflats ; i++)
      flattranslation[i] = i;
}

//
// R_InitSpriteLumps
// Finds the width and hoffset of all sprites in the wad,
// so the sprite does not need to be cached completely
// just for having the header info ready during rendering.
//
void R_InitSpriteLumps(void)
{
   int i;
   const patch_t *patch;

   firstspritelump = W_GetNumForName("S_START") + 1;
   lastspritelump = W_GetNumForName("S_END") - 1;
   numspritelumps = lastspritelump - firstspritelump + 1;

   // killough 4/9/98: make columnd offsets 32-bit;
   // clean up malloc-ing to use sizeof

   spritewidth = Z_Malloc(numspritelumps*sizeof*spritewidth, PU_STATIC, 0);
   spriteoffset = Z_Malloc(numspritelumps*sizeof*spriteoffset, PU_STATIC, 0);
   spritetopoffset =
      Z_Malloc(numspritelumps*sizeof*spritetopoffset, PU_STATIC, 0);

   for (i=0 ; i< numspritelumps ; i++)
   {
      patch = W_CacheLumpNum(firstspritelump+i);
      spritewidth[i] = SHORT(patch->width)<<FRACBITS;
      spriteoffset[i] = SHORT(patch->leftoffset)<<FRACBITS;
      spritetopoffset[i] = SHORT(patch->topoffset)<<FRACBITS;
      W_UnlockLumpNum(firstspritelump+i);
   }
}

//
// R_InitColormaps
//
// killough 3/20/98: rewritten to allow dynamic colormaps
// and to remove unnecessary 256-byte alignment
//
// killough 4/4/98: Add support for C_START/C_END markers
//

void R_InitColormaps(void)
{
   int i;
   firstcolormaplump = W_GetNumForName("C_START");
   lastcolormaplump  = W_GetNumForName("C_END");
   numcolormaps = lastcolormaplump - firstcolormaplump;
   colormaps = Z_Malloc(sizeof(*colormaps) * numcolormaps, PU_STATIC, 0);
   colormaps[0] = (lighttable_t *)W_CacheLumpName("COLORMAP");
   for (i=1; i<numcolormaps; i++)
      colormaps[i] = (lighttable_t *)W_CacheLumpNum(i+firstcolormaplump);
   // cph - always lock
}

// killough 4/4/98: get colormap number from name
// killough 4/11/98: changed to return -1 for illegal names
// killough 4/17/98: changed to use ns_colormaps tag

int R_ColormapNumForName(const char *name)
{
   register int i = 0;
   if (strncasecmp(name,"COLORMAP",8))     // COLORMAP predefined to return 0
      if ((i = (W_CheckNumForName)(name, ns_colormaps)) != -1)
         i -= firstcolormaplump;
   return i;
}

//
// R_InitTranMap
//
// Initialize translucency filter map
//
// By Lee Killough 2/21/98
//

int tran_filter_pct = 66;       // filter percent

#define TSC 12        /* number of fixed point digits in filter percent */

void R_InitTranMap(int progress)
{
  struct _cache {
   unsigned char pct;
   unsigned char playpal[256];
}
cache;
   int lump = W_CheckNumForName("TRANMAP");

   // If a tranlucency filter map lump is present, use it
   if (lump != -1)  // Set a pointer to the translucency filter maps.
      main_tranmap = W_CacheLumpNum(lump);   // killough 4/11/98
   else
   {   // Compose a default transparent filter map based on PLAYPAL.
      const byte *playpal = W_CacheLumpName("PLAYPAL");
      byte       *my_tranmap;

      int cachefd = open(GAMEBASE"tranmap.dat", O_RDWR);

      main_tranmap = my_tranmap = Z_Malloc(256*256, PU_STATIC, 0);  // killough 4/11/98

      // Use cached translucency filter if it's available

      if ((cachefd<0) ? cachefd = open(GAMEBASE"tranmap.dat",O_WRONLY | O_CREAT) , 1 :
            read(cachefd, &cache, sizeof(cache)) != sizeof(cache) ||
            cache.pct != tran_filter_pct ||
            memcmp(cache.playpal, playpal, sizeof cache.playpal) ||
            read(cachefd, my_tranmap, 256*256) != 256*256 ) // killough 4/11/98
      {

         long *stackdeath=malloc(256*7*sizeof(long)); // This was a bunch of static varibles, way too big for rockbox
         long *pal[3], *tot, *pal_w1[3];
         pal[0]=&stackdeath[0];
         pal[1]=&stackdeath[256];
         pal[2]=&stackdeath[256*2];
         tot=&stackdeath[256*3];
         pal_w1[0]=&stackdeath[256*4];
         pal_w1[1]=&stackdeath[256*5];
         pal_w1[2]=&stackdeath[256*6];
         long w1 = ((unsigned long) tran_filter_pct<<TSC)/100;
         long w2 = (1l<<TSC)-w1;

         if (progress)
            printf("Please wait: Tranmap build");
         // First, convert playpal into long int type, and transpose array,
         // for fast inner-loop calculations. Precompute tot array.

         {
            register int i = 255;
            register const unsigned char *p = playpal+255*3;
            do
            {
               register long t,d;
               pal_w1[0][i] = (pal[0][i] = t = p[0]) * w1;
               d = t*t;
               pal_w1[1][i] = (pal[1][i] = t = p[1]) * w1;
               d += t*t;
               pal_w1[2][i] = (pal[2][i] = t = p[2]) * w1;
               d += t*t;
               p -= 3;
               tot[i] = d << (TSC-1);
            }
            while (--i>=0);
         }

         // Next, compute all entries using minimum arithmetic.

         {
            int i,j;
            byte *tp = my_tranmap;
            for (i=0;i<256;i++)
            {
               long r1 = pal[0][i] * w2;
               long g1 = pal[1][i] * w2;
               long b1 = pal[2][i] * w2;
               if (!(i & 31) && progress)
                  //jff 8/3/98 use logical output routine
                  printf(" Computing: %d", 256/32-i/32);
               for (j=0;j<256;j++,tp++)
               {
                  register int color = 255;
                  register long err;
                  long r = pal_w1[0][j] + r1;
                  long g = pal_w1[1][j] + g1;
                  long b = pal_w1[2][j] + b1;
                  long best = LONG_MAX;
                  do
                     if ((err = tot[color] - pal[0][color]*r
                                - pal[1][color]*g - pal[2][color]*b) < best)
                        best = err, *tp = color;
                  while (--color >= 0)
                     ;
               }
            }
         }

         free(stackdeath); // Free this beast

         if (cachefd)        // write out the cached translucency map
         {
            cache.pct = tran_filter_pct;
            memcpy(cache.playpal, playpal, 256);
            lseek(cachefd, 0, SEEK_SET);
            write(cachefd, &cache, sizeof cache);
            write(cachefd,main_tranmap, 256*256);
            // CPhipps - leave close for a few lines...
         }

      }

      if (cachefd)              // killough 11/98: fix filehandle leak
         close(cachefd);

      W_UnlockLumpName("PLAYPAL");
   }
}

//
// R_InitData
// Locates all the lumps
//  that will be used by all views
// Must be called after W_Init.
//
void R_InitData (void)
{
   R_InitTextures ();
   printf ("\nInitTextures");
   R_InitFlats ();
   printf ("\nInitFlats");
   R_InitSpriteLumps ();
   printf ("\nInitSprites");
   if (general_translucency)             // killough 3/1/98
      R_InitTranMap(1);
   R_InitColormaps ();
   printf ("\nInitColormaps");
}

//
// R_FlatNumForName
// Retrieval, get a flat number for a flat name.
//
// killough 4/17/98: changed to use ns_flats namespace
//

int R_FlatNumForName(const char *name)    // killough -- const added
{
   int i = (W_CheckNumForName)(name, ns_flats);
   if (i == -1)
      I_Error("R_FlatNumForName: %s not found", name);
   return i - firstflat;
}

//
// R_CheckTextureNumForName
// Check whether texture is available.
// Filter out NoTexture indicator.
//
// Rewritten by Lee Killough to use hash table for fast lookup. Considerably
// reduces the time needed to start new levels. See w_wad.c for comments on
// the hashing algorithm, which is also used for lump searches.
//
// killough 1/21/98, 1/31/98
//

int R_CheckTextureNumForName(const char *name)
{
   int i = 0;
   if (*name != '-')     // "NoTexture" marker.
   {
      i = textures[W_LumpNameHash(name) % (unsigned) numtextures]->index;
      while (i >= 0 && strncasecmp(textures[i]->name,name,8))
         i = textures[i]->next;
   }
   return i;
}

//
// R_TextureNumForName
// Calls R_CheckTextureNumForName,
//  aborts with error message.
//

int R_TextureNumForName(const char *name)  // const added -- killough
{
   int i = R_CheckTextureNumForName(name);
   if (i == -1)
      I_Error("R_TextureNumForName: %s not found", name);
   return i;
}

//
// R_PrecacheLevel
// Preloads all relevant graphics for the level.
//
// Totally rewritten by Lee Killough to use less memory,
// to avoid using alloca(), and to improve performance.
// cph - new wad lump handling, calls cache functions but acquires no locks

// Structures from p_spec.c
// Used to fully cache animations in the level -> avoids stalls on Hard Drive Systems
typedef struct
{
   boolean     istexture;
   int         picnum;
   int         basepic;
   int         numpics;
   int         speed;

} anim_t;
extern anim_t*  anims;
extern anim_t*  lastanim;

anim_t * isAnim(int flatnum, boolean texcheck)
{
   anim_t *checkf;
   for(checkf=anims; checkf<lastanim; checkf++)
   {
      if((flatnum>=checkf->basepic || flatnum<=checkf->numpics)&&checkf->istexture==texcheck)
         return checkf;
   }
   return 0;
}

void R_PrecacheLevel(void)
{
   register int i, j;
   register byte *hitlist;
   anim_t *cacheanim;

   if (demoplayback)
      return;

   {
      size_t size = numflats > numsprites  ? numflats : numsprites;
      hitlist = malloc((size_t)numtextures > size ? (unsigned)numtextures : size);
   }
   // Precache flats.

   memset(hitlist, 0, numflats);

   for (i = numsectors; --i >= 0; )
      hitlist[sectors[i].floorpic] = hitlist[sectors[i].ceilingpic] = 1;

   // If flat is an animation, load those too
   // Definately not the most efficient, but better then stalls in game
   for(i=0; i<numflats; i++)
      if(hitlist[i])
         if((cacheanim=isAnim(i,0)))
            for(j=0; j<cacheanim->numpics; j++)
               hitlist[cacheanim->basepic+j]=1;

   for (i = numflats; --i >= 0; )
      if (hitlist[i])
         (W_CacheLumpNum)(firstflat + i, 0);

   // Precache textures.

   memset(hitlist, 0, numtextures);

   for (i = numsides; --i >= 0;)
      hitlist[sides[i].bottomtexture] =
         hitlist[sides[i].toptexture] =
            hitlist[sides[i].midtexture] = 1;

   // If texture is an animation, load those too
   // Definately not the most efficient, but better then stalls in game
   for(i=0; i<numsides; i++)
      if(hitlist[i])
         if((cacheanim=isAnim(i,1)))
            for(j=0; j<cacheanim->numpics; j++)
               hitlist[cacheanim->basepic+j]=1;

   // Sky texture is always present.
   // Note that F_SKY1 is the name used to
   //  indicate a sky floor/ceiling as a flat,
   //  while the sky texture is stored like
   //  a wall texture, with an episode dependend
   //  name.

   hitlist[skytexture] = 1;

   for (i = numtextures; --i >= 0; )
      if (hitlist[i])
      {
         texture_t *texture = textures[i];
         int j = texture->patchcount;
         while (--j >= 0)
            (W_CacheLumpNum)(texture->patches[j].patch, 0);
      }

   // Precache sprites.
   memset(hitlist, 0, numsprites);

   {
      thinker_t *th;
      for (th = thinkercap.next ; th != &thinkercap ; th=th->next)
         if (th->function == P_MobjThinker)
            hitlist[((mobj_t *)th)->sprite] = 1;
   }

   for (i=numsprites; --i >= 0;)
      if (hitlist[i])
      {
         int j = sprites[i].numframes;
         while (--j >= 0)
         {
            short *sflump = sprites[i].spriteframes[j].lump;
            int k = 7;
            do
               (W_CacheLumpNum)(firstspritelump + sflump[k], 0);
            while (--k >= 0);
         }
      }
   free(hitlist);
}

// Proff - Added for OpenGL
void R_SetPatchNum(patchnum_t *patchnum, const char *name)
{
   patch_t *patch;

   patch = (patch_t *) W_CacheLumpName(name);
   patchnum->width = patch->width;
   patchnum->height = patch->height;
   patchnum->leftoffset = patch->leftoffset;
   patchnum->topoffset = patch->topoffset;
   patchnum->lumpnum = W_GetNumForName(name);
   W_UnlockLumpName(name);
}