dxx-rebirth/similar/editor/mine.cpp
Kp 6ab3fb262b Add convenience function underlying_value
Add a helper to deduce the enum type of a value, and use an appropriate
std::underlying_type<T> expression for that enum type.  This avoids the
need to repeat the type of the enum at the site of each cast, and moves
the casts into the helper to make the callers easier to read.
2021-09-04 12:17:14 +00:00

700 lines
23 KiB
C++

/*
* Portions of this file are copyright Rebirth contributors and licensed as
* described in COPYING.txt.
* Portions of this file are copyright Parallax Software and licensed
* according to the Parallax license below.
* See COPYING.txt for license details.
THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
SOFTWARE CORPORATION ("PARALLAX"). PARALLAX, IN DISTRIBUTING THE CODE TO
END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
IN USING, DISPLAYING, AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
FREE PURPOSES. IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES. THE END-USER UNDERSTANDS
AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
COPYRIGHT 1993-1998 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED.
*/
/*
*
* Mine specific editing functions, such as load_mine, save_mine
*
*/
#include <cinttypes>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include "gr.h"
#include "bm.h" // for MAX_TEXTURES
#include "inferno.h"
#include "segment.h"
#include "editor.h"
#include "editor/esegment.h"
#include "wall.h"
#include "dxxerror.h"
#include "textures.h"
#include "physfsx.h"
#include "gamemine.h"
#include "gamesave.h"
#include "switch.h"
#include "fuelcen.h"
#include "compiler-range_for.h"
#include "d_range.h"
#include "partial_range.h"
#include "d_underlying_value.h"
#define REMOVE_EXT(s) (*(strchr( (s), '.' ))='\0')
int New_file_format_save = 1;
#if defined(DXX_BUILD_DESCENT_II)
namespace dsx {
namespace {
// Converts descent 2 texture numbers back to descent 1 texture numbers.
// Only works properly when the full Descent 1 texture set (descent.pig) is available.
static texture_index convert_to_d1_tmap_num(const texture_index tmap_num)
{
switch (tmap_num)
{
case 137: return 0; // grey rock001
case 0: return 1;
case 1: return 3; // rock021
case 270: return 6; // blue rock002
case 271: return 7; // yellow rock265
case 2: return 8; // rock004
case 138: return 9; // purple (devil:179)
case 272: return 10; // red rock006
case 139: return 11;
case 140: return 12; //devil:43
case 3: return 13; // rock014
case 4: return 14; // rock019
case 5: return 15; // rock020
case 6: return 16;
case 141: return 17;
case 129: return 18;
case 7: return 19;
case 142: return 20;
case 143: return 21;
case 8: return 22;
case 9: return 23;
case 10: return 24;
case 144: return 25; //devil:35
case 11: return 26;
case 12: return 27;
case 145: return 28; //devil:43
//range handled by default case, returns 13..21 (- 16)
case 163: return 38; //devil:27
case 147: return 39; //31
case 22: return 40;
case 266: return 41;
case 23: return 42;
case 24: return 43;
case 136: return 44; //devil:135
case 25: return 45;
case 26: return 46;
case 27: return 47;
case 28: return 48;
case 146: return 49; //devil:60
case 131: return 50; //devil:138
case 29: return 51;
case 30: return 52;
case 31: return 53;
case 32: return 54;
case 165: return 55; //devil:193
case 33: return 56;
case 132: return 57; //devil:119
// range handled by default case, returns 58..88 (+ 24)
case 197: return 88; //devil:15
// range handled by default case, returns 89..106 (- 25)
case 167: return 132;
// range handled by default case, returns 107..114 (- 26)
case 148: return 141; //devil:106
case 115: return 142;
case 116: return 143;
case 117: return 144;
case 118: return 145;
case 119: return 146;
case 149: return 147;
case 120: return 148;
case 121: return 149;
case 122: return 150;
case 123: return 151;
case 124: return 152;
case 125: return 153; // rock263
case 150: return 154;
case 126: return 155; // rock269
case 200: return 156; // metl002
case 201: return 157; // metl003
case 186: return 158; //devil:227
case 190: return 159; //devil:246
case 151: return 160;
case 152: return 161; //devil:206
case 202: return 162;
case 203: return 163;
case 204: return 164;
case 205: return 165;
case 206: return 166;
case 153: return 167;
case 154: return 168;
case 155: return 169;
case 156: return 170;//206;
case 157: return 171;//227;
case 207: return 172;
case 208: return 173;
case 158: return 174;
case 159: return 175;
// range handled by default case, returns 209..217 (+ 33)
case 160: return 185;
// range handled by default case, returns 218..224 (+ 32)
case 161: return 193;
case 162: return 194;//206;
case 166: return 195;
case 225: return 196;
case 226: return 197;
case 193: return 198;
case 168: return 199; //devil:204
case 169: return 200; //devil:204
case 227: return 201;
case 170: return 202; //devil:227
// range handled by default case, returns 228..234 (+ 25)
case 171: return 210; //devil:242
case 172: return 211; //devil:240
// range handled by default case, returns 235..242 (+ 23)
case 173: return 220; //devil:240
case 243: return 221;
case 244: return 222;
case 174: return 223;
case 245: return 224;
case 246: return 225;
case 164: return 226;//247; matching names but not matching textures
case 179: return 227; //devil:181
case 196: return 228;//248; matching names but not matching textures
case 175: return 229; //devil:66
case 176: return 230; //devil:66
// range handled by default case, returns 249..257 (+ 18)
case 177: return 240; //devil:132
case 130: return 241; //devil:131
case 178: return 242; //devil:15
case 180: return 243; //devil:38
case 258: return 244;
case 259: return 245;
case 181: return 246; // grate metl127
case 260: return 247;
case 261: return 248;
case 262: return 249;
case 340: return 250; // white doorframe metl126
case 412: return 251; // red doorframe metl133
case 410: return 252; // blue doorframe metl134
case 411: return 253; // yellow doorframe metl135
case 263: return 254; // metl136
case 264: return 255; // metl139
case 265: return 256; // metl140
case 182: return 257;//246; brig001
case 183: return 258;//246; brig002
case 184: return 259;//246; brig003
case 185: return 260;//246; brig004
case 273: return 261; // exit01
case 274: return 262; // exit02
case 187: return 263; // ceil001
case 275: return 264; // ceil002
case 276: return 265; // ceil003
case 188: return 266; //devil:291
// range handled by default case, returns 277..291 (+ 10)
case 293: return 282;
case 189: return 283;
case 295: return 284;
case 296: return 285;
case 298: return 286;
// range handled by default case, returns 300..310 (+ 13)
case 191: return 298; // devil:374 misc010
// range handled by default case, returns 311..326 (+ 12)
case 192: return 315; // bad producer misc044
// range handled by default case, returns 327..337 (+ 11)
case 352: return 327; // arw01
case 353: return 328; // misc17
case 354: return 329; // fan01
case 380: return 330; // mntr04
case 379: return 331;//373; matching names but not matching textures
case 355: return 332;//344; matching names but not matching textures
case 409: return 333; // lava misc11 //devil:404
case 356: return 334; // ctrl04
case 357: return 335; // ctrl01
case 358: return 336; // ctrl02
case 359: return 337; // ctrl03
case 360: return 338; // misc14
case 361: return 339; // producer misc16
case 362: return 340; // misc049
case 364: return 341; // misc060
case 363: return 342; // blown01
case 366: return 343; // misc061
case 365: return 344;
case 368: return 345;
case 376: return 346;
case 370: return 347;
case 367: return 348;
case 372: return 349;
case 369: return 350;
case 374: return 351;//429; matching names but not matching textures
case 375: return 352;//387; matching names but not matching textures
case 371: return 353;
case 377: return 354;//425; matching names but not matching textures
case 408: return 355;
case 378: return 356; // lava02
case 383: return 357;//384; matching names but not matching textures
case 384: return 358;//385; matching names but not matching textures
case 385: return 359;//386; matching names but not matching textures
case 386: return 360;
case 387: return 361;
case 194: return 362; // mntr04b (devil: -1)
case 388: return 363;
case 391: return 364;
case 392: return 365;
case 393: return 366;
case 394: return 367;
case 395: return 368;
case 396: return 369;
case 195: return 370; // mntr04d (devil: -1)
// range 371..584 handled by default case (wall01 and door frames)
default:
// ranges:
if (tmap_num >= 13 && tmap_num <= 21)
return tmap_num + 16;
if (tmap_num >= 34 && tmap_num <= 63)
return tmap_num + 24;
if (tmap_num >= 64 && tmap_num <= 106)
return tmap_num + 25;
if (tmap_num >= 107 && tmap_num <= 114)
return tmap_num + 26;
if (tmap_num >= 209 && tmap_num <= 217)
return tmap_num - 33;
if (tmap_num >= 218 && tmap_num <= 224)
return tmap_num - 32;
if (tmap_num >= 228 && tmap_num <= 234)
return tmap_num - 25;
if (tmap_num >= 235 && tmap_num <= 242)
return tmap_num - 23;
if (tmap_num >= 249 && tmap_num <= 257)
return tmap_num - 18;
if (tmap_num >= 277 && tmap_num <= 291)
return tmap_num - 10;
if (tmap_num >= 300 && tmap_num <= 310)
return tmap_num - 13;
if (tmap_num >= 311 && tmap_num <= 326)
return tmap_num - 12;
if (tmap_num >= 327 && tmap_num <= 337)
return tmap_num - 11; // matching names but not matching textures
// wall01 and door frames:
if (tmap_num > 434 && tmap_num < 731)
{
if (New_file_format_save) return tmap_num - 64;
// d1 shareware needs special treatment:
if (tmap_num < 478) return tmap_num - 68;
if (tmap_num < 490) return tmap_num - 73;
if (tmap_num < 537) return tmap_num - 91;
if (tmap_num < 557) return tmap_num - 104;
if (tmap_num < 573) return tmap_num - 111;
if (tmap_num < 603) return tmap_num - 117;
if (tmap_num < 635) return tmap_num - 141;
if (tmap_num < 731) return tmap_num - 147;
}
Warning("can't convert unknown texture #%hu to descent 1.\n", tmap_num);
return tmap_num;
}
}
}
}
#endif
namespace {
static int save_mine_data(PHYSFS_File * SaveFile);
// -----------------------------------------------------------------------------
// Save mine will:
// 1. Write file info, header info, editor info, vertex data, segment data,
// and new_segment in that order, marking their file offset.
// 2. Go through all the fields and fill in the offset, size, and sizeof
// values in the headers.
static int med_save_mine(const char * filename)
{
char ErrorMessage[256];
auto &&[SaveFile, physfserr] = PHYSFSX_openWriteBuffered(filename);
if (!SaveFile)
{
snprintf(ErrorMessage, sizeof(ErrorMessage), "ERROR: Unable to open %.200s: %s\n", filename, PHYSFS_getErrorByCode(physfserr));
ui_messagebox( -2, -2, 1, ErrorMessage, "Ok" );
return 1;
}
save_mine_data(SaveFile);
//==================== CLOSE THE FILE =============================
return 0;
}
// -----------------------------------------------------------------------------
// saves to an already-open file
static int save_mine_data(PHYSFS_File * SaveFile)
{
auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
int header_offset, editor_offset, vertex_offset, segment_offset, texture_offset, walls_offset, triggers_offset; //, links_offset;
int newseg_verts_offset;
int newsegment_offset;
med_compress_mine();
warn_if_concave_segments();
std::array<d_fname, MAX_TEXTURES> current_tmap_list;
auto &TmapInfo = LevelUniqueTmapInfoState.TmapInfo;
for (int i=0;i<NumTextures;i++)
current_tmap_list[i] = TmapInfo[i].filename;
//=================== Calculate offsets into file ==================
header_offset = PHYSFS_tell(SaveFile) + sizeof(mine_fileinfo);
editor_offset = header_offset + sizeof(mine_header);
texture_offset = editor_offset + sizeof(mine_editor);
vertex_offset = texture_offset + (13*NumTextures);
segment_offset = vertex_offset + (sizeof(vms_vector) * LevelSharedVertexState.Num_vertices);
newsegment_offset = segment_offset + (sizeof(segment) * LevelSharedSegmentState.Num_segments);
newseg_verts_offset = newsegment_offset + sizeof(segment);
walls_offset = newseg_verts_offset + (sizeof(vms_vector)*8);
auto &Triggers = LevelUniqueWallSubsystemState.Triggers;
auto &Walls = LevelUniqueWallSubsystemState.Walls;
triggers_offset = walls_offset + (sizeof(wall)*Walls.get_count());
//===================== SAVE FILE INFO ========================
mine_fileinfo.fileinfo_signature= 0x2884;
mine_fileinfo.fileinfo_version = MINE_VERSION;
mine_fileinfo.fileinfo_sizeof = sizeof(mine_fileinfo);
mine_fileinfo.header_offset = header_offset;
mine_fileinfo.header_size = sizeof(mine_header);
mine_fileinfo.editor_offset = editor_offset;
mine_fileinfo.editor_size = sizeof(mine_editor);
mine_fileinfo.vertex_offset = vertex_offset;
mine_fileinfo.vertex_howmany = LevelSharedVertexState.Num_vertices;
mine_fileinfo.vertex_sizeof = sizeof(vms_vector);
mine_fileinfo.segment_offset = segment_offset;
mine_fileinfo.segment_howmany = LevelSharedSegmentState.Num_segments;
mine_fileinfo.segment_sizeof = sizeof(segment);
mine_fileinfo.newseg_verts_offset = newseg_verts_offset;
mine_fileinfo.newseg_verts_howmany = 8;
mine_fileinfo.newseg_verts_sizeof = sizeof(vms_vector);
mine_fileinfo.texture_offset = texture_offset;
mine_fileinfo.texture_howmany = NumTextures;
mine_fileinfo.texture_sizeof = 13; // num characters in a name
mine_fileinfo.walls_offset = walls_offset;
mine_fileinfo.walls_howmany = Walls.get_count();
mine_fileinfo.walls_sizeof = sizeof(wall);
mine_fileinfo.triggers_offset = triggers_offset;
mine_fileinfo.triggers_howmany = Triggers.get_count();
mine_fileinfo.triggers_sizeof = sizeof(trigger);
// Write the fileinfo
PHYSFS_write( SaveFile, &mine_fileinfo, sizeof(mine_fileinfo), 1 );
//===================== SAVE HEADER INFO ========================
mine_header.num_vertices = LevelSharedVertexState.Num_vertices;
mine_header.num_segments = LevelSharedSegmentState.Num_segments;
// Write the editor info
if (header_offset != PHYSFS_tell(SaveFile))
Error( "OFFSETS WRONG IN MINE.C!" );
PHYSFS_write( SaveFile, &mine_header, sizeof(mine_header), 1 );
//===================== SAVE EDITOR INFO ==========================
mine_editor.current_seg = Cursegp;
mine_editor.newsegment_offset = newsegment_offset;
mine_editor.newsegment_size = sizeof(segment);
// Next 3 vars added 10/07 by JAS
mine_editor.Curside = Curside;
if (Markedsegp)
mine_editor.Markedsegp = Markedsegp;
else
mine_editor.Markedsegp = -1;
mine_editor.Markedside = Markedside;
range_for (const int i, xrange(10u))
mine_editor.Groupsegp[i] = vmsegptridx(Groupsegp[i]);
range_for (const int i, xrange(10u))
mine_editor.Groupside[i] = Groupside[i];
if (editor_offset != PHYSFS_tell(SaveFile))
Error( "OFFSETS WRONG IN MINE.C!" );
PHYSFS_write( SaveFile, &mine_editor, sizeof(mine_editor), 1 );
//===================== SAVE TEXTURE INFO ==========================
if (texture_offset != PHYSFS_tell(SaveFile))
Error( "OFFSETS WRONG IN MINE.C!" );
range_for (auto &i, partial_const_range(current_tmap_list, NumTextures))
PHYSFS_write(SaveFile, i.data(), i.size(), 1);
//===================== SAVE VERTEX INFO ==========================
if (vertex_offset != PHYSFS_tell(SaveFile))
Error( "OFFSETS WRONG IN MINE.C!" );
auto &Vertices = LevelSharedVertexState.get_vertices();
PHYSFS_write(SaveFile, Vertices, sizeof(vms_vector), LevelSharedVertexState.Num_vertices);
//===================== SAVE SEGMENT INFO =========================
if (segment_offset != PHYSFS_tell(SaveFile))
Error( "OFFSETS WRONG IN MINE.C!" );
Error("Sorry, v20 segment support is broken.");
#if 0
PHYSFS_write(SaveFile, &Segments.front(), sizeof(segment), LevelSharedSegmentState.Num_segments);
//===================== SAVE NEWSEGMENT INFO ======================
if (newsegment_offset != PHYSFS_tell(SaveFile))
Error( "OFFSETS WRONG IN MINE.C!" );
PHYSFS_write( SaveFile, &New_segment, sizeof(segment), 1 );
#endif
if (newseg_verts_offset != PHYSFS_tell(SaveFile))
Error( "OFFSETS WRONG IN MINE.C!" );
PHYSFS_write( SaveFile, &Vertices[New_segment.verts[0]], sizeof(vms_vector), 8 );
//==================== CLOSE THE FILE =============================
return 0;
}
#define COMPILED_MINE_VERSION 0
static void dump_fix_as_short( fix value, int nbits, PHYSFS_File *SaveFile )
{
short short_value;
auto int_value = value >> nbits;
if (int_value > INT16_MAX)
{
short_value = INT16_MAX;
}
else if( int_value < -0x7fff ) {
short_value = -0x7fff;
}
else
short_value = static_cast<short>(int_value);
PHYSFS_writeSLE16(SaveFile, short_value);
}
//version of dump for unsigned values
static void dump_fix_as_ushort( fix value, int nbits, PHYSFS_File *SaveFile )
{
uint int_value=0;
ushort short_value;
if (value < 0) {
Int3(); //hey---show this to Matt
value = 0;
}
else
int_value = value >> nbits;
if( int_value > 0xffff ) {
short_value = 0xffff;
}
else
short_value = int_value;
PHYSFS_writeULE16(SaveFile, short_value);
}
static void write_children(const shared_segment &seg, const unsigned bit_mask, PHYSFS_File *const SaveFile)
{
auto &children = seg.children;
for (int bit = 0; bit < MAX_SIDES_PER_SEGMENT; bit++)
{
if (bit_mask & (1 << bit))
PHYSFS_writeSLE16(SaveFile, children[bit]);
}
}
static void write_verts(const shared_segment &seg, PHYSFS_File *const SaveFile)
{
range_for (auto &i, seg.verts)
PHYSFS_writeSLE16(SaveFile, static_cast<uint16_t>(i));
}
static void write_special(const shared_segment &seg, const unsigned bit_mask, PHYSFS_File *const SaveFile)
{
if (bit_mask & (1 << MAX_SIDES_PER_SEGMENT))
{
PHYSFSX_writeU8(SaveFile, seg.special);
PHYSFSX_writeU8(SaveFile, seg.matcen_num);
PHYSFS_writeULE16(SaveFile, seg.station_idx);
}
}
}
int med_save_mine(const mine_filename_type &filename)
{
return med_save_mine(filename.data());
}
// -----------------------------------------------------------------------------
// saves compiled mine data to an already-open file...
namespace dsx {
int save_mine_data_compiled(PHYSFS_File *SaveFile)
{
auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
ubyte version = COMPILED_MINE_VERSION;
ubyte bit_mask = 0;
med_compress_mine();
warn_if_concave_segments();
if (Highest_segment_index >= MAX_SEGMENTS) {
char message[128];
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));
ui_messagebox( -2, -2, 1, message, "Ok" );
}
auto &Vertices = LevelSharedVertexState.get_vertices();
if (Vertices.get_count() > MAX_VERTICES)
{
char message[128];
snprintf(message, sizeof(message), "Error: Too many vertices (%i > %" PRIuFAST32 ") for game (not editor)", Vertices.get_count(), static_cast<uint_fast32_t>(MAX_VERTICES));
ui_messagebox( -2, -2, 1, message, "Ok" );
}
//=============================== Writing part ==============================
PHYSFSX_writeU8(SaveFile, version); // 1 byte = compiled version
if (New_file_format_save)
{
PHYSFS_writeSLE16(SaveFile, LevelSharedVertexState.Num_vertices); // 2 bytes = Num_vertices
PHYSFS_writeSLE16(SaveFile, LevelSharedSegmentState.Num_segments); // 2 bytes = Num_segments
}
else
{
PHYSFS_writeSLE32(SaveFile, LevelSharedVertexState.Num_vertices); // 4 bytes = Num_vertices
PHYSFS_writeSLE32(SaveFile, LevelSharedSegmentState.Num_segments); // 4 bytes = Num_segments
}
range_for (auto &i, partial_const_range(Vertices, LevelSharedVertexState.Num_vertices))
PHYSFSX_writeVector(SaveFile, i);
const auto Num_segments = LevelSharedSegmentState.Num_segments;
for (segnum_t segnum = 0; segnum < Num_segments; segnum++)
{
const cscusegment &&seg = vcsegptr(segnum);
for (short sidenum = 0; sidenum < MAX_SIDES_PER_SEGMENT; sidenum++)
{
if (seg.s.children[sidenum] != segment_none)
bit_mask |= (1 << sidenum);
}
if (seg.s.special != 0 || seg.s.matcen_num != 0 || seg.s.station_idx != station_none)
bit_mask |= (1 << MAX_SIDES_PER_SEGMENT);
if (New_file_format_save)
PHYSFSX_writeU8(SaveFile, bit_mask);
else
bit_mask = 0x7F;
if (Gamesave_current_version == 5) // d2 SHAREWARE level
{
write_special(seg, bit_mask, SaveFile);
write_verts(seg, SaveFile);
write_children(seg, bit_mask, SaveFile);
}
else
{
write_children(seg, bit_mask, SaveFile);
write_verts(seg, SaveFile);
if (Gamesave_current_version <= 1) // descent 1 level
write_special(seg, bit_mask, SaveFile);
}
if (Gamesave_current_version <= 5) // descent 1 thru d2 SHAREWARE level
dump_fix_as_ushort(seg.u.static_light, 4, SaveFile);
// Write the walls as a 6 byte array
bit_mask = 0;
for (short sidenum = 0; sidenum < MAX_SIDES_PER_SEGMENT; sidenum++)
{
if (seg.s.sides[sidenum].wall_num != wall_none)
{
bit_mask |= (1 << sidenum);
}
}
if (New_file_format_save)
PHYSFSX_writeU8(SaveFile, bit_mask);
else
bit_mask = 0x3F;
for (short sidenum = 0; sidenum < MAX_SIDES_PER_SEGMENT; sidenum++)
{
if (bit_mask & (1 << sidenum))
PHYSFSX_writeU8(SaveFile, underlying_value(seg.s.sides[sidenum].wall_num));
}
for (short sidenum = 0; sidenum < MAX_SIDES_PER_SEGMENT; sidenum++)
{
if (seg.s.children[sidenum] == segment_none || seg.s.sides[sidenum].wall_num != wall_none)
{
auto tmap_num = seg.u.sides[sidenum].tmap_num;
auto tmap_num2 = seg.u.sides[sidenum].tmap_num2;
#if defined(DXX_BUILD_DESCENT_II)
if (Gamesave_current_version <= 3) // convert texture numbers back to d1
{
tmap_num = build_texture1_value(convert_to_d1_tmap_num(get_texture_index(tmap_num)));
if (tmap_num2 != texture2_value::None)
{
tmap_num2 = build_texture2_value(convert_to_d1_tmap_num(get_texture_index(tmap_num2)), get_texture_rotation_high(tmap_num2));
}
}
#endif
uint16_t write_tmap_num = static_cast<uint16_t>(tmap_num);
if (tmap_num2 != texture2_value::None && New_file_format_save)
write_tmap_num |= 0x8000;
PHYSFS_writeSLE16(SaveFile, write_tmap_num);
if (tmap_num2 != texture2_value::None || !New_file_format_save)
PHYSFS_writeSLE16(SaveFile, static_cast<uint16_t>(tmap_num2));
range_for (auto &i, seg.u.sides[sidenum].uvls)
{
dump_fix_as_short(i.u, 5, SaveFile);
dump_fix_as_short(i.v, 5, SaveFile);
dump_fix_as_ushort(i.l, 1, SaveFile);
}
}
}
}
#if defined(DXX_BUILD_DESCENT_II)
if (Gamesave_current_version > 5)
for (segnum_t i = 0; i < Num_segments; i++)
segment2_write(vcsegptr(i), SaveFile);
#endif
return 0;
}
}