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.
16 * Routines for reading and writing IFF files
24 #define COMPRESS 1 //do the RLE or not? (for debugging mostly)
25 #define WRITE_TINY 0 //should we write a TINY chunk?
27 #define MIN_COMPRESS_WIDTH 65 //don't compress if less than this wide
36 //#include "nocfile.h"
40 //Internal constants and structures for this library
42 //Type values for bitmaps
53 #define mskHasTransparentColor 2
55 //Palette entry structure
56 typedef struct pal_entry {
60 //structure of the header in the file
61 typedef struct iff_bitmap_header {
62 short w,h; //width and height of this bitmap
63 short x,y; //generally unused
64 short type; //see types above
65 short transparentcolor; //which color is transparent (if any)
66 short pagewidth,pageheight; //width & height of source screen
67 sbyte nplanes; //number of planes (8 for 256 color image)
68 sbyte masking,compression; //see constants above
69 sbyte xaspect,yaspect; //aspect ratio (usually 5/6)
70 pal_entry palette[256]; //the palette for this bitmap
71 ubyte *raw_data; //ptr to array of data
72 short row_size; //offset to next row
75 ubyte iff_transparent_color;
76 ubyte iff_has_transparency; // 0=no transparency, 1=iff_transparent_color is valid
78 typedef struct fake_file {
84 #define MAKE_SIG(a,b,c,d) (((int32_t)(a)<<24)+((int32_t)(b)<<16)+((c)<<8)+(d))
86 #define form_sig MAKE_SIG('F','O','R','M')
87 #define ilbm_sig MAKE_SIG('I','L','B','M')
88 #define body_sig MAKE_SIG('B','O','D','Y')
89 #define pbm_sig MAKE_SIG('P','B','M',' ')
90 #define bmhd_sig MAKE_SIG('B','M','H','D')
91 #define anhd_sig MAKE_SIG('A','N','H','D')
92 #define cmap_sig MAKE_SIG('C','M','A','P')
93 #define tiny_sig MAKE_SIG('T','I','N','Y')
94 #define anim_sig MAKE_SIG('A','N','I','M')
95 #define dlta_sig MAKE_SIG('D','L','T','A')
98 //void printsig(int32_t s)
100 // char *t=(char *) &s;
102 ///* printf("%c%c%c%c",*(&s+3),*(&s+2),*(&s+1),s);*/
103 // printf("%c%c%c%c",t[3],t[2],t[1],t[0]);
107 int32_t get_sig(FFILE *f)
111 // if ((s[3]=cfgetc(f))==EOF) return(EOF);
112 // if ((s[2]=cfgetc(f))==EOF) return(EOF);
113 // if ((s[1]=cfgetc(f))==EOF) return(EOF);
114 // if ((s[0]=cfgetc(f))==EOF) return(EOF);
116 #ifndef WORDS_BIGENDIAN
117 if (f->position>=f->length) return EOF;
118 s[3] = f->data[f->position++];
119 if (f->position>=f->length) return EOF;
120 s[2] = f->data[f->position++];
121 if (f->position>=f->length) return EOF;
122 s[1] = f->data[f->position++];
123 if (f->position>=f->length) return EOF;
124 s[0] = f->data[f->position++];
126 if (f->position>=f->length) return EOF;
127 s[0] = 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[2] = f->data[f->position++];
132 if (f->position>=f->length) return EOF;
133 s[3] = f->data[f->position++];
136 return(*((int32_t *) s));
139 int put_sig(int32_t sig,FILE *f)
141 char *s = (char *) &sig;
146 return fputc(s[0],f);
150 char get_byte(FFILE *f)
153 return f->data[f->position++];
156 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 sbyte *new_data, *destptr, *rowptr;
609 int bytes_per_row,byteofs;
610 ubyte checkmask,newbyte,setbit;
612 MALLOC(new_data, sbyte, 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 = (signed char *) &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 = (unsigned char *) 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, sbyte *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) {
755 form_len = get_long(ifile);
757 form_type = get_sig(ifile);
759 if (form_type == anim_sig)
761 else if ((form_type == pbm_sig) || (form_type == ilbm_sig))
762 ret = iff_parse_ilbm_pbm(ifile,form_type,&bmheader,form_len,prev_bm);
764 ret = IFF_UNKNOWN_FORM;
766 if (ret != IFF_NO_ERROR) { //got an error parsing
767 if (bmheader.raw_data) d_free(bmheader.raw_data);
771 //If IFF file is ILBM, convert to PPB
772 if (bmheader.type == TYPE_ILBM) {
774 ret = convert_ilbm_to_pbm(&bmheader);
776 if (ret != IFF_NO_ERROR)
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)
813 //returns error codes - see IFF.H. see GR.H for bitmap_type
814 int iff_read_bitmap(char *ifilename,grs_bitmap *bm,int bitmap_type,ubyte *palette)
816 int ret; //return code
819 ret = open_fake_file(ifilename,&ifile); //read in entire file
820 if (ret == IFF_NO_ERROR) {
822 ret = iff_parse_bitmap(&ifile,bm,bitmap_type,(signed char *) palette,NULL);
825 if (ifile.data) d_free(ifile.data);
827 close_fake_file(&ifile);
834 //like iff_read_bitmap(), but reads into a bitmap that already exists,
835 //without allocating memory for the bitmap.
836 int iff_read_into_bitmap(char *ifilename, grs_bitmap *bm, sbyte *palette)
838 int ret; //return code
841 ret = open_fake_file(ifilename,&ifile); //read in entire file
842 if (ret == IFF_NO_ERROR) {
843 ret = iff_parse_bitmap(&ifile,bm,bm->bm_type,palette,NULL);
846 if (ifile.data) d_free(ifile.data);
848 close_fake_file(&ifile);
857 int write_bmhd(FILE *ofile,iff_bitmap_header *bitmap_header)
859 put_sig(bmhd_sig,ofile);
860 put_long((long) BMHD_SIZE,ofile);
862 put_word(bitmap_header->w,ofile);
863 put_word(bitmap_header->h,ofile);
864 put_word(bitmap_header->x,ofile);
865 put_word(bitmap_header->y,ofile);
867 put_byte(bitmap_header->nplanes,ofile);
868 put_byte(bitmap_header->masking,ofile);
869 put_byte(bitmap_header->compression,ofile);
870 put_byte(0,ofile); /* pad */
872 put_word(bitmap_header->transparentcolor,ofile);
873 put_byte(bitmap_header->xaspect,ofile);
874 put_byte(bitmap_header->yaspect,ofile);
876 put_word(bitmap_header->pagewidth,ofile);
877 put_word(bitmap_header->pageheight,ofile);
883 int write_pal(FILE *ofile,iff_bitmap_header *bitmap_header)
887 int n_colors = 1<<bitmap_header->nplanes;
889 put_sig(cmap_sig,ofile);
890 // put_long(sizeof(pal_entry) * n_colors,ofile);
891 put_long(3 * n_colors,ofile);
893 //printf("new write pal %d %d\n",3,n_colors);
895 for (i=0; i<256; i++) {
897 r = bitmap_header->palette[i].r * 4 + (bitmap_header->palette[i].r?3:0);
898 g = bitmap_header->palette[i].g * 4 + (bitmap_header->palette[i].g?3:0);
899 b = bitmap_header->palette[i].b * 4 + (bitmap_header->palette[i].b?3:0);
905 //printf("write pal %d %d\n",sizeof(pal_entry),n_colors);
906 // fwrite(bitmap_header->palette,sizeof(pal_entry),n_colors,ofile);
911 int rle_span(ubyte *dest,ubyte *src,int len)
913 int n,lit_cnt,rep_cnt;
914 ubyte last,*cnt_ptr,*dptr;
920 last=src[0]; lit_cnt=1;
922 for (n=1;n<len;n++) {
924 if (src[n] == last) {
929 while (n<len && rep_cnt<128 && src[n]==last) {n++; rep_cnt++;}
931 if (rep_cnt > 2 || lit_cnt < 2) {
933 if (lit_cnt > 1) {*cnt_ptr = lit_cnt-2; --dptr;} //save old lit count
934 *dptr++ = -(rep_cnt-1);
937 lit_cnt = (n<len)?1:0;
939 continue; //go to next char
947 cnt_ptr = dptr++; //save place for count
948 *dptr++=last; //store first char
951 *dptr++ = last = src[n];
953 if (lit_cnt == 127) {
967 *dptr++=last; //store first char
969 else if (lit_cnt > 1)
970 *cnt_ptr = lit_cnt-1;
975 #define EVEN(a) ((a+1)&0xfffffffel)
977 //returns length of chunk
978 int write_body(FILE *ofile,iff_bitmap_header *bitmap_header,int compression_on)
980 int w=bitmap_header->w,h=bitmap_header->h;
982 long len = EVEN(w) * h,newlen,total_len=0;
983 ubyte *p=bitmap_header->raw_data,*new_span;
986 put_sig(body_sig,ofile);
987 save_pos = ftell(ofile);
990 //if (! (new_span = d_malloc(bitmap_header->w+(bitmap_header->w/128+2)*2))) return IFF_NO_MEM;
991 MALLOC( new_span, ubyte, bitmap_header->w + (bitmap_header->w/128+2)*2);
992 if (new_span == NULL) return IFF_NO_MEM;
994 for (y=bitmap_header->h;y--;) {
996 if (compression_on) {
997 total_len += newlen = rle_span(new_span,p,bitmap_header->w+odd);
998 fwrite(new_span,newlen,1,ofile);
1001 fwrite(p,bitmap_header->w+odd,1,ofile);
1003 p+=bitmap_header->row_size; //bitmap_header->w;
1006 if (compression_on) { //write actual data length
1007 Assert(fseek(ofile,save_pos,SEEK_SET)==0);
1008 put_long(total_len,ofile);
1009 Assert(fseek(ofile,total_len,SEEK_CUR)==0);
1010 if (total_len&1) fputc(0,ofile); //pad to even
1015 return ((compression_on) ? (EVEN(total_len)+8) : (len+8));
1020 //write a small representation of a bitmap. returns size
1021 int write_tiny(CFILE *ofile,iff_bitmap_header *bitmap_header,int compression_on)
1025 int len,total_len=0,newlen;
1027 ubyte *p = bitmap_header->raw_data;
1028 ubyte tspan[80],new_span[80*2];
1031 skip = max((bitmap_header->w+79)/80,(bitmap_header->h+63)/64);
1033 new_w = bitmap_header->w / skip;
1034 new_h = bitmap_header->h / skip;
1038 len = new_w * new_h + 4;
1040 put_sig(tiny_sig,ofile);
1041 save_pos = cftell(ofile);
1042 put_long(EVEN(len),ofile);
1044 put_word(new_w,ofile);
1045 put_word(new_h,ofile);
1047 for (y=0;y<new_h;y++) {
1048 for (x=xofs=0;x<new_w;x++,xofs+=skip)
1051 if (compression_on) {
1052 total_len += newlen = rle_span(new_span,tspan,new_w+odd);
1053 fwrite(new_span,newlen,1,ofile);
1056 fwrite(p,new_w+odd,1,ofile);
1058 p += skip * bitmap_header->row_size; //bitmap_header->w;
1062 if (compression_on) {
1063 Assert(cfseek(ofile,save_pos,SEEK_SET)==0);
1064 put_long(4+total_len,ofile);
1065 Assert(cfseek(ofile,4+total_len,SEEK_CUR)==0);
1066 if (total_len&1) cfputc(0,ofile); //pad to even
1069 return ((compression_on) ? (EVEN(total_len)+8+4) : (len+8));
1073 int write_pbm(FILE *ofile,iff_bitmap_header *bitmap_header,int compression_on) /* writes a pbm iff file */
1076 long raw_size = EVEN(bitmap_header->w) * bitmap_header->h;
1077 long body_size,tiny_size,pbm_size = 4 + BMHD_SIZE + 8 + EVEN(raw_size) + sizeof(pal_entry)*(1<<bitmap_header->nplanes)+8;
1080 //printf("write_pbm\n");
1082 put_sig(form_sig,ofile);
1083 save_pos = ftell(ofile);
1084 put_long(pbm_size+8,ofile);
1085 put_sig(pbm_sig,ofile);
1087 ret = write_bmhd(ofile,bitmap_header);
1088 if (ret != IFF_NO_ERROR) return ret;
1090 ret = write_pal(ofile,bitmap_header);
1091 if (ret != IFF_NO_ERROR) return ret;
1094 tiny_size = write_tiny(ofile,bitmap_header,compression_on);
1099 body_size = write_body(ofile,bitmap_header,compression_on);
1101 pbm_size = 4 + BMHD_SIZE + body_size + tiny_size + sizeof(pal_entry)*(1<<bitmap_header->nplanes)+8;
1103 Assert(fseek(ofile,save_pos,SEEK_SET)==0);
1104 put_long(pbm_size+8,ofile);
1105 Assert(fseek(ofile,pbm_size+8,SEEK_CUR)==0);
1111 //writes an IFF file from a grs_bitmap structure. writes palette if not null
1112 //returns error codes - see IFF.H.
1113 int iff_write_bitmap(char *ofilename,grs_bitmap *bm,ubyte *palette)
1116 iff_bitmap_header bmheader;
1120 if (bm->bm_type == BM_RGB15) return IFF_BAD_BM_TYPE;
1123 compression_on = (bm->bm_w>=MIN_COMPRESS_WIDTH);
1128 //fill in values in bmheader
1130 bmheader.x = bmheader.y = 0;
1131 bmheader.w = bm->bm_w;
1132 bmheader.h = bm->bm_h;
1133 bmheader.type = TYPE_PBM;
1134 bmheader.transparentcolor = iff_transparent_color;
1135 bmheader.pagewidth = bm->bm_w; //I don't think it matters what I write
1136 bmheader.pageheight = bm->bm_h;
1137 bmheader.nplanes = 8;
1138 bmheader.masking = mskNone;
1139 if (iff_has_transparency) {
1140 bmheader.masking |= mskHasTransparentColor;
1142 bmheader.compression = (compression_on?cmpByteRun1:cmpNone);
1144 bmheader.xaspect = bmheader.yaspect = 1; //I don't think it matters what I write
1145 bmheader.raw_data = bm->bm_data;
1146 bmheader.row_size = bm->bm_rowsize;
1148 if (palette) memcpy(&bmheader.palette,palette,256*3);
1150 //open file and write
1152 if ((ofile = fopen(ofilename,"wb")) == NULL) {
1155 ret = write_pbm(ofile,&bmheader,compression_on);
1163 //read in many brushes. fills in array of pointers, and n_bitmaps.
1164 //returns iff error codes
1165 int iff_read_animbrush(char *ifilename,grs_bitmap **bm_list,int max_bitmaps,int *n_bitmaps,ubyte *palette)
1167 int ret; //return code
1169 iff_bitmap_header bmheader;
1175 ret = open_fake_file(ifilename,&ifile); //read in entire file
1176 if (ret != IFF_NO_ERROR) goto done;
1178 bmheader.raw_data = NULL;
1180 sig=get_sig(&ifile);
1181 form_len = get_long(&ifile);
1183 if (sig != form_sig) {
1188 form_type = get_sig(&ifile);
1190 if ((form_type == pbm_sig) || (form_type == ilbm_sig))
1191 ret = IFF_FORM_BITMAP;
1192 else if (form_type == anim_sig) {
1193 int anim_end = ifile.position + form_len - 4;
1195 while (ifile.position < anim_end && *n_bitmaps < max_bitmaps) {
1197 grs_bitmap *prev_bm;
1199 prev_bm = *n_bitmaps>0?bm_list[*n_bitmaps-1]:NULL;
1201 MALLOC(bm_list[*n_bitmaps] , grs_bitmap, 1 );
1202 bm_list[*n_bitmaps]->bm_data = NULL;
1204 ret = iff_parse_bitmap(&ifile,bm_list[*n_bitmaps],form_type,*n_bitmaps>0?NULL:(signed char *)palette,prev_bm);
1206 if (ret != IFF_NO_ERROR)
1212 if (ifile.position < anim_end) //ran out of room
1213 ret = IFF_TOO_MANY_BMS;
1217 ret = IFF_UNKNOWN_FORM;
1221 close_fake_file(&ifile);
1227 //text for error messges
1228 char error_messages[] = {
1230 "Not enough mem for loading or processing bitmap.\0"
1231 "IFF file has unknown FORM type.\0"
1232 "Not an IFF file.\0"
1233 "Cannot open file.\0"
1234 "Tried to save invalid type, like BM_RGB15.\0"
1235 "Bad data in file.\0"
1236 "ANIM file cannot be loaded with normal bitmap loader.\0"
1237 "Normal bitmap file cannot be loaded with anim loader.\0"
1238 "Array not big enough on anim brush read.\0"
1239 "Unknown mask type in bitmap header.\0"
1240 "Error reading file.\0"
1244 //function to return pointer to error message
1245 char *iff_errormsg(int error_number)
1247 char *p = error_messages;
1249 while (error_number--) {
1251 if (!p) return NULL;