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
 * Functions for managing the pig files.
23
 *
24
 */
25
 
26
#include <stdio.h>
27
#include <string.h>
28
 
29
#include "pstypes.h"
30
#include "strutil.h"
31
#include "inferno.h"
32
#include "gr.h"
33
#include "grdef.h"
34
#include "u_mem.h"
35
#include "iff.h"
36
#include "dxxerror.h"
37
#include "sounds.h"
38
#include "songs.h"
39
#include "bm.h"
40
#include "hash.h"
41
#include "common/2d/bitmap.h"
42
#include "args.h"
43
#include "palette.h"
44
#include "gamefont.h"
45
#include "gamepal.h"
46
#include "physfsx.h"
47
#include "rle.h"
48
#include "screens.h"
49
#include "piggy.h"
50
#include "gamemine.h"
51
#include "textures.h"
52
#include "texmerge.h"
53
#include "paging.h"
54
#include "game.h"
55
#include "text.h"
56
#include "vclip.h"
57
#include "newmenu.h"
58
#include "makesig.h"
59
#include "console.h"
60
#include "compiler-cf_assert.h"
61
#include "compiler-range_for.h"
62
#include "d_range.h"
63
#include "partial_range.h"
64
#include <memory>
65
 
66
#if defined(DXX_BUILD_DESCENT_I)
67
#include "custom.h"
68
#include "snddecom.h"
69
#define DEFAULT_PIGFILE_REGISTERED      "descent.pig"
70
 
71
#elif defined(DXX_BUILD_DESCENT_II)
72
#define DEFAULT_PIGFILE_REGISTERED      "groupa.pig"
73
#define DEFAULT_PIGFILE_SHAREWARE       "d2demo.pig"
74
#define DEFAULT_HAMFILE_REGISTERED      "descent2.ham"
75
#define DEFAULT_HAMFILE_SHAREWARE       "d2demo.ham"
76
 
77
#define D1_PALETTE "palette.256"
78
 
79
#define DEFAULT_SNDFILE ((Piggy_hamfile_version < 3)?DEFAULT_HAMFILE_SHAREWARE:(GameArg.SndDigiSampleRate==SAMPLE_RATE_22K)?"descent2.s22":"descent2.s11")
80
 
81
#define MAC_ALIEN1_PIGSIZE      5013035
82
#define MAC_ALIEN2_PIGSIZE      4909916
83
#define MAC_FIRE_PIGSIZE        4969035
84
#define MAC_GROUPA_PIGSIZE      4929684 // also used for mac shareware
85
#define MAC_ICE_PIGSIZE         4923425
86
#define MAC_WATER_PIGSIZE       4832403
87
 
88
unsigned Num_aliases;
89
std::array<alias, MAX_ALIASES> alias_list;
90
 
91
int Piggy_hamfile_version = 0;
92
#endif
93
 
94
static std::unique_ptr<ubyte[]> BitmapBits;
95
static std::unique_ptr<ubyte[]> SoundBits;
96
 
97
struct SoundFile
98
{
99
        char    name[15];
100
};
101
 
102
#if defined(DXX_BUILD_DESCENT_II)
103
namespace {
104
#endif
105
hashtable AllBitmapsNames;
106
hashtable AllDigiSndNames;
107
std::array<int, MAX_BITMAP_FILES> GameBitmapOffset;
108
#if defined(DXX_BUILD_DESCENT_II)
109
}
110
#endif
111
 
112
unsigned Num_bitmap_files;
113
int Num_sound_files = 0;
114
 
115
namespace dsx {
116
std::array<digi_sound, MAX_SOUND_FILES> GameSounds;
117
static std::array<int, MAX_SOUND_FILES> SoundOffset;
118
GameBitmaps_array GameBitmaps;
119
}
120
 
121
#if defined(DXX_BUILD_DESCENT_I)
122
#define DBM_FLAG_LARGE  128             // Flags added onto the flags struct in b
123
static
124
#endif
125
std::array<BitmapFile, MAX_BITMAP_FILES> AllBitmaps;
126
static std::array<SoundFile, MAX_SOUND_FILES> AllSounds;
127
 
128
#define DBM_FLAG_ABM    64 // animated bitmap
129
 
130
static int Piggy_bitmap_cache_size;
131
static int Piggy_bitmap_cache_next;
132
static uint8_t *Piggy_bitmap_cache_data;
133
static std::array<uint8_t, MAX_BITMAP_FILES> GameBitmapFlags;
134
static std::array<uint16_t, MAX_BITMAP_FILES> GameBitmapXlat;
135
 
136
#if defined(DXX_BUILD_DESCENT_I)
137
#define PIGGY_BUFFER_SIZE (2048*1024)
138
#elif defined(DXX_BUILD_DESCENT_II)
139
#define PIGFILE_ID              MAKE_SIG('G','I','P','P') //PPIG
140
#define PIGFILE_VERSION         2
141
#define PIGGY_BUFFER_SIZE (2400*1024)
142
#endif
143
#define PIGGY_SMALL_BUFFER_SIZE (1400*1024)             // size of buffer when CGameArg.SysLowMem is set
144
 
145
static RAIIPHYSFS_File Piggy_fp;
146
 
147
ubyte bogus_bitmap_initialized=0;
148
std::array<uint8_t, 64 * 64> bogus_data;
149
namespace dsx {
150
digi_sound bogus_sound;
151
}
152
 
153
#if defined(DXX_BUILD_DESCENT_I)
154
grs_bitmap bogus_bitmap;
155
int MacPig = 0; // using the Macintosh pigfile?
156
int PCSharePig = 0; // using PC Shareware pigfile?
157
static std::array<int, MAX_SOUND_FILES> SoundCompressed;
158
#elif defined(DXX_BUILD_DESCENT_II)
159
char Current_pigfile[FILENAME_LEN] = "";
160
int Pigfile_initialized=0;
161
 
162
static std::unique_ptr<ubyte[]> Bitmap_replacement_data;
163
 
164
#define BM_FLAGS_TO_COPY (BM_FLAG_TRANSPARENT | BM_FLAG_SUPER_TRANSPARENT \
165
                         | BM_FLAG_NO_LIGHTING | BM_FLAG_RLE | BM_FLAG_RLE_BIG)
166
#endif
167
 
168
#define DBM_NUM_FRAMES  63
169
 
170
struct DiskBitmapHeader
171
{
172
        char name[8];
173
        ubyte dflags;           // bits 0-5 anim frame num, bit 6 abm flag
174
        ubyte width;            // low 8 bits here, 4 more bits in wh_extra
175
        ubyte height;           // low 8 bits here, 4 more bits in wh_extra
176
#if defined(DXX_BUILD_DESCENT_II)
177
        ubyte wh_extra;         // bits 0-3 width, bits 4-7 height
178
#endif
179
        ubyte flags;
180
        ubyte avg_color;
181
        int offset;
182
} __pack__;
183
#if defined(DXX_BUILD_DESCENT_I)
184
static_assert(sizeof(DiskBitmapHeader) == 0x11, "sizeof(DiskBitmapHeader) must be 0x11");
185
#elif defined(DXX_BUILD_DESCENT_II)
186
static_assert(sizeof(DiskBitmapHeader) == 0x12, "sizeof(DiskBitmapHeader) must be 0x12");
187
 
188
#define DISKBITMAPHEADER_D1_SIZE 17 // no wh_extra
189
#endif
190
 
191
struct DiskSoundHeader
192
{
193
        char name[8];
194
        int length;
195
        int data_length;
196
        int offset;
197
} __pack__;
198
 
199
#if defined(DXX_BUILD_DESCENT_II)
200
static void free_bitmap_replacements();
201
static void free_d1_tmap_nums();
202
#if DXX_USE_EDITOR
203
static int piggy_is_substitutable_bitmap(char * name, char (&subst_name)[32]);
204
static void piggy_write_pigfile(const char *filename);
205
static void write_int(int i,PHYSFS_File *file);
206
#endif
207
static int piggy_is_needed(int soundnum);
208
#endif
209
 
210
namespace dcx {
211
 
212
static void get_bitmap_name_from_header(std::array<char, 13> &output_name, const DiskBitmapHeader &bmh)
213
{
214
        if (bmh.dflags & DBM_FLAG_ABM)
215
                snprintf(output_name.data(), output_name.size(), "%.8s#%d", bmh.name, bmh.dflags & DBM_NUM_FRAMES);
216
        else
217
        {
218
                memcpy(output_name.data(), bmh.name, 8);
219
                output_name[8] = 0;
220
        }
221
}
222
 
223
}
224
 
225
/*
226
 * reads a DiskBitmapHeader structure from a PHYSFS_File
227
 */
228
namespace dsx {
229
static void DiskBitmapHeader_read(DiskBitmapHeader *dbh, PHYSFS_File *fp)
230
{
231
        PHYSFS_read(fp, dbh->name, 8, 1);
232
        dbh->dflags = PHYSFSX_readByte(fp);
233
        dbh->width = PHYSFSX_readByte(fp);
234
        dbh->height = PHYSFSX_readByte(fp);
235
#if defined(DXX_BUILD_DESCENT_II)
236
        dbh->wh_extra = PHYSFSX_readByte(fp);
237
#endif
238
        dbh->flags = PHYSFSX_readByte(fp);
239
        dbh->avg_color = PHYSFSX_readByte(fp);
240
        dbh->offset = PHYSFSX_readInt(fp);
241
}
242
}
243
 
244
/*
245
 * reads a DiskSoundHeader structure from a PHYSFS_File
246
 */
247
static void DiskSoundHeader_read(DiskSoundHeader *dsh, PHYSFS_File *fp)
248
{
249
        PHYSFS_read(fp, dsh->name, 8, 1);
250
        dsh->length = PHYSFSX_readInt(fp);
251
        dsh->data_length = PHYSFSX_readInt(fp);
252
        dsh->offset = PHYSFSX_readInt(fp);
253
}
254
 
255
#if defined(DXX_BUILD_DESCENT_II)
256
/*
257
 * reads a descent 1 DiskBitmapHeader structure from a PHYSFS_File
258
 */
259
static void DiskBitmapHeader_d1_read(DiskBitmapHeader *dbh, PHYSFS_File *fp)
260
{
261
        PHYSFS_read(fp, dbh->name, 8, 1);
262
        dbh->dflags = PHYSFSX_readByte(fp);
263
        dbh->width = PHYSFSX_readByte(fp);
264
        dbh->height = PHYSFSX_readByte(fp);
265
        dbh->wh_extra = 0;
266
        dbh->flags = PHYSFSX_readByte(fp);
267
        dbh->avg_color = PHYSFSX_readByte(fp);
268
        dbh->offset = PHYSFSX_readInt(fp);
269
}
270
#endif
271
 
272
void swap_0_255(grs_bitmap &bmp)
273
{
274
        auto a = [](uint8_t &c) {
275
                if (c == 0)
276
                        c = 255;
277
                else if (c == 255)
278
                        c = 0;
279
        };
280
        auto d = bmp.get_bitmap_data();
281
        std::for_each(d, d + (bmp.bm_h * bmp.bm_w), a);
282
}
283
 
284
namespace dsx {
285
 
286
bitmap_index piggy_register_bitmap(grs_bitmap &bmp, const char *const name, const int in_file)
287
{
288
        bitmap_index temp;
289
        assert(Num_bitmap_files < AllBitmaps.size());
290
 
291
        temp.index = Num_bitmap_files;
292
 
293
        if (!in_file) {
294
#if defined(DXX_BUILD_DESCENT_II)
295
#if DXX_USE_EDITOR
296
                if ( GameArg.EdiMacData )
297
                        swap_0_255(bmp);
298
#endif
299
#endif
300
                if (CGameArg.DbgNoCompressPigBitmap)
301
                        gr_bitmap_rle_compress(bmp);
302
        }
303
#if defined(DXX_BUILD_DESCENT_II)
304
        else if (SoundOffset[Num_sound_files] == 0)
305
                SoundOffset[Num_sound_files] = -1;              // make sure this sound's data is not individually freed
306
#endif
307
 
308
        auto &abn = AllBitmaps[Num_bitmap_files].name;
309
        abn.back() = 0;
310
        strncpy(abn.data(), name, abn.size() - 1);
311
        hashtable_insert(&AllBitmapsNames, AllBitmaps[Num_bitmap_files].name.data(), Num_bitmap_files);
312
#if defined(DXX_BUILD_DESCENT_I)
313
        GameBitmaps[Num_bitmap_files] = bmp;
314
#endif
315
        if ( !in_file ) {
316
                GameBitmapOffset[Num_bitmap_files] = 0;
317
                GameBitmapFlags[Num_bitmap_files] = bmp.get_flags();
318
        }
319
        Num_bitmap_files++;
320
 
321
        return temp;
322
}
323
 
324
int piggy_register_sound( digi_sound * snd, const char * name, int in_file )
325
{
326
        int i;
327
 
328
        Assert( Num_sound_files < MAX_SOUND_FILES );
329
 
330
        strncpy( AllSounds[Num_sound_files].name, name, 12 );
331
        hashtable_insert( &AllDigiSndNames, AllSounds[Num_sound_files].name, Num_sound_files );
332
        GameSounds[Num_sound_files] = *snd;
333
#if defined(DXX_BUILD_DESCENT_I)
334
//added/moved on 11/13/99 by Victor Rachels to ready for changing freq
335
//#ifdef ALLEGRO
336
        GameSounds[Num_sound_files].bits = snd->bits;
337
        GameSounds[Num_sound_files].freq = snd->freq;
338
 
339
#ifdef ALLEGRO
340
//end this section move - VR
341
        GameSounds[Num_sound_files].priority = 128;
342
        GameSounds[Num_sound_files].loop_start = 0;
343
        GameSounds[Num_sound_files].loop_end = GameSounds[Num_sound_files].len;
344
        GameSounds[Num_sound_files].param = -1;
345
#endif
346
#endif
347
        if ( !in_file ) {
348
                SoundOffset[Num_sound_files] = 0;      
349
        }
350
#if defined(DXX_BUILD_DESCENT_I)
351
        else if (SoundOffset[Num_sound_files] == 0)
352
                SoundOffset[Num_sound_files] = -1;              // make sure this sound's data is not individually freed
353
#endif
354
 
355
        i = Num_sound_files;
356
        Num_sound_files++;
357
        return i;
358
}
359
 
360
bitmap_index piggy_find_bitmap(const char * name)
361
{
362
        bitmap_index bmp;
363
 
364
        bmp.index = 0;
365
 
366
#if defined(DXX_BUILD_DESCENT_II)
367
        size_t namelen;
368
        const char *t;
369
        if ((t=strchr(name,'#'))!=NULL)
370
                namelen = t - name;
371
        else
372
                namelen = strlen(name);
373
 
374
        char temp[FILENAME_LEN];
375
        range_for (auto &i, partial_const_range(alias_list, Num_aliases))
376
                if (i.alias_name[namelen] == 0 && d_strnicmp(name, i.alias_name,namelen)==0) {
377
                        if (t) {                //extra stuff for ABMs
378
                                struct splitpath_t path;
379
                                d_splitpath(i.file_name, &path);
380
                                snprintf(temp, sizeof(temp), "%.*s%s\n", DXX_ptrdiff_cast_int(path.base_end - path.base_start), path.base_start, t);
381
                                name = temp;
382
                        }
383
                        else
384
                                name = i.file_name;
385
                        break;
386
                }
387
#endif
388
 
389
        int i;
390
        i = hashtable_search( &AllBitmapsNames, name );
391
        Assert( i != 0 );
392
        if ( i < 0 )
393
                return bmp;
394
 
395
        bmp.index = i;
396
        return bmp;
397
}
398
 
399
}
400
 
401
int piggy_find_sound(const char *name)
402
{
403
        int i;
404
 
405
        i = hashtable_search( &AllDigiSndNames, name );
406
 
407
        if ( i < 0 )
408
                return 255;
409
 
410
        return i;
411
}
412
 
413
namespace dsx {
414
static void piggy_close_file()
415
{
416
        if (Piggy_fp)
417
        {
418
                Piggy_fp.reset();
419
#if defined(DXX_BUILD_DESCENT_II)
420
                Current_pigfile[0] = 0;
421
#endif
422
        }
423
}
424
}
425
 
426
#if defined(DXX_BUILD_DESCENT_I)
427
int properties_init()
428
{
429
        int sbytes = 0;
430
        std::array<char, 13> temp_name;
431
        digi_sound temp_sound;
432
        DiskBitmapHeader bmh;
433
        DiskSoundHeader sndh;
434
        int header_size, N_bitmaps, N_sounds;
435
        int size;
436
        int Pigdata_start;
437
        int pigsize;
438
        int retval;
439
        GameSounds = {};
440
        SoundOffset = {};
441
 
442
        static_assert(GameBitmapXlat.size() == GameBitmaps.size(), "size mismatch");
443
        for (unsigned i = 0; i < GameBitmaps.size(); ++i)
444
        {
445
                GameBitmapXlat[i] = i;
446
                GameBitmaps[i].set_flags(BM_FLAG_PAGED_OUT);
447
        }
448
 
449
        if ( !bogus_bitmap_initialized )        {
450
                ubyte c;
451
                bogus_bitmap_initialized = 1;
452
                c = gr_find_closest_color( 0, 0, 63 );
453
                bogus_data.fill(c);
454
                c = gr_find_closest_color( 63, 0, 0 );
455
                // Make a big red X !
456
                range_for (const unsigned i, xrange(64u))
457
                {
458
                        bogus_data[i*64+i] = c;
459
                        bogus_data[i*64+(63-i)] = c;
460
                }
461
                gr_init_bitmap(bogus_bitmap, bm_mode::linear, 0, 0, 64, 64, 64, bogus_data.data());
462
                piggy_register_bitmap(bogus_bitmap, "bogus", 1);
463
#ifdef ALLEGRO
464
                bogus_sound.len = 64*64;
465
#else
466
        bogus_sound.length = 64*64;
467
#endif
468
                bogus_sound.data = bogus_data.data();
469
//added on 11/13/99 by Victor Rachels to ready for changing freq
470
                bogus_sound.freq = 11025;
471
                bogus_sound.bits = 8;
472
//end this section addition - VR
473
                GameBitmapOffset[0] = 0;
474
        }
475
 
476
        Piggy_fp = PHYSFSX_openReadBuffered(DEFAULT_PIGFILE_REGISTERED);
477
        if (!Piggy_fp)
478
        {
479
                if (!PHYSFSX_exists("BITMAPS.TBL",1) && !PHYSFSX_exists("BITMAPS.BIN",1))
480
                        Error("Cannot find " DEFAULT_PIGFILE_REGISTERED " or BITMAPS.TBL");
481
                return 1;       // need to run gamedata_read_tbl
482
        }
483
 
484
        pigsize = PHYSFS_fileLength(Piggy_fp);
485
        switch (pigsize) {
486
                case D1_SHARE_BIG_PIGSIZE:
487
                case D1_SHARE_10_PIGSIZE:
488
                case D1_SHARE_PIGSIZE:
489
                        PCSharePig = 1;
490
                        Pigdata_start = 0;
491
                        break;
492
                case D1_10_BIG_PIGSIZE:
493
                case D1_10_PIGSIZE:
494
                        Pigdata_start = 0;
495
                        break;
496
                default:
497
                        Warning("Unknown size for " DEFAULT_PIGFILE_REGISTERED);
498
                        Int3();
499
                        DXX_BOOST_FALLTHROUGH;
500
                case D1_MAC_PIGSIZE:
501
                case D1_MAC_SHARE_PIGSIZE:
502
                        MacPig = 1;
503
                        DXX_BOOST_FALLTHROUGH;
504
                case D1_PIGSIZE:
505
                case D1_OEM_PIGSIZE:
506
                        Pigdata_start = PHYSFSX_readInt(Piggy_fp );
507
                        break;
508
        }
509
 
510
        HiresGFXAvailable = MacPig;     // for now at least
511
 
512
        if (PCSharePig)
513
                retval = PIGGY_PC_SHAREWARE;    // run gamedata_read_tbl in shareware mode
514
        else if (GameArg.EdiNoBm || (!PHYSFSX_exists("BITMAPS.TBL",1) && !PHYSFSX_exists("BITMAPS.BIN",1)))
515
        {
516
                properties_read_cmp(Vclip, Piggy_fp);   // Note connection to above if!!!
517
                range_for (auto &i, GameBitmapXlat)
518
                {
519
                        i = PHYSFSX_readShort(Piggy_fp);
520
                        if (PHYSFS_eof(Piggy_fp))
521
                                break;
522
                }
523
                retval = 0;     // don't run gamedata_read_tbl
524
        }
525
        else
526
                retval = 1;     // run gamedata_read_tbl
527
 
528
        PHYSFSX_fseek( Piggy_fp, Pigdata_start, SEEK_SET );
529
        size = PHYSFS_fileLength(Piggy_fp) - Pigdata_start;
530
 
531
        N_bitmaps = PHYSFSX_readInt(Piggy_fp);
532
        size -= sizeof(int);
533
        N_sounds = PHYSFSX_readInt(Piggy_fp);
534
        size -= sizeof(int);
535
 
536
        header_size = (N_bitmaps*sizeof(DiskBitmapHeader)) + (N_sounds*sizeof(DiskSoundHeader));
537
 
538
        for (unsigned i = 0; i < N_bitmaps; ++i)
539
        {
540
                DiskBitmapHeader_read(&bmh, Piggy_fp);
541
 
542
                GameBitmapFlags[i+1] = 0;
543
                if ( bmh.flags & BM_FLAG_TRANSPARENT ) GameBitmapFlags[i+1] |= BM_FLAG_TRANSPARENT;
544
                if ( bmh.flags & BM_FLAG_SUPER_TRANSPARENT ) GameBitmapFlags[i+1] |= BM_FLAG_SUPER_TRANSPARENT;
545
                if ( bmh.flags & BM_FLAG_NO_LIGHTING ) GameBitmapFlags[i+1] |= BM_FLAG_NO_LIGHTING;
546
                if ( bmh.flags & BM_FLAG_RLE ) GameBitmapFlags[i+1] |= BM_FLAG_RLE;
547
 
548
                GameBitmapOffset[i+1] = bmh.offset + header_size + (sizeof(int)*2) + Pigdata_start;
549
                Assert( (i+1) == Num_bitmap_files );
550
 
551
                //size -= sizeof(DiskBitmapHeader);
552
                get_bitmap_name_from_header(temp_name, bmh);
553
 
554
                grs_bitmap temp_bitmap{};
555
                const auto iwidth = (bmh.dflags & DBM_FLAG_LARGE) ? bmh.width + 256 : bmh.width;
556
                gr_init_bitmap(temp_bitmap, bm_mode::linear, 0, 0,
557
                        iwidth, bmh.height,
558
                        iwidth, Piggy_bitmap_cache_data);
559
                temp_bitmap.add_flags(BM_FLAG_PAGED_OUT);
560
#if !DXX_USE_OGL
561
                temp_bitmap.avg_color = bmh.avg_color;
562
#endif
563
 
564
                if (MacPig)
565
                {
566
                        // HACK HACK HACK!!!!!
567
                        if (!d_strnicmp(bmh.name, "cockpit") || !d_strnicmp(bmh.name, "status") || !d_strnicmp(bmh.name, "rearview")) {
568
                                temp_bitmap.bm_w = temp_bitmap.bm_rowsize = 640;
569
                                if (GameBitmapFlags[i+1] & BM_FLAG_RLE)
570
                                        GameBitmapFlags[i+1] |= BM_FLAG_RLE_BIG;
571
                        }
572
                        if (!d_strnicmp(bmh.name, "cockpit") || !d_strnicmp(bmh.name, "rearview"))
573
                                temp_bitmap.bm_h = 480;
574
                }
575
 
576
                piggy_register_bitmap(temp_bitmap, temp_name.data(), 1);
577
        }
578
 
579
        if (!MacPig)
580
        {
581
        for (unsigned i = 0; i < N_sounds; ++i)
582
        {
583
                DiskSoundHeader_read(&sndh, Piggy_fp);
584
 
585
                //size -= sizeof(DiskSoundHeader);
586
#ifdef ALLEGRO
587
                temp_sound.len = sndh.length;
588
#else
589
                temp_sound.length = sndh.length;
590
#endif
591
 
592
//added on 11/13/99 by Victor Rachels to ready for changing freq
593
                temp_sound.bits = 8;
594
                temp_sound.freq = 11025;
595
//end this section addition - VR
596
                temp_sound.data = reinterpret_cast<uint8_t *>(sndh.offset + header_size + (sizeof(int)*2)+Pigdata_start);
597
                SoundOffset[Num_sound_files] = sndh.offset + header_size + (sizeof(int)*2)+Pigdata_start;
598
                if (PCSharePig)
599
                        SoundCompressed[Num_sound_files] = sndh.data_length;
600
                std::array<char, 9> temp_name_read;
601
                memcpy(temp_name_read.data(), sndh.name, temp_name_read.size() - 1);
602
                temp_name_read.back() = 0;
603
                piggy_register_sound(&temp_sound, temp_name_read.data(), 1);
604
                sbytes += sndh.length;
605
        }
606
 
607
                SoundBits = std::make_unique<ubyte[]>(sbytes + 16);
608
        }
609
 
610
#if 1   //def EDITOR
611
        Piggy_bitmap_cache_size = size - header_size - sbytes + 16;
612
        Assert( Piggy_bitmap_cache_size > 0 );
613
#else
614
        Piggy_bitmap_cache_size = PIGGY_BUFFER_SIZE;
615
        if (CGameArg.SysLowMem)
616
                Piggy_bitmap_cache_size = PIGGY_SMALL_BUFFER_SIZE;
617
#endif
618
        BitmapBits = std::make_unique<ubyte[]>(Piggy_bitmap_cache_size);
619
        Piggy_bitmap_cache_data = BitmapBits.get();
620
        Piggy_bitmap_cache_next = 0;
621
 
622
        return retval;
623
}
624
#elif defined(DXX_BUILD_DESCENT_II)
625
 
626
//initialize a pigfile, reading headers
627
//returns the size of all the bitmap data
628
void piggy_init_pigfile(const char *filename)
629
{
630
        int i;
631
        std::array<char, 13> temp_name;
632
        DiskBitmapHeader bmh;
633
        int header_size, N_bitmaps, data_start;
634
#if DXX_USE_EDITOR
635
        int data_size;
636
#endif
637
 
638
        piggy_close_file();             //close old pig if still open
639
 
640
        Piggy_fp = PHYSFSX_openReadBuffered(filename);
641
 
642
        //try pigfile for shareware
643
        if (!Piggy_fp)
644
                Piggy_fp = PHYSFSX_openReadBuffered(DEFAULT_PIGFILE_SHAREWARE);
645
 
646
        if (Piggy_fp) {                         //make sure pig is valid type file & is up-to-date
647
                int pig_id,pig_version;
648
 
649
                pig_id = PHYSFSX_readInt(Piggy_fp);
650
                pig_version = PHYSFSX_readInt(Piggy_fp);
651
                if (pig_id != PIGFILE_ID || pig_version != PIGFILE_VERSION) {
652
                        Piggy_fp.reset(); //out of date pig
653
                                                //..so pretend it's not here
654
                }
655
        }
656
 
657
        if (!Piggy_fp) {
658
 
659
#if DXX_USE_EDITOR
660
                        return;         //if editor, ok to not have pig, because we'll build one
661
                #else
662
                        Error("Cannot load required file <%s>",filename);
663
                #endif
664
        }
665
 
666
        strncpy(Current_pigfile, filename, sizeof(Current_pigfile) - 1);
667
 
668
        N_bitmaps = PHYSFSX_readInt(Piggy_fp);
669
 
670
        header_size = N_bitmaps * sizeof(DiskBitmapHeader);
671
 
672
        data_start = header_size + PHYSFS_tell(Piggy_fp);
673
#if DXX_USE_EDITOR
674
        data_size = PHYSFS_fileLength(Piggy_fp) - data_start;
675
#endif
676
        Num_bitmap_files = 1;
677
 
678
        for (i=0; i<N_bitmaps; i++ )
679
        {
680
                int width;
681
                grs_bitmap *bm = &GameBitmaps[i + 1];
682
 
683
                DiskBitmapHeader_read(&bmh, Piggy_fp);
684
                get_bitmap_name_from_header(temp_name, bmh);
685
                width = bmh.width + (static_cast<short>(bmh.wh_extra & 0x0f) << 8);
686
                gr_init_bitmap(*bm, bm_mode::linear, 0, 0, width, bmh.height + (static_cast<short>(bmh.wh_extra & 0xf0) << 4), width, NULL);
687
                bm->set_flags(BM_FLAG_PAGED_OUT);
688
#if !DXX_USE_OGL
689
                bm->avg_color = bmh.avg_color;
690
#endif
691
 
692
                GameBitmapFlags[i+1] = bmh.flags & BM_FLAGS_TO_COPY;
693
 
694
                GameBitmapOffset[i+1] = bmh.offset + data_start;
695
                Assert( (i+1) == Num_bitmap_files );
696
                piggy_register_bitmap(*bm, temp_name.data(), 1);
697
        }
698
 
699
#if DXX_USE_EDITOR
700
        Piggy_bitmap_cache_size = data_size + (data_size/10);   //extra mem for new bitmaps
701
        Assert( Piggy_bitmap_cache_size > 0 );
702
#else
703
        Piggy_bitmap_cache_size = PIGGY_BUFFER_SIZE;
704
        if (CGameArg.SysLowMem)
705
                Piggy_bitmap_cache_size = PIGGY_SMALL_BUFFER_SIZE;
706
#endif
707
        BitmapBits = std::make_unique<ubyte[]>(Piggy_bitmap_cache_size);
708
        Piggy_bitmap_cache_data = BitmapBits.get();
709
        Piggy_bitmap_cache_next = 0;
710
 
711
        Pigfile_initialized=1;
712
}
713
 
714
//reads in a new pigfile (for new palette)
715
//returns the size of all the bitmap data
716
void piggy_new_pigfile(char *pigname)
717
{
718
        int i;
719
        std::array<char, 13> temp_name;
720
        DiskBitmapHeader bmh;
721
        int header_size, N_bitmaps, data_start;
722
#if DXX_USE_EDITOR
723
        int must_rewrite_pig = 0;
724
#endif
725
 
726
        d_strlwr(pigname);
727
 
728
        if (d_strnicmp(Current_pigfile, pigname, sizeof(Current_pigfile)) == 0 // correct pig already loaded
729
            && !Bitmap_replacement_data) // no need to reload: no bitmaps were altered
730
                return;
731
 
732
        if (!Pigfile_initialized) {                     //have we ever opened a pigfile?
733
                piggy_init_pigfile(pigname);            //..no, so do initialization stuff
734
                return;
735
        }
736
        else
737
                piggy_close_file();             //close old pig if still open
738
 
739
        Piggy_bitmap_cache_next = 0;            //free up cache
740
 
741
        strncpy(Current_pigfile, pigname, sizeof(Current_pigfile) - 1);
742
 
743
        Piggy_fp = PHYSFSX_openReadBuffered(pigname);
744
 
745
        //try pigfile for shareware
746
        if (!Piggy_fp)
747
                Piggy_fp = PHYSFSX_openReadBuffered(DEFAULT_PIGFILE_SHAREWARE);
748
 
749
        if (Piggy_fp) {  //make sure pig is valid type file & is up-to-date
750
                int pig_id,pig_version;
751
 
752
                pig_id = PHYSFSX_readInt(Piggy_fp);
753
                pig_version = PHYSFSX_readInt(Piggy_fp);
754
                if (pig_id != PIGFILE_ID || pig_version != PIGFILE_VERSION) {
755
                        Piggy_fp.reset();              //out of date pig
756
                                                //..so pretend it's not here
757
                }
758
        }
759
 
760
#if !DXX_USE_EDITOR
761
        if (!Piggy_fp)
762
                Error("Cannot open correct version of <%s>", pigname);
763
#endif
764
 
765
        if (Piggy_fp) {
766
 
767
                N_bitmaps = PHYSFSX_readInt(Piggy_fp);
768
 
769
                header_size = N_bitmaps * sizeof(DiskBitmapHeader);
770
 
771
                data_start = header_size + PHYSFS_tell(Piggy_fp);
772
 
773
                for (i=1; i<=N_bitmaps; i++ )
774
                {
775
                        grs_bitmap *bm = &GameBitmaps[i];
776
                        int width;
777
 
778
                        DiskBitmapHeader_read(&bmh, Piggy_fp);
779
                        get_bitmap_name_from_header(temp_name, bmh);
780
#if DXX_USE_EDITOR
781
                        //Make sure name matches
782
                        if (strcmp(temp_name.data(), AllBitmaps[i].name.data()))
783
                        {
784
                                //Int3();       //this pig is out of date.  Delete it
785
                                must_rewrite_pig=1;
786
                        }
787
#endif
788
 
789
                        AllBitmaps[i].name = temp_name;
790
 
791
                        width = bmh.width + (static_cast<short>(bmh.wh_extra & 0x0f) << 8);
792
                        gr_set_bitmap_data(*bm, NULL);  // free ogl texture
793
                        gr_init_bitmap(*bm, bm_mode::linear, 0, 0, width, bmh.height + (static_cast<short>(bmh.wh_extra & 0xf0) << 4), width, NULL);
794
                        bm->set_flags(BM_FLAG_PAGED_OUT);
795
#if !DXX_USE_OGL
796
                        bm->avg_color = bmh.avg_color;
797
#endif
798
 
799
                        GameBitmapFlags[i] = bmh.flags & BM_FLAGS_TO_COPY;
800
 
801
                        GameBitmapOffset[i] = bmh.offset + data_start;
802
                }
803
        }
804
        else
805
                N_bitmaps = 0;          //no pigfile, so no bitmaps
806
 
807
#if !DXX_USE_EDITOR
808
 
809
        Assert(N_bitmaps == Num_bitmap_files-1);
810
 
811
        #else
812
 
813
        if (must_rewrite_pig || (N_bitmaps < Num_bitmap_files-1)) {
814
                int size;
815
 
816
                //re-read the bitmaps that aren't in this pig
817
 
818
                for (i=N_bitmaps+1;i<Num_bitmap_files;i++) {
819
                        if (const auto p = strchr(AllBitmaps[i].name.data(), '#'))
820
                        {   // this is an ABM == animated bitmap
821
                                char abmname[FILENAME_LEN];
822
                                unsigned fnum;
823
                                int iff_error;          //reference parm to avoid warning message
824
                                palette_array_t newpal;
825
                                char basename[FILENAME_LEN];
826
                                unsigned nframes;
827
 
828
                                const std::size_t len = p - AllBitmaps[i].name.data();
829
                                cf_assert(len < AllBitmaps[i].name.size());
830
                                memcpy(basename, AllBitmaps[i].name.data(), len);
831
                                basename[len] = 0;
832
 
833
                                snprintf(abmname, sizeof(abmname), "%.8s.abm", basename);
834
 
835
                                std::array<std::unique_ptr<grs_main_bitmap>, MAX_BITMAPS_PER_BRUSH> bm;
836
                                iff_error = iff_read_animbrush(abmname,bm,&nframes,newpal);
837
 
838
                                if (iff_error != IFF_NO_ERROR)  {
839
                                        Error("File %s - IFF error: %s",abmname,iff_errormsg(iff_error));
840
                                }
841
 
842
                                for (fnum=0;fnum<nframes; fnum++)       {
843
                                        char tempname[20];
844
                                        int SuperX;
845
 
846
                                        snprintf(tempname, sizeof(tempname), "%s#%u", basename, fnum);
847
 
848
                                        //SuperX = (GameBitmaps[i+fnum].bm_flags&BM_FLAG_SUPER_TRANSPARENT)?254:-1;
849
                                        SuperX = (GameBitmapFlags[i+fnum]&BM_FLAG_SUPER_TRANSPARENT)?254:-1;
850
                                        //above makes assumption that supertransparent color is 254
851
 
852
                                        gr_remap_bitmap_good(*bm[fnum].get(), newpal, iff_has_transparency ? iff_transparent_color : -1, SuperX);
853
 
854
#if !DXX_USE_OGL
855
                                        bm[fnum]->avg_color = compute_average_pixel(bm[fnum].get());
856
#endif
857
 
858
                                        if ( GameArg.EdiMacData )
859
                                                swap_0_255(*bm[fnum].get());
860
 
861
                                        if (CGameArg.DbgNoCompressPigBitmap)
862
                                                gr_bitmap_rle_compress(*bm[fnum].get());
863
 
864
                                        if (bm[fnum]->get_flag_mask(BM_FLAG_RLE))
865
                                                size = *reinterpret_cast<const int *>(bm[fnum]->bm_data);
866
                                        else
867
                                                size = bm[fnum]->bm_w * bm[fnum]->bm_h;
868
 
869
                                        memcpy( &Piggy_bitmap_cache_data[Piggy_bitmap_cache_next],bm[fnum]->bm_data,size);
870
                                        d_free(bm[fnum]->bm_mdata);
871
                                        bm[fnum]->bm_mdata = &Piggy_bitmap_cache_data[Piggy_bitmap_cache_next];
872
                                        Piggy_bitmap_cache_next += size;
873
 
874
                                        GameBitmaps[i+fnum] = std::move(*bm[fnum]);
875
                                }
876
 
877
                                i += nframes-1;         //filled in multiple bitmaps
878
                        }
879
                        else {          //this is a BBM
880
 
881
                                grs_bitmap n;
882
                                palette_array_t newpal;
883
                                int iff_error;
884
                                char bbmname[FILENAME_LEN];
885
                                int SuperX;
886
 
887
                                snprintf(bbmname, sizeof(bbmname), "%.8s.bbm", AllBitmaps[i].name.data());
888
                                iff_error = iff_read_bitmap(bbmname, n, &newpal);
889
 
890
                                if (iff_error != IFF_NO_ERROR)          {
891
                                        Error("File %s - IFF error: %s",bbmname,iff_errormsg(iff_error));
892
                                }
893
 
894
                                SuperX = (GameBitmapFlags[i]&BM_FLAG_SUPER_TRANSPARENT)?254:-1;
895
                                //above makes assumption that supertransparent color is 254
896
 
897
                                gr_remap_bitmap_good(n, newpal, iff_has_transparency ? iff_transparent_color : -1, SuperX);
898
 
899
#if !DXX_USE_OGL
900
                                n.avg_color = compute_average_pixel(&n);
901
#endif
902
 
903
                                if ( GameArg.EdiMacData )
904
                                        swap_0_255(n);
905
 
906
                                if (CGameArg.DbgNoCompressPigBitmap)
907
                                        gr_bitmap_rle_compress(n);
908
 
909
                                if (n.get_flag_mask(BM_FLAG_RLE))
910
                                        size = *reinterpret_cast<const int *>(n.bm_data);
911
                                else
912
                                        size = n.bm_w * n.bm_h;
913
 
914
                                memcpy( &Piggy_bitmap_cache_data[Piggy_bitmap_cache_next],n.bm_data,size);
915
                                d_free(n.bm_mdata);
916
                                n.bm_mdata = &Piggy_bitmap_cache_data[Piggy_bitmap_cache_next];
917
                                Piggy_bitmap_cache_next += size;
918
 
919
                                GameBitmaps[i] = n;
920
                        }
921
                }
922
 
923
                //@@Dont' do these things which are done when writing
924
                //@@for (i=0; i < Num_bitmap_files; i++ )       {
925
                //@@    bitmap_index bi;
926
                //@@    bi.index = i;
927
                //@@    PIGGY_PAGE_IN( bi );
928
                //@@}
929
                //@@
930
                //@@piggy_close_file();
931
 
932
                piggy_write_pigfile(pigname);
933
 
934
                Current_pigfile[0] = 0;                 //say no pig, to force reload
935
 
936
                piggy_new_pigfile(pigname);             //read in just-generated pig
937
 
938
 
939
        }
940
        #endif  //ifdef EDITOR
941
 
942
}
943
 
944
#define HAMFILE_ID              MAKE_SIG('!','M','A','H') //HAM!
945
#define HAMFILE_VERSION 3
946
//version 1 -> 2:  save marker_model_num
947
//version 2 -> 3:  removed sound files
948
 
949
#define SNDFILE_ID              MAKE_SIG('D','N','S','D') //DSND
950
#define SNDFILE_VERSION 1
951
 
952
int read_hamfile()
953
{
954
        int ham_id;
955
        int sound_offset = 0;
956
        int shareware = 0;
957
 
958
        auto ham_fp = PHYSFSX_openReadBuffered(DEFAULT_HAMFILE_REGISTERED);
959
 
960
        if (!ham_fp)
961
        {
962
                ham_fp = PHYSFSX_openReadBuffered(DEFAULT_HAMFILE_SHAREWARE);
963
                if (ham_fp)
964
                {
965
                        shareware = 1;
966
                        GameArg.SndDigiSampleRate = SAMPLE_RATE_11K;
967
                        if (CGameArg.SndDisableSdlMixer)
968
                        {
969
                                digi_close();
970
                                digi_init();
971
                        }
972
                }
973
        }
974
 
975
        if (!ham_fp) {
976
                return 0;
977
        }
978
 
979
        //make sure ham is valid type file & is up-to-date
980
        ham_id = PHYSFSX_readInt(ham_fp);
981
        Piggy_hamfile_version = PHYSFSX_readInt(ham_fp);
982
        if (ham_id != HAMFILE_ID)
983
                Error("Cannot open ham file %s or %s\n", DEFAULT_HAMFILE_REGISTERED, DEFAULT_HAMFILE_SHAREWARE);
984
#if 0
985
        if (ham_id != HAMFILE_ID || Piggy_hamfile_version != HAMFILE_VERSION) {
986
                Must_write_hamfile = 1;
987
                PHYSFS_close(ham_fp);                                           //out of date ham
988
                return 0;
989
        }
990
#endif
991
 
992
        if (Piggy_hamfile_version < 3) // hamfile contains sound info, probably PC demo
993
        {
994
                sound_offset = PHYSFSX_readInt(ham_fp);
995
 
996
                if (shareware) // deal with interactive PC demo
997
                {
998
                        GameArg.GfxSkipHiresGFX = 1;
999
                        //CGameArg.SysLowMem = 1;
1000
                }
1001
        }
1002
 
1003
        #if 1 //ndef EDITOR
1004
        {
1005
                bm_read_all(Vclip, ham_fp);
1006
                //PHYSFS_read( ham_fp, GameBitmapXlat, sizeof(ushort)*MAX_BITMAP_FILES, 1 );
1007
                range_for (auto &i, GameBitmapXlat)
1008
                {
1009
                        i = PHYSFSX_readShort(ham_fp);
1010
                        if (PHYSFS_eof(ham_fp))
1011
                                break;
1012
                }
1013
        }
1014
        #endif
1015
 
1016
        if (Piggy_hamfile_version < 3) {
1017
                int N_sounds;
1018
                int sound_start;
1019
                int header_size;
1020
                int i;
1021
                DiskSoundHeader sndh;
1022
                digi_sound temp_sound;
1023
                char temp_name_read[16];
1024
                int sbytes = 0;
1025
                static int justonce = 1;
1026
 
1027
                if (!justonce)
1028
                {
1029
                        return 1;
1030
                }
1031
                justonce = 0;
1032
 
1033
                PHYSFSX_fseek(ham_fp, sound_offset, SEEK_SET);
1034
                N_sounds = PHYSFSX_readInt(ham_fp);
1035
 
1036
                sound_start = PHYSFS_tell(ham_fp);
1037
 
1038
                header_size = N_sounds * sizeof(DiskSoundHeader);
1039
 
1040
                //Read sounds
1041
 
1042
                for (i=0; i<N_sounds; i++ ) {
1043
                        DiskSoundHeader_read(&sndh, ham_fp);
1044
                        temp_sound.length = sndh.length;
1045
                        temp_sound.data = reinterpret_cast<uint8_t *>(static_cast<uintptr_t>(sndh.offset + header_size + sound_start));
1046
                        SoundOffset[Num_sound_files] = sndh.offset + header_size + sound_start;
1047
                        memcpy( temp_name_read, sndh.name, 8 );
1048
                        temp_name_read[8] = 0;
1049
                        piggy_register_sound( &temp_sound, temp_name_read, 1 );
1050
                        if (piggy_is_needed(i))
1051
                                sbytes += sndh.length;
1052
                }
1053
                SoundBits = std::make_unique<ubyte[]>(sbytes + 16);
1054
        }
1055
        return 1;
1056
}
1057
 
1058
#if defined(DXX_BUILD_DESCENT_I)
1059
static
1060
#endif
1061
int read_sndfile()
1062
{
1063
        int snd_id,snd_version;
1064
        int N_sounds;
1065
        int sound_start;
1066
        int header_size;
1067
        int i;
1068
        DiskSoundHeader sndh;
1069
        digi_sound temp_sound;
1070
        char temp_name_read[16];
1071
        int sbytes = 0;
1072
 
1073
        auto snd_fp = PHYSFSX_openReadBuffered(DEFAULT_SNDFILE);
1074
        if (!snd_fp)
1075
                return 0;
1076
 
1077
        //make sure soundfile is valid type file & is up-to-date
1078
        snd_id = PHYSFSX_readInt(snd_fp);
1079
        snd_version = PHYSFSX_readInt(snd_fp);
1080
        if (snd_id != SNDFILE_ID || snd_version != SNDFILE_VERSION) {
1081
                return 0;
1082
        }
1083
 
1084
        N_sounds = PHYSFSX_readInt(snd_fp);
1085
 
1086
        sound_start = PHYSFS_tell(snd_fp);
1087
        header_size = N_sounds*sizeof(DiskSoundHeader);
1088
 
1089
        //Read sounds
1090
 
1091
        for (i=0; i<N_sounds; i++ ) {
1092
                DiskSoundHeader_read(&sndh, snd_fp);
1093
                temp_sound.length = sndh.length;
1094
                temp_sound.data = reinterpret_cast<uint8_t *>(static_cast<uintptr_t>(sndh.offset + header_size + sound_start));
1095
                SoundOffset[Num_sound_files] = sndh.offset + header_size + sound_start;
1096
                memcpy( temp_name_read, sndh.name, 8 );
1097
                temp_name_read[8] = 0;
1098
                piggy_register_sound( &temp_sound, temp_name_read, 1 );
1099
                if (piggy_is_needed(i))
1100
                        sbytes += sndh.length;
1101
        }
1102
        SoundBits = std::make_unique<ubyte[]>(sbytes + 16);
1103
        return 1;
1104
}
1105
 
1106
int properties_init(void)
1107
{
1108
        int ham_ok=0,snd_ok=0;
1109
        for (unsigned i = 0; i < MAX_SOUND_FILES; ++i)
1110
        {
1111
                GameSounds[i].length = 0;
1112
                GameSounds[i].data = NULL;
1113
                SoundOffset[i] = 0;
1114
        }
1115
 
1116
        for (unsigned i = 0; i < GameBitmapXlat.size(); ++i)
1117
        {
1118
                GameBitmapXlat[i] = i;
1119
        }
1120
 
1121
        if ( !bogus_bitmap_initialized )        {
1122
                ubyte c;
1123
 
1124
                bogus_bitmap_initialized = 1;
1125
                c = gr_find_closest_color( 0, 0, 63 );
1126
                bogus_data.fill(c);
1127
                c = gr_find_closest_color( 63, 0, 0 );
1128
                // Make a big red X !
1129
                range_for (const unsigned i, xrange(64u))
1130
                {
1131
                        bogus_data[i*64+i] = c;
1132
                        bogus_data[i*64+(63-i)] = c;
1133
                }
1134
                gr_init_bitmap(GameBitmaps[Num_bitmap_files], bm_mode::linear, 0, 0, 64, 64, 64, bogus_data.data());
1135
                piggy_register_bitmap(GameBitmaps[Num_bitmap_files], "bogus", 1);
1136
                bogus_sound.length = 64*64;
1137
                bogus_sound.data = bogus_data.data();
1138
                GameBitmapOffset[0] = 0;
1139
        }
1140
 
1141
        snd_ok = ham_ok = read_hamfile();
1142
 
1143
        if (Piggy_hamfile_version >= 3)
1144
        {
1145
                snd_ok = read_sndfile();
1146
                if (!snd_ok)
1147
                        Error("Cannot open sound file: %s\n", DEFAULT_SNDFILE);
1148
        }
1149
 
1150
        return (ham_ok && snd_ok);               //read ok
1151
}
1152
#endif
1153
 
1154
static int piggy_is_needed(int soundnum)
1155
{
1156
        if (!CGameArg.SysLowMem)
1157
                return 1;
1158
 
1159
        range_for (auto i, AltSounds)
1160
        {
1161
                if (i < 255 && Sounds[i] == soundnum)
1162
                        return 1;
1163
        }
1164
        return 0;
1165
}
1166
 
1167
#if defined(DXX_BUILD_DESCENT_I)
1168
void piggy_read_sounds(int pc_shareware)
1169
{
1170
        uint8_t * ptr;
1171
        int i, sbytes;
1172
        int lastsize = 0;
1173
 
1174
        if (MacPig)
1175
        {
1176
                // Read Mac sounds converted to RAW format (too messy to read them directly from the resource fork code-wise)
1177
                char soundfile[32] = "Sounds/sounds.array";
1178
                // hack for Mac Demo
1179
                if (auto array = PHYSFSX_openReadBuffered(soundfile))
1180
                {
1181
                        if (PHYSFS_read(array, Sounds, Sounds.size(), 1) != 1)  // make the 'Sounds' index array match with the sounds we're about to read in
1182
                        {
1183
                                con_printf(CON_URGENT,"Warning: Can't read Sounds/sounds.array: %s", PHYSFS_getErrorByCode(PHYSFS_getLastErrorCode())); // Pierre-Marie Baty -- work around PHYSFS_getLastError() deprecation
1184
                                return;
1185
                        }
1186
                }
1187
                else if (PHYSFSX_fsize(DEFAULT_PIGFILE_REGISTERED) == D1_MAC_SHARE_PIGSIZE)
1188
                {
1189
                        con_printf(CON_URGENT,"Warning: Missing Sounds/sounds.array for Mac data files");
1190
                        return;
1191
                }
1192
 
1193
                for (i = 0; i < MAX_SOUND_FILES; i++)
1194
                {
1195
                        snprintf(soundfile, sizeof(soundfile), "SND%04d.raw", i);
1196
                        if (ds_load(0, soundfile) == 255)
1197
                                break;
1198
                }
1199
 
1200
                return;
1201
        }
1202
 
1203
        ptr = SoundBits.get();
1204
        sbytes = 0;
1205
 
1206
        RAIIdmem<uint8_t[]> lastbuf;
1207
        for (i=0; i<Num_sound_files; i++ )
1208
        {
1209
                digi_sound *snd = &GameSounds[i];
1210
 
1211
                if ( SoundOffset[i] > 0 )
1212
                {
1213
                        if ( piggy_is_needed(i) )
1214
                        {
1215
                                PHYSFSX_fseek( Piggy_fp, SoundOffset[i], SEEK_SET );
1216
 
1217
                                // Read in the sound data!!!
1218
                                snd->data = ptr;
1219
#ifdef ALLEGRO
1220
                                ptr += snd->len;
1221
                                sbytes += snd->len;
1222
#else
1223
                                ptr += snd->length;
1224
                                sbytes += snd->length;
1225
#endif
1226
                //Arne's decompress for shareware on all soundcards - Tim@Rikers.org
1227
                                if (pc_shareware)
1228
                                {
1229
                                        if (lastsize < SoundCompressed[i]) {
1230
                                                MALLOC(lastbuf, uint8_t[], SoundCompressed[i]);
1231
                                        }
1232
                                        PHYSFS_read( Piggy_fp, lastbuf, SoundCompressed[i], 1 );
1233
                                        sound_decompress(lastbuf.get(), SoundCompressed[i], snd->data);
1234
                                }
1235
                                else
1236
#ifdef ALLEGRO
1237
                                        PHYSFS_read( Piggy_fp, snd->data, snd->len, 1 );
1238
#else
1239
                                        PHYSFS_read( Piggy_fp, snd->data, snd->length, 1 );
1240
#endif
1241
                        }
1242
                }
1243
        }
1244
}
1245
#elif defined(DXX_BUILD_DESCENT_II)
1246
void piggy_read_sounds(void)
1247
{
1248
        uint8_t * ptr;
1249
        int i, sbytes;
1250
 
1251
        ptr = SoundBits.get();
1252
        sbytes = 0;
1253
        auto fp = PHYSFSX_openReadBuffered(DEFAULT_SNDFILE);
1254
        if (!fp)
1255
                return;
1256
 
1257
        for (i=0; i<Num_sound_files; i++ )      {
1258
                digi_sound *snd = &GameSounds[i];
1259
 
1260
                if ( SoundOffset[i] > 0 )       {
1261
                        if ( piggy_is_needed(i) )       {
1262
                                PHYSFSX_fseek( fp, SoundOffset[i], SEEK_SET );
1263
 
1264
                                // Read in the sound data!!!
1265
                                snd->data = ptr;
1266
                                ptr += snd->length;
1267
                                sbytes += snd->length;
1268
                                PHYSFS_read( fp, snd->data, snd->length, 1 );
1269
                        }
1270
                        else
1271
                                snd->data = reinterpret_cast<uint8_t *>(-1);
1272
                }
1273
        }
1274
}
1275
#endif
1276
 
1277
namespace dsx {
1278
void piggy_bitmap_page_in( bitmap_index bitmap )
1279
{
1280
        grs_bitmap * bmp;
1281
        int i,org_i;
1282
 
1283
        org_i = 0;
1284
 
1285
        i = bitmap.index;
1286
        Assert( i >= 0 );
1287
        Assert( i < MAX_BITMAP_FILES );
1288
        Assert( i < Num_bitmap_files );
1289
        Assert( Piggy_bitmap_cache_size > 0 );
1290
 
1291
        if ( i < 1 ) return;
1292
        if ( i >= MAX_BITMAP_FILES ) return;
1293
        if ( i >= Num_bitmap_files ) return;
1294
 
1295
        if ( GameBitmapOffset[i] == 0 ) return;         // A read-from-disk bitmap!!!
1296
 
1297
        if (CGameArg.SysLowMem)
1298
        {
1299
                org_i = i;
1300
                i = GameBitmapXlat[i];          // Xlat for low-memory settings!
1301
        }
1302
 
1303
        bmp = &GameBitmaps[i];
1304
 
1305
        if (bmp->get_flag_mask(BM_FLAG_PAGED_OUT))
1306
        {
1307
                pause_game_world_time p;
1308
 
1309
        ReDoIt:
1310
                PHYSFSX_fseek( Piggy_fp, GameBitmapOffset[i], SEEK_SET );
1311
 
1312
                gr_set_bitmap_flags(*bmp, GameBitmapFlags[i]);
1313
#if defined(DXX_BUILD_DESCENT_I)
1314
                gr_set_bitmap_data (*bmp, &Piggy_bitmap_cache_data [Piggy_bitmap_cache_next]);
1315
#endif
1316
 
1317
                if (bmp->get_flag_mask(BM_FLAG_RLE))
1318
                {
1319
                        int zsize = PHYSFSX_readInt(Piggy_fp);
1320
#if defined(DXX_BUILD_DESCENT_I)
1321
 
1322
                        // GET JOHN NOW IF YOU GET THIS ASSERT!!!
1323
                        Assert( Piggy_bitmap_cache_next+zsize < Piggy_bitmap_cache_size );
1324
                        if ( Piggy_bitmap_cache_next+zsize >= Piggy_bitmap_cache_size ) {
1325
                                piggy_bitmap_page_out_all();
1326
                                goto ReDoIt;
1327
                        }
1328
                        memcpy( &Piggy_bitmap_cache_data[Piggy_bitmap_cache_next], &zsize, sizeof(int) );
1329
                        Piggy_bitmap_cache_next += sizeof(int);
1330
                        PHYSFS_read( Piggy_fp, &Piggy_bitmap_cache_data[Piggy_bitmap_cache_next], 1, zsize-4 );
1331
                        if (MacPig)
1332
                        {
1333
                                rle_swap_0_255(*bmp);
1334
                                memcpy(&zsize, bmp->bm_data, 4);
1335
                        }
1336
                        Piggy_bitmap_cache_next += zsize-4;
1337
#elif defined(DXX_BUILD_DESCENT_II)
1338
                        int pigsize = PHYSFS_fileLength(Piggy_fp);
1339
 
1340
                        // GET JOHN NOW IF YOU GET THIS ASSERT!!!
1341
                        //Assert( Piggy_bitmap_cache_next+zsize < Piggy_bitmap_cache_size );
1342
                        if ( Piggy_bitmap_cache_next+zsize >= Piggy_bitmap_cache_size ) {
1343
                                Int3();
1344
                                piggy_bitmap_page_out_all();
1345
                                goto ReDoIt;
1346
                        }
1347
                        PHYSFS_read( Piggy_fp, &Piggy_bitmap_cache_data[Piggy_bitmap_cache_next+4], 1, zsize-4 );
1348
                        PUT_INTEL_INT(&Piggy_bitmap_cache_data[Piggy_bitmap_cache_next], zsize);
1349
                        gr_set_bitmap_data(*bmp, &Piggy_bitmap_cache_data[Piggy_bitmap_cache_next]);
1350
 
1351
#ifndef MACDATA
1352
                        switch (pigsize) {
1353
                        default:
1354
                                if (!GameArg.EdiMacData)
1355
                                        break;
1356
                                DXX_BOOST_FALLTHROUGH;
1357
                        case MAC_ALIEN1_PIGSIZE:
1358
                        case MAC_ALIEN2_PIGSIZE:
1359
                        case MAC_FIRE_PIGSIZE:
1360
                        case MAC_GROUPA_PIGSIZE:
1361
                        case MAC_ICE_PIGSIZE:
1362
                        case MAC_WATER_PIGSIZE:
1363
                                rle_swap_0_255(*bmp);
1364
                                memcpy(&zsize, bmp->bm_data, 4);
1365
                                break;
1366
                        }
1367
#endif
1368
 
1369
                        Piggy_bitmap_cache_next += zsize;
1370
                        if ( Piggy_bitmap_cache_next+zsize >= Piggy_bitmap_cache_size ) {
1371
                                Int3();
1372
                                piggy_bitmap_page_out_all();
1373
                                goto ReDoIt;
1374
                        }
1375
#endif
1376
 
1377
                } else {
1378
                        // GET JOHN NOW IF YOU GET THIS ASSERT!!!
1379
                        Assert( Piggy_bitmap_cache_next+(bmp->bm_h*bmp->bm_w) < Piggy_bitmap_cache_size );
1380
                        if ( Piggy_bitmap_cache_next+(bmp->bm_h*bmp->bm_w) >= Piggy_bitmap_cache_size ) {
1381
                                piggy_bitmap_page_out_all();
1382
                                goto ReDoIt;
1383
                        }
1384
                        PHYSFS_read( Piggy_fp, &Piggy_bitmap_cache_data[Piggy_bitmap_cache_next], 1, bmp->bm_h*bmp->bm_w );
1385
#if defined(DXX_BUILD_DESCENT_I)
1386
                        Piggy_bitmap_cache_next+=bmp->bm_h*bmp->bm_w;
1387
                        if (MacPig)
1388
                                swap_0_255(*bmp);
1389
#elif defined(DXX_BUILD_DESCENT_II)
1390
                        int pigsize = PHYSFS_fileLength(Piggy_fp);
1391
                        gr_set_bitmap_data(*bmp, &Piggy_bitmap_cache_data[Piggy_bitmap_cache_next]);
1392
                        Piggy_bitmap_cache_next+=bmp->bm_h*bmp->bm_w;
1393
 
1394
#ifndef MACDATA
1395
                        switch (pigsize) {
1396
                        default:
1397
                                if (!GameArg.EdiMacData)
1398
                                        break;
1399
                                DXX_BOOST_FALLTHROUGH;
1400
                        case MAC_ALIEN1_PIGSIZE:
1401
                        case MAC_ALIEN2_PIGSIZE:
1402
                        case MAC_FIRE_PIGSIZE:
1403
                        case MAC_GROUPA_PIGSIZE:
1404
                        case MAC_ICE_PIGSIZE:
1405
                        case MAC_WATER_PIGSIZE:
1406
                                swap_0_255(*bmp);
1407
                                break;
1408
                        }
1409
#endif
1410
#endif
1411
                }
1412
 
1413
                //@@if ( bmp->bm_selector ) {
1414
                //@@#if !defined(WINDOWS) && !defined(MACINTOSH)
1415
                //@@    if (!dpmi_modify_selector_base( bmp->bm_selector, bmp->bm_data ))
1416
                //@@            Error( "Error modifying selector base in piggy.c\n" );
1417
                //@@#endif
1418
                //@@}
1419
 
1420
                compute_average_rgb(bmp, bmp->avg_color_rgb);
1421
 
1422
        }
1423
 
1424
        if (CGameArg.SysLowMem)
1425
        {
1426
                if ( org_i != i )
1427
                        GameBitmaps[org_i] = GameBitmaps[i];
1428
        }
1429
 
1430
//@@Removed from John's code:
1431
//@@#ifndef WINDOWS
1432
//@@    if ( bmp->bm_selector ) {
1433
//@@            if (!dpmi_modify_selector_base( bmp->bm_selector, bmp->bm_data ))
1434
//@@                    Error( "Error modifying selector base in piggy.c\n" );
1435
//@@    }
1436
//@@#endif
1437
 
1438
}
1439
}
1440
 
1441
namespace dsx {
1442
void piggy_bitmap_page_out_all()
1443
{
1444
        int i;
1445
 
1446
        Piggy_bitmap_cache_next = 0;
1447
 
1448
        texmerge_flush();
1449
        rle_cache_flush();
1450
 
1451
        for (i=0; i<Num_bitmap_files; i++ ) {
1452
                if ( GameBitmapOffset[i] > 0 ) {        // Don't page out bitmaps read from disk!!!
1453
                        GameBitmaps[i].set_flags(BM_FLAG_PAGED_OUT);
1454
                        gr_set_bitmap_data(GameBitmaps[i], nullptr);
1455
                }
1456
        }
1457
 
1458
}
1459
}
1460
 
1461
void piggy_load_level_data()
1462
{
1463
        piggy_bitmap_page_out_all();
1464
        paging_touch_all(Vclip);
1465
}
1466
 
1467
#if defined(DXX_BUILD_DESCENT_II)
1468
#if DXX_USE_EDITOR
1469
 
1470
static void piggy_write_pigfile(const char *filename)
1471
{
1472
        int bitmap_data_start, data_offset;
1473
        DiskBitmapHeader bmh;
1474
        int org_offset;
1475
        char subst_name[32];
1476
        int i;
1477
        char tname[FILENAME_LEN];
1478
 
1479
        for (i=0; i < Num_bitmap_files; i++ ) {
1480
                bitmap_index bi;
1481
                bi.index = i;
1482
                PIGGY_PAGE_IN( bi );
1483
        }
1484
 
1485
        piggy_close_file();
1486
 
1487
        auto pig_fp = PHYSFSX_openWriteBuffered(filename);       //open PIG file
1488
        Assert(pig_fp);
1489
 
1490
        write_int(PIGFILE_ID,pig_fp);
1491
        write_int(PIGFILE_VERSION,pig_fp);
1492
 
1493
        Num_bitmap_files--;
1494
        PHYSFS_write( pig_fp, &Num_bitmap_files, sizeof(int), 1 );
1495
        Num_bitmap_files++;
1496
 
1497
        bitmap_data_start = PHYSFS_tell(pig_fp);
1498
        bitmap_data_start += (Num_bitmap_files - 1) * sizeof(DiskBitmapHeader);
1499
        data_offset = bitmap_data_start;
1500
 
1501
        change_filename_extension(tname,filename,"lst");
1502
        auto fp1 = PHYSFSX_openWriteBuffered(tname);
1503
        change_filename_extension(tname,filename,"all");
1504
        auto fp2 = PHYSFSX_openWriteBuffered(tname);
1505
 
1506
        for (i=1; i < Num_bitmap_files; i++ ) {
1507
                grs_bitmap *bmp;
1508
 
1509
                const auto name = AllBitmaps[i].name.data();
1510
                {
1511
                        char *p1;
1512
                        if (const auto p = strchr(name, '#')) {   // this is an ABM == animated bitmap
1513
                                int n;
1514
                                p1 = p; p1++;
1515
                                n = atoi(p1);
1516
                                *p = 0;
1517
                                if (fp2 && n==0)
1518
                                        PHYSFSX_printf( fp2, "%s.abm\n", name);
1519
                                memcpy(bmh.name, name, 8);
1520
                                Assert( n <= DBM_NUM_FRAMES );
1521
                                bmh.dflags = DBM_FLAG_ABM + n;
1522
                                *p = '#';
1523
                        } else {
1524
                                if (fp2)
1525
                                        PHYSFSX_printf( fp2, "%s.bbm\n", name);
1526
                                memcpy(bmh.name, name, 8);
1527
                                bmh.dflags = 0;
1528
                        }
1529
                }
1530
                bmp = &GameBitmaps[i];
1531
 
1532
                assert(!bmp->get_flag_mask(BM_FLAG_PAGED_OUT));
1533
 
1534
                if (fp1)
1535
                        PHYSFSX_printf(fp1, "BMP: %s, size %d bytes", name, bmp->bm_rowsize * bmp->bm_h);
1536
                org_offset = PHYSFS_tell(pig_fp);
1537
                bmh.offset = data_offset - bitmap_data_start;
1538
                PHYSFSX_fseek( pig_fp, data_offset, SEEK_SET );
1539
 
1540
                if (bmp->get_flag_mask(BM_FLAG_RLE))
1541
                {
1542
                        const auto size = reinterpret_cast<const int *>(bmp->bm_data);
1543
                        PHYSFS_write( pig_fp, bmp->bm_data, sizeof(ubyte), *size );
1544
                        data_offset += *size;
1545
                        if (fp1)
1546
                                PHYSFSX_printf( fp1, ", and is already compressed to %d bytes.\n", *size );
1547
                } else {
1548
                        PHYSFS_write( pig_fp, bmp->bm_data, sizeof(ubyte), bmp->bm_rowsize * bmp->bm_h );
1549
                        data_offset += bmp->bm_rowsize * bmp->bm_h;
1550
                        if (fp1)
1551
                                PHYSFSX_printf( fp1, ".\n" );
1552
                }
1553
                PHYSFSX_fseek( pig_fp, org_offset, SEEK_SET );
1554
                Assert( GameBitmaps[i].bm_w < 4096 );
1555
                bmh.width = (GameBitmaps[i].bm_w & 0xff);
1556
                bmh.wh_extra = ((GameBitmaps[i].bm_w >> 8) & 0x0f);
1557
                Assert( GameBitmaps[i].bm_h < 4096 );
1558
                bmh.height = GameBitmaps[i].bm_h;
1559
                bmh.wh_extra |= ((GameBitmaps[i].bm_h >> 4) & 0xf0);
1560
                bmh.flags = GameBitmaps[i].get_flags();
1561
                if (piggy_is_substitutable_bitmap(name, subst_name))
1562
                {
1563
                        bitmap_index other_bitmap;
1564
                        other_bitmap = piggy_find_bitmap( subst_name );
1565
                        GameBitmapXlat[i] = other_bitmap.index;
1566
                        bmh.flags |= BM_FLAG_PAGED_OUT;
1567
                } else {
1568
                        bmh.flags &= ~BM_FLAG_PAGED_OUT;
1569
                }
1570
                bmh.avg_color = compute_average_pixel(&GameBitmaps[i]);
1571
                PHYSFS_write(pig_fp, &bmh, sizeof(DiskBitmapHeader), 1);        // Mark as a bitmap
1572
        }
1573
        PHYSFSX_printf( fp1, " Dumped %d assorted bitmaps.\n", Num_bitmap_files );
1574
}
1575
 
1576
static void write_int(int i, PHYSFS_File *file)
1577
{
1578
        if (PHYSFS_write( file, &i, sizeof(i), 1) != 1)
1579
                Error( "Error reading int in gamesave.c" );
1580
 
1581
}
1582
#endif
1583
#endif
1584
 
1585
namespace dsx {
1586
void piggy_close()
1587
{
1588
        int i;
1589
 
1590
#if defined(DXX_BUILD_DESCENT_I)
1591
        custom_close();
1592
#endif
1593
        piggy_close_file();
1594
        BitmapBits.reset();
1595
        SoundBits.reset();
1596
        for (i = 0; i < Num_sound_files; i++)
1597
                if (SoundOffset[i] == 0)
1598
                        d_free(GameSounds[i].data);
1599
#if defined(DXX_BUILD_DESCENT_II)
1600
        free_bitmap_replacements();
1601
        free_d1_tmap_nums();
1602
#endif
1603
}
1604
}
1605
 
1606
void remove_char( char * s, char c )
1607
{
1608
        char *p = strchr(s,c);
1609
        if (p) *p = '\0';
1610
}
1611
 
1612
#if defined(DXX_BUILD_DESCENT_II)
1613
#if DXX_USE_EDITOR
1614
static const BitmapFile *piggy_does_bitmap_exist_slow(const char *const name)
1615
{
1616
        range_for (auto &i, partial_const_range(AllBitmaps, Num_bitmap_files))
1617
        {
1618
                if (!strcmp(i.name.data(), name))
1619
                        return &i;
1620
        }
1621
        return nullptr;
1622
}
1623
 
1624
 
1625
constexpr char gauge_bitmap_names[][9] = {
1626
        "gauge01",
1627
        "gauge02",
1628
        "gauge06",
1629
        "targ01",
1630
        "targ02",
1631
        "targ03",
1632
        "targ04",
1633
        "targ05",
1634
        "targ06",
1635
        "gauge18",
1636
#if defined(DXX_BUILD_DESCENT_I)
1637
        "targ01pc",
1638
        "targ02pc",
1639
        "targ03pc",
1640
        "gaug18pc"
1641
#elif defined(DXX_BUILD_DESCENT_II)
1642
        "gauge01b",
1643
        "gauge02b",
1644
        "gauge06b",
1645
        "targ01b",
1646
        "targ02b",
1647
        "targ03b",
1648
        "targ04b",
1649
        "targ05b",
1650
        "targ06b",
1651
        "gauge18b",
1652
        "gauss1",
1653
        "helix1",
1654
        "phoenix1"
1655
#endif
1656
};
1657
 
1658
static const char (*piggy_is_gauge_bitmap(const char *const base_name))[9]
1659
{
1660
        range_for (auto &i, gauge_bitmap_names)
1661
        {
1662
                if (!d_stricmp(base_name, i))
1663
                        return &i;
1664
        }
1665
        return nullptr;
1666
}
1667
 
1668
static int piggy_is_substitutable_bitmap(char * name, char (&subst_name)[32])
1669
{
1670
        int frame;
1671
        char * p;
1672
        char base_name[ 16 ];
1673
 
1674
        strcpy( subst_name, name );
1675
        p = strchr( subst_name, '#' );
1676
        if ( p ) {
1677
                frame = atoi( &p[1] );
1678
                *p = 0;
1679
                strcpy( base_name, subst_name );
1680
                if ( !piggy_is_gauge_bitmap( base_name )) {
1681
                        snprintf(subst_name, sizeof(subst_name), "%s#%d", base_name, frame + 1);
1682
                        if ( piggy_does_bitmap_exist_slow( subst_name )  ) {
1683
                                if ( frame & 1 ) {
1684
                                        snprintf(subst_name, sizeof(subst_name), "%s#%d", base_name, frame - 1);
1685
                                        return 1;
1686
                                }
1687
                        }
1688
                }
1689
        }
1690
        strcpy( subst_name, name );
1691
        return 0;
1692
}
1693
#endif
1694
 
1695
 
1696
/*
1697
 * Functions for loading replacement textures
1698
 *  1) From .pog files
1699
 *  2) From descent.pig (for loading d1 levels)
1700
 */
1701
 
1702
static void free_bitmap_replacements()
1703
{
1704
        Bitmap_replacement_data.reset();
1705
}
1706
 
1707
void load_bitmap_replacements(const char *level_name)
1708
{
1709
        char ifile_name[FILENAME_LEN];
1710
        //first, free up data allocated for old bitmaps
1711
        free_bitmap_replacements();
1712
 
1713
        change_filename_extension(ifile_name, level_name, ".POG" );
1714
        if (auto ifile = PHYSFSX_openReadBuffered(ifile_name))
1715
        {
1716
                int id,version;
1717
                unsigned n_bitmaps;
1718
                int bitmap_data_size;
1719
 
1720
                id = PHYSFSX_readInt(ifile);
1721
                version = PHYSFSX_readInt(ifile);
1722
 
1723
                if (id != MAKE_SIG('G','O','P','D') || version != 1) {
1724
                        return;
1725
                }
1726
 
1727
                n_bitmaps = PHYSFSX_readInt(ifile);
1728
 
1729
                RAIIdmem<uint16_t[]> indices;
1730
                MALLOC( indices, uint16_t[], n_bitmaps );
1731
 
1732
                range_for (auto &i, unchecked_partial_range(indices.get(), n_bitmaps))
1733
                        i = PHYSFSX_readShort(ifile);
1734
 
1735
                bitmap_data_size = PHYSFS_fileLength(ifile) - PHYSFS_tell(ifile) - sizeof(DiskBitmapHeader) * n_bitmaps;
1736
                Bitmap_replacement_data = std::make_unique<ubyte[]>(bitmap_data_size);
1737
 
1738
                range_for (const auto i, unchecked_partial_range(indices.get(), n_bitmaps))
1739
                {
1740
                        DiskBitmapHeader bmh;
1741
                        grs_bitmap *bm = &GameBitmaps[i];
1742
                        int width;
1743
 
1744
                        DiskBitmapHeader_read(&bmh, ifile);
1745
 
1746
                        width = bmh.width + (static_cast<short>(bmh.wh_extra & 0x0f) << 8);
1747
                        gr_set_bitmap_data(*bm, NULL);  // free ogl texture
1748
                        gr_init_bitmap(*bm, bm_mode::linear, 0, 0, width, bmh.height + (static_cast<short>(bmh.wh_extra & 0xf0) << 4), width, NULL);
1749
#if !DXX_USE_OGL
1750
                        bm->avg_color = bmh.avg_color;
1751
#endif
1752
                        bm->bm_data = reinterpret_cast<uint8_t *>(static_cast<uintptr_t>(bmh.offset));
1753
 
1754
                        gr_set_bitmap_flags(*bm, bmh.flags & BM_FLAGS_TO_COPY);
1755
 
1756
                        GameBitmapOffset[i] = 0; // don't try to read bitmap from current pigfile
1757
                }
1758
 
1759
                PHYSFS_read(ifile,Bitmap_replacement_data,1,bitmap_data_size);
1760
 
1761
                range_for (const auto i, unchecked_partial_range(indices.get(), n_bitmaps))
1762
                {
1763
                        grs_bitmap *bm = &GameBitmaps[i];
1764
                        gr_set_bitmap_data(*bm, &Bitmap_replacement_data[reinterpret_cast<uintptr_t>(bm->bm_data)]);
1765
                }
1766
                last_palette_loaded_pig[0]= 0;  //force pig re-load
1767
                texmerge_flush();       //for re-merging with new textures
1768
        }
1769
}
1770
 
1771
/* calculate table to translate d1 bitmaps to current palette,
1772
 * return -1 on error
1773
 */
1774
static int get_d1_colormap(palette_array_t &d1_palette, std::array<color_palette_index, 256> &colormap)
1775
{
1776
        auto palette_file = PHYSFSX_openReadBuffered(D1_PALETTE);
1777
        if (!palette_file || PHYSFS_fileLength(palette_file) != 9472)
1778
                return -1;
1779
        PHYSFS_read( palette_file, &d1_palette[0], sizeof(d1_palette[0]), d1_palette.size() );
1780
        build_colormap_good(d1_palette, colormap);
1781
        // don't change transparencies:
1782
        colormap[254] = color_palette_index{254};
1783
        colormap[255] = TRANSPARENCY_COLOR;
1784
        return 0;
1785
}
1786
 
1787
#define JUST_IN_CASE 132 /* is enough for d1 pc registered */
1788
static void bitmap_read_d1( grs_bitmap *bitmap, /* read into this bitmap */
1789
                     PHYSFS_File *d1_Piggy_fp, /* read from this file */
1790
                     int bitmap_data_start, /* specific to file */
1791
                     DiskBitmapHeader *bmh, /* header info for bitmap */
1792
                     uint8_t **next_bitmap, /* where to write it (if 0, use malloc) */
1793
                                         palette_array_t &d1_palette, /* what palette the bitmap has */
1794
                                         std::array<color_palette_index, 256> &colormap) /* how to translate bitmap's colors */
1795
{
1796
        int zsize, pigsize = PHYSFS_fileLength(d1_Piggy_fp);
1797
        uint8_t *data;
1798
        int width;
1799
 
1800
        width = bmh->width + (static_cast<short>(bmh->wh_extra & 0x0f) << 8);
1801
        gr_set_bitmap_data(*bitmap, NULL);      // free ogl texture
1802
        gr_init_bitmap(*bitmap, bm_mode::linear, 0, 0, width, bmh->height + (static_cast<short>(bmh->wh_extra & 0xf0) << 4), width, NULL);
1803
#if !DXX_USE_OGL
1804
        bitmap->avg_color = bmh->avg_color;
1805
#endif
1806
        gr_set_bitmap_flags(*bitmap, bmh->flags & BM_FLAGS_TO_COPY);
1807
 
1808
        PHYSFSX_fseek(d1_Piggy_fp, bitmap_data_start + bmh->offset, SEEK_SET);
1809
        if (bmh->flags & BM_FLAG_RLE) {
1810
                zsize = PHYSFSX_readInt(d1_Piggy_fp);
1811
                PHYSFSX_fseek(d1_Piggy_fp, -4, SEEK_CUR);
1812
        } else
1813
                zsize = bitmap->bm_h * bitmap->bm_w;
1814
 
1815
        if (next_bitmap) {
1816
                data = *next_bitmap;
1817
                *next_bitmap += zsize;
1818
        } else {
1819
                MALLOC(data, ubyte, zsize + JUST_IN_CASE);
1820
        }
1821
        if (!data) return;
1822
 
1823
        PHYSFS_read(d1_Piggy_fp, data, 1, zsize);
1824
        gr_set_bitmap_data(*bitmap, data);
1825
        switch(pigsize) {
1826
        case D1_MAC_PIGSIZE:
1827
        case D1_MAC_SHARE_PIGSIZE:
1828
                if (bmh->flags & BM_FLAG_RLE)
1829
                        rle_swap_0_255(*bitmap);
1830
                else
1831
                        swap_0_255(*bitmap);
1832
        }
1833
        if (bmh->flags & BM_FLAG_RLE)
1834
                rle_remap(*bitmap, colormap);
1835
        else
1836
                gr_remap_bitmap_good(*bitmap, d1_palette, TRANSPARENCY_COLOR, -1);
1837
        if (bmh->flags & BM_FLAG_RLE) { // size of bitmap could have changed!
1838
                int new_size;
1839
                memcpy(&new_size, bitmap->bm_data, 4);
1840
                if (next_bitmap) {
1841
                        *next_bitmap += new_size - zsize;
1842
                } else {
1843
                        Assert( zsize + JUST_IN_CASE >= new_size );
1844
                        bitmap->bm_mdata = reinterpret_cast<uint8_t *>(d_realloc(bitmap->bm_mdata, new_size));
1845
                        Assert(bitmap->bm_data);
1846
                }
1847
        }
1848
}
1849
 
1850
#define D1_MAX_TEXTURES 800
1851
#define D1_MAX_TMAP_NUM 1630 // 1621 in descent.pig Mac registered
1852
 
1853
/* the inverse of the d2 Textures array, but for the descent 1 pigfile.
1854
 * "Textures" looks up a d2 bitmap index given a d2 tmap_num.
1855
 * "d1_tmap_nums" looks up a d1 tmap_num given a d1 bitmap. "-1" means "None".
1856
 */
1857
using d1_tmap_nums_t = std::array<short, D1_MAX_TMAP_NUM>;
1858
static std::unique_ptr<d1_tmap_nums_t> d1_tmap_nums;
1859
 
1860
static void free_d1_tmap_nums() {
1861
        d1_tmap_nums.reset();
1862
}
1863
 
1864
static void bm_read_d1_tmap_nums(PHYSFS_File *d1pig)
1865
{
1866
        int i, d1_index;
1867
 
1868
        PHYSFSX_fseek(d1pig, 8, SEEK_SET);
1869
        d1_tmap_nums = std::make_unique<d1_tmap_nums_t>();
1870
        d1_tmap_nums->fill(-1);
1871
        for (i = 0; i < D1_MAX_TEXTURES; i++) {
1872
                d1_index = PHYSFSX_readShort(d1pig);
1873
                Assert(d1_index >= 0 && d1_index < D1_MAX_TMAP_NUM);
1874
                (*d1_tmap_nums)[d1_index] = i;
1875
                if (PHYSFS_eof(d1pig))
1876
                        break;
1877
        }
1878
}
1879
 
1880
// this function is at the same position in the d1 shareware piggy loading 
1881
// algorithm as bm_load_sub in main/bmread.c
1882
static int get_d1_bm_index(char *filename, PHYSFS_File *d1_pig) {
1883
        int i, N_bitmaps;
1884
        DiskBitmapHeader bmh;
1885
        if (strchr (filename, '.'))
1886
                *strchr (filename, '.') = '\0'; // remove extension
1887
        PHYSFSX_fseek (d1_pig, 0, SEEK_SET);
1888
        N_bitmaps = PHYSFSX_readInt (d1_pig);
1889
        PHYSFSX_fseek (d1_pig, 8, SEEK_SET);
1890
        for (i = 1; i <= N_bitmaps; i++) {
1891
                DiskBitmapHeader_d1_read(&bmh, d1_pig);
1892
                if (!d_strnicmp(bmh.name, filename, 8))
1893
                        return i;
1894
        }
1895
        return -1;
1896
}
1897
 
1898
// imitate the algorithm of gamedata_read_tbl in main/bmread.c
1899
static void read_d1_tmap_nums_from_hog(PHYSFS_File *d1_pig)
1900
{
1901
#define LINEBUF_SIZE 600
1902
        int reading_textures = 0;
1903
        short texture_count = 0;
1904
        int bitmaps_tbl_is_binary = 0;
1905
        int i;
1906
 
1907
        auto bitmaps = PHYSFSX_openReadBuffered("bitmaps.tbl");
1908
        if (!bitmaps) {
1909
                bitmaps = PHYSFSX_openReadBuffered ("bitmaps.bin");
1910
                bitmaps_tbl_is_binary = 1;
1911
        }
1912
 
1913
        if (!bitmaps) {
1914
                Warning ("Could not find bitmaps.* for reading d1 textures");
1915
                return;
1916
        }
1917
 
1918
        d1_tmap_nums = std::make_unique<d1_tmap_nums_t>();
1919
        d1_tmap_nums->fill(-1);
1920
 
1921
        for (PHYSFSX_gets_line_t<LINEBUF_SIZE> inputline; PHYSFSX_fgets (inputline, bitmaps);)
1922
        {
1923
                char *arg;
1924
 
1925
                if (bitmaps_tbl_is_binary)
1926
                        decode_text_line((inputline));
1927
                else
1928
                        while (inputline[(i=strlen(inputline))-2]=='\\')
1929
                                PHYSFSX_fgets(inputline,bitmaps,i-2); // strip comments
1930
                REMOVE_EOL(inputline);
1931
                if (strchr(inputline, ';')!=NULL) REMOVE_COMMENTS(inputline);
1932
                if (strlen(inputline) == LINEBUF_SIZE-1) {
1933
                        Warning("Possible line truncation in BITMAPS.TBL");
1934
                        return;
1935
                }
1936
                arg = strtok( inputline, space_tab );
1937
                if (arg && arg[0] == '@') {
1938
                        arg++;
1939
                        //Registered_only = 1;
1940
                }
1941
 
1942
                while (arg != NULL) {
1943
                        if (*arg == '$')
1944
                                reading_textures = 0; // default
1945
                        if (!strcmp(arg, "$TEXTURES")) // BM_TEXTURES
1946
                                reading_textures = 1;
1947
                        else if (! d_stricmp(arg, "$ECLIP") // BM_ECLIP
1948
                                   || ! d_stricmp(arg, "$WCLIP")) // BM_WCLIP
1949
                                        texture_count++;
1950
                        else // not a special token, must be a bitmap!
1951
                                if (reading_textures) {
1952
                                        while (*arg == '\t' || *arg == ' ')
1953
                                                arg++;//remove unwanted blanks
1954
                                        if (*arg == '\0')
1955
                                                break;
1956
                                        if (d1_tmap_num_unique(texture_count)) {
1957
                                                int d1_index = get_d1_bm_index(arg, d1_pig);
1958
                                                if (d1_index >= 0 && d1_index < D1_MAX_TMAP_NUM) {
1959
                                                        (*d1_tmap_nums)[d1_index] = texture_count;
1960
                                                        //int d2_index = d2_index_for_d1_index(d1_index);
1961
                                                }
1962
                                }
1963
                                Assert (texture_count < D1_MAX_TEXTURES);
1964
                                texture_count++;
1965
                        }
1966
 
1967
                        arg = strtok (NULL, equal_space);
1968
                }
1969
        }
1970
}
1971
 
1972
/* If the given d1_index is the index of a bitmap we have to load
1973
 * (because it is unique to descent 1), then returns the d2_index that
1974
 * the given d1_index replaces.
1975
 * Returns -1 if the given d1_index is not unique to descent 1.
1976
 */
1977
static short d2_index_for_d1_index(short d1_index)
1978
{
1979
        Assert(d1_index >= 0 && d1_index < D1_MAX_TMAP_NUM);
1980
        if (! d1_tmap_nums || (*d1_tmap_nums)[d1_index] == -1
1981
            || ! d1_tmap_num_unique((*d1_tmap_nums)[d1_index]))
1982
                return -1;
1983
 
1984
        return Textures[convert_d1_tmap_num((*d1_tmap_nums)[d1_index])].index;
1985
}
1986
 
1987
#define D1_BITMAPS_SIZE 300000
1988
void load_d1_bitmap_replacements()
1989
{
1990
        DiskBitmapHeader bmh;
1991
        int pig_data_start, bitmap_header_start, bitmap_data_start;
1992
        int N_bitmaps;
1993
        short d1_index, d2_index;
1994
        uint8_t * next_bitmap;
1995
        palette_array_t d1_palette;
1996
        char *p;
1997
        int pigsize;
1998
 
1999
        auto d1_Piggy_fp = PHYSFSX_openReadBuffered(D1_PIGFILE);
2000
 
2001
#define D1_PIG_LOAD_FAILED "Failed loading " D1_PIGFILE
2002
        if (!d1_Piggy_fp) {
2003
                Warning(D1_PIG_LOAD_FAILED);
2004
                return;
2005
        }
2006
 
2007
        //first, free up data allocated for old bitmaps
2008
        free_bitmap_replacements();
2009
 
2010
        std::array<color_palette_index, 256> colormap;
2011
        if (get_d1_colormap( d1_palette, colormap ) != 0)
2012
                Warning("Could not load descent 1 color palette");
2013
 
2014
        pigsize = PHYSFS_fileLength(d1_Piggy_fp);
2015
        switch (pigsize) {
2016
        case D1_SHARE_BIG_PIGSIZE:
2017
        case D1_SHARE_10_PIGSIZE:
2018
        case D1_SHARE_PIGSIZE:
2019
        case D1_10_BIG_PIGSIZE:
2020
        case D1_10_PIGSIZE:
2021
                pig_data_start = 0;
2022
                // OK, now we need to read d1_tmap_nums by emulating d1's gamedata_read_tbl()
2023
                read_d1_tmap_nums_from_hog(d1_Piggy_fp);
2024
                break;
2025
        default:
2026
                Warning("Unknown size for " D1_PIGFILE);
2027
                Int3();
2028
                DXX_BOOST_FALLTHROUGH;
2029
        case D1_PIGSIZE:
2030
        case D1_OEM_PIGSIZE:
2031
        case D1_MAC_PIGSIZE:
2032
        case D1_MAC_SHARE_PIGSIZE:
2033
                pig_data_start = PHYSFSX_readInt(d1_Piggy_fp );
2034
                bm_read_d1_tmap_nums(d1_Piggy_fp); //was: bm_read_all_d1(fp);
2035
                //for (i = 0; i < 1800; i++) GameBitmapXlat[i] = PHYSFSX_readShort(d1_Piggy_fp);
2036
                break;
2037
        }
2038
 
2039
        PHYSFSX_fseek( d1_Piggy_fp, pig_data_start, SEEK_SET );
2040
        N_bitmaps = PHYSFSX_readInt(d1_Piggy_fp);
2041
        {
2042
                int N_sounds = PHYSFSX_readInt(d1_Piggy_fp);
2043
                int header_size = N_bitmaps * DISKBITMAPHEADER_D1_SIZE
2044
                        + N_sounds * sizeof(DiskSoundHeader);
2045
                bitmap_header_start = pig_data_start + 2 * sizeof(int);
2046
                bitmap_data_start = bitmap_header_start + header_size;
2047
        }
2048
 
2049
        Bitmap_replacement_data = std::make_unique<ubyte[]>(D1_BITMAPS_SIZE);
2050
        if (!Bitmap_replacement_data) {
2051
                Warning(D1_PIG_LOAD_FAILED);
2052
                return;
2053
        }
2054
 
2055
        next_bitmap = Bitmap_replacement_data.get();
2056
 
2057
        for (d1_index = 1; d1_index <= N_bitmaps; d1_index++ ) {
2058
                d2_index = d2_index_for_d1_index(d1_index);
2059
                // only change bitmaps which are unique to d1
2060
                if (d2_index != -1) {
2061
                        PHYSFSX_fseek(d1_Piggy_fp, bitmap_header_start + (d1_index-1) * DISKBITMAPHEADER_D1_SIZE, SEEK_SET);
2062
                        DiskBitmapHeader_d1_read(&bmh, d1_Piggy_fp);
2063
 
2064
                        bitmap_read_d1( &GameBitmaps[d2_index], d1_Piggy_fp, bitmap_data_start, &bmh, &next_bitmap, d1_palette, colormap );
2065
                        Assert(next_bitmap - Bitmap_replacement_data.get() < D1_BITMAPS_SIZE);
2066
                        GameBitmapOffset[d2_index] = 0; // don't try to read bitmap from current d2 pigfile
2067
                        GameBitmapFlags[d2_index] = bmh.flags;
2068
 
2069
                        auto &abname = AllBitmaps[d2_index].name;
2070
                        if ((p = strchr(abname.data(), '#')) /* d2 BM is animated */
2071
                             && !(bmh.dflags & DBM_FLAG_ABM) ) { /* d1 bitmap is not animated */
2072
                                int i, len = p - abname.data();
2073
                                for (i = 0; i < Num_bitmap_files; i++)
2074
                                        if (i != d2_index && ! memcmp(abname.data(), AllBitmaps[i].name.data(), len))
2075
                                        {
2076
                                                gr_set_bitmap_data(GameBitmaps[i], NULL);       // free ogl texture
2077
                                                GameBitmaps[i] = GameBitmaps[d2_index];
2078
                                                GameBitmapOffset[i] = 0;
2079
                                                GameBitmapFlags[i] = bmh.flags;
2080
                                        }
2081
                        }
2082
                }
2083
        }
2084
        last_palette_loaded_pig[0]= 0;  //force pig re-load
2085
 
2086
        texmerge_flush();       //for re-merging with new textures
2087
}
2088
 
2089
 
2090
/*
2091
 * Find and load the named bitmap from descent.pig
2092
 * similar to read_extra_bitmap_iff
2093
 */
2094
bitmap_index read_extra_bitmap_d1_pig(const char *name)
2095
{
2096
        bitmap_index bitmap_num;
2097
        grs_bitmap * n = &GameBitmaps[extra_bitmap_num];
2098
 
2099
        bitmap_num.index = 0;
2100
 
2101
        {
2102
                int pig_data_start, bitmap_header_start, bitmap_data_start;
2103
                int N_bitmaps;
2104
                palette_array_t d1_palette;
2105
                int pigsize;
2106
                auto d1_Piggy_fp = PHYSFSX_openReadBuffered(D1_PIGFILE);
2107
                if (!d1_Piggy_fp)
2108
                {
2109
                        Warning(D1_PIG_LOAD_FAILED);
2110
                        return bitmap_num;
2111
                }
2112
 
2113
                std::array<color_palette_index, 256> colormap;
2114
                if (get_d1_colormap( d1_palette, colormap ) != 0)
2115
                        Warning("Could not load descent 1 color palette");
2116
 
2117
                pigsize = PHYSFS_fileLength(d1_Piggy_fp);
2118
                switch (pigsize) {
2119
                case D1_SHARE_BIG_PIGSIZE:
2120
                case D1_SHARE_10_PIGSIZE:
2121
                case D1_SHARE_PIGSIZE:
2122
                case D1_10_BIG_PIGSIZE:
2123
                case D1_10_PIGSIZE:
2124
                        pig_data_start = 0;
2125
                        break;
2126
                default:
2127
                        Warning("Unknown size for " D1_PIGFILE);
2128
                        Int3();
2129
                        DXX_BOOST_FALLTHROUGH;
2130
                case D1_PIGSIZE:
2131
                case D1_OEM_PIGSIZE:
2132
                case D1_MAC_PIGSIZE:
2133
                case D1_MAC_SHARE_PIGSIZE:
2134
                        pig_data_start = PHYSFSX_readInt(d1_Piggy_fp );
2135
 
2136
                        break;
2137
                }
2138
 
2139
                PHYSFSX_fseek( d1_Piggy_fp, pig_data_start, SEEK_SET );
2140
                N_bitmaps = PHYSFSX_readInt(d1_Piggy_fp);
2141
                {
2142
                        int N_sounds = PHYSFSX_readInt(d1_Piggy_fp);
2143
                        int header_size = N_bitmaps * DISKBITMAPHEADER_D1_SIZE
2144
                                + N_sounds * sizeof(DiskSoundHeader);
2145
                        bitmap_header_start = pig_data_start + 2 * sizeof(int);
2146
                        bitmap_data_start = bitmap_header_start + header_size;
2147
                }
2148
 
2149
                for (unsigned i = 1;; ++i)
2150
                {
2151
                        if (i > N_bitmaps)
2152
                        {
2153
                                con_printf(CON_DEBUG, "could not find bitmap %s", name);
2154
                                return bitmap_num;
2155
                        }
2156
                        DiskBitmapHeader bmh;
2157
                        DiskBitmapHeader_d1_read(&bmh, d1_Piggy_fp);
2158
                        if (!d_strnicmp(bmh.name, name, 8))
2159
                        {
2160
                                bitmap_read_d1(n, d1_Piggy_fp, bitmap_data_start, &bmh, 0, d1_palette, colormap);
2161
                                break;
2162
                        }
2163
                }
2164
        }
2165
 
2166
#if !DXX_USE_OGL
2167
        n->avg_color = 0;       //compute_average_pixel(n);
2168
#endif
2169
 
2170
        bitmap_num.index = extra_bitmap_num;
2171
 
2172
        GameBitmaps[extra_bitmap_num++] = *n;
2173
 
2174
        return bitmap_num;
2175
}
2176
#endif
2177
 
2178
/*
2179
 * reads a bitmap_index structure from a PHYSFS_File
2180
 */
2181
void bitmap_index_read(PHYSFS_File *fp, bitmap_index &bi)
2182
{
2183
        bi.index = PHYSFSX_readShort(fp);
2184
}
2185
 
2186
/*
2187
 * reads n bitmap_index structs from a PHYSFS_File
2188
 */
2189
void bitmap_index_read_n(PHYSFS_File *fp, const partial_range_t<bitmap_index *> r)
2190
{
2191
        range_for (auto &i, r)
2192
                i.index = PHYSFSX_readShort(fp);
2193
}