mirror of
https://github.com/nillerusr/source-engine.git
synced 2024-12-22 06:06:50 +00:00
927 lines
20 KiB
C++
927 lines
20 KiB
C++
//========= Copyright Valve Corporation, All rights reserved. ============//
|
|
//
|
|
// Purpose:
|
|
//
|
|
// $NoKeywords: $
|
|
//=============================================================================//
|
|
|
|
#include "stdafx.h"
|
|
#include <io.h>
|
|
#include "hammer.h"
|
|
#include "MapEntity.h"
|
|
#include "MapFace.h"
|
|
#include "MapSolid.h"
|
|
#include "MapStudioModel.h"
|
|
#include "MapWorld.h"
|
|
#include "GlobalFunctions.h"
|
|
#include "VisGroup.h"
|
|
#include "MapDoc.h"
|
|
#include "MapDisp.h"
|
|
|
|
// memdbgon must be the last include file in a .cpp file!!!
|
|
#include <tier0/memdbgon.h>
|
|
|
|
static CMapWorld *pLoadingWorld;
|
|
static float fThisVersion;
|
|
static BOOL bCorrupt;
|
|
|
|
class COldVisGroup
|
|
{
|
|
public:
|
|
|
|
char m_szName[128];
|
|
color32 m_rgbColor;
|
|
|
|
DWORD m_dwID;
|
|
bool m_bVisible;
|
|
};
|
|
|
|
|
|
float GetFileVersion() { return fThisVersion; }
|
|
|
|
|
|
static void WriteString(std::fstream& file, LPCTSTR pszString)
|
|
{
|
|
BYTE cLen = strlen(pszString)+1;
|
|
file.write((char*)&cLen, 1);
|
|
file.write(pszString, cLen);
|
|
}
|
|
|
|
static void ReadString(std::fstream& file, char * pszString)
|
|
{
|
|
BYTE cLen;
|
|
file.read((char *)&cLen, 1);
|
|
file.read(pszString, cLen);
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Loads a solid face from RMF.
|
|
//-----------------------------------------------------------------------------
|
|
int CMapFace::SerializeRMF(std::fstream& file, BOOL fIsStoring)
|
|
{
|
|
int iSize;
|
|
|
|
if (fIsStoring)
|
|
{
|
|
//
|
|
// After 3.3 the alignment of vec4_t's changed. We never save the new format,
|
|
// since RMF is no longer being revved.
|
|
//
|
|
TEXTURE_33 OldTex33;
|
|
memset(&OldTex33, 0, sizeof(OldTex33));
|
|
|
|
memcpy(OldTex33.texture, texture.texture, sizeof(OldTex33.texture));
|
|
|
|
OldTex33.UAxis[0] = texture.UAxis[0];
|
|
OldTex33.UAxis[1] = texture.UAxis[1];
|
|
OldTex33.UAxis[2] = texture.UAxis[2];
|
|
OldTex33.UAxis[3] = texture.UAxis[3];
|
|
|
|
OldTex33.VAxis[0] = texture.VAxis[0];
|
|
OldTex33.VAxis[1] = texture.VAxis[1];
|
|
OldTex33.VAxis[2] = texture.VAxis[2];
|
|
OldTex33.VAxis[3] = texture.VAxis[3];
|
|
|
|
OldTex33.rotate = texture.rotate;
|
|
|
|
OldTex33.scale[0] = texture.scale[0];
|
|
OldTex33.scale[1] = texture.scale[1];
|
|
|
|
OldTex33.smooth = texture.smooth;
|
|
OldTex33.material = texture.material;
|
|
OldTex33.q2surface = texture.q2surface;
|
|
OldTex33.q2contents = texture.q2contents;
|
|
OldTex33.nLightmapScale = texture.nLightmapScale;
|
|
|
|
file.write((char *)&OldTex33, sizeof(OldTex33));
|
|
|
|
iSize = nPoints;
|
|
file.write((char *)&iSize, sizeof(int));
|
|
|
|
//
|
|
// Save face points. We don't serialize the Vectors directly because the memory
|
|
// layout changed with SSE optimizations.
|
|
//
|
|
float SavePoints[256][3];
|
|
for (int i = 0; i < iSize; i++)
|
|
{
|
|
SavePoints[i][0] = Points[i].x;
|
|
SavePoints[i][1] = Points[i].y;
|
|
SavePoints[i][2] = Points[i].z;
|
|
}
|
|
|
|
file.write((char *)SavePoints, nPoints * 3 * sizeof(float));
|
|
|
|
//
|
|
// Save plane points. We don't serialize the Vectors directly because the memory
|
|
// layout changed with SSE optimizations.
|
|
//
|
|
for (int i = 0; i < 3; i++)
|
|
{
|
|
SavePoints[i][0] = plane.planepts[i].x;
|
|
SavePoints[i][1] = plane.planepts[i].y;
|
|
SavePoints[i][2] = plane.planepts[i].z;
|
|
}
|
|
|
|
file.write((char *)SavePoints, 3 * 3 * sizeof(float));
|
|
}
|
|
else
|
|
{
|
|
// Pre-2.2 used a different texture structure format.
|
|
TEXTURE_21 OldTex;
|
|
memset(&OldTex, 0, sizeof(OldTex));
|
|
|
|
if (fThisVersion < 0.9f)
|
|
{
|
|
// Read the name
|
|
file.read(OldTex.texture, 16);
|
|
|
|
// Ensure name is ASCIIZ
|
|
OldTex.texture[16] = 0;
|
|
|
|
// Read the rest - skip the name
|
|
file.read((char *)&OldTex.rotate, sizeof(OldTex.rotate) + sizeof(OldTex.shift) + sizeof(OldTex.scale));
|
|
}
|
|
else if (fThisVersion < 1.2f)
|
|
{
|
|
// Didn't have smooth/material groups:
|
|
file.read((char *)&OldTex, 40);
|
|
file.read((char *)&OldTex, sizeof(OldTex.texture) - (MAX_PATH) + sizeof(OldTex.rotate) + sizeof(OldTex.shift) + sizeof(OldTex.scale));
|
|
}
|
|
else if (fThisVersion < 1.7f)
|
|
{
|
|
// No quake2 fields yet and smaller texture size.
|
|
file.read((char *)&OldTex, 40);
|
|
file.read((char *)&OldTex.rotate, sizeof(OldTex) - (sizeof(int) * 3) - MAX_PATH);
|
|
}
|
|
else if (fThisVersion < 1.8f)
|
|
{
|
|
// Texture name field changed from 40 to MAX_PATH in size.
|
|
file.read((char *)&OldTex, 40);
|
|
file.read((char *)&OldTex.rotate, sizeof(OldTex) - MAX_PATH);
|
|
}
|
|
else if (fThisVersion < 2.2f)
|
|
{
|
|
file.read((char *)&OldTex, sizeof(OldTex));
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// After 3.3 the alignment of vec4_t's changed. We never save the new format,
|
|
// since RMF is no longer being revved.
|
|
//
|
|
TEXTURE_33 OldTex33;
|
|
memset(&OldTex33, 0, sizeof(OldTex33));
|
|
|
|
file.read((char *)&OldTex33, sizeof(OldTex33));
|
|
|
|
memcpy(texture.texture, OldTex33.texture, sizeof(texture.texture));
|
|
|
|
texture.UAxis[0] = OldTex33.UAxis[0];
|
|
texture.UAxis[1] = OldTex33.UAxis[1];
|
|
texture.UAxis[2] = OldTex33.UAxis[2];
|
|
texture.UAxis[3] = OldTex33.UAxis[3];
|
|
|
|
texture.VAxis[0] = OldTex33.VAxis[0];
|
|
texture.VAxis[1] = OldTex33.VAxis[1];
|
|
texture.VAxis[2] = OldTex33.VAxis[2];
|
|
texture.VAxis[3] = OldTex33.VAxis[3];
|
|
|
|
texture.rotate = OldTex33.rotate;
|
|
|
|
texture.scale[0] = OldTex33.scale[0];
|
|
texture.scale[1] = OldTex33.scale[1];
|
|
|
|
texture.smooth = OldTex33.smooth;
|
|
texture.material = OldTex33.material;
|
|
texture.q2surface = OldTex33.q2surface;
|
|
texture.q2contents = OldTex33.q2contents;
|
|
texture.nLightmapScale = OldTex33.nLightmapScale;
|
|
|
|
if (texture.nLightmapScale == 0)
|
|
{
|
|
texture.nLightmapScale = g_pGameConfig->GetDefaultLightmapScale();
|
|
}
|
|
}
|
|
|
|
// If reading from a pre-2.2 RMF file, copy the texture info from the old format.
|
|
if (fThisVersion < 2.2f)
|
|
{
|
|
memcpy(texture.texture, OldTex.texture, sizeof(texture.texture));
|
|
memcpy(texture.scale, OldTex.scale, sizeof(texture.scale));
|
|
texture.rotate = OldTex.rotate;
|
|
texture.smooth = OldTex.smooth;
|
|
texture.material = OldTex.material;
|
|
texture.q2surface = OldTex.q2surface;
|
|
texture.q2contents = OldTex.q2contents;
|
|
texture.UAxis[3] = OldTex.shift[0];
|
|
texture.VAxis[3] = OldTex.shift[1];
|
|
}
|
|
|
|
if (fThisVersion < 1.8f)
|
|
{
|
|
texture.texture[40] = 0;
|
|
}
|
|
|
|
//
|
|
// Reverse forward slashes if we are not using materials.
|
|
//
|
|
if (g_pGameConfig->GetTextureFormat() != tfVMT)
|
|
{
|
|
for (int i = strlen(texture.texture) - 1; i >= 0; i--)
|
|
{
|
|
if (texture.texture[i] == '/')
|
|
{
|
|
texture.texture[i] = '\\';
|
|
}
|
|
}
|
|
}
|
|
|
|
if (texture.texture[1] == ':')
|
|
{
|
|
char szBuf[MAX_PATH];
|
|
char *psz;
|
|
strcpy(szBuf, texture.texture);
|
|
psz = strstr(szBuf, "textures\\");
|
|
if (psz)
|
|
{
|
|
memset(texture.texture, 0, sizeof(texture.texture));
|
|
psz += strlen("textures\\");
|
|
strcpy(texture.texture, psz);
|
|
}
|
|
}
|
|
|
|
if (fThisVersion < 0.6f)
|
|
{
|
|
float light;
|
|
file.read((char*) &light, sizeof(light));
|
|
}
|
|
|
|
//
|
|
// Load the points into an array of float[3]'s and transfer them into
|
|
// an array of Vectors which will be used for face creation. We can't
|
|
// load directly into the Vectors because the memory layout changed
|
|
// when SSE optimizations were added.
|
|
//
|
|
float LoadPoints[256][3];
|
|
|
|
file.read((char *)&iSize, sizeof(int));
|
|
file.read((char *)&LoadPoints, iSize * 3 * sizeof(float));
|
|
|
|
Vector CreatePoints[256];
|
|
for (int i = 0; i < iSize; i++)
|
|
{
|
|
CreatePoints[i].x = LoadPoints[i][0];
|
|
CreatePoints[i].y = LoadPoints[i][1];
|
|
CreatePoints[i].z = LoadPoints[i][2];
|
|
|
|
//
|
|
// Negate Z for older RMF files.
|
|
//
|
|
if (fThisVersion < 0.5f)
|
|
{
|
|
CreatePoints[i].z = -CreatePoints[i].z;
|
|
}
|
|
}
|
|
|
|
if (fThisVersion < 2.2f)
|
|
{
|
|
CreateFace(CreatePoints, iSize);
|
|
}
|
|
|
|
//
|
|
// Load the plane points. We don't really need them, but they can fix the face if, somehow, it
|
|
// was saved without any points. RMF could have been smaller if we only saved these plane points.
|
|
//
|
|
if (fThisVersion >= 0.7f)
|
|
{
|
|
//
|
|
// Load the points into an array of float[3]'s and transfer them into
|
|
// the array of Vectors. We can't load directly into the Vectors because the memory
|
|
// layout changed when SSE optimizations were added.
|
|
//
|
|
float LoadPlanePoints[3][3];
|
|
file.read((char *)LoadPlanePoints, sizeof(LoadPlanePoints));
|
|
|
|
for (int i = 0; i < 3; i++)
|
|
{
|
|
plane.planepts[i].x = LoadPlanePoints[i][0];
|
|
plane.planepts[i].y = LoadPlanePoints[i][1];
|
|
plane.planepts[i].z = LoadPlanePoints[i][2];
|
|
}
|
|
|
|
CalcPlane();
|
|
|
|
// If reading from an older RMF file, set up the texture axes Quake-style.
|
|
if (fThisVersion < 2.2f)
|
|
{
|
|
InitializeTextureAxes(TEXTURE_ALIGN_QUAKE, INIT_TEXTURE_AXES | INIT_TEXTURE_FORCE);
|
|
}
|
|
}
|
|
|
|
if( fThisVersion < 2.2f )
|
|
{
|
|
SetTexture(texture.texture);
|
|
}
|
|
|
|
//
|
|
// version 3.4 -- added displacement info to faces
|
|
//
|
|
if( ( fThisVersion >= 3.4f ) && ( fThisVersion <= 3.6f ) )
|
|
{
|
|
bool bHasMapDisp;
|
|
|
|
if( fThisVersion >= 3.5f )
|
|
{
|
|
int nLoadHasMapDisp;
|
|
|
|
// check displacement mapping flag
|
|
file.read( ( char* )&nLoadHasMapDisp, sizeof( int ) );
|
|
bHasMapDisp = nLoadHasMapDisp != 0;
|
|
}
|
|
else
|
|
{
|
|
// check displacement mapping flag
|
|
file.read( ( char* )&bHasMapDisp, sizeof( bool ) );
|
|
}
|
|
|
|
if( bHasMapDisp )
|
|
{
|
|
EditDispHandle_t handle = EditDispMgr()->Create();
|
|
SetDisp( handle );
|
|
|
|
CMapDisp *pDisp = EditDispMgr()->GetDisp( handle );
|
|
pDisp->SetParent( this );
|
|
pDisp->SerializedLoadRMF( file, this, fThisVersion );
|
|
}
|
|
}
|
|
|
|
if (fThisVersion >= 2.2f)
|
|
{
|
|
CreateFace(CreatePoints, iSize);
|
|
SetTexture(texture.texture);
|
|
}
|
|
}
|
|
|
|
if (file.bad())
|
|
{
|
|
return(-1);
|
|
}
|
|
|
|
return(0);
|
|
}
|
|
|
|
|
|
int MDkeyvalue::SerializeRMF(std::fstream& file, BOOL fIsStoring)
|
|
{
|
|
// load/save a keyvalue
|
|
if( fIsStoring )
|
|
{
|
|
WriteString(file, szKey);
|
|
WriteString(file, szValue);
|
|
}
|
|
else
|
|
{
|
|
ReadString(file, szKey);
|
|
ReadString(file, szValue);
|
|
}
|
|
|
|
if( file.bad() )
|
|
return -1;
|
|
return 0;
|
|
}
|
|
|
|
|
|
int CMapSolid::SerializeRMF(std::fstream& file, BOOL fIsStoring)
|
|
{
|
|
int iRvl, iSize;
|
|
|
|
// load/save children
|
|
CMapClass::SerializeRMF(file, fIsStoring);
|
|
|
|
// load/save a brush
|
|
if(fIsStoring)
|
|
{
|
|
// serialize the Faces
|
|
iSize = Faces.GetCount();
|
|
file.write((char*) &iSize, sizeof(int));
|
|
for(int i = 0; i < iSize; i++)
|
|
{
|
|
iRvl = Faces[i].SerializeRMF(file, fIsStoring);
|
|
if(iRvl < 0)
|
|
return iRvl;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// There once was a bug that caused black solids. Fix it here.
|
|
if ((r == 0) && (g == 0) || (b == 0))
|
|
{
|
|
PickRandomColor();
|
|
}
|
|
|
|
// read Faces
|
|
file.read((char*) &iSize, sizeof(int));
|
|
Faces.SetCount(iSize);
|
|
|
|
for(int i = 0; i < iSize; i++)
|
|
{
|
|
// extract face
|
|
iRvl = Faces[i].SerializeRMF(file, fIsStoring);
|
|
if (iRvl < 0)
|
|
{
|
|
return(iRvl);
|
|
}
|
|
|
|
Faces[i].SetRenderColor(r, g, b);
|
|
Faces[i].SetParent(this);
|
|
}
|
|
|
|
CalcBounds();
|
|
|
|
//
|
|
// Set solid type based on texture name.
|
|
//
|
|
m_eSolidType = HL1SolidTypeFromTextureName(Faces[0].texture.texture);
|
|
}
|
|
|
|
if (file.bad())
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : file -
|
|
// fIsStoring -
|
|
// Output : int
|
|
//-----------------------------------------------------------------------------
|
|
int CEditGameClass::SerializeRMF(std::fstream& file, BOOL fIsStoring)
|
|
{
|
|
int iSize, iRvl;
|
|
int iAngle = 0;
|
|
|
|
if (fIsStoring)
|
|
{
|
|
// save data
|
|
WriteString(file, GetClassName());
|
|
file.write((char*) &iAngle, sizeof(iAngle));
|
|
|
|
int nSpawnFlags = GetSpawnFlags();
|
|
file.write((char *)&nSpawnFlags, sizeof(nSpawnFlags));
|
|
|
|
//
|
|
// Write the number of keyvalues.
|
|
//
|
|
iSize = 0;
|
|
for ( int z=m_KeyValues.GetFirst(); z != m_KeyValues.GetInvalidIndex(); z=m_KeyValues.GetNext( z ) )
|
|
{
|
|
iSize++;
|
|
}
|
|
file.write((char*) &iSize, sizeof(int));
|
|
|
|
//
|
|
// Write the keyvalues.
|
|
//
|
|
for ( int z=m_KeyValues.GetFirst(); z != m_KeyValues.GetInvalidIndex(); z=m_KeyValues.GetNext( z ) )
|
|
{
|
|
MDkeyvalue KeyValue = m_KeyValues.GetKeyValue(z);
|
|
|
|
iRvl = KeyValue.SerializeRMF(file, fIsStoring);
|
|
if (iRvl < 0)
|
|
{
|
|
return iRvl;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Save dummy timeline info.
|
|
//
|
|
BOOL bTimeline = FALSE;
|
|
int nTime = 0;
|
|
file.write((char*) &bTimeline, sizeof bTimeline);
|
|
file.write((char*) &nTime, sizeof nTime);
|
|
file.write((char*) &nTime, sizeof nTime);
|
|
}
|
|
else
|
|
{
|
|
char buf[128];
|
|
ReadString(file, buf);
|
|
file.read((char*) &iAngle, sizeof(iAngle));
|
|
|
|
int nSpawnFlags;
|
|
file.read((char *)&nSpawnFlags, sizeof(nSpawnFlags));
|
|
|
|
Assert(buf[0]);
|
|
|
|
CEditGameClass::SetClass(buf, true);
|
|
|
|
//
|
|
// Read the keyvalues.
|
|
//
|
|
file.read((char *) &iSize, sizeof(int));
|
|
for (int i = 0; i < iSize; i++ )
|
|
{
|
|
MDkeyvalue KeyValue;
|
|
iRvl = KeyValue.SerializeRMF(file, fIsStoring);
|
|
if (iRvl < 0)
|
|
{
|
|
return iRvl;
|
|
}
|
|
m_KeyValues.SetValue(KeyValue.szKey, KeyValue.szValue);
|
|
}
|
|
|
|
SetSpawnFlags(nSpawnFlags);
|
|
m_KeyValues.SetValue("classname", buf);
|
|
|
|
// backwards compatibility for old iAngle
|
|
if (iAngle)
|
|
{
|
|
ImportAngle(iAngle);
|
|
}
|
|
|
|
//
|
|
// Dummy timeline information - unused.
|
|
//
|
|
if (fThisVersion >= 1.5f)
|
|
{
|
|
BOOL bTimeline;
|
|
int nTime;
|
|
|
|
file.read((char*) &bTimeline, sizeof bTimeline);
|
|
file.read((char*) &nTime, sizeof nTime);
|
|
file.read((char*) &nTime, sizeof nTime);
|
|
}
|
|
}
|
|
|
|
return file.bad() ? -1 : 0;
|
|
}
|
|
|
|
|
|
int CMapClass::SerializeRMF(std::fstream& file, BOOL fIsStoring)
|
|
{
|
|
int iSize, iRvl;
|
|
|
|
if(fIsStoring)
|
|
{
|
|
// write type
|
|
WriteString(file, GetType());
|
|
|
|
//
|
|
// Write the visgroup ID (zero if none).
|
|
//
|
|
DWORD dwID = 0;
|
|
|
|
/*if (m_pVisGroup)
|
|
{
|
|
// visgroupfixme: how to handle saving RMF? save the first group??
|
|
dwID = m_pVisGroup->GetID();
|
|
}*/
|
|
|
|
file.write((char *)&dwID, sizeof(dwID));
|
|
|
|
//
|
|
// Write the object color.
|
|
//
|
|
file.write((char *)&r, sizeof(BYTE));
|
|
file.write((char *)&g, sizeof(BYTE));
|
|
file.write((char *)&b, sizeof(BYTE));
|
|
|
|
//
|
|
// Save children.
|
|
//
|
|
int nChildCount = 0;
|
|
|
|
FOR_EACH_OBJ( m_Children, pos )
|
|
{
|
|
CMapClass *pChild = m_Children.Element(pos);
|
|
if (pChild->ShouldSerialize())
|
|
{
|
|
nChildCount++;
|
|
}
|
|
}
|
|
|
|
file.write((char *)&nChildCount, sizeof(int));
|
|
|
|
FOR_EACH_OBJ( m_Children, pos )
|
|
{
|
|
CMapClass *pChild = m_Children.Element(pos);
|
|
if (pChild->ShouldSerialize())
|
|
{
|
|
iRvl = pChild->SerializeRMF(file, fIsStoring);
|
|
if (iRvl < 0)
|
|
{
|
|
return iRvl;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// read our stuff
|
|
if(fThisVersion < 1.0f)
|
|
{
|
|
// kill group information .. unfortunate
|
|
file.read((char*) &iSize, sizeof(int));
|
|
file.seekg(iSize, std::ios::cur);
|
|
}
|
|
else
|
|
{
|
|
// just read the visgroup ID but ignore it
|
|
DWORD dwGroupID;
|
|
file.read((char*) &dwGroupID, sizeof(DWORD));
|
|
}
|
|
|
|
//
|
|
// Read the object color.
|
|
//
|
|
file.read((char *)&r, sizeof(BYTE));
|
|
file.read((char *)&g, sizeof(BYTE));
|
|
file.read((char *)&b, sizeof(BYTE));
|
|
|
|
// load children
|
|
file.read((char*) &iSize, sizeof(int));
|
|
for(int i = 0; i < iSize; i++)
|
|
{
|
|
char buf[128];
|
|
ReadString(file, buf);
|
|
CMapClass *pChild = CMapClassManager::CreateObject(buf);
|
|
if(!pChild)
|
|
{
|
|
bCorrupt = TRUE;
|
|
return -1;
|
|
}
|
|
iRvl = pChild->SerializeRMF(file, fIsStoring);
|
|
if(iRvl < 0)
|
|
return iRvl;
|
|
AddChild(pChild);
|
|
}
|
|
}
|
|
|
|
return file.bad() ? -1 : 0;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : file -
|
|
// fIsStoring -
|
|
// Output :
|
|
//-----------------------------------------------------------------------------
|
|
int CMapEntity::SerializeRMF(std::fstream &file, BOOL fIsStoring)
|
|
{
|
|
int iSize;
|
|
Vector Origin;
|
|
|
|
//
|
|
// Read/write base class.
|
|
//
|
|
CMapClass::SerializeRMF(file, fIsStoring);
|
|
CEditGameClass::SerializeRMF(file, fIsStoring);
|
|
|
|
if (fIsStoring)
|
|
{
|
|
// Write flags
|
|
file.write((char*) &flags, sizeof(flags));
|
|
|
|
// Write origin
|
|
GetOrigin(Origin);
|
|
file.write((char *)Origin.Base(), 3 * sizeof(float));
|
|
|
|
// Save padding for unused "complex" field
|
|
iSize = 0;
|
|
file.write((char*) &iSize, sizeof(int));
|
|
}
|
|
else
|
|
{
|
|
// Read flags
|
|
file.read((char *)&flags, sizeof(flags));
|
|
|
|
// Read origin
|
|
file.read((char *)Origin.Base(), 3 * sizeof(float));
|
|
SetOrigin(Origin);
|
|
|
|
if (IsClass())
|
|
{
|
|
// Known class. Determine flags based on the class.
|
|
flags = IsSolidClass() ? (flags & ~flagPlaceholder) : (flags | flagPlaceholder);
|
|
}
|
|
else
|
|
{
|
|
// Unknown class. Determine flags by looking for children (only solid ents have children at this point).
|
|
flags = (m_Children.Count() > 0) ? (flags & ~flagPlaceholder) : (flags | flagPlaceholder);
|
|
}
|
|
|
|
if (!(IsPlaceholder()))
|
|
{
|
|
CMapPoint::SetOrigin(Vector(0, 0, 0));
|
|
}
|
|
|
|
GetOrigin(Origin);
|
|
|
|
// import for previous to 0.5
|
|
if (fThisVersion < 0.5f)
|
|
{
|
|
Origin.z = -Origin.z;
|
|
}
|
|
|
|
// load unused "complex" field
|
|
file.read((char *)&iSize, sizeof(int));
|
|
|
|
SetOrigin(Origin);
|
|
|
|
//
|
|
// HACK: Set our class to NULL so that it is properly set from our "classname"
|
|
// key in PostloadWorld.
|
|
//
|
|
m_szClass[0] = '\0';
|
|
|
|
CalcBounds(TRUE);
|
|
}
|
|
|
|
if (file.bad())
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int CMapWorld::SerializeRMF(std::fstream &file, BOOL fIsStoring)
|
|
{
|
|
float fVersion = 3.7f;
|
|
float fLastCompat = 0.3f;
|
|
|
|
int nSolids = 0;
|
|
int iSize;
|
|
|
|
pLoadingWorld = this;
|
|
bCorrupt = FALSE;
|
|
|
|
// load/save a world
|
|
if(fIsStoring)
|
|
{
|
|
// write version
|
|
file.write((char*) &fVersion, sizeof(fVersion));
|
|
|
|
file.write("RMF", 3);
|
|
|
|
// we don't save vis groups
|
|
iSize = 0;
|
|
file.write((char*) &iSize, sizeof(int));
|
|
|
|
// save children & local data
|
|
if(CMapClass::SerializeRMF(file, fIsStoring) == -1)
|
|
goto FatalError;
|
|
|
|
// save ceditgameclass
|
|
if(CEditGameClass::SerializeRMF(file, fIsStoring) == -1)
|
|
goto FatalError;
|
|
|
|
// save paths
|
|
iSize = m_Paths.Count();
|
|
file.write((char*) &iSize, sizeof(iSize));
|
|
|
|
FOR_EACH_OBJ( m_Paths, pos )
|
|
{
|
|
CMapPath *pPath = m_Paths.Element(pos);
|
|
pPath->SerializeRMF(file, TRUE);
|
|
}
|
|
|
|
if(file.bad())
|
|
goto FatalError;
|
|
}
|
|
else
|
|
{
|
|
// read & check version
|
|
file.read((char*) &fThisVersion, sizeof(fThisVersion));
|
|
if(fThisVersion < fLastCompat || fThisVersion > fVersion)
|
|
{
|
|
CString str;
|
|
str.Format("Oops! SerializeRMF() v%1.1f tried to load a file v%1.1f. Aborting.",
|
|
fVersion, fThisVersion);
|
|
AfxMessageBox(str);
|
|
return -1;
|
|
}
|
|
|
|
char buf[128];
|
|
|
|
if(fThisVersion >= 0.8f)
|
|
{
|
|
file.read(buf, 3);
|
|
if(strncmp(buf, "RMF", 3))
|
|
{
|
|
AfxMessageBox("Invalid file type.");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
// load groups
|
|
if (fThisVersion >= 1.0f)
|
|
{
|
|
file.read((char*) &iSize, sizeof(int));
|
|
|
|
for ( int i = 0; i < iSize; i++)
|
|
{
|
|
// just skip vis groups
|
|
COldVisGroup oldVisGroup;
|
|
file.read((char*) &oldVisGroup, sizeof(COldVisGroup));
|
|
}
|
|
}
|
|
|
|
m_Render2DBox.ResetBounds();
|
|
|
|
// make sure it's a CMapWorld
|
|
ReadString(file, buf);
|
|
if(strcmp(buf, GetType()))
|
|
{
|
|
AfxMessageBox("Invalid file type.");
|
|
return -1;
|
|
}
|
|
|
|
// load children & local data
|
|
if(CMapClass::SerializeRMF(file, fIsStoring) == -1)
|
|
goto FatalError;
|
|
|
|
// load ceditgameclass & CMapClass
|
|
if(CEditGameClass::SerializeRMF(file, fIsStoring) == -1)
|
|
goto FatalError;
|
|
|
|
if(fThisVersion < 1.0f)
|
|
{
|
|
const int old_group_bytes = 134;
|
|
file.read((char*) &iSize, sizeof(int));
|
|
file.seekg(old_group_bytes * iSize, std::ios::cur);
|
|
}
|
|
|
|
// load paths
|
|
if(fThisVersion >= 1.1f)
|
|
{
|
|
file.read((char*) &iSize, sizeof iSize);
|
|
for(int i = 0; i < iSize; i++)
|
|
{
|
|
CMapPath *pPath = new CMapPath;
|
|
pPath->SerializeRMF(file, FALSE);
|
|
if(pPath->GetNodeCount() == 0)
|
|
{
|
|
delete pPath;
|
|
continue; // no add dead paths
|
|
}
|
|
m_Paths.AddToTail(pPath);
|
|
}
|
|
}
|
|
|
|
// read camera
|
|
if(fThisVersion < 1.4f)
|
|
{
|
|
float unused[3];
|
|
file.read((char*) unused, sizeof(float)*3);
|
|
file.read((char*) unused, sizeof(float)*3);
|
|
}
|
|
|
|
if(file.bad())
|
|
goto FatalError;
|
|
|
|
PostloadWorld();
|
|
|
|
if (g_pGameConfig->GetTextureFormat() == tfVMT)
|
|
{
|
|
// do batch search and replace of textures from trans.txt if it exists.
|
|
char translationFilename[MAX_PATH];
|
|
Q_snprintf( translationFilename, sizeof( translationFilename ), "materials/trans.txt" );
|
|
FileHandle_t searchReplaceFP = fopen( translationFilename, "r" );
|
|
if( searchReplaceFP )
|
|
{
|
|
CMapDoc::GetActiveMapDoc()->BatchReplaceTextures( searchReplaceFP );
|
|
g_pFileSystem->Close( searchReplaceFP );
|
|
}
|
|
}
|
|
}
|
|
|
|
return nSolids;
|
|
|
|
FatalError:
|
|
CString str;
|
|
if(bCorrupt)
|
|
{
|
|
// file-is-corrupt error
|
|
str.Format("The file is corrupt.");
|
|
AfxMessageBox(str);
|
|
|
|
return -1;
|
|
}
|
|
|
|
// OS error.
|
|
str.Format("The OS reported an error %s the file: %s",
|
|
fIsStoring ? "saving" : "loading", strerror(errno));
|
|
AfxMessageBox(str);
|
|
|
|
return -1;
|
|
}
|