/* THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX SOFTWARE CORPORATION ("PARALLAX"). PARALLAX, IN DISTRIBUTING THE CODE TO END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS IN USING, DISPLAYING, AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE FREE PURPOSES. IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES. THE END-USER UNDERSTANDS AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE. COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED. */ #ifdef RCS static char rcsid[] = "$Id: iff.c,v 1.2 2001-01-20 13:49:14 bradleyb Exp $"; #endif #include #define COMPRESS 1 //do the RLE or not? (for debugging mostly) #define WRITE_TINY 0 //should we write a TINY chunk? #define MIN_COMPRESS_WIDTH 65 //don't compress if less than this wide #include #include #include #include "u_mem.h" #include "iff.h" //#include "nocfile.h" #include "cfile.h" #include "error.h" //Internal constants and structures for this library //Type values for bitmaps #define TYPE_PBM 0 #define TYPE_ILBM 1 //Compression types #define cmpNone 0 #define cmpByteRun1 1 //Masking types #define mskNone 0 #define mskHasMask 1 #define mskHasTransparentColor 2 //Palette entry structure typedef struct pal_entry {byte r,g,b;} pal_entry; //structure of the header in the file typedef struct iff_bitmap_header { short w,h; //width and height of this bitmap short x,y; //generally unused short type; //see types above short transparentcolor; //which color is transparent (if any) short pagewidth,pageheight; //width & height of source screen byte nplanes; //number of planes (8 for 256 color image) byte masking,compression; //see constants above byte xaspect,yaspect; //aspect ratio (usually 5/6) pal_entry palette[256]; //the palette for this bitmap ubyte *raw_data; //ptr to array of data short row_size; //offset to next row } iff_bitmap_header; ubyte iff_transparent_color; ubyte iff_has_transparency; // 0=no transparency, 1=iff_transparent_color is valid typedef struct fake_file { ubyte *data; int position; int length; } FFILE; #define MIN(a,b) ((aposition>=f->length) return EOF; s[3] = f->data[f->position++]; if (f->position>=f->length) return EOF; s[2] = f->data[f->position++]; if (f->position>=f->length) return EOF; s[1] = f->data[f->position++]; if (f->position>=f->length) return EOF; s[0] = f->data[f->position++]; #else if (f->position>=f->length) return EOF; s[0] = f->data[f->position++]; if (f->position>=f->length) return EOF; s[1] = f->data[f->position++]; if (f->position>=f->length) return EOF; s[2] = f->data[f->position++]; if (f->position>=f->length) return EOF; s[3] = f->data[f->position++]; #endif return(*((long *) s)); } int put_sig(long sig,FILE *f) { char *s = (char *) &sig; fputc(s[3],f); fputc(s[2],f); fputc(s[1],f); return fputc(s[0],f); } char get_byte(FFILE *f) { //return cfgetc(f); return f->data[f->position++]; } int put_byte(unsigned char c,FILE *f) { return fputc(c,f); } int get_word(FFILE *f) { unsigned char c0,c1; // c1=cfgetc(f); // c0=cfgetc(f); if (f->position>=f->length) return EOF; c1 = f->data[f->position++]; if (f->position>=f->length) return EOF; c0 = f->data[f->position++]; if (c0==0xff) return(EOF); return(((int)c1<<8) + c0); } int put_word(int n,FILE *f) { unsigned char c0,c1; c0 = (n & 0xff00) >> 8; c1 = n & 0xff; put_byte(c0,f); return put_byte(c1,f); } int put_long(long n,FILE *f) { int n0,n1; n0 = (int) ((n & 0xffff0000l) >> 16); n1 = (int) (n & 0xffff); put_word(n0,f); return put_word(n1,f); } long get_long(FFILE *f) { unsigned char c0,c1,c2,c3; //c3=cfgetc(f); //c2=cfgetc(f); //c1=cfgetc(f); //c0=cfgetc(f); if (f->position>=f->length) return EOF; c3 = f->data[f->position++]; if (f->position>=f->length) return EOF; c2 = f->data[f->position++]; if (f->position>=f->length) return EOF; c1 = f->data[f->position++]; if (f->position>=f->length) return EOF; c0 = f->data[f->position++]; //printf("get_long %x %x %x %x\n",c3,c2,c1,c0); // if (c0==0xff) return(EOF); return(((long)c3<<24) + ((long)c2<<16) + ((long)c1<<8) + c0); } int parse_bmhd(FFILE *ifile,long len,iff_bitmap_header *bmheader) { len++; /* so no "parm not used" warning */ // debug("parsing bmhd len=%ld\n",len); bmheader->w = get_word(ifile); bmheader->h = get_word(ifile); bmheader->x = get_word(ifile); bmheader->y = get_word(ifile); bmheader->nplanes = get_byte(ifile); bmheader->masking = get_byte(ifile); bmheader->compression = get_byte(ifile); get_byte(ifile); /* skip pad */ bmheader->transparentcolor = get_word(ifile); bmheader->xaspect = get_byte(ifile); bmheader->yaspect = get_byte(ifile); bmheader->pagewidth = get_word(ifile); bmheader->pageheight = get_word(ifile); iff_transparent_color = bmheader->transparentcolor; iff_has_transparency = 0; if (bmheader->masking == mskHasTransparentColor) iff_has_transparency = 1; else if (bmheader->masking != mskNone && bmheader->masking != mskHasMask) return IFF_UNKNOWN_MASK; // debug("w,h=%d,%d x,y=%d,%d\n",w,h,x,y); // debug("nplanes=%d, masking=%d ,compression=%d, transcolor=%d\n",nplanes,masking,compression,transparentcolor); return IFF_NO_ERROR; } // the buffer pointed to by raw_data is stuffed with a pointer to decompressed pixel data int parse_body(FFILE *ifile,long len,iff_bitmap_header *bmheader) { unsigned char *p=bmheader->raw_data; int width,depth; signed char n; int nn,wid_cnt,end_cnt,plane; char ignore=0; unsigned char *data_end; int end_pos; int row_count=0; width=0; depth=0; end_pos = ifile->position + len; if (len&1) end_pos++; if (bmheader->type == TYPE_PBM) { width=bmheader->w; depth=1; } else if (bmheader->type == TYPE_ILBM) { width = (bmheader->w+7)/8; depth=bmheader->nplanes; } end_cnt = (width&1)?-1:0; data_end = p + width*bmheader->h*depth; if (bmheader->compression == cmpNone) { /* no compression */ int y; for (y=bmheader->h;y;y--) { int x; // for (x=bmheader->w;x;x--) *p++=cfgetc(ifile); // cfread(p, bmheader->w, 1, ifile); // p += bmheader->w; for (x=0;xdata[ifile->position++]; if (bmheader->masking == mskHasMask) ifile->position += width; //skip mask! // if (bmheader->w & 1) ignore = cfgetc(ifile); if (bmheader->w & 1) ifile->position++; } //cnt = len - bmheader->h * ((bmheader->w+1)&~1); } else if (bmheader->compression == cmpByteRun1) for (wid_cnt=width,plane=0;ifile->position 2048) { // printf("."); // old_cnt=cnt; // } if (wid_cnt == end_cnt) { wid_cnt = width; plane++; if ((bmheader->masking == mskHasMask && plane==depth+1) || (bmheader->masking != mskHasMask && plane==depth)) plane=0; } Assert(wid_cnt > end_cnt); //n=cfgetc(ifile); n=ifile->data[ifile->position++]; if (n >= 0) { // copy next n+1 bytes from source, they are not compressed nn = (int) n+1; wid_cnt -= nn; if (wid_cnt==-1) {--nn; Assert(width&1);} if (plane==depth) //masking row ifile->position += nn; else while (nn--) *p++=ifile->data[ifile->position++]; if (wid_cnt==-1) ifile->position++; } else if (n>=-127) { // next -n + 1 bytes are following byte c=ifile->data[ifile->position++]; nn = (int) -n+1; wid_cnt -= nn; if (wid_cnt==-1) {--nn; Assert(width&1);} if (plane!=depth) //not masking row {memset(p,c,nn); p+=nn;} } #ifndef NDEBUG if ((p-bmheader->raw_data) % width == 0) row_count++; Assert((p-bmheader->raw_data) - (width*row_count) < width); #endif } if (p!=data_end) //if we don't have the whole bitmap... return IFF_CORRUPT; //...the give an error //Pretend we read the whole chuck, because if we didn't, it's because //we didn't read the last mask like or the last rle record for padding //or whatever and it's not important, because we check to make sure //we got the while bitmap, and that's what really counts. ifile->position = end_pos; if (ignore) ignore++; // haha, suppress the evil warning message return IFF_NO_ERROR; } //modify passed bitmap int parse_delta(FFILE *ifile,long len,iff_bitmap_header *bmheader) { unsigned char *p=bmheader->raw_data; int y; long chunk_end = ifile->position + len; get_long(ifile); //longword, seems to be equal to 4. Don't know what it is for (y=0;yh;y++) { ubyte n_items; int cnt = bmheader->w; ubyte code; n_items = get_byte(ifile); while (n_items--) { code = get_byte(ifile); if (code==0) { //repeat ubyte rep,val; rep = get_byte(ifile); val = get_byte(ifile); cnt -= rep; if (cnt==-1) rep--; while (rep--) *p++ = val; } else if (code > 0x80) { //skip cnt -= (code-0x80); p += (code-0x80); if (cnt==-1) p--; } else { //literal cnt -= code; if (cnt==-1) code--; while (code--) *p++ = get_byte(ifile); if (cnt==-1) get_byte(ifile); } } if (cnt == -1) { if (!bmheader->w&1) return IFF_CORRUPT; } else if (cnt) return IFF_CORRUPT; } if (ifile->position == chunk_end-1) //pad ifile->position++; if (ifile->position != chunk_end) return IFF_CORRUPT; else return IFF_NO_ERROR; } // the buffer pointed to by raw_data is stuffed with a pointer to bitplane pixel data void skip_chunk(FFILE *ifile,long len) { //int c,i; int ilen; ilen = (len+1) & ~1; //printf( "Skipping %d chunk\n", ilen ); ifile->position += ilen; if (ifile->position >= ifile->length ) { ifile->position = ifile->length; } // for (i=0; iposition; end_pos = start_pos-4+form_len; // printf(" %ld ",form_len); // printsig(form_type); // printf("\n"); if (form_type == pbm_sig) bmheader->type = TYPE_PBM; else bmheader->type = TYPE_ILBM; while ((ifile->position < end_pos) && (sig=get_sig(ifile)) != EOF) { len=get_long(ifile); if (len==EOF) break; // printf(" "); // printsig(sig); // printf(" %ld\n",len); switch (sig) { case bmhd_sig: { int ret; int save_w=bmheader->w,save_h=bmheader->h; //printf("Parsing header\n"); ret = parse_bmhd(ifile,len,bmheader); if (ret != IFF_NO_ERROR) return ret; if (bmheader->raw_data) { if (save_w != bmheader->w || save_h != bmheader->h) return IFF_BM_MISMATCH; } else { MALLOC( bmheader->raw_data, ubyte, bmheader->w * bmheader->h ); if (!bmheader->raw_data) return IFF_NO_MEM; } break; } case anhd_sig: if (!prev_bm) return IFF_CORRUPT; bmheader->w = prev_bm->bm_w; bmheader->h = prev_bm->bm_h; bmheader->type = prev_bm->bm_type; MALLOC( bmheader->raw_data, ubyte, bmheader->w * bmheader->h ); memcpy(bmheader->raw_data, prev_bm->bm_data, bmheader->w * bmheader->h ); skip_chunk(ifile,len); break; case cmap_sig: { int ncolors=(int) (len/3),cnum; unsigned char r,g,b; //printf("Parsing RGB map\n"); for (cnum=0;cnumdata[ifile->position++]; g = ifile->data[ifile->position++]; b = ifile->data[ifile->position++]; r >>= 2; bmheader->palette[cnum].r = r; g >>= 2; bmheader->palette[cnum].g = g; b >>= 2; bmheader->palette[cnum].b = b; } //if (len & 1) ignore = cfgetc(ifile); if (len & 1 ) ifile->position++; break; } case body_sig: { int r; //printf("Parsing body\n"); if ((r=parse_body(ifile,len,bmheader))!=IFF_NO_ERROR) return r; break; } case dlta_sig: { int r; if ((r=parse_delta(ifile,len,bmheader))!=IFF_NO_ERROR) return r; break; } default: skip_chunk(ifile,len); break; } } //if (ignore) ignore++; if (ifile->position != start_pos-4+form_len) return IFF_CORRUPT; return IFF_NO_ERROR; /* ok! */ } //convert an ILBM file to a PBM file int convert_ilbm_to_pbm(iff_bitmap_header *bmheader) { int x,y,p; byte *new_data,*destptr,*rowptr; int bytes_per_row,byteofs; ubyte checkmask,newbyte,setbit; MALLOC( new_data, byte, bmheader->w * bmheader->h ); if (new_data == NULL) return IFF_NO_MEM; destptr = new_data; bytes_per_row = 2*((bmheader->w+15)/16); for (y=0;yh;y++) { rowptr = &bmheader->raw_data[y * bytes_per_row * bmheader->nplanes]; for (x=0,checkmask=0x80;xw;x++) { byteofs = x >> 3; for (p=newbyte=0,setbit=1;pnplanes;p++) { if (rowptr[bytes_per_row * p + byteofs] & checkmask) newbyte |= setbit; setbit <<= 1; } *destptr++ = newbyte; if ((checkmask >>= 1) == 0) checkmask=0x80; } } d_free(bmheader->raw_data); bmheader->raw_data = new_data; bmheader->type = TYPE_PBM; return IFF_NO_ERROR; } #define INDEX_TO_15BPP(i) ((short)((((palptr[(i)].r/2)&31)<<10)+(((palptr[(i)].g/2)&31)<<5)+((palptr[(i)].b/2 )&31))) int convert_rgb15(grs_bitmap *bm,iff_bitmap_header *bmheader) { ushort *new_data; int x,y; int newptr = 0; pal_entry *palptr; palptr = bmheader->palette; // if ((new_data = d_malloc(bm->bm_w * bm->bm_h * 2)) == NULL) // {ret=IFF_NO_MEM; goto done;} MALLOC(new_data, ushort, bm->bm_w * bm->bm_h * 2); if (new_data == NULL) return IFF_NO_MEM; for (y=0; ybm_h; y++) { for (x=0; xw; x++) new_data[newptr++] = INDEX_TO_15BPP(bmheader->raw_data[y*bmheader->w+x]); } d_free(bm->bm_data); //get rid of old-style data bm->bm_data = (ubyte *) new_data; //..and point to new data bm->bm_rowsize *= 2; //two bytes per row return IFF_NO_ERROR; } //read in a entire file into a fake file structure int open_fake_file(char *ifilename,FFILE *ffile) { CFILE *ifile; int ret; //printf( "Reading %s\n", ifilename ); ffile->data=NULL; if ((ifile = cfopen(ifilename,"rb")) == NULL) return IFF_NO_FILE; ffile->length = cfilelength(ifile); MALLOC(ffile->data,ubyte,ffile->length); if (cfread(ffile->data, 1, ffile->length, ifile) < ffile->length) ret = IFF_READ_ERROR; else ret = IFF_NO_ERROR; ffile->position = 0; if (ifile) cfclose(ifile); return ret; } void close_fake_file(FFILE *f) { if (f->data) d_free(f->data); f->data = NULL; } //copy an iff header structure to a grs_bitmap structure void copy_iff_to_grs(grs_bitmap *bm,iff_bitmap_header *bmheader) { bm->bm_x = bm->bm_y = 0; bm->bm_w = bmheader->w; bm->bm_h = bmheader->h; bm->bm_type = bmheader->type; bm->bm_rowsize = bmheader->w; bm->bm_data = bmheader->raw_data; bm->bm_flags = bm->bm_handle = 0; } //if bm->bm_data is set, use it (making sure w & h are correct), else //allocate the memory int iff_parse_bitmap(FFILE *ifile,grs_bitmap *bm,int bitmap_type,byte *palette,grs_bitmap *prev_bm) { int ret; //return code iff_bitmap_header bmheader; long sig,form_len; long form_type; bmheader.raw_data = bm->bm_data; if (bmheader.raw_data) { bmheader.w = bm->bm_w; bmheader.h = bm->bm_h; } sig=get_sig(ifile); if (sig != form_sig) { ret = IFF_NOT_IFF; goto done; } form_len = get_long(ifile); form_type = get_sig(ifile); if (form_type == anim_sig) ret = IFF_FORM_ANIM; else if ((form_type == pbm_sig) || (form_type == ilbm_sig)) ret = iff_parse_ilbm_pbm(ifile,form_type,&bmheader,form_len,prev_bm); else ret = IFF_UNKNOWN_FORM; if (ret != IFF_NO_ERROR) { //got an error parsing if (bmheader.raw_data) d_free(bmheader.raw_data); goto done; } //If IFF file is ILBM, convert to PPB if (bmheader.type == TYPE_ILBM) { ret = convert_ilbm_to_pbm(&bmheader); if (ret != IFF_NO_ERROR) goto done; } //Copy data from iff_bitmap_header structure into grs_bitmap structure copy_iff_to_grs(bm,&bmheader); #ifndef MACINTOSH if (palette) memcpy(palette,&bmheader.palette,sizeof(bmheader.palette)); #else // if (palette) memcpy(palette,&bmheader.palette, 768); // pal_entry is 4 bytes on mac if (palette) { ubyte *c; int i; c = palette; for (i = 0; i < 256; i++) { *c++ = bmheader.palette[i].r; *c++ = bmheader.palette[i].g; *c++ = bmheader.palette[i].b; } } #endif //Now do post-process if required if (bitmap_type == BM_RGB15) { ret = convert_rgb15(bm,&bmheader); if (ret != IFF_NO_ERROR) goto done; } done: return ret; } //returns error codes - see IFF.H. see GR.H for bitmap_type int iff_read_bitmap(char *ifilename,grs_bitmap *bm,int bitmap_type,ubyte *palette) { int ret; //return code FFILE ifile; ret = open_fake_file(ifilename,&ifile); //read in entire file if (ret != IFF_NO_ERROR) goto done; bm->bm_data = NULL; ret = iff_parse_bitmap(&ifile,bm,bitmap_type,palette,NULL); done: if (ifile.data) d_free(ifile.data); close_fake_file(&ifile); return ret; } //like iff_read_bitmap(), but reads into a bitmap that already exists, //without allocating memory for the bitmap. int iff_read_into_bitmap(char *ifilename,grs_bitmap *bm,byte *palette) { int ret; //return code FFILE ifile; ret = open_fake_file(ifilename,&ifile); //read in entire file if (ret != IFF_NO_ERROR) goto done; ret = iff_parse_bitmap(&ifile,bm,bm->bm_type,palette,NULL); done: if (ifile.data) d_free(ifile.data); close_fake_file(&ifile); return ret; } #define BMHD_SIZE 20 int write_bmhd(FILE *ofile,iff_bitmap_header *bitmap_header) { put_sig(bmhd_sig,ofile); put_long((long) BMHD_SIZE,ofile); put_word(bitmap_header->w,ofile); put_word(bitmap_header->h,ofile); put_word(bitmap_header->x,ofile); put_word(bitmap_header->y,ofile); put_byte(bitmap_header->nplanes,ofile); put_byte(bitmap_header->masking,ofile); put_byte(bitmap_header->compression,ofile); put_byte(0,ofile); /* pad */ put_word(bitmap_header->transparentcolor,ofile); put_byte(bitmap_header->xaspect,ofile); put_byte(bitmap_header->yaspect,ofile); put_word(bitmap_header->pagewidth,ofile); put_word(bitmap_header->pageheight,ofile); return IFF_NO_ERROR; } int write_pal(FILE *ofile,iff_bitmap_header *bitmap_header) { int i; int n_colors = 1<nplanes; put_sig(cmap_sig,ofile); // put_long(sizeof(pal_entry) * n_colors,ofile); put_long(3 * n_colors,ofile); //printf("new write pal %d %d\n",3,n_colors); for (i=0; i<256; i++) { unsigned char r,g,b; r = bitmap_header->palette[i].r * 4 + (bitmap_header->palette[i].r?3:0); g = bitmap_header->palette[i].g * 4 + (bitmap_header->palette[i].g?3:0); b = bitmap_header->palette[i].b * 4 + (bitmap_header->palette[i].b?3:0); fputc(r,ofile); fputc(g,ofile); fputc(b,ofile); } //printf("write pal %d %d\n",sizeof(pal_entry),n_colors); // fwrite(bitmap_header->palette,sizeof(pal_entry),n_colors,ofile); return IFF_NO_ERROR; } int rle_span(ubyte *dest,ubyte *src,int len) { int n,lit_cnt,rep_cnt; ubyte last,*cnt_ptr,*dptr; cnt_ptr=0; dptr = dest; last=src[0]; lit_cnt=1; for (n=1;n 2 || lit_cnt < 2) { if (lit_cnt > 1) {*cnt_ptr = lit_cnt-2; --dptr;} //save old lit count *dptr++ = -(rep_cnt-1); *dptr++ = last; last = src[n]; lit_cnt = (n 1) *cnt_ptr = lit_cnt-1; return dptr-dest; } #define EVEN(a) ((a+1)&0xfffffffel) //returns length of chunk int write_body(FILE *ofile,iff_bitmap_header *bitmap_header,int compression_on) { int w=bitmap_header->w,h=bitmap_header->h; int y,odd=w&1; long len = EVEN(w) * h,newlen,total_len=0; ubyte *p=bitmap_header->raw_data,*new_span; long save_pos; put_sig(body_sig,ofile); save_pos = ftell(ofile); put_long(len,ofile); //if (! (new_span = d_malloc(bitmap_header->w+(bitmap_header->w/128+2)*2))) return IFF_NO_MEM; MALLOC( new_span, ubyte, bitmap_header->w + (bitmap_header->w/128+2)*2); if (new_span == NULL) return IFF_NO_MEM; for (y=bitmap_header->h;y--;) { if (compression_on) { total_len += newlen = rle_span(new_span,p,bitmap_header->w+odd); fwrite(new_span,newlen,1,ofile); } else fwrite(p,bitmap_header->w+odd,1,ofile); p+=bitmap_header->row_size; //bitmap_header->w; } if (compression_on) { //write actual data length Assert(fseek(ofile,save_pos,SEEK_SET)==0); put_long(total_len,ofile); Assert(fseek(ofile,total_len,SEEK_CUR)==0); if (total_len&1) fputc(0,ofile); //pad to even } d_free(new_span); return ((compression_on) ? (EVEN(total_len)+8) : (len+8)); } #if WRITE_TINY //write a small representation of a bitmap. returns size int write_tiny(CFILE *ofile,iff_bitmap_header *bitmap_header,int compression_on) { int skip; int new_w,new_h; int len,total_len=0,newlen; int x,y,xofs,odd; ubyte *p = bitmap_header->raw_data; ubyte tspan[80],new_span[80*2]; long save_pos; skip = max((bitmap_header->w+79)/80,(bitmap_header->h+63)/64); new_w = bitmap_header->w / skip; new_h = bitmap_header->h / skip; odd = new_w & 1; len = new_w * new_h + 4; put_sig(tiny_sig,ofile); save_pos = cftell(ofile); put_long(EVEN(len),ofile); put_word(new_w,ofile); put_word(new_h,ofile); for (y=0;yrow_size; //bitmap_header->w; } if (compression_on) { Assert(cfseek(ofile,save_pos,SEEK_SET)==0); put_long(4+total_len,ofile); Assert(cfseek(ofile,4+total_len,SEEK_CUR)==0); if (total_len&1) cfputc(0,ofile); //pad to even } return ((compression_on) ? (EVEN(total_len)+8+4) : (len+8)); } #endif int write_pbm(FILE *ofile,iff_bitmap_header *bitmap_header,int compression_on) /* writes a pbm iff file */ { int ret; long raw_size = EVEN(bitmap_header->w) * bitmap_header->h; long body_size,tiny_size,pbm_size = 4 + BMHD_SIZE + 8 + EVEN(raw_size) + sizeof(pal_entry)*(1<nplanes)+8; long save_pos; //printf("write_pbm\n"); put_sig(form_sig,ofile); save_pos = ftell(ofile); put_long(pbm_size+8,ofile); put_sig(pbm_sig,ofile); ret = write_bmhd(ofile,bitmap_header); if (ret != IFF_NO_ERROR) return ret; ret = write_pal(ofile,bitmap_header); if (ret != IFF_NO_ERROR) return ret; #if WRITE_TINY tiny_size = write_tiny(ofile,bitmap_header,compression_on); #else tiny_size = 0; #endif body_size = write_body(ofile,bitmap_header,compression_on); pbm_size = 4 + BMHD_SIZE + body_size + tiny_size + sizeof(pal_entry)*(1<nplanes)+8; Assert(fseek(ofile,save_pos,SEEK_SET)==0); put_long(pbm_size+8,ofile); Assert(fseek(ofile,pbm_size+8,SEEK_CUR)==0); return ret; } //writes an IFF file from a grs_bitmap structure. writes palette if not null //returns error codes - see IFF.H. int iff_write_bitmap(char *ofilename,grs_bitmap *bm,ubyte *palette) { FILE *ofile; iff_bitmap_header bmheader; int ret; int compression_on; if (bm->bm_type == BM_RGB15) return IFF_BAD_BM_TYPE; #if COMPRESS compression_on = (bm->bm_w>=MIN_COMPRESS_WIDTH); #else compression_on = 0; #endif //fill in values in bmheader bmheader.x = bmheader.y = 0; bmheader.w = bm->bm_w; bmheader.h = bm->bm_h; bmheader.type = TYPE_PBM; bmheader.transparentcolor = iff_transparent_color; bmheader.pagewidth = bm->bm_w; //I don't think it matters what I write bmheader.pageheight = bm->bm_h; bmheader.nplanes = 8; bmheader.masking = mskNone; if (iff_has_transparency) { bmheader.masking |= mskHasTransparentColor; } bmheader.compression = (compression_on?cmpByteRun1:cmpNone); bmheader.xaspect = bmheader.yaspect = 1; //I don't think it matters what I write bmheader.raw_data = bm->bm_data; bmheader.row_size = bm->bm_rowsize; if (palette) memcpy(&bmheader.palette,palette,256*3); //open file and write if ((ofile = fopen(ofilename,"wb")) == NULL) {ret=IFF_NO_FILE; goto done;} ret = write_pbm(ofile,&bmheader,compression_on); done: fclose(ofile); return ret; } //read in many brushes. fills in array of pointers, and n_bitmaps. //returns iff error codes int iff_read_animbrush(char *ifilename,grs_bitmap **bm_list,int max_bitmaps,int *n_bitmaps,ubyte *palette) { int ret; //return code FFILE ifile; iff_bitmap_header bmheader; long sig,form_len; long form_type; *n_bitmaps=0; ret = open_fake_file(ifilename,&ifile); //read in entire file if (ret != IFF_NO_ERROR) goto done; bmheader.raw_data = NULL; sig=get_sig(&ifile); form_len = get_long(&ifile); if (sig != form_sig) { ret = IFF_NOT_IFF; goto done; } form_type = get_sig(&ifile); if ((form_type == pbm_sig) || (form_type == ilbm_sig)) ret = IFF_FORM_BITMAP; else if (form_type == anim_sig) { int anim_end = ifile.position + form_len - 4; while (ifile.position < anim_end && *n_bitmaps < max_bitmaps) { grs_bitmap *prev_bm; prev_bm = *n_bitmaps>0?bm_list[*n_bitmaps-1]:NULL; MALLOC(bm_list[*n_bitmaps] , grs_bitmap, 1 ); bm_list[*n_bitmaps]->bm_data = NULL; ret = iff_parse_bitmap(&ifile,bm_list[*n_bitmaps],form_type,*n_bitmaps>0?NULL:palette,prev_bm); if (ret != IFF_NO_ERROR) goto done; (*n_bitmaps)++; } if (ifile.position < anim_end) //ran out of room ret = IFF_TOO_MANY_BMS; } else ret = IFF_UNKNOWN_FORM; done: close_fake_file(&ifile); return ret; } //text for error messges char error_messages[] = { "No error.\0" "Not enough mem for loading or processing bitmap.\0" "IFF file has unknown FORM type.\0" "Not an IFF file.\0" "Cannot open file.\0" "Tried to save invalid type, like BM_RGB15.\0" "Bad data in file.\0" "ANIM file cannot be loaded with normal bitmap loader.\0" "Normal bitmap file cannot be loaded with anim loader.\0" "Array not big enough on anim brush read.\0" "Unknown mask type in bitmap header.\0" "Error reading file.\0" }; //function to return pointer to error message char *iff_errormsg(int error_number) { char *p = error_messages; while (error_number--) { if (!p) return NULL; p += strlen(p)+1; } return p; }