2 THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
3 SOFTWARE CORPORATION ("PARALLAX"). PARALLAX, IN DISTRIBUTING THE CODE TO
4 END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
5 ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
6 IN USING, DISPLAYING, AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
7 SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
8 FREE PURPOSES. IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
9 CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES. THE END-USER UNDERSTANDS
10 AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
11 COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED.
15 static char rcsid[] = "$Id: iff.c,v 1.3 2001-01-22 13:37:46 bradleyb Exp $";
20 #define COMPRESS 1 //do the RLE or not? (for debugging mostly)
21 #define WRITE_TINY 0 //should we write a TINY chunk?
23 #define MIN_COMPRESS_WIDTH 65 //don't compress if less than this wide
32 //#include "nocfile.h"
36 //Internal constants and structures for this library
38 //Type values for bitmaps
49 #define mskHasTransparentColor 2
51 //Palette entry structure
52 typedef struct pal_entry {byte r,g,b;} pal_entry;
54 //structure of the header in the file
55 typedef struct iff_bitmap_header {
56 short w,h; //width and height of this bitmap
57 short x,y; //generally unused
58 short type; //see types above
59 short transparentcolor; //which color is transparent (if any)
60 short pagewidth,pageheight; //width & height of source screen
61 byte nplanes; //number of planes (8 for 256 color image)
62 byte masking,compression; //see constants above
63 byte xaspect,yaspect; //aspect ratio (usually 5/6)
64 pal_entry palette[256]; //the palette for this bitmap
65 ubyte *raw_data; //ptr to array of data
66 short row_size; //offset to next row
69 ubyte iff_transparent_color;
70 ubyte iff_has_transparency; // 0=no transparency, 1=iff_transparent_color is valid
72 typedef struct fake_file {
78 #define MIN(a,b) ((a<b)?a:b)
80 #define MAKE_SIG(a,b,c,d) (((long)(a)<<24)+((long)(b)<<16)+((c)<<8)+(d))
82 #define form_sig MAKE_SIG('F','O','R','M')
83 #define ilbm_sig MAKE_SIG('I','L','B','M')
84 #define body_sig MAKE_SIG('B','O','D','Y')
85 #define pbm_sig MAKE_SIG('P','B','M',' ')
86 #define bmhd_sig MAKE_SIG('B','M','H','D')
87 #define anhd_sig MAKE_SIG('A','N','H','D')
88 #define cmap_sig MAKE_SIG('C','M','A','P')
89 #define tiny_sig MAKE_SIG('T','I','N','Y')
90 #define anim_sig MAKE_SIG('A','N','I','M')
91 #define dlta_sig MAKE_SIG('D','L','T','A')
94 //void printsig(long s)
96 // char *t=(char *) &s;
98 ///* printf("%c%c%c%c",*(&s+3),*(&s+2),*(&s+1),s);*/
99 // printf("%c%c%c%c",t[3],t[2],t[1],t[0]);
103 long get_sig(FFILE *f)
107 // if ((s[3]=cfgetc(f))==EOF) return(EOF);
108 // if ((s[2]=cfgetc(f))==EOF) return(EOF);
109 // if ((s[1]=cfgetc(f))==EOF) return(EOF);
110 // if ((s[0]=cfgetc(f))==EOF) return(EOF);
113 if (f->position>=f->length) return EOF;
114 s[3] = f->data[f->position++];
115 if (f->position>=f->length) return EOF;
116 s[2] = f->data[f->position++];
117 if (f->position>=f->length) return EOF;
118 s[1] = f->data[f->position++];
119 if (f->position>=f->length) return EOF;
120 s[0] = f->data[f->position++];
122 if (f->position>=f->length) return EOF;
123 s[0] = f->data[f->position++];
124 if (f->position>=f->length) return EOF;
125 s[1] = f->data[f->position++];
126 if (f->position>=f->length) return EOF;
127 s[2] = f->data[f->position++];
128 if (f->position>=f->length) return EOF;
129 s[3] = f->data[f->position++];
132 return(*((long *) s));
135 int put_sig(long sig,FILE *f)
137 char *s = (char *) &sig;
142 return fputc(s[0],f);
146 char get_byte(FFILE *f)
149 return f->data[f->position++];
152 int put_byte(unsigned char c,FILE *f)
159 int get_word(FFILE *f)
166 if (f->position>=f->length) return EOF;
167 c1 = f->data[f->position++];
168 if (f->position>=f->length) return EOF;
169 c0 = f->data[f->position++];
171 if (c0==0xff) return(EOF);
173 return(((int)c1<<8) + c0);
177 int put_word(int n,FILE *f)
181 c0 = (n & 0xff00) >> 8;
185 return put_byte(c1,f);
188 int put_long(long n,FILE *f)
192 n0 = (int) ((n & 0xffff0000l) >> 16);
193 n1 = (int) (n & 0xffff);
196 return put_word(n1,f);
200 long get_long(FFILE *f)
202 unsigned char c0,c1,c2,c3;
209 if (f->position>=f->length) return EOF;
210 c3 = f->data[f->position++];
211 if (f->position>=f->length) return EOF;
212 c2 = f->data[f->position++];
213 if (f->position>=f->length) return EOF;
214 c1 = f->data[f->position++];
215 if (f->position>=f->length) return EOF;
216 c0 = f->data[f->position++];
218 //printf("get_long %x %x %x %x\n",c3,c2,c1,c0);
220 // if (c0==0xff) return(EOF);
222 return(((long)c3<<24) + ((long)c2<<16) + ((long)c1<<8) + c0);
226 int parse_bmhd(FFILE *ifile,long len,iff_bitmap_header *bmheader)
228 len++; /* so no "parm not used" warning */
230 // debug("parsing bmhd len=%ld\n",len);
232 bmheader->w = get_word(ifile);
233 bmheader->h = get_word(ifile);
234 bmheader->x = get_word(ifile);
235 bmheader->y = get_word(ifile);
237 bmheader->nplanes = get_byte(ifile);
238 bmheader->masking = get_byte(ifile);
239 bmheader->compression = get_byte(ifile);
240 get_byte(ifile); /* skip pad */
242 bmheader->transparentcolor = get_word(ifile);
243 bmheader->xaspect = get_byte(ifile);
244 bmheader->yaspect = get_byte(ifile);
246 bmheader->pagewidth = get_word(ifile);
247 bmheader->pageheight = get_word(ifile);
249 iff_transparent_color = bmheader->transparentcolor;
251 iff_has_transparency = 0;
253 if (bmheader->masking == mskHasTransparentColor)
254 iff_has_transparency = 1;
256 else if (bmheader->masking != mskNone && bmheader->masking != mskHasMask)
257 return IFF_UNKNOWN_MASK;
259 // debug("w,h=%d,%d x,y=%d,%d\n",w,h,x,y);
260 // debug("nplanes=%d, masking=%d ,compression=%d, transcolor=%d\n",nplanes,masking,compression,transparentcolor);
266 // the buffer pointed to by raw_data is stuffed with a pointer to decompressed pixel data
267 int parse_body(FFILE *ifile,long len,iff_bitmap_header *bmheader)
269 unsigned char *p=bmheader->raw_data;
272 int nn,wid_cnt,end_cnt,plane;
274 unsigned char *data_end;
283 end_pos = ifile->position + len;
287 if (bmheader->type == TYPE_PBM) {
290 } else if (bmheader->type == TYPE_ILBM) {
291 width = (bmheader->w+7)/8;
292 depth=bmheader->nplanes;
295 end_cnt = (width&1)?-1:0;
297 data_end = p + width*bmheader->h*depth;
299 if (bmheader->compression == cmpNone) { /* no compression */
302 for (y=bmheader->h;y;y--) {
304 // for (x=bmheader->w;x;x--) *p++=cfgetc(ifile);
305 // cfread(p, bmheader->w, 1, ifile);
308 for (x=0;x<width*depth;x++)
309 *p++=ifile->data[ifile->position++];
311 if (bmheader->masking == mskHasMask)
312 ifile->position += width; //skip mask!
314 // if (bmheader->w & 1) ignore = cfgetc(ifile);
315 if (bmheader->w & 1) ifile->position++;
318 //cnt = len - bmheader->h * ((bmheader->w+1)&~1);
321 else if (bmheader->compression == cmpByteRun1)
322 for (wid_cnt=width,plane=0;ifile->position<end_pos && p<data_end;) {
325 // if (old_cnt-cnt > 2048) {
330 if (wid_cnt == end_cnt) {
333 if ((bmheader->masking == mskHasMask && plane==depth+1) ||
334 (bmheader->masking != mskHasMask && plane==depth))
338 Assert(wid_cnt > end_cnt);
341 n=ifile->data[ifile->position++];
343 if (n >= 0) { // copy next n+1 bytes from source, they are not compressed
346 if (wid_cnt==-1) {--nn; Assert(width&1);}
347 if (plane==depth) //masking row
348 ifile->position += nn;
350 while (nn--) *p++=ifile->data[ifile->position++];
351 if (wid_cnt==-1) ifile->position++;
353 else if (n>=-127) { // next -n + 1 bytes are following byte
354 c=ifile->data[ifile->position++];
357 if (wid_cnt==-1) {--nn; Assert(width&1);}
358 if (plane!=depth) //not masking row
359 {memset(p,c,nn); p+=nn;}
363 if ((p-bmheader->raw_data) % width == 0)
366 Assert((p-bmheader->raw_data) - (width*row_count) < width);
371 if (p!=data_end) //if we don't have the whole bitmap...
372 return IFF_CORRUPT; //...the give an error
374 //Pretend we read the whole chuck, because if we didn't, it's because
375 //we didn't read the last mask like or the last rle record for padding
376 //or whatever and it's not important, because we check to make sure
377 //we got the while bitmap, and that's what really counts.
379 ifile->position = end_pos;
381 if (ignore) ignore++; // haha, suppress the evil warning message
386 //modify passed bitmap
387 int parse_delta(FFILE *ifile,long len,iff_bitmap_header *bmheader)
389 unsigned char *p=bmheader->raw_data;
391 long chunk_end = ifile->position + len;
393 get_long(ifile); //longword, seems to be equal to 4. Don't know what it is
395 for (y=0;y<bmheader->h;y++) {
397 int cnt = bmheader->w;
400 n_items = get_byte(ifile);
404 code = get_byte(ifile);
406 if (code==0) { //repeat
409 rep = get_byte(ifile);
410 val = get_byte(ifile);
418 else if (code > 0x80) { //skip
430 *p++ = get_byte(ifile);
446 if (ifile->position == chunk_end-1) //pad
449 if (ifile->position != chunk_end)
455 // the buffer pointed to by raw_data is stuffed with a pointer to bitplane pixel data
456 void skip_chunk(FFILE *ifile,long len)
462 //printf( "Skipping %d chunk\n", ilen );
464 ifile->position += ilen;
466 if (ifile->position >= ifile->length ) {
467 ifile->position = ifile->length;
471 // for (i=0; i<ilen; i++ )
472 // c = cfgetc(ifile);
473 //Assert(cfseek(ifile,ilen,SEEK_CUR)==0);
476 //read an ILBM or PBM file
477 // Pass pointer to opened file, and to empty bitmap_header structure, and form length
478 int iff_parse_ilbm_pbm(FFILE *ifile,long form_type,iff_bitmap_header *bmheader,int form_len,grs_bitmap *prev_bm)
482 long start_pos,end_pos;
484 start_pos = ifile->position;
485 end_pos = start_pos-4+form_len;
487 // printf(" %ld ",form_len);
488 // printsig(form_type);
491 if (form_type == pbm_sig)
492 bmheader->type = TYPE_PBM;
494 bmheader->type = TYPE_ILBM;
496 while ((ifile->position < end_pos) && (sig=get_sig(ifile)) != EOF) {
503 // printf(" %ld\n",len);
509 int save_w=bmheader->w,save_h=bmheader->h;
511 //printf("Parsing header\n");
513 ret = parse_bmhd(ifile,len,bmheader);
515 if (ret != IFF_NO_ERROR)
518 if (bmheader->raw_data) {
520 if (save_w != bmheader->w || save_h != bmheader->h)
521 return IFF_BM_MISMATCH;
526 MALLOC( bmheader->raw_data, ubyte, bmheader->w * bmheader->h );
527 if (!bmheader->raw_data)
537 if (!prev_bm) return IFF_CORRUPT;
539 bmheader->w = prev_bm->bm_w;
540 bmheader->h = prev_bm->bm_h;
541 bmheader->type = prev_bm->bm_type;
543 MALLOC( bmheader->raw_data, ubyte, bmheader->w * bmheader->h );
545 memcpy(bmheader->raw_data, prev_bm->bm_data, bmheader->w * bmheader->h );
546 skip_chunk(ifile,len);
552 int ncolors=(int) (len/3),cnum;
555 //printf("Parsing RGB map\n");
556 for (cnum=0;cnum<ncolors;cnum++) {
560 r = ifile->data[ifile->position++];
561 g = ifile->data[ifile->position++];
562 b = ifile->data[ifile->position++];
563 r >>= 2; bmheader->palette[cnum].r = r;
564 g >>= 2; bmheader->palette[cnum].g = g;
565 b >>= 2; bmheader->palette[cnum].b = b;
567 //if (len & 1) ignore = cfgetc(ifile);
568 if (len & 1 ) ifile->position++;
576 //printf("Parsing body\n");
577 if ((r=parse_body(ifile,len,bmheader))!=IFF_NO_ERROR)
584 if ((r=parse_delta(ifile,len,bmheader))!=IFF_NO_ERROR)
589 skip_chunk(ifile,len);
594 //if (ignore) ignore++;
596 if (ifile->position != start_pos-4+form_len)
599 return IFF_NO_ERROR; /* ok! */
602 //convert an ILBM file to a PBM file
603 int convert_ilbm_to_pbm(iff_bitmap_header *bmheader)
606 byte *new_data,*destptr,*rowptr;
607 int bytes_per_row,byteofs;
608 ubyte checkmask,newbyte,setbit;
610 MALLOC( new_data, byte, bmheader->w * bmheader->h );
611 if (new_data == NULL) return IFF_NO_MEM;
615 bytes_per_row = 2*((bmheader->w+15)/16);
617 for (y=0;y<bmheader->h;y++) {
619 rowptr = &bmheader->raw_data[y * bytes_per_row * bmheader->nplanes];
621 for (x=0,checkmask=0x80;x<bmheader->w;x++) {
625 for (p=newbyte=0,setbit=1;p<bmheader->nplanes;p++) {
627 if (rowptr[bytes_per_row * p + byteofs] & checkmask)
633 *destptr++ = newbyte;
635 if ((checkmask >>= 1) == 0) checkmask=0x80;
640 d_free(bmheader->raw_data);
641 bmheader->raw_data = new_data;
643 bmheader->type = TYPE_PBM;
648 #define INDEX_TO_15BPP(i) ((short)((((palptr[(i)].r/2)&31)<<10)+(((palptr[(i)].g/2)&31)<<5)+((palptr[(i)].b/2 )&31)))
650 int convert_rgb15(grs_bitmap *bm,iff_bitmap_header *bmheader)
657 palptr = bmheader->palette;
659 // if ((new_data = d_malloc(bm->bm_w * bm->bm_h * 2)) == NULL)
660 // {ret=IFF_NO_MEM; goto done;}
661 MALLOC(new_data, ushort, bm->bm_w * bm->bm_h * 2);
662 if (new_data == NULL)
665 for (y=0; y<bm->bm_h; y++) {
667 for (x=0; x<bmheader->w; x++)
668 new_data[newptr++] = INDEX_TO_15BPP(bmheader->raw_data[y*bmheader->w+x]);
672 d_free(bm->bm_data); //get rid of old-style data
673 bm->bm_data = (ubyte *) new_data; //..and point to new data
675 bm->bm_rowsize *= 2; //two bytes per row
681 //read in a entire file into a fake file structure
682 int open_fake_file(char *ifilename,FFILE *ffile)
687 //printf( "Reading %s\n", ifilename );
691 if ((ifile = cfopen(ifilename,"rb")) == NULL) return IFF_NO_FILE;
693 ffile->length = cfilelength(ifile);
695 MALLOC(ffile->data,ubyte,ffile->length);
697 if (cfread(ffile->data, 1, ffile->length, ifile) < ffile->length)
698 ret = IFF_READ_ERROR;
704 if (ifile) cfclose(ifile);
709 void close_fake_file(FFILE *f)
717 //copy an iff header structure to a grs_bitmap structure
718 void copy_iff_to_grs(grs_bitmap *bm,iff_bitmap_header *bmheader)
720 bm->bm_x = bm->bm_y = 0;
721 bm->bm_w = bmheader->w;
722 bm->bm_h = bmheader->h;
723 bm->bm_type = bmheader->type;
724 bm->bm_rowsize = bmheader->w;
725 bm->bm_data = bmheader->raw_data;
727 bm->bm_flags = bm->bm_handle = 0;
731 //if bm->bm_data is set, use it (making sure w & h are correct), else
732 //allocate the memory
733 int iff_parse_bitmap(FFILE *ifile,grs_bitmap *bm,int bitmap_type,byte *palette,grs_bitmap *prev_bm)
735 int ret; //return code
736 iff_bitmap_header bmheader;
740 bmheader.raw_data = bm->bm_data;
742 if (bmheader.raw_data) {
743 bmheader.w = bm->bm_w;
744 bmheader.h = bm->bm_h;
749 if (sig != form_sig) {
754 form_len = get_long(ifile);
756 form_type = get_sig(ifile);
758 if (form_type == anim_sig)
760 else if ((form_type == pbm_sig) || (form_type == ilbm_sig))
761 ret = iff_parse_ilbm_pbm(ifile,form_type,&bmheader,form_len,prev_bm);
763 ret = IFF_UNKNOWN_FORM;
765 if (ret != IFF_NO_ERROR) { //got an error parsing
766 if (bmheader.raw_data) d_free(bmheader.raw_data);
770 //If IFF file is ILBM, convert to PPB
771 if (bmheader.type == TYPE_ILBM) {
773 ret = convert_ilbm_to_pbm(&bmheader);
775 if (ret != IFF_NO_ERROR) goto done;
778 //Copy data from iff_bitmap_header structure into grs_bitmap structure
780 copy_iff_to_grs(bm,&bmheader);
783 if (palette) memcpy(palette,&bmheader.palette,sizeof(bmheader.palette));
785 // if (palette) memcpy(palette,&bmheader.palette, 768); // pal_entry is 4 bytes on mac
791 for (i = 0; i < 256; i++) {
792 *c++ = bmheader.palette[i].r;
793 *c++ = bmheader.palette[i].g;
794 *c++ = bmheader.palette[i].b;
799 //Now do post-process if required
801 if (bitmap_type == BM_RGB15) {
802 ret = convert_rgb15(bm,&bmheader);
803 if (ret != IFF_NO_ERROR) goto done;
812 //returns error codes - see IFF.H. see GR.H for bitmap_type
813 int iff_read_bitmap(char *ifilename,grs_bitmap *bm,int bitmap_type,ubyte *palette)
815 int ret; //return code
818 ret = open_fake_file(ifilename,&ifile); //read in entire file
819 if (ret != IFF_NO_ERROR) goto done;
823 ret = iff_parse_bitmap(&ifile,bm,bitmap_type,palette,NULL);
827 if (ifile.data) d_free(ifile.data);
829 close_fake_file(&ifile);
836 //like iff_read_bitmap(), but reads into a bitmap that already exists,
837 //without allocating memory for the bitmap.
838 int iff_read_into_bitmap(char *ifilename,grs_bitmap *bm,byte *palette)
840 int ret; //return code
843 ret = open_fake_file(ifilename,&ifile); //read in entire file
844 if (ret != IFF_NO_ERROR) goto done;
846 ret = iff_parse_bitmap(&ifile,bm,bm->bm_type,palette,NULL);
850 if (ifile.data) d_free(ifile.data);
852 close_fake_file(&ifile);
861 int write_bmhd(FILE *ofile,iff_bitmap_header *bitmap_header)
863 put_sig(bmhd_sig,ofile);
864 put_long((long) BMHD_SIZE,ofile);
866 put_word(bitmap_header->w,ofile);
867 put_word(bitmap_header->h,ofile);
868 put_word(bitmap_header->x,ofile);
869 put_word(bitmap_header->y,ofile);
871 put_byte(bitmap_header->nplanes,ofile);
872 put_byte(bitmap_header->masking,ofile);
873 put_byte(bitmap_header->compression,ofile);
874 put_byte(0,ofile); /* pad */
876 put_word(bitmap_header->transparentcolor,ofile);
877 put_byte(bitmap_header->xaspect,ofile);
878 put_byte(bitmap_header->yaspect,ofile);
880 put_word(bitmap_header->pagewidth,ofile);
881 put_word(bitmap_header->pageheight,ofile);
887 int write_pal(FILE *ofile,iff_bitmap_header *bitmap_header)
891 int n_colors = 1<<bitmap_header->nplanes;
893 put_sig(cmap_sig,ofile);
894 // put_long(sizeof(pal_entry) * n_colors,ofile);
895 put_long(3 * n_colors,ofile);
897 //printf("new write pal %d %d\n",3,n_colors);
899 for (i=0; i<256; i++) {
901 r = bitmap_header->palette[i].r * 4 + (bitmap_header->palette[i].r?3:0);
902 g = bitmap_header->palette[i].g * 4 + (bitmap_header->palette[i].g?3:0);
903 b = bitmap_header->palette[i].b * 4 + (bitmap_header->palette[i].b?3:0);
909 //printf("write pal %d %d\n",sizeof(pal_entry),n_colors);
910 // fwrite(bitmap_header->palette,sizeof(pal_entry),n_colors,ofile);
915 int rle_span(ubyte *dest,ubyte *src,int len)
917 int n,lit_cnt,rep_cnt;
918 ubyte last,*cnt_ptr,*dptr;
924 last=src[0]; lit_cnt=1;
926 for (n=1;n<len;n++) {
928 if (src[n] == last) {
933 while (n<len && rep_cnt<128 && src[n]==last) {n++; rep_cnt++;}
935 if (rep_cnt > 2 || lit_cnt < 2) {
937 if (lit_cnt > 1) {*cnt_ptr = lit_cnt-2; --dptr;} //save old lit count
938 *dptr++ = -(rep_cnt-1);
941 lit_cnt = (n<len)?1:0;
943 continue; //go to next char
951 cnt_ptr = dptr++; //save place for count
952 *dptr++=last; //store first char
955 *dptr++ = last = src[n];
957 if (lit_cnt == 127) {
971 *dptr++=last; //store first char
973 else if (lit_cnt > 1)
974 *cnt_ptr = lit_cnt-1;
979 #define EVEN(a) ((a+1)&0xfffffffel)
981 //returns length of chunk
982 int write_body(FILE *ofile,iff_bitmap_header *bitmap_header,int compression_on)
984 int w=bitmap_header->w,h=bitmap_header->h;
986 long len = EVEN(w) * h,newlen,total_len=0;
987 ubyte *p=bitmap_header->raw_data,*new_span;
990 put_sig(body_sig,ofile);
991 save_pos = ftell(ofile);
994 //if (! (new_span = d_malloc(bitmap_header->w+(bitmap_header->w/128+2)*2))) return IFF_NO_MEM;
995 MALLOC( new_span, ubyte, bitmap_header->w + (bitmap_header->w/128+2)*2);
996 if (new_span == NULL) return IFF_NO_MEM;
998 for (y=bitmap_header->h;y--;) {
1000 if (compression_on) {
1001 total_len += newlen = rle_span(new_span,p,bitmap_header->w+odd);
1002 fwrite(new_span,newlen,1,ofile);
1005 fwrite(p,bitmap_header->w+odd,1,ofile);
1007 p+=bitmap_header->row_size; //bitmap_header->w;
1010 if (compression_on) { //write actual data length
1011 Assert(fseek(ofile,save_pos,SEEK_SET)==0);
1012 put_long(total_len,ofile);
1013 Assert(fseek(ofile,total_len,SEEK_CUR)==0);
1014 if (total_len&1) fputc(0,ofile); //pad to even
1019 return ((compression_on) ? (EVEN(total_len)+8) : (len+8));
1024 //write a small representation of a bitmap. returns size
1025 int write_tiny(CFILE *ofile,iff_bitmap_header *bitmap_header,int compression_on)
1029 int len,total_len=0,newlen;
1031 ubyte *p = bitmap_header->raw_data;
1032 ubyte tspan[80],new_span[80*2];
1035 skip = max((bitmap_header->w+79)/80,(bitmap_header->h+63)/64);
1037 new_w = bitmap_header->w / skip;
1038 new_h = bitmap_header->h / skip;
1042 len = new_w * new_h + 4;
1044 put_sig(tiny_sig,ofile);
1045 save_pos = cftell(ofile);
1046 put_long(EVEN(len),ofile);
1048 put_word(new_w,ofile);
1049 put_word(new_h,ofile);
1051 for (y=0;y<new_h;y++) {
1052 for (x=xofs=0;x<new_w;x++,xofs+=skip)
1055 if (compression_on) {
1056 total_len += newlen = rle_span(new_span,tspan,new_w+odd);
1057 fwrite(new_span,newlen,1,ofile);
1060 fwrite(p,new_w+odd,1,ofile);
1062 p += skip * bitmap_header->row_size; //bitmap_header->w;
1066 if (compression_on) {
1067 Assert(cfseek(ofile,save_pos,SEEK_SET)==0);
1068 put_long(4+total_len,ofile);
1069 Assert(cfseek(ofile,4+total_len,SEEK_CUR)==0);
1070 if (total_len&1) cfputc(0,ofile); //pad to even
1073 return ((compression_on) ? (EVEN(total_len)+8+4) : (len+8));
1077 int write_pbm(FILE *ofile,iff_bitmap_header *bitmap_header,int compression_on) /* writes a pbm iff file */
1080 long raw_size = EVEN(bitmap_header->w) * bitmap_header->h;
1081 long body_size,tiny_size,pbm_size = 4 + BMHD_SIZE + 8 + EVEN(raw_size) + sizeof(pal_entry)*(1<<bitmap_header->nplanes)+8;
1084 //printf("write_pbm\n");
1086 put_sig(form_sig,ofile);
1087 save_pos = ftell(ofile);
1088 put_long(pbm_size+8,ofile);
1089 put_sig(pbm_sig,ofile);
1091 ret = write_bmhd(ofile,bitmap_header);
1092 if (ret != IFF_NO_ERROR) return ret;
1094 ret = write_pal(ofile,bitmap_header);
1095 if (ret != IFF_NO_ERROR) return ret;
1098 tiny_size = write_tiny(ofile,bitmap_header,compression_on);
1103 body_size = write_body(ofile,bitmap_header,compression_on);
1105 pbm_size = 4 + BMHD_SIZE + body_size + tiny_size + sizeof(pal_entry)*(1<<bitmap_header->nplanes)+8;
1107 Assert(fseek(ofile,save_pos,SEEK_SET)==0);
1108 put_long(pbm_size+8,ofile);
1109 Assert(fseek(ofile,pbm_size+8,SEEK_CUR)==0);
1115 //writes an IFF file from a grs_bitmap structure. writes palette if not null
1116 //returns error codes - see IFF.H.
1117 int iff_write_bitmap(char *ofilename,grs_bitmap *bm,ubyte *palette)
1120 iff_bitmap_header bmheader;
1124 if (bm->bm_type == BM_RGB15) return IFF_BAD_BM_TYPE;
1127 compression_on = (bm->bm_w>=MIN_COMPRESS_WIDTH);
1132 //fill in values in bmheader
1134 bmheader.x = bmheader.y = 0;
1135 bmheader.w = bm->bm_w;
1136 bmheader.h = bm->bm_h;
1137 bmheader.type = TYPE_PBM;
1138 bmheader.transparentcolor = iff_transparent_color;
1139 bmheader.pagewidth = bm->bm_w; //I don't think it matters what I write
1140 bmheader.pageheight = bm->bm_h;
1141 bmheader.nplanes = 8;
1142 bmheader.masking = mskNone;
1143 if (iff_has_transparency) {
1144 bmheader.masking |= mskHasTransparentColor;
1146 bmheader.compression = (compression_on?cmpByteRun1:cmpNone);
1148 bmheader.xaspect = bmheader.yaspect = 1; //I don't think it matters what I write
1149 bmheader.raw_data = bm->bm_data;
1150 bmheader.row_size = bm->bm_rowsize;
1152 if (palette) memcpy(&bmheader.palette,palette,256*3);
1154 //open file and write
1156 if ((ofile = fopen(ofilename,"wb")) == NULL) {ret=IFF_NO_FILE; goto done;}
1158 ret = write_pbm(ofile,&bmheader,compression_on);
1167 //read in many brushes. fills in array of pointers, and n_bitmaps.
1168 //returns iff error codes
1169 int iff_read_animbrush(char *ifilename,grs_bitmap **bm_list,int max_bitmaps,int *n_bitmaps,ubyte *palette)
1171 int ret; //return code
1173 iff_bitmap_header bmheader;
1179 ret = open_fake_file(ifilename,&ifile); //read in entire file
1180 if (ret != IFF_NO_ERROR) goto done;
1182 bmheader.raw_data = NULL;
1184 sig=get_sig(&ifile);
1185 form_len = get_long(&ifile);
1187 if (sig != form_sig) {
1192 form_type = get_sig(&ifile);
1194 if ((form_type == pbm_sig) || (form_type == ilbm_sig))
1195 ret = IFF_FORM_BITMAP;
1196 else if (form_type == anim_sig) {
1197 int anim_end = ifile.position + form_len - 4;
1199 while (ifile.position < anim_end && *n_bitmaps < max_bitmaps) {
1201 grs_bitmap *prev_bm;
1203 prev_bm = *n_bitmaps>0?bm_list[*n_bitmaps-1]:NULL;
1205 MALLOC(bm_list[*n_bitmaps] , grs_bitmap, 1 );
1207 bm_list[*n_bitmaps]->bm_data = NULL;
1209 ret = iff_parse_bitmap(&ifile,bm_list[*n_bitmaps],form_type,*n_bitmaps>0?NULL:palette,prev_bm);
1211 if (ret != IFF_NO_ERROR)
1217 if (ifile.position < anim_end) //ran out of room
1218 ret = IFF_TOO_MANY_BMS;
1222 ret = IFF_UNKNOWN_FORM;
1226 close_fake_file(&ifile);
1232 //text for error messges
1233 char error_messages[] = {
1235 "Not enough mem for loading or processing bitmap.\0"
1236 "IFF file has unknown FORM type.\0"
1237 "Not an IFF file.\0"
1238 "Cannot open file.\0"
1239 "Tried to save invalid type, like BM_RGB15.\0"
1240 "Bad data in file.\0"
1241 "ANIM file cannot be loaded with normal bitmap loader.\0"
1242 "Normal bitmap file cannot be loaded with anim loader.\0"
1243 "Array not big enough on anim brush read.\0"
1244 "Unknown mask type in bitmap header.\0"
1245 "Error reading file.\0"
1249 //function to return pointer to error message
1250 char *iff_errormsg(int error_number)
1252 char *p = error_messages;
1254 while (error_number--) {
1256 if (!p) return NULL;