// (c) 1994-2007 Electronic Arts Inc.

/* ABSTRACT */
/*------------------------------------------------------------------*/
/*                                                                  */
/*               BIF - Binary Information File Gimex                */
/*                                                                  */
/*                    by FrANK G. Barchard, EAC                     */
/*                                                                  */
/*------------------------------------------------------------------*/
/*                                                                  */
/* Version Date   SE History:                                       */
/* ------- ----   -- --------                                       */
/*    1.00 940705 FB Image Alchemy BIF Format, based on RAW         */
/*    1.01 940705 FB white space instead of nl seperators           */
/*    1.02 940806 FB creator/type, mac version alphabits            */
/*    1.03 950529 FB ginstance signature                            */
/*    1.04 980302 FB set BIF=name.bif to associate raw to bif       */
/*    1.05 031120 FB multiframe for raw movies                      */
/*                                                                  */
/*------------------------------------------------------------------*/
/*                                                                  */
/* Module Notes:                                                    */
/* -------------                                                    */
/* 8,24,32 bit                                                      */
/* Reentrant                                                        */
/* Line buffered                                                    */
/* Files: bifread.cpp bifwrite.cpp bifgimex.h                       */
/*                                                                  */
/*------------------------------------------------------------------*/
/*                                                                  */
/* Format Notes:                                                    */
/* -------------                                                    */
/* BIF files where are generated by Image Alchemy.                  */
/* Appendix E: http://www.handmadesw.com/Support/alchemy.pdf        */
/* http://www.oreilly.com/www/centers/gff/formats/hsibif/           */
/*                                                                  */
/* BIF stands for Binary Information File, and is a text file       */
/* script that describes a raw image file.                          */
/*                                                                  */
/* supported commands                                               */
/* BIF          required  identifies file as bif                    */
/* filename     optional  name of binary image file (assumes *.raw) */
/* width        required  width of image in pixels                  */
/* height       optional  height of image in pixels                 */
/* channels     optional  number of channels (1,3,or 4) 3 default   */
/* planes       optional  same as channels                          */
/* header       optional  size of header to skip in bytes           */
/* leftpadding  optional  number of left edge bytes to skip         */
/* rightpadding optional  number of right edge bytes to skip        */
/* order        optional  channel order g, rgb, bgr, argb, or bgra  */
/* upsidedown   optional  image is upside down                      */
/*                                                                  */
/* gimex extensions                                                 */
/* intel        extension image is intel byte ordering (for 16 bit) */
/* imageplanes  extension number of full image planes (psd style)   */
/* frames       extension number of frames                          */
/* framepadding extension number of bytes between frames            */
/*                                                                  */
/* unsupported commands                                             */
/* interleave             channel interleave 0=byte,1=line,2=plane  */
/* format                 ascii, group3, group4 (fax)               */
/* faxoptions             bitreversal                               */
/* bitspersample          may be 1, 8 or 16 (big endian by default) */
/* bitorder               msblsb or lsbmsb                          */
/* byteorder              motorola or intel                         */
/* signed                 by default 16 bit is unsigned             */
/*                                                                  */
/* Lines that begin with # are comments                             */
/* Numbers may be in hex, prefixed by 0x                            */
/* Filename may be specified more than once for planar files        */
/*   This feature is not supported by gimex                         */
/*                                                                  */
/* Example:                                                         */
/*                                                                  */
/* BIF                                                              */
/* filename astrnaut.tga                                            */
/* width      523                                                   */
/* height     478                                                   */
/* header      18                                                   */
/* channels     4                                                   */
/* order      bgra                                                  */
/* leftpadding  0                                                   */
/* rightpadding 0                                                   */
/* upsidedown                                                       */
/*                                                                  */
/* Extension: 2 channels changed meaning to 3 channels of 2 bytes,  */
/* for 16 bit formats. order still applies.  intel flag fetches     */
/* 2 bytes Intel style.  default is Motorola style.                 */
/*                                                                  */
/*------------------------------------------------------------------*/
/* END ABSTRACT */

#include <stdlib.h>
#include <string.h>
#include "gimex.h"
#include "bifgimex.h"

#define BIFMAXLINE 512

int BIFisdigit(int c)
{
    int ok=0;

    if (c>='0' && c<='9')
        ok = 1;
    return(ok);
}

/****************************************************************/
/*  Get a String                                                */
/*  - used by BIFatoi                                           */
/****************************************************************/

static char    *BIFgetstring(char *buf, char *s)
{
    char c;
    char n;
    char *d;
    char firstc;
    char *olds;
    int numerr=0;

    d = buf;
    firstc = *s;
    olds = s;
    if (firstc==(char)'"')
    {
        do
        {
            ++s;
            do
            {
                c = *s++;
                if (c==(char)'\\')
                {
                    c = *s++;
                    switch(c)
                    {
                        case 'n':   *d++ = (char)10;    break;
                        case 't':   *d++ = (char)'\t';  break;
                        case 'b':   *d++ = (char)'\b';  break;
                        case 'r':   *d++ = (char)13;    break;
                        case 'f':   *d++ = (char)'\f';  break;
                        case '\\':  *d++ = (char)'\\';  break;
                        case '<':   *d++ = (char)'<';   break;
                        case '\'':	*d++ = (char)'\'';   break;
                        case '"':	*d++ = (char)'"'; break;
                        default:
                            c -= '0';
                            if ((unsigned char) c < (unsigned char) 8)
                            {
                                n = c;
                                c = (char)(*s++-'0');
                                if ((unsigned char) c < (unsigned char) 8)
                                {
                                    n = (char)(n*8+c);
                                    c = (char)(*s++-'0');
                                    if ((unsigned char) c < (unsigned char) 8)
                                    {
                                        if ((unsigned char) n >= (unsigned char) 32)
                                        {
                                            numerr = 1;
                                            n = 0;
                                            c = 0;
                                        }
                                        n = (char)(n*8+c);
                                        ++s;
                                    }
                                }
                                *d++ = n;
                            }
                            if (c==(char)8 || c==(char)9)
                            {
                                numerr = 2; /*Error(loc+(int)(s-olds), BadDigit); */
                                n = 0;
                                c = 0;
                            }
                            --s;
                            c = '0';
                            break;
                    }
                }
                else
                {
                    *d++ = c;
                }
            } while (c!=(char)'\0' && c!=(char)'"');
        } while (*s==(char)'"');

        if (c==(char)'"')
            *--d = '\0';                        /* remove close quote or slash */
        else
            --s;
    }
    else
    {
        if (firstc==(char)'\'')
        {
            do
            {
                ++s;
                do
                {
                    c = *s++;
                    *d++ = c;
                } while (c!=(char)'\0' && c!=(char)'\'');
            } while (*s==(char)'\'');

            if (c==(char)'\'')
                *--d = '\0';                    /* remove close quote or slash */
            else
                --s;
        }
        else
        {
            if (firstc==(char)'\\')
            {
                ++s;
            }

            do
            {
                c = *s++;
                *d++ = c;
            } while (c>' ' && c!=(char)';' && c!=(char)',');

            if (c)
            {
                *--d = '\0';                    /* remove ; or , or white space */
                if (*(d-1)==(char)'\\')
                    *--d = '\0';                /* remove slash */
            }
            else
                --s;

            if (firstc==(char)'\\')
            {
                numerr = 3; /*Error(loc+(int)(s-olds), Undef); */
            }
        }
    }
    if (numerr)
        s = "";

    return(s);
}


/****************************************************************/
/*  Get a Single Number                                         */
/****************************************************************/

int BIFatoiradix(char *s,int Radix)
{
    char                c;
    char                *opstart;
    char                *numstart;
    int                 base;
    int                nextval;
    int                 stringlen;
    char                buf[BIFMAXLINE];
    int numerr=0;


    if (*s<='@' || Radix>10)
    {
        c = *s++;
        if (*s)                                 /* more than 1 char? */
        {
            opstart = s;
            if (c<'0')
            {
                base = 16;
                if (c!=(char)'$')                       /* hex  */
                {
                    switch (c)
                    {
                        case '"':				/* start a string */
                        case '\'':
                            nextval = 0L;
                            s = BIFgetstring(buf, s-1);
                            if (*(s-1)!=(char)'"' && *(s-1)!=(char)'\'')    /* No closing delimiter */
                            {
                                numerr = 4; /*Error(loc, NoStrEnd); */
                            }
                            else
                            {
                                stringlen = strlen(buf);
                                if (stringlen>4)
                                {
                                    numerr = 5; /*Error(loc, SizeErr); */
                                }
                                else
                                {
                                    s = buf;
                                    while (stringlen--)
                                    {
                                        nextval = (nextval<<8) | (unsigned char) *s++;
                                    }
                                }
                            }
                            return(nextval);

                        case '%':               /* binary */
                            base = 2;
                            break;

                        case '&':               /* hex (arm style) */
                            base = 16;
                            break;

                        case 0:                 /* end of term */
                            numerr = 6; /*Error(loc, OperErr); */
                            return(0L);
                            break;

                        default:
                            base = 0;
                            break;
                    }
                }
            }
            else
            {
                base = Radix;
                --s;

                if (c==(char)'0')                       /* hex 0x */
                {
                    if (*(s+1)==(char)'x')
                    {
                        if (Radix<34)
                        {
                            base = 16;
                            s += 2;
                        }
                    }
                }
                else
                {
                    if (c==(char)'@')                   /* octal */
                    {
                        base = 8;
                        ++s;
                    }
                }
            }

            /* base 10 is optimized the most */

            nextval = 0L;
            if (base==10)
            {
                c = (char)(*s++-'0');
                if ((unsigned char) c < (unsigned char) 10)
                {
                    numstart = s;
                    nextval = (int) ((unsigned char) c);

                    c = (char)(*s++-'0');
                    if ((unsigned char) c < (unsigned char) 10)
                    {
                        nextval = (int) ((int) nextval * 10 + (unsigned char) c);

                        c = (char)(*s++-'0');
                        if ((unsigned char) c < (unsigned char) 10)
                        {
                            nextval = (int) ((int) nextval * 10 + (unsigned char) c);

                            c = (char)(*s++-'0');
                            if ((unsigned char) c < (unsigned char) 10)
                            {
                                do
                                {
                                    nextval = nextval * 10 + (unsigned char) c;

                                    c = (char)(*s++-'0');
                                } while ((unsigned char) c < (unsigned char) 10);
                            }
                        }
                    }
                    if (!*(s-1))    /* did we finish the number? */
                    {
                        /* more than 10 digits? */

                        if ((int)(s-numstart) > 10)
                          numerr = 7; /*Error(loc, SizeErr); */

                        return(nextval);
                    }
                }
            }
            else if (base==16)
            {
                c = (char)(*s++-'0');
                if ((unsigned char) c >= (unsigned char) 10)
                    c = (char)(((c+'0')|' ')-'a'+10);

                if ((unsigned char) c < (unsigned char) 16)
                {
                    numstart = s;
                    do
                    {
                        do
                        {
                            do
                            {
                                nextval = (nextval<<4) + (unsigned char) c;

                                c = (char)(*s++-'0');
                            } while ((unsigned char) c < (unsigned char) 10);

                            c = (char)(((c+'0')|' ')-'a'+10);

                        } while ((unsigned char) c < (unsigned char) 16);

                        if (*(s-1)==(char)'_')
                        {
                            ++numstart;             /* adjust start for len calc */
                            c = (char)(*s++-'0');
                            if ((unsigned char) c >= (unsigned char) 10)
                                c = (char)(((c+'0')|' ')-'a'+10);
                        }
                    } while ((unsigned char) c < (unsigned char) 16);

                    if (!*(s-1))                    /* did we finish the number? */
                    {
                        /* more than 8 digits? */

                        if ((int)(s-numstart) > 8)
                            numerr = 8; /*Error(loc, SizeErr); */

                        return(nextval);
                    }
                }
            }
            else
            {
                numstart = s;
                c = *s++;
                while (c)
                {
                    c -= '0';
                    if ((unsigned char) c >= (unsigned char) 10)
                        c = (char)(((c+'0')|' ')-'a'+10);

                    if ((unsigned char) c >= (unsigned char) base)
                    {
                        if (*(s-1)==(char)'_')
                        {
                            ++numstart;             /* adjust start for len calc */
                            continue;
                        }
                        break;
                    }

                    if ((unsigned int) nextval<3200L)
                    {
                        nextval = (int) nextval * base + (unsigned char) c;
                    }
                    else
                    {
                        nextval = (nextval*base) + (unsigned char) c;
                    }
                    c = *s++;
                }

                if (!*(s-1))                    /* did we finish the number? */
                {
                    /* more than 32 digits? */

                    if ((int)(s-numstart-1) > 32)
                        numerr = 9; /*Error(loc, SizeErr); */

                    return(nextval);
                }
            }

            if (*(s-1)==(char)'$')                          /* set base */
            {
                if (nextval>=2 && nextval<=34)
                {
                    base = Radix;
                    Radix = nextval;
                    nextval = BIFatoiradix(s,Radix);
                    Radix = base;
                    return(nextval);
                }
            }

            s = opstart-1;
            if (BIFisdigit(*s))                 /* was first char a digit */
            {
                while (*s)                      /* skip to the end */
                {
                    ++s;
                }

                c = *(s-1);
                *(s-1) = '\0';
                switch (c|' ')
                {
                    case 'b':
                        base = Radix;
                        Radix = 2;
                        nextval = BIFatoiradix(opstart-1,Radix);
                        Radix = base;
                        *(s-1) = c;
                        return(nextval);

                    case 'd':
                        base = Radix;
                        Radix = 10;
                        nextval = BIFatoiradix(opstart-1,Radix);
                        Radix = base;
                        *(s-1) = c;
                        return(nextval);

                    case 'h':
                        base = Radix;
                        Radix = 16;
                        nextval = BIFatoiradix(opstart-1,Radix);
                        Radix = base;
                        *(s-1) = c;
                        return(nextval);

                    case 'o':
                    case 'q':
                        base = Radix;
                        Radix = 8;
                        nextval = BIFatoiradix(opstart-1,Radix);
                        Radix = base;
                        *(s-1) = c;
                        return(nextval);
                    default:;
                }
                *(s-1) = c;
                s = opstart-1;
            }
        }
        else
        {
            c -= '0';
            if ((unsigned char) c >= (unsigned char) 10)
                c = (char)(((c+'0')|' ')-'a'+10);

            if ((unsigned char) c < (unsigned char) Radix)
            {
                return((int) c );
            }
            c = *--s;
        }
    }

    if (*s)
    {
        if (*s==(char)'^')                          /* control character */
        {
            if (!*(s+2))
            {
                c = (char)((*(s+1)|' ')-'a'+1);
                if ((unsigned char) c < (unsigned char) 27)
                {
                    return((int) c );
                }
            }
        }
    }
    numerr = 10; /* Error(loc, Undef); */
    return(0L);
}

int BIFatoi(char *s)
{
    return(BIFatoiradix(s,10));
}


static char *BIFgetcharfield(char *parsebuf, char *biffile, char *field)
{
    unsigned char *s= (unsigned char *)biffile;
    int fieldlen;
    char *d=0;

    fieldlen = strlen(field);

    while (*s)
    {
        while (*s && *s<=' ')       /* white space */
            ++s;
        if (!strncmp((char *)s,field,fieldlen))
        {
            while (*s && *s>' ')    /* not white space (skip field name) */
                ++s;
            while (*s && *s<=' ')   /* white space (skip spaces after field name) */
                ++s;
            d = parsebuf;
            while (*s && *s>' ')    /* not white space (fetch argument) */
                *d++ = *s++;
            *d++ = 0;
            d = parsebuf;
            break;
        }
        while (*s && *s>' ')
            ++s;
    }
    return(d);
}

static int BIFgetintfield(char *biffile, char *field)
{
    char *s;
    int val=-1;
    char parsebuf[80];

    s = BIFgetcharfield(parsebuf,biffile,field);
    if (s)
    {
        if (*s)
            val = BIFatoi(s);
        else
            val = 0;
    }
    return(val);
}

/****************************************************************/
/*  Information Functions                                       */
/****************************************************************/

GABOUT * GCALL BIF_about(void)
{
    GABOUT *info;

    info = (GABOUT *) galloc(sizeof(GABOUT));
    if (info)
    {
        memset(info, 0, sizeof(GABOUT));

        info->signature       = GMAKEID('.','B','I','F');
        info->size            = sizeof(GABOUT);
        info->version         = GIMEX_VERSION;
        info->canimport       = 1;    /* supports importing */
        info->canexport       = 1;    /* supports exporting */
        info->importpacked    = 0;    /* supports importing packed */
        info->exportpacked    = 0;    /* supports exporting packed */
        info->import8         = 0;    /* supports importing 8 bit indexed */
        info->export8         = 0;    /* supports exporting 8 bit indexed */
        info->import32        = 1;    /* supports importing 32 bit direct rgb */
        info->export32        = 1;    /* supports exporting 32 bit direct rgb */
        info->multiframe      = 1;    /* supports multiple frames */
        info->multifile       = 0;    /* format requires additional files or resource fork */
        info->multisize       = 1;    /* supports each frame different size */
        info->framebuffer     = 0;    /* module requires memory to buffer entire frame */
        info->external        = 0;    /* module uses external resources */
        info->usesfile        = 1;    /* module is file based */
        info->maxframename    = 0;    /* max frame name character */
        info->defaultquality  = 100;  /* default pack quality */
        info->mactype[0]      = GMAKEID('.','B','I','F');   /* mac file system types used */
        strcpy(info->extensions[0], ".bif");                /* extensions */
        strcpy(info->authorstr,     "FrANK G. Barchard");   /* author */
        strcpy(info->versionstr,    "1.05");                /* version # */
        strcpy(info->shorttypestr,  "BIF");                 /* type */
        strcpy(info->wordtypestr,   "BIF");                 /* wordtype */
        strcpy(info->longtypestr,   "Binary Info File");    /* longtype */
    }
    return(info);
}

/* check for reasonable header: */
/* has 3 byte id BIF */

int GCALL BIF_is(GSTREAM *g)
{
    char bifheader[4];
    int ok=0;

#if !defined(__PPU__)
    char *bifname = getenv("BIF");
    if (bifname)
        g = gopen(bifname);
#endif

    gseek(g,(GPOS)0);
    if (gread(g,bifheader,4))
    {
        if (bifheader[0]=='B'
         && bifheader[1]=='I'
         && bifheader[2]=='F')
            ok = 100;
    }
#if !defined(__PPU__)
    if (bifname)
        gclose(g);
#endif

    return(ok);
}

/****************************************************************/
/*  Import Functions                                            */
/****************************************************************/

int GCALL BIF_open(GINSTANCE **gxptr, GSTREAM *g, const char *pathname,bool framecountflag)
{
    GINSTANCE *gx;
    int ok=0;

    gx = (GINSTANCE *) galloc(sizeof(GINSTANCE));
    if (gx)
    {
        memset(gx, 0, sizeof(GINSTANCE));
        gx->signature = GMAKEID('.','B','I','F');
        gx->size      = sizeof(GINSTANCE);
        gx->gstream   = g;
        gx->gref      = (char *)pathname;
        *gxptr        = gx;

#if !defined(__PPU__)
        char *bifname = getenv("BIF");
        if (bifname)
            g = gopen(bifname);
#endif

        if (g)
        {
            gseek(g,(GPOS)0);
            int flen = (int)glen(g);
            char *biffile = (char *) galloc(flen+1);
            if (biffile)
            {
                biffile[flen]=0;
                ok = gread(g,biffile,flen);

                if (ok)
                {
                    int frames = BIFgetintfield(biffile,"frames");
                    if (frames<=0)
                        frames = 1;
                    gx->frames    = frames;
                    ok = frames;
                }
                gfree(biffile);
            }
#if !defined(__PPU__)
            if (bifname)
                gclose(g);
#endif
        }
    }
    return(ok);
}


GINFO * GCALL BIF_info(GINSTANCE *gx, int framenum)
{
    GINFO *ginfo=0;
    int colours;
    int bpp;
    int alphabits;
    int i;
    int ok;
    char *fname;
    char parsebuf[80];
    char *biffile;
    int flen;
    GSTREAM *g;
    char *bifname = NULL;

    g = gx->gstream;
#if !defined(__PPU__)
    bifname = getenv("BIF");
    if (bifname)
        g = gopen(bifname);
#endif

    gseek(g,(GPOS)0);
    flen = (int)glen(g);
    biffile = (char *) galloc(flen+1);
    if (biffile)
    {
        biffile[flen]=0;
        ok = gread(g,biffile,flen);

#if !defined(__PPU__)
        if (bifname)
            gclose(g);
#endif

        if (ok)
        {
            g = gx->gstream;
            if (!bifname)
            {
                fname = BIFgetcharfield(parsebuf,biffile,"filename");
                if (!fname)
                {
                    strcpy(parsebuf,(const char *) gx->gref);
                    if (strchr(parsebuf,'.'))
                        *(strchr(parsebuf,'.')) = 0;
                    strcat(parsebuf,".raw");
                    fname = parsebuf;
                }

                g = gopen(fname);
            }

            if (g)
            {
                flen = (int)glen(g);

                if (!bifname)
                    gclose(g);

                ginfo = (GINFO *) galloc(sizeof(GINFO));
                if (ginfo)
                {
                    int width        = BIFgetintfield(biffile,"width");
                    int height       = BIFgetintfield(biffile,"height");
                    int header       = BIFgetintfield(biffile,"header");
                    int channels     = BIFgetintfield(biffile,"channels");
                    int leftpadding  = BIFgetintfield(biffile,"leftpadding");
                    int rightpadding = BIFgetintfield(biffile,"rightpadding");
                    int imageplanes  = BIFgetintfield(biffile,"imageplanes");
                    int frames       = BIFgetintfield(biffile,"frames");

                    if (channels<0)
                        channels = BIFgetintfield(biffile,"planes");
                    if (channels<0)     channels=3;
                    if (leftpadding<0)  leftpadding=0;
                    if (rightpadding<0) rightpadding=0;
                    if (imageplanes<0)  imageplanes=1;
                    if (frames<=0)      frames=1;
                    if (height<0)
                    {
                        if (header>=0)
                            height = (flen-header)/frames/(width*channels+leftpadding+rightpadding);
                        else
                            height = flen/frames/(width*channels+leftpadding+rightpadding);
                    }
                    if (header<0)
                    {
                        header = flen-((width*channels+leftpadding+rightpadding)*height);
                    }
                    bpp = channels*imageplanes*8;
                    alphabits = (bpp==32) ? 8:0;

                    memset(ginfo,0,sizeof(GINFO));

                    /* template for ginfo */

                    ginfo->signature        = GMAKEID('.','B','I','F');   /* Gimex type */
                    ginfo->size             = sizeof(GINFO);            /* Length of GINFO */
                    ginfo->version          = GIMEX_VERSION;
                    ginfo->framenum         = framenum; /* Current frame */
                    ginfo->subtype          = 0;        /* Internal format sub-type 0-default */
                    ginfo->packed           = 0;        /* Type of packing on original image. 0 none, 1 run, n other */
                    ginfo->quality          = 100;      /* Quality of lossy packing 0..100 */
                    ginfo->alphabits        = alphabits;/* Number of bits in alpha channel */
                    ginfo->redbits          = 8;        /* Number of bits in red channel */
                    ginfo->greenbits        = 8;        /* Number of bits in grreen channel */
                    ginfo->bluebits         = 8;        /* Number of bits in blue channel */
                    ginfo->centerx          = 0;        /* Center Point relative to upper left corner */
                    ginfo->centery          = 0;
                    ginfo->defaultx         = 0;        /* Default Coordinate Point */
                    ginfo->defaulty         = 0;
                    ginfo->numhotspots      = 0;        /* Number of Hot Spots defined */

                    ginfo->width            = width;    /* Width of bitmap in pixels */
                    ginfo->height           = height;   /* Height of bitmap in pixels */

                    colours = (bpp==8) ? 256:0;         /* Number of colours in palette */
                    if (colours)
                    {
                        ginfo->bpp              = 8;    /* Bits per pixel (8 or 32) */
                        ginfo->originalbpp      = 8;    /* Bits per pixel in original image (1 to 32) */
                        ginfo->numcolours  = colours;  /* Number of colours in original indexed palette */

                        for (i=0; i<colours; ++i)
                        {
                            ginfo->colourtbl[i].a = 255;
                            ginfo->colourtbl[i].r = (char)(i);
                            ginfo->colourtbl[i].g = (char)(i);
                            ginfo->colourtbl[i].b = (char)(i);
                        }
                    }
                    else
                    {
                        ginfo->bpp              = 32;   /* Bits per pixel (8 or 32) */
                        ginfo->originalbpp      = bpp;  /* Bits per pixel in original image (1 to 32) */
                        ginfo->numcolours  = 0;    /* Number of colours in original indexed palette */
                    }
                }
            }
        }
        gfree(biffile);
    }
    return(ginfo);
}

int GCALL BIF_read(GINSTANCE *gx, GINFO *ginfo, char *dest, int rowbytes)
{
    char *linebuf;
    char *lineptr;
    int bpl;
    int x;
    int y;
    int ok=0;
    char *d;
    int bpp;
    int imageplane;
    int planebytes;
    int i;
    int ai;
    int ri;
    int gi;
    int bi;
    char *fname;
    char parsebuf[80];
    char *biffile;
    char *order;
    int flen;
    GSTREAM *g;
    char *bifname = NULL;

    g = gx->gstream;
#if !defined(__PPU__)
    bifname = getenv("BIF");
    if (bifname)
        g = gopen(bifname);
#endif

    gseek(g,(GPOS)0);
    flen = (int)glen(g);
    biffile = (char *) galloc(flen+1);
    if (biffile)
    {
        biffile[flen]=0;
        ok = gread(g,biffile,flen);

#if !defined(__PPU__)
        if (bifname)
            gclose(g);
#endif

        if (ok)
        {
            g = gx->gstream;
            if (!bifname)
            {
                fname = BIFgetcharfield(parsebuf,biffile,"filename");
                if (!fname)
                {
                    strcpy(parsebuf,(const char *) gx->gref);
                    if (strchr(parsebuf,'.'))
                        *(strchr(parsebuf,'.')) = 0;
                    strcat(parsebuf,".raw");
                    fname = parsebuf;
                }

                g = gopen(fname);
            }

            if (g)
            {
                flen = (int)glen(g);

                int width = ginfo->width;
                int height = ginfo->height;

                int header       = BIFgetintfield(biffile,"header");
                int channels     = BIFgetintfield(biffile,"channels");
                int leftpadding  = BIFgetintfield(biffile,"leftpadding");
                int rightpadding = BIFgetintfield(biffile,"rightpadding");
                int upsidedown   = BIFgetintfield(biffile,"upsidedown");
                int intel        = BIFgetintfield(biffile,"intel");
                int imageplanes  = BIFgetintfield(biffile,"imageplanes");
                int framepadding = BIFgetintfield(biffile,"framepadding");
                if (channels<0)
                    channels = BIFgetintfield(biffile,"planes");
                if (channels<0)     channels=3;
                if (leftpadding<0)  leftpadding=0;
                if (rightpadding<0) rightpadding=0;
                if (imageplanes<0)  imageplanes=1;
                if (framepadding<0) framepadding=0;

                if (header<0)
                {
                    header = flen-((width*channels+leftpadding+rightpadding)*height*imageplanes);
                }

                bpp = channels*8;
                bpl = (width*channels+leftpadding+rightpadding);
                linebuf = (char *) galloc(bpl);
                planebytes = bpl*height;

                order = BIFgetcharfield(parsebuf,biffile,"order");
                if (order)
                {
                    ai = ri = gi = bi = 0;
                    for (i=0; order[i]; ++i)
                    {
                        switch (order[i]|' ')
                        {
                            default:
                            case 'a':   ai = i; break;
                            case 'r':   ri = i; break;
                            case 'g':   gi = i; break;
                            case 'b':   bi = i; break;
                        }
                    }
                }
                else
                {
                    ri = 0;
                    gi = 1;
                    bi = 2;
                    ai = 3;
                }


                if (linebuf)
                {
                    for (imageplane=0; imageplane<imageplanes; ++imageplane)
                    {
                        for (y=0; y<ginfo->height; ++y)
                        {
                            if (upsidedown>=0)
                                d = (dest+rowbytes*(ginfo->height-1-y));
                            else
                                d = (dest+rowbytes*y);
                            int framesize = ginfo->height*bpl+framepadding;


                            gseek(g,header + framesize*ginfo->framenum + y*bpl+imageplane*planebytes);
                            ok = gread(g,linebuf,bpl);
                            lineptr = linebuf+leftpadding;

                            for (x=0; x<ginfo->width; ++x)
                            {
                                switch (bpp)
                                {
                                    case 8:
                                        *d++ = *lineptr++;
                                        break;

                                    case 16:
                                        if (intel>=0)
                                            i = ggeti(lineptr,2);
                                        else
                                            i = ggetm(lineptr,2);

                                        if (ai==3)  /* rgba 5551 for N64 */
                                        {
                                            parsebuf[0] = (char)((((i>>11)&31)*255+16)/31);
                                            parsebuf[1] = (char)((((i>> 6)&31)*255+16)/31);
                                            parsebuf[2] = (char)((((i>> 1)&31)*255+16)/31);
                                        }
                                        else
                                        {
                                            parsebuf[0] = (char)((((i>>10)&31)*255+16)/31);
                                            parsebuf[1] = (char)((((i>> 5)&31)*255+16)/31);
                                            parsebuf[2] = (char)((((i>> 0)&31)*255+16)/31);
                                        }

                                        ((ARGB *) d)->a = 255;
                                        ((ARGB *) d)->r = parsebuf[ri];
                                        ((ARGB *) d)->g = parsebuf[gi];
                                        ((ARGB *) d)->b = parsebuf[bi];
                                        d += 4;
                                        lineptr += 2;
                                        break;

                                    case 24:
                                        ((ARGB *) d)->a = 255;
                                        ((ARGB *) d)->r = lineptr[ri];
                                        ((ARGB *) d)->g = lineptr[gi];
                                        ((ARGB *) d)->b = lineptr[bi];
                                        d += 4;
                                        lineptr += 3;
                                        break;

                                    case 32:
                                        ((ARGB *) d)->a = lineptr[ai];
                                        ((ARGB *) d)->r = lineptr[ri];
                                        ((ARGB *) d)->g = lineptr[gi];
                                        ((ARGB *) d)->b = lineptr[bi];
                                        d += 4;
                                        lineptr += 4;
                                        break;
                                }
                            }
                        }
                    }
                    gfree(linebuf);
                }
                if (!bifname)
                    gclose(g);
            }
        }
        gfree(biffile);
    }
    return(ok);
}

int GCALL BIF_close(GINSTANCE *gx)
{
    int ok=0;

    if (gx)
        ok = gfree((void *) gx);

    return(ok);
}

