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