
/***************************************************************************
 * Purpose : Quick and dirty sky port                                      *
 *         : 99/05/25 Quick conversion to hardware                         *
 *                                                                         *
 **************************************************************************/

/**
 * \ingroup driversky2
 * \page coresky2overview PS2 Core Library Overview
 *
 * The PS2 has numerous features that are not included in the
 * cross-platform RenderWare API.  To provide access to these features we
 * provide a set of PS2-specific functions.  These functions are
 * documented in this section, and begin with an RpSky prefix.  Note that
 * these functions are not implemented in a plugin RpSky, so no
 * additional include files and libraries are necessary to use them.
 *
 * The majority of the RpSky functions are used to control rendering
 * behaviour.  One area with many API functions is the setting of the "K"
 * and "L" values used by the GS to bias mipmap level computation.  There
 * are also function to control additional render states that the GS
 * supports, choice of clippers, and the Z-depth.
 *
 * At this time there are also many functions which might prove useful to
 * developers; these are listed as unsupported.  The intention is to
 * replace these with supported RpSky functions at a later date.  You are
 * free to use these functions, but should understand that they could be
 * dropped from future releases.
 */
/***************************************************************************
 Includes
 */

#define BETTERPACK
#define HIDEPALETTE

#if defined (BETTERPACK)
#ifndef HIDEPALETTE
#define HIDEPALETTE
#endif /* !HIDEPALETTE */
#endif /* defined (BETTERPACK) */

#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <string.h>

/* Sony lib includes. I can't believe I'm using them */
#include <eekernel.h>
#include <eetypes.h>
#include <eeregs.h>
#include <libgraph.h>
#include <libdma.h>
#include <libdev.h>

#ifdef GSB
#include "gsbapi.h"
#endif /* GSB */
/* end Sony includes */

#include "batypes.h"
#include "badevice.h"
#include "balibtyp.h"
#include "baraster.h"
#include "baimage.h"
#include "batextur.h"
#include "babinary.h"
#include "babintex.h"
#include "bacamera.h"
#include "bavector.h"
#include "bamemory.h"
#include "barwtyp.h"
#include "baimmedi.h"
#include "babinary.h"
#include "babintex.h"
#include "baresour.h" /* required for rpworld.h */

/* String abstraction API for unicode support */
#include "rwstring.h"

/* Include for rwID_SKYMIPMAPVAL */
#include "rpcriter.h"

/* Drag in the vertex format */
#include "drvmodel.h"

#include "badma.h"
/* GS defines */
#include "gs.h"
/* baasm.s externs */
#include "baasm.h"

#include "baskytran.h"

/* Other driver module includes */
#include "texcache.h"
#include "skyblit.h"
#include "skyconv.h"
#include "skyinst.h"
#include "baevent.h"
#include "drvfns.h"

/* Include profiler */
#include "devprofile.h"

/* This files header */
#include "basky.h"

#if (!defined(DOXYGEN))
static const char rcsid[] __RWUNUSED__ =
"@@(#)$Id: basky.c,v 1.206 2001/09/26 14:02:52 Rabin Exp $";
#endif /* (!defined(DOXYGEN)) */

/* WARNING: I assume that Z values in XYZ{F}[23] pack into bottom end */

/* Some fog support code appears here, but will need work in drvmodel.h
   Also note that we have to turn fog on/off on a per prim basis as a fog
   costs 1 cycle per pixel. Their fog values are the reverse of ours:
   RW 0 = not fogged, 255 = fogged
   GS 255 = not fogged, 0 = fogged */

/****************************************************************************
 Global (extern) Prototypes
 */

/****************************************************************************
 Local Types
 */

typedef struct __rwSkyNativeTexture _rwSkyNativeTexture;
struct __rwSkyNativeTexture
{
    RwUInt8    id[4];               /* Yes, these are RwUInt8s, not RwChars */
    RwInt32    filterAndAddress;    /* Same as in babintex.c */
};

typedef struct __rwSkyNativeRaster _rwSkyNativeRaster;
struct __rwSkyNativeRaster
{
    RwInt32    width, height, depth;
    RwUInt16   format;
    RwUInt16   version;
    RwUInt32   lsb, msb;
    RwUInt32   palOffset;
    RwUInt32   maxMipLevel;
    RwUInt32   miptbp1Lsb, miptbp1Msb;
    RwUInt32   miptbp2Lsb, miptbp2Msb;
    RwUInt32   sysMemSize;
    RwUInt32   sysMemPalSize;
    RwUInt32   texCacheSize;
    RwUInt32   mipmapKL; /* Was mipmapK. This should be compatible */
};



/* A structure to make the initial framebuffer clear a bit more readable */
typedef struct _clearPkt clearPkt  __attribute__((aligned(16)));

struct _clearPkt
{
    sceGifTag     giftag;
    unsigned long alpha;
    unsigned long alphaadr;
    unsigned long frame;
    unsigned long frameadr;
    unsigned long prmodecont;
    unsigned long prmodecontadr;
    unsigned long scissor;
    unsigned long scissoradr;
    unsigned long test;
    unsigned long testadr;
    unsigned long xyoffset;
    unsigned long xyoffsetadr;
    unsigned long zbuf;
    unsigned long zbufadr;
    unsigned long prim;
    unsigned long primadr;
    unsigned long rgbaqa;
    unsigned long rgbaqadra;
    unsigned long xyza;
    unsigned long xyzadra;
    unsigned long rgbaqb;
    unsigned long rgbaqadrb;
    unsigned long xyzb;
    unsigned long xyzadrb;
};

static clearPkt skyInitialClear;

/****************************************************************************
 Local (Static) Prototypes
 */


/****************************************************************************
 Local Defines
 */

/* Force draw to displayed buffer */
#define VISDRAWx
/* Draw a logo */
#define LOGOx

#ifdef LOGO
#define LOGOOFFX (24)
#define LOGOOFFY (300)
#define LOGOSIZE (128)
#endif /* LOGO */

/* Define twos complement values for the dither matrix */
#define DIMX_4 4l
#define DIMX_3 5l
#define DIMX_2 6l
#define DIMX_1 7l
#define DIMX0 0l
#define DIMX1 1l
#define DIMX2 2l
#define DIMX3 3l

/****************************************************************************
 Globals (across program)
 */

u_long128* fogTable;
RwVideoMode *skyVideoMode = (RwVideoMode *)NULL;

/* this is global so we can access this if the app shuts RW down */
RwBool cacheOpened = FALSE;
RwBool needCache = TRUE;

/****************************************************************************
 Local (static) Globals
 */

#ifdef ALTTEXFORM
static RwBool skyNoNewStyleRasters = FALSE;
#endif /* ALTTEXFORM */

static long skyRenderTime = 0;
static int skyRenderTimeInProgress = 0;

#ifdef LOGO
static RwBool logoLoaded = FALSE;
static RwTexture *logoTexture = NULL;
static float logoScaleX = 1.0f;
#endif /* LOGO */

#ifdef SHOWUPLOADEDTEXTURES
RwBool    bGShowUploadTextures = FALSE;
RwTexture *tpShowUploadedTextures[SHOWUPLOADEDTEXTUREMAXTEXTURES];
RwRaster  *rpShowUploadedRasters[SHOWUPLOADEDTEXTUREMAXTEXTURES];
#endif /*  SHOWUPLOADEDTEXTURES */

RwBool skySuspended = FALSE;

#ifdef GSB
/* These are used to decide if we use a sigle draw/display buffer to save */
/* memory at the expense of rendering time */
#define DOUBLE_BUFFER 0
#define SINGLE_BUFFER 1
/*
 * We overload the video mode flag stuff completely
 * (Buffer<<31)|(INTERLACE<<24)|(FIELD<<16)|(VESA)
 */
static RwVideoMode          videoModes[] = {
    {640, 480, 16, (RwVideoModeFlag)((SINGLE_BUFFER<<31)
                                     |(SCE_GS_NOINTERLACE<<24)
                                     |(SCE_GS_FRAME<<16)|(SCE_GS_VESA1A))},
    {640, 480, 32, (RwVideoModeFlag)((SINGLE_BUFFER<<31)
                                     |(SCE_GS_NOINTERLACE<<24)
                                     |(SCE_GS_FRAME<<16)|(SCE_GS_VESA1A))},
    {640, 480, 16, (RwVideoModeFlag)((DOUBLE_BUFFER<<31)
                                     |(SCE_GS_NOINTERLACE<<24)
                                     |(SCE_GS_FRAME<<16)|(SCE_GS_VESA1A))},
    {640, 480, 32, (RwVideoModeFlag)((DOUBLE_BUFFER<<31)
                                     |(SCE_GS_NOINTERLACE<<24)
                                     |(SCE_GS_FRAME<<16)|(SCE_GS_VESA1A))},
#ifdef GSPLUS
    {1920, 1080, 16, (RwVideoModeFlag)((SINGLE_BUFFER<<31)
                                       |(SCE_GS_NOINTERLACE<<24)
                                       |(SCE_GS_FRAME<<16)|(SCE_GS_DTV1080P))},
    {1920, 1080, 32, (RwVideoModeFlag)((SINGLE_BUFFER<<31)
                                       |(SCE_GS_NOINTERLACE<<24)
                                       |(SCE_GS_FRAME<<16)|(SCE_GS_DTV1080P))},
    {1920, 1080, 16, (RwVideoModeFlag)((DOUBLE_BUFFER<<31)
                                       |(SCE_GS_NOINTERLACE<<24)
                                       |(SCE_GS_FRAME<<16)|(SCE_GS_DTV1080P))},
    {1920, 1080, 32, (RwVideoModeFlag)((DOUBLE_BUFFER<<31)
                                       |(SCE_GS_NOINTERLACE<<24)
                                       |(SCE_GS_FRAME<<16)|(SCE_GS_DTV1080P))},
#endif /* GSPLUS */
};
#else /* GSB */

static RwVideoMode videoModes[] = {

    /*
     * These are the PAL modes 50Hz
     *   16 bit color depth
     */
    {256, 256, 16, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE)},
    {320, 256, 16, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE)},
    {384, 256, 16, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE)},
    {512, 256, 16, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE)},
    {640, 256, 16, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE)},
    /*     FSAA0 Supersample */
    {512, 512, 16, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE|rwVIDEOMODEFSAA0)},
    {640, 512, 16, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE|rwVIDEOMODEFSAA0)},
    /*      Interlaced */
    {256, 512, 16, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE|rwVIDEOMODEINTERLACE)},
    {320, 512, 16, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE|rwVIDEOMODEINTERLACE)},
    {384, 512, 16, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE|rwVIDEOMODEINTERLACE)},
    {512, 512, 16, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE|rwVIDEOMODEINTERLACE)},
    {640, 512, 16, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE|rwVIDEOMODEINTERLACE)},
    /*     Interlaced FSAA1 Dual read-circuit */
    {256, 512, 16, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE|rwVIDEOMODEINTERLACE|rwVIDEOMODEFSAA1)},
    {320, 512, 16, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE|rwVIDEOMODEINTERLACE|rwVIDEOMODEFSAA1)},
    {384, 512, 16, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE|rwVIDEOMODEINTERLACE|rwVIDEOMODEFSAA1)},
    {512, 512, 16, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE|rwVIDEOMODEINTERLACE|rwVIDEOMODEFSAA1)},
    {640, 512, 16, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE|rwVIDEOMODEINTERLACE|rwVIDEOMODEFSAA1)},
    /*      Flicker free */
    {256, 256, 16, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE|rwVIDEOMODEFFINTERLACE)},
    {320, 256, 16, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE|rwVIDEOMODEFFINTERLACE)},
    {384, 256, 16, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE|rwVIDEOMODEFFINTERLACE)},
    {512, 256, 16, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE|rwVIDEOMODEFFINTERLACE)},
    {640, 256, 16, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE|rwVIDEOMODEFFINTERLACE)},

    /*
     * These are the PAL modes 50Hz
     *   32 bit color
     */
    {256, 256, 32, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE)},
    {320, 256, 32, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE)},
    {384, 256, 32, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE)},
    {512, 256, 32, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE)},
    {640, 256, 32, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE)},
    /*     Interlaced FSAA0 Supersample */
    {512, 512, 32, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE|rwVIDEOMODEFSAA0)},
    {640, 512, 32, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE|rwVIDEOMODEFSAA0)},
    /*     Interlaced */
    {256, 512, 32, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE|rwVIDEOMODEINTERLACE)},
    {320, 512, 32, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE|rwVIDEOMODEINTERLACE)},
    {384, 512, 32, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE|rwVIDEOMODEINTERLACE)},
    {512, 512, 32, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE|rwVIDEOMODEINTERLACE)},
    {640, 512, 32, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE|rwVIDEOMODEINTERLACE)},
    /*     Interlaced FSAA1 Dual read-circuit */
    {256, 512, 32, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE|rwVIDEOMODEINTERLACE|rwVIDEOMODEFSAA1)},
    {320, 512, 32, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE|rwVIDEOMODEINTERLACE|rwVIDEOMODEFSAA1)},
    {384, 512, 32, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE|rwVIDEOMODEINTERLACE|rwVIDEOMODEFSAA1)},
    {512, 512, 32, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE|rwVIDEOMODEINTERLACE|rwVIDEOMODEFSAA1)},
    {640, 512, 32, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE|rwVIDEOMODEINTERLACE|rwVIDEOMODEFSAA1)},
    /*     Flicker free */
    {256, 256, 32, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE|rwVIDEOMODEFFINTERLACE)},
    {320, 256, 32, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE|rwVIDEOMODEFFINTERLACE)},
    {384, 256, 32, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE|rwVIDEOMODEFFINTERLACE)},
    {512, 256, 32, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE|rwVIDEOMODEFFINTERLACE)},
    {640, 256, 32, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE|rwVIDEOMODEFFINTERLACE)},

    /*
     * These are the NTSC modes 60 Hz
     *   16 bit color depth
     */
    {256, 224, 16, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE)},
    {320, 224, 16, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE)},
    {384, 224, 16, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE)},
    {512, 224, 16, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE)},
    {640, 224, 16, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE)},
    /*     Interlaced FSAA0 Supersample */
    {512, 448, 16, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE|rwVIDEOMODEFSAA0)},
    {640, 448, 16, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE|rwVIDEOMODEFSAA0)},
    /*     Interlaced */
    {256, 448, 16, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE|rwVIDEOMODEINTERLACE)},
    {320, 448, 16, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE|rwVIDEOMODEINTERLACE)},
    {384, 448, 16, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE|rwVIDEOMODEINTERLACE)},
    {512, 448, 16, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE|rwVIDEOMODEINTERLACE)},
    {640, 448, 16, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE|rwVIDEOMODEINTERLACE)},
    /*     Interlaced FSAA1 Dual read-circuit */
    {256, 448, 16, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE|rwVIDEOMODEINTERLACE|rwVIDEOMODEFSAA1)},
    {320, 448, 16, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE|rwVIDEOMODEINTERLACE|rwVIDEOMODEFSAA1)},
    {384, 448, 16, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE|rwVIDEOMODEINTERLACE|rwVIDEOMODEFSAA1)},
    {512, 448, 16, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE|rwVIDEOMODEINTERLACE|rwVIDEOMODEFSAA1)},
    {640, 448, 16, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE|rwVIDEOMODEINTERLACE|rwVIDEOMODEFSAA1)},
    /*     Flicker free*/
    {256, 224, 16, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE|rwVIDEOMODEFFINTERLACE)},
    {320, 224, 16, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE|rwVIDEOMODEFFINTERLACE)},
    {384, 224, 16, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE|rwVIDEOMODEFFINTERLACE)},
    {512, 224, 16, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE|rwVIDEOMODEFFINTERLACE)},
    {640, 224, 16, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE|rwVIDEOMODEFFINTERLACE)},

    /*
     * These are the NTSC modes 60 Hz
     *   32 bit color depth
     */
    {256, 224, 32, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE)},
    {320, 224, 32, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE)},
    {384, 224, 32, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE)},
    {512, 224, 32, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE)},
    {640, 224, 32, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE)},
    /*     Interlaced FSAA0 Supersample */
    {512, 448, 32, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE|rwVIDEOMODEFSAA0)},
    {640, 448, 32, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE|rwVIDEOMODEFSAA0)},
    /*     Interlaced */
    {256, 448, 32, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE|rwVIDEOMODEINTERLACE)},
    {320, 448, 32, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE|rwVIDEOMODEINTERLACE)},
    {384, 448, 32, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE|rwVIDEOMODEINTERLACE)},
    {512, 448, 32, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE|rwVIDEOMODEINTERLACE)},
    {640, 448, 32, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE|rwVIDEOMODEINTERLACE)},
    /*     Interlaced FSAA1 Dual read-circuit */
    {256, 448, 32, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE|rwVIDEOMODEINTERLACE|rwVIDEOMODEFSAA1)},
    {320, 448, 32, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE|rwVIDEOMODEINTERLACE|rwVIDEOMODEFSAA1)},
    {384, 448, 32, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE|rwVIDEOMODEINTERLACE|rwVIDEOMODEFSAA1)},
    {512, 448, 32, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE|rwVIDEOMODEINTERLACE|rwVIDEOMODEFSAA1)},
    {640, 448, 32, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE|rwVIDEOMODEINTERLACE|rwVIDEOMODEFSAA1)},
    /*     Flicker free */
    {256, 224, 32, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE|rwVIDEOMODEFFINTERLACE)},
    {320, 224, 32, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE|rwVIDEOMODEFFINTERLACE)},
    {384, 224, 32, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE|rwVIDEOMODEFFINTERLACE)},
    {512, 224, 32, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE|rwVIDEOMODEFFINTERLACE)},
    {640, 224, 32, (RwVideoModeFlag)(rwVIDEOMODEEXCLUSIVE|rwVIDEOMODEFFINTERLACE)}  /* Default mode */
};
#endif /* GSB */

static RwInt32 currentMode;
static RwInt32 currentCameraFormat;
static RwBool engineStarted;

/* The device globals */
RwRwDeviceGlobals           dgGGlobals;

/* GS registers are write only */
long skyZbuf_1 = 0;
long skyFrame_1 = 0;
long skyTest_1 = 0;
long skyXyoffset_1 = 0;
long skyFogcol = 0;
long skyClamp_1 = 0;
long skyTex1_1 = 0;
long skyAlpha_1 = 0;
#if defined(GSB) && defined(GSPLUS)
long skyFrame_2 = 0;
long skyZbuf_2 = 0;
#endif /* defined(GSB) && defined(GSPLUS) */

/* These two magic rasters are created at engine start and point to the actual frame buffers */
RwRaster        *skyDisplayRaster  = (RwRaster *) NULL;
RwRaster        *skyDrawRaster     = (RwRaster *) NULL;
_SkyMemBlock    skyFakeCacheFrameBuffer0;
_SkyMemBlock    skyFakeCacheFrameBuffer1;

long skyPrim_State = 0x8;
RwRaster *skyTextureRaster;
RwBool skyAlphaTex;
RwBool skyVertexAlpha;

static RwUInt32 skyDitherState;
RwCullMode gSkyCullState;

static int skySrcBlend = 2;
static int skyDestBlend = 3;

/* The offset of the device specific data in rasters & cameras */
RwInt32 skyRasterExt;
RwInt32 skyCameraExt;

static RwInt32 skyTextureExt;
/* This can be changed by RpSkyRenderStateSet (rwRENDERSTATEMAXMIPLEVELS) */
RwInt32 skyMaxMipLevels = 4;

/* scale up U,V by these: dead */
/* static int skyRasterUScale; */
/* static int skyRasterVScale; */

/* Sky fog tables are 96 entries of 8 bits, offset by 4 bits in 16 bit words */
/* They are interspersed by dma and vif tags to upload. Note entry order is */
/* 0, 2, 4..94 followed by 1, 3, 5..95 */
static u_long128 skyLinearFog[14];
static u_long128 skyExpFog[14];
static u_long128 skyExp2Fog[14];
static u_long128 skyMungedUserFog[14];
/* This one runs from 0->no fog to 255->fogged */
static RwUInt8 skyUserFog[256];

/* [dst][src] ZERO=0, ONE=1, SRCALPHA=2, INVSRCALPHA=3, DESTALPHA=4,
   INVDESTALPHA=5. Illegal states are negative. Note that [ONE][ONE] needs
   code support */
static int skyBlendStates[6][6] = {
    { 0x0000008a, 0x0000000a, 0x00000088, 0x00000002, 0x00000098, 0x00000012 },
    { 0x0000004a, 0x00000068, 0x00000048, 0xffffffff, 0x00000058, 0xffffffff },
    { 0x00000089, 0x00000009, 0xffffffff, 0x00000001, 0xffffffff, 0xffffffff },
    { 0x00000046, 0xffffffff, 0x00000044, 0xffffffff, 0xffffffff, 0xffffffff },
    { 0x00000099, 0x00000019, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000011 },
    { 0x00000056, 0xffffffff, 0xffffffff, 0xffffffff, 0x00000054, 0xffffffff }};

#if defined(GSB) && defined(GSPLUS)
sceGsPlusDBuffDc sweDb;
#else /* defined(GSB) && defined(GSPLUS) */
sceGsDBuffDc sweDb;
#endif /* defined(GSB) && defined(GSPLUS) */

/* curent frame set */
int skyFrameBit = 0;

/* Are buffering alpha polys */
static RwBool skyBufferAlphaPolys = 0;

/* Temp offset store */
long skyXOff = 0;
long skyYOff = 0;

/* default mipmap k */
static RwUInt32 skyDefaultMipmapKL = 0xfc0;

static RwUInt32 textureBankStart;

static RwUInt32 zBufferDepth = 16;

#ifdef GSB
/* The number of groups of gsms generating frames */
RwInt32 skyNumSeqFrms;
/* Our system GSM Id. We assume that gGSM ids start at 0 */
RwInt32 skyGsmId;
/* Our frame number in the sequence. This requires that we know stuff */
RwInt32 skyRenderSlot;
/* Are we trying to save memory and single buffer */
RwBool skySingleBuffer;
/* Do we need to stall on the first true buffer flip */
RwInt32 skyGsmSysReadyRequired = 5;
RwInt32 skyGsmSysReadyRequiredStraight = 6;

/* Our fake value of the global Vsync count */
unsigned long skyGCVTValue = 0;
/* A lagging version for use in the dma chain itself */
unsigned long skyDMAGCVTValue = 0;

/* Have we done a wait on DrawNext yet? */
RwBool skyNeedDrawNext;

/* We provide a function to set these. The defines are just defaults */
RwInt32 skyNumGGSMS = NUM_gGSMS;
RwInt32 skyNumGGSMSFrame = NUM_gGSMS_FRAME;
RwInt32 skyNumZMergFrame = NUM_ZM_FRAME;

/* We need additional offsets if we are tiled */
RwInt32 skyGsmOffsetX = 0;
RwInt32 skyGsmOffsetY = 0;
RwInt32 skyGsmWidth;
RwInt32 skyGsmHeight;

int skyGSMInputCh=0;
unsigned char skyGSMInput[6];
RwBool skyGSMReadInput = FALSE;
#endif /* GSB */

/* the FSAA0 mode needs a visible width which the user can specify */
RwUInt32 skyFSAA0VisibleWidth = 0;
RwUInt32 skyFSAA0RenderWidth = 0;
RwUInt32 skyFSAA0RenderHeight = 0;
/* Count the first four frames to construct the FSAA0 dma packets. */
RwUInt32 skyFSAA0FrameCount = 0;


/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

                          General hardware stuff

 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */

/****************************************************************************
 _rwSkyFindMSB

 On entry   : RwUInt32
 On exit    : Bit number of MSB (-1 if 0)
 */

RwInt32
_rwSkyFindMSB(RwUInt32 nInt)
{
    RwInt32 nPos = -1;

    RWFUNCTION(RWSTRING("_rwSkyFindMSB"));

    while (nInt)
    {
        nPos++;
        nInt>>=1;
    }

    RWRETURN(nPos);
}

/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

                          Alpha Buffered support

 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */

#if (0)
static void
replyBufferedAlphaPolys(void)
{
    RWFUNCTION(RWSTRING("replyBufferedAlphaPolys"));

    RWRETURNVOID();
}
#endif /* (0) */

/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

                          Render functions

 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */

/****************************************************************************
 _rwSkySetRenderState

 On entry   : State
            : pParam
 On exit    :
 */

RwBool
_rwSkySetRenderState(RwRenderState nState, void *pParam)
{
    long                tmp, tmp1;
    u_long128           ltmp = 0;

    RWFUNCTION(RWSTRING("_rwSkySetRenderState"));

    switch (nState)
    {
        case rwRENDERSTATETEXTURERASTER:
            {
                RwBool result = TRUE;

                /* Raster to texture with */
                skyTextureRaster = (RwRaster *) pParam;
#ifdef SHOWUPLOADEDTEXTURES
                if (bGShowUploadTextures)
                {
                    printf("Set Texture State Raster %x\n",
                           (int) skyTextureRaster);
                }
#endif           /*SHOWUPLOADEDTEXTURES*/
                if (skyTextureRaster)
                {
                    RpSkyTexCacheAccessSpeculate(skyTextureRaster);

                    /* Do what it takes to get the raster selected */
                    if (skyTexCacheAccessRaster(skyTextureRaster, FALSE))
                    {
                        skyPrim_State |= 0x10;

                        /* skyRasterUScale = skyTextureRaster->width << 4; */
                        /* skyRasterVScale = skyTextureRaster->height << 4; */

                        /* If an alpha format texture - enable alpha blending */
                        switch (skyTextureRaster->cFormat &
                                (rwRASTERFORMATPIXELFORMATMASK >> 8))
                        {
                            case (rwRASTERFORMAT888 >> 8):
                                {
                                    skyAlphaTex = FALSE;
                                    break;
                                }
                            default:
                                {
                                    skyAlphaTex = TRUE;
                                    break;
                                }
                        }
                    }
                    else
                    {
                        /* We do something sensible on fail */
                        skyAlphaTex = FALSE;
                        skyPrim_State &= ~0x10l;
                        skyTextureRaster = (RwRaster *) NULL;
                        result = FALSE;
                    }
                }
                else
                {
                    skyAlphaTex = FALSE;
                    skyPrim_State &= ~0x10l;
                }

                if (skyVertexAlpha || skyAlphaTex)
                {
                    skyPrim_State |= 0x40;
                }
                else
                {
                    skyPrim_State &= ~0x40l;
                }

                /* Finally iff skyAlphaTex we turn on Alpha test */
                if (skyAlphaTex)
                {
                    if (!(skyTest_1 & 1))
                    {
                        skyTest_1 |= 1;
                        sweOpenLocalPkt(SWE_LPS_CONT, -2);

                        tmp = /* NLOOP */ 1l
#ifdef LESSEOPS
                            | /* EOP */ (0l << 15)
#else /* LESSEOPS */
                            | /* EOP */ (1l << 15)
#endif /* LESSEOPS */
                            | /* PRE */ (0l << 46)
                            | /* FLG */ (0l << 58)
                            | /* NREG */ (1l << 60);
                        tmp1 = /* A+D */ (0xel << (64 - 64));
                        MAKE128(ltmp, tmp1, tmp);
                        SWEADDCONTGIFFAST(ltmp, 1);

                        tmp = skyTest_1;
                        tmp1 = (GS_TEST_1 << (64 - 64));
                        MAKE128(ltmp, tmp1, tmp);
                        SWEADDCONTFAST(ltmp);
                    }
                }
                else
                {
                    if (skyTest_1 & 1)
                    {
                        skyTest_1 &= ~1l;
                        sweOpenLocalPkt(SWE_LPS_CONT, -2);

                        tmp = /* NLOOP */ 1l
#ifdef LESSEOPS
                            | /* EOP */ (0l << 15)
#else /* LESSEOPS */
                            | /* EOP */ (1l << 15)
#endif /* LESSEOPS */
                            | /* PRE */ (0l << 46)
                            | /* FLG */ (0l << 58)
                            | /* NREG */ (1l << 60);
                        tmp1 = /* A+D */ (0xel << (64 - 64));
                        MAKE128(ltmp, tmp1, tmp);
                        SWEADDCONTGIFFAST(ltmp, 1);

                        tmp = skyTest_1;
                        tmp1 = (GS_TEST_1 << (64 - 64));
                        MAKE128(ltmp, tmp1, tmp);
                        SWEADDCONTFAST(ltmp);
                    }
                }

                RWRETURN(result);
            }
        case rwRENDERSTATETEXTUREADDRESS:
            {
                /* Clamp, wrap, mirror or border */

                switch ((RwTextureAddressMode)
                        ((RwUInt32) ((RwUInt32) pParam)))
                {
                    case rwTEXTUREADDRESSWRAP:
                        {
                            skyClamp_1 = (0 | (0 << 2));
                            break;
                        }
                    case rwTEXTUREADDRESSCLAMP:
                        {
                            skyClamp_1 = (1 | (1 << 2));
                            break;
                        }
                    case rwTEXTUREADDRESSMIRROR:
                    case rwTEXTUREADDRESSBORDER:
                    default:
                        {
                            /* Can't do this */
                            RWRETURN(FALSE);
                        }
                }

                sweOpenLocalPkt(SWE_LPS_CONT, -2);

                tmp = /* NLOOP */ 1l
#ifdef LESSEOPS
                    | /* EOP */ (0l << 15)
#else /* LESSEOPS */
                    | /* EOP */ (1l << 15)
#endif /* LESSEOPS */
                    | /* PRE */ (0l << 46)
                    | /* FLG */ (0l << 58)
                    | /* NREG */ (1l << 60);
                tmp1 = /* A+D */ (0xel << (64 - 64));
                MAKE128(ltmp, tmp1, tmp);
                SWEADDCONTGIFFAST(ltmp, 1);

                tmp = skyClamp_1;
                tmp1 = (GS_CLAMP_1 << (64 - 64));
                MAKE128(ltmp, tmp1, tmp);
                SWEADDCONTFAST(ltmp);

                break;
            }
        case rwRENDERSTATETEXTUREADDRESSU:
            {
                /* Clamp, wrap, mirror or border */
                switch ((RwTextureAddressMode)
                        ((RwUInt32) ((RwUInt32) pParam)))
                {
                    case rwTEXTUREADDRESSWRAP:
                        {
                            skyClamp_1 &= ~0x3l;
                            break;
                        }
                    case rwTEXTUREADDRESSCLAMP:
                        {
                            skyClamp_1 |= 1;
                            break;
                        }
                    case rwTEXTUREADDRESSMIRROR:
                    case rwTEXTUREADDRESSBORDER:
                    default:
                        {
                            /* Can't do this */
                            RWRETURN(FALSE);
                        }
                }

                sweOpenLocalPkt(SWE_LPS_CONT, -2);

                tmp = /* NLOOP */ 1l
#ifdef LESSEOPS
                    | /* EOP */ (0l << 15)
#else /* LESSEOPS */
                    | /* EOP */ (1l << 15)
#endif /* LESSEOPS */
                    | /* PRE */ (0l << 46)
                    | /* FLG */ (0l << 58)
                    | /* NREG */ (1l << 60);
                tmp1 = /* A+D */ (0xel << (64 - 64));
                MAKE128(ltmp, tmp1, tmp);
                SWEADDCONTGIFFAST(ltmp, 1);

                tmp = skyClamp_1;
                tmp1 = (GS_CLAMP_1 << (64 - 64));
                MAKE128(ltmp, tmp1, tmp);
                SWEADDCONTFAST(ltmp);

                break;
            }
        case rwRENDERSTATETEXTUREADDRESSV:
            {
                /* Clamp, wrap, mirror or border */
                switch ((RwTextureAddressMode) ((RwUInt32) pParam))
                {
                    case rwTEXTUREADDRESSWRAP:
                        {
                            skyClamp_1 &= ~0xcl;
                            break;
                        }
                    case rwTEXTUREADDRESSCLAMP:
                        {
                            skyClamp_1 |= (1 << 2);
                            break;
                        }
                    case rwTEXTUREADDRESSMIRROR:
                    case rwTEXTUREADDRESSBORDER:
                    default:
                        {
                            /* Can't do this */
                            RWRETURN(FALSE);
                        }
                }

                sweOpenLocalPkt(SWE_LPS_CONT, -2);

                tmp = /* NLOOP */ 1l
#ifdef LESSEOPS
                    | /* EOP */ (0l << 15)
#else /* LESSEOPS */
                    | /* EOP */ (1l << 15)
#endif /* LESSEOPS */
                    | /* PRE */ (0l << 46)
                    | /* FLG */ (0l << 58)
                    | /* NREG */ (1l << 60);
                tmp1 = /* A+D */ (0xel << (64 - 64));
                MAKE128(ltmp, tmp1, tmp);
                SWEADDCONTGIFFAST(ltmp, 1);

                tmp = skyClamp_1;
                tmp1 = (GS_CLAMP_1 << (64 - 64));
                MAKE128(ltmp, tmp1, tmp);
                SWEADDCONTFAST(ltmp);

                break;
            }
        case rwRENDERSTATETEXTUREPERSPECTIVE:
            {
                /* Perspective correction on/off */
                if (pParam)
                {
                    skyPrim_State &= ~0x100l;
                }
                else
                {
                    /* Can't do this */
                    RWRETURN(FALSE);
                }
                break;
            }
        case rwRENDERSTATEZTESTENABLE:
            {
                /* Z test on/off */
                if (pParam)
                {
                    /* Depth on, >= */
                    skyTest_1 &= ~0x70000l;
                    skyTest_1 |= 0x50000l;
                }
                else
                {
                    /* Depth on, always */
                    skyTest_1 &= ~0x70000l;
                    skyTest_1 |= 0x30000l;
                }
                if ((skyZbuf_1 & (1l << 32)) && (!pParam))
                {
                    /* Depth off as no write */
                    /* Disabled due to GS bug */
                    /* skyTest_1 &= ~0x10000l; */
                }

                sweOpenLocalPkt(SWE_LPS_CONT, -2);

                tmp = /* NLOOP */ 1l
#ifdef LESSEOPS
                    | /* EOP */ (0l << 15)
#else /* LESSEOPS */
                    | /* EOP */ (1l << 15)
#endif /* LESSEOPS */
                    | /* PRE */ (0l << 46)
                    | /* FLG */ (0l << 58)
                    | /* NREG */ (1l << 60);
                tmp1 = /* A+D */ (0xel << (64 - 64));
                MAKE128(ltmp, tmp1, tmp);
                SWEADDCONTGIFFAST(ltmp, 1);

                tmp = skyTest_1;
                tmp1 = (GS_TEST_1 << (64 - 64));
                MAKE128(ltmp, tmp1, tmp);
                SWEADDCONTFAST(ltmp);
                break;
            }
        case rwRENDERSTATESHADEMODE:
            {
                /* Flat or gouraud */
                if (rwSHADEMODEFLAT ==
                    (RwShadeMode) ((RwUInt32) pParam))
                {
                    skyPrim_State &= ~0x8l;
                }
                else if (rwSHADEMODEGOURAUD ==
                         (RwShadeMode) ((RwUInt32) pParam))
                {
                    skyPrim_State |= 0x8l;
                }
                else
                {
                    RWRETURN(FALSE);
                }
                break;
            }
        case rwRENDERSTATEZWRITEENABLE:
            {
                /* Z write on/off */
                if ((RwBool) ((RwUInt32) pParam))
                {
                    skyZbuf_1 &= ~(1l << 32);
                    if (!(skyTest_1 & 0x10000l))
                    {
                        skyTest_1 &= ~0x70000l;
                        skyTest_1 |= 0x30000l;
                    }
                }
                else
                {
                    skyZbuf_1 |= (1l << 32);
                    if ((skyTest_1 & 0x70000l) == 0x30000l)
                    {
                        /* Disabled due to bug in GS */
                        /* skyTest_1 &= ~0x10000l; */
                    }
                }

                sweOpenLocalPkt(SWE_LPS_CONT, -3);

                tmp = /* NLOOP */ 2l
#ifdef LESSEOPS
                    | /* EOP */ (0l << 15)
#else /* LESSEOPS */
                    | /* EOP */ (1l << 15)
#endif /* LESSEOPS */
                    | /* PRE */ (0l << 46)
                    | /* FLG */ (0l << 58)
                    | /* NREG */ (1l << 60);
                tmp1 = /* A+D */ (0xel << (64 - 64));
                MAKE128(ltmp, tmp1, tmp);
                SWEADDCONTGIFFAST(ltmp, 2);

                tmp = skyZbuf_1;
                tmp1 = (GS_ZBUF_1 << (64 - 64));
                MAKE128(ltmp, tmp1, tmp);
                SWEADDCONTFAST(ltmp);

                tmp = skyTest_1;
                tmp1 = (GS_TEST_1 << (64 - 64));
                MAKE128(ltmp, tmp1, tmp);
                SWEADDCONTFAST(ltmp);
                break;
            }
        case rwRENDERSTATETEXTUREFILTER:
            {
                /* Point sample, bilinear, trilinear, etc */
                switch ((RwTextureFilterMode) ((RwUInt32) pParam))
                {
                    case rwFILTERNEAREST:
                        {
                            /* Point sampled */
                            skyTex1_1 &= ~0x1e0l;
                            break;
                        }
                    case rwFILTERLINEAR:
                        {
                            /* Bilinear */
                            skyTex1_1 &= ~0x1e0l;
                            skyTex1_1 |= 0x60;
                            break;
                        }
                    case rwFILTERMIPNEAREST:
                        {
                            /* Point sampled per pixel mip map */
                            skyTex1_1 &= ~0x1e0l;
                            skyTex1_1 |= 0x80;
                            break;
                        }
                    case rwFILTERMIPLINEAR:
                        {
                            /* Bilinear per pixel mipmap */
                            skyTex1_1 &= ~0x1e0l;
                            skyTex1_1 |= 0x120;
                            break;
                        }
                    case rwFILTERLINEARMIPNEAREST:
                        {
                            /* MipMap interp point sampled */
                            skyTex1_1 &= ~0x1e0l;
                            skyTex1_1 |= 0xc0;
                            break;
                        }
                    case rwFILTERLINEARMIPLINEAR:
                        {
                            /* Trilinear */
                            skyTex1_1 &= ~0x1e0l;
                            skyTex1_1 |= 0x160;
                            break;
                        }
                    default:
                        {
                            RWRETURN(FALSE);
                        }
                }

                sweOpenLocalPkt(SWE_LPS_CONT, -2);

                tmp = /* NLOOP */ 1l
#ifdef LESSEOPS
                    | /* EOP */ (0l << 15)
#else /* LESSEOPS */
                    | /* EOP */ (1l << 15)
#endif /* LESSEOPS */
                    | /* PRE */ (0l << 46)
                    | /* FLG */ (0l << 58)
                    | /* NREG */ (1l << 60);
                tmp1 = /* A+D */ (0xel << (64 - 64));
                MAKE128(ltmp, tmp1, tmp);
                SWEADDCONTGIFFAST(ltmp, 1);

                tmp = skyTex1_1;
                tmp1 = (GS_TEX1_1 << (64 - 64));
                MAKE128(ltmp, tmp1, tmp);
                SWEADDCONTFAST(ltmp);

                break;
            }
            /* Warning: Not all combinations of Blend render states are valid,
             * so going from Cd(1-As) to Cs(1-As)+CdAs via Cs(1-As)+Cd(1-As)
             * should fail, where as going via CdAs should not. Until we can
             * set both SRC and DEST blend states together, we have a hack where
             * even when we fail, we still store the RW blend state if it might
             * make sense later */
        case rwRENDERSTATESRCBLEND:
            {
                /* Src alpha, 1-src alpha, etc */
                /* on PSX-II, you get:
                 * ({Cs, Cd, 0} - {Cs, Cd, 0})*{As, Ad, FIX}>>7 + {Cs, Cd, 0} */
                switch ((RwBlendFunction) ((RwUInt32) pParam))
                {
                    case rwBLENDZERO:
                        {
                            skySrcBlend = 0;
                            break;
                        }
                    case rwBLENDONE:
                        {
                            skySrcBlend = 1;
                            break;
                        }
                    case rwBLENDSRCALPHA:
                        {
                            skySrcBlend = 2;
                            break;
                        }
                    case rwBLENDINVSRCALPHA:
                        {
                            skySrcBlend = 3;
                            break;
                        }
                    case rwBLENDDESTALPHA:
                        {
                            skySrcBlend = 4;
                            break;
                        }
                    case rwBLENDINVDESTALPHA:
                        {
                            skySrcBlend = 5;
                            break;
                        }
                    default:
                        {
                            RWRETURN(FALSE);
                        }
                }
                if ((skyAlpha_1 =
                     (long) skyBlendStates[skyDestBlend][skySrcBlend]) <
                    0)
                {
                    RWRETURN(FALSE);
                }
                else if ((skySrcBlend == 1) && (skyDestBlend == 1))
                {
                    skyAlpha_1 |= (0x80l << 32);
                }

                sweOpenLocalPkt(SWE_LPS_CONT, -2);

                tmp = /* NLOOP */ 1l
#ifdef LESSEOPS
                    | /* EOP */ (0l << 15)
#else /* LESSEOPS */
                    | /* EOP */ (1l << 15)
#endif /* LESSEOPS */
                    | /* PRE */ (0l << 46)
                    | /* FLG */ (0l << 58)
                    | /* NREG */ (1l << 60);
                tmp1 = /* A+D */ (0xel << (64 - 64));
                MAKE128(ltmp, tmp1, tmp);
                SWEADDCONTGIFFAST(ltmp, 1);

                tmp = skyAlpha_1;
                tmp1 = (GS_ALPHA_1 << (64 - 64));
                MAKE128(ltmp, tmp1, tmp);
                SWEADDCONTFAST(ltmp);
                break;
            }
        case rwRENDERSTATEDESTBLEND:
            {
                /* Src alpha, 1-src alpha, etc */
                /* on PSX-II, you get:
                 * ({Cs, Cd, 0} - {Cs, Cd, 0})*{As, Ad, FIX}>>7 + {Cs, Cd, 0} */
                switch ((RwBlendFunction) ((RwUInt32) pParam))
                {
                    case rwBLENDZERO:
                        {
                            skyDestBlend = 0;
                            break;
                        }
                    case rwBLENDONE:
                        {
                            skyDestBlend = 1;
                            break;
                        }
                    case rwBLENDSRCALPHA:
                        {
                            skyDestBlend = 2;
                            break;
                        }
                    case rwBLENDINVSRCALPHA:
                        {
                            skyDestBlend = 3;
                            break;
                        }
                    case rwBLENDDESTALPHA:
                        {
                            skyDestBlend = 4;
                            break;
                        }
                    case rwBLENDINVDESTALPHA:
                        {
                            skyDestBlend = 5;
                            break;
                        }
                    default:
                        {
                            RWRETURN(FALSE);
                        }
                }
                if ((skyAlpha_1 =
                     (long) skyBlendStates[skyDestBlend][skySrcBlend]) <
                    0)
                {
                    RWRETURN(FALSE);
                }
                else if ((skySrcBlend == 1) && (skyDestBlend == 1))
                {
                    skyAlpha_1 |= (0x80l << 32);
                }

                sweOpenLocalPkt(SWE_LPS_CONT, -2);

                tmp = /* NLOOP */ 1l
#ifdef LESSEOPS
                    | /* EOP */ (0l << 15)
#else /* LESSEOPS */
                    | /* EOP */ (1l << 15)
#endif /* LESSEOPS */
                    | /* PRE */ (0l << 46)
                    | /* FLG */ (0l << 58)
                    | /* NREG */ (1l << 60);
                tmp1 = /* A+D */ (0xel << (64 - 64));
                MAKE128(ltmp, tmp1, tmp);
                SWEADDCONTGIFFAST(ltmp, 1);

                tmp = skyAlpha_1;
                tmp1 = (GS_ALPHA_1 << (64 - 64));
                MAKE128(ltmp, tmp1, tmp);
                SWEADDCONTFAST(ltmp);
                break;
            }
        case rwRENDERSTATEVERTEXALPHAENABLE:
            {
                /* Alpha in vertices enabled */
                skyVertexAlpha = (pParam != NULL);
                if (skyVertexAlpha || skyAlphaTex)
                {
                    skyPrim_State |= 0x40;
                }
                else
                {
                    skyPrim_State &= ~0x40l;
                }
                break;
            }
        case rwRENDERSTATEFOGENABLE:
            {
                /* Turns on fogging (all polygons will be fogged) */
                if ((RwBool) ((RwUInt32) pParam))
                {
                    skyPrim_State |= 0x20;
                    skyTransType |= TRANSFOG;
                }
                else
                {
                    skyPrim_State &= ~0x20l;
                    skyTransType &= ~TRANSFOG;
                }
                break;
            }
        case rwRENDERSTATEFOGCOLOR:
            {
                /* Color used for fogging */

                sweOpenLocalPkt(SWE_LPS_CONT, -2);

                skyFogcol =
                    (((RwUInt32) ((RwUInt32) pParam)) & 0xff0000) >> 16
                    | (((RwUInt32) ((RwUInt32) pParam)) & 0xff00) |
                    (((RwUInt32) ((RwUInt32) pParam)) & 0xff) << 16;
                tmp = /* NLOOP */ 1l
#ifdef LESSEOPS
                    | /* EOP */ (0l << 15)
#else /* LESSEOPS */
                    | /* EOP */ (1l << 15)
#endif /* LESSEOPS */
                    | /* PRE */ (0l << 46)
                    | /* FLG */ (0l << 58)
                    | /* NREG */ (1l << 60);
                tmp1 = /* A+D */ (0xel << (64 - 64));
                MAKE128(ltmp, tmp1, tmp);
                SWEADDCONTGIFFAST(ltmp, 1);

                tmp = skyFogcol;
                tmp1 = (GS_FOGCOL << (64 - 64));
                MAKE128(ltmp, tmp1, tmp);
                SWEADDCONTFAST(ltmp);
                break;
            }
        case rwRENDERSTATEFOGTYPE:
            {
                /* we now longer use fog tables, however the
                 * renderstate is encoded by them...
                 */

                /* Select the type of fogging to use */
                switch ((RwFogType) ((RwUInt32) pParam))
                {
                    case rwFOGTYPELINEAR:
                        {
                            fogTable = skyLinearFog;
                            break;
                        }
                    case rwFOGTYPEEXPONENTIAL: /* fall through */
                    case rwFOGTYPEEXPONENTIAL2: /* fall trhough */
                    default:
                        {
                            RWRETURN(FALSE);
                        }
                }
                break;
            }
        case rwRENDERSTATEFOGTABLE:
            {
                /* unsupported in PP */
                RWRETURN(FALSE);
            }

        case rwRENDERSTATEALPHAPRIMITIVEBUFFER:
            {
#if 0
                /* Save alpha polygons till last? */
                if (skyBufferAlphaPolys)
                {
                    if (pParam == NULL)
                    {
                        /* clear buffer */
                        replyBufferedAlphaPolys();
                        skyBufferAlphaPolys = 0;
                    }
                }
                else if (pParam)
                {
                    skyBufferAlphaPolys = TRUE;
                }
#else
                /* We can't do buffered polys */
                if (pParam != NULL)
                {
                    RWRETURN(FALSE);
                }
#endif
                break;
            }
        case rwRENDERSTATECULLMODE:
        {
            RWASSERT(((RwUInt32)pParam == rwCULLMODECULLNONE) ||
                     ((RwUInt32)pParam == rwCULLMODECULLBACK) ||
                     ((RwUInt32)pParam == rwCULLMODECULLFRONT));
            if ((RwUInt32)pParam == rwCULLMODECULLNONE)
            {
                skyTransType = (RwCullMode)
                    ( ((RwUInt32)skyTransType) & ~((RwUInt32)TRANSCULL) );
            }
            else
            {
                skyTransType = (RwCullMode)
                    ( ((RwUInt32)skyTransType) |  ((RwUInt32)TRANSCULL) );
            }
            gSkyCullState = (RwCullMode) pParam;
            break;
        }


        case rwRENDERSTATEBORDERCOLOR:
        default:
            {
                /* Not supported or recognised */
                RWRETURN(FALSE);
            }
    }

    RWRETURN(TRUE);
}

/****************************************************************************
 SkyGetRenderState

 On entry   : State
            : Param
 On exit    : TRUE
 */

static              RwBool
SkyGetRenderState(RwRenderState nState, void *pParam)
{
    RWFUNCTION(RWSTRING("SkyGetRenderState"));

    switch (nState)
    {
        case rwRENDERSTATETEXTURERASTER:
            {
                /* Raster being texture with */
                /* 0xffffffff is used as a special value */
                *(RwRaster **) pParam = skyTextureRaster
                                                  != (RwRaster*)0xffffffff
                                        ?skyTextureRaster : NULL;
                break;
            }
        case rwRENDERSTATETEXTUREADDRESS:
            {
                if ((skyClamp_1 & 0x3) == (skyClamp_1 & 0xC))
                {
                    if (skyClamp_1 & 0x3)
                    {
                        *(RwInt32 *) pParam = rwTEXTUREADDRESSCLAMP;
                    }
                    else
                    {
                        *(RwInt32 *) pParam = rwTEXTUREADDRESSWRAP;
                    }
                }
                else
                {
                    *(RwInt32 *) pParam =
                        rwTEXTUREADDRESSNATEXTUREADDRESS;
                }
                break;
            }
        case rwRENDERSTATETEXTUREADDRESSU:
            {
                /* Clamp, wrap, mirror or border */
                if (skyClamp_1 & 0x3)
                {
                    *(RwInt32 *) pParam = rwTEXTUREADDRESSCLAMP;
                }
                else
                {
                    *(RwInt32 *) pParam = rwTEXTUREADDRESSWRAP;
                }
                break;
            }
        case rwRENDERSTATETEXTUREADDRESSV:
            {
                /* Clamp, wrap, mirror or border */
                if (skyClamp_1 & 0xC)
                {
                    *(RwInt32 *) pParam = rwTEXTUREADDRESSCLAMP;
                }
                else
                {
                    *(RwInt32 *) pParam = rwTEXTUREADDRESSWRAP;
                }
                break;
            }
        case rwRENDERSTATETEXTUREPERSPECTIVE:
            {
                /* Perspective correction on/off */
                if (skyPrim_State & 0x100)
                {
                    *(RwBool *) pParam = FALSE;
                }
                else
                {
                    *(RwBool *) pParam = TRUE;
                }
                break;
            }
        case rwRENDERSTATEZTESTENABLE:
            {
                /* Z test on/off */
                *(RwBool *) pParam =
                    ((skyTest_1 & (3 << 17)) == (2 << 17));
                break;
            }
        case rwRENDERSTATESHADEMODE:
            {
                /* Flat or gouraud */
                *(RwShadeMode *) pParam =
                    skyPrim_State & 0x8 ? rwSHADEMODEGOURAUD :
                    rwSHADEMODEFLAT;
                break;
            }
        case rwRENDERSTATEZWRITEENABLE:
            {
                /* Z write on/off */
                *(RwBool *) pParam = !(skyZbuf_1 & (1l << 32));
                break;
            }
        case rwRENDERSTATETEXTUREFILTER:
            {
                /* Point sample, bilinear, trilinear, etc */
                switch (skyTex1_1 & 0x1e0)
                {
                    case 0:
                        {
                            *(RwTextureFilterMode *) pParam =
                                rwFILTERNEAREST;
                            break;
                        }
                    case 0x60:
                        {
                            *(RwTextureFilterMode *) pParam =
                                rwFILTERLINEAR;
                            break;
                        }
                    case 0x80:
                        {
                            *(RwTextureFilterMode *) pParam =
                                rwFILTERMIPNEAREST;
                            break;
                        }
                    case 0x120:
                        {
                            *(RwTextureFilterMode *) pParam =
                                rwFILTERMIPLINEAR;
                            break;
                        }
                    case 0xc0:
                        {
                            *(RwTextureFilterMode *) pParam =
                                rwFILTERLINEARMIPNEAREST;
                            break;
                        }
                    case 0x160:
                        {
                            *(RwTextureFilterMode *) pParam =
                                rwFILTERLINEARMIPLINEAR;
                            break;
                        }
                    default:
                        {
                            RWRETURN(FALSE);
                        }
                }
                break;
            }
        case rwRENDERSTATESRCBLEND:
            {
                /* Src alpha, 1-src alpha, etc */
                /* We refuse to return a value if we are in an intermediate state */
                /* or if the alpha register has been directly altered */
                if ((skyAlpha_1 < 0)
                    || (skyAlpha_1 !=
                        ((long)
                         skyBlendStates[skyDestBlend][skySrcBlend] |
                         ((skySrcBlend == 1
                           && skyDestBlend == 1) ? (0x80l << 32) : 0))))
                {
                    RWRETURN(FALSE);
                }
                switch (skySrcBlend)
                {
                    case 0:
                        {
                            *(RwBlendFunction *) pParam = rwBLENDZERO;
                            break;
                        }
                    case 1:
                        {
                            *(RwBlendFunction *) pParam = rwBLENDONE;
                            break;
                        }
                    case 2:
                        {
                            *(RwBlendFunction *) pParam =
                                rwBLENDSRCALPHA;
                            break;
                        }
                    case 3:
                        {
                            *(RwBlendFunction *) pParam =
                                rwBLENDINVSRCALPHA;
                            break;
                        }
                    case 4:
                        {
                            *(RwBlendFunction *) pParam =
                                rwBLENDDESTALPHA;
                            break;
                        }
                    case 5:
                        {
                            *(RwBlendFunction *) pParam =
                                rwBLENDINVDESTALPHA;
                            break;
                        }
                    default:
                        {
                            RWRETURN(FALSE);
                        }
                }
                break;
            }
        case rwRENDERSTATEDESTBLEND:
            {
                /* src alpha, 1-src alpha, etc */
                if ((skyAlpha_1 < 0)
                    || (skyAlpha_1 !=
                        ((long)
                         skyBlendStates[skyDestBlend][skySrcBlend] |
                         ((skySrcBlend == 1
                           && skyDestBlend == 1) ? (0x80l << 32) : 0))))
                {
                    RWRETURN(FALSE);
                }
                switch (skyDestBlend)
                {
                    case 0:
                        {
                            *(RwBlendFunction *) pParam = rwBLENDZERO;
                            break;
                        }
                    case 1:
                        {
                            *(RwBlendFunction *) pParam = rwBLENDONE;
                            break;
                        }
                    case 2:
                        {
                            *(RwBlendFunction *) pParam =
                                rwBLENDSRCALPHA;
                            break;
                        }
                    case 3:
                        {
                            *(RwBlendFunction *) pParam =
                                rwBLENDINVSRCALPHA;
                            break;
                        }
                    case 4:
                        {
                            *(RwBlendFunction *) pParam =
                                rwBLENDDESTALPHA;
                            break;
                        }
                    case 5:
                        {
                            *(RwBlendFunction *) pParam =
                                rwBLENDINVDESTALPHA;
                            break;
                        }
                    default:
                        {
                            RWRETURN(FALSE);
                        }
                }
                break;
            }
        case rwRENDERSTATEVERTEXALPHAENABLE:
            {
                /* Alpha in vertices enabled? */
                *(RwBool *) pParam = skyVertexAlpha;
                break;
            }
        case rwRENDERSTATEFOGENABLE:
            {
                /* Fogging? (all polygons will be fogged) */
                *(RwBool *) pParam =
                    skyPrim_State & 0x20 ? TRUE : FALSE;
                break;
            }
        case rwRENDERSTATEFOGCOLOR:
            {
                /* Color used for fogging */
                *(RwUInt32 *) pParam = ((skyFogcol) & 0xff) << 16
                    | ((skyFogcol) & 0xff00)
                    | ((skyFogcol) & 0xff0000) >> 16 | 0xff000000;
                break;
            }
        case rwRENDERSTATEFOGTYPE:
            {
                /* Type of fogging to use */
                if (fogTable == skyLinearFog)
                {
                    *(RwFogType *) pParam = rwFOGTYPELINEAR;
                }
                else if (fogTable == skyExpFog)
                {
                    *(RwFogType *) pParam = rwFOGTYPEEXPONENTIAL;
                }
                else if (fogTable == skyExp2Fog)
                {
                    *(RwFogType *) pParam = rwFOGTYPEEXPONENTIAL2;
                }
                else
                {
                    *(RwFogType *) pParam = rwFOGTYPENAFOGTYPE;
                }
                break;
            }
        case rwRENDERSTATEFOGTABLE:
            {
                /* User 256 entry fog table */
                if (fogTable == skyMungedUserFog)
                {
                    *(RwUInt8 **) pParam = skyUserFog;
                }
                else
                {
                    /* We act as if linear/exp/exp2 are hardware modes
                     * and we don't return fog tables for them */
                    RWRETURN(FALSE);
                }
                break;
            }
        case rwRENDERSTATEALPHAPRIMITIVEBUFFER:
            {
                /* Save alpha polygons till last? */
                *(RwBool *) pParam = skyBufferAlphaPolys;
                break;
            }
       case rwRENDERSTATECULLMODE:
            {
                RWASSERT((gSkyCullState == rwCULLMODECULLNONE) ||
                         (gSkyCullState == rwCULLMODECULLBACK) ||
                         (gSkyCullState == rwCULLMODECULLFRONT));
               *(RwUInt32 *)pParam = gSkyCullState;
            }
            break;
        case rwRENDERSTATEBORDERCOLOR:
        default:
            {
                /* Not supported or unrecognised */
                RWRETURN(FALSE);
            }
    }

    RWRETURN(TRUE);
}

/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

                        Plugin functions

 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */

/**
 *
 * \ingroup driversky2
 * \ref RpSkyRenderStateSet
 *  is used to set device specific renderstate
 * for which there is, as yet, no generic equavalent.
 *
 * \param nState The state to set:
 * \li rpSKYRENDERSTATEDITHER  - 0=off, 0x55555555=half, else on
 * \li rpSKYRENDERSTATEALPHA_1 - Direct access to ALPHA_1 with bits
 * 32-39 moved down to 24-31 to allow a singe 32 bit value
 * \li rpSKYRENDERSTATEATEST_1 - Direct access to the bottom 16 bits
 * of TEST_1
 * \li rpSKYRENDERSTATEFARFOGPLANE - Specifies the distance in camera
 * space where the fog will reach full intensity.  Setting this value to less
 * than the far clip plane will result in fog reaching the fog colour before
 * clipping occurs.  Note that using this state means that RenderWare will
 * cease using the far clip plane as the full fog distance.  Changing the
 * far clip plane will no longer change the fog behaviour.  This render
 * state is used for linear fog only.  Note that the fogging calculations
 * perform a linear ramp between the camera fog distance and the far fog
 * plane.  We apologise that the fog distance is a property
 * of a camera, and the far fog distance is a render state.
 * \li rpSKYRENDERSTATEMAXMIPLEVELS - Specifies the maximum number of mip
 * levels that rasters will be created with. Changing this value does not
 * affect existing rasters. Mip levels will only be created if they are
 * greater than or equal to 8 texels in both dimensions, so you are not
 * guaranteed to get the maximum number of mip levels for all rasters.
 * The maximum allowed value for rpSKYRENDERSTATEMAXMIPLEVELS is 7.
 *
 * \param pParam A state specific argument, cast to be a void*
 *
 * \return Returns TRUE if successful or FALSE if there is an error.
 *
 * \see RpSkyRenderStateGet
 */
RwBool
RpSkyRenderStateSet(RpSkyRenderState nState, void *pParam)
{
    unsigned long tmp, tmp1;
    u_long128 ltmp;
    RWAPIFUNCTION(RWSTRING("RpSkyRenderStateSet"));

    switch (nState)
    {
        case rpSKYRENDERSTATEDITHER:
            {
                /* Mustn't do this in 32 bit, or if engine isn't started */
                if ((videoModes[currentMode].depth == 32) || (!engineStarted))
                {
                    RWRETURN(FALSE);
                }
                if (sweOpenLocalPkt(SWE_LPS_CONT, -2))
                {
                    tmp = /* NLOOP */ 1l
                        | /* EOP */ (1l<<15)
                        | /* PRE */ (0l<<46)
                        | /* FLG */ (0l<<58)
                        | /* NREG */ (1l<<60);
                    tmp1 = /* A+D */ (0xel<<(64-64));
                    MAKE128(ltmp, tmp1, tmp);
                    SWEADDCONTGIFFAST(ltmp, 1);

                    if ((RwUInt32)pParam == 0)
                    {
                        tmp = 0;
                        skyDitherState = 0;
                    }
                    else
                        if ((RwUInt32)pParam == 0x55555555)
                        {
                            tmp = (DIMX0 << 0)  | /* dimx(0,0) = 0 */
                                (DIMX3 << 4)  | /* dimx(0,1) = 3 */
                                (DIMX0 << 8)  | /* dimx(0,2) = 0 */
                                (DIMX3 << 12) | /* dimx(0,3) = 3 */
                                (DIMX2 << 16) | /* dimx(1,0) = 2 */
                                (DIMX1 << 20) | /* dimx(1,1) = 1 */
                                (DIMX2 << 24) | /* dimx(1,2) = 2 */
                                (DIMX1 << 28) | /* dimx(1,3) = 1 */
                                (DIMX0 << 32) | /* dimx(2,0) = 0 */
                                (DIMX3 << 36) | /* dimx(2,1) = 3 */
                                (DIMX0 << 40) | /* dimx(2,2) = 0 */
                                (DIMX3 << 44) | /* dimx(2,3) = 3 */
                                (DIMX2 << 48) | /* dimx(3,0) = 2 */
                                (DIMX1 << 52) | /* dimx(3,1) = 1 */
                                (DIMX2 << 56) | /* dimx(3,2) = 2 */
                                (DIMX1 << 60);  /* dimx(3,3) = 1 */
                            skyDitherState = 0x55555555;
                        }
                        else
                        {
                            tmp = (DIMX_4 << 0)  | /* dimx(0,0) = -4 */
                                (DIMX2 << 4)   | /* dimx(0,1) = 2 */
                                (DIMX_3 << 8)  | /* dimx(0,2) = -3 */
                                (DIMX3 << 12)  | /* dimx(0,3) = 3 */
                                (DIMX0 << 16)  | /* dimx(1,0) = 0 */
                                (DIMX_2 << 20) | /* dimx(1,1) = -2 */
                                (DIMX1 << 24)  | /* dimx(1,2) = 1 */
                                (DIMX_1 << 28) | /* dimx(1,3) = -1 */
                                (DIMX_3 << 32) | /* dimx(2,0) = -3 */
                                (DIMX3 << 36)  | /* dimx(2,1) = 3 */
                                (DIMX_4 << 40) | /* dimx(2,2) = -4 */
                                (DIMX2 << 44)  | /* dimx(2,3) = 2 */
                                (DIMX1 << 48)  | /* dimx(3,0) = 1 */
                                (DIMX_1 << 52) | /* dimx(3,1) = -1 */
                                (DIMX0 << 56)  | /* dimx(3,2) = 0 */
                                (DIMX_2 << 60);  /* dimx(3,3) = -2 */
                            skyDitherState = 1;
                        }
                    MAKE128(ltmp, GS_DIMX, tmp);
                    SWEADDCONTFAST(ltmp);
                    break;
                }
                else
                {
                    RWRETURN(FALSE);
                }
            }
        case rpSKYRENDERSTATEALPHA_1:
            {
                if (sweOpenLocalPkt(SWE_LPS_CONT, -2))
                {
                    tmp = /* NLOOP */ 1l
                        | /* EOP */ (1l<<15)
                        | /* PRE */ (0l<<46)
                        | /* FLG */ (0l<<58)
                        | /* NREG */ (1l<<60);
                    tmp1 = /* A+D */ (0xel<<(64-64));
                    MAKE128(ltmp, tmp1, tmp);
                    SWEADDCONTGIFFAST(ltmp, 1);

                    skyAlpha_1 = ((unsigned long)(unsigned int)pParam & 0xff)
                        | ((unsigned long)((unsigned int)pParam
                                           & 0xff000000l)<<8);
                    MAKE128(ltmp, GS_ALPHA_1, skyAlpha_1);
                    SWEADDCONTFAST(ltmp);
                    break;
                }
                else
                {
                    RWRETURN(FALSE);
                }
            }
        case rpSKYRENDERSTATEATEST_1:
            {
                if (sweOpenLocalPkt(SWE_LPS_CONT, -2))
                {
                    tmp = /* NLOOP */ 1l
                        | /* EOP */ (1l<<15)
                        | /* PRE */ (0l<<46)
                        | /* FLG */ (0l<<58)
                        | /* NREG */ (1l<<60);
                    tmp1 = /* A+D */ (0xel<<(64-64));
                    MAKE128(ltmp, tmp1, tmp);
                    SWEADDCONTGIFFAST(ltmp, 1);

                    skyTest_1 &= ~0xffffl;
                    skyTest_1 |= (unsigned int)pParam & 0xffff;
                    MAKE128(ltmp, GS_TEST_1, skyTest_1);
                    SWEADDCONTFAST(ltmp);
                    break;
                }
                else
                {
                    RWRETURN(FALSE);
                }
            }


        case rpSKYRENDERSTATEFARFOGPLANE:
            {
                /* This is a one way process - no going back */
                useFarClip  = FALSE;
                farFogPlane = *((RwReal *)(pParam));
            }
            break;

        case rpSKYRENDERSTATEMAXMIPLEVELS:
            {
                /* Levels in a raster go from 0 to n-1.
                 * Levels are only created if >= 8x8 texels.
                 * NumLevels is stored in rasterExtension->maxMipLevel
                 * (numlevels is: (rasExt->maxMipLevel >> 2) + 1) */
                RwInt32 maxMipLevels;

                /* Must remain (> 0) and (<= 7) */
                maxMipLevels = *((RwInt32 *)(pParam));
                if ((maxMipLevels > 0) && (maxMipLevels <= 7))
                {
                    skyMaxMipLevels = maxMipLevels;
                }
                else
                {
                    RWASSERT(maxMipLevels > 0);
                    RWASSERT(maxMipLevels <= 7);
                    RWRETURN(FALSE);
                }
            }
            break;

        default:
            {
                RWRETURN(FALSE);
            }
    }
    RWRETURN(TRUE);
}

/**
 *
 * \ingroup driversky2
 * \ref RpSkyRenderStateGet
 *  is used to get device specific renderstate
 * for which there is, as yet, no generic equavalent.  Because in many cases
 * setting these values changes the default behaviour of RenderWare it is
 * recommended that you contact rw3-support to discuss any implications
 * that might arise from their use.
 *
 * \param nState The state to get:
 * \li rpSKYRENDERSTATEDITHER  - 0=off, 0x55555555=half, else on
 * \li rpSKYRENDERSTATEALPHA_1 - Direct access to ALPHA_1 with bits
 * 32-39 moved down to 24-31 to allow a singe 32 bit value
 * \li rpSKYRENDERSTATEATEST_1 - Direct access to the bottom 16 bits
 * of TEST_1
 * \li rpSKYRENDERSTATEFARFOGPLANE - Retreives the value of the
 * far fogging distance.  If the corresponding Set function has not
 * been called, then the pParam contents will be set to 0.0f to indicate
 * that the default fogging behaviour (using the far clip plane as the
 * value for the far fog distance) is selected.
 * \li rpSKYRENDERSTATEMAXMIPLEVELS - Retreives the maximum number of mip
 * levels that rasters will be created with. Mip levels are only be created
 * if they are greater than or equal to 8 texels in both dimensions, so you
 * are not guaranteed to get the maximum number of mip levels for all rasters.
 *
 * \param pParam A state specific argument, cast to be a void*
 *
 * \return Returns TRUE if successful or FALSE if there is an error.
 *
 * \see RpSkyRenderStateSet
 */
RwBool
RpSkyRenderStateGet(RpSkyRenderState nState, void *pParam)
{
    RWAPIFUNCTION(RWSTRING("RpSkyRenderStateGet"));

    switch (nState)
    {
        case rpSKYRENDERSTATEDITHER:
            {
                /* No meaning */
                if ((videoModes[currentMode].depth == 32) || (!engineStarted))
                {
                    RWRETURN(FALSE);
                }

                *(RwUInt32*)pParam = skyDitherState;
                break;
            }
        case rpSKYRENDERSTATEALPHA_1:
            {
                *(RwUInt32*)pParam = (RwUInt32)((skyAlpha_1 & 0xff)
                                                | ((skyAlpha_1 >> 8) & 0xff000000l));
                break;
            }
        case rpSKYRENDERSTATEATEST_1:
            {
                *(RwUInt32*)pParam = (RwUInt32)skyTest_1;
                break;
            }
        case rpSKYRENDERSTATEFARFOGPLANE:
            {
                if (useFarClip)
                {
                    /* Maybe this should return the current camera far clip? */
                    *(RwReal*)pParam = 0.0f;
                }
                else
                {
                    *(RwReal*)pParam = farFogPlane;
                }
                break;
            }
        case rpSKYRENDERSTATEMAXMIPLEVELS:
            {
                *(RwInt32*)pParam = skyMaxMipLevels;
                break;
            }
        default:
            {
                RWRETURN(FALSE);
            }
    }
    RWRETURN(TRUE);
}

/****************************************************************************
 RpSkySelectDeepZBuffer

 On entry   : val
 On exit    : TRUE on success
 */
/**
 *
 * \ingroup driversky2
 * \ref RpSkySelectDeepZBuffer
 *  is used to select the default Z buffer
 * depth that will be created. It must be called before \ref RwEngineStart.
 * By default a 16 bit Z-buffer will be used to conserve VRAM.  Note that
 * when the Z-buffer raster is created, the application should pass in
 * a depth of zero to request the default Z-buffer depth.
 *
 * \param val TRUE for a deep Z-buffer.
 *
 * \return Returns TRUE if successful or FALSE if there is an error.
 */
RwBool
RpSkySelectDeepZBuffer(RwBool val)
{
    RWAPIFUNCTION(RWSTRING("RpSkySelectDeepZBuffer"));
    if (!engineStarted)
    {
        if (val)
        {
        /*  skyVU1Transforms[0] = &vu1nfzStTrans;*/
            zBufferDepth = 32;
            RWSRCGLOBAL(dOpenDevice).zBufferNear = 16777215.0f;
        }
        else
        {
         /* skyVU1Transforms[0] = &vu1nfStTrans;*/
            zBufferDepth = 16;
            RWSRCGLOBAL(dOpenDevice).zBufferNear = 65535.0f;
        }
        RWRETURN(TRUE);
    }
    RWRETURN(FALSE);
}

/****************************************************************************
 RpSkySelectTrueTSClipper

 On entry   : val
 On exit    : TRUE on success
 */
/**
 *
 * \ingroup driversky2
 * \ref RpSkySelectTrueTSClipper
 *  is used to select the style of
 * clipping used on tristripped objects. By default tristripped objects
 * are triangle culled, which is quick, but requires that the object
 * be built with triangles of a bounded size. This function can be used
 * to set on/off true clipping of tristripped objects, allowing clip planes
 * to be pushed smoothly through objects at the expense of performance.
 * (Trilist objects are set to use true clipped) This function can be called
 * when ever a change is needed.  Bear in mind that objects which are fully
 * inside the camera frustum will always be drawn with a pipeline that
 * performs no clipping.  The clipping pipeline is considerably slower
 * than the non-clipping pipelines.
 *
 * \param val TRUE for a true clipper
 *
 * \return Returns TRUE if successful or FALSE if there is an error.
 */
RwBool
RpSkySelectTrueTSClipper(RwBool val)
{
    RWAPIFUNCTION(RWSTRING("RpSkySelectTrueTSClipper"));
   /* if (!engineStarted)
    {
        if (val)
        {
            skyVU1Transforms[TRANSPER|TRANSSTRIP|TRANSCLIP|TRANSNFOG|TRANSTRI] = &vu1StclipTrans;
            skyVU1Transforms[TRANSPER|TRANSSTRIP|TRANSCLIP|TRANSFOG|TRANSTRI] = &vu1StclipTransf;

        }
        else
        {
            skyVU1Transforms[TRANSPER|TRANSSTRIP|TRANSCLIP|TRANSNFOG|TRANSTRI] = &vu1StcTrans;
            skyVU1Transforms[TRANSPER|TRANSSTRIP|TRANSCLIP|TRANSFOG|TRANSTRI] = &vu1StcTrans;
        }
        RWRETURN(TRUE);
    }
    RWRETURN(FALSE);

    */

    skyTSClipperMode = val;

    RWRETURN(TRUE);
}

/****************************************************************************
 RpSkyGetTrueTSClipper

 On entry   : none
 On exit    : TriStrip clipper state
 */
/**
 *
 * \ingroup driversky2
 * \ref RpSkyGetTrueTSClipper
 *  is used to retrieve the style of
 *  clipping used on tristripped objects.
 *
 * \return Returns TRUE if true clipping is on or FALSE if it's off.
 *
 * \see RpSkySelectTrueTSClipper
 */
RwBool
RpSkyGetTrueTSClipper(void)
{
    RWAPIFUNCTION(RWSTRING("RpSkyGetTrueTSClipper"));

    RWRETURN(skyTSClipperMode);
}


/****************************************************************************
 RpSkySelectTrueTLClipper

 On entry   : val
 On exit    : TRUE on success
 */
/**
 *
 * \ingroup driversky2
 * \ref RpSkySelectTrueTLClipper
 *  is used to select the style of
 * clipping used on triangle list objects. By default tri-list objects
 * are triangle clipped, which is slow,  but do not requires that the object
 * be built with triangles of a bounded size. This function can be used
 * to set on/off true clipping of tri-list objects, allowing clip planes
 * to be pushed smoothly through objects at the expense of performance.
 * This function can be called when needed to set dynamicaly the clipping
 * mode of a specific object.  Bear in mind that objects which are fully
 * inside the camera frustum will always be drawn with a pipeline that
 * performs no clipping.  The clipping pipeline is considerably slower
 * than the non-clipping pipelines.
 *
 * \param val TRUE for a true clipper
 *
 * \return Returns TRUE if successful or FALSE if there is an error.
 */
RwBool
RpSkySelectTrueTLClipper(RwBool val)
{
    RWAPIFUNCTION(RWSTRING("RpSkySelectTrueTLClipper"));
   /* if (!engineStarted)
    {
        if (val)
        {
            skyVU1Transforms[TRANSPER|TRANSSTRIP|TRANSCLIP|TRANSNFOG|TRANSTRI] = &vu1StclipTrans;
            skyVU1Transforms[TRANSPER|TRANSSTRIP|TRANSCLIP|TRANSFOG|TRANSTRI] = &vu1StclipTransf;

        }
        else
        {
            skyVU1Transforms[TRANSPER|TRANSSTRIP|TRANSCLIP|TRANSNFOG|TRANSTRI] = &vu1StcTrans;
            skyVU1Transforms[TRANSPER|TRANSSTRIP|TRANSCLIP|TRANSFOG|TRANSTRI] = &vu1StcTrans;
        }
        RWRETURN(TRUE);
    }
    RWRETURN(FALSE);

    */

    skyTLClipperMode = val;

    RWRETURN(TRUE);
}

/****************************************************************************
 RpSkyGetTrueTLClipper

 On entry   : none
 On exit    : TriList clipper state
 */
/**
 *
 * \ingroup driversky2
 * \ref RpSkyGetTrueTLClipper
 *  is used to retrieve the style of
 * clipping used on triangle list objects.
 *
 * \return Returns TRUE if true clipping is on or FALSE if it's off.
 *
 * \see RpSkySelectTrueTLClipper
 */
RwBool
RpSkyGetTrueTLClipper(void)
{
    RWAPIFUNCTION(RWSTRING("RpSkyGetTrueTLClipper"));

    RWRETURN(skyTLClipperMode);
}


/****************************************************************************
 RpSkyTextureSetDefaultMipmapK

 On entry   : val
 On exit    : Value actually set
 */
/**
 *
 * \ingroup driversky2
 * \ref RpSkyTextureSetDefaultMipmapK
 *  is used to set the default "K"
 * value that will be used when textures are created. This controls the
 * way mipmap levels are selected by the GS. Except in the unusual situation
 * where all textures have been sized to get an even texture density across
 * the scene, it is better to set each texture's "K" directly using
 * \ref RpSkyTextureSetMipmapK.  A toolkit to help calculate suitable "K"
 * values is available.
 * "K" specifies the distance a polygon needs to be from the camera such
 * that the pixel to texel ratio when rendered is 1:1 assuming the polygon
 * is flat on the screen.  "L" specifies the angle of inclination between\
 * the polygon and the camera viewwindow, it's a 2-bit value where 0 says
 * the angle is 0 degrees and 3 says it's 90 degrees.
 * Essentially they allow you to work out the pixel:texel ratio when
 * rendering a single pixel if you know it's distance from the camera,
 * i.e the GS can get the mipmap level purely from the value in Z.  Further
 * information is available in the GS User's Manual
 *
 * \param val floating point "K" value.
 *
 * \return K value actually asigned (Limited precision is available)
 *
 * \see RpSkyTextureSetDefaultMipmapL
 * \see RpSkyTextureGetDefaultMipmapK
 * \see RpSkyTextureGetDefaultMipmapL
 * \see RpSkyTextureSetMipmapK
 * \see RpSkyTextureSetMipmapL
 * \see RpSkyTextureGetMipmapK
 * \see RpSkyTextureGetMipmapL
 */
RwReal
RpSkyTextureSetDefaultMipmapK(RwReal val)
{
    RwInt32 i;
    RWAPIFUNCTION(RWSTRING("RpSkyTextureSetDefaultMipmapK"));

    i = (RwInt32)(val*16.0f);
    if (i < -2048)
    {
        i = -2048;
    }
    else if (i > 2047)
    {
        i = 2047;
    }
    /* Mask off bits */
    skyDefaultMipmapKL &= ~0xfff;
    skyDefaultMipmapKL |= (RwUInt32)i & 0xfff;
    RWRETURN((RwReal)i * 0.0625f);
}

/****************************************************************************
 RpSkyTextureSetDefaultMipmapL

 On entry   : val
 On exit    : TRUE on success
 */
/**
 *
 * \ingroup driversky2
 * \ref RpSkyTextureSetDefaultMipmapL
 *  is used to set the default "L"
 * value that will be used when textures are created. This controls the
 * way mipmap levels are selected by the GS. It will almost alway be better
 * to set this per texture using \ref RpSkyTextureSetMipmapL
 * See \ref RpSkyTextureSetDefaultMipmapK for further information.
 *
 * \param val "L" value.
 *
 * \return L value actually asigned (Limited range is available)
 *
 * \see RpSkyTextureSetDefaultMipmapK
 * \see RpSkyTextureGetDefaultMipmapK
 * \see RpSkyTextureGetDefaultMipmapL
 * \see RpSkyTextureSetMipmapK
 * \see RpSkyTextureSetMipmapL
 * \see RpSkyTextureGetMipmapK
 * \see RpSkyTextureGetMipmapL
 */
RwUInt32
RpSkyTextureSetDefaultMipmapL(RwUInt32 val)
{
    RWAPIFUNCTION(RWSTRING("RpSkyTextureSetDefaultMipmapL"));

    if (val > 3)
    {
        val = 3;
    }
    skyDefaultMipmapKL &= ~(0x03<<12);
    skyDefaultMipmapKL |= (val<<12);
    RWRETURN(val);
}

/****************************************************************************
 RpSkyTextureGetDefaultMipmapK

 On entry   :
 On exit    : K
 */
/**
 *
 * \ingroup driversky2
 * \ref RpSkyTextureGetDefaultMipmapK
 *  is used to get the current
 * default "K" value used at texture creation.
 * See \ref RpSkyTextureSetDefaultMipmapK for further information.
 *
 * \return K value currently used by default
 *
 * \see RpSkyTextureSetDefaultMipmapK
 * \see RpSkyTextureSetDefaultMipmapL
 * \see RpSkyTextureGetDefaultMipmapL
 * \see RpSkyTextureSetMipmapK
 * \see RpSkyTextureSetMipmapL
 * \see RpSkyTextureGetMipmapK
 * \see RpSkyTextureGetMipmapL
 */
RwReal
RpSkyTextureGetDefaultMipmapK(void)
{
    RwInt32 i;
    RWAPIFUNCTION(RWSTRING("RpSkyTextureGetDefaultMipmapK"));

    i = skyDefaultMipmapKL & 0xfff;
    if (i & 0x800)
    {
        i |= ~0xfff;
    }
    RWRETURN((RwReal)i * 0.0625f);
}

/****************************************************************************
 RpSkyTextureGetDefaultMipmapL

 On entry   :
 On exit    : L
 */
/**
 *
 * \ingroup driversky2
 * \ref RpSkyTextureGetDefaultMipmapL
 *  is used to get the current
 * default "L" value used at texture creation.
 * See \ref RpSkyTextureSetDefaultMipmapK for further information.
 *
 * \return L value currently used by default
 *
 * \see RpSkyTextureSetDefaultMipmapK
 * \see RpSkyTextureSetDefaultMipmapL
 * \see RpSkyTextureGetDefaultMipmapK
 * \see RpSkyTextureSetMipmapK
 * \see RpSkyTextureSetMipmapL
 * \see RpSkyTextureGetMipmapK
 * \see RpSkyTextureGetMipmapL
 */
RwUInt32
RpSkyTextureGetDefaultMipmapL(void)
{
    RwUInt32 i;
    RWAPIFUNCTION(RWSTRING("RpSkyTextureGetDefaultMipmapL"));

    i = (skyDefaultMipmapKL & 0x3000) >> 12;
    RWRETURN(i);
}

/****************************************************************************
 RpSkyTextureSetMipmapK

 On entry   : tex
 On exit    : TRUE on success
 */
/**
 *
 * \ingroup driversky2
 * \ref RpSkyTextureSetMipmapK
 *  is used to set the "K" value that will
 * be used when a particular texture is drawn. This controls the way mipmap
 * levels are selected by the GS.
 * See \ref RpSkyTextureSetDefaultMipmapK for further information.
 *
 * \param tex texture to update.
 * \param val floating point K value.
 *
 * \return The texture, otherwise NULL on failure
 *
 * \see RpSkyTextureSetDefaultMipmapK
 * \see RpSkyTextureSetDefaultMipmapL
 * \see RpSkyTextureGetDefaultMipmapK
 * \see RpSkyTextureGetDefaultMipmapL
 * \see RpSkyTextureSetMipmapL
 * \see RpSkyTextureGetMipmapK
 * \see RpSkyTextureGetMipmapL
 */
RwTexture*
RpSkyTextureSetMipmapK(RwTexture *tex, RwReal val)
{
    RwInt32 i;
    RWAPIFUNCTION(RWSTRING("RpSkyTextureSetMipmapK"));

    if ((tex) && (tex->raster))
    {
        i = (RwInt32)(val*16.0f);
        if (i < -2048)
        {
            i = -2048;
        }
        else if (i > 2047)
        {
            i = 2047;
        }
        RASTEREXTFROMRASTER(tex->raster)->mipmapKL &= ~0xfff;
        RASTEREXTFROMRASTER(tex->raster)->mipmapKL |= (RwUInt16)i & 0xfff;
        RWRETURN(tex);
    }
    RWRETURN((RwTexture *)NULL);
}

/****************************************************************************
 RpSkyTextureSetMipmapL

 On entry   : tex
 On exit    : TRUE on success
 */
/**
 *
 * \ingroup driversky2
 * \ref RpSkyTextureSetMipmapL
 *  is used to set the "L" value that will
 * be used when a particular texture is drawn. This controls the way mipmap
 * levels are selected by the GS.
 * See \ref RpSkyTextureSetDefaultMipmapK for further information.
 *
 * \param tex texture to update.
 * \param val L value.
 *
 * \return The texture, otherwise NULL on failure
 *
 * \see RpSkyTextureSetDefaultMipmapK
 * \see RpSkyTextureSetDefaultMipmapL
 * \see RpSkyTextureGetDefaultMipmapK
 * \see RpSkyTextureGetDefaultMipmapL
 * \see RpSkyTextureSetMipmapK
 * \see RpSkyTextureGetMipmapK
 * \see RpSkyTextureGetMipmapL
 */
RwTexture*
RpSkyTextureSetMipmapL(RwTexture *tex, RwUInt32 val)
{
    RWAPIFUNCTION(RWSTRING("RpSkyTextureSetMipmapL"));

    if ((tex) && (tex->raster))
    {
        if (val > 3)
        {
            val = 3;
        }
        RASTEREXTFROMRASTER(tex->raster)->mipmapKL &= ~0x3000;
        RASTEREXTFROMRASTER(tex->raster)->mipmapKL |= (RwUInt16)val << 12;
        RWRETURN(tex);
    }
    RWRETURN((RwTexture *)NULL);
}

/****************************************************************************
 RpSkyTextureGetMipmapK

 On entry   : tex
 On exit    : K
 */
/**
 *
 * \ingroup driversky2
 * \ref RpSkyTextureGetMipmapK
 *  is used to set the "K" value that will
 * be used when a particular texture is drawn. This controls the way mipmap
 * levels are selected by the GS.
 * See \ref RpSkyTextureSetDefaultMipmapK for further information.
 *
 * \param tex texture being queried
 *
 * \return The texture's K value
 *
 * \see RpSkyTextureSetDefaultMipmapK
 * \see RpSkyTextureSetDefaultMipmapL
 * \see RpSkyTextureGetDefaultMipmapK
 * \see RpSkyTextureGetDefaultMipmapL
 * \see RpSkyTextureSetMipmapK
 * \see RpSkyTextureSetMipmapL
 * \see RpSkyTextureGetMipmapL
 */
RwReal
RpSkyTextureGetMipmapK(RwTexture *tex)
{
    RWAPIFUNCTION(RWSTRING("RpSkyTextureGetMipmapK"));
    if ((tex) && (tex->raster))
    {
        RwInt32 i;

        i = RASTEREXTFROMRASTER(tex->raster)->mipmapKL & 0xfff;
        if (i & 0x800)
        {
            i |= ~0xfff;
        }
        RWRETURN((RwReal)i * 0.0625f);
    }
    RWRETURN((RwReal)0.0f);
}

/****************************************************************************
 RpSkyTextureGetMipmapL

 On entry   : tex
 On exit    : L
 */
/**
 *
 * \ingroup driversky2
 * \ref RpSkyTextureGetMipmapL
 *  is used to set the "L" value that will
 * be used when a particular texture is drawn. This controls the way mipmap
 * levels are selected by the GS.
 * See \ref RpSkyTextureSetDefaultMipmapK for further information.
 *
 * \param tex texture being queried
 *
 * \return The texture's L value
 *
 * \see RpSkyTextureSetDefaultMipmapK
 * \see RpSkyTextureSetDefaultMipmapL
 * \see RpSkyTextureGetDefaultMipmapK
 * \see RpSkyTextureGetDefaultMipmapL
 * \see RpSkyTextureSetMipmapK
 * \see RpSkyTextureSetMipmapL
 * \see RpSkyTextureGetMipmapK
 */
RwUInt32
RpSkyTextureGetMipmapL(RwTexture *tex)
{
    RWAPIFUNCTION(RWSTRING("RpSkyTextureGetMipmapL"));
    if ((tex) && (tex->raster))
    {
        RwUInt32 i;

        i = (RASTEREXTFROMRASTER(tex->raster)->mipmapKL & 0x3000)>>12;
        RWRETURN(i);
    }
    RWRETURN(0);
}

/****************************************************************************
 skyReadTexCB

 On entry   :
 On exit    :
 */
static RwStream *
skyReadTexCB(RwStream *stream, RwInt32 len, void *obj,
              RwInt32 __RWUNUSED__ off,
              RwInt32 __RWUNUSED__ sizeInObj)
{
    RwTexture *tex = (RwTexture*)obj;
    RwInt32 val;
    RWFUNCTION(RWSTRING("skyReadTexCB"));

    if ((len == 4) && (tex) && (tex->raster))
    {
        if (RwStreamReadInt(stream, &val, sizeof(RwInt32)))
        {
            RASTEREXTFROMRASTER(tex->raster)->mipmapKL = (RwUInt16)val;
            RWRETURN(stream);
        }
        RWRETURN((RwStream *)NULL);
    }
    RWRETURN((RwStream *)NULL);
}

/****************************************************************************
 skyWriteTexCB

 On entry   :
 On exit    :
 */
static RwStream *
skyWriteTexCB(RwStream *stream, RwInt32 __RWUNUSED__ len, const void *obj,
               RwInt32 __RWUNUSED__ off, RwInt32 __RWUNUSED__ sizeInObj)
{
    const RwTexture *tex = (const RwTexture*)obj;
    RwInt32 val;
    RWFUNCTION(RWSTRING("skyWriteTexCB"));

    if ((tex) && (tex->raster))
    {
        val = (RwInt32)(RASTEREXTFROMRASTER(tex->raster)->mipmapKL);
        if (RwStreamWriteInt(stream, &val, sizeof(RwInt32)))
        {
            RWRETURN(stream);
        }
        RWRETURN((RwStream *)NULL);
    }
    RWRETURN((RwStream *)NULL);
}

/****************************************************************************
 skyGetSizeTexCB

 On entry   :
 On exit    : 4
 */
static RwInt32
skyGetSizeTexCB(const void * __RWUNUSED__ obj, RwInt32 __RWUNUSED__ off,
                RwInt32 __RWUNUSED__ size)
{
    RWFUNCTION(RWSTRING("skyGetSizeTexCB"));
    RWRETURN(4);
}

/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

                        Standard functions

 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */

/****************************************************************************
 SkyCameraBeginUpdate

 On entry   : NULL
            : camera pointer (IN)
            : 0
 On exit    : TRUE on success
 */
static RwBool
SkyCameraBeginUpdate(void * __RWUNUSED__ pOut, void *pCam,
                     RwInt32 __RWUNUSED__ nData)
{
    unsigned       long tmp, tmp1;
    u_long128      ltmp = 0;
    RwCamera       *cam = (RwCamera*)pCam;
    _SkyCameraExt  *camExt = CAMERAEXTFROMCAMERA(cam);
    RwV3d          up, right, upRight, upLeft, vTmp, vTmp2;
    RwV3d          *frustVerts;
    RwFrustumPlane *frustPlanes;
    RwReal         scaleUp, scaleRight;

    RWFUNCTION(RWSTRING("SkyCameraBeginUpdate"));

#ifdef MORECPU
    if (sweNumFlipsInQueue > sweMaxFlips+1)
    {
        _sweWaitQueue();
    }
#endif /* MORECPU */

#ifdef GSB
    /* if (skyGCVTValue % skyNumSeqFrms == (unsigned)skyRenderSlot) */
    /* { */
    _sweAddPkt(NULL, SWE_PKT_WAIT_DRAWNEXT);
    /* } */
#endif /* GSB */

    /* */
    if (!skyRenderTimeInProgress)
    {
        skyRenderTime -= _sweReadTimer();
        skyRenderTimeInProgress = 1;
    }

    /* We consider this to be the begining of a frame */
#ifdef LOGO
    if (!logoLoaded)
    {
        logoTexture = RwTextureRead("rw_logo", "rw_logom");
        /* The logo load may have failed, but we want to stop load attempts */
        logoLoaded = TRUE;
        logoScaleX = (float)videoModes[currentMode].width/((float)videoModes[currentMode].height*1.45f);
        if (!logoTexture)
        {
            scePrintf("Failed to load rw_logo\n");
        }
    }
#endif /* LOGO */

    if ((cam->frameBuffer->cType & rwRASTERTYPEMASK) == rwRASTERTYPECAMERATEXTURE)
    {
        _SkyRasterExt *reExt = RASTEREXTFROMRASTER(cam->frameBuffer);

        /* If the texture is not locked in cache we fail */
        if (reExt->bLocked==FALSE)
        {
            RWMESSAGE((RWSTRING("error - rwRASTERTYPECAMERATEXTURE must be locked in cache")));
            RWRETURN(FALSE);
        }
        /* We build a fake frame_1 to permit rendering */
        skyFrame_1 = ((reExt->lsb>>5)&0x1ff) | ((reExt->lsb<<2)&0x3f0000)
            | ((reExt->lsb<<4)&0x3f000000);
#if defined(GSB) && defined(GSPLUS)
        skyFrame_2 = (reExt->lsb3 >> 5)&0xfff;
#endif /* defined(GSB) && defined(GSPLUS) */
    }
    else
    {
        if (skyFrameBit & 0x1)
        {
            skyFrame_1 = *(long*)&sweDb.draw01.frame1;
#if defined(GSB) && defined(GSPLUS)
            skyFrame_2 = *(long*)&sweDb.draw01.eframe;
#endif /* defined(GSB) && defined(GSPLUS) */
        }
        else
        {
            skyFrame_1 = *(long*)&sweDb.draw11.frame1;
#if defined(GSB) && defined(GSPLUS)
            skyFrame_2 = *(long*)&sweDb.draw11.eframe;
#endif /* defined(GSB) && defined(GSPLUS) */
        }
    }

#if !defined NOGARBAGECOLLECT
    _sweGarbageCollectChain();
#endif    

#if defined(GSB) && defined(GSPLUS)
    sweOpenLocalPkt(SWE_LPS_CONT, -11);

    tmp = /* NLOOP */ 10l
#ifdef LESSEOPS
        | /* EOP */ (0l<<15)
#else /* LESSEOPS */
        | /* EOP */ (1l<<15)
#endif /* LESSEOPS */
        | /* PRE */ (0l<<46)
        | /* FLG */ (0l<<58)
        | /* NREG */(1l<<60);
    tmp1 = /* A+D */ (0xel<<(64-64));
    MAKE128(ltmp, tmp1, tmp);
    SWEADDCONTGIFFAST(ltmp, 8);
#else /* defined(GSB) && defined(GSPLUS) */
    sweOpenLocalPkt(SWE_LPS_CONT, -9);

    tmp = /* NLOOP */ 8l
#ifdef LESSEOPS
        | /* EOP */ (0l<<15)
#else /* LESSEOPS */
        | /* EOP */ (1l<<15)
#endif /* LESSEOPS */
        | /* PRE */ (0l<<46)
        | /* FLG */ (0l<<58)
        | /* NREG */(1l<<60);
    tmp1 = /* A+D */ (0xel<<(64-64));
    MAKE128(ltmp, tmp1, tmp);
    SWEADDCONTGIFFAST(ltmp, 6);
#endif /* defined(GSB) && defined(GSPLUS) */

#if (defined(__MWERKS__))
    /*
     * Code Warrior cannot inline SWE_HALF_OFFSET()
     * so use SWE_HALF_OFFSET_MACRO() instead.
     */
    if (((cam->frameBuffer->cType & rwRASTERTYPEMASK)
         == rwRASTERTYPECAMERATEXTURE))
    {
        skyXyoffset_1 = 0;
    }
    else
    {
        SWE_HALF_OFFSET_MACRO(skyXyoffset_1);
    }

    /* Grab the offset if there is one */
    if ((cam->frameBuffer->cType & rwRASTERTYPEMASK)==
        rwRASTERTYPECAMERATEXTURE)
    {
        skyXyoffset_1 =
            ( skyXyoffset_1<<35) |
//This possibly a bug, should be cam->frameBuffer->parent->width not cam->frameBuffer->width
//just no-one's subrastered a cameratexture yet :o)
            (((cam->frameBuffer->nOffsetX & 0xfffl)+
              (2048-(cam->frameBuffer->width>>1)))<<4) |
            (((cam->frameBuffer->nOffsetY & 0xfffl)+
              (2048-(cam->frameBuffer->height>>1)))<<36);
    }
    else
    {
#ifdef GSB
        skyXyoffset_1  =
            ( skyXyoffset_1 << 35 ) |
            (( skyXOff+skyGsmOffsetX)<<4) |
            (( skyYOff+skyGsmOffsetY)<<36);
#else /* GSB */
        skyXyoffset_1 =
            ( skyXyoffset_1 << 35 ) |
            ((skyXOff)<<4) |
            ((skyYOff)<<36);
#endif /* GSB */
    }

#else /* defined(__MWERKS__) */

    /* Grab the offset if there is one */
    if ((cam->frameBuffer->cType & rwRASTERTYPEMASK)==
        rwRASTERTYPECAMERATEXTURE)
    {
        skyXyoffset_1  =
//Bug here too... should be parent width/height
            (((cam->frameBuffer->nOffsetX & 0xfffl)+
              (2048-(cam->frameBuffer->width>>1)))<<4) |
            (((cam->frameBuffer->nOffsetY & 0xfffl) +
              (2048-(cam->frameBuffer->height>>1)))<<36) |
            (((cam->frameBuffer->cType & rwRASTERTYPEMASK)
              == rwRASTERTYPECAMERATEXTURE)?0:(SWE_HALF_OFFSET()<<35));
    }
    else
    {
#ifdef GSB
        skyXyoffset_1  =
            (( skyXOff+skyGsmOffsetX)<<4) |
            (( skyYOff+skyGsmOffsetY)<<36) |
            (((cam->frameBuffer->cType & rwRASTERTYPEMASK)
              == rwRASTERTYPECAMERATEXTURE)?0:(SWE_HALF_OFFSET()<<35));
#else /* GSB */
        skyXyoffset_1  =
            ((skyXOff)<<4) |
            ((skyYOff)<<36) |
            (((cam->frameBuffer->cType & rwRASTERTYPEMASK)
              == rwRASTERTYPECAMERATEXTURE)?0:(SWE_HALF_OFFSET()<<35));
#endif /* GSB */
    }
#endif /* defined(__MWERKS__) */

    tmp1 = GS_XYOFFSET_1;
    MAKE128(ltmp, tmp1, skyXyoffset_1);
    SWEADDCONTFAST(ltmp);

    tmp1 = GS_XYOFFSET_2;
    MAKE128(ltmp, tmp1, skyXyoffset_1);
    SWEADDCONTFAST(ltmp);

    /* Set up any scissor required here */
#ifdef GSB
    {
        int x,y,w,h;

        x = cam->frameBuffer->nOffsetX-skyGsmOffsetX>0?
            cam->frameBuffer->nOffsetX-skyGsmOffsetX:0;
        y = cam->frameBuffer->nOffsetY-skyGsmOffsetY>0?
            cam->frameBuffer->nOffsetY-skyGsmOffsetY:0;
        w = x + cam->frameBuffer->width;
        h = y + cam->frameBuffer->height;
        if (w>skyGsmWidth)
        {
            w = skyGsmWidth;
        }
        if (h>skyGsmHeight)
        {
            h = skyGsmHeight;
        }
        tmp = (x & 0x7ffl)
            | ((w-1)<<16)
            | ((y & 0x7ffl)<<32)
            | (((long)(h)-1l) <<48);
    }
#else /* GSB */
    tmp = (cam->frameBuffer->nOffsetX & 0x7ffl)
        | ((cam->frameBuffer->width+cam->frameBuffer->nOffsetX-1)<<16)
        | ((cam->frameBuffer->nOffsetY & 0x7ffl)<<32)
        | (((long)(cam->frameBuffer->height+cam->frameBuffer->nOffsetY)-1l)
           <<48);
#endif /* GSB */
    tmp1 = GS_SCISSOR_1;
    MAKE128(ltmp, tmp1, tmp);
    SWEADDCONTFAST(ltmp);

    /* Set up any scissor required here */
    tmp1 = GS_SCISSOR_2;
    MAKE128(ltmp, tmp1, tmp);
    SWEADDCONTFAST(ltmp);

    /* Restore test_1/2 */

    tmp1 = GS_TEST_1;
    MAKE128(ltmp, tmp1, skyTest_1);
    SWEADDCONTFAST(ltmp);

    tmp1 = GS_TEST_2;
    MAKE128(ltmp, tmp1, skyTest_1);
    SWEADDCONTFAST(ltmp);

    /* Write frame 1 and 2 */
    tmp = skyFrame_1;
    tmp1 = GS_FRAME_1;
    MAKE128(ltmp, tmp1, tmp);
    SWEADDCONTFAST(ltmp);

    tmp1 = GS_FRAME_2;
    MAKE128(ltmp, tmp1, tmp);
    SWEADDCONTFAST(ltmp);

#if defined(GSB) && defined(GSPLUS)
    /* Write frame 1 and 2 */
    tmp = skyFrame_2;
    tmp1 = GS_FRAME2_1;
    MAKE128(ltmp, tmp1, tmp);
    SWEADDCONTFAST(ltmp);

    tmp1 = GS_FRAME2_2;
    MAKE128(ltmp, tmp1, tmp);
    SWEADDCONTFAST(ltmp);
#endif /* defined(GSB) && defined(GSPLUS) */

    _rwSkyPipeSetupForCamera(cam);

    /* Set up the large frustum in the camera (for figuring out if we need to cull) */
    frustVerts = camExt->largeFrustumVerts;
    frustPlanes = camExt->largeFrustumPlanes;

    /* Raster sizes are >>1 because we deal with each corner separately */
    /* Scales are -1 because we add to the original values */
    scaleUp = ((2047.9374f-(RwReal)(cam->frameBuffer->height>>1)) / (RwReal)(cam->frameBuffer->height>>1)) - (RwReal)(1.0f);
    scaleRight = ((2047.9374f-(RwReal)(cam->frameBuffer->width>>1)) / (RwReal)(cam->frameBuffer->width>>1)) - (RwReal)(1.0f);

    /* Halve the scales because the source deltas are for full height/width, not half */
    scaleUp *= (RwReal)(0.5f);
    scaleRight *= (RwReal)(0.5f);

    /* Figure out front deltas */
    RwV3dSub(&up, &cam->frustumCorners[1], &cam->frustumCorners[2]);
    RwV3dSub(&right, &cam->frustumCorners[3], &cam->frustumCorners[2]);
    RwV3dScale(&up, &up, scaleUp);
    RwV3dScale(&right, &right, scaleRight);
    RwV3dAdd(&upRight, &up, &right);
    RwV3dSub(&upLeft, &up, &right);

    /* Front vertices (TR, TL, BL, BR) */
    RwV3dAdd(&frustVerts[0], &cam->frustumCorners[0], &upRight);
    RwV3dAdd(&frustVerts[1], &cam->frustumCorners[1], &upLeft);
    RwV3dSub(&frustVerts[2], &cam->frustumCorners[2], &upRight);
    RwV3dSub(&frustVerts[3], &cam->frustumCorners[3], &upLeft);

    /* Figure out rear deltas */
    RwV3dSub(&up, &cam->frustumCorners[5], &cam->frustumCorners[6]);
    RwV3dSub(&right, &cam->frustumCorners[7], &cam->frustumCorners[6]);
    RwV3dScale(&up, &up, scaleUp);
    RwV3dScale(&right, &right, scaleRight);
    RwV3dAdd(&upRight, &up, &right);
    RwV3dSub(&upLeft, &up, &right);

    /* Rear vertices (TR, TL, BL, BR) */
    RwV3dAdd(&frustVerts[4], &cam->frustumCorners[4], &upRight);
    RwV3dAdd(&frustVerts[5], &cam->frustumCorners[5], &upLeft);
    RwV3dSub(&frustVerts[6], &cam->frustumCorners[6], &upRight);
    RwV3dSub(&frustVerts[7], &cam->frustumCorners[7], &upLeft);

    /* Now just need to figure out planes */

    /* Near and far planes are exactly the same as standard frustum */
    frustPlanes[0] = cam->frustumPlanes[0];
    frustPlanes[1] = cam->frustumPlanes[1];

    /* Left plane */
    RwV3dSub(&vTmp, &frustVerts[1], &frustVerts[5]);
    RwV3dSub(&vTmp2, &frustVerts[6], &frustVerts[5]);
    RwV3dCrossProduct(&frustPlanes[2].plane.normal, &vTmp, &vTmp2);
    _rwV3dNormalize(&frustPlanes[2].plane.normal, &
                    frustPlanes[2].plane.normal);
    frustPlanes[2].plane.distance = RwV3dDotProduct(&frustVerts[1], &frustPlanes[2].plane.normal);
    RWPLANESETCLOSEST(frustPlanes[2]);

    /* Top plane */
    RwV3dSub(&vTmp2, &frustVerts[4], &frustVerts[5]);
    RwV3dCrossProduct(&frustPlanes[3].plane.normal, &vTmp2, &vTmp);
    _rwV3dNormalize(&frustPlanes[3].plane.normal,
                    &frustPlanes[3].plane.normal);
    frustPlanes[3].plane.distance = RwV3dDotProduct(&frustVerts[1], &frustPlanes[3].plane.normal);
    RWPLANESETCLOSEST(frustPlanes[3]);

    /* Right plane */
    RwV3dSub(&vTmp, &frustVerts[3], &frustVerts[7]);
    RwV3dSub(&vTmp2, &frustVerts[4], &frustVerts[7]);
    RwV3dCrossProduct(&frustPlanes[4].plane.normal, &vTmp, &vTmp2);
    _rwV3dNormalize(&frustPlanes[4].plane.normal,
                    &frustPlanes[4].plane.normal);
    frustPlanes[4].plane.distance = RwV3dDotProduct(&frustVerts[3], &frustPlanes[4].plane.normal);
    RWPLANESETCLOSEST(frustPlanes[4]);

    /* Bottom plane */
    RwV3dSub(&vTmp2, &frustVerts[6], &frustVerts[7]);
    RwV3dCrossProduct(&frustPlanes[5].plane.normal, &vTmp2, &vTmp);
    _rwV3dNormalize(&frustPlanes[5].plane.normal,
                    &frustPlanes[5].plane.normal);
    frustPlanes[5].plane.distance = RwV3dDotProduct(&frustVerts[3], &frustPlanes[5].plane.normal);
    RWPLANESETCLOSEST(frustPlanes[5]);

    RWRETURN(TRUE);
}

/****************************************************************************
 SkyCameraEndUpdate

 On entry   : NULL
            : camera pointer (IN)
            : 0
 On exit    : TRUE on success
 */
static              RwBool
SkyCameraEndUpdate(void * __RWUNUSED__ pOut,
                   void * __RWUNUSED__ pCam,
                   RwInt32 __RWUNUSED__ nData)
{
    RWFUNCTION(RWSTRING("SkyCameraEndUpdate"));


    _sweFlush();

    RWVALIDATEDEBUGSTACKDEPTH();


    /* If we were rendering into a texture we need to add a texflush */
    if ((((RwCamera *) pCam)->frameBuffer->cType & rwRASTERTYPEMASK)
        == rwRASTERTYPECAMERATEXTURE)
    {
        long                tmp, tmp1;
        u_long128           ltmp = 0;


        sweOpenLocalPkt(SWE_LPS_CONT, -2);

        tmp = /* NLOOP */ 1l
#ifdef LESSEOPS
            | /* EOP */ (0l << 15)
#else /* LESSEOPS */
            | /* EOP */ (1l << 15)
#endif /* LESSEOPS */
            | /* PRE */ (0l << 46)
            | /* FLG */ (0l << 58)
            | /* NREG */ (1l << 60);
        tmp1 = /* A+D */ (0xel << (64 - 64));
        MAKE128(ltmp, tmp1, tmp);
        SWEADDCONTGIFFAST(ltmp, 1);

        tmp = 0;
        tmp1 = (GS_TEXFLUSH << (64 - 64));
        MAKE128(ltmp, tmp1, tmp);
        SWEADDCONTFAST(ltmp);
    }

    RWVALIDATEDEBUGSTACKDEPTH();

    if (skyRenderTimeInProgress)
    {
        skyRenderTime += _sweReadTimer();
        skyRenderTimeInProgress = 0;
    }

    RWVALIDATEDEBUGSTACKDEPTH();


    RWRETURN(TRUE);
}

/****************************************************************************
 SkyCameraClear

 On entry   : camera pointer (IN)
            : pointer to RwRGBA giving frame buffer color
            : bit field indicating what to clear (rwCAMERACLEARIMAGE,
                                                  rwCAMERACLEARZ)
 On exit    : TRUE on success
 */
static RwBool
SkyCameraClear(void *pCam, void *pCol, RwInt32 nClearWhich)
{
    unsigned long tmp, tmp1;
    u_long128 ltmp = 0;
    RwRGBA *col = (RwRGBA*)pCol;
    RwCamera *cam = (RwCamera *)pCam;
    RWFUNCTION(RWSTRING("SkyCameraClear"));

#ifdef MORECPU
    if (sweNumFlipsInQueue > sweMaxFlips+1)
    {
        _sweWaitQueue();
    }
#endif /* MORECPU */

#ifdef GSB
    /* if (skyGCVTValue % skyNumSeqFrms == (unsigned)skyRenderSlot) */
    /* { */
    _sweAddPkt(NULL, SWE_PKT_WAIT_DRAWNEXT);
    /* } */
    /* else */
    /* { */
    /*    return(TRUE); */
    /* } */
#endif /* GSB */

    if ((cam->frameBuffer->cType & rwRASTERTYPEMASK)
        == rwRASTERTYPECAMERATEXTURE)
    {
        _SkyRasterExt *reExt = RASTEREXTFROMRASTER(cam->frameBuffer);

        /* If the texture is not locked in cache we fail */
        if (reExt->bLocked==FALSE)
        {
            RWMESSAGE((RWSTRING("error - rwRASTERTYPECAMERATEXTURE must be locked in cache")));
            RWRETURN(FALSE);
        }
        /* We build a fake frame_1 to permit rendering */
        skyFrame_1 = ((reExt->lsb>>5)&0x1ff) | ((reExt->lsb<<2)&0x3f0000)
            | ((reExt->lsb<<4)&0x3f000000);
#if defined(GSB) && defined(GSPLUS)
        skyFrame_2 = (reExt->lsb3 >> 5)&0xfff;
#endif /* defined(GSB) && defined(GSPLUS) */
    }
    else
    {
        if (skyFrameBit & 0x1)
        {
            skyFrame_1 = *(long*)&sweDb.draw01.frame1;
#if defined(GSB) && defined(GSPLUS)
            skyFrame_2 = *(long*)&sweDb.draw01.eframe;
#endif /* defined(GSB) && defined(GSPLUS) */
        }
        else
        {
            skyFrame_1 = *(long*)&sweDb.draw11.frame1;
#if defined(GSB) && defined(GSPLUS)
            skyFrame_2 = *(long*)&sweDb.draw11.eframe;
#endif /* defined(GSB) && defined(GSPLUS) */
        }
    }

#if defined(GSB) && defined(GSPLUS)
    sweOpenLocalPkt(SWE_LPS_CONT, -15);

    tmp = /* NLOOP */ 14l
#ifdef LESSEOPS
        | /* EOP */ (0l<<15)
#else /* LESSEOPS */
        | /* EOP */ (1l<<15)
#endif /* LESSEOPS */
        | /* PRE */ (0l<<46)
        | /* FLG */ (0l<<58)
        | /* NREG */(1l<<60);
    tmp1 = /* A+D */ (0xel<<(64-64));
    MAKE128(ltmp, tmp1, tmp);
    SWEADDCONTGIFFAST(ltmp, 14);
#else /* defined(GSB) && defined(GSPLUS) */
    sweOpenLocalPkt(SWE_LPS_CONT, -14);

    tmp = /* NLOOP */ 13l
#ifdef LESSEOPS
        | /* EOP */ (0l<<15)
#else /* LESSEOPS */
        | /* EOP */ (1l<<15)
#endif /* LESSEOPS */
        | /* PRE */ (0l<<46)
        | /* FLG */ (0l<<58)
        | /* NREG */(1l<<60);
    tmp1 = /* A+D */ (0xel<<(64-64));
    MAKE128(ltmp, tmp1, tmp);
    SWEADDCONTGIFFAST(ltmp, 13);
#endif /* defined(GSB) && defined(GSPLUS) */

    if (nClearWhich & rwCAMERACLEARIMAGE)
    {
        /* Write to frame buffer buffer */
        tmp = skyFrame_1;
        tmp &= 0xffffffffl;
        tmp1 = GS_FRAME_1;
        MAKE128(ltmp, tmp1, tmp);
        SWEADDCONTFAST(ltmp);

#if defined(GSB) && defined(GSPLUS)
        MAKE128(ltmp, GS_FRAME2_1, skyFrame_2);
        SWEADDCONTFAST(ltmp);
#endif /* defined(GSB) && defined(GSPLUS) */
    }
    else
    {
        /* Don't touch the Frame buffer */
        tmp = skyFrame_1;
        tmp |= (0xffffffffl<<32);
        tmp1 = GS_FRAME_1;
        MAKE128(ltmp, tmp1, tmp);
        SWEADDCONTFAST(ltmp);

#if defined(GSB) && defined(GSPLUS)
        MAKE128(ltmp, GS_FRAME2_1, skyFrame_2);
        SWEADDCONTFAST(ltmp);
#endif /* defined(GSB) && defined(GSPLUS) */
    }

    if (nClearWhich & rwCAMERACLEARZ)
    {
        /* write to Zbuffer */
        tmp = skyZbuf_1;
        tmp &= ~(1l<<32);
        tmp1 = GS_ZBUF_1;
        MAKE128(ltmp, tmp1, tmp);
        SWEADDCONTFAST(ltmp);

#if defined(GSB) && defined(GSPLUS)
        /* Don't need to touch top 3 bits in ZBUF2_1 */
#endif /* defined(GSB) && defined(GSPLUS) */
    }
    else
    {
        /* Don't touch the Zbuffer */
        tmp = skyZbuf_1;
        tmp |= (1l<<32);
        tmp1 = GS_ZBUF_1;
        MAKE128(ltmp, tmp1, tmp);
        SWEADDCONTFAST(ltmp);

#if defined(GSB) && defined(GSPLUS)
        /* Don't need to touch top 3 bits in ZBUF2_1 */
#endif /* defined(GSB) && defined(GSPLUS) */
    }

    /* Force writes */
    tmp = skyTest_1;
    tmp &= ~0x54001l;
    /* rely on zbuf mask to stop z write due to GS bug */
    tmp |=  0x30000l;

    tmp1 = GS_TEST_1;
    MAKE128(ltmp, tmp1, tmp);
    SWEADDCONTFAST(ltmp);

    /* We can't rely on the xy offset currently set */
#if 0
    tmp = (cam->frameBuffer->nOffsetX & 0xfffl)<<4
        | (cam->frameBuffer->nOffsetY & 0xfffl)<<36;
#else
    /* Offsets can't be negative, so */
    tmp = (0l<<4)|(0l<<36);
#endif
    tmp1 = GS_XYOFFSET_1;
    MAKE128(ltmp, tmp1, tmp);
    SWEADDCONTFAST(ltmp);

    /* We need to unset any Scissor */
    tmp = 0l | (0x7fffl<<16) | (0l<<32) | (0x7fffl<<48);
    tmp1 = GS_SCISSOR_1;
    MAKE128(ltmp, tmp1, tmp);
    SWEADDCONTFAST(ltmp);

    /* now draw a sprite */
    tmp = 6;
    tmp1 = GS_PRIM;
    MAKE128(ltmp, tmp1, tmp);
    SWEADDCONTFAST(ltmp);

#ifdef GSB
    tmp = (0l<<32) | ((cam->frameBuffer->nOffsetY-skyGsmOffsetY>0?
                       cam->frameBuffer->nOffsetY-skyGsmOffsetY:0)<< 20)
        | ((cam->frameBuffer->nOffsetX-skyGsmOffsetX>0?
            cam->frameBuffer->nOffsetX-skyGsmOffsetX:0) << 4);
#else /* GSB */
    tmp = (0l<<32) | (cam->frameBuffer->nOffsetY<< 20)
        | (cam->frameBuffer->nOffsetX << 4);
#endif /* GSB */
    tmp1 = GS_XYZ2;
    MAKE128(ltmp, tmp1, tmp);
    SWEADDCONTFAST(ltmp);

    if (nClearWhich & rwCAMERACLEARIMAGE)
    {
        tmp = (RwUInt32) ( ((RwInt32)(((float)col->alpha)*0.502f)<<24) |
                           ((RwInt32)(col->blue)<<16) |
                           ((RwInt32)(col->green)<<8) |
                           ((RwInt32)(col->red)) );
    }
    else
    {
        tmp = 0;
    }
    tmp1 = GS_RGBAQ;
    MAKE128(ltmp, tmp1, tmp);
    SWEADDCONTFAST(ltmp);

#ifdef GSB
    {
        int x,y;

        x = cam->frameBuffer->width+cam->frameBuffer->nOffsetX-skyGsmOffsetX>0?
            cam->frameBuffer->width+cam->frameBuffer->nOffsetX-skyGsmOffsetX:0;
        y = cam->frameBuffer->height+cam->frameBuffer->nOffsetY-skyGsmOffsetY>0?
            cam->frameBuffer->height+cam->frameBuffer->nOffsetY-skyGsmOffsetY:0;
        if (x>skyGsmWidth)
        {
            x = skyGsmWidth;
        }
        if (y>skyGsmHeight)
        {
            y = skyGsmHeight;
        }
        tmp = (0l<<32) | ((y) << 20) | ((x) << 4);
    }
#else /* GSB */
    tmp = (0l<<32) | ((cam->frameBuffer->height+cam->frameBuffer->nOffsetY) << 20)
        | ((cam->frameBuffer->width+cam->frameBuffer->nOffsetX) << 4);
#endif /* GSB */
    tmp1 = GS_XYZ2;
    MAKE128(ltmp, tmp1, tmp);
    SWEADDCONTFAST(ltmp);

    tmp = skyFrame_1;
    tmp1 = GS_FRAME_1;
    MAKE128(ltmp, tmp1, tmp);
    SWEADDCONTFAST(ltmp);

    tmp = skyZbuf_1;
    tmp1 = GS_ZBUF_1;
    MAKE128(ltmp, tmp1, tmp);
    SWEADDCONTFAST(ltmp);

    tmp = (skyXyoffset_1 & ~(1l<<35)) | (SWE_HALF_OFFSET()<<35);
    tmp1 = GS_XYOFFSET_1;
    MAKE128(ltmp, tmp1, tmp);
    SWEADDCONTFAST(ltmp);

    tmp1 = GS_XYOFFSET_2;
    MAKE128(ltmp, tmp1, tmp);
    SWEADDCONTFAST(ltmp);

    RWRETURN(TRUE);
}


/*
 * _skyRaster[Get|Set]Twiddling created/exposed for RpMipSplit, so
 * that the twiddling flag can be safely accessed from the plugin.
 */
RwBool
_skyRasterGetTwiddling(RwRaster *ras)
{
    _SkyRasterExt *rasExt;

    RWFUNCTION(RWSTRING("_skyRasterGetTwiddling"));
    RWASSERT(NULL != ras);

    rasExt = RASTEREXTFROMRASTER(ras);

    if (rasExt->flags & 2)
    {
        RWRETURN(TRUE);
    }

    RWRETURN(FALSE);
}

RwRaster *
_skyRasterSetTwiddling(RwRaster *ras, RwBool twiddling)
{
    _SkyRasterExt *rasExt;

    RWFUNCTION(RWSTRING("_skyRasterSetTwiddling"));
    RWASSERT(NULL != ras);

    rasExt = RASTEREXTFROMRASTER(ras);
    rasExt->flags |= 2;
    if (FALSE == twiddling) rasExt->flags &= ~2;

    RWRETURN(ras);
}


/****************************************************************************
 SkyRasterShowRaster

 On entry   : raster pointer (IN)
            : device specific pointer (IN) - HWND for windows, eg
            : 0
 On exit    : TRUE on success
 */
static RwBool
SkyRasterShowRaster(void * __RWUNUSED__ pRas,
                    void *pDev,
                    RwInt32 __RWUNUSED__ nData)
{
    RWFUNCTION(RWSTRING("SkyRasterShowRaster"));

#ifdef GSB
    if (skyGCVTValue % skyNumSeqFrms == skyRenderSlot)
    {
        if (skyGsmSysReadyRequiredStraight)
        {
            skyGsmSysReadyRequiredStraight--;
        }
    }
    if (!(skyGsmSysReadyRequiredStraight))
    {
#if 1
        if (skyGSMReadInput)
        {
            if (gsbReceiveData(skyGSMInputCh, skyGSMInput, 6, GSB_IOBLOCK) < 0)
            {
                printf("fail\n");
            }
            else
            {
                /* printf("%x, %x, %x, %x, %x, %x\n", skyGSMInput[0], skyGSMInput[1], skyGSMInput[2], skyGSMInput[3], skyGSMInput[4], skyGSMInput[5]); */
            }
        }
#endif
    }
    /* We only want to request a flip on a displayed frame */
    if (skyGCVTValue % skyNumSeqFrms != (unsigned)skyRenderSlot)
    {
        /* We do need to increment real time however */
        skyGCVTValue++;
        _sweAddPkt(NULL, SWE_PKT_INC_TIME);
        /* Warning: This only works in SYNC builds. FIX! */
        _sweFlush();
        RWRETURN(TRUE);
    }
#endif /* GSB */

#ifdef RWMETRICS
    /* We're done with these now */
    _sweMetricsReset();
#endif /* RWMETRICS */

    if (skyRenderTime)
    {
        sweFrameRenderCount = skyRenderTime;
        skyRenderTime = 0;
        skyRenderTimeInProgress = 0;
    }

    /* Update the texture cache */
    skyTexCacheEndFrame();

#ifdef LOGO
    if (logoTexture)
    {
        unsigned long tmp, tmp1;
        u_long128 ltmp;

        _rwSkySetRenderState(rwRENDERSTATETEXTURERASTER, (logoTexture->raster));

        sweOpenLocalPkt(SWE_LPS_PRIM, -5);

        tmp = /* NLOOP */ 1l
            | /* EOP */ (1<<15)
            | /* PRE */ (0l<<46)
            | /* PRIM */ (0x0l<<47)
            | /* FLG */ (1l<<58)
            | /* NREG */ (7l<<60);
        tmp1 = /* PRIM */ (0<<(64-64))
            | /* ST */ (2l<<(68-64))
            | /* RGBAQ */ (1l<<(72-64))
            | /* XYZ2 */ (4l<<(76-64))
            | /* ST */ (2l<<(80-64))
            | /* RGBAQ */ (1l<<(84-64))
            | /* XYZ2 */ (4l<<(88-64));
        MAKE128(ltmp, tmp1, tmp);
        SWEADDCONTGIFFAST(ltmp, 4);

        tmp = 0x56l; /* PRIM */
        tmp1 = 0x0000000000000000;   /* ST */
        MAKE128(ltmp, tmp1, tmp);
        SWEADDCONTFAST(ltmp);

        tmp = 0x3f80000080ffffff; /* RGBAQ */
        tmp1 = 0xffffffff00000000
            | ((LOGOOFFY+4096-videoModes[currentMode].height)<<19)
            | ((4096+videoModes[currentMode].width
                -(int)((float)(LOGOSIZE+LOGOOFFX)*logoScaleX))<<3);/* XYZ2 */
        MAKE128(ltmp, tmp1, tmp);
        SWEADDCONTFAST(ltmp);

        tmp = 0x3f8000003f800000; /* ST */
        tmp1 = 0x3f80000080ffffff; /* RGBAQ */
        MAKE128(ltmp, tmp1, tmp);
        SWEADDCONTFAST(ltmp);

        tmp = 0xffffffff00000000
            | ((LOGOOFFY+4096-videoModes[currentMode].height+LOGOSIZE)<<19)
            | ((4096+videoModes[currentMode].width
                -(int)((float)(LOGOOFFX)*logoScaleX))<<3); /* XYZ2 */
        MAKE128(ltmp, 0l, tmp);
        SWEADDCONTFAST(ltmp);
        sweCloseLocalPkt();
    }
#endif /* LOGO */

    /* For now, I'm going to write to the fake flip register */

#if 0
    /* Sample uses VIF1 to do the transfer */
    skyDMA_ch1_source_chain(&screen_refresh);
    /* Why not use the GIF (ch2) directly ? */
#else
#if 0
#if defined(GSB) && defined(GSPLUS)
    sceGsPlusSwapDBuffDc(&sweDb, skyFrameBit++);
#else /* defined(GSB) && defined(GSPLUS) */
    sceGsSwapDBuffDc(&sweDb, skyFrameBit++);
#endif /* defined(GSB) && defined(GSPLUS) */
#else
    _sweReqFlip(&sweDb, skyFrameBit++, (unsigned int)pDev);
#endif
#if 0
    /* This is moved to camera clear/begin to increase overlap time */
    if (skyFrameBit & 0x1)
    {
        skyFrame_1 = *(long*)&sweDb.draw0.frame1;
#if defined(GSB) && defined(GSPLUS)
        skyFrame_2 = *(long*)&sweDb.draw0.eframe;
#endif /* defined(GSB) && defined(GSPLUS) */
    }
    else
    {
        skyFrame_1 = *(long*)&sweDb.draw1.frame1;
#if defined(GSB) && defined(GSPLUS)
        skyFrame_2 = *(long*)&sweDb.draw1.eframe;
#endif /* defined(GSB) && defined(GSPLUS) */
    }
#endif
#endif

    if (skyVideoMode->flags & rwVIDEOMODEFSAA0)
    {
        skyTexCacheRestore();
    }

    RWRETURN(TRUE);
}

/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

                         Texture access

 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */

/****************************************************************************
 SkyTextureSetRaster

 On entry   : Texture
            : Raster
            : 0
 On exit    : TRUE on success
 */
static RwBool
SkyTextureSetRaster(void *pTex,void *pRas,RwInt32 __RWUNUSED__ nIn)
{
    RwTexture *tex = (RwTexture *)pTex;
    RwRaster *rpRas = (RwRaster *)pRas;

    RWFUNCTION(RWSTRING("SkyTextureSetRaster"));

    /* Try and set the raster */
    tex->raster = rpRas;

    /* All done */
    RWRETURN(TRUE);
}

/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

                      Finding the appropriate format

 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */

/****************************************************************************
 SkyGetRasterFormat

 On entry   :
            : Raster
            : Flags
 On exit    :
 */
static RwBool
SkyGetRasterFormat(void * __RWUNUSED__ pInOut, void *pRaster, RwInt32 flags)
{
    RwRaster *rpRas = (RwRaster *)pRaster;
    RwInt32 nFormat = flags & (RwInt32)rwRASTERFORMATPIXELFORMATMASK;
    RwInt32 mipmapFlag = flags & (RwInt32)(rwRASTERFORMATMIPMAP|rwRASTERFORMATAUTOMIPMAP);
    RwInt32 palette = flags & (RwInt32)(rwRASTERFORMATPAL4|rwRASTERFORMATPAL8);

    RWFUNCTION(RWSTRING("SkyGetRasterFormat"));

    /* Copy over types */
    rpRas->cType    = (RwUInt8)(flags & (RwInt32)rwRASTERTYPEMASK);
    rpRas->cFlags   = (RwUInt8)(flags & (RwInt32)(~rwRASTERTYPEMASK));

    switch (rpRas->cType)
    {
        case rwRASTERTYPENORMAL:
        case rwRASTERTYPECAMERATEXTURE:
            {
                /* No palettised or mipmap rasters */
                if (palette || mipmapFlag)
                {
                    RWERROR((E_RW_INVRASTERFORMAT));
                    RWRETURN(FALSE);
                }

                /* Drop through to next case */
            }
        case rwRASTERTYPETEXTURE:
            {
                /* By default textures are 1555 for 16 bit, 8 bit palettised for 8
                 * bit or 8888 for 32 bit.
                 * Stupid depths for palettised textures get rejected later.
                 */
                if (nFormat == (RwInt32)rwRASTERFORMATDEFAULT)
                {
                    switch (rpRas->depth)
                    {
                        case (4):
                            nFormat = (RwInt32)(rwRASTERFORMATPAL4|rwRASTERFORMAT1555);
                            break;

                        case (8):
                            nFormat = (RwInt32)(rwRASTERFORMATPAL8|rwRASTERFORMAT1555);
                            break;

                        case (24):
                            nFormat = (RwInt32)rwRASTERFORMAT888;
                            break;

                        case (32):
                            nFormat = (RwInt32)rwRASTERFORMAT8888;
                            break;

                        case (16):
                        default:
                            nFormat = (RwInt32)rwRASTERFORMAT1555;
                            break;
                    }
                }

                /* Don't forget the mipmap flag */
                rpRas->cFormat =  ( (RwUInt8) ((nFormat |
                                                mipmapFlag |
                                                palette) >>8) );

                /* Make sure the depth is good */
                switch (palette)
                {
                    case (0):
                        {
                            switch (nFormat)
                            {
                                case (rwRASTERFORMAT1555):
                                    if ((rpRas->depth != 0) && (rpRas->depth != 16))
                                    {
                                        RWERROR((E_RW_INVRASTERDEPTH));
                                        RWRETURN(FALSE);
                                    }
                                    rpRas->depth = 16;
                                    break;

                                case (rwRASTERFORMAT888):
                                    if ((rpRas->depth != 0) && (rpRas->depth != 24))
                                    {
                                        RWERROR((E_RW_INVRASTERDEPTH));
                                        RWRETURN(FALSE);
                                    }
                                    rpRas->depth = 24;
                                    break;

                                case (rwRASTERFORMAT8888):
                                    if ((rpRas->depth != 0) && (rpRas->depth != 32))
                                    {
                                        RWERROR((E_RW_INVRASTERDEPTH));
                                        RWRETURN(FALSE);
                                    }
                                    rpRas->depth = 32;
                                    break;

                                default:
                                    /* Unsupported raster format */
                                    RWERROR((E_RW_INVRASTERFORMAT));
                                    RWRETURN(FALSE);
                            }
                            break;
                        }
                    case (rwRASTERFORMATPAL4):
                        {
                            if ((rpRas->depth != 0) && (rpRas->depth != 4))
                            {
                                RWERROR((E_RW_INVRASTERDEPTH));
                                RWRETURN(FALSE);
                            }
                            rpRas->depth = 4;

                            /* Reject stupid CLUT formats */
                            switch (nFormat)
                            {
                                case (rwRASTERFORMAT1555):
                                case (rwRASTERFORMAT8888):
                                    /* These formats are cool. */
                                    break;

                                default:
                                    /* Can't use this format with palettised rasters */
                                    RWERROR((E_RW_INVRASTERFORMAT));
                                    RWRETURN(FALSE);
                            }
                            break;
                        }
                    case (rwRASTERFORMATPAL8):
                        {
                            if ((rpRas->depth != 0) && (rpRas->depth != 8))
                            {
                                RWERROR((E_RW_INVRASTERDEPTH));
                                RWRETURN(FALSE);
                            }
                            rpRas->depth = 8;

                            /* Reject stupid CLUT formats */
                            switch (nFormat)
                            {
                                case (rwRASTERFORMAT1555):
                                case (rwRASTERFORMAT8888):
                                    /* These formats are cool. */
                                    break;

                                default:
                                    /* Can't use this format with palettised rasters */
                                    RWERROR((E_RW_INVRASTERFORMAT));
                                    RWRETURN(FALSE);
                            }
                            break;
                        }
                    default:
                        {
                            /* Can't do this - palettised 4 and 8 at the same time? */
                            RWERROR((E_RW_INVRASTERFORMAT));
                            RWRETURN(FALSE);
                        }
                }

                break;
            }
        case rwRASTERTYPECAMERA:
            {
                /* No mipmaps or palettised rasters here */
                if (mipmapFlag || palette)
                {
                    RWERROR((E_RW_INVRASTERFORMAT));
                    RWRETURN(FALSE);
                }

                /* 1555 or 8888 depending on camera depth */
                if ((rpRas->depth != 0) &&
                    (rpRas->depth != videoModes[currentMode].depth))
                {
                    RWERROR((E_RW_INVRASTERDEPTH));
                    RWRETURN(FALSE);
                }
                rpRas->depth = videoModes[currentMode].depth;

                if ((nFormat != (RwInt32)rwRASTERFORMATDEFAULT) &&
                    (nFormat != currentCameraFormat))
                {
                    RWERROR((E_RW_INVRASTERFORMAT));
                    RWRETURN(FALSE);
                }
                nFormat = currentCameraFormat;

                rpRas->cFormat = (RwUInt8)(nFormat>>8);

                break;
            }
        case rwRASTERTYPEZBUFFER:
            {
                /* No mipmaps or palettised rasters here */
                if (mipmapFlag || palette)
                {
                    RWERROR((E_RW_INVRASTERFORMAT));
                    RWRETURN(FALSE);
                }

                /* zBufferDepth bit Z buffers */
                if ((rpRas->depth != 0) &&
                    (rpRas->depth != (RwInt32)zBufferDepth))
                {
                    RWERROR((E_RW_INVRASTERDEPTH));
                    RWRETURN(FALSE);
                }
                rpRas->depth = zBufferDepth;

                /* By default, Z buffers are 16 bit */
                if ((nFormat != (RwInt32)rwRASTERFORMATDEFAULT) &&
                    (nFormat != (RwInt32)(zBufferDepth==16?rwRASTERFORMAT16
                                          :rwRASTERFORMAT32)))
                {
                    RWERROR((E_RW_INVRASTERFORMAT));
                    RWRETURN(FALSE);
                }
                nFormat = (RwInt32)(zBufferDepth==16?rwRASTERFORMAT16
                                    :rwRASTERFORMAT32);

                rpRas->cFormat = (RwUInt8)(nFormat>>8);

                break;
            }
        default:
            {
                RWRETURN(FALSE);
            }
    }

    RWRETURN(TRUE);
}

#ifdef BETTERPACK
/************************************************************************
 * This code is included from work in progress. Eventually mipmapped    *
 * textures will be uploaded in one shot, but for now the more efficient*
 * packing code is inserted into the original version to save VRAM.     *
 *                                                                      *
 * This code will only work correctly for the sizes used at present.    *
 * If you wish to change things you will probably need to rework the    *
 * palette hiding logic (In fact, maxlevels 7, min w,h 8 also work)     *
 ************************************************************************/

/*
 * Macros to take a linear address and turn it into a block in a physical
 * address.
 */

/* 32 -> 0->0 1->2 2->4 3->1 4->3 */
#define PSMCT32BlockToAddress(B)        \
    (((B)&~0x1f)                        \
    |(((B) & 0x1) << 0)                 \
    |(((B) & 0x2) << 1)                 \
    |(((B) & 0x4) << 2)                 \
    |(((B) & 0x8) >> 2)                 \
    |(((B) & 0x10)>> 1))

/* 32Z -> 32 ^ 0x18 */
#define PSMZ32BlockToAddress(B)         \
    (PSMCT32BlockToAddress(B) ^ 0x18)

/* 16 -> 0->1 1->3 2->0 3->2 4->4 */
#define PSMCT16BlockToAddress(B)        \
    (((B)&~0x1f)                        \
    |(((B) & 0x1) << 1)                 \
    |(((B) & 0x2) << 2)                 \
    |(((B) & 0x4) >> 2)                 \
    |(((B) & 0x8) >> 1)                 \
    |(((B) & 0x10)<< 0))

/* 16Z -> 16 ^ 0x18 */
#define PSMZ16BlockToAddress(B)         \
    (PSMCT16BlockToAddress(B) ^ 0x18)

/* 16S -> 0->1 1->4 2->0 3->3 4->2 */
#define PSMCT16SBlockToAddress(B)       \
    (((B)&~0x1f)                        \
    |(((B) & 0x1) << 1)                 \
    |(((B) & 0x2) << 3)                 \
    |(((B) & 0x4) >> 2)                 \
    |(((B) & 0x8) << 0)                 \
    |(((B) &0x10) >> 2))

/* 16ZZ -> 16S ^ 0x18 */
#define PSMZ16SBlockToAddress(B)        \
    (PSMCT16SBlockToAddress(B) ^ 0x18)

/* 8 -> 0->0 1->2 2->4 3->1 4->3 */
#define PSMT8BlockToAddress(B)          \
    (((B)&~0x1f)                        \
    |(((B) & 0x1) << 0)                 \
    |(((B) & 0x2) << 1)                 \
    |(((B) & 0x4) << 2)                 \
    |(((B) & 0x8) >> 2)                 \
    |(((B) &0x10) >> 1))

/* 4 -> 0->1 1->3 2->0 3->2 4->4 */
#define PSMT4BlockToAddress(B)          \
    (((B)&~0x1f)                        \
    |(((B) & 0x1) << 1)                 \
    |(((B) & 0x2) << 2)                 \
    |(((B) & 0x4) >> 2)                 \
    |(((B) & 0x8) >> 1)                 \
    |(((B) &0x10) << 0))

/* 32 -> 0->0 1->3 2->1 3->4 4->2 */
#define PSMCT32AddressToBlock(A)        \
    (((A)&~0x1f)                        \
    |(((A) & 0x1) << 0)                 \
    |(((A) & 0x2) << 2)                 \
    |(((A) & 0x4) >> 1)                 \
    |(((A) & 0x8) << 1)                 \
    |(((A) & 0x10)>> 2))

/* 16 -> 0->2 1->0 2->3 3->1 4->4 */
#define PSMCT16AddressToBlock(A)        \
    (((A)&~0x1f)                        \
    |(((A) & 0x1) << 2)                 \
    |(((A) & 0x2) >> 1)                 \
    |(((A) & 0x4) << 1)                 \
    |(((A) & 0x8) >> 2)                 \
    |(((A) &0x10) << 0))

/* 8 -> 0->0 1->3 2->1 3->4 4->2 */
#define PSMT8AddressToBlock(A)          \
    (((A)&~0x1f)                        \
    |(((A) & 0x1) << 0)                 \
    |(((A) & 0x2) << 2)                 \
    |(((A) & 0x4) >> 1)                 \
    |(((A) & 0x8) << 1)                 \
    |(((A) &0x10) >> 2))

/* 4 -> 0->2 1->0 2->3 3->1 4->4 */
#define PSMT4AddressToBlock(A)          \
    (((A)&~0x1f)                        \
    |(((A) & 0x1) << 2)                 \
    |(((A) & 0x2) >> 1)                 \
    |(((A) & 0x4) << 1)                 \
    |(((A) & 0x8) >> 2)                 \
    |(((A) &0x10) << 0))

#define GSBlockToAddress(__result, B, FORMAT)           \
MACRO_START                                             \
{                                                       \
    switch (FORMAT)                                     \
    {                                                   \
        case SCE_GS_PSMCT32:                            \
        case SCE_GS_PSMCT24:                            \
        case SCE_GS_PSMT8H:                             \
        case SCE_GS_PSMT4HL:                            \
        case SCE_GS_PSMT4HH:                            \
        {                                               \
            __result = PSMCT32BlockToAddress((B));      \
            break;                                      \
        }                                               \
        case SCE_GS_PSMCT16:                            \
        {                                               \
            __result = PSMCT16BlockToAddress((B));      \
            break;                                      \
        }                                               \
        case SCE_GS_PSMCT16S:                           \
        {                                               \
            __result = PSMCT16SBlockToAddress((B));     \
            break;                                      \
        }                                               \
        case SCE_GS_PSMT8:                              \
        {                                               \
            __result = PSMT8BlockToAddress((B));        \
            break;                                      \
        }                                               \
        case SCE_GS_PSMT4:                              \
        {                                               \
            __result = PSMT4BlockToAddress((B));        \
            break;                                      \
        }                                               \
        case SCE_GS_PSMZ32:                             \
        case SCE_GS_PSMZ24:                             \
        {                                               \
            __result = PSMZ32BlockToAddress((B));       \
            break;                                      \
        }                                               \
        case SCE_GS_PSMZ16:                             \
        {                                               \
            __result = PSMZ16BlockToAddress((B));       \
            break;                                      \
        }                                               \
        case SCE_GS_PSMZ16S:                            \
        {                                               \
            __result = PSMZ16SBlockToAddress((B));      \
            break;                                      \
        }                                               \
        default:                                        \
        __result = (B);                             \
    }                                                   \
}                                                       \
MACRO_STOP


/* Block sizes */
#define PSMCT32BlockWidth 8
#define PSMCT32BlockHeight 8

#define PSMZ32BlockWidth 8
#define PSMZ32BlockHeight 8

#define PSMCT16BlockWidth 16
#define PSMCT16BlockHeight 8

#define PSMZ16BlockWidth 16
#define PSMZ16BlockHeight 8

#define PSMCT16SBlockWidth 16
#define PSMCT16SBlockHeight 8

#define PSMZ16SBlockWidth 16
#define PSMZ16SBlockHeight 8

#define PSMT8BlockWidth 16
#define PSMT8BlockHeight 16

#define PSMT4BlockWidth 32
#define PSMT4BlockHeight 16

/* Page sizes */
#define PSMCT32PageWidth 64
#define PSMCT32PageHeight 32

#define PSMZ32PageWidth 64
#define PSMZ32PageHeight 32

#define PSMCT16PageWidth 64
#define PSMCT16PageHeight 64

#define PSMZ16PageWidth 64
#define PSMZ16PageHeight 64

#define PSMCT16SPageWidth 64
#define PSMCT16SPageHeight 64

#define PSMZ16SPageWidth 64
#define PSMZ16SPageHeight 64

#define PSMT8PageWidth 128
#define PSMT8PageHeight 64

#define PSMT4PageWidth 128
#define PSMT4PageHeight 128

/* Min transfer width */
#define PSMCT32MinTW 2

#define PSMZ32MinTW 2

#define PSMCT24MinTW 8

#define PSMZ24MinTW 8

#define PSMCT16MinTW 4

#define PSMCT16SMinTW 4

#define PSMZ16MinTW 4

#define PSMZ16SMinTW 4

#define PSMT8MinTW 8

#define PSMT8HMinTW 8

#define PSMT4MinTW 8

#define PSMT4HLMinTW 8

#define PSMT4HHMinTW 8

static void
calcOffsets(RwUInt32 width, RwUInt32 height, int format,
            RwUInt64 *offsets, RwUInt64 *bufwidth, RwUInt32 *xyoff,
            RwUInt32 *texMemSize, RwUInt32 *paletteOffset)
{
    RwUInt32 blockWidth;
    RwUInt32 blockHeight;
    RwUInt32 pageWidth;
    RwUInt32 pageHeight;

    int palette;
    int palettewidth;
    int bufheight[7];
    int bufstart[7];
    int maxlevel;

    /* Sub page stuff */
    int interioroffsetx;
    int interioroffsety;
    int piviot;

    /* stack for final fill stage */
    int stackx[8];
    RwUInt32 stackh[8];
    RwUInt32 stackw[8];
    RwUInt32 stacko[8];
    int stackp;

    int i;

    RWFUNCTION(RWSTRING("calcOffsets"));

    switch (format)
    {
        case SCE_GS_PSMCT32:
        case SCE_GS_PSMCT24:
        case SCE_GS_PSMT8H:
        case SCE_GS_PSMT4HL:
        case SCE_GS_PSMT4HH:
            {
                blockWidth = PSMCT32BlockWidth;
                blockHeight = PSMCT32BlockHeight;
                pageWidth = PSMCT32PageWidth;
                pageHeight = PSMCT32PageHeight;
                break;
            }
        case SCE_GS_PSMCT16:
            {
                blockWidth = PSMCT16BlockWidth;
                blockHeight = PSMCT16BlockHeight;
                pageWidth = PSMCT16PageWidth;
                pageHeight = PSMCT16PageHeight;
                break;
            }
        case SCE_GS_PSMCT16S:
            {
                blockWidth = PSMCT16SBlockWidth;
                blockHeight = PSMCT16SBlockHeight;
                pageWidth = PSMCT16SPageWidth;
                pageHeight = PSMCT16SPageHeight;
                break;
            }
        case SCE_GS_PSMT8:
            {
                blockWidth = PSMT8BlockWidth;
                blockHeight = PSMT8BlockHeight;
                pageWidth = PSMT8PageWidth;
                pageHeight = PSMT8PageHeight;
                break;
            }
        case SCE_GS_PSMT4:
            {
                blockWidth = PSMT4BlockWidth;
                blockHeight = PSMT4BlockHeight;
                pageWidth = PSMT4PageWidth;
                pageHeight = PSMT4PageHeight;
                break;
            }
        case SCE_GS_PSMZ32:
        case SCE_GS_PSMZ24:
            {
                blockWidth = PSMZ32BlockWidth;
                blockHeight = PSMZ32BlockHeight;
                pageWidth = PSMZ32PageWidth;
                pageHeight = PSMZ32PageHeight;
                break;
            }
        case SCE_GS_PSMZ16:
            {
                blockWidth = PSMZ16BlockWidth;
                blockHeight = PSMZ16BlockHeight;
                pageWidth = PSMZ16PageWidth;
                pageHeight = PSMZ16PageHeight;
                break;
            }
        case SCE_GS_PSMZ16S:
        default:
            {
                blockWidth = PSMZ16SBlockWidth;
                blockHeight = PSMZ16SBlockHeight;
                pageWidth = PSMZ16SPageWidth;
                pageHeight = PSMZ16SPageHeight;
                break;
            }
    }

    maxlevel = 1;
    i = height > width? width : height;
    while ((i > 8) && (maxlevel < skyMaxMipLevels))
    {
        maxlevel += 1;
        i >>= 1;
    }

    offsets[0] = 0;
    bufwidth[0] = ((width + pageWidth - 1)/pageWidth) * (pageWidth/64);
    bufheight[0] = ((height + pageHeight -1 )/pageHeight);
    bufstart[0] = 0;

    i = 1;
    interioroffsetx = 0;
    interioroffsety = 0;
    piviot = 0;
    stackp = 0;
    while (i < maxlevel)
    {
        if ((width >= pageWidth) && (height >= pageHeight))
        {
            /* Easy case. Previous level filled a whole number of pages */
            offsets[i] = offsets[i-1]+(width/blockWidth)*(height/blockHeight);
            bufwidth[i] = (((width>>1)+pageWidth-1)/pageWidth)*(pageWidth/64);
            bufheight[i] = (((height>>1) + pageHeight -1 )/pageHeight);
            bufstart[i] = offsets[i];
            interioroffsetx = 0;
            interioroffsety = 0;
        }
        else if ((width < pageWidth) && (height >= pageHeight))
        {
            /* Previous level fills a level vertically, but doesn't
               horizontally. */
            /* We have to catch the case where there isn't any space
               at the right of the block */
            bufstart[i] = bufstart[i-1];
            bufwidth[i] = bufwidth[i-1];
            bufheight[i] = bufheight[i-1];
            if ((interioroffsetx + (width > blockWidth ? width:blockWidth))
                < pageWidth)
            {
                stackx[stackp] = interioroffsetx;
                stackh[stackp] = (height)>blockHeight?(height)
                    :blockHeight;
                stackw[stackp] = (width)>blockWidth?(width):blockWidth;
                stacko[stackp] = offsets[i-1]+(((height)+blockHeight-1)
                                               /blockHeight)
                    *(pageWidth/blockWidth);
                stackp++;
                /* as its all power of 2, we know we can put the new new
                   level next to the old */
                interioroffsetx += width > blockWidth ? width:blockWidth;
                offsets[i] = offsets[i-1]+((width+blockWidth-1)/blockWidth);
            }
            else
            {
                if (stackp)
                {
                    offsets[i] = stacko[stackp-1];
                    if (((width>>1) > blockWidth ? (width>>1):blockWidth)
                        < stackw[stackp-1])
                    {
                        stacko[stackp-1] += (((width>>1)+blockWidth-1)
                                             /blockWidth);
                        stackw[stackp-1] -= ((width>>1)>blockWidth?(width>>1)
                                             :blockWidth);
                    }
                    else if (((height>>1)>blockHeight ?
                              (height>>1):blockHeight) < stackh[stackp-1])
                    {
                        stacko[stackp-1] += (((height>>1)+blockHeight-1)
                                             /blockHeight)
                            *(pageWidth/blockWidth);
                        stackh[stackp-1] -= ( (height>>1)>blockHeight?
                                              (height>>1)
                                              :blockHeight );
                    }
                    else
                    {
                        stackp--;
                    }
                    piviot = 1;
                }
                else
                {
                    /* We need to move down to the end of the level */
                    interioroffsety += ( (height>blockHeight) ?
                                         height:
                                         blockHeight );
                    offsets[i] = offsets[i-1]+((height+blockHeight-1)
                                               /blockHeight)
                        *(pageWidth/blockWidth);
                }
            }
        }
        else if ((width >= pageWidth) && (height <pageHeight))
        {
            /* Previous level is a whole number of pages wide, but there is
               probably some spare space beneath */
            /* Same buf width as last one */
            bufstart[i] = bufstart[i-1];
            bufwidth[i] = bufwidth[i-1];
            bufheight[i] = bufheight[i-1];
            if ((interioroffsety + (height > blockHeight ? height:blockHeight))
                < pageHeight)
            {
                /* as its all power of 2, we know we can put the new new
                   level next to the old */
                interioroffsety += height > blockHeight ? height : blockHeight;
                offsets[i] = offsets[i-1]+((height+blockHeight-1)/blockHeight)
                    *(pageWidth/blockWidth)
                    *(bufwidth[i]/(pageWidth/64));
            }
            else
            {
                /* We need to move down to the end of the level */
                interioroffsetx += width > blockWidth ? width:blockWidth;
                offsets[i] = offsets[i-1]+((width+blockWidth-1)/blockWidth);
            }
        }
        else
        {
            bufstart[i] = bufstart[i-1];
            bufwidth[i] = bufwidth[i-1];
            bufheight[i] = bufheight[i-1];
            /* We make a choice to go in the major direction, ie based on
               which offset is 0 */
            if ((interioroffsetx + (width > blockWidth ? width :blockWidth))
                < (bufwidth[i]*64))
            {
                stackx[stackp] = interioroffsetx;
                stackh[stackp] = (height)>blockHeight?(height)
                    :blockHeight;
                stackw[stackp] = (width)>blockWidth?(width):blockWidth;
                stacko[stackp] = offsets[i-1]+(((height)+blockHeight-1)
                                               /blockHeight)
                    *(pageWidth/blockWidth);
                stackp++;

                interioroffsetx += width > blockWidth ? width:blockWidth;
                offsets[i] = offsets[i-1]+((width+blockWidth-1)/blockWidth);
            }
            else if (((interioroffsety +  ( (height > blockHeight) ?
                                            height:
                                            blockHeight) )
                      < bufheight[i]*pageHeight)
                     && (!piviot))
            {
                interioroffsety += blockHeight ? height:blockHeight;
                offsets[i] = offsets[i-1]+((height+blockHeight-1)/blockHeight)
                    *(pageWidth/blockWidth);
                piviot = i;
            }
            else
            {
                offsets[i] = stacko[stackp-1];
                if (((width>>1) > blockWidth ? (width>>1):blockWidth)
                    < stackw[stackp-1])
                {
                    stacko[stackp-1] += (((width>>1)+blockWidth-1)/blockWidth);
                    stackw[stackp-1] -= ((width>>1) > blockWidth ? (width>>1)
                                         :blockWidth);
                }
                else if (((height>>1) > blockHeight ? (height>>1):blockHeight)
                         < stackh[stackp-1])
                {
                    stacko[stackp-1] += (((height>>1)+blockHeight-1)
                                         /blockHeight)
                        *(pageWidth/blockWidth);
                    stackh[stackp-1] -= ((height>>1) > blockHeight?(height>>1)
                                         :blockHeight);
                }
                else
                {
                    stackp--;
                }
            }
        }

        width >>= 1;
        height >>= 1;

        i++;
    }

    {
        /* If we have a palette */
        /* Can we hide the palette ? */
        if ((bufwidth[maxlevel-1]*64 == width)
            && (bufheight[maxlevel-1]*pageHeight == height))
        {
            if ((format == SCE_GS_PSMT8)
                || (format == SCE_GS_PSMT4))
            {
                palette = bufstart[maxlevel-1]
                    + (bufwidth[maxlevel-1]*64/blockWidth)
                    *(bufheight[maxlevel-1]*pageHeight)/blockHeight;
                palettewidth = 1;
            }
            else
            {
                palette = 0;
                palettewidth = 0;
            }
        }
        else
        {
            if (format == SCE_GS_PSMT8)
            {
                /* bottom four blocks required */
                palette = bufstart[maxlevel-1]+((64*bufwidth[maxlevel-1])
                                                /pageWidth)
                    *bufheight[maxlevel-1]*32
                    -((64*bufwidth[maxlevel-1])/pageWidth)
                    *(pageWidth/blockWidth)
                    -2;
                palettewidth = bufwidth[maxlevel-1];
            }
            else if (format == SCE_GS_PSMT4)
            {
                /* bottom block required */
                palette = bufstart[maxlevel-1]+((64*bufwidth[maxlevel-1])
                                                /pageWidth)
                    *bufheight[maxlevel-1]*32 - 1;
                palettewidth = bufwidth[maxlevel-1];
            }
            else
            {
                palette = 0;
                palettewidth = 0;
            }
        }
    }
    width <<= maxlevel-1;
    height <<= maxlevel-1;
    {
        int result;
        RwUInt32 bw,bs;

        /* Postprocess offsets and add xyoffsets for TRXPOS */
        bs = bufstart[0];
        bw = bufwidth[0];
        for (i=0; i<maxlevel; i++)
        {
            /* Only reset if we have to. This should alow 128xN 8 bits to
               upload in 1 go. */
            if ((bw != bufwidth[i])
                || (((((offsets[i] - bs)*blockWidth)/(bw*64))*blockHeight)
                    > 0x7ff))
            {
                bs = bufstart[i];
                bw = bufwidth[i];
                RWASSERT(offsets[i] == (RwUInt64)bufstart[i]);
                xyoff[i] = 0;
            }
            else
            {
                RwUInt32 x, y;

                x = ((offsets[i] - bs)*blockWidth) % (bw*64);
                y = (((offsets[i] - bs)*blockWidth)/(bw*64))*blockHeight;
                xyoff[i] = (y << 16) | x;
            }
            if (bw*64/pageWidth > 1)
            {
                /* offset is across the full buffer width, but we are
                   tiled in memory */
                offsets[i] = (offsets[i] & ~((32*64*bw/pageWidth)
                                             -(pageWidth/blockWidth)))
                             |((offsets[i] & ((bw*64/blockWidth)
                                              -(pageWidth/blockWidth)))
                               <<((32*64*bw/pageWidth)/(bw*64/blockWidth)-1))
                             |((offsets[i] & ((32*64*bw/pageWidth)
                                              -(bw*64/blockWidth)))
                               >>((bw*64/blockWidth)/(pageWidth/blockWidth)-1));
            }
            GSBlockToAddress(result, offsets[i], format);
            offsets[i] = result;
        }
        /* palette offset is always correct */
        GSBlockToAddress(result, palette, format);
        palette = result;
    }
    *paletteOffset = palette;
    /* Return Size of texture only, in words */

    *texMemSize = (bufstart[maxlevel-1] +
                   ((bufwidth[maxlevel-1]*64)/blockWidth) *
                   ((bufheight[maxlevel-1]*pageHeight)
                    /blockHeight)) *64;

    RWRETURNVOID();
}

/************************************************************************
 * End Insert                                                           *
 ************************************************************************/
#endif /* BETTERPACK */

/****************************************************************************
 skyTransferMinSize

 enforce GS min transfers by pixel format.

 On entry   : format
            : raster extension flags
            : min transfer width return
            : min transfer height return
 */

static void
skyTransferMinSize(long format, RwUInt8 flags, int *minTW, int *minTH)
{
    int mintw = 0;
    int minth = 0;

    RWFUNCTION(RWSTRING("skyTransferMinSize"));
    /* Compute min width and height */
    minth = 1;
    switch (format)
    {
        case SCE_GS_PSMCT32:
        {
            mintw  = PSMCT32MinTW;
            break;
        }
        case SCE_GS_PSMZ32:
        {
            mintw = PSMZ32MinTW;
            break;
        }
        case SCE_GS_PSMCT24:
        {
            mintw = PSMCT24MinTW;
            break;
        }
        case SCE_GS_PSMZ24:
        {
            mintw = PSMZ24MinTW;
            break;
        }
        case SCE_GS_PSMCT16:
        {
            mintw = PSMCT16MinTW;
            break;
        }
        case SCE_GS_PSMCT16S:
        {
            mintw = PSMCT16SMinTW;
            break;
        }
        case SCE_GS_PSMZ16:
        {
            mintw = PSMZ16MinTW;
            break;
        }
        case SCE_GS_PSMZ16S:
        {
            mintw = PSMZ16SMinTW;
            break;
        }
        case SCE_GS_PSMT8:
        {
            mintw = PSMT8MinTW;
            break;
        }
        case SCE_GS_PSMT8H:
        {
            mintw = PSMT8HMinTW;
            break;
        }
        case SCE_GS_PSMT4:
        {
            mintw = PSMT4MinTW;
            break;
        }
        case SCE_GS_PSMT4HL:
        {
            mintw = PSMT4HLMinTW;
            break;
        }
        case SCE_GS_PSMT4HH:
        {
            mintw = PSMT4HHMinTW;
            break;
        }
        default:
            RWASSERT((int)"Duff format" & 0);
    }
    /* Fixup for twiddled case */
    if ((flags & 2)
        && (format == SCE_GS_PSMT8))
    {
        /* assuming that twiddling happens in a block */
        mintw = 16;
        minth = 4; /* column height */
    }
#if 0
    /* This is very complicated if texture is sub page
       sized, so we don't do it for now */
    if ((flags & 2)
        && (format == SCE_GS_PSMT4))
    {
        /* assuming that twiddling happens in a block */
        mintw = 32;
        minth = 4; /* column height */
        /* We also need to mirror width/height */
    }
#endif
    if ((flags & 4)
        && (format == SCE_GS_PSMT4))
    {
        /* assuming that twiddling happens in a block */
        mintw = 32;
        minth = 4; /* column height */
    }
    *minTW = mintw;
    *minTH = minth;
    RWRETURNVOID();
}

/****************************************************************************
 SkyRasterCreate

 Create a raster

 On entry   : NULL
            : pRaster - raster to allocate
            : Flags
 On exit    : TRUE on success
 */
static RwBool
SkyRasterCreate(void * __RWUNUSED__ pInOut, void *pRaster, RwInt32 flags)
{
    static const RwUInt32 rasterPageWidths[32] = {128, 128, 128, 128, 128, 128, 128, 128,
                                                  64,  64,  64,  64,  64,  64,  64,  64,
                                                  64,  64,  64,  64,  64,  64,  64,  64,
                                                  64,  64,  64,  64,  64,  64,  64,  64};
    static const RwUInt32 rasterPageHeights[32] = {128, 128, 128, 128,  64,  64,  64,  64,
                                                   64,  64,  64,  64,  64,  64,  64,  64,
                                                   32,  32,  32,  32,  32,  32,  32,  32,
                                                   32,  32,  32,  32,  32,  32,  32,  32};
    static const RwUInt32 PSCMT3224BlockOffsets[8] = { 0, 2, 2, 8, 8, 10, 10, 32 };
    static const RwUInt32 PSCMT16BlockOffsets[8] = { 0, 1, 4, 5, 16, 17, 20, 21 };
    static const RwUInt32 PSCMT16SBlockOffsets[8] = { 0, 1, 8, 9, 4, 5, 12, 13 };
    static const RwUInt32 PSMT8BlockOffsets[8] = { 0, 2, 2, 8, 8, 10, 10, 32 };
    static const RwUInt32 PSMT4BlockOffsets[8] = { 0, 1, 4, 5, 16, 17, 20, 21 };

    RwRaster      *rpRas = (RwRaster *)pRaster;
    _SkyRasterExt *ext = RASTEREXTFROMRASTER(rpRas);

    RWFUNCTION(RWSTRING("SkyRasterCreate"));

    if (!SkyGetRasterFormat(pInOut,pRaster,flags))
    {
        RWRETURN(FALSE);
    }

    RWASSERT(skyMaxMipLevels <= 7);

    /* Set up */
    rpRas->cpPixels = (unsigned char *)NULL;
    rpRas->palette = (unsigned char *)NULL;
    ext->bLocked = FALSE;
    /* No upload packets at creation */
    ext->cachePkts = FALSE;
    ext->palUploadPkt = (u_long128 *)NULL;
    ext->mipUploadPkts[0] = (u_long128 *)NULL;
    ext->mipUploadPkts[1] = (u_long128 *)NULL;
    ext->mipUploadPkts[2] = (u_long128 *)NULL;
    ext->mipUploadPkts[3] = (u_long128 *)NULL;
    ext->mipUploadPkts[4] = (u_long128 *)NULL;
    ext->mipUploadPkts[5] = (u_long128 *)NULL;
    ext->mipUploadPkts[6] = (u_long128 *)NULL;
    /* Not cached to start with */
    ext->mpCacheEntry = (_SkyMemBlock *)NULL;
    /* Flags are initially all zero */
    ext->flags = 0;

    rpRas->originalWidth = rpRas->width;
    rpRas->originalHeight = rpRas->height;
    rpRas->originalPixels = rpRas->cpPixels;

    /* If is not a camera or Z buffer, then we need to allocate real memory */
    if ((rpRas->width)&&(rpRas->height))
    {
        switch (rpRas->cType)
        {
            case rwRASTERTYPECAMERATEXTURE:
                {
                    if (rpRas->cFormat & ((rwRASTERFORMATPAL4|rwRASTERFORMATPAL8)
                                          >> 8))
                    {
                        /* We can't render into palatised textures */
                        RWERROR((E_RW_INVRASTERFORMAT));
                        RWRETURN(FALSE);
                    }
                    if (rpRas->cFormat & ((rwRASTERFORMATMIPMAP|rwRASTERFORMATAUTOMIPMAP) >> 8))
                    {
                        /* For now we can't render into a mipmapped texture */
                        RWERROR((E_RW_INVRASTERFORMAT));
                        RWRETURN(FALSE);
                    }
                    /* Size restrictions are somewhat complex:
                       We need to be able to use the standard Z-buffer with
                       this texture. So we ensure that the width+63/64*height
                       is less than that of zwidth+63/64*zheight */
                    if ((((rpRas->width+63)>>6)*rpRas->height) >
                        (((videoModes[currentMode].width+63)>>6)
                         *videoModes[currentMode].height))
                    {
                        RWERROR((E_RW_INVRASTERFORMAT));
                        RWRETURN(FALSE);
                    }
                    /* This will drop through into the next case */
                }
            case rwRASTERTYPETEXTURE:
                {
                    RwUInt64 offsetBy64[7], widthBy64[7];
                    RwUInt32 xyoffset[7];
                    RwInt32  bytesPerPalPixel, palHeight, palWidth, overW, overH;
                    RwUInt32 pageWidth, pageHeight, palPageWidth, palPageHeight;

                    pageWidth = rasterPageWidths[rpRas->depth-1];
                    pageHeight = rasterPageHeights[rpRas->depth-1];

                    /* We set up a TEX0 and a size (for caching) if a texture */
                    overW = _rwSkyFindMSB(rpRas->width);
                    if ((1<<overW) < rpRas->width)
                    {
                        overW++;
                    }
                    overH = _rwSkyFindMSB(rpRas->height);
                    if ((1<<overH) < rpRas->height)
                    {
                        overH++;
                    }
                    ext->lsb = ((((RwUInt32)rpRas->width>pageWidth)?
                                 rpRas->width>>6:(pageWidth>>6))<<14 |
                                (overW & 0xf) << 26 |
                                (overH & 0xf) << 30 );
                    ext->msb = ((overH & 0xf) >> 2 |
                                0l << 3 ) ; /* MODULATE */

                    /* We set up mipmapK to be 0 initially */
                    ext->mipmapKL = skyDefaultMipmapKL;

                    /* It's a texture so we cache the packets */
                    ext->cachePkts = TRUE;

                    /* Fill in a type, whether texture alpha exists */
                    /* Don't think that the following looks right (gjd) */
                    /* switch (rpRas->cFormat & (rwRASTERFORMATPAL4|rwRASTERFORMATPAL8 >> 8)) */
                    switch (rpRas->cFormat & ((rwRASTERFORMATPAL4|rwRASTERFORMATPAL8) >> 8))
                    {
                        case (0):
                            {
                                /* No palette for this one */
                                bytesPerPalPixel = palWidth = palHeight = 0;
                                palPageWidth = palPageHeight = 0;

                                switch (rpRas->cFormat & (rwRASTERFORMATPIXELFORMATMASK >> 8))
                                {
                                    case (rwRASTERFORMAT1555>>8):
                                        {
                                            ext->lsb |= PSMCT16S << 20;
                                            ext->msb |= 1l << (34-32);    /* Use RGBA from texture */
                                            break;
                                        }
                                    case (rwRASTERFORMAT888>>8):
                                        {
                                            ext->lsb |= PSMCT24 << 20;
                                            break;
                                        }
                                    case (rwRASTERFORMAT8888>>8):
                                        {
                                            ext->lsb |= PSMCT32 << 20;
                                            ext->msb |= 1l << (34-32);    /* Use RGBA from texture */
                                            break;
                                        }
                                    default:
                                        {
                                /* Unsupported format */
                                            RWERROR((E_RW_INVRASTERFORMAT));
                                            RWRETURN(FALSE);
                                        }
                                }
                                break;
                            }
                        case (rwRASTERFORMATPAL4>>8):
                            {
                                palWidth = 8;
                                palHeight = 2;

                                ext->lsb |= PSMT4 << 20;
                                ext->msb |= 1l << (34-32);    /* Use RGBA from texture */
                                ext->msb |= 1l << (61-32);

                                switch (rpRas->cFormat & (rwRASTERFORMATPIXELFORMATMASK >> 8))
                                {
                                    case (rwRASTERFORMAT1555>>8):
                                        {
                                            ext->msb |= PSMCT16S << (51-32);
                                            bytesPerPalPixel = 2;
                                            palPageWidth = 64;
                                            palPageHeight = 64;
                                            break;
                                        }
                                    case (rwRASTERFORMAT8888>>8):
                                        {
                                            ext->msb |= PSMCT32 << (51-32);
                                            bytesPerPalPixel = 4;
                                            palPageWidth = 64;
                                            palPageHeight = 32;
                                            break;
                                        }
                                    default:
                                        {
                                /* Unsupported format */
                                            RWERROR((E_RW_INVRASTERFORMAT));
                                            RWRETURN(FALSE);
                                        }
                                }
                                break;
                            }
                        case (rwRASTERFORMATPAL8>>8):
                            {
                                palWidth = 16;
                                palHeight = 16;

                                ext->lsb |= PSMT8 << 20;
                                ext->msb |= 1l << (34-32);    /* Use RGBA from texture */
                                ext->msb |= 1l << (61-32);

                                switch (rpRas->cFormat & (rwRASTERFORMATPIXELFORMATMASK >> 8))
                                {
                                    case (rwRASTERFORMAT1555>>8):
                                        {
                                            ext->msb |= PSMCT16S << (51-32);
                                            bytesPerPalPixel = 2;
                                            palPageWidth = 64;
                                            palPageHeight = 64;
                                            break;
                                        }
                                    case (rwRASTERFORMAT8888>>8):
                                        {
                                            ext->msb |= PSMCT32 << (51-32);
                                            bytesPerPalPixel = 4;
                                            palPageWidth = 64;
                                            palPageHeight = 32;
                                            break;
                                        }
                                    default:
                                        {
                                /* Unsupported format */
                                            RWERROR((E_RW_INVRASTERFORMAT));
                                            RWRETURN(FALSE);
                                        }
                                }
                                break;
                            }
                        default:
                            {
                                /* Huh? ! */
                                RWERROR((E_RW_INVRASTERFORMAT));
                                RWRETURN(FALSE);
                            }
                    }

                    /* Set up the stride */
                    rpRas->stride = (rpRas->depth * rpRas->width) >> 3;

                    /* Set up size for texture caching, and calculated texture base pointers */
                    if (rpRas->cFormat & (rwRASTERFORMATMIPMAP >> 8))
                    {
                        RwUInt32 levels, texMemSize, sysMemSize, width, height;
#ifdef BETTERPACK
                        RwUInt32 paletteOffset;
#endif /* BETTERPACK */
                        RwUInt64 prevWidthBy64;
                        RwUInt64 word64;
                        RwUInt32 maxMip;
                        RwUInt32 lastMapPages, lastMapPageWidth, lastMapPageHeight;
                        RwUInt32 sizeBeforePrevMip, blockOffsetBy4;

                        width = rpRas->width;
                        height = rpRas->height;
                        levels = 0;
                        texMemSize = 0;
                        sysMemSize = 0;
                        sizeBeforePrevMip = 0;

                        offsetBy64[0] = offsetBy64[1] = offsetBy64[2] = 0;
                        offsetBy64[3] = offsetBy64[4] = offsetBy64[5] = 0;
                        offsetBy64[6] = 0;
                        widthBy64[0] = widthBy64[1] = widthBy64[2] = 1;
                        widthBy64[3] = widthBy64[4] = widthBy64[5] = 1;
                        widthBy64[6] = 1;

                        prevWidthBy64 = (width > pageWidth) ? (width >> 6) : (pageWidth >> 6);

                        while ((width != 0) && (height != 0)
                               && (levels < (RwUInt32)skyMaxMipLevels) &&
                               ((rpRas->width < 8 || rpRas->height < 8) ||
                                (width >= 8 && height >= 8)))
                        {
                            RwUInt32 stride;
                            RwUInt32 size;

                            size = (rpRas->depth * width * height) >> 3;
                            size = (size + 15) & ~15;

                            /* Space in system memory for this level */
                            sysMemSize += size;

                            widthBy64[levels] = (width > pageWidth) ? (width >> 6) : (pageWidth >> 6);
                            stride = (rpRas->depth * (widthBy64[levels] << 6)) >> 3;

                            /* If we go to a different width, we need to align */
                            if (widthBy64[levels] != prevWidthBy64)
                            {
                                RwUInt32 maxMip = levels-1;

                                RWASSERT(levels > 0);

                                lastMapPageWidth = ((rpRas->width >> maxMip) + (pageWidth-1)) / pageWidth;
                                lastMapPageHeight = ((rpRas->height >> maxMip) + (pageHeight-1)) / pageHeight;
                                lastMapPages = lastMapPageWidth * lastMapPageHeight;
                                texMemSize = ((sizeBeforePrevMip) + (lastMapPages * 2048)) & ~2047;
                            }
                            prevWidthBy64 = widthBy64[levels];

                            /* Align to 64 words */
                            texMemSize = ((texMemSize + 63) & ~63);

                            /* find out how many multiples of 4 blocks we are through
                               current page */
                            blockOffsetBy4 = (texMemSize & 2047) / 256;

                            switch((ext->lsb >> 20) & 0x3F)
                            {
                                case PSMCT32:
                                case PSMCT24:
                                    offsetBy64[levels] = ((texMemSize / 2048) * 32) + PSCMT3224BlockOffsets[blockOffsetBy4];
                                    break;
                                case PSMCT16:
                                    offsetBy64[levels] = ((texMemSize / 2048) * 32) + PSCMT16BlockOffsets[blockOffsetBy4];
                                    break;
                                case PSMCT16S:
                                    offsetBy64[levels] = ((texMemSize / 2048) * 32) + PSCMT16SBlockOffsets[blockOffsetBy4];
                                    break;
                                case PSMT8:
                                    offsetBy64[levels] = ((texMemSize / 2048) * 32) + PSMT8BlockOffsets[blockOffsetBy4];
                                    break;
                                case PSMT4:
                                    offsetBy64[levels] = ((texMemSize / 2048) * 32) + PSMT4BlockOffsets[blockOffsetBy4];
                                    break;
                            }

                            /* cache texmemsize before this mipmap in case we need to
                               align to page boundaries due to page width changes */
                            sizeBeforePrevMip = texMemSize;
                            texMemSize += (height * stride) / 4; /* Four bytes per word */
                            texMemSize = (texMemSize + 63) & ~63;      /* Align on 64 bytes */

                            levels++;
                            width >>= 1;
                            height >>= 1;
                        }

#ifdef BETTERPACK
#if 0
                        lastMapPageWidth = ((rpRas->width >> (levels-1)) + (pageWidth-1)) / pageWidth;
                        lastMapPageHeight = ((rpRas->height >> (levels-1)) + (pageHeight-1)) / pageHeight;
                        lastMapPages = lastMapPageWidth * lastMapPageHeight;
printf("old: %d\n", ((offsetBy64[levels-1] * 64) + (lastMapPages * 2048))&~2047);
#endif
                        calcOffsets(rpRas->width, rpRas->height,
                                    ((ext->lsb>>20)&0x3F), offsetBy64,
                                    widthBy64, xyoffset, &texMemSize,
                                    &paletteOffset);
#if 0
printf("new: %d\n", texMemSize);
#endif
#endif /* BETTERPACK */

                        /* So how much memory do we need */
                        ext->sysMemSize = sysMemSize;

                        /* How many mipmap levels do we have? */
                        maxMip = levels-1;
                        ext->maxMipLevel = maxMip << 2;

                        /* Set up the base addresses for the mipmap */
                        word64 = ((offsetBy64[1]&0x3fff) <<  0)
                            | (widthBy64[1] << 14) |
                            ((offsetBy64[2]&0x3fff) << 20)
                            | (widthBy64[2] << 34) |
                            ((offsetBy64[3]&0x3fff) << 40)
                            | (widthBy64[3] << 54);
                        ext->miptbp1Lsb = (RwUInt32)(word64);
                        ext->miptbp1Msb = (RwUInt32)(word64 >> 32);
#if defined(GSB) && defined(GSPLUS)
                        word64 = ((offsetBy64[1]&0x1ffff) <<  0) |
                            ((offsetBy64[2]&0x1ffff) << 20) |
                            ((offsetBy64[3]&0x1ffff) << 40);
                        ext->miptbp3Lsb = (RwUInt32)(word64);
                        ext->miptbp3Msb = (RwUInt32)(word64 >> 32);
#endif /* defined(GSB) && defined(GSPLUS) */
                        word64 = ((offsetBy64[4]&0x3fff) <<  0)
                            | (widthBy64[4] << 14) |
                            ((offsetBy64[5]&0x3fff) << 20)
                            | (widthBy64[5] << 34) |
                            ((offsetBy64[6]&0x3fff) << 40)
                            | (widthBy64[6] << 54);
                        ext->miptbp2Lsb = (RwUInt32)(word64);
                        ext->miptbp2Msb = (RwUInt32)(word64 >> 32);
#if defined(GSB) && defined(GSPLUS)
                        word64 = ((offsetBy64[4]&0x1ffff) <<  0) |
                            ((offsetBy64[5]&0x1ffff) << 20) |
                            ((offsetBy64[6]&0x1ffff) << 40);
                        ext->miptbp4Lsb = (RwUInt32)(word64);
                        ext->miptbp4Msb = (RwUInt32)(word64 >> 32);
#endif /* defined(GSB) && defined(GSPLUS) */

                        ext->sysMemPalSize = palWidth * palHeight * bytesPerPalPixel;
#ifndef HIDEPALETTE
                        /* Round up to next page size */
                        lastMapPageWidth = ((rpRas->width >> maxMip) + (pageWidth-1)) / pageWidth;
                        lastMapPageHeight = ((rpRas->height >> maxMip) + (pageHeight-1)) / pageHeight;
                        lastMapPages = lastMapPageWidth * lastMapPageHeight;
                        ext->nTexCacheSize = ((offsetBy64[maxMip] * 64) + (lastMapPages * 2048)) & ~2047;

                        /* Sort out the palette alignment */
                        if (ext->sysMemPalSize)
                        {
                            /* Bang in the palette offset (Need to align to new page, 'cos it's a different format) */
                            ext->palOffset = ext->nTexCacheSize>>6;

                            /* Now re-align to next page boundary for size */
                            lastMapPageWidth = (palWidth + (palPageWidth-1)) / palPageWidth;
                            lastMapPageHeight = (palHeight + (palPageHeight-1)) / palPageHeight;
                            lastMapPages = lastMapPageWidth * lastMapPageHeight;
                            ext->nTexCacheSize = (ext->nTexCacheSize + (lastMapPages * 2048)) & ~2047;
                        }
                        else
                        {
                            ext->palOffset = 0;
                        }
#else /* !HIDEPALETTE */
#ifdef BETTERPACK
                        ext->nTexCacheSize = texMemSize;
                        if (ext->sysMemPalSize)
                        {
                            ext->palOffset = paletteOffset;
                            if ((paletteOffset<<6)==texMemSize)
                            {
                                /* Palette takes the next page */
                                ext->nTexCacheSize += 2048;
                            }
                        }
                        else
                        {
                            ext->palOffset = 0;
                        }
#else /* BETTERPACK */
                        /* Sort out the palette alignment */
#if 1
                        if (ext->sysMemPalSize)
                        {
                            lastMapPageWidth = ((rpRas->width >> maxMip)
                                                + (pageWidth-1)) / pageWidth;
                            if (palHeight == 2)
                            {
                                int blockStart;

                                /* Convert offset back into block */
                                blockStart = (offsetBy64[maxMip]&1)
                                    | ((offsetBy64[maxMip]&4)>>1)
                                    | ((offsetBy64[maxMip]&16)>>2);
                                /* Round to next full page */
                                lastMapPageHeight = ((((rpRas->height >> maxMip)
                                                       +15)>>4)+blockStart+7)
                                                                      >>3;
                                lastMapPages = lastMapPageWidth
                                    * lastMapPageHeight;
                                ext->nTexCacheSize = ((offsetBy64[maxMip] * 64)
                                                      + (lastMapPages * 2048))
                                    & ~2047;
                                /* We are a 16 entry palette which will fit */
                                /* in a single block */
                                if ((rpRas->width >> maxMip) < 128)
                                {
                                /* Space on the end of a row of blocks */
                                /* We know that we always start on the */
                                /* beginning of a row */
                                /* 0->10, 1->11, 4->14, 5->15, 16->26, */
                                /* 17->27, 20->30, 21->31 */
                                    RWASSERT(!(offsetBy64[maxMip]&31&10));
                                    ext->palOffset = offsetBy64[maxMip] + 10;

#if 0
                                /* Round to next full page */
                                    lastMapPageHeight = ((((rpRas->height >> maxMip)
                                                           +15)>>4)+blockStart+7)
                                                                          >>3;
                                    lastMapPages = lastMapPageWidth
                                        * lastMapPageHeight;
                                    ext->nTexCacheSize = ((offsetBy64[maxMip] * 64)
                                                          + (lastMapPages * 2048))
                                        & ~2047;
#endif
                                }
                                else
                                {
                                /* Have to put it after texture */
                                    if ((((rpRas->height
                                           >> maxMip)+15)>>4)+blockStart < 8)
                                    {
                                        /* Space on end of block */
                                        ext->palOffset = (offsetBy64[maxMip]&~31)
                                            + PSMT4BlockOffsets[(((rpRas->height
                                                                   >> maxMip)+15)
                                                                 >>4)+blockStart];
                                        /* Strictly ....
                                           lastMapPageHeight = ((((rpRas->height
                                           >> maxMip)
                                           +15)>>4)
                                           +blockStart+7+1) >>3;
                                           but the if condition protects us, so
                                           we factor out common code */
#if 0
                                        lastMapPageHeight = ((((rpRas->height
                                                                >> maxMip)
                                                               +15)>>4)
                                                             +blockStart+7) >>3;
                                        lastMapPages = lastMapPageWidth
                                            * lastMapPageHeight;
                                        ext->nTexCacheSize = ((offsetBy64[maxMip]
                                                               *64)
                                                              +(lastMapPages*2048))
                                            & ~2047;
#endif
                                    }
                                    else
                                    {
                                        /* Its going to have to have a page to */
                                        /* itself. Sob */
#if 0
                                        lastMapPageHeight = ((((rpRas->height
                                                                >> maxMip)
                                                               +15)>>4)
                                                             +blockStart+7) >>3;
                                        lastMapPages = lastMapPageWidth
                                            * lastMapPageHeight;
                                        ext->nTexCacheSize = ((offsetBy64[maxMip]
                                                               *64)
                                                              +(lastMapPages*2048))
                                            & ~2047;
#endif
                                        ext->palOffset = ext->nTexCacheSize>>6;
                                        ext->nTexCacheSize += 2048;
                                    }
                                }
                            }
                            else
                            {
                                /* must be a 256 entry palette */
                                /* A palette takes either 1x2 or 2x2 blocks */
                                /* For simplicity, I assume 2x2 */
                                int blockStart;

                                /* Convert offset back into block */
                                blockStart = ((offsetBy64[maxMip]&2)>>1)
                                    | ((offsetBy64[maxMip]&8)>>2);
                                /* Round to next full page */
                                lastMapPageHeight = ((((rpRas->height >> maxMip)
                                                       +15)>>4)+blockStart+3)
                                                                      >>2;
                                lastMapPages = lastMapPageWidth
                                    * lastMapPageHeight;
                                ext->nTexCacheSize = ((offsetBy64[maxMip] * 64)
                                                      + (lastMapPages * 2048))
                                    & ~2047;
                                /* if the last mip is < 128 wide and is 2 blocks */
                                /* from end, or spans 2 pages, we can put it in */
                                /* the bottom right of the final page */
                                if (((rpRas->width >> maxMip) < 128)
                                    && ((blockStart < 3)
                                        ||((((rpRas->height >> maxMip)+15)>>4)>1)))
                                {
                                /* Space on the end of a row of blocks */
                                /* We know that we always start on the */
                                /* beginning of a row */
                                /* 0,2,8->28 */
                                    RWASSERT(!(offsetBy64[maxMip]&31&21));
                                    ext->palOffset = (offsetBy64[maxMip]&~31) + 28;
                                /* If it spans two pages */
                                /* Second half of conditional strictly
                                   unecessary, but left in as not speed
                                   critical code path */
                                    if ((blockStart == 3)
                                        && ((((rpRas->height >> maxMip)+15)>>4)>1))
                                    {
                                        ext->palOffset += 32;
                                    }
#if 0
                                /* Round to next full page */
                                    lastMapPageHeight = ((((rpRas->height >> maxMip)
                                                           +15)>>4)+blockStart+3)
                                                                          >>2;
                                    lastMapPages = lastMapPageWidth
                                        * lastMapPageHeight;
                                    ext->nTexCacheSize = ((offsetBy64[maxMip] * 64)
                                                          + (lastMapPages * 2048))
                                        & ~2047;
#endif
                                }
                                else
                                {
                                /* Have to put it after texture */
                                    if ((((rpRas->height
                                           >> maxMip)+15)>>4)+blockStart < 3)
                                    {
                                        /* Space on end of block */
                                        ext->palOffset = (offsetBy64[maxMip]&~31)
                                            + PSMT8BlockOffsets[(((rpRas->height
                                                                   >> maxMip)+15)
                                                                 >>4)+blockStart];
                                        /* Strictly ....
                                           lastMapPageHeight = ((((rpRas->height
                                           >> maxMip)
                                           +15)>>4)
                                           +blockStart+3+2) >>2;
                                           but the if condition protects us, so
                                           we factor out common code */
#if 0
                                        lastMapPageHeight = ((((rpRas->height
                                                                >> maxMip)
                                                               +15)>>4)
                                                             +blockStart+3) >>2;
                                        lastMapPages = lastMapPageWidth
                                            * lastMapPageHeight;
                                        ext->nTexCacheSize = ((offsetBy64[maxMip]
                                                               *64)
                                                              +(lastMapPages*2048))
                                            & ~2047;
#endif
                                    }
                                    else
                                    {
                                        /* Its going to have to have a page to */
                                        /* itself. Sob */
#if 0
                                        lastMapPageHeight = ((((rpRas->height
                                                                >> maxMip)
                                                               +15)>>4)
                                                             +blockStart+3) >>2;
                                        lastMapPages = lastMapPageWidth
                                            * lastMapPageHeight;
                                        ext->nTexCacheSize = ((offsetBy64[maxMip]
                                                               *64)
                                                              +(lastMapPages*2048))
                                            & ~2047;
#endif
                                        ext->palOffset = ext->nTexCacheSize>>6;
                                        ext->nTexCacheSize += 2048;
                                    }
                                }
                            }
                        }
                        else
#endif
                        {
                            /* Round up to next page size */
                            lastMapPageWidth = ((rpRas->width >> maxMip)
                                                + (pageWidth-1)) / pageWidth;
                            lastMapPageHeight = ((rpRas->height >> maxMip)
                                                 + (pageHeight-1)) / pageHeight;
                            lastMapPages = lastMapPageWidth * lastMapPageHeight;
                            ext->nTexCacheSize = ((offsetBy64[maxMip] * 64)
                                                  + (lastMapPages * 2048)) & ~2047;
                            ext->palOffset = 0;
                        }
#endif /* BETTERPACK */
#endif /* !HIDEPALETTE */
                    }
                    else
                    {
                        RwUInt64 word64;
                        RwUInt32 lastMapPages, lastMapPageWidth, lastMapPageHeight;
                        RwUInt32 widthByPage;
                        RwUInt32 size;

                        size = rpRas->height * rpRas->stride;
                        size = (size + 15) & ~15;
                        ext->sysMemSize = size;

                        /* Set to something sensible */
                        word64 = (1l << 14) | (1l << 34) | (1l << 54);
                        ext->miptbp1Lsb = (RwUInt32)(word64);
                        ext->miptbp1Msb = (RwUInt32)(word64 >> 32);
                        ext->miptbp2Lsb = (RwUInt32)(word64);
                        ext->miptbp2Msb = (RwUInt32)(word64 >> 32);
                        ext->maxMipLevel = 0;

                        /* So that we can reference in common code below */
                        offsetBy64[0] = 0;
                        widthBy64[0] = ((rpRas->width + pageWidth-1)/pageWidth)
                                       *(pageWidth/64);
                        xyoffset[0] = 0;

                        /* Round up to next page size */
                        widthByPage = (rpRas->width + pageWidth-1) / pageWidth;
                        lastMapPageHeight = (rpRas->height + (pageHeight-1)) / pageHeight;
                        lastMapPages = widthByPage * lastMapPageHeight;
                        ext->nTexCacheSize = ((lastMapPages * 2048) & ~2047);

                        /* Sort out the palette alignment */
                        ext->sysMemPalSize = palWidth * palHeight * bytesPerPalPixel;
                        if (ext->sysMemPalSize)
                        {
#ifdef HIDEPALETTE
                            if (((RwInt32)(widthByPage*pageWidth)> rpRas->width)
                                ||((RwInt32)(lastMapPageHeight*pageHeight)
                                   > rpRas->height))
                            {
                                /* We can definately hide the palette in the
                                   bottom corner of the page */
                                ext->palOffset = (ext->nTexCacheSize>>6)-4;
                            }
                            else
#endif /* HIDEPALETTE */
                            {
                                /* Bang in the palette offset (Need to align
                                   to new page, 'cos it's a different format) */
                                ext->palOffset = ext->nTexCacheSize>>6;

                                /* Now re-align to next page boundary for size*/
                                lastMapPageWidth = (palWidth + (palPageWidth-1))
                                                   / palPageWidth;
                                lastMapPageHeight = (palHeight + (palPageHeight
                                                                  -1))
                                                    / palPageHeight;
                                lastMapPages = lastMapPageWidth
                                               * lastMapPageHeight;
                                ext->nTexCacheSize = (ext->nTexCacheSize
                                                      + (lastMapPages * 2048))
                                                     & ~2047;
                            }
                        }
                        else
                        {
                            ext->palOffset = 0;
                        }
                    }

                    /* Make the system memory size a multiple of 16 bytes */
                    ext->sysMemSize = (ext->sysMemSize + 15) & ~15;

                    /* We now decide if we can use the new texture upload
                       format. This is limited to mip 0 being less than
                       equal to 32767 qw in size */
#ifdef ALTTEXFORM
                    if ((!skyNoNewStyleRasters)
                        && ((((rpRas->height*rpRas->width*rpRas->depth)>>3)>>4)
                            < 32767))
                    {
                        int minTW;
                        int minTH;
                        unsigned long tmp, tmp1;
                        u_long128 ltmp, giftag, giftag1;
                        u_long128 *data, *data1;
                        int w,h,i;
                        int pktSize;
                        RwUInt64 palxyoffset = 0;
                        int pixelDataSize;
                        int prefixDataSize;

                        /* new format 1 */
                        ext->flags |= 1;

                        /* Do we intend to twiddle? */
                        /* This should really be user controlled as it */
                        /* affects the min width and heights that can */
                        /* be transfered */
                        if (((ext->lsb>>20)&0x3f) == SCE_GS_PSMT8)
                        {
                            ext->flags |= 2;
                            if (((ext->msb >> (51-32)) & 0xf) == SCE_GS_PSMCT32)
                            {
                                /* Final mip must be 1 page in width */
                                if (widthBy64[((ext->maxMipLevel) >> 2)] == 2)
                                {
                                    RwUInt32 bs;
                                    RwUInt32 x, y;

                                    /* Figure out where effective buffer
                                       start is given the offset of last
                                       mip level */

                                    x = xyoffset[((ext->maxMipLevel)>>2)]&0x7ff;
                                    y = (xyoffset[((ext->maxMipLevel)>>2)]>>16)
                                        & 0x7ff;
                                    bs = PSMT8AddressToBlock(offsetBy64[((
                                                         ext->maxMipLevel)>>2)])
                                         - (x / PSMT8BlockWidth)
                                         - ((y / PSMT8BlockHeight)
                                            * (PSMT8PageWidth/PSMT8BlockWidth));

                                    x = ((PSMCT32AddressToBlock(ext->palOffset)
                                          -bs)*PSMCT32BlockWidth)
                                        % 64;
                                    y = (((PSMCT32AddressToBlock(ext->palOffset)
                                           -bs)*PSMCT32BlockWidth)
                                         /64)*PSMCT32BlockHeight;

                                    /* We have to be able to generate a valid
                                       TRXPOS from the pal offset */
                                    if (y < 0x800)
                                    {
                                        palxyoffset = (RwUInt64)((y << 16) | x)
                                                                <<32;
                                    }
                                    else
                                    {
                                        palxyoffset = 0;
                                    }
                                }
                                else
                                {
                                    palxyoffset = 0;
                                }
                            }
                        }
                        if (((ext->lsb>>20)&0x3f) == SCE_GS_PSMT4)
                        {
                            ext->flags |= 4;
                            /* One day we will twiddle palettes so that they
                               always seem to be SCE_GS_PSMCT16 for 4 bit */
                            if (((ext->msb >> (51-32)) & 0xf) == SCE_GS_PSMCT16)
                            {
                                /* Final mip must be 1 page in width */
                                if (widthBy64[((ext->maxMipLevel) >> 2)] == 2)
                                {
                                    RwUInt32 bs;
                                    RwUInt32 x, y;

                                    /* Figure out where effective buffer
                                       start is given the offset of last
                                       mip level */

                                    x = xyoffset[((ext->maxMipLevel)>>2)]&0x7ff;
                                    y = (xyoffset[((ext->maxMipLevel)>>2)]>>16)
                                        & 0x7ff;
                                    bs = PSMT4AddressToBlock(offsetBy64[((
                                                         ext->maxMipLevel)>>2)])
                                         - (x / PSMT4BlockWidth)
                                         - ((y / PSMT4BlockHeight)
                                            * (PSMT4PageWidth/PSMT4BlockWidth));

                                    x = ((PSMCT16AddressToBlock(ext->palOffset)
                                          -bs)*PSMCT16BlockWidth)
                                        % 64;
                                    y = (((PSMCT16AddressToBlock(ext->palOffset)
                                           -bs)*PSMCT16BlockWidth)
                                         /64)*PSMCT16BlockHeight;

                                    /* We have to be able to generate a valid
                                       TRXPOS from the pal offset */
                                    if (y < 0x800)
                                    {
                                        palxyoffset = (RwUInt64)((y << 16) | x)
                                                                <<32;
                                    }
                                    else
                                    {
                                        palxyoffset = 0;
                                    }
                                }
                                else
                                {
                                    palxyoffset = 0;
                                }
                            }
                        }

                        /* We need to compute the main memory size */
                        /* Each mip needs gif + 3 reg + gif <+ data> = 5 qw */
                        ext->sysMemSize = (5*(((ext->maxMipLevel) >> 2)+1))<<4;

                        /* Compute min width and height */
                        skyTransferMinSize(((ext->lsb>>20)&0x3f), ext->flags,
                                           &minTW, &minTH);

                        w = rpRas->width;
                        h = rpRas->height;
                        /* add space for each mip level's data */
                        for (i = 0; i <= ((ext->maxMipLevel) >> 2); i++)
                        {
                            int levelWidth, levelHeight;

                            levelWidth  = w < minTW? minTW : w;
                            levelHeight = h < minTH? minTW : h;
                            ext->sysMemSize += (((levelWidth * levelHeight
                                                  *rpRas->depth)>>3)+15) & ~0xf;
                            w >>= 1;
                            h >>= 1;
                        }
                        pixelDataSize = ext->sysMemSize;
                        /* And similarly for the palette */
                        if (ext->sysMemPalSize)
                        {
                            /* we know that this is a qw multiple */
                            if (palHeight == 2)
                            {
                                palHeight = 3;
                            }
                            ext->sysMemPalSize = palWidth * palHeight
                                                 * bytesPerPalPixel + (5<<4);
                        }

                        /* We also need a prefix pseudo packet that contains */
                        /* an upload template */
                        /* <address of mip0>   <qwc of template>
                                               <cnt2>
                           <A+D>               <gif1>
                           <blit>              <vals>
                           <address of pkt0>   <ref>
                                               <cnt2>
                           <A+D>               <gif1>
                           <blit>              <vals>
                           <address of pkt0>   <ref>
                           ...                                     */
                        prefixDataSize = 16;
                        pktSize = 0;

                        for (i = 0; i <= ((ext->maxMipLevel) >> 2); i++)
                        {
                            if (xyoffset[i] == 0)
                            {
                                prefixDataSize += 4<<4;
                                pktSize++;
                            }
                        }
                        if (ext->sysMemPalSize)
                        {
                            prefixDataSize += 4<<4;
                            pktSize++;
                        }

                        /* Allocate with both "raster" and "palette" on 128s */
                        if ((ext->sysMemPalSize) && !(palxyoffset))
                        {
                        rpRas->cpPixels = (RwUInt8 *)RwSky2Malloc(
                                              /* Prefix + alow to next 8qw */
                                              (prefixDataSize+112)
                                              /* data rounded up to 8qw */
                                              + ((ext->sysMemSize+127)&~0x7f)
                                              /* palette rounded to cacheline */
                                              + ((ext->sysMemPalSize+63)&~63));
                        }
                        else
                        {
                        rpRas->cpPixels = (RwUInt8 *)RwSky2Malloc(
                                              /* Prefix + alow to next 8qw */
                                              (prefixDataSize+112)
                                              /* data rounded up to cacheline */
                                              + ((ext->sysMemSize
                                                  +ext->sysMemPalSize+63)
                                                 &~0x3f));
                        }

                        if (!rpRas->cpPixels)
                        {
                            RWERROR((E_RW_NOMEM, (prefixDataSize+112) + ((ext->sysMemSize+127)&~0x7f) + ((ext->sysMemPalSize+63)&~0x3f)));
                            RWRETURN(FALSE);
                        }

                        ((RwUInt32*)(rpRas->cpPixels))[0] = pktSize;
                        ((RwUInt8**)(rpRas->cpPixels))[1] = (RwUInt8 *)
                                      (((RwUInt32)rpRas->cpPixels
                                                             +16
                                                             + ((pktSize*4)<<4)
                                                             + 0x7f) & ~0x7f);
                        ((RwUInt32*)(rpRas->cpPixels))[2] = pktSize;

                        /* If we have a palxyoffset, we will fix up later */
                        if ((ext->sysMemPalSize) && !(palxyoffset))
                        {
                            /* System memory pointer for palette */
                            rpRas->palette = (RwUInt8 *)((((int)rpRas->cpPixels
                                                           +prefixDataSize+112)
                                                          &~0x7f)
                                                         +((ext->sysMemSize
                                                            +127)&~0x7f));
                            /* rather nasty */
                            /* This points to the actual palette data, but */
                            /* valid packet data prefixes it */
                            rpRas->palette = rpRas->palette + (5<<4);
                        }

                        /* Layout giftags etc */
                        data = (u_long128*)((RwUInt8**)(rpRas->cpPixels))[1];
                        data1 = (u_long128*)(rpRas->cpPixels) + 1;

                        tmp = /* NLOOP */ 3l
                            | /* EOP */ (0l<<15)
                            | /* PRE */ (0l<<46)
                            | /* FLG */ (0l<<58)
                            | /* NREG */ (1l<<60);
                        tmp1 = /* A+D */ (0xel<<(64-64));

                        MAKE128(giftag, tmp1, tmp);

                        tmp = /* NLOOP */ 1l
                            | /* EOP */ (0l<<15)
                            | /* PRE */ (0l<<46)
                            | /* FLG */ (0l<<58)
                            | /* NREG */ (1l<<60);
                        tmp1 = /* A+D */ (0xel<<(64-64));

                        MAKE128(giftag1, tmp1, tmp);

                        w = rpRas->width;
                        h = rpRas->height;
                        for (i = 0; i <= ((ext->maxMipLevel) >> 2); i++)
                        {
                            int levelWidth, levelHeight;

                            levelWidth  = w < minTW? minTW : w;
                            levelHeight = h < minTH? minTH : h;

                            *data++ = giftag;

                            if (((ext->flags & 2)
                                 && (((ext->lsb>>20)&0x3f) == SCE_GS_PSMT8))
                                || ((ext->flags & 4)
                                    && (((ext->lsb>>20)&0x3f) == SCE_GS_PSMT4)))
                            {
                                /* Offsets are half */
                                tmp = (long)(xyoffset[i]&~0x10001) << 31;
                            }
                            else
                            {
                                tmp = (long)xyoffset[i] << 32;
                            }
                            tmp1 = GS_TRXPOS;
                            MAKE128(ltmp, tmp1, tmp);
                            *data++ = ltmp;

                            if (((ext->flags & 2)
                                 && (((ext->lsb>>20)&0x3f) == SCE_GS_PSMT8))
                                || ((ext->flags & 4)
                                    && (((ext->lsb>>20)&0x3f) == SCE_GS_PSMT4)))
                            {
                                /* Need to adjust for half size blocks */
                                tmp = (levelWidth>>1)
                                      | (((long)levelHeight>>1) << 32);
                            }
                            else
                            {
                                tmp = levelWidth | ((long)levelHeight << 32);
                            }
                            tmp1 = GS_TRXREG;
                            MAKE128(ltmp, tmp1, tmp);
                            *data++ = ltmp;

                            tmp = 0;
                            tmp1 = GS_TRXDIR;
                            MAKE128(ltmp, tmp1, tmp);
                            *data++ = ltmp;

                            /* An image mode gif tag for the data */
                            tmp = ((((levelWidth * levelHeight
                                                * rpRas->depth)>>3)+15) >> 4)
                                  | (2l << 58);
                            pktSize = tmp & 0x7fff;
                            MAKE128(ltmp, 0, tmp);
                            *data++ = ltmp;

                            /* Build dma pkt data */
                            if (xyoffset[i] == 0)
                            {
                                tmp = (1l<<28)|2l;
                                tmp1 = ((0x50l << 24) | 2l) << 32;
                                MAKE128(ltmp, tmp1, tmp);
                                *data1++ = ltmp;

                                *data1++ = giftag1;

                                if ((ext->flags & 2)
                                    && (((ext->lsb>>20)&0x3f) == SCE_GS_PSMT8))
                                {
                                    tmp = ((widthBy64[i]&0x3e) << 15)
                                           | (SCE_GS_PSMCT32 << 24)
                                           | (offsetBy64[i] << 32)
                                           | ((widthBy64[i]&0x3e) << 47)
                                           | ((long)SCE_GS_PSMCT32 << 56);
                                }
                                else if ((ext->flags & 4)
                                     && (((ext->lsb>>20)&0x3f) == SCE_GS_PSMT4))
                                {
                                    tmp = ((widthBy64[i]&0x3e) << 15)
                                           | (SCE_GS_PSMCT16 << 24)
                                           | (offsetBy64[i] << 32)
                                           | ((widthBy64[i]&0x3e) << 47)
                                           | ((long)SCE_GS_PSMCT16 << 56);
                                }
                                else
                                {
                                    tmp = (widthBy64[i] << 16)
                                           | (((ext->lsb>>20)&0x3f) << 24)
                                           | (offsetBy64[i] << 32)
                                           | (widthBy64[i] << 48)
                                           | ((long)((ext->lsb>>20)&0x3f)<<56);
                                }
                                MAKE128(ltmp, GS_BITBLTBUF, tmp);
                                *data1++ = ltmp;

                                /* Build ref */
                                tmp = (3l<<28) | (pktSize+5)
                                      | (((long)(int)(data - 5)) << 32);
                                tmp1 = ((0x50l << 24) | (pktSize+5)) << 32;
                                MAKE128(ltmp, tmp1, tmp);
                                *data1++ = ltmp;
                            }
                            else
                            {
                                /* Inc qwc in previous ref tag */
                                tmp = *(RwUInt32*)(data1-1);
                                tmp = (3l << 28) | ((tmp & 0xffff)+(pktSize+5));
                                *(RwUInt32*)(data1-1) = tmp;

                                tmp = *(((RwUInt32*)data1)-1);
                                tmp = (0x50l << 24)
                                      | ((tmp & 0xffff)+(pktSize+5));
                                *(((RwUInt32*)data1)-1) = tmp;
                            }


                            /* Skip the actual data */
                            data += pktSize;

                            w >>= 1;
                            h >>= 1;
                        }

                        if (ext->sysMemPalSize)
                        {
                            if (palxyoffset)
                            {
                                /* Palette pkt follows mip levels. Pointer
                                   is set to actual data */
                                rpRas->palette = (RwUInt8*)(data+5);
                            }

                            /* Add tags for palette */
                            data = (u_long128*)(rpRas->palette) - 5;
                            *data++ = giftag;

                            tmp1 = GS_TRXPOS;
                            MAKE128(ltmp, tmp1, palxyoffset);
                            *data++ = ltmp;

                            tmp = palWidth | ((long)palHeight << 32);
                            tmp1 = GS_TRXREG;
                            MAKE128(ltmp, tmp1, tmp);
                            *data++ = ltmp;

                            tmp = 0;
                            tmp1 = GS_TRXDIR;
                            MAKE128(ltmp, tmp1, tmp);
                            *data++ = ltmp;

                            /* An image mode gif tag for the data */
                            tmp = ((palWidth * palHeight * bytesPerPalPixel
                                    +15) >> 4)
                                  | (2l << 58);
                            MAKE128(ltmp, 0, tmp);
                            *data++ = ltmp;
                            pktSize = tmp & 0x7fff;

                            tmp = (1l<<28)|2l;
                            tmp1 = ((0x50l << 24) | 2l) << 32;
                            MAKE128(ltmp, tmp1, tmp);
                            *data1++ = ltmp;

                            *data1++ = giftag1;

                            if (palxyoffset)
                            {
                                /* Nick the bitbltbuf reg value from the
                                   last one */
                                *data1 = *(data1 - 4);
                                data1++;

                                /* Dec pktsize by 1. We have built the upload
                                   pkt so that we have the option of uploading
                                   the palette on its own. */
                                ((RwUInt32*)(rpRas->cpPixels))[0]--;

                                /* Add to previous ref */
                                tmp = *(RwUInt32*)(data1-4);
                                tmp = (3l << 28) | ((tmp & 0xffff)+(pktSize+5));
                                *(RwUInt32*)(data1-4) = tmp;

                                tmp = *(((RwUInt32*)(data1-3))-1);
                                tmp = (0x50l << 24)
                                      | ((tmp & 0xffff)+(pktSize+5));
                                *(((RwUInt32*)(data1-3))-1) = tmp;
                            }
                            else
                            {
                                tmp = (1l << 16)
                                       | ((long)((ext->msb>>(51-32))&0xf)<<24)
                                       | ((long)ext->palOffset << 32)
                                       | (1l << 48)
                                       | ((long)((ext->msb>>(51-32))&0xf)<<56);
                                MAKE128(ltmp, GS_BITBLTBUF, tmp);
                                *data1++ = ltmp;
                            }

                            /* Build ref */
                            tmp = (3l<<28) | (pktSize+5)
                                  | (((long)(int)(data - 5)) << 32);
                            tmp1 = ((0x50l << 24) | (pktSize+5)) << 32;
                            MAKE128(ltmp, tmp1, tmp);
                            *data1++ = ltmp;
                        }
                    }
                    else
#endif /* ALTTEXFORM */
                    {
                        /* Revert to old code */

                        /* Allocate space (for pixels and palette) */
                        rpRas->cpPixels = (RwUInt8 *)RwSky2Malloc(ext->sysMemSize + ext->sysMemPalSize);

                        if (!rpRas->cpPixels)
                        {
                            RWERROR((E_RW_NOMEM, ext->sysMemSize + ext->sysMemPalSize));
                            RWRETURN(FALSE);
                        }

                        if (ext->sysMemPalSize)
                        {
                            /* System memory pointer for palette */
                            rpRas->palette = rpRas->cpPixels + ext->sysMemSize;
                        }

#ifdef TWIDDLEEIGHT
                        /* Permit old style textures to be twiddled too */
                        if (rpRas->depth == 8)
                        {
                            ext->flags |= 2;
                        }
#if 0
                        if (rpRas->depth == 4)
                        {
                            ext->flags |= 4;
                        }
#endif
#endif

                        skyPrepareUploadRaster(rpRas);
                    }

                    rpRas->originalPixels = rpRas->cpPixels;
                    rpRas->originalStride = rpRas->stride;

                    if (ext->flags & 1)
                    {
                        /* Need to adjust cpPixels */
                        rpRas->cpPixels = (RwUInt8*)
                             ((u_long128*)((RwUInt8**)(rpRas->cpPixels))[1]+5);
                    }

                    break;
                }
            case rwRASTERTYPENORMAL:
                {
                    RwUInt32 lastMapPages, lastMapPageWidth, lastMapPageHeight;
                    RwUInt32 pageWidth, pageHeight;

                    pageWidth = rasterPageWidths[rpRas->depth-1];
                    pageHeight = rasterPageHeights[rpRas->depth-1];

                    /* Set up the stride */
                    rpRas->stride = (rpRas->depth * rpRas->width) >> 3;

                    /* Doesn't hurt to fill this in */
                    ext->sysMemSize = rpRas->height * rpRas->stride;

                    /* Round up to next page size in video memory */
                    lastMapPageWidth = (rpRas->width + (pageWidth-1)) / pageWidth;
                    lastMapPageHeight = (rpRas->height + (pageHeight-1)) / pageHeight;
                    lastMapPages = lastMapPageWidth * lastMapPageHeight;
                    ext->nTexCacheSize = lastMapPages * 2048;

                    if (!(rpRas->cFlags & (RwUInt8) rwRASTERDONTALLOCATE))
                    {
                        /* Allocate space */
                        rpRas->cpPixels = (RwUInt8 *)RwSky2Malloc(ext->sysMemSize);

                        if (!rpRas->cpPixels)
                        {
                            RWERROR((E_RW_NOMEM, ext->sysMemSize));
                            RWRETURN(FALSE);
                        }
                    }
                    else
                    {
                        rpRas->cpPixels = (unsigned char *)NULL;
                    }

                    rpRas->originalPixels = rpRas->cpPixels;
                    rpRas->originalStride = rpRas->stride;

                    break;
                }
            case rwRASTERTYPEZBUFFER:
                {
#if 0
                    if (rpRas->width > videoModes[currentMode].width ||
                        rpRas->height > videoModes[currentMode].height)
#else
                        if ((((rpRas->width+63)>>6)*rpRas->height) >
                            (((videoModes[currentMode].width+63)>>6)
                             *videoModes[currentMode].height))
#endif
                        {
                            /* Bigger than the screen - so fail */
                            RWERROR((E_RW_INVRASTERSIZE));
                            RWRETURN(FALSE);
                        }

                    rpRas->stride = 0;
                    rpRas->cpPixels = (unsigned char *)NULL;
                    rpRas->cFlags = (RwUInt8)rwRASTERDONTALLOCATE;

                    rpRas->originalPixels = rpRas->cpPixels;
                    rpRas->originalStride = rpRas->stride;

                    break;
                }
            case rwRASTERTYPECAMERA:
                {
                    if (rpRas->width > videoModes[currentMode].width ||
                        rpRas->height > videoModes[currentMode].height)
                    {
                        /* Bigger than the screen - so fail */
                        RWERROR((E_RW_INVRASTERSIZE));
                        RWRETURN(FALSE);
                    }

                    rpRas->stride = (((videoModes[currentMode].depth+7)>>3)
                                     *((videoModes[currentMode].width+63)>>6))<<6;
                    rpRas->cpPixels = (unsigned char *)NULL;

                    rpRas->originalPixels = rpRas->cpPixels;
                    rpRas->originalStride = rpRas->stride;

                    rpRas->cFlags = (RwUInt8)rwRASTERDONTALLOCATE;
                    break;
                }
            default:
                {
                    RWRETURN(FALSE);
                }
        }
    }
    else
    {
        rpRas->cFlags = (RwUInt8)rwRASTERDONTALLOCATE;   /* Not allocated */
        rpRas->stride = 0;
        rpRas->originalStride = rpRas->stride;
    }

    /* Zero pending ref cont */
    RASTEREXTFROMRASTER(rpRas)->dmaRefCount = 0;
    RASTEREXTFROMRASTER(rpRas)->dmaClrCount = 0;


    RWRETURN(TRUE);
}

/****************************************************************************
 SkyRasterSubRaster

 On entry   : Raster (OUT)
            : Raster (IN)
            : None
 On exit    : TRUE on success
 */
static RwBool
SkyRasterSubRaster(void *pRas, void *pIn, RwInt32 __RWUNUSED__ nIn)
{
    RwRaster        *rpRas = (RwRaster *)pRas;
    RwRaster        *rpIn = (RwRaster *)pIn;
    RwRaster        *pixelOwner = rpIn->parent;
    _SkyRasterExt *rasExtIn   = RASTEREXTFROMRASTER(rpIn);
    _SkyRasterExt *rasExt     = RASTEREXTFROMRASTER(rpRas);

    RWFUNCTION(RWSTRING("SkyRasterSubRaster"));

    /* It won't get this far if rwRASTERDONTALLOCATE is not set
     * in the raster - see check in RwRasterSubRaster().
     */

    /* Core already set up offset and size */

    /* Can't sub raster textures to be used as textures because we need
     * the alignment for DMA purposes. However, actually changing the
     * type here is probably bad form, so we fix up as much as we can.
     */
    if ((rpRas->cType & rwRASTERTYPEMASK) == rwRASTERTYPETEXTURE)
    {
        /* We force the subraster to be old form */
        rasExt->flags &= ~0x1;
        /* We force the twiddle type to match */
        if (rasExtIn->flags & 0x2)
        {
            rasExt->flags |= 0x2;
        }
        else
        {
            rasExt->flags &= ~0x2;
        }
    }

    /* enforce a twiddle restriction */
    {
        if (rasExtIn->flags & 2)
        {
            if ((rpRas->nOffsetX & 0x15)
                || (rpRas->nOffsetY & 0x7))
            {
                RWRETURN(FALSE);
            }
            /* Force twiddling in subraster */
            rasExt->flags |= 2;
        }
    }


    /* Inherit some stuff */
    rpRas->cFormat = rpIn->cFormat;
    rpRas->stride = rpIn->stride;
    rpRas->depth = rpIn->depth;


    /* If we are trying to subraster a camera into a camera texture then ensure that alignment */
    /* is valid and create the appropriate register settings */
    if ((rpIn->cType & rwRASTERTYPECAMERA) && (rpRas->cType & rwRASTERTYPECAMERATEXTURE))
    {
        /* Adjust bounding box to fall on a valid rectangle */
        RwInt32 nAdjust;

        /* Make sure we don't even consider trying to upload this */
        rpRas->cType = rpIn->cType | (RwUInt8)rwRASTERDONTALLOCATE;

        /* Start X on the previous 64 pixel boundary */
        nAdjust = rpRas->nOffsetX & ((1<<6)-1);
        rpRas->nOffsetX -= nAdjust;
        rpRas->width    += nAdjust;

        /* Make width a power of two */
        nAdjust = rpRas->width & ((1<<_rwSkyFindMSB(rpRas->width))-1);
        if (nAdjust)
        {
            rpRas->width = (rpRas->width-nAdjust)<<1;
        }

        /* If we are now overrunning the buffer then move X back more */
        nAdjust = (rpRas->nOffsetX+rpRas->width) - rpIn->width;
        if (nAdjust>0 && nAdjust<rpRas->nOffsetX)
        {
            rpRas->nOffsetX -= nAdjust;
        }

        /* Start Y on the previous 32/64 pixel boundary depending on pixel format*/
        nAdjust = rpRas->nOffsetY & ((320/(rpIn->stride>>8))-1);
        rpRas->nOffsetY -= nAdjust;
        rpRas->height   += nAdjust;

        /* Make width a power of two */
        nAdjust = rpRas->height & ((1<<_rwSkyFindMSB(rpRas->height))-1);
        if (nAdjust)
        {
            rpRas->height = (rpRas->height-nAdjust)<<1;
        }

        /* If we are now overrunning the buffer then move Y back more */
        nAdjust = (rpRas->nOffsetY+rpRas->height) - rpIn->height;
        if (nAdjust>0 && nAdjust<rpRas->nOffsetY)
        {
            rpRas->nOffsetY -= nAdjust;
        }

        /* Setup a valid set of tex registers for this new texture ! */
        {
            _SkyRasterExt *rasExt   = RASTEREXTFROMRASTER(rpRas);
            /* Tex0 - TBP0 - Note that we don't know which buffer will be used so just provide the offset*/
            /* looks odd. RE */
            rasExt->lsb = (((rpRas->nOffsetX & 0xfffl)>>1) + (rpRas->nOffsetY*(rpIn->stride>>8)))&0x1ff;
            /* Tex0 - TBW */
            rasExt->lsb |=  ((skyFrame_1>>16)&0x3f)<<14;
            /* Tex0 - PSM */
            rasExt->lsb |= ((skyFrame_1>>24)&0x3f)<<20;
            /* Tex0 - TW */
            rasExt->lsb |= (_rwSkyFindMSB(rpRas->width) & 0xf) << 26;
            /* Tex0 - TCC */
            rasExt->msb = 0l << (34-32);    /* Use Alpha from TexA register */
            /* Tex0 - TH */
            rasExt->lsb |= (_rwSkyFindMSB(rpRas->height) & 0xf) << 30;
            rasExt->msb |= (_rwSkyFindMSB(rpRas->height) & 0xf) >> 2;
            /* Tex0 - TFX */
            rasExt->msb |= 0l << (35-32);   /* MODULATE */
#if defined(GSB) && defined(GSPLUS)
            rasExt->lsb3 = (((rpRas->nOffsetX & 0xfffl)>>1) + (rpRas->nOffsetY*(rpIn->stride>>8)))&0xfff;
            rasExt->msb3 = 0;
#endif /* defined(GSB) && defined(GSPLUS) */
            rasExt->maxMipLevel = 0;
        }
    }
    else
    {
        /* Normal case is just a copy */
        rpRas->cType = rpIn->cType;
    }

    rpRas->cpPixels = (pixelOwner->cpPixels) +
        (pixelOwner->stride * rpRas->nOffsetY) +
        (((rpRas->nOffsetX * ((pixelOwner->depth + 7))) >> 3));
    rpRas->palette = pixelOwner->palette;

    RWRETURN(TRUE);
}

/****************************************************************************
 SkyRasterDestroy


 On entry   : NULL
            : Raster to destroy
 On exit    : TRUE on success
 */
static RwBool
SkyRasterDestroy(void *__RWUNUSED__ pOut,
                 void *pRaster,
                 RwInt32 __RWUNUSED__ nIn)
{
    RwRaster *rpRas = (RwRaster *)pRaster;
    _SkyRasterExt *rasExt = RASTEREXTFROMRASTER(rpRas);

    RWFUNCTION(RWSTRING("SkyRasterDestroy"));

    if (rasExt->bLocked)
    {
        skyTexCacheRasterLock(rpRas, FALSE);
    }
    skyTexCacheReleaseRaster(rpRas);

#ifdef SHOWUPLOADEDTEXTURES
    {
        RwInt32 nIndex;
        /* Find a this texture */
        for (nIndex=0; nIndex<SHOWUPLOADEDTEXTUREMAXTEXTURES; nIndex++)
        {
            if (rpShowUploadedRasters[nIndex]==rpRas)
            {
                printf("Removing Texture %s\n", RwTextureGetName(tpShowUploadedTextures[nIndex]));
                tpShowUploadedTextures[nIndex] = NULL;
                rpShowUploadedRasters[nIndex] = NULL;
                break;
            }
        }
    }
#endif

    if (!(rpRas->cFlags & (RwUInt8)rwRASTERDONTALLOCATE))
    {
        _SkyRasterExt *rasExt = RASTEREXTFROMRASTER(rpRas);

        RWASSERT(0<= rasExt->dmaRefCount);

        /* We wait for the ref count to drop to zero (no procrastinated
           destroy to fix a memory trampling bug, where the reference
           count could be decrimented after the structure containing it
           had been freed. The real soultion is to move the reference
           count into the pixel data in the !rwRASTERDONTALLOCATE case */
        /* Some custom pipes do the wrong thing, hence chain test */
        while ((rasExt->dmaRefCount > 0) && (sweCurrentBase != sweChainEnd))
        {
            _sweFlush();
        }

        /* Some custom pipes do the wrong thing, hence the following */
        RWASSERT(0 ==  rasExt->dmaRefCount);
        rasExt->dmaRefCount = 0;

        if (rasExt->dmaRefCount == 0)
        {
            if (rpRas->originalPixels)
            {
                RwSky2Free(rpRas->originalPixels);
            }
        }
        else
        {
            /* We need to defer the delete */
            /* This is a bit sneaky */
            /* We want to call free with this, so don't pass CIRCALLOC flag */
            _sweAddPkt(rpRas->originalPixels, SWE_PKT_NULL | SWE_PKT_LOCAL);
        }

        {
            RwInt32 level;
            _SkyRasterExt *rasExt = RASTEREXTFROMRASTER(rpRas);

            RWASSERT(0<= rasExt->dmaRefCount);

            if (rasExt->dmaRefCount == 0)
            {
                if (rasExt->palUploadPkt)
                {
                    RwSky2Free(rasExt->palUploadPkt);
                    rasExt->palUploadPkt = (u_long128 *)NULL;
                }
                for (level = 0; level < 7; level++)
                {
                    if (rasExt->mipUploadPkts[level])
                    {
                        RwSky2Free(rasExt->mipUploadPkts[level]);
                        rasExt->mipUploadPkts[level] = (u_long128 *)NULL;
                    }
                }
            }
            else
            {
                /* We need to defer the delete */
                /* This is a bit sneaky */
                /* We want to call free with this, so don't pass CIRCALLOC flag */
                if (rasExt->palUploadPkt)
                {
                    _sweAddPkt(rasExt->palUploadPkt, SWE_PKT_NULL | SWE_PKT_LOCAL);
                    rasExt->palUploadPkt = (u_long128 *)NULL;
                }
                for (level = 0; level < 7; level++)
                {
                    if (rasExt->mipUploadPkts[level])
                    {
                        _sweAddPkt(rasExt->mipUploadPkts[level], SWE_PKT_NULL | SWE_PKT_LOCAL);
                        rasExt->mipUploadPkts[level] = (u_long128 *)NULL;
                    }
                }
            }
        }
    }

    RWRETURN(TRUE);
}


/* These macros are somewhat missnamed, as you pass in the location in
   a 32 (or 16) bit block and are returned the location in the source
   4, 8 or 16 bit block */

#define EvenColumn8to32(A)   \
        (((((A) & 2) << 2)   \
          |(((A) & 4) >> 2)  \
          |(((A) & 8) >> 2)  \
          |(((A) &16) >> 2)  \
          |(((A) &32) >> 1)) \
         ^((((A) & 1) << 5)  \
          |(((A) & 1) << 2)))

#define OddColumn8to32(A)    \
        (((((A) & 2) << 2)   \
          |(((A) & 4) >> 2)  \
          |(((A) & 8) >> 2)  \
          |(((A) &16) >> 2)  \
          |(((A) &32) >> 1)) \
         ^((((A) & 1) << 5)  \
          |((~(A)& 1) << 2)))

#define Location16to32(A)    \
        ((((A) & 2) >> 1)    \
         |(((A) & 1) << 3)  \
         |(((A) & 4) >> 1)  \
         |(((A) & 8) >> 1)  \
         |(((A) &16)))

#define EvenColumn4to32(A)   \
        (((((A) & 2) << 2)   \
          |(((A) & 4) << 2)  \
          |(((A) & 8) >> 3)  \
          |(((A) &16) >> 3)  \
          |(((A) &32) >> 3)  \
          |(((A) &64) >> 1)) \
         ^((((A) & 1) << 6)  \
          |(((A) & 1) << 2)))

#define OddColumn4to32(A)    \
        (((((A) & 2) << 2)   \
          |(((A) & 4) << 2)  \
          |(((A) & 8) >> 3)  \
          |(((A) &16) >> 3)  \
          |(((A) &32) >> 3)  \
          |(((A) &64) >> 1)) \
         ^((((A) & 1) << 6)  \
          |((~(A)& 1) << 2)))

#define EvenColumn4to16(A)   \
        (((((A) & 2) << 2)   \
          |(((A) &32) >> 1)  \
          |(((A) & 4) >> 2)  \
          |(((A) & 8) >> 2)  \
          |(((A) &16) >> 2)  \
          |(((A) &64) >> 1)) \
         ^((((A) & 1) << 6)  \
          |(((A) & 1) << 2)))

#define OddColumn4to16(A)    \
        (((((A) & 2) << 2)   \
          |(((A) &32) >> 1)  \
          |(((A) & 4) >> 2)  \
          |(((A) & 8) >> 2)  \
          |(((A) &16) >> 2)  \
          |(((A) &64) >> 1)) \
         ^((((A) & 1) << 6)  \
          |((~(A)& 1) << 2)))

/************************************************************************
 * Helper funtions for raster lock and unlock. These reduce stack
 * usage during mipmap generation. From a suggestion by "Adisak"
 */
static void
SkyRasterLock_32to8(RwRaster *rpRas)
{
    /* Need a scan buffer that is width*4 in size */
    /* max width is 1024, so 4K is required */
    RwUInt8 buf[4096];
    int i, j, k, l, x, y;
    u_long128 column[4];
    RwUInt8 *src;

    RWFUNCTION(RWSTRING("SkyRasterLock_32to8"));

    for (y = 0; y < rpRas->height; y += 4)
    {
        src = (RwUInt8*)column;
        for (x=0; x<rpRas->width; x+= 16)
        {
            column[0] = *(u_long128*)(rpRas->cpPixels
                                      +(y*rpRas->stride)
                                      +(x*2));
            column[1] = *(u_long128*)(rpRas->cpPixels
                                      +(y*rpRas->stride)
                                      +(x*2)+16);
            column[2] = *(u_long128*)(rpRas->cpPixels
                                      +((y+2)*rpRas->stride)
                                      +(x*2));
            column[3] = *(u_long128*)(rpRas->cpPixels
                                      +((y+2)*rpRas->stride)
                                      +(x*2)+16);
            for (j=0; j<64; j += 16)
            {
                for (k=0; k<16; k++)
                {
                    if (y & 4)
                    {
                        l = OddColumn8to32(j+k);
                    }
                    else
                    {
                        l = EvenColumn8to32(j+k);
                    }
                    buf[x+(l&0xf)+((l>>4)<<10)] = src[j+k] ;
                }
            }
        }
        /* Copy back into the raster */
        /* Copy a 4 pixel high stripe */
        for (i=0; i<4; i++)
        {
            for (x = 0; x< rpRas->width; x+= 16)
            {
                *(u_long128*)(rpRas->cpPixels +((y+i)*rpRas->stride)+x)
                            = *(u_long128*)(buf+i*1024+x);
            }
        }
    }
    RWRETURNVOID();
}

static void
SkyRasterLock_16to4(RwRaster *rpRas)
{
    /* Need a scan buffer that is width*4*depth in size */
    /* max width is 1024, so 2K is required */
    RwUInt8 buf[2048];
    int i, j, k, l, x, y;
    u_long128 column[4];
    RwUInt8 *src;

    RWFUNCTION(RWSTRING("SkyRasterLock_16to4"));

    for (y = 0; y < rpRas->height; y += 4)
    {
        src = (RwUInt8*)column;
        for (x=0; x<rpRas->width; x+= 32)
        {
            column[0] = *(u_long128*)(rpRas->cpPixels
                                      +(y*rpRas->stride)
                                      +(x));
            column[1] = *(u_long128*)(rpRas->cpPixels
                                      +(y*rpRas->stride)
                                      +(x)+16);
            column[2] = *(u_long128*)(rpRas->cpPixels
                                      +((y+2)*rpRas->stride)
                                      +(x));
            column[3] = *(u_long128*)(rpRas->cpPixels
                                      +((y+2)*rpRas->stride)
                                      +(x)+16);
            /* Slightly anoying: We need to clear down buf */
            *(RwUInt64*)&(buf[x>>1]) = 0;
            *(RwUInt64*)&(buf[(x>>1)+8]) = 0;
            *(RwUInt64*)&(buf[(x>>1)+512]) = 0;
            *(RwUInt64*)&(buf[(x>>1)+512+8]) = 0;
            *(RwUInt64*)&(buf[(x>>1)+1024]) = 0;
            *(RwUInt64*)&(buf[(x>>1)+1024+8]) = 0;
            *(RwUInt64*)&(buf[(x>>1)+1536]) = 0;
            *(RwUInt64*)&(buf[(x>>1)+1536+8]) = 0;

            for (j=0; j<128; j += 32)
            {
                for (k=0; k<32; k++)
                {
                    if (y & 4)
                    {
                        l = OddColumn4to16(j+k);
                    }
                    else
                    {
                        l = EvenColumn4to16(j+k);
                    }
                    if (l&1)
                    {
                        buf[(x+(l&0x1f)+((l>>5)<<10))>>1]
                                     |= (k&1)?(src[(j+k)>>1]&0xf0)
                                              :(src[(j+k)>>1]&0xf)<<4;
                    }
                    else
                    {
                        buf[(x+(l&0x1f)+((l>>5)<<10))>>1]
                                     |= (k&1)?((src[(j+k)>>1] &0xf0)>>4)
                                             :src[(j+k)>>1]&0xf;
                    }
                }
            }
        }
        /* Copy back into the raster */
        /* Copy a 4 pixel high stripe */
        for (i=0; i<4; i++)
        {
            for (x = 0; x< (rpRas->width)>>1; x+= (32>>1))
            {
                *(u_long128*)(rpRas->cpPixels+((y+i)*rpRas->stride) +x)
                                    = *(u_long128*)(buf+i*512+x);
            }
        }
    }
    RWRETURNVOID();
}

static void
SkyRasterUnlock_8to32(RwRaster *rpRas)
{
    /* Need a scan buffer that is width*4 in size */
    /* max width is 1024, so 4K is required */
    RwUInt8 buf[4096];
    int i, j, k, l, x, y;
    u_long128 column[4];
    RwUInt8 *dst;

    RWFUNCTION(RWSTRING("SkyRasterUnlock_8to32"));

    for (y = 0; y < rpRas->height; y += 4)
    {
        /* Copy a 4 pixel high stripe */
        for (i=0; i<4; i++)
        {
            for (x = 0; x< rpRas->width; x+= 16)
            {
                *(u_long128*)(buf+i*1024+x) =
                   *(u_long128*)(rpRas->cpPixels
                                 +((y+i)*rpRas->stride)
                                 +x);
            }
        }
        /* now copy back. Note that the 32 bit */
        /* texture is 2*width in memory */
        dst = (RwUInt8*)column;
        for (x=0; x<rpRas->width; x+= 16)
        {
            for (j=0; j<64; j += 16)
            {
                for (k=0; k<16; k++)
                {
                    if (y & 4)
                    {
                        l = OddColumn8to32(j+k);
                    }
                    else
                    {
                        l = EvenColumn8to32(j+k);
                    }
                    dst[j+k] = buf[x+(l&0xf)+((l>>4)<<10)];
                }
            }
            /* Copy back into the raster */
            *(u_long128*)(rpRas->cpPixels
                          +(y*rpRas->stride)
                          +(x*2)) = column[0];
            *(u_long128*)(rpRas->cpPixels
                          +(y*rpRas->stride)
                          +(x*2)+16) = column[1];
            *(u_long128*)(rpRas->cpPixels
                          +((y+2)*rpRas->stride)
                          +(x*2)) = column[2];
            *(u_long128*)(rpRas->cpPixels
                          +((y+2)*rpRas->stride)
                          +(x*2)+16) = column[3];
        }
    }
    RWRETURNVOID();
}

static void
SkyRasterUnlock_4to16(RwRaster *rpRas)
{
    /* Need a scan buffer that is depth*width*4 in size */
    /* max width is 1024, so 2K is required */
    RwUInt8 buf[2048];
    int i, j, k, l, x, y;
    u_long128 column[4];
    RwUInt8 *dst;

    RWFUNCTION(RWSTRING("SkyRasterUnlock_4to16"));

    for (y = 0; y < rpRas->height; y += 4)
    {
        /* Copy a 4 pixel high stripe */
        for (i=0; i<4; i++)
        {
            for (x = 0; x< (rpRas->width)>>1; x+= (32>>1))
            {
                *(u_long128*)(buf+i*512+x) =
                   *(u_long128*)(rpRas->cpPixels
                                 +((y+i)*rpRas->stride)
                                 +x);
            }
        }
        /* No need to clear down dst as we always write
           bottom 4 bits first */
        /* now copy back. Note that the 16 bit */
        /* texture is 2*width in memory */
        dst = (RwUInt8*)column;
        for (x=0; x<rpRas->width; x+= 32)
        {
            for (j=0; j<128; j += 32)
            {
                for (k=0; k<32; k++)
                {
                    if (y & 4)
                    {
                        l = OddColumn4to16(j+k);
                    }
                    else
                    {
                        l = EvenColumn4to16(j+k);
                    }
                    if (k&1)
                    {
                        dst[(j+k)>>1]
                            |= l&1 ?
                               buf[(x+(l&0x1f)
                                     +((l>>5)<<10))
                                    >>1]&0xf0
                               :(buf[(x+(l&0x1f)
                                     +((l>>5)<<10))>>1]&0xf)
                                <<4;
                    }
                    else
                    {
                        dst[(j+k)>>1]
                            = l&1 ?
                               (buf[(x+(l&0x1f)
                                     +((l>>5)<<10))
                                    >>1]>>4)&0xf
                               :buf[(x+(l&0x1f)
                                     +((l>>5)<<10))>>1]&0xf;
                    }
                }
            }
            /* Copy back into the raster */
            *(u_long128*)(rpRas->cpPixels
                          +(y*rpRas->stride)
                          +(x)) = column[0];
            *(u_long128*)(rpRas->cpPixels
                          +(y*rpRas->stride)
                          +(x)+16) = column[1];
            *(u_long128*)(rpRas->cpPixels
                          +((y+2)*rpRas->stride)
                          +(x)) = column[2];
            *(u_long128*)(rpRas->cpPixels
                          +((y+2)*rpRas->stride)
                          +(x)+16) = column[3];
        }
    }
    RWRETURNVOID();
}
/****************************************************************************
 SkyRasterLock

 On entry   : Pixel pointer
            : raster
            : Flags
 On exit    : TRUE on success
 */
#if defined(GSB) && defined(GSPLUS)
static sceGsPlusStoreImage skySis __attribute__((aligned(64)));
#else /* defined(GSB) && defined(GSPLUS) */
static sceGsStoreImage skySis __attribute__((aligned(64)));
#endif /* defined(GSB) && defined(GSPLUS) */

static RwBool
SkyRasterLock(void *pPixels, void *pRaster, RwInt32 flags)
{
    RwUInt8         **cppPixels = (RwUInt8 **)pPixels;
    RwRaster        *rpRas = (RwRaster *)pRaster;
    RwUInt8         mipLevel = (RwUInt8)((flags & (RwInt32)0xFF00) >> 8);
    _SkyRasterExt   *ext;
    RwUInt8         currentLevel;

    RWFUNCTION(RWSTRING("SkyRasterLock"));

    flags = flags & 0xFF;

    /* currently nofetch has no effect on the sky2 driver */
    flags = flags & ~rwRASTERLOCKNOFETCH;

    /* Can't lock Z buffer raster */
    switch (rpRas->cType & rwRASTERTYPEMASK)
    {
        case rwRASTERTYPECAMERA:
            {
                int transHeight, transMaxHeight, transOffsetY;
                RwUInt8 *dst;

                /* For now we only allow lock to read */
                /*if (flags != (RwInt32)rwRASTERLOCKREAD)*/
                if (flags & (~(RwInt32)rwRASTERLOCKREAD))
                {
                    RWERROR((E_RW_INVRASTERLOCKREQ));
                    RWRETURN(FALSE);
                }

                /* only mip level 0 makes any sense */
                if (mipLevel != 0)
                {
                    RWERROR((E_RW_INVRASTERMIPLEVEL));
                    RWRETURN(FALSE);
                }

                /* we have an aligned destination */
                if ((!(rpRas->cpPixels)) || ((int)(rpRas->cpPixels) & 0xf))
                {
                    RWRETURN(FALSE);
                }

                /* We have to spin until all actions in the dma complete */
                while ((sweCurrentBase != sweChainEnd) || sweFlipPending)
                {
                    /* !! WARNING WILL HAVE TO CHANGE WHEN AUTO FILP GOES IN !! */
                    /* Ugly */
                    _sweFlush();
                }
                /* We need to ensure all graphics have completed */
                sceGsSyncPath(0,0);

                /* Initialise initial values */
                transHeight = rpRas->height;
                transOffsetY = rpRas->nOffsetY;
                dst = rpRas->cpPixels;

                /* largest number of lines we can do */
                transMaxHeight = (65535*16)/(((rpRas->width*rpRas->depth)>>3));

                if (transMaxHeight < transHeight)
                {
                    /* ensure that the transfer will restart on a qw boundary */
                    while ((transMaxHeight * (((rpRas->width*rpRas->depth)>>3))) & 0xf)
                    {
                        transMaxHeight--;
                    }

                    /* couldn't find an aligned start! */
                    RWASSERT(transMaxHeight);
                    if (!transMaxHeight)
                    {
                        RWRETURN(FALSE);
                    }
                }
                else
                {
                    /* we'll do it in one go */
                    transMaxHeight = transHeight;
                }

                /* flush destination */
                SyncDCache(rpRas->cpPixels,
                           ((RwUInt8*)(rpRas->cpPixels))+(((rpRas->width*rpRas->depth)>>3))*rpRas->height);

                while (transHeight)
                {
#if 0
                    /* This assert needs tp be fixed */
                    RWASSERT(!((((rpRas->width*rpRas->depth)>>3))*transMaxHeight & ~0x7fff));
#endif

#if defined(GSB) && defined(GSPLUS)
                    sceGsPlusSetDefStoreImage(&skySis, (skyFrame_2 & 0xfff)<<5,
                                              (skyFrame_1 & 0x3f0000)>>16,
                                              (skyFrame_1 & 0x3f000000)>>24,
                                              rpRas->nOffsetX & 0x7ff,
                                              transOffsetY & 0x7ff,
                                              rpRas->width, transMaxHeight);
#else /* defined(GSB) && defined(GSPLUS) */
                    sceGsSetDefStoreImage(&skySis,
                                          (skyFrame_1 & 0x1ff)<<5,
                                          (skyFrame_1 & 0x3f0000)>>16,
                                          (skyFrame_1 & 0x3f000000)>>24,
                                          rpRas->nOffsetX & 0x7ff, transOffsetY & 0x7ff,
                                          rpRas->width, transMaxHeight);
#endif /* defined(GSB) && defined(GSPLUS) */

#if defined(GSB) && defined(GSPLUS)
                    SyncDCache(&skySis, ((RwUInt8*)(&skySis))+sizeof(sceGsPlusStoreImage));
#else /* defined(GSB) && defined(GSPLUS) */
                    SyncDCache(&skySis, ((RwUInt8*)(&skySis))+sizeof(sceGsStoreImage));
#endif /* defined(GSB) && defined(GSPLUS) */

                    /* We have to ensure that all dma is stopped on ch-2 and 1 */
                    while ((*(volatile u_int *)&(sceDmaGetChan(SCE_DMA_VIF1)->chcr))&0x100)
                        ;
                    while ((*(volatile u_int *)&(sceDmaGetChan(SCE_DMA_GIF)->chcr))&0x100)
                        ;
                    sceGsSyncPath(0,0);

                    /* transfer a block */
#if defined(GSB) && defined(GSPLUS)
                    if (sceGsPlusExecStoreImage(&skySis, (u_long128*)(dst)) == -1)
#else /* defined(GSB) && defined(GSPLUS) */
                        if (sceGsExecStoreImage(&skySis, (u_long128*)(dst)) == -1)
#endif /* defined(GSB) && defined(GSPLUS) */
                        {
#if defined(GSB) && defined(GSPLUS)
                            if (sceGsPlusExecStoreImage(&skySis, (u_long128*)(dst))
                                == -1)
#else /* defined(GSB) && defined(GSPLUS) */
                                if (sceGsExecStoreImage(&skySis, (u_long128*)(dst)) == -1)
#endif /* defined(GSB) && defined(GSPLUS) */
                                {
                                    RWRETURN(FALSE);
                                }
                        }

                    transHeight -= transMaxHeight;
                    transOffsetY += transMaxHeight;
                    dst += (((rpRas->width*rpRas->depth)>>3)) * transMaxHeight;
                    if (transHeight < transMaxHeight)
                    {
                        /* scrag end */
                        transMaxHeight = transHeight;
                    }
                }

                /* setup stride now pixels have been transferred */
                rpRas->stride = (rpRas->width * rpRas->depth)>>3;

                (*cppPixels) = rpRas->cpPixels;

                /* can only lock for read */
                rpRas->privateFlags |= rwRASTERPIXELLOCKEDREAD;

                RWRETURN(TRUE);
            }
        case rwRASTERTYPECAMERATEXTURE:
            {
                _SkyRasterExt *reExt = RASTEREXTFROMRASTER(rpRas);
                int transHeight, transMaxHeight, transOffsetY;
                RwUInt8 *dst;

                /* For now we only allow lock to read */
                /*if (flags != (RwInt32)rwRASTERLOCKREAD)*/
                if (flags & (~(RwInt32)rwRASTERLOCKREAD))
                {
                    RWERROR((E_RW_INVRASTERLOCKREQ));
                    RWRETURN(FALSE);
                }
                /* Raster must be in cache */
                if (!reExt->mpCacheEntry)
                {
                    RWERROR((E_RW_INVRASTERLOCKREQ));
                    RWRETURN(FALSE);
                }

                /* only mip level 0 makes any sense */
                if (mipLevel != 0)
                {
                    RWERROR((E_RW_INVRASTERMIPLEVEL));
                    RWRETURN(FALSE);
                }

                /* we have an aligned destination */
                if ((!(rpRas->cpPixels)) || ((int)(rpRas->cpPixels) & 0xf))
                {
                    RWRETURN(FALSE);
                }

                /* We have to spin until all actions in the dma complete */
                while ((sweCurrentBase != sweChainEnd) || sweFlipPending)
                {
                    /* !! WARNING WILL HAVE TO CHANGE WHEN AUTO FILP GOES IN !! */
                    /* Ugly */
                    _sweFlush();
                }
                /* We need to ensure all graphics have completed */
                sceGsSyncPath(0,0);

                /* Initialise initial values */
                transHeight = rpRas->height;
                transOffsetY = rpRas->nOffsetY;
                dst = rpRas->cpPixels;

                /* largest number of lines we can do */
                transMaxHeight = (65535*16)/(((rpRas->width*rpRas->depth)>>3));

                if (transMaxHeight < transHeight)
                {
                    /* ensure that the transfer will restart on a qw boundary */
                    while (((transMaxHeight * ((rpRas->width*rpRas->depth)>>3))) & 0xf)
                    {
                        transMaxHeight--;
                    }

                    /* couldn't find an aligned start! */
                    RWASSERT(transMaxHeight);
                    if (!transMaxHeight)
                    {
                        RWRETURN(FALSE);
                    }
                }
                else
                {
                    /* we'll do it in one go */
                    transMaxHeight = transHeight;
                }

                /* flush destination */
                SyncDCache(rpRas->cpPixels,
                           ((RwUInt8*)(rpRas->cpPixels))+(((rpRas->width*rpRas->depth)>>3))*rpRas->height);

                while (transHeight)
                {
#if 0
                    /* This assert needs tp be fixed */
                    RWASSERT(!((((rpRas->width*rpRas->depth)>>3))*transMaxHeight & ~0x7fff));
#endif

#if defined(GSB) && defined(GSPLUS)
#error "Can't write this 'cause I've returned the docs!"
#else /* defined(GSB) && defined(GSPLUS) */
                    sceGsSetDefStoreImage(&skySis,
                                          (reExt->lsb & 0x3fff),
                                          ((reExt->lsb>>14)&0x3f),
                                          ((reExt->lsb>>20)&0x3f),
                                          rpRas->nOffsetX & 0x7ff, transOffsetY & 0x7ff,
                                          rpRas->width, transMaxHeight);
#endif /* defined(GSB) && defined(GSPLUS) */

#if defined(GSB) && defined(GSPLUS)
                    SyncDCache(&skySis, ((RwUInt8*)(&skySis))+sizeof(sceGsPlusStoreImage));
#else /* defined(GSB) && defined(GSPLUS) */
                    SyncDCache(&skySis, ((RwUInt8*)(&skySis))+sizeof(sceGsStoreImage));
#endif /* defined(GSB) && defined(GSPLUS) */

                    /* We have to ensure that all dma is stopped on ch-2 and 1 */
                    while ((*(volatile u_int *)&(sceDmaGetChan(SCE_DMA_VIF1)->chcr))&0x100)
                        ;
                    while ((*(volatile u_int *)&(sceDmaGetChan(SCE_DMA_GIF)->chcr))&0x100)
                        ;
                    sceGsSyncPath(0,0);

                    /* transfer a block */
#if defined(GSB) && defined(GSPLUS)
                    if (sceGsPlusExecStoreImage(&skySis, (u_long128*)(dst)) == -1)
#else /* defined(GSB) && defined(GSPLUS) */
                        if (sceGsExecStoreImage(&skySis, (u_long128*)(dst)) == -1)
#endif /* defined(GSB) && defined(GSPLUS) */
                        {
#if defined(GSB) && defined(GSPLUS)
                            if (sceGsPlusExecStoreImage(&skySis, (u_long128*)(dst))
                                == -1)
#else /* defined(GSB) && defined(GSPLUS) */
                                if (sceGsExecStoreImage(&skySis, (u_long128*)(dst)) == -1)
#endif /* defined(GSB) && defined(GSPLUS) */
                                {
                                    RWRETURN(FALSE);
                                }
                        }

                    transHeight -= transMaxHeight;
                    transOffsetY += transMaxHeight;
                    dst += (((rpRas->width*rpRas->depth)>>3)) * transMaxHeight;
                    if (transHeight < transMaxHeight)
                    {
                        /* scrag end */
                        transMaxHeight = transHeight;
                    }
                }

                /* setup stride now pixels have been transferred */
                rpRas->stride = (rpRas->width * rpRas->depth)>>3;

                (*cppPixels) = rpRas->cpPixels;

                /* can only lock for read */
                rpRas->privateFlags |= rwRASTERPIXELLOCKEDREAD;

                RWRETURN(TRUE);
            }
        case rwRASTERTYPEZBUFFER:
            {
                RWERROR((E_RW_INVRASTERLOCKREQ));
                RWRETURN(FALSE);
            }

        default:
            {
                break;
            }
    }

    ext = RASTEREXTFROMRASTER(rpRas);
    if (mipLevel > ((ext->maxMipLevel) >> 2))
    {
        RWERROR((E_RW_INVRASTERMIPLEVEL));
        RWRETURN(FALSE);
    }

    if (flags & rwRASTERLOCKWRITE)
    {
        /* Kick it out of cache so it gets updated */
        skyTexCacheReleaseRaster(rpRas);
    }

#if 0
    /* Initialised in RasterCreate */
    rpRas->originalPixels = rpRas->cpPixels;
    rpRas->originalWidth = rpRas->width;
    rpRas->originalHeight = rpRas->height;
    rpRas->originalStride = rpRas->stride;
#endif

    if (!(ext->flags & 1))
    {
        for (currentLevel = 0; currentLevel < mipLevel; currentLevel++)
        {
            rpRas->stride = (rpRas->depth * rpRas->width) >> 3;
            rpRas->cpPixels = (RwUInt8*)(((RwUInt32)(rpRas->cpPixels)+15)&~15);
            rpRas->cpPixels += (rpRas->stride * rpRas->height);
            rpRas->width = rpRas->width >> 1;
            rpRas->height = rpRas->height >> 1;
        }
        rpRas->cpPixels = (RwUInt8*)(((RwUInt32)(rpRas->cpPixels) + 15) & ~15);
        rpRas->stride = (rpRas->depth * rpRas->width) >> 3;
    }
    else
    {
        int minTW, minTH, w, h;

        /* we need to skip over all the inlined stuff */
        skyTransferMinSize(((ext->lsb>>20)&0x3f), ext->flags, &minTW, &minTH);
        w = rpRas->width;
        h = rpRas->height;
        /* Initial cpPixels points to mip 0 data */
        /* add space for each mip level's data */
        for (currentLevel = 0; currentLevel < mipLevel; currentLevel++)
        {
            int levelWidth, levelHeight;

            /* skip data */
            levelWidth  = w < minTW? minTW : w;
            levelHeight = h < minTH? minTW : h;
            rpRas->cpPixels += (((levelWidth*levelHeight*rpRas->depth)>>3)+15)
                               & ~0xf;
            /* skip inline data */
            rpRas->cpPixels += (5<<4);
            w >>= 1;
            h >>= 1;
        }
        rpRas->stride = (rpRas->depth * (w < minTW? minTW : w)) >> 3;
        rpRas->width = w;
        rpRas->height = h;

    }

    if (flags & ((RwInt32)rwRASTERLOCKREAD))
    {
        /* We may need to twiddle outgoing data */
        if ((ext->flags & 2)
            && (((ext->lsb>>20)&0x3f) == SCE_GS_PSMT8)
            && (rpRas->stride >= 16))
        {
            SkyRasterLock_32to8(rpRas);
        }
        else if ((ext->flags & 4)
            && (((ext->lsb>>20)&0x3f) == SCE_GS_PSMT4)
            && (rpRas->stride >= 16))
        {
            SkyRasterLock_16to4(rpRas);
        }
    }

    (*cppPixels) = rpRas->cpPixels;
    ext->lockedMipLevel = mipLevel;

    if (flags & rwRASTERLOCKREAD)
    {
        rpRas->privateFlags |= rwRASTERPIXELLOCKEDREAD;
    }
    if (flags & rwRASTERLOCKWRITE)
    {
        rpRas->privateFlags |= rwRASTERPIXELLOCKEDWRITE;
    }

    RWRETURN(TRUE);
}

/****************************************************************************
 SkyRasterUnlock

 On entry   : NULL
            : Raster
            : Flags
 On exit    : TRUE on success
 */
static RwBool
SkyRasterUnlock(void * __RWUNUSED__ pInOut, void *pRaster,
                RwInt32 __RWUNUSED__ flags)
{
    RwRaster    *rpRas = (RwRaster *)pRaster;

    RWFUNCTION(RWSTRING("SkyRasterUnlock"));

    if (!(rpRas->privateFlags & rwRASTERPIXELLOCKED))
    {
        RWRETURN(FALSE);
    }

    switch (rpRas->cType & rwRASTERTYPEMASK)
    {
        case rwRASTERTYPECAMERA :
        case rwRASTERTYPECAMERATEXTURE :
        case rwRASTERTYPEZBUFFER :
            {
                /* SyncDCache to flush any changes to the pixels */
                if( rpRas->cpPixels )
                {
                    SyncDCache(rpRas->cpPixels,
                               SCESYNCDCACHEROUNDUP(rpRas->cpPixels +
                                                    ((rpRas->width
                                                      * rpRas->height
                                                      * rpRas->depth) / 8)));
                }
                /* Only have mipLevel 0 so values are not buffered */
                break;
            }
        default:
            {
                const _SkyRasterExt *rasExt = RASTEREXTFROMRASTER(rpRas);

                /* SyncDCache to flush any changes to the pixels */
                if( rpRas->cpPixels )
                {
#if 1
                    /* We may need to twiddle incomming data */
                    if ((rasExt->flags & 2)
                        && (((rasExt->lsb>>20)&0x3f) == SCE_GS_PSMT8)
                        && (rpRas->stride >= 16))
                    {
                        SkyRasterUnlock_8to32(rpRas);
                    }
                    else if ((rasExt->flags & 4)
                        && (((rasExt->lsb>>20)&0x3f) == SCE_GS_PSMT4)
                        && (rpRas->stride >= 16))
                    {
                        SkyRasterUnlock_4to16(rpRas);
                    }
#endif

                    SyncDCache(rpRas->cpPixels,
                               SCESYNCDCACHEROUNDUP(rpRas->cpPixels +
                                                    ((rpRas->width
                                                      * rpRas->height
                                                      * rpRas->depth) / 8)));
                }
                /* restore mipLevel 0 settings */
                rpRas->width = rpRas->originalWidth;
                rpRas->height = rpRas->originalHeight;
                rpRas->stride = rpRas->originalStride;
                rpRas->cpPixels = rpRas->originalPixels;
                if (rasExt->flags & 1)
                {
                    /* new format raster needs cpPixels adjusted */
                    /* Need to adjust cpPixels */
                    rpRas->cpPixels = (RwUInt8*)
                             ((u_long128*)((RwUInt8**)(rpRas->cpPixels))[1]+5);
                }
            }
    }


    if ((rpRas->cFormat & (rwRASTERFORMATMIPMAP >> 8)) &&
        (rpRas->cFormat & (rwRASTERFORMATAUTOMIPMAP >> 8)) &&
        (rpRas->privateFlags & rwRASTERPIXELLOCKEDWRITE))
    {
        rpRas->privateFlags = rpRas->privateFlags & ~rwRASTERPIXELLOCKED;
        RwTextureRasterGenerateMipmaps(rpRas, (RwImage *)NULL);
    }
    else
    {
        rpRas->privateFlags = rpRas->privateFlags & ~rwRASTERPIXELLOCKED;
    }

    RWRETURN(TRUE);
}

/****************************************************************************
 SkyRasterLockPalette

 On entry   : Pixel pointer
            : raster
            : Flags
 On exit    : TRUE on success
 */
static RwBool
SkyRasterLockPalette(void *pPalette, void *pRaster, RwInt32 flags)
{
    RwRaster    *rpRas = (RwRaster *)pRaster;

    RWFUNCTION(RWSTRING("SkyRasterLockPalette"));

    /* Can only do this if we have a palette */
    if (rpRas->cFormat & ((rwRASTERFORMATPAL4|rwRASTERFORMATPAL8)>>8))
    {
        RwUInt8     **cppPalette = (RwUInt8 **)pPalette;

        /*if ((flags == rwRASTERLOCKWRITE) ||
          (flags == rwRASTERLOCKREADWRITE))*/
        /* Now done as bits specifically, so test for write bit */
        if (flags & rwRASTERLOCKWRITE)
        {
            /* Kick it out of cache so it gets updated */
            skyTexCacheReleaseRaster(rpRas);
        }

        (*cppPalette) = rpRas->palette;
        if (flags & rwRASTERLOCKWRITE)
        {
            rpRas->privateFlags |= rwRASTERPALETTELOCKEDWRITE;
        }
        if (flags & rwRASTERLOCKREAD)
        {
            rpRas->privateFlags |= rwRASTERPALETTELOCKEDREAD;
        }

        RWRETURN(TRUE);
    }

    RWRETURN(FALSE);
}

/****************************************************************************
 SkyRasterUnlockPalette

 On entry   : NULL
            : Raster
            : Flags
 On exit    : TRUE on success
 */
static RwBool
SkyRasterUnlockPalette(void * __RWUNUSED__ pInOut,
                       void *pRaster,
                       RwInt32 __RWUNUSED__ flags)
{
    RwRaster    *rpRas = (RwRaster *)pRaster;
    RwRasterFormat format ;

    RWFUNCTION(RWSTRING("SkyRasterUnlockPalette"));

    format = (RwRasterFormat) RwRasterGetFormat(rpRas);

    /* One day we will have to do stuff on texture unlock */

    /* Can only do this if we have a palette */
    if (format & (rwRASTERFORMATPAL4|rwRASTERFORMATPAL8))
    {
        /* Make sure the palette data is written through from DCache */
        if( rpRas->palette )
        {
            RwInt32 nPaletteSize = 0, numPalEntries = 0;

            if (format & rwRASTERFORMATPAL4)
            {
                numPalEntries = 16;
            }
            else
            {
                numPalEntries = 256;
            }

            switch (format & rwRASTERFORMATPIXELFORMATMASK)
            {
            case rwRASTERFORMAT1555:
                    /* 2 bytes per palette entry */
                    nPaletteSize = numPalEntries * 2;
                    break;
            case rwRASTERFORMAT8888:
                    /* 4 bytes per palette entry */
                    nPaletteSize = numPalEntries * 4;
                    break;
                default:
                    /* Can't do this format - should never get here */
                    RWERROR((E_RW_INVRASTERFORMAT));
                    RWRETURN(FALSE);
            }

            SyncDCache( rpRas->palette,
                        SCESYNCDCACHEROUNDUP(rpRas->palette + nPaletteSize) );

            rpRas->privateFlags = rpRas->privateFlags & ~rwRASTERPALETTELOCKED;
        }
        RWRETURN(TRUE);
    }

    RWRETURN(FALSE);
}

static RwBool
SkyRasterGetMipLevels(void *pOut,
                      void *pInOut,
                      RwInt32 __RWUNUSED__  nI)
{
    RwRaster *raster = (RwRaster *)pInOut;
    RwInt32  *numMipLevels = (RwInt32 *)pOut;
    const _SkyRasterExt *rasExt = RASTEREXTFROMRASTER(raster);

    RWFUNCTION(RWSTRING("SkyRasterGetMipLevels"));
    RWASSERT(raster);
    RWASSERT(numMipLevels);

    if (raster->cpPixels)
    {
        if ((raster->cFormat & (rwRASTERFORMATMIPMAP >> 8)) && (rasExt))
        {
            *numMipLevels = (rasExt->maxMipLevel >> 2) + 1;
        }
        else
        {
            *numMipLevels = 1;
        }
    }
    else
    {
        RWRETURN(FALSE);
    }

    RWRETURN(TRUE);
}

/****************************************************************************
 sizeTEXInStream

 On entry   : Raster to size up
 On exit    : Size of raster when serialized
 */
static RwUInt32
sizeTEXInStream(const RwRaster *raster)
{
    RwUInt32                size;
    const _SkyRasterExt     *rasExt;
    RwUInt32                pixelDataSize;

    RWFUNCTION(RWSTRING("sizeTEXInStream"));
    RWASSERT(raster);

    rasExt = RASTEREXTFROMRASTERCONST(raster);
    pixelDataSize = rasExt->sysMemSize + rasExt->sysMemPalSize;

    /* The raster structure */
    size = (sizeof(_rwSkyNativeRaster) + rwCHUNKHEADERSIZE);

    /* The pixel data itself */
    size += (pixelDataSize + rwCHUNKHEADERSIZE);

    RWRETURN(size);
}

/****************************************************************************
 writeTEXToStream

 On entry   : Stream to write to
            : Raster to write
            : Number of bytes to write
 On exit    : RwRaster pointer on success, NULL on failure.
 */
static const RwRaster *
writeTEXToStream(RwStream *stream,
                 const RwRaster *raster,
                 RwInt32 __RWUNUSED__  bytesToWrite)
{
    _rwSkyNativeRaster      nativeRaster;
    const _SkyRasterExt     *rasExt;
    RwUInt32                pixelDataSize;

    RWFUNCTION(RWSTRING("writeTEXToStream"))
    RWASSERT(stream);
    RWASSERT(raster);

    /* If the raster is locked then we don't know the correct
     * values.. i.e. mip level 0 might have been altered and
     * these changes will not have been propagated to the other
     * mip levels as this is done when the raster is unlocked.
     */
    if (raster->privateFlags & rwRASTERLOCKED)
    {
        RWMESSAGE(("Cannot serialize a raster with either pixels or palette locked"));
        RWRETURN((const RwRaster *)NULL);
    }

    /* We're gonna need these */
    rasExt = RASTEREXTFROMRASTERCONST(raster);
    pixelDataSize = rasExt->sysMemSize + rasExt->sysMemPalSize;

    /* Dump the state of the raster extension */
    nativeRaster.width = raster->width;
    nativeRaster.height = raster->height;
    nativeRaster.depth = raster->depth;
    nativeRaster.format = ((RwUInt16)raster->cFormat << 8) | raster->cType;
    nativeRaster.version = 0;
    /* For twiddled but compatible textures we use version 1 */
    if ((rasExt->flags == 2) && (raster->depth == 8))
    {
        nativeRaster.version = 1;
    }
#ifdef ALTTEXFORM
    if ((rasExt->flags & 1) == 1)
    {
        /* Things are abit more complex. We have dma data in the texture */
        nativeRaster.version = 2;
    }
#endif /* ALTTEXFORM */
    nativeRaster.lsb = rasExt->lsb;
    nativeRaster.msb = rasExt->msb;
    nativeRaster.palOffset = rasExt->palOffset;
    nativeRaster.maxMipLevel = (RwUInt32)rasExt->maxMipLevel;
    nativeRaster.miptbp1Lsb = rasExt->miptbp1Lsb;
    nativeRaster.miptbp1Msb = rasExt->miptbp1Msb;
    nativeRaster.miptbp2Lsb = rasExt->miptbp2Lsb;
    nativeRaster.miptbp2Msb = rasExt->miptbp2Msb;
    nativeRaster.sysMemSize = rasExt->sysMemSize;
    nativeRaster.sysMemPalSize = rasExt->sysMemPalSize;
    nativeRaster.texCacheSize = rasExt->nTexCacheSize;
    nativeRaster.mipmapKL = rasExt->mipmapKL;
#if defined(GSB) && defined(GSPLUS)
    /* We don't dump the GSPlus specific stuff, as this is just addresses */
#endif /* defined(GSB) && defined(GSPLUS) */

    /* Write the raster descriptor structure */
    if (!RwStreamWriteChunkHeader(stream, rwID_STRUCT, sizeof(nativeRaster)))
    {
        RWRETURN((const RwRaster *)NULL);
    }
    if (!RwStreamWriteInt(stream, (RwInt32 *)&nativeRaster,
                          sizeof(nativeRaster)))
    {
        RWRETURN((const RwRaster *)NULL);
    }

    /* And finally the pixel data (no endianess issues - read and write on
     * same machine).
     */
    if (!RwStreamWriteChunkHeader(stream, rwID_STRUCT, pixelDataSize))
    {
        RWRETURN((const RwRaster *)NULL);
    }
#ifdef ALTTEXFORM
    if ((nativeRaster.version == 0) || (nativeRaster.version == 1))
    {
#endif /* ALTTEXFORM */
        if (!RwStreamWrite(stream, raster->cpPixels, pixelDataSize))
        {
            RWRETURN((const RwRaster *)NULL);
        }
#ifdef ALTTEXFORM
    }
    else if (nativeRaster.version == 2)
    {
        RwUInt32 dataAddr;

        /* This is messy */

        dataAddr = (RwUInt32)((RwUInt8**)(raster->originalPixels))[1];

        /* Write actual pixel data */
        if (!RwStreamWrite(stream, (RwUInt8*)dataAddr, rasExt->sysMemSize))
        {
            RWRETURN((const RwRaster *)NULL);
        }

        /* If there is a palette, write the palette */
        if ((rasExt->sysMemPalSize)
            && (!RwStreamWrite(stream, raster->palette-(5<<4),
                               rasExt->sysMemPalSize)))
        {
            RWRETURN((const RwRaster *)NULL);
        }
    }
    else
    {
        RWMESSAGE(("Failed to serialize a raster (unknown raster serial data version number)"));
        RWRETURN((const RwRaster *)NULL);
    }
#endif /* ALTTEXFORM */

    /* All done - that was nice and easy - not like D3D at all :-) */
    RWRETURN(raster);
}

/****************************************************************************
 readTEXFromStream

 On entry   : Stream to read from
            : Bytes to read
 On exit    : RwRaster pointer on success, NULL on failure
 */
static RwRaster *
readTEXFromStream(RwStream *stream, RwInt32 __RWUNUSED__ structLength)
{
    _rwSkyNativeRaster      nativeRaster;
    RwRaster                *raster;
    _SkyRasterExt           *rasExt;
    RwUInt32                origMemAlloced;
    RwUInt32                length, version;

    RWFUNCTION(RWSTRING("readTEXFromStream"));
    RWASSERT(stream);

    /* Read in the raster descriptor structure */
    if (!RwStreamFindChunk(stream, rwID_STRUCT, &length, &version))
    {
        RWRETURN((RwRaster *)NULL);
    }

    RWASSERT(version >= rwLIBRARYBASEVERSION);
    RWASSERT(version <= rwLIBRARYCURRENTVERSION);
    if ((version >= rwLIBRARYBASEVERSION) &&
        (version <= rwLIBRARYCURRENTVERSION))
    {
        RWASSERT(length <= sizeof(nativeRaster));
        memset(&nativeRaster, 0, sizeof(nativeRaster));
        if (!RwStreamReadInt(stream, (RwInt32 *)&nativeRaster, length))
        {
            RWRETURN((RwRaster *)NULL);
        }

#ifdef ALTTEXFORM
        if (nativeRaster.version < 2)
        {
            skyNoNewStyleRasters = TRUE;
        }
#endif /* ALTTEXFORM */
        /* Create the raster to receive the pixel data */
        raster = RwRasterCreate(nativeRaster.width, nativeRaster.height,
                                nativeRaster.depth, nativeRaster.format);

#ifdef ALTTEXFORM
        skyNoNewStyleRasters = FALSE;
#endif /* ALTTEXFORM */
        if (!raster)
        {
            RWRETURN((RwRaster *)NULL);
        }

        /* compatible old textures have this filled with a sign extention */
#ifdef TWIDDLEEIGHT
#ifdef ALTTEXFORM
        RWASSERT((nativeRaster.version == 0)
                 || (nativeRaster.version == 1)
                 || (nativeRaster.version == 2)
                 || (nativeRaster.version == 0xffff));
#else /* ALTTEXFORM */
        RWASSERT((nativeRaster.version == 0)
                 || (nativeRaster.version == 1)
                 || (nativeRaster.version == 0xffff));
#endif /* ALTTEXFORM */
#else
        RWASSERT((nativeRaster.version == 0)
                 || (nativeRaster.version == 0xffff));
#endif
        /* Find out how much memory we already have */
        rasExt = RASTEREXTFROMRASTER(raster);
        origMemAlloced = rasExt->sysMemSize + rasExt->sysMemPalSize;

        /* Fill in the extension with the right numbers for the pixel data */
        rasExt->lsb = nativeRaster.lsb;
        rasExt->msb = nativeRaster.msb;
        rasExt->palOffset = nativeRaster.palOffset;
        rasExt->maxMipLevel = nativeRaster.maxMipLevel;
        rasExt->miptbp1Lsb = nativeRaster.miptbp1Lsb;
        rasExt->miptbp1Msb = nativeRaster.miptbp1Msb;
        rasExt->miptbp2Lsb = nativeRaster.miptbp2Lsb;
        rasExt->miptbp2Msb = nativeRaster.miptbp2Msb;
        rasExt->sysMemSize = nativeRaster.sysMemSize;
        rasExt->sysMemPalSize = nativeRaster.sysMemPalSize;
        rasExt->nTexCacheSize = nativeRaster.texCacheSize;
        rasExt->mipmapKL = nativeRaster.mipmapKL;
#if defined(GSB) && defined(GSPLUS)
        rasExt->lsb3 = 0;
        rasExt->msb3 = 0;
        rasExt->miptbp3Lsb = 0;
        rasExt->miptbp3Msb = 0;
        rasExt->miptbp4Lsb = 0;
        rasExt->miptbp4Msb = 0;
#endif /* defined(GSB) && defined(GSPLUS) */
#ifdef ALTTEXFORM
        if (nativeRaster.version < 2)
        {
#endif /* ALTTEXFORM */
        raster->palette = raster->cpPixels + rasExt->sysMemSize;
#ifdef ALTTEXFORM
        }
#endif /* ALTTEXFORM */

#ifdef ALTTEXFORM
        if (nativeRaster.version < 2)
        {
#endif /* ALTTEXFORM */
        /* At the moment we overwrite the flags field in the raster */
        /* This code is very brittle: it must be kept in sync with */
        /* the raster creation function */
        if (nativeRaster.version == 1)
        {
            /* A version 1 native texture is an original format raster, */
            /* but twiddled */
            if (!(rasExt->flags & 1))
            {
                rasExt->flags |= 2;
            }
            else
            {
                RwRasterDestroy(raster);
                RWRETURN((RwRaster *)NULL);
            }
        }
        else
        {
            /* all others are non-twiddled */
            if (!(rasExt->flags & 1))
            {
                rasExt->flags &= ~2;
            }
            else
            {
                RwRasterDestroy(raster);
                RWRETURN((RwRaster *)NULL);
            }
        }
#ifdef ALTTEXFORM
        }
#endif /* ALTTEXFORM */

        /* Read in the pixel data */
        if (!RwStreamFindChunk(stream, rwID_STRUCT, &length, &version))
        {
            RwRasterDestroy(raster);
            RWRETURN((RwRaster *)NULL);
        }
        RWASSERT(version >= rwLIBRARYBASEVERSION);
        RWASSERT(version <= rwLIBRARYCURRENTVERSION);
        if ((version >= rwLIBRARYBASEVERSION) &&
            (version <= rwLIBRARYCURRENTVERSION))
        {
            RWASSERT(length <= origMemAlloced); /* SANITY CHECK */
#ifdef ALTTEXFORM
            if (nativeRaster.version < 2)
            {
#endif /* ALTTEXFORM */
            if (RwStreamRead(stream, raster->cpPixels, length) != length)
            {
                RwRasterDestroy(raster);
                RWRETURN((RwRaster *)NULL);
            }

            SyncDCache( raster->cpPixels, SCESYNCDCACHEROUNDUP(raster->cpPixels + length) );
#ifdef ALTTEXFORM
            }
            else if (nativeRaster.version == 2)
            {
                /* Need to read in the thing in two chunks */
                if (RwStreamRead(stream,
                                 ((RwUInt8**)(raster->originalPixels))[1],
                                 rasExt->sysMemSize) != rasExt->sysMemSize)
                {
                    RwRasterDestroy(raster);
                    RWRETURN((RwRaster *)NULL);
                }

                if ((rasExt->sysMemPalSize)
                    && (RwStreamRead(stream, raster->palette-(5<<4),
                                     rasExt->sysMemPalSize)
                        != rasExt->sysMemPalSize))
                {
                    RwRasterDestroy(raster);
                    RWRETURN((RwRaster *)NULL);
                }
                SyncDCache(((RwUInt8**)(raster->originalPixels))[1],
                           SCESYNCDCACHEROUNDUP(raster->palette - (5<<4)
                                                + rasExt->sysMemPalSize));
            }
            else
            {
                RwRasterDestroy(raster);
                RWRETURN((RwRaster *)NULL);
            }
#endif /* ALTTEXFORM */
        }
        else
        {
            RWERROR((E_RW_BADVERSION));
            RWRETURN((RwRaster *)NULL);
        }

        /* All done */
        RWRETURN(raster);
    }
    else
    {
        RWERROR((E_RW_BADVERSION));
        RWRETURN((RwRaster *)NULL);
    }
}

/****************************************************************************
 SkyNativeTextureGetSize

 On entry   : Pointer to size value to fill in
            : Texture to size up
            : 0
 On exit    : TRUE on success
 */
static RwBool
SkyNativeTextureGetSize(void *pSize, void *pTex,
                        RwInt32 __RWUNUSED__ unusedNumber)
{
    RwUInt32    *size = (RwUInt32 *)pSize;
    RwTexture   *texture = (RwTexture *)pTex;
    RwRaster    *raster;

    RWFUNCTION(RWSTRING("SkyNativeTextureGetSize"));
    RWASSERT(size);
    RWASSERT(texture);

    /* Figure out a length and send it back */
    *size = 0;

    /* The header */
    (*size) += (sizeof(_rwSkyNativeTexture) + rwCHUNKHEADERSIZE);

    /* The strings */
    (*size) += (rwStringStreamGetSize(texture->name) + rwCHUNKHEADERSIZE);
    (*size) += (rwStringStreamGetSize(texture->mask) + rwCHUNKHEADERSIZE);

    /* The native raster data */
    raster = RwTextureGetRaster(texture);
    RWASSERT(raster);
    (*size) += (sizeTEXInStream(raster) + rwCHUNKHEADERSIZE);

    RWRETURN(TRUE);
}

/****************************************************************************
 SkyNativeTextureWrite

 On entry   : Stream to write to
            : Texture to write
            : Amount to write
 On exit    : TRUE on success
 */
static RwBool
SkyNativeTextureWrite(void *pStream, void *pTex, RwInt32 __RWUNUSED__ size)
{
    RwStream                *stream = (RwStream *)pStream;
    RwTexture               *texture = (RwTexture *)pTex;
    RwRaster                *raster;
    _rwSkyNativeTexture     nativeTexture;
    RwTextureFilterMode     filtering;
    RwTextureAddressMode    addressingU;
    RwTextureAddressMode    addressingV;
    RwUInt32                sizeOfNativeTextureData;

    RWFUNCTION(RWSTRING("SkyNativeTextureWrite"));
    RWASSERT(stream);
    RWASSERT(texture);

    raster = RwTextureGetRaster(texture);
    RWASSERT(raster);

    /* Struct header for _rwSkyNativeTexture structure */
    if (!RwStreamWriteChunkHeader(stream, rwID_STRUCT, sizeof(nativeTexture)))
    {
        RWRETURN(FALSE);
    }

    /* Set up the identifier */
    memcpy(nativeTexture.id, "PS2\0", 4);
    filtering = RwTextureGetFilterMode(texture);
    addressingU = RwTextureGetAddressingU(texture);
    addressingV = RwTextureGetAddressingV(texture);

    nativeTexture.filterAndAddress = (((RwInt32)filtering) & 0xFF) |
        ((((RwInt32)addressingU) & 0x0F) << 8) |
        ((((RwInt32)addressingV) & 0x0F) << 12);

    /* Convert and write it */
    RwMemLittleEndian(&nativeTexture.filterAndAddress, sizeof(nativeTexture.filterAndAddress));
    if (!RwStreamWrite(stream, &nativeTexture, sizeof(nativeTexture)))
    {
        RWRETURN(FALSE);
    }

    /* Now the strings */
    if (!rwStringStreamWrite(texture->name, stream))
    {
        RWRETURN(FALSE);
    }
    if (!rwStringStreamWrite(texture->mask, stream))
    {
        RWRETURN(FALSE);
    }

    /* The native raster data */
    sizeOfNativeTextureData = sizeTEXInStream(raster);
    if (!RwStreamWriteChunkHeader(stream, rwID_STRUCT, sizeOfNativeTextureData))
    {
        RWRETURN(FALSE);
    }
    if (!writeTEXToStream(stream, raster, sizeOfNativeTextureData))
    {
        RWRETURN(FALSE);
    }

    RWRETURN(TRUE);
}

/****************************************************************************
 SkyNativeTextureRead

 On entry   : Stream to read from
            : Pointer to texture pointer to fill in
            : Amount to read
 On exit    : TRUE on success
 */
static RwBool
SkyNativeTextureRead(void *pStream, void *ppTex, RwInt32 __RWUNUSED__ size)
{
    RwBool                  result;
    RwStream                *stream = (RwStream *)pStream;
    RwTexture               **texture = (RwTexture **)ppTex;
    RwTextureAddressMode    addressingU;
    RwTextureAddressMode    addressingV;
    _rwSkyNativeTexture     nativeTexture;
    RwChar                  textureName[rwTEXTUREBASENAMELENGTH*4];
    RwChar                  textureMask[rwTEXTUREBASENAMELENGTH*4];
    RwUInt32                structLength, version;
    RwRaster                *raster;

    RWFUNCTION(RWSTRING("SkyNativeTextureRead"));
    RWASSERT(pStream);
    RWASSERT(ppTex);

    /* We ain't got it yet */
    *texture = (RwTexture *)NULL;

    if (!RwStreamFindChunk(stream, rwID_STRUCT, &structLength, &version))
    {
        RWRETURN(FALSE);
    }

    RWASSERT(version >= rwLIBRARYBASEVERSION);
    RWASSERT(version <= rwLIBRARYCURRENTVERSION);

    result = ((version >= rwLIBRARYBASEVERSION) &&
              (version <= rwLIBRARYCURRENTVERSION));
    
    if (result)
    {
        RwTextureFilterMode        textureFilterMode;
        RwTexture                 *checkTexture;

        /* This gives us the protection against changing structure sizes */
        RWASSERT(structLength <= sizeof(nativeTexture));
        memset(&nativeTexture, 0, sizeof(nativeTexture));
        if (RwStreamRead(stream, &nativeTexture, structLength) != structLength)
        {
            RWRETURN(FALSE);
        }

        /* Convert it */
        RwMemNative(&nativeTexture.filterAndAddress, 
                    sizeof(nativeTexture.filterAndAddress));

        /* Check the ID field */
        if ((nativeTexture.id[0] != 'P') || (nativeTexture.id[1] != 'S') ||
            (nativeTexture.id[2] != '2') || (nativeTexture.id[3] != 0))
        {
            RWRETURN(FALSE);
        }

        if (!rwStringStreamFindAndRead(textureName,stream))
        {
            RWRETURN(FALSE);
        }
        if (!rwStringStreamFindAndRead(textureMask,stream))
        {
            RWRETURN(FALSE);
        }

        /* Find the native data 'structure' 
         * - This just contains the pixel data */
        if (!RwStreamFindChunk(stream, rwID_STRUCT, &structLength, &version))
        {
            RWRETURN(FALSE);
        }

        RWASSERT(version >= rwLIBRARYBASEVERSION);
        RWASSERT(version <= rwLIBRARYCURRENTVERSION);
        if ((version >= rwLIBRARYBASEVERSION) &&
            (version <= rwLIBRARYCURRENTVERSION))
        {
            raster = readTEXFromStream(stream, structLength);
            if (!raster)
            {
                RWRETURN(FALSE);
            }
        }
        else
        {
            RWERROR((E_RW_BADVERSION));
            RWRETURN(FALSE);
        }

        /* Create a texture from the raster */
        *texture = RwTextureCreate(raster);
        if (!(*texture))
        {
            RwRasterDestroy(raster);
            RWRETURN(FALSE);
        }

        RwTextureSetName(*texture, textureName);
        RwTextureSetMaskName(*texture, textureMask);

#ifdef SHOWUPLOADEDTEXTURES
        {
            RwInt32 nIndex;
            /* Find a spare slot for this texture */
            for (nIndex=0; nIndex<SHOWUPLOADEDTEXTUREMAXTEXTURES; nIndex++)
            {
                if (tpShowUploadedTextures[nIndex]==NULL)
                {
                    printf("Registering Texture %s Raster %x\n", 
                           textureName, raster);
                    tpShowUploadedTextures[nIndex] = *texture;
                    rpShowUploadedRasters[nIndex] = raster;
                    break;
                }
            }
        }
#endif

        /* Set up Texture Modes */
        textureFilterMode = 
            (RwTextureFilterMode)(nativeTexture.filterAndAddress & 0xFF);

        checkTexture = RwTextureSetFilterMode(*texture, textureFilterMode);


        /* Extract addressing */
        addressingU = (RwTextureAddressMode)
                      ((nativeTexture.filterAndAddress >> 8) & 0x0F);
        addressingV = (RwTextureAddressMode)
                      ((nativeTexture.filterAndAddress >> 12) & 0x0F);

        /* Make sure addressingV is valid so files old than 3.04 still work */
        if (addressingV == rwTEXTUREADDRESSNATEXTUREADDRESS)
        {
            addressingV = addressingU;
        }

        checkTexture = RwTextureSetAddressingU(*texture, addressingU);
        checkTexture = RwTextureSetAddressingV(*texture, addressingV);

    }
    else
    {
        RWERROR((E_RW_BADVERSION));
    }

    RWRETURN(result);

}

/****************************************************************************
 SkyHintRenderFront2Back

 On entry   : NULL
            : NULL
            : 0
 On exit    : TRUE on success
 */
static RwBool
SkyHintRenderFront2Back(void * __RWUNUSED__ pInOut, void * __RWUNUSED__ pIn , RwInt32 __RWUNUSED__ flags)
{
    RWFUNCTION(RWSTRING("SkyHintRenderFront2Back"));

    RWRETURN(TRUE);
}

/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

                              Default

 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */

/******************* Standard functions ************************************/

/* Standard functions */
static RwStandard saGStandards[] =
{
    /* Camera ops */
    {rwSTANDARDCAMERABEGINUPDATE, SkyCameraBeginUpdate},
    {rwSTANDARDCAMERAENDUPDATE, SkyCameraEndUpdate},
    {rwSTANDARDCAMERACLEAR, SkyCameraClear},

      /* Raster/Pixel operations */
    {rwSTANDARDRASTERSHOWRASTER, SkyRasterShowRaster},
    {rwSTANDARDRGBTOPIXEL, _rwSkyRGBToPixel},
    {rwSTANDARDPIXELTORGB, _rwSkyPixelToRGB},
    {rwSTANDARDRASTERSETIMAGE, _rwSkyRasterSetImage},
    {rwSTANDARDIMAGEGETRASTER, _rwSkyImageGetRaster},

    /* Raster creation and destruction */
    {rwSTANDARDRASTERDESTROY, SkyRasterDestroy},
    {rwSTANDARDRASTERCREATE, SkyRasterCreate},
    {rwSTANDARDRASTERSUBRASTER, SkyRasterSubRaster},

    /* Finding about a raster type */
    {rwSTANDARDIMAGEFINDRASTERFORMAT, _rwSkyImageFindRasterFormat},

    /* Texture operations */
    {rwSTANDARDTEXTURESETRASTER, SkyTextureSetRaster},

    /* Locking and releasing */
    {rwSTANDARDRASTERLOCK, SkyRasterLock},
    {rwSTANDARDRASTERUNLOCK, SkyRasterUnlock},
    {rwSTANDARDRASTERLOCKPALETTE, SkyRasterLockPalette},
    {rwSTANDARDRASTERUNLOCKPALETTE, SkyRasterUnlockPalette},

      /* Raster operations */
    {rwSTANDARDRASTERCLEAR, _rwSkyRasterClear},
    {rwSTANDARDRASTERCLEARRECT, _rwSkyRasterClearRect},
    {rwSTANDARDRASTERRENDER, _rwSkyRasterRender},
    {rwSTANDARDRASTERRENDERSCALED, _rwSkyRasterRenderScaled},
    {rwSTANDARDRASTERRENDERFAST, _rwSkyRasterRenderFast},
    {rwSTANDARDSETRASTERCONTEXT, _rwSkyRasterSetContext},

      /* Render order hint */
    {rwSTANDARDHINTRENDERF2B, SkyHintRenderFront2Back},

    /* Device specific texture dictionary serialisation */
    {rwSTANDARDNATIVETEXTUREGETSIZE, SkyNativeTextureGetSize},
    {rwSTANDARDNATIVETEXTUREWRITE, SkyNativeTextureWrite},
    {rwSTANDARDNATIVETEXTUREREAD, SkyNativeTextureRead},

    /* Raster mip levels */
    {rwSTANDARDRASTERGETMIPLEVELS, SkyRasterGetMipLevels}
};

/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

                      Setting up of standard functions

 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */

/****************************************************************************
 SkyNoStandard

 On entry   : pOut
            : pInOut
            : nIn
 On exit    : FALSE
 */
static RwBool
SkyNoStandard(void *__RWUNUSED__ pOut,
              void * __RWUNUSED__ pInOut,
              RwInt32 __RWUNUSED__ nIn)
{
    RWFUNCTION(RWSTRING("SkyNoStandard"));

    RWRETURN(FALSE);
}

/****************************************************************************
 SkySetStandards

 On entry   : Standard functions
            : Space
            : Standards
            : Amount of standards
 On exit    :
 */
static void
SkySetStandards(RwStandardFunc *fppFuncs, RwInt32 nNumFuncs,
                RwStandard *spStandards, RwInt32 nNumStandards)
{
    RwInt32 nI;

    RWFUNCTION(RWSTRING("SkySetStandards"));

    /* Clear out all of the standards initially */
    for (nI=0;nI<nNumFuncs;nI++)
    {
        fppFuncs[nI] = SkyNoStandard;
    }

    /* Fill in all of the standards */

    while (nNumStandards--)
    {
        if ((spStandards->nStandard<nNumFuncs)&&
            (spStandards->nStandard>=0))
        {
            fppFuncs[spStandards->nStandard] =
                spStandards->fpStandard;
        }

        spStandards++;
    }

    RWRETURNVOID();
}

/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

                           System access

 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */

static void
setInitialRenderState(void)
{
    long tmp, tmp1;
    u_long128 gifTagCmd = 0;
    u_long128 alpha1Cmd = 0;
    u_long128 clamp1Cmd = 0;
    u_long128 pabeCmd = 0;
    u_long128 texACmd = 0;
    u_long128 dimxCmd = 0;

    RWFUNCTION(RWSTRING("setInitialRenderState"));

    /* First set up our values */
    skySrcBlend = 2; /* SRCALPHA */
    skyDestBlend = 3; /* INVSRCALPHA */
    skyAlpha_1 = (long)skyBlendStates[skyDestBlend][skySrcBlend];

    skyVertexAlpha = FALSE; /* Alpha blending off to start with */
    skyAlphaTex = FALSE;
    skyTextureRaster = (RwRaster *)NULL;
    skyPrim_State = 0x8; /* Gouraud shading on */

    gSkyCullState = rwCULLMODECULLNONE;
    skyClamp_1 = 0;

    /* Now send them to the GS */
    tmp = /* NLOOP */ 5l
#ifdef LESSEOPS
        | /* EOP */ (0l<<15)
#else /* LESSEOPS */
        | /* EOP */ (1l<<15)
#endif /* LESSEOPS */
        | /* PRE */ (0l<<46)
        | /* FLG */ (0l<<58)
        | /* NREG */ (1l<<60);
    tmp1 = /* A+D */ (0xel<<(64-64));
    MAKE128(gifTagCmd, tmp1, tmp);

    MAKE128(alpha1Cmd, GS_ALPHA_1, skyAlpha_1);
    MAKE128(clamp1Cmd, GS_CLAMP_1, skyClamp_1);
    MAKE128(pabeCmd, GS_PABE, 0);
    tmp = /* TA0 */ (0x0l) |
        /* AEM */ (0l<<15) |
        /* TA1 */ (0X80l<<32);
    MAKE128(texACmd, GS_TEXA, tmp);
    tmp = (DIMX_4 << 0)  | /* dimx(0,0) = -4 */
        (DIMX2 << 4)   | /* dimx(0,1) = 2 */
        (DIMX_3 << 8)  | /* dimx(0,2) = -3 */
        (DIMX3 << 12)  | /* dimx(0,3) = 3 */
        (DIMX0 << 16)  | /* dimx(1,0) = 0 */
        (DIMX_2 << 20) | /* dimx(1,1) = -2 */
        (DIMX1 << 24)  | /* dimx(1,2) = 1 */
        (DIMX_1 << 28) | /* dimx(1,3) = -1 */
        (DIMX_3 << 32) | /* dimx(2,0) = -3 */
        (DIMX3 << 36)  | /* dimx(2,1) = 3 */
        (DIMX_4 << 40) | /* dimx(2,2) = -4 */
        (DIMX2 << 44)  | /* dimx(2,3) = 2 */
        (DIMX1 << 48)  | /* dimx(3,0) = 1 */
        (DIMX_1 << 52) | /* dimx(3,1) = -1 */
        (DIMX0 << 56)  | /* dimx(3,2) = 0 */
        (DIMX_2 << 60);  /* dimx(3,3) = -2 */
    MAKE128(dimxCmd, GS_DIMX, tmp);
    skyDitherState = 1;

    sweOpenLocalPkt(SWE_LPS_CONT, -6);
    SWEADDCONTGIFFAST(gifTagCmd, 5);
    SWEADDCONTFAST(alpha1Cmd);
    SWEADDCONTFAST(clamp1Cmd);
    SWEADDCONTFAST(pabeCmd);
    SWEADDCONTFAST(texACmd);
    SWEADDCONTFAST(dimxCmd);

    /* Set global rendering state */
    _rwSkySetRenderState(rwRENDERSTATEZTESTENABLE, (void *) TRUE);
    _rwSkySetRenderState(rwRENDERSTATEZWRITEENABLE, (void *) TRUE);

    RWRETURNVOID();
}

#ifdef PROFILE
void
sweResetCount(void)
{
    RWFUNCTION(RWSTRING("sweResetCount"));

    /* This form of profiling is no longer used */

    RWRETURNVOID();
}

void
sweDumpCount(void)
{
    RWFUNCTION(RWSTRING("sweDumpCount"));

    printf("Defunct profiler invoked\n");

    RWRETURNVOID();

}
#endif /* PROFILE */

/************************************************************************
 *  Slaves to SkySystem()
 */

static RwBool
SkyUseMode(RwInt32 nIn)
{
    RwBool result = FALSE;

    RWFUNCTION(RWSTRING("SkyUseMode"));

    /* Mode index must be in range, and engine not started */
    result =
        (!engineStarted) &&
        (nIn < (RwInt32) (sizeof(videoModes) / sizeof(RwVideoMode)));

    if (result)
    {
        currentMode = nIn;
        if (videoModes[currentMode].depth == 32)
        {
            currentCameraFormat = (RwInt32) rwRASTERFORMAT8888;
        }
        else
        {
            currentCameraFormat = (RwInt32) rwRASTERFORMAT1555;
        }
    }

    RWRETURN(result);
}

static RwBool
SkyGetNumModes(void *pOut)
{
    RWFUNCTION(RWSTRING("SkyGetNumModes"));

    *((RwInt32 *) pOut) = sizeof(videoModes) / sizeof(RwVideoMode);

    RWRETURN(TRUE);
}

static RwBool
SkyGetModeinfo(void *pOut, RwInt32 nIn)
{
    RwBool             result;
    RwVideoMode        *vpMode;

    RWFUNCTION(RWSTRING("SkyGetModeinfo"));

    vpMode = (RwVideoMode *) pOut;

    result =
        nIn < ((RwInt32) (sizeof(videoModes) / sizeof(RwVideoMode)));

    if (result)
    {
        *vpMode = videoModes[nIn];
    }

    /* Mode index out of range */
    RWRETURN(result);
}

static RwBool
SkyGetMode(void *pOut)
{
    RWFUNCTION(RWSTRING("SkyGetMode"));

    *((RwInt32 *) pOut) = currentMode;
    RWRETURN(TRUE);
}

static RwBool
SkyFocus(void)
{
    RWFUNCTION(RWSTRING("SkyFocus"));

    /* Irrelevant for this console port - yeah!!! */
    RWRETURN(TRUE);
}

/******************* Opening/Closing **********************/

/* Gives the device access to the libraries device block */

static RwBool
SkyRegister(void *pOut, void *pInOut)
{
    RwBool              result;
    RwDevice           *dpDevice;
    RwMemoryFunctions  *mfpFuncs;
    RwDevice           *lpdGSkyDevice;

    RWFUNCTION(RWSTRING("SkyRegister"));

    dpDevice = (RwDevice *) pOut;
    mfpFuncs = (RwMemoryFunctions *) pInOut;
    lpdGSkyDevice = _rwDeviceGetHandle();

    /* Set up the fast device access.. */

    *dpDevice = *lpdGSkyDevice;

    dgGGlobals.memFuncs = mfpFuncs;

    /* Reserve some memory inside raster structures */
    skyRasterExt = RwRasterRegisterPlugin(sizeof(_SkyRasterExt),
                                          rwID_DEVICEMODULE,
                                          (RwPluginObjectConstructor)NULL,
                                          (RwPluginObjectDestructor)NULL,
                                          (RwPluginObjectCopy)NULL);

    result = (skyRasterExt >= 0);
    if (result)
    {
        skyCameraExt =
            RwCameraRegisterPlugin(sizeof(_SkyCameraExt),
                                   rwID_DEVICEMODULE,
                                   (RwPluginObjectConstructor)NULL,
                                   (RwPluginObjectDestructor)NULL,
                                   (RwPluginObjectCopy)NULL);
        result = (skyCameraExt >=0);
        if (result)
        {
            skyTextureExt =
                RwTextureRegisterPlugin(0,
                                        rwID_SKYMIPMAPVAL,
                                        (RwPluginObjectConstructor)NULL,
                                        (RwPluginObjectDestructor)NULL,
                                        (RwPluginObjectCopy)NULL);

            result = (skyTextureExt >= 0);
            if (result)
            {
                skyTextureExt =
                    RwTextureRegisterPluginStream(rwID_SKYMIPMAPVAL,
                                                  skyReadTexCB,
                                                  skyWriteTexCB,
                                                  skyGetSizeTexCB);
                result = (skyTextureExt >= 0);
            }
        }
    }

    RWRETURN(result);
}

/******************* Opening/Closing **********************/

/* The purpose of Open is to put the hardware in a state where
 * it can be queried (about its capabilities). This function also
 * tests if it is a candidate at all (like it can be started at
 * all). NOTE this includes setting up a minimal set of standard
 * handlers that can be used for querying the device. */

static RwBool
SkyOpen(void)
{
    int i;
    RWFUNCTION(RWSTRING("SkyOpen"));

    /* What's a window handle? */

#ifdef SHOWUPLOADEDTEXTURES
    {
        RwInt32 nIndex;
        /* Find a this texture */
        for (nIndex = 0;
             nIndex < SHOWUPLOADEDTEXTUREMAXTEXTURES;
             nIndex++)
        {
            tpShowUploadedTextures[nIndex] = NULL;
            rpShowUploadedRasters[nIndex] = NULL;
        }
    }
#endif

    /* I'm going to kick the co-pros at this point. I suspect that */
    /* we might later move this start */
    /* Enable cop2 access */

#if (!defined(__MINGW32__))
    __asm
        (
         "mfc0 $3,$12;              \
          dli $2,0x40000000;        \
          or $3,$2,$3;              \
          mtc0 $3,$12"
         );
#endif /* (!defined(__MINGW32__)) */

    /* Build fog tables */
    /* First add dma and vif tags */

    {
        unsigned long tmp, tmp1;
        u_long128 ltmp = 0;

        tmp = (1l << 31) | (7l << 28) | (13l);
        tmp1 = (0x1l << 24) | (1l << 8) | (2l) | ((0x5l << 24) << 32);
        MAKE128(ltmp, tmp1, tmp);
        skyLinearFog[0] = skyExpFog[0] = skyExp2Fog[0] =
            skyMungedUserFog[0] = ltmp;

        tmp = (0x20l << 24) | (0x3f3f3f3fl << 32);
        tmp1 = (0x71l << 24) | (48l << 16) | (0x4001l);
        MAKE128(ltmp, tmp1, tmp);
        skyLinearFog[1] =
            skyExpFog[1] =
            skyExp2Fog[1] =
            skyMungedUserFog[1] = ltmp;

        tmp1 = ((0x71l << 24) | (48l << 16) | (0x4202l)) << 32;
        MAKE128(ltmp, tmp1, 0l);
        skyLinearFog[7] =
            skyExpFog[7] =
            skyExp2Fog[7] =
            skyMungedUserFog[7] = ltmp;

    }
    /* Table entries are from ((unsigned short)tbl)[14] even */
    /* and ((unsigned short)tbl[64] odd */
    /* We index the table from the far clip plane */
    /* We adjust the entries from the 256 entry table as follows */
    /* J=i*2.6842 v = ([int(J)]*(1.0-frc(J))+[int(J)+1]*frc(J))/2 */

    for (i = 0; i < 96; i++)
    {
        RwInt32 quantize;

        /* This uses doubles, but is only done once */
        /* Remember that fog values are reversed to RW */
        if (!((95 - i) % 2))
        {
            /* even */

            quantize = (RwInt32) (255.0f - (i * 2.6842f));
            ((RwUInt16 *) skyLinearFog)[14 + ((95 - i) >> 1)] =
                ((RwUInt16) quantize) << 4;

            quantize = (RwInt32)
                (255.0f / RwPow(256.0f, (i / 95.0f)));
            ((RwUInt16 *) skyExpFog)[14 + ((95 - i) >> 1)] =
                ((RwUInt16) quantize) << 4;

            quantize = (RwInt32)
                ( 255.0f / RwPow(256.0f,  (i / 95.0f) * (i / 95.0f)));
            ((RwUInt16 *) skyExp2Fog)[14 + ((95 - i) >> 1)] =
                ((RwUInt16) quantize) << 4;

        }
        else
        {
            /* odd */

            quantize = (RwInt32)
                (255.0f - (i * 2.6842f));
            ((RwUInt16 *) skyLinearFog)[64 + ((95 - i) >> 1)] =
                ((RwUInt16) quantize) << 4;

            quantize = (RwInt32)
                (255.0f / RwPow(256.0f, (i / 95.0f)));
            ((RwUInt16 *) skyExpFog)[64 + ((95 - i) >> 1)] =
                ((RwUInt16) quantize) << 4;

            quantize = (RwInt32)
                ( 255.0f / RwPow(256.0f, (i / 95.0f) * (i / 95.0f)) );
            ((RwUInt16 *) skyExp2Fog)[64 + ((95 - i) >> 1)] =
                ((RwUInt16) quantize) << 4;

        }

    }
    SyncDCache(skyLinearFog, SCESYNCDCACHEROUNDUP(&skyLinearFog[14]));
    SyncDCache(skyExpFog, SCESYNCDCACHEROUNDUP(&skyExpFog[14]));
    SyncDCache(skyExp2Fog, SCESYNCDCACHEROUNDUP(&skyExp2Fog[14]));
    SyncDCache(skyMungedUserFog, SCESYNCDCACHEROUNDUP(&skyMungedUserFog[14]));
    fogTable = skyLinearFog;

    /* Default to the last mode */
    engineStarted = FALSE;
    currentMode = (sizeof(videoModes) / sizeof(RwVideoMode)) - 1;

    if (videoModes[currentMode].depth == 32)
    {
        currentCameraFormat = (RwInt32) rwRASTERFORMAT8888;

    }
    else
    {
        currentCameraFormat = (RwInt32) rwRASTERFORMAT1555;

    }
    /* Done */
    RWRETURN(TRUE);
}

/* The purpose of Close is to remove any set up that was performed
   by Open */

static RwBool
SkyClose(void)
{
    RWFUNCTION(RWSTRING("SkyClose"));

    /* All done - nothing to do */
#ifdef PROFILE
    sweDumpCount();
#endif /* PROFILE */
#ifdef PG
    _sweProfileDump();
#endif /* PG */

    RWRETURN(TRUE);
}

/************** Starting stopping ******************/


/* Puts back the hardware into the state it was in immediately
           after being opened */

static RwBool
SkyStop(void)
{
    RWFUNCTION(RWSTRING("SkyStop"));

    /* All done */
    _sweClose();
    skyTexCacheCloseCache();

    engineStarted = FALSE;
    cacheOpened = FALSE;
    needCache = TRUE;

    RWRETURN(TRUE);
}

/* Installs device specific overloads */
static RwBool
SkyFinalizeStart(void)
{
    RwBool result;
    RWFUNCTION(RWSTRING("SkyFinalizeStart"));

    _rwSkyOverloadPipelines();

    result = _rwSkyRasterFinaliseStart();

    /* Create the framebuffer texture access rasters */
    result = result && _rpSkyCreateFrameBufferRasters();

    if (!result)
    {
        _rwSkyUnOverloadPipelines();
    }
    else
    {
        /* Put in the ASM multiplies */
        rwMatrixSetMultFn(matrixASMMult);
        rwVectorSetMultFn((rwVectorMultFn) vectorASMMultPoint,
                          (rwVectorMultFn) vectorASMMultVector);
    }

    RWRETURN(result);
}

/* Removes device specific overloads */
static RwBool
SkyInitiateStop(void)
{
    RWFUNCTION(RWSTRING("SkyInitiateStop"));

    /* Remove the pipeline overloads */
    _rwSkyRasterInitiateStop();
    _rpSkyDestroyFrameBufferRasters();
    _rwSkyUnOverloadPipelines();

    RWRETURN(TRUE);
}

/* Query caps of device */
static RwBool
SkyGetMaxTextureSize(void *pOut)
{
    RWFUNCTION(RWSTRING("SkyGetMaxTextureSize"));

    *(RwInt32 *) pOut = 1024;
    RWRETURN(TRUE);
}

static RwBool
SkyGetTexMemSize(void *pOut)
{
    RwBool result;

    RWFUNCTION(RWSTRING("SkyGetTexMemSize"));

    result = engineStarted;

    if (result)
    {
#if defined(GSB) && defined(GSPLUS)
        *(RwInt32 *) pOut = ((8 * 1024 * 1024) - textureBankStart) * 4;
#else /* defined(GSB) && defined(GSPLUS) */
        *(RwInt32 *) pOut = ((1 * 1024 * 1024) - textureBankStart) * 4;
#endif /* defined(GSB) && defined(GSPLUS) */

    }

    RWRETURN(result);
}

/************* pipeline initialisation **************************/
static RwBool
SkyInitPipeline(void)
{
    RWFUNCTION(RWSTRING("SkyInitPipeline"));

    /* The pipeline already comes empty, we don't need to blank
     * anything out.
     */

    RWRETURN(TRUE);
}

/************* standard device functions ************************/

static RwBool
SkyStandards(void *pOut, RwInt32 nIn)
{
    RWFUNCTION(RWSTRING("SkyStandards"));

    SkySetStandards((RwStandardFunc *) pOut,
                    nIn,
                    saGStandards,
                    sizeof(saGStandards) / sizeof(RwStandard));

    RWRETURN(TRUE);
}

/* Start means that this bit of hardware has been selected for
 * rendering - so get it into a state ready for rendering */

static RwBool
SkyStart(void)
{
    RwBool result;
    RWFUNCTION(RWSTRING("SkyStart"));

    /* Set up a very crude screen clear which we can kick off */
    /* immediately after GsResetGraph to meet TRC screen clear */
    /* of PlayStation2 requirement */
    skyInitialClear.giftag.NLOOP = 1;
    skyInitialClear.giftag.EOP = 1;
    skyInitialClear.giftag.pad16 = 0;
    skyInitialClear.giftag.id = 0;
    skyInitialClear.giftag.PRE = 0;
    skyInitialClear.giftag.PRIM = 0;
    skyInitialClear.giftag.FLG = 0;
    skyInitialClear.giftag.NREG = 12;
    skyInitialClear.giftag.REGS0 = 0xe;
    skyInitialClear.giftag.REGS1 = 0xe;
    skyInitialClear.giftag.REGS2 = 0xe;
    skyInitialClear.giftag.REGS3 = 0xe;
    skyInitialClear.giftag.REGS4 = 0xe;
    skyInitialClear.giftag.REGS5 = 0xe;
    skyInitialClear.giftag.REGS6 = 0xe;
    skyInitialClear.giftag.REGS7 = 0xe;
    skyInitialClear.giftag.REGS8 = 0xe;
    skyInitialClear.giftag.REGS9 = 0xe;
    skyInitialClear.giftag.REGS10 = 0xe;
    skyInitialClear.giftag.REGS11 = 0xe;

    skyInitialClear.alpha = 0;
    skyInitialClear.alphaadr = 0x42;

    skyInitialClear.frame = 0x100000l;
    skyInitialClear.frameadr = 0x4c;

    skyInitialClear.prmodecont = 0x1l;
    skyInitialClear.prmodecontadr = 0x1a;

    skyInitialClear.scissor = 0x03ff000003ff0000l;
    skyInitialClear.scissoradr = 0x40;

    skyInitialClear.test = 0x30000l;
    skyInitialClear.testadr = 0x47;

    skyInitialClear.xyoffset = 0x0l;
    skyInitialClear.xyoffsetadr = 0x18;

    skyInitialClear.zbuf = 0x10a000100l;
    skyInitialClear.zbufadr = 0x4e;

    skyInitialClear.prim = 0x6l;
    skyInitialClear.primadr = 0x0;

    skyInitialClear.rgbaqa = 0x0l;
    skyInitialClear.rgbaqadra = 0x1;

    skyInitialClear.xyza = 0x0l;
    skyInitialClear.xyzadra = 0x5;

    skyInitialClear.rgbaqb = 0x0l;
    skyInitialClear.rgbaqadrb = 0x1;

    skyInitialClear.xyzb = 0x3fff3fffl;
    skyInitialClear.xyzadrb = 0x5;

    SyncDCache(&skyInitialClear,
               SCESYNCDCACHEROUNDUP((RwUInt8 *) & skyInitialClear
                                    + sizeof(skyInitialClear)));

#ifdef GSB
    gsbInit();
    skyGsmId = gsbGetLocalModuleId();
    skyNumSeqFrms = skyNumGGSMS / (skyNumGGSMSFrame*skyNumZMergFrame);
    skyRenderSlot = skyGsmId/(skyNumGGSMSFrame*skyNumZMergFrame);
    RWASSERT(skyRenderSlot < skyNumSeqFrms);
    skyNeedDrawNext = TRUE;
#endif /* GSB */

    /* kick the required display mode */
    _rwSkyInitConfigSystem();

    sceGsSyncV(0);
#ifdef GSB
    sceGsResetGraph(0, (videoModes[currentMode].flags >> 24) & 0x1,
                    (videoModes[currentMode].flags) & 0xffff,
                    (videoModes[currentMode].flags >> 16) & 0xff);
    if (skyGSMReadInput)
    {
        skyGSMInputCh = gsbOpenChannel(GSB_HOST, GSB_OPEN_HOST);
    }
    {
        int                 i;

        for (i = 0; i < 6; i++)
        {
            skyGSMInput[i] = 0;
        }
    }
#else /* GSB */
    /* Really I should have a flag, but I'll just go by the height */
    if ((videoModes[currentMode].height == 256)
        || (videoModes[currentMode].height == 512))
    {
        /* PAL mode */
        if (videoModes[currentMode].flags & rwVIDEOMODEFFINTERLACE)
        {
            sceGsResetGraph(0, SCE_GS_INTERLACE, SCE_GS_PAL, SCE_GS_FRAME);
        }
        else if (videoModes[currentMode].flags & rwVIDEOMODEFSAA0)
        {
            sceGsResetGraph(0, SCE_GS_INTERLACE, SCE_GS_PAL, SCE_GS_FRAME);
        }
        else if (videoModes[currentMode].flags & rwVIDEOMODEINTERLACE)
        {
            sceGsResetGraph(0, SCE_GS_INTERLACE, SCE_GS_PAL, SCE_GS_FIELD);
        }
        else
        {
            sceGsResetGraph(0, SCE_GS_NOINTERLACE, SCE_GS_PAL, SCE_GS_FRAME);
        }
    }
    else
    {
        /* NTSC is the default */
        if (videoModes[currentMode].flags & rwVIDEOMODEFFINTERLACE)
        {
            sceGsResetGraph(0, SCE_GS_INTERLACE, SCE_GS_NTSC, SCE_GS_FRAME);
        }
        else if (videoModes[currentMode].flags & rwVIDEOMODEFSAA0)
        {
            sceGsResetGraph(0, SCE_GS_INTERLACE, SCE_GS_NTSC, SCE_GS_FRAME);
        }
        else if (videoModes[currentMode].flags & rwVIDEOMODEINTERLACE)
        {
            sceGsResetGraph(0, SCE_GS_INTERLACE, SCE_GS_NTSC, SCE_GS_FIELD);
        }
        else
        {
            sceGsResetGraph(0, SCE_GS_NOINTERLACE, SCE_GS_NTSC, SCE_GS_FRAME);
        }
    }
#endif /* GSB */
    /* Dispatch the screen clear */
    sceDmaSendN(sceDmaGetChan(2), &skyInitialClear,
                sizeof(skyInitialClear) >> 4);
#ifdef GSB
    skyGsmWidth = videoModes[currentMode].width /
        (skyNumGGSMSFrame > 1 ? 2 : 1);
    skyGsmHeight = videoModes[currentMode].height /
        (skyNumGGSMSFrame > 2 ? 2 : 1);

#if defined(GSB) && defined(GSPLUS)
    sceGsPlusSetDefDBuffDc(&sweDb, (videoModes[currentMode].depth == 16 ?
                                 SCE_GS_PSMCT16S : SCE_GS_PSMCT32),
                           skyGsmWidth, skyGsmHeight,
                           SCE_GS_ZGEQUAL,
                           (zBufferDepth ==
                            16 ? SCE_GS_PSMZ16S : SCE_GS_PSMZ32),
                           SCE_GS_NOCLEAR);
#else /* defined(GSB) && defined(GSPLUS) */
    sceGsSetDefDBuffDc(&sweDb, (videoModes[currentMode].depth == 16 ?
                             SCE_GS_PSMCT16S : SCE_GS_PSMCT32),
                       skyGsmWidth,
                       skyGsmHeight,
                       SCE_GS_ZGEQUAL,
                       (zBufferDepth == 16 ? SCE_GS_PSMZ16S : SCE_GS_PSMZ32),
                       SCE_GS_NOCLEAR);
#endif /* defined(GSB) && defined(GSPLUS) */
    /* We now need to adjust the display fields in this structure */
    if (skyNumGGSMSFrame > 1)
    {
        skyGsmOffsetX = -(videoModes[currentMode].width / 4);
        skyGsmOffsetX = 0;
    }
    else
    {
        skyGsmOffsetX = 0;
    }
    if (skyNumGGSMSFrame > 2)
    {
        skyGsmOffsetY = -(videoModes[currentMode].height / 4);
        skyGsmOffsetY = 0;
    }
    else
    {
        skyGsmOffsetY = 0;
    }
    if (
        ((skyGsmId
          -
          skyRenderSlot * skyNumGGSMSFrame * skyNumZMergFrame) %
         skyNumGGSMSFrame) & 1)
    {
        /* Move display across */
        sweDb.disp[0].display.DX += (videoModes[currentMode].width / 2)
            * (1 << sweDb.disp[0].display.MAGH);
        sweDb.disp[1].display.DX = sweDb.disp[0].display.DX;
        skyGsmOffsetX = (videoModes[currentMode].width / 4);
        skyGsmOffsetX = (videoModes[currentMode].width / 2);
    }
    if (
        ((skyGsmId
          -
          skyRenderSlot * skyNumGGSMSFrame * skyNumZMergFrame) %
         skyNumGGSMSFrame) & 2)
    {
        /* Move display down */
        sweDb.disp[0].display.DY += videoModes[currentMode].height / 2;
        sweDb.disp[1].display.DY = sweDb.disp[0].display.DY;
        skyGsmOffsetY = videoModes[currentMode].height / 4;
        skyGsmOffsetY = videoModes[currentMode].height / 2;
    }
    SyncDCache(&sweDb,
               SCESYNCDCACHEROUNDUP((RwUInt8 *) &sweDb + sizeof(sweDb)));
#else /* GSB */

    /* in the FSAA mode the width and height are specified for
     * the back screen, but we want this function to compute the
     * front screen settings, so sort out the width and height.
     */
    if (videoModes[currentMode].flags & rwVIDEOMODEFSAA0)
    {
        RwUInt32 skyFSAA0VisibleHeight =
            videoModes[currentMode].height >> 1;

        if (skyFSAA0VisibleWidth == 0)
        {
            skyFSAA0VisibleWidth = videoModes[currentMode].width >> 1;
        }

        sceGsSetDefDBuffDc( &sweDb,
                            (videoModes[currentMode].depth == 16 ?
                             SCE_GS_PSMCT16S : SCE_GS_PSMCT32),
                            skyFSAA0VisibleWidth,
                            skyFSAA0VisibleHeight,
                            SCE_GS_ZGEQUAL,
                            (zBufferDepth == 16 ?
                             SCE_GS_PSMZ16S : SCE_GS_PSMZ32),
                            SCE_GS_NOCLEAR );

        /* Reset FSAA0 frame counter so that new dma packets get
         * constructed incase FSAA0 mode is different to the last
         * one used.
         */
        skyFSAA0FrameCount = 0;

        skyFSAA0RenderWidth = videoModes[currentMode].width;
        skyFSAA0RenderHeight = videoModes[currentMode].height;

        /* cos we've changed the screen size we have to compute
         * correct xyoffsets... with the centre of the screen 2048,2048
         */
        *(long *) &sweDb.draw01.xyoffset1 =
            (2048l - (skyFSAA0RenderWidth >> 1)) << 4 |
            (2048l - (skyFSAA0RenderHeight >> 1)) << 36;
    }
    else
    {
        sceGsSetDefDBuffDc( &sweDb,
                            (videoModes[currentMode].depth == 16 ?
                             SCE_GS_PSMCT16S : SCE_GS_PSMCT32),
                            videoModes[currentMode].width,
                            videoModes[currentMode].height,
                            SCE_GS_ZGEQUAL,
                            (zBufferDepth == 16 ?
                             SCE_GS_PSMZ16S : SCE_GS_PSMZ32),
                            SCE_GS_NOCLEAR );
    }

#endif /* GSB */

    /* We now test to see if we are in a FIELD interlace mode */
#ifdef GSB
    if (videoModes[currentMode].flags
        & ((SCE_GS_INTERLACE << 24) | (SCE_GS_FIELD << 16)))
#else /* GSB */
        if (videoModes[currentMode].flags & rwVIDEOMODEINTERLACE)
#endif /* GSB */
        {
            int                 size;

            /* libgraph doesn't set up correctly, so we fudge a bit */
            size = videoModes[currentMode].width
                * videoModes[currentMode].height;
            if (videoModes[currentMode].depth == 16)
            {
                size >>= 1;
            }
            size = (size + 2047) >> 11;
            sweDb.disp[1].dispfb.FBP = size;
            sweDb.draw01.frame1.FBP = size;
            sweDb.draw02.frame2.FBP = size;
            sweDb.draw01.zbuf1.ZBP += size;
            sweDb.draw02.zbuf2.ZBP += size;
            sweDb.draw11.zbuf1.ZBP += size;
            sweDb.draw12.zbuf2.ZBP += size;

            /* If FSAA1
             * Set up circuit 1 to render normally, then offset
             * circuit 2 by half a pixel in x & y
             * Build DMA packets straight away
             */
            if (videoModes[currentMode].flags & rwVIDEOMODEFSAA1)
            {
                unsigned long       tmp, tmp1;
                u_long128           ltmp;

                /* DMA header */
                tmp = (7l << 28) | (3); /* END DMAtag, 3 quadword after the dmatag */
                tmp1 = 0l;
                MAKE128(ltmp, tmp1, tmp);
                tcaaDisp.dmaDisp1[0] = ltmp;
                tcaaDisp.dmaDisp2[0] = ltmp;

                /* GIF path3 Address+Data mode */
                tmp = /* NLOOP */ 2l
                    | /* EOP */ (1l << 15)
                    | /* PRE */ (0l << 46)
                    | /* FLG */ (0l << 58)
                    | /* NREG */ (1l << 60);
                tmp1 = /* A+D */ (0xel << (64 - 64));
                MAKE128(ltmp, tmp1, tmp);
                tcaaDisp.dmaDisp1[1] = ltmp;
                tcaaDisp.dmaDisp2[1] = ltmp;

                /* DISPFB1 Register (Display 0) */
                tmp = *(long *) &sweDb.disp[0].dispfb;
                tmp1 = (unsigned long) GS_DISPFB1;
                MAKE128(ltmp, tmp1, tmp);
                tcaaDisp.dmaDisp1[2] = ltmp;

                /* DISPFB1 Register (Display 1) */
                tmp = *(long *) &sweDb.disp[1].dispfb;
                tmp1 = (unsigned long) GS_DISPFB1;
                MAKE128(ltmp, tmp1, tmp);
                tcaaDisp.dmaDisp2[2] = ltmp;

                /* DISPLAY1 Register (Display 0) */
                tmp = *(long *) &sweDb.disp[0].display;
                tmp1 = (unsigned long) GS_DISPLAY1;
                MAKE128(ltmp, tmp1, tmp);
                tcaaDisp.dmaDisp1[3] = ltmp;

                /* DISPLAY Register (Display 1) */
                tmp = *(long *) &sweDb.disp[1].display;
                tmp1 = (unsigned long) GS_DISPLAY1;
                MAKE128(ltmp, tmp1, tmp);
                tcaaDisp.dmaDisp2[3] = ltmp;

                /*     SyncDCache(&tcaaDisp, SCESYNCDCACHEROUNDUP(((int)&tcaaDisp)+sizeof(tcaaDisp))); */
                SyncDCache(tcaaDisp.dmaDisp1,
                           SCESYNCDCACHEROUNDUP((tcaaDisp.dmaDisp1) + 4));
                SyncDCache(tcaaDisp.dmaDisp2,
                           SCESYNCDCACHEROUNDUP((tcaaDisp.dmaDisp2) + 4));
            }

            tcaaDisp.display10 = *(tGS_DISPLAY1 *) & sweDb.disp[0].display; /* DISPLAY1 */
            tcaaDisp.dispfb10 = *(tGS_DISPFB1 *) & sweDb.disp[0].dispfb; /* DISPFB1 */

            tcaaDisp.display11 = *(tGS_DISPLAY1 *) & sweDb.disp[1].display; /* DISPLAY1 */
            tcaaDisp.dispfb11 = *(tGS_DISPFB1 *) & sweDb.disp[1].dispfb; /* DISPFB1 */

            sweDb.disp[0].dispfb.DBY = 1; /*  dispfb2 has DBY field of 1 */
            sweDb.disp[0].display.DH = sweDb.disp[0].display.DH - 1;
            sweDb.disp[0].display.DX = sweDb.disp[0].display.DX + 2; /*  display2 has magh/2 */
            sweDb.disp[0].pmode.ALP = 0x80;
            sweDb.disp[0].pmode.EN1 = 1;
            sweDb.disp[0].pmode.EN2 = 1;

            sweDb.disp[1].dispfb.DBY = 1; /*  dispfb2 has DBY field of 1 */
            sweDb.disp[1].display.DH = sweDb.disp[1].display.DH - 1;
            sweDb.disp[1].display.DX = sweDb.disp[1].display.DX + 2; /*  display2 has magh/2 */
            sweDb.disp[1].pmode.ALP = 0x80;
            sweDb.disp[1].pmode.EN1 = 1;
            sweDb.disp[1].pmode.EN2 = 1;
            /* Endif FSAA1 */

            SyncDCache(&sweDb,
                       SCESYNCDCACHEROUNDUP((RwUInt8 *) &sweDb+sizeof(sweDb)));
        }

#ifdef GSB
    /* We potentially need to collaps the frame buffers */
    if (((videoModes[currentMode].flags >> 31) & 0x1) == SINGLE_BUFFER)
    {
        int fbp;

        /* Choose the display that starts at 0 */
        if (sweDb.disp[0].dispfb.FBP == 0)
        {
            fbp = sweDb.disp[1].dispfb.FBP;
            sweDb.disp[1] = sweDb.disp[0];
        }
        else
        {
            fbp = sweDb.disp[0].dispfb.FBP;
            sweDb.disp[0] = sweDb.disp[1];
        }
        /* choose the frame buffer that begins at 0 and move z buffer */
        /* down to just above it */
        if (sweDb.draw01.frame1.FBP == 0)
        {
            sweDb.draw11 = sweDb.draw01;
            sweDb.draw12 = sweDb.draw02;
        }
        else
        {
            sweDb.draw01 = sweDb.draw11;
            sweDb.draw02 = sweDb.draw12;
        }
        sweDb.draw01.zbuf1.ZBP = sweDb.draw02.zbuf2.ZBP
            = sweDb.draw11.zbuf1.ZBP = sweDb.draw12.zbuf2.ZBP = fbp;

        SyncDCache(&sweDb,
                   SCESYNCDCACHEROUNDUP((RwUInt8 *) &sweDb + sizeof(sweDb)));
        skySingleBuffer = TRUE;
    }
    else
    {
        skySingleBuffer = FALSE;
    }
#endif /* GSB */

    /*
     * In PAL modes we have a single ???x256 display buffer, a single
     * ???x512 draw buffer and a single ???x512 Z buffer, so fix up.
     * In NTSC modes we have a single ???x224 display buffer, a single
     * ???x448 draw buffer and a single ???x448 Z buffer, so fix up
     */
    if (videoModes[currentMode].flags & rwVIDEOMODEFSAA0)
    {
        int vw64 = (skyFSAA0VisibleWidth + 63) & ~63;
        int vh64 = ((videoModes[currentMode].height >> 1) + 63) & ~63;

        /* sweDb.disp[0] always visible buffer */
        /* sweDb.disp[1] is never used */
        /* sweDb.draw0 is never used */
        /* sweDb.draw1 is used as the back buffer */
        /* zdepth=16 or 32, framebuff depth=16 or 32 */

        textureBankStart = 0;

        /* Half height * width shift right to accomidate page size */
        /* And half if 16bit color depth */
        textureBankStart =
            ((vh64 * vw64) >> (videoModes[currentMode].depth == 16));

        textureBankStart = (textureBankStart + 2047) & ~2047;

        sweDb.draw11.frame1.FBMSK = 0;
        sweDb.draw11.frame1.PSM = (videoModes[currentMode].depth == 16 ?
                                SCE_GS_PSMCT16S : SCE_GS_PSMCT32);
        sweDb.draw11.frame1.FBW = skyFSAA0RenderWidth >> 6;
        sweDb.draw11.frame1.FBP = textureBankStart >> 11;

        /* Back buffer: full height * full width shift right to accomidate */
        /* page size and half if 16bit color depth */
        textureBankStart +=
            ((skyFSAA0RenderHeight * skyFSAA0RenderWidth) >>
            (videoModes[currentMode].depth == 16));
        textureBankStart = (textureBankStart + 2047) & ~2047;

        sweDb.draw11.zbuf1.ZMSK = 0;
        sweDb.draw11.zbuf1.PSM = (zBufferDepth == 16 ?
                               SCE_GS_PSMZ16S : SCE_GS_PSMZ32);
        sweDb.draw11.zbuf1.ZBP = textureBankStart >> 11;

        /* ZBuffer: full height * full width shift right to accomidate */
        /* page size and half if 16bit color depth */
        textureBankStart +=
            ((skyFSAA0RenderHeight * skyFSAA0RenderWidth) >>
            (zBufferDepth == 16));

        /* first free page after the zbuffer */
        textureBankStart = (textureBankStart + 2047) & ~2047;

        /* ensure front and back are the same */
        sweDb.draw01.frame1 = sweDb.draw11.frame1;
        sweDb.draw01.zbuf1 = sweDb.draw11.zbuf1;

        /* copy context 1 to context 2 */
        sweDb.draw02.frame2 = sweDb.draw01.frame1;
        sweDb.draw02.zbuf2  = sweDb.draw01.zbuf1;
        sweDb.draw12.frame2 = sweDb.draw01.frame1;
        sweDb.draw12.zbuf2  = sweDb.draw01.zbuf1;

        sweDb.disp[1] = sweDb.disp[0];
    }
    else
        /* Set up the texture cache (we know that there is a second frame
         * buffer, because we are not in an interlaced mode).
         */
    {
        RwUInt32 fbWidth = sweDb.draw11.frame1.FBW * 64;
        RwUInt32 fbHeight = videoModes[currentMode].height;

#ifdef GSB
        /*  Looks wrong to me guv. */
        /* if ((skyGsmId-skyRenderSlot*skyNumGGSMSFrame)&2) */
        if (skyNumGGSMSFrame > 2)
        {
            fbHeight /= 2;
        }
#endif /* GSB */

        /* First find the end of the frame buffers */
#if defined(GSB) && defined(GSPLUS)
        /* not having the headers, we guess! */
        textureBankStart = (*(long *) &sweDb.draw01.ezbuf) & 0xfff;
        if (((*(long *) &sweDb.draw11.ezbuf) & 0xfff) > textureBankStart)
        {
            textureBankStart = (*(long *) &sweDb.draw11.ezbuf) & 0xfff;
        }
#else /* defined(GSB) && defined(GSPLUS) */
        textureBankStart = sweDb.draw01.zbuf1.ZBP;
        if (sweDb.draw11.zbuf1.ZBP > textureBankStart)
        {
            textureBankStart = sweDb.draw11.zbuf1.ZBP;
        }
#endif /* defined(GSB) && defined(GSPLUS) */

        textureBankStart *= 2048;
        if (sweDb.draw11.zbuf1.PSM > 1)
        {
            /* 16 bit Z buffer */
            fbHeight = (fbHeight + 63) & ~63;
            textureBankStart += ((fbWidth * fbHeight) / 2);
        }
        else
        {
            /* 24 or 32 bit Z buffer */
            fbHeight = (fbHeight + 31) & ~31;
            textureBankStart += (fbWidth * fbHeight);
        }

        /* Then align it to an 8k byte page boundary */
        textureBankStart = (textureBankStart + 2047) & ~2047;
    }

    /* Then start up the cache */
#if defined(GSB) && defined(GSPLUS)
    /* GSPlus is 32Mb */
    skyTexCacheOpenCache(textureBankStart,
                         (8 * 1024 * 1024) - textureBankStart);
#else /* defined(GSB) && defined(GSPLUS) */

    skyTexCacheOpenCache(textureBankStart,
                         (1 * 1024 * 1024) - textureBankStart);
#endif /* defined(GSB) && defined(GSPLUS) */

    /* say that it's too late to change our minds */
    cacheOpened = TRUE;

    skySuspended = FALSE;

    result = _sweOpen();

    if (!result)
    {
        skyTexCacheCloseCache();
    }
    else
    {
#if 0
        printf("sweDb: \n");
        printf("sweDb.disp[0].pmode       %lx\n", *(long *) &sweDb.disp[0].pmode);
        printf("sweDb.disp[0].smode2      %lx\n",
               *(long *) &sweDb.disp[0].smode2);
        printf("sweDb.disp[0].dispfb      %lx\n",
               *(long *) &sweDb.disp[0].dispfb);
        printf("sweDb.disp[0].display     %lx\n",
               *(long *) &sweDb.disp[0].display);
        printf("sweDb.disp[0].bgcolor     %lx\n",
               *(long *) &sweDb.disp[0].bgcolor);
        printf("sweDb.disp[1].pmode       %lx\n", *(long *) &sweDb.disp[1].pmode);
        printf("sweDb.disp[1].smode2      %lx\n",
               *(long *) &sweDb.disp[1].smode2);
        printf("sweDb.disp[1].dispfb      %lx\n",
               *(long *) &sweDb.disp[1].dispfb);
        printf("sweDb.disp[1].display     %lx\n",
               *(long *) &sweDb.disp[1].display);
        printf("sweDb.disp[1].bgcolor     %lx\n",
               *(long *) &sweDb.disp[1].bgcolor);
        printf("sweDb.giftag0             %lx\n", *(long *) &sweDb.giftag0);
        printf("sweDb.draw01.frame1       %lx\n", *(long *) &sweDb.draw01.frame1);
        printf("sweDb.draw01.zbuf1        %lx\n", *(long *) &sweDb.draw01.zbuf1);
        printf("sweDb.draw01.xyoffset1    %lx\n",
               *(long *) &sweDb.draw01.xyoffset1);
        printf("sweDb.draw01.scissor1     %lx\n",
               *(long *) &sweDb.draw01.scissor1);
        printf("sweDb.draw01.prmodecont   %lx\n",
               *(long *) &sweDb.draw01.prmodecont);
        printf("sweDb.draw01.colclamp     %lx\n",
               *(long *) &sweDb.draw01.colclamp);
        printf("sweDb.draw01.dthe         %lx\n", *(long *) &sweDb.draw01.dthe);
        printf("sweDb.draw01.test1        %lx\n", *(long *) &sweDb.draw01.test1);
        printf("sweDb.clear0.testa        %lx\n", *(long *) &sweDb.clear0.testa);
        printf("sweDb.clear0.prim         %lx\n", *(long *) &sweDb.clear0.prim);
        printf("sweDb.clear0.rgbaq        %lx\n", *(long *) &sweDb.clear0.rgbaq);
        printf("sweDb.clear0.xyz2a        %lx\n", *(long *) &sweDb.clear0.xyz2a);
        printf("sweDb.clear0.xyz2b        %lx\n", *(long *) &sweDb.clear0.xyz2b);
        printf("sweDb.clear0.testb        %lx\n", *(long *) &sweDb.clear0.testb);
        printf("sweDb.giftag1             %lx\n", *(long *) &sweDb.giftag1);
        printf("sweDb.draw11.frame1       %lx\n", *(long *) &sweDb.draw11.frame1);
        printf("sweDb.draw11.zbuf1        %lx\n", *(long *) &sweDb.draw11.zbuf1);
        printf("sweDb.draw11.xyoffset1    %lx\n",
               *(long *) &sweDb.draw11.xyoffset1);
        printf("sweDb.draw11.scissor1     %lx\n",
               *(long *) &sweDb.draw11.scissor1);
        printf("sweDb.draw11.prmodecont   %lx\n",
               *(long *) &sweDb.draw11.prmodecont);
        printf("sweDb.draw11.colclamp     %lx\n",
               *(long *) &sweDb.draw11.colclamp);
        printf("sweDb.draw11.dthe         %lx\n", *(long *) &sweDb.draw11.dthe);
        printf("sweDb.draw11.test1        %lx\n", *(long *) &sweDb.draw11.test1);
        printf("sweDb.clear1.testa        %lx\n", *(long *) &sweDb.clear1.testa);
        printf("sweDb.clear1.prim         %lx\n", *(long *) &sweDb.clear1.prim);
        printf("sweDb.clear1.rgbaq        %lx\n", *(long *) &sweDb.clear1.rgbaq);
        printf("sweDb.clear1.xyz2a        %lx\n", *(long *) &sweDb.clear1.xyz2a);
        printf("sweDb.clear1.xyz2b        %lx\n", *(long *) &sweDb.clear1.xyz2b);
        printf("sweDb.clear1.testb        %lx\n", *(long *) &sweDb.clear1.testb);
#endif

#ifdef GSB
        /* correct the xoffset register */
        sweDb.draw01.xyoffset1.OFX =
            (2048 - (videoModes[currentMode].width >> 1)) << 4;
        sweDb.draw01.xyoffset1.OFY =
            (2048 - (videoModes[currentMode].height >> 1)) << 4;
#endif /* GSB */
        *(long *) &sweDb.draw11.xyoffset1 = *(long *) &sweDb.draw01.xyoffset1;
        /* Hack in alpha test */
        *(long *) &sweDb.draw01.test1 |= 0x140b;
        *(long *) &sweDb.draw11.test1 |= 0x140b;

        /* Setup context 2 */
        if (videoModes[currentMode].flags & rwVIDEOMODEFSAA0)
        {
            *(long *) &sweDb.draw02.xyoffset2 = *(long *) &sweDb.draw01.xyoffset1;
            *(long *) &sweDb.draw12.xyoffset2 = *(long *) &sweDb.draw01.xyoffset1;

            /* Hack in alpha test */
            *(long *) &sweDb.draw02.test2 |= 0x140b;
            *(long *) &sweDb.draw12.test2 |= 0x140b;
        }

#ifdef VISDRAW
        /* Swap display regs */
        {
#if defined(GSB) && defined(GSPLUS)
            sceGsPlusDispEnv    env;
#else /* defined(GSB) && defined(GSPLUS) */
            sceGsDispEnv        env;
#endif /* defined(GSB) && defined(GSPLUS) */

            env = sweDb.disp[0];
            sweDb.disp[0] = sweDb.disp[1];
            sweDb.disp[1] = env;
        }
#endif /* VISDRAW */

        SyncDCache(&sweDb,
                   SCESYNCDCACHEROUNDUP((RwUInt8 *) &sweDb + sizeof(sweDb)));

        skyFrameBit = 0;
#if defined(GSB) && defined(GSPLUS)
        sceGsPlusSwapDBuffDc(&sweDb, skyFrameBit++);
#else /* defined(GSB) && defined(GSPLUS) */
        sceGsSwapDBuffDc(&sweDb, skyFrameBit++);
#endif /* defined(GSB) && defined(GSPLUS) */
        /* DMA up a set of defaults */
        /* Sample uses VIF1 to do the transfer */
#if 0
        skyDMA_enable();
        skyDMA_ch1_source_chain(&gs_set_up);
#else
        sceDmaReset(1);
        sceDmaSend(sceDmaGetChan(SCE_DMA_VIF1), &gs_set_up);
#endif

        skyFrame_1 = *(long *) &sweDb.draw01.frame1;
        skyZbuf_1 = *(long *) &sweDb.draw01.zbuf1;
#if defined(GSB) && defined(GSPLUS)
        skyFrame_2 = *(long *) &sweDb.draw01.eframe;
        skyZbuf_2 = *(long *) &sweDb.draw01.ezbuf;
#endif /* defined(GSB) && defined(GSPLUS) */
        skyTest_1 = *(long *) &sweDb.draw01.test1;
        skyXyoffset_1 = *(long *) &sweDb.draw01.xyoffset1 & ~(1l << 35);

        skyXOff = (skyXyoffset_1 >> 4) & 0xfff;
        skyYOff = (skyXyoffset_1 >> 36) & 0xfff;

        /* Setup Linear Fog Settings */
        useFarClip = TRUE;

        /* Set up initial values and send to the GS */
        setInitialRenderState();

        if (videoModes[currentMode].flags & rwVIDEOMODEFFINTERLACE)
        {
            sweUseHalfOffset = TRUE;
        }
        else
        {
            sweUseHalfOffset = FALSE;
        }

        skyVideoMode = &(videoModes[currentMode]);

        engineStarted = TRUE;

#ifdef PROFILE
        sweResetCount();
#endif /* PROFILE */
#ifdef PG
        _sweProfileReset();
#endif /* PG */

        /* Add current fog table to dispatch list */
        _sweAddPkt(fogTable, SWE_PKT_DMA_MODE_CHAIN_TTE | SWE_PKT_VU1);

#ifdef GSB
#if 0
        printf("%s(%d):%s - gsbDrawDone(GSB_DRAWDONE_CLEAR)\n",
               __FILE__, __LINE__, __FUNCTION__);
        gsbDrawDone(GSB_DRAWDONE_CLEAR);

        printf("%s(%d):%s - gsbSetSysReady(GSB_READY_SET)\n",
               __FILE__, __LINE__, __FUNCTION__);
        gsbSetSysReady(GSB_READY_SET);
#else
        /* I'm going to merely flag this as required */
        skyGsmSysReadyRequired = 10;

#endif
#endif /* GSB */
    }

    RWRETURN(result);
}

/****************************************************************************
 SkySystem

 On entry   : Request
            : Data out
            : Data in/out
            : Number in
 On exit    : TRUE on success
 */
static RwBool
SkySystem(RwInt32 nRequest,  void *pOut,  void *pInOut,  RwInt32 nIn)
{
    RwBool result = FALSE;
    RWFUNCTION(RWSTRING("SkySystem"));

    switch (nRequest)
    {
        /******************** Mode controls ***********************/
        case rwDEVICESYSTEMUSEMODE:
            result = SkyUseMode(nIn);
            break;

        case rwDEVICESYSTEMGETNUMMODES:
            result = SkyGetNumModes(pOut);
            break;

        case rwDEVICESYSTEMGETMODEINFO:
            result = SkyGetModeinfo(pOut, nIn);
            break;

        case rwDEVICESYSTEMGETMODE:
            result = SkyGetMode(pOut);
            break;

        case rwDEVICESYSTEMFOCUS:
            result = SkyFocus();
            break;

        case rwDEVICESYSTEMREGISTER:
            result = SkyRegister(pOut, pInOut);
            break;

        case rwDEVICESYSTEMOPEN:
            result = SkyOpen();
            break;

        case rwDEVICESYSTEMCLOSE:
            result = SkyClose();
            break;

        case rwDEVICESYSTEMSTART:
            result = SkyStart();
            break;

        case rwDEVICESYSTEMSTOP:
            result = SkyStop();
            break;

        case rwDEVICESYSTEMFINALIZESTART:
            result = SkyFinalizeStart();
            break;

        case rwDEVICESYSTEMINITIATESTOP:
            result = SkyInitiateStop();
            break;

        case rwDEVICESYSTEMGETMAXTEXTURESIZE:
            result = SkyGetMaxTextureSize(pOut);
            break;

        case rwDEVICESYSTEMGETTEXMEMSIZE:
            result = SkyGetTexMemSize(pOut);
            break;

        case rwDEVICESYSTEMINITPIPELINE:
            result = SkyInitPipeline();
            break;

        case rwDEVICESYSTEMSTANDARDS:
            result = SkyStandards(pOut, nIn);
            break;


#ifdef RWMETRICS
            /***************************** metrics **************************/
        case rwDEVICESYSTEMGETMETRICBLOCK:
            *(RwSkyMetrics **)pOut = _sweMetricsGet();
            result = TRUE;
            break;
#endif /* RWMETRICS */

            /*************************** not supported **********************/

    }

    RWRETURN(result);
}

/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

                     Getting the device structure

 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */

/****************************************************************************
 _rwDeviceGetHandle

 On entry   :
 On exit    : device block handle
 */
RwDevice *
_rwDeviceGetHandle(void)
{
    static RwDevice dGSkyDevice=
    {
        (RwReal)(1),                  /* Default gamma correction */

        SkySystem,

        (RwReal)(65535.0f), (RwReal)(0.0f), /* Z buffer limits - what does it matter? */

        _rwSkySetRenderState,            /* Setting the rendering state */
        SkyGetRenderState,            /* Getting the rendering state */

        /* These get set up when the immediate mode module is set up */
        (RwIm2DRenderLineFunction)NULL,
        (RwIm2DRenderTriangleFunction)NULL,
        (RwIm2DRenderPrimitiveFunction)NULL,
        (RwIm2DRenderIndexedPrimitiveFunction)NULL,

        /* These get set up when the immediate mode module is set up */
        (RwIm3DRenderLineFunction)NULL,
        (RwIm3DRenderTriangleFunction)NULL,
        (RwIm3DRenderPrimitiveFunction)NULL,
        (RwIm3DRenderIndexedPrimitiveFunction)NULL
    };    /* NULL system */

    RWFUNCTION(RWSTRING("_rwDeviceGetHandle"));

    RWRETURN(&dGSkyDevice);
}

/**
 * \ingroup sky2unsupported
 * \ref _rwSkyFSAAMode0SetVisibleWidth
 *  is used to specify the width of the
 * front (visible) buffer in the full-screen antialiasing (mode 0) video modes.
 * In these modes the application is rendering to a non-visible back buffer.
 * This back buffer is resampled during V-blank to the visible buffer.  By
 * default, the visible width is half of the non-visible buffer.  Use this
 * API call if you require a different width.  In all cases the height of the
 * visible buffer will be 224 (NTSC) or 256 (PAL).  The FSAA0 video modes are
 * intended for 60Hz use only.
 *
 * \param width Width of the visible screen.  One of 256,320,384,512,640.
 * \return Returns the TRUE on success, FALSE if the function fails because the
 * width is not supported or the engine has been started.
 *
 * \warning Note that this function must be called before the engine is started.
 */
RwBool
_rwSkyFSAAMode0SetVisibleWidth(RwUInt32 width)
{
    RwBool result =
        ( (!engineStarted) &&
          ( ( 256 == width ) ||
            ( 320 == width ) ||
            ( 384 == width ) ||
            ( 512 == width ) ||
            ( 640 == width ) ) );

    RWFUNCTION(RWSTRING("_rwSkyFSAAMode0SetVisibleWidth"));

    if (result)
    {
#if 0
        printf("Set width to %d\n", width);
#endif
        skyFSAA0VisibleWidth = width;
    }

    RWRETURN(result);
}

#ifdef GSB

/* #### Needs to be fixed to cope the Z merge #### */
/****************************************************************************
 * _rpSkySetGGSMs

 On entry   : Total number of gGSMs
 : Number of gGSMs per framebuffer
 : Number of frames merged (ie frame*zm total in use per frame)
 On exit    : TRUE on success
*/
RwBool
_rpSkySetGGSMs(RwInt32 total, RwInt32 frame, RwInt32 zm)
{
    RWFUNCTION(RWSTRING("_rpSkySetGGSMs"));
    if (!engineStarted)
    {
        /* At present we can only cope with 1,2,4 per framebuffer */
        if ((frame != 1) && (frame != 2) && (frame != 4))
        {
            RWRETURN(FALSE);
        }
        /* Number if gGSMs must be divisible by framebuffer * zm */
        if ((total%(frame*zm)) != 0)
        {
            RWRETURN(FALSE);
        }
        skyNumGGSMS = total;
        skyNumGGSMSFrame = frame;
        skyNumZMergFrame = zm;
        RWRETURN(TRUE);
    }
    RWRETURN(FALSE);
}

/****************************************************************************
 _rpSkySetInput

 On entry   : Should we fetch input
 On exit    :
 */
void
_rpSkySetInput(RwBool state)
{
    RWFUNCTION(RWSTRING("_rpSkySetInput"));

    skyGSMReadInput = state;

    RWRETURNVOID();
}
#endif /* GSB */

/* **************************************************************************
 * \ref RpSkySuspend drains the dma chain, then disables
 * our interrupts and unhooks all our interrupt handlers.
 * This enables external code, such as an MPEG cut
 * scene player to meet the Techinal Note requirement that VBLANK_S is
 * not hooked if sceGsSyncV(0) is used.
 *
 * \return  Success/failure
 */
RwBool
RpSkySuspend(void)
{
    RWAPIFUNCTION(RWSTRING("RpSkySuspend"));

    if (skySuspended)
    {
        RWRETURN(FALSE);
    }

    _sweUnhook();

    skySuspended = TRUE;

    RWRETURN(TRUE);
}

/* **************************************************************************
 * \ref RpSkyResume  restores any trammpled video mode.
 * Reattaches our interrupt handlers and enable interrupts that RW requires.
 * Reestablishes some renderstate.
 * This function should not be called with a VBLANK_S handler installed.
 *
 * \return  Success/failure
 */
RwBool
RpSkyResume(void)
{
    RWAPIFUNCTION(RWSTRING("RpSkyResume"));

    if (!skySuspended)
    {
        RWRETURN(FALSE);
    }

    sceGsSyncV(0);

    /* Really I should have a flag, but I'll just go by the height */
    if ((videoModes[currentMode].height == 256)
        || (videoModes[currentMode].height == 512))
    {
        /* PAL mode */
        if (videoModes[currentMode].flags & rwVIDEOMODEFFINTERLACE)
        {
            sceGsResetGraph(0, SCE_GS_INTERLACE, SCE_GS_PAL, SCE_GS_FRAME);
        }
        else if (videoModes[currentMode].flags & rwVIDEOMODEFSAA0)
        {
            sceGsResetGraph(0, SCE_GS_INTERLACE, SCE_GS_PAL, SCE_GS_FRAME);
        }
        else if (videoModes[currentMode].flags & rwVIDEOMODEINTERLACE)
        {
            sceGsResetGraph(0, SCE_GS_INTERLACE, SCE_GS_PAL, SCE_GS_FIELD);
        }
        else
        {
            sceGsResetGraph(0, SCE_GS_NOINTERLACE, SCE_GS_PAL, SCE_GS_FRAME);
        }
    }
    else
    {
        /* NTSC is the default */
        if (videoModes[currentMode].flags & rwVIDEOMODEFFINTERLACE)
        {
            sceGsResetGraph(0, SCE_GS_INTERLACE, SCE_GS_NTSC, SCE_GS_FRAME);
        }
        else if (videoModes[currentMode].flags & rwVIDEOMODEFSAA0)
        {
            sceGsResetGraph(0, SCE_GS_INTERLACE, SCE_GS_NTSC, SCE_GS_FRAME);
        }
        else if (videoModes[currentMode].flags & rwVIDEOMODEINTERLACE)
        {
            sceGsResetGraph(0, SCE_GS_INTERLACE, SCE_GS_NTSC, SCE_GS_FIELD);
        }
        else
        {
            sceGsResetGraph(0, SCE_GS_NOINTERLACE, SCE_GS_NTSC, SCE_GS_FRAME);
        }
    }
    sceGsSwapDBuffDc(&sweDb, skyFrameBit++);

    sceDmaSendN(sceDmaGetChan(2), &skyInitialClear,
                sizeof(skyInitialClear) >> 4);
    skyTexCacheFlush();
    setInitialRenderState();
    skySuspended = !_sweHook();
    RWRETURN(skySuspended);
}

/* **************************************************************************
 * \ref RpSkyFrameBufferRastersUpdate  
 * 
 *
 * \return  Display Raster
 */
void        
_rpSkyFrameBufferRastersUpdate(void)
{
    _SkyMemBlock *pDisplayBuffer;
    _SkyMemBlock *pDrawBuffer;
    RWFUNCTION(RWSTRING("_rpSkyFrameBufferRastersUpdate"));
    
    if (skyFrameBit & 0x1)
    {
        pDisplayBuffer  = &skyFakeCacheFrameBuffer1;
        pDrawBuffer = &skyFakeCacheFrameBuffer0;
    }
    else
    {
        pDisplayBuffer  = &skyFakeCacheFrameBuffer0;
        pDrawBuffer = &skyFakeCacheFrameBuffer1;
    }

    pDisplayBuffer->raster  = skyDisplayRaster;
    if (skyDisplayRaster)
    {
        RASTEREXTFROMRASTER(skyDisplayRaster)->mpCacheEntry = pDisplayBuffer;
    }

    pDrawBuffer->raster = skyDrawRaster;
    if (skyDrawRaster)
    {
        RASTEREXTFROMRASTER(skyDrawRaster)->mpCacheEntry    = pDrawBuffer;
    }

    RWRETURNVOID();
}

/* **************************************************************************
 * \ref _rpSkyDestroyFrameBufferRaster  Destroys a single FrameBuffer Raster
 * 
 *
 * \return  Display Raster
 */
static void
SkyDestroyFBRaster(RwRaster *rpRaster)
{
    RWFUNCTION(RWSTRING("SkyDestroyFBRaster"));

    if (rpRaster)
    {
        RwRasterDestroy(rpRaster);
        rpRaster = (RwRaster *)NULL;
    }

    RWRETURNVOID();
}

/* **************************************************************************
 * \ref RpSkyDestroyFrameBufferRasters  Destroys up FrameBuffer Rasters
 * 
 *
 * \return  Display Raster
 */
RwBool      
_rpSkyDestroyFrameBufferRasters(void)
{
    RwBool result;
    
    RWFUNCTION(RWSTRING("_rpSkyDestroyFrameBufferRasters"));

    result = engineStarted;
    
    if (result)
    {

        /* Destroy Camera Psuedo Textures */
        
        if (skyDrawRaster)
        {
            SkyDestroyFBRaster(skyDrawRaster);
            skyDrawRaster = (RwRaster *)NULL;
        }
        

        if (skyDisplayRaster)
        {
            SkyDestroyFBRaster(skyDisplayRaster);
            skyDisplayRaster = (RwRaster *)NULL;
        }
        
    }

    RWRETURN(result);
}

/* **************************************************************************
 * \ref _rpSkyCreateFrameBufferRaster  Creates a single FrameBuffer Raster
 * 
 *
 * \return  Display Raster
 */
static RwRaster *       
SkyCreateFBRaster(void)
{
    RwRaster           *rpRaster;
    RwInt32     nAdjust;
    RWFUNCTION(RWSTRING("SkyCreateFBRaster"));

    /* Start with a framebuffer camera */
    rpRaster = RwRasterCreate(videoModes[currentMode].width, 
                              videoModes[currentMode].height, 
                              0, rwRASTERTYPECAMERA);

    /* Force convert to a cameratexture so we can consider it for rendering */
    rpRaster->cType = rwRASTERTYPECAMERATEXTURE | rwRASTERDONTALLOCATE;

    /* Increase the size of the raster to a power of 2
     * - we know that the frame buffer starts on valid bounds*/
    nAdjust = rpRaster->width & ((1<<_rwSkyFindMSB(rpRaster->width))-1);
    if (nAdjust)
    {
        rpRaster->width = (rpRaster->width-nAdjust)<<1;
    }

    nAdjust = rpRaster->height & ((1<<_rwSkyFindMSB(rpRaster->height))-1);
    if (nAdjust)
    {
        rpRaster->height = (rpRaster->height-nAdjust)<<1;
    }

    /* Setup a valid set of tex registers for this new texture ! */
    {
        _SkyRasterExt *rasExt   = RASTEREXTFROMRASTER(rpRaster);

#if (0)
        RwInt32 nFrameBuffer = (*(long*)&sweDb.draw01.frame1)&0x1ff;
#endif /* (0) */

        /* Tex0 - TBP0  */
        rasExt->lsb = 0;
        /* Tex0 - TBW */
        rasExt->lsb |=  ((skyFrame_1>>16)&0x3f)<<14;
        /* Tex0 - PSM */
        rasExt->lsb |= ((skyFrame_1>>24)&0x3f)<<20;
        /* Tex0 - TW */
        rasExt->lsb |= (_rwSkyFindMSB(rpRaster->width) & 0xf) << 26;
        /* Tex0 - TCC */
        rasExt->msb = 0l << (34-32);    /* Use Alpha from TexA register */
        /* Tex0 - TH */
        rasExt->lsb |= (_rwSkyFindMSB(rpRaster->height) & 0xf) << 30;
        rasExt->msb |= (_rwSkyFindMSB(rpRaster->height) & 0xf) >> 2;
        /* Tex0 - TFX */
        rasExt->msb |= 0l << (35-32);   /* MODULATE */

        rasExt->bLocked = TRUE;
        rasExt->maxMipLevel = 0;
    }

    RWRETURN(rpRaster);
}

/* **************************************************************************
 * \ref RpSkyCreateFrameBufferRasters  Creates up FrameBuffer Rasters
 * 
 *
 * \return  Display Raster
 */
RwBool      
_rpSkyCreateFrameBufferRasters(void)
{
    RWFUNCTION(RWSTRING("_rpSkyCreateFrameBufferRasters"));

    if (!engineStarted)
    {
        RWRETURN(FALSE);
    }

    /* Create fake permenant cache entries which point at the frame buffers */
    /* Frame Buffers are 2048 aligned, Tex Cache is 1 word aligned */
    skyFakeCacheFrameBuffer0.address = 
        ((*(long*)&sweDb.draw01.frame1)&0x1ff) << 11;
    skyFakeCacheFrameBuffer1.address = 
        ((*(long*)&sweDb.draw11.frame1)&0x1ff) << 11;

    /* Create Camera Psuedo Textures */
    skyDisplayRaster = SkyCreateFBRaster();
    skyDrawRaster    = SkyCreateFBRaster();

    /* Set up the initial state of cache entries, 
     * we swap this with the frame buffers */
    _rpSkyFrameBufferRastersUpdate();

    RWRETURN(TRUE);
}

/* **************************************************************************
 * \ref RpSkyGetDisplayBufferRaster  Retrieves the Display Buffer as a Texture Raster
 * 
 *
 * \return  Display Raster
 */
RwRaster *  
RpSkyGetDisplayBufferRaster(void)
{
    RWAPIFUNCTION(RWSTRING("RpSkyGetDisplayBufferRaster"));
    RWRETURN(skyDisplayRaster);
}

/* **************************************************************************
 * \ref RpSkyGetDrawBufferRaster
 * Retrieves the Draw Buffer as a Texture Raster
 *
 * \return  Draw Raster
 */
RwRaster *  
RpSkyGetDrawBufferRaster(void)
{
    RWAPIFUNCTION(RWSTRING("RpSkyGetDrawBufferRaster"));

    RWRETURN(skyDrawRaster);
}

