/***************************************************************************
 *                                                                         *
 * Module  : skyblit.c                                                     *
 *                                                                         *
 * Purpose : Blit module for PSX 2                                         *
 *                                                                         *
 **************************************************************************/

/***************************************************************************
 Includes
 */

#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>
/* end Sony includes */

#include "batypes.h"
#include "badevice.h"
#include "balibtyp.h"
#include "baraster.h"
#include "baimage.h"
#include "barwtyp.h"

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

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

#include "devprofile.h"

#include "basky.h"

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

static const char rcsid[] __RWUNUSED__ =
    "@@(#)$Id: skyblit.c,v 1.21 2001/08/08 14:13:39 Rabin Exp $";

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

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

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

#define BITSTOBYTES(bits) (((bits)+7)>>3)

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

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

static RwRaster *skyCurRas;
static RwRect   destClipRect;

static RwRaster	*blitRaster[2];
static RwUInt32 rasterNum;

static RwUInt64 oldSkyTex1_1; /* Cached value while we do the blit */

/****************************************************************************
 _rwSkyRasterFinaliseStart

 On entry   : None
 On exit    : TRUE on success
 */
RwBool
_rwSkyRasterFinaliseStart(void)
{
    RWFUNCTION(RWSTRING("_rwSkyRasterFinaliseStart"));

    /* No raster context to start with */
    skyCurRas = (RwRaster *)NULL;

    /* Allocate two rasters for double buffering blit stripes
     * (We will make these sub rasters for texture upload, since we know how the texture cache works)
     */
    blitRaster[0] = RwRasterCreate(0, 0, 0, rwRASTERDONTALLOCATE | rwRASTERTYPENORMAL);
    if (!blitRaster[0])
    {
        RWRETURN(FALSE);
    }
    blitRaster[1] = RwRasterCreate(0, 0, 0, rwRASTERDONTALLOCATE | rwRASTERTYPENORMAL);
    if (!blitRaster[1])
    {
        RwRasterDestroy(blitRaster[0]);
        blitRaster[0] = (RwRaster *)NULL;
        RWRETURN(FALSE);
    }

    /* Start with double buffer 0 */
    rasterNum = 0;

    RWRETURN(TRUE);
}

/****************************************************************************
 _rwSkyRasterInitiateStop

 On entry   : None
 On exit    : TRUE on success
 */
void
_rwSkyRasterInitiateStop(void)
{
    RWFUNCTION(RWSTRING("_rwSkyRasterInitiateStop"));

    if (blitRaster[0])
    {
        RwRasterDestroy(blitRaster[0]);
        blitRaster[0] = (RwRaster *)NULL;
    }

    if (blitRaster[1])
    {
        RwRasterDestroy(blitRaster[1]);
        blitRaster[1] = (RwRaster *)NULL;
    }

    RWRETURNVOID();
}

/****************************************************************************
 spriteSetup

 On entry   : Raster type to setup for
 On exit    : None
 */
static void
spriteSetup(RwRasterType type)
{
    RwUInt64 tmp, tmp1;
    u_long128 ltmp = 0;

    RWFUNCTION(RWSTRING("spriteSetup"));

    if (skyFrameBit & 0x1)
    {
        skyFrame_1 = *(RwUInt64 *)&sweDb.draw01.frame1;
#if defined(GSB) && defined(GSPLUS)
        skyFrame_2 = *(RwUInt64 *)&sweDb.draw01.eframe;
#endif /* defined(GSB) && defined(GSPLUS) */
    }
    else
    {
        skyFrame_1 = *(RwUInt64 *)&sweDb.draw11.frame1;
#if defined(GSB) && defined(GSPLUS)
        skyFrame_2 = *(RwUInt64 *)&sweDb.draw11.eframe;
#endif /* defined(GSB) && defined(GSPLUS) */
    }

    sweOpenLocalPkt(SWE_LPS_CONT, 0);

    /* We know this will be followed by at least one sprite plus
     * the unsetup stuff, so no need ever for EOP.
     */
    tmp = /* NLOOP */ 5l
         | /* EOP */ (0l<<15)
         | /* PRE */ (0l<<46)
         | /* FLG */ (0l<<58)
         | /* NREG */(1l<<60);
    tmp1 = /* A+D */ (0xel<<(64-64));
    MAKE128(ltmp, tmp1, tmp);
    SWEADDCONTGIFFAST(ltmp, 11);

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

        /* Don't touch the Zbuffer */
        tmp = skyZbuf_1;
        tmp |= (1l<<32);
        MAKE128(ltmp, GS_ZBUF_1, tmp);
        SWEADDCONTFAST(ltmp);

        /* Force writes */
        tmp = skyTest_1;
        tmp &= ~0x54001l;
        /* Rely on Z mask bit to prevent writes due to GS bug */
        tmp |=  0x30000l;
        MAKE128(ltmp, GS_TEST_1, tmp);
        SWEADDCONTFAST(ltmp);
    }
    else
    {
        /* write to Zbuffer */
        tmp = skyZbuf_1;
        tmp &= ~(1l<<32);
        MAKE128(ltmp, GS_ZBUF_1, tmp);
        SWEADDCONTFAST(ltmp);

        /* Don't touch the Frame buffer */
        tmp = skyFrame_1;
        tmp |= (0xffffffffl<<32);
        MAKE128(ltmp, GS_FRAME_1, tmp);
        SWEADDCONTFAST(ltmp);

        /* Force writes */
        tmp = skyTest_1;
        tmp &= ~0x44001l;
        tmp |=  0x30000l;
        MAKE128(ltmp, GS_TEST_1, tmp);
        SWEADDCONTFAST(ltmp);
    }

    /* Setup alpha blend to be src alpha/inv src alpha */
    MAKE128(ltmp, GS_ALPHA_1, 0x44);
    SWEADDCONTFAST(ltmp);

    /* Setup filtering to be bilinear (gets uploaded when the texture gets selected) */
    oldSkyTex1_1 = skyTex1_1; /* Cache old value so we can put it back */
    skyTex1_1 &= ~0x1e0l;
    skyTex1_1 |= 0x60;

    /* We can't rely on the xy offset currently set */
    tmp = (skyCurRas->nOffsetX & 0xfffl)<<4
          | (skyCurRas->nOffsetY & 0xfffl)<<36
          | (SWE_HALF_OFFSET()<<35);
    MAKE128(ltmp, GS_XYOFFSET_1, tmp);
    SWEADDCONTFAST(ltmp);

    RWRETURNVOID();
}

/****************************************************************************
 spriteRGBAZ

 On entry   : Coords of dst corners
            : RGBA and Z
            : Whether to blend src alpha/inv src alpha
 On exit    : None
 */
static void
spriteRGBAZ(RwInt32 tlX, RwInt32 tlY, RwInt32 brX, RwInt32 brY,
            RwUInt32 r, RwUInt32 g, RwUInt32 b, RwUInt32 a, RwUInt32 z,
            RwBool alphaBlend)
{
    RwUInt64 tmp, tmp1;
    u_long128 ltmp = 0;

    RWFUNCTION(RWSTRING("spriteRGBAZ"));

    sweOpenLocalPkt(SWE_LPS_CONT, 0);

    /* We know this will be followed by an unsetup packet, so no
     * need ever for EOP bit.
     */
    tmp = /* NLOOP */ 4l
         | /* EOP */ (0l<<15)
         | /* PRE */ (0l<<46)
         | /* FLG */ (0l<<58)
         | /* NREG */(1l<<60);
    tmp1 = /* A+D */ (0xel<<(64-64));
    MAKE128(ltmp, tmp1, tmp);
    SWEADDCONTGIFFAST(ltmp, 11);

    /* now draw a sprite */
    if (alphaBlend)
    {
        MAKE128(ltmp, GS_PRIM, 0x46);
    }
    else
    {
        MAKE128(ltmp, GS_PRIM, 0x6);
    }
    SWEADDCONTFAST(ltmp);

    tmp = ((RwUInt64)z << 32) | (tlY << 20) | (tlX << 4);
    MAKE128(ltmp, GS_XYZ2, tmp);
    SWEADDCONTFAST(ltmp);

    tmp = (a << 24) | (b << 16) | (g << 8) | (r);
    MAKE128(ltmp, GS_RGBAQ, tmp);
    SWEADDCONTFAST(ltmp);

    tmp = ((RwUInt64)z << 32) | (brY << 20) | (brX << 4);
    MAKE128(ltmp, GS_XYZ2, tmp);
    SWEADDCONTFAST(ltmp);

    RWRETURNVOID();
}

/****************************************************************************
 spriteUV

 On entry   : Coords of dst corners
            : Srce corner tex coords
            : Whether to blend src alpha/inv src alpha
 On exit    : None
 */
static void
spriteUV(RwInt32 tlX, RwInt32 tlY, RwInt32 brX, RwInt32 brY,
         RwInt32 tlU, RwInt32 tlV, RwInt32 brU, RwInt32 brV,
         RwBool alphaBlend)
{
    RwUInt64 tmp, tmp1;
    u_long128 ltmp = 0;

    RWFUNCTION(RWSTRING("spriteUV"));

    sweOpenLocalPkt(SWE_LPS_CONT, 0);

    /* We know this will be followed by an unsetup packet, so no
     * need ever for EOP bit.
     */
    tmp = /* NLOOP */ 6l
         | /* EOP */ (0l<<15)
         | /* PRE */ (0l<<46)
         | /* FLG */ (0l<<58)
         | /* NREG */(1l<<60);
    tmp1 = /* A+D */ (0xel<<(64-64));
    MAKE128(ltmp, tmp1, tmp);
    SWEADDCONTGIFFAST(ltmp, 11);

    /* Draw a sprite */
    if (alphaBlend)
    {
        MAKE128(ltmp, GS_PRIM, 0x156);
    }
    else
    {
        MAKE128(ltmp, GS_PRIM, 0x116);
    }
    SWEADDCONTFAST(ltmp);

    tmp = ((tlV << 16) | (tlU << 0));
    MAKE128(ltmp, GS_UV, tmp);
    SWEADDCONTFAST(ltmp);

    tmp = (tlY << 20) | (tlX << 4);
    MAKE128(ltmp, GS_XYZ2, tmp);
    SWEADDCONTFAST(ltmp);

    tmp = (0x80FFFFFFl);
    MAKE128(ltmp, GS_RGBAQ, tmp);
    SWEADDCONTFAST(ltmp);

    tmp = ((brV << 16) | (brU << 0));
    MAKE128(ltmp, GS_UV, tmp);
    SWEADDCONTFAST(ltmp);

    tmp = (brY << 20) | (brX <<4);
    MAKE128(ltmp, GS_XYZ2, tmp);
    SWEADDCONTFAST(ltmp);

    RWRETURNVOID();
}

/****************************************************************************
 spriteUnsetup

 On entry   : None
 On exit    : None
 */
static void
spriteUnsetup(void)
{
    RwUInt64 tmp, tmp1;
    u_long128 ltmp = 0;

    RWFUNCTION(RWSTRING("spriteUnsetup"));

    sweOpenLocalPkt(SWE_LPS_CONT, 0);

    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(ltmp, tmp1, tmp);
    SWEADDCONTGIFFAST(ltmp, 11);

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

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

    /* Restore alpha blending to be what it should be */
    MAKE128(ltmp, GS_ALPHA_1, skyAlpha_1);
    SWEADDCONTFAST(ltmp);

    /* Put filtering back the way it was (Since we are currently outside of
     * a CameraBeginUpdate cliche, we will assume we can step on the
     * current texture state... :-)
     */
    skyTex1_1 &= ~0x1e0l;
    skyTex1_1 |= (oldSkyTex1_1 & 0x1e0l);

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

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

    skyTextureRaster = (RwRaster *)NULL;

    RWRETURNVOID();
}

/****************************************************************************
 _rwSkyRasterClearRect

 On entry   : NULL
            : RwRect defining area to clear
            : Pixel value defining what to clear the raster to
 On exit    : TRUE on success
 */
RwBool
_rwSkyRasterClearRect(void * __RWUNUSED__ pOut, void *pRect, RwInt32 nValue)
{
    RwRect *rect = (RwRect*)pRect;
    RWFUNCTION(RWSTRING("_rwSkyRasterClearRect"));
    PFENTRY(PF_rwSkyRasterClearRect);
    RWASSERT(rect);

    switch (skyCurRas->cType)
    {
        case (rwRASTERTYPECAMERA):
        case (rwRASTERTYPEZBUFFER):
        {
            spriteSetup((RwRasterType)(skyCurRas->cType));
            if (((skyCurRas->cFormat << 8) & rwRASTERFORMATPIXELFORMATMASK)
                == rwRASTERFORMAT1555)
            {
                spriteRGBAZ(rect->x, rect->y, rect->x+rect->w, rect->y+rect->h,
                            (nValue & 0x001f) << 3, (nValue & 0x03e0) >> 2,
                            (nValue & 0x7c00) >> 7, (nValue & 0x8000) >> 8,
                            nValue, FALSE);
            }
            else
            {
                spriteRGBAZ(rect->x, rect->y, rect->x+rect->w, rect->y+rect->h,
                            (nValue & 0xff) , (nValue & 0xff00) >> 8,
                            (nValue & 0xff0000) >> 16,
                            (nValue & 0xff000000) >> 24,
                            nValue, FALSE);
            }
            spriteUnsetup();

            break;
        }
        case (rwRASTERTYPENORMAL):
        case (rwRASTERTYPETEXTURE):
        {
            RwUInt32 fill;
            RwInt32  h;
            RwInt32  w;

            if (!skyCurRas->cpPixels)
            {
                /* If can't see pixels, can't clear 'em */
                PFEXIT(PF_rwSkyRasterClearRect);
                RWRETURN(FALSE);
            }

            /* Switch based on depth */
            switch (BITSTOBYTES(skyCurRas->depth))
            {
                case (4):
                {
                    RwUInt32 *mem;

                    mem = (RwUInt32*)(skyCurRas->cpPixels +
                                      skyCurRas->stride * rect->y +
                                      (rect->x << 1));
                    fill = ((RwUInt32)skyCurRas->stride >> 2) - rect->w;
                    h = rect->h;
                    while (h--)
                    {
                        w = rect->w;
                        while (w--)
                        {
                            *mem++ = (RwUInt32)nValue;
                        }
                        mem += fill;
                    }
                    break;
                }
                case (2):
                {
                    RwUInt16 *mem;

                    mem = (RwUInt16*)(skyCurRas->cpPixels +
                                      skyCurRas->stride * rect->y +
                                      (rect->x << 1));
                    fill = ((RwUInt32)skyCurRas->stride >> 1) - rect->w;
                    h = rect->h;
                    while (h--)
                    {
                        w = rect->w;
                        while (w--)
                        {
                            *mem++ = (RwUInt16)nValue;
                        }
                        mem += fill;
                    }
                    break;
                }
                default:
                {
                    /* Don't know this raster depth */
                    PFEXIT(PF_rwSkyRasterClearRect);
                    RWRETURN(FALSE);
                }
            }
        }
        default:
        {
            /* Don't know this raster type */
            PFEXIT(PF_rwSkyRasterClearRect);
            RWRETURN(FALSE);
        }
    }

    PFEXIT(PF_rwSkyRasterClearRect);
    RWRETURN(TRUE);
}

/****************************************************************************
 unScaledSpriteClip

 On entry   : Source rectangle
            : Destination rectangle
            : Destination clip rectangle is implicit (destClipRect)
 On exit    : TRUE if there is anything left after clipping
 */
static RwBool
unScaledSpriteClip(RwRect *srcRect, RwRect *dstRect)
{
    RWFUNCTION(RWSTRING("unScaledSpriteClip"));
    RWASSERT(srcRect);
    RWASSERT(dstRect);

    /* Trivial reject first */
    if (((dstRect->x + dstRect->w) > destClipRect.x) &&
        ((dstRect->y + dstRect->h) > destClipRect.y) &&
        (dstRect->x < (destClipRect.x + destClipRect.w)) &&
        (dstRect->y < (destClipRect.y + destClipRect.h)))
    {
        /* Now clip to rectangle */
        if (dstRect->x < destClipRect.x)
        {
            RwInt32 adjustLeft = (destClipRect.x - dstRect->x);
            dstRect->w -= adjustLeft;
            srcRect->x += adjustLeft;
            dstRect->x = destClipRect.x;
        }
        if ((dstRect->x + dstRect->w) > (destClipRect.x + destClipRect.w))
        {
            dstRect->w = (destClipRect.x + destClipRect.w) - dstRect->x;
        }
        if (dstRect->y < destClipRect.y)
        {
            RwInt32 adjustTop = (destClipRect.y - dstRect->y);
            dstRect->h -= adjustTop;
            srcRect->y += adjustTop;
            dstRect->y = destClipRect.y;
        }
        if ((dstRect->y + dstRect->h) > (destClipRect.y + destClipRect.h))
        {
            dstRect->h = (destClipRect.y + destClipRect.h) - dstRect->y;
        }

        /* Anything left? */
        if ((dstRect->w > 0) && (dstRect->h > 0))
        {
            srcRect->w = dstRect->w;
            srcRect->h = dstRect->h;

            RWRETURN(TRUE);
        }
    }

    RWRETURN(FALSE);
}

/****************************************************************************
 scaledSpriteClip

 On entry   : Source rectangle
            : Destination rectangle
            : Destination clip rectangle is implicit (destClipRect)
 On exit    : TRUE if there is anything left after clipping
 */
static RwBool
scaledSpriteClip(RwRect *srcRect, RwRect *dstRect)
{
    RWFUNCTION(RWSTRING("scaledSpriteClip"));
    RWASSERT(srcRect);
    RWASSERT(dstRect);

    /* Trivial reject first */
    if (((dstRect->x + dstRect->w) > destClipRect.x) &&
        ((dstRect->y + dstRect->h) > destClipRect.y) &&
        (dstRect->x < (destClipRect.x + destClipRect.w)) &&
        (dstRect->y < (destClipRect.y + destClipRect.h)))
    {
        /* Now clip to rectangle */
        if (dstRect->x < destClipRect.x)
        {
            RwInt32 adjustLeft = (destClipRect.x - dstRect->x);
            RwInt32 srcAdjustLeft = (adjustLeft * srcRect->w) / dstRect->w;
            dstRect->w -= adjustLeft;
            srcRect->w -= srcAdjustLeft;
            dstRect->x  = destClipRect.x;
            srcRect->x += srcAdjustLeft;
        }
        if ((dstRect->x + dstRect->w) > (destClipRect.x + destClipRect.w))
        {
            RwInt32 adjustRight = (dstRect->x + dstRect->w) - (destClipRect.x + destClipRect.w);
            RwInt32 srcAdjustRight = (adjustRight * srcRect->w) / dstRect->w;
            dstRect->w -= adjustRight;
            srcRect->w -= srcAdjustRight;
        }
        if (dstRect->y < destClipRect.y)
        {
            RwInt32 adjustTop = (destClipRect.y - dstRect->y);
            RwInt32 srcAdjustTop = (adjustTop * srcRect->h) / dstRect->h;
            dstRect->h -= adjustTop;
            srcRect->h -= srcAdjustTop;
            dstRect->y  = destClipRect.y;
            srcRect->y += srcAdjustTop;
        }
        if ((dstRect->y + dstRect->h) > (destClipRect.y + destClipRect.h))
        {
            RwInt32 adjustBottom = (dstRect->y + dstRect->h) - (destClipRect.y + destClipRect.h);
            RwInt32 srcAdjustBottom = (adjustBottom * srcRect->h) / dstRect->h;
            dstRect->h -= adjustBottom;
            srcRect->h -= srcAdjustBottom;
        }

        /* Anything left? */
        if ((dstRect->w > 0) && (dstRect->h > 0) && (srcRect->w > 0) && (srcRect->h > 0))
        {
            RWRETURN(TRUE);
        }
    }

    RWRETURN(FALSE);
}

/****************************************************************************
 _rwSkyRasterClear

 On entry   : NULL
            : NULL
            : Pixel value defining what to clear the raster to
 On exit    : TRUE on success
 */
RwBool
_rwSkyRasterClear(void * __RWUNUSED__ pOut, 
                  void * __RWUNUSED__ pInOut , 
                  RwInt32 nValue)
{
    RwBool success = FALSE;
    RWFUNCTION(RWSTRING("_rwSkyRasterClear"));
    PFENTRY(PF_rwSkyRasterClear);

    if (skyCurRas)
    {
        RwRect rect;

        rect.x = rect.y = 0;
        rect.w = skyCurRas->width;
        rect.h = skyCurRas->height;

        PFCALL(PF_rwSkyRasterClear);
        success = (RwBool)_rwSkyRasterClearRect(NULL, &rect, nValue);
        PFRET(PF_rwSkyRasterClear);
    }

    PFEXIT(PF_rwSkyRasterClear);
    RWRETURN(success);
}

/****************************************************************************
 skyRasterSetupForUpload

 On entry   : Raster to set up for upload (sets up rasExt sufficiently)
 On exit    : None
 */
static void
skyRasterSetupForUpload(RwRaster *raster)
{
    _SkyRasterExt *rasExt = RASTEREXTFROMRASTER(raster);
    const RwUInt64 word64 =  (1l << 14) | (1l << 34) | (1l << 54);
    /* Simulate a width and height that is 
     * next power of two up (for texturing) */
    RwUInt32      width = 1 << (_rwSkyFindMSB(raster->width-1)+1);
    RwUInt32      height = 1 << (_rwSkyFindMSB(raster->height-1)+1);
    RwUInt32      bytesPerPixel = BITSTOBYTES(raster->depth);
    RwUInt32      i;

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

    if (rasExt->palUploadPkt)
    {
        RwSky2Free(rasExt->palUploadPkt);
        rasExt->palUploadPkt = (u_long128 *)NULL;
    }
    for (i=0; i<7; i++)
    {
        if (rasExt->mipUploadPkts[i])
        {
            RwSky2Free(rasExt->mipUploadPkts[i]);
            rasExt->mipUploadPkts[i] = (u_long128 *)NULL;
        }
    }

    rasExt->lsb = ((width>>6?width>>6:1) << 14) |
                  ((_rwSkyFindMSB(width) & 0xf) << 26) |
                  ((_rwSkyFindMSB(height) & 0xf) << 30);
    rasExt->msb = ((_rwSkyFindMSB(height) & 0xf) >> 2) |
                  (0l << 3); /* MODULATE */

    /* We set up mipmapKL to be 0 (not used, hopefully, since we don't mipmap
       blits) */
    rasExt->mipmapKL = 0;

    switch (raster->cFormat & ((rwRASTERFORMATPIXELFORMATMASK
                                | rwRASTERFORMATPAL8 | rwRASTERFORMATPAL4)>>8))
    {
        case (rwRASTERFORMAT1555>>8):
        {
            rasExt->lsb |= PSMCT16S << 20;
            rasExt->msb |= 11 << 2; /* Use RGBA from texture */
            break;
        }
        case ((rwRASTERFORMAT1555|rwRASTERFORMATPAL8)>>8):
        {
            rasExt->lsb |= PSMT8 << 20;
            rasExt->msb |= 11 << 2; /* Use RGBA from texture */
            rasExt->msb |= PSMCT16S << 19;
            rasExt->msb |= 1l << 29;
            break;
        }
        case ((rwRASTERFORMAT8888|rwRASTERFORMATPAL8)>>8):
        {
            rasExt->lsb |= PSMT8 << 20;
            rasExt->msb |= 11 << 2; /* Use RGBA from texture */
            rasExt->msb |= PSMCT32 << 19;
            rasExt->msb |= 1l << 29;
            break;
        }
        case ((rwRASTERFORMAT1555|rwRASTERFORMATPAL4)>>8):
        {
            rasExt->lsb |= PSMT4 << 20;
            rasExt->msb |= 11 << 2; /* Use RGBA from texture */
            rasExt->msb |= PSMCT16S << 19;
            rasExt->msb |= 1l << 29;
            break;
        }
        case ((rwRASTERFORMAT8888|rwRASTERFORMATPAL4)>>8):
        {
            rasExt->lsb |= PSMT4 << 20;
            rasExt->msb |= 11 << 2; /* Use RGBA from texture */
            rasExt->msb |= PSMCT32 << 19;
            rasExt->msb |= 1l << 29;
            break;
        }
        case (rwRASTERFORMAT888>>8):
        {
            rasExt->lsb |= PSMCT24 << 20;
            break;
        }
        case (rwRASTERFORMAT8888>>8):
        {
            rasExt->lsb |= PSMCT32 << 20;
            rasExt->msb |= 11 << 2; /* Use RGBA from texture */
            break;
        }
        default:
        {
            break;
        }
    }

    /* Leave the cached bits alone */

    /* Set up the size for texture caching */
    rasExt->sysMemSize = height * width * bytesPerPixel;

    /* Size in texture memory needs to account for block size */
    if (raster->depth > 16)
    {
        RwUInt32 heightBy32 = (raster->height + 31) / 32;
        RwUInt32 widthBy64 = (raster->width + 63) / 64;
        RwUInt32 lastMapBlocks = widthBy64 * heightBy32;

        rasExt->nTexCacheSize = ((lastMapBlocks * 2048) & ~2047);
        rasExt->sysMemPalSize = 0;
    }
    else if (raster->depth == 16)
    {
        RwUInt32 heightBy64 = (raster->height + 63) / 64;
        RwUInt64 widthBy64 = (raster->width + 63) / 64;
        RwUInt32 lastMapBlocks = widthBy64 * heightBy64;

        rasExt->nTexCacheSize = ((lastMapBlocks * 2048) & ~2047);
        rasExt->sysMemPalSize = 0;
    }
    else if (raster->depth == 8)
    {
        RwUInt32 heightBy64 = (raster->height + 63) / 64;
        RwUInt64 widthBy128 = (raster->width + 127) / 128;
        RwUInt32 lastMapBlocks = widthBy128 * heightBy64;

        rasExt->nTexCacheSize = ((lastMapBlocks * 2048) & ~2047)+2048;
        rasExt->sysMemPalSize = 1;
    }
    else /* if (raster->depth == 4) */
    {
        RwUInt32 heightBy128 = (raster->height + 127) / 128;
        RwUInt64 widthBy128 = (raster->width + 127) / 128;
        RwUInt32 lastMapBlocks = widthBy128 * heightBy128;

        rasExt->nTexCacheSize = ((lastMapBlocks * 2048) & ~2047)+2048;
        rasExt->sysMemPalSize = 1;
    }

    /* Set to something sensible */
    rasExt->miptbp1Lsb = (RwUInt32)(word64);
    rasExt->miptbp1Msb = (RwUInt32)(word64 >> 32);
    rasExt->miptbp2Lsb = (RwUInt32)(word64);
    rasExt->miptbp2Msb = (RwUInt32)(word64 >> 32);
    rasExt->maxMipLevel = 0;

    RWRETURNVOID();
}

/****************************************************************************
 _rwSkyRasterRender

 On entry   : Raster to render (blit)
            : RwRect defining location to blit to (take x and y only)
            : 0
 On exit    : TRUE on success
 */
RwBool
_rwSkyRasterRender(void *pRas, void *pLoc, RwInt32 __RWUNUSED__ nData)
{
    RwRect     *rect = (RwRect*)pLoc;
    RwRaster   *srcRas = (RwRaster*)pRas;
    RwRect     dstRect, srcRect;
    _SkyRasterExt *rasExt;

    RWFUNCTION(RWSTRING("_rwSkyRasterRender"));
    PFENTRY(PF_rwSkyRasterRender);
    RWASSERT(rect);
    RWASSERT(srcRas);

    rasExt = RASTEREXTFROMRASTER(pRas);

    /* Clip the thing */
    srcRect.x = 0;
    srcRect.y = 0;
    srcRect.w = srcRas->width;
    srcRect.h = srcRas->height;

    /* Figure out destination rectangle (with subraster offset) */
    dstRect.x = rect->x + skyCurRas->nOffsetX;
    dstRect.y = rect->y + skyCurRas->nOffsetY;
    dstRect.w = srcRas->width;
    dstRect.h = srcRas->height;

    if (!unScaledSpriteClip(&srcRect, &dstRect))
    {
        /* All clipped away, nothing to do */
        PFEXIT(PF_rwSkyRasterRender);
        RWRETURN(TRUE);
    }

    if ((srcRas->cType == rwRASTERTYPEZBUFFER) ||
        (srcRas->cType == rwRASTERTYPECAMERA))
    {
        /* We can do this, but I'm not going to do it yet */
        PFEXIT(PF_rwSkyRasterRender);
        RWRETURN(FALSE);
    }

    switch (skyCurRas->cType)
    {
        case (rwRASTERTYPECAMERA):
        {
            /* Start with a sensible power of two height for stripes.
             * This will be subdivided to finish off.
             */
            RwUInt32   maxBlitPassHeight = 64;
            RwUInt32   srcY = srcRect.y;
            RwUInt32   srcEndY = srcRect.y + srcRect.h;
            RwUInt32   dstY = dstRect.y;
            RwUInt32   vertLeft;

            /* Setup for sprite rendering */
            spriteSetup((RwRasterType)(skyCurRas->cType));

            while ((vertLeft = srcEndY - srcY) > 0)
            {
                RwUInt32 heightToUse = vertLeft;

                if (heightToUse > maxBlitPassHeight)
                {
                    heightToUse = maxBlitPassHeight;
                }

                /* Max it out */
                srcRect.y = srcY;
                srcRect.h = heightToUse;

                if (vertLeft > maxBlitPassHeight)
                {
                    /* Allow one texel overlap for filter artifact reduction */
                    heightToUse--;
                }

                /* Upload the raster to the card */
                skyTexCacheReleaseRaster(blitRaster[rasterNum]);
                RwRasterSubRaster(blitRaster[rasterNum], srcRas, &srcRect);
                skyRasterSetupForUpload(blitRaster[rasterNum]);
                skyTexCacheAccessRaster(blitRaster[rasterNum], FALSE);

                /* Now render a sprite with it */
                spriteUV(dstRect.x, dstY,
                         dstRect.x + dstRect.w, dstY + heightToUse,
                         (0 << 4) + 0x8, (0 << 4) + 0x8,
                         (srcRect.w << 4) - 0x8, (srcRect.h << 4) - 0x8,
                         TRUE);

                /* Skip to next stripe */
                srcY += heightToUse; /* Allow overlap with next pass */
                dstY += heightToUse;

                /* Skip to the other buffer */
                rasterNum ^= 1;
            }

            /* Put the state back for normal rendering */
            spriteUnsetup();

            DI();
            rasExt->dmaRefCount++;
            EI();

            rasExt->dmaClrCount += 1;
            if (rasExt->dmaClrCount == 1)
            {
               _sweProcrastinatedAddURef((unsigned int*)&(rasExt->dmaRefCount));
            }

            break;
        }
        case (rwRASTERTYPENORMAL):
        case (rwRASTERTYPETEXTURE):
        {
            RwInt32 srcfill, dstfill;
            RwInt32 w;
            RwInt32 srcBytesPerPixel, dstBytesPerPixel;

            /* Just do it in software */
            if (!skyCurRas->cpPixels || !srcRas->cpPixels)
            {
                /* Can't blit if we can't see the pixels */
                PFEXIT(PF_rwSkyRasterRender);
                RWRETURN(FALSE);
            }

            srcBytesPerPixel = BITSTOBYTES(srcRas->depth);
            dstBytesPerPixel = BITSTOBYTES(skyCurRas->depth);

            /* Take offset off again (cpPixels accounts for sub raster offset) */
            dstRect.x -= skyCurRas->nOffsetX;
            dstRect.y -= skyCurRas->nOffsetY;

            srcfill = ((RwUInt32)srcRas->stride / srcBytesPerPixel) - dstRect.w;
            dstfill = ((RwUInt32)skyCurRas->stride / dstBytesPerPixel) - dstRect.w;

            /* Build a number we can switch on */
            switch ((dstBytesPerPixel * 10) + srcBytesPerPixel)
            {
                case 44:
                {
                    RwUInt32 *src, *dst;

                    dst = (RwUInt32*)(skyCurRas->cpPixels +
                                      skyCurRas->stride*dstRect.y +
                                      dstBytesPerPixel*dstRect.x);
                    src = (RwUInt32*)(srcRas->cpPixels +
                                      srcRas->stride*srcRect.y +
                                      srcBytesPerPixel*srcRect.x);

                    while (dstRect.h--)
                    {
                        w = dstRect.w;
                        while (w--)
                        {
                            RwUInt32 srcPixel = *src;

                            if ((srcPixel & 0xff000000) > 0x7f000000)
                            {
                                *dst = srcPixel;
                            }
                            else
                            {
                                /* Alpha blend it...! */
                                RwUInt32 dstPixel = *dst;
                                RwUInt32 srca, srcia;

                                srca = (srcPixel & 0xff000000) >> 24;
                                srcia = 128 - srca;
                                *dst = (((((srcPixel & 0xff000000) >> 24) * srca +
                                          ((dstPixel & 0xff000000) >> 24) * srcia) & 0x7f80) << 17) +
                                       (((((srcPixel & 0x00ff0000) >> 16) * srca +
                                          ((dstPixel & 0x00ff0000) >> 16) * srcia) & 0x7f80) << 9) +
                                       (((((srcPixel & 0x0000ff00) >>  8) * srca +
                                          ((dstPixel & 0x0000ff00) >>  8) * srcia) & 0x7f80) << 1) +
                                       (((((srcPixel & 0x000000ff)      ) * srca +
                                          ((dstPixel & 0x000000ff)      ) * srcia) & 0x7f80) >> 7);
                            }
                            dst++;
                            src++;
                        }
                        src += srcfill;
                        dst += dstfill;
                    }

                    break;
                }
                case 22:
                {
                    RwUInt16 *src, *dst;

                    dst = (RwUInt16*)(skyCurRas->cpPixels +
                                      skyCurRas->stride*dstRect.y +
                                      dstBytesPerPixel*dstRect.x);
                    src = (RwUInt16*)(srcRas->cpPixels +
                                      srcRas->stride*srcRect.y +
                                      srcBytesPerPixel*srcRect.x);

                    while (dstRect.h--)
                    {
                        w = dstRect.w;
                        while (w--)
                        {
                            RwUInt16 srcPixel = *src;

                            if (srcPixel & 0x8000)
                            {
                                *dst = srcPixel;
                            }
                            dst++;
                            src++;
                        }
                        src += srcfill;
                        dst += dstfill;
                    }

                    break;
                }
                default:
                {
                    /* Can't handle depth converting blits yet */
                    PFEXIT(PF_rwSkyRasterRender);
                    RWRETURN(FALSE);
                }
            }
            break;
        }
        case (rwRASTERTYPEZBUFFER):
        default:
        {
            /* Can;t do this one */
            PFEXIT(PF_rwSkyRasterRender);
            RWRETURN(FALSE);
        }
    }

    PFEXIT(PF_rwSkyRasterRender);
    RWRETURN(TRUE);
}

/****************************************************************************
 _rwSkyRasterRenderScaled

 On entry   : Raster to render (blit)
            : RwRect defining region to blit to (take x, y, w and h)
            : 0
 On exit    : TRUE on success
 */
RwBool
_rwSkyRasterRenderScaled(void *pRas, void *pRect, RwInt32 __RWUNUSED__ nData)
{
    RwRect     *rect = (RwRect*)pRect;
    RwRaster   *srcRas = (RwRaster*)pRas;
    RwRect     dstRect, srcRect;
    _SkyRasterExt *rasExt;

    RWFUNCTION(RWSTRING("_rwSkyRasterRenderScaled"));
    PFENTRY(PF_rwSkyRasterRenderScaled);

    rasExt = RASTEREXTFROMRASTER(pRas);

    /* RwRasterSubRaster takes care of sub raster offsets of source */
    srcRect.x = 0;
    srcRect.y = 0;
    srcRect.w = srcRas->width;
    srcRect.h = srcRas->height;

    /* Figure out destination rectangle (with subraster offset) */
    dstRect.x = rect->x + skyCurRas->nOffsetX;
    dstRect.y = rect->y + skyCurRas->nOffsetY;
    dstRect.w = rect->w;
    dstRect.h = rect->h;

    if (!scaledSpriteClip(&srcRect, &dstRect))
    {
        /* All clipped out */
        PFEXIT(PF_rwSkyRasterRenderScaled);
        RWRETURN(TRUE);
    }

    if ((srcRas->cType == rwRASTERTYPEZBUFFER) ||
        (srcRas->cType == rwRASTERTYPECAMERA))
    {
        /* We can do this, but I'm not going to do it yet */
        PFEXIT(PF_rwSkyRasterRenderScaled);
        RWRETURN(FALSE);
    }

    switch (skyCurRas->cType)
    {
        case (rwRASTERTYPECAMERA):
        {
            /* Start with a sensible power of two height for stripes.
             * This will be subdivided to finish off.
             */
            RwUInt32   maxBlitPassHeight = 64;
            RwUInt32   srcY = srcRect.y;
            RwUInt32   srcEndY = srcRect.y + srcRect.h;
            RwUInt32   dstY16_16 = dstRect.y << 16;
            RwUInt32   vertLeft;
            RwUInt32   oldDstH = dstRect.h, oldSrcH = srcRect.h;

            /* Setup for sprite rendering */
            spriteSetup((RwRasterType)(skyCurRas->cType));

            while ((vertLeft = srcEndY - srcY) > 0)
            {
                RwUInt32 dstHeight16_16;
                RwUInt32 heightToUse = vertLeft;

                if (heightToUse > maxBlitPassHeight)
                {
                    heightToUse = maxBlitPassHeight;
                }

                /* Max it out */
                srcRect.y = srcY;
                srcRect.h = heightToUse;

                if (vertLeft > maxBlitPassHeight)
                {
                    /* Allow one texel overlap for filter artifact reduction */
                    heightToUse--;
                }
                dstHeight16_16 = (((heightToUse << 16) * oldDstH) / oldSrcH);

                /* Upload the raster to the card */
                skyTexCacheReleaseRaster(blitRaster[rasterNum]);
                RwRasterSubRaster(blitRaster[rasterNum], srcRas, &srcRect);
                skyRasterSetupForUpload(blitRaster[rasterNum]);
                skyTexCacheAccessRaster(blitRaster[rasterNum], FALSE);

                /* Now render a sprite with it */
                spriteUV(dstRect.x, dstY16_16 >> 16,
                         dstRect.x + dstRect.w, (dstY16_16 + dstHeight16_16) >> 16,
                         (0 << 4) + 0x8, (0 << 4) + 0x8,
                         (srcRect.w << 4) - 0x8, (srcRect.h << 4) - 0x8,
                         TRUE);

                /* Skip to next stripe */
                srcY += heightToUse; /* Allow overlap with next pass */
                dstY16_16 += dstHeight16_16;

                /* Skip to the other buffer */
                rasterNum ^= 1;
            }

            /* Put the state back for normal rendering */
            spriteUnsetup();

            DI();
            rasExt->dmaRefCount++;
            EI();

            rasExt->dmaClrCount += 1;
            if (rasExt->dmaClrCount == 1)
            {
               _sweProcrastinatedAddURef((unsigned int*)&(rasExt->dmaRefCount));
            }

            break;
        }
        case (rwRASTERTYPENORMAL):
        case (rwRASTERTYPETEXTURE):
        {
            RwInt32 w;
            RwInt32 srcBytesPerPixel, dstBytesPerPixel;
            RwInt32 vertLimit, vertError, vertStep;
            RwInt32 horzLimit, horzError, horzStep;
            RwUInt8 *src, *dst;

            /* Just do it in software */
            if (!skyCurRas->cpPixels || !srcRas->cpPixels)
            {
                /* Can't blit if we can't see the pixels */
                PFEXIT(PF_rwSkyRasterRenderScaled);
                RWRETURN(FALSE);
            }

            srcBytesPerPixel = BITSTOBYTES(srcRas->depth);
            dstBytesPerPixel = BITSTOBYTES(skyCurRas->depth);

            /* Take offset off again (cpPixels accounts for sub raster offset) */
            dstRect.x -= skyCurRas->nOffsetX;
            dstRect.y -= skyCurRas->nOffsetY;

            /* Build interpolator values */
            vertLimit = dstRect.h;
            vertStep = srcRect.h;
            horzLimit = dstRect.w;
            horzStep = srcRect.w;

            /* Top left of blit areas */
            dst = (skyCurRas->cpPixels + skyCurRas->stride * dstRect.y +
                                         dstBytesPerPixel * dstRect.x);
            src = (srcRas->cpPixels + srcRas->stride * srcRect.y +
                                      srcBytesPerPixel * srcRect.x);

            /* We need to be very precise with the scanline step here, so
             * don't use fill step, but use stride directly.
             */
            /* Build a number we can switch on */
            switch ((dstBytesPerPixel * 10) + srcBytesPerPixel)
            {
                case 44:
                {
                    vertError = -vertLimit;
                    while (dstRect.h--)
                    {
                        RwUInt32 *dstLine = (RwUInt32 *)dst;
                        RwUInt32 *srcLine = (RwUInt32 *)src;

                        w = dstRect.w;
                        horzError = -horzLimit;
                        while (w--)
                        {
                            RwUInt32 srcPixel = *srcLine;
                            if ((srcPixel & 0xff000000) > 0x7f000000)
                            {
                                *dstLine = srcPixel;
                            }
                            else
                            {
                                /* Alpha blend the pixels...! */
                                RwUInt32 dstPixel = *dstLine;
                                RwUInt32 srca, srcia;

                                srca = (srcPixel & 0xff000000) >> 24;
                                srcia = 128 - srca;
                                *dstLine = (((((srcPixel & 0xff000000) >> 24) * srca +
                                              ((dstPixel & 0xff000000) >> 24) * srcia) & 0x7f80) <<17) +
                                           (((((srcPixel & 0x00ff0000) >> 16) * srca +
                                              ((dstPixel & 0x00ff0000) >> 16) * srcia) & 0x7f80) <<9) +
                                           (((((srcPixel & 0x0000ff00) >>  8) * srca +
                                              ((dstPixel & 0x0000ff00) >>  8) * srcia) & 0x7f80) << 1) +
                                           (((((srcPixel & 0x000000ff)      ) * srca +
                                              ((dstPixel & 0x000000ff)      ) * srcia) & 0x7f80) >> 7);
                            }

                            /* Step the pointers */
                            dstLine++;
                            horzError += horzStep;
                            while (horzError > 0)
                            {
                                srcLine++;
                                horzError -= horzLimit;
                            }
                        }

                        /* Step the pointers */
                        dst += skyCurRas->stride;
                        vertError += vertStep;
                        while (vertError > 0)
                        {
                            src += srcRas->stride;
                            vertError -= vertLimit;
                        }
                    }

                    break;
                }
                case 22:
                {
                    vertError = -vertLimit;
                    while (dstRect.h--)
                    {
                        RwUInt16 *dstLine = (RwUInt16 *)dst;
                        RwUInt16 *srcLine = (RwUInt16 *)src;

                        w = dstRect.w;
                        horzError = -horzLimit;
                        while (w--)
                        {
                            RwUInt16 srcPixel = *srcLine;
                            if (srcPixel & 0x8000)
                            {
                                *dstLine = srcPixel;
                            }

                            /* Step the pointers */
                            dstLine++;
                            horzError += horzStep;
                            while (horzError > 0)
                            {
                                srcLine++;
                                horzError -= horzLimit;
                            }
                        }

                        /* Step the pointers */
                        dst += skyCurRas->stride;
                        vertError += vertStep;
                        while (vertError > 0)
                        {
                            src += srcRas->stride;
                            vertError -= vertLimit;
                        }
                    }

                    break;
                }
                default:
                {
                    /* Can't do depth converting blits yet */
                    PFEXIT(PF_rwSkyRasterRenderScaled);
                    RWRETURN(FALSE);
                }
            }

            /* C'est tout */
            break;
        }
        case (rwRASTERTYPEZBUFFER):
        default:
        {
            /* Can't do this raster type */
            PFEXIT(PF_rwSkyRasterRenderScaled);
            RWRETURN(FALSE);
        }
    }

    PFEXIT(PF_rwSkyRasterRenderScaled);
    RWRETURN(TRUE);
}

/****************************************************************************
 _rwSkyRasterRenderFast

 On entry   : Raster to render (blit) - raster has no mask info, so optimise for this
            : RwRect defining location to blit to (take x and y only)
            : 0
 On exit    : TRUE on success
 */
RwBool
_rwSkyRasterRenderFast(void *pRas, void *pLoc, RwInt32 __RWUNUSED__ nData)
{
    RwRect     *rect = (RwRect*)pLoc;
    RwRaster   *srcRas = (RwRaster*)pRas;
    RwRect     dstRect, srcRect;
    _SkyRasterExt *rasExt;

    RWFUNCTION(RWSTRING("_rwSkyRasterRenderFast"));
    PFENTRY(PF_rwSkyRasterRenderFast);
    RWASSERT(rect);
    RWASSERT(srcRas);

    rasExt = RASTEREXTFROMRASTER(pRas);

    /* Clip the thing */
    srcRect.x = 0;
    srcRect.y = 0;
    srcRect.w = srcRas->width;
    srcRect.h = srcRas->height;

    /* Figure out destination rectangle (with subraster offset) */
    dstRect.x = rect->x + skyCurRas->nOffsetX;
    dstRect.y = rect->y + skyCurRas->nOffsetY;
    dstRect.w = srcRas->width;
    dstRect.h = srcRas->height;

    if (!unScaledSpriteClip(&srcRect, &dstRect))
    {
        /* All clipped away, nothing to do */
        PFEXIT(PF_rwSkyRasterRenderFast);
        RWRETURN(TRUE);
    }

    if ((srcRas->cType == rwRASTERTYPEZBUFFER) ||
        (srcRas->cType == rwRASTERTYPECAMERA))
    {
        /* We can do this, but I'm not going to do it yet */
        PFEXIT(PF_rwSkyRasterRenderFast);
        RWRETURN(FALSE);
    }

    if (skyCurRas->cType == rwRASTERTYPEZBUFFER)
    {
        PFEXIT(PF_rwSkyRasterRenderFast);
        RWRETURN(FALSE);
    }

    switch (skyCurRas->cType)
    {
        case (rwRASTERTYPECAMERA):
        {
            /* Start with a sensible power of two height for stripes.
             * This will be subdivided to finish off.
             */
            RwUInt32   maxBlitPassHeight = 64;
            RwUInt32   srcY = srcRect.y;
            RwUInt32   srcEndY = srcRect.y + srcRect.h;
            RwUInt32   dstY = dstRect.y;
            RwUInt32   vertLeft;

            /* Setup for sprite rendering */
            spriteSetup((RwRasterType)(skyCurRas->cType));

            while ((vertLeft = srcEndY - srcY) > 0)
            {
                RwUInt32 heightToUse = vertLeft;

                if (heightToUse > maxBlitPassHeight)
                {
                    heightToUse = maxBlitPassHeight;
                }

                /* Max it out */
                srcRect.y = srcY;
                srcRect.h = heightToUse;

                if (vertLeft > maxBlitPassHeight)
                {
                    /* Allow one texel overlap for filter artifact reduction */
                    heightToUse--;
                }

                /* Upload the raster to the card */
                skyTexCacheReleaseRaster(blitRaster[rasterNum]);
                RwRasterSubRaster(blitRaster[rasterNum], srcRas, &srcRect);
                skyRasterSetupForUpload(blitRaster[rasterNum]);
                skyTexCacheAccessRaster(blitRaster[rasterNum], FALSE);

                /* Now render a sprite with it */
                spriteUV(dstRect.x, dstY,
                         dstRect.x + dstRect.w, dstY + heightToUse,
                         (0 << 4) + 0x8, (0 << 4) + 0x8,
                         (srcRect.w << 4) - 0x8, (srcRect.h << 4) - 0x8,
                         FALSE);

                /* Skip to next stripe */
                srcY += heightToUse; /* Allow overlap with next pass */
                dstY += heightToUse;

                /* Skip to the other buffer */
                rasterNum ^= 1;
            }

            /* Put the state back for normal rendering */
            spriteUnsetup();

            DI();
            rasExt->dmaRefCount++;
            EI();

            rasExt->dmaClrCount += 1;
            if (rasExt->dmaClrCount == 1)
            {
               _sweProcrastinatedAddURef((unsigned int*)&(rasExt->dmaRefCount));
            }

            break;
        }
        case (rwRASTERTYPENORMAL):
        case (rwRASTERTYPETEXTURE):
        {
            RwInt32 srcBytesPerPixel, dstBytesPerPixel;

            /* Just do it in software */
            if (!skyCurRas->cpPixels || !srcRas->cpPixels)
            {
                /* No pixels, can't do it */
                PFEXIT(PF_rwSkyRasterRenderFast);
                RWRETURN(FALSE);
            }

            srcBytesPerPixel = BITSTOBYTES(srcRas->depth);
            dstBytesPerPixel = BITSTOBYTES(skyCurRas->depth);

            /* Take offset off again (cpPixels accounts for sub raster offset) */
            dstRect.x -= skyCurRas->nOffsetX;
            dstRect.y -= skyCurRas->nOffsetY;

            /* Build a number we can switch on */
            switch ((dstBytesPerPixel * 10) + srcBytesPerPixel)
            {
                case 22:
                case 44:
                {
                    RwUInt8  *src, *dst;
                    RwUInt32 bytesPerPixel = BITSTOBYTES(skyCurRas->depth);

                    dst = (skyCurRas->cpPixels +
                           skyCurRas->stride * dstRect.y +
                           dstBytesPerPixel * dstRect.x);
                    src = (srcRas->cpPixels +
                           srcRas->stride * srcRect.y +
                           srcBytesPerPixel * srcRect.x);

                    while (dstRect.h--)
                    {
                        memcpy(dst, src, dstRect.w * bytesPerPixel);
                        src += srcRas->stride;
                        dst += skyCurRas->stride;
                    }

                    break;
                }
                default:
                {
                    /* Can't do depth converting blit yet */
                    PFEXIT(PF_rwSkyRasterRenderFast);
                    RWRETURN(FALSE);
                }
            }
            break;
        }
        case (rwRASTERTYPEZBUFFER):
        default:
        {
            /* Can't do this raster type */
            PFEXIT(PF_rwSkyRasterRenderFast);
            RWRETURN(FALSE);
        }
    }

    PFEXIT(PF_rwSkyRasterRenderFast);
    RWRETURN(TRUE);
}

/****************************************************************************
 _rwSkyRasterSetContext

 On entry   : NULL
            : Raster to be the destination of future raster ops.
            : 0
 On exit    : TRUE on success
 */
RwBool
_rwSkyRasterSetContext(void *__RWUNUSED__ pOut , void *pRas, RwInt32 __RWUNUSED__ nData)
{
    RWFUNCTION(RWSTRING("_rwSkyRasterSetContext"));
    PFENTRY(PF_rwSkyRasterSetContext);

    skyCurRas = (RwRaster*)pRas;

    destClipRect.x = skyCurRas->nOffsetX;
    destClipRect.y = skyCurRas->nOffsetY;
    destClipRect.w = skyCurRas->width;
    destClipRect.h = skyCurRas->height;

    PFEXIT(PF_rwSkyRasterSetContext);
    RWRETURN(TRUE);
}

