ZenNode/DOOM/level.cpp

1211 lines
39 KiB
C++
Raw Normal View History

2020-12-23 07:45:27 +00:00
//----------------------------------------------------------------------------
//
// File: level.cpp
// Date: 26-Oct-1994
// Programmer: Marc Rousseau
//
// Description: Object classes for manipulating Doom Maps
//
// Copyright (c) 1994-2004 Marc Rousseau, All Rights Reserved.
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
//
// Revision History:
//
//----------------------------------------------------------------------------
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "common.hpp"
#include "logger.hpp"
#include "level.hpp"
#include "wad.hpp"
DBG_REGISTER ( __FILE__ );
#define SWAP_ENDIAN_16(x) (((( x ) & 0xFF ) << 8 ) | ((( x ) >> 8 ) & 0xFF ))
//#define min(x,y) ((( x ) < ( y )) ? x : y )
#if defined ( __GNUC__ ) || defined ( __INTEL_COMPILER )
char *strupr ( char *ptr )
{
for ( int i = 0; ptr[i]; i++ ) {
ptr[i] = toupper ( ptr[i] );
}
return ptr;
}
#endif
DoomLevel::sLevelLump::sLevelLump () :
changed ( false ),
byteOrder ( BYTE_ORDER ),
elementSize ( 0 ),
elementCount ( 0 ),
dataSize ( 0 ),
rawData ( NULL )
{
}
DoomLevel::DoomLevel ( const char *_name, WAD *_wad, bool bLoadData ) :
m_Wad ( _wad ),
m_IsHexen ( false ),
m_Title ( NULL ),
m_Music ( NULL ),
m_Cluster ( 0 ),
m_ThingData ( NULL ),
m_LineDefData ( NULL )
{
FUNCTION_ENTRY ( this, "DoomLevel ctor", true );
m_Title = NULL;
m_Music = NULL;
m_Cluster = -1;
m_IsHexen = false;
memset ( m_Name, 0, sizeof ( m_Name ));
for ( int i = 0; i < 8; i++ ) {
if ( _name[i] == '\0' ) break;
m_Name[i] = ( char ) toupper ( _name[i] );
}
m_Map.elementSize = 1;
m_Thing.elementSize = 1;
m_LineDef.elementSize = 1;
m_SideDef.elementSize = sizeof ( wSideDef );
m_Vertex.elementSize = sizeof ( wVertex );
m_Sector.elementSize = sizeof ( wSector );
m_Segs.elementSize = sizeof ( wSegs );
m_SubSector.elementSize = sizeof ( wSSector );
m_Node.elementSize = sizeof ( wNode );
m_Reject.elementSize = 1;
m_BlockMap.elementSize = 1;
m_Behavior.elementSize = 1;
if ( bLoadData == true ) Load ();
LoadHexenInfo ();
}
DoomLevel::~DoomLevel ()
{
FUNCTION_ENTRY ( this, "DoomLevel dtor", true );
if ( m_Title != NULL ) free (( char * ) m_Title );
if ( m_Music != NULL ) free (( char * ) m_Music );
CleanUp ();
}
bool DoomLevel::IsValid ( bool checkBSP, bool print ) const
{
FUNCTION_ENTRY ( this, "DoomLevel::isValid", true );
bool isValid = true;
bool *used = new bool [ SideDefCount () ];
memset ( used, false, sizeof ( bool ) * SideDefCount ());
// Sanity check for LINEDEFS
const wLineDef *lineDef = GetLineDefs ();
for ( int i = 0; i < LineDefCount (); i++ ) {
if ( lineDef [i].start >= VertexCount ()) {
if ( print == true ) fprintf ( stderr, "LINEDEFS[%d].%s vertex is invalid (%d/%d)\n", i, "start", lineDef [i].start, VertexCount ());
isValid = false;
}
if ( lineDef [i].end >= VertexCount ()) {
if ( print == true ) fprintf ( stderr, "LINEDEFS[%d].%s vertex is invalid (%d/%d)\n", i, "end", lineDef [i].end, VertexCount ());
isValid = false;
}
if ( lineDef [i].sideDef [ LEFT_SIDEDEF ] != NO_SIDEDEF ) {
if ( lineDef [i].sideDef [ LEFT_SIDEDEF ] >= SideDefCount ()) {
if ( print == true ) fprintf ( stderr, "LINEDEFS[%d].sideDef[%s] is invalid (%d/%d)\n", i, "left", lineDef [i].sideDef [LEFT_SIDEDEF], SideDefCount ());
isValid = false;
} else {
used [ lineDef [i].sideDef [ LEFT_SIDEDEF ]] = true;
}
}
if ( lineDef [i].sideDef [ RIGHT_SIDEDEF ] != NO_SIDEDEF ) {
if ( lineDef [i].sideDef [ RIGHT_SIDEDEF ] >= SideDefCount ()) {
if ( print == true ) fprintf ( stderr, "LINEDEFS[%d].sideDef[%s] is invalid (%d/%d)\n", i, "right", lineDef [i].sideDef [RIGHT_SIDEDEF], SideDefCount ());
isValid = false;
} else {
used [ lineDef [i].sideDef [ RIGHT_SIDEDEF ]] = true;
}
}
}
// Sanity check for SIDEDEFS
const wSideDef *sideDef = GetSideDefs ();
for ( int i = 0; i < SideDefCount (); i++ ) {
if (( sideDef [i].sector >= SectorCount ()) && ( used [i] == true )) {
if ( print == true ) fprintf ( stderr, "SIDEDEFS[%d].sector is invalid (%d/%d)\n", i, sideDef [i].sector, SectorCount ());
isValid = false;
}
}
delete [] used;
if ( checkBSP == true ) {
// Sanity check for SEGS
const wSegs *segs = GetSegs ();
for ( int i = 0; i < SegCount (); i++ ) {
if ( segs [i].start >= VertexCount ()) {
if ( print == true ) fprintf ( stderr, "SEGS[%d].%s m_Vertex is invalid (%d/%d)\n", i, "start", segs [i].start, VertexCount ());
isValid = false;
}
if ( segs [i].end >= VertexCount ()) {
if ( print == true ) fprintf ( stderr, "SEGS[%d].%s m_Vertex is invalid (%d/%d)\n", i, "end", segs [i].end, VertexCount ());
isValid = false;
}
if ( segs [i].lineDef >= LineDefCount ()) {
if ( print == true ) fprintf ( stderr, "SEGS[%d].lineDef is invalid (%d/%d)\n", i, segs [i].lineDef, LineDefCount ());
isValid = false;
}
if ( segs [i].start == segs [i].end ) {
if ( print == true ) fprintf ( stderr, "SEGS[%d] is zero length\n", i );
isValid = false;
}
}
// Sanity check for SSECTORS
const wSSector *subSector = GetSubSectors ();
for ( int i = 0; i < SubSectorCount (); i++ ) {
if ( subSector [i].first >= SegCount ()) {
if ( print == true ) fprintf ( stderr, "SSECTORS[%d].first is invalid (%d/%d)\n", i, subSector [i].first, SegCount ());
isValid = false;
}
if ( subSector [i].first + subSector [i].num > SegCount ()) {
if ( print == true ) fprintf ( stderr, "SSECTORS[%d].num is invalid (%d/%d)\n", i, subSector [i].num, SegCount ());
isValid = false;
}
}
// Sanity check for NODES
const wNode *node = GetNodes ();
if ( NodeCount () < 2 ) {
if ( print == true ) fprintf ( stderr, "NODES structure is invalid\n" );
isValid = false;
}
for ( int i = 0; i < NodeCount (); i++ ) {
UINT16 child;
child = node [i].child [0];
if (( node [i].dx == 0 ) && ( node[i].dy == 0 )) {
if ( print == true ) fprintf ( stderr, "NODES[%d] is invalid (dx == dy == 0)\n", i );
isValid = false;
}
if ( child & 0x8000 ) {
if (( child & 0x7FFF ) >= SubSectorCount ()) {
if ( print == true ) fprintf ( stderr, "NODES[%d].child[%d] is invalid (0x8000 | %d/%d)\n", i, 0, child & 0x7FFF, SubSectorCount ());
isValid = false;
}
} else {
if ( child >= NodeCount ()) {
if ( print == true ) fprintf ( stderr, "NODES[%d].child[%d] is invalid (%d/%d)\n", i, 0, child, NodeCount ());
isValid = false;
}
}
child = node [i].child [1];
if ( child & 0x8000 ) {
if (( child & 0x7FFF ) >= SubSectorCount ()) {
if ( print == true ) fprintf ( stderr, "NODES[%d].child[%d] is invalid (0x8000 | %d/%d)\n", i, 1, child & 0x7FFF, SubSectorCount ());
isValid = false;
}
} else {
if ( child >= NodeCount ()) {
if ( print == true ) fprintf ( stderr, "NODES[%d].child[%d] is invalid (%d/%d)\n", i, 1, child, NodeCount ());
isValid = false;
}
}
}
}
return isValid;
}
bool DoomLevel::IsDirty () const
{
FUNCTION_ENTRY ( this, "DoomLevel::IsDirty", true );
if ( m_Map.changed == true ) return true;
if ( m_Thing.changed == true ) return true;
if ( m_LineDef.changed == true ) return true;
if ( m_SideDef.changed == true ) return true;
if ( m_Vertex.changed == true ) return true;
if ( m_Sector.changed == true ) return true;
if ( m_Segs.changed == true ) return true;
if ( m_SubSector.changed == true ) return true;
if ( m_Node.changed == true ) return true;
if ( m_Reject.changed == true ) return true;
if ( m_BlockMap.changed == true ) return true;
if ( m_Behavior.changed == true ) return true;
return false;
}
void DoomLevel::CleanUpEntry ( sLevelLump *entry )
{
FUNCTION_ENTRY ( this, "DoomLevel::CleanUpEntry", true );
delete [] ( char * ) entry->rawData;
entry->changed = false;
entry->byteOrder = BYTE_ORDER;
entry->elementCount = 0;
entry->dataSize = 0;
entry->rawData = NULL;
}
void DoomLevel::CleanUp ()
{
FUNCTION_ENTRY ( this, "DoomLevel::CleanUp", true );
CleanUpEntry ( &m_Map );
CleanUpEntry ( &m_Thing );
CleanUpEntry ( &m_LineDef );
CleanUpEntry ( &m_SideDef );
CleanUpEntry ( &m_Vertex );
CleanUpEntry ( &m_Sector );
CleanUpEntry ( &m_Segs );
CleanUpEntry ( &m_SubSector );
CleanUpEntry ( &m_Node );
CleanUpEntry ( &m_Reject );
CleanUpEntry ( &m_BlockMap );
CleanUpEntry ( &m_Behavior );
delete [] m_ThingData;
delete [] m_LineDefData;
m_ThingData = NULL;
m_LineDefData = NULL;
}
#if ( BYTE_ORDER == BIG_ENDIAN )
void DoomLevel::AdjustByteOrderMap ( int byteOrder )
{
FUNCTION_ENTRY ( this, "DoomLevel::AdjustByteOrderMap", true );
if ( m_Map.byteOrder != byteOrder ) {
// Nothing to do for this type
}
m_Map.byteOrder = byteOrder;
}
void DoomLevel::AdjustByteOrderThing ( int byteOrder )
{
FUNCTION_ENTRY ( this, "DoomLevel::AdjustByteOrderThing", true );
if ( m_Thing.byteOrder != byteOrder ) {
wThing *thing = m_ThingData;
for ( int i = 0; i < m_Thing.elementCount; i++ ) {
thing [i].xPos = SWAP_ENDIAN_16 ( thing [i].xPos );
thing [i].yPos = SWAP_ENDIAN_16 ( thing [i].yPos );
thing [i].angle = SWAP_ENDIAN_16 ( thing [i].angle );
thing [i].type = SWAP_ENDIAN_16 ( thing [i].type );
thing [i].attr = SWAP_ENDIAN_16 ( thing [i].attr );
thing [i].tid = SWAP_ENDIAN_16 ( thing [i].tid );
thing [i].altitude = SWAP_ENDIAN_16 ( thing [i].altitude );
}
}
m_Thing.byteOrder = byteOrder;
}
void DoomLevel::AdjustByteOrderLineDef ( int byteOrder )
{
FUNCTION_ENTRY ( this, "DoomLevel::AdjustByteOrderLineDef", true );
if ( m_LineDef.byteOrder != byteOrder ) {
wLineDef *lineDef = m_LineDefData;
for ( int i = 0; i < m_LineDef.elementCount; i++ ) {
lineDef [i].start = SWAP_ENDIAN_16 ( lineDef [i].start );
lineDef [i].end = SWAP_ENDIAN_16 ( lineDef [i].end );
lineDef [i].flags = SWAP_ENDIAN_16 ( lineDef [i].flags );
lineDef [i].type = SWAP_ENDIAN_16 ( lineDef [i].type );
lineDef [i].tag = SWAP_ENDIAN_16 ( lineDef [i].tag );
lineDef [i].sideDef [0] = SWAP_ENDIAN_16 ( lineDef [i].sideDef [0] );
lineDef [i].sideDef [1] = SWAP_ENDIAN_16 ( lineDef [i].sideDef [1] );
}
}
m_LineDef.byteOrder = byteOrder;
}
void DoomLevel::AdjustByteOrderSideDef ( int byteOrder )
{
FUNCTION_ENTRY ( this, "DoomLevel::AdjustByteOrderSideDef", true );
if ( m_SideDef.byteOrder != byteOrder ) {
wSideDef *sideDef = ( wSideDef * ) m_SideDef.rawData;
for ( int i = 0; i < m_SideDef.elementCount; i++ ) {
sideDef [i].xOff = SWAP_ENDIAN_16 ( sideDef [i].xOff );
sideDef [i].yOff = SWAP_ENDIAN_16 ( sideDef [i].yOff );
sideDef [i].sector = SWAP_ENDIAN_16 ( sideDef [i].sector );
}
}
m_SideDef.byteOrder = byteOrder;
}
void DoomLevel::AdjustByteOrderVertex ( int byteOrder )
{
FUNCTION_ENTRY ( this, "DoomLevel::AdjustByteOrderVertex", true );
if ( m_Vertex.byteOrder != byteOrder ) {
wVertex *vertex = ( wVertex * ) m_Vertex.rawData;
for ( int i = 0; i < m_Vertex.elementCount; i++ ) {
vertex [i].x = SWAP_ENDIAN_16 ( vertex [i].x );
vertex [i].y = SWAP_ENDIAN_16 ( vertex [i].y );
}
}
m_Vertex.byteOrder = byteOrder;
}
void DoomLevel::AdjustByteOrderSector ( int byteOrder )
{
FUNCTION_ENTRY ( this, "DoomLevel::AdjustByteOrderSector", true );
if ( m_Sector.byteOrder != byteOrder ) {
wSector *sector = ( wSector * ) m_Sector.rawData;
for ( int i = 0; i < m_Sector.elementCount; i++ ) {
sector [i].floorh = SWAP_ENDIAN_16 ( sector [i].floorh );
sector [i].ceilh = SWAP_ENDIAN_16 ( sector [i].ceilh );
sector [i].light = SWAP_ENDIAN_16 ( sector [i].light );
sector [i].special = SWAP_ENDIAN_16 ( sector [i].special );
sector [i].trigger = SWAP_ENDIAN_16 ( sector [i].trigger );
}
}
m_Sector.byteOrder = byteOrder;
}
void DoomLevel::AdjustByteOrderSegs ( int byteOrder )
{
FUNCTION_ENTRY ( this, "DoomLevel::AdjustByteOrderSegs", true );
if ( m_Segs.byteOrder != byteOrder ) {
wSegs *segs = ( wSegs * ) m_Segs.rawData;
for ( int i = 0; i < m_Segs.elementCount; i++ ) {
segs [i].start = SWAP_ENDIAN_16 ( segs [i].start );
segs [i].end = SWAP_ENDIAN_16 ( segs [i].end );
segs [i].angle = SWAP_ENDIAN_16 ( segs [i].angle );
segs [i].lineDef = SWAP_ENDIAN_16 ( segs [i].lineDef );
segs [i].flip = SWAP_ENDIAN_16 ( segs [i].flip );
segs [i].offset = SWAP_ENDIAN_16 ( segs [i].offset );
}
}
m_Segs.byteOrder = byteOrder;
}
void DoomLevel::AdjustByteOrderSubSector ( int byteOrder )
{
FUNCTION_ENTRY ( this, "DoomLevel::AdjustByteOrderSubSector", true );
if ( m_SubSector.byteOrder != byteOrder ) {
wSSector *ssector = ( wSSector * ) m_SubSector.rawData;
for ( int i = 0; i < m_SubSector.elementCount; i++ ) {
ssector [i].num = SWAP_ENDIAN_16 ( ssector [i].num );
ssector [i].first = SWAP_ENDIAN_16 ( ssector [i].first );
}
}
m_SubSector.byteOrder = byteOrder;
}
void DoomLevel::AdjustByteOrderNode ( int byteOrder )
{
FUNCTION_ENTRY ( this, "DoomLevel::AdjustByteOrderNode", true );
if ( m_Node.byteOrder != byteOrder ) {
// The entire structure is composed of 16-bit entries
UINT16 *ptr = ( UINT16 * ) m_Node.rawData;
for ( int i = 0; i < ( int ) m_Node.dataSize / 2; i++ ) {
ptr [i] = SWAP_ENDIAN_16 ( ptr [i] );
}
}
m_Node.byteOrder = byteOrder;
}
void DoomLevel::AdjustByteOrderReject ( int byteOrder )
{
FUNCTION_ENTRY ( this, "DoomLevel::AdjustByteOrderReject", true );
if ( m_Reject.byteOrder != byteOrder ) {
// Nothing to do for this type
}
m_Reject.byteOrder = byteOrder;
}
void DoomLevel::AdjustByteOrderBlockMap ( int byteOrder )
{
FUNCTION_ENTRY ( this, "DoomLevel::AdjustByteOrderBlockMap", true );
if ( m_BlockMap.byteOrder != byteOrder ) {
// The entire structure is composed of 16-bit entries
UINT16 *ptr = ( UINT16 * ) m_BlockMap.rawData;
for ( int i = 0; i < ( int ) m_BlockMap.dataSize / 2; i++ ) {
ptr [i] = SWAP_ENDIAN_16 ( ptr [i] );
}
}
m_BlockMap.byteOrder = byteOrder;
}
void DoomLevel::AdjustByteOrderBehavior ( int byteOrder )
{
FUNCTION_ENTRY ( this, "DoomLevel::AdjustByteOrderBehavior", true );
if ( m_Behavior.byteOrder != byteOrder ) {
// Nothing to do for this type
}
m_Behavior.byteOrder = byteOrder;
}
void DoomLevel::AdjustByteOrder ( int byteOrder )
{
FUNCTION_ENTRY ( this, "DoomLevel::AdjustByteOrder", true );
AdjustByteOrderMap ( byteOrder );
AdjustByteOrderThing ( byteOrder );
AdjustByteOrderLineDef ( byteOrder );
AdjustByteOrderSideDef ( byteOrder );
AdjustByteOrderVertex ( byteOrder );
AdjustByteOrderSector ( byteOrder );
AdjustByteOrderSegs ( byteOrder );
AdjustByteOrderSubSector ( byteOrder );
AdjustByteOrderNode ( byteOrder );
AdjustByteOrderReject ( byteOrder );
AdjustByteOrderBlockMap ( byteOrder );
AdjustByteOrderBehavior ( byteOrder );
}
#endif
void DoomLevel::ReplaceVertices ( int *map, wVertex *newVertices, int count )
{
wLineDef *lineDef = ( wLineDef * ) GetLineDefs ();
for ( int i = 0; i < LineDefCount (); i++ ) {
lineDef [i].start = ( UINT16 ) map [ lineDef [i].start ];
lineDef [i].end = ( UINT16 ) map [ lineDef [i].end ];
}
m_LineDef.changed = true;
wSegs *segs = ( wSegs * ) GetSegs ();
for ( int i = 0; i < SegCount (); i++ ) {
segs [i].start = ( UINT16 ) map [ segs [i].start ];
segs [i].end = ( UINT16 ) map [ segs [i].end ];
}
m_Segs.changed = true;
delete [] ( char * ) m_Vertex.rawData;
delete [] map;
m_Vertex.changed = true;
m_Vertex.elementCount = count;
m_Vertex.rawData = newVertices;
}
void DoomLevel::TrimVertices ()
{
FUNCTION_ENTRY ( this, "DoomLevel::TrimVertices", true );
int *used = new int [ VertexCount () ];
memset ( used, 0, sizeof ( int ) * VertexCount ());
wLineDef *lineDef = ( wLineDef * ) GetLineDefs ();
for ( int i = 0; i < LineDefCount (); i++ ) {
used [ lineDef [i].start ] = 1;
used [ lineDef [i].end ] = 1;
}
wSegs *segs = ( wSegs * ) GetSegs ();
for ( int i = 0; i < SegCount (); i++ ) {
used [ segs [i].start ] = 1;
used [ segs [i].end ] = 1;
}
const wVertex *oldVertices = GetVertices ();
wVertex *newVertices = new wVertex [ VertexCount () ];
int count = 0;
for ( int i = 0; i < VertexCount (); i++ ) {
if ( used [i] == 1 ) {
newVertices [count] = oldVertices [i];
used [i] = count++;
}
}
if ( VertexCount () == count ) {
delete [] newVertices;
delete [] used;
return;
}
ReplaceVertices ( used, newVertices, count );
}
void DoomLevel::PackVertices ()
{
FUNCTION_ENTRY ( this, "DoomLevel::PackVertices", true );
int *used = new int [ VertexCount () ];
memset ( used, 0, sizeof ( int ) * VertexCount ());
int count = 0;
UINT32 *vert = ( UINT32 * ) m_Vertex.rawData;
for ( int i = 0, j; i < VertexCount (); i++ ) {
UINT32 currentVert = vert [i];
for ( j = 0; j < i; j++ ) {
if ( vert [j] == currentVert ) break;
}
used [i] = j;
if ( i == j ) count++;
}
if ( VertexCount () == count ) {
delete [] used;
return;
}
const wVertex *oldVertices = GetVertices ();
wVertex *newVertices = new wVertex [ count ];
count = 0;
for ( int i = 0; i < VertexCount (); i++ ) {
if ( used [i] == i ) {
newVertices [count] = oldVertices [i];
used [i] = count++;
} else {
used [i] = used [ used [i]];
}
}
ReplaceVertices ( used, newVertices, count );
}
void DoomLevel::ConvertRaw1ToThing ( int max, wThing1 *src, wThing *dest )
{
FUNCTION_ENTRY ( NULL, "DoomLevel::ConvertRaw1ToThing", true );
memset ( dest, 0, sizeof ( wThing ) * max );
for ( int i = 0; i < max; i++ ) {
memcpy ( &dest [i], &src [i], sizeof ( wThing1 ));
}
}
void DoomLevel::ConvertRaw2ToThing ( int max, wThing2 *src, wThing *dest )
{
FUNCTION_ENTRY ( NULL, "DoomLevel::ConvertRaw2ToThing", true );
memset ( dest, 0, sizeof ( wThing ) * max );
for ( int i = 0; i < max; i++ ) {
dest [i].xPos = src [i].xPos;
dest [i].yPos = src [i].yPos;
dest [i].angle = src [i].angle;
dest [i].type = src [i].type;
dest [i].attr = src [i].attr;
dest [i].tid = src [i].tid;
dest [i].altitude = src [i].altitude;
dest [i].special = src [i].special;
memcpy ( dest [i].arg, src [i].arg, sizeof ( src[i].arg ));
}
}
void DoomLevel::ConvertThingToRaw1 ( int max, wThing *src, wThing1 *dest )
{
FUNCTION_ENTRY ( NULL, "DoomLevel::ConvertThingToRaw1", true );
memset ( dest, 0, sizeof ( wThing1 ) * max );
for ( int i = 0; i < max; i++ ) {
memcpy ( &dest [i], &src [i], sizeof ( wThing1 ));
}
}
void DoomLevel::ConvertThingToRaw2 ( int max, wThing *src, wThing2 *dest )
{
FUNCTION_ENTRY ( NULL, "DoomLevel::ConvertThingToRaw2", true );
memset ( dest, 0, sizeof ( wThing2 ) * max );
for ( int i = 0; i < max; i++ ) {
dest [i].xPos = src [i].xPos;
dest [i].yPos = src [i].yPos;
dest [i].angle = src [i].angle;
dest [i].type = src [i].type;
dest [i].attr = src [i].attr;
dest [i].tid = src [i].tid;
dest [i].altitude = src [i].altitude;
dest [i].special = src [i].special;
memcpy ( dest [i].arg, src [i].arg, sizeof ( dest[i].arg ));
}
}
void DoomLevel::ConvertRaw1ToLineDef ( int max, wLineDef1 *src, wLineDef *dest )
{
FUNCTION_ENTRY ( NULL, "DoomLevel::ConvertRaw1ToLineDef", true );
memset ( dest, 0, sizeof ( wLineDef ) * max );
for ( int i = 0; i < max; i++ ) {
memcpy ( &dest [i], &src [i], sizeof ( wLineDef1 ));
}
}
void DoomLevel::ConvertRaw2ToLineDef ( int max, wLineDef2 *src, wLineDef *dest )
{
FUNCTION_ENTRY ( NULL, "DoomLevel::ConvertRaw2toLineDef", true );
memset ( dest, 0, sizeof ( wLineDef ) * max );
for ( int i = 0; i < max; i++ ) {
dest [i].start = src [i].start;
dest [i].end = src [i].end;
dest [i].flags = src [i].flags;
dest [i].type = 0;
dest [i].tag = 0;
dest [i].sideDef[0] = src [i].sideDef[0];
dest [i].sideDef[1] = src [i].sideDef[1];
dest [i].special = src [i].special;
memcpy ( dest [i].arg, src [i].arg, sizeof ( src[i].arg ));
}
}
void DoomLevel::ConvertLineDefToRaw1 ( int max, wLineDef *src, wLineDef1 *dest )
{
FUNCTION_ENTRY ( NULL, "DoomLevel::ConvertLineDefToRaw1", true );
memset ( dest, 0, sizeof ( wLineDef1 ) * max );
for ( int i = 0; i < max; i++ ) {
memcpy ( &dest [i], &src [i], sizeof ( wLineDef1 ));
}
}
void DoomLevel::ConvertLineDefToRaw2 ( int max, wLineDef *src, wLineDef2 *dest )
{
FUNCTION_ENTRY ( NULL, "DoomLevel::ConvertLineDefToRaw2", true );
memset ( dest, 0, sizeof ( wLineDef2 ) * max );
for ( int i = 0; i < max; i++ ) {
dest [i].start = src [i].start;
dest [i].end = src [i].end;
dest [i].flags = src [i].flags;
dest [i].sideDef[0] = src [i].sideDef[0];
dest [i].sideDef[1] = src [i].sideDef[1];
dest [i].special = src [i].special;
memcpy ( dest [i].arg, src [i].arg, sizeof ( dest[i].arg ));
}
}
bool DoomLevel::LoadThings ( bool hexenFormat )
{
FUNCTION_ENTRY ( this, "DoomLevel::LoadThings", true );
int size = 0;
int count = 0;
delete [] m_ThingData;
if ( hexenFormat == false ) {
size = sizeof ( wThing1 );
count = m_Thing.dataSize / size;
m_ThingData = new wThing [ count ];
ConvertRaw1ToThing ( count, ( wThing1 * ) m_Thing.rawData, m_ThingData );
} else {
size = sizeof ( wThing2 );
count = m_Thing.dataSize / size;
m_ThingData = new wThing [ count ];
ConvertRaw2ToThing ( count, ( wThing2 * ) m_Thing.rawData, m_ThingData );
}
m_Thing.byteOrder = LITTLE_ENDIAN;
m_Thing.elementCount = count;
m_Thing.elementSize = size;
return (( int ) m_Thing.dataSize == size * count ) ? true : false;
}
bool DoomLevel::LoadLineDefs ( bool hexenFormat )
{
FUNCTION_ENTRY ( this, "DoomLevel::LoadLineDefs", true );
int size = 0;
int count = 0;
delete [] m_LineDefData;
if ( hexenFormat == false ) {
size = sizeof ( wLineDef1 );
count = m_LineDef.dataSize / size;
m_LineDefData = new wLineDef [ count ];
ConvertRaw1ToLineDef ( count, ( wLineDef1 * ) m_LineDef.rawData, m_LineDefData );
} else {
size = sizeof ( wLineDef2 );
count = m_LineDef.dataSize / size;
m_LineDefData = new wLineDef [ count ];
ConvertRaw2ToLineDef ( count, ( wLineDef2 * ) m_LineDef.rawData, m_LineDefData );
}
m_LineDef.byteOrder = LITTLE_ENDIAN;
m_LineDef.elementCount = count;
m_LineDef.elementSize = size;
return (( int ) m_LineDef.dataSize == size * count ) ? true : false;
}
bool DoomLevel::ReadEntry ( sLevelLump *entry, const char *name, const wadDirEntry *start, const wadDirEntry *end, bool required )
{
FUNCTION_ENTRY ( this, "DoomLevel::ReadEntry", true );
const wadDirEntry *dir = m_Wad->FindDir ( name, start, end );
if ( dir == NULL ) return ( required == true ) ? false : true;
entry->rawData = m_Wad->ReadEntry ( dir, &entry->dataSize );
entry->elementCount = entry->dataSize / entry->elementSize;
entry->byteOrder = LITTLE_ENDIAN;
return true;
}
void DoomLevel::DetermineType ()
{
FUNCTION_ENTRY ( this, "DoomLevel::DetermineType", true );
m_IsHexen = false;
// Look for the easy things first
if ( m_Behavior.rawData != NULL ) {
m_IsHexen = true;
return;
}
// See if we have an exact match in structure sizes in only 1 of the two formats
int isType1 = ( m_Thing.dataSize % sizeof ( wThing1 )) + ( m_LineDef.dataSize % sizeof ( wLineDef1 ));
int isType2 = ( m_Thing.dataSize % sizeof ( wThing2 )) + ( m_LineDef.dataSize % sizeof ( wLineDef2 ));
if ( isType1 != isType2 ) {
if ( isType1 == 0 ) return;
if ( isType2 == 0 ) {
m_IsHexen = true;
return;
}
}
// See if we have a valid level in only 1 of the two formats
LoadThings ( false );
LoadLineDefs ( false );
// Make sure we have the correct byte order
AdjustByteOrder ( BYTE_ORDER );
bool isValid1 = IsValid ( false, false );
LoadThings ( true );
LoadLineDefs ( true );
// Make sure we have the correct byte order
AdjustByteOrder ( BYTE_ORDER );
bool isValid2 = IsValid ( false, false );
if ( isValid1 != isValid2 ) {
if ( isValid1 == true ) return;
if ( isValid2 == true ) {
m_IsHexen = true;
return;
}
}
// Pick the one with the fewest invalid things found?
fprintf ( stderr, "Argh!!!\n" );
}
bool DoomLevel::Load ()
{
FUNCTION_ENTRY ( this, "DoomLevel::Load", true );
if ( m_Wad == NULL ) return false;
const wadDirEntry *start = m_Wad->FindDir ( Name ());
if ( start == NULL ) return false;
const wadDirEntry *end = start + min ( 11, ( int ) ( m_Wad->DirSize () - 1 ));
bool valid = true;
valid &= ReadEntry ( &m_Map, Name (), start, end, true );
valid &= ReadEntry ( &m_Thing, "THINGS", start, end, true );
valid &= ReadEntry ( &m_LineDef, "LINEDEFS", start, end, true );
valid &= ReadEntry ( &m_SideDef, "SIDEDEFS", start, end, true );
valid &= ReadEntry ( &m_Vertex, "VERTEXES", start, end, true );
valid &= ReadEntry ( &m_Sector, "SECTORS", start, end, true );
valid &= ReadEntry ( &m_Segs, "SEGS", start, end, false );
valid &= ReadEntry ( &m_SubSector, "SSECTORS", start, end, false );
valid &= ReadEntry ( &m_Node, "NODES", start, end, false );
valid &= ReadEntry ( &m_Reject, "REJECT", start, end, false );
valid &= ReadEntry ( &m_BlockMap, "BLOCKMAP", start, end, false );
valid &= ReadEntry ( &m_Behavior, "BEHAVIOR", start, end, false );
if ( RejectSize () != 0 ) {
int mask = ( 0xFF >> ( RejectSize () * 8 - SectorCount () * SectorCount ())) & 0xFF;
(( UINT8 * ) m_Reject.rawData ) [ RejectSize () - 1 ] &= ( UINT8 ) mask;
}
DetermineType ();
LoadThings ( m_IsHexen );
LoadLineDefs ( m_IsHexen );
// Switch to native byte ordering
AdjustByteOrder ( BYTE_ORDER );
return valid;
}
bool DoomLevel::LoadHexenInfo ()
{
FUNCTION_ENTRY ( this, "DoomLevel::LoadHexenInfo", true );
if ( m_Wad == NULL ) return false;
const wadDirEntry *dir = m_Wad->FindDir ( "MAPINFO" );
if ( dir == NULL ) return false;
int level;
sscanf ( m_Name, "MAP%02d", &level );
UINT32 Size;
char *buffer = ( char * ) m_Wad->ReadEntry ( dir, &Size, true );
char *ptr = buffer;
if ( m_Title != NULL ) free (( char * ) m_Title );
m_Title = NULL;
do {
if (( ptr = strstr ( ptr, "\nmap " )) == NULL ) break;
if ( atoi ( &ptr[5] ) == level ) {
while ( *ptr++ != '"' );
strtok ( ptr, "\"" );
m_Title = strdup ( ptr );
ptr += strlen ( ptr ) + 1;
char *clstr = strstr ( ptr, "\ncluster " );
char *next = strstr ( ptr, "\n\r" );
if ( clstr && ( clstr < next )) {
m_Cluster = atoi ( clstr + 9 );
}
break;
} else {
ptr++;
}
} while ( ptr && *ptr );
delete [] buffer;
if ( m_Title != NULL ) {
ptr = ( char * ) m_Title + 1;
while ( *ptr ) {
*ptr = ( char ) tolower ( *ptr );
if ( *ptr == ' ' ) {
while ( *ptr == ' ' ) ptr++;
if ( strncmp ( ptr, "OF ", 3 ) == 0 ) ptr--;
}
if ( *ptr ) ptr++;
}
}
dir = m_Wad->FindDir ( "SNDINFO" );
if ( dir == NULL ) return true;
buffer = ( char * ) m_Wad->ReadEntry ( dir, &Size, true );
ptr = buffer;
do {
if (( ptr = strstr ( ptr, "\n$MAP" )) == NULL ) break;
if ( atoi ( &ptr[5] ) == level ) {
ptr += 25;
strtok ( ptr, "\r" );
m_Music = strupr ( strdup ( ptr ));
break;
} else {
ptr++;
}
} while ( ptr && *ptr );
delete [] buffer;
return true;
}
void DoomLevel::AddToWAD ( WAD *m_Wad )
{
FUNCTION_ENTRY ( this, "DoomLevel::AddToWAD", true );
// Make sure data is in little endian when writing to the file
AdjustByteOrder ( LITTLE_ENDIAN );
StoreThings ();
StoreLineDefs ();
m_Wad->InsertAfter (( const wLumpName * ) Name (), m_Map.dataSize, m_Map.rawData, false );
m_Wad->InsertAfter (( const wLumpName * ) "THINGS", m_Thing.dataSize, m_Thing.rawData, false );
m_Wad->InsertAfter (( const wLumpName * ) "LINEDEFS", m_LineDef.dataSize, m_LineDef.rawData, false );
m_Wad->InsertAfter (( const wLumpName * ) "SIDEDEFS", m_SideDef.dataSize, m_SideDef.rawData, false );
m_Wad->InsertAfter (( const wLumpName * ) "VERTEXES", m_Vertex.dataSize, m_Vertex.rawData, false );
m_Wad->InsertAfter (( const wLumpName * ) "SEGS", m_Segs.dataSize, m_Segs.rawData, false );
m_Wad->InsertAfter (( const wLumpName * ) "SSECTORS", m_SubSector.dataSize, m_SubSector.rawData, false );
m_Wad->InsertAfter (( const wLumpName * ) "NODES", m_Node.dataSize, m_Node.rawData, false );
m_Wad->InsertAfter (( const wLumpName * ) "SECTORS", m_Sector.dataSize, m_Sector.rawData, false );
m_Wad->InsertAfter (( const wLumpName * ) "REJECT", m_Reject.dataSize, m_Reject.rawData, false );
m_Wad->InsertAfter (( const wLumpName * ) "BLOCKMAP", m_BlockMap.dataSize, m_BlockMap.rawData, false );
if (( m_IsHexen == true ) && ( m_Behavior.rawData != NULL )) {
m_Wad->InsertAfter (( const wLumpName * ) "BEHAVIOR", m_Behavior.dataSize, m_Behavior.rawData, false );
}
// Switch back to native byte ordering
AdjustByteOrder ( BYTE_ORDER );
}
void DoomLevel::StoreThings ()
{
FUNCTION_ENTRY ( this, "DoomLevel::StoreThings", true );
AdjustByteOrderThing ( LITTLE_ENDIAN );
if (( int ) m_Thing.dataSize < ThingCount () * m_Thing.elementSize ) {
delete [] ( char * ) m_Thing.rawData;
m_Thing.rawData = new char [ ThingCount () * m_Thing.elementSize ];
}
if ( m_IsHexen == false ) {
ConvertThingToRaw1 ( ThingCount (), m_ThingData, ( wThing1 * ) m_Thing.rawData );
} else {
ConvertThingToRaw2 ( ThingCount (), m_ThingData, ( wThing2 * ) m_Thing.rawData );
}
m_LineDef.dataSize = ThingCount () * m_LineDef.elementSize;
}
void DoomLevel::StoreLineDefs ()
{
FUNCTION_ENTRY ( this, "DoomLevel::StoreLineDefs", true );
AdjustByteOrderLineDef ( LITTLE_ENDIAN );
if (( int ) m_LineDef.dataSize < LineDefCount () * m_LineDef.elementSize ) {
delete [] ( char * ) m_LineDef.rawData;
m_LineDef.rawData = new char [ LineDefCount () * m_LineDef.elementSize ];
}
if ( m_IsHexen == false ) {
ConvertLineDefToRaw1 ( LineDefCount (), m_LineDefData, ( wLineDef1 * ) m_LineDef.rawData );
} else {
ConvertLineDefToRaw2 ( LineDefCount (), m_LineDefData, ( wLineDef2 * ) m_LineDef.rawData );
}
m_LineDef.dataSize = LineDefCount () * m_LineDef.elementSize;
}
bool DoomLevel::UpdateEntry ( sLevelLump *lump, const char *name, const char *follows, bool required )
{
FUNCTION_ENTRY ( this, "DoomLevel::UpdateEntry", true );
if ( lump->changed == false ) return false;
lump->changed = false;
const wadDirEntry *start = m_Wad->FindDir ( Name ());
const wadDirEntry *end = start + min ( 10, ( int ) ( m_Wad->DirSize () - 1 ));
bool changed = false;
const wadDirEntry *dir = m_Wad->FindDir ( name, start, end );
if ( dir == NULL ) {
if ( required == true ) {
fprintf ( stderr, "Map %s is missing required lump: %s\n", Name (), name );
} else {
const wadDirEntry *last = m_Wad->FindDir ( follows, start, end );
changed |= m_Wad->InsertAfter (( const wLumpName * ) name, lump->dataSize, lump->rawData, false, last );
}
} else {
changed |= m_Wad->WriteEntry ( dir, lump->dataSize, lump->rawData, false );
}
return changed;
}
bool DoomLevel::UpdateWAD ()
{
FUNCTION_ENTRY ( this, "DoomLevel::UpdateWAD", true );
if ( m_Wad == NULL ) return false;
if ( m_Wad->FindDir ( Name ()) == NULL ) return false;
if ( IsDirty () == false ) return false;
// Make sure data is in little endian when writing to the file
AdjustByteOrder ( LITTLE_ENDIAN );
StoreThings ();
StoreLineDefs ();
bool changed = false;
changed |= UpdateEntry ( &m_Thing, "THINGS", NULL, true );
changed |= UpdateEntry ( &m_LineDef, "LINEDEFS", NULL, true );
changed |= UpdateEntry ( &m_SideDef, "SIDEDEFS", NULL, true );
changed |= UpdateEntry ( &m_Vertex, "VERTEXES", NULL, true );
changed |= UpdateEntry ( &m_Segs, "SEGS", "VERTEXES", false );
changed |= UpdateEntry ( &m_SubSector, "SSECTORS", "SEGS", false );
changed |= UpdateEntry ( &m_Node, "NODES", "SSECTORS", false );
changed |= UpdateEntry ( &m_Sector, "SECTORS", "NODES", false );
changed |= UpdateEntry ( &m_Reject, "REJECT", "SECTORS", false );
changed |= UpdateEntry ( &m_BlockMap, "BLOCKMAP", "REJECT", false );
if (( m_IsHexen == true ) && ( m_Behavior.rawData != NULL )) {
changed |= UpdateEntry ( &m_Behavior, "BEHAVIOR", "BLOCKMAP", false );
}
// Switch back to native byte ordering
AdjustByteOrder ( BYTE_ORDER );
return changed;
}
void DoomLevel::NewEntry ( sLevelLump *entry, int newCount, void *newData )
{
FUNCTION_ENTRY ( this, "DoomLevel::NewEntry", true );
delete [] ( char * ) entry->rawData;
entry->byteOrder = BYTE_ORDER;
entry->changed = true;
entry->elementCount = newCount;
entry->dataSize = newCount * entry->elementSize;
entry->rawData = newData;
}
void DoomLevel::NewThings ( int newCount, wThing *newData )
{
FUNCTION_ENTRY ( this, "DoomLevel::NewThings", true );
delete [] ( char * ) m_ThingData;
m_ThingData = newData;
m_Thing.byteOrder = BYTE_ORDER;
m_Thing.changed = true;
m_Thing.elementCount = newCount;
}
void DoomLevel::NewLineDefs ( int newCount, wLineDef *newData )
{
FUNCTION_ENTRY ( this, "DoomLevel::NewLineDefs", true );
delete [] ( char * ) m_LineDefData;
m_LineDefData = newData;
m_LineDef.byteOrder = BYTE_ORDER;
m_LineDef.changed = true;
m_LineDef.elementCount = newCount;
}
void DoomLevel::NewSideDefs ( int newCount, wSideDef *newData )
{
FUNCTION_ENTRY ( this, "DoomLevel::NewSideDefs", true );
NewEntry ( &m_SideDef, newCount, newData );
}
void DoomLevel::NewVertices ( int newCount, wVertex *newData )
{
FUNCTION_ENTRY ( this, "DoomLevel::NewVertices", true );
NewEntry ( &m_Vertex, newCount, newData );
}
void DoomLevel::NewSectors ( int newCount, wSector *newData )
{
FUNCTION_ENTRY ( this, "DoomLevel::NewSectors", true );
NewEntry ( &m_Sector, newCount, newData );
}
void DoomLevel::NewSegs ( int newCount, wSegs *newData )
{
FUNCTION_ENTRY ( this, "DoomLevel::NewSegs", true );
NewEntry ( &m_Segs, newCount, newData );
}
void DoomLevel::NewSubSectors ( int newCount, wSSector *newData )
{
FUNCTION_ENTRY ( this, "DoomLevel::NewSubSectors", true );
NewEntry ( &m_SubSector, newCount, newData );
}
void DoomLevel::NewNodes ( int newCount, wNode *newData )
{
FUNCTION_ENTRY ( this, "DoomLevel::NewNodes", true );
NewEntry ( &m_Node, newCount, newData );
}
void DoomLevel::NewReject ( int newSize, UINT8 *newData )
{
FUNCTION_ENTRY ( this, "DoomLevel::NewReject", true );
int mask = ( 0xFF >> ( newSize * 8 - SectorCount () * SectorCount ())) & 0xFF;
newData [ newSize - 1 ] &= ( UINT8 ) mask;
NewEntry ( &m_Reject, newSize, newData );
}
void DoomLevel::NewBlockMap ( int newSize, wBlockMap *newData )
{
FUNCTION_ENTRY ( this, "DoomLevel::NewBlockMap", true );
NewEntry ( &m_BlockMap, newSize, newData );
}
void DoomLevel::NewBehavior ( int newSize, char *newData )
{
FUNCTION_ENTRY ( this, "DoomLevel::NewBehavior", true );
NewEntry ( &m_Behavior, newSize, newData );
}