/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "Licence").
 * You may not use this file except in compliance with the Licence.
 *
 * You can obtain a copy of the licence at RISC OS path @.^.LICENCE
 * or  http://www.riscosdev.com/lanman98/LICENCE.CDDL
 * See the Licence for the specific language governing permissions
 * and limitations under the Licence.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the Licence file. If applicable, add the
 * following below this CDDL HEADER, with the fields enclosed by
 * brackets "[]" replaced with your own identifying information:
 * Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
 
/*
 *   Portions Copyright 1996 Warm Silence Software Ltd.
 */

/********************************************************************/
/*                                                                  */
/* Name: PGspr.c                                                    */
/* Author: Paul Gardiner.                                           */
/* Function: Provides a library of functions for accessing sprites. */
/*                                                                  */
/********************************************************************/

#include "kernel.h"
#include "swis.h"
static _kernel_swi_regs regs;
#include "stdio.h"
#include "stdlib.h"
#include "PGerr.h"
#include "PGgr.h"
#include "PGgrH.h"
#include "PGmode.h"
#include "PGspr.h"
#include "PGsprH.h"

static int translation(spr_sprite spr)
{
    int bpp, haspalette;

    regs.r[0] = 0x225;
    regs.r[1] = spr->area;
    regs.r[2] = spr->sprite + spr->area;
    regs.r[3] = -1;
    _kernel_swi(OS_SpriteOp, &regs, &regs);
    haspalette = regs.r[3];
    regs.r[0] = 0x228;
    _kernel_swi(OS_SpriteOp, &regs, &regs);
    regs.r[0] = regs.r[6];
    regs.r[1] = 9;
    _kernel_swi(OS_ReadModeVariable, &regs, &regs);
    bpp = regs.r[2];
    regs.r[0] = -1;
    _kernel_swi(OS_ReadModeVariable, &regs, &regs);
    if(haspalette || regs.r[2] != bpp)
        return bpp;
    else
        return -1;
}


spr_type SprType(int mode, int xsize, int ysize)
{
    spr_type stype;
    int xeig, yeig, log2bpp;

    regs.r[0] = mode;
    regs.r[1] = 4;
    _kernel_swi(OS_ReadModeVariable, &regs, &regs);
    xeig = regs.r[2];
    regs.r[1] = 5;
    _kernel_swi(OS_ReadModeVariable, &regs, &regs);
    yeig = regs.r[2];
    regs.r[1] = 9;
    _kernel_swi(OS_ReadModeVariable, &regs, &regs);
    log2bpp = regs.r[2];
    stype.mode = mode;
    stype.xsize = (xsize - 1 >> xeig) + 1;
    stype.ysize = (ysize - 1 >> yeig) + 1;
    stype.memory = (((stype.xsize << log2bpp) - 1 >> 5) + 1) * stype.ysize << 2;
    return stype;
}

spr_type SprNewType(int col, int xsize, int ysize)
{
    spr_type stype;

    if(col < 0 || col > 5)
        ErrorFatal("Invalid col in SprNewType");
    stype.mode = ((col + 1) << 27) | (45 << 14) | (45 << 1) | 1;
    stype.xsize = xsize;
    stype.ysize = ysize;
    stype.memory = (((stype.xsize << col) - 1 >> 5) + 1) * stype.ysize << 2;
    return stype;
}

spr_sprite SprCreate(spr_type st)
{
    spr_sprite *spr_arr;

    spr_arr = SprCreateN(1, st);
    if(spr_arr == NULL)
    {
        return NULL;
    }
    else
    {
        return spr_arr[0];
    }
}

spr_sprite *SprCreateN(int n, spr_type st)
{
    spr_sprite spr_data, *spr_arr;
    int area_size, data_size, arr_size;
    int area;
    int i;
    char buf[20];

    area_size = 16 + n * (44 + st.memory);
    data_size = n * sizeof(struct spr_sprite_s);
    arr_size = (n + 1) * sizeof(spr_sprite);
    area = (int) malloc(area_size + data_size + arr_size);
    if(area == 0)
        return NULL;
    spr_data = (spr_sprite) (area + area_size);
    spr_arr = (spr_sprite *) (area + area_size + data_size);
    ((int *) area)[0] = 16 + n * (44 + st.memory);
    ((int *) area)[2] = 16;
    regs.r[0] = 0x109;
    regs.r[1] = area;
    _kernel_swi(OS_SpriteOp, &regs, &regs);
    for(i = 0; i < n; i++)
    {
        regs.r[0] = 0x10f;
        regs.r[1] = area;
        sprintf(buf, "Sprite%d", i+1);
        regs.r[2] = (int) buf;
        regs.r[3] = 0;
        regs.r[4] = st.xsize;
        regs.r[5] = st.ysize;
        regs.r[6] = st.mode;
        _kernel_swi(OS_SpriteOp, &regs, &regs);
        regs.r[0] = 0x118;
        regs.r[1] = area;
        regs.r[2] = (int) buf;
        _kernel_swi(OS_SpriteOp, &regs, &regs);
        spr_data[i].sprite = regs.r[2] - area;
        spr_data[i].area = area;
        spr_data[i].save = NULL;
        spr_arr[i] = spr_data + i;
    }
    spr_arr[i] = NULL;
    return spr_arr;
}

spr_sprite SprLoad(char *filename)
{
    spr_sprite *spr_arr;

    spr_arr = SprLoadN(filename);
    if(spr_arr == NULL)
    {
        return NULL;
    }
    else
    {
        return spr_arr[0];
    }
}

spr_sprite *SprLoadN(char *filename)
{
    char buf[30];
    int i, nspr;
    int area_size, data_size, arr_size;
    int area;
    spr_sprite *spr_arr, spr_data;

    regs.r[0] = 17;
    regs.r[1] = (int) filename;
    _kernel_swi(OS_File, &regs, &regs);
    if(regs.r[0] == 0)
        return NULL;
    area_size = regs.r[4] + 4;
    area = (int) malloc(area_size + 512);  /* ?????? */
    if(area == 0)
        return NULL;
    ((int *) area)[0] = regs.r[4] + 4;
    regs.r[0] = 0x10a;
    regs.r[1] = area;
    regs.r[2] = (int) filename;
    _kernel_swi(OS_SpriteOp, &regs, &regs);
    regs.r[0] = 0x108;
    regs.r[1] = area;
    _kernel_swi(OS_SpriteOp, &regs, &regs);
    nspr = regs.r[3];
    data_size = sizeof(struct spr_sprite_s) * nspr;
    arr_size = sizeof(spr_sprite) * (nspr + 1);
    area = (int) realloc((void *) area, area_size + data_size + arr_size);
    if(area == 0)
        return NULL;
    spr_data = (spr_sprite) (area + area_size);
    spr_arr = (spr_sprite *) (area + area_size + data_size);
    for(i = 0; i < nspr; i++)
    {
        regs.r[0] = 0x10d;
        regs.r[1] = area;
        regs.r[2] = (int) buf;
        regs.r[3] = 30;
        regs.r[4] = i + 1;
        _kernel_swi(OS_SpriteOp, &regs, &regs);
        regs.r[0] = 0x118;
        regs.r[1] = area;
        regs.r[2] = (int) buf;
        _kernel_swi(OS_SpriteOp, &regs, &regs);
        spr_data[i].sprite = regs.r[2] - area;
        spr_data[i].area = area;
        spr_data[i].save = NULL;
        spr_arr[i] = spr_data + i;
    }
    spr_arr[i] = NULL;
    return spr_arr;
}

spr_sprite SprScreen(int xorig, int yorig, int xsize, int ysize)
{
    spr_type st;
    int area_size, data_size;
    int area;
    spr_sprite spr;

    st = SprType(ModeGet(), xsize, ysize);
    area_size = 16 + 44 + st.memory;
    data_size = sizeof(struct spr_sprite_s);
    area = (int) malloc(area_size + data_size);
    if(area == 0)
        return NULL;
    spr = (spr_sprite) (area + area_size);
    ((int *) area)[0] = 16 + 44 + st.memory;
    ((int *) area)[2] = 16;
    regs.r[0] = 0x109;
    regs.r[1] = area;
    _kernel_swi(OS_SpriteOp, &regs, &regs);
    regs.r[0] = 0x110;
    regs.r[1] = area;
    regs.r[2] = (int) "Sprite0";
    regs.r[3] = 0;
    regs.r[4] = xorig;
    regs.r[5] = yorig;
    regs.r[6] = xorig + xsize - 1;
    regs.r[7] = yorig + ysize - 1;
    _kernel_swi(OS_SpriteOp, &regs, &regs);
    spr->area = area;
    spr->sprite = regs.r[2] - area;
    spr->save = NULL;
    return spr;
}

void SprFree(spr_sprite spr)
{
    if(spr != NULL)
    {
        free((void *) spr->save);
        free((void *) spr->area);
    }
}

void SprFreeN(spr_sprite *spr_arr)
{
    int i;

    if(spr_arr != NULL)
    {
        for(i = 0; spr_arr[i] != NULL; ++i)
            free((void *) spr_arr[i]->save);
        free((void *) spr_arr[0]->area);
    }
}

void SprSave(spr_sprite spr, char *filename)
{
    if(spr != NULL)
    {
        regs.r[0] = 0x10c;
        regs.r[1] = spr->area;
        regs.r[2] = (int) filename;
        _kernel_swi(OS_SpriteOp, &regs, &regs);
    }
}

void SprRemode(spr_sprite spr, int mode)
{
    if(spr != NULL)
        ((int *)(spr->sprite + spr->area))[10] = mode;
}

void SprVDU(spr_sprite spr)
{
    if(spr == NULL)
    {
        regs.r[2] =  0;
        regs.r[3] =  1;
        xyzGRxyz.indirected = 0;
    }
    else
    {
        regs.r[1] = spr->area;
        regs.r[2] = spr->sprite + spr->area;
        if(spr->save == NULL)
        {
            regs.r[0] = 0x23e;
            _kernel_swi(OS_SpriteOp, &regs, &regs);
            spr->save = (int) malloc(regs.r[3]);
            if(spr->save == NULL)
                ErrorFatal("Insufficient memory for sprite save area");
            ((int *) spr->save)[0] = 0;
        }
        regs.r[3] = spr->save;
        xyzGRxyz.indirected = 1;
    }
    regs.r[0] = 0x23c;
    _kernel_swi(OS_SpriteOp, &regs, &regs);
}

void SprPlot(spr_sprite spr, int x, int y)
{
    char tr[256];

    regs.r[0] = 68;
    regs.r[1] = x;
    regs.r[2] = y;
    _kernel_swi(OS_Plot, &regs, &regs);
    if(spr != NULL)
    {
        if(xyzGRxyz.indirected)
        {
            regs.r[0] = 0x21c;
            regs.r[1] = spr->area;
            regs.r[2] = spr->sprite + spr->area;
            regs.r[5] = 8;
        _kernel_swi(OS_SpriteOp, &regs, &regs);
        }
        else
        {
            switch(translation(spr))
            {
                case -1:
                    regs.r[7] = 0;
                    break;
                case 3:
                    regs.r[0] = spr->area;
                    regs.r[1] = spr->sprite + spr->area;
/*                    regs.r[0] = 28;
                    regs.r[1] = 0; */
                    regs.r[2] = -1;
                    regs.r[3] = -1;
                    regs.r[4] = (int) tr;
                    regs.r[5] = 1;
                    _kernel_swi(ColourTrans_SelectTable, &regs, &regs);
                    regs.r[7] = (int) tr;
                    break;
                default:
                    regs.r[0] = 0x234;
                    regs.r[1] = spr->area;
                    regs.r[2] = spr->sprite + spr->area;
                    regs.r[3] = x;
                    regs.r[4] = y;
                    regs.r[5] = 8;
                    regs.r[6] = 0;
                    regs.r[7] = (int) tr;
                    _kernel_swi(Wimp_ReadPixTrans, &regs, &regs);
                    break;
            }
            regs.r[0] = 0x234;
            regs.r[1] = spr->area;
            regs.r[2] = spr->sprite + spr->area;
            regs.r[3] = x;
            regs.r[4] = y;
            regs.r[5] = 8;
            regs.r[6] = 0;
            _kernel_swi(OS_SpriteOp, &regs, &regs);
        }
    }
}

void SprPlotQ(spr_sprite spr)
{
    if(spr != NULL)
    {
        regs.r[0] = 0x21c;
        regs.r[1] = spr->area;
        regs.r[2] = spr->sprite + spr->area;
        regs.r[5] = 8;
        _kernel_swi(OS_SpriteOp, &regs, &regs);
    }
}

void SprBlt(spr_action act, spr_sprite spr)
{
    if(spr != NULL)
    {
        regs.r[0] = 0x21c;
        regs.r[1] = spr->area;
        regs.r[2] = spr->sprite + spr->area;
        regs.r[5] = act;
        _kernel_swi(OS_SpriteOp, &regs, &regs);
    }
}

void SprPlotS(spr_scale sc_req, spr_sprite spr, int x, int y)
{
    int  sc[4];
    char tr[16];

    if(spr != NULL)
    {
        regs.r[0] = 0x234;
        regs.r[1] = spr->area;
        regs.r[2] = spr->sprite + spr->area;
        regs.r[3] = x;
        regs.r[4] = y;
        regs.r[5] = 8;
        if(!xyzGRxyz.indirected)
        {
            regs.r[6] = (int) sc;
            regs.r[7] = (int) tr;
            _kernel_swi(Wimp_ReadPixTrans, &regs, &regs);
            sc[0] *= sc_req.mult;
            sc[1] *= sc_req.mult;
            sc[2] *= sc_req.div;
            sc[3] *= sc_req.div;
        }
        else
        {
            regs.r[6] = (int) sc;
            regs.r[7] = 0;
            sc[0] = sc_req.mult;
            sc[1] = sc_req.mult;
            sc[2] = sc_req.div;
            sc[3] = sc_req.div;
        }
        regs.r[0] = 0x234;
        _kernel_swi(OS_SpriteOp, &regs, &regs);
    }
}

void SprPlotBM(spr_scale sc_req, spr_sprite spr, int x, int y)
{
    int  sc[4];
    char tr[16];
    unsigned int palette[20];
    int fgcol, bgcol;

    if(spr != NULL)
    {
        fgcol = xyzGRxyz.fgcol;
        bgcol = xyzGRxyz.bgcol;
        if(!xyzGRxyz.indirected)
        {
            regs.r[1] = (int) palette;
            _kernel_swi(Wimp_ReadPalette, &regs, &regs);
            regs.r[0] = (palette[bgcol] | (palette[bgcol] >> 4)) & 0xFFFFFF00;
            _kernel_swi(ColourTrans_ReturnColourNumber, &regs, &regs);
            tr[0] = regs.r[0];
            regs.r[0] = (palette[fgcol] | (palette[fgcol] >> 4)) & 0xFFFFFF00;
            _kernel_swi(ColourTrans_ReturnColourNumber, &regs, &regs);
            tr[1] = regs.r[0];
            regs.r[0] = 0x200;
            regs.r[1] = spr->area;
            regs.r[2] = spr->sprite + spr->area;
            regs.r[6] = (int) sc;
            regs.r[7] = 0;
            _kernel_swi(Wimp_ReadPixTrans, &regs, &regs);
            sc[0] *= sc_req.mult;
            sc[1] *= sc_req.mult;
            sc[2] *= sc_req.div;
            sc[3] *= sc_req.div;
        }
        else
        {
            tr[0] = bgcol;
            tr[1] = fgcol;
            regs.r[6] = (int) sc;
            sc[0] = sc_req.mult;
            sc[1] = sc_req.mult;
            sc[2] = sc_req.div;
            sc[3] = sc_req.div;
        }
        regs.r[0] = 0x234;
        regs.r[1] = spr->area;
        regs.r[2] = spr->sprite + spr->area;
        regs.r[3] = x;
        regs.r[4] = y;
        regs.r[5] = 8;
        regs.r[6] = (int) sc;
        regs.r[7] = (int) tr;
        _kernel_swi(OS_SpriteOp, &regs, &regs);
    }
}

void SprGetSize(spr_sprite spr, int *x, int *y)
{
    if(spr != NULL)
    {
        regs.r[0] = 0x228;
        regs.r[1] = spr->area;
        regs.r[2] = spr->sprite + spr->area;
        _kernel_swi(OS_SpriteOp, &regs, &regs);
        *x = regs.r[3];
        *y = regs.r[4];
        regs.r[0] = regs.r[6];
        regs.r[1] = 4;
        _kernel_swi(OS_ReadModeVariable, &regs, &regs);
        *x <<= regs.r[2];
        regs.r[0] = regs.r[6];
        regs.r[1] = 5;
        _kernel_swi(OS_ReadModeVariable, &regs, &regs);
        *y <<= regs.r[2];
    }
}

int SprBPP(spr_sprite spr)
{
    if(spr == NULL)
        return -1;
    regs.r[0] = 0x228;
    regs.r[1] = spr->area;
    regs.r[2] = spr->sprite + spr->area;
    _kernel_swi(OS_SpriteOp, &regs, &regs);
    regs.r[0] = regs.r[6];
    regs.r[1] = 9;
    _kernel_swi(OS_ReadModeVariable, &regs, &regs);
    return regs.r[2];
}

sprmem_t SprDirect(spr_sprite spr)
{
    sprmem_t sm;

    if(spr != NULL)
    {
        int cb;

        cb = ((int *) spr)[0] + ((int *) spr)[1];
        sm.base  = (char *) (cb + ((int *) cb)[8]);
        sm.yinc  = (((int *) cb)[4] + 1) * 4;
        sm.limit = sm.base + sm.yinc  * ((((int *) cb)[5]) + 1);
    }
    else
    {
        int param[2];

        param[0] = 148;
        param[1] = -1;
        regs.r[0] = (int) param;
        regs.r[1] = (int) &sm.base;
        _kernel_swi(OS_ReadVduVariables, &regs, &regs);
        regs.r[0] = -1;
        regs.r[1] = 6;
        _kernel_swi(OS_ReadModeVariable, &regs, &regs);
        sm.yinc = regs.r[2];
        regs.r[1] = 12;
        _kernel_swi(OS_ReadModeVariable, &regs, &regs);
        sm.limit = sm.base + sm.yinc * (regs.r[2] + 1);
    }
    return sm;
}
