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