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