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