Subversion Repositories Games.Descent

Rev

Blame | Last modification | View Log | Download | RSS feed

  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.  
  700.