/***************************************************************************
                          prim.c  -  description
                             -------------------
    begin                : Sun Mar 08 2009
    copyright            : (C) 1999-2009 by Pete Bernert
    web                  : www.pbernert.com   
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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. See also the license.txt file for *
 *   additional informations.                                              *
 *                                                                         *
 ***************************************************************************/

#include "stdafx.h"

#define _IN_PRIMDRAW

#include "externals.h"
#include "gpu.h"
#include "draw.h"
#include "soft.h"
#include "texture.h"

////////////////////////////////////////////////////////////////////////                                          
// defines
////////////////////////////////////////////////////////////////////////

#define DEFOPAQUEON  glAlphaFunc(GL_EQUAL,0.0f);bBlendEnable=FALSE;glDisable(GL_BLEND);                                
#define DEFOPAQUEOFF glAlphaFunc(GL_GREATER,0.49f);

////////////////////////////////////////////////////////////////////////                                          
// globals
////////////////////////////////////////////////////////////////////////

BOOL           bDrawTextured;                          // current active drawing states
BOOL           bDrawSmoothShaded;
BOOL           bOldSmoothShaded;
BOOL           bDrawNonShaded;
BOOL           bDrawMultiPass;
int            iOffscreenDrawing;
int            iDrawnSomething=0;

BOOL           bRenderFrontBuffer=FALSE;               // flag for front buffer rendering

GLubyte        ubGloAlpha;                             // texture alpha
GLubyte        ubGloColAlpha;                          // color alpha
int            iFilterType;                            // type of filter
BOOL           bFullVRam=FALSE;                        // sign for tex win
BOOL           bDrawDither;                            // sign for dither
BOOL           bUseMultiPass;                          // sign for multi pass
GLuint         gTexName;                               // binded texture
BOOL           bTexEnabled;                            // texture enable flag
BOOL           bBlendEnable;                           // blend enable flag
PSXRect_t      xrUploadArea;                           // rect to upload
PSXRect_t      xrUploadAreaIL;                         // rect to upload
PSXRect_t      xrUploadAreaRGB24;                      // rect to upload rgb24
int            iSpriteTex=0;                           // flag for "hey, it's a sprite"
unsigned short usMirror;                               // mirror, mirror on the wall

BOOL           bNeedUploadAfter=FALSE;                 // sign for uploading in next frame
BOOL           bNeedUploadTest=FALSE;                  // sign for upload test
BOOL           bUsingTWin=FALSE;                       // tex win active flag
BOOL           bUsingMovie=FALSE;                      // movie active flag
PSXRect_t      xrMovieArea;                            // rect for movie upload
short          sSprite_ux2;                            // needed for sprire adjust
short          sSprite_vy2;                            // 
uint32_t       ulOLDCOL=0;                             // active color
uint32_t       ulClutID;                               // clut

uint32_t      dwCfgFixes;                              // game fixes
uint32_t      dwActFixes=0;
uint32_t      dwEmuFixes=0;
BOOL          bUseFixes;

int           drawX,drawY,drawW,drawH;                 // offscreen drawing checkers
short         sxmin,sxmax,symin,symax;

////////////////////////////////////////////////////////////////////////                                          
// Update global TP infos
////////////////////////////////////////////////////////////////////////

void UpdateGlobalTP(unsigned short gdata)
{
 GlobalTextAddrX = (gdata << 6) & 0x3c0;

 if(iGPUHeight==1024)                                  // ZN mode
  {
   if(dwGPUVersion==2)                                 // very special zn gpu
    {
     GlobalTextAddrY =((gdata & 0x60 ) << 3);
     GlobalTextIL    =(gdata & 0x2000) >> 13;
     GlobalTextABR = (unsigned short)((gdata >> 7) & 0x3);
     GlobalTextTP = (gdata >> 9) & 0x3;
     if(GlobalTextTP==3) GlobalTextTP=2;             
     GlobalTexturePage = (GlobalTextAddrX>>6)+(GlobalTextAddrY>>4);
     usMirror =0;
     STATUSREG = (STATUSREG & 0xffffe000 ) | (gdata & 0x1fff );
     return;
    }
   else                                                // "enhanced" psx gpu
    {
     GlobalTextAddrY = (unsigned short)(((gdata << 4) & 0x100) | ((gdata >> 2) & 0x200));
    }
  }
 else GlobalTextAddrY = (gdata << 4) & 0x100;          // "normal" psx gpu

 usMirror=gdata&0x3000;
 
 GlobalTextTP = (gdata >> 7) & 0x3;                    // tex mode (4,8,15)
 if(GlobalTextTP==3) GlobalTextTP=2;                   // seen in Wild9 :(
 GlobalTextABR = (gdata >> 5) & 0x3;                   // blend mode

 GlobalTexturePage = (GlobalTextAddrX>>6)+(GlobalTextAddrY>>4);

 STATUSREG&=~0x07ff;                                   // Clear the necessary bits
 STATUSREG|=(gdata & 0x07ff);                          // set the necessary bits
}

unsigned int DoubleBGR2RGB (unsigned int BGR)
{
 unsigned int ebx,eax,edx;

 ebx=(BGR&0x000000ff)<<1;
 if(ebx&0x00000100) ebx=0x000000ff;

 eax=(BGR&0x0000ff00)<<1;
 if(eax&0x00010000) eax=0x0000ff00;

 edx=(BGR&0x00ff0000)<<1;
 if(edx&0x01000000) edx=0x00ff0000;

 return (ebx|eax|edx);
}

unsigned short BGR24to16 (uint32_t BGR)
{
 return ((BGR>>3)&0x1f)|((BGR&0xf80000)>>9)|((BGR&0xf800)>>6);
}

////////////////////////////////////////////////////////////////////////
// OpenGL primitive drawing commands
////////////////////////////////////////////////////////////////////////

static __inline void PRIMdrawTexturedQuad(OGLVertex* vertex1, OGLVertex* vertex2, 
                                   OGLVertex* vertex3, OGLVertex* vertex4) 
{
 glBegin(GL_TRIANGLE_STRIP);
  glTexCoord2fv(&vertex1->sow);
  glVertex3fv(&vertex1->x);
  
  glTexCoord2fv(&vertex2->sow);
  glVertex3fv(&vertex2->x);
  
  glTexCoord2fv(&vertex4->sow);
  glVertex3fv(&vertex4->x);
  
  glTexCoord2fv(&vertex3->sow);
  glVertex3fv(&vertex3->x);
 glEnd();
}

///////////////////////////////////////////////////////// 

static __inline void PRIMdrawTexturedTri(OGLVertex* vertex1, OGLVertex* vertex2, 
                                  OGLVertex* vertex3) 
{
 glBegin(GL_TRIANGLES);
  glTexCoord2fv(&vertex1->sow);
  glVertex3fv(&vertex1->x);

  glTexCoord2fv(&vertex2->sow);
  glVertex3fv(&vertex2->x);

  glTexCoord2fv(&vertex3->sow);
  glVertex3fv(&vertex3->x);
 glEnd();
}

///////////////////////////////////////////////////////// 

static __inline void PRIMdrawTexGouraudTriColor(OGLVertex* vertex1, OGLVertex* vertex2, 
                                         OGLVertex* vertex3) 
{
 glBegin(GL_TRIANGLES);

  SETPCOL(vertex1); 
  glTexCoord2fv(&vertex1->sow);
  glVertex3fv(&vertex1->x);

  SETPCOL(vertex2); 
  glTexCoord2fv(&vertex2->sow);
  glVertex3fv(&vertex2->x);

  SETPCOL(vertex3); 
  glTexCoord2fv(&vertex3->sow);
  glVertex3fv(&vertex3->x);
 glEnd();
}

///////////////////////////////////////////////////////// 

static __inline void PRIMdrawTexGouraudTriColorQuad(OGLVertex* vertex1, OGLVertex* vertex2, 
                                             OGLVertex* vertex3, OGLVertex* vertex4) 
{
 glBegin(GL_TRIANGLE_STRIP);
  SETPCOL(vertex1); 
  glTexCoord2fv(&vertex1->sow);
  glVertex3fv(&vertex1->x);

  SETPCOL(vertex2); 
  glTexCoord2fv(&vertex2->sow);
  glVertex3fv(&vertex2->x);

  SETPCOL(vertex4); 
  glTexCoord2fv(&vertex4->sow);
  glVertex3fv(&vertex4->x);

  SETPCOL(vertex3); 
  glTexCoord2fv(&vertex3->sow);
  glVertex3fv(&vertex3->x);
 glEnd();
}

///////////////////////////////////////////////////////// 

static __inline void PRIMdrawTri(OGLVertex* vertex1, OGLVertex* vertex2, OGLVertex* vertex3) 
{
 glBegin(GL_TRIANGLES);
  glVertex3fv(&vertex1->x);
  glVertex3fv(&vertex2->x);
  glVertex3fv(&vertex3->x);
 glEnd();
}

///////////////////////////////////////////////////////// 

static __inline void PRIMdrawTri2(OGLVertex* vertex1, OGLVertex* vertex2, 
                           OGLVertex* vertex3, OGLVertex* vertex4) 
{
 glBegin(GL_TRIANGLE_STRIP);                           
  glVertex3fv(&vertex1->x);
  glVertex3fv(&vertex3->x);
  glVertex3fv(&vertex2->x);
  glVertex3fv(&vertex4->x);
 glEnd();
}

///////////////////////////////////////////////////////// 

static __inline void PRIMdrawGouraudTriColor(OGLVertex* vertex1, OGLVertex* vertex2, 
                                      OGLVertex* vertex3) 
{
 glBegin(GL_TRIANGLES);                           
  SETPCOL(vertex1); 
  glVertex3fv(&vertex1->x);
       
  SETPCOL(vertex2); 
  glVertex3fv(&vertex2->x);

  SETPCOL(vertex3); 
  glVertex3fv(&vertex3->x);
 glEnd();
}

///////////////////////////////////////////////////////// 

static __inline void PRIMdrawGouraudTri2Color(OGLVertex* vertex1, OGLVertex* vertex2, 
                                       OGLVertex* vertex3, OGLVertex* vertex4) 
{
 glBegin(GL_TRIANGLE_STRIP);                           
  SETPCOL(vertex1); 
  glVertex3fv(&vertex1->x);
       
  SETPCOL(vertex3); 
  glVertex3fv(&vertex3->x);

  SETPCOL(vertex2); 
  glVertex3fv(&vertex2->x);

  SETPCOL(vertex4); 
  glVertex3fv(&vertex4->x);
 glEnd();
}

///////////////////////////////////////////////////////// 

static __inline void PRIMdrawFlatLine(OGLVertex* vertex1, OGLVertex* vertex2,OGLVertex* vertex3, OGLVertex* vertex4)
{
 glBegin(GL_QUADS);

  SETPCOL(vertex1); 

  glVertex3fv(&vertex1->x);
  glVertex3fv(&vertex2->x);
  glVertex3fv(&vertex3->x);
  glVertex3fv(&vertex4->x);
 glEnd();
}

///////////////////////////////////////////////////////// 
     
static __inline void PRIMdrawGouraudLine(OGLVertex* vertex1, OGLVertex* vertex2,OGLVertex* vertex3, OGLVertex* vertex4)
{
 glBegin(GL_QUADS);

  SETPCOL(vertex1); 
  glVertex3fv(&vertex1->x);

  SETPCOL(vertex2); 
  glVertex3fv(&vertex2->x);

  SETPCOL(vertex3); 
  glVertex3fv(&vertex3->x);

  SETPCOL(vertex4); 
  glVertex3fv(&vertex4->x);
 glEnd();
}

///////////////////////////////////////////////////////// 
             
static __inline void PRIMdrawQuad(OGLVertex* vertex1, OGLVertex* vertex2, 
                           OGLVertex* vertex3, OGLVertex* vertex4) 
{
 glBegin(GL_QUADS);
  glVertex3fv(&vertex1->x);
  glVertex3fv(&vertex2->x);
  glVertex3fv(&vertex3->x);
  glVertex3fv(&vertex4->x);
 glEnd();
}

////////////////////////////////////////////////////////////////////////                                          
// Transparent blending settings
////////////////////////////////////////////////////////////////////////

static GLenum obm1=GL_ZERO;
static GLenum obm2=GL_ZERO;

typedef struct SEMITRANSTAG
{
 GLenum  srcFac;
 GLenum  dstFac;
 GLubyte alpha;
} SemiTransParams;

SemiTransParams TransSets[4]=
{
 {GL_SRC_ALPHA,GL_SRC_ALPHA,          127},
 {GL_ONE,      GL_ONE,                255},
 {GL_ZERO,     GL_ONE_MINUS_SRC_COLOR,255},
 {GL_ONE_MINUS_SRC_ALPHA,GL_ONE,      192}
}; 

////////////////////////////////////////////////////////////////////////

void SetSemiTrans(void)
{
/*
* 0.5 x B + 0.5 x F
* 1.0 x B + 1.0 x F
* 1.0 x B - 1.0 x F
* 1.0 x B +0.25 x F
*/

 if(!DrawSemiTrans)                                    // no semi trans at all?
  {
   if(bBlendEnable)
    {glDisable(GL_BLEND);bBlendEnable=FALSE;}          // -> don't wanna blend
   ubGloAlpha=ubGloColAlpha=255;                       // -> full alpha
   return;                                             // -> and bye
  }

 ubGloAlpha=ubGloColAlpha=TransSets[GlobalTextABR].alpha;

 if(!bBlendEnable)
  {glEnable(GL_BLEND);bBlendEnable=TRUE;}              // wanna blend

 if(TransSets[GlobalTextABR].srcFac!=obm1 || 
    TransSets[GlobalTextABR].dstFac!=obm2)
  {
   if(glBlendEquationEXTEx==NULL)
    {
     obm1=TransSets[GlobalTextABR].srcFac;
     obm2=TransSets[GlobalTextABR].dstFac;
     glBlendFunc(obm1,obm2);                           // set blend func
    }
   else
   if(TransSets[GlobalTextABR].dstFac !=GL_ONE_MINUS_SRC_COLOR)
    {
     if(obm2==GL_ONE_MINUS_SRC_COLOR)
      glBlendEquationEXTEx(FUNC_ADD_EXT);
     obm1=TransSets[GlobalTextABR].srcFac;
     obm2=TransSets[GlobalTextABR].dstFac;
     glBlendFunc(obm1,obm2);                           // set blend func
    }
   else
    {
     glBlendEquationEXTEx(FUNC_REVERSESUBTRACT_EXT);
     obm1=TransSets[GlobalTextABR].srcFac;
     obm2=TransSets[GlobalTextABR].dstFac;
     glBlendFunc(GL_ONE,GL_ONE);                       // set blend func
    }
  }
}

void SetScanTrans(void)                                // blending for scan lines
{
 if(glBlendEquationEXTEx!=NULL)
  {
   if(obm2==GL_ONE_MINUS_SRC_COLOR)
    glBlendEquationEXTEx(FUNC_ADD_EXT);
  }

 obm1=TransSets[0].srcFac;
 obm2=TransSets[0].dstFac;
 glBlendFunc(obm1,obm2);                               // set blend func
}

void SetScanTexTrans(void)                             // blending for scan mask texture
{
 if(glBlendEquationEXTEx!=NULL)
  {
   if(obm2==GL_ONE_MINUS_SRC_COLOR)
    glBlendEquationEXTEx(FUNC_ADD_EXT);
  }

 obm1=TransSets[2].srcFac;
 obm2=TransSets[2].dstFac;
 glBlendFunc(obm1,obm2);                               // set blend func
}

////////////////////////////////////////////////////////////////////////                                          
// multi pass in old 'Advanced blending' mode... got it from Lewpy :)
////////////////////////////////////////////////////////////////////////                                          

SemiTransParams MultiTexTransSets[4][2]=
{
 {
 {GL_ONE      ,GL_SRC_ALPHA,          127},
 {GL_SRC_ALPHA,GL_ONE,                127}
 },
 {
 {GL_ONE,      GL_SRC_ALPHA,          255},
 {GL_SRC_ALPHA,GL_ONE,                255}
 },
 {
 {GL_ZERO,     GL_ONE_MINUS_SRC_COLOR,255},
 {GL_ZERO,     GL_ONE_MINUS_SRC_COLOR,255}
 },
 {
 {GL_SRC_ALPHA,GL_ONE,                127},
 {GL_ONE_MINUS_SRC_ALPHA,GL_ONE,      255}
 }
}; 

////////////////////////////////////////////////////////////////////////                                          

SemiTransParams MultiColTransSets[4]=
{
 {GL_ONE_MINUS_SRC_ALPHA,GL_SRC_ALPHA,127},
 {GL_ONE,      GL_ONE,                255},
 {GL_ZERO,     GL_ONE_MINUS_SRC_COLOR,255},
 {GL_SRC_ALPHA,GL_ONE,                127}
}; 

////////////////////////////////////////////////////////////////////////                                          

void SetSemiTransMulti(int Pass)
{
 static GLenum bm1=GL_ZERO;
 static GLenum bm2=GL_ONE;

 ubGloAlpha=255;
 ubGloColAlpha=255;
 
 // are we enabling SemiTransparent mode?
 if(DrawSemiTrans)
  {
   if(bDrawTextured)
    {
     bm1=MultiTexTransSets[GlobalTextABR][Pass].srcFac;
     bm2=MultiTexTransSets[GlobalTextABR][Pass].dstFac;
     ubGloAlpha=MultiTexTransSets[GlobalTextABR][Pass].alpha;
    }
   // no texture
   else
    {
     bm1=MultiColTransSets[GlobalTextABR].srcFac;
     bm2=MultiColTransSets[GlobalTextABR].dstFac;
     ubGloColAlpha=MultiColTransSets[GlobalTextABR].alpha;
    }
  }
 // no shading
 else
  {
   if(Pass==0)
    {
     // disable blending
     bm1=GL_ONE;bm2=GL_ZERO;
    }
   else
    {
     // disable blending, but add src col a second time
     bm1=GL_ONE;bm2=GL_ONE;
    }
  }

 if(!bBlendEnable)
  {glEnable(GL_BLEND);bBlendEnable=TRUE;}              // wanna blend

 if(bm1!=obm1 || bm2!=obm2)
  {
   glBlendFunc(bm1,bm2);                               // set blend func
   obm1=bm1;obm2=bm2;
  }
}

////////////////////////////////////////////////////////////////////////                                          
// Set several rendering stuff including blending 
////////////////////////////////////////////////////////////////////////

static __inline void SetZMask3O(void)
{
 if(iUseMask && DrawSemiTrans && !iSetMask)
  {
   vertex[0].z=vertex[1].z=vertex[2].z=gl_z;
   gl_z+=0.00004f;
  }
}

static __inline void SetZMask3(void)
{
 if(iUseMask)
  {
   if(iSetMask || DrawSemiTrans)
    {vertex[0].z=vertex[1].z=vertex[2].z=0.95f;}
   else
    {
     vertex[0].z=vertex[1].z=vertex[2].z=gl_z;
     gl_z+=0.00004f;
    }
  }
}

static __inline void SetZMask3NT(void)
{
 if(iUseMask)
  {
   if(iSetMask)
    {vertex[0].z=vertex[1].z=vertex[2].z=0.95f;}
   else
    {
     vertex[0].z=vertex[1].z=vertex[2].z=gl_z;
     gl_z+=0.00004f;
    }
  }
}

////////////////////////////////////////////////////////////////////////

static __inline void SetZMask4O(void)
{
 if(iUseMask && DrawSemiTrans && !iSetMask)
  {
   vertex[0].z=vertex[1].z=vertex[2].z=vertex[3].z=gl_z;
   gl_z+=0.00004f;
  }
}

static __inline void SetZMask4(void)
{
 if(iUseMask)
  {
   if(iSetMask || DrawSemiTrans)
    {vertex[0].z=vertex[1].z=vertex[2].z=vertex[3].z=0.95f;}
   else
    {
     vertex[0].z=vertex[1].z=vertex[2].z=vertex[3].z=gl_z;
     gl_z+=0.00004f;
    }
  }
}

static __inline void SetZMask4NT(void)
{
 if(iUseMask)
  {
   if(iSetMask==1)
    {vertex[0].z=vertex[1].z=vertex[2].z=vertex[3].z=0.95f;}
   else
    {
     vertex[0].z=vertex[1].z=vertex[2].z=vertex[3].z=gl_z;
     gl_z+=0.00004f;
    }
  }
}

static __inline void SetZMask4SP(void)
{
 if(iUseMask)
  {
   if(iSetMask==1)
    {vertex[0].z=vertex[1].z=vertex[2].z=vertex[3].z=0.95f;}
   else
    {
     if(bCheckMask)
      {
       vertex[0].z=vertex[1].z=vertex[2].z=vertex[3].z=gl_z;
       gl_z+=0.00004f;
      }
     else
      {vertex[0].z=vertex[1].z=vertex[2].z=vertex[3].z=0.95f;}
    }
  }
}

////////////////////////////////////////////////////////////////////////

static __inline void SetRenderState(uint32_t DrawAttributes)
{
 bDrawNonShaded = (SHADETEXBIT(DrawAttributes)) ? TRUE : FALSE;
 DrawSemiTrans = (SEMITRANSBIT(DrawAttributes)) ? TRUE : FALSE;
}                         

////////////////////////////////////////////////////////////////////////                                          

static __inline void SetRenderColor(uint32_t DrawAttributes)
{
 if(bDrawNonShaded) {g_m1=g_m2=g_m3=128;}
 else
  {
   g_m1=DrawAttributes&0xff;
   g_m2=(DrawAttributes>>8)&0xff;
   g_m3=(DrawAttributes>>16)&0xff;
  }
}

////////////////////////////////////////////////////////////////////////                                          
                               
void SetRenderMode(uint32_t DrawAttributes, BOOL bSCol)
{
 if((bUseMultiPass) && (bDrawTextured) && !(bDrawNonShaded))
      {bDrawMultiPass = TRUE; SetSemiTransMulti(0);}
 else {bDrawMultiPass = FALSE;SetSemiTrans();}

 if(bDrawTextured)                                     // texture ? build it/get it from cache
  {
   GLuint currTex;
   if(bUsingTWin)       currTex=LoadTextureWnd(GlobalTexturePage,GlobalTextTP, ulClutID);
   else if(bUsingMovie) currTex=LoadTextureMovie();
   else                 currTex=SelectSubTextureS(GlobalTextTP,ulClutID);

   if(gTexName!=currTex)
    {gTexName=currTex;glBindTexture(GL_TEXTURE_2D,currTex);}

   if(!bTexEnabled)                                    // -> turn texturing on
    {bTexEnabled=TRUE;glEnable(GL_TEXTURE_2D);}
  }
 else                                                  // no texture ?
 if(bTexEnabled) 
  {bTexEnabled=FALSE;glDisable(GL_TEXTURE_2D);}        // -> turn texturing off

 if(bSCol)                                             // also set color ?
  {
   if((dwActFixes&4) && ((DrawAttributes&0x00ffffff)==0))
     DrawAttributes|=0x007f7f7f;

   if(bDrawNonShaded)                                  // -> non shaded?
    {
     if(bGLBlend)  vertex[0].c.lcol=0x7f7f7f;          // --> solid color...
     else          vertex[0].c.lcol=0xffffff;
    }
   else                                                // -> shaded?
    {
     if(!bUseMultiPass && !bGLBlend)                   // --> given color...
          vertex[0].c.lcol=DoubleBGR2RGB(DrawAttributes);
     else vertex[0].c.lcol=DrawAttributes;
    }
   vertex[0].c.col[3]=ubGloAlpha;                      // -> set color with
   SETCOL(vertex[0]);                                  //    texture alpha
  }
 
 if(bDrawSmoothShaded!=bOldSmoothShaded)               // shading changed?
  {
   if(bDrawSmoothShaded) glShadeModel(GL_SMOOTH);      // -> set actual shading
   else                  glShadeModel(GL_FLAT);
   bOldSmoothShaded=bDrawSmoothShaded;
  }
}

////////////////////////////////////////////////////////////////////////                                          
// Set Opaque multipass color
////////////////////////////////////////////////////////////////////////

void SetOpaqueColor(uint32_t DrawAttributes)
{
 if(bDrawNonShaded) return;                            // no shading? bye
  
 DrawAttributes=DoubleBGR2RGB(DrawAttributes);         // multipass is just half color, so double it on opaque pass
 vertex[0].c.lcol=DrawAttributes|0xff000000;
 SETCOL(vertex[0]);                                    // set color
}

////////////////////////////////////////////////////////////////////////                                          
// Fucking stupid screen coord checking
////////////////////////////////////////////////////////////////////////

BOOL ClipVertexListScreen(void)
{
 if (lx0 >= PSXDisplay.DisplayEnd.x)      goto NEXTSCRTEST;
 if (ly0 >= PSXDisplay.DisplayEnd.y)      goto NEXTSCRTEST;
 if (lx2 <  PSXDisplay.DisplayPosition.x) goto NEXTSCRTEST;
 if (ly2 <  PSXDisplay.DisplayPosition.y) goto NEXTSCRTEST;

 return TRUE;

NEXTSCRTEST:
 if(PSXDisplay.InterlacedTest) return FALSE;

 if (lx0 >= PreviousPSXDisplay.DisplayEnd.x)      return FALSE;
 if (ly0 >= PreviousPSXDisplay.DisplayEnd.y)      return FALSE;
 if (lx2 <  PreviousPSXDisplay.DisplayPosition.x) return FALSE;
 if (ly2 <  PreviousPSXDisplay.DisplayPosition.y) return FALSE;

 return TRUE;
}

////////////////////////////////////////////////////////////////////////

BOOL bDrawOffscreenFront(void)
{
 if(sxmin < PSXDisplay.DisplayPosition.x) return FALSE;   // must be complete in front
 if(symin < PSXDisplay.DisplayPosition.y) return FALSE;
 if(sxmax > PSXDisplay.DisplayEnd.x)      return FALSE;
 if(symax > PSXDisplay.DisplayEnd.y)      return FALSE;
 return TRUE;
}

BOOL bOnePointInFront(void)
{
 if(sxmax< PSXDisplay.DisplayPosition.x)
  return FALSE;

 if(symax< PSXDisplay.DisplayPosition.y)
  return FALSE;

 if(sxmin>=PSXDisplay.DisplayEnd.x)
  return FALSE;

 if(symin>=PSXDisplay.DisplayEnd.y)
  return FALSE;

 return TRUE;
}
 

BOOL bOnePointInBack(void)
{
 if(sxmax< PreviousPSXDisplay.DisplayPosition.x)
  return FALSE;

 if(symax< PreviousPSXDisplay.DisplayPosition.y)
  return FALSE;

 if(sxmin>=PreviousPSXDisplay.DisplayEnd.x)
  return FALSE;

 if(symin>=PreviousPSXDisplay.DisplayEnd.y)
  return FALSE;

 return TRUE;
}
 
BOOL bDrawOffscreen4(void)
{
 BOOL bFront;short sW,sH;

 sxmax=max(lx0,max(lx1,max(lx2,lx3)));
 if(sxmax<drawX) return FALSE;
 sxmin=min(lx0,min(lx1,min(lx2,lx3)));
 if(sxmin>drawW) return FALSE;
 symax=max(ly0,max(ly1,max(ly2,ly3)));
 if(symax<drawY) return FALSE;
 symin=min(ly0,min(ly1,min(ly2,ly3)));
 if(symin>drawH) return FALSE;

 if(PSXDisplay.Disabled) return TRUE;                  // disabled? ever

 if(iOffscreenDrawing==1) return bFullVRam;

 if(dwActFixes&1 && iOffscreenDrawing==4)
  {
   if(PreviousPSXDisplay.DisplayPosition.x==PSXDisplay.DisplayPosition.x &&
      PreviousPSXDisplay.DisplayPosition.y==PSXDisplay.DisplayPosition.y &&
      PreviousPSXDisplay.DisplayEnd.x==PSXDisplay.DisplayEnd.x &&
      PreviousPSXDisplay.DisplayEnd.y==PSXDisplay.DisplayEnd.y)
    {
     bRenderFrontBuffer=TRUE;
     return FALSE;
    }
  }

 sW=drawW-1;sH=drawH-1;
 
 sxmin=min(sW,max(sxmin,drawX));
 sxmax=max(drawX,min(sxmax,sW));
 symin=min(sH,max(symin,drawY));
 symax=max(drawY,min(symax,sH));

 if(bOnePointInBack()) return bFullVRam;

 if(iOffscreenDrawing==2) 
      bFront=bDrawOffscreenFront();
 else bFront=bOnePointInFront();

 if(bFront)
  {
   if(PSXDisplay.InterlacedTest) return bFullVRam;      // -> ok, no need for adjust
                               
   vertex[0].x=lx0 - PSXDisplay.DisplayPosition.x+PreviousPSXDisplay.Range.x0;
   vertex[1].x=lx1 - PSXDisplay.DisplayPosition.x+PreviousPSXDisplay.Range.x0;
   vertex[2].x=lx2 - PSXDisplay.DisplayPosition.x+PreviousPSXDisplay.Range.x0;
   vertex[3].x=lx3 - PSXDisplay.DisplayPosition.x+PreviousPSXDisplay.Range.x0;
   vertex[0].y=ly0 - PSXDisplay.DisplayPosition.y+PreviousPSXDisplay.Range.y0;
   vertex[1].y=ly1 - PSXDisplay.DisplayPosition.y+PreviousPSXDisplay.Range.y0;
   vertex[2].y=ly2 - PSXDisplay.DisplayPosition.y+PreviousPSXDisplay.Range.y0;
   vertex[3].y=ly3 - PSXDisplay.DisplayPosition.y+PreviousPSXDisplay.Range.y0;

   if(iOffscreenDrawing==4 && !(dwActFixes&1))         // -> frontbuffer wanted
    {
     bRenderFrontBuffer=TRUE;
     //return TRUE;
    }
   return bFullVRam;                                   // -> but no od
  }

 return TRUE;
}

////////////////////////////////////////////////////////////////////////
 
BOOL bDrawOffscreen3(void)
{
 BOOL bFront;short sW,sH;

 sxmax=max(lx0,max(lx1,lx2));
 if(sxmax<drawX) return FALSE;
 sxmin=min(lx0,min(lx1,lx2));
 if(sxmin>drawW) return FALSE;
 symax=max(ly0,max(ly1,ly2));
 if(symax<drawY) return FALSE;
 symin=min(ly0,min(ly1,ly2));
 if(symin>drawH) return FALSE;

 if(PSXDisplay.Disabled) return TRUE;                  // disabled? ever

 if(iOffscreenDrawing==1) return bFullVRam;

 sW=drawW-1;sH=drawH-1;
 sxmin=min(sW,max(sxmin,drawX));
 sxmax=max(drawX,min(sxmax,sW));
 symin=min(sH,max(symin,drawY));
 symax=max(drawY,min(symax,sH));

 if(bOnePointInBack()) return bFullVRam;

 if(iOffscreenDrawing==2) 
      bFront=bDrawOffscreenFront();
 else bFront=bOnePointInFront();

 if(bFront)
  {
   if(PSXDisplay.InterlacedTest) return bFullVRam;     // -> ok, no need for adjust

   vertex[0].x=lx0 - PSXDisplay.DisplayPosition.x+PreviousPSXDisplay.Range.x0;
   vertex[1].x=lx1 - PSXDisplay.DisplayPosition.x+PreviousPSXDisplay.Range.x0;
   vertex[2].x=lx2 - PSXDisplay.DisplayPosition.x+PreviousPSXDisplay.Range.x0;
   vertex[0].y=ly0 - PSXDisplay.DisplayPosition.y+PreviousPSXDisplay.Range.y0;
   vertex[1].y=ly1 - PSXDisplay.DisplayPosition.y+PreviousPSXDisplay.Range.y0;
   vertex[2].y=ly2 - PSXDisplay.DisplayPosition.y+PreviousPSXDisplay.Range.y0;

   if(iOffscreenDrawing==4)                            // -> frontbuffer wanted
    {
     bRenderFrontBuffer=TRUE;
   //  return TRUE;
    }

   return bFullVRam;                                   // -> but no od
  }

 return TRUE;
}

////////////////////////////////////////////////////////////////////////

BOOL FastCheckAgainstScreen(short imageX0,short imageY0,short imageX1,short imageY1)
{
 PSXRect_t xUploadArea;

 imageX1 += imageX0;
 imageY1 += imageY0;

 if (imageX0 < PreviousPSXDisplay.DisplayPosition.x)
   xUploadArea.x0 = PreviousPSXDisplay.DisplayPosition.x;
 else
 if (imageX0 > PreviousPSXDisplay.DisplayEnd.x)
   xUploadArea.x0 = PreviousPSXDisplay.DisplayEnd.x;
 else
   xUploadArea.x0 = imageX0;

 if(imageX1 < PreviousPSXDisplay.DisplayPosition.x)
   xUploadArea.x1 = PreviousPSXDisplay.DisplayPosition.x;
 else
 if (imageX1 > PreviousPSXDisplay.DisplayEnd.x)
   xUploadArea.x1 = PreviousPSXDisplay.DisplayEnd.x;
 else
   xUploadArea.x1 = imageX1;

 if (imageY0 < PreviousPSXDisplay.DisplayPosition.y)
   xUploadArea.y0 = PreviousPSXDisplay.DisplayPosition.y;
 else
 if (imageY0 > PreviousPSXDisplay.DisplayEnd.y)
   xUploadArea.y0 = PreviousPSXDisplay.DisplayEnd.y;
 else
   xUploadArea.y0 = imageY0;

 if (imageY1 < PreviousPSXDisplay.DisplayPosition.y)
   xUploadArea.y1 = PreviousPSXDisplay.DisplayPosition.y;
 else
 if (imageY1 > PreviousPSXDisplay.DisplayEnd.y)
   xUploadArea.y1 = PreviousPSXDisplay.DisplayEnd.y;
 else
   xUploadArea.y1 = imageY1;

 if ((xUploadArea.x0 != xUploadArea.x1) && (xUploadArea.y0 != xUploadArea.y1))
      return TRUE;
 else return FALSE;
}

BOOL CheckAgainstScreen(short imageX0,short imageY0,short imageX1,short imageY1)
{
 imageX1 += imageX0;
 imageY1 += imageY0;

 if (imageX0 < PreviousPSXDisplay.DisplayPosition.x)
   xrUploadArea.x0 = PreviousPSXDisplay.DisplayPosition.x;
 else
 if (imageX0 > PreviousPSXDisplay.DisplayEnd.x)
   xrUploadArea.x0 = PreviousPSXDisplay.DisplayEnd.x;
 else
   xrUploadArea.x0 = imageX0;

 if(imageX1 < PreviousPSXDisplay.DisplayPosition.x)
   xrUploadArea.x1 = PreviousPSXDisplay.DisplayPosition.x;
 else
 if (imageX1 > PreviousPSXDisplay.DisplayEnd.x)
   xrUploadArea.x1 = PreviousPSXDisplay.DisplayEnd.x;
 else
   xrUploadArea.x1 = imageX1;

 if (imageY0 < PreviousPSXDisplay.DisplayPosition.y)
   xrUploadArea.y0 = PreviousPSXDisplay.DisplayPosition.y;
 else
 if (imageY0 > PreviousPSXDisplay.DisplayEnd.y)
   xrUploadArea.y0 = PreviousPSXDisplay.DisplayEnd.y;
 else
   xrUploadArea.y0 = imageY0;

 if (imageY1 < PreviousPSXDisplay.DisplayPosition.y)
   xrUploadArea.y1 = PreviousPSXDisplay.DisplayPosition.y;
 else
 if (imageY1 > PreviousPSXDisplay.DisplayEnd.y)
   xrUploadArea.y1 = PreviousPSXDisplay.DisplayEnd.y;
 else
   xrUploadArea.y1 = imageY1;

 if ((xrUploadArea.x0 != xrUploadArea.x1) && (xrUploadArea.y0 != xrUploadArea.y1))
      return TRUE;
 else return FALSE;
}

BOOL FastCheckAgainstFrontScreen(short imageX0,short imageY0,short imageX1,short imageY1)
{
 PSXRect_t xUploadArea;

 imageX1 += imageX0;
 imageY1 += imageY0;

 if (imageX0 < PSXDisplay.DisplayPosition.x)
   xUploadArea.x0 = PSXDisplay.DisplayPosition.x;
 else
 if (imageX0 > PSXDisplay.DisplayEnd.x)
   xUploadArea.x0 = PSXDisplay.DisplayEnd.x;
 else
   xUploadArea.x0 = imageX0;

 if(imageX1 < PSXDisplay.DisplayPosition.x)
   xUploadArea.x1 = PSXDisplay.DisplayPosition.x;
 else
 if (imageX1 > PSXDisplay.DisplayEnd.x)
   xUploadArea.x1 = PSXDisplay.DisplayEnd.x;
 else
   xUploadArea.x1 = imageX1;

 if (imageY0 < PSXDisplay.DisplayPosition.y)
   xUploadArea.y0 = PSXDisplay.DisplayPosition.y;
 else
 if (imageY0 > PSXDisplay.DisplayEnd.y)
   xUploadArea.y0 = PSXDisplay.DisplayEnd.y;
 else
   xUploadArea.y0 = imageY0;

 if (imageY1 < PSXDisplay.DisplayPosition.y)
   xUploadArea.y1 = PSXDisplay.DisplayPosition.y;
 else
 if (imageY1 > PSXDisplay.DisplayEnd.y)
   xUploadArea.y1 = PSXDisplay.DisplayEnd.y;
 else
   xUploadArea.y1 = imageY1;

 if ((xUploadArea.x0 != xUploadArea.x1) && (xUploadArea.y0 != xUploadArea.y1))
      return TRUE; 
 else return FALSE;
}

BOOL CheckAgainstFrontScreen(short imageX0,short imageY0,short imageX1,short imageY1)
{
 imageX1 += imageX0;
 imageY1 += imageY0;

 if (imageX0 < PSXDisplay.DisplayPosition.x)
   xrUploadArea.x0 = PSXDisplay.DisplayPosition.x;
 else
 if (imageX0 > PSXDisplay.DisplayEnd.x)
   xrUploadArea.x0 = PSXDisplay.DisplayEnd.x;
 else
   xrUploadArea.x0 = imageX0;

 if(imageX1 < PSXDisplay.DisplayPosition.x)
   xrUploadArea.x1 = PSXDisplay.DisplayPosition.x;
 else
 if (imageX1 > PSXDisplay.DisplayEnd.x)
   xrUploadArea.x1 = PSXDisplay.DisplayEnd.x;
 else
   xrUploadArea.x1 = imageX1;

 if (imageY0 < PSXDisplay.DisplayPosition.y)
   xrUploadArea.y0 = PSXDisplay.DisplayPosition.y;
 else
 if (imageY0 > PSXDisplay.DisplayEnd.y)
   xrUploadArea.y0 = PSXDisplay.DisplayEnd.y;
 else
   xrUploadArea.y0 = imageY0;

 if (imageY1 < PSXDisplay.DisplayPosition.y)
   xrUploadArea.y1 = PSXDisplay.DisplayPosition.y;
 else
 if (imageY1 > PSXDisplay.DisplayEnd.y)
   xrUploadArea.y1 = PSXDisplay.DisplayEnd.y;
 else
   xrUploadArea.y1 = imageY1;

 if ((xrUploadArea.x0 != xrUploadArea.x1) && (xrUploadArea.y0 != xrUploadArea.y1))
      return TRUE; 
 else return FALSE;
}

////////////////////////////////////////////////////////////////////////

void PrepareFullScreenUpload (int Position)
{
 if (Position==-1)                                     // rgb24
  {
   if(PSXDisplay.Interlaced)
    {
     xrUploadArea.x0 = PSXDisplay.DisplayPosition.x;
     xrUploadArea.x1 = PSXDisplay.DisplayEnd.x;
     xrUploadArea.y0 = PSXDisplay.DisplayPosition.y;
     xrUploadArea.y1 = PSXDisplay.DisplayEnd.y;
    }
   else
    {
     xrUploadArea.x0 = PreviousPSXDisplay.DisplayPosition.x;
     xrUploadArea.x1 = PreviousPSXDisplay.DisplayEnd.x;
     xrUploadArea.y0 = PreviousPSXDisplay.DisplayPosition.y;
     xrUploadArea.y1 = PreviousPSXDisplay.DisplayEnd.y;
    }

   if(bNeedRGB24Update)
    {
     if(lClearOnSwap) 
      {
//       lClearOnSwap=0;
      }
     else    
     if(PSXDisplay.Interlaced && PreviousPSXDisplay.RGB24<2) // in interlaced mode we upload at least two full frames (GT1 menu)
      {
       PreviousPSXDisplay.RGB24++;
      }
     else
      {
       xrUploadArea.y1 = min(xrUploadArea.y0+xrUploadAreaRGB24.y1,xrUploadArea.y1);
       xrUploadArea.y0+=xrUploadAreaRGB24.y0;
      }
    }
  }
 else
 if (Position)
  {
   xrUploadArea.x0 = PSXDisplay.DisplayPosition.x;
   xrUploadArea.x1 = PSXDisplay.DisplayEnd.x;
   xrUploadArea.y0 = PSXDisplay.DisplayPosition.y;
   xrUploadArea.y1 = PSXDisplay.DisplayEnd.y;
  }
 else
  {
   xrUploadArea.x0 = PreviousPSXDisplay.DisplayPosition.x;
   xrUploadArea.x1 = PreviousPSXDisplay.DisplayEnd.x;
   xrUploadArea.y0 = PreviousPSXDisplay.DisplayPosition.y;
   xrUploadArea.y1 = PreviousPSXDisplay.DisplayEnd.y;
  }

 if (xrUploadArea.x0 < 0)               xrUploadArea.x0 = 0;
 else
 if (xrUploadArea.x0 > 1023)            xrUploadArea.x0 = 1023;

 if (xrUploadArea.x1 < 0)               xrUploadArea.x1 = 0;
 else
 if (xrUploadArea.x1 > 1024)            xrUploadArea.x1 = 1024;

 if (xrUploadArea.y0 < 0)               xrUploadArea.y0 = 0;
 else
 if (xrUploadArea.y0 > iGPUHeightMask)  xrUploadArea.y0 = iGPUHeightMask;

 if (xrUploadArea.y1 < 0)               xrUploadArea.y1 = 0;
 else
 if (xrUploadArea.y1 > iGPUHeight)      xrUploadArea.y1 = iGPUHeight;

 if (PSXDisplay.RGB24)
  {
   InvalidateTextureArea(xrUploadArea.x0,xrUploadArea.y0,xrUploadArea.x1-xrUploadArea.x0,xrUploadArea.y1-xrUploadArea.y0);
  }
}

////////////////////////////////////////////////////////////////////////
// Upload screen (MDEC and such)
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////

unsigned char * LoadDirectMovieFast(void);

void UploadScreenEx(int Position)
{
 short ya,yb,xa,xb,x, y, YStep, XStep, U, UStep,ux[4],vy[4];

 if(!PSXDisplay.DisplayMode.x) return;
 if(!PSXDisplay.DisplayMode.y) return;

 glDisable(GL_SCISSOR_TEST);
 glShadeModel(GL_FLAT);
 bOldSmoothShaded=FALSE;
 glDisable(GL_BLEND);
 bBlendEnable=FALSE;
 glDisable(GL_TEXTURE_2D);
 bTexEnabled=FALSE;
 glDisable(GL_ALPHA_TEST);

 glPixelZoom(((float)rRatioRect.right)/((float)PSXDisplay.DisplayMode.x),
             -1.0f*(((float)rRatioRect.bottom)/((float)PSXDisplay.DisplayMode.y)));
                                                      
 //----------------------------------------------------//

 YStep = 256;                                          // max texture size
 XStep = 256;
 UStep = (PSXDisplay.RGB24 ? 128 : 0);
 ya    = xrUploadArea.y0;
 yb    = xrUploadArea.y1;
 xa    = xrUploadArea.x0;
 xb    = xrUploadArea.x1;
 
 for(y=ya;y<=yb;y+=YStep)                              // loop y
  {
   U = 0;
   for(x=xa;x<=xb;x+=XStep)                            // loop x
    {
     ly0 = ly1 = y;                                    // -> get y coords
     ly2 = y + YStep;
     if (ly2 > yb) ly2 = yb;
     ly3 = ly2;

     lx0 = lx3 = x;                                    // -> get x coords
     lx1 = x + XStep;
     if (lx1 > xb) lx1 = xb;

     lx2 = lx1;

     ux[0]=ux[3]=(xa - x);                             // -> set tex x coords
     if (ux[0] < 0) ux[0]=ux[3]=0;
     ux[2]=ux[1]=(xb - x);
     if (ux[2] > 256) ux[2]=ux[1]=256;

     vy[0]=vy[1]=(ya - y);                             // -> set tex y coords
     if (vy[0] < 0) vy[0]=vy[1]=0;
     vy[2]=vy[3]=(yb - y);
     if (vy[2] > 256) vy[2]=vy[3]=256;

     if ((ux[0] >= ux[2]) ||                           // -> cheaters never win...
         (vy[0] >= vy[2])) continue;                   //    (but winners always cheat...)
                
     xrMovieArea.x0=lx0+U; xrMovieArea.y0=ly0;
     xrMovieArea.x1=lx2+U; xrMovieArea.y1=ly2;
     
     offsetScreenUpload(Position);

     glRasterPos2f(vertex[0].x,vertex[0].y);

     glDrawPixels(xrMovieArea.x1-xrMovieArea.x0,
                  xrMovieArea.y1-xrMovieArea.y0,
                  GL_RGBA,GL_UNSIGNED_BYTE,
                  LoadDirectMovieFast());

     U+=UStep;
    }
  }

 //----------------------------------------------------//

 glPixelZoom(1.0F,1.0F);

 glEnable(GL_ALPHA_TEST);
 glEnable(GL_SCISSOR_TEST);
}

////////////////////////////////////////////////////////////////////////

void UploadScreen(int Position)
{
 short x, y, YStep, XStep, U, s, UStep,ux[4],vy[4];
 short xa,xb,ya,yb;

 if(xrUploadArea.x0>1023) xrUploadArea.x0=1023;
 if(xrUploadArea.x1>1024) xrUploadArea.x1=1024;
 if(xrUploadArea.y0>iGPUHeightMask)  xrUploadArea.y0=iGPUHeightMask;
 if(xrUploadArea.y1>iGPUHeight)      xrUploadArea.y1=iGPUHeight;

 if(xrUploadArea.x0==xrUploadArea.x1) return;
 if(xrUploadArea.y0==xrUploadArea.y1) return;

 if(PSXDisplay.Disabled && iOffscreenDrawing<4) return;

 iDrawnSomething   = 2;
 iLastRGB24=PSXDisplay.RGB24+1;

 if(bSkipNextFrame) return;

 if(dwActFixes & 2) {UploadScreenEx(Position);return;}

 bUsingMovie       = TRUE;
 bDrawTextured     = TRUE;                             // just doing textures
 bDrawSmoothShaded = FALSE;

 if(bGLBlend) vertex[0].c.lcol=0xff7f7f7f;             // set solid col
 else          vertex[0].c.lcol=0xffffffff;
 SETCOL(vertex[0]); 

 SetOGLDisplaySettings(0);

 YStep = 256;                                          // max texture size
 XStep = 256;

 UStep = (PSXDisplay.RGB24 ? 128 : 0);
 
 ya=xrUploadArea.y0;
 yb=xrUploadArea.y1;
 xa=xrUploadArea.x0;
 xb=xrUploadArea.x1;

 for(y=ya;y<=yb;y+=YStep)                              // loop y
  {
   U = 0;
   for(x=xa;x<=xb;x+=XStep)                            // loop x
    {
     ly0 = ly1 = y;                                    // -> get y coords
     ly2 = y + YStep;
     if (ly2 > yb) ly2 = yb;
     ly3 = ly2;

     lx0 = lx3 = x;                                    // -> get x coords
     lx1 = x + XStep;
     if (lx1 > xb) lx1 = xb;

     lx2 = lx1;

     ux[0]=ux[3]=(xa - x);                             // -> set tex x coords
     if (ux[0] < 0) ux[0]=ux[3]=0;
     ux[2]=ux[1]=(xb - x);
     if (ux[2] > 256) ux[2]=ux[1]=256;

     vy[0]=vy[1]=(ya - y);                             // -> set tex y coords
     if (vy[0] < 0) vy[0]=vy[1]=0;
     vy[2]=vy[3]=(yb - y);
     if (vy[2] > 256) vy[2]=vy[3]=256;

     if ((ux[0] >= ux[2]) ||                           // -> cheaters never win...
         (vy[0] >= vy[2])) continue;                   //    (but winners always cheat...)
                
     xrMovieArea.x0=lx0+U; xrMovieArea.y0=ly0;
     xrMovieArea.x1=lx2+U; xrMovieArea.y1=ly2;

     s=ux[2] - ux[0]; if(s>255) s=255;

     gl_ux[2] = gl_ux[1] = s;
     s=vy[2] - vy[0]; if(s>255) s=255;
     gl_vy[2] = gl_vy[3] = s;
     gl_ux[0] = gl_ux[3] = gl_vy[0] = gl_vy[1] = 0;

     SetRenderState((uint32_t)0x01000000);
     SetRenderMode((uint32_t)0x01000000, FALSE);  // upload texture data
     offsetScreenUpload(Position);
     assignTextureVRAMWrite();

     PRIMdrawTexturedQuad(&vertex[0], &vertex[1], &vertex[2], &vertex[3]);

     U+=UStep;
    }
  }

 bUsingMovie=FALSE;                                    // done...
 bDisplayNotSet = TRUE;
}

////////////////////////////////////////////////////////////////////////
// Detect next screen
////////////////////////////////////////////////////////////////////////

BOOL IsCompleteInsideNextScreen(short x, short y, short xoff, short yoff)
{        
 if (x > PSXDisplay.DisplayPosition.x+1)     return FALSE;
 if ((x + xoff) < PSXDisplay.DisplayEnd.x-1) return FALSE;
 yoff+=y;
 if (y >= PSXDisplay.DisplayPosition.y &&
     y <= PSXDisplay.DisplayEnd.y )
  {
   if ((yoff) >= PSXDisplay.DisplayPosition.y &&
       (yoff) <= PSXDisplay.DisplayEnd.y ) return TRUE;
  }   
 if (y > PSXDisplay.DisplayPosition.y+1) return FALSE;
 if (yoff < PSXDisplay.DisplayEnd.y-1)   return FALSE;
 return TRUE;
}

BOOL IsPrimCompleteInsideNextScreen(short x, short y, short xoff, short yoff)
{
 x+=PSXDisplay.DrawOffset.x;
 if (x > PSXDisplay.DisplayPosition.x+1) return FALSE;
 y+=PSXDisplay.DrawOffset.y;
 if (y > PSXDisplay.DisplayPosition.y+1) return FALSE;
 xoff+=PSXDisplay.DrawOffset.x;
 if (xoff < PSXDisplay.DisplayEnd.x-1)   return FALSE;
 yoff+=PSXDisplay.DrawOffset.y;
 if (yoff < PSXDisplay.DisplayEnd.y-1)   return FALSE;
 return TRUE;
}

BOOL IsInsideNextScreen(short x, short y, short xoff, short yoff)
{                    
 if (x > PSXDisplay.DisplayEnd.x) return FALSE;
 if (y > PSXDisplay.DisplayEnd.y) return FALSE;
 if ((x + xoff) < PSXDisplay.DisplayPosition.x) return FALSE;
 if ((y + yoff) < PSXDisplay.DisplayPosition.y) return FALSE;
 return TRUE;
}

////////////////////////////////////////////////////////////////////////
// mask stuff...
////////////////////////////////////////////////////////////////////////

//Mask1    Set mask bit while drawing. 1 = on
//Mask2    Do not draw to mask areas. 1= on

void cmdSTP(unsigned char * baseAddr)
{
 uint32_t gdata = ((uint32_t*)baseAddr)[0];

 STATUSREG&=~0x1800;                                   // clear the necessary bits
 STATUSREG|=((gdata & 0x03) << 11);                    // set the current bits

 if(!iUseMask) return;

 if(gdata&1) {sSetMask=0x8000;lSetMask=0x80008000;iSetMask=1;}
 else        {sSetMask=0;     lSetMask=0;         iSetMask=0;}

 if(gdata&2) 
  {
   if(!(gdata&1)) iSetMask=2;
   bCheckMask=TRUE;
   if(iDepthFunc==0) return;
   iDepthFunc=0;
   glDepthFunc(GL_LESS);
  }
 else
  {
   bCheckMask=FALSE;
   if(iDepthFunc==1) return;
   glDepthFunc(GL_ALWAYS);
   iDepthFunc=1;
  }
}

////////////////////////////////////////////////////////////////////////
// cmd: Set texture page infos
////////////////////////////////////////////////////////////////////////

void cmdTexturePage(unsigned char * baseAddr)
{
 uint32_t gdata = ((uint32_t *)baseAddr)[0];
 UpdateGlobalTP((unsigned short)gdata);
 GlobalTextREST = (gdata&0x00ffffff)>>9;
}

////////////////////////////////////////////////////////////////////////
// cmd: turn on/off texture window
////////////////////////////////////////////////////////////////////////

void cmdTextureWindow(unsigned char *baseAddr)
{
 uint32_t gdata = ((uint32_t *)baseAddr)[0];
 uint32_t YAlign,XAlign;

 ulGPUInfoVals[INFO_TW]=gdata&0xFFFFF;

 if(gdata & 0x020)
  TWin.Position.y1 = 8;    // xxxx1
 else if (gdata & 0x040)
  TWin.Position.y1 = 16;   // xxx10
 else if (gdata & 0x080)
  TWin.Position.y1 = 32;   // xx100
 else if (gdata & 0x100)
  TWin.Position.y1 = 64;   // x1000
 else if (gdata & 0x200)
  TWin.Position.y1 = 128;  // 10000
 else
  TWin.Position.y1 = 256;  // 00000

  // Texture window size is determined by the least bit set of the relevant 5 bits

 if (gdata & 0x001)
  TWin.Position.x1 = 8;    // xxxx1
 else if (gdata & 0x002)
  TWin.Position.x1 = 16;   // xxx10
 else if (gdata & 0x004)
  TWin.Position.x1 = 32;   // xx100
 else if (gdata & 0x008)
  TWin.Position.x1 = 64;   // x1000
 else if (gdata & 0x010)
  TWin.Position.x1 = 128;  // 10000
 else
  TWin.Position.x1 = 256;  // 00000

 // Re-calculate the bit field, because we can't trust what is passed in the data

 YAlign = (uint32_t)(32 - (TWin.Position.y1 >> 3));
 XAlign = (uint32_t)(32 - (TWin.Position.x1 >> 3));

 // Absolute position of the start of the texture window

 TWin.Position.y0 = (short)(((gdata >> 15) & YAlign) << 3);
 TWin.Position.x0 = (short)(((gdata >> 10) & XAlign) << 3);

 if((TWin.Position.x0 == 0 &&                          // tw turned off
     TWin.Position.y0 == 0 &&
     TWin.Position.x1 == 0 &&
     TWin.Position.y1 == 0) ||
     (TWin.Position.x1 == 256 &&
      TWin.Position.y1 == 256))
  {
   bUsingTWin = FALSE;                                 // -> just do it

#ifdef OWNSCALE
   TWin.UScaleFactor = 1.0f;
   TWin.VScaleFactor = 1.0f;
#else
   TWin.UScaleFactor = 
   TWin.VScaleFactor = 1.0f/256.0f;
#endif
  }
 else                                                  // tw turned on
  {
   bUsingTWin = TRUE;

   TWin.OPosition.y1 = TWin.Position.y1;               // -> get psx sizes
   TWin.OPosition.x1 = TWin.Position.x1;              

   if(TWin.Position.x1<=2)   TWin.Position.x1=2;       // -> set OGL sizes
   else
   if(TWin.Position.x1<=4)   TWin.Position.x1=4;
   else
   if(TWin.Position.x1<=8)   TWin.Position.x1=8;
   else
   if(TWin.Position.x1<=16)  TWin.Position.x1=16;
   else
   if(TWin.Position.x1<=32)  TWin.Position.x1=32;
   else
   if(TWin.Position.x1<=64)  TWin.Position.x1=64;
   else
   if(TWin.Position.x1<=128) TWin.Position.x1=128;
   else
   if(TWin.Position.x1<=256) TWin.Position.x1=256;
   
   if(TWin.Position.y1<=2)   TWin.Position.y1=2;
   else
   if(TWin.Position.y1<=4)   TWin.Position.y1=4;
   else
   if(TWin.Position.y1<=8)   TWin.Position.y1=8;
   else
   if(TWin.Position.y1<=16)  TWin.Position.y1=16;
   else
   if(TWin.Position.y1<=32)  TWin.Position.y1=32;
   else
   if(TWin.Position.y1<=64)  TWin.Position.y1=64;
   else
   if(TWin.Position.y1<=128) TWin.Position.y1=128;
   else
   if(TWin.Position.y1<=256) TWin.Position.y1=256;

#ifdef OWNSCALE
   TWin.UScaleFactor = (float)TWin.Position.x1;
   TWin.VScaleFactor = (float)TWin.Position.y1;
#else
   TWin.UScaleFactor = ((float)TWin.Position.x1)/256.0f; // -> set scale factor
   TWin.VScaleFactor = ((float)TWin.Position.y1)/256.0f;
#endif
  }
}

////////////////////////////////////////////////////////////////////////
// mmm, Lewpy uses that in TileS ... I don't ;)
////////////////////////////////////////////////////////////////////////

/*
void ClampToPSXDrawAreaOffset(short *x0, short *y0, short *x1, short *y1)
{
 if (*x0 < PSXDisplay.DrawArea.x0)
  {
   *x1 -= (PSXDisplay.DrawArea.x0 - *x0);
   *x0 = PSXDisplay.DrawArea.x0;
  }
 else
 if (*x0 > PSXDisplay.DrawArea.x1)
  {
   *x0 = PSXDisplay.DrawArea.x1;
   *x1 = 0;
  }

 if (*y0 < PSXDisplay.DrawArea.y0)
  {
   *y1 -= (PSXDisplay.DrawArea.y0 - *y0);
   *y0 = PSXDisplay.DrawArea.y0;
  }
 else
 if (*y0 > PSXDisplay.DrawArea.y1)
  {
   *y0 = PSXDisplay.DrawArea.y1;
   *y1 = 0;
  }

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

 if ((*x1 + *x0) > PSXDisplay.DrawArea.x1)
  *x1 = (PSXDisplay.DrawArea.x1 -  *x0 + 1);

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

 if ((*y1 + *y0) > PSXDisplay.DrawArea.y1)
  *y1 = (PSXDisplay.DrawArea.y1 -  *y0 + 1);
}
*/

////////////////////////////////////////////////////////////////////////
// Check draw area dimensions
////////////////////////////////////////////////////////////////////////

void ClampToPSXScreen(short *x0, short *y0, short *x1, short *y1)
{
 if (*x0 < 0)               *x0 = 0;
 else
 if (*x0 > 1023)            *x0 = 1023;
            
 if (*x1 < 0)               *x1 = 0;
 else
 if (*x1 > 1023)            *x1 = 1023;

 if (*y0 < 0)               *y0 = 0;
 else
 if (*y0 > iGPUHeightMask)  *y0 = iGPUHeightMask;
            
 if (*y1 < 0)               *y1 = 0;
 else
 if (*y1 > iGPUHeightMask)  *y1 = iGPUHeightMask;
}

////////////////////////////////////////////////////////////////////////
// Used in Load Image and Blk Fill
////////////////////////////////////////////////////////////////////////

void ClampToPSXScreenOffset(short *x0, short *y0, short *x1, short *y1)
{
 if (*x0 < 0)
  { *x1 += *x0;  *x0 = 0; }
 else
 if (*x0 > 1023)
  { *x0 = 1023;  *x1 = 0; }

 if (*y0 < 0)
  { *y1 += *y0;  *y0 = 0; }
 else
 if (*y0 > iGPUHeightMask)
  { *y0 = iGPUHeightMask;   *y1 = 0; }

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

 if ((*x1 + *x0) > 1024) *x1 = (1024 -  *x0);

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

 if ((*y1 + *y0) > iGPUHeight)  *y1 = (iGPUHeight -  *y0);
}

////////////////////////////////////////////////////////////////////////
// cmd: start of drawing area... primitives will be clipped inside
////////////////////////////////////////////////////////////////////////

void cmdDrawAreaStart(unsigned char * baseAddr)
{
 uint32_t gdata = ((uint32_t *)baseAddr)[0];

 drawX = gdata & 0x3ff;                                // for soft drawing
 if(drawX>=1024) drawX=1023;

 if(dwGPUVersion==2)
  {
   ulGPUInfoVals[INFO_DRAWSTART]=gdata&0x3FFFFF;
   drawY  = (gdata>>12)&0x3ff;
  }
 else
  {
   ulGPUInfoVals[INFO_DRAWSTART]=gdata&0xFFFFF;
   drawY  = (gdata>>10)&0x3ff;
  }

 if(drawY>=iGPUHeight) drawY=iGPUHeightMask;

 PreviousPSXDisplay.DrawArea.y0=PSXDisplay.DrawArea.y0;
 PreviousPSXDisplay.DrawArea.x0=PSXDisplay.DrawArea.x0;

 PSXDisplay.DrawArea.y0 = (short)drawY;                // for OGL drawing
 PSXDisplay.DrawArea.x0 = (short)drawX;
}

////////////////////////////////////////////////////////////////////////
// cmd: end of drawing area... primitives will be clipped inside
////////////////////////////////////////////////////////////////////////

void cmdDrawAreaEnd(unsigned char * baseAddr)
{
 uint32_t gdata = ((uint32_t *)baseAddr)[0];

 drawW = gdata & 0x3ff;                                // for soft drawing
 if(drawW>=1024) drawW=1023;

 if(dwGPUVersion==2)
  {
   ulGPUInfoVals[INFO_DRAWEND]=gdata&0x3FFFFF;
   drawH  = (gdata>>12)&0x3ff;
  }
 else
  {
   ulGPUInfoVals[INFO_DRAWEND]=gdata&0xFFFFF;
   drawH  = (gdata>>10)&0x3ff;
  }
 
 if(drawH>=iGPUHeight) drawH=iGPUHeightMask;

 PSXDisplay.DrawArea.y1 = (short)drawH;                // for OGL drawing
 PSXDisplay.DrawArea.x1 = (short)drawW;

 ClampToPSXScreen(&PSXDisplay.DrawArea.x0,             // clamp
                  &PSXDisplay.DrawArea.y0,
                  &PSXDisplay.DrawArea.x1,
                  &PSXDisplay.DrawArea.y1);

 bDisplayNotSet = TRUE;
}

////////////////////////////////////////////////////////////////////////
// cmd: draw offset... will be added to prim coords
////////////////////////////////////////////////////////////////////////

void cmdDrawOffset(unsigned char * baseAddr)
{
 uint32_t gdata = ((uint32_t *)baseAddr)[0];

 PreviousPSXDisplay.DrawOffset.x = 
  PSXDisplay.DrawOffset.x = (short)(gdata & 0x7ff);

 if (dwGPUVersion == 2)
  {
   ulGPUInfoVals[INFO_DRAWOFF] = gdata&0x7FFFFF;
   PSXDisplay.DrawOffset.y = (short)((gdata>>12) & 0x7ff);
  }
 else
  {
   ulGPUInfoVals[INFO_DRAWOFF]=gdata&0x3FFFFF;
   PSXDisplay.DrawOffset.y = (short)((gdata>>11) & 0x7ff);
  }
 
 PSXDisplay.DrawOffset.x=(short)(((int)PSXDisplay.DrawOffset.x<<21)>>21);
 PSXDisplay.DrawOffset.y=(short)(((int)PSXDisplay.DrawOffset.y<<21)>>21);

 PSXDisplay.CumulOffset.x =                            // new OGL prim offsets
  PSXDisplay.DrawOffset.x - PSXDisplay.GDrawOffset.x + PreviousPSXDisplay.Range.x0;
 PSXDisplay.CumulOffset.y = 
  PSXDisplay.DrawOffset.y - PSXDisplay.GDrawOffset.y + PreviousPSXDisplay.Range.y0;
}

////////////////////////////////////////////////////////////////////////
// cmd: load image to vram
////////////////////////////////////////////////////////////////////////

void primLoadImage(unsigned char * baseAddr)
{
 unsigned short *sgpuData = ((unsigned short *) baseAddr);

 VRAMWrite.x      = sgpuData[2]&0x03ff;
 VRAMWrite.y      = sgpuData[3]&iGPUHeightMask;
 VRAMWrite.Width  = sgpuData[4];
 VRAMWrite.Height = sgpuData[5];

 iDataWriteMode = DR_VRAMTRANSFER;
 VRAMWrite.ImagePtr = psxVuw + (VRAMWrite.y<<10) + VRAMWrite.x;
 VRAMWrite.RowsRemaining = VRAMWrite.Width;
 VRAMWrite.ColsRemaining = VRAMWrite.Height;

 bNeedWriteUpload=TRUE;
}

////////////////////////////////////////////////////////////////////////

void PrepareRGB24Upload(void)
{
 VRAMWrite.x=(VRAMWrite.x*2)/3;
 VRAMWrite.Width=(VRAMWrite.Width*2)/3;

 if(!PSXDisplay.InterlacedTest && // NEW
    CheckAgainstScreen(VRAMWrite.x, VRAMWrite.y, VRAMWrite.Width, VRAMWrite.Height))
  {
   xrUploadArea.x0-=PreviousPSXDisplay.DisplayPosition.x;
   xrUploadArea.x1-=PreviousPSXDisplay.DisplayPosition.x;
   xrUploadArea.y0-=PreviousPSXDisplay.DisplayPosition.y;
   xrUploadArea.y1-=PreviousPSXDisplay.DisplayPosition.y;
  }  
 else
 if(CheckAgainstFrontScreen(VRAMWrite.x, VRAMWrite.y, VRAMWrite.Width, VRAMWrite.Height))
  {
   xrUploadArea.x0-=PSXDisplay.DisplayPosition.x;
   xrUploadArea.x1-=PSXDisplay.DisplayPosition.x;
   xrUploadArea.y0-=PSXDisplay.DisplayPosition.y;
   xrUploadArea.y1-=PSXDisplay.DisplayPosition.y;
  }  
 else return;

 if(bRenderFrontBuffer) 
  {
   updateFrontDisplay();
  }

 if(bNeedRGB24Update==FALSE)
  {
   xrUploadAreaRGB24=xrUploadArea;
   bNeedRGB24Update=TRUE;
  }
 else
  {
   xrUploadAreaRGB24.x0=min(xrUploadAreaRGB24.x0,xrUploadArea.x0);
   xrUploadAreaRGB24.x1=max(xrUploadAreaRGB24.x1,xrUploadArea.x1);
   xrUploadAreaRGB24.y0=min(xrUploadAreaRGB24.y0,xrUploadArea.y0);
   xrUploadAreaRGB24.y1=max(xrUploadAreaRGB24.y1,xrUploadArea.y1);
  }
}

////////////////////////////////////////////////////////////////////////

void CheckWriteUpdate()
{
 int iX=0,iY=0;

 if(VRAMWrite.Width)   iX=1;
 if(VRAMWrite.Height)  iY=1;

 InvalidateTextureArea(VRAMWrite.x, VRAMWrite.y, VRAMWrite.Width-iX, VRAMWrite.Height-iY);

 if(PSXDisplay.Interlaced && !iOffscreenDrawing) return;

 if(PSXDisplay.RGB24) {PrepareRGB24Upload();return;}

 if(!PSXDisplay.InterlacedTest &&
    CheckAgainstScreen(VRAMWrite.x, VRAMWrite.y, VRAMWrite.Width, VRAMWrite.Height)) 
  {
   if(dwActFixes&0x800) return;

   if(bRenderFrontBuffer) 
    {
     updateFrontDisplay();
    }

   UploadScreen(FALSE);

   bNeedUploadTest=TRUE;
  }
 else 
 if(iOffscreenDrawing)
  {
   if (CheckAgainstFrontScreen(VRAMWrite.x, VRAMWrite.y, VRAMWrite.Width, VRAMWrite.Height)) 
    {
     if(PSXDisplay.InterlacedTest)
      {
       if(PreviousPSXDisplay.InterlacedNew) 
        {
         PreviousPSXDisplay.InterlacedNew=FALSE;
         bNeedInterlaceUpdate=TRUE;
         xrUploadAreaIL.x0=PSXDisplay.DisplayPosition.x;
         xrUploadAreaIL.y0=PSXDisplay.DisplayPosition.y;
         xrUploadAreaIL.x1=PSXDisplay.DisplayPosition.x+PSXDisplay.DisplayModeNew.x;
         xrUploadAreaIL.y1=PSXDisplay.DisplayPosition.y+PSXDisplay.DisplayModeNew.y;
         if(xrUploadAreaIL.x1>1023) xrUploadAreaIL.x1=1023;
         if(xrUploadAreaIL.y1>511)  xrUploadAreaIL.y1=511;
        }

       if(bNeedInterlaceUpdate==FALSE)
        {
         xrUploadAreaIL=xrUploadArea;
         bNeedInterlaceUpdate=TRUE;
        }
       else
        {
         xrUploadAreaIL.x0=min(xrUploadAreaIL.x0,xrUploadArea.x0);
         xrUploadAreaIL.x1=max(xrUploadAreaIL.x1,xrUploadArea.x1);
         xrUploadAreaIL.y0=min(xrUploadAreaIL.y0,xrUploadArea.y0);
         xrUploadAreaIL.y1=max(xrUploadAreaIL.y1,xrUploadArea.y1);
        }
       return;
      }

     if(!bNeedUploadAfter)
      {
       bNeedUploadAfter = TRUE;
       xrUploadArea.x0=VRAMWrite.x;
       xrUploadArea.x1=VRAMWrite.x+VRAMWrite.Width;
       xrUploadArea.y0=VRAMWrite.y;
       xrUploadArea.y1=VRAMWrite.y+VRAMWrite.Height;
      }
     else
      {
       xrUploadArea.x0=min(xrUploadArea.x0,VRAMWrite.x);
       xrUploadArea.x1=max(xrUploadArea.x1,VRAMWrite.x+VRAMWrite.Width);
       xrUploadArea.y0=min(xrUploadArea.y0,VRAMWrite.y);
       xrUploadArea.y1=max(xrUploadArea.y1,VRAMWrite.y+VRAMWrite.Height);
      }

     if(dwActFixes&0x8000)
      {
       if((xrUploadArea.x1-xrUploadArea.x0)>=(PSXDisplay.DisplayMode.x-32) &&
          (xrUploadArea.y1-xrUploadArea.y0)>=(PSXDisplay.DisplayMode.y-32))
        {
         UploadScreen(-1);
         updateFrontDisplay();
        }
      }
    }
  }
}

////////////////////////////////////////////////////////////////////////
// cmd: vram -> psx mem
////////////////////////////////////////////////////////////////////////

void primStoreImage(unsigned char * baseAddr)
{
 unsigned short *sgpuData = ((unsigned short *) baseAddr);

 VRAMRead.x      = sgpuData[2]&0x03ff;
 VRAMRead.y      = sgpuData[3]&iGPUHeightMask;
 VRAMRead.Width  = sgpuData[4];
 VRAMRead.Height = sgpuData[5];

 VRAMRead.ImagePtr = psxVuw + (VRAMRead.y<<10) + VRAMRead.x;
 VRAMRead.RowsRemaining = VRAMRead.Width;
 VRAMRead.ColsRemaining = VRAMRead.Height;

 iDataReadMode = DR_VRAMTRANSFER;

 STATUSREG |= GPUSTATUS_READYFORVRAM;
}

////////////////////////////////////////////////////////////////////////
// cmd: blkfill - NO primitive! Doesn't care about draw areas...
////////////////////////////////////////////////////////////////////////

void primBlkFill(unsigned char * baseAddr)
{
 uint32_t *gpuData = ((uint32_t *) baseAddr);
 short *sgpuData = ((short *) baseAddr);

 iDrawnSomething = 1;

 sprtX = sgpuData[2];
 sprtY = sgpuData[3];
 sprtW = sgpuData[4] & 0x3ff;
 sprtH = sgpuData[5] & iGPUHeightMask;

 sprtW = (sprtW + 15) & ~15;

 // Increase H & W if they are one short of full values, because they never can be full values
 if (sprtH == iGPUHeightMask)  sprtH=iGPUHeight;
 if (sprtW == 1023)            sprtW=1024; 
        
 // x and y of start
 ly0 = ly1 = sprtY;
 ly2 = ly3 = (sprtY+sprtH);
 lx0 = lx3 = sprtX;
 lx1 = lx2 = (sprtX+sprtW);

 offsetBlk();

 if(ClipVertexListScreen())                           
  {
   PSXDisplay_t * pd;
   if(PSXDisplay.InterlacedTest) pd=&PSXDisplay;
   else                          pd=&PreviousPSXDisplay;

   if ((lx0 <= pd->DisplayPosition.x+16) &&
       (ly0 <= pd->DisplayPosition.y+16) &&
       (lx2 >= pd->DisplayEnd.x-16) &&
       (ly2 >= pd->DisplayEnd.y-16))
    {
     GLclampf g,b,r;
     g=((GLclampf)GREEN(gpuData[0]))/255.0f;
     b=((GLclampf)BLUE(gpuData[0]))/255.0f;
     r=((GLclampf)RED(gpuData[0]))/255.0f;
     
     glDisable(GL_SCISSOR_TEST);                       
     glClearColor(r,g,b,1.0f);
     glClear(uiBufferBits); 
     gl_z=0.0f;

     if(gpuData[0]!=0x02000000 &&
        (ly0>pd->DisplayPosition.y ||
         ly2<pd->DisplayEnd.y))
      {
       bDrawTextured     = FALSE;
       bDrawSmoothShaded = FALSE;
       SetRenderState((uint32_t)0x01000000);
       SetRenderMode((uint32_t)0x01000000, FALSE);
       vertex[0].c.lcol=0xff000000;
       SETCOL(vertex[0]); 
       if(ly0>pd->DisplayPosition.y)
        {
         vertex[0].x=0;vertex[0].y=0;
         vertex[1].x=pd->DisplayEnd.x-pd->DisplayPosition.x;vertex[1].y=0;
         vertex[2].x=vertex[1].x;vertex[2].y=ly0-pd->DisplayPosition.y;
         vertex[3].x=0;vertex[3].y=vertex[2].y;
         PRIMdrawQuad(&vertex[0], &vertex[1], &vertex[2], &vertex[3]);
        }
       if(ly2<pd->DisplayEnd.y)
        {
         vertex[0].x=0;vertex[0].y=(pd->DisplayEnd.y-pd->DisplayPosition.y)-(pd->DisplayEnd.y-ly2);
         vertex[1].x=pd->DisplayEnd.x-pd->DisplayPosition.x;vertex[1].y=vertex[0].y;
         vertex[2].x=vertex[1].x;vertex[2].y=pd->DisplayEnd.y;
         vertex[3].x=0;vertex[3].y=vertex[2].y;
         PRIMdrawQuad(&vertex[0], &vertex[1], &vertex[2], &vertex[3]);
        }
      }

     glEnable(GL_SCISSOR_TEST);                       
    }
   else
    {
     bDrawTextured     = FALSE;
     bDrawSmoothShaded = FALSE;
     SetRenderState((uint32_t)0x01000000);
     SetRenderMode((uint32_t)0x01000000, FALSE);
     vertex[0].c.lcol=gpuData[0]|0xff000000;
     SETCOL(vertex[0]); 
     glDisable(GL_SCISSOR_TEST);                       
     PRIMdrawQuad(&vertex[0], &vertex[1], &vertex[2], &vertex[3]);
     glEnable(GL_SCISSOR_TEST);                       
    }
  }

 //mmm... will clean all stuff, also if not all _should_ be cleaned...
 //if (IsInsideNextScreen(sprtX, sprtY, sprtW, sprtH))
 // try this:
 if (IsCompleteInsideNextScreen(sprtX, sprtY, sprtW, sprtH))
  {
   lClearOnSwapColor = COLOR(gpuData[0]);
   lClearOnSwap = 1;
  }

 if(iOffscreenDrawing)
  {
   ClampToPSXScreenOffset( &sprtX, &sprtY, &sprtW, &sprtH);
   if ((sprtW == 0) || (sprtH == 0)) return;
   InvalidateTextureArea(sprtX, sprtY, sprtW-1, sprtH-1);   

   sprtW+=sprtX;
   sprtH+=sprtY;

   FillSoftwareArea(sprtX, sprtY, sprtW, sprtH, BGR24to16(gpuData[0]));
  }
}
  
////////////////////////////////////////////////////////////////////////
// cmd: move image vram -> vram
////////////////////////////////////////////////////////////////////////

void MoveImageWrapped(short imageX0,short imageY0,
                      short imageX1,short imageY1,
                      short imageSX,short imageSY)
{
 int i,j,imageXE,imageYE;

 if(iFrameReadType&2)
  {
   imageXE=imageX0+imageSX;
   imageYE=imageY0+imageSY;

   if(imageYE>iGPUHeight && imageXE>1024) 
    {
     CheckVRamRead(0,0,
                   (imageXE&0x3ff),
                   (imageY0&iGPUHeightMask),
                   FALSE);
    }

   if(imageXE>1024) 
    {
     CheckVRamRead(0,imageY0, 
                   (imageXE&0x3ff),
                   (imageYE>iGPUHeight)?iGPUHeight:imageYE,
                   FALSE);
    }

   if(imageYE>iGPUHeight) 
    {
     CheckVRamRead(imageX0,0, 
                   (imageXE>1024)?1024:imageXE,
                   imageYE&iGPUHeightMask,
                   FALSE);
    }

   CheckVRamRead(imageX0,imageY0, 
                 (imageXE>1024)?1024:imageXE,
                 (imageYE>iGPUHeight)?iGPUHeight:imageYE,
                 FALSE);
  }

 for(j=0;j<imageSY;j++)
  for(i=0;i<imageSX;i++)
   psxVuw [(1024*((imageY1+j)&iGPUHeightMask))+((imageX1+i)&0x3ff)]=
    psxVuw[(1024*((imageY0+j)&iGPUHeightMask))+((imageX0+i)&0x3ff)];

 if(!PSXDisplay.RGB24)
  {
   imageXE=imageX1+imageSX;
   imageYE=imageY1+imageSY;

   if(imageYE>iGPUHeight && imageXE>1024) 
    {
     InvalidateTextureArea(0,0,
                           (imageXE&0x3ff)-1,
                           (imageYE&iGPUHeightMask)-1);
    }

   if(imageXE>1024) 
    {
     InvalidateTextureArea(0,imageY1,
                           (imageXE&0x3ff)-1,
                           ((imageYE>iGPUHeight)?iGPUHeight:imageYE)-imageY1-1);
    }

   if(imageYE>iGPUHeight) 
    {
     InvalidateTextureArea(imageX1,0,
                           ((imageXE>1024)?1024:imageXE)-imageX1-1,
                           (imageYE&iGPUHeightMask)-1);
    }

   InvalidateTextureArea(imageX1,imageY1,
                         ((imageXE>1024)?1024:imageXE)-imageX1-1,
                         ((imageYE>iGPUHeight)?iGPUHeight:imageYE)-imageY1-1);
  }
}

////////////////////////////////////////////////////////////////////////

void primMoveImage(unsigned char * baseAddr)
{
 short *sgpuData = ((short *) baseAddr);
 short imageY0,imageX0,imageY1,imageX1,imageSX,imageSY,i,j;

 imageX0 = sgpuData[2]&0x03ff;
 imageY0 = sgpuData[3]&iGPUHeightMask;
 imageX1 = sgpuData[4]&0x03ff;
 imageY1 = sgpuData[5]&iGPUHeightMask;
 imageSX = sgpuData[6];
 imageSY = sgpuData[7];

 if((imageX0 == imageX1) && (imageY0 == imageY1)) return;  
 if(imageSX<=0) return;
 if(imageSY<=0) return;

 if(iGPUHeight==1024 && sgpuData[7]>1024) return;

 if((imageY0+imageSY)>iGPUHeight ||
    (imageX0+imageSX)>1024       ||
    (imageY1+imageSY)>iGPUHeight ||
    (imageX1+imageSX)>1024)
  {
   MoveImageWrapped(imageX0,imageY0,imageX1,imageY1,imageSX,imageSY);
   if((imageY0+imageSY)>iGPUHeight) imageSY=iGPUHeight-imageY0;
   if((imageX0+imageSX)>1024)       imageSX=1024-imageX0;
   if((imageY1+imageSY)>iGPUHeight) imageSY=iGPUHeight-imageY1;
   if((imageX1+imageSX)>1024)       imageSX=1024-imageX1;
  }

 if(iFrameReadType&2)
  CheckVRamRead(imageX0,imageY0, 
                imageX0+imageSX,
                imageY0+imageSY,
                FALSE);

 if(imageSX&1)
  {
   unsigned short *SRCPtr, *DSTPtr;
   unsigned short LineOffset;

   SRCPtr = psxVuw + (1024*imageY0) + imageX0;
   DSTPtr = psxVuw + (1024*imageY1) + imageX1;

   LineOffset = 1024 - imageSX;

   for(j=0;j<imageSY;j++)
    {
     for(i=0;i<imageSX;i++) *DSTPtr++ = *SRCPtr++;
     SRCPtr += LineOffset;
     DSTPtr += LineOffset;
    }
  }
 else
  {
   uint32_t *SRCPtr, *DSTPtr;
   unsigned short LineOffset;
   int dx=imageSX>>1;

   SRCPtr = (uint32_t *)(psxVuw + (1024*imageY0) + imageX0);
   DSTPtr = (uint32_t *)(psxVuw + (1024*imageY1) + imageX1);

   LineOffset = 512 - dx;

   for(j=0;j<imageSY;j++)
    {
     for(i=0;i<dx;i++) *DSTPtr++ = *SRCPtr++;
     SRCPtr += LineOffset;
     DSTPtr += LineOffset;
    }
  }

 if (!PSXDisplay.RGB24)
  {
   InvalidateTextureArea(imageX1,imageY1,imageSX-1,imageSY-1);

   if (CheckAgainstScreen(imageX1,imageY1,imageSX,imageSY)) 
    {
     if(imageX1>=PreviousPSXDisplay.DisplayPosition.x &&
        imageX1<PreviousPSXDisplay.DisplayEnd.x &&
        imageY1>=PreviousPSXDisplay.DisplayPosition.y &&
        imageY1<PreviousPSXDisplay.DisplayEnd.y)
      {
       imageX1 += imageSX;
       imageY1 += imageSY;

       if(imageX1>=PreviousPSXDisplay.DisplayPosition.x &&
          imageX1<=PreviousPSXDisplay.DisplayEnd.x &&
          imageY1>=PreviousPSXDisplay.DisplayPosition.y &&
          imageY1<=PreviousPSXDisplay.DisplayEnd.y)
        {
         if(!(
               imageX0>=PSXDisplay.DisplayPosition.x &&
               imageX0<PSXDisplay.DisplayEnd.x &&
               imageY0>=PSXDisplay.DisplayPosition.y &&
               imageY0<PSXDisplay.DisplayEnd.y 
              ))
          {
           if(bRenderFrontBuffer) 
            {
             updateFrontDisplay();
            }
 
           UploadScreen(FALSE);
          }
         else bFakeFrontBuffer=TRUE;
        }
      }

     bNeedUploadTest=TRUE;
    }
   else
   if(iOffscreenDrawing)
    {
     if (CheckAgainstFrontScreen(imageX1,imageY1,imageSX,imageSY)) 
      {
       if(!PSXDisplay.InterlacedTest &&
//          !bFullVRam &&
          ((
            imageX0>=PreviousPSXDisplay.DisplayPosition.x &&
            imageX0<PreviousPSXDisplay.DisplayEnd.x &&
            imageY0>=PreviousPSXDisplay.DisplayPosition.y &&
            imageY0<PreviousPSXDisplay.DisplayEnd.y
           ) ||
           (
            imageX0>=PSXDisplay.DisplayPosition.x &&
            imageX0<PSXDisplay.DisplayEnd.x &&
            imageY0>=PSXDisplay.DisplayPosition.y &&
            imageY0<PSXDisplay.DisplayEnd.y
           )))
        return;

       bNeedUploadTest=TRUE;

       if(!bNeedUploadAfter)
        {
         bNeedUploadAfter = TRUE;
         xrUploadArea.x0=imageX0;
         xrUploadArea.x1=imageX0+imageSX;
         xrUploadArea.y0=imageY0;
         xrUploadArea.y1=imageY0+imageSY;
        }
       else
        {
         xrUploadArea.x0=min(xrUploadArea.x0,imageX0);
         xrUploadArea.x1=max(xrUploadArea.x1,imageX0+imageSX);
         xrUploadArea.y0=min(xrUploadArea.y0,imageY0);
         xrUploadArea.y1=max(xrUploadArea.y1,imageY0+imageSY);
        }
      }
    }
  }
}


////////////////////////////////////////////////////////////////////////
// cmd: draw free-size Tile 
////////////////////////////////////////////////////////////////////////

void primTileS(unsigned char * baseAddr)
{
 uint32_t *gpuData = ((uint32_t *)baseAddr);
 short *sgpuData = ((short *) baseAddr);

 sprtX = sgpuData[2];
 sprtY = sgpuData[3];
 sprtW = sgpuData[4] & 0x3ff;
 sprtH = sgpuData[5] & iGPUHeightMask;

 // x and y of start

 lx0 = sprtX;
 ly0 = sprtY;

 offsetST();

 if((dwActFixes&1) &&                                  // FF7 special game gix (battle cursor)
    sprtX==0 && sprtY==0 && sprtW==24 && sprtH==16) 
  return;

 bDrawTextured = FALSE;
 bDrawSmoothShaded = FALSE;

 SetRenderState(gpuData[0]);

 if(iOffscreenDrawing)
  {
   if(IsPrimCompleteInsideNextScreen(lx0,ly0,lx2,ly2) ||
      (ly0==-6 && ly2==10))                            // OH MY GOD... I DIDN'T WANT TO DO IT... BUT I'VE FOUND NO OTHER WAY... HACK FOR GRADIUS SHOOTER :(
    {
     lClearOnSwapColor = COLOR(gpuData[0]);
     lClearOnSwap = 1;
    }

   offsetPSX4();
   if(bDrawOffscreen4())
    {
     if(!(iTileCheat && sprtH==32 && gpuData[0]==0x60ffffff)) // special cheat for certain ZiNc games
      {
       InvalidateTextureAreaEx();   
       FillSoftwareAreaTrans(lx0,ly0,lx2,ly2,
                             BGR24to16(gpuData[0]));  
      }
    }
  }

 SetRenderMode(gpuData[0], FALSE);
 SetZMask4NT();

 if(bIgnoreNextTile) {bIgnoreNextTile=FALSE;return;}

 vertex[0].c.lcol=gpuData[0];
 vertex[0].c.col[3]=ubGloColAlpha;
 SETCOL(vertex[0]); 
 
 PRIMdrawQuad(&vertex[0], &vertex[1], &vertex[2], &vertex[3]);

 iDrawnSomething=1;
}

////////////////////////////////////////////////////////////////////////
// cmd: draw 1 dot Tile (point)
////////////////////////////////////////////////////////////////////////

void primTile1(unsigned char * baseAddr)
{
 uint32_t *gpuData = ((uint32_t *)baseAddr);
 short *sgpuData = ((short *)baseAddr);

 sprtX = sgpuData[2];
 sprtY = sgpuData[3];
 sprtW = 1;
 sprtH = 1;

 lx0 = sprtX;
 ly0 = sprtY;

 offsetST();

 bDrawTextured = FALSE;
 bDrawSmoothShaded = FALSE;

 SetRenderState(gpuData[0]);

 if(iOffscreenDrawing)
  {
   offsetPSX4();

   if(bDrawOffscreen4())
    {
     InvalidateTextureAreaEx();   
     FillSoftwareAreaTrans(lx0,ly0,lx2,ly2,
                           BGR24to16(gpuData[0]));          
    }
  }

 SetRenderMode(gpuData[0], FALSE);
 SetZMask4NT();

 vertex[0].c.lcol=gpuData[0];vertex[0].c.col[3]=ubGloColAlpha;
 SETCOL(vertex[0]); 

 PRIMdrawQuad(&vertex[0], &vertex[1], &vertex[2], &vertex[3]);

 iDrawnSomething=1;
}

////////////////////////////////////////////////////////////////////////
// cmd: draw 8 dot Tile (small rect)
////////////////////////////////////////////////////////////////////////

void primTile8(unsigned char * baseAddr)
{
 uint32_t *gpuData = ((uint32_t *)baseAddr);
 short *sgpuData = ((short *) baseAddr);

 sprtX = sgpuData[2];
 sprtY = sgpuData[3];
 sprtW = 8;
 sprtH = 8;

 lx0 = sprtX;
 ly0 = sprtY;

 offsetST();

 bDrawTextured = FALSE;
 bDrawSmoothShaded = FALSE;
 SetRenderState(gpuData[0]);

 if(iOffscreenDrawing)
  {
   offsetPSX4();

   if(bDrawOffscreen4())
    {
     InvalidateTextureAreaEx();   
     FillSoftwareAreaTrans(lx0,ly0,lx2,ly2,
                           BGR24to16(gpuData[0]));    
    }
  }

 SetRenderMode(gpuData[0], FALSE);
 SetZMask4NT();

 vertex[0].c.lcol=gpuData[0];
 vertex[0].c.col[3]=ubGloColAlpha;
 SETCOL(vertex[0]); 

 PRIMdrawQuad(&vertex[0], &vertex[1], &vertex[2], &vertex[3]);

 iDrawnSomething=1;
}

////////////////////////////////////////////////////////////////////////
// cmd: draw 16 dot Tile (medium rect)
////////////////////////////////////////////////////////////////////////

void primTile16(unsigned char * baseAddr)
{
 uint32_t *gpuData = ((uint32_t *)baseAddr);
 short *sgpuData = ((short *)baseAddr);

 sprtX = sgpuData[2];
 sprtY = sgpuData[3];
 sprtW = 16;
 sprtH = 16;
 // x and y of start
 lx0 = sprtX;
 ly0 = sprtY;

 offsetST();

 bDrawTextured = FALSE;
 bDrawSmoothShaded = FALSE;
 SetRenderState(gpuData[0]);

 if(iOffscreenDrawing)
  {
   offsetPSX4();

   if(bDrawOffscreen4())
    {
     InvalidateTextureAreaEx();   
     FillSoftwareAreaTrans(lx0,ly0,lx2,ly2,
                           BGR24to16(gpuData[0]));    
    }
  }

 SetRenderMode(gpuData[0], FALSE);
 SetZMask4NT();

 vertex[0].c.lcol=gpuData[0];
 vertex[0].c.col[3]=ubGloColAlpha;
 SETCOL(vertex[0]); 

 PRIMdrawQuad(&vertex[0], &vertex[1], &vertex[2], &vertex[3]);

 iDrawnSomething=1;
}

////////////////////////////////////////////////////////////////////////
// helper: filter effect by multipass rendering
////////////////////////////////////////////////////////////////////////

void DrawMultiBlur(void)
{
 int lABR,lDST;
 float fx,fy;

 lABR=GlobalTextABR;
 lDST=DrawSemiTrans;

 fx=(float)PSXDisplay.DisplayMode.x/(float)(iResX); 
 fy=(float)PSXDisplay.DisplayMode.y/(float)(iResY);

 vertex[0].x+=fx;vertex[1].x+=fx;
 vertex[2].x+=fx;vertex[3].x+=fx;

 GlobalTextABR=0;
 DrawSemiTrans=1;
 SetSemiTrans();

 PRIMdrawTexturedQuad(&vertex[0], &vertex[1], &vertex[2], &vertex[3]);

 vertex[0].y+=fy;vertex[1].y+=fy;
 vertex[2].y+=fy;vertex[3].y+=fy;
 PRIMdrawTexturedQuad(&vertex[0], &vertex[1], &vertex[2], &vertex[3]);

 if(bDrawMultiPass) {obm1=obm2=GL_SRC_ALPHA;}

 GlobalTextABR=lABR;
 DrawSemiTrans=lDST;
}

////////////////////////////////////////////////////////////////////////

#define   POFF 0.375f

void DrawMultiFilterSprite(void)
{
 int lABR,lDST;

 if(bUseMultiPass || DrawSemiTrans || ubOpaqueDraw) 
  {
   PRIMdrawTexturedQuad(&vertex[0], &vertex[1], &vertex[2], &vertex[3]);
   return;
  }

 lABR=GlobalTextABR;
 lDST=DrawSemiTrans;
 vertex[0].c.col[3]=ubGloAlpha/2;                      // -> set color with
 SETCOL(vertex[0]);                                    //    texture alpha
 PRIMdrawTexturedQuad(&vertex[0], &vertex[1], &vertex[2], &vertex[3]);
 vertex[0].x+=POFF;vertex[1].x+=POFF;
 vertex[2].x+=POFF;vertex[3].x+=POFF;
 vertex[0].y+=POFF;vertex[1].y+=POFF;
 vertex[2].y+=POFF;vertex[3].y+=POFF;
 GlobalTextABR=0;
 DrawSemiTrans=1;
 SetSemiTrans();
 PRIMdrawTexturedQuad(&vertex[0], &vertex[1], &vertex[2], &vertex[3]);
 GlobalTextABR=lABR;
 DrawSemiTrans=lDST;
}

////////////////////////////////////////////////////////////////////////
// cmd: small sprite (textured rect)
////////////////////////////////////////////////////////////////////////

void primSprt8(unsigned char * baseAddr)
{
 uint32_t *gpuData = ((uint32_t *) baseAddr);
 short *sgpuData = ((short *) baseAddr);
 short s;

 iSpriteTex=1;

 sprtX = sgpuData[2];
 sprtY = sgpuData[3];
 sprtW = 8;
 sprtH = 8;

 lx0 = sprtX;
 ly0 = sprtY;

 offsetST();

 // do texture stuff
 gl_ux[0]=gl_ux[3]=baseAddr[8];//gpuData[2]&0xff;

 if(usMirror & 0x1000) 
  {
   s=gl_ux[0];
   s-=sprtW-1;
   if(s<0) {s=0;}
   gl_ux[0]=gl_ux[3]=s;
  }

 sSprite_ux2=s=gl_ux[0]+sprtW; 
 if(s)     s--;
 if(s>255) s=255;
 gl_ux[1]=gl_ux[2]=s;
 // Y coords
 gl_vy[0]=gl_vy[1]=baseAddr[9];//(gpuData[2]>>8)&0xff;

 if(usMirror & 0x2000) 
  {
   s=gl_vy[0];
   s-=sprtH-1;
   if(s<0) {s=0;}
   gl_vy[0]=gl_vy[1]=s;
  }

 sSprite_vy2=s=gl_vy[0]+sprtH; 
 if(s)     s--;
 if(s>255) s=255;
 gl_vy[2]=gl_vy[3]=s;

 ulClutID=(gpuData[2]>>16);

 bDrawTextured = TRUE;
 bDrawSmoothShaded = FALSE;
 SetRenderState(gpuData[0]);

 if(iOffscreenDrawing)      
  {
   offsetPSX4();

   if(bDrawOffscreen4())
    {
     InvalidateTextureAreaEx();   
     SetRenderColor(gpuData[0]);
     lx0-=PSXDisplay.DrawOffset.x;
     ly0-=PSXDisplay.DrawOffset.y;

     if(bUsingTWin) DrawSoftwareSpriteTWin(baseAddr,8,8);
     else
     if(usMirror)   DrawSoftwareSpriteMirror(baseAddr,8,8);
     else
     DrawSoftwareSprite(baseAddr,8,8,baseAddr[8],baseAddr[9]);
    }
  }

 SetRenderMode(gpuData[0], TRUE);
 SetZMask4SP();

 sSprite_ux2=gl_ux[0]+sprtW;
 sSprite_vy2=gl_vy[0]+sprtH;

 assignTextureSprite();

 if(iFilterType>4) 
  DrawMultiFilterSprite();
 else
  PRIMdrawTexturedQuad(&vertex[0], &vertex[1], &vertex[2], &vertex[3]);

 if(bDrawMultiPass)
  {
   SetSemiTransMulti(1);
   PRIMdrawTexturedQuad(&vertex[0], &vertex[1], &vertex[2], &vertex[3]);
  }

 if(ubOpaqueDraw)
  {
   SetZMask4O();
   if(bUseMultiPass) SetOpaqueColor(gpuData[0]);
   DEFOPAQUEON

   if(bSmallAlpha && iFilterType<=2)
    {
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
     PRIMdrawTexturedQuad(&vertex[0], &vertex[1], &vertex[2], &vertex[3]);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
     SetZMask4O();
    }

   PRIMdrawTexturedQuad(&vertex[0], &vertex[1], &vertex[2], &vertex[3]);
   DEFOPAQUEOFF
  }

 iSpriteTex=0;
 iDrawnSomething=1;
}

////////////////////////////////////////////////////////////////////////
// cmd: medium sprite (textured rect)
////////////////////////////////////////////////////////////////////////

void primSprt16(unsigned char * baseAddr)
{
 uint32_t *gpuData = ((uint32_t *) baseAddr);
 short *sgpuData = ((short *) baseAddr);
 short s;

 iSpriteTex=1;

 sprtX = sgpuData[2];
 sprtY = sgpuData[3];
 sprtW = 16;
 sprtH = 16;

 lx0 = sprtX;
 ly0 = sprtY;

 offsetST();

 // do texture stuff
 gl_ux[0]=gl_ux[3]=baseAddr[8];//gpuData[2]&0xff;

 if(usMirror & 0x1000) 
  {
   s=gl_ux[0];
   s-=sprtW-1;
   if(s<0) {s=0;}
   gl_ux[0]=gl_ux[3]=s;
  }

 sSprite_ux2=s=gl_ux[0]+sprtW; 
 if(s)     s--;
 if(s>255) s=255;
 gl_ux[1]=gl_ux[2]=s; 
 // Y coords
 gl_vy[0]=gl_vy[1]=baseAddr[9];//(gpuData[2]>>8)&0xff;

 if(usMirror & 0x2000) 
  {
   s=gl_vy[0];
   s-=sprtH-1;
   if(s<0) {s=0;}
   gl_vy[0]=gl_vy[1]=s;
  }

 sSprite_vy2=s=gl_vy[0]+sprtH; 
 if(s)     s--;
 if(s>255) s=255;
 gl_vy[2]=gl_vy[3]=s;

 ulClutID=(gpuData[2]>>16);

 bDrawTextured = TRUE;
 bDrawSmoothShaded = FALSE;
 SetRenderState(gpuData[0]);

 if(iOffscreenDrawing)  
  {
   offsetPSX4();

   if(bDrawOffscreen4())
    {
     InvalidateTextureAreaEx();   
     SetRenderColor(gpuData[0]);
     lx0-=PSXDisplay.DrawOffset.x;
     ly0-=PSXDisplay.DrawOffset.y;
     if(bUsingTWin) DrawSoftwareSpriteTWin(baseAddr,16,16);
     else
     if(usMirror)   DrawSoftwareSpriteMirror(baseAddr,16,16);
     else
     DrawSoftwareSprite(baseAddr,16,16,baseAddr[8],baseAddr[9]);
    }
  }

 SetRenderMode(gpuData[0], TRUE);
 SetZMask4SP();

 sSprite_ux2=gl_ux[0]+sprtW;
 sSprite_vy2=gl_vy[0]+sprtH;

 assignTextureSprite();

 if(iFilterType>4) 
  DrawMultiFilterSprite();
 else
  PRIMdrawTexturedQuad(&vertex[0], &vertex[1], &vertex[2], &vertex[3]);

 if(bDrawMultiPass)
  {
   SetSemiTransMulti(1);
   PRIMdrawTexturedQuad(&vertex[0], &vertex[1], &vertex[2], &vertex[3]);
  }

 if(ubOpaqueDraw)
  {
   SetZMask4O();
   if(bUseMultiPass) SetOpaqueColor(gpuData[0]);
   DEFOPAQUEON

   if(bSmallAlpha && iFilterType<=2)
    {
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
     PRIMdrawTexturedQuad(&vertex[0], &vertex[1], &vertex[2], &vertex[3]);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
     SetZMask4O();
    }

   PRIMdrawTexturedQuad(&vertex[0], &vertex[1], &vertex[2], &vertex[3]);
   DEFOPAQUEOFF
  }

 iSpriteTex=0;
 iDrawnSomething=1;
}

////////////////////////////////////////////////////////////////////////
// cmd: free-size sprite (textured rect)
////////////////////////////////////////////////////////////////////////
 
void primSprtSRest(unsigned char * baseAddr,unsigned short type)
{
 uint32_t *gpuData = ((uint32_t *)baseAddr);
 short *sgpuData = ((short *) baseAddr);
 short s;
 unsigned short sTypeRest=0;

 sprtX = sgpuData[2];
 sprtY = sgpuData[3];
 sprtW = sgpuData[6] & 0x3ff;
 sprtH = sgpuData[7] & 0x1ff;

 // do texture stuff
 switch(type)
  {
   case 1:
    gl_vy[0]=gl_vy[1]=baseAddr[9];
    s=256-baseAddr[8];
    sprtW-=s;
    sprtX+=s;
    gl_ux[0]=gl_ux[3]=0;
    break;
   case 2:
    gl_ux[0]=gl_ux[3]=baseAddr[8];
    s=256-baseAddr[9];
    sprtH-=s;
    sprtY+=s;
    gl_vy[0]=gl_vy[1]=0;
    break;
   case 3:
    s=256-baseAddr[8];
    sprtW-=s;
    sprtX+=s;
    gl_ux[0]=gl_ux[3]=0;
    s=256-baseAddr[9];
    sprtH-=s;
    sprtY+=s;
    gl_vy[0]=gl_vy[1]=0;
    break;

   case 4:
    gl_vy[0]=gl_vy[1]=baseAddr[9];
    s=512-baseAddr[8];
    sprtW-=s;
    sprtX+=s;
    gl_ux[0]=gl_ux[3]=0;
    break;
   case 5:
    gl_ux[0]=gl_ux[3]=baseAddr[8];
    s=512-baseAddr[9];
    sprtH-=s;
    sprtY+=s;
    gl_vy[0]=gl_vy[1]=0;
    break;
   case 6:
    s=512-baseAddr[8];
    sprtW-=s;
    sprtX+=s;
    gl_ux[0]=gl_ux[3]=0;
    s=512-baseAddr[9];
    sprtH-=s;
    sprtY+=s;
    gl_vy[0]=gl_vy[1]=0;
    break;

  }

 if(usMirror & 0x1000) 
  {
   s=gl_ux[0];
   s-=sprtW-1;if(s<0) s=0;
   gl_ux[0]=gl_ux[3]=s;
  }
 if(usMirror & 0x2000) 
  {
   s=gl_vy[0];
   s-=sprtH-1;if(s<0) {s=0;}
   gl_vy[0]=gl_vy[1]=s;
  }

 sSprite_ux2=s=gl_ux[0]+sprtW; 
 if(s>255) s=255;
 gl_ux[1]=gl_ux[2]=s;
 sSprite_vy2=s=gl_vy[0]+sprtH; 
 if(s>255) s=255;
 gl_vy[2]=gl_vy[3]=s;

 if(!bUsingTWin)
  {
   if(sSprite_ux2>256) 
    {sprtW=256-gl_ux[0];sSprite_ux2=256;sTypeRest+=1;}
   if(sSprite_vy2>256) 
    {sprtH=256-gl_vy[0];sSprite_vy2=256;sTypeRest+=2;}
  }
 
 lx0 = sprtX;
 ly0 = sprtY;

 offsetST();

 ulClutID=(gpuData[2]>>16);

 bDrawTextured = TRUE;
 bDrawSmoothShaded = FALSE;
 SetRenderState(gpuData[0]);

 if(iOffscreenDrawing)
  {
   offsetPSX4();

   if(bDrawOffscreen4())
    {
     InvalidateTextureAreaEx();   
     SetRenderColor(gpuData[0]);
     lx0-=PSXDisplay.DrawOffset.x;
     ly0-=PSXDisplay.DrawOffset.y;
     if(bUsingTWin) DrawSoftwareSpriteTWin(baseAddr,sprtW,sprtH);
     else
     if(usMirror)   DrawSoftwareSpriteMirror(baseAddr,sprtW,sprtH);
     else
     DrawSoftwareSprite(baseAddr,sprtW,sprtH,baseAddr[8],baseAddr[9]);
    }
  }

 SetRenderMode(gpuData[0], TRUE);
 SetZMask4SP();

 sSprite_ux2=gl_ux[0]+sprtW;
 sSprite_vy2=gl_vy[0]+sprtH;

 assignTextureSprite();

 if(iFilterType>4) 
  DrawMultiFilterSprite();
 else
  PRIMdrawTexturedQuad(&vertex[0], &vertex[1], &vertex[2], &vertex[3]);
 
 if(bDrawMultiPass)
  {
   SetSemiTransMulti(1);
   PRIMdrawTexturedQuad(&vertex[0], &vertex[1], &vertex[2], &vertex[3]);
  }

 if(ubOpaqueDraw)
  {
   SetZMask4O();
   if(bUseMultiPass) SetOpaqueColor(gpuData[0]);
   DEFOPAQUEON

   if(bSmallAlpha && iFilterType<=2)
    {
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
     PRIMdrawTexturedQuad(&vertex[0], &vertex[1], &vertex[2], &vertex[3]);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
     SetZMask4O();
    }

   PRIMdrawTexturedQuad(&vertex[0], &vertex[1], &vertex[2], &vertex[3]);
   DEFOPAQUEOFF
  }

 if(sTypeRest && type<4) 
  {
   if(sTypeRest&1  && type==1) primSprtSRest(baseAddr,4);
   if(sTypeRest&2  && type==2) primSprtSRest(baseAddr,5);
   if(sTypeRest==3 && type==3) primSprtSRest(baseAddr,6);
  }
}

void primSprtS(unsigned char * baseAddr)
{
 uint32_t *gpuData = ((uint32_t *) baseAddr);
 short *sgpuData = ((short *) baseAddr);

 short s;
 unsigned short sTypeRest=0;

 sprtX = sgpuData[2];
 sprtY = sgpuData[3];
 sprtW = sgpuData[6] & 0x3ff;
 sprtH = sgpuData[7] & 0x1ff;

 if(!sprtH) return;
 if(!sprtW) return;

 iSpriteTex=1;

 // do texture stuff
 gl_ux[0]=gl_ux[3]=baseAddr[8];//gpuData[2]&0xff;
 gl_vy[0]=gl_vy[1]=baseAddr[9];//(gpuData[2]>>8)&0xff;

 if(usMirror & 0x1000) 
  {
   s=gl_ux[0];
   s-=sprtW-1;
   if(s<0) {s=0;}
   gl_ux[0]=gl_ux[3]=s;
  }
 if(usMirror & 0x2000) 
  {
   s=gl_vy[0];
   s-=sprtH-1;
   if(s<0) {s=0;}
   gl_vy[0]=gl_vy[1]=s;
  }

 sSprite_ux2=s=gl_ux[0]+sprtW; 
 if(s)     s--;
 if(s>255) s=255;
 gl_ux[1]=gl_ux[2]=s;
 sSprite_vy2=s=gl_vy[0]+sprtH; 
 if(s)     s--;
 if(s>255) s=255;
 gl_vy[2]=gl_vy[3]=s;

 if(!bUsingTWin)
  {
   if(sSprite_ux2>256) 
    {sprtW=256-gl_ux[0];sSprite_ux2=256;sTypeRest+=1;}
   if(sSprite_vy2>256) 
    {sprtH=256-gl_vy[0];sSprite_vy2=256;sTypeRest+=2;}
  }

 lx0 = sprtX;
 ly0 = sprtY;

 offsetST();

 ulClutID=(gpuData[2]>>16);

 bDrawTextured = TRUE;
 bDrawSmoothShaded = FALSE;
 SetRenderState(gpuData[0]);

 if(iOffscreenDrawing)
  {
   offsetPSX4();

   if(bDrawOffscreen4())
    {
     InvalidateTextureAreaEx();   
     SetRenderColor(gpuData[0]);
     lx0-=PSXDisplay.DrawOffset.x;
     ly0-=PSXDisplay.DrawOffset.y;
     if(bUsingTWin) DrawSoftwareSpriteTWin(baseAddr,sprtW,sprtH);
     else
     if(usMirror)   DrawSoftwareSpriteMirror(baseAddr,sprtW,sprtH);
     else
     DrawSoftwareSprite(baseAddr,sprtW,sprtH,baseAddr[8],baseAddr[9]);
    }
  }

 SetRenderMode(gpuData[0], TRUE);
 SetZMask4SP();

 if((dwActFixes&1) && gTexFrameName && gTexName==gTexFrameName) 
  {iSpriteTex=0;return;}

 sSprite_ux2=gl_ux[0]+sprtW;
 sSprite_vy2=gl_vy[0]+sprtH;

 assignTextureSprite();

 if(iFilterType>4) 
  DrawMultiFilterSprite();
 else
  PRIMdrawTexturedQuad(&vertex[0], &vertex[1], &vertex[2], &vertex[3]);
 
 if(bDrawMultiPass)
  {
   SetSemiTransMulti(1);
   PRIMdrawTexturedQuad(&vertex[0], &vertex[1], &vertex[2], &vertex[3]);
  }

 if(ubOpaqueDraw)
  {
   SetZMask4O();
   if(bUseMultiPass) SetOpaqueColor(gpuData[0]);
   DEFOPAQUEON

   if(bSmallAlpha && iFilterType<=2)
    {
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
     PRIMdrawTexturedQuad(&vertex[0], &vertex[1], &vertex[2], &vertex[3]);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
     SetZMask4O();
    }

   PRIMdrawTexturedQuad(&vertex[0], &vertex[1], &vertex[2], &vertex[3]);
   DEFOPAQUEOFF
  }

 if(sTypeRest) 
  {
   if(sTypeRest&1)  primSprtSRest(baseAddr,1);
   if(sTypeRest&2)  primSprtSRest(baseAddr,2);
   if(sTypeRest==3) primSprtSRest(baseAddr,3);
  }

 iSpriteTex=0;
 iDrawnSomething=1;
}

////////////////////////////////////////////////////////////////////////
// cmd: flat shaded Poly4
////////////////////////////////////////////////////////////////////////

void primPolyF4(unsigned char *baseAddr)
{
 uint32_t *gpuData = ((uint32_t *) baseAddr);
 short *sgpuData = ((short *) baseAddr);

 lx0 = sgpuData[2];
 ly0 = sgpuData[3];
 lx1 = sgpuData[4];
 ly1 = sgpuData[5];
 lx2 = sgpuData[6];
 ly2 = sgpuData[7];
 lx3 = sgpuData[8];
 ly3 = sgpuData[9];

 if(offset4()) return;

 bDrawTextured = FALSE;
 bDrawSmoothShaded = FALSE;
 SetRenderState(gpuData[0]);

 if(iOffscreenDrawing)
  {
   offsetPSX4();
   if(bDrawOffscreen4())
    {
     InvalidateTextureAreaEx();   
     drawPoly4F(gpuData[0]);
    }
  }

 SetRenderMode(gpuData[0], FALSE);
 SetZMask4NT();

 vertex[0].c.lcol=gpuData[0];vertex[0].c.col[3]=ubGloColAlpha;
 SETCOL(vertex[0]); 

 PRIMdrawTri2(&vertex[0], &vertex[1], &vertex[2],&vertex[3]);

 iDrawnSomething=1;
}

////////////////////////////////////////////////////////////////////////
// cmd: smooth shaded Poly4
////////////////////////////////////////////////////////////////////////

void primPolyG4(unsigned char * baseAddr);

BOOL bDrawOffscreenFrontFF9G4(void)
{
 if(lx0< PSXDisplay.DisplayPosition.x) return FALSE;   // must be complete in front
 if(lx0> PSXDisplay.DisplayEnd.x)      return FALSE;
 if(ly0< PSXDisplay.DisplayPosition.y) return FALSE;
 if(ly0> PSXDisplay.DisplayEnd.y)      return FALSE;
 if(lx1< PSXDisplay.DisplayPosition.x) return FALSE;
 if(lx1> PSXDisplay.DisplayEnd.x)      return FALSE;
 if(ly1< PSXDisplay.DisplayPosition.y) return FALSE;
 if(ly1> PSXDisplay.DisplayEnd.y)      return FALSE;
 if(lx2< PSXDisplay.DisplayPosition.x) return FALSE;
 if(lx2> PSXDisplay.DisplayEnd.x)      return FALSE;
 if(ly2< PSXDisplay.DisplayPosition.y) return FALSE;
 if(ly2> PSXDisplay.DisplayEnd.y)      return FALSE;
 if(lx3< PSXDisplay.DisplayPosition.x) return FALSE;
 if(lx3> PSXDisplay.DisplayEnd.x)      return FALSE;
 if(ly3< PSXDisplay.DisplayPosition.y) return FALSE;
 if(ly3> PSXDisplay.DisplayEnd.y)      return FALSE;
 return TRUE;
}

BOOL bCheckFF9G4(unsigned char * baseAddr)
{
 static unsigned char pFF9G4Cache[32];
 static int iFF9Fix=0;

 if(baseAddr)
  {
   if(iFF9Fix==0)
    {
     if(bDrawOffscreenFrontFF9G4())
      {
       short *sgpuData = ((short *) pFF9G4Cache);
       iFF9Fix=2;
       memcpy(pFF9G4Cache,baseAddr,32);

       if(sgpuData[2]==142)
        {
         sgpuData[2] +=65;
         sgpuData[10]+=65;
        }
       return TRUE;
      }
     else iFF9Fix=1;
    }
   return FALSE;
  }

 if(iFF9Fix==2)
  {
   int labr=GlobalTextABR;
   GlobalTextABR = 1;
   primPolyG4(pFF9G4Cache);
   GlobalTextABR = labr;
  }
 iFF9Fix = 0;

 return FALSE;
}

////////////////////////////////////////////////////////////////////////

void primPolyG4(unsigned char * baseAddr)
{
 uint32_t *gpuData = (uint32_t *)baseAddr;
 short *sgpuData = ((short *)baseAddr);

 lx0 = sgpuData[2];
 ly0 = sgpuData[3];
 lx1 = sgpuData[6];
 ly1 = sgpuData[7];
 lx2 = sgpuData[10];
 ly2 = sgpuData[11];
 lx3 = sgpuData[14];
 ly3 = sgpuData[15];

 if(offset4()) return;

 bDrawTextured = FALSE;
 bDrawSmoothShaded = TRUE;
 SetRenderState(gpuData[0]);

 if(iOffscreenDrawing)
  {
   offsetPSX4();

   if((dwActFixes&512) && bCheckFF9G4(baseAddr)) return;

   if(bDrawOffscreen4())
    {
     InvalidateTextureAreaEx();   
     drawPoly4G(gpuData[0], gpuData[2], gpuData[4], gpuData[6]);
    }     
  }

 SetRenderMode(gpuData[0], FALSE);
 SetZMask4NT();

 vertex[0].c.lcol=gpuData[0];
 vertex[1].c.lcol=gpuData[2];
 vertex[2].c.lcol=gpuData[4];
 vertex[3].c.lcol=gpuData[6];

 vertex[0].c.col[3]=vertex[1].c.col[3]=vertex[2].c.col[3]=vertex[3].c.col[3]=ubGloAlpha;


 PRIMdrawGouraudTri2Color(&vertex[0],&vertex[1], &vertex[2], &vertex[3]);

 iDrawnSomething=1;
}

////////////////////////////////////////////////////////////////////////
// cmd: flat shaded Texture3
////////////////////////////////////////////////////////////////////////

BOOL DoLineCheck(uint32_t *gpuData)
{
 BOOL bQuad=FALSE;short dx,dy;

 if(lx0==lx1)
  {
   dx=lx0-lx2;if(dx<0) dx=-dx;

   if(ly1==ly2) 
    {
     dy=ly1-ly0;if(dy<0) dy=-dy;
     if(dx<=1)
      {
       vertex[3]=vertex[2];
       vertex[2]=vertex[0];
       vertex[2].x=vertex[3].x;
      }
     else
     if(dy<=1)
      {
       vertex[3]=vertex[2];
       vertex[2].y=vertex[0].y;
      }
     else return FALSE;

     bQuad=TRUE;
    }
   else
   if(ly0==ly2) 
    {
     dy=ly0-ly1;if(dy<0) dy=-dy;
     if(dx<=1)
      {
       vertex[3]=vertex[1];
       vertex[3].x=vertex[2].x;
      }
     else
     if(dy<=1)
      {
       vertex[3]=vertex[2];
       vertex[3].y=vertex[1].y;
      }
     else return FALSE;

     bQuad=TRUE;
    }
  }

 if(lx0==lx2)
  {
   dx=lx0-lx1;if(dx<0) dx=-dx;

   if(ly2==ly1) 
    {
     dy=ly2-ly0;if(dy<0) dy=-dy;
     if(dx<=1)
      {
       vertex[3]=vertex[1];
       vertex[1]=vertex[0];
       vertex[1].x=vertex[3].x;
      }
     else
     if(dy<=1)
      {
       vertex[3]=vertex[1];
       vertex[1].y=vertex[0].y;
      }
     else return FALSE;

     bQuad=TRUE;
    }
   else
   if(ly0==ly1)
    {
     dy=ly2-ly0;if(dy<0) dy=-dy;
     if(dx<=1)
      {
       vertex[3]=vertex[2];
       vertex[3].x=vertex[1].x;
      }
     else
     if(dy<=1)
      {
       vertex[3]=vertex[1];
       vertex[3].y=vertex[2].y;
      }
     else return FALSE;

     bQuad=TRUE;
    }
  }

 if(lx1==lx2)
  {
   dx=lx1-lx0;if(dx<0) dx=-dx;

   if(ly1==ly0)
    {
     dy=ly1-ly2;if(dy<0) dy=-dy;

     if(dx<=1)
      {
       vertex[3]=vertex[2];
       vertex[2].x=vertex[0].x;
      }
     else
     if(dy<=1)
      {
       vertex[3]=vertex[2];
       vertex[2]=vertex[0];
       vertex[2].y=vertex[3].y;
      }
     else return FALSE;

     bQuad=TRUE;
    }
   else
   if(ly2==ly0)
    {
     dy=ly2-ly1;if(dy<0) dy=-dy;

     if(dx<=1)
      {
       vertex[3]=vertex[1];
       vertex[1].x=vertex[0].x;
      }
     else
     if(dy<=1)
      {
       vertex[3]=vertex[1];
       vertex[1]=vertex[0];
       vertex[1].y=vertex[3].y;
      }
     else return FALSE;

     bQuad=TRUE;
    }
  }

 if(!bQuad) return FALSE;

 PRIMdrawTexturedQuad(&vertex[0], &vertex[1], &vertex[3], &vertex[2]);

 if(bDrawMultiPass)
  {
   SetSemiTransMulti(1);
   PRIMdrawTexturedQuad(&vertex[0], &vertex[1], &vertex[3], &vertex[2]);
  }

 if(ubOpaqueDraw)
  {
   SetZMask4O();
   if(bUseMultiPass) SetOpaqueColor(gpuData[0]);
   DEFOPAQUEON
   PRIMdrawTexturedQuad(&vertex[0], &vertex[1], &vertex[3], &vertex[2]);
   DEFOPAQUEOFF
  }

 iDrawnSomething=1;

 return TRUE;
}

////////////////////////////////////////////////////////////////////////

void primPolyFT3(unsigned char * baseAddr)
{
 uint32_t *gpuData = ((uint32_t *) baseAddr);
 short *sgpuData = ((short *) baseAddr);

 lx0 = sgpuData[2];
 ly0 = sgpuData[3];
 lx1 = sgpuData[6];
 ly1 = sgpuData[7];
 lx2 = sgpuData[10];
 ly2 = sgpuData[11];

 if(offset3()) return;
    
 // do texture UV coordinates stuff
 gl_ux[0]=gl_ux[3]=baseAddr[8];//gpuData[2]&0xff;
 gl_vy[0]=gl_vy[3]=baseAddr[9];//(gpuData[2]>>8)&0xff;
 gl_ux[1]=baseAddr[16];//gpuData[4]&0xff;
 gl_vy[1]=baseAddr[17];//(gpuData[4]>>8)&0xff;
 gl_ux[2]=baseAddr[24];//gpuData[6]&0xff;
 gl_vy[2]=baseAddr[25];//(gpuData[6]>>8)&0xff;

 UpdateGlobalTP((unsigned short)(gpuData[4]>>16));
 ulClutID=gpuData[2]>>16;

 bDrawTextured = TRUE;
 bDrawSmoothShaded = FALSE;
 SetRenderState(gpuData[0]);

 if(iOffscreenDrawing)
  {
   offsetPSX3();
   if(bDrawOffscreen3())
    {
     InvalidateTextureAreaEx();   
     SetRenderColor(gpuData[0]);
     drawPoly3FT(baseAddr);
    }
  }

 SetRenderMode(gpuData[0], TRUE);
 SetZMask3();

 assignTexture3();

 if(!(dwActFixes&0x10))
  {
   if(DoLineCheck(gpuData)) return;
  }

 PRIMdrawTexturedTri(&vertex[0], &vertex[1], &vertex[2]);

 if(bDrawMultiPass)
  {
   SetSemiTransMulti(1);
   PRIMdrawTexturedTri(&vertex[0], &vertex[1], &vertex[2]);
  }

 if(ubOpaqueDraw)
  {
   SetZMask3O();
   if(bUseMultiPass) SetOpaqueColor(gpuData[0]);
   DEFOPAQUEON
   PRIMdrawTexturedTri(&vertex[0], &vertex[1], &vertex[2]);
   DEFOPAQUEOFF
  }

 iDrawnSomething=1;
}

////////////////////////////////////////////////////////////////////////
// cmd: flat shaded Texture4
////////////////////////////////////////////////////////////////////////

#define ST_FAC             255.99f

void RectTexAlign(void)
{
 int UFlipped = FALSE;
 int VFlipped = FALSE;

 if(gTexName==gTexFrameName) return;

 if(ly0==ly1)
  {
   if(!((lx1==lx3 && ly3==ly2 && lx2==lx0) ||
        (lx1==lx2 && ly2==ly3 && lx3==lx0)))
    return;

   if(ly0<ly2) 
    {
     if (vertex[0].tow > vertex[2].tow)
      VFlipped = 1;
    }
   else
    {
     if (vertex[0].tow < vertex[2].tow)
      VFlipped = 2;
    }
  }
 else
 if(ly0==ly2)
  {
   if(!((lx2==lx3 && ly3==ly1 && lx1==lx0) ||
        (lx2==lx1 && ly1==ly3 && lx3==lx0)))
    return;

   if(ly0<ly1) 
    {
     if (vertex[0].tow > vertex[1].tow)
      VFlipped = 3;
    }
   else
    {
     if (vertex[0].tow < vertex[1].tow)
      VFlipped = 4;
    }
  }
 else
 if(ly0==ly3)
  {
   if(!((lx3==lx2 && ly2==ly1 && lx1==lx0) ||
        (lx3==lx1 && ly1==ly2 && lx2==lx0)))
    return;

   if(ly0<ly1) 
    {
     if (vertex[0].tow > vertex[1].tow)
      VFlipped = 5;
    }
   else
    {
     if (vertex[0].tow < vertex[1].tow)
      VFlipped = 6;
    }
  }
 else return;

 if(lx0==lx1)
  {
   if(lx0<lx2) 
    {
     if (vertex[0].sow > vertex[2].sow)
      UFlipped = 1;
    }
   else
    {
     if (vertex[0].sow < vertex[2].sow)
      UFlipped = 2;
    }
  }
 else
 if(lx0==lx2)
  {
   if(lx0<lx1) 
    {
     if (vertex[0].sow > vertex[1].sow)
      UFlipped = 3;
    }
   else
    {
     if (vertex[0].sow < vertex[1].sow)
      UFlipped = 4;
    }
  }
 else
 if(lx0==lx3)
  {
   if(lx0<lx1) 
    {
     if (vertex[0].sow > vertex[1].sow)
      UFlipped = 5;
    }
   else
    {
     if (vertex[0].sow < vertex[1].sow)
      UFlipped = 6;
    }
  }

 if (UFlipped)
  {
#ifdef OWNSCALE
   if(bUsingTWin)
    {
     switch(UFlipped)
      {
       case 1:
        vertex[2].sow+=0.95f/TWin.UScaleFactor; 
        vertex[3].sow+=0.95f/TWin.UScaleFactor;
        break;
       case 2:
        vertex[0].sow+=0.95f/TWin.UScaleFactor; 
        vertex[1].sow+=0.95f/TWin.UScaleFactor;
        break;
       case 3:
        vertex[1].sow+=0.95f/TWin.UScaleFactor; 
        vertex[3].sow+=0.95f/TWin.UScaleFactor;
        break;
       case 4:
        vertex[0].sow+=0.95f/TWin.UScaleFactor; 
        vertex[2].sow+=0.95f/TWin.UScaleFactor;
        break;
       case 5:
        vertex[1].sow+=0.95f/TWin.UScaleFactor; 
        vertex[2].sow+=0.95f/TWin.UScaleFactor;
        break;
       case 6:
        vertex[0].sow+=0.95f/TWin.UScaleFactor; 
        vertex[3].sow+=0.95f/TWin.UScaleFactor;
        break;
      }
    }
   else
    {
     switch(UFlipped)
      {
       case 1:
        vertex[2].sow+=1.0f/ST_FAC; 
        vertex[3].sow+=1.0f/ST_FAC;
        break;
       case 2:
        vertex[0].sow+=1.0f/ST_FAC; 
        vertex[1].sow+=1.0f/ST_FAC;
        break;
       case 3:
        vertex[1].sow+=1.0f/ST_FAC; 
        vertex[3].sow+=1.0f/ST_FAC;
        break;
       case 4:
        vertex[0].sow+=1.0f/ST_FAC; 
        vertex[2].sow+=1.0f/ST_FAC;
        break;
       case 5:
        vertex[1].sow+=1.0f/ST_FAC; 
        vertex[2].sow+=1.0f/ST_FAC;
        break;
       case 6:
        vertex[0].sow+=1.0f/ST_FAC; 
        vertex[3].sow+=1.0f/ST_FAC;
        break;
      }
    }
#else
   if(bUsingTWin)
    {
     switch(UFlipped)
      {
       case 1:
        vertex[2].sow+=1.0f/TWin.UScaleFactor; 
        vertex[3].sow+=1.0f/TWin.UScaleFactor;
        break;
       case 2:
        vertex[0].sow+=1.0f/TWin.UScaleFactor; 
        vertex[1].sow+=1.0f/TWin.UScaleFactor;
        break;
       case 3:
        vertex[1].sow+=1.0f/TWin.UScaleFactor; 
        vertex[3].sow+=1.0f/TWin.UScaleFactor;
        break;
       case 4:
        vertex[0].sow+=1.0f/TWin.UScaleFactor; 
        vertex[2].sow+=1.0f/TWin.UScaleFactor;
        break;
       case 5:
        vertex[1].sow+=1.0f/TWin.UScaleFactor; 
        vertex[2].sow+=1.0f/TWin.UScaleFactor;
        break;
       case 6:
        vertex[0].sow+=1.0f/TWin.UScaleFactor; 
        vertex[3].sow+=1.0f/TWin.UScaleFactor;
        break;
      }
    }
   else
    {
     switch(UFlipped)
      {
       case 1:
        vertex[2].sow+=1.0f; 
        vertex[3].sow+=1.0f;
        break;
       case 2:
        vertex[0].sow+=1.0f; 
        vertex[1].sow+=1.0f;
        break;
       case 3:
        vertex[1].sow+=1.0f; 
        vertex[3].sow+=1.0f;
        break;
       case 4:
        vertex[0].sow+=1.0f; 
        vertex[2].sow+=1.0f;
        break;
       case 5:
        vertex[1].sow+=1.0f; 
        vertex[2].sow+=1.0f;
        break;
       case 6:
        vertex[0].sow+=1.0f; 
        vertex[3].sow+=1.0f;
        break;
      }
    }
#endif
  }

 if (VFlipped)
  {
#ifdef OWNSCALE
   if(bUsingTWin)
    {
     switch(VFlipped)
      {
       case 1:
        vertex[2].tow+=0.95f/TWin.VScaleFactor; 
        vertex[3].tow+=0.95f/TWin.VScaleFactor;
        break;
       case 2:
        vertex[0].tow+=0.95f/TWin.VScaleFactor; 
        vertex[1].tow+=0.95f/TWin.VScaleFactor;
        break;
       case 3:
        vertex[1].tow+=0.95f/TWin.VScaleFactor; 
        vertex[3].tow+=0.95f/TWin.VScaleFactor;
        break;
       case 4:
        vertex[0].tow+=0.95f/TWin.VScaleFactor; 
        vertex[2].tow+=0.95f/TWin.VScaleFactor;
        break;
       case 5:
        vertex[1].tow+=0.95f/TWin.VScaleFactor; 
        vertex[2].tow+=0.95f/TWin.VScaleFactor;
        break;
       case 6:
        vertex[0].tow+=0.95f/TWin.VScaleFactor; 
        vertex[3].tow+=0.95f/TWin.VScaleFactor;
        break;
      }
    }
   else
    {
     switch(VFlipped)
      {
       case 1:
        vertex[2].tow+=1.0f/ST_FAC; 
        vertex[3].tow+=1.0f/ST_FAC;
        break;
       case 2:
        vertex[0].tow+=1.0f/ST_FAC; 
        vertex[1].tow+=1.0f/ST_FAC;
        break;
       case 3:
        vertex[1].tow+=1.0f/ST_FAC;
        vertex[3].tow+=1.0f/ST_FAC;
        break;
       case 4:
        vertex[0].tow+=1.0f/ST_FAC; 
        vertex[2].tow+=1.0f/ST_FAC;
        break;
       case 5:
        vertex[1].tow+=1.0f/ST_FAC;
        vertex[2].tow+=1.0f/ST_FAC;
        break;
       case 6:
        vertex[0].tow+=1.0f/ST_FAC;
        vertex[3].tow+=1.0f/ST_FAC;
        break;
      }
    }
#else
   if(bUsingTWin)
    {
     switch(VFlipped)
      {
       case 1:
        vertex[2].tow+=1.0f/TWin.VScaleFactor; 
        vertex[3].tow+=1.0f/TWin.VScaleFactor;
        break;
       case 2:
        vertex[0].tow+=1.0f/TWin.VScaleFactor; 
        vertex[1].tow+=1.0f/TWin.VScaleFactor;
        break;
       case 3:
        vertex[1].tow+=1.0f/TWin.VScaleFactor; 
        vertex[3].tow+=1.0f/TWin.VScaleFactor;
        break;
       case 4:
        vertex[0].tow+=1.0f/TWin.VScaleFactor; 
        vertex[2].tow+=1.0f/TWin.VScaleFactor;
        break;
       case 5:
        vertex[1].tow+=1.0f/TWin.VScaleFactor; 
        vertex[2].tow+=1.0f/TWin.VScaleFactor;
        break;
       case 6:
        vertex[0].tow+=1.0f/TWin.VScaleFactor; 
        vertex[3].tow+=1.0f/TWin.VScaleFactor;
        break;
      }
    }
   else
    {
     switch(VFlipped)
      {
       case 1:
        vertex[2].tow+=1.0f; 
        vertex[3].tow+=1.0f;
        break;
       case 2:
        vertex[0].tow+=1.0f; 
        vertex[1].tow+=1.0f;
        break;
       case 3:
        vertex[1].tow+=1.0f; 
        vertex[3].tow+=1.0f;
        break;
       case 4:
        vertex[0].tow+=1.0f; 
        vertex[2].tow+=1.0f;
        break;
       case 5:
        vertex[1].tow+=1.0f; 
        vertex[2].tow+=1.0f;
        break;
       case 6:
        vertex[0].tow+=1.0f; 
        vertex[3].tow+=1.0f;
        break;
      }
    }
#endif
  }

}

void primPolyFT4(unsigned char * baseAddr)
{
 uint32_t *gpuData = ((uint32_t *)baseAddr);
 short *sgpuData = ((short *) baseAddr);

 lx0 = sgpuData[2];
 ly0 = sgpuData[3];
 lx1 = sgpuData[6];
 ly1 = sgpuData[7];
 lx2 = sgpuData[10];
 ly2 = sgpuData[11];
 lx3 = sgpuData[14];
 ly3 = sgpuData[15];

 if(offset4()) return;

 gl_vy[0]=baseAddr[9];//((gpuData[2]>>8)&0xff);
 gl_vy[1]=baseAddr[17];//((gpuData[4]>>8)&0xff);
 gl_vy[2]=baseAddr[25];//((gpuData[6]>>8)&0xff);
 gl_vy[3]=baseAddr[33];//((gpuData[8]>>8)&0xff);
 
 gl_ux[0]=baseAddr[8];//(gpuData[2]&0xff);
 gl_ux[1]=baseAddr[16];//(gpuData[4]&0xff);
 gl_ux[2]=baseAddr[24];//(gpuData[6]&0xff);
 gl_ux[3]=baseAddr[32];//(gpuData[8]&0xff);

 UpdateGlobalTP((unsigned short)(gpuData[4]>>16));
 ulClutID=(gpuData[2]>>16);

 bDrawTextured = TRUE;
 bDrawSmoothShaded = FALSE;
 SetRenderState(gpuData[0]);

 if(iOffscreenDrawing)
  {
   offsetPSX4();
   if(bDrawOffscreen4())
    {
     InvalidateTextureAreaEx();   
     SetRenderColor(gpuData[0]);
     drawPoly4FT(baseAddr);
    }
  }

 SetRenderMode(gpuData[0], TRUE);

 SetZMask4();

 assignTexture4();

 RectTexAlign();

 PRIMdrawTexturedQuad(&vertex[0], &vertex[1], &vertex[3], &vertex[2]);

 if(bDrawMultiPass)
  {
   SetSemiTransMulti(1);
   PRIMdrawTexturedQuad(&vertex[0], &vertex[1], &vertex[3], &vertex[2]);
  }

 if(ubOpaqueDraw)
  {
   SetZMask4O();
   if(bUseMultiPass) SetOpaqueColor(gpuData[0]);
   DEFOPAQUEON

   if(bSmallAlpha && iFilterType<=2)
    {
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
     PRIMdrawTexturedQuad(&vertex[0], &vertex[1], &vertex[3], &vertex[2]);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
     SetZMask4O();
    }

   PRIMdrawTexturedQuad(&vertex[0], &vertex[1], &vertex[3], &vertex[2]);
   DEFOPAQUEOFF
  }

 iDrawnSomething=1;
}

////////////////////////////////////////////////////////////////////////
// cmd: smooth shaded Texture3
////////////////////////////////////////////////////////////////////////

void primPolyGT3(unsigned char *baseAddr)
{    
 uint32_t *gpuData = ((uint32_t *) baseAddr);
 short *sgpuData = ((short *) baseAddr);

 lx0 = sgpuData[2];
 ly0 = sgpuData[3];
 lx1 = sgpuData[8];
 ly1 = sgpuData[9];
 lx2 = sgpuData[14];
 ly2 = sgpuData[15];

 if(offset3()) return;

 // do texture stuff
 gl_ux[0]=gl_ux[3]=baseAddr[8];//gpuData[2]&0xff;
 gl_vy[0]=gl_vy[3]=baseAddr[9];//(gpuData[2]>>8)&0xff;
 gl_ux[1]=baseAddr[20];//gpuData[5]&0xff;
 gl_vy[1]=baseAddr[21];//(gpuData[5]>>8)&0xff;
 gl_ux[2]=baseAddr[32];//gpuData[8]&0xff;
 gl_vy[2]=baseAddr[33];//(gpuData[8]>>8)&0xff;

 UpdateGlobalTP((unsigned short)(gpuData[5]>>16));
 ulClutID=(gpuData[2]>>16);
           
 bDrawTextured = TRUE;
 bDrawSmoothShaded = TRUE;
 SetRenderState(gpuData[0]);

 if(iOffscreenDrawing)
  {
   offsetPSX3();
   if(bDrawOffscreen3())
    {
     InvalidateTextureAreaEx();   
     drawPoly3GT(baseAddr);
    }
  }

 SetRenderMode(gpuData[0], FALSE);
 SetZMask3();

 assignTexture3();

 if(bDrawNonShaded)
  {
   //if(!bUseMultiPass) vertex[0].lcol=DoubleBGR2RGB(gpuData[0]); else vertex[0].lcol=gpuData[0];
   // eat this...
   if(bGLBlend) vertex[0].c.lcol=0x7f7f7f;
   else         vertex[0].c.lcol=0xffffff;
   vertex[0].c.col[3]=ubGloAlpha;
   SETCOL(vertex[0]); 

   PRIMdrawTexturedTri(&vertex[0], &vertex[1], &vertex[2]);

   if(ubOpaqueDraw)
    {
     SetZMask3O();
     DEFOPAQUEON
     PRIMdrawTexturedTri(&vertex[0], &vertex[1], &vertex[2]);
     DEFOPAQUEOFF
    }
   return; 
  }

 if(!bUseMultiPass  && !bGLBlend)
  {
   vertex[0].c.lcol=DoubleBGR2RGB(gpuData[0]); 
   vertex[1].c.lcol=DoubleBGR2RGB(gpuData[3]); 
   vertex[2].c.lcol=DoubleBGR2RGB(gpuData[6]);
  }
 else
  {
   vertex[0].c.lcol=gpuData[0];
   vertex[1].c.lcol=gpuData[3];
   vertex[2].c.lcol=gpuData[6];
  }
 vertex[0].c.col[3]=vertex[1].c.col[3]=vertex[2].c.col[3]=ubGloAlpha;

 PRIMdrawTexGouraudTriColor(&vertex[0], &vertex[1], &vertex[2]);

 if(bDrawMultiPass)
  {
   SetSemiTransMulti(1);
   PRIMdrawTexGouraudTriColor(&vertex[0], &vertex[1], &vertex[2]);
  }

 if(ubOpaqueDraw)
  {
   SetZMask3O();
   if(bUseMultiPass) 
    {
     vertex[0].c.lcol=DoubleBGR2RGB(gpuData[0]);
     vertex[1].c.lcol=DoubleBGR2RGB(gpuData[3]);
     vertex[2].c.lcol=DoubleBGR2RGB(gpuData[6]);
     vertex[0].c.col[3]=vertex[1].c.col[3]=vertex[2].c.col[3]=ubGloAlpha;
    }
   DEFOPAQUEON
   PRIMdrawTexGouraudTriColor(&vertex[0], &vertex[1], &vertex[2]);
   DEFOPAQUEOFF
  }

 iDrawnSomething=1;
}

////////////////////////////////////////////////////////////////////////
// cmd: smooth shaded Poly3
////////////////////////////////////////////////////////////////////////

void primPolyG3(unsigned char *baseAddr)
{    
 uint32_t *gpuData = ((uint32_t *)baseAddr);
 short *sgpuData = ((short *) baseAddr);

 lx0 = sgpuData[2];
 ly0 = sgpuData[3];
 lx1 = sgpuData[6];
 ly1 = sgpuData[7];
 lx2 = sgpuData[10];
 ly2 = sgpuData[11];

 if(offset3()) return;

 bDrawTextured = FALSE;
 bDrawSmoothShaded = TRUE;
 SetRenderState(gpuData[0]);

 if(iOffscreenDrawing) 
  {
   offsetPSX3();
   if(bDrawOffscreen3())
    {
     InvalidateTextureAreaEx();   
     drawPoly3G(gpuData[0], gpuData[2], gpuData[4]);
    }
  }

 SetRenderMode(gpuData[0], FALSE);
 SetZMask3NT();

 vertex[0].c.lcol=gpuData[0];
 vertex[1].c.lcol=gpuData[2];
 vertex[2].c.lcol=gpuData[4];
 vertex[0].c.col[3]=vertex[1].c.col[3]=vertex[2].c.col[3]=ubGloColAlpha; 

 PRIMdrawGouraudTriColor(&vertex[0], &vertex[1], &vertex[2]);

 iDrawnSomething=1;
}

////////////////////////////////////////////////////////////////////////
// cmd: smooth shaded Texture4
////////////////////////////////////////////////////////////////////////

void primPolyGT4(unsigned char *baseAddr)
{ 
 uint32_t *gpuData = ((uint32_t *)baseAddr);
 short *sgpuData = ((short *)baseAddr);

 lx0 = sgpuData[2];
 ly0 = sgpuData[3];
 lx1 = sgpuData[8];
 ly1 = sgpuData[9];
 lx2 = sgpuData[14];
 ly2 = sgpuData[15];
 lx3 = sgpuData[20];
 ly3 = sgpuData[21];

 if(offset4()) return;

 // do texture stuff
 gl_ux[0]=baseAddr[8];//gpuData[2]&0xff;
 gl_vy[0]=baseAddr[9];//(gpuData[2]>>8)&0xff;
 gl_ux[1]=baseAddr[20];//gpuData[5]&0xff;
 gl_vy[1]=baseAddr[21];//(gpuData[5]>>8)&0xff;
 gl_ux[2]=baseAddr[32];//gpuData[8]&0xff;
 gl_vy[2]=baseAddr[33];//(gpuData[8]>>8)&0xff;
 gl_ux[3]=baseAddr[44];//gpuData[11]&0xff;
 gl_vy[3]=baseAddr[45];//(gpuData[11]>>8)&0xff;

 UpdateGlobalTP((unsigned short)(gpuData[5]>>16));
 ulClutID=(gpuData[2]>>16);

 bDrawTextured     = TRUE;
 bDrawSmoothShaded = TRUE;
 SetRenderState(gpuData[0]);

 if(iOffscreenDrawing)
  {
   offsetPSX4();
   if(bDrawOffscreen4())
    {
     InvalidateTextureAreaEx();   
     drawPoly4GT(baseAddr);
    }     
  }

 SetRenderMode(gpuData[0], FALSE);
 SetZMask4();

 assignTexture4();

 RectTexAlign();

 if(bDrawNonShaded)
  {
   //if(!bUseMultiPass) vertex[0].lcol=DoubleBGR2RGB(gpuData[0]); else vertex[0].lcol=gpuData[0];
   if(bGLBlend) vertex[0].c.lcol=0x7f7f7f;
   else          vertex[0].c.lcol=0xffffff;
   vertex[0].c.col[3]=ubGloAlpha;
   SETCOL(vertex[0]); 

   PRIMdrawTexturedQuad(&vertex[0], &vertex[1], &vertex[3], &vertex[2]);
  
   if(ubOpaqueDraw)
    {
     SetZMask4O();
     ubGloAlpha=ubGloColAlpha=0xff;   
     DEFOPAQUEON
     PRIMdrawTexturedQuad(&vertex[0], &vertex[1], &vertex[3], &vertex[2]);
     DEFOPAQUEOFF
    }
   return;
  }

 if(!bUseMultiPass  && !bGLBlend) 
  {
   vertex[0].c.lcol=DoubleBGR2RGB(gpuData[0]);
   vertex[1].c.lcol=DoubleBGR2RGB(gpuData[3]);
   vertex[2].c.lcol=DoubleBGR2RGB(gpuData[6]);
   vertex[3].c.lcol=DoubleBGR2RGB(gpuData[9]);
  }
 else
  {
   vertex[0].c.lcol=gpuData[0];
   vertex[1].c.lcol=gpuData[3];
   vertex[2].c.lcol=gpuData[6];
   vertex[3].c.lcol=gpuData[9];
  }

 vertex[0].c.col[3]=vertex[1].c.col[3]=vertex[2].c.col[3]=vertex[3].c.col[3]=ubGloAlpha; 

 PRIMdrawTexGouraudTriColorQuad(&vertex[0], &vertex[1], &vertex[3],&vertex[2]);
 
 if(bDrawMultiPass)
  {
   SetSemiTransMulti(1);
   PRIMdrawTexGouraudTriColorQuad(&vertex[0], &vertex[1], &vertex[3],&vertex[2]);
  }

 if(ubOpaqueDraw)
  {
   SetZMask4O();
   if(bUseMultiPass) 
    {
     vertex[0].c.lcol=DoubleBGR2RGB(gpuData[0]);
     vertex[1].c.lcol=DoubleBGR2RGB(gpuData[3]);
     vertex[2].c.lcol=DoubleBGR2RGB(gpuData[6]);
     vertex[3].c.lcol=DoubleBGR2RGB(gpuData[9]);
     vertex[0].c.col[3]=vertex[1].c.col[3]=vertex[2].c.col[3]=vertex[3].c.col[3]=ubGloAlpha; 
    }
   ubGloAlpha=ubGloColAlpha=0xff;   
   DEFOPAQUEON
   PRIMdrawTexGouraudTriColorQuad(&vertex[0], &vertex[1], &vertex[3],&vertex[2]);
   DEFOPAQUEOFF
  }

 iDrawnSomething=1;
}

////////////////////////////////////////////////////////////////////////
// cmd: smooth shaded Poly3
////////////////////////////////////////////////////////////////////////

void primPolyF3(unsigned char *baseAddr)
{    
 uint32_t *gpuData = ((uint32_t *) baseAddr);
 short *sgpuData = ((short *) baseAddr);

 lx0 = sgpuData[2];
 ly0 = sgpuData[3];
 lx1 = sgpuData[4];
 ly1 = sgpuData[5];
 lx2 = sgpuData[6];
 ly2 = sgpuData[7];

 if(offset3()) return;

 bDrawTextured     = FALSE;
 bDrawSmoothShaded = FALSE;
 SetRenderState(gpuData[0]);

 if(iOffscreenDrawing)
  {
   offsetPSX3();
   if(bDrawOffscreen3())
    {
     InvalidateTextureAreaEx();   
     drawPoly3F(gpuData[0]);
    }
  }

 SetRenderMode(gpuData[0], FALSE);
 SetZMask3NT();

 vertex[0].c.lcol=gpuData[0];
 vertex[0].c.col[3]=ubGloColAlpha;
 SETCOL(vertex[0]); 

 PRIMdrawTri(&vertex[0], &vertex[1], &vertex[2]);

 iDrawnSomething=1;
}

////////////////////////////////////////////////////////////////////////
// cmd: skipping shaded polylines
////////////////////////////////////////////////////////////////////////

void primLineGSkip(unsigned char *baseAddr)
{    
 uint32_t *gpuData = ((uint32_t *) baseAddr);
 short *sgpuData = ((short *) baseAddr);
 int iMax=255;
 int i=2;

 lx1 = sgpuData[2];
 ly1 = sgpuData[3];

 while(!(((gpuData[i] & 0xF000F000) == 0x50005000) && i>=4))
  {
   i++;

   ly1 = (short)((gpuData[i]>>16) & 0xffff);
   lx1 = (short)(gpuData[i] & 0xffff);

   i++;if(i>iMax) break;
  }
}

////////////////////////////////////////////////////////////////////////
// cmd: shaded polylines
////////////////////////////////////////////////////////////////////////

void primLineGEx(unsigned char *baseAddr)
{    
 uint32_t *gpuData = ((uint32_t *) baseAddr);
 int iMax=255;
 short cx0,cx1,cy0,cy1;int i;BOOL bDraw=TRUE;

 bDrawTextured = FALSE;
 bDrawSmoothShaded = TRUE;
 SetRenderState(gpuData[0]);
 SetRenderMode(gpuData[0], FALSE);
 SetZMask4NT();

 vertex[0].c.lcol=vertex[3].c.lcol=gpuData[0];
 vertex[0].c.col[3]=vertex[3].c.col[3]=ubGloColAlpha; 
 ly1 = (short)((gpuData[1]>>16) & 0xffff);
 lx1 = (short)(gpuData[1] & 0xffff);

 i=2;

 //while((gpuData[i]>>24)!=0x55)
 //while((gpuData[i]&0x50000000)!=0x50000000) 
 // currently best way to check for poly line end:
 while(!(((gpuData[i] & 0xF000F000) == 0x50005000) && i>=4))
  {
   ly0 = ly1;lx0=lx1;
   vertex[1].c.lcol=vertex[2].c.lcol=vertex[0].c.lcol;
   vertex[0].c.lcol=vertex[3].c.lcol=gpuData[i];
   vertex[0].c.col[3]=vertex[3].c.col[3]=ubGloColAlpha; 

   i++;

   ly1 = (short)((gpuData[i]>>16) & 0xffff);
   lx1 = (short)(gpuData[i] & 0xffff);

   if(offsetline()) bDraw=FALSE; else bDraw=TRUE;
  
   if (bDraw && ((lx0 != lx1) || (ly0 != ly1)))
    {
     if(iOffscreenDrawing)
      {
       cx0=lx0;cx1=lx1;cy0=ly0;cy1=ly1;
       offsetPSXLine();
       if(bDrawOffscreen4())
        {
         InvalidateTextureAreaEx();   
         drawPoly4G(gpuData[i-3],gpuData[i-1],gpuData[i-3],gpuData[i-1]);
        }
       lx0=cx0;lx1=cx1;ly0=cy0;ly1=cy1;
      }

     PRIMdrawGouraudLine(&vertex[0], &vertex[1], &vertex[2], &vertex[3]);
    }
   i++;  

   if(i>iMax) break;
  }

 iDrawnSomething=1;
}

////////////////////////////////////////////////////////////////////////
// cmd: shaded polyline2
////////////////////////////////////////////////////////////////////////

void primLineG2(unsigned char *baseAddr)
{    
 uint32_t *gpuData = ((uint32_t *) baseAddr);
 short *sgpuData = ((short *) baseAddr);

 lx0 = sgpuData[2];
 ly0 = sgpuData[3];
 lx1 = sgpuData[6];
 ly1 = sgpuData[7];

 vertex[0].c.lcol=vertex[3].c.lcol=gpuData[0];
 vertex[1].c.lcol=vertex[2].c.lcol=gpuData[2];
 vertex[0].c.col[3]=vertex[1].c.col[3]=vertex[2].c.col[3]=vertex[3].c.col[3]=ubGloColAlpha; 

 bDrawTextured = FALSE;
 bDrawSmoothShaded = TRUE;

 if((lx0 == lx1) && (ly0 == ly1)) return;
    
 if(offsetline()) return;
    
 SetRenderState(gpuData[0]);
 SetRenderMode(gpuData[0], FALSE);
 SetZMask4NT();

 if(iOffscreenDrawing)
  {
   offsetPSXLine();
   if(bDrawOffscreen4())
    {
     InvalidateTextureAreaEx();   
     drawPoly4G(gpuData[0],gpuData[2],gpuData[0],gpuData[2]);
    }
  }

 //if(ClipVertexList4())
 PRIMdrawGouraudLine(&vertex[0], &vertex[1], &vertex[2], &vertex[3]);

 iDrawnSomething=1;
}

////////////////////////////////////////////////////////////////////////
// cmd: skipping flat polylines
////////////////////////////////////////////////////////////////////////

void primLineFSkip(unsigned char *baseAddr)
{
 uint32_t *gpuData = ((uint32_t *) baseAddr);
 int i=2,iMax=255;

 ly1 = (short)((gpuData[1]>>16) & 0xffff);
 lx1 = (short)(gpuData[1] & 0xffff);

 while(!(((gpuData[i] & 0xF000F000) == 0x50005000) && i>=3))
  {
   ly1 = (short)((gpuData[i]>>16) & 0xffff);
   lx1 = (short)(gpuData[i] & 0xffff);
   i++;if(i>iMax) break;
  }             
}

////////////////////////////////////////////////////////////////////////
// cmd: drawing flat polylines
////////////////////////////////////////////////////////////////////////

void primLineFEx(unsigned char *baseAddr)
{
 uint32_t *gpuData = ((uint32_t *) baseAddr);
 int iMax;
 short cx0,cx1,cy0,cy1;int i;

 iMax=255;

 bDrawTextured = FALSE;
 bDrawSmoothShaded = FALSE;
 SetRenderState(gpuData[0]);
 SetRenderMode(gpuData[0], FALSE);
 SetZMask4NT();

 vertex[0].c.lcol=gpuData[0];
 vertex[0].c.col[3]=ubGloColAlpha; 

 ly1 = (short)((gpuData[1]>>16) & 0xffff);
 lx1 = (short)(gpuData[1] & 0xffff);

 i=2;

// while(!(gpuData[i]&0x40000000)) 
// while((gpuData[i]>>24)!=0x55)
// while((gpuData[i]&0x50000000)!=0x50000000) 
// currently best way to check for poly line end:
 while(!(((gpuData[i] & 0xF000F000) == 0x50005000) && i>=3))
  {
   ly0 = ly1;lx0=lx1;
   ly1 = (short)((gpuData[i]>>16) & 0xffff);
   lx1 = (short)(gpuData[i] & 0xffff);

   if(!offsetline())
    {
     if(iOffscreenDrawing)
      {
       cx0=lx0;cx1=lx1;cy0=ly0;cy1=ly1;
       offsetPSXLine();
       if(bDrawOffscreen4())
        {
         InvalidateTextureAreaEx();   
         drawPoly4F(gpuData[0]);
        }
       lx0=cx0;lx1=cx1;ly0=cy0;ly1=cy1;
      }
     PRIMdrawFlatLine(&vertex[0], &vertex[1], &vertex[2], &vertex[3]);
    }
                  
   i++;if(i>iMax) break;
  }

 iDrawnSomething=1;
}

////////////////////////////////////////////////////////////////////////
// cmd: drawing flat polyline2
////////////////////////////////////////////////////////////////////////

void primLineF2(unsigned char *baseAddr)
{
 uint32_t *gpuData = ((uint32_t *) baseAddr);
 short *sgpuData = ((short *) baseAddr);

 lx0 = sgpuData[2];
 ly0 = sgpuData[3];
 lx1 = sgpuData[4];
 ly1 = sgpuData[5];

 if(offsetline()) return;

 bDrawTextured = FALSE;
 bDrawSmoothShaded = FALSE;
 SetRenderState(gpuData[0]);
 SetRenderMode(gpuData[0], FALSE);
 SetZMask4NT();

 vertex[0].c.lcol=gpuData[0];
 vertex[0].c.col[3]=ubGloColAlpha; 

 if(iOffscreenDrawing)
  {
   offsetPSXLine();
   if(bDrawOffscreen4())
    {
     InvalidateTextureAreaEx();   
     drawPoly4F(gpuData[0]);
    }
  }

 //if(ClipVertexList4()) 
 PRIMdrawFlatLine(&vertex[0], &vertex[1], &vertex[2], &vertex[3]);

 iDrawnSomething=1;
}

////////////////////////////////////////////////////////////////////////
// cmd: well, easiest command... not implemented
////////////////////////////////////////////////////////////////////////

void primNI(unsigned char *bA)
{
}

////////////////////////////////////////////////////////////////////////
// cmd func ptr table
////////////////////////////////////////////////////////////////////////

void (*primTableJ[256])(unsigned char *) = 
{
    // 00
    primNI,primNI,primBlkFill,primNI,primNI,primNI,primNI,primNI,
    // 08
    primNI,primNI,primNI,primNI,primNI,primNI,primNI,primNI,
    // 10
    primNI,primNI,primNI,primNI,primNI,primNI,primNI,primNI,
    // 18
    primNI,primNI,primNI,primNI,primNI,primNI,primNI,primNI,
    // 20
    primPolyF3,primPolyF3,primPolyF3,primPolyF3,primPolyFT3,primPolyFT3,primPolyFT3,primPolyFT3,
    // 28
    primPolyF4,primPolyF4,primPolyF4,primPolyF4,primPolyFT4,primPolyFT4,primPolyFT4,primPolyFT4,
    // 30
    primPolyG3,primPolyG3,primPolyG3,primPolyG3,primPolyGT3,primPolyGT3,primPolyGT3,primPolyGT3,
    // 38
    primPolyG4,primPolyG4,primPolyG4,primPolyG4,primPolyGT4,primPolyGT4,primPolyGT4,primPolyGT4,
    // 40
    primLineF2,primLineF2,primLineF2,primLineF2,primNI,primNI,primNI,primNI,
    // 48
    primLineFEx,primLineFEx,primLineFEx,primLineFEx,primLineFEx,primLineFEx,primLineFEx,primLineFEx,
    // 50
    primLineG2,primLineG2,primLineG2,primLineG2,primNI,primNI,primNI,primNI,
    // 58
    primLineGEx,primLineGEx,primLineGEx,primLineGEx,primLineGEx,primLineGEx,primLineGEx,primLineGEx,
    // 60
    primTileS,primTileS,primTileS,primTileS,primSprtS,primSprtS,primSprtS,primSprtS,
    // 68
    primTile1,primTile1,primTile1,primTile1,primNI,primNI,primNI,primNI,
    // 70
    primTile8,primTile8,primTile8,primTile8,primSprt8,primSprt8,primSprt8,primSprt8,
    // 78
    primTile16,primTile16,primTile16,primTile16,primSprt16,primSprt16,primSprt16,primSprt16,
    // 80
    primMoveImage,primNI,primNI,primNI,primNI,primNI,primNI,primNI,
    // 88
    primNI,primNI,primNI,primNI,primNI,primNI,primNI,primNI,
    // 90
    primNI,primNI,primNI,primNI,primNI,primNI,primNI,primNI,
    // 98
    primNI,primNI,primNI,primNI,primNI,primNI,primNI,primNI,
    // a0
    primLoadImage,primNI,primNI,primNI,primNI,primNI,primNI,primNI,
    // a8
    primNI,primNI,primNI,primNI,primNI,primNI,primNI,primNI,
    // b0
    primNI,primNI,primNI,primNI,primNI,primNI,primNI,primNI,
    // b8
    primNI,primNI,primNI,primNI,primNI,primNI,primNI,primNI,
    // c0
    primStoreImage,primNI,primNI,primNI,primNI,primNI,primNI,primNI,
    // c8
    primNI,primNI,primNI,primNI,primNI,primNI,primNI,primNI,
    // d0
    primNI,primNI,primNI,primNI,primNI,primNI,primNI,primNI,
    // d8
    primNI,primNI,primNI,primNI,primNI,primNI,primNI,primNI,
    // e0
    primNI,cmdTexturePage,cmdTextureWindow,cmdDrawAreaStart,cmdDrawAreaEnd,cmdDrawOffset,cmdSTP,primNI,
    // e8
    primNI,primNI,primNI,primNI,primNI,primNI,primNI,primNI,
    // f0
    primNI,primNI,primNI,primNI,primNI,primNI,primNI,primNI,
    // f8
    primNI,primNI,primNI,primNI,primNI,primNI,primNI,primNI
};

////////////////////////////////////////////////////////////////////////
// cmd func ptr table for skipping
////////////////////////////////////////////////////////////////////////

void (*primTableSkip[256])(unsigned char *) = 
{
    // 00
    primNI,primNI,primBlkFill,primNI,primNI,primNI,primNI,primNI,
    // 08
    primNI,primNI,primNI,primNI,primNI,primNI,primNI,primNI,
    // 10
    primNI,primNI,primNI,primNI,primNI,primNI,primNI,primNI,
    // 18
    primNI,primNI,primNI,primNI,primNI,primNI,primNI,primNI,
    // 20
    primNI,primNI,primNI,primNI,primNI,primNI,primNI,primNI,
    // 28
    primNI,primNI,primNI,primNI,primNI,primNI,primNI,primNI,
    // 30
    primNI,primNI,primNI,primNI,primNI,primNI,primNI,primNI,
    // 38
    primNI,primNI,primNI,primNI,primNI,primNI,primNI,primNI,
    // 40
    primNI,primNI,primNI,primNI,primNI,primNI,primNI,primNI,
    // 48
    primLineFSkip,primLineFSkip,primLineFSkip,primLineFSkip,primLineFSkip,primLineFSkip,primLineFSkip,primLineFSkip,
    // 50
    primNI,primNI,primNI,primNI,primNI,primNI,primNI,primNI,
    // 58
    primLineGSkip,primLineGSkip,primLineGSkip,primLineGSkip,primLineGSkip,primLineGSkip,primLineGSkip,primLineGSkip,
    // 60
    primNI,primNI,primNI,primNI,primNI,primNI,primNI,primNI,
    // 68
    primNI,primNI,primNI,primNI,primNI,primNI,primNI,primNI,
    // 70
    primNI,primNI,primNI,primNI,primNI,primNI,primNI,primNI,
    // 78
    primNI,primNI,primNI,primNI,primNI,primNI,primNI,primNI,
    // 80
    primMoveImage,primNI,primNI,primNI,primNI,primNI,primNI,primNI,
    // 88
    primNI,primNI,primNI,primNI,primNI,primNI,primNI,primNI,
    // 90
    primNI,primNI,primNI,primNI,primNI,primNI,primNI,primNI,
    // 98
    primNI,primNI,primNI,primNI,primNI,primNI,primNI,primNI,
    // a0
    primLoadImage,primNI,primNI,primNI,primNI,primNI,primNI,primNI,
    // a8
    primNI,primNI,primNI,primNI,primNI,primNI,primNI,primNI,
    // b0
    primNI,primNI,primNI,primNI,primNI,primNI,primNI,primNI,
    // b8
    primNI,primNI,primNI,primNI,primNI,primNI,primNI,primNI,
    // c0
    primStoreImage,primNI,primNI,primNI,primNI,primNI,primNI,primNI,
    // c8
    primNI,primNI,primNI,primNI,primNI,primNI,primNI,primNI,
    // d0
    primNI,primNI,primNI,primNI,primNI,primNI,primNI,primNI,
    // d8
    primNI,primNI,primNI,primNI,primNI,primNI,primNI,primNI,
    // e0
    primNI,cmdTexturePage,cmdTextureWindow,cmdDrawAreaStart,cmdDrawAreaEnd,cmdDrawOffset,cmdSTP,primNI,
    // e8
    primNI,primNI,primNI,primNI,primNI,primNI,primNI,primNI,
    // f0
    primNI,primNI,primNI,primNI,primNI,primNI,primNI,primNI,
    // f8
    primNI,primNI,primNI,primNI,primNI,primNI,primNI,primNI
};
