dxx-rebirth/similar/main/gamemine.cpp
Kp 6b3e78054c Remove support for reading Miner's .LVL files
The Miner LVL files are not present in retail Descent 2 data.  I know of
no way to obtain them.  The code for loading them has been marked as
broken since 2018, and no one reported it.  Remove this support to
simplify future work.
2020-12-20 20:39:07 +00:00

581 lines
20 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-1999 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED.
*/
/*
*
* Functions for loading mines in the game
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include "pstypes.h"
#include "inferno.h"
#include "segment.h"
#include "textures.h"
#include "wall.h"
#include "object.h"
#include "gamemine.h"
#include "dxxerror.h"
#include "gameseg.h"
#include "physfsx.h"
#include "switch.h"
#include "game.h"
#include "newmenu.h"
#if DXX_USE_EDITOR
#include "editor/editor.h"
#include "editor/esegment.h"
#include "d_range.h"
#endif
#include "fuelcen.h"
#include "hash.h"
#include "key.h"
#include "piggy.h"
#include "gamesave.h"
#include "compiler-poison.h"
#include "compiler-range_for.h"
#include "d_levelstate.h"
#include "partial_range.h"
#define REMOVE_EXT(s) (*(strchr( (s), '.' ))='\0')
int New_file_format_load = 1; // "new file format" is everything newer than d1 shareware
namespace dsx {
/*
* reads a segment2 structure from a PHYSFS_File
*/
static void segment2_read(const msmusegment s2, PHYSFS_File *fp)
{
s2.s.special = PHYSFSX_readByte(fp);
s2.s.matcen_num = PHYSFSX_readByte(fp);
/* station_idx is overwritten by the caller in some cases, but set
* it here for compatibility with how the game previously worked */
s2.s.station_idx = PHYSFSX_readByte(fp);
const auto s2_flags = PHYSFSX_readByte(fp);
#if defined(DXX_BUILD_DESCENT_I)
(void)s2_flags; // descent 2 ambient sound handling
if (s2.s.special >= MAX_CENTER_TYPES)
s2.s.special = SEGMENT_IS_NOTHING; // remove goals etc.
#elif defined(DXX_BUILD_DESCENT_II)
s2.s.s2_flags = s2_flags;
#endif
s2.u.static_light = PHYSFSX_readFix(fp);
}
#if defined(DXX_BUILD_DESCENT_I)
#elif defined(DXX_BUILD_DESCENT_II)
int d1_pig_present = 0; // can descent.pig from descent 1 be loaded?
/* Converts descent 1 texture numbers to descent 2 texture numbers.
* Textures from d1 which are unique to d1 have extra spaces around "return".
* If we can load the original d1 pig, we make sure this function is bijective.
* This function was updated using the file config/convtabl.ini from devil 2.2.
*/
uint16_t convert_d1_tmap_num(const uint16_t d1_tmap_num)
{
switch (d1_tmap_num) {
case 0: case 2: case 4: case 5:
// all refer to grey rock001 (exception to bijectivity rule)
return d1_pig_present ? 137 : 43; // (devil:95)
case 1: return 0;
case 3: return 1; // rock021
case 6: return 270; // blue rock002
case 7: return 271; // yellow rock265
case 8: return 2; // rock004
case 9: return d1_pig_present ? 138 : 62; // purple (devil:179)
case 10: return 272; // red rock006
case 11: return d1_pig_present ? 139 : 117;
case 12: return d1_pig_present ? 140 : 12; //devil:43
case 13: return 3; // rock014
case 14: return 4; // rock019
case 15: return 5; // rock020
case 16: return 6;
case 17: return d1_pig_present ? 141 : 52;
case 18: return 129;
case 19: return 7;
case 20: return d1_pig_present ? 142 : 22;
case 21: return d1_pig_present ? 143 : 9;
case 22: return 8;
case 23: return 9;
case 24: return 10;
case 25: return d1_pig_present ? 144 : 12; //devil:35
case 26: return 11;
case 27: return 12;
case 28: return d1_pig_present ? 145 : 11; //devil:43
//range handled by default case, returns 13..21 (- 16)
case 38: return 163; //devil:27
case 39: return 147; //31
case 40: return 22;
case 41: return 266;
case 42: return 23;
case 43: return 24;
case 44: return 136; //devil:135
case 45: return 25;
case 46: return 26;
case 47: return 27;
case 48: return 28;
case 49: return d1_pig_present ? 146 : 43; //devil:60
case 50: return 131; //devil:138
case 51: return 29;
case 52: return 30;
case 53: return 31;
case 54: return 32;
case 55: return 165; //devil:193
case 56: return 33;
case 57: return 132; //devil:119
// range handled by default case, returns 34..63 (- 24)
case 88: return 197; //devil:15
// range handled by default case, returns 64..106 (- 25)
case 132: return 167;
// range handled by default case, returns 107..114 (- 26)
case 141: return d1_pig_present ? 148 : 110; //devil:106
case 142: return 115;
case 143: return 116;
case 144: return 117;
case 145: return 118;
case 146: return 119;
case 147: return d1_pig_present ? 149 : 93;
case 148: return 120;
case 149: return 121;
case 150: return 122;
case 151: return 123;
case 152: return 124;
case 153: return 125; // rock263
case 154: return d1_pig_present ? 150 : 27;
case 155: return 126; // rock269
case 156: return 200; // metl002
case 157: return 201; // metl003
case 158: return 186; //devil:227
case 159: return 190; //devil:246
case 160: return d1_pig_present ? 151 : 206;
case 161: return d1_pig_present ? 152 : 114; //devil:206
case 162: return 202;
case 163: return 203;
case 164: return 204;
case 165: return 205;
case 166: return 206;
case 167: return d1_pig_present ? 153 : 206;
case 168: return d1_pig_present ? 154 : 206;
case 169: return d1_pig_present ? 155 : 206;
case 170: return d1_pig_present ? 156 : 227;//206;
case 171: return d1_pig_present ? 157 : 206;//227;
case 172: return 207;
case 173: return 208;
case 174: return d1_pig_present ? 158 : 202;
case 175: return d1_pig_present ? 159 : 206;
// range handled by default case, returns 209..217 (+ 33)
case 185: return d1_pig_present ? 160 : 217;
// range handled by default case, returns 218..224 (+ 32)
case 193: return d1_pig_present ? 161 : 206;
case 194: return d1_pig_present ? 162 : 203;//206;
case 195: return d1_pig_present ? 166 : 234;
case 196: return 225;
case 197: return 226;
case 198: return d1_pig_present ? 193 : 225;
case 199: return d1_pig_present ? 168 : 206; //devil:204
case 200: return d1_pig_present ? 169 : 206; //devil:204
case 201: return 227;
case 202: return d1_pig_present ? 170 : 206; //devil:227
// range handled by default case, returns 228..234 (+ 25)
case 210: return d1_pig_present ? 171 : 234; //devil:242
case 211: return d1_pig_present ? 172 : 206; //devil:240
// range handled by default case, returns 235..242 (+ 23)
case 220: return d1_pig_present ? 173 : 242; //devil:240
case 221: return 243;
case 222: return 244;
case 223: return d1_pig_present ? 174 : 313;
case 224: return 245;
case 225: return 246;
case 226: return 164;//247; matching names but not matching textures
case 227: return 179; //devil:181
case 228: return 196;//248; matching names but not matching textures
case 229: return d1_pig_present ? 175 : 15; //devil:66
case 230: return d1_pig_present ? 176 : 15; //devil:66
// range handled by default case, returns 249..257 (+ 18)
case 240: return d1_pig_present ? 177 : 6; //devil:132
case 241: return 130; //devil:131
case 242: return d1_pig_present ? 178 : 78; //devil:15
case 243: return d1_pig_present ? 180 : 33; //devil:38
case 244: return 258;
case 245: return 259;
case 246: return d1_pig_present ? 181 : 321; // grate metl127
case 247: return 260;
case 248: return 261;
case 249: return 262;
case 250: return 340; // white doorframe metl126
case 251: return 412; // red doorframe metl133
case 252: return 410; // blue doorframe metl134
case 253: return 411; // yellow doorframe metl135
case 254: return 263; // metl136
case 255: return 264; // metl139
case 256: return 265; // metl140
case 257: return d1_pig_present ? 182 : 249;//246; brig001
case 258: return d1_pig_present ? 183 : 251;//246; brig002
case 259: return d1_pig_present ? 184 : 252;//246; brig003
case 260: return d1_pig_present ? 185 : 256;//246; brig004
case 261: return 273; // exit01
case 262: return 274; // exit02
case 263: return d1_pig_present ? 187 : 281; // ceil001
case 264: return 275; // ceil002
case 265: return 276; // ceil003
case 266: return d1_pig_present ? 188 : 279; //devil:291
// range handled by default case, returns 277..291 (+ 10)
case 282: return 293;
case 283: return d1_pig_present ? 189 : 295;
case 284: return 295;
case 285: return 296;
case 286: return 298;
// range handled by default case, returns 300..310 (+ 13)
case 298: return d1_pig_present ? 191 : 364; // devil:374 misc010
// range handled by default case, returns 311..326 (+ 12)
case 315: return d1_pig_present ? 192 : 361; // bad producer misc044
// range handled by default case, returns 327..337 (+ 11)
case 327: return 352; // arw01
case 328: return 353; // misc17
case 329: return 354; // fan01
case 330: return 380; // mntr04
case 331: return 379;//373; matching names but not matching textures
case 332: return 355;//344; matching names but not matching textures
case 333: return 409; // lava misc11 //devil:404
case 334: return 356; // ctrl04
case 335: return 357; // ctrl01
case 336: return 358; // ctrl02
case 337: return 359; // ctrl03
case 338: return 360; // misc14
case 339: return 361; // producer misc16
case 340: return 362; // misc049
case 341: return 364; // misc060
case 342: return 363; // blown01
case 343: return 366; // misc061
case 344: return 365;
case 345: return 368;
case 346: return 376;
case 347: return 370;
case 348: return 367;
case 349: return 372;
case 350: return 369;
case 351: return 374;//429; matching names but not matching textures
case 352: return 375;//387; matching names but not matching textures
case 353: return 371;
case 354: return 377;//425; matching names but not matching textures
case 355: return 408;
case 356: return 378; // lava02
case 357: return 383;//384; matching names but not matching textures
case 358: return 384;//385; matching names but not matching textures
case 359: return 385;//386; matching names but not matching textures
case 360: return 386;
case 361: return 387;
case 362: return d1_pig_present ? 194 : 388; // mntr04b (devil: -1)
case 363: return 388;
case 364: return 391;
case 365: return 392;
case 366: return 393;
case 367: return 394;
case 368: return 395;
case 369: return 396;
case 370: return d1_pig_present ? 195 : 392; // mntr04d (devil: -1)
// range 371..584 handled by default case (wall01 and door frames)
default:
// ranges:
if (d1_tmap_num >= 29 && d1_tmap_num <= 37)
return d1_tmap_num - 16;
if (d1_tmap_num >= 58 && d1_tmap_num <= 87)
return d1_tmap_num - 24;
if (d1_tmap_num >= 89 && d1_tmap_num <= 131)
return d1_tmap_num - 25;
if (d1_tmap_num >= 133 && d1_tmap_num <= 140)
return d1_tmap_num - 26;
if (d1_tmap_num >= 176 && d1_tmap_num <= 184)
return d1_tmap_num + 33;
if (d1_tmap_num >= 186 && d1_tmap_num <= 192)
return d1_tmap_num + 32;
if (d1_tmap_num >= 203 && d1_tmap_num <= 209)
return d1_tmap_num + 25;
if (d1_tmap_num >= 212 && d1_tmap_num <= 219)
return d1_tmap_num + 23;
if (d1_tmap_num >= 231 && d1_tmap_num <= 239)
return d1_tmap_num + 18;
if (d1_tmap_num >= 267 && d1_tmap_num <= 281)
return d1_tmap_num + 10;
if (d1_tmap_num >= 287 && d1_tmap_num <= 297)
return d1_tmap_num + 13;
if (d1_tmap_num >= 299 && d1_tmap_num <= 314)
return d1_tmap_num + 12;
if (d1_tmap_num >= 316 && d1_tmap_num <= 326)
return d1_tmap_num + 11; // matching names but not matching textures
// wall01 and door frames:
if (d1_tmap_num > 370 && d1_tmap_num < 584) {
if (New_file_format_load) return d1_tmap_num + 64;
// d1 shareware needs special treatment:
if (d1_tmap_num < 410) return d1_tmap_num + 68;
if (d1_tmap_num < 417) return d1_tmap_num + 73;
if (d1_tmap_num < 446) return d1_tmap_num + 91;
if (d1_tmap_num < 453) return d1_tmap_num + 104;
if (d1_tmap_num < 462) return d1_tmap_num + 111;
if (d1_tmap_num < 486) return d1_tmap_num + 117;
if (d1_tmap_num < 494) return d1_tmap_num + 141;
if (d1_tmap_num < 584) return d1_tmap_num + 147;
}
{ // handle rare case where orientation != 0
short tmap_num = d1_tmap_num & TMAP_NUM_MASK;
short orient = d1_tmap_num & ~TMAP_NUM_MASK;
if (orient != 0) {
return orient | convert_d1_tmap_num(tmap_num);
} else {
Warning("can't convert unknown descent 1 texture #%d.\n", tmap_num);
return d1_tmap_num;
}
}
}
}
#endif
}
#if DXX_USE_EDITOR
namespace dsx {
tmap_xlate_table_array tmap_xlate_table;
}
struct mtfi mine_top_fileinfo; // Should be same as first two fields below...
struct mfi mine_fileinfo;
struct mh mine_header;
struct me mine_editor;
// -----------------------------------------------------------------------------
//loads from an already-open file
// returns 0=everything ok, 1=old version, -1=error
#endif
#define COMPILED_MINE_VERSION 0
static void read_children(shared_segment &segp, const unsigned bit_mask, PHYSFS_File *const LoadFile)
{
for (int bit=0; bit<MAX_SIDES_PER_SEGMENT; bit++) {
if (bit_mask & (1 << bit)) {
segp.children[bit] = PHYSFSX_readShort(LoadFile);
} else
segp.children[bit] = segment_none;
}
}
static void read_verts(shared_segment &segp, PHYSFS_File *const LoadFile)
{
// Read short Segments[segnum].verts[MAX_VERTICES_PER_SEGMENT]
range_for (auto &i, segp.verts)
i = PHYSFSX_readShort(LoadFile);
}
static void read_special(shared_segment &segp, const unsigned bit_mask, PHYSFS_File *const LoadFile)
{
if (bit_mask & (1 << MAX_SIDES_PER_SEGMENT)) {
// Read ubyte Segments[segnum].special
segp.special = PHYSFSX_readByte(LoadFile);
// Read byte Segments[segnum].matcen_num
segp.matcen_num = PHYSFSX_readByte(LoadFile);
// Read short Segments[segnum].value
segp.station_idx = PHYSFSX_readShort(LoadFile);
} else {
segp.special = 0;
segp.matcen_num = -1;
segp.station_idx = station_none;
}
}
namespace dsx {
int load_mine_data_compiled(PHYSFS_File *LoadFile, const char *const Gamesave_current_filename)
{
auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
auto &Vertices = LevelSharedVertexState.get_vertices();
ubyte compiled_version;
short temp_short;
ubyte bit_mask;
#if defined(DXX_BUILD_DESCENT_II)
LevelSharedSeismicState.Level_shake_frequency = 0;
LevelSharedSeismicState.Level_shake_duration = 0;
d1_pig_present = PHYSFSX_exists(D1_PIGFILE,1);
#endif
if (!strcmp(strchr(Gamesave_current_filename, '.'), ".sdl"))
New_file_format_load = 0; // descent 1 shareware
else
New_file_format_load = 1;
// For compiled levels, textures map to themselves, prevent tmap_override always being gray,
// bug which Matt and John refused to acknowledge, so here is Mike, fixing it.
//
// Although in a cloud of arrogant glee, he forgot to ifdef it on EDITOR!
// (Matt told me to write that!)
#if DXX_USE_EDITOR
for (int i=0; i<MAX_TEXTURES; i++)
tmap_xlate_table[i] = i;
#endif
// memset( Segments, 0, sizeof(segment)*MAX_SEGMENTS );
fuelcen_reset();
//=============================== Reading part ==============================
compiled_version = PHYSFSX_readByte(LoadFile);
(void)compiled_version;
DXX_POISON_VAR(Vertices, 0xfc);
if (New_file_format_load)
LevelSharedVertexState.Num_vertices = PHYSFSX_readShort(LoadFile);
else
LevelSharedVertexState.Num_vertices = PHYSFSX_readInt(LoadFile);
assert(LevelSharedVertexState.Num_vertices <= MAX_VERTICES);
DXX_POISON_VAR(Segments, 0xfc);
if (New_file_format_load)
LevelSharedSegmentState.Num_segments = PHYSFSX_readShort(LoadFile);
else
LevelSharedSegmentState.Num_segments = PHYSFSX_readInt(LoadFile);
assert(LevelSharedSegmentState.Num_segments <= MAX_SEGMENTS);
range_for (auto &i, partial_range(Vertices, LevelSharedVertexState.Num_vertices))
PHYSFSX_readVector(LoadFile, i);
const auto Num_segments = LevelSharedSegmentState.Num_segments;
for (segnum_t segnum=0; segnum < Num_segments; segnum++ ) {
const msmusegment segp = vmsegptr(segnum);
#if DXX_USE_EDITOR
segp.s.segnum = segnum;
segp.s.group = 0;
#endif
if (New_file_format_load)
bit_mask = PHYSFSX_readByte(LoadFile);
else
bit_mask = 0x7f; // read all six children and special stuff...
if (Gamesave_current_version == 5) { // d2 SHAREWARE level
read_special(segp,bit_mask,LoadFile);
read_verts(segp,LoadFile);
read_children(segp,bit_mask,LoadFile);
} else {
read_children(segp,bit_mask,LoadFile);
read_verts(segp,LoadFile);
if (Gamesave_current_version <= 1) { // descent 1 level
read_special(segp,bit_mask,LoadFile);
}
}
segp.u.objects = object_none;
if (Gamesave_current_version <= 5) { // descent 1 thru d2 SHAREWARE level
// Read fix Segments[segnum].static_light (shift down 5 bits, write as short)
const uint16_t temp_static_light = PHYSFSX_readShort(LoadFile);
segp.u.static_light = static_cast<fix>(temp_static_light) << 4;
}
// Read the walls as a 6 byte array
if (New_file_format_load)
bit_mask = PHYSFSX_readByte(LoadFile);
else
bit_mask = 0x3f; // read all six sides
for (int sidenum=0; sidenum<MAX_SIDES_PER_SEGMENT; sidenum++) {
ubyte byte_wallnum;
auto &sside = segp.s.sides[sidenum];
if (bit_mask & (1 << sidenum)) {
byte_wallnum = PHYSFSX_readByte(LoadFile);
if ( byte_wallnum == 255 )
sside.wall_num = wall_none;
else
sside.wall_num = byte_wallnum;
} else
sside.wall_num = wall_none;
}
for (int sidenum=0; sidenum<MAX_SIDES_PER_SEGMENT; sidenum++ ) {
auto &uside = segp.u.sides[sidenum];
if (segp.s.children[sidenum] == segment_none || segp.s.sides[sidenum].wall_num != wall_none) {
// Read short Segments[segnum].sides[sidenum].tmap_num;
const uint16_t temp_tmap1_num = PHYSFSX_readShort(LoadFile);
#if defined(DXX_BUILD_DESCENT_I)
uside.tmap_num = build_texture1_value(convert_tmap(temp_tmap1_num & 0x7fff));
if (New_file_format_load && !(temp_tmap1_num & 0x8000))
uside.tmap_num2 = texture2_value::None;
else {
// Read short Segments[segnum].sides[sidenum].tmap_num2;
const auto tmap_num2 = texture2_value{static_cast<uint16_t>(PHYSFSX_readShort(LoadFile))};
uside.tmap_num2 = build_texture2_value(convert_tmap(get_texture_index(tmap_num2)), get_texture_rotation_high(tmap_num2));
}
#elif defined(DXX_BUILD_DESCENT_II)
const uint16_t masked_temp_tmap1_num = New_file_format_load ? (temp_tmap1_num & 0x7fff) : temp_tmap1_num;
uside.tmap_num = build_texture1_value(masked_temp_tmap1_num);
if (Gamesave_current_version <= 1)
uside.tmap_num = build_texture1_value(convert_d1_tmap_num(get_texture_index(uside.tmap_num)));
if (New_file_format_load && !(temp_tmap1_num & 0x8000))
uside.tmap_num2 = texture2_value::None;
else {
// Read short Segments[segnum].sides[sidenum].tmap_num2;
const auto tmap_num2 = static_cast<texture2_value>(PHYSFSX_readShort(LoadFile));
uside.tmap_num2 = (Gamesave_current_version <= 1 && tmap_num2 != texture2_value::None)
? build_texture2_value(convert_d1_tmap_num(get_texture_index(tmap_num2)), get_texture_rotation_high(tmap_num2))
: tmap_num2;
}
#endif
// Read uvl Segments[segnum].sides[sidenum].uvls[4] (u,v>>5, write as short, l>>1 write as short)
range_for (auto &i, uside.uvls) {
temp_short = PHYSFSX_readShort(LoadFile);
i.u = static_cast<fix>(temp_short) << 5;
temp_short = PHYSFSX_readShort(LoadFile);
i.v = static_cast<fix>(temp_short) << 5;
const uint16_t temp_light = PHYSFSX_readShort(LoadFile);
i.l = static_cast<fix>(temp_light) << 1;
}
} else {
uside.tmap_num = texture1_value::None;
uside.tmap_num2 = texture2_value::None;
uside.uvls = {};
}
}
}
Vertices.set_count(LevelSharedVertexState.Num_vertices);
Segments.set_count(Num_segments);
validate_segment_all(LevelSharedSegmentState); // Fill in side type and normals.
range_for (const auto &&pi, vmsegptridx)
{
if (Gamesave_current_version > 5)
segment2_read(pi, LoadFile);
fuelcen_activate(pi);
}
reset_objects(LevelUniqueObjectState, 1); //one object, the player
return 0;
}
}