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.
19 static char rcsid[] = "$Id: iff.c,v 1.4 2001-01-31 15:17:48 bradleyb Exp $";
22 #define COMPRESS 1 //do the RLE or not? (for debugging mostly)
23 #define WRITE_TINY 0 //should we write a TINY chunk?
25 #define MIN_COMPRESS_WIDTH 65 //don't compress if less than this wide
34 //#include "nocfile.h"
38 //Internal constants and structures for this library
40 //Type values for bitmaps
51 #define mskHasTransparentColor 2
53 //Palette entry structure
54 typedef struct pal_entry {byte r,g,b;} pal_entry;
56 //structure of the header in the file
57 typedef struct iff_bitmap_header {
58 short w,h; //width and height of this bitmap
59 short x,y; //generally unused
60 short type; //see types above
61 short transparentcolor; //which color is transparent (if any)
62 short pagewidth,pageheight; //width & height of source screen
63 byte nplanes; //number of planes (8 for 256 color image)
64 byte masking,compression; //see constants above
65 byte xaspect,yaspect; //aspect ratio (usually 5/6)
66 pal_entry palette[256]; //the palette for this bitmap
67 ubyte *raw_data; //ptr to array of data
68 short row_size; //offset to next row
71 ubyte iff_transparent_color;
72 ubyte iff_has_transparency; // 0=no transparency, 1=iff_transparent_color is valid
74 typedef struct fake_file {
80 #define MIN(a,b) ((a<b)?a:b)
82 #define MAKE_SIG(a,b,c,d) (((long)(a)<<24)+((long)(b)<<16)+((c)<<8)+(d))
84 #define form_sig MAKE_SIG('F','O','R','M')
85 #define ilbm_sig MAKE_SIG('I','L','B','M')
86 #define body_sig MAKE_SIG('B','O','D','Y')
87 #define pbm_sig MAKE_SIG('P','B','M',' ')
88 #define bmhd_sig MAKE_SIG('B','M','H','D')
89 #define anhd_sig MAKE_SIG('A','N','H','D')
90 #define cmap_sig MAKE_SIG('C','M','A','P')
91 #define tiny_sig MAKE_SIG('T','I','N','Y')
92 #define anim_sig MAKE_SIG('A','N','I','M')
93 #define dlta_sig MAKE_SIG('D','L','T','A')
96 //void printsig(long s)
98 // char *t=(char *) &s;
100 ///* printf("%c%c%c%c",*(&s+3),*(&s+2),*(&s+1),s);*/
101 // printf("%c%c%c%c",t[3],t[2],t[1],t[0]);
105 long get_sig(FFILE *f)
109 // if ((s[3]=cfgetc(f))==EOF) return(EOF);
110 // if ((s[2]=cfgetc(f))==EOF) return(EOF);
111 // if ((s[1]=cfgetc(f))==EOF) return(EOF);
112 // if ((s[0]=cfgetc(f))==EOF) return(EOF);
115 if (f->position>=f->length) return EOF;
116 s[3] = f->data[f->position++];
117 if (f->position>=f->length) return EOF;
118 s[2] = f->data[f->position++];
119 if (f->position>=f->length) return EOF;
120 s[1] = f->data[f->position++];
121 if (f->position>=f->length) return EOF;
122 s[0] = f->data[f->position++];
124 if (f->position>=f->length) return EOF;
125 s[0] = f->data[f->position++];
126 if (f->position>=f->length) return EOF;
127 s[1] = f->data[f->position++];
128 if (f->position>=f->length) return EOF;
129 s[2] = f->data[f->position++];
130 if (f->position>=f->length) return EOF;
131 s[3] = f->data[f->position++];
134 return(*((long *) s));
137 int put_sig(long sig,FILE *f)
139 char *s = (char *) &sig;
144 return fputc(s[0],f);
148 char get_byte(FFILE *f)
151 return f->data[f->position++];
154 int put_byte(unsigned char c,FILE *f)
161 int get_word(FFILE *f)
168 if (f->position>=f->length) return EOF;
169 c1 = f->data[f->position++];
170 if (f->position>=f->length) return EOF;
171 c0 = f->data[f->position++];
173 if (c0==0xff) return(EOF);
175 return(((int)c1<<8) + c0);
179 int put_word(int n,FILE *f)
183 c0 = (n & 0xff00) >> 8;
187 return put_byte(c1,f);
190 int put_long(long n,FILE *f)
194 n0 = (int) ((n & 0xffff0000l) >> 16);
195 n1 = (int) (n & 0xffff);
198 return put_word(n1,f);
202 long get_long(FFILE *f)
204 unsigned char c0,c1,c2,c3;
211 if (f->position>=f->length) return EOF;
212 c3 = f->data[f->position++];
213 if (f->position>=f->length) return EOF;
214 c2 = f->data[f->position++];
215 if (f->position>=f->length) return EOF;
216 c1 = f->data[f->position++];
217 if (f->position>=f->length) return EOF;
218 c0 = f->data[f->position++];
220 //printf("get_long %x %x %x %x\n",c3,c2,c1,c0);
222 // if (c0==0xff) return(EOF);
224 return(((long)c3<<24) + ((long)c2<<16) + ((long)c1<<8) + c0);
228 int parse_bmhd(FFILE *ifile,long len,iff_bitmap_header *bmheader)
230 len++; /* so no "parm not used" warning */
232 // debug("parsing bmhd len=%ld\n",len);
234 bmheader->w = get_word(ifile);
235 bmheader->h = get_word(ifile);
236 bmheader->x = get_word(ifile);
237 bmheader->y = get_word(ifile);
239 bmheader->nplanes = get_byte(ifile);
240 bmheader->masking = get_byte(ifile);
241 bmheader->compression = get_byte(ifile);
242 get_byte(ifile); /* skip pad */
244 bmheader->transparentcolor = get_word(ifile);
245 bmheader->xaspect = get_byte(ifile);
246 bmheader->yaspect = get_byte(ifile);
248 bmheader->pagewidth = get_word(ifile);
249 bmheader->pageheight = get_word(ifile);
251 iff_transparent_color = bmheader->transparentcolor;
253 iff_has_transparency = 0;
255 if (bmheader->masking == mskHasTransparentColor)
256 iff_has_transparency = 1;
258 else if (bmheader->masking != mskNone && bmheader->masking != mskHasMask)
259 return IFF_UNKNOWN_MASK;
261 // debug("w,h=%d,%d x,y=%d,%d\n",w,h,x,y);
262 // debug("nplanes=%d, masking=%d ,compression=%d, transcolor=%d\n",nplanes,masking,compression,transparentcolor);
268 // the buffer pointed to by raw_data is stuffed with a pointer to decompressed pixel data
269 int parse_body(FFILE *ifile,long len,iff_bitmap_header *bmheader)
271 unsigned char *p=bmheader->raw_data;
274 int nn,wid_cnt,end_cnt,plane;
276 unsigned char *data_end;
285 end_pos = ifile->position + len;
289 if (bmheader->type == TYPE_PBM) {
292 } else if (bmheader->type == TYPE_ILBM) {
293 width = (bmheader->w+7)/8;
294 depth=bmheader->nplanes;
297 end_cnt = (width&1)?-1:0;
299 data_end = p + width*bmheader->h*depth;
301 if (bmheader->compression == cmpNone) { /* no compression */
304 for (y=bmheader->h;y;y--) {
306 // for (x=bmheader->w;x;x--) *p++=cfgetc(ifile);
307 // cfread(p, bmheader->w, 1, ifile);
310 for (x=0;x<width*depth;x++)
311 *p++=ifile->data[ifile->position++];
313 if (bmheader->masking == mskHasMask)
314 ifile->position += width; //skip mask!
316 // if (bmheader->w & 1) ignore = cfgetc(ifile);
317 if (bmheader->w & 1) ifile->position++;
320 //cnt = len - bmheader->h * ((bmheader->w+1)&~1);
323 else if (bmheader->compression == cmpByteRun1)
324 for (wid_cnt=width,plane=0;ifile->position<end_pos && p<data_end;) {
327 // if (old_cnt-cnt > 2048) {
332 if (wid_cnt == end_cnt) {
335 if ((bmheader->masking == mskHasMask && plane==depth+1) ||
336 (bmheader->masking != mskHasMask && plane==depth))
340 Assert(wid_cnt > end_cnt);
343 n=ifile->data[ifile->position++];
345 if (n >= 0) { // copy next n+1 bytes from source, they are not compressed
348 if (wid_cnt==-1) {--nn; Assert(width&1);}
349 if (plane==depth) //masking row
350 ifile->position += nn;
352 while (nn--) *p++=ifile->data[ifile->position++];
353 if (wid_cnt==-1) ifile->position++;
355 else if (n>=-127) { // next -n + 1 bytes are following byte
356 c=ifile->data[ifile->position++];
359 if (wid_cnt==-1) {--nn; Assert(width&1);}
360 if (plane!=depth) //not masking row
361 {memset(p,c,nn); p+=nn;}
365 if ((p-bmheader->raw_data) % width == 0)
368 Assert((p-bmheader->raw_data) - (width*row_count) < width);
373 if (p!=data_end) //if we don't have the whole bitmap...
374 return IFF_CORRUPT; //...the give an error
376 //Pretend we read the whole chuck, because if we didn't, it's because
377 //we didn't read the last mask like or the last rle record for padding
378 //or whatever and it's not important, because we check to make sure
379 //we got the while bitmap, and that's what really counts.
381 ifile->position = end_pos;
383 if (ignore) ignore++; // haha, suppress the evil warning message
388 //modify passed bitmap
389 int parse_delta(FFILE *ifile,long len,iff_bitmap_header *bmheader)
391 unsigned char *p=bmheader->raw_data;
393 long chunk_end = ifile->position + len;
395 get_long(ifile); //longword, seems to be equal to 4. Don't know what it is
397 for (y=0;y<bmheader->h;y++) {
399 int cnt = bmheader->w;
402 n_items = get_byte(ifile);
406 code = get_byte(ifile);
408 if (code==0) { //repeat
411 rep = get_byte(ifile);
412 val = get_byte(ifile);
420 else if (code > 0x80) { //skip
432 *p++ = get_byte(ifile);
448 if (ifile->position == chunk_end-1) //pad
451 if (ifile->position != chunk_end)
457 // the buffer pointed to by raw_data is stuffed with a pointer to bitplane pixel data
458 void skip_chunk(FFILE *ifile,long len)
464 //printf( "Skipping %d chunk\n", ilen );
466 ifile->position += ilen;
468 if (ifile->position >= ifile->length ) {
469 ifile->position = ifile->length;
473 // for (i=0; i<ilen; i++ )
474 // c = cfgetc(ifile);
475 //Assert(cfseek(ifile,ilen,SEEK_CUR)==0);
478 //read an ILBM or PBM file
479 // Pass pointer to opened file, and to empty bitmap_header structure, and form length
480 int iff_parse_ilbm_pbm(FFILE *ifile,long form_type,iff_bitmap_header *bmheader,int form_len,grs_bitmap *prev_bm)
484 long start_pos,end_pos;
486 start_pos = ifile->position;
487 end_pos = start_pos-4+form_len;
489 // printf(" %ld ",form_len);
490 // printsig(form_type);
493 if (form_type == pbm_sig)
494 bmheader->type = TYPE_PBM;
496 bmheader->type = TYPE_ILBM;
498 while ((ifile->position < end_pos) && (sig=get_sig(ifile)) != EOF) {
505 // printf(" %ld\n",len);
511 int save_w=bmheader->w,save_h=bmheader->h;
513 //printf("Parsing header\n");
515 ret = parse_bmhd(ifile,len,bmheader);
517 if (ret != IFF_NO_ERROR)
520 if (bmheader->raw_data) {
522 if (save_w != bmheader->w || save_h != bmheader->h)
523 return IFF_BM_MISMATCH;
528 MALLOC( bmheader->raw_data, ubyte, bmheader->w * bmheader->h );
529 if (!bmheader->raw_data)
539 if (!prev_bm) return IFF_CORRUPT;
541 bmheader->w = prev_bm->bm_w;
542 bmheader->h = prev_bm->bm_h;
543 bmheader->type = prev_bm->bm_type;
545 MALLOC( bmheader->raw_data, ubyte, bmheader->w * bmheader->h );
547 memcpy(bmheader->raw_data, prev_bm->bm_data, bmheader->w * bmheader->h );
548 skip_chunk(ifile,len);
554 int ncolors=(int) (len/3),cnum;
557 //printf("Parsing RGB map\n");
558 for (cnum=0;cnum<ncolors;cnum++) {
562 r = ifile->data[ifile->position++];
563 g = ifile->data[ifile->position++];
564 b = ifile->data[ifile->position++];
565 r >>= 2; bmheader->palette[cnum].r = r;
566 g >>= 2; bmheader->palette[cnum].g = g;
567 b >>= 2; bmheader->palette[cnum].b = b;
569 //if (len & 1) ignore = cfgetc(ifile);
570 if (len & 1 ) ifile->position++;
578 //printf("Parsing body\n");
579 if ((r=parse_body(ifile,len,bmheader))!=IFF_NO_ERROR)
586 if ((r=parse_delta(ifile,len,bmheader))!=IFF_NO_ERROR)
591 skip_chunk(ifile,len);
596 //if (ignore) ignore++;
598 if (ifile->position != start_pos-4+form_len)
601 return IFF_NO_ERROR; /* ok! */
604 //convert an ILBM file to a PBM file
605 int convert_ilbm_to_pbm(iff_bitmap_header *bmheader)
608 byte *new_data,*destptr,*rowptr;
609 int bytes_per_row,byteofs;
610 ubyte checkmask,newbyte,setbit;
612 MALLOC( new_data, byte, bmheader->w * bmheader->h );
613 if (new_data == NULL) return IFF_NO_MEM;
617 bytes_per_row = 2*((bmheader->w+15)/16);
619 for (y=0;y<bmheader->h;y++) {
621 rowptr = &bmheader->raw_data[y * bytes_per_row * bmheader->nplanes];
623 for (x=0,checkmask=0x80;x<bmheader->w;x++) {
627 for (p=newbyte=0,setbit=1;p<bmheader->nplanes;p++) {
629 if (rowptr[bytes_per_row * p + byteofs] & checkmask)
635 *destptr++ = newbyte;
637 if ((checkmask >>= 1) == 0) checkmask=0x80;
642 d_free(bmheader->raw_data);
643 bmheader->raw_data = new_data;
645 bmheader->type = TYPE_PBM;
650 #define INDEX_TO_15BPP(i) ((short)((((palptr[(i)].r/2)&31)<<10)+(((palptr[(i)].g/2)&31)<<5)+((palptr[(i)].b/2 )&31)))
652 int convert_rgb15(grs_bitmap *bm,iff_bitmap_header *bmheader)
659 palptr = bmheader->palette;
661 // if ((new_data = d_malloc(bm->bm_w * bm->bm_h * 2)) == NULL)
662 // {ret=IFF_NO_MEM; goto done;}
663 MALLOC(new_data, ushort, bm->bm_w * bm->bm_h * 2);
664 if (new_data == NULL)
667 for (y=0; y<bm->bm_h; y++) {
669 for (x=0; x<bmheader->w; x++)
670 new_data[newptr++] = INDEX_TO_15BPP(bmheader->raw_data[y*bmheader->w+x]);
674 d_free(bm->bm_data); //get rid of old-style data
675 bm->bm_data = (ubyte *) new_data; //..and point to new data
677 bm->bm_rowsize *= 2; //two bytes per row
683 //read in a entire file into a fake file structure
684 int open_fake_file(char *ifilename,FFILE *ffile)
689 //printf( "Reading %s\n", ifilename );
693 if ((ifile = cfopen(ifilename,"rb")) == NULL) return IFF_NO_FILE;
695 ffile->length = cfilelength(ifile);
697 MALLOC(ffile->data,ubyte,ffile->length);
699 if (cfread(ffile->data, 1, ffile->length, ifile) < ffile->length)
700 ret = IFF_READ_ERROR;
706 if (ifile) cfclose(ifile);
711 void close_fake_file(FFILE *f)
719 //copy an iff header structure to a grs_bitmap structure
720 void copy_iff_to_grs(grs_bitmap *bm,iff_bitmap_header *bmheader)
722 bm->bm_x = bm->bm_y = 0;
723 bm->bm_w = bmheader->w;
724 bm->bm_h = bmheader->h;
725 bm->bm_type = bmheader->type;
726 bm->bm_rowsize = bmheader->w;
727 bm->bm_data = bmheader->raw_data;
729 bm->bm_flags = bm->bm_handle = 0;
733 //if bm->bm_data is set, use it (making sure w & h are correct), else
734 //allocate the memory
735 int iff_parse_bitmap(FFILE *ifile,grs_bitmap *bm,int bitmap_type,byte *palette,grs_bitmap *prev_bm)
737 int ret; //return code
738 iff_bitmap_header bmheader;
742 bmheader.raw_data = bm->bm_data;
744 if (bmheader.raw_data) {
745 bmheader.w = bm->bm_w;
746 bmheader.h = bm->bm_h;
751 if (sig != form_sig) {
756 form_len = get_long(ifile);
758 form_type = get_sig(ifile);
760 if (form_type == anim_sig)
762 else if ((form_type == pbm_sig) || (form_type == ilbm_sig))
763 ret = iff_parse_ilbm_pbm(ifile,form_type,&bmheader,form_len,prev_bm);
765 ret = IFF_UNKNOWN_FORM;
767 if (ret != IFF_NO_ERROR) { //got an error parsing
768 if (bmheader.raw_data) d_free(bmheader.raw_data);
772 //If IFF file is ILBM, convert to PPB
773 if (bmheader.type == TYPE_ILBM) {
775 ret = convert_ilbm_to_pbm(&bmheader);
777 if (ret != IFF_NO_ERROR) goto done;
780 //Copy data from iff_bitmap_header structure into grs_bitmap structure
782 copy_iff_to_grs(bm,&bmheader);
785 if (palette) memcpy(palette,&bmheader.palette,sizeof(bmheader.palette));
787 // if (palette) memcpy(palette,&bmheader.palette, 768); // pal_entry is 4 bytes on mac
793 for (i = 0; i < 256; i++) {
794 *c++ = bmheader.palette[i].r;
795 *c++ = bmheader.palette[i].g;
796 *c++ = bmheader.palette[i].b;
801 //Now do post-process if required
803 if (bitmap_type == BM_RGB15) {
804 ret = convert_rgb15(bm,&bmheader);
805 if (ret != IFF_NO_ERROR) goto done;
814 //returns error codes - see IFF.H. see GR.H for bitmap_type
815 int iff_read_bitmap(char *ifilename,grs_bitmap *bm,int bitmap_type,ubyte *palette)
817 int ret; //return code
820 ret = open_fake_file(ifilename,&ifile); //read in entire file
821 if (ret != IFF_NO_ERROR) goto done;
825 ret = iff_parse_bitmap(&ifile,bm,bitmap_type,palette,NULL);
829 if (ifile.data) d_free(ifile.data);
831 close_fake_file(&ifile);
838 //like iff_read_bitmap(), but reads into a bitmap that already exists,
839 //without allocating memory for the bitmap.
840 int iff_read_into_bitmap(char *ifilename,grs_bitmap *bm,byte *palette)
842 int ret; //return code
845 ret = open_fake_file(ifilename,&ifile); //read in entire file
846 if (ret != IFF_NO_ERROR) goto done;
848 ret = iff_parse_bitmap(&ifile,bm,bm->bm_type,palette,NULL);
852 if (ifile.data) d_free(ifile.data);
854 close_fake_file(&ifile);
863 int write_bmhd(FILE *ofile,iff_bitmap_header *bitmap_header)
865 put_sig(bmhd_sig,ofile);
866 put_long((long) BMHD_SIZE,ofile);
868 put_word(bitmap_header->w,ofile);
869 put_word(bitmap_header->h,ofile);
870 put_word(bitmap_header->x,ofile);
871 put_word(bitmap_header->y,ofile);
873 put_byte(bitmap_header->nplanes,ofile);
874 put_byte(bitmap_header->masking,ofile);
875 put_byte(bitmap_header->compression,ofile);
876 put_byte(0,ofile); /* pad */
878 put_word(bitmap_header->transparentcolor,ofile);
879 put_byte(bitmap_header->xaspect,ofile);
880 put_byte(bitmap_header->yaspect,ofile);
882 put_word(bitmap_header->pagewidth,ofile);
883 put_word(bitmap_header->pageheight,ofile);
889 int write_pal(FILE *ofile,iff_bitmap_header *bitmap_header)
893 int n_colors = 1<<bitmap_header->nplanes;
895 put_sig(cmap_sig,ofile);
896 // put_long(sizeof(pal_entry) * n_colors,ofile);
897 put_long(3 * n_colors,ofile);
899 //printf("new write pal %d %d\n",3,n_colors);
901 for (i=0; i<256; i++) {
903 r = bitmap_header->palette[i].r * 4 + (bitmap_header->palette[i].r?3:0);
904 g = bitmap_header->palette[i].g * 4 + (bitmap_header->palette[i].g?3:0);
905 b = bitmap_header->palette[i].b * 4 + (bitmap_header->palette[i].b?3:0);
911 //printf("write pal %d %d\n",sizeof(pal_entry),n_colors);
912 // fwrite(bitmap_header->palette,sizeof(pal_entry),n_colors,ofile);
917 int rle_span(ubyte *dest,ubyte *src,int len)
919 int n,lit_cnt,rep_cnt;
920 ubyte last,*cnt_ptr,*dptr;
926 last=src[0]; lit_cnt=1;
928 for (n=1;n<len;n++) {
930 if (src[n] == last) {
935 while (n<len && rep_cnt<128 && src[n]==last) {n++; rep_cnt++;}
937 if (rep_cnt > 2 || lit_cnt < 2) {
939 if (lit_cnt > 1) {*cnt_ptr = lit_cnt-2; --dptr;} //save old lit count
940 *dptr++ = -(rep_cnt-1);
943 lit_cnt = (n<len)?1:0;
945 continue; //go to next char
953 cnt_ptr = dptr++; //save place for count
954 *dptr++=last; //store first char
957 *dptr++ = last = src[n];
959 if (lit_cnt == 127) {
973 *dptr++=last; //store first char
975 else if (lit_cnt > 1)
976 *cnt_ptr = lit_cnt-1;
981 #define EVEN(a) ((a+1)&0xfffffffel)
983 //returns length of chunk
984 int write_body(FILE *ofile,iff_bitmap_header *bitmap_header,int compression_on)
986 int w=bitmap_header->w,h=bitmap_header->h;
988 long len = EVEN(w) * h,newlen,total_len=0;
989 ubyte *p=bitmap_header->raw_data,*new_span;
992 put_sig(body_sig,ofile);
993 save_pos = ftell(ofile);
996 //if (! (new_span = d_malloc(bitmap_header->w+(bitmap_header->w/128+2)*2))) return IFF_NO_MEM;
997 MALLOC( new_span, ubyte, bitmap_header->w + (bitmap_header->w/128+2)*2);
998 if (new_span == NULL) return IFF_NO_MEM;
1000 for (y=bitmap_header->h;y--;) {
1002 if (compression_on) {
1003 total_len += newlen = rle_span(new_span,p,bitmap_header->w+odd);
1004 fwrite(new_span,newlen,1,ofile);
1007 fwrite(p,bitmap_header->w+odd,1,ofile);
1009 p+=bitmap_header->row_size; //bitmap_header->w;
1012 if (compression_on) { //write actual data length
1013 Assert(fseek(ofile,save_pos,SEEK_SET)==0);
1014 put_long(total_len,ofile);
1015 Assert(fseek(ofile,total_len,SEEK_CUR)==0);
1016 if (total_len&1) fputc(0,ofile); //pad to even
1021 return ((compression_on) ? (EVEN(total_len)+8) : (len+8));
1026 //write a small representation of a bitmap. returns size
1027 int write_tiny(CFILE *ofile,iff_bitmap_header *bitmap_header,int compression_on)
1031 int len,total_len=0,newlen;
1033 ubyte *p = bitmap_header->raw_data;
1034 ubyte tspan[80],new_span[80*2];
1037 skip = max((bitmap_header->w+79)/80,(bitmap_header->h+63)/64);
1039 new_w = bitmap_header->w / skip;
1040 new_h = bitmap_header->h / skip;
1044 len = new_w * new_h + 4;
1046 put_sig(tiny_sig,ofile);
1047 save_pos = cftell(ofile);
1048 put_long(EVEN(len),ofile);
1050 put_word(new_w,ofile);
1051 put_word(new_h,ofile);
1053 for (y=0;y<new_h;y++) {
1054 for (x=xofs=0;x<new_w;x++,xofs+=skip)
1057 if (compression_on) {
1058 total_len += newlen = rle_span(new_span,tspan,new_w+odd);
1059 fwrite(new_span,newlen,1,ofile);
1062 fwrite(p,new_w+odd,1,ofile);
1064 p += skip * bitmap_header->row_size; //bitmap_header->w;
1068 if (compression_on) {
1069 Assert(cfseek(ofile,save_pos,SEEK_SET)==0);
1070 put_long(4+total_len,ofile);
1071 Assert(cfseek(ofile,4+total_len,SEEK_CUR)==0);
1072 if (total_len&1) cfputc(0,ofile); //pad to even
1075 return ((compression_on) ? (EVEN(total_len)+8+4) : (len+8));
1079 int write_pbm(FILE *ofile,iff_bitmap_header *bitmap_header,int compression_on) /* writes a pbm iff file */
1082 long raw_size = EVEN(bitmap_header->w) * bitmap_header->h;
1083 long body_size,tiny_size,pbm_size = 4 + BMHD_SIZE + 8 + EVEN(raw_size) + sizeof(pal_entry)*(1<<bitmap_header->nplanes)+8;
1086 //printf("write_pbm\n");
1088 put_sig(form_sig,ofile);
1089 save_pos = ftell(ofile);
1090 put_long(pbm_size+8,ofile);
1091 put_sig(pbm_sig,ofile);
1093 ret = write_bmhd(ofile,bitmap_header);
1094 if (ret != IFF_NO_ERROR) return ret;
1096 ret = write_pal(ofile,bitmap_header);
1097 if (ret != IFF_NO_ERROR) return ret;
1100 tiny_size = write_tiny(ofile,bitmap_header,compression_on);
1105 body_size = write_body(ofile,bitmap_header,compression_on);
1107 pbm_size = 4 + BMHD_SIZE + body_size + tiny_size + sizeof(pal_entry)*(1<<bitmap_header->nplanes)+8;
1109 Assert(fseek(ofile,save_pos,SEEK_SET)==0);
1110 put_long(pbm_size+8,ofile);
1111 Assert(fseek(ofile,pbm_size+8,SEEK_CUR)==0);
1117 //writes an IFF file from a grs_bitmap structure. writes palette if not null
1118 //returns error codes - see IFF.H.
1119 int iff_write_bitmap(char *ofilename,grs_bitmap *bm,ubyte *palette)
1122 iff_bitmap_header bmheader;
1126 if (bm->bm_type == BM_RGB15) return IFF_BAD_BM_TYPE;
1129 compression_on = (bm->bm_w>=MIN_COMPRESS_WIDTH);
1134 //fill in values in bmheader
1136 bmheader.x = bmheader.y = 0;
1137 bmheader.w = bm->bm_w;
1138 bmheader.h = bm->bm_h;
1139 bmheader.type = TYPE_PBM;
1140 bmheader.transparentcolor = iff_transparent_color;
1141 bmheader.pagewidth = bm->bm_w; //I don't think it matters what I write
1142 bmheader.pageheight = bm->bm_h;
1143 bmheader.nplanes = 8;
1144 bmheader.masking = mskNone;
1145 if (iff_has_transparency) {
1146 bmheader.masking |= mskHasTransparentColor;
1148 bmheader.compression = (compression_on?cmpByteRun1:cmpNone);
1150 bmheader.xaspect = bmheader.yaspect = 1; //I don't think it matters what I write
1151 bmheader.raw_data = bm->bm_data;
1152 bmheader.row_size = bm->bm_rowsize;
1154 if (palette) memcpy(&bmheader.palette,palette,256*3);
1156 //open file and write
1158 if ((ofile = fopen(ofilename,"wb")) == NULL) {ret=IFF_NO_FILE; goto done;}
1160 ret = write_pbm(ofile,&bmheader,compression_on);
1169 //read in many brushes. fills in array of pointers, and n_bitmaps.
1170 //returns iff error codes
1171 int iff_read_animbrush(char *ifilename,grs_bitmap **bm_list,int max_bitmaps,int *n_bitmaps,ubyte *palette)
1173 int ret; //return code
1175 iff_bitmap_header bmheader;
1181 ret = open_fake_file(ifilename,&ifile); //read in entire file
1182 if (ret != IFF_NO_ERROR) goto done;
1184 bmheader.raw_data = NULL;
1186 sig=get_sig(&ifile);
1187 form_len = get_long(&ifile);
1189 if (sig != form_sig) {
1194 form_type = get_sig(&ifile);
1196 if ((form_type == pbm_sig) || (form_type == ilbm_sig))
1197 ret = IFF_FORM_BITMAP;
1198 else if (form_type == anim_sig) {
1199 int anim_end = ifile.position + form_len - 4;
1201 while (ifile.position < anim_end && *n_bitmaps < max_bitmaps) {
1203 grs_bitmap *prev_bm;
1205 prev_bm = *n_bitmaps>0?bm_list[*n_bitmaps-1]:NULL;
1207 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: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;