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