/*
Copyright (C) 1994-1995  Apogee Software, Ltd.
Copyright (C) 2002-2015  icculus.org, GNU/Linux port
Copyright (C) 2017-2018  Steven LeVesque

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.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/
// RT_BUILD.C

#include "rt_def.h"
#include <string.h>
#include "watcom.h"
#include <stdio.h>
#include <stdlib.h>
#include "rt_build.h"
#include "_rt_buil.h"
#include "rt_dr_a.h"
#include "rt_draw.h"
#include "rt_scale.h"
#include "rt_menu.h"
#include "rt_main.h"
#include "isr.h"
#include "rt_util.h"
#include "engine.h"
#include "lumpy.h"
#include "rt_fc_a.h"
#include "z_zone.h"
#include "w_wad.h"
#include "rt_view.h"
#include "rt_cfg.h"
#include "rt_vid.h"

#include "rt_sound.h"
#include "modexlib.h"
#include "rt_str.h"

byte * intensitytable;


// LOCAL VARIABLES

static byte * menubuf;
static byte * menubuffers[2];
static char menutitles[2][40];
static int alternatemenubuf=0;
static int titleshade=16;
static int titleshadedir=1;
static int titleyoffset=0;
static char titlestring[40]="\0";
static int readytoflip;
static boolean MenuBufStarted=false;
static int mindist=0x2700;
static boolean BackgroundDrawn=false;

static plane_t planelist[MAXPLANES],*planeptr;

static int StringShade=16;

extern void (*USL_MeasureString)(const char *, int *, int *, font_t *);

static char strbuf[MaxString];

//******************************************************************************
//
// DrawRotPost
//
//******************************************************************************

void DrawRotPost ( int height, byte * src, byte * buf, int origheight)
{
    int y1;
    int y2;

    hp_srcstep=(origheight<<18)/height;
    y1 = (((centery<<8)-(height<<5)+(MENUOFFY<<8)));
    y2 = (((height<<6)+y1)>>8);

    if (((y1>>8)>=200) || (y2<0))
        return;
    if (y1<0)
    {
        hp_startfrac=FixedMulShift(-y1,hp_srcstep,8);
        y2 = (((height<<6)+y1)>>8);
        y1=0;
    }
    else
    {
        hp_startfrac=FixedMulShift(255-(y1&0xff),hp_srcstep,8);
        y1>>=8;
    }
    if (y2>200)
    {
        DrawMenuPost(200-y1, src, buf+ylookup[y1]);
    }
    else
    {
        DrawMenuPost(y2-y1, src, buf+ylookup[y1]);
    }
}



/*
========================
=
= GetPoint
=
========================
*/

void GetPoint (int x1, int y1, int px, int py, int * screenx, int * height, int angle)
{

    fixed gxt,gyt,nx,ny;
    fixed gxtt,gytt;
    int gx,gy;


//
// translate point to view centered coordinates
//
    gx = x1-px;
    gy = y1-py;

//
// calculate newx
//
    gxt = FixedMul(gx,costable[angle]);
    gyt = FixedMul(gy,sintable[angle]);
    nx =gxt-gyt;

    if (nx<mindist)
        nx=mindist;


//
// calculate newy
//
    gxtt = FixedMul(gx,sintable[angle]);
    gytt = FixedMul(gy,costable[angle]);
    ny = gytt+gxtt;

// too close, don't overflow the divid'


    *screenx = 160 + ((ny*NORMALWIDTHMULTIPLIER)/nx);            // DEBUG: use assembly divide

    *height = NORMALHEIGHTDIVISOR/nx;

    if (*screenx<0) *screenx=0;

}


/*
========================
=
= InterpolatePlane
=
========================
*/

void InterpolatePlane (visobj_t * plane)
{
    int d1,d2;
    int top;
    int topinc;
    int bot;
    int botinc;
    int i;
    int texture;
    int dh;
    int dx;
    int height;


    dx=(plane->x2-plane->x1+1);
    if (plane->h1<=0 || plane->h2<=0 || (dx==0))
        return;
    d1=65536/plane->h1;
    d2=65536/plane->h2;
    dh=((plane->h2-plane->h1)<<8)/dx;
    top=0;
    topinc=(d1)*((plane->textureend-plane->texturestart)>>4);
    bot=d2*dx;
    botinc=d1-d2;
    height=(plane->h1<<8);
    if (plane->x1>=viewwidth)
        return;
    for (i=plane->x1; i<=plane->x2; i++)
    {
        if ((i>=0 && i<viewwidth)&&(posts[i].wallheight<(height>>8)))
        {
            if (bot)
            {
                texture=((top/bot)+(plane->texturestart>>4))>>6;
                posts[i].texture=texture*plane->viewx;
                posts[i].lump=plane->shapenum;
                posts[i].wallheight=(height>>8);
                posts[i].offset=plane->viewx;
            }
        }
        top+=topinc;
        bot+=botinc;
        height+=dh;
    }
}

//******************************************************************************
//
// DrawPlanePosts
//
//******************************************************************************

void   DrawPlanePosts (void)
{
    int height;
    char * buf;
    byte * shape = NULL;
    int lastwall=-2;
    int i;

    shadingtable=colormap+(16<<8);
    {
        buf=(char *)(bufferofs);

        for (i=0; i<viewwidth; i++,buf++)
        {
            height=(posts[i].wallheight);
            if (height<=4)
                continue;
            if (lastwall!=posts[i].lump)
            {
                lastwall=posts[i].lump;
                if (lastwall==-1)
                    shape=menubuf;
                else
                    shape=W_CacheLumpNum(lastwall,PU_CACHE, Cvt_patch_t, 1);
            }
            DrawRotPost (height,shape+posts[i].texture,(byte*)buf,posts[i].offset);
        }
    }
}

//******************************************************************************
//
// NextPlaneptr
//
//******************************************************************************

void NextPlaneptr ( void )
{
    if (planeptr < &planelist[MAXPLANES-1]) // don't let it overflo'
        planeptr++;
}

//******************************************************************************
//
// RestPlaneptr
//
//******************************************************************************

void ResetPlaneptr ( void )
{
    planeptr = &planelist[0];
}

//******************************************************************************
//
// NextVisptr
//
//******************************************************************************

void NextVisptr ( void )
{
    if (visptr < &vislist[MAXVISIBLE-1]) // don't let it overflo'
        visptr++;
}

//******************************************************************************
//
// ResetVisptr
//
//******************************************************************************

void ResetVisptr ( void )
{
    visptr = &vislist[0];
}

//******************************************************************************
//
// SetupPlanes
//
//******************************************************************************

void SetupPlanes ( void )
{
    int i;

    for (i=0; i<320; i++)
        posts[i].wallheight=-1;
}



//******************************************************************************
//
// CalcPlanes
//
//******************************************************************************

void CalcPlanes ( int px, int py, int angle )
{
    plane_t * pptr;

    ResetVisptr();
    for (pptr = &planelist[0]; pptr<planeptr; pptr++)
    {
        if (SideOfLine(pptr->x1,pptr->y1,pptr->x2,pptr->y2,px,py)<0)
        {
            GetPoint (pptr->x1,pptr->y1,px,py,&visptr->x1,&visptr->h1,angle);
            GetPoint (pptr->x2,pptr->y2,px,py,&visptr->x2,&visptr->h2,angle);
            visptr->textureend=0x0;
            visptr->texturestart=pptr->texturewidth;
        }
        else
        {
            GetPoint (pptr->x2,pptr->y2,px,py,&visptr->x1,&visptr->h1,angle);
            GetPoint (pptr->x1,pptr->y1,px,py,&visptr->x2,&visptr->h2,angle);
            visptr->texturestart=0x0;
            visptr->textureend=pptr->texturewidth;
        }
        visptr->shapenum=pptr->texture;
        visptr->viewx=pptr->origheight;
        visptr->viewheight=MAX(visptr->h1,visptr->h2);
        NextVisptr();
    }
}


//******************************************************************************
//
// DrawTransformedPlanes
//
//******************************************************************************

void DrawTransformedPlanes ( void )
{
    int numvisible;
    int greatest;
    int height;
    int i;
    visobj_t * closest = NULL;

    numvisible = visptr-&vislist[0];
    if (!numvisible)
        return;                                     // no visible objects
    for (i = 0; i<numvisible; i++)
    {
        greatest = 0;
        for (visstep=&vislist[0] ; visstep<visptr ; visstep++)
        {
            height = visstep->viewheight;
            if (height > greatest)
            {
                greatest = height;
                closest = visstep;
            }
        }
        InterpolatePlane(closest);
        closest->viewheight = 0;
    }
}


//******************************************************************************
//
// ClearMenuBuf
//
//******************************************************************************

void ClearMenuBuf ( void )
{
    byte * shape;

    if (MenuBufStarted==false)
        Error("Called ClearMenuBuf without menubuf started\n");

    shape=W_CacheLumpName(MENUBACKNAME,PU_CACHE, Cvt_patch_t, 1);
    shape+=8;
    memcpy (menubuf,shape,TEXTUREW*TEXTUREHEIGHT);
}

//******************************************************************************
//
// ShutdownMenuBuf
//
//******************************************************************************

void ShutdownMenuBuf ( void )
{
    if (MenuBufStarted==false)
        return;
    MenuBufStarted=false;
    SafeFree(menubuffers[0]);
    SafeFree(menubuffers[1]);
    if (loadedgame==false)
        SetViewSize(viewsize);
}

//******************************************************************************
//
// SetupMenuBuf
//
//******************************************************************************

void SetupMenuBuf ( void )
{
#define SRCH 148
#define SRCW 258
#define PLANEX1 (-0x24000)
#define PLANEX2 (0x23fff)
#define PLANEW (16<<10)
#define PLANEY (0x40000)
#define PLANEW2 (0x5a827)

    if (MenuBufStarted==true)
        return;
    MenuBufStarted=true;

    // No top offsets like in game

    centery=100;
    centeryfrac=centery<<16;

    strcpy(titlestring,menutitles[0]);

    screenofs=0;
    viewwidth=320;
    viewheight=200;

    alternatemenubuf=0;

    ResetPlaneptr();
    planeptr->texture=-1;
    planeptr->y1=PLANEX1;
    planeptr->x1=PLANEW;
    planeptr->y2=PLANEX2;
    planeptr->x2=PLANEW;
    planeptr->origheight=TEXTUREHEIGHT;
    planeptr->texturewidth=TEXTUREWIDTH;
    NextPlaneptr();
    planeptr->texture=-1;
    planeptr->y1=PLANEX1;
    planeptr->x1=-PLANEW;
    planeptr->y2=PLANEX2;
    planeptr->x2=-PLANEW;
    planeptr->origheight=TEXTUREHEIGHT;
    planeptr->texturewidth=TEXTUREWIDTH;
    NextPlaneptr();
    planeptr->texture=W_GetNumForName(MENUBACKNAME);
    planeptr->y1=PLANEX1;
    planeptr->x1=PLANEW;
    planeptr->y2=PLANEX1;
    planeptr->x2=-PLANEW;
    planeptr->origheight=TEXTUREHEIGHT;
    planeptr->texturewidth=TEXTUREWIDTH;
    NextPlaneptr();
    planeptr->texture=W_GetNumForName(MENUBACKNAME);
    planeptr->y1=PLANEX2;
    planeptr->x1=PLANEW;
    planeptr->y2=PLANEX2;
    planeptr->x2=-PLANEW;
    planeptr->origheight=TEXTUREHEIGHT;
    planeptr->texturewidth=TEXTUREWIDTH;
    NextPlaneptr();

    menubuffers[0]=SafeMalloc(TEXTUREW*TEXTUREHEIGHT);
    menubuffers[1]=SafeMalloc(TEXTUREW*TEXTUREHEIGHT);
    menubuf=menubuffers[0];
    ClearMenuBuf();
    BackgroundDrawn=false;
}


//******************************************************************************
//
// PositionMenuBuf
//
//******************************************************************************

void PositionMenuBuf( int angle, int distance, boolean drawbackground )
{
    int px,py;
    font_t * oldfont;
    int width,height;


    if (MenuBufStarted==false)
        Error("Called PositionMenuBuf without menubuf started\n");
    CalcTics();
    SetupPlanes();
    if ((drawbackground==true) || (BackgroundDrawn==false))
    {
        VL_DrawPostPic (W_GetNumForName("trilogo"));
    }
    px=FixedMulShift(distance,costable[angle&(FINEANGLES-1)],16);
    py=FixedMulShift(-distance,sintable[angle&(FINEANGLES-1)],16);
    CalcPlanes(px,py,(angle+ANG180)&(FINEANGLES-1));
    DrawTransformedPlanes();
    DrawPlanePosts();
    oldfont=CurrentFont;
    CurrentFont = (font_t *)W_CacheLumpName ("newfnt1", PU_CACHE, Cvt_font_t, 1);
    US_MeasureStr (&width, &height, "%s", titlestring);
    US_ClippedPrint ((320-width)>>1, MENUTITLEY-titleyoffset, titlestring);
    CurrentFont=oldfont;
    FlipPage();
    titleshade+=titleshadedir;
    if (abs(titleshade-16)>6)
        titleshadedir=-titleshadedir;
}

//******************************************************************************
//
// RefreshMenuBuf
//
//******************************************************************************

void RefreshMenuBuf( int time )
{
    int i;

    if (MenuBufStarted==false)
        Error("Called RefreshMenuBuf without menubuf started\n");

    if (readytoflip)
        return;

    for (i=0; i<=time; i+=tics)
    {
        //PositionMenuBuf (0,NORMALVIEW,false);
        PositionMenuBuf (0,NORMALVIEW,true);//bna++ in not true bg in menu is no redrawn
    }
}

//******************************************************************************
//
// ScaleMenuBufPost
//
//******************************************************************************

void ScaleMenuBufPost (byte * src, int topoffset, byte * buf)
{
    int  d;
    int  offset;
    int  length;
    int  s;


    while (1)
    {
        offset=*(src++);
        if (offset==0xff)
            return;
        else
        {
            d=offset-topoffset;
            length=*(src++);
            for (s=0; s<length; s++,d++)
                *(buf+d)=*(src+s);
            src+=length;
        }
    }
}

//******************************************************************************
//
// SetAlternateMenuBuf
//
//******************************************************************************

void SetAlternateMenuBuf ( void )
{
    if (MenuBufStarted==false)
        Error("Called SetAlternateMenuBuf without menubuf started\n");

    alternatemenubuf^=1;
    readytoflip=1;
    menubuf=menubuffers[alternatemenubuf];
}

//******************************************************************************
//
// SetMenuTitle
//
//******************************************************************************

void SetMenuTitle ( const char * menutitle )
{
    if (MenuBufStarted==false)
        Error("Called SetMenuTitle without menubuf started\n");
    strcpy(menutitles[alternatemenubuf],menutitle);
    if (readytoflip==0)
        strcpy(titlestring,menutitle);
}

//******************************************************************************
//
// DrawMenuBufPicture
//
//******************************************************************************

void DrawMenuBufPicture (int x, int y, const byte * pic, int w, int h)
{
    byte *buffer;
    int i;

    if (MenuBufStarted==false)
        Error("Called DrawMenuBufPictoure without menubuf started\n");

    if ((x<0) || (x+w>=TEXTUREW))
        Error ("DrawMenuBufPicture: x is out of range\n");
    if ((y<0) || (y+h>=TEXTUREHEIGHT))
        Error ("DrawMenuBufPicture: y is out of range\n");

    for (i=0; i<w; i++)
    {
        buffer = (byte*)menubuf+y+((x+i)*TEXTUREHEIGHT);
        memcpy(buffer,pic,h);
        pic+=h;
    }
}

//******************************************************************************
//
// DrawMenuBufItem
//
//******************************************************************************

void DrawMenuBufItem (int x, int y, int shapenum)
{
    byte *buffer;
    int cnt;
    byte *shape;
    patch_t *p;

    if (MenuBufStarted==false)
        Error("Called DrawMenuBufItem without menubuf started\n");

    shape = W_CacheLumpNum (shapenum, PU_CACHE, Cvt_patch_t, 1);
    p = (patch_t *)shape;

    if (((x-p->leftoffset)<0) || ((x-p->leftoffset+p->width)>=TEXTUREW))
        Error ("DrawMenuBufItem: x is out of range\n");
    if (((y-p->topoffset)<0) || ((y-p->topoffset+p->height)>=TEXTUREHEIGHT))
        Error ("DrawMenuBufItem: y is out of range\n");

    buffer = (byte*)menubuf+y+((x-p->leftoffset)*TEXTUREHEIGHT);

    for (cnt = 0; cnt < p->width; cnt++,buffer+=TEXTUREHEIGHT)
        ScaleMenuBufPost ((byte *)(p->collumnofs[cnt]+shape),
                          p->topoffset, buffer);
}

//******************************************************************************
//
// IScaleMenuBufPost
//
//******************************************************************************

void IScaleMenuBufPost (byte * src, int topoffset, byte * buf, int color)
{
    int  d;
    int  offset;
    int  length;
    int  s;


    while (1)
    {
        offset=*(src++);
        if (offset==0xff)
            return;
        else
        {
            d=offset-topoffset;
            length=*(src++);
            for (s=0; s<length; s++,d++)
                *(buf+d)=*(intensitytable+((*(src+s))<<8)+color);
            src+=length;
        }
    }
}


//******************************************************************************
//
// DrawIMenuBufItem
//
//******************************************************************************

void DrawIMenuBufItem (int x, int y, int shapenum, int color)
{
    byte *buffer;
    int cnt;
    byte *shape;
    patch_t *p;


    if (MenuBufStarted==false)
        Error("Called DrawIMenuBufItem without menubuf started\n");

    shape = W_CacheLumpNum (shapenum, PU_CACHE, Cvt_patch_t, 1);
    p = (patch_t *)shape;

    if (((x-p->leftoffset)<0) || ((x-p->leftoffset+p->width)>=TEXTUREW))
        Error ("DrawIMenuBufItem: x is out of range\n");
    if (((y-p->topoffset)<0) || ((y-p->topoffset+p->height)>=TEXTUREHEIGHT))
        Error ("DrawIMenuBufItem: y is out of range\n");

    buffer = (byte*)menubuf+y+((x-p->leftoffset)*TEXTUREHEIGHT);

    for (cnt = 0; cnt < p->width; cnt++,buffer+=TEXTUREHEIGHT)
        IScaleMenuBufPost ((byte *)(p->collumnofs[cnt]+shape),
                           p->topoffset, buffer, color);
}


//******************************************************************************
//
// TScaleMenuBufPost
//
//******************************************************************************
void TScaleMenuBufPost (byte * src, int topoffset, byte * buf)
{
    int  d;
    int  offset;
    int  length;
    byte pixel;
    int  s;


    while (1)
    {
        offset=*(src++);
        if (offset==0xff)
            return;
        else
        {
            d=offset-topoffset;
            length=*(src++);
            for (s=0; s<length; s++,d++)
            {
                pixel = *(buf+d);
                pixel = *(shadingtable+pixel);
                *(buf+d) = pixel;
            }
            src+=length;
        }
    }
}


//******************************************************************************
//
// CScaleMenuBufPost
//
//******************************************************************************
void CScaleMenuBufPost (byte * src, int topoffset, byte * buf)
{
    int  d;
    int  offset;
    int  length;
    byte pixel;
    int  s;


    while (1)
    {
        offset=*(src++);
        if (offset==0xff)
            return;
        else
        {
            d=offset-topoffset;
            length=*(src++);
            for (s=0; s<length; s++,d++)
            {
                pixel = *(src+s);
                pixel = *(shadingtable+pixel);
                *(buf+d) = pixel;
            }
            src+=length;
        }
    }
}


//******************************************************************************
//
// EraseMenuBufRegion
//
//******************************************************************************

void EraseMenuBufRegion (int x, int y, int width, int height)
{
    byte *buffer;
    int xx,yy;
    byte * shape;

    if (MenuBufStarted==false)
        Error("Called EraseMenuBufRegion without menubuf started\n");

    if ((x<0) || (x+width>=TEXTUREW))
        Error ("EraseMenuBufRegion: x is out of range\n");
    if ((y<0) || (y+height>=TEXTUREHEIGHT))
        Error ("EraseMenuBufRegion: y is out of range\n");

    shape=W_CacheLumpName(MENUBACKNAME,PU_CACHE, Cvt_patch_t, 1);
    shape+=8;
    shape+=(x*TEXTUREHEIGHT)+y;
    buffer = (byte*)menubuf+(x*TEXTUREHEIGHT)+y;

    for (xx = 0; xx < width; xx++)
    {
        for (yy = 0; yy < height; yy++)
            *(buffer+yy)=*(shape+yy);
        buffer+=TEXTUREHEIGHT;
        shape+=TEXTUREHEIGHT;
    }
}


//******************************************************************************
//
// DrawTMenuBufPic
//
//******************************************************************************

void DrawTMenuBufPic (int x, int y, int shapenum)
{
    byte *buffer;
    byte *buf;
    int xx,yy;
    int plane;
    int pixel;
    byte *shape;
    pic_t *p;

    if (MenuBufStarted==false)
        Error("Called DrawTMenuBufPic without menubuf started\n");

    shadingtable=colormap+(25<<8);

    shape = W_CacheLumpNum (shapenum, PU_CACHE, Cvt_pic_t, 1);
    p = (pic_t *)shape;

    if ((x<0) || ((x+(p->width<<2))>=TEXTUREW))
        Error ("DrawTMenuBufPic: x is out of range\n");
    if ((y<0) || ((y+p->height)>=TEXTUREHEIGHT))
        Error ("DrawTMenuBufPic: y is out of range\n");

    buffer = (byte*)menubuf+(x*TEXTUREHEIGHT)+y;

    for (plane=0; plane<4; plane++,buffer+=TEXTUREHEIGHT)
    {
        for (yy = 0; yy < p->height; yy++)
        {
            buf=buffer+yy;
            for (xx = 0; xx < p->width; xx++,buf+=TEXTUREHEIGHT<<2)
            {
                pixel = *(buf);
                pixel = *(shadingtable+pixel);
                *(buf) = pixel;
            }
        }
    }
}


//******************************************************************************
//
// DrawTMenuBufItem
//
//******************************************************************************

void DrawTMenuBufItem (int x, int y, int shapenum)
{
    byte *buffer;
    int cnt;
    byte *shape;
    patch_t *p;


    if (MenuBufStarted==false)
        Error("Called DrawTMenuBufItem without menubuf started\n");

    shape = W_CacheLumpNum (shapenum, PU_CACHE, Cvt_patch_t, 1);
    p = (patch_t *)shape;

    shadingtable=colormap+(25<<8);

    if (((x-p->leftoffset)<0) || ((x-p->leftoffset+p->width)>=TEXTUREW))
        Error ("DrawTMenuBufItem: x is out of range\n");
    if (((y-p->topoffset)<0) || ((y-p->topoffset+p->height)>=TEXTUREHEIGHT))
        Error ("DrawTMenuBufItem: y is out of range\n");

    buffer = (byte*)menubuf+y+((x-p->leftoffset)*TEXTUREHEIGHT);

    for (cnt = 0; cnt < p->width; cnt++,buffer+=TEXTUREHEIGHT)
        TScaleMenuBufPost ((byte *)(p->collumnofs[cnt]+shape),
                           p->topoffset, buffer);
}

//******************************************************************************
//
// DrawColoredMenuBufItem
//
//******************************************************************************

void DrawColoredMenuBufItem (int x, int y, int shapenum, int color)
{
    byte *buffer;
    int cnt;
    byte *shape;
    patch_t *p;


    if (MenuBufStarted==false)
        Error("Called DrawColoredMenuBufItem without menubuf started\n");

    shape = W_CacheLumpNum (shapenum, PU_CACHE, Cvt_patch_t, 1);
    p = (patch_t *)shape;

    shadingtable=playermaps[color]+(16<<8);

    if (((x-p->leftoffset)<0) || ((x-p->leftoffset+p->width)>=TEXTUREW))
        Error ("DrawColoredMenuBufItem: x is out of range\n");
    if (((y-p->topoffset)<0) || ((y-p->topoffset+p->height)>=TEXTUREHEIGHT))
        Error ("DrawColoredMenuBufItem: y is out of range\n");

    buffer = (byte*)menubuf+y+((x-p->leftoffset)*TEXTUREHEIGHT);

    for (cnt = 0; cnt < p->width; cnt++,buffer+=TEXTUREHEIGHT)
        CScaleMenuBufPost ((byte *)(p->collumnofs[cnt]+shape),
                           p->topoffset, buffer);
}

//******************************************************************************
//
// DrawMenuBufPic
//
//******************************************************************************

void DrawMenuBufPic (int x, int y, int shapenum)
{
    byte *buffer;
    byte *buf;
    int xx,yy;
    int plane;
    byte *shape;
    byte *src;
    pic_t *p;

    if (MenuBufStarted==false)
        Error("Called DrawMenuBufPic without menubuf started\n");

    shape = W_CacheLumpNum (shapenum, PU_CACHE, Cvt_pic_t, 1);
    p = (pic_t *)shape;

    if ((x<0) || ((x+(p->width<<2))>=TEXTUREW))
        Error ("DrawTMenuBufPic: x is out of range\n");
    if ((y<0) || ((y+p->height)>=TEXTUREHEIGHT))
        Error ("DrawTMenuBufPic: y is out of range\n");


    buffer = (byte*)menubuf+(x*TEXTUREHEIGHT)+y;

    src=(byte *)&p->data;
    for (plane=0; plane<4; plane++,buffer+=TEXTUREHEIGHT)
    {
        for (yy = 0; yy < p->height; yy++)
        {
            buf=buffer+yy;
            for (xx = 0; xx < p->width; xx++,buf+=TEXTUREHEIGHT<<2)
                *(buf)=*(src++);
        }
    }
}




//******************************************************************************
//
// DrawTMenuBufBox
//
//******************************************************************************


void DrawTMenuBufBox ( int x, int y, int width, int height )
{
    byte *buffer;
    int   xx;
    int   yy;
    int   pixel;

    if (MenuBufStarted==false)
        Error("Called DrawTMenuBufBox without menubuf started\n");

    shadingtable = colormap + ( 25 << 8 );

    if ( ( x < 0 ) || ( ( x + width ) >= TEXTUREW ) )
        Error ("DrawTMenuBar : x is out of range\n");
    if ( ( y < 0 ) || ( y + height ) >= TEXTUREHEIGHT )
        Error ("DrawTMenuBar : y is out of range\n");

    buffer = ( byte * )menubuf + ( x * TEXTUREHEIGHT ) + y;

    for ( xx = 0; xx < width; xx++ )
    {
        for ( yy = 0; yy < height; yy++ )
        {
            pixel = *( buffer + yy );
            pixel = *( shadingtable + pixel );
            *( buffer + yy ) = pixel;
        }

        buffer += TEXTUREHEIGHT;
    }
}


//******************************************************************************
//
// DrawTMenuBufHLine
//
//******************************************************************************

void DrawTMenuBufHLine (int x, int y, int width, boolean up)
{
    byte *buffer;
    byte *buf;
    int xx;
    int plane;
    int pixel;
    int w = width>>2;
    int ww = w;

    if ((x<0) || ((x+(width))>=TEXTUREW))
        Error ("DrawTMenuBufBox: x is out of range\n");
    if (y<0)
        Error ("DrawTMenuBufBox: y is out of range\n");

    buffer = (byte*)menubuf+(x*TEXTUREHEIGHT)+y;

    if (up)
        shadingtable=colormap+(13<<8);
    else
        shadingtable=colormap+(25<<8);

    for (plane = 0; plane < 4; plane++, buffer += TEXTUREHEIGHT)
    {
        w = ww;
        switch (plane)
        {
        case 0:
            if (width % 4)
                w++;
            break;
        case 1:
            if ((width % 4) > 1)
                w++;
            break;
        case 2:
            if ((width % 4) > 2)
                w++;
            break;
        }


        buf = buffer;
        for (xx = 0; xx < w; xx++, buf += (TEXTUREHEIGHT<<2))
        {
            pixel = *(buf);
            pixel = *(shadingtable+pixel);
            *(buf) = pixel;
        }
    }
}

//******************************************************************************
//
// DrawTMenuBufVLine
//
//******************************************************************************

void DrawTMenuBufVLine (int x, int y, int height, boolean up)
{
    byte *buffer;
    byte *buf;
    int yy;
    int pixel;

    if (x<0)
        Error ("DrawTMenuBufBox: x is out of range\n");
    if ((y<0) || ((y+height)>=TEXTUREHEIGHT))
        Error ("DrawTMenuBufBox: y is out of range\n");

    buffer = (byte*)menubuf+(x*TEXTUREHEIGHT)+y;

    if (up)
        shadingtable=colormap+(13<<8);
    else
        shadingtable=colormap+(25<<8);

    for (yy = 0; yy < height; yy++)
    {
        buf = buffer+yy;
        pixel = *(buf);
        pixel = *(shadingtable+pixel);
        *(buf) = pixel;
    }
}

//******************************************************************************
//******************************************************************************
//
// STRING ROUTINES
//
//******************************************************************************
//******************************************************************************


//******************************************************************************
//
// DrawMenuBufPropString ()
//
//******************************************************************************

void DrawMenuBufPropString (int px, int py, const char *string)
{
    byte  pix;
    int   width,height,ht;
    byte  *source, *dest, *origdest;
    int   ch;


    if (MenuBufStarted==false)
        Error("Called DrawMenuBufPropString without menubuf started\n");

    ht = CurrentFont->height;
    dest = origdest = (byte*)menubuf+(px*TEXTUREHEIGHT)+py;

    while ((ch = (unsigned char)*string++)!=0)
    {
        ch -= 31;
        width = CurrentFont->width[ch];
        source = ((byte *)CurrentFont)+CurrentFont->charofs[ch];
        while (width--)
        {
            height = ht;
            while (height--)
            {
                pix = *source;
                if (pix)
                    *dest = pix;

                source++;
                dest ++;
            }

            PrintX++;
            origdest+=TEXTUREHEIGHT;
            dest = origdest;
        }
    }

}


//******************************************************************************
//
// DrawMenuBufIString ()
//
//******************************************************************************

void DrawMenuBufIString (int px, int py, const char *string, int color)
{
    byte  pix;
    int   width,height,ht;
    byte  *source, *dest, *origdest;
    int   ch;

    if (MenuBufStarted==false)
        Error("Called DrawMenuBufPropString without menubuf started\n");

    if ( ( color < 0 ) || ( color > 255 ) )
    {
        Error( "Intensity Color out of range\n" );
    }

    ht = IFont->height;
    dest = origdest = (byte*)menubuf+(px*TEXTUREHEIGHT)+py;

    PrintX = px;
    PrintY = py;

    while ((ch = (unsigned char)*string++)!=0)
    {
        // Tab
        if ( ch == '\x9' )
        {
            int offset;

            PrintX   -= px;
            offset    = 4 * 5 - PrintX % ( 4 * 5 );
            PrintX   += offset + px;
            origdest += offset * TEXTUREHEIGHT;
            dest      = origdest;
            continue;
        }

        ch -= 31;
        width = IFont->width[ ch ];

        source = ( ( byte * )IFont ) + IFont->charofs[ ch ];

        while (width--)
        {
            height = ht;
            while (height--)
            {
                pix = *source;
                if ( pix != 0xFE )
                {
                    *dest = ( ( byte )intensitytable[ ( pix << 8 ) + color ] );
                    GetIntensityColor( pix );
                }

                source++;
                dest ++;
            }

            PrintX++;
            origdest+=TEXTUREHEIGHT;
            dest = origdest;
        }
    }

}


//******************************************************************************
//
// DrawTMenuBufPropString ()
//
// Draws a string at px, py to bufferofs
//
//******************************************************************************

void DrawTMenuBufPropString (int px, int py, const char *string)
{
    byte  pix;
    int   width,height,ht;
    byte  *source, *dest, *origdest;
    int   ch;


    if (MenuBufStarted==false)
        Error("Called DrawTMenuBufPropString without menubuf started\n");

    ht = CurrentFont->height;
    dest = origdest = (byte*)menubuf+(px*TEXTUREHEIGHT)+py;

    shadingtable=colormap+(StringShade<<8);
    while ((ch = (unsigned char)*string++)!=0)
    {
        ch -= 31;
        width = CurrentFont->width[ch];
        source = ((byte *)CurrentFont)+CurrentFont->charofs[ch];
        while (width--)
        {
            height = ht;
            while (height--)
            {
                pix = *source;
                if (pix)
                {
                    pix = *dest;
                    pix = *(shadingtable+pix);
                    *dest = pix;
                }
                source++;
                dest ++;
            }

            PrintX++;
            origdest+=TEXTUREHEIGHT;
            dest = origdest;
        }
    }
}


//******************************************************************************
//
// MenuBufCPrintLine() - Prints a string centered on the current line and
//    advances to the next line. Newlines are not supported.
//
//******************************************************************************

void MenuBufCPrintLine (const char *s)
{
    int w, h;

    USL_MeasureString (s, &w, &h, CurrentFont);

    if (w > WindowW)
        Error("MenuBufCPrintLine() - String exceeds width");

    PrintX = WindowX + ((WindowW - w) / 2);
    DrawMenuBufPropString (PrintX, PrintY, s);
    PrintY += h;
}

//******************************************************************************
//
// MenuBufCPrint() - Prints a string in the current window. Newlines are
//    supported.
//
//******************************************************************************

void MenuBufCPrint (const char *string)
{
    char  c,
          *se,
          *s;

    /* !!! FIXME: this is lame. */
    if (strlen(string) >= sizeof (strbuf))
    {
        fprintf(stderr, "buffer overflow!\n");
        return;
    }

    /* prevent writing to literal strings... ( MenubufCPrint("feh"); ) */
    strcpy(strbuf, string);
    s = strbuf;

    while (*s)
    {
        se = s;
        while ((c = *se) && (c != '\n'))
            se++;
        *se = '\0';

        MenuBufCPrintLine(s);

        s = se;
        if (c)
        {
            *se = c;
            s++;
        }
    }
}

//******************************************************************************
//
// MenuBufPrintLine() - Prints a string on the current line and
//    advances to the next line. Newlines are not supported.
//
//******************************************************************************

void MenuBufPrintLine (const char *s)
{
    int w, h;

    USL_MeasureString (s, &w, &h, CurrentFont);

    if (w > WindowW)
        Error("MenuBufCPrintLine() - String exceeds width");

    PrintX = WindowX;
    DrawMenuBufPropString (PrintX, PrintY, s);
    PrintY += h;
}

//******************************************************************************
//
// MenuBufPrint() - Prints a string in the current window. Newlines are
//    supported.
//
//******************************************************************************

void MenuBufPrint (const char *string)
{
    char  c,
          *se,
          *s;

    strcpy(strbuf, string);
    s = strbuf;

    while (*s)
    {
        se = s;
        while ((c = *se) && (c != '\n'))
            se++;
        *se = '\0';

        MenuBufPrintLine(s);

        s = se;
        if (c)
        {
            *se = c;
            s++;
        }
    }
}

//******************************************************************************
//
// MenuTBufPrintLine() - Prints a string on the current line and
//    advances to the next line. Newlines are not supported.
//
//******************************************************************************

void MenuTBufPrintLine (const char *s, int shade)
{
    int w, h;

    USL_MeasureString (s, &w, &h, CurrentFont);

    if (w > WindowW)
        Error("MenuBufCPrintLine() - String exceeds width");

    PrintX = WindowX;
    StringShade=shade;
    DrawTMenuBufPropString (PrintX, PrintY, s);
    PrintY += h;
}

//******************************************************************************
//
// FlipMenuBuf
//
//******************************************************************************

void FlipMenuBuf ( void )
{
    int i;
    int dh;
    int h;
    int y;
    int dy;
    int time;
    int flip;

    if (MenuBufStarted==false)
        Error("Called FlipMenuBuf without menubuf started\n");

    if (!readytoflip)
        Error("FlipMenuBuf called without SetAlternateMenuBuf beforehand");
    readytoflip=0;
    if (Menuflipspeed<=5)
    {
        menubuf=menubuffers[alternatemenubuf];
        strcpy(titlestring,menutitles[alternatemenubuf]);
        RefreshMenuBuf(0);
    }
    else
    {
        menubuf=menubuffers[alternatemenubuf^1];
        strcpy(titlestring,menutitles[alternatemenubuf^1]);
        time=Menuflipspeed-5;
        dh=(1024<<8)/time;
        h=0;
        dy=((MENUTITLEY*6)<<8)/time;
        y=0;
        flip=0;
        titleyoffset=0;
        for (i=0; i<time; i+=tics)
        {
            PositionMenuBuf(h>>8,NORMALVIEW,true);
            h+=dh*tics;
            y+=dy*tics;
            titleyoffset=y>>8;
            if ((h>=512<<8) && (flip==0))
            {
                MN_PlayMenuSnd (SD_MENUFLIP);
                h=1536<<8;
                dy=-dy;
                menubuf=menubuffers[alternatemenubuf];
                strcpy(titlestring,menutitles[alternatemenubuf]);
                flip=1;
            }
        }
    }
    titleyoffset=0;
    BackgroundDrawn=false;
}



//******************************************************************************
//
// RotatePlane
//
//******************************************************************************

void RotatePlane ( void )
{
    SetupMenuBuf();

    while (!(Keyboard[0x01]))
    {
        RefreshMenuBuf(100);
        SetAlternateMenuBuf();
        ClearMenuBuf();
        DrawMenuBufPic  (0,0,W_GetNumForName("newg11"));
        DrawMenuBufItem (0,0,W_GetNumForName("apogee"));
        FlipMenuBuf();
        EraseMenuBufRegion(30,30,30,30);
        RefreshMenuBuf(100);
        SetAlternateMenuBuf();
        ClearMenuBuf();
        FlipMenuBuf();
    }
    ShutdownMenuBuf();
}
