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

/* ABSTRACT */
/*------------------------------------------------------------------*/
/*                                                                  */
/*                        BIK - Raw Gimex                           */
/*                                                                  */
/*                    by FrANK G. Barchard, EAC                     */
/*                                                                  */
/*------------------------------------------------------------------*/
/*                                                                  */
/* Version Date   SE History:                                       */
/* ------- ----   -- --------                                       */
/*    1.00 030520 FB initial bink gimex using bink sdk              */
/*    1.01 030526 FB zero out destination frame                     */
/*    1.02 030527 FB BINKCOPYALL                                    */
/*                                                                  */
/*------------------------------------------------------------------*/
/*                                                                  */
/* Module Notes:                                                    */
/* -------------                                                    */
/* 32 bit                                                           */
/* Reentrant                                                        */
/* Files: bikread.cpp bikwrite.cpp bikgimex.h                       */
/*                                                                  */
/*------------------------------------------------------------------*/
/*                                                                  */
/* Format Notes:                                                    */
/* -------------                                                    */
/* BIK files generated by Bink compression tools                    */
/*                                                                  */
/*------------------------------------------------------------------*/
/* END ABSTRACT */

#include <string.h>
#include "gimex.h"
#include "bikgimex.h"
#include "windows.h"
#include "bink.h"

/****************************************************************/
/*  Internal Functions                                          */
/****************************************************************/

static HMODULE binkdllhandle=0;

HBINK (RADEXPLINK *GBinkOpen)(const char PTR4* name,U32 flags);
S32  (RADEXPLINK *GBinkCopyToBuffer)(HBINK bnk,void* dest,S32 destpitch,U32 destheight,U32 destx,U32 desty,U32 flags);
S32  (RADEXPLINK *GBinkDoFrame)(HBINK bnk);
void (RADEXPLINK *GBinkGoto)(HBINK bnk,U32 frame,S32 flags);  // use 1 for the first frame
void (RADEXPLINK *GBinkClose)(HBINK bnk);

static void BINK_freedll(void)
{
    if (binkdllhandle)
    {
        FreeLibrary(binkdllhandle);
        binkdllhandle = 0;
    }
}

int BINK_loaddll(void)
{
    int ok=1;

    if (!binkdllhandle)
    {
        ok = 0;
        binkdllhandle = LoadLibrary("binkw32.dll");
        if (binkdllhandle)
        {
            *(void **)&GBinkOpen         =  (void *) GetProcAddress(binkdllhandle,"_BinkOpen@8");
            *(void **)&GBinkCopyToBuffer =  (void *) GetProcAddress(binkdllhandle,"_BinkCopyToBuffer@28");
            *(void **)&GBinkDoFrame      =  (void *) GetProcAddress(binkdllhandle,"_BinkDoFrame@4");
            *(void **)&GBinkGoto         =  (void *) GetProcAddress(binkdllhandle,"_BinkGoto@12");
            *(void **)&GBinkClose        =  (void *) GetProcAddress(binkdllhandle,"_BinkClose@4");

            if (binkdllhandle && GBinkOpen)
                ok = 1;
            else
                BINK_freedll();
        }
    }
    return(ok);
}

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

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

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

        info->signature       = GMAKEID('B','I','N','K');
        info->size            = sizeof(GABOUT);
        info->version         = GIMEX_VERSION;
        info->canimport       = 1;    /* supports importing */
        info->canexport       = 0;    /* 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        = 0;    /* supports exporting 32 bit direct rgb */
        info->multiframe      = 1;    /* supports multiple frames */
        info->multifile       = 0;    /* format requires additional files or resource fork */
        info->multisize       = 0;    /* 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->globalpalette   = 0;    /* one palette per file */
        info->maxframename    = 0;    /* max frame name character */
        info->defaultquality  = 100;  /* default pack quality */
        info->movie           = 1;    /* movie file format */
        info->file64          = 0;    /* this format supports 64 bit file sizes */
        info->mactype[0]      = GMAKEID('B','I','N','K');   /* mac file system types used */
        strcpy(info->extensions[0], ".bik");                /* extensions */
        strcpy(info->authorstr,     "FrANK G. Barchard");   /* author */
        strcpy(info->versionstr,    "1.02");                /* version # */
        strcpy(info->shorttypestr,  "BIK");                 /* type */
        strcpy(info->wordtypestr,   "BIK");                 /* wordtype */
        strcpy(info->longtypestr,   "Bink");             /* longtype */


        {
            static int plugintested = 0;

            if (!plugintested)
            {
                plugintested = 1;
                if (!BINK_loaddll())
                {
                    plugintested = -1;
                }
            }
            if (plugintested<0)
            {
                info->canimport       = 0;    /* supports importing */
                info->importpacked    = 0;    /* supports importing packed */
                info->import8         = 0;    /* supports importing 8 bit indexed */
                info->import32        = 0;    /* supports importing 32 bit direct rgb */
                info->canexport       = 0;    /* supports exporting */
                info->exportpacked    = 0;    /* supports exporting packed */
                info->export8         = 0;    /* supports exporting 8 bit indexed */
                info->export32        = 0;    /* supports exporting 32 bit direct rgb */
            }
        }
    }
    return(info);
}

/* check for reasonable header: */
/* has 4 byte id 'BIKi' */
// lotr_both_bink_550.bik
// 00000: 42494b69 e0800101 86030000 64e40000  BIKi`.......dd..
// 00010: 86030000 80020000 c0010000 b50b0000  ........@...5...
// 00020: 64000000 00000000 00000000 490e0000  d...........I...
// 00030: 240f0000 ec3c0000 54ba0000 a8090100  $...l<..T:..(...
// 00040: 388f0100 f40e0200 cc460200 58bd0200  8...t...LF..X=..
// 00050: 24f80200 c0300300 b86a0300 34840300  $x..@0..8j..4...
// 00060: 4cee0300 3c7e0400 dcf10400 8c640500  Ln..<...\q...d..
// 00070: 24a20500 a4100600 344d0600 70c00600  $"..$...4M..p@..
// 00080: f0340700 047a0700 ec040800 64510800  p4...z..l...dQ..
// 00090: 88d90800 ec6d0900 40ae0900 70320a00  .Y..lm..@...p2..

int GCALL BIK_is(GSTREAM *g)
{
    unsigned char bikheader[4];
    int ok=0;

    gseek(g,(GPOS)0);
    if (gread(g,bikheader,4))
    {
        if (bikheader[0]=='B'
         && bikheader[1]=='I'
         && bikheader[2]=='K')
        {
            if (BINK_loaddll())
            {
                if (bikheader[3]=='i')
                    ok = 100;
                else
                    ok = 90;
            }
        }
    }
    return(ok);
}

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

int GCALL BIK_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));
        *gxptr = gx;
        gx->frames = 1;
        gx->signature=GMAKEID('B','I','N','K');
        gx->size=sizeof(GINSTANCE);
        gx->gstream = g;
        gseek(g,0);

        if (BINK_loaddll())
        {
            HBINK Bink = GBinkOpen( pathname, BINKALPHA | BINKNOSKIP | BINKPRELOADALL);

            gx->gref = (void *) Bink;
            if (gx->gref)
            {
                ok = gx->frames = Bink->Frames;
            }
        }
    }

    return(ok);
}

GINFO * GCALL BIK_info(GINSTANCE *gx, int framenum)
{
    HBINK Bink = (HBINK) gx->gref;
    GINFO *ginfo=0;

    if (framenum < gx->frames)
        ginfo = (GINFO *) galloc(sizeof(GINFO));
    if (ginfo)
    {
        memset(ginfo,0,sizeof(GINFO));

        /* template for ginfo */

        ginfo->signature        = GMAKEID('B','I','N','K');   /* 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        = 0;    /* 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  = Bink->Width;   /* Width of bitmap in pixels */
        ginfo->height = Bink->Height;  /* Height of bitmap in pixels */
        ginfo->bpp              = 32;   /* Bits per pixel (8 or 32) */
        ginfo->originalbpp      = 24;   /* Bits per pixel in original image (1 to 32) */
        ginfo->numcolours       = 0;    /* Number of colours in original indexed palette */
        ginfo->fps              = (float)Bink->FrameRate/Bink->FrameRateDiv;
    }

    return(ginfo);
}

int GCALL BIK_read(GINSTANCE *gx, GINFO *ginfo, char *dest, int rowbytes)
{
    int ok = 0;
    HBINK Bink = (HBINK) gx->gref;

    if (Bink)
    {
        GBinkGoto(Bink, ginfo->framenum+1, 0); // By default (using no flags), Bink performs a jump by seeking to the nearest key frame and then decompressing up to your specified frame.

        GBinkDoFrame( Bink );


        // convert YUV to RGBA
        GBinkCopyToBuffer( Bink,
                          dest,
                          rowbytes,
                          Bink->Height,
                          0,0,
                          BINKSURFACE32 | BINKCOPYALL);

#if 1
        // stamp alpha
        for (int y=0; y<ginfo->height; ++y)
        {
            ARGB *d = (ARGB *) ((char *)dest+y*rowbytes);
            for (int x=0; x<ginfo->width; ++x)
            {
                d->a = 255;
                ++d;
            }
        }
#endif

        ok = 1;
    }

    return ok;
}

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

    if (gx)
    {
        HBINK Bink = (HBINK) gx->gref;

        if ( Bink )
        {
            GBinkClose( Bink );
        }

        ok = gfree((void *) gx);
    }
    return(ok);
}

