/*
 *
 * lbm2ppm.c
 *
 * convert a .lbm picture (PC DeluxePaintII) into
 * a .ppm picture (NetPBM package)
 *
 * usage: cat file.pbm | pbm2ppm >file.ppm
 *        or
 *        pbm2ppm file.pbm >file.ppm
 *        or
 *        pbm2ppm file.pbm |xv -
 *
 * compiles under Linux 2.0 and AmigaDos 3.0
 *
 * 0.71 -- January 1998 -- Laurent Clevy  (clevy@ie2.u-psud.fr)
 *      tested only on 8 bits and packed pictures
 * 
 *
 * Format note :
 * The Body's color indexes aren't interleaved, like in ILBM Bodies.
 * 'IL' in ILBM stands for 'InterLeaved', 'L' in 'LBM' should mean 'Linear'
 */


#include<stdio.h>
#include<stdlib.h>

#define get32(f) (fgetc(f)<<24) | (fgetc(f)<<16) | (fgetc(f)<<8) | fgetc(f)
#define get16(f) (fgetc(f)<<8) | fgetc(f)


#define CHK_FORM	0x464F524D
#define CHK_PBM		0x50424D20
#define CHK_BMHD	0x424D4844
#define CHK_CMAP	0x434D4150
#define CHK_DPPS	0x44505053
#define CHK_CRNG	0x43524E47
#define CHK_TINY	0x54494E59
#define CHK_BODY	0x424F4459

#define cmpNone         0
#define cmpByteRun1     1


int main(int argc, char *argv[])
{
    FILE* in;
    signed char sdata;
    unsigned long val;
    int depth, height, width;
    unsigned long len;
    int **cmap;
    int col;
    int i,j;
    unsigned int udata;
    unsigned char pack;

    if (argc==2) {
        in=(FILE*)fopen(argv[1],"rb");
        if (!in) {
            fprintf(stderr,"Can't open \"%s\"\n",argv[1]);
            exit(1);
        }
    }
    else if (argc==1) {
        in=stdin;
    }
    else {
        fprintf(stderr,"Usage: lbm2ppm [lbmfile]\n");
        exit(1);
    }

    val=get32(in);
    if (val!=CHK_FORM) {
        fprintf(stderr,"Group identifier (FORM) not found\n");
        exit(1);
    }
    get32(in);
    val=get32(in);
    if (val!=CHK_PBM) {
        fprintf(stderr,"Form type identifier (PBM ) not found\n");
        exit(1);
    }

    val=get32(in);
    while (!feof(in)) {

        switch(val) {

        case CHK_BMHD:

            len=get32(in);
            /* chunks length is rounded to the next even number */
            if (len%2)
                len++;

            width=get16(in);
            len-=2;
            height=get16(in);
            len-=2;
            get32(in);
            len-=4;

            depth=fgetc(in);
            len--;
            if (depth>8) {
                fprintf(stderr,"can't handle depth>8, aborting...\n");
                exit(1);
            }

            fgetc(in);
            len--;
            /* ByteRun1 compression ? */
            pack=fgetc(in);
            len--;

            while( len>0 && !feof(in)) {
                fgetc(in);
                len--;
            }

            if (len!=0) {
                fprintf(stderr,"BMHD chunk truncated\n");
                exit(1);
            }

            if (len!=0) {
                fprintf(stderr,"can't handle depths>8, aborting...\n");
                exit(1);
            }

            /* PPM binary header */
            puts("P6");
            printf("%d %d\n",width,height);
            printf("%d\n",(1<<depth)-1);

            fprintf(stderr,"size: %dx%dx%d %s\n",width,height,depth,
		    pack!=cmpNone ? "Packed":"UnPacked" );

            /* colormap allocation */
            cmap=(int**)malloc(sizeof(int*)*(1<<depth));
            if (!cmap) {
                fprintf(stderr, "can't malloc cmap\n");
                exit(1);
            }
            for(i=0; i<(1<<depth); i++) {
                cmap[i]=(int*)malloc(sizeof(int)*3);
                if (!cmap[i]) {
                    fprintf(stderr, "can't malloc cmap[]\n");
                    exit(1);
                }
            }
            break;

        case CHK_CMAP:
            len=get32(in);
            /* chunks length is rounded to the next even number */
            if (len%2)
                len++;

            /* fill colormap */
            col=0;
            cmap[col][0]=fgetc(in);
            cmap[col][1]=fgetc(in);
            cmap[col][2]=fgetc(in);
            len-=3;
            col++;
            while( len>0 && !feof(in)) {
                cmap[col][0]=fgetc(in);
                cmap[col][1]=fgetc(in);
                cmap[col][2]=fgetc(in);
                len-=3;
                col++;
            }
            if (len!=0) {
                fprintf(stderr,"CMAP chunk truncated\n");
                exit(1);
            }
            break;

        case CHK_TINY:
        case CHK_DPPS:
        case CHK_CRNG:
            len=get32(in);
            /* chunks length is rounded to the next even number */
            if (len%2)
                len++;

            fgetc(in);
            len--;
            while( len>0 && !feof(in)) {
                fgetc(in);
                len--;
            }

            if (len!=0) {
                fprintf(stderr,"chunk 0x%x truncated\n",val);
                exit(1);
            }
            break;

        case CHK_BODY:
            len=get32(in);
            /* chunks length is rounded to the next even number */
            if (len%2)
                len++;
 
            /* packed */
            if (pack==cmpByteRun1) {
                sdata=fgetc(in);
		while( len>0 && !feof(in)) {
		    len--;
		    /* ByteRun1 decompression */

		    /* [0..127]   : followed by n+1 bytes of data. */
		    if (sdata>=0) {
                        i=sdata+1;
			for(j=0; j<i; j++){
                            udata=fgetc(in);
			    len--;
			    if (!feof(in)) {
			        putchar( cmap[ udata ][0] );
				putchar( cmap[ udata ][1] );
				putchar( cmap[ udata ][2] );
                            }
                        }
                    }
                    /* [-1..-127] : followed by byte to be repeated (-n)+1 times*/
                    else if (sdata<=-1 && sdata>=-127) {
                        i=(-sdata)+1;
			udata=fgetc(in);
			len--;
			for(j=0; j<i; j++) {
                            putchar( cmap[ udata ][0] );
			    putchar( cmap[ udata ][1] );
			    putchar( cmap[ udata ][2] );
                        }
                    }
                    /* -128       : NOOP. */

                    sdata=fgetc(in);
                }
            }
            else if (pack==cmpNone) {
                udata=fgetc(in);
                len--;
                while( len>0 && !feof(in)) {
                    putchar( cmap[ udata ][0] );
		    putchar( cmap[ udata ][1] );
		    putchar( cmap[ udata ][2] );
		    udata=fgetc(in);
		    len--;
                }
            }
            else
                fprintf(stderr,"Unknown compression\n");

            if (len!=0) {
                fprintf(stderr,"chunk BODY truncated\n",val);
                exit(1);
            }
            break;

        default:
            fprintf(stderr,"unknown chunk : 0x%x\n",val);
        }
        val=get32(in);
    }

    for(i=0; i<(1<<depth); i++)
        free(cmap[i]);
    free(cmap);

    if (argc==2)
        fclose(in);
}


