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