Subversion Repositories Games.Descent

Rev

Details | Last modification | View Log | RSS feed

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