Subversion Repositories Games.Descent

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
1 pmbaty 1
/*
2
 * Portions of this file are copyright Rebirth contributors and licensed as
3
 * described in COPYING.txt.
4
 * Portions of this file are copyright Parallax Software and licensed
5
 * according to the Parallax license below.
6
 * See COPYING.txt for license details.
7
 
8
THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
9
SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
10
END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
11
ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
12
IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
13
SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
14
FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
15
CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
16
AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
17
COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
18
*/
19
 
20
/*
21
 *
22
 * Routines for reading and writing IFF files
23
 *
24
 */
25
 
26
#define COMPRESS                1       //do the RLE or not? (for debugging mostly)
27
#define WRITE_TINY      0       //should we write a TINY chunk?
28
 
29
#define MIN_COMPRESS_WIDTH      65      //don't compress if less than this wide
30
 
31
#include <stdio.h>
32
#include <stdlib.h>
33
#include <string.h>
34
 
35
#include "u_mem.h"
36
#include "iff.h"
37
#include "dxxerror.h"
38
#include "makesig.h"
39
#include "physfsx.h"
40
#include "gr.h"
41
 
42
#include "dxxsconf.h"
43
#include "compiler-range_for.h"
44
#include "partial_range.h"
45
#include <memory>
46
 
47
//Internal constants and structures for this library
48
 
49
//Type values for bitmaps
50
#define TYPE_PBM                0
51
#define TYPE_ILBM               1
52
 
53
//Compression types
54
#define cmpNone 0
55
#define cmpByteRun1     1
56
 
57
//Masking types
58
#define mskNone 0
59
#define mskHasMask      1
60
#define mskHasTransparentColor 2
61
 
62
//structure of the header in the file
63
struct iff_bitmap_header : prohibit_void_ptr<iff_bitmap_header>
64
{
65
        short w,h;                                              //width and height of this bitmap
66
        short x,y;                                              //generally unused
67
        short type;                                             //see types above
68
        short transparentcolor;         //which color is transparent (if any)
69
        short pagewidth,pageheight; //width & height of source screen
70
        sbyte nplanes;              //number of planes (8 for 256 color image)
71
        sbyte masking,compression;  //see constants above
72
        sbyte xaspect,yaspect;      //aspect ratio (usually 5/6)
73
        palette_array_t palette;                //the palette for this bitmap
74
        short row_size;                         //offset to next row
75
        RAIIdmem<uint8_t[]> raw_data;                           //ptr to array of data
76
};
77
 
78
ubyte iff_transparent_color;
79
ubyte iff_has_transparency;     // 0=no transparency, 1=iff_transparent_color is valid
80
 
81
#define form_sig MAKE_SIG('F','O','R','M')
82
#define ilbm_sig MAKE_SIG('I','L','B','M')
83
#define body_sig MAKE_SIG('B','O','D','Y')
84
#define pbm_sig  MAKE_SIG('P','B','M',' ')
85
#define bmhd_sig MAKE_SIG('B','M','H','D')
86
#define anhd_sig MAKE_SIG('A','N','H','D')
87
#define cmap_sig MAKE_SIG('C','M','A','P')
88
#define tiny_sig MAKE_SIG('T','I','N','Y')
89
#define anim_sig MAKE_SIG('A','N','I','M')
90
#define dlta_sig MAKE_SIG('D','L','T','A')
91
 
92
static int32_t get_sig(PHYSFS_File *f)
93
{
94
        int s;
95
 
96
        PHYSFS_readSBE32(f, &s);
97
        return s;
98
}
99
 
100
#define put_sig(sig, f) PHYSFS_writeSBE32(f, sig)
101
 
102
static int parse_bmhd(PHYSFS_File *ifile,long len,iff_bitmap_header *bmheader)
103
{
104
        len++;  /* so no "parm not used" warning */
105
 
106
//  debug("parsing bmhd len=%ld\n",len);
107
 
108
        PHYSFS_readSBE16(ifile, &bmheader->w);
109
        PHYSFS_readSBE16(ifile, &bmheader->h);
110
        PHYSFS_readSBE16(ifile, &bmheader->x);
111
        PHYSFS_readSBE16(ifile, &bmheader->y);
112
 
113
        bmheader->nplanes = PHYSFSX_readByte(ifile);
114
        bmheader->masking = PHYSFSX_readByte(ifile);
115
        bmheader->compression = PHYSFSX_readByte(ifile);
116
        PHYSFSX_readByte(ifile);        /* skip pad */
117
 
118
        PHYSFS_readSBE16(ifile, &bmheader->transparentcolor);
119
        bmheader->xaspect = PHYSFSX_readByte(ifile);
120
        bmheader->yaspect = PHYSFSX_readByte(ifile);
121
 
122
        PHYSFS_readSBE16(ifile, &bmheader->pagewidth);
123
        PHYSFS_readSBE16(ifile, &bmheader->pageheight);
124
 
125
        iff_transparent_color = bmheader->transparentcolor;
126
 
127
        iff_has_transparency = 0;
128
 
129
        if (bmheader->masking == mskHasTransparentColor)
130
                iff_has_transparency = 1;
131
 
132
        else if (bmheader->masking != mskNone && bmheader->masking != mskHasMask)
133
                return IFF_UNKNOWN_MASK;
134
 
135
//  debug("w,h=%d,%d x,y=%d,%d\n",w,h,x,y);
136
//  debug("nplanes=%d, masking=%d ,compression=%d, transcolor=%d\n",nplanes,masking,compression,transparentcolor);
137
 
138
        return IFF_NO_ERROR;
139
}
140
 
141
 
142
//  the buffer pointed to by raw_data is stuffed with a pointer to decompressed pixel data
143
namespace dsx {
144
static int parse_body(PHYSFS_File *ifile,long len,iff_bitmap_header *bmheader)
145
{
146
        auto p = bmheader->raw_data.get();
147
        int width,depth;
148
        signed char n;
149
        int nn,wid_cnt,end_cnt,plane;
150
        unsigned char *data_end;
151
        int end_pos;
152
        #ifndef NDEBUG
153
        int row_count=0;
154
        #endif
155
 
156
        width=0;
157
        depth=0;
158
 
159
        end_pos = PHYSFS_tell(ifile) + len;
160
        if (len&1)
161
                end_pos++;
162
 
163
        if (bmheader->type == TYPE_PBM) {
164
                width=bmheader->w;
165
                depth=1;
166
        } else if (bmheader->type == TYPE_ILBM) {
167
                width = (bmheader->w+7)/8;
168
                depth=bmheader->nplanes;
169
        }
170
 
171
        end_cnt = (width&1)?-1:0;
172
 
173
        data_end = p + width*bmheader->h*depth;
174
 
175
        if (bmheader->compression == cmpNone) {        /* no compression */
176
                int y;
177
 
178
                for (y=bmheader->h;y;y--) {
179
                        PHYSFS_read(ifile, p, width, depth);
180
                        p += bmheader->w;
181
 
182
                        if (bmheader->masking == mskHasMask)
183
                                PHYSFSX_fseek(ifile, width, SEEK_CUR);                          //skip mask!
184
 
185
                        if (bmheader->w & 1) PHYSFSX_fgetc(ifile);
186
                }
187
 
188
                //cnt = len - bmheader->h * ((bmheader->w+1)&~1);
189
 
190
        }
191
        else if (bmheader->compression == cmpByteRun1)
192
                for (wid_cnt=width,plane=0; PHYSFS_tell(ifile) < end_pos && p<data_end;) {
193
                        unsigned char c;
194
 
195
                        if (wid_cnt == end_cnt) {
196
                                wid_cnt = width;
197
                                plane++;
198
                                if ((bmheader->masking == mskHasMask && plane==depth+1) ||
199
                                    (bmheader->masking != mskHasMask && plane==depth))
200
                                        plane=0;
201
                        }
202
 
203
                        Assert(wid_cnt > end_cnt);
204
 
205
                        n=PHYSFSX_fgetc(ifile);
206
 
207
                        if (n >= 0) {                       // copy next n+1 bytes from source, they are not compressed
208
                                nn = static_cast<int>(n)+1;
209
                                wid_cnt -= nn;
210
                                if (wid_cnt==-1) {--nn; Assert(width&1);}
211
                                if (plane==depth)       //masking row
212
                                        PHYSFSX_fseek(ifile, nn, SEEK_CUR);
213
                                else
214
                                {
215
                                        PHYSFS_read(ifile, p, nn, 1);
216
                                        p += nn;
217
                                }
218
                                if (wid_cnt==-1) PHYSFSX_fseek(ifile, 1, SEEK_CUR);
219
                        }
220
                        else if (n>=-127) {             // next -n + 1 bytes are following byte
221
                                c=PHYSFSX_fgetc(ifile);
222
                                const int negative_n = -n;
223
                                nn = negative_n + 1;
224
                                wid_cnt -= nn;
225
                                if (wid_cnt==-1) {--nn; Assert(width&1);}
226
                                if (plane!=depth)       //not masking row
227
                                        {memset(p,c,nn); p+=nn;}
228
                        }
229
 
230
                        #ifndef NDEBUG
231
                        if ((p - bmheader->raw_data.get()) % width == 0)
232
                                        row_count++;
233
 
234
                        Assert((p - bmheader->raw_data.get()) - (width*row_count) < width);
235
                        #endif
236
 
237
                }
238
 
239
#if defined(DXX_BUILD_DESCENT_I)
240
        if (bmheader->masking==mskHasMask && p==data_end && PHYSFS_tell(ifile)==end_pos-2)              //I don't know why...
241
                PHYSFSX_fseek(ifile, 1, SEEK_CUR);              //...but if I do this it works
242
 
243
        if (p==data_end && PHYSFS_tell(ifile)==end_pos-1)               //must be a pad byte
244
                //ignore = PHYSFSX_fgetc(ifile);                //get pad byte
245
                PHYSFSX_fseek(ifile, 1, SEEK_CUR);
246
        else
247
                if (PHYSFS_tell(ifile)!=end_pos || p!=data_end) {
248
//                      debug("IFF Error: p=%x, data_end=%x, cnt=%d\n",p,data_end,cnt);
249
                        return IFF_CORRUPT;
250
                }
251
#elif defined(DXX_BUILD_DESCENT_II)
252
        if (p!=data_end)                                //if we don't have the whole bitmap...
253
                return IFF_CORRUPT;             //...the give an error
254
 
255
        //Pretend we read the whole chuck, because if we didn't, it's because
256
        //we didn't read the last mask like or the last rle record for padding
257
        //or whatever and it's not important, because we check to make sure
258
        //we got the while bitmap, and that's what really counts.
259
#endif
260
 
261
        return IFF_NO_ERROR;
262
}
263
}
264
 
265
//modify passed bitmap
266
static int parse_delta(PHYSFS_File *ifile,long len,iff_bitmap_header *bmheader)
267
{
268
        auto p = bmheader->raw_data.get();
269
        long chunk_end = PHYSFS_tell(ifile) + len;
270
 
271
        PHYSFSX_fseek(ifile, 4, SEEK_CUR);              //longword, seems to be equal to 4.  Don't know what it is
272
 
273
        for (int y=0;y<bmheader->h;y++) {
274
                ubyte n_items;
275
                int cnt = bmheader->w;
276
                ubyte code;
277
 
278
                n_items = PHYSFSX_readByte(ifile);
279
 
280
                while (n_items--) {
281
 
282
                        code = PHYSFSX_readByte(ifile);
283
 
284
                        if (code==0) {                          //repeat
285
                                ubyte rep,val;
286
 
287
                                rep = PHYSFSX_readByte(ifile);
288
                                val = PHYSFSX_readByte(ifile);
289
 
290
                                cnt -= rep;
291
                                if (cnt==-1)
292
                                        rep--;
293
                                while (rep--)
294
                                        *p++ = val;
295
                        }
296
                        else if (code > 0x80) { //skip
297
                                cnt -= (code-0x80);
298
                                p += (code-0x80);
299
                                if (cnt==-1)
300
                                        p--;
301
                        }
302
                        else {                                          //literal
303
                                cnt -= code;
304
                                if (cnt==-1)
305
                                        code--;
306
 
307
                                while (code--)
308
                                        *p++ = PHYSFSX_readByte(ifile);
309
 
310
                                if (cnt==-1)
311
                                        PHYSFSX_readByte(ifile);
312
                        }
313
 
314
                }
315
 
316
                if (cnt == -1) {
317
                        if (!bmheader->w)
318
                                return IFF_CORRUPT;
319
                }
320
                else if (cnt)
321
                        return IFF_CORRUPT;
322
        }
323
 
324
        if (PHYSFS_tell(ifile) == chunk_end-1)          //pad
325
                PHYSFSX_fseek(ifile, 1, SEEK_CUR);
326
 
327
        if (PHYSFS_tell(ifile) != chunk_end)
328
                return IFF_CORRUPT;
329
        else
330
                return IFF_NO_ERROR;
331
}
332
 
333
//  the buffer pointed to by raw_data is stuffed with a pointer to bitplane pixel data
334
static void skip_chunk(PHYSFS_File *ifile,long len)
335
{
336
        int ilen;
337
        ilen = (len+1) & ~1;
338
 
339
        PHYSFSX_fseek(ifile,ilen,SEEK_CUR);
340
}
341
 
342
//read an ILBM or PBM file
343
// Pass pointer to opened file, and to empty bitmap_header structure, and form length
344
static int iff_parse_ilbm_pbm(PHYSFS_File *ifile,long form_type,iff_bitmap_header *bmheader,int form_len,grs_bitmap *prev_bm)
345
{
346
        int sig,len;
347
        long start_pos,end_pos;
348
 
349
        start_pos = PHYSFS_tell(ifile);
350
        end_pos = start_pos-4+form_len;
351
 
352
                        if (form_type == pbm_sig)
353
                                bmheader->type = TYPE_PBM;
354
                        else
355
                                bmheader->type = TYPE_ILBM;
356
 
357
                        while ((PHYSFS_tell(ifile) < end_pos) && (sig=get_sig(ifile)) != EOF) {
358
 
359
                                PHYSFS_readSBE32(ifile, &len);
360
 
361
                                switch (sig) {
362
 
363
                                        case bmhd_sig: {
364
                                                int ret;
365
                                                int save_w=bmheader->w,save_h=bmheader->h;
366
 
367
                                                ret = parse_bmhd(ifile,len,bmheader);
368
 
369
                                                if (ret != IFF_NO_ERROR)
370
                                                        return ret;
371
 
372
                                                if (bmheader->raw_data) {
373
 
374
                                                        if (save_w != bmheader->w  ||  save_h != bmheader->h)
375
                                                                return IFF_BM_MISMATCH;
376
 
377
                                                }
378
                                                else {
379
                                                        MALLOC(bmheader->raw_data, uint8_t[], bmheader->w * bmheader->h);
380
                                                        if (!bmheader->raw_data)
381
                                                                return IFF_NO_MEM;
382
                                                }
383
 
384
                                                break;
385
 
386
                                        }
387
 
388
                                        case anhd_sig:
389
 
390
                                                if (!prev_bm) return IFF_CORRUPT;
391
 
392
                                                bmheader->w = prev_bm->bm_w;
393
                                                bmheader->h = prev_bm->bm_h;
394
                                                bmheader->type = prev_bm->get_type();
395
 
396
                                                MALLOC(bmheader->raw_data, uint8_t[], bmheader->w * bmheader->h);
397
 
398
                                                memcpy(bmheader->raw_data.get(), prev_bm->bm_data, bmheader->w * bmheader->h);
399
                                                skip_chunk(ifile,len);
400
 
401
                                                break;
402
 
403
                                        case cmap_sig:
404
                                        {
405
                                                unsigned ncolors=(len/3);
406
 
407
                                                range_for (auto &p, partial_range(bmheader->palette, ncolors))
408
                                                {
409
                                                        p.r = static_cast<unsigned char>(PHYSFSX_fgetc(ifile)) >> 2;
410
                                                        p.g = static_cast<unsigned char>(PHYSFSX_fgetc(ifile)) >> 2;
411
                                                        p.b = static_cast<unsigned char>(PHYSFSX_fgetc(ifile)) >> 2;
412
                                                }
413
                                                if (len & 1) PHYSFSX_fgetc(ifile);
414
 
415
                                                break;
416
                                        }
417
 
418
                                        case body_sig:
419
                                        {
420
                                                int r;
421
                                                if ((r=parse_body(ifile,len,bmheader))!=IFF_NO_ERROR)
422
                                                        return r;
423
                                                break;
424
                                        }
425
                                        case dlta_sig:
426
                                        {
427
                                                int r;
428
                                                if ((r=parse_delta(ifile,len,bmheader))!=IFF_NO_ERROR)
429
                                                        return r;
430
                                                break;
431
                                        }
432
                                        default:
433
                                                skip_chunk(ifile,len);
434
                                                break;
435
                                }
436
                        }
437
 
438
        if (PHYSFS_tell(ifile) != start_pos-4+form_len)
439
                return IFF_CORRUPT;
440
 
441
        return IFF_NO_ERROR;    /* ok! */
442
}
443
 
444
//convert an ILBM file to a PBM file
445
static int convert_ilbm_to_pbm(iff_bitmap_header *bmheader)
446
{
447
        int x,p;
448
        int bytes_per_row,byteofs;
449
        ubyte checkmask,newbyte,setbit;
450
 
451
        RAIIdmem<uint8_t[]> new_data;
452
        MALLOC(new_data, uint8_t[], bmheader->w * bmheader->h);
453
        if (new_data == NULL) return IFF_NO_MEM;
454
 
455
        auto destptr = new_data.get();
456
 
457
        bytes_per_row = 2*((bmheader->w+15)/16);
458
 
459
        for (int y=0;y<bmheader->h;y++) {
460
 
461
                const auto rowptr = reinterpret_cast<int8_t *>(&bmheader->raw_data[y * bytes_per_row * bmheader->nplanes]);
462
 
463
                for (x=0,checkmask=0x80;x<bmheader->w;x++) {
464
 
465
                        byteofs = x >> 3;
466
 
467
                        for (p=newbyte=0,setbit=1;p<bmheader->nplanes;p++) {
468
 
469
                                if (rowptr[bytes_per_row * p + byteofs] & checkmask)
470
                                        newbyte |= setbit;
471
 
472
                                setbit <<= 1;
473
                        }
474
 
475
                        *destptr++ = newbyte;
476
 
477
                        if ((checkmask >>= 1) == 0) checkmask=0x80;
478
                }
479
 
480
        }
481
 
482
        bmheader->raw_data = std::move(new_data);
483
 
484
        bmheader->type = TYPE_PBM;
485
 
486
        return IFF_NO_ERROR;
487
}
488
 
489
#define INDEX_TO_15BPP(i) (static_cast<short>((((palptr[(i)].r/2)&31)<<10)+(((palptr[(i)].g/2)&31)<<5)+((palptr[(i)].b/2 )&31)))
490
 
491
namespace dsx {
492
static int convert_rgb15(grs_bitmap &bm,iff_bitmap_header &bmheader)
493
{
494
        palette_array_t::iterator palptr = begin(bmheader.palette);
495
 
496
#if defined(DXX_BUILD_DESCENT_I)
497
        for (int y=0; y < bm.bm_h; y++) {
498
                for (int x=0; x < bmheader.w; x++)
499
                        gr_bm_pixel(*grd_curcanv, bm, x, y, INDEX_TO_15BPP(bm.get_bitmap_data()[y * bmheader.w + x]));
500
        }
501
#elif defined(DXX_BUILD_DESCENT_II)
502
        uint16_t *new_data;
503
        MALLOC(new_data, ushort, bm.bm_w * bm.bm_h * 2);
504
        if (new_data == NULL)
505
                return IFF_NO_MEM;
506
 
507
        unsigned newptr = 0;
508
        for (int y=0; y < bm.bm_h; y++) {
509
 
510
                for (int x=0; x < bmheader.w; x++)
511
                        new_data[newptr++] = INDEX_TO_15BPP(bm.get_bitmap_data()[y * bmheader.w + x]);
512
 
513
        }
514
 
515
        d_free(bm.bm_mdata);                            //get rid of old-style data
516
        bm.bm_mdata = reinterpret_cast<uint8_t *>(new_data);                    //..and point to new data
517
 
518
        bm.bm_rowsize *= 2;                             //two bytes per row
519
#endif
520
        return IFF_NO_ERROR;
521
 
522
}
523
}
524
 
525
//copy an iff header structure to a grs_bitmap structure
526
static void copy_iff_to_grs(grs_bitmap &bm,iff_bitmap_header &bmheader)
527
{
528
        gr_init_bitmap(bm, static_cast<bm_mode>(bmheader.type), 0, 0, bmheader.w, bmheader.h, bmheader.w, bmheader.raw_data.release());
529
}
530
 
531
//if bm->bm_data is set, use it (making sure w & h are correct), else
532
//allocate the memory
533
static int iff_parse_bitmap(PHYSFS_File *ifile, grs_bitmap &bm, int bitmap_type, palette_array_t *palette, grs_bitmap *prev_bm)
534
{
535
        int ret;                        //return code
536
        iff_bitmap_header bmheader;
537
        int sig,form_len;
538
        long form_type;
539
 
540
        bmheader.raw_data.reset(bm.get_bitmap_data());
541
 
542
        if (bmheader.raw_data) {
543
                bmheader.w = bm.bm_w;
544
                bmheader.h = bm.bm_h;
545
        }//added 05/17/99 Matt Mueller - don't just leave them unitialized
546
        else{
547
                bmheader.w=bmheader.h=0;
548
        }
549
 
550
        sig=get_sig(ifile);
551
 
552
        if (sig != form_sig) {
553
                return IFF_NOT_IFF;
554
        }
555
 
556
        PHYSFS_readSBE32(ifile, &form_len);
557
 
558
        form_type = get_sig(ifile);
559
 
560
        if (form_type == anim_sig)
561
                ret = IFF_FORM_ANIM;
562
        else if ((form_type == pbm_sig) || (form_type == ilbm_sig))
563
                ret = iff_parse_ilbm_pbm(ifile,form_type,&bmheader,form_len,prev_bm);
564
        else
565
                ret = IFF_UNKNOWN_FORM;
566
 
567
        if (ret != IFF_NO_ERROR) {              //got an error parsing
568
                return ret;
569
        }
570
 
571
        //If IFF file is ILBM, convert to PPB
572
        if (bmheader.type == TYPE_ILBM) {
573
 
574
                ret = convert_ilbm_to_pbm(&bmheader);
575
 
576
                if (ret != IFF_NO_ERROR)
577
                        return ret;
578
        }
579
 
580
        //Copy data from iff_bitmap_header structure into grs_bitmap structure
581
 
582
        copy_iff_to_grs(bm,bmheader);
583
 
584
        if (palette)
585
                *palette = bmheader.palette;
586
 
587
        //Now do post-process if required
588
 
589
        if (bitmap_type == bm_mode::rgb15) {
590
                ret = convert_rgb15(bm, bmheader);
591
                if (ret != IFF_NO_ERROR)
592
                        return ret;
593
        }
594
 
595
        return ret;
596
 
597
}
598
 
599
//returns error codes - see IFF.H.  see GR.H for bitmap_type
600
int iff_read_bitmap(const char *const ifilename, grs_bitmap &bm, palette_array_t *const palette)
601
{
602
        int ret;                        //return code
603
        auto ifile = PHYSFSX_openReadBuffered(ifilename);
604
        if (!ifile)
605
                return IFF_NO_FILE;
606
 
607
        bm.bm_data = nullptr;
608
        ret = iff_parse_bitmap(ifile, bm, bm_mode::linear, palette, nullptr);
609
        return ret;
610
}
611
 
612
#define BMHD_SIZE 20
613
 
614
#if 0
615
static int write_bmhd(PHYSFS_File *ofile,iff_bitmap_header *bitmap_header)
616
{
617
        put_sig(bmhd_sig,ofile);
618
        PHYSFS_writeSBE32(ofile, BMHD_SIZE);
619
 
620
        PHYSFS_writeSBE16(ofile, bitmap_header->w);
621
        PHYSFS_writeSBE16(ofile, bitmap_header->h);
622
        PHYSFS_writeSBE16(ofile, bitmap_header->x);
623
        PHYSFS_writeSBE16(ofile, bitmap_header->y);
624
 
625
        PHYSFSX_writeU8(ofile, bitmap_header->nplanes);
626
        PHYSFSX_writeU8(ofile, bitmap_header->masking);
627
        PHYSFSX_writeU8(ofile, bitmap_header->compression);
628
        PHYSFSX_writeU8(ofile, 0);      /* pad */
629
 
630
        PHYSFS_writeSBE16(ofile, bitmap_header->transparentcolor);
631
        PHYSFSX_writeU8(ofile, bitmap_header->xaspect);
632
        PHYSFSX_writeU8(ofile, bitmap_header->yaspect);
633
 
634
        PHYSFS_writeSBE16(ofile, bitmap_header->pagewidth);
635
        PHYSFS_writeSBE16(ofile, bitmap_header->pageheight);
636
 
637
        return IFF_NO_ERROR;
638
 
639
}
640
 
641
static int write_pal(PHYSFS_File *ofile,iff_bitmap_header *bitmap_header)
642
{
643
        int n_colors = 1<<bitmap_header->nplanes;
644
 
645
        put_sig(cmap_sig,ofile);
646
//      PHYSFS_writeSBE32(sizeof(pal_entry) * n_colors,ofile);
647
        PHYSFS_writeSBE32(ofile, 3 * n_colors);
648
 
649
        range_for (auto &c, bitmap_header->palette)
650
        {
651
                unsigned char r,g,b;
652
                r = c.r * 4 + (c.r?3:0);
653
                g = c.g * 4 + (c.g?3:0);
654
                b = c.b * 4 + (c.b?3:0);
655
                PHYSFSX_writeU8(ofile, r);
656
                PHYSFSX_writeU8(ofile, g);
657
                PHYSFSX_writeU8(ofile, b);
658
        }
659
 
660
        return IFF_NO_ERROR;
661
}
662
 
663
static int rle_span(ubyte *dest,ubyte *src,int len)
664
{
665
        int lit_cnt,rep_cnt;
666
        ubyte last,*cnt_ptr,*dptr;
667
 
668
        cnt_ptr=0;
669
 
670
        dptr = dest;
671
 
672
        last=src[0]; lit_cnt=1;
673
 
674
        for (int n=1;n<len;n++) {
675
 
676
                if (src[n] == last) {
677
 
678
                        rep_cnt = 2;
679
 
680
                        n++;
681
                        while (n<len && rep_cnt<128 && src[n]==last) {n++; rep_cnt++;}
682
 
683
                        if (rep_cnt > 2 || lit_cnt < 2) {
684
 
685
                                if (lit_cnt > 1) {*cnt_ptr = lit_cnt-2; --dptr;}                //save old lit count
686
                                *dptr++ = -(rep_cnt-1);
687
                                *dptr++ = last;
688
                                last = src[n];
689
                                lit_cnt = (n<len)?1:0;
690
 
691
                                continue;               //go to next char
692
                        } else n--;
693
 
694
                }
695
 
696
                {
697
 
698
                        if (lit_cnt==1) {
699
                                cnt_ptr = dptr++;               //save place for count
700
                                *dptr++=last;                   //store first char
701
                        }
702
 
703
                        *dptr++ = last = src[n];
704
 
705
                        if (lit_cnt == 127) {
706
                                *cnt_ptr = lit_cnt;
707
                                //cnt_ptr = dptr++;
708
                                lit_cnt = 1;
709
                                last = src[++n];
710
                        }
711
                        else lit_cnt++;
712
                }
713
 
714
 
715
        }
716
 
717
        if (lit_cnt==1) {
718
                *dptr++ = 0;
719
                *dptr++=last;                   //store first char
720
        }
721
        else if (lit_cnt > 1)
722
                *cnt_ptr = lit_cnt-1;
723
 
724
        return dptr-dest;
725
}
726
 
727
#define EVEN(a) ((a+1)&0xfffffffel)
728
 
729
//returns length of chunk
730
static int write_body(PHYSFS_File *ofile,iff_bitmap_header *bitmap_header,int compression_on)
731
{
732
        int w=bitmap_header->w,h=bitmap_header->h;
733
        int y,odd=w&1;
734
        long len = EVEN(w) * h,newlen,total_len=0;
735
        uint8_t *p=bitmap_header->raw_data;
736
        long save_pos;
737
 
738
        put_sig(body_sig,ofile);
739
        save_pos = PHYSFS_tell(ofile);
740
        PHYSFS_writeSBE32(ofile, len);
741
 
742
        RAIIdmem<uint8_t[]> new_span;
743
        MALLOC( new_span, uint8_t[], bitmap_header->w + (bitmap_header->w/128+2)*2);
744
        if (!new_span) return IFF_NO_MEM;
745
 
746
        for (y=bitmap_header->h;y--;) {
747
 
748
                if (compression_on) {
749
                        total_len += newlen = rle_span(new_span,p,bitmap_header->w+odd);
750
                        PHYSFS_write(ofile,new_span,newlen,1);
751
                }
752
                else
753
                        PHYSFS_write(ofile,p,bitmap_header->w+odd,1);
754
 
755
                p+=bitmap_header->row_size;     //bitmap_header->w;
756
        }
757
 
758
        if (compression_on) {           //write actual data length
759
                Assert(PHYSFSX_fseek(ofile,save_pos,SEEK_SET)==0);
760
                (void)save_pos;
761
                PHYSFS_writeSBE32(ofile, total_len);
762
                Assert(PHYSFSX_fseek(ofile,total_len,SEEK_CUR)==0);
763
                if (total_len&1) PHYSFSX_writeU8(ofile, 0);             //pad to even
764
        }
765
        return ((compression_on) ? (EVEN(total_len)+8) : (len+8));
766
 
767
}
768
 
769
#if WRITE_TINY
770
//write a small representation of a bitmap. returns size
771
int write_tiny(PHYSFS_File *ofile,iff_bitmap_header *bitmap_header,int compression_on)
772
{
773
        int skip;
774
        int new_w,new_h;
775
        int len,total_len=0,newlen;
776
        int x,xofs,odd;
777
        uint8_t *p = bitmap_header->raw_data;
778
        ubyte tspan[80],new_span[80*2];
779
        long save_pos;
780
 
781
        skip = max((bitmap_header->w+79)/80,(bitmap_header->h+63)/64);
782
 
783
        new_w = bitmap_header->w / skip;
784
        new_h = bitmap_header->h / skip;
785
 
786
        odd = new_w & 1;
787
 
788
        len = new_w * new_h + 4;
789
 
790
        put_sig(tiny_sig,ofile);
791
        save_pos = PHYSFS_tell(ofile);
792
        PHYSFS_writeSBE32(ofile, EVEN(len));
793
 
794
        PHYSFS_writeSBE16(ofile, new_w);
795
        PHYSFS_writeSBE16(ofile, new_h);
796
 
797
        for (int y=0;y<new_h;y++) {
798
                for (x=xofs=0;x<new_w;x++,xofs+=skip)
799
                        tspan[x] = p[xofs];
800
 
801
                if (compression_on) {
802
                        total_len += newlen = rle_span(new_span,tspan,new_w+odd);
803
                        PHYSFS_write(ofile,new_span,newlen,1);
804
                }
805
                else
806
                        PHYSFS_write(ofile,p,new_w+odd,1);
807
 
808
                p += skip * bitmap_header->row_size;            //bitmap_header->w;
809
 
810
        }
811
 
812
        if (compression_on) {
813
                Assert(PHYSFSX_fseek(ofile,save_pos,SEEK_SET)==0);
814
                (void)save_pos;
815
                PHYSFS_writeSBE32(ofile, 4+total_len);
816
                Assert(PHYSFSX_fseek(ofile,4+total_len,SEEK_CUR)==0);
817
                if (total_len&1) PHYSFSX_writeU8(ofile, 0);             //pad to even
818
        }
819
 
820
        return ((compression_on) ? (EVEN(total_len)+8+4) : (len+8));
821
}
822
#endif
823
 
824
static int write_pbm(PHYSFS_File *ofile,iff_bitmap_header *bitmap_header,int compression_on)                    /* writes a pbm iff file */
825
{
826
        int ret;
827
        long raw_size = EVEN(bitmap_header->w) * bitmap_header->h;
828
        long body_size,tiny_size,pbm_size = 4 + BMHD_SIZE + 8 + EVEN(raw_size) + sizeof(rgb_t)*(1<<bitmap_header->nplanes)+8;
829
        long save_pos;
830
 
831
        put_sig(form_sig,ofile);
832
        save_pos = PHYSFS_tell(ofile);
833
        PHYSFS_writeSBE32(ofile, pbm_size+8);
834
        put_sig(pbm_sig,ofile);
835
 
836
        ret = write_bmhd(ofile,bitmap_header);
837
        if (ret != IFF_NO_ERROR) return ret;
838
 
839
        ret = write_pal(ofile,bitmap_header);
840
        if (ret != IFF_NO_ERROR) return ret;
841
 
842
#if WRITE_TINY
843
        tiny_size = write_tiny(ofile,bitmap_header,compression_on);
844
#else
845
        tiny_size = 0;
846
#endif
847
 
848
        body_size = write_body(ofile,bitmap_header,compression_on);
849
 
850
        pbm_size = 4 + BMHD_SIZE + body_size + tiny_size + sizeof(rgb_t)*(1<<bitmap_header->nplanes)+8;
851
 
852
        Assert(PHYSFSX_fseek(ofile,save_pos,SEEK_SET)==0);
853
        (void)save_pos;
854
        PHYSFS_writeSBE32(ofile, pbm_size+8);
855
        Assert(PHYSFSX_fseek(ofile,pbm_size+8,SEEK_CUR)==0);
856
 
857
        return ret;
858
 
859
}
860
 
861
//writes an IFF file from a grs_bitmap structure. writes palette if not null
862
//returns error codes - see IFF.H.
863
int iff_write_bitmap(const char *ofilename,grs_bitmap *bm,palette_array_t *palette)
864
{
865
        iff_bitmap_header bmheader;
866
        int ret;
867
        int compression_on;
868
 
869
        if (bm->bm_type == bm_mode::rgb15) return IFF_BAD_BM_TYPE;
870
 
871
#if COMPRESS
872
        compression_on = (bm->bm_w>=MIN_COMPRESS_WIDTH);
873
#else
874
        compression_on = 0;
875
#endif
876
 
877
        //fill in values in bmheader
878
 
879
        bmheader.x = bmheader.y = 0;
880
        bmheader.w = bm->bm_w;
881
        bmheader.h = bm->bm_h;
882
        bmheader.type = TYPE_PBM;
883
        bmheader.transparentcolor = iff_transparent_color;
884
        bmheader.pagewidth = bm->bm_w;  //I don't think it matters what I write
885
        bmheader.pageheight = bm->bm_h;
886
        bmheader.nplanes = 8;
887
        bmheader.masking = mskNone;
888
        if (iff_has_transparency)       {
889
                 bmheader.masking |= mskHasTransparentColor;
890
        }
891
        bmheader.compression = (compression_on?cmpByteRun1:cmpNone);
892
 
893
        bmheader.xaspect = bmheader.yaspect = 1;        //I don't think it matters what I write
894
        bmheader.raw_data = bm->get_bitmap_data();
895
        bmheader.row_size = bm->bm_rowsize;
896
 
897
        if (palette)
898
                bmheader.palette = *palette;
899
 
900
        //open file and write
901
 
902
        RAIIPHYSFS_File ofile{PHYSFS_openWrite(ofilename)};
903
        if (!ofile)
904
                return IFF_NO_FILE;
905
 
906
        ret = write_pbm(ofile,&bmheader,compression_on);
907
        return ret;
908
}
909
#endif
910
 
911
//read in many brushes.  fills in array of pointers, and n_bitmaps.
912
//returns iff error codes
913
int iff_read_animbrush(const char *ifilename,std::array<std::unique_ptr<grs_main_bitmap>, MAX_BITMAPS_PER_BRUSH> &bm_list,unsigned *n_bitmaps,palette_array_t &palette)
914
{
915
        int ret = IFF_NO_ERROR;                 //return code
916
        int sig,form_len;
917
        long form_type;
918
 
919
        *n_bitmaps=0;
920
 
921
        auto ifile = PHYSFSX_openReadBuffered(ifilename);
922
        if (!ifile)
923
                return IFF_NO_FILE;
924
 
925
        sig=get_sig(ifile);
926
        PHYSFS_readSBE32(ifile, &form_len);
927
 
928
        if (sig != form_sig) {
929
                ret = IFF_NOT_IFF;
930
                goto done;
931
        }
932
 
933
        form_type = get_sig(ifile);
934
 
935
        if ((form_type == pbm_sig) || (form_type == ilbm_sig))
936
                ret = IFF_FORM_BITMAP;
937
        else if (form_type == anim_sig) {
938
                int anim_end = PHYSFS_tell(ifile) + form_len - 4;
939
 
940
                while (PHYSFS_tell(ifile) < anim_end && *n_bitmaps < bm_list.size()) {
941
 
942
                        grs_bitmap *prev_bm;
943
 
944
                        prev_bm = *n_bitmaps>0?bm_list[*n_bitmaps-1].get() : nullptr;
945
 
946
                        auto &n = bm_list[*n_bitmaps];
947
                        n = std::make_unique<grs_main_bitmap>();
948
 
949
                        ret = iff_parse_bitmap(ifile, *n.get(), form_type, *n_bitmaps > 0 ? nullptr : &palette, prev_bm);
950
 
951
                        if (ret != IFF_NO_ERROR)
952
                                goto done;
953
 
954
                        (*n_bitmaps)++;
955
                }
956
 
957
                if (PHYSFS_tell(ifile) < anim_end)      //ran out of room
958
                        ret = IFF_TOO_MANY_BMS;
959
 
960
        }
961
        else
962
                ret = IFF_UNKNOWN_FORM;
963
 
964
done:
965
        return ret;
966
}
967
 
968
//text for error messges
969
constexpr char error_messages[] = {
970
        "No error.\0"
971
        "Not enough mem for loading or processing bitmap.\0"
972
        "IFF file has unknown FORM type.\0"
973
        "Not an IFF file.\0"
974
        "Cannot open file.\0"
975
        "Tried to save invalid type, like bm_mode::rgb15.\0"
976
        "Bad data in file.\0"
977
        "ANIM file cannot be loaded with normal bitmap loader.\0"
978
        "Normal bitmap file cannot be loaded with anim loader.\0"
979
        "Array not big enough on anim brush read.\0"
980
        "Unknown mask type in bitmap header.\0"
981
        "Error reading file.\0"
982
};
983
 
984
 
985
//function to return pointer to error message
986
const char *iff_errormsg(int error_number)
987
{
988
        const char *p = error_messages;
989
 
990
        while (error_number--) {
991
 
992
                if (!p) return NULL;
993
 
994
                p += strlen(p)+1;
995
 
996
        }
997
 
998
        return p;
999
 
1000
}
1001
 
1002