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-1998 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED.
18 */
19
20 /*
21 *
22 * Mine specific editing functions, such as load_mine, save_mine
23 *
24 */
25
26 #include <cinttypes>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <math.h>
30 #include <string.h>
31 #include "gr.h"
32 #include "bm.h" // for MAX_TEXTURES
33 #include "inferno.h"
34 #include "segment.h"
35 #include "editor.h"
36 #include "editor/esegment.h"
37 #include "wall.h"
38 #include "dxxerror.h"
39 #include "textures.h"
40 #include "physfsx.h"
41 #include "gamemine.h"
42 #include "gamesave.h"
43 #include "switch.h"
44 #include "fuelcen.h"
45
46 #include "compiler-range_for.h"
47 #include "d_range.h"
48 #include "partial_range.h"
49 #include "d_underlying_value.h"
50
51 #define REMOVE_EXT(s) (*(strchr( (s), '.' ))='\0')
52
53 int New_file_format_save = 1;
54
55 #if defined(DXX_BUILD_DESCENT_II)
56 namespace dsx {
57
58 namespace {
59
60 // Converts descent 2 texture numbers back to descent 1 texture numbers.
61 // Only works properly when the full Descent 1 texture set (descent.pig) is available.
convert_to_d1_tmap_num(const texture_index tmap_num)62 static texture_index convert_to_d1_tmap_num(const texture_index tmap_num)
63 {
64 switch (tmap_num)
65 {
66 case 137: return 0; // grey rock001
67 case 0: return 1;
68 case 1: return 3; // rock021
69 case 270: return 6; // blue rock002
70 case 271: return 7; // yellow rock265
71 case 2: return 8; // rock004
72 case 138: return 9; // purple (devil:179)
73 case 272: return 10; // red rock006
74 case 139: return 11;
75 case 140: return 12; //devil:43
76 case 3: return 13; // rock014
77 case 4: return 14; // rock019
78 case 5: return 15; // rock020
79 case 6: return 16;
80 case 141: return 17;
81 case 129: return 18;
82 case 7: return 19;
83 case 142: return 20;
84 case 143: return 21;
85 case 8: return 22;
86 case 9: return 23;
87 case 10: return 24;
88 case 144: return 25; //devil:35
89 case 11: return 26;
90 case 12: return 27;
91 case 145: return 28; //devil:43
92 //range handled by default case, returns 13..21 (- 16)
93 case 163: return 38; //devil:27
94 case 147: return 39; //31
95 case 22: return 40;
96 case 266: return 41;
97 case 23: return 42;
98 case 24: return 43;
99 case 136: return 44; //devil:135
100 case 25: return 45;
101 case 26: return 46;
102 case 27: return 47;
103 case 28: return 48;
104 case 146: return 49; //devil:60
105 case 131: return 50; //devil:138
106 case 29: return 51;
107 case 30: return 52;
108 case 31: return 53;
109 case 32: return 54;
110 case 165: return 55; //devil:193
111 case 33: return 56;
112 case 132: return 57; //devil:119
113 // range handled by default case, returns 58..88 (+ 24)
114 case 197: return 88; //devil:15
115 // range handled by default case, returns 89..106 (- 25)
116 case 167: return 132;
117 // range handled by default case, returns 107..114 (- 26)
118 case 148: return 141; //devil:106
119 case 115: return 142;
120 case 116: return 143;
121 case 117: return 144;
122 case 118: return 145;
123 case 119: return 146;
124 case 149: return 147;
125 case 120: return 148;
126 case 121: return 149;
127 case 122: return 150;
128 case 123: return 151;
129 case 124: return 152;
130 case 125: return 153; // rock263
131 case 150: return 154;
132 case 126: return 155; // rock269
133 case 200: return 156; // metl002
134 case 201: return 157; // metl003
135 case 186: return 158; //devil:227
136 case 190: return 159; //devil:246
137 case 151: return 160;
138 case 152: return 161; //devil:206
139 case 202: return 162;
140 case 203: return 163;
141 case 204: return 164;
142 case 205: return 165;
143 case 206: return 166;
144 case 153: return 167;
145 case 154: return 168;
146 case 155: return 169;
147 case 156: return 170;//206;
148 case 157: return 171;//227;
149 case 207: return 172;
150 case 208: return 173;
151 case 158: return 174;
152 case 159: return 175;
153 // range handled by default case, returns 209..217 (+ 33)
154 case 160: return 185;
155 // range handled by default case, returns 218..224 (+ 32)
156 case 161: return 193;
157 case 162: return 194;//206;
158 case 166: return 195;
159 case 225: return 196;
160 case 226: return 197;
161 case 193: return 198;
162 case 168: return 199; //devil:204
163 case 169: return 200; //devil:204
164 case 227: return 201;
165 case 170: return 202; //devil:227
166 // range handled by default case, returns 228..234 (+ 25)
167 case 171: return 210; //devil:242
168 case 172: return 211; //devil:240
169 // range handled by default case, returns 235..242 (+ 23)
170 case 173: return 220; //devil:240
171 case 243: return 221;
172 case 244: return 222;
173 case 174: return 223;
174 case 245: return 224;
175 case 246: return 225;
176 case 164: return 226;//247; matching names but not matching textures
177 case 179: return 227; //devil:181
178 case 196: return 228;//248; matching names but not matching textures
179 case 175: return 229; //devil:66
180 case 176: return 230; //devil:66
181 // range handled by default case, returns 249..257 (+ 18)
182 case 177: return 240; //devil:132
183 case 130: return 241; //devil:131
184 case 178: return 242; //devil:15
185 case 180: return 243; //devil:38
186 case 258: return 244;
187 case 259: return 245;
188 case 181: return 246; // grate metl127
189 case 260: return 247;
190 case 261: return 248;
191 case 262: return 249;
192 case 340: return 250; // white doorframe metl126
193 case 412: return 251; // red doorframe metl133
194 case 410: return 252; // blue doorframe metl134
195 case 411: return 253; // yellow doorframe metl135
196 case 263: return 254; // metl136
197 case 264: return 255; // metl139
198 case 265: return 256; // metl140
199 case 182: return 257;//246; brig001
200 case 183: return 258;//246; brig002
201 case 184: return 259;//246; brig003
202 case 185: return 260;//246; brig004
203 case 273: return 261; // exit01
204 case 274: return 262; // exit02
205 case 187: return 263; // ceil001
206 case 275: return 264; // ceil002
207 case 276: return 265; // ceil003
208 case 188: return 266; //devil:291
209 // range handled by default case, returns 277..291 (+ 10)
210 case 293: return 282;
211 case 189: return 283;
212 case 295: return 284;
213 case 296: return 285;
214 case 298: return 286;
215 // range handled by default case, returns 300..310 (+ 13)
216 case 191: return 298; // devil:374 misc010
217 // range handled by default case, returns 311..326 (+ 12)
218 case 192: return 315; // bad producer misc044
219 // range handled by default case, returns 327..337 (+ 11)
220 case 352: return 327; // arw01
221 case 353: return 328; // misc17
222 case 354: return 329; // fan01
223 case 380: return 330; // mntr04
224 case 379: return 331;//373; matching names but not matching textures
225 case 355: return 332;//344; matching names but not matching textures
226 case 409: return 333; // lava misc11 //devil:404
227 case 356: return 334; // ctrl04
228 case 357: return 335; // ctrl01
229 case 358: return 336; // ctrl02
230 case 359: return 337; // ctrl03
231 case 360: return 338; // misc14
232 case 361: return 339; // producer misc16
233 case 362: return 340; // misc049
234 case 364: return 341; // misc060
235 case 363: return 342; // blown01
236 case 366: return 343; // misc061
237 case 365: return 344;
238 case 368: return 345;
239 case 376: return 346;
240 case 370: return 347;
241 case 367: return 348;
242 case 372: return 349;
243 case 369: return 350;
244 case 374: return 351;//429; matching names but not matching textures
245 case 375: return 352;//387; matching names but not matching textures
246 case 371: return 353;
247 case 377: return 354;//425; matching names but not matching textures
248 case 408: return 355;
249 case 378: return 356; // lava02
250 case 383: return 357;//384; matching names but not matching textures
251 case 384: return 358;//385; matching names but not matching textures
252 case 385: return 359;//386; matching names but not matching textures
253 case 386: return 360;
254 case 387: return 361;
255 case 194: return 362; // mntr04b (devil: -1)
256 case 388: return 363;
257 case 391: return 364;
258 case 392: return 365;
259 case 393: return 366;
260 case 394: return 367;
261 case 395: return 368;
262 case 396: return 369;
263 case 195: return 370; // mntr04d (devil: -1)
264 // range 371..584 handled by default case (wall01 and door frames)
265 default:
266 // ranges:
267 if (tmap_num >= 13 && tmap_num <= 21)
268 return tmap_num + 16;
269 if (tmap_num >= 34 && tmap_num <= 63)
270 return tmap_num + 24;
271 if (tmap_num >= 64 && tmap_num <= 106)
272 return tmap_num + 25;
273 if (tmap_num >= 107 && tmap_num <= 114)
274 return tmap_num + 26;
275 if (tmap_num >= 209 && tmap_num <= 217)
276 return tmap_num - 33;
277 if (tmap_num >= 218 && tmap_num <= 224)
278 return tmap_num - 32;
279 if (tmap_num >= 228 && tmap_num <= 234)
280 return tmap_num - 25;
281 if (tmap_num >= 235 && tmap_num <= 242)
282 return tmap_num - 23;
283 if (tmap_num >= 249 && tmap_num <= 257)
284 return tmap_num - 18;
285 if (tmap_num >= 277 && tmap_num <= 291)
286 return tmap_num - 10;
287 if (tmap_num >= 300 && tmap_num <= 310)
288 return tmap_num - 13;
289 if (tmap_num >= 311 && tmap_num <= 326)
290 return tmap_num - 12;
291 if (tmap_num >= 327 && tmap_num <= 337)
292 return tmap_num - 11; // matching names but not matching textures
293 // wall01 and door frames:
294 if (tmap_num > 434 && tmap_num < 731)
295 {
296 if (New_file_format_save) return tmap_num - 64;
297 // d1 shareware needs special treatment:
298 if (tmap_num < 478) return tmap_num - 68;
299 if (tmap_num < 490) return tmap_num - 73;
300 if (tmap_num < 537) return tmap_num - 91;
301 if (tmap_num < 557) return tmap_num - 104;
302 if (tmap_num < 573) return tmap_num - 111;
303 if (tmap_num < 603) return tmap_num - 117;
304 if (tmap_num < 635) return tmap_num - 141;
305 if (tmap_num < 731) return tmap_num - 147;
306 }
307 Warning("can't convert unknown texture #%hu to descent 1.\n", tmap_num);
308 return tmap_num;
309 }
310 }
311
312 }
313
314 }
315 #endif
316
317 namespace {
318
319 static int save_mine_data(PHYSFS_File * SaveFile);
320
321 // -----------------------------------------------------------------------------
322 // Save mine will:
323 // 1. Write file info, header info, editor info, vertex data, segment data,
324 // and new_segment in that order, marking their file offset.
325 // 2. Go through all the fields and fill in the offset, size, and sizeof
326 // values in the headers.
327
med_save_mine(const char * filename)328 static int med_save_mine(const char * filename)
329 {
330 char ErrorMessage[256];
331
332 auto &&[SaveFile, physfserr] = PHYSFSX_openWriteBuffered(filename);
333 if (!SaveFile)
334 {
335 snprintf(ErrorMessage, sizeof(ErrorMessage), "ERROR: Unable to open %.200s: %s\n", filename, PHYSFS_getErrorByCode(physfserr));
336 ui_messagebox( -2, -2, 1, ErrorMessage, "Ok" );
337 return 1;
338 }
339
340 save_mine_data(SaveFile);
341
342 //==================== CLOSE THE FILE =============================
343 return 0;
344 }
345
346 // -----------------------------------------------------------------------------
347 // saves to an already-open file
save_mine_data(PHYSFS_File * SaveFile)348 static int save_mine_data(PHYSFS_File * SaveFile)
349 {
350 auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
351 int header_offset, editor_offset, vertex_offset, segment_offset, texture_offset, walls_offset, triggers_offset; //, links_offset;
352 int newseg_verts_offset;
353 int newsegment_offset;
354 med_compress_mine();
355 warn_if_concave_segments();
356
357 std::array<d_fname, MAX_TEXTURES> current_tmap_list;
358 auto &TmapInfo = LevelUniqueTmapInfoState.TmapInfo;
359 for (int i=0;i<NumTextures;i++)
360 current_tmap_list[i] = TmapInfo[i].filename;
361
362 //=================== Calculate offsets into file ==================
363
364 header_offset = PHYSFS_tell(SaveFile) + sizeof(mine_fileinfo);
365 editor_offset = header_offset + sizeof(mine_header);
366 texture_offset = editor_offset + sizeof(mine_editor);
367 vertex_offset = texture_offset + (13*NumTextures);
368 segment_offset = vertex_offset + (sizeof(vms_vector) * LevelSharedVertexState.Num_vertices);
369 newsegment_offset = segment_offset + (sizeof(segment) * LevelSharedSegmentState.Num_segments);
370 newseg_verts_offset = newsegment_offset + sizeof(segment);
371 walls_offset = newseg_verts_offset + (sizeof(vms_vector)*8);
372 auto &Triggers = LevelUniqueWallSubsystemState.Triggers;
373 auto &Walls = LevelUniqueWallSubsystemState.Walls;
374 triggers_offset = walls_offset + (sizeof(wall)*Walls.get_count());
375
376 //===================== SAVE FILE INFO ========================
377
378 mine_fileinfo.fileinfo_signature= 0x2884;
379 mine_fileinfo.fileinfo_version = MINE_VERSION;
380 mine_fileinfo.fileinfo_sizeof = sizeof(mine_fileinfo);
381 mine_fileinfo.header_offset = header_offset;
382 mine_fileinfo.header_size = sizeof(mine_header);
383 mine_fileinfo.editor_offset = editor_offset;
384 mine_fileinfo.editor_size = sizeof(mine_editor);
385 mine_fileinfo.vertex_offset = vertex_offset;
386 mine_fileinfo.vertex_howmany = LevelSharedVertexState.Num_vertices;
387 mine_fileinfo.vertex_sizeof = sizeof(vms_vector);
388 mine_fileinfo.segment_offset = segment_offset;
389 mine_fileinfo.segment_howmany = LevelSharedSegmentState.Num_segments;
390 mine_fileinfo.segment_sizeof = sizeof(segment);
391 mine_fileinfo.newseg_verts_offset = newseg_verts_offset;
392 mine_fileinfo.newseg_verts_howmany = 8;
393 mine_fileinfo.newseg_verts_sizeof = sizeof(vms_vector);
394 mine_fileinfo.texture_offset = texture_offset;
395 mine_fileinfo.texture_howmany = NumTextures;
396 mine_fileinfo.texture_sizeof = 13; // num characters in a name
397 mine_fileinfo.walls_offset = walls_offset;
398 mine_fileinfo.walls_howmany = Walls.get_count();
399 mine_fileinfo.walls_sizeof = sizeof(wall);
400 mine_fileinfo.triggers_offset = triggers_offset;
401 mine_fileinfo.triggers_howmany = Triggers.get_count();
402 mine_fileinfo.triggers_sizeof = sizeof(trigger);
403
404 // Write the fileinfo
405 PHYSFS_write( SaveFile, &mine_fileinfo, sizeof(mine_fileinfo), 1 );
406
407 //===================== SAVE HEADER INFO ========================
408
409 mine_header.num_vertices = LevelSharedVertexState.Num_vertices;
410 mine_header.num_segments = LevelSharedSegmentState.Num_segments;
411
412 // Write the editor info
413 if (header_offset != PHYSFS_tell(SaveFile))
414 Error( "OFFSETS WRONG IN MINE.C!" );
415
416 PHYSFS_write( SaveFile, &mine_header, sizeof(mine_header), 1 );
417
418 //===================== SAVE EDITOR INFO ==========================
419 mine_editor.current_seg = Cursegp;
420 mine_editor.newsegment_offset = newsegment_offset;
421 mine_editor.newsegment_size = sizeof(segment);
422
423 // Next 3 vars added 10/07 by JAS
424 mine_editor.Curside = Curside;
425 if (Markedsegp)
426 mine_editor.Markedsegp = Markedsegp;
427 else
428 mine_editor.Markedsegp = -1;
429 mine_editor.Markedside = Markedside;
430 range_for (const int i, xrange(10u))
431 mine_editor.Groupsegp[i] = vmsegptridx(Groupsegp[i]);
432 range_for (const int i, xrange(10u))
433 mine_editor.Groupside[i] = Groupside[i];
434
435 if (editor_offset != PHYSFS_tell(SaveFile))
436 Error( "OFFSETS WRONG IN MINE.C!" );
437 PHYSFS_write( SaveFile, &mine_editor, sizeof(mine_editor), 1 );
438
439 //===================== SAVE TEXTURE INFO ==========================
440
441 if (texture_offset != PHYSFS_tell(SaveFile))
442 Error( "OFFSETS WRONG IN MINE.C!" );
443 range_for (auto &i, partial_const_range(current_tmap_list, NumTextures))
444 PHYSFS_write(SaveFile, i.data(), i.size(), 1);
445
446 //===================== SAVE VERTEX INFO ==========================
447
448 if (vertex_offset != PHYSFS_tell(SaveFile))
449 Error( "OFFSETS WRONG IN MINE.C!" );
450 auto &Vertices = LevelSharedVertexState.get_vertices();
451 PHYSFS_write(SaveFile, Vertices, sizeof(vms_vector), LevelSharedVertexState.Num_vertices);
452
453 //===================== SAVE SEGMENT INFO =========================
454
455 if (segment_offset != PHYSFS_tell(SaveFile))
456 Error( "OFFSETS WRONG IN MINE.C!" );
457 Error("Sorry, v20 segment support is broken.");
458 #if 0
459 PHYSFS_write(SaveFile, &Segments.front(), sizeof(segment), LevelSharedSegmentState.Num_segments);
460
461 //===================== SAVE NEWSEGMENT INFO ======================
462
463 if (newsegment_offset != PHYSFS_tell(SaveFile))
464 Error( "OFFSETS WRONG IN MINE.C!" );
465 PHYSFS_write( SaveFile, &New_segment, sizeof(segment), 1 );
466 #endif
467
468 if (newseg_verts_offset != PHYSFS_tell(SaveFile))
469 Error( "OFFSETS WRONG IN MINE.C!" );
470 PHYSFS_write( SaveFile, &Vertices[New_segment.verts[0]], sizeof(vms_vector), 8 );
471
472 //==================== CLOSE THE FILE =============================
473
474 return 0;
475
476 }
477
478
479
480 #define COMPILED_MINE_VERSION 0
481
dump_fix_as_short(fix value,int nbits,PHYSFS_File * SaveFile)482 static void dump_fix_as_short( fix value, int nbits, PHYSFS_File *SaveFile )
483 {
484 short short_value;
485
486 auto int_value = value >> nbits;
487 if (int_value > INT16_MAX)
488 {
489 short_value = INT16_MAX;
490 }
491 else if( int_value < -0x7fff ) {
492 short_value = -0x7fff;
493 }
494 else
495 short_value = static_cast<short>(int_value);
496
497 PHYSFS_writeSLE16(SaveFile, short_value);
498 }
499
500 //version of dump for unsigned values
dump_fix_as_ushort(fix value,int nbits,PHYSFS_File * SaveFile)501 static void dump_fix_as_ushort( fix value, int nbits, PHYSFS_File *SaveFile )
502 {
503 uint int_value=0;
504 ushort short_value;
505
506 if (value < 0) {
507 Int3(); //hey---show this to Matt
508 value = 0;
509 }
510 else
511 int_value = value >> nbits;
512
513 if( int_value > 0xffff ) {
514 short_value = 0xffff;
515 }
516 else
517 short_value = int_value;
518
519 PHYSFS_writeULE16(SaveFile, short_value);
520 }
521
write_children(const shared_segment & seg,const unsigned bit_mask,PHYSFS_File * const SaveFile)522 static void write_children(const shared_segment &seg, const unsigned bit_mask, PHYSFS_File *const SaveFile)
523 {
524 auto &children = seg.children;
525 for (int bit = 0; bit < MAX_SIDES_PER_SEGMENT; bit++)
526 {
527 if (bit_mask & (1 << bit))
528 PHYSFS_writeSLE16(SaveFile, children[bit]);
529 }
530 }
531
write_verts(const shared_segment & seg,PHYSFS_File * const SaveFile)532 static void write_verts(const shared_segment &seg, PHYSFS_File *const SaveFile)
533 {
534 range_for (auto &i, seg.verts)
535 PHYSFS_writeSLE16(SaveFile, static_cast<uint16_t>(i));
536 }
537
write_special(const shared_segment & seg,const unsigned bit_mask,PHYSFS_File * const SaveFile)538 static void write_special(const shared_segment &seg, const unsigned bit_mask, PHYSFS_File *const SaveFile)
539 {
540 if (bit_mask & (1 << MAX_SIDES_PER_SEGMENT))
541 {
542 PHYSFSX_writeU8(SaveFile, underlying_value(seg.special));
543 PHYSFSX_writeU8(SaveFile, underlying_value(seg.matcen_num));
544 PHYSFS_writeULE16(SaveFile, underlying_value(seg.station_idx));
545 }
546 }
547
548 }
549
med_save_mine(const mine_filename_type & filename)550 int med_save_mine(const mine_filename_type &filename)
551 {
552 return med_save_mine(filename.data());
553 }
554
555 // -----------------------------------------------------------------------------
556 // saves compiled mine data to an already-open file...
557 namespace dsx {
save_mine_data_compiled(PHYSFS_File * SaveFile)558 int save_mine_data_compiled(PHYSFS_File *SaveFile)
559 {
560 auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
561 ubyte version = COMPILED_MINE_VERSION;
562 ubyte bit_mask = 0;
563
564 med_compress_mine();
565 warn_if_concave_segments();
566
567 if (Highest_segment_index >= MAX_SEGMENTS) {
568 char message[128];
569 snprintf(message, sizeof(message), "Error: Too many segments (%i > %" PRIuFAST32 ") for game (not editor)", Highest_segment_index+1, static_cast<uint_fast32_t>(MAX_SEGMENTS));
570 ui_messagebox( -2, -2, 1, message, "Ok" );
571 }
572
573 auto &Vertices = LevelSharedVertexState.get_vertices();
574 if (Vertices.get_count() > MAX_VERTICES)
575 {
576 char message[128];
577 snprintf(message, sizeof(message), "Error: Too many vertices (%i > %" PRIuFAST32 ") for game (not editor)", Vertices.get_count(), static_cast<uint_fast32_t>(MAX_VERTICES));
578 ui_messagebox( -2, -2, 1, message, "Ok" );
579 }
580
581 //=============================== Writing part ==============================
582 PHYSFSX_writeU8(SaveFile, version); // 1 byte = compiled version
583 if (New_file_format_save)
584 {
585 PHYSFS_writeSLE16(SaveFile, LevelSharedVertexState.Num_vertices); // 2 bytes = Num_vertices
586 PHYSFS_writeSLE16(SaveFile, LevelSharedSegmentState.Num_segments); // 2 bytes = Num_segments
587 }
588 else
589 {
590 PHYSFS_writeSLE32(SaveFile, LevelSharedVertexState.Num_vertices); // 4 bytes = Num_vertices
591 PHYSFS_writeSLE32(SaveFile, LevelSharedSegmentState.Num_segments); // 4 bytes = Num_segments
592 }
593
594 range_for (auto &i, partial_const_range(Vertices, LevelSharedVertexState.Num_vertices))
595 PHYSFSX_writeVector(SaveFile, i);
596
597 const auto Num_segments = LevelSharedSegmentState.Num_segments;
598 for (segnum_t segnum = 0; segnum < Num_segments; segnum++)
599 {
600 const cscusegment &&seg = vcsegptr(segnum);
601 for (short sidenum = 0; sidenum < MAX_SIDES_PER_SEGMENT; sidenum++)
602 {
603 if (seg.s.children[sidenum] != segment_none)
604 bit_mask |= (1 << sidenum);
605 }
606
607 if (seg.s.special != segment_special::nothing || seg.s.matcen_num != materialization_center_number::None || seg.s.station_idx != station_number::None)
608 bit_mask |= (1 << MAX_SIDES_PER_SEGMENT);
609
610 if (New_file_format_save)
611 PHYSFSX_writeU8(SaveFile, bit_mask);
612 else
613 bit_mask = 0x7F;
614
615 if (Gamesave_current_version == 5) // d2 SHAREWARE level
616 {
617 write_special(seg, bit_mask, SaveFile);
618 write_verts(seg, SaveFile);
619 write_children(seg, bit_mask, SaveFile);
620 }
621 else
622 {
623 write_children(seg, bit_mask, SaveFile);
624 write_verts(seg, SaveFile);
625 if (Gamesave_current_version <= 1) // descent 1 level
626 write_special(seg, bit_mask, SaveFile);
627 }
628
629 if (Gamesave_current_version <= 5) // descent 1 thru d2 SHAREWARE level
630 dump_fix_as_ushort(seg.u.static_light, 4, SaveFile);
631
632 // Write the walls as a 6 byte array
633 bit_mask = 0;
634 for (short sidenum = 0; sidenum < MAX_SIDES_PER_SEGMENT; sidenum++)
635 {
636 if (seg.s.sides[sidenum].wall_num != wall_none)
637 {
638 bit_mask |= (1 << sidenum);
639 }
640 }
641 if (New_file_format_save)
642 PHYSFSX_writeU8(SaveFile, bit_mask);
643 else
644 bit_mask = 0x3F;
645
646 for (short sidenum = 0; sidenum < MAX_SIDES_PER_SEGMENT; sidenum++)
647 {
648 if (bit_mask & (1 << sidenum))
649 PHYSFSX_writeU8(SaveFile, underlying_value(seg.s.sides[sidenum].wall_num));
650 }
651
652 for (short sidenum = 0; sidenum < MAX_SIDES_PER_SEGMENT; sidenum++)
653 {
654 if (seg.s.children[sidenum] == segment_none || seg.s.sides[sidenum].wall_num != wall_none)
655 {
656 auto tmap_num = seg.u.sides[sidenum].tmap_num;
657 auto tmap_num2 = seg.u.sides[sidenum].tmap_num2;
658
659 #if defined(DXX_BUILD_DESCENT_II)
660 if (Gamesave_current_version <= 3) // convert texture numbers back to d1
661 {
662 tmap_num = build_texture1_value(convert_to_d1_tmap_num(get_texture_index(tmap_num)));
663 if (tmap_num2 != texture2_value::None)
664 {
665 tmap_num2 = build_texture2_value(convert_to_d1_tmap_num(get_texture_index(tmap_num2)), get_texture_rotation_high(tmap_num2));
666 }
667 }
668 #endif
669
670 uint16_t write_tmap_num = static_cast<uint16_t>(tmap_num);
671 if (tmap_num2 != texture2_value::None && New_file_format_save)
672 write_tmap_num |= 0x8000;
673
674 PHYSFS_writeSLE16(SaveFile, write_tmap_num);
675 if (tmap_num2 != texture2_value::None || !New_file_format_save)
676 PHYSFS_writeSLE16(SaveFile, static_cast<uint16_t>(tmap_num2));
677
678 range_for (auto &i, seg.u.sides[sidenum].uvls)
679 {
680 dump_fix_as_short(i.u, 5, SaveFile);
681 dump_fix_as_short(i.v, 5, SaveFile);
682 dump_fix_as_ushort(i.l, 1, SaveFile);
683 }
684 }
685 }
686
687 }
688
689 #if defined(DXX_BUILD_DESCENT_II)
690 if (Gamesave_current_version > 5)
691 for (segnum_t i = 0; i < Num_segments; i++)
692 segment2_write(vcsegptr(i), SaveFile);
693 #endif
694
695 return 0;
696 }
697 }
698
699
700