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