1 /* $Id: iff.c,v 1.10 2004-12-01 06:57:28 btb 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.10 2004-12-01 06:57:28 btb 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 MAKE_SIG(a,b,c,d) (((long)(a)<<24)+((long)(b)<<16)+((c)<<8)+(d))
91 #define form_sig MAKE_SIG('F','O','R','M')
92 #define ilbm_sig MAKE_SIG('I','L','B','M')
93 #define body_sig MAKE_SIG('B','O','D','Y')
94 #define pbm_sig MAKE_SIG('P','B','M',' ')
95 #define bmhd_sig MAKE_SIG('B','M','H','D')
96 #define anhd_sig MAKE_SIG('A','N','H','D')
97 #define cmap_sig MAKE_SIG('C','M','A','P')
98 #define tiny_sig MAKE_SIG('T','I','N','Y')
99 #define anim_sig MAKE_SIG('A','N','I','M')
100 #define dlta_sig MAKE_SIG('D','L','T','A')
103 //void printsig(long s)
105 // char *t=(char *) &s;
107 ///* printf("%c%c%c%c",*(&s+3),*(&s+2),*(&s+1),s);*/
108 // printf("%c%c%c%c",t[3],t[2],t[1],t[0]);
112 long get_sig(FFILE *f)
116 // if ((s[3]=cfgetc(f))==EOF) return(EOF);
117 // if ((s[2]=cfgetc(f))==EOF) return(EOF);
118 // if ((s[1]=cfgetc(f))==EOF) return(EOF);
119 // if ((s[0]=cfgetc(f))==EOF) return(EOF);
121 #ifndef WORDS_BIGENDIAN
122 if (f->position>=f->length) return EOF;
123 s[3] = f->data[f->position++];
124 if (f->position>=f->length) return EOF;
125 s[2] = 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[0] = f->data[f->position++];
131 if (f->position>=f->length) return EOF;
132 s[0] = f->data[f->position++];
133 if (f->position>=f->length) return EOF;
134 s[1] = f->data[f->position++];
135 if (f->position>=f->length) return EOF;
136 s[2] = f->data[f->position++];
137 if (f->position>=f->length) return EOF;
138 s[3] = f->data[f->position++];
141 return(*((long *) s));
144 int put_sig(long sig,FILE *f)
146 char *s = (char *) &sig;
151 return fputc(s[0],f);
155 char get_byte(FFILE *f)
158 return f->data[f->position++];
161 int put_byte(unsigned char c,FILE *f)
166 int get_word(FFILE *f)
173 if (f->position>=f->length) return EOF;
174 c1 = f->data[f->position++];
175 if (f->position>=f->length) return EOF;
176 c0 = f->data[f->position++];
178 if (c0==0xff) return(EOF);
180 return(((int)c1<<8) + c0);
184 int put_word(int n,FILE *f)
188 c0 = (n & 0xff00) >> 8;
192 return put_byte(c1,f);
195 int put_long(long n,FILE *f)
199 n0 = (int) ((n & 0xffff0000l) >> 16);
200 n1 = (int) (n & 0xffff);
203 return put_word(n1,f);
207 long get_long(FFILE *f)
209 unsigned char c0,c1,c2,c3;
216 if (f->position>=f->length) return EOF;
217 c3 = f->data[f->position++];
218 if (f->position>=f->length) return EOF;
219 c2 = f->data[f->position++];
220 if (f->position>=f->length) return EOF;
221 c1 = f->data[f->position++];
222 if (f->position>=f->length) return EOF;
223 c0 = f->data[f->position++];
225 //printf("get_long %x %x %x %x\n",c3,c2,c1,c0);
227 // if (c0==0xff) return(EOF);
229 return(((long)c3<<24) + ((long)c2<<16) + ((long)c1<<8) + c0);
233 int parse_bmhd(FFILE *ifile,long len,iff_bitmap_header *bmheader)
235 len++; /* so no "parm not used" warning */
237 // debug("parsing bmhd len=%ld\n",len);
239 bmheader->w = get_word(ifile);
240 bmheader->h = get_word(ifile);
241 bmheader->x = get_word(ifile);
242 bmheader->y = get_word(ifile);
244 bmheader->nplanes = get_byte(ifile);
245 bmheader->masking = get_byte(ifile);
246 bmheader->compression = get_byte(ifile);
247 get_byte(ifile); /* skip pad */
249 bmheader->transparentcolor = get_word(ifile);
250 bmheader->xaspect = get_byte(ifile);
251 bmheader->yaspect = get_byte(ifile);
253 bmheader->pagewidth = get_word(ifile);
254 bmheader->pageheight = get_word(ifile);
256 iff_transparent_color = bmheader->transparentcolor;
258 iff_has_transparency = 0;
260 if (bmheader->masking == mskHasTransparentColor)
261 iff_has_transparency = 1;
263 else if (bmheader->masking != mskNone && bmheader->masking != mskHasMask)
264 return IFF_UNKNOWN_MASK;
266 // debug("w,h=%d,%d x,y=%d,%d\n",w,h,x,y);
267 // debug("nplanes=%d, masking=%d ,compression=%d, transcolor=%d\n",nplanes,masking,compression,transparentcolor);
273 // the buffer pointed to by raw_data is stuffed with a pointer to decompressed pixel data
274 int parse_body(FFILE *ifile,long len,iff_bitmap_header *bmheader)
276 unsigned char *p=bmheader->raw_data;
279 int nn,wid_cnt,end_cnt,plane;
281 unsigned char *data_end;
290 end_pos = ifile->position + len;
294 if (bmheader->type == TYPE_PBM) {
297 } else if (bmheader->type == TYPE_ILBM) {
298 width = (bmheader->w+7)/8;
299 depth=bmheader->nplanes;
302 end_cnt = (width&1)?-1:0;
304 data_end = p + width*bmheader->h*depth;
306 if (bmheader->compression == cmpNone) { /* no compression */
309 for (y=bmheader->h;y;y--) {
311 // for (x=bmheader->w;x;x--) *p++=cfgetc(ifile);
312 // cfread(p, bmheader->w, 1, ifile);
315 for (x=0;x<width*depth;x++)
316 *p++=ifile->data[ifile->position++];
318 if (bmheader->masking == mskHasMask)
319 ifile->position += width; //skip mask!
321 // if (bmheader->w & 1) ignore = cfgetc(ifile);
322 if (bmheader->w & 1) ifile->position++;
325 //cnt = len - bmheader->h * ((bmheader->w+1)&~1);
328 else if (bmheader->compression == cmpByteRun1)
329 for (wid_cnt=width,plane=0;ifile->position<end_pos && p<data_end;) {
332 // if (old_cnt-cnt > 2048) {
337 if (wid_cnt == end_cnt) {
340 if ((bmheader->masking == mskHasMask && plane==depth+1) ||
341 (bmheader->masking != mskHasMask && plane==depth))
345 Assert(wid_cnt > end_cnt);
348 n=ifile->data[ifile->position++];
350 if (n >= 0) { // copy next n+1 bytes from source, they are not compressed
353 if (wid_cnt==-1) {--nn; Assert(width&1);}
354 if (plane==depth) //masking row
355 ifile->position += nn;
357 while (nn--) *p++=ifile->data[ifile->position++];
358 if (wid_cnt==-1) ifile->position++;
360 else if (n>=-127) { // next -n + 1 bytes are following byte
361 c=ifile->data[ifile->position++];
364 if (wid_cnt==-1) {--nn; Assert(width&1);}
365 if (plane!=depth) //not masking row
366 {memset(p,c,nn); p+=nn;}
370 if ((p-bmheader->raw_data) % width == 0)
373 Assert((p-bmheader->raw_data) - (width*row_count) < width);
378 if (p!=data_end) //if we don't have the whole bitmap...
379 return IFF_CORRUPT; //...the give an error
381 //Pretend we read the whole chuck, because if we didn't, it's because
382 //we didn't read the last mask like or the last rle record for padding
383 //or whatever and it's not important, because we check to make sure
384 //we got the while bitmap, and that's what really counts.
386 ifile->position = end_pos;
388 if (ignore) ignore++; // haha, suppress the evil warning message
393 //modify passed bitmap
394 int parse_delta(FFILE *ifile,long len,iff_bitmap_header *bmheader)
396 unsigned char *p=bmheader->raw_data;
398 long chunk_end = ifile->position + len;
400 get_long(ifile); //longword, seems to be equal to 4. Don't know what it is
402 for (y=0;y<bmheader->h;y++) {
404 int cnt = bmheader->w;
407 n_items = get_byte(ifile);
411 code = get_byte(ifile);
413 if (code==0) { //repeat
416 rep = get_byte(ifile);
417 val = get_byte(ifile);
425 else if (code > 0x80) { //skip
437 *p++ = get_byte(ifile);
453 if (ifile->position == chunk_end-1) //pad
456 if (ifile->position != chunk_end)
462 // the buffer pointed to by raw_data is stuffed with a pointer to bitplane pixel data
463 void skip_chunk(FFILE *ifile,long len)
469 //printf( "Skipping %d chunk\n", ilen );
471 ifile->position += ilen;
473 if (ifile->position >= ifile->length ) {
474 ifile->position = ifile->length;
478 // for (i=0; i<ilen; i++ )
479 // c = cfgetc(ifile);
480 //Assert(cfseek(ifile,ilen,SEEK_CUR)==0);
483 //read an ILBM or PBM file
484 // Pass pointer to opened file, and to empty bitmap_header structure, and form length
485 int iff_parse_ilbm_pbm(FFILE *ifile,long form_type,iff_bitmap_header *bmheader,int form_len,grs_bitmap *prev_bm)
489 long start_pos,end_pos;
491 start_pos = ifile->position;
492 end_pos = start_pos-4+form_len;
494 // printf(" %ld ",form_len);
495 // printsig(form_type);
498 if (form_type == pbm_sig)
499 bmheader->type = TYPE_PBM;
501 bmheader->type = TYPE_ILBM;
503 while ((ifile->position < end_pos) && (sig=get_sig(ifile)) != EOF) {
510 // printf(" %ld\n",len);
516 int save_w=bmheader->w,save_h=bmheader->h;
518 //printf("Parsing header\n");
520 ret = parse_bmhd(ifile,len,bmheader);
522 if (ret != IFF_NO_ERROR)
525 if (bmheader->raw_data) {
527 if (save_w != bmheader->w || save_h != bmheader->h)
528 return IFF_BM_MISMATCH;
533 MALLOC( bmheader->raw_data, ubyte, bmheader->w * bmheader->h );
534 if (!bmheader->raw_data)
544 if (!prev_bm) return IFF_CORRUPT;
546 bmheader->w = prev_bm->bm_w;
547 bmheader->h = prev_bm->bm_h;
548 bmheader->type = prev_bm->bm_type;
550 MALLOC( bmheader->raw_data, ubyte, bmheader->w * bmheader->h );
552 memcpy(bmheader->raw_data, prev_bm->bm_data, bmheader->w * bmheader->h );
553 skip_chunk(ifile,len);
559 int ncolors=(int) (len/3),cnum;
562 //printf("Parsing RGB map\n");
563 for (cnum=0;cnum<ncolors;cnum++) {
567 r = ifile->data[ifile->position++];
568 g = ifile->data[ifile->position++];
569 b = ifile->data[ifile->position++];
570 r >>= 2; bmheader->palette[cnum].r = r;
571 g >>= 2; bmheader->palette[cnum].g = g;
572 b >>= 2; bmheader->palette[cnum].b = b;
574 //if (len & 1) ignore = cfgetc(ifile);
575 if (len & 1 ) ifile->position++;
583 //printf("Parsing body\n");
584 if ((r=parse_body(ifile,len,bmheader))!=IFF_NO_ERROR)
591 if ((r=parse_delta(ifile,len,bmheader))!=IFF_NO_ERROR)
596 skip_chunk(ifile,len);
601 //if (ignore) ignore++;
603 if (ifile->position != start_pos-4+form_len)
606 return IFF_NO_ERROR; /* ok! */
609 //convert an ILBM file to a PBM file
610 int convert_ilbm_to_pbm(iff_bitmap_header *bmheader)
613 sbyte *new_data, *destptr, *rowptr;
614 int bytes_per_row,byteofs;
615 ubyte checkmask,newbyte,setbit;
617 MALLOC(new_data, sbyte, bmheader->w * bmheader->h);
618 if (new_data == NULL) return IFF_NO_MEM;
622 bytes_per_row = 2*((bmheader->w+15)/16);
624 for (y=0;y<bmheader->h;y++) {
626 rowptr = (signed char *) &bmheader->raw_data[y * bytes_per_row * bmheader->nplanes];
628 for (x=0,checkmask=0x80;x<bmheader->w;x++) {
632 for (p=newbyte=0,setbit=1;p<bmheader->nplanes;p++) {
634 if (rowptr[bytes_per_row * p + byteofs] & checkmask)
640 *destptr++ = newbyte;
642 if ((checkmask >>= 1) == 0) checkmask=0x80;
647 d_free(bmheader->raw_data);
648 bmheader->raw_data = (unsigned char *) new_data;
650 bmheader->type = TYPE_PBM;
655 #define INDEX_TO_15BPP(i) ((short)((((palptr[(i)].r/2)&31)<<10)+(((palptr[(i)].g/2)&31)<<5)+((palptr[(i)].b/2 )&31)))
657 int convert_rgb15(grs_bitmap *bm,iff_bitmap_header *bmheader)
664 palptr = bmheader->palette;
666 // if ((new_data = d_malloc(bm->bm_w * bm->bm_h * 2)) == NULL)
667 // {ret=IFF_NO_MEM; goto done;}
668 MALLOC(new_data, ushort, bm->bm_w * bm->bm_h * 2);
669 if (new_data == NULL)
672 for (y=0; y<bm->bm_h; y++) {
674 for (x=0; x<bmheader->w; x++)
675 new_data[newptr++] = INDEX_TO_15BPP(bmheader->raw_data[y*bmheader->w+x]);
679 d_free(bm->bm_data); //get rid of old-style data
680 bm->bm_data = (ubyte *) new_data; //..and point to new data
682 bm->bm_rowsize *= 2; //two bytes per row
688 //read in a entire file into a fake file structure
689 int open_fake_file(char *ifilename,FFILE *ffile)
694 //printf( "Reading %s\n", ifilename );
698 if ((ifile = cfopen(ifilename,"rb")) == NULL) return IFF_NO_FILE;
700 ffile->length = cfilelength(ifile);
702 MALLOC(ffile->data,ubyte,ffile->length);
704 if (cfread(ffile->data, 1, ffile->length, ifile) < ffile->length)
705 ret = IFF_READ_ERROR;
711 if (ifile) cfclose(ifile);
716 void close_fake_file(FFILE *f)
724 //copy an iff header structure to a grs_bitmap structure
725 void copy_iff_to_grs(grs_bitmap *bm,iff_bitmap_header *bmheader)
727 bm->bm_x = bm->bm_y = 0;
728 bm->bm_w = bmheader->w;
729 bm->bm_h = bmheader->h;
730 bm->bm_type = bmheader->type;
731 bm->bm_rowsize = bmheader->w;
732 bm->bm_data = bmheader->raw_data;
734 bm->bm_flags = bm->bm_handle = 0;
738 //if bm->bm_data is set, use it (making sure w & h are correct), else
739 //allocate the memory
740 int iff_parse_bitmap(FFILE *ifile, grs_bitmap *bm, int bitmap_type, sbyte *palette, grs_bitmap *prev_bm)
742 int ret; //return code
743 iff_bitmap_header bmheader;
747 bmheader.raw_data = bm->bm_data;
749 if (bmheader.raw_data) {
750 bmheader.w = bm->bm_w;
751 bmheader.h = bm->bm_h;
756 if (sig != form_sig) {
760 form_len = get_long(ifile);
762 form_type = get_sig(ifile);
764 if (form_type == anim_sig)
766 else if ((form_type == pbm_sig) || (form_type == ilbm_sig))
767 ret = iff_parse_ilbm_pbm(ifile,form_type,&bmheader,form_len,prev_bm);
769 ret = IFF_UNKNOWN_FORM;
771 if (ret != IFF_NO_ERROR) { //got an error parsing
772 if (bmheader.raw_data) d_free(bmheader.raw_data);
776 //If IFF file is ILBM, convert to PPB
777 if (bmheader.type == TYPE_ILBM) {
779 ret = convert_ilbm_to_pbm(&bmheader);
781 if (ret != IFF_NO_ERROR)
785 //Copy data from iff_bitmap_header structure into grs_bitmap structure
787 copy_iff_to_grs(bm,&bmheader);
790 if (palette) memcpy(palette,&bmheader.palette,sizeof(bmheader.palette));
792 // if (palette) memcpy(palette,&bmheader.palette, 768); // pal_entry is 4 bytes on mac
798 for (i = 0; i < 256; i++) {
799 *c++ = bmheader.palette[i].r;
800 *c++ = bmheader.palette[i].g;
801 *c++ = bmheader.palette[i].b;
806 //Now do post-process if required
808 if (bitmap_type == BM_RGB15) {
809 ret = convert_rgb15(bm,&bmheader);
810 if (ret != IFF_NO_ERROR)
818 //returns error codes - see IFF.H. see GR.H for bitmap_type
819 int iff_read_bitmap(char *ifilename,grs_bitmap *bm,int bitmap_type,ubyte *palette)
821 int ret; //return code
824 ret = open_fake_file(ifilename,&ifile); //read in entire file
825 if (ret == IFF_NO_ERROR) {
827 ret = iff_parse_bitmap(&ifile,bm,bitmap_type,(signed char *) palette,NULL);
830 if (ifile.data) d_free(ifile.data);
832 close_fake_file(&ifile);
839 //like iff_read_bitmap(), but reads into a bitmap that already exists,
840 //without allocating memory for the bitmap.
841 int iff_read_into_bitmap(char *ifilename, grs_bitmap *bm, sbyte *palette)
843 int ret; //return code
846 ret = open_fake_file(ifilename,&ifile); //read in entire file
847 if (ret == IFF_NO_ERROR) {
848 ret = iff_parse_bitmap(&ifile,bm,bm->bm_type,palette,NULL);
851 if (ifile.data) d_free(ifile.data);
853 close_fake_file(&ifile);
862 int write_bmhd(FILE *ofile,iff_bitmap_header *bitmap_header)
864 put_sig(bmhd_sig,ofile);
865 put_long((long) BMHD_SIZE,ofile);
867 put_word(bitmap_header->w,ofile);
868 put_word(bitmap_header->h,ofile);
869 put_word(bitmap_header->x,ofile);
870 put_word(bitmap_header->y,ofile);
872 put_byte(bitmap_header->nplanes,ofile);
873 put_byte(bitmap_header->masking,ofile);
874 put_byte(bitmap_header->compression,ofile);
875 put_byte(0,ofile); /* pad */
877 put_word(bitmap_header->transparentcolor,ofile);
878 put_byte(bitmap_header->xaspect,ofile);
879 put_byte(bitmap_header->yaspect,ofile);
881 put_word(bitmap_header->pagewidth,ofile);
882 put_word(bitmap_header->pageheight,ofile);
888 int write_pal(FILE *ofile,iff_bitmap_header *bitmap_header)
892 int n_colors = 1<<bitmap_header->nplanes;
894 put_sig(cmap_sig,ofile);
895 // put_long(sizeof(pal_entry) * n_colors,ofile);
896 put_long(3 * n_colors,ofile);
898 //printf("new write pal %d %d\n",3,n_colors);
900 for (i=0; i<256; i++) {
902 r = bitmap_header->palette[i].r * 4 + (bitmap_header->palette[i].r?3:0);
903 g = bitmap_header->palette[i].g * 4 + (bitmap_header->palette[i].g?3:0);
904 b = bitmap_header->palette[i].b * 4 + (bitmap_header->palette[i].b?3:0);
910 //printf("write pal %d %d\n",sizeof(pal_entry),n_colors);
911 // fwrite(bitmap_header->palette,sizeof(pal_entry),n_colors,ofile);
916 int rle_span(ubyte *dest,ubyte *src,int len)
918 int n,lit_cnt,rep_cnt;
919 ubyte last,*cnt_ptr,*dptr;
925 last=src[0]; lit_cnt=1;
927 for (n=1;n<len;n++) {
929 if (src[n] == last) {
934 while (n<len && rep_cnt<128 && src[n]==last) {n++; rep_cnt++;}
936 if (rep_cnt > 2 || lit_cnt < 2) {
938 if (lit_cnt > 1) {*cnt_ptr = lit_cnt-2; --dptr;} //save old lit count
939 *dptr++ = -(rep_cnt-1);
942 lit_cnt = (n<len)?1:0;
944 continue; //go to next char
952 cnt_ptr = dptr++; //save place for count
953 *dptr++=last; //store first char
956 *dptr++ = last = src[n];
958 if (lit_cnt == 127) {
972 *dptr++=last; //store first char
974 else if (lit_cnt > 1)
975 *cnt_ptr = lit_cnt-1;
980 #define EVEN(a) ((a+1)&0xfffffffel)
982 //returns length of chunk
983 int write_body(FILE *ofile,iff_bitmap_header *bitmap_header,int compression_on)
985 int w=bitmap_header->w,h=bitmap_header->h;
987 long len = EVEN(w) * h,newlen,total_len=0;
988 ubyte *p=bitmap_header->raw_data,*new_span;
991 put_sig(body_sig,ofile);
992 save_pos = ftell(ofile);
995 //if (! (new_span = d_malloc(bitmap_header->w+(bitmap_header->w/128+2)*2))) return IFF_NO_MEM;
996 MALLOC( new_span, ubyte, bitmap_header->w + (bitmap_header->w/128+2)*2);
997 if (new_span == NULL) return IFF_NO_MEM;
999 for (y=bitmap_header->h;y--;) {
1001 if (compression_on) {
1002 total_len += newlen = rle_span(new_span,p,bitmap_header->w+odd);
1003 fwrite(new_span,newlen,1,ofile);
1006 fwrite(p,bitmap_header->w+odd,1,ofile);
1008 p+=bitmap_header->row_size; //bitmap_header->w;
1011 if (compression_on) { //write actual data length
1012 Assert(fseek(ofile,save_pos,SEEK_SET)==0);
1013 put_long(total_len,ofile);
1014 Assert(fseek(ofile,total_len,SEEK_CUR)==0);
1015 if (total_len&1) fputc(0,ofile); //pad to even
1020 return ((compression_on) ? (EVEN(total_len)+8) : (len+8));
1025 //write a small representation of a bitmap. returns size
1026 int write_tiny(CFILE *ofile,iff_bitmap_header *bitmap_header,int compression_on)
1030 int len,total_len=0,newlen;
1032 ubyte *p = bitmap_header->raw_data;
1033 ubyte tspan[80],new_span[80*2];
1036 skip = max((bitmap_header->w+79)/80,(bitmap_header->h+63)/64);
1038 new_w = bitmap_header->w / skip;
1039 new_h = bitmap_header->h / skip;
1043 len = new_w * new_h + 4;
1045 put_sig(tiny_sig,ofile);
1046 save_pos = cftell(ofile);
1047 put_long(EVEN(len),ofile);
1049 put_word(new_w,ofile);
1050 put_word(new_h,ofile);
1052 for (y=0;y<new_h;y++) {
1053 for (x=xofs=0;x<new_w;x++,xofs+=skip)
1056 if (compression_on) {
1057 total_len += newlen = rle_span(new_span,tspan,new_w+odd);
1058 fwrite(new_span,newlen,1,ofile);
1061 fwrite(p,new_w+odd,1,ofile);
1063 p += skip * bitmap_header->row_size; //bitmap_header->w;
1067 if (compression_on) {
1068 Assert(cfseek(ofile,save_pos,SEEK_SET)==0);
1069 put_long(4+total_len,ofile);
1070 Assert(cfseek(ofile,4+total_len,SEEK_CUR)==0);
1071 if (total_len&1) cfputc(0,ofile); //pad to even
1074 return ((compression_on) ? (EVEN(total_len)+8+4) : (len+8));
1078 int write_pbm(FILE *ofile,iff_bitmap_header *bitmap_header,int compression_on) /* writes a pbm iff file */
1081 long raw_size = EVEN(bitmap_header->w) * bitmap_header->h;
1082 long body_size,tiny_size,pbm_size = 4 + BMHD_SIZE + 8 + EVEN(raw_size) + sizeof(pal_entry)*(1<<bitmap_header->nplanes)+8;
1085 //printf("write_pbm\n");
1087 put_sig(form_sig,ofile);
1088 save_pos = ftell(ofile);
1089 put_long(pbm_size+8,ofile);
1090 put_sig(pbm_sig,ofile);
1092 ret = write_bmhd(ofile,bitmap_header);
1093 if (ret != IFF_NO_ERROR) return ret;
1095 ret = write_pal(ofile,bitmap_header);
1096 if (ret != IFF_NO_ERROR) return ret;
1099 tiny_size = write_tiny(ofile,bitmap_header,compression_on);
1104 body_size = write_body(ofile,bitmap_header,compression_on);
1106 pbm_size = 4 + BMHD_SIZE + body_size + tiny_size + sizeof(pal_entry)*(1<<bitmap_header->nplanes)+8;
1108 Assert(fseek(ofile,save_pos,SEEK_SET)==0);
1109 put_long(pbm_size+8,ofile);
1110 Assert(fseek(ofile,pbm_size+8,SEEK_CUR)==0);
1116 //writes an IFF file from a grs_bitmap structure. writes palette if not null
1117 //returns error codes - see IFF.H.
1118 int iff_write_bitmap(char *ofilename,grs_bitmap *bm,ubyte *palette)
1121 iff_bitmap_header bmheader;
1125 if (bm->bm_type == BM_RGB15) return IFF_BAD_BM_TYPE;
1128 compression_on = (bm->bm_w>=MIN_COMPRESS_WIDTH);
1133 //fill in values in bmheader
1135 bmheader.x = bmheader.y = 0;
1136 bmheader.w = bm->bm_w;
1137 bmheader.h = bm->bm_h;
1138 bmheader.type = TYPE_PBM;
1139 bmheader.transparentcolor = iff_transparent_color;
1140 bmheader.pagewidth = bm->bm_w; //I don't think it matters what I write
1141 bmheader.pageheight = bm->bm_h;
1142 bmheader.nplanes = 8;
1143 bmheader.masking = mskNone;
1144 if (iff_has_transparency) {
1145 bmheader.masking |= mskHasTransparentColor;
1147 bmheader.compression = (compression_on?cmpByteRun1:cmpNone);
1149 bmheader.xaspect = bmheader.yaspect = 1; //I don't think it matters what I write
1150 bmheader.raw_data = bm->bm_data;
1151 bmheader.row_size = bm->bm_rowsize;
1153 if (palette) memcpy(&bmheader.palette,palette,256*3);
1155 //open file and write
1157 if ((ofile = fopen(ofilename,"wb")) == NULL) {
1160 ret = write_pbm(ofile,&bmheader,compression_on);
1168 //read in many brushes. fills in array of pointers, and n_bitmaps.
1169 //returns iff error codes
1170 int iff_read_animbrush(char *ifilename,grs_bitmap **bm_list,int max_bitmaps,int *n_bitmaps,ubyte *palette)
1172 int ret; //return code
1174 iff_bitmap_header bmheader;
1180 ret = open_fake_file(ifilename,&ifile); //read in entire file
1181 if (ret != IFF_NO_ERROR) goto done;
1183 bmheader.raw_data = NULL;
1185 sig=get_sig(&ifile);
1186 form_len = get_long(&ifile);
1188 if (sig != form_sig) {
1193 form_type = get_sig(&ifile);
1195 if ((form_type == pbm_sig) || (form_type == ilbm_sig))
1196 ret = IFF_FORM_BITMAP;
1197 else if (form_type == anim_sig) {
1198 int anim_end = ifile.position + form_len - 4;
1200 while (ifile.position < anim_end && *n_bitmaps < max_bitmaps) {
1202 grs_bitmap *prev_bm;
1204 prev_bm = *n_bitmaps>0?bm_list[*n_bitmaps-1]:NULL;
1206 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:(signed char *)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;