]> icculus.org git repositories - btb/d2x.git/blob - iff/iff.c
Got rid of all compiler warnings, for non-OpenGL on linux, anyway...
[btb/d2x.git] / iff / iff.c
1 /*
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.
12 */
13
14 #ifdef RCS
15 static char rcsid[] = "$Id: iff.c,v 1.2 2001-01-20 13:49:14 bradleyb Exp $";
16 #endif
17
18 #include <conf.h>
19
20 #define COMPRESS                1       //do the RLE or not? (for debugging mostly)
21 #define WRITE_TINY      0       //should we write a TINY chunk?
22
23 #define MIN_COMPRESS_WIDTH      65      //don't compress if less than this wide
24
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28
29 #include "u_mem.h"
30 #include "iff.h"
31
32 //#include "nocfile.h"
33 #include "cfile.h"
34 #include "error.h"
35
36 //Internal constants and structures for this library
37
38 //Type values for bitmaps
39 #define TYPE_PBM                0
40 #define TYPE_ILBM               1
41
42 //Compression types
43 #define cmpNone 0
44 #define cmpByteRun1     1
45
46 //Masking types
47 #define mskNone 0
48 #define mskHasMask      1
49 #define mskHasTransparentColor 2
50
51 //Palette entry structure
52 typedef struct pal_entry {byte r,g,b;} pal_entry;
53
54 //structure of the header in the file
55 typedef struct iff_bitmap_header {
56         short w,h;                                              //width and height of this bitmap
57         short x,y;                                              //generally unused
58         short type;                                             //see types above
59         short transparentcolor;         //which color is transparent (if any)
60         short pagewidth,pageheight; //width & height of source screen
61         byte nplanes;                                   //number of planes (8 for 256 color image)
62         byte masking,compression;       //see constants above
63         byte xaspect,yaspect;           //aspect ratio (usually 5/6)
64         pal_entry palette[256];         //the palette for this bitmap
65         ubyte *raw_data;                                //ptr to array of data
66         short row_size;                         //offset to next row
67 } iff_bitmap_header;
68
69 ubyte iff_transparent_color;
70 ubyte iff_has_transparency;     // 0=no transparency, 1=iff_transparent_color is valid
71
72 typedef struct fake_file {
73         ubyte *data;
74         int position;
75         int length;
76 } FFILE;
77
78 #define MIN(a,b) ((a<b)?a:b)
79
80 #define MAKE_SIG(a,b,c,d) (((long)(a)<<24)+((long)(b)<<16)+((c)<<8)+(d))
81
82 #define form_sig MAKE_SIG('F','O','R','M')
83 #define ilbm_sig MAKE_SIG('I','L','B','M')
84 #define body_sig MAKE_SIG('B','O','D','Y')
85 #define pbm_sig  MAKE_SIG('P','B','M',' ')
86 #define bmhd_sig MAKE_SIG('B','M','H','D')
87 #define anhd_sig MAKE_SIG('A','N','H','D')
88 #define cmap_sig MAKE_SIG('C','M','A','P')
89 #define tiny_sig MAKE_SIG('T','I','N','Y')
90 #define anim_sig MAKE_SIG('A','N','I','M')
91 #define dlta_sig MAKE_SIG('D','L','T','A')
92
93 #ifndef NDEBUG
94 //void printsig(long s)
95 //{
96 //      char *t=(char *) &s;
97 //
98 ///*  printf("%c%c%c%c",*(&s+3),*(&s+2),*(&s+1),s);*/
99 //      printf("%c%c%c%c",t[3],t[2],t[1],t[0]);
100 //}
101 #endif
102
103 long get_sig(FFILE *f)
104 {
105         char s[4];
106
107 //      if ((s[3]=cfgetc(f))==EOF) return(EOF);
108 //      if ((s[2]=cfgetc(f))==EOF) return(EOF);
109 //      if ((s[1]=cfgetc(f))==EOF) return(EOF);
110 //      if ((s[0]=cfgetc(f))==EOF) return(EOF);
111
112 #ifndef MACINTOSH
113         if (f->position>=f->length) return EOF;
114         s[3] = f->data[f->position++];
115         if (f->position>=f->length) return EOF;
116         s[2] = f->data[f->position++];
117         if (f->position>=f->length) return EOF;
118         s[1] = f->data[f->position++];
119         if (f->position>=f->length) return EOF;
120         s[0] = f->data[f->position++];
121 #else
122         if (f->position>=f->length) return EOF;
123         s[0] = f->data[f->position++];
124         if (f->position>=f->length) return EOF;
125         s[1] = f->data[f->position++];
126         if (f->position>=f->length) return EOF;
127         s[2] = f->data[f->position++];
128         if (f->position>=f->length) return EOF;
129         s[3] = f->data[f->position++];
130 #endif
131
132         return(*((long *) s));
133 }
134
135 int put_sig(long sig,FILE *f)
136 {
137         char *s = (char *) &sig;
138
139         fputc(s[3],f);
140         fputc(s[2],f);
141         fputc(s[1],f);
142         return fputc(s[0],f);
143
144 }
145
146 char get_byte(FFILE *f)
147 {
148         //return cfgetc(f);
149         return f->data[f->position++];
150 }
151
152 int put_byte(unsigned char c,FILE *f)
153 {
154         return fputc(c,f);
155 }
156
157         
158
159 int get_word(FFILE *f)
160 {
161         unsigned char c0,c1;
162
163 //      c1=cfgetc(f);
164 //      c0=cfgetc(f);
165
166         if (f->position>=f->length) return EOF;
167         c1 = f->data[f->position++];
168         if (f->position>=f->length) return EOF;
169         c0 = f->data[f->position++];
170
171         if (c0==0xff) return(EOF);
172
173         return(((int)c1<<8) + c0);
174
175 }
176
177 int put_word(int n,FILE *f)
178 {
179         unsigned char c0,c1;
180
181         c0 = (n & 0xff00) >> 8;
182         c1 = n & 0xff;
183
184         put_byte(c0,f);
185         return put_byte(c1,f);
186 }
187
188 int put_long(long n,FILE *f)
189 {
190         int n0,n1;
191
192         n0 = (int) ((n & 0xffff0000l) >> 16);
193         n1 = (int) (n & 0xffff);
194
195         put_word(n0,f);
196         return put_word(n1,f);
197
198 }
199
200 long get_long(FFILE *f)
201 {
202         unsigned char c0,c1,c2,c3;
203
204         //c3=cfgetc(f);
205         //c2=cfgetc(f);
206         //c1=cfgetc(f);
207         //c0=cfgetc(f);
208
209         if (f->position>=f->length) return EOF;
210         c3 = f->data[f->position++];
211         if (f->position>=f->length) return EOF;
212         c2 = f->data[f->position++];
213         if (f->position>=f->length) return EOF;
214         c1 = f->data[f->position++];
215         if (f->position>=f->length) return EOF;
216         c0 = f->data[f->position++];
217
218 //printf("get_long %x %x %x %x\n",c3,c2,c1,c0);
219
220 //  if (c0==0xff) return(EOF);
221
222         return(((long)c3<<24) + ((long)c2<<16) + ((long)c1<<8) + c0);
223
224 }
225
226 int parse_bmhd(FFILE *ifile,long len,iff_bitmap_header *bmheader)
227 {
228         len++;  /* so no "parm not used" warning */
229
230 //  debug("parsing bmhd len=%ld\n",len);
231
232         bmheader->w = get_word(ifile);
233         bmheader->h = get_word(ifile);
234         bmheader->x = get_word(ifile);
235         bmheader->y = get_word(ifile);
236
237         bmheader->nplanes = get_byte(ifile);
238         bmheader->masking = get_byte(ifile);
239         bmheader->compression = get_byte(ifile);
240         get_byte(ifile);        /* skip pad */
241
242         bmheader->transparentcolor = get_word(ifile);
243         bmheader->xaspect = get_byte(ifile);
244         bmheader->yaspect = get_byte(ifile);
245
246         bmheader->pagewidth = get_word(ifile);
247         bmheader->pageheight = get_word(ifile);
248
249         iff_transparent_color = bmheader->transparentcolor;
250
251         iff_has_transparency = 0;
252
253         if (bmheader->masking == mskHasTransparentColor)
254                 iff_has_transparency = 1;
255
256         else if (bmheader->masking != mskNone && bmheader->masking != mskHasMask)
257                 return IFF_UNKNOWN_MASK;
258
259 //  debug("w,h=%d,%d x,y=%d,%d\n",w,h,x,y);
260 //  debug("nplanes=%d, masking=%d ,compression=%d, transcolor=%d\n",nplanes,masking,compression,transparentcolor);
261
262         return IFF_NO_ERROR;
263 }
264
265
266 //  the buffer pointed to by raw_data is stuffed with a pointer to decompressed pixel data
267 int parse_body(FFILE *ifile,long len,iff_bitmap_header *bmheader)
268 {
269         unsigned char  *p=bmheader->raw_data;
270         int width,depth;
271         signed char n;
272         int nn,wid_cnt,end_cnt,plane;
273         char ignore=0;
274         unsigned char *data_end;
275         int end_pos;
276         int row_count=0;
277
278         width=0;
279         depth=0;
280
281         end_pos = ifile->position + len;
282         if (len&1)
283                 end_pos++;
284
285         if (bmheader->type == TYPE_PBM) {
286                 width=bmheader->w;
287                 depth=1;
288         } else if (bmheader->type == TYPE_ILBM) {
289                 width = (bmheader->w+7)/8;
290                 depth=bmheader->nplanes;
291         }
292
293         end_cnt = (width&1)?-1:0;
294
295         data_end = p + width*bmheader->h*depth;
296
297         if (bmheader->compression == cmpNone) {        /* no compression */
298                 int y;
299
300                 for (y=bmheader->h;y;y--) {
301                         int x;
302 //                      for (x=bmheader->w;x;x--) *p++=cfgetc(ifile);
303 //                      cfread(p, bmheader->w, 1, ifile);
304 //                      p += bmheader->w;
305
306                         for (x=0;x<width*depth;x++)
307                                 *p++=ifile->data[ifile->position++];
308
309                         if (bmheader->masking == mskHasMask)
310                                 ifile->position += width;                               //skip mask!
311
312 //                      if (bmheader->w & 1) ignore = cfgetc(ifile);
313                         if (bmheader->w & 1) ifile->position++;
314                 }
315
316                 //cnt = len - bmheader->h * ((bmheader->w+1)&~1);
317
318         }
319         else if (bmheader->compression == cmpByteRun1)
320                 for (wid_cnt=width,plane=0;ifile->position<end_pos && p<data_end;) {
321                         unsigned char c;
322
323 //                      if (old_cnt-cnt > 2048) {
324 //              printf(".");
325 //                              old_cnt=cnt;
326 //                      }
327
328                         if (wid_cnt == end_cnt) {
329                                 wid_cnt = width;
330                                 plane++;
331                                 if ((bmheader->masking == mskHasMask && plane==depth+1) ||
332                                     (bmheader->masking != mskHasMask && plane==depth))
333                                         plane=0;
334                         }
335
336                         Assert(wid_cnt > end_cnt);
337
338                         //n=cfgetc(ifile);
339                         n=ifile->data[ifile->position++];
340
341                         if (n >= 0) {                       // copy next n+1 bytes from source, they are not compressed
342                                 nn = (int) n+1;
343                                 wid_cnt -= nn;
344                                 if (wid_cnt==-1) {--nn; Assert(width&1);}
345                                 if (plane==depth)       //masking row
346                                         ifile->position += nn;
347                                 else
348                                         while (nn--) *p++=ifile->data[ifile->position++];
349                                 if (wid_cnt==-1) ifile->position++;
350                         }
351                         else if (n>=-127) {             // next -n + 1 bytes are following byte
352                                 c=ifile->data[ifile->position++];
353                                 nn = (int) -n+1;
354                                 wid_cnt -= nn;
355                                 if (wid_cnt==-1) {--nn; Assert(width&1);}
356                                 if (plane!=depth)       //not masking row
357                                         {memset(p,c,nn); p+=nn;}
358                         }
359
360                         #ifndef NDEBUG
361                         if ((p-bmheader->raw_data) % width == 0)
362                                         row_count++;
363
364                         Assert((p-bmheader->raw_data) - (width*row_count) < width);
365                         #endif
366
367                 }
368
369         if (p!=data_end)                                //if we don't have the whole bitmap...
370                 return IFF_CORRUPT;             //...the give an error
371
372         //Pretend we read the whole chuck, because if we didn't, it's because
373         //we didn't read the last mask like or the last rle record for padding
374         //or whatever and it's not important, because we check to make sure
375         //we got the while bitmap, and that's what really counts.
376
377         ifile->position = end_pos;
378         
379         if (ignore) ignore++;   // haha, suppress the evil warning message
380
381         return IFF_NO_ERROR;
382 }
383
384 //modify passed bitmap
385 int parse_delta(FFILE *ifile,long len,iff_bitmap_header *bmheader)
386 {
387         unsigned char  *p=bmheader->raw_data;
388         int y;
389         long chunk_end = ifile->position + len;
390
391         get_long(ifile);                //longword, seems to be equal to 4.  Don't know what it is
392
393         for (y=0;y<bmheader->h;y++) {
394                 ubyte n_items;
395                 int cnt = bmheader->w;
396                 ubyte code;
397
398                 n_items = get_byte(ifile);
399
400                 while (n_items--) {
401
402                         code = get_byte(ifile);
403
404                         if (code==0) {                          //repeat
405                                 ubyte rep,val;
406
407                                 rep = get_byte(ifile);
408                                 val = get_byte(ifile);
409
410                                 cnt -= rep;
411                                 if (cnt==-1)
412                                         rep--;
413                                 while (rep--)
414                                         *p++ = val;
415                         }
416                         else if (code > 0x80) { //skip
417                                 cnt -= (code-0x80);
418                                 p += (code-0x80);
419                                 if (cnt==-1)
420                                         p--;
421                         }
422                         else {                                          //literal
423                                 cnt -= code;
424                                 if (cnt==-1)
425                                         code--;
426
427                                 while (code--)
428                                         *p++ = get_byte(ifile);
429
430                                 if (cnt==-1)
431                                         get_byte(ifile);
432                         }
433
434                 }
435
436                 if (cnt == -1) {
437                         if (!bmheader->w&1)
438                                 return IFF_CORRUPT;
439                 }
440                 else if (cnt)
441                         return IFF_CORRUPT;
442         }
443
444         if (ifile->position == chunk_end-1)             //pad
445                 ifile->position++;
446
447         if (ifile->position != chunk_end)
448                 return IFF_CORRUPT;
449         else
450                 return IFF_NO_ERROR;
451 }
452
453 //  the buffer pointed to by raw_data is stuffed with a pointer to bitplane pixel data
454 void skip_chunk(FFILE *ifile,long len)
455 {
456         //int c,i;
457         int ilen;
458         ilen = (len+1) & ~1;
459
460 //printf( "Skipping %d chunk\n", ilen );
461
462         ifile->position += ilen;
463
464         if (ifile->position >= ifile->length )  {
465                 ifile->position = ifile->length;
466         }
467
468
469 //      for (i=0; i<ilen; i++ )
470 //              c = cfgetc(ifile);
471         //Assert(cfseek(ifile,ilen,SEEK_CUR)==0);
472 }
473
474 //read an ILBM or PBM file
475 // Pass pointer to opened file, and to empty bitmap_header structure, and form length
476 int iff_parse_ilbm_pbm(FFILE *ifile,long form_type,iff_bitmap_header *bmheader,int form_len,grs_bitmap *prev_bm)
477 {
478         long sig,len;
479         //char ignore=0;
480         long start_pos,end_pos;
481
482         start_pos = ifile->position;
483         end_pos = start_pos-4+form_len;
484
485 //      printf(" %ld ",form_len);
486 //      printsig(form_type);
487 //      printf("\n");
488
489                         if (form_type == pbm_sig)
490                                 bmheader->type = TYPE_PBM;
491                         else
492                                 bmheader->type = TYPE_ILBM;
493
494                         while ((ifile->position < end_pos) && (sig=get_sig(ifile)) != EOF) {
495
496                                 len=get_long(ifile);
497                                 if (len==EOF) break;
498
499 //              printf(" ");
500 //              printsig(sig);
501 //              printf(" %ld\n",len);
502
503                                 switch (sig) {
504
505                                         case bmhd_sig: {
506                                                 int ret;
507                                                 int save_w=bmheader->w,save_h=bmheader->h;
508
509                                                 //printf("Parsing header\n");
510
511                                                 ret = parse_bmhd(ifile,len,bmheader);
512
513                                                 if (ret != IFF_NO_ERROR)
514                                                         return ret;
515
516                                                 if (bmheader->raw_data) {
517
518                                                         if (save_w != bmheader->w  ||  save_h != bmheader->h)
519                                                                 return IFF_BM_MISMATCH;
520
521                                                 }
522                                                 else {
523
524                                                         MALLOC( bmheader->raw_data, ubyte, bmheader->w * bmheader->h );
525                                                         if (!bmheader->raw_data)
526                                                                 return IFF_NO_MEM;
527                                                 }
528
529                                                 break;
530
531                                         }
532
533                                         case anhd_sig:
534
535                                                 if (!prev_bm) return IFF_CORRUPT;
536
537                                                 bmheader->w = prev_bm->bm_w;
538                                                 bmheader->h = prev_bm->bm_h;
539                                                 bmheader->type = prev_bm->bm_type;
540
541                                                 MALLOC( bmheader->raw_data, ubyte, bmheader->w * bmheader->h );
542
543                                                 memcpy(bmheader->raw_data, prev_bm->bm_data, bmheader->w * bmheader->h );
544                                                 skip_chunk(ifile,len);
545
546                                                 break;
547
548                                         case cmap_sig:
549                                         {
550                                                 int ncolors=(int) (len/3),cnum;
551                                                 unsigned char r,g,b;
552
553                                                 //printf("Parsing RGB map\n");
554                                                 for (cnum=0;cnum<ncolors;cnum++) {
555 //                                                      r=cfgetc(ifile);
556 //                                                      g=cfgetc(ifile);
557 //                                                      b=cfgetc(ifile);
558                                                         r = ifile->data[ifile->position++];
559                                                         g = ifile->data[ifile->position++];
560                                                         b = ifile->data[ifile->position++];
561                                                         r >>= 2; bmheader->palette[cnum].r = r;
562                                                         g >>= 2; bmheader->palette[cnum].g = g;
563                                                         b >>= 2; bmheader->palette[cnum].b = b;
564                                                 }
565                                                 //if (len & 1) ignore = cfgetc(ifile);
566                                                 if (len & 1 ) ifile->position++;
567
568                                                 break;
569                                         }
570
571                                         case body_sig:
572                                         {
573                                                 int r;
574                                                 //printf("Parsing body\n");
575                                                 if ((r=parse_body(ifile,len,bmheader))!=IFF_NO_ERROR)
576                                                         return r;
577                                                 break;
578                                         }
579                                         case dlta_sig:
580                                         {
581                                                 int r;
582                                                 if ((r=parse_delta(ifile,len,bmheader))!=IFF_NO_ERROR)
583                                                         return r;
584                                                 break;
585                                         }
586                                         default:
587                                                 skip_chunk(ifile,len);
588                                                 break;
589                                 }
590                         }
591
592         //if (ignore) ignore++;
593
594         if (ifile->position != start_pos-4+form_len)
595                 return IFF_CORRUPT;
596
597         return IFF_NO_ERROR;    /* ok! */
598 }
599
600 //convert an ILBM file to a PBM file
601 int convert_ilbm_to_pbm(iff_bitmap_header *bmheader)
602 {
603         int x,y,p;
604         byte *new_data,*destptr,*rowptr;
605         int bytes_per_row,byteofs;
606         ubyte checkmask,newbyte,setbit;
607
608         MALLOC( new_data, byte, bmheader->w * bmheader->h );
609         if (new_data == NULL) return IFF_NO_MEM;
610
611         destptr = new_data;
612
613         bytes_per_row = 2*((bmheader->w+15)/16);
614
615         for (y=0;y<bmheader->h;y++) {
616
617                 rowptr = &bmheader->raw_data[y * bytes_per_row * bmheader->nplanes];
618
619                 for (x=0,checkmask=0x80;x<bmheader->w;x++) {
620
621                         byteofs = x >> 3;
622
623                         for (p=newbyte=0,setbit=1;p<bmheader->nplanes;p++) {
624
625                                 if (rowptr[bytes_per_row * p + byteofs] & checkmask)
626                                         newbyte |= setbit;
627
628                                 setbit <<= 1;
629                         }
630
631                         *destptr++ = newbyte;
632
633                         if ((checkmask >>= 1) == 0) checkmask=0x80;
634                 }
635
636         }
637
638         d_free(bmheader->raw_data);
639         bmheader->raw_data = new_data;
640
641         bmheader->type = TYPE_PBM;
642
643         return IFF_NO_ERROR;
644 }
645
646 #define INDEX_TO_15BPP(i) ((short)((((palptr[(i)].r/2)&31)<<10)+(((palptr[(i)].g/2)&31)<<5)+((palptr[(i)].b/2 )&31)))
647
648 int convert_rgb15(grs_bitmap *bm,iff_bitmap_header *bmheader)
649 {
650         ushort *new_data;
651         int x,y;
652         int newptr = 0;
653         pal_entry *palptr;
654
655         palptr = bmheader->palette;
656
657 //        if ((new_data = d_malloc(bm->bm_w * bm->bm_h * 2)) == NULL)
658 //            {ret=IFF_NO_MEM; goto done;}
659        MALLOC(new_data, ushort, bm->bm_w * bm->bm_h * 2);
660        if (new_data == NULL)
661            return IFF_NO_MEM;
662
663         for (y=0; y<bm->bm_h; y++) {
664
665                 for (x=0; x<bmheader->w; x++)
666                         new_data[newptr++] = INDEX_TO_15BPP(bmheader->raw_data[y*bmheader->w+x]);
667
668         }
669
670         d_free(bm->bm_data);                            //get rid of old-style data
671         bm->bm_data = (ubyte *) new_data;                       //..and point to new data
672
673         bm->bm_rowsize *= 2;                            //two bytes per row
674
675         return IFF_NO_ERROR;
676
677 }
678
679 //read in a entire file into a fake file structure
680 int open_fake_file(char *ifilename,FFILE *ffile)
681 {
682         CFILE *ifile;
683         int ret;
684
685         //printf( "Reading %s\n", ifilename );
686
687         ffile->data=NULL;
688
689         if ((ifile = cfopen(ifilename,"rb")) == NULL) return IFF_NO_FILE;
690
691         ffile->length = cfilelength(ifile);
692
693         MALLOC(ffile->data,ubyte,ffile->length);
694
695         if (cfread(ffile->data, 1, ffile->length, ifile) < ffile->length)
696                 ret = IFF_READ_ERROR;
697         else
698                 ret = IFF_NO_ERROR;
699
700         ffile->position = 0;
701
702         if (ifile) cfclose(ifile);
703
704         return ret;
705 }
706
707 void close_fake_file(FFILE *f)
708 {
709         if (f->data)
710                 d_free(f->data);
711
712         f->data = NULL;
713 }
714
715 //copy an iff header structure to a grs_bitmap structure
716 void copy_iff_to_grs(grs_bitmap *bm,iff_bitmap_header *bmheader)
717 {
718         bm->bm_x = bm->bm_y = 0;
719         bm->bm_w = bmheader->w;
720         bm->bm_h = bmheader->h;
721         bm->bm_type = bmheader->type;
722         bm->bm_rowsize = bmheader->w;
723         bm->bm_data = bmheader->raw_data;
724
725         bm->bm_flags = bm->bm_handle = 0;
726         
727 }
728
729 //if bm->bm_data is set, use it (making sure w & h are correct), else
730 //allocate the memory
731 int iff_parse_bitmap(FFILE *ifile,grs_bitmap *bm,int bitmap_type,byte *palette,grs_bitmap *prev_bm)
732 {
733         int ret;                        //return code
734         iff_bitmap_header bmheader;
735         long sig,form_len;
736         long form_type;
737
738         bmheader.raw_data = bm->bm_data;
739
740         if (bmheader.raw_data) {
741                 bmheader.w = bm->bm_w;
742                 bmheader.h = bm->bm_h;
743         }
744
745         sig=get_sig(ifile);
746
747         if (sig != form_sig) {
748                 ret = IFF_NOT_IFF;
749                 goto done;
750         }
751
752         form_len = get_long(ifile);
753
754         form_type = get_sig(ifile);
755
756         if (form_type == anim_sig)
757                 ret = IFF_FORM_ANIM;
758         else if ((form_type == pbm_sig) || (form_type == ilbm_sig))
759                 ret = iff_parse_ilbm_pbm(ifile,form_type,&bmheader,form_len,prev_bm);
760         else
761                 ret = IFF_UNKNOWN_FORM;
762
763         if (ret != IFF_NO_ERROR) {              //got an error parsing
764                 if (bmheader.raw_data) d_free(bmheader.raw_data); 
765                 goto done;
766         }
767
768         //If IFF file is ILBM, convert to PPB
769         if (bmheader.type == TYPE_ILBM) {
770
771                 ret = convert_ilbm_to_pbm(&bmheader);
772
773                 if (ret != IFF_NO_ERROR) goto done;
774         }
775
776         //Copy data from iff_bitmap_header structure into grs_bitmap structure
777
778         copy_iff_to_grs(bm,&bmheader);
779
780 #ifndef MACINTOSH
781         if (palette) memcpy(palette,&bmheader.palette,sizeof(bmheader.palette));
782 #else
783 //      if (palette) memcpy(palette,&bmheader.palette, 768);                    // pal_entry is 4 bytes on mac
784         if (palette) {
785                 ubyte *c;
786                 int i;
787                 
788                 c = palette;
789                 for (i = 0; i < 256; i++) {
790                         *c++ = bmheader.palette[i].r;
791                         *c++ = bmheader.palette[i].g;
792                         *c++ = bmheader.palette[i].b;
793                 }
794         }
795 #endif
796
797         //Now do post-process if required
798
799         if (bitmap_type == BM_RGB15) {
800                 ret = convert_rgb15(bm,&bmheader);
801                 if (ret != IFF_NO_ERROR) goto done;
802         }
803
804 done:
805
806         return ret;
807
808 }
809
810 //returns error codes - see IFF.H.  see GR.H for bitmap_type
811 int iff_read_bitmap(char *ifilename,grs_bitmap *bm,int bitmap_type,ubyte *palette)
812 {
813         int ret;                        //return code
814         FFILE ifile;
815
816         ret = open_fake_file(ifilename,&ifile);         //read in entire file
817         if (ret != IFF_NO_ERROR) goto done;
818
819         bm->bm_data = NULL;
820
821         ret = iff_parse_bitmap(&ifile,bm,bitmap_type,palette,NULL);
822
823 done:
824
825         if (ifile.data) d_free(ifile.data);
826
827         close_fake_file(&ifile);
828
829         return ret;
830
831
832 }
833
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,byte *palette)
837 {
838         int ret;                        //return code
839         FFILE ifile;
840
841         ret = open_fake_file(ifilename,&ifile);         //read in entire file
842         if (ret != IFF_NO_ERROR) goto done;
843
844         ret = iff_parse_bitmap(&ifile,bm,bm->bm_type,palette,NULL);
845
846 done:
847
848         if (ifile.data) d_free(ifile.data);
849
850         close_fake_file(&ifile);
851
852         return ret;
853
854
855 }
856
857 #define BMHD_SIZE 20
858
859 int write_bmhd(FILE *ofile,iff_bitmap_header *bitmap_header)
860 {
861         put_sig(bmhd_sig,ofile);
862         put_long((long) BMHD_SIZE,ofile);
863
864         put_word(bitmap_header->w,ofile);
865         put_word(bitmap_header->h,ofile);
866         put_word(bitmap_header->x,ofile);
867         put_word(bitmap_header->y,ofile);
868
869         put_byte(bitmap_header->nplanes,ofile);
870         put_byte(bitmap_header->masking,ofile);
871         put_byte(bitmap_header->compression,ofile);
872         put_byte(0,ofile);      /* pad */
873
874         put_word(bitmap_header->transparentcolor,ofile);
875         put_byte(bitmap_header->xaspect,ofile);
876         put_byte(bitmap_header->yaspect,ofile);
877
878         put_word(bitmap_header->pagewidth,ofile);
879         put_word(bitmap_header->pageheight,ofile);
880
881         return IFF_NO_ERROR;
882
883 }
884
885 int write_pal(FILE *ofile,iff_bitmap_header *bitmap_header)
886 {
887         int     i;
888
889         int n_colors = 1<<bitmap_header->nplanes;
890
891         put_sig(cmap_sig,ofile);
892 //      put_long(sizeof(pal_entry) * n_colors,ofile);
893         put_long(3 * n_colors,ofile);
894
895 //printf("new write pal %d %d\n",3,n_colors);
896
897         for (i=0; i<256; i++) {
898                 unsigned char r,g,b;
899                 r = bitmap_header->palette[i].r * 4 + (bitmap_header->palette[i].r?3:0);
900                 g = bitmap_header->palette[i].g * 4 + (bitmap_header->palette[i].g?3:0);
901                 b = bitmap_header->palette[i].b * 4 + (bitmap_header->palette[i].b?3:0);
902                 fputc(r,ofile);
903                 fputc(g,ofile);
904                 fputc(b,ofile);
905         }
906
907 //printf("write pal %d %d\n",sizeof(pal_entry),n_colors);
908 //      fwrite(bitmap_header->palette,sizeof(pal_entry),n_colors,ofile);
909
910         return IFF_NO_ERROR;
911 }
912
913 int rle_span(ubyte *dest,ubyte *src,int len)
914 {
915         int n,lit_cnt,rep_cnt;
916         ubyte last,*cnt_ptr,*dptr;
917
918         cnt_ptr=0;
919
920         dptr = dest;
921
922         last=src[0]; lit_cnt=1;
923
924         for (n=1;n<len;n++) {
925
926                 if (src[n] == last) {
927
928                         rep_cnt = 2;
929
930                         n++;
931                         while (n<len && rep_cnt<128 && src[n]==last) {n++; rep_cnt++;}
932
933                         if (rep_cnt > 2 || lit_cnt < 2) {
934
935                                 if (lit_cnt > 1) {*cnt_ptr = lit_cnt-2; --dptr;}                //save old lit count
936                                 *dptr++ = -(rep_cnt-1);
937                                 *dptr++ = last;
938                                 last = src[n];
939                                 lit_cnt = (n<len)?1:0;
940
941                                 continue;               //go to next char
942                         } else n--;
943
944                 }
945
946                 {
947
948                         if (lit_cnt==1) {
949                                 cnt_ptr = dptr++;               //save place for count
950                                 *dptr++=last;                   //store first char
951                         }
952
953                         *dptr++ = last = src[n];
954
955                         if (lit_cnt == 127) {
956                                 *cnt_ptr = lit_cnt;
957                                 //cnt_ptr = dptr++;
958                                 lit_cnt = 1;
959                                 last = src[++n];
960                         }
961                         else lit_cnt++;
962                 }
963
964
965         }
966
967         if (lit_cnt==1) {
968                 *dptr++ = 0;
969                 *dptr++=last;                   //store first char
970         }
971         else if (lit_cnt > 1)
972                 *cnt_ptr = lit_cnt-1;
973
974         return dptr-dest;
975 }
976
977 #define EVEN(a) ((a+1)&0xfffffffel)
978
979 //returns length of chunk
980 int write_body(FILE *ofile,iff_bitmap_header *bitmap_header,int compression_on)
981 {
982         int w=bitmap_header->w,h=bitmap_header->h;
983         int y,odd=w&1;
984         long len = EVEN(w) * h,newlen,total_len=0;
985         ubyte *p=bitmap_header->raw_data,*new_span;
986         long save_pos;
987
988         put_sig(body_sig,ofile);
989         save_pos = ftell(ofile);
990         put_long(len,ofile);
991
992     //if (! (new_span = d_malloc(bitmap_header->w+(bitmap_header->w/128+2)*2))) return IFF_NO_MEM;
993     MALLOC( new_span, ubyte, bitmap_header->w + (bitmap_header->w/128+2)*2);
994     if (new_span == NULL) return IFF_NO_MEM;
995
996         for (y=bitmap_header->h;y--;) {
997
998                 if (compression_on) {
999                         total_len += newlen = rle_span(new_span,p,bitmap_header->w+odd);
1000                         fwrite(new_span,newlen,1,ofile);
1001                 }
1002                 else
1003                         fwrite(p,bitmap_header->w+odd,1,ofile);
1004
1005                 p+=bitmap_header->row_size;     //bitmap_header->w;
1006         }
1007
1008         if (compression_on) {           //write actual data length
1009                 Assert(fseek(ofile,save_pos,SEEK_SET)==0);
1010                 put_long(total_len,ofile);
1011                 Assert(fseek(ofile,total_len,SEEK_CUR)==0);
1012                 if (total_len&1) fputc(0,ofile);                //pad to even
1013         }
1014
1015         d_free(new_span);
1016
1017         return ((compression_on) ? (EVEN(total_len)+8) : (len+8));
1018
1019 }
1020
1021 #if WRITE_TINY
1022 //write a small representation of a bitmap. returns size
1023 int write_tiny(CFILE *ofile,iff_bitmap_header *bitmap_header,int compression_on)
1024 {
1025         int skip;
1026         int new_w,new_h;
1027         int len,total_len=0,newlen;
1028         int x,y,xofs,odd;
1029         ubyte *p = bitmap_header->raw_data;
1030         ubyte tspan[80],new_span[80*2];
1031         long save_pos;
1032
1033         skip = max((bitmap_header->w+79)/80,(bitmap_header->h+63)/64);
1034
1035         new_w = bitmap_header->w / skip;
1036         new_h = bitmap_header->h / skip;
1037
1038         odd = new_w & 1;
1039
1040         len = new_w * new_h + 4;
1041
1042         put_sig(tiny_sig,ofile);
1043         save_pos = cftell(ofile);
1044         put_long(EVEN(len),ofile);
1045
1046         put_word(new_w,ofile);
1047         put_word(new_h,ofile);
1048
1049         for (y=0;y<new_h;y++) {
1050                 for (x=xofs=0;x<new_w;x++,xofs+=skip)
1051                         tspan[x] = p[xofs];
1052
1053                 if (compression_on) {
1054                         total_len += newlen = rle_span(new_span,tspan,new_w+odd);
1055                         fwrite(new_span,newlen,1,ofile);
1056                 }
1057                 else
1058                         fwrite(p,new_w+odd,1,ofile);
1059
1060                 p += skip * bitmap_header->row_size;            //bitmap_header->w;
1061
1062         }
1063
1064         if (compression_on) {
1065                 Assert(cfseek(ofile,save_pos,SEEK_SET)==0);
1066                 put_long(4+total_len,ofile);
1067                 Assert(cfseek(ofile,4+total_len,SEEK_CUR)==0);
1068                 if (total_len&1) cfputc(0,ofile);               //pad to even
1069         }
1070
1071         return ((compression_on) ? (EVEN(total_len)+8+4) : (len+8));
1072 }
1073 #endif
1074
1075 int write_pbm(FILE *ofile,iff_bitmap_header *bitmap_header,int compression_on)                  /* writes a pbm iff file */
1076 {
1077         int ret;
1078         long raw_size = EVEN(bitmap_header->w) * bitmap_header->h;
1079         long body_size,tiny_size,pbm_size = 4 + BMHD_SIZE + 8 + EVEN(raw_size) + sizeof(pal_entry)*(1<<bitmap_header->nplanes)+8;
1080         long save_pos;
1081
1082 //printf("write_pbm\n");
1083
1084         put_sig(form_sig,ofile);
1085         save_pos = ftell(ofile);
1086         put_long(pbm_size+8,ofile);
1087         put_sig(pbm_sig,ofile);
1088
1089         ret = write_bmhd(ofile,bitmap_header);
1090         if (ret != IFF_NO_ERROR) return ret;
1091
1092         ret = write_pal(ofile,bitmap_header);
1093         if (ret != IFF_NO_ERROR) return ret;
1094
1095 #if WRITE_TINY
1096         tiny_size = write_tiny(ofile,bitmap_header,compression_on);
1097 #else
1098         tiny_size = 0;
1099 #endif
1100
1101         body_size = write_body(ofile,bitmap_header,compression_on);
1102
1103         pbm_size = 4 + BMHD_SIZE + body_size + tiny_size + sizeof(pal_entry)*(1<<bitmap_header->nplanes)+8;
1104
1105         Assert(fseek(ofile,save_pos,SEEK_SET)==0);
1106         put_long(pbm_size+8,ofile);
1107         Assert(fseek(ofile,pbm_size+8,SEEK_CUR)==0);
1108
1109         return ret;
1110
1111 }
1112
1113 //writes an IFF file from a grs_bitmap structure. writes palette if not null
1114 //returns error codes - see IFF.H.
1115 int iff_write_bitmap(char *ofilename,grs_bitmap *bm,ubyte *palette)
1116 {
1117         FILE *ofile;
1118         iff_bitmap_header bmheader;
1119         int ret;
1120         int compression_on;
1121
1122         if (bm->bm_type == BM_RGB15) return IFF_BAD_BM_TYPE;
1123
1124 #if COMPRESS
1125         compression_on = (bm->bm_w>=MIN_COMPRESS_WIDTH);
1126 #else
1127         compression_on = 0;
1128 #endif
1129
1130         //fill in values in bmheader
1131
1132         bmheader.x = bmheader.y = 0;
1133         bmheader.w = bm->bm_w;
1134         bmheader.h = bm->bm_h;
1135         bmheader.type = TYPE_PBM;
1136         bmheader.transparentcolor = iff_transparent_color;
1137         bmheader.pagewidth = bm->bm_w;  //I don't think it matters what I write
1138         bmheader.pageheight = bm->bm_h;
1139         bmheader.nplanes = 8;
1140         bmheader.masking = mskNone;
1141         if (iff_has_transparency)       {
1142                  bmheader.masking |= mskHasTransparentColor;
1143         }
1144         bmheader.compression = (compression_on?cmpByteRun1:cmpNone);
1145
1146         bmheader.xaspect = bmheader.yaspect = 1;        //I don't think it matters what I write
1147         bmheader.raw_data = bm->bm_data;
1148         bmheader.row_size = bm->bm_rowsize;
1149
1150         if (palette) memcpy(&bmheader.palette,palette,256*3);
1151
1152         //open file and write
1153
1154         if ((ofile = fopen(ofilename,"wb")) == NULL) {ret=IFF_NO_FILE; goto done;}
1155
1156         ret = write_pbm(ofile,&bmheader,compression_on);
1157
1158 done:
1159
1160         fclose(ofile);
1161
1162         return ret;
1163 }
1164
1165 //read in many brushes.  fills in array of pointers, and n_bitmaps.
1166 //returns iff error codes
1167 int iff_read_animbrush(char *ifilename,grs_bitmap **bm_list,int max_bitmaps,int *n_bitmaps,ubyte *palette)
1168 {
1169         int ret;                        //return code
1170         FFILE ifile;
1171         iff_bitmap_header bmheader;
1172         long sig,form_len;
1173         long form_type;
1174
1175         *n_bitmaps=0;
1176
1177         ret = open_fake_file(ifilename,&ifile);         //read in entire file
1178         if (ret != IFF_NO_ERROR) goto done;
1179
1180         bmheader.raw_data = NULL;
1181
1182         sig=get_sig(&ifile);
1183         form_len = get_long(&ifile);
1184
1185         if (sig != form_sig) {
1186                 ret = IFF_NOT_IFF;
1187                 goto done;
1188         }
1189
1190         form_type = get_sig(&ifile);
1191
1192         if ((form_type == pbm_sig) || (form_type == ilbm_sig))
1193                 ret = IFF_FORM_BITMAP;
1194         else if (form_type == anim_sig) {
1195                 int anim_end = ifile.position + form_len - 4;
1196
1197                 while (ifile.position < anim_end && *n_bitmaps < max_bitmaps) {
1198
1199                         grs_bitmap *prev_bm;
1200
1201                         prev_bm = *n_bitmaps>0?bm_list[*n_bitmaps-1]:NULL;
1202
1203                    MALLOC(bm_list[*n_bitmaps] , grs_bitmap, 1 );
1204
1205                         bm_list[*n_bitmaps]->bm_data = NULL;
1206
1207                         ret = iff_parse_bitmap(&ifile,bm_list[*n_bitmaps],form_type,*n_bitmaps>0?NULL:palette,prev_bm);
1208
1209                         if (ret != IFF_NO_ERROR)
1210                                 goto done;
1211
1212                         (*n_bitmaps)++;
1213                 }
1214
1215                 if (ifile.position < anim_end)  //ran out of room
1216                         ret = IFF_TOO_MANY_BMS;
1217
1218         }
1219         else
1220                 ret = IFF_UNKNOWN_FORM;
1221
1222 done:
1223
1224         close_fake_file(&ifile);
1225
1226         return ret;
1227
1228 }
1229
1230 //text for error messges
1231 char error_messages[] = {
1232         "No error.\0"
1233         "Not enough mem for loading or processing bitmap.\0"
1234         "IFF file has unknown FORM type.\0"
1235         "Not an IFF file.\0"
1236         "Cannot open file.\0"
1237         "Tried to save invalid type, like BM_RGB15.\0"
1238         "Bad data in file.\0"
1239         "ANIM file cannot be loaded with normal bitmap loader.\0"
1240         "Normal bitmap file cannot be loaded with anim loader.\0"
1241         "Array not big enough on anim brush read.\0"
1242         "Unknown mask type in bitmap header.\0"
1243         "Error reading file.\0"
1244 };
1245
1246
1247 //function to return pointer to error message
1248 char *iff_errormsg(int error_number)
1249 {
1250         char *p = error_messages;
1251
1252         while (error_number--) {
1253
1254                 if (!p) return NULL;
1255
1256                 p += strlen(p)+1;
1257
1258         }
1259
1260         return p;
1261
1262 }
1263
1264