/* Auto: make
*/

IMPORT BPTR SnapFile;
IMPORT BYTE TranspBuf[];

#define ID(a,b,c,d) ((a << 24L) | (b << 16L) | (c << 8L) | (d))

struct ckHdr {
    LONG ChunkName;
    LONG ChunkSize;
};

struct BitMapHeader
{
   UWORD w,h;
   WORD  x,y;
   UBYTE nPlanes;
   UBYTE masking;
   UBYTE compression;
   UBYTE pad1;
   UWORD transparentColor;
   UBYTE xAspect, yAspect;
   WORD  pageWidth, pageHeight;
};

struct ckHdr FORM = {
    ID('F','O','R','M'),
    0L
};
LONG TYPE = ID('I','L','B','M');

struct ckHdr BMHD = {
    ID('B','M','H','D'),
    sizeof(struct BitMapHeader)
};
struct BitMapHeader BMHdr = {
   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};

struct ckHdr CAMG = {
    ID('C','A','M','G'),
    4L
};
ULONG ViewMode = NULL;

struct ckHdr CMAP = {
    ID('C','M','A','P'),
    0L
};

struct ckHdr BODY = {
    ID('B','O','D','Y'),
    0L
};

UBYTE *buf;
WORD bufcnt;
ULONG totalsize;

WORD bumpmode;
#define BUMP_CALC   1
#define BUMP_SAVE   2

WORD bump(cnt, dataptr, size)
WORD cnt;
UBYTE *dataptr;
WORD size;
{
    REGISTER LONG tsize = size;
    if (tsize) {
        totalsize += tsize + 1;     /* Don't forget the count-byte */
    }
    if (bumpmode == BUMP_CALC) {    /* Just calculating? */
        return 1;                   /* Don't do the save */
    }
    if (bufcnt + tsize + 1 >= 4096 || tsize == 0) {
        if (Write(SnapFile, buf, (LONG)bufcnt) == -1L) {
            return 0;
        }
        bufcnt = 0;
    }
    buf[bufcnt++] = cnt;
    CopyMem((char *)dataptr, (char *)&buf[bufcnt], tsize);
    bufcnt += tsize;
    return 1;
}

ULONG WriteBody(BM)
struct BitMap *BM;
{
    WORD scanline, plane;
    REGISTER WORD bpr = BM->BytesPerRow;
    REGISTER WORD i, j;
    LONG offset = 0L;
    REGISTER UBYTE data;
    REGISTER UBYTE *bd;

    totalsize = 0L;

    if (!(buf = AllocMem(4096L, MEMF_PUBLIC))) {
        return NULL;
    }

    bufcnt = 0;
    for (scanline = 0; scanline < BM->Rows; ++scanline) {
        for (plane = 0; plane < BM->Depth; ++plane) {
            bd = BM->Planes[plane]+offset;
            i = 1;
            j = bpr - 1;
            data = bd[0];
            while (j) {
                if (bd[i] == data) {        /* Equal bytes? */
                    --i;                    /* First equal byte */
                    if (i > 0) {            /* Old "random" data to save */
                        if (!bump((WORD)(i - 1), bd, i)) {
                            return NULL;
                        }
                    }
                    bd = &bd[i];        /* Start of equal bytes */
                    i = 2;              /* 0 & 1 is equal       */
                    --j;
                    while (i < 128 && j && bd[i] == data) {
                        ++i;
                        --j;
                    }
                    if (!bump((WORD)-(i - 1), &bd[i - 1], 1)) {
                        return NULL;
                    }
                    goto new_block;
                } else {                    /* Not equal. Check block range */
                    if (i == 128) {         /* Block limit                  */
                        if (!bump((WORD)(i - 1), bd, i)) {
                            return NULL;
                        }
new_block:
                        bd = &bd[i];  /* Start new block              */
                        i = 0;
                        if (j == 0) {
                            break;
                        }
                    }
                }
                  /* Different byte or a new start */
                data = bd[i];               /* New possible equal       */
next_byte:
                ++i;
                --j;
            }
            if (i != 0) {                   /* Data to save */
                if (!bump((WORD)(i - 1), bd, i)) {
                    return NULL;
                }
            }
        }
        offset += BM->BytesPerRow;
    }
    if (!bump(0, NULL, 0)) {   /* Flush any bytes left if the buffer */
        return NULL;
    }
    FreeMem(buf, 4096L);
    return totalsize;
}

WORD SaveGS(GS)
struct GfxSnap *GS;
{
    ULONG BODYPos;
    UBYTE *oldtitle;

    oldtitle = GS->window->Title;
    SetWindowTitles(GS->window, "Saving...", NULL);

    bumpmode = BUMP_SAVE;

    BMHdr.w = GS->BM.BytesPerRow * 8;
    BMHdr.h = GS->height;
    BMHdr.x = BMHdr.y = 0;
    BMHdr.nPlanes = GS->depth;
    BMHdr.masking = 0;
    BMHdr.compression = 1;
    BMHdr.transparentColor = dectoint(TranspBuf);
    BMHdr.xAspect = BMHdr.xAspect = 1;
    BMHdr.pageWidth = GS->pagew;
    BMHdr.pageHeight = GS->pageh;
    ViewMode = GS->viewmode;

    CMAP.ChunkSize = (LONG)3 * (GS->viewmode & HAM ? 16 : 1L << GS->depth);

    if (Write(SnapFile, (char *)&FORM,
      (LONG)(sizeof(FORM) + sizeof(TYPE) +
             sizeof(BMHD) + sizeof(BMHdr) +
             sizeof(CAMG) + sizeof(ViewMode) +
             sizeof(CMAP))) == -1L) {
        return 0;
    }
    if (Write(SnapFile, (char *)&GS->rgb[0], CMAP.ChunkSize) == -1L) {
        return 0;
    }
    BODYPos = Seek(SnapFile, 0L, OFFSET_CURRENT);
    if (Write(SnapFile, (char *)&BODY, (LONG)sizeof(BODY)) == -1L) {
        return 0;
    }
    if (!(BODY.ChunkSize = WriteBody(&GS->BM))) {
        return 0;
    }
    FORM.ChunkSize = BODYPos - sizeof(FORM) + sizeof(BODY) + BODY.ChunkSize;
    if (FORM.ChunkSize & 1) {                       /* Odd size */
        if (Write(SnapFile, "X", 1L) == -1L) {
            return 0;
        }
        ++FORM.ChunkSize;
    }
    Seek(SnapFile, 0L, OFFSET_BEGINNING);
    Write(SnapFile, (char *)&FORM, (LONG)sizeof(FORM));
    Seek(SnapFile, BODYPos, OFFSET_BEGINNING);
    Write(SnapFile, (char *)&BODY, (LONG)sizeof(BODY));
    SetWindowTitles(GS->window, oldtitle, NULL);
    return 1;
}
