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 | } |