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