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