/*
**  (C)2018-2020 Robert Szacki Software House
**
**  > Magazyn <
**
**  $Id: IFF.c,v 1.1 12/.0/.0 .2:.1:.2 Robert Exp $
*/

#include <dos/dos.h>
#include <libraries/iffparse.h>
#include <datatypes/pictureclass.h>
#include <datatypes/soundclass.h>
#include <exec/memory.h>

#include <clib/dos_protos.h>
#include <clib/iffparse_protos.h>
#include <clib/exec_protos.h>
#include <clib/graphics_protos.h>

#include "IFF.h"

struct IFFHandle* openIFF(STRPTR name, LONG mode)
{
    struct IFFHandle* iff;

    if (iff = AllocIFF())
    {
        BPTR f;

        if (iff->iff_Stream = f = Open(name, mode == IFFF_WRITE ? MODE_NEWFILE : MODE_OLDFILE))
        {
            LONG err;

            InitIFFasDOS(iff);
            if ((err = OpenIFF(iff, mode)) == 0)
            {
                return(iff);
            }
            Close(f);
        }
        FreeIFF(iff);
    }
    return(NULL);
}

void closeIFF(struct IFFHandle* iff)
{
    CloseIFF(iff);
    Close(iff->iff_Stream);
    FreeIFF(iff);
}

LONG scanIFF(struct IFFHandle* iff, ULONG type, ULONG* props, WORD count)
{
    LONG err;
    if ((err = PropChunks(iff, props, count)) == 0)
    {
        if ((err = StopChunk(iff, type, ID_BODY)) == 0)
        {
            if ((err = StopOnExit(iff, type, ID_FORM)) == 0)
            {
                if ((err = ParseIFF(iff, IFFPARSE_SCAN)) == 0
                    || err == IFFERR_EOC
                    || err == IFFERR_EOF)
                {
                    return(err);
                }
            }
        }
    }
    return(err);
}

struct IFFHandle* openILBM(STRPTR name, struct BitMapHeader* bmhd)
{
    struct IFFHandle* iff;
    ULONG props[] =
    {
        ID_ILBM, ID_BMHD,
        ID_ILBM, ID_CMAP
    };

    if (iff = openIFF(name, IFFF_READ))
    {
        if (scanIFF(iff, ID_ILBM, props, 2) == 0)
        {
            struct StoredProperty* sp;

            if (sp = FindProp(iff, ID_ILBM, ID_BMHD))
            {
                *bmhd = *(struct BitMapHeader* )sp->sp_Data;
                return(iff);
            }
        }
        closeIFF(iff);
    }
    return(NULL);
}

ULONG* loadColors(struct IFFHandle* iff, WORD* colorCount)
{
    struct StoredProperty* sp;

    if (sp = FindProp(iff, ID_ILBM, ID_CMAP))
    {
        WORD colors = sp->sp_Size / 3;
        ULONG* pal;

        if (pal = AllocMem((sp->sp_Size + 2) * sizeof(ULONG), MEMF_PUBLIC))
        {
            WORD i;
            UBYTE* cmap = sp->sp_Data;

            pal[0] = colors << 16;
            for (i = 1; i <= sp->sp_Size; i++)
            {
                UBYTE color = *cmap++;
                pal[i] = RGB(color);
            }
            pal[i] = 0L;
            *colorCount = colors;
            return(pal);
        }
    }
    return(NULL);
}

void freeColors(ULONG* pal, WORD colors)
{
    FreeMem(pal, ((colors * 3) + 2) * sizeof(ULONG));
}

BOOL unpackRow(BYTE** bufptr, LONG* sizeptr, BYTE** planeptr, WORD bpr, WORD cmp)
{
    BYTE *buf = *bufptr, *plane = *planeptr;
    LONG size = *sizeptr;

    if (cmp == cmpNone)
    {
        if (size < bpr)
        {
            return(FALSE);
        }
        size -= bpr;
        CopyMem(buf, plane, bpr);
        buf += bpr;
        plane += bpr;
    }
    else if (cmp == cmpByteRun1)
    {
        while (bpr > 0)
        {
            BYTE con;
            if (size < 1)
            {
                return(FALSE);
            }
            if ((con = *buf++) >= 0)
            {
                WORD count = con + 1;
                if (size < count || bpr < count)
                {
                    return(FALSE);
                }
                size -= count;
                bpr -= count;
                while (count-- > 0)
                {
                    *plane++ = *buf++;
                }
            }
            else if (con != -128)
            {
                WORD count = (-con) + 1;
                BYTE data;
                if (size < 1 || bpr < count)
                {
                    return(FALSE);
                }
                size--;
                bpr -= count;
                data = *buf++;
                while (count-- > 0)
                {
                    *plane++ = data;
                }
            }
        }
    }
    else
    {
        return(FALSE);
    }

    *bufptr = buf;
    *sizeptr = size;
    *planeptr = plane;
    return(TRUE);
}

BOOL unpackBitMap(BYTE *buf, LONG size, struct BitMap* bm, struct BitMapHeader* bmhd)
{
    WORD width  = bmhd->bmh_Width,
         height = bmhd->bmh_Height,
         depth  = bmhd->bmh_Depth,
         bpr    = RowBytes(width),
         cmp    = bmhd->bmh_Compression,
         msk    = bmhd->bmh_Masking,
         plane, row;

    PLANEPTR planes[9];

    for (plane = 0; plane < depth; plane++)
    {
        planes[plane] = bm->Planes[plane];
    }

    for (row = 0; row < height; row++)
    {
        for (plane = 0; plane < depth; plane++)
        {
            if (!unpackRow(&buf, &size, &planes[plane], bpr, cmp))
            {
                return(FALSE);
            }
        }
    }
    return(TRUE);
}

struct BitMap* loadBitMap(struct IFFHandle* iff, struct BitMapHeader* bmhd)
{
    struct BitMap* bm;

    if (bm = AllocBitMap(bmhd->bmh_Width, bmhd->bmh_Height, bmhd->bmh_Depth, 0, NULL))
    {
        struct ContextNode* cn;

        if ((cn = CurrentChunk(iff)) && cn->cn_Type == ID_ILBM && cn->cn_ID == ID_BODY)
        {
            BYTE* buf;
            LONG size = cn->cn_Size;
            BOOL success = FALSE;

            if (buf = AllocMem(size, MEMF_PUBLIC))
            {
                if (ReadChunkBytes(iff, buf, size) == size)
                {
                    success = unpackBitMap(buf, size, bm, bmhd);
                }
                FreeMem(buf, size);
                if (success)
                {
                    return(bm);
                }
            }
        }
        FreeBitMap(bm);
    }
    return(NULL);
}