1 /* $Id: iff.c,v 1.9 2004-08-28 23:17:45 schaffner Exp $ */
3 THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
4 SOFTWARE CORPORATION ("PARALLAX"). PARALLAX, IN DISTRIBUTING THE CODE TO
5 END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
6 ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
7 IN USING, DISPLAYING, AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
8 SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
9 FREE PURPOSES. IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
10 CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES. THE END-USER UNDERSTANDS
11 AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
12 COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED.
17 * Routines for reading and writing IFF files
26 static char rcsid[] = "$Id: iff.c,v 1.9 2004-08-28 23:17:45 schaffner Exp $";
29 #define COMPRESS 1 //do the RLE or not? (for debugging mostly)
30 #define WRITE_TINY 0 //should we write a TINY chunk?
32 #define MIN_COMPRESS_WIDTH 65 //don't compress if less than this wide
41 //#include "nocfile.h"
45 //Internal constants and structures for this library
47 //Type values for bitmaps
58 #define mskHasTransparentColor 2
60 //Palette entry structure
61 typedef struct pal_entry {
65 //structure of the header in the file
66 typedef struct iff_bitmap_header {
67 short w,h; //width and height of this bitmap
68 short x,y; //generally unused
69 short type; //see types above
70 short transparentcolor; //which color is transparent (if any)
71 short pagewidth,pageheight; //width & height of source screen
72 sbyte nplanes; //number of planes (8 for 256 color image)
73 sbyte masking,compression; //see constants above
74 sbyte xaspect,yaspect; //aspect ratio (usually 5/6)
75 pal_entry palette[256]; //the palette for this bitmap
76 ubyte *raw_data; //ptr to array of data
77 short row_size; //offset to next row
80 ubyte iff_transparent_color;
81 ubyte iff_has_transparency; // 0=no transparency, 1=iff_transparent_color is valid
83 typedef struct fake_file {
89 #define MIN(a,b) ((a<b)?a:b)
91 #define MAKE_SIG(a,b,c,d) (((long)(a)<<24)+((long)(b)<<16)+((c)<<8)+(d))
93 #define form_sig MAKE_SIG('F','O','R','M')
94 #define ilbm_sig MAKE_SIG('I','L','B','M')
95 #define body_sig MAKE_SIG('B','O','D','Y')
96 #define pbm_sig MAKE_SIG('P','B','M',' ')
97 #define bmhd_sig MAKE_SIG('B','M','H','D')
98 #define anhd_sig MAKE_SIG('A','N','H','D')
99 #define cmap_sig MAKE_SIG('C','M','A','P')
100 #define tiny_sig MAKE_SIG('T','I','N','Y')
101 #define anim_sig MAKE_SIG('A','N','I','M')
102 #define dlta_sig MAKE_SIG('D','L','T','A')
105 //void printsig(long s)
107 // char *t=(char *) &s;
109 ///* printf("%c%c%c%c",*(&s+3),*(&s+2),*(&s+1),s);*/
110 // printf("%c%c%c%c",t[3],t[2],t[1],t[0]);
114 long get_sig(FFILE *f)
118 // if ((s[3]=cfgetc(f))==EOF) return(EOF);
119 // if ((s[2]=cfgetc(f))==EOF) return(EOF);
120 // if ((s[1]=cfgetc(f))==EOF) return(EOF);
121 // if ((s[0]=cfgetc(f))==EOF) return(EOF);
123 #ifndef WORDS_BIGENDIAN
124 if (f->position>=f->length) return EOF;
125 s[3] = 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[1] = f->data[f->position++];
130 if (f->position>=f->length) return EOF;
131 s[0] = f->data[f->position++];
133 if (f->position>=f->length) return EOF;
134 s[0] = f->data[f->position++];
135 if (f->position>=f->length) return EOF;
136 s[1] = f->data[f->position++];
137 if (f->position>=f->length) return EOF;
138 s[2] = f->data[f->position++];
139 if (f->position>=f->length) return EOF;
140 s[3] = f->data[f->position++];
143 return(*((long *) s));
146 int put_sig(long sig,FILE *f)
148 char *s = (char *) &sig;
153 return fputc(s[0],f);
157 char get_byte(FFILE *f)
160 return f->data[f->position++];
163 int put_byte(unsigned char c,FILE *f)
168 int get_word(FFILE *f)
175 if (f->position>=f->length) return EOF;
176 c1 = f->data[f->position++];
177 if (f->position>=f->length) return EOF;
178 c0 = f->data[f->position++];
180 if (c0==0xff) return(EOF);
182 return(((int)c1<<8) + c0);
186 int put_word(int n,FILE *f)
190 c0 = (n & 0xff00) >> 8;
194 return put_byte(c1,f);
197 int put_long(long n,FILE *f)
201 n0 = (int) ((n & 0xffff0000l) >> 16);
202 n1 = (int) (n & 0xffff);
205 return put_word(n1,f);
209 long get_long(FFILE *f)
211 unsigned char c0,c1,c2,c3;
218 if (f->position>=f->length) return EOF;
219 c3 = f->data[f->position++];
220 if (f->position>=f->length) return EOF;
221 c2 = f->data[f->position++];
222 if (f->position>=f->length) return EOF;
223 c1 = f->data[f->position++];
224 if (f->position>=f->length) return EOF;
225 c0 = f->data[f->position++];
227 //printf("get_long %x %x %x %x\n",c3,c2,c1,c0);
229 // if (c0==0xff) return(EOF);
231 return(((long)c3<<24) + ((long)c2<<16) + ((long)c1<<8) + c0);
235 int parse_bmhd(FFILE *ifile,long len,iff_bitmap_header *bmheader)
237 len++; /* so no "parm not used" warning */
239 // debug("parsing bmhd len=%ld\n",len);
241 bmheader->w = get_word(ifile);
242 bmheader->h = get_word(ifile);
243 bmheader->x = get_word(ifile);
244 bmheader->y = get_word(ifile);
246 bmheader->nplanes = get_byte(ifile);
247 bmheader->masking = get_byte(ifile);
248 bmheader->compression = get_byte(ifile);
249 get_byte(ifile); /* skip pad */
251 bmheader->transparentcolor = get_word(ifile);
252 bmheader->xaspect = get_byte(ifile);
253 bmheader->yaspect = get_byte(ifile);
255 bmheader->pagewidth = get_word(ifile);
256 bmheader->pageheight = get_word(ifile);
258 iff_transparent_color = bmheader->transparentcolor;
260 iff_has_transparency = 0;
262 if (bmheader->masking == mskHasTransparentColor)
263 iff_has_transparency = 1;
265 else if (bmheader->masking != mskNone && bmheader->masking != mskHasMask)
266 return IFF_UNKNOWN_MASK;
268 // debug("w,h=%d,%d x,y=%d,%d\n",w,h,x,y);
269 // debug("nplanes=%d, masking=%d ,compression=%d, transcolor=%d\n",nplanes,masking,compression,transparentcolor);
275 // the buffer pointed to by raw_data is stuffed with a pointer to decompressed pixel data
276 int parse_body(FFILE *ifile,long len,iff_bitmap_header *bmheader)
278 unsigned char *p=bmheader->raw_data;
281 int nn,wid_cnt,end_cnt,plane;
283 unsigned char *data_end;
292 end_pos = ifile->position + len;
296 if (bmheader->type == TYPE_PBM) {
299 } else if (bmheader->type == TYPE_ILBM) {
300 width = (bmheader->w+7)/8;
301 depth=bmheader->nplanes;
304 end_cnt = (width&1)?-1:0;
306 data_end = p + width*bmheader->h*depth;
308 if (bmheader->compression == cmpNone) { /* no compression */
311 for (y=bmheader->h;y;y--) {
313 // for (x=bmheader->w;x;x--) *p++=cfgetc(ifile);
314 // cfread(p, bmheader->w, 1, ifile);
317 for (x=0;x<width*depth;x++)
318 *p++=ifile->data[ifile->position++];
320 if (bmheader->masking == mskHasMask)
321 ifile->position += width; //skip mask!
323 // if (bmheader->w & 1) ignore = cfgetc(ifile);
324 if (bmheader->w & 1) ifile->position++;
327 //cnt = len - bmheader->h * ((bmheader->w+1)&~1);
330 else if (bmheader->compression == cmpByteRun1)
331 for (wid_cnt=width,plane=0;ifile->position<end_pos && p<data_end;) {
334 // if (old_cnt-cnt > 2048) {
339 if (wid_cnt == end_cnt) {
342 if ((bmheader->masking == mskHasMask && plane==depth+1) ||
343 (bmheader->masking != mskHasMask && plane==depth))
347 Assert(wid_cnt > end_cnt);
350 n=ifile->data[ifile->position++];
352 if (n >= 0) { // copy next n+1 bytes from source, they are not compressed
355 if (wid_cnt==-1) {--nn; Assert(width&1);}
356 if (plane==depth) //masking row
357 ifile->position += nn;
359 while (nn--) *p++=ifile->data[ifile->position++];
360 if (wid_cnt==-1) ifile->position++;
362 else if (n>=-127) { // next -n + 1 bytes are following byte
363 c=ifile->data[ifile->position++];
366 if (wid_cnt==-1) {--nn; Assert(width&1);}
367 if (plane!=depth) //not masking row
368 {memset(p,c,nn); p+=nn;}
372 if ((p-bmheader->raw_data) % width == 0)
375 Assert((p-bmheader->raw_data) - (width*row_count) < width);
380 if (p!=data_end) //if we don't have the whole bitmap...
381 return IFF_CORRUPT; //...the give an error
383 //Pretend we read the whole chuck, because if we didn't, it's because
384 //we didn't read the last mask like or the last rle record for padding
385 //or whatever and it's not important, because we check to make sure
386 //we got the while bitmap, and that's what really counts.
388 ifile->position = end_pos;
390 if (ignore) ignore++; // haha, suppress the evil warning message
395 //modify passed bitmap
396 int parse_delta(FFILE *ifile,long len,iff_bitmap_header *bmheader)
398 unsigned char *p=bmheader->raw_data;
400 long chunk_end = ifile->position + len;
402 get_long(ifile); //longword, seems to be equal to 4. Don't know what it is
404 for (y=0;y<bmheader->h;y++) {
406 int cnt = bmheader->w;
409 n_items = get_byte(ifile);
413 code = get_byte(ifile);
415 if (code==0) { //repeat
418 rep = get_byte(ifile);
419 val = get_byte(ifile);
427 else if (code > 0x80) { //skip
439 *p++ = get_byte(ifile);
455 if (ifile->position == chunk_end-1) //pad
458 if (ifile->position != chunk_end)
464 // the buffer pointed to by raw_data is stuffed with a pointer to bitplane pixel data
465 void skip_chunk(FFILE *ifile,long len)
471 //printf( "Skipping %d chunk\n", ilen );
473 ifile->position += ilen;
475 if (ifile->position >= ifile->length ) {
476 ifile->position = ifile->length;
480 // for (i=0; i<ilen; i++ )
481 // c = cfgetc(ifile);
482 //Assert(cfseek(ifile,ilen,SEEK_CUR)==0);
485 //read an ILBM or PBM file
486 // Pass pointer to opened file, and to empty bitmap_header structure, and form length
487 int iff_parse_ilbm_pbm(FFILE *ifile,long form_type,iff_bitmap_header *bmheader,int form_len,grs_bitmap *prev_bm)
491 long start_pos,end_pos;
493 start_pos = ifile->position;
494 end_pos = start_pos-4+form_len;
496 // printf(" %ld ",form_len);
497 // printsig(form_type);
500 if (form_type == pbm_sig)
501 bmheader->type = TYPE_PBM;
503 bmheader->type = TYPE_ILBM;
505 while ((ifile->position < end_pos) && (sig=get_sig(ifile)) != EOF) {
512 // printf(" %ld\n",len);
518 int save_w=bmheader->w,save_h=bmheader->h;
520 //printf("Parsing header\n");
522 ret = parse_bmhd(ifile,len,bmheader);
524 if (ret != IFF_NO_ERROR)
527 if (bmheader->raw_data) {
529 if (save_w != bmheader->w || save_h != bmheader->h)
530 return IFF_BM_MISMATCH;
535 MALLOC( bmheader->raw_data, ubyte, bmheader->w * bmheader->h );
536 if (!bmheader->raw_data)
546 if (!prev_bm) return IFF_CORRUPT;
548 bmheader->w = prev_bm->bm_w;
549 bmheader->h = prev_bm->bm_h;
550 bmheader->type = prev_bm->bm_type;
552 MALLOC( bmheader->raw_data, ubyte, bmheader->w * bmheader->h );
554 memcpy(bmheader->raw_data, prev_bm->bm_data, bmheader->w * bmheader->h );
555 skip_chunk(ifile,len);
561 int ncolors=(int) (len/3),cnum;
564 //printf("Parsing RGB map\n");
565 for (cnum=0;cnum<ncolors;cnum++) {
569 r = ifile->data[ifile->position++];
570 g = ifile->data[ifile->position++];
571 b = ifile->data[ifile->position++];
572 r >>= 2; bmheader->palette[cnum].r = r;
573 g >>= 2; bmheader->palette[cnum].g = g;
574 b >>= 2; bmheader->palette[cnum].b = b;
576 //if (len & 1) ignore = cfgetc(ifile);
577 if (len & 1 ) ifile->position++;
585 //printf("Parsing body\n");
586 if ((r=parse_body(ifile,len,bmheader))!=IFF_NO_ERROR)
593 if ((r=parse_delta(ifile,len,bmheader))!=IFF_NO_ERROR)
598 skip_chunk(ifile,len);
603 //if (ignore) ignore++;
605 if (ifile->position != start_pos-4+form_len)
608 return IFF_NO_ERROR; /* ok! */
611 //convert an ILBM file to a PBM file
612 int convert_ilbm_to_pbm(iff_bitmap_header *bmheader)
615 sbyte *new_data, *destptr, *rowptr;
616 int bytes_per_row,byteofs;
617 ubyte checkmask,newbyte,setbit;
619 MALLOC(new_data, sbyte, bmheader->w * bmheader->h);
620 if (new_data == NULL) return IFF_NO_MEM;
624 bytes_per_row = 2*((bmheader->w+15)/16);
626 for (y=0;y<bmheader->h;y++) {
628 rowptr = (signed char *) &bmheader->raw_data[y * bytes_per_row * bmheader->nplanes];
630 for (x=0,checkmask=0x80;x<bmheader->w;x++) {
634 for (p=newbyte=0,setbit=1;p<bmheader->nplanes;p++) {
636 if (rowptr[bytes_per_row * p + byteofs] & checkmask)
642 *destptr++ = newbyte;
644 if ((checkmask >>= 1) == 0) checkmask=0x80;
649 d_free(bmheader->raw_data);
650 bmheader->raw_data = (unsigned char *) new_data;
652 bmheader->type = TYPE_PBM;
657 #define INDEX_TO_15BPP(i) ((short)((((palptr[(i)].r/2)&31)<<10)+(((palptr[(i)].g/2)&31)<<5)+((palptr[(i)].b/2 )&31)))
659 int convert_rgb15(grs_bitmap *bm,iff_bitmap_header *bmheader)
666 palptr = bmheader->palette;
668 // if ((new_data = d_malloc(bm->bm_w * bm->bm_h * 2)) == NULL)
669 // {ret=IFF_NO_MEM; goto done;}
670 MALLOC(new_data, ushort, bm->bm_w * bm->bm_h * 2);
671 if (new_data == NULL)
674 for (y=0; y<bm->bm_h; y++) {
676 for (x=0; x<bmheader->w; x++)
677 new_data[newptr++] = INDEX_TO_15BPP(bmheader->raw_data[y*bmheader->w+x]);
681 d_free(bm->bm_data); //get rid of old-style data
682 bm->bm_data = (ubyte *) new_data; //..and point to new data
684 bm->bm_rowsize *= 2; //two bytes per row
690 //read in a entire file into a fake file structure
691 int open_fake_file(char *ifilename,FFILE *ffile)
696 //printf( "Reading %s\n", ifilename );
700 if ((ifile = cfopen(ifilename,"rb")) == NULL) return IFF_NO_FILE;
702 ffile->length = cfilelength(ifile);
704 MALLOC(ffile->data,ubyte,ffile->length);
706 if (cfread(ffile->data, 1, ffile->length, ifile) < ffile->length)
707 ret = IFF_READ_ERROR;
713 if (ifile) cfclose(ifile);
718 void close_fake_file(FFILE *f)
726 //copy an iff header structure to a grs_bitmap structure
727 void copy_iff_to_grs(grs_bitmap *bm,iff_bitmap_header *bmheader)
729 bm->bm_x = bm->bm_y = 0;
730 bm->bm_w = bmheader->w;
731 bm->bm_h = bmheader->h;
732 bm->bm_type = bmheader->type;
733 bm->bm_rowsize = bmheader->w;
734 bm->bm_data = bmheader->raw_data;
736 bm->bm_flags = bm->bm_handle = 0;
740 //if bm->bm_data is set, use it (making sure w & h are correct), else
741 //allocate the memory
742 int iff_parse_bitmap(FFILE *ifile, grs_bitmap *bm, int bitmap_type, sbyte *palette, grs_bitmap *prev_bm)
744 int ret; //return code
745 iff_bitmap_header bmheader;
749 bmheader.raw_data = bm->bm_data;
751 if (bmheader.raw_data) {
752 bmheader.w = bm->bm_w;
753 bmheader.h = bm->bm_h;
758 if (sig != form_sig) {
762 form_len = get_long(ifile);
764 form_type = get_sig(ifile);
766 if (form_type == anim_sig)
768 else if ((form_type == pbm_sig) || (form_type == ilbm_sig))
769 ret = iff_parse_ilbm_pbm(ifile,form_type,&bmheader,form_len,prev_bm);
771 ret = IFF_UNKNOWN_FORM;
773 if (ret != IFF_NO_ERROR) { //got an error parsing
774 if (bmheader.raw_data) d_free(bmheader.raw_data);
778 //If IFF file is ILBM, convert to PPB
779 if (bmheader.type == TYPE_ILBM) {
781 ret = convert_ilbm_to_pbm(&bmheader);
783 if (ret != IFF_NO_ERROR)
787 //Copy data from iff_bitmap_header structure into grs_bitmap structure
789 copy_iff_to_grs(bm,&bmheader);
792 if (palette) memcpy(palette,&bmheader.palette,sizeof(bmheader.palette));
794 // if (palette) memcpy(palette,&bmheader.palette, 768); // pal_entry is 4 bytes on mac
800 for (i = 0; i < 256; i++) {
801 *c++ = bmheader.palette[i].r;
802 *c++ = bmheader.palette[i].g;
803 *c++ = bmheader.palette[i].b;
808 //Now do post-process if required
810 if (bitmap_type == BM_RGB15) {
811 ret = convert_rgb15(bm,&bmheader);
812 if (ret != IFF_NO_ERROR)
820 //returns error codes - see IFF.H. see GR.H for bitmap_type
821 int iff_read_bitmap(char *ifilename,grs_bitmap *bm,int bitmap_type,ubyte *palette)
823 int ret; //return code
826 ret = open_fake_file(ifilename,&ifile); //read in entire file
827 if (ret == IFF_NO_ERROR) {
829 ret = iff_parse_bitmap(&ifile,bm,bitmap_type,(signed char *) palette,NULL);
832 if (ifile.data) d_free(ifile.data);
834 close_fake_file(&ifile);
841 //like iff_read_bitmap(), but reads into a bitmap that already exists,
842 //without allocating memory for the bitmap.
843 int iff_read_into_bitmap(char *ifilename, grs_bitmap *bm, sbyte *palette)
845 int ret; //return code
848 ret = open_fake_file(ifilename,&ifile); //read in entire file
849 if (ret == IFF_NO_ERROR) {
850 ret = iff_parse_bitmap(&ifile,bm,bm->bm_type,palette,NULL);
853 if (ifile.data) d_free(ifile.data);
855 close_fake_file(&ifile);
864 int write_bmhd(FILE *ofile,iff_bitmap_header *bitmap_header)
866 put_sig(bmhd_sig,ofile);
867 put_long((long) BMHD_SIZE,ofile);
869 put_word(bitmap_header->w,ofile);
870 put_word(bitmap_header->h,ofile);
871 put_word(bitmap_header->x,ofile);
872 put_word(bitmap_header->y,ofile);
874 put_byte(bitmap_header->nplanes,ofile);
875 put_byte(bitmap_header->masking,ofile);
876 put_byte(bitmap_header->compression,ofile);
877 put_byte(0,ofile); /* pad */
879 put_word(bitmap_header->transparentcolor,ofile);
880 put_byte(bitmap_header->xaspect,ofile);
881 put_byte(bitmap_header->yaspect,ofile);
883 put_word(bitmap_header->pagewidth,ofile);
884 put_word(bitmap_header->pageheight,ofile);
890 int write_pal(FILE *ofile,iff_bitmap_header *bitmap_header)
894 int n_colors = 1<<bitmap_header->nplanes;
896 put_sig(cmap_sig,ofile);
897 // put_long(sizeof(pal_entry) * n_colors,ofile);
898 put_long(3 * n_colors,ofile);
900 //printf("new write pal %d %d\n",3,n_colors);
902 for (i=0; i<256; i++) {
904 r = bitmap_header->palette[i].r * 4 + (bitmap_header->palette[i].r?3:0);
905 g = bitmap_header->palette[i].g * 4 + (bitmap_header->palette[i].g?3:0);
906 b = bitmap_header->palette[i].b * 4 + (bitmap_header->palette[i].b?3:0);
912 //printf("write pal %d %d\n",sizeof(pal_entry),n_colors);
913 // fwrite(bitmap_header->palette,sizeof(pal_entry),n_colors,ofile);
918 int rle_span(ubyte *dest,ubyte *src,int len)
920 int n,lit_cnt,rep_cnt;
921 ubyte last,*cnt_ptr,*dptr;
927 last=src[0]; lit_cnt=1;
929 for (n=1;n<len;n++) {
931 if (src[n] == last) {
936 while (n<len && rep_cnt<128 && src[n]==last) {n++; rep_cnt++;}
938 if (rep_cnt > 2 || lit_cnt < 2) {
940 if (lit_cnt > 1) {*cnt_ptr = lit_cnt-2; --dptr;} //save old lit count
941 *dptr++ = -(rep_cnt-1);
944 lit_cnt = (n<len)?1:0;
946 continue; //go to next char
954 cnt_ptr = dptr++; //save place for count
955 *dptr++=last; //store first char
958 *dptr++ = last = src[n];
960 if (lit_cnt == 127) {
974 *dptr++=last; //store first char
976 else if (lit_cnt > 1)
977 *cnt_ptr = lit_cnt-1;
982 #define EVEN(a) ((a+1)&0xfffffffel)
984 //returns length of chunk
985 int write_body(FILE *ofile,iff_bitmap_header *bitmap_header,int compression_on)
987 int w=bitmap_header->w,h=bitmap_header->h;
989 long len = EVEN(w) * h,newlen,total_len=0;
990 ubyte *p=bitmap_header->raw_data,*new_span;
993 put_sig(body_sig,ofile);
994 save_pos = ftell(ofile);
997 //if (! (new_span = d_malloc(bitmap_header->w+(bitmap_header->w/128+2)*2))) return IFF_NO_MEM;
998 MALLOC( new_span, ubyte, bitmap_header->w + (bitmap_header->w/128+2)*2);
999 if (new_span == NULL) return IFF_NO_MEM;
1001 for (y=bitmap_header->h;y--;) {
1003 if (compression_on) {
1004 total_len += newlen = rle_span(new_span,p,bitmap_header->w+odd);
1005 fwrite(new_span,newlen,1,ofile);
1008 fwrite(p,bitmap_header->w+odd,1,ofile);
1010 p+=bitmap_header->row_size; //bitmap_header->w;
1013 if (compression_on) { //write actual data length
1014 Assert(fseek(ofile,save_pos,SEEK_SET)==0);
1015 put_long(total_len,ofile);
1016 Assert(fseek(ofile,total_len,SEEK_CUR)==0);
1017 if (total_len&1) fputc(0,ofile); //pad to even
1022 return ((compression_on) ? (EVEN(total_len)+8) : (len+8));
1027 //write a small representation of a bitmap. returns size
1028 int write_tiny(CFILE *ofile,iff_bitmap_header *bitmap_header,int compression_on)
1032 int len,total_len=0,newlen;
1034 ubyte *p = bitmap_header->raw_data;
1035 ubyte tspan[80],new_span[80*2];
1038 skip = max((bitmap_header->w+79)/80,(bitmap_header->h+63)/64);
1040 new_w = bitmap_header->w / skip;
1041 new_h = bitmap_header->h / skip;
1045 len = new_w * new_h + 4;
1047 put_sig(tiny_sig,ofile);
1048 save_pos = cftell(ofile);
1049 put_long(EVEN(len),ofile);
1051 put_word(new_w,ofile);
1052 put_word(new_h,ofile);
1054 for (y=0;y<new_h;y++) {
1055 for (x=xofs=0;x<new_w;x++,xofs+=skip)
1058 if (compression_on) {
1059 total_len += newlen = rle_span(new_span,tspan,new_w+odd);
1060 fwrite(new_span,newlen,1,ofile);
1063 fwrite(p,new_w+odd,1,ofile);
1065 p += skip * bitmap_header->row_size; //bitmap_header->w;
1069 if (compression_on) {
1070 Assert(cfseek(ofile,save_pos,SEEK_SET)==0);
1071 put_long(4+total_len,ofile);
1072 Assert(cfseek(ofile,4+total_len,SEEK_CUR)==0);
1073 if (total_len&1) cfputc(0,ofile); //pad to even
1076 return ((compression_on) ? (EVEN(total_len)+8+4) : (len+8));
1080 int write_pbm(FILE *ofile,iff_bitmap_header *bitmap_header,int compression_on) /* writes a pbm iff file */
1083 long raw_size = EVEN(bitmap_header->w) * bitmap_header->h;
1084 long body_size,tiny_size,pbm_size = 4 + BMHD_SIZE + 8 + EVEN(raw_size) + sizeof(pal_entry)*(1<<bitmap_header->nplanes)+8;
1087 //printf("write_pbm\n");
1089 put_sig(form_sig,ofile);
1090 save_pos = ftell(ofile);
1091 put_long(pbm_size+8,ofile);
1092 put_sig(pbm_sig,ofile);
1094 ret = write_bmhd(ofile,bitmap_header);
1095 if (ret != IFF_NO_ERROR) return ret;
1097 ret = write_pal(ofile,bitmap_header);
1098 if (ret != IFF_NO_ERROR) return ret;
1101 tiny_size = write_tiny(ofile,bitmap_header,compression_on);
1106 body_size = write_body(ofile,bitmap_header,compression_on);
1108 pbm_size = 4 + BMHD_SIZE + body_size + tiny_size + sizeof(pal_entry)*(1<<bitmap_header->nplanes)+8;
1110 Assert(fseek(ofile,save_pos,SEEK_SET)==0);
1111 put_long(pbm_size+8,ofile);
1112 Assert(fseek(ofile,pbm_size+8,SEEK_CUR)==0);
1118 //writes an IFF file from a grs_bitmap structure. writes palette if not null
1119 //returns error codes - see IFF.H.
1120 int iff_write_bitmap(char *ofilename,grs_bitmap *bm,ubyte *palette)
1123 iff_bitmap_header bmheader;
1127 if (bm->bm_type == BM_RGB15) return IFF_BAD_BM_TYPE;
1130 compression_on = (bm->bm_w>=MIN_COMPRESS_WIDTH);
1135 //fill in values in bmheader
1137 bmheader.x = bmheader.y = 0;
1138 bmheader.w = bm->bm_w;
1139 bmheader.h = bm->bm_h;
1140 bmheader.type = TYPE_PBM;
1141 bmheader.transparentcolor = iff_transparent_color;
1142 bmheader.pagewidth = bm->bm_w; //I don't think it matters what I write
1143 bmheader.pageheight = bm->bm_h;
1144 bmheader.nplanes = 8;
1145 bmheader.masking = mskNone;
1146 if (iff_has_transparency) {
1147 bmheader.masking |= mskHasTransparentColor;
1149 bmheader.compression = (compression_on?cmpByteRun1:cmpNone);
1151 bmheader.xaspect = bmheader.yaspect = 1; //I don't think it matters what I write
1152 bmheader.raw_data = bm->bm_data;
1153 bmheader.row_size = bm->bm_rowsize;
1155 if (palette) memcpy(&bmheader.palette,palette,256*3);
1157 //open file and write
1159 if ((ofile = fopen(ofilename,"wb")) == NULL) {
1162 ret = write_pbm(ofile,&bmheader,compression_on);
1170 //read in many brushes. fills in array of pointers, and n_bitmaps.
1171 //returns iff error codes
1172 int iff_read_animbrush(char *ifilename,grs_bitmap **bm_list,int max_bitmaps,int *n_bitmaps,ubyte *palette)
1174 int ret; //return code
1176 iff_bitmap_header bmheader;
1182 ret = open_fake_file(ifilename,&ifile); //read in entire file
1183 if (ret != IFF_NO_ERROR) goto done;
1185 bmheader.raw_data = NULL;
1187 sig=get_sig(&ifile);
1188 form_len = get_long(&ifile);
1190 if (sig != form_sig) {
1195 form_type = get_sig(&ifile);
1197 if ((form_type == pbm_sig) || (form_type == ilbm_sig))
1198 ret = IFF_FORM_BITMAP;
1199 else if (form_type == anim_sig) {
1200 int anim_end = ifile.position + form_len - 4;
1202 while (ifile.position < anim_end && *n_bitmaps < max_bitmaps) {
1204 grs_bitmap *prev_bm;
1206 prev_bm = *n_bitmaps>0?bm_list[*n_bitmaps-1]:NULL;
1208 MALLOC(bm_list[*n_bitmaps] , grs_bitmap, 1 );
1209 bm_list[*n_bitmaps]->bm_data = NULL;
1211 ret = iff_parse_bitmap(&ifile,bm_list[*n_bitmaps],form_type,*n_bitmaps>0?NULL:(signed char *)palette,prev_bm);
1213 if (ret != IFF_NO_ERROR)
1219 if (ifile.position < anim_end) //ran out of room
1220 ret = IFF_TOO_MANY_BMS;
1224 ret = IFF_UNKNOWN_FORM;
1228 close_fake_file(&ifile);
1234 //text for error messges
1235 char error_messages[] = {
1237 "Not enough mem for loading or processing bitmap.\0"
1238 "IFF file has unknown FORM type.\0"
1239 "Not an IFF file.\0"
1240 "Cannot open file.\0"
1241 "Tried to save invalid type, like BM_RGB15.\0"
1242 "Bad data in file.\0"
1243 "ANIM file cannot be loaded with normal bitmap loader.\0"
1244 "Normal bitmap file cannot be loaded with anim loader.\0"
1245 "Array not big enough on anim brush read.\0"
1246 "Unknown mask type in bitmap header.\0"
1247 "Error reading file.\0"
1251 //function to return pointer to error message
1252 char *iff_errormsg(int error_number)
1254 char *p = error_messages;
1256 while (error_number--) {
1258 if (!p) return NULL;