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