Details | Last modification | View Log | RSS feed
| Rev | Author | Line No. | Line |
|---|---|---|---|
| 1 | pmbaty | 1 | /* |
| 2 | * This file is part of the DXX-Rebirth project <https://www.dxx-rebirth.com/>. |
||
| 3 | * It is copyright by its individual contributors, as recorded in the |
||
| 4 | * project's Git history. See COPYING.txt at the top level for license |
||
| 5 | * terms and a link to the Git history. |
||
| 6 | */ |
||
| 7 | /* |
||
| 8 | * Load custom textures & robot data |
||
| 9 | */ |
||
| 10 | |||
| 11 | #include <memory> |
||
| 12 | #include <string.h> |
||
| 13 | #include "gr.h" |
||
| 14 | #include "pstypes.h" |
||
| 15 | #include "piggy.h" |
||
| 16 | #include "textures.h" |
||
| 17 | #include "robot.h" |
||
| 18 | #include "weapon.h" |
||
| 19 | #include "digi.h" |
||
| 20 | #include "hash.h" |
||
| 21 | #include "u_mem.h" |
||
| 22 | #include "custom.h" |
||
| 23 | #include "physfsx.h" |
||
| 24 | |||
| 25 | #include "compiler-range_for.h" |
||
| 26 | #include "partial_range.h" |
||
| 27 | #include <iterator> |
||
| 28 | #include <memory> |
||
| 29 | |||
| 30 | //#define D2TMAP_CONV // used for testing |
||
| 31 | |||
| 32 | namespace { |
||
| 33 | |||
| 34 | struct snd_info |
||
| 35 | { |
||
| 36 | unsigned int length; |
||
| 37 | uint8_t *data; |
||
| 38 | }; |
||
| 39 | |||
| 40 | } |
||
| 41 | |||
| 42 | struct DiskBitmapHeader2 |
||
| 43 | { |
||
| 44 | char name[8]; |
||
| 45 | ubyte dflags; |
||
| 46 | ubyte width; |
||
| 47 | ubyte height; |
||
| 48 | ubyte hi_wh; |
||
| 49 | ubyte flags; |
||
| 50 | ubyte avg_color; |
||
| 51 | int offset; |
||
| 52 | } __pack__; |
||
| 53 | |||
| 54 | struct DiskBitmapHeader |
||
| 55 | { |
||
| 56 | char name[8]; |
||
| 57 | ubyte dflags; |
||
| 58 | ubyte width; |
||
| 59 | ubyte height; |
||
| 60 | ubyte flags; |
||
| 61 | ubyte avg_color; |
||
| 62 | int offset; |
||
| 63 | } __pack__; |
||
| 64 | |||
| 65 | struct DiskSoundHeader |
||
| 66 | { |
||
| 67 | char name[8]; |
||
| 68 | int length; |
||
| 69 | int data_length; |
||
| 70 | int offset; |
||
| 71 | } __pack__; |
||
| 72 | |||
| 73 | namespace { |
||
| 74 | |||
| 75 | struct custom_info |
||
| 76 | { |
||
| 77 | int offset; |
||
| 78 | int repl_idx; // 0x80000000 -> sound, -1 -> n/a |
||
| 79 | unsigned int flags; |
||
| 80 | int width, height; |
||
| 81 | }; |
||
| 82 | |||
| 83 | } |
||
| 84 | |||
| 85 | static std::array<grs_bitmap, MAX_BITMAP_FILES> BitmapOriginal; |
||
| 86 | static std::array<snd_info, MAX_SOUND_FILES> SoundOriginal; |
||
| 87 | |||
| 88 | static int load_pig1(PHYSFS_File *f, unsigned num_bitmaps, unsigned num_sounds, unsigned &num_custom, std::unique_ptr<custom_info[]> &ci) |
||
| 89 | { |
||
| 90 | int data_ofs; |
||
| 91 | int i; |
||
| 92 | DiskBitmapHeader bmh; |
||
| 93 | DiskSoundHeader sndh; |
||
| 94 | char name[15]; |
||
| 95 | |||
| 96 | num_custom = 0; |
||
| 97 | |||
| 98 | if (num_bitmaps <= MAX_BITMAP_FILES) // <v1.4 pig? |
||
| 99 | { |
||
| 100 | PHYSFSX_fseek(f, 8, SEEK_SET); |
||
| 101 | data_ofs = 8; |
||
| 102 | } |
||
| 103 | else if (num_bitmaps > 0 && num_bitmaps < PHYSFS_fileLength(f)) // >=v1.4 pig? |
||
| 104 | { |
||
| 105 | PHYSFSX_fseek(f, num_bitmaps, SEEK_SET); |
||
| 106 | data_ofs = num_bitmaps + 8; |
||
| 107 | num_bitmaps = PHYSFSX_readInt(f); |
||
| 108 | num_sounds = PHYSFSX_readInt(f); |
||
| 109 | } |
||
| 110 | else |
||
| 111 | return -1; // invalid pig file |
||
| 112 | |||
| 113 | if (num_bitmaps >= MAX_BITMAP_FILES || |
||
| 114 | num_sounds >= MAX_SOUND_FILES) |
||
| 115 | return -1; // invalid pig file |
||
| 116 | ci = std::make_unique<custom_info[]>(num_bitmaps + num_sounds); |
||
| 117 | custom_info *cip = ci.get(); |
||
| 118 | data_ofs += num_bitmaps * sizeof(DiskBitmapHeader) + num_sounds * sizeof(DiskSoundHeader); |
||
| 119 | i = num_bitmaps; |
||
| 120 | |||
| 121 | while (i--) |
||
| 122 | { |
||
| 123 | if (PHYSFS_read(f, &bmh, sizeof(DiskBitmapHeader), 1) < 1) |
||
| 124 | { |
||
| 125 | return -1; |
||
| 126 | } |
||
| 127 | |||
| 128 | snprintf(name, sizeof(name), "%.8s%c%d", bmh.name, (bmh.dflags & DBM_FLAG_ABM) ? '#' : 0, bmh.dflags & 63); |
||
| 129 | |||
| 130 | cip->offset = bmh.offset + data_ofs; |
||
| 131 | cip->repl_idx = hashtable_search(&AllBitmapsNames, name); |
||
| 132 | cip->flags = bmh.flags & (BM_FLAG_TRANSPARENT | BM_FLAG_SUPER_TRANSPARENT | BM_FLAG_NO_LIGHTING | BM_FLAG_RLE); |
||
| 133 | cip->width = bmh.width + ((bmh.dflags & DBM_FLAG_LARGE) ? 256 : 0); |
||
| 134 | cip->height = bmh.height; |
||
| 135 | cip++; |
||
| 136 | } |
||
| 137 | |||
| 138 | i = num_sounds; |
||
| 139 | |||
| 140 | while (i--) |
||
| 141 | { |
||
| 142 | if (PHYSFS_read(f, &sndh, sizeof(DiskSoundHeader), 1) < 1) |
||
| 143 | { |
||
| 144 | return -1; |
||
| 145 | } |
||
| 146 | |||
| 147 | memcpy(name, sndh.name, 8); |
||
| 148 | name[8] = 0; |
||
| 149 | cip->offset = sndh.offset + data_ofs; |
||
| 150 | cip->repl_idx = hashtable_search(&AllDigiSndNames, name) | 0x80000000; |
||
| 151 | cip->width = sndh.length; |
||
| 152 | cip++; |
||
| 153 | } |
||
| 154 | |||
| 155 | num_custom = num_bitmaps + num_sounds; |
||
| 156 | |||
| 157 | return 0; |
||
| 158 | } |
||
| 159 | |||
| 160 | static int load_pog(PHYSFS_File *f, int pog_sig, int pog_ver, unsigned &num_custom, std::unique_ptr<custom_info[]> &ci) |
||
| 161 | { |
||
| 162 | int data_ofs; |
||
| 163 | int num_bitmaps; |
||
| 164 | int no_repl = 0; |
||
| 165 | DiskBitmapHeader2 bmh; |
||
| 166 | |||
| 167 | #ifdef D2TMAP_CONV |
||
| 168 | int x, j, N_d2tmap; |
||
| 169 | int *d2tmap = NULL; |
||
| 170 | PHYSFS_File *f2 = NULL; |
||
| 171 | if ((f2 = PHYSFSX_openReadBuffered("d2tmap.bin"))) |
||
| 172 | { |
||
| 173 | N_d2tmap = PHYSFSX_readInt(f2); |
||
| 174 | if ((d2tmap = d_malloc(N_d2tmap * sizeof(d2tmap[0])))) |
||
| 175 | for (i = 0; i < N_d2tmap; i++) |
||
| 176 | d2tmap[i] = PHYSFSX_readShort(f2); |
||
| 177 | PHYSFS_close(f2); |
||
| 178 | } |
||
| 179 | #endif |
||
| 180 | |||
| 181 | num_custom = 0; |
||
| 182 | |||
| 183 | if (pog_sig == 0x47495050 && pog_ver == 2) /* PPIG */ |
||
| 184 | no_repl = 1; |
||
| 185 | else if (pog_sig != 0x474f5044 || pog_ver != 1) /* DPOG */ |
||
| 186 | return -1; // no pig2/pog file/unknown version |
||
| 187 | |||
| 188 | num_bitmaps = PHYSFSX_readInt(f); |
||
| 189 | ci = std::make_unique<custom_info[]>(num_bitmaps); |
||
| 190 | custom_info *cip = ci.get(); |
||
| 191 | data_ofs = 12 + num_bitmaps * sizeof(DiskBitmapHeader2); |
||
| 192 | |||
| 193 | if (!no_repl) |
||
| 194 | { |
||
| 195 | for (int i = num_bitmaps; i--;) |
||
| 196 | (cip++)->repl_idx = PHYSFSX_readShort(f); |
||
| 197 | |||
| 198 | cip = ci.get(); |
||
| 199 | data_ofs += num_bitmaps * 2; |
||
| 200 | } |
||
| 201 | |||
| 202 | #ifdef D2TMAP_CONV |
||
| 203 | if (d2tmap) |
||
| 204 | for (i = 0; i < num_bitmaps; i++) |
||
| 205 | { |
||
| 206 | x = cip[i].repl_idx; |
||
| 207 | cip[i].repl_idx = -1; |
||
| 208 | |||
| 209 | for (j = 0; j < N_d2tmap; j++) |
||
| 210 | if (x == d2tmap[j]) |
||
| 211 | { |
||
| 212 | cip[i].repl_idx = Textures[j % NumTextures].index; |
||
| 213 | break; |
||
| 214 | } |
||
| 215 | } |
||
| 216 | #endif |
||
| 217 | for (int i = num_bitmaps; i--;) |
||
| 218 | { |
||
| 219 | if (PHYSFS_read(f, &bmh, sizeof(DiskBitmapHeader2), 1) < 1) |
||
| 220 | { |
||
| 221 | return -1; |
||
| 222 | } |
||
| 223 | |||
| 224 | cip->offset = bmh.offset + data_ofs; |
||
| 225 | cip->flags = bmh.flags & (BM_FLAG_TRANSPARENT | BM_FLAG_SUPER_TRANSPARENT | BM_FLAG_NO_LIGHTING | BM_FLAG_RLE); |
||
| 226 | cip->width = bmh.width + ((bmh.hi_wh & 15) << 8); |
||
| 227 | cip->height = bmh.height + ((bmh.hi_wh >> 4) << 8); |
||
| 228 | cip++; |
||
| 229 | } |
||
| 230 | |||
| 231 | num_custom = num_bitmaps; |
||
| 232 | |||
| 233 | return 0; |
||
| 234 | } |
||
| 235 | |||
| 236 | // load custom textures/sounds from pog/pig file |
||
| 237 | // returns 0 if ok, <0 on error |
||
| 238 | static int load_pigpog(const d_fname &pogname) |
||
| 239 | { |
||
| 240 | unsigned num_custom; |
||
| 241 | grs_bitmap *bmp; |
||
| 242 | digi_sound *snd; |
||
| 243 | uint8_t *p; |
||
| 244 | auto f = PHYSFSX_openReadBuffered(pogname); |
||
| 245 | int i, j, rc = -1; |
||
| 246 | unsigned int x = 0; |
||
| 247 | |||
| 248 | if (!f) |
||
| 249 | return -1; // pog file doesn't exist |
||
| 250 | |||
| 251 | i = PHYSFSX_readInt(f); |
||
| 252 | x = PHYSFSX_readInt(f); |
||
| 253 | |||
| 254 | std::unique_ptr<custom_info[]> ci; |
||
| 255 | if (load_pog(f, i, x, num_custom, ci) && load_pig1(f, i, x, num_custom, ci)) |
||
| 256 | { |
||
| 257 | return rc; |
||
| 258 | } |
||
| 259 | |||
| 260 | custom_info *cip = ci.get(); |
||
| 261 | i = num_custom; |
||
| 262 | |||
| 263 | while (i--) |
||
| 264 | { |
||
| 265 | x = cip->repl_idx; |
||
| 266 | if (cip->repl_idx >= 0) |
||
| 267 | { |
||
| 268 | PHYSFSX_fseek( f, cip->offset, SEEK_SET ); |
||
| 269 | |||
| 270 | if ( cip->flags & BM_FLAG_RLE ) |
||
| 271 | j = PHYSFSX_readInt(f); |
||
| 272 | else |
||
| 273 | j = cip->width * cip->height; |
||
| 274 | |||
| 275 | if (!MALLOC(p, ubyte, j)) |
||
| 276 | { |
||
| 277 | return rc; |
||
| 278 | } |
||
| 279 | |||
| 280 | bmp = &GameBitmaps[x]; |
||
| 281 | |||
| 282 | if (BitmapOriginal[x].get_flag_mask(0x80)) // already customized? |
||
| 283 | gr_free_bitmap_data(*bmp); |
||
| 284 | else |
||
| 285 | { |
||
| 286 | // save original bitmap info |
||
| 287 | BitmapOriginal[x] = *bmp; |
||
| 288 | BitmapOriginal[x].add_flags(0x80); |
||
| 289 | if (GameBitmapOffset[x]) // from pig? |
||
| 290 | { |
||
| 291 | BitmapOriginal[x].add_flags(BM_FLAG_PAGED_OUT); |
||
| 292 | BitmapOriginal[x].bm_data = reinterpret_cast<uint8_t *>(static_cast<uintptr_t>(GameBitmapOffset[x])); |
||
| 293 | } |
||
| 294 | } |
||
| 295 | |||
| 296 | GameBitmapOffset[x] = 0; // not in pig |
||
| 297 | *bmp = {}; |
||
| 298 | gr_init_bitmap(*bmp, bm_mode::linear, 0, 0, cip->width, cip->height, cip->width, p); |
||
| 299 | gr_set_bitmap_flags(*bmp, cip->flags & 255); |
||
| 300 | #if !DXX_USE_OGL |
||
| 301 | bmp->avg_color = cip->flags >> 8; |
||
| 302 | #endif |
||
| 303 | |||
| 304 | if ( cip->flags & BM_FLAG_RLE ) |
||
| 305 | { |
||
| 306 | int *ip = reinterpret_cast<int *>(p); |
||
| 307 | *ip = j; |
||
| 308 | p += 4; |
||
| 309 | j -= 4; |
||
| 310 | } |
||
| 311 | |||
| 312 | if (PHYSFS_read(f, p, 1, j) < 1) |
||
| 313 | { |
||
| 314 | return rc; |
||
| 315 | } |
||
| 316 | |||
| 317 | } |
||
| 318 | else if ((cip->repl_idx + 1) < 0) |
||
| 319 | { |
||
| 320 | PHYSFSX_fseek( f, cip->offset, SEEK_SET ); |
||
| 321 | snd = &GameSounds[x & 0x7fffffff]; |
||
| 322 | |||
| 323 | j = cip->width; |
||
| 324 | if (!MALLOC(p, ubyte, j)) |
||
| 325 | { |
||
| 326 | return rc; |
||
| 327 | } |
||
| 328 | |||
| 329 | if (SoundOriginal[x & 0x7fffffff].length & 0x80000000) // already customized? |
||
| 330 | d_free(snd->data); |
||
| 331 | else |
||
| 332 | { |
||
| 333 | #ifdef ALLEGRO |
||
| 334 | SoundOriginal[x & 0x7fffffff].length = snd->len | 0x80000000; |
||
| 335 | #else |
||
| 336 | SoundOriginal[x & 0x7fffffff].length = snd->length | 0x80000000; |
||
| 337 | #endif |
||
| 338 | SoundOriginal[x & 0x7fffffff].data = snd->data; |
||
| 339 | } |
||
| 340 | |||
| 341 | #ifdef ALLEGRO |
||
| 342 | snd->loop_end = snd->len = j; |
||
| 343 | #else |
||
| 344 | snd->length = j; |
||
| 345 | #endif |
||
| 346 | snd->data = p; |
||
| 347 | |||
| 348 | if (PHYSFS_read(f, p, j, 1) < 1) |
||
| 349 | { |
||
| 350 | return rc; |
||
| 351 | } |
||
| 352 | } |
||
| 353 | cip++; |
||
| 354 | } |
||
| 355 | rc = 0; |
||
| 356 | return rc; |
||
| 357 | } |
||
| 358 | |||
| 359 | static int read_d2_robot_info(PHYSFS_File *fp, robot_info &ri) |
||
| 360 | { |
||
| 361 | int j, k; |
||
| 362 | |||
| 363 | ri.model_num = PHYSFSX_readInt(fp); |
||
| 364 | |||
| 365 | for (j = 0; j < MAX_GUNS; j++) |
||
| 366 | PHYSFSX_readVector(fp, ri.gun_points[j]); |
||
| 367 | for (j = 0; j < MAX_GUNS; j++) |
||
| 368 | ri.gun_submodels[j] = PHYSFSX_readByte(fp); |
||
| 369 | ri.exp1_vclip_num = PHYSFSX_readShort(fp); |
||
| 370 | ri.exp1_sound_num = PHYSFSX_readShort(fp); |
||
| 371 | ri.exp2_vclip_num = PHYSFSX_readShort(fp); |
||
| 372 | ri.exp2_sound_num = PHYSFSX_readShort(fp); |
||
| 373 | const auto weapon_type = PHYSFSX_readByte(fp); |
||
| 374 | ri.weapon_type = weapon_type < N_weapon_types ? static_cast<weapon_id_type>(weapon_type) : weapon_id_type::LASER_ID_L1; |
||
| 375 | /*ri.weapon_type2 =*/ PHYSFSX_readByte(fp); |
||
| 376 | ri.n_guns = PHYSFSX_readByte(fp); |
||
| 377 | ri.contains_id = PHYSFSX_readByte(fp); |
||
| 378 | ri.contains_count = PHYSFSX_readByte(fp); |
||
| 379 | ri.contains_prob = PHYSFSX_readByte(fp); |
||
| 380 | ri.contains_type = PHYSFSX_readByte(fp); |
||
| 381 | /*ri.kamikaze =*/ PHYSFSX_readByte(fp); |
||
| 382 | ri.score_value = PHYSFSX_readShort(fp); |
||
| 383 | /*ri.badass =*/ PHYSFSX_readByte(fp); |
||
| 384 | /*ri.energy_drain =*/ PHYSFSX_readByte(fp); |
||
| 385 | ri.lighting = PHYSFSX_readFix(fp); |
||
| 386 | ri.strength = PHYSFSX_readFix(fp); |
||
| 387 | ri.mass = PHYSFSX_readFix(fp); |
||
| 388 | ri.drag = PHYSFSX_readFix(fp); |
||
| 389 | for (j = 0; j < NDL; j++) |
||
| 390 | ri.field_of_view[j] = PHYSFSX_readFix(fp); |
||
| 391 | for (j = 0; j < NDL; j++) |
||
| 392 | ri.firing_wait[j] = PHYSFSX_readFix(fp); |
||
| 393 | for (j = 0; j < NDL; j++) |
||
| 394 | /*ri.firing_wait2[j] =*/ PHYSFSX_readFix(fp); |
||
| 395 | for (j = 0; j < NDL; j++) |
||
| 396 | ri.turn_time[j] = PHYSFSX_readFix(fp); |
||
| 397 | #if 0 // not used in d1, removed in d2 |
||
| 398 | for (j = 0; j < NDL; j++) |
||
| 399 | ri.fire_power[j] = PHYSFSX_readFix(fp); |
||
| 400 | for (j = 0; j < NDL; j++) |
||
| 401 | ri.shield[j] = PHYSFSX_readFix(fp); |
||
| 402 | #endif |
||
| 403 | for (j = 0; j < NDL; j++) |
||
| 404 | ri.max_speed[j] = PHYSFSX_readFix(fp); |
||
| 405 | for (j = 0; j < NDL; j++) |
||
| 406 | ri.circle_distance[j] = PHYSFSX_readFix(fp); |
||
| 407 | for (j = 0; j < NDL; j++) |
||
| 408 | ri.rapidfire_count[j] = PHYSFSX_readByte(fp); |
||
| 409 | for (j = 0; j < NDL; j++) |
||
| 410 | ri.evade_speed[j] = PHYSFSX_readByte(fp); |
||
| 411 | ri.cloak_type = PHYSFSX_readByte(fp); |
||
| 412 | ri.attack_type = PHYSFSX_readByte(fp); |
||
| 413 | ri.see_sound = PHYSFSX_readByte(fp); |
||
| 414 | ri.attack_sound = PHYSFSX_readByte(fp); |
||
| 415 | ri.claw_sound = PHYSFSX_readByte(fp); |
||
| 416 | /*ri.taunt_sound =*/ PHYSFSX_readByte(fp); |
||
| 417 | ri.boss_flag = PHYSFSX_readByte(fp); |
||
| 418 | /*ri.companion =*/ PHYSFSX_readByte(fp); |
||
| 419 | /*ri.smart_blobs =*/ PHYSFSX_readByte(fp); |
||
| 420 | /*ri.energy_blobs =*/ PHYSFSX_readByte(fp); |
||
| 421 | /*ri.thief =*/ PHYSFSX_readByte(fp); |
||
| 422 | /*ri.pursuit =*/ PHYSFSX_readByte(fp); |
||
| 423 | /*ri.lightcast =*/ PHYSFSX_readByte(fp); |
||
| 424 | /*ri.death_roll =*/ PHYSFSX_readByte(fp); |
||
| 425 | /*ri.flags =*/ PHYSFSX_readByte(fp); |
||
| 426 | /*ri.pad[0] =*/ PHYSFSX_readByte(fp); |
||
| 427 | /*ri.pad[1] =*/ PHYSFSX_readByte(fp); |
||
| 428 | /*ri.pad[2] =*/ PHYSFSX_readByte(fp); |
||
| 429 | /*ri.deathroll_sound =*/ PHYSFSX_readByte(fp); |
||
| 430 | /*ri.glow =*/ PHYSFSX_readByte(fp); |
||
| 431 | /*ri.behavior =*/ PHYSFSX_readByte(fp); |
||
| 432 | /*ri.aim =*/ PHYSFSX_readByte(fp); |
||
| 433 | |||
| 434 | for (j = 0; j < MAX_GUNS + 1; j++) |
||
| 435 | { |
||
| 436 | for (k = 0; k < N_ANIM_STATES; k++) |
||
| 437 | { |
||
| 438 | ri.anim_states[j][k].n_joints = PHYSFSX_readShort(fp); |
||
| 439 | ri.anim_states[j][k].offset = PHYSFSX_readShort(fp); |
||
| 440 | } |
||
| 441 | } |
||
| 442 | ri.always_0xabcd = PHYSFSX_readInt(fp); |
||
| 443 | |||
| 444 | return 1; |
||
| 445 | } |
||
| 446 | |||
| 447 | namespace dsx { |
||
| 448 | |||
| 449 | static void load_hxm(const d_fname &hxmname) |
||
| 450 | { |
||
| 451 | auto &Robot_joints = LevelSharedRobotJointState.Robot_joints; |
||
| 452 | unsigned int repl_num; |
||
| 453 | int i; |
||
| 454 | auto f = PHYSFSX_openReadBuffered(hxmname); |
||
| 455 | int n_items; |
||
| 456 | |||
| 457 | if (!f) |
||
| 458 | return; // hxm file doesn't exist |
||
| 459 | |||
| 460 | if (PHYSFSX_readInt(f) != 0x21584d48) /* HMX! */ |
||
| 461 | { |
||
| 462 | // invalid hxm file |
||
| 463 | return; |
||
| 464 | } |
||
| 465 | |||
| 466 | if (PHYSFSX_readInt(f) != 1) |
||
| 467 | { |
||
| 468 | // unknown version |
||
| 469 | return; |
||
| 470 | } |
||
| 471 | |||
| 472 | // read robot info |
||
| 473 | if ((n_items = PHYSFSX_readInt(f)) != 0) |
||
| 474 | { |
||
| 475 | auto &Robot_info = LevelSharedRobotInfoState.Robot_info; |
||
| 476 | for (i = 0; i < n_items; i++) |
||
| 477 | { |
||
| 478 | repl_num = PHYSFSX_readInt(f); |
||
| 479 | |||
| 480 | if (repl_num >= MAX_ROBOT_TYPES) |
||
| 481 | { |
||
| 482 | PHYSFSX_fseek(f, 480, SEEK_CUR); /* sizeof d2_robot_info */ |
||
| 483 | } |
||
| 484 | else |
||
| 485 | { |
||
| 486 | if (!(read_d2_robot_info(f, Robot_info[repl_num]))) |
||
| 487 | { |
||
| 488 | return; |
||
| 489 | } |
||
| 490 | } |
||
| 491 | } |
||
| 492 | } |
||
| 493 | |||
| 494 | // read joint positions |
||
| 495 | if ((n_items = PHYSFSX_readInt(f)) != 0) |
||
| 496 | { |
||
| 497 | for (i = 0; i < n_items; i++) |
||
| 498 | { |
||
| 499 | repl_num = PHYSFSX_readInt(f); |
||
| 500 | |||
| 501 | if (repl_num >= MAX_ROBOT_JOINTS) |
||
| 502 | PHYSFSX_fseek(f, sizeof(jointpos), SEEK_CUR); |
||
| 503 | else |
||
| 504 | { |
||
| 505 | jointpos_read(f, Robot_joints[repl_num]); |
||
| 506 | } |
||
| 507 | } |
||
| 508 | } |
||
| 509 | |||
| 510 | // read polygon models |
||
| 511 | if ((n_items = PHYSFSX_readInt(f)) != 0) |
||
| 512 | { |
||
| 513 | for (i = 0; i < n_items; i++) |
||
| 514 | { |
||
| 515 | polymodel *pm; |
||
| 516 | |||
| 517 | repl_num = PHYSFSX_readInt(f); |
||
| 518 | if (repl_num >= MAX_POLYGON_MODELS) |
||
| 519 | { |
||
| 520 | PHYSFSX_readInt(f); // skip n_models |
||
| 521 | PHYSFSX_fseek(f, 734 - 8 + PHYSFSX_readInt(f) + 8, SEEK_CUR); |
||
| 522 | } |
||
| 523 | else |
||
| 524 | { |
||
| 525 | auto &Polygon_models = LevelSharedPolygonModelState.Polygon_models; |
||
| 526 | pm = &Polygon_models[repl_num]; |
||
| 527 | polymodel_read(pm, f); |
||
| 528 | const auto model_data_size = pm->model_data_size; |
||
| 529 | pm->model_data = std::make_unique<uint8_t[]>(model_data_size); |
||
| 530 | if (PHYSFS_read(f, pm->model_data, model_data_size, 1) < 1) |
||
| 531 | { |
||
| 532 | pm->model_data.reset(); |
||
| 533 | return; |
||
| 534 | } |
||
| 535 | |||
| 536 | Dying_modelnums[repl_num] = PHYSFSX_readInt(f); |
||
| 537 | Dead_modelnums[repl_num] = PHYSFSX_readInt(f); |
||
| 538 | } |
||
| 539 | } |
||
| 540 | } |
||
| 541 | |||
| 542 | // read object bitmaps |
||
| 543 | if ((n_items = PHYSFSX_readInt(f)) != 0) |
||
| 544 | { |
||
| 545 | for (i = 0; i < n_items; i++) |
||
| 546 | { |
||
| 547 | repl_num = PHYSFSX_readInt(f); |
||
| 548 | auto v = PHYSFSX_readShort(f); |
||
| 549 | if (repl_num < ObjBitmaps.size()) |
||
| 550 | ObjBitmaps[repl_num].index = v; |
||
| 551 | } |
||
| 552 | } |
||
| 553 | } |
||
| 554 | |||
| 555 | } |
||
| 556 | |||
| 557 | // undo customized items |
||
| 558 | static void custom_remove() |
||
| 559 | { |
||
| 560 | int i; |
||
| 561 | auto bmo = std::begin(BitmapOriginal); |
||
| 562 | auto bmp = std::begin(GameBitmaps); |
||
| 563 | |||
| 564 | for (i = 0; i < MAX_BITMAP_FILES; bmo++, bmp++, i++) |
||
| 565 | if (bmo->get_flag_mask(0x80)) |
||
| 566 | { |
||
| 567 | gr_free_bitmap_data(*bmp); |
||
| 568 | *bmp = *bmo; |
||
| 569 | |||
| 570 | if (bmo->get_flag_mask(BM_FLAG_PAGED_OUT)) |
||
| 571 | { |
||
| 572 | GameBitmapOffset[i] = static_cast<int>(reinterpret_cast<uintptr_t>(bmo->bm_data)); |
||
| 573 | gr_set_bitmap_flags(*bmp, BM_FLAG_PAGED_OUT); |
||
| 574 | gr_set_bitmap_data(*bmp, nullptr); |
||
| 575 | } |
||
| 576 | else |
||
| 577 | { |
||
| 578 | gr_set_bitmap_flags(*bmp, bmo->get_flag_mask(0x7f)); |
||
| 579 | } |
||
| 580 | bmo->clear_flags(); |
||
| 581 | } |
||
| 582 | for (i = 0; i < MAX_SOUND_FILES; i++) |
||
| 583 | if (SoundOriginal[i].length & 0x80000000) |
||
| 584 | { |
||
| 585 | d_free(GameSounds[i].data); |
||
| 586 | GameSounds[i].data = SoundOriginal[i].data; |
||
| 587 | #ifdef ALLEGRO |
||
| 588 | GameSounds[i].len = SoundOriginal[i].length & 0x7fffffff; |
||
| 589 | #else |
||
| 590 | GameSounds[i].length = SoundOriginal[i].length & 0x7fffffff; |
||
| 591 | #endif |
||
| 592 | SoundOriginal[i].length = 0; |
||
| 593 | } |
||
| 594 | } |
||
| 595 | |||
| 596 | void load_custom_data(const d_fname &level_name) |
||
| 597 | { |
||
| 598 | custom_remove(); |
||
| 599 | d_fname custom_file; |
||
| 600 | using std::begin; |
||
| 601 | using std::end; |
||
| 602 | using std::copy; |
||
| 603 | using std::next; |
||
| 604 | auto bl = begin(level_name); |
||
| 605 | auto bc = begin(custom_file); |
||
| 606 | auto &pg1 = ".pg1"; |
||
| 607 | copy(bl, next(bl, custom_file.size() - sizeof(pg1)), bc); |
||
| 608 | auto o = std::find(bc, next(bc, custom_file.size() - sizeof(pg1)), '.'); |
||
| 609 | copy(begin(pg1), end(pg1), o); |
||
| 610 | load_pigpog(custom_file); |
||
| 611 | auto &dtx = "dtx"; |
||
| 612 | ++o; |
||
| 613 | copy(begin(dtx), end(dtx), o); |
||
| 614 | load_pigpog(custom_file); |
||
| 615 | auto &hx1 = "hx1"; |
||
| 616 | copy(begin(hx1), end(hx1), o); |
||
| 617 | load_hxm(custom_file); |
||
| 618 | } |
||
| 619 | |||
| 620 | void custom_close() |
||
| 621 | { |
||
| 622 | custom_remove(); |
||
| 623 | } |