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