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
 * Bitmap and palette loading functions.
23
 *
24
 */
25
 
26
#include <algorithm>
27
#include <stdio.h>
28
#include <stdlib.h>
29
#include <string.h>
30
 
31
#include "pstypes.h"
32
#include "inferno.h"
33
#include "gr.h"
34
#include "bm.h"
35
#include "u_mem.h"
36
#include "dxxerror.h"
37
#include "object.h"
38
#include "vclip.h"
39
#include "effects.h"
40
#include "polyobj.h"
41
#include "wall.h"
42
#include "textures.h"
43
#include "game.h"
44
#include "multi.h"
45
#include "iff.h"
46
#include "powerup.h"
47
#include "sounds.h"
48
#include "piggy.h"
49
#include "aistruct.h"
50
#include "robot.h"
51
#include "weapon.h"
52
#include "gauges.h"
53
#include "player.h"
54
#include "endlevel.h"
55
#include "cntrlcen.h"
56
#include "makesig.h"
57
#include "interp.h"
58
#include "console.h"
59
#include "rle.h"
60
#include "physfsx.h"
61
#include "internal.h"
62
#include "strutil.h"
63
 
64
#if DXX_USE_EDITOR
65
#include "editor/texpage.h"
66
#endif
67
 
68
#include "compiler-range_for.h"
69
#include "d_range.h"
70
#include "partial_range.h"
71
#include <memory>
72
 
73
std::array<ubyte, MAX_SOUNDS> Sounds, AltSounds;
74
 
75
unsigned NumTextures;
76
 
77
#if DXX_USE_EDITOR
78
int Num_object_subtypes = 1;
79
#endif
80
 
81
namespace dsx {
82
#if defined(DXX_BUILD_DESCENT_I)
83
int Num_total_object_types;
84
 
85
sbyte   ObjType[MAX_OBJTYPE];
86
sbyte   ObjId[MAX_OBJTYPE];
87
fix     ObjStrength[MAX_OBJTYPE];
88
#elif defined(DXX_BUILD_DESCENT_II)
89
//the polygon model number to use for the marker
90
int     Marker_model_num = -1;
91
unsigned N_ObjBitmaps;
92
static void bm_free_extra_objbitmaps();
93
#endif
94
 
95
Textures_array Textures;                // All textures.
96
//for each model, a model number for dying & dead variants, or -1 if none
97
std::array<int, MAX_POLYGON_MODELS> Dying_modelnums, Dead_modelnums;
98
std::array<bitmap_index, N_COCKPIT_BITMAPS> cockpit_bitmap;
99
}
100
 
101
//right now there's only one player ship, but we can have another by
102
//adding an array and setting the pointer to the active ship.
103
namespace dcx {
104
player_ship only_player_ship;
105
 
106
//----------------- Miscellaneous bitmap pointers ---------------
107
unsigned Num_cockpits;
108
}
109
 
110
//---------------- Variables for wall textures ------------------
111
 
112
//---------------- Variables for object textures ----------------
113
 
114
int             First_multi_bitmap_num=-1;
115
 
116
std::array<bitmap_index, MAX_OBJ_BITMAPS> ObjBitmaps;
117
std::array<ushort, MAX_OBJ_BITMAPS>          ObjBitmapPtrs;     // These point back into ObjBitmaps, since some are used twice.
118
 
119
namespace dsx {
120
void gamedata_close()
121
{
122
        free_polygon_models();
123
#if defined(DXX_BUILD_DESCENT_II)
124
        bm_free_extra_objbitmaps();
125
#endif
126
        free_endlevel_data();
127
        rle_cache_close();
128
        piggy_close();
129
}
130
}
131
 
132
/*
133
 * reads n tmap_info structs from a PHYSFS_File
134
 */
135
#if defined(DXX_BUILD_DESCENT_I)
136
static void tmap_info_read(tmap_info &ti, PHYSFS_File *fp)
137
{
138
        PHYSFS_read(fp, ti.filename, 13, 1);
139
        ti.flags = PHYSFSX_readByte(fp);
140
        ti.lighting = PHYSFSX_readFix(fp);
141
        ti.damage = PHYSFSX_readFix(fp);
142
        ti.eclip_num = PHYSFSX_readInt(fp);
143
}
144
 
145
//-----------------------------------------------------------------
146
// Initializes game properties data (including texture caching system) and sound data.
147
int gamedata_init()
148
{
149
        int retval;
150
 
151
        init_polygon_models();
152
        retval = properties_init();                             // This calls properties_read_cmp if appropriate
153
        if (retval)
154
                gamedata_read_tbl(Vclip, retval == PIGGY_PC_SHAREWARE);
155
 
156
        piggy_read_sounds(retval == PIGGY_PC_SHAREWARE);
157
 
158
        return 0;
159
}
160
 
161
namespace dsx {
162
 
163
// Read compiled properties data from descent.pig
164
// (currently only ever called if D1)
165
void properties_read_cmp(d_vclip_array &Vclip, PHYSFS_File * fp)
166
{
167
        auto &Effects = LevelUniqueEffectsClipState.Effects;
168
        auto &Robot_joints = LevelSharedRobotJointState.Robot_joints;
169
        auto &TmapInfo = LevelUniqueTmapInfoState.TmapInfo;
170
        auto &WallAnims = GameSharedState.WallAnims;
171
        //  bitmap_index is a short
172
 
173
        NumTextures = PHYSFSX_readInt(fp);
174
        bitmap_index_read_n(fp, Textures);
175
        range_for (tmap_info &ti, TmapInfo)
176
                tmap_info_read(ti, fp);
177
 
178
        PHYSFS_read(fp, Sounds, sizeof(Sounds[0]), Sounds.size());
179
        PHYSFS_read(fp, AltSounds, sizeof(AltSounds[0]), AltSounds.size());
180
 
181
        Num_vclips = PHYSFSX_readInt(fp);
182
        range_for (vclip &vc, Vclip)
183
                vclip_read(fp, vc);
184
 
185
        Num_effects = PHYSFSX_readInt(fp);
186
        range_for (eclip &ec, Effects)
187
                eclip_read(fp, ec);
188
 
189
        Num_wall_anims = PHYSFSX_readInt(fp);
190
        range_for (auto &w, WallAnims)
191
                wclip_read(fp, w);
192
 
193
        LevelSharedRobotInfoState.N_robot_types = PHYSFSX_readInt(fp);
194
        auto &Robot_info = LevelSharedRobotInfoState.Robot_info;
195
        range_for (auto &r, Robot_info)
196
                robot_info_read(fp, r);
197
 
198
        N_robot_joints = PHYSFSX_readInt(fp);
199
        range_for (auto &r, Robot_joints)
200
                jointpos_read(fp, r);
201
 
202
        N_weapon_types = PHYSFSX_readInt(fp);
203
        weapon_info_read_n(Weapon_info, MAX_WEAPON_TYPES, fp, 0);
204
 
205
        N_powerup_types = PHYSFSX_readInt(fp);
206
        range_for (auto &p, Powerup_info)
207
                powerup_type_info_read(fp, p);
208
 
209
        auto &Polygon_models = LevelSharedPolygonModelState.Polygon_models;
210
        N_polygon_models = PHYSFSX_readInt(fp);
211
        {
212
                const auto &&r = partial_range(Polygon_models, N_polygon_models);
213
        range_for (auto &p, r)
214
                polymodel_read(&p, fp);
215
 
216
        range_for (auto &p, r)
217
                polygon_model_data_read(&p, fp);
218
        }
219
 
220
        bitmap_index_read_n(fp, partial_range(Gauges, MAX_GAUGE_BMS));
221
 
222
        range_for (auto &i, Dying_modelnums)
223
                i = PHYSFSX_readInt(fp);
224
        range_for (auto &i, Dead_modelnums)
225
                i = PHYSFSX_readInt(fp);
226
 
227
        bitmap_index_read_n(fp, ObjBitmaps);
228
        range_for (auto &i, ObjBitmapPtrs)
229
                i = PHYSFSX_readShort(fp);
230
 
231
        player_ship_read(&only_player_ship, fp);
232
 
233
        Num_cockpits = PHYSFSX_readInt(fp);
234
        bitmap_index_read_n(fp, cockpit_bitmap);
235
 
236
        PHYSFS_read(fp, Sounds, sizeof(Sounds[0]), Sounds.size());
237
        PHYSFS_read(fp, AltSounds, sizeof(AltSounds[0]), AltSounds.size());
238
 
239
        Num_total_object_types = PHYSFSX_readInt(fp);
240
        PHYSFS_read( fp, ObjType, sizeof(ubyte), MAX_OBJTYPE );
241
        PHYSFS_read( fp, ObjId, sizeof(ubyte), MAX_OBJTYPE );
242
        range_for (auto &i, ObjStrength)
243
                i = PHYSFSX_readFix(fp);
244
 
245
        First_multi_bitmap_num = PHYSFSX_readInt(fp);
246
        Reactors[0].n_guns = PHYSFSX_readInt(fp);
247
 
248
        range_for (auto &i, Reactors[0].gun_points)
249
                PHYSFSX_readVector(fp, i);
250
        range_for (auto &i, Reactors[0].gun_dirs)
251
                PHYSFSX_readVector(fp, i);
252
 
253
        exit_modelnum = PHYSFSX_readInt(fp);   
254
        destroyed_exit_modelnum = PHYSFSX_readInt(fp);
255
 
256
#if DXX_USE_EDITOR
257
        //Build tmaplist
258
        auto &&effect_range = partial_const_range(Effects, Num_effects);
259
        LevelUniqueTmapInfoState.Num_tmaps = TextureEffects + std::count_if(effect_range.begin(), effect_range.end(), [](const eclip &e) { return e.changing_wall_texture >= 0; });
260
        #endif
261
}
262
 
263
}
264
#elif defined(DXX_BUILD_DESCENT_II)
265
static void tmap_info_read(tmap_info &ti, PHYSFS_File *fp)
266
{
267
        ti.flags = PHYSFSX_readByte(fp);
268
        PHYSFSX_readByte(fp);
269
        PHYSFSX_readByte(fp);
270
        PHYSFSX_readByte(fp);
271
        ti.lighting = PHYSFSX_readFix(fp);
272
        ti.damage = PHYSFSX_readFix(fp);
273
        ti.eclip_num = PHYSFSX_readShort(fp);
274
        ti.destroyed = PHYSFSX_readShort(fp);
275
        ti.slide_u = PHYSFSX_readShort(fp);
276
        ti.slide_v = PHYSFSX_readShort(fp);
277
}
278
 
279
//-----------------------------------------------------------------
280
// Initializes game properties data (including texture caching system) and sound data.
281
int gamedata_init()
282
{
283
        init_polygon_models();
284
 
285
#if DXX_USE_EDITOR
286
        // The pc_shareware argument is currently unused for Descent 2,
287
        // but *may* be useful for loading Descent 1 Shareware texture properties.
288
        if (!gamedata_read_tbl(Vclip, 0))
289
#endif
290
                if (!properties_init())                         // This calls properties_read_cmp
291
                                Error("Cannot open ham file\n");
292
 
293
        piggy_read_sounds();
294
 
295
        return 0;
296
}
297
 
298
namespace dsx {
299
 
300
void bm_read_all(d_vclip_array &Vclip, PHYSFS_File * fp)
301
{
302
        auto &Effects = LevelUniqueEffectsClipState.Effects;
303
        auto &Robot_joints = LevelSharedRobotJointState.Robot_joints;
304
        auto &TmapInfo = LevelUniqueTmapInfoState.TmapInfo;
305
        auto &WallAnims = GameSharedState.WallAnims;
306
        unsigned t;
307
 
308
        NumTextures = PHYSFSX_readInt(fp);
309
        bitmap_index_read_n(fp, partial_range(Textures, NumTextures));
310
        range_for (tmap_info &ti, partial_range(TmapInfo, NumTextures))
311
                tmap_info_read(ti, fp);
312
 
313
        t = PHYSFSX_readInt(fp);
314
        PHYSFS_read( fp, Sounds, sizeof(ubyte), t );
315
        PHYSFS_read( fp, AltSounds, sizeof(ubyte), t );
316
 
317
        Num_vclips = PHYSFSX_readInt(fp);
318
        range_for (vclip &vc, partial_range(Vclip, Num_vclips))
319
                vclip_read(fp, vc);
320
 
321
        Num_effects = PHYSFSX_readInt(fp);
322
        range_for (eclip &ec, partial_range(Effects, Num_effects))
323
                eclip_read(fp, ec);
324
 
325
        Num_wall_anims = PHYSFSX_readInt(fp);
326
        range_for (auto &w, partial_range(WallAnims, Num_wall_anims))
327
                wclip_read(fp, w);
328
 
329
        auto &Robot_info = LevelSharedRobotInfoState.Robot_info;
330
        LevelSharedRobotInfoState.N_robot_types = PHYSFSX_readInt(fp);
331
        range_for (auto &r, partial_range(Robot_info, LevelSharedRobotInfoState.N_robot_types))
332
                robot_info_read(fp, r);
333
 
334
        N_robot_joints = PHYSFSX_readInt(fp);
335
        range_for (auto &r, partial_range(Robot_joints, N_robot_joints))
336
                jointpos_read(fp, r);
337
 
338
        N_weapon_types = PHYSFSX_readInt(fp);
339
        weapon_info_read_n(Weapon_info, N_weapon_types, fp, Piggy_hamfile_version);
340
 
341
        N_powerup_types = PHYSFSX_readInt(fp);
342
        range_for (auto &p, partial_range(Powerup_info, N_powerup_types))
343
                powerup_type_info_read(fp, p);
344
 
345
        auto &Polygon_models = LevelSharedPolygonModelState.Polygon_models;
346
        N_polygon_models = PHYSFSX_readInt(fp);
347
        {
348
                const auto &&r = partial_range(Polygon_models, N_polygon_models);
349
        range_for (auto &p, r)
350
                polymodel_read(&p, fp);
351
 
352
        range_for (auto &p, r)
353
                polygon_model_data_read(&p, fp);
354
        }
355
 
356
        range_for (auto &i, partial_range(Dying_modelnums, N_polygon_models))
357
                i = PHYSFSX_readInt(fp);
358
        range_for (auto &i, partial_range(Dead_modelnums, N_polygon_models))
359
                i = PHYSFSX_readInt(fp);
360
 
361
        t = PHYSFSX_readInt(fp);
362
        bitmap_index_read_n(fp, partial_range(Gauges, t));
363
        bitmap_index_read_n(fp, partial_range(Gauges_hires, t));
364
 
365
        N_ObjBitmaps = PHYSFSX_readInt(fp);
366
        bitmap_index_read_n(fp, partial_range(ObjBitmaps, N_ObjBitmaps));
367
        range_for (auto &i, partial_range(ObjBitmapPtrs, N_ObjBitmaps))
368
                i = PHYSFSX_readShort(fp);
369
 
370
        player_ship_read(&only_player_ship, fp);
371
 
372
        Num_cockpits = PHYSFSX_readInt(fp);
373
        bitmap_index_read_n(fp, partial_range(cockpit_bitmap, Num_cockpits));
374
 
375
//@@    PHYSFS_read( fp, &Num_total_object_types, sizeof(int), 1 );
376
//@@    PHYSFS_read( fp, ObjType, sizeof(byte), Num_total_object_types );
377
//@@    PHYSFS_read( fp, ObjId, sizeof(byte), Num_total_object_types );
378
//@@    PHYSFS_read( fp, ObjStrength, sizeof(fix), Num_total_object_types );
379
 
380
        First_multi_bitmap_num = PHYSFSX_readInt(fp);
381
 
382
        Num_reactors = PHYSFSX_readInt(fp);
383
        reactor_read_n(fp, partial_range(Reactors, Num_reactors));
384
 
385
        Marker_model_num = PHYSFSX_readInt(fp);
386
 
387
        //@@PHYSFS_read( fp, &N_controlcen_guns, sizeof(int), 1 );
388
        //@@PHYSFS_read( fp, controlcen_gun_points, sizeof(vms_vector), N_controlcen_guns );
389
        //@@PHYSFS_read( fp, controlcen_gun_dirs, sizeof(vms_vector), N_controlcen_guns );
390
 
391
        if (Piggy_hamfile_version < 3) { // D1
392
                exit_modelnum = PHYSFSX_readInt(fp);
393
                destroyed_exit_modelnum = PHYSFSX_readInt(fp);
394
        }
395
        else    // D2: to be loaded later
396
                exit_modelnum = destroyed_exit_modelnum = N_polygon_models;
397
}
398
 
399
int extra_bitmap_num = 0;
400
bool Exit_models_loaded; // this and below only really used for D2
401
bool Exit_bitmaps_loaded;
402
unsigned Exit_bitmap_index;
403
 
404
static void bm_free_extra_objbitmaps()
405
{
406
        int i;
407
 
408
        if (!extra_bitmap_num)
409
                extra_bitmap_num = Num_bitmap_files;
410
 
411
        for (i = Num_bitmap_files; i < extra_bitmap_num; i++)
412
        {
413
                N_ObjBitmaps--;
414
                d_free(GameBitmaps[i].bm_mdata);
415
        }
416
        extra_bitmap_num = Num_bitmap_files;
417
        Exit_bitmaps_loaded = false;
418
}
419
 
420
static void bm_free_extra_models()
421
{
422
        Exit_models_loaded = false;
423
        const auto base = std::min(N_D2_POLYGON_MODELS.value, exit_modelnum);
424
        auto &Polygon_models = LevelSharedPolygonModelState.Polygon_models;
425
        range_for (auto &p, partial_range(Polygon_models, base, std::exchange(N_polygon_models, base)))
426
                free_model(p);
427
}
428
 
429
//type==1 means 1.1, type==2 means 1.2 (with weapons)
430
void bm_read_extra_robots(const char *fname, Mission::descent_version_type type)
431
{
432
        auto &Robot_joints = LevelSharedRobotJointState.Robot_joints;
433
        int t,version;
434
 
435
        auto fp = PHYSFSX_openReadBuffered(fname);
436
        if (!fp)
437
        {
438
                Error("Failed to open HAM file \"%s\"", fname);
439
                return;
440
        }
441
 
442
        if (type == Mission::descent_version_type::descent2z)
443
        {
444
                int sig;
445
 
446
                sig = PHYSFSX_readInt(fp);
447
                if (sig != MAKE_SIG('X','H','A','M'))
448
                        return;
449
                version = PHYSFSX_readInt(fp);
450
        }
451
        else
452
                version = 0;
453
        (void)version; // NOTE: we do not need it, but keep it for possible further use
454
 
455
        bm_free_extra_models();
456
        bm_free_extra_objbitmaps();
457
 
458
        //read extra weapons
459
 
460
        t = PHYSFSX_readInt(fp);
461
        N_weapon_types = N_D2_WEAPON_TYPES+t;
462
        weapon_info_read_n(Weapon_info, N_weapon_types, fp, 3, N_D2_WEAPON_TYPES);
463
 
464
        //now read robot info
465
 
466
        t = PHYSFSX_readInt(fp);
467
        const auto N_robot_types = LevelSharedRobotInfoState.N_robot_types = N_D2_ROBOT_TYPES + t;
468
        if (N_robot_types >= MAX_ROBOT_TYPES)
469
                Error("Too many robots (%d) in <%s>.  Max is %d.",t,fname,MAX_ROBOT_TYPES-N_D2_ROBOT_TYPES);
470
        auto &Robot_info = LevelSharedRobotInfoState.Robot_info;
471
        range_for (auto &r, partial_range(Robot_info, N_D2_ROBOT_TYPES.value, N_robot_types))
472
                robot_info_read(fp, r);
473
 
474
        t = PHYSFSX_readInt(fp);
475
        N_robot_joints = N_D2_ROBOT_JOINTS+t;
476
        if (N_robot_joints >= MAX_ROBOT_JOINTS)
477
                Error("Too many robot joints (%d) in <%s>.  Max is %d.",t,fname,MAX_ROBOT_JOINTS-N_D2_ROBOT_JOINTS);
478
        range_for (auto &r, partial_range(Robot_joints, N_D2_ROBOT_JOINTS.value, N_robot_joints))
479
                jointpos_read(fp, r);
480
 
481
        unsigned u = PHYSFSX_readInt(fp);
482
        auto &Polygon_models = LevelSharedPolygonModelState.Polygon_models;
483
        N_polygon_models = N_D2_POLYGON_MODELS+u;
484
        if (N_polygon_models >= MAX_POLYGON_MODELS)
485
                Error("Too many polygon models (%d) in <%s>.  Max is %d.",u,fname,MAX_POLYGON_MODELS-N_D2_POLYGON_MODELS);
486
        {
487
                const auto &&r = partial_range(Polygon_models, N_D2_POLYGON_MODELS.value, N_polygon_models);
488
                range_for (auto &p, r)
489
                polymodel_read(&p, fp);
490
 
491
                range_for (auto &p, r)
492
                        polygon_model_data_read(&p, fp);
493
        }
494
 
495
        range_for (auto &i, partial_range(Dying_modelnums, N_D2_POLYGON_MODELS.value, N_polygon_models))
496
                i = PHYSFSX_readInt(fp);
497
        range_for (auto &i, partial_range(Dead_modelnums, N_D2_POLYGON_MODELS.value, N_polygon_models))
498
                i = PHYSFSX_readInt(fp);
499
 
500
        t = PHYSFSX_readInt(fp);
501
        if (N_D2_OBJBITMAPS+t >= ObjBitmaps.size())
502
                Error("Too many object bitmaps (%d) in <%s>.  Max is %" DXX_PRI_size_type ".", t, fname, ObjBitmaps.size() - N_D2_OBJBITMAPS);
503
        bitmap_index_read_n(fp, partial_range(ObjBitmaps, N_D2_OBJBITMAPS.value, N_D2_OBJBITMAPS + t));
504
 
505
        t = PHYSFSX_readInt(fp);
506
        if (N_D2_OBJBITMAPPTRS+t >= ObjBitmapPtrs.size())
507
                Error("Too many object bitmap pointers (%d) in <%s>.  Max is %" DXX_PRI_size_type ".", t, fname, ObjBitmapPtrs.size() - N_D2_OBJBITMAPPTRS);
508
        range_for (auto &i, partial_range(ObjBitmapPtrs, N_D2_OBJBITMAPPTRS.value, N_D2_OBJBITMAPPTRS + t))
509
                i = PHYSFSX_readShort(fp);
510
}
511
 
512
int Robot_replacements_loaded = 0;
513
 
514
void load_robot_replacements(const d_fname &level_name)
515
{
516
        auto &Robot_joints = LevelSharedRobotJointState.Robot_joints;
517
        int t,i,j;
518
        char ifile_name[FILENAME_LEN];
519
 
520
        change_filename_extension(ifile_name, level_name, ".HXM" );
521
 
522
        auto fp = PHYSFSX_openReadBuffered(ifile_name);
523
        if (!fp)                //no robot replacement file
524
                return;
525
 
526
        t = PHYSFSX_readInt(fp);                        //read id "HXM!"
527
        if (t!= 0x21584d48)
528
                Error("ID of HXM! file incorrect");
529
 
530
        t = PHYSFSX_readInt(fp);                        //read version
531
        if (t<1)
532
                Error("HXM! version too old (%d)",t);
533
 
534
        t = PHYSFSX_readInt(fp);                        //read number of robots
535
        auto &Robot_info = LevelSharedRobotInfoState.Robot_info;
536
        const auto N_robot_types = LevelSharedRobotInfoState.N_robot_types;
537
        for (j=0;j<t;j++) {
538
                i = PHYSFSX_readInt(fp);                //read robot number
539
                if (i<0 || i>=N_robot_types)
540
                        Error("Robots number (%d) out of range in (%s).  Range = [0..%d].",i,static_cast<const char *>(level_name),N_robot_types-1);
541
                robot_info_read(fp, Robot_info[i]);
542
        }
543
 
544
        t = PHYSFSX_readInt(fp);                        //read number of joints
545
        for (j=0;j<t;j++) {
546
                i = PHYSFSX_readInt(fp);                //read joint number
547
                if (i<0 || i>=N_robot_joints)
548
                        Error("Robots joint (%d) out of range in (%s).  Range = [0..%d].",i,static_cast<const char *>(level_name),N_robot_joints-1);
549
                jointpos_read(fp, Robot_joints[i]);
550
        }
551
 
552
        auto &Polygon_models = LevelSharedPolygonModelState.Polygon_models;
553
        t = PHYSFSX_readInt(fp);                        //read number of polygon models
554
        for (j=0;j<t;j++)
555
        {
556
                i = PHYSFSX_readInt(fp);                //read model number
557
                if (i<0 || i>=N_polygon_models)
558
                        Error("Polygon model (%d) out of range in (%s).  Range = [0..%d].",i,static_cast<const char *>(level_name),N_polygon_models-1);
559
 
560
                free_model(Polygon_models[i]);
561
                polymodel_read(&Polygon_models[i], fp);
562
                polygon_model_data_read(&Polygon_models[i], fp);
563
 
564
                Dying_modelnums[i] = PHYSFSX_readInt(fp);
565
                Dead_modelnums[i] = PHYSFSX_readInt(fp);
566
        }
567
 
568
        t = PHYSFSX_readInt(fp);                        //read number of objbitmaps
569
        for (j=0;j<t;j++) {
570
                i = PHYSFSX_readInt(fp);                //read objbitmap number
571
                if (i < 0 || i >= ObjBitmaps.size())
572
                        Error("Object bitmap number (%d) out of range in (%s).  Range = [0..%" DXX_PRI_size_type "].", i, static_cast<const char *>(level_name), ObjBitmaps.size() - 1);
573
                bitmap_index_read(fp, ObjBitmaps[i]);
574
        }
575
 
576
        t = PHYSFSX_readInt(fp);                        //read number of objbitmapptrs
577
        for (j=0;j<t;j++) {
578
                i = PHYSFSX_readInt(fp);                //read objbitmapptr number
579
                if (i < 0 || i >= ObjBitmapPtrs.size())
580
                        Error("Object bitmap pointer (%d) out of range in (%s).  Range = [0..%" DXX_PRI_size_type "].", i, static_cast<const char *>(level_name), ObjBitmapPtrs.size() - 1);
581
                ObjBitmapPtrs[i] = PHYSFSX_readShort(fp);
582
        }
583
        Robot_replacements_loaded = 1;
584
}
585
 
586
 
587
/*
588
 * Routines for loading exit models
589
 *
590
 * Used by d1 levels (including some add-ons), and by d2 shareware.
591
 * Could potentially be used by d2 add-on levels, but only if they
592
 * don't use "extra" robots... or maybe they do
593
 */
594
 
595
// formerly exitmodel_bm_load_sub
596
static bitmap_index read_extra_bitmap_iff(const char * filename )
597
{
598
        bitmap_index bitmap_num;
599
        grs_bitmap * n = &GameBitmaps[extra_bitmap_num];
600
        palette_array_t newpal;
601
        int iff_error;          //reference parm to avoid warning message
602
 
603
        bitmap_num.index = 0;
604
 
605
        //MALLOC( new, grs_bitmap, 1 );
606
        iff_error = iff_read_bitmap(filename, *n, &newpal);
607
        if (iff_error != IFF_NO_ERROR)          {
608
                con_printf(CON_DEBUG, "Error loading exit model bitmap <%s> - IFF error: %s", filename, iff_errormsg(iff_error));
609
                return bitmap_num;
610
        }
611
 
612
        gr_remap_bitmap_good(*n, newpal, iff_has_transparency ? iff_transparent_color : -1, 254);
613
 
614
#if !DXX_USE_OGL
615
        n->avg_color = 0;       //compute_average_pixel(new);
616
#endif
617
 
618
        bitmap_num.index = extra_bitmap_num;
619
 
620
        GameBitmaps[extra_bitmap_num++] = *n;
621
 
622
        //d_free( n );
623
        return bitmap_num;
624
}
625
 
626
// formerly load_exit_model_bitmap
627
static grs_bitmap *bm_load_extra_objbitmap(const char *name)
628
{
629
        assert(N_ObjBitmaps < ObjBitmaps.size());
630
        {
631
                ObjBitmaps[N_ObjBitmaps] = read_extra_bitmap_iff(name);
632
 
633
                if (ObjBitmaps[N_ObjBitmaps].index == 0)
634
                {
635
                        RAIIdmem<char[]> name2(d_strdup(name));
636
                        *strrchr(name2.get(), '.') = '\0';
637
                        ObjBitmaps[N_ObjBitmaps] = read_extra_bitmap_d1_pig(name2.get());
638
                }
639
                if (ObjBitmaps[N_ObjBitmaps].index == 0)
640
                        return NULL;
641
 
642
                if (GameBitmaps[ObjBitmaps[N_ObjBitmaps].index].bm_w!=64 || GameBitmaps[ObjBitmaps[N_ObjBitmaps].index].bm_h!=64)
643
                        Error("Bitmap <%s> is not 64x64",name);
644
                ObjBitmapPtrs[N_ObjBitmaps] = N_ObjBitmaps;
645
                N_ObjBitmaps++;
646
                assert(N_ObjBitmaps < ObjBitmaps.size());
647
                return &GameBitmaps[ObjBitmaps[N_ObjBitmaps-1].index];
648
        }
649
}
650
 
651
static void bm_unload_last_objbitmaps(unsigned count)
652
{
653
        assert(N_ObjBitmaps >= count);
654
 
655
        unsigned new_N_ObjBitmaps = N_ObjBitmaps - count;
656
        range_for (auto &o, partial_range(ObjBitmaps, new_N_ObjBitmaps, N_ObjBitmaps))
657
                d_free(GameBitmaps[o.index].bm_mdata);
658
        N_ObjBitmaps = new_N_ObjBitmaps;
659
}
660
 
661
// only called for D2 registered, but there is a D1 check anyway for
662
// possible later use
663
int load_exit_models()
664
{
665
        int start_num;
666
 
667
/*
668
        don't free extra models in native D2 mode -- ziplantil. it's our
669
        responsibility to make sure the exit stuff is already loaded rather than
670
        loading it all again.
671
 
672
        however, in D1 mode, we always need to reload everything due to how
673
        the exit data is loaded (which is different from D2 native mode)
674
*/
675
        if (EMULATING_D1) // D1?
676
        {
677
                bm_free_extra_models();
678
                bm_free_extra_objbitmaps();
679
        }
680
 
681
        // make sure there is enough space to load textures and models
682
        if (!Exit_bitmaps_loaded && N_ObjBitmaps > ObjBitmaps.size() - 6)
683
        {
684
                return 0;
685
        }
686
        if (!Exit_models_loaded && N_polygon_models > MAX_POLYGON_MODELS - 2)
687
        {
688
                return 0;
689
        }
690
 
691
        start_num = N_ObjBitmaps;
692
        if (!Exit_bitmaps_loaded)
693
        {
694
                if (!bm_load_extra_objbitmap("steel1.bbm") ||
695
                        !bm_load_extra_objbitmap("rbot061.bbm") ||
696
                        !bm_load_extra_objbitmap("rbot062.bbm") ||
697
                        !bm_load_extra_objbitmap("steel1.bbm") ||
698
                        !bm_load_extra_objbitmap("rbot061.bbm") ||
699
                        !bm_load_extra_objbitmap("rbot063.bbm"))
700
                {
701
                        // unload the textures that we already loaded
702
                        bm_unload_last_objbitmaps(N_ObjBitmaps - start_num);
703
                        con_puts(CON_NORMAL, "Can't load exit models!");
704
                        return 0;
705
                }      
706
                Exit_bitmap_index = start_num;
707
        }
708
 
709
        auto &Polygon_models = LevelSharedPolygonModelState.Polygon_models;
710
        if (Exit_models_loaded && exit_modelnum < N_polygon_models && destroyed_exit_modelnum < N_polygon_models)
711
        {
712
                // already loaded, just adjust texture indexes
713
                Polygon_models[exit_modelnum].first_texture = Exit_bitmap_index;
714
                Polygon_models[destroyed_exit_modelnum].first_texture = Exit_bitmap_index+3;
715
                return 1;
716
        }
717
 
718
        if (auto exit_hamfile = PHYSFSX_openReadBuffered("exit.ham"))
719
        {
720
                exit_modelnum = N_polygon_models++;
721
                destroyed_exit_modelnum = N_polygon_models++;
722
                polymodel_read(&Polygon_models[exit_modelnum], exit_hamfile);
723
                polymodel_read(&Polygon_models[destroyed_exit_modelnum], exit_hamfile);
724
                Polygon_models[exit_modelnum].first_texture = start_num;
725
                Polygon_models[destroyed_exit_modelnum].first_texture = start_num+3;
726
 
727
                polygon_model_data_read(&Polygon_models[exit_modelnum], exit_hamfile);
728
 
729
                polygon_model_data_read(&Polygon_models[destroyed_exit_modelnum], exit_hamfile);
730
        } else if (PHYSFSX_exists("exit01.pof",1) && PHYSFSX_exists("exit01d.pof",1)) {
731
 
732
                exit_modelnum = load_polygon_model("exit01.pof", 3, start_num, NULL);
733
                destroyed_exit_modelnum = load_polygon_model("exit01d.pof", 3, start_num + 3, NULL);
734
 
735
#if DXX_USE_OGL
736
                ogl_cache_polymodel_textures(exit_modelnum);
737
                ogl_cache_polymodel_textures(destroyed_exit_modelnum);
738
#endif
739
        }
740
        else if ((exit_hamfile = PHYSFSX_openReadBuffered(D1_PIGFILE)))
741
        {
742
                int offset, offset2;
743
                int hamsize;
744
                hamsize = PHYSFS_fileLength(exit_hamfile);
745
                switch (hamsize) { //total hack for loading models
746
                case D1_PIGSIZE:
747
                        offset = 91848;     /* and 92582  */
748
                        offset2 = 383390;   /* and 394022 */
749
                        break;
750
                default:
751
                case D1_SHARE_BIG_PIGSIZE:
752
                case D1_SHARE_10_PIGSIZE:
753
                case D1_SHARE_PIGSIZE:
754
                case D1_10_BIG_PIGSIZE:
755
                case D1_10_PIGSIZE:
756
                        Int3();             /* exit models should be in .pofs */
757
                        DXX_BOOST_FALLTHROUGH;
758
                case D1_OEM_PIGSIZE:
759
                case D1_MAC_PIGSIZE:
760
                case D1_MAC_SHARE_PIGSIZE:
761
                        // unload the textures that we already loaded
762
                        bm_unload_last_objbitmaps(N_ObjBitmaps - start_num);
763
                        con_puts(CON_NORMAL, "Can't load exit models!");
764
                        return 0;
765
                }
766
                PHYSFSX_fseek(exit_hamfile, offset, SEEK_SET);
767
                exit_modelnum = N_polygon_models++;
768
                destroyed_exit_modelnum = N_polygon_models++;
769
                polymodel_read(&Polygon_models[exit_modelnum], exit_hamfile);
770
                polymodel_read(&Polygon_models[destroyed_exit_modelnum], exit_hamfile);
771
                Polygon_models[exit_modelnum].first_texture = start_num;
772
                Polygon_models[destroyed_exit_modelnum].first_texture = start_num+3;
773
 
774
                PHYSFSX_fseek(exit_hamfile, offset2, SEEK_SET);
775
                polygon_model_data_read(&Polygon_models[exit_modelnum], exit_hamfile);
776
                polygon_model_data_read(&Polygon_models[destroyed_exit_modelnum], exit_hamfile);
777
        } else {
778
                // unload the textures that we already loaded
779
                bm_unload_last_objbitmaps(N_ObjBitmaps - start_num);
780
                con_puts(CON_NORMAL, "Can't load exit models!");
781
                return 0;
782
        }
783
 
784
        // set to be loaded, but only on D2 - always reload the data on D1
785
        Exit_models_loaded = Exit_bitmaps_loaded = !EMULATING_D1;
786
        return 1;
787
}
788
 
789
}
790
#endif
791
 
792
void compute_average_rgb(grs_bitmap *bm, std::array<fix, 3> &rgb)
793
{
794
        rgb = {};
795
        if (unlikely(!bm->get_bitmap_data()))
796
                return;
797
        const uint_fast32_t bm_h = bm->bm_h;
798
        const uint_fast32_t bm_w = bm->bm_w;
799
        if (unlikely(!bm_h) || unlikely(!bm_w))
800
                return;
801
 
802
        const auto process_one = [&rgb](uint8_t color) {
803
                if (color == TRANSPARENCY_COLOR)
804
                        return;
805
                auto &t_rgb = gr_palette[color];
806
                if (t_rgb.r == t_rgb.g && t_rgb.r == t_rgb.b)
807
                        return;
808
                rgb[0] += t_rgb.r;
809
                rgb[1] += t_rgb.g;
810
                rgb[2] += t_rgb.b;
811
        };
812
        if (bm->get_flag_mask(BM_FLAG_RLE))
813
        {
814
                bm_rle_expand expander(*bm);
815
                const auto &&buf = std::make_unique<uint8_t[]>(bm_w);
816
                range_for (const uint_fast32_t i, xrange(bm_h))
817
                {
818
                        (void)i;
819
                        const auto &&range = unchecked_partial_range(buf.get(), bm_w);
820
                        if (expander.step(bm_rle_expand_range(range.begin(), range.end())) != bm_rle_expand::again)
821
                                break;
822
                        range_for (const auto color, range)
823
                                process_one(color);
824
                }
825
        }
826
        else
827
        {
828
                range_for (const auto color, unchecked_partial_range(bm->bm_data, bm_w * bm_h))
829
                        process_one(color);
830
        }
831
}