You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1200 lines
26 KiB
1200 lines
26 KiB
//========= Copyright Valve Corporation, All rights reserved. ============//
|
|
//
|
|
// Purpose:
|
|
//
|
|
// $NoKeywords: $
|
|
//=============================================================================//
|
|
|
|
#include "stdafx.h"
|
|
#include <io.h>
|
|
#include "hammer.h"
|
|
#include "GlobalFunctions.h"
|
|
#include "MapErrorsDlg.h"
|
|
#include "Options.h"
|
|
#include "MapDoc.h"
|
|
#include "MapEntity.h"
|
|
#include "MapFace.h"
|
|
#include "MapGroup.h"
|
|
#include "MapSolid.h"
|
|
#include "MapStudioModel.h"
|
|
#include "MapWorld.h"
|
|
#include "progdlg.h"
|
|
#include "TextureSystem.h"
|
|
#include "MapDisp.h"
|
|
|
|
// memdbgon must be the last include file in a .cpp file!!!
|
|
#include <tier0/memdbgon.h>
|
|
|
|
#pragma optimize("g", off)
|
|
|
|
#pragma warning(disable: 4748) // buffer overrung with optimizations off - remove if we turn "g" back on
|
|
|
|
#define TEXTURE_NAME_LEN 128
|
|
|
|
// All files are opened in binary, and we want to save CR/LF
|
|
#define ENDLINE "\r\n"
|
|
|
|
|
|
enum
|
|
{
|
|
fileOsError = -1, // big error!
|
|
fileError = -2, // problem
|
|
fileOk = -3, // loaded ok
|
|
fileDone = -4 // got not-my-kind of line
|
|
};
|
|
|
|
|
|
BOOL bSaveVisiblesOnly;
|
|
|
|
|
|
static BOOL bErrors;
|
|
static int nInvalidSolids;
|
|
static CProgressDlg *pProgDlg;
|
|
|
|
static MAPFORMAT MapFormat;
|
|
|
|
static BOOL bStuffed;
|
|
static char szStuffed[255];
|
|
|
|
static UINT uMapVersion = 0;
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : buf -
|
|
//-----------------------------------------------------------------------------
|
|
static void StuffLine(char * buf)
|
|
{
|
|
Assert(!bStuffed);
|
|
V_strcpy_safe(szStuffed, buf);
|
|
bStuffed = TRUE;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : file -
|
|
// buf -
|
|
//-----------------------------------------------------------------------------
|
|
static void GetLine(std::fstream& file, char *buf)
|
|
{
|
|
if(bStuffed)
|
|
{
|
|
if(buf)
|
|
strcpy(buf, szStuffed);
|
|
bStuffed = FALSE;
|
|
return;
|
|
}
|
|
|
|
char szBuf[1024];
|
|
|
|
while(1)
|
|
{
|
|
file >> std::ws;
|
|
file.getline(szBuf, 512);
|
|
if(file.eof())
|
|
return;
|
|
if(!strncmp(szBuf, "//", 2))
|
|
continue;
|
|
file >> std::ws;
|
|
if(buf)
|
|
{
|
|
// char *p = strchr(szBuf, '\n');
|
|
// if(p) p[0] = 0;
|
|
// p = strchr(szBuf, '\r');
|
|
// if(p) p[0] = 0;
|
|
strcpy(buf, szBuf);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : pObject -
|
|
// file -
|
|
// pIntersecting -
|
|
// Output : int
|
|
//-----------------------------------------------------------------------------
|
|
static int SaveSolidChildrenOf(CMapClass *pObject, std::fstream& file, BoundBox *pIntersecting = NULL)
|
|
{
|
|
CMapWorld *pWorld = (CMapWorld*) CMapClass::GetWorldObject(pObject);
|
|
|
|
//
|
|
// If we are only saving visible objects and this object isn't visible, don't save it.
|
|
//
|
|
if (bSaveVisiblesOnly && (pObject != pWorld) && !pObject->IsVisible())
|
|
{
|
|
return fileOk; // not an error - return ok
|
|
}
|
|
|
|
//
|
|
// If we are only saving objects within a particular bounding box and this object isn't, don't save it.
|
|
//
|
|
if (pIntersecting && !pObject->IsIntersectingBox(pIntersecting->bmins, pIntersecting->bmaxs))
|
|
{
|
|
return fileOk;
|
|
}
|
|
|
|
|
|
const CMapObjectList *pChildren = pObject->GetChildren();
|
|
FOR_EACH_OBJ( *pChildren, pos )
|
|
{
|
|
int iRvl = -1;
|
|
CMapClass *pChild = pChildren->Element(pos);
|
|
|
|
if (!pIntersecting || pChild->IsIntersectingBox(pIntersecting->bmins, pIntersecting->bmaxs))
|
|
{
|
|
if (pChild->IsMapClass(MAPCLASS_TYPE(CMapSolid)))
|
|
{
|
|
if (!bSaveVisiblesOnly || pChild->IsVisible())
|
|
{
|
|
iRvl = pChild->SerializeMAP(file, TRUE);
|
|
}
|
|
}
|
|
else if (pChild->IsMapClass(MAPCLASS_TYPE(CMapGroup)))
|
|
{
|
|
iRvl = SaveSolidChildrenOf(pChild, file, pIntersecting);
|
|
}
|
|
|
|
// return error if there is an error
|
|
if (iRvl != -1 && iRvl != fileOk)
|
|
{
|
|
return iRvl;
|
|
}
|
|
}
|
|
}
|
|
|
|
return fileOk; // ok.
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : pObject -
|
|
// file -
|
|
// pIntersecting -
|
|
// Output : int
|
|
//-----------------------------------------------------------------------------
|
|
static int SaveEntityChildrenOf(CMapClass *pObject, std::fstream& file, BoundBox *pIntersecting)
|
|
{
|
|
CMapWorld *pWorld = (CMapWorld *)CMapClass::GetWorldObject(pObject);
|
|
|
|
if (bSaveVisiblesOnly && pObject != pWorld && !pObject->IsVisible())
|
|
{
|
|
return fileOk; // no error
|
|
}
|
|
|
|
if (pIntersecting && !pObject->IsIntersectingBox(pIntersecting->bmins, pIntersecting->bmaxs))
|
|
{
|
|
return fileOk;
|
|
}
|
|
|
|
|
|
const CMapObjectList *pChildren = pObject->GetChildren();
|
|
FOR_EACH_OBJ( *pChildren, pos )
|
|
{
|
|
int iRvl = -1;
|
|
|
|
CMapClass *pChild = pChildren->Element(pos);
|
|
|
|
if (!pIntersecting || pChild->IsIntersectingBox(pIntersecting->bmins, pIntersecting->bmaxs))
|
|
{
|
|
if (pChild->IsMapClass(MAPCLASS_TYPE(CMapEntity)))
|
|
{
|
|
if (!bSaveVisiblesOnly || pChild->IsVisible())
|
|
{
|
|
iRvl = pChild->SerializeMAP(file, TRUE);
|
|
}
|
|
}
|
|
else if (pChild->IsMapClass(MAPCLASS_TYPE(CMapGroup)))
|
|
{
|
|
iRvl = SaveEntityChildrenOf(pChild, file, pIntersecting);
|
|
}
|
|
|
|
// return error if there is an error
|
|
if (iRvl != -1 && iRvl != fileOk)
|
|
{
|
|
return iRvl;
|
|
}
|
|
}
|
|
}
|
|
|
|
return fileOk; // ok.
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : *pObject -
|
|
// file -
|
|
// Output : int
|
|
//-----------------------------------------------------------------------------
|
|
static int ReadSolids(CMapClass *pObject, std::fstream& file)
|
|
{
|
|
int nSolids = 0;
|
|
char szBuf[128];
|
|
|
|
while(1)
|
|
{
|
|
GetLine(file, szBuf);
|
|
if(szBuf[0] != '{')
|
|
{
|
|
StuffLine(szBuf);
|
|
break;
|
|
}
|
|
|
|
CMapSolid *pSolid = new CMapSolid;
|
|
int iRvl = pSolid->SerializeMAP(file, FALSE);
|
|
if(iRvl == fileError)
|
|
{
|
|
// delete the solid
|
|
delete pSolid;
|
|
++nInvalidSolids;
|
|
}
|
|
else if(iRvl == fileOsError)
|
|
{
|
|
// big problem
|
|
delete pSolid;
|
|
return fileOsError;
|
|
}
|
|
else
|
|
pObject->AddChild(pSolid);
|
|
|
|
++nSolids;
|
|
}
|
|
|
|
return nSolids;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : file -
|
|
// Output : int
|
|
//-----------------------------------------------------------------------------
|
|
static int PeekChar(std::fstream& file)
|
|
{
|
|
if(bStuffed) // stuffed.. return first char
|
|
return szStuffed[0];
|
|
char szBuf[1024];
|
|
szBuf[0] = 0;
|
|
// get next line
|
|
GetLine(file, szBuf);
|
|
|
|
// still blank? return eof
|
|
if(szBuf[0] == 0)
|
|
return EOF;
|
|
|
|
// to get it next call to getline
|
|
StuffLine(szBuf);
|
|
|
|
return szBuf[0];
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose: Sets the MAP format for saving.
|
|
// Input : mf - MAP format to use when saving.
|
|
//-----------------------------------------------------------------------------
|
|
void SetMapFormat(MAPFORMAT mf)
|
|
{
|
|
Assert((mf == mfHalfLife) || (mf == mfHalfLife2));
|
|
MapFormat = mf;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : file -
|
|
// fIsStoring -
|
|
// Output : int
|
|
//-----------------------------------------------------------------------------
|
|
int CMapClass::SerializeMAP(std::fstream& file, BOOL fIsStoring)
|
|
{
|
|
// no info stored in MAPs ..
|
|
return fileOk;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : file -
|
|
// fIsStoring -
|
|
// Output : int
|
|
//-----------------------------------------------------------------------------
|
|
int CMapFace::SerializeMAP(std::fstream& file, BOOL fIsStoring)
|
|
{
|
|
char szBuf[512];
|
|
|
|
if (fIsStoring)
|
|
{
|
|
char *pszTexture;
|
|
char szTexture[sizeof(texture.texture)+1];
|
|
szTexture[sizeof(texture.texture)] = 0;
|
|
memcpy(szTexture, texture.texture, sizeof texture.texture);
|
|
strlwr(szTexture);
|
|
|
|
if (MapFormat == mfQuake2)
|
|
{
|
|
pszTexture = strstr(szTexture, ".");
|
|
if (pszTexture)
|
|
{
|
|
*pszTexture = 0;
|
|
}
|
|
pszTexture = strstr(szTexture, "textures\\");
|
|
if (pszTexture == NULL)
|
|
{
|
|
pszTexture = szTexture;
|
|
}
|
|
else
|
|
{
|
|
pszTexture += strlen("textures\\");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pszTexture = szTexture;
|
|
}
|
|
|
|
strupr(szTexture);
|
|
|
|
|
|
//
|
|
// Reverse the slashes -- thank you id.
|
|
//
|
|
for (int i = strlen(pszTexture) - 1; i >= 0; i--)
|
|
{
|
|
if (pszTexture[i] == '\\')
|
|
pszTexture[i] = '/';
|
|
}
|
|
|
|
//
|
|
// Convert the plane points to integers.
|
|
//
|
|
for (int nPlane = 0; nPlane < 3; nPlane++)
|
|
{
|
|
plane.planepts[nPlane][0] = V_rint(plane.planepts[nPlane][0]);
|
|
plane.planepts[nPlane][1] = V_rint(plane.planepts[nPlane][1]);
|
|
plane.planepts[nPlane][2] = V_rint(plane.planepts[nPlane][2]);
|
|
}
|
|
|
|
//
|
|
// Check for duplicate plane points. All three plane points must be unique
|
|
// or it isn't a valid plane. Try to fix it if it isn't valid.
|
|
//
|
|
if (!CheckFace())
|
|
{
|
|
Fix();
|
|
}
|
|
|
|
sprintf(szBuf,
|
|
"( %.0f %.0f %.0f ) ( %.0f %.0f %.0f ) ( %.0f %.0f %.0f ) "
|
|
"%s "
|
|
"[ %g %g %g %g ] "
|
|
"[ %g %g %g %g ] "
|
|
"%g %g %g ",
|
|
|
|
plane.planepts[0][0], plane.planepts[0][1], plane.planepts[0][2],
|
|
plane.planepts[1][0], plane.planepts[1][1], plane.planepts[1][2],
|
|
plane.planepts[2][0], plane.planepts[2][1], plane.planepts[2][2],
|
|
|
|
pszTexture,
|
|
|
|
(double)texture.UAxis[0], (double)texture.UAxis[1], (double)texture.UAxis[2], (double)texture.UAxis[3],
|
|
(double)texture.VAxis[0], (double)texture.VAxis[1], (double)texture.VAxis[2], (double)texture.VAxis[3],
|
|
|
|
(double)texture.rotate,
|
|
(double)texture.scale[0],
|
|
(double)texture.scale[1]);
|
|
|
|
file << szBuf << ENDLINE;
|
|
|
|
return fileOk;
|
|
}
|
|
else
|
|
{
|
|
// load the plane
|
|
GetLine(file, szBuf);
|
|
|
|
if(szBuf[0] != '(')
|
|
{
|
|
StuffLine(szBuf);
|
|
return fileDone;
|
|
}
|
|
|
|
char szTexName[TEXTURE_NAME_LEN];
|
|
DWORD q2contents;
|
|
DWORD q2surface;
|
|
int nLightmapScale;
|
|
int nDummy;
|
|
int nRead;
|
|
|
|
if( uMapVersion >= 340 )
|
|
{
|
|
COMPILE_TIME_ASSERT( ARRAYSIZE(szTexName) == 128 );
|
|
nRead = sscanf(szBuf,
|
|
"( %f %f %f ) ( %f %f %f ) ( %f %f %f ) "
|
|
"%127s "
|
|
"[ %f %f %f %f ] "
|
|
"[ %f %f %f %f ] "
|
|
"%f %f %f "
|
|
"%u %u %u",
|
|
|
|
&plane.planepts[0][0], &plane.planepts[0][1], &plane.planepts[0][2],
|
|
&plane.planepts[1][0], &plane.planepts[1][1], &plane.planepts[1][2],
|
|
&plane.planepts[2][0], &plane.planepts[2][1], &plane.planepts[2][2],
|
|
|
|
szTexName,
|
|
|
|
&texture.UAxis[0], &texture.UAxis[1], &texture.UAxis[2], &texture.UAxis[3],
|
|
&texture.VAxis[0], &texture.VAxis[1], &texture.VAxis[2], &texture.VAxis[3],
|
|
|
|
&texture.rotate,
|
|
&texture.scale[0],
|
|
&texture.scale[1],
|
|
|
|
&q2contents,
|
|
&q2surface,
|
|
&nLightmapScale);
|
|
// Guarantee null-termination to keep /analyze happy.
|
|
szTexName[ ARRAYSIZE(szTexName) - 1 ] = 0;
|
|
|
|
if (nRead < 21)
|
|
{
|
|
bErrors = TRUE;
|
|
}
|
|
else if (nRead == 24)
|
|
{
|
|
// got q2 values - set them here
|
|
texture.q2contents = q2contents;
|
|
texture.q2surface = q2surface;
|
|
texture.nLightmapScale = nLightmapScale;
|
|
if (texture.nLightmapScale == 0)
|
|
{
|
|
texture.nLightmapScale = g_pGameConfig->GetDefaultLightmapScale();
|
|
}
|
|
}
|
|
|
|
//
|
|
// very cheesy HACK!!! -- this will be better when we have chunks
|
|
//
|
|
if( uMapVersion <= 350 )
|
|
{
|
|
if( ( file.peek() != '(' ) && ( file.peek() != '}' ) )
|
|
{
|
|
EditDispHandle_t handle = EditDispMgr()->Create();
|
|
SetDisp( handle );
|
|
|
|
CMapDisp *pDisp = EditDispMgr()->GetDisp( handle );
|
|
pDisp->SerializedLoadMAP( file, this, uMapVersion );
|
|
}
|
|
}
|
|
}
|
|
else if (uMapVersion >= 220 )
|
|
{
|
|
COMPILE_TIME_ASSERT( ARRAYSIZE(szTexName) == 128 );
|
|
nRead = sscanf(szBuf,
|
|
"( %f %f %f ) ( %f %f %f ) ( %f %f %f ) "
|
|
"%127s "
|
|
"[ %f %f %f %f ] "
|
|
"[ %f %f %f %f ] "
|
|
"%f %f %f "
|
|
"%u %u %u",
|
|
|
|
&plane.planepts[0][0], &plane.planepts[0][1], &plane.planepts[0][2],
|
|
&plane.planepts[1][0], &plane.planepts[1][1], &plane.planepts[1][2],
|
|
&plane.planepts[2][0], &plane.planepts[2][1], &plane.planepts[2][2],
|
|
|
|
szTexName,
|
|
|
|
&texture.UAxis[0], &texture.UAxis[1], &texture.UAxis[2], &texture.UAxis[3],
|
|
&texture.VAxis[0], &texture.VAxis[1], &texture.VAxis[2], &texture.VAxis[3],
|
|
|
|
&texture.rotate,
|
|
&texture.scale[0],
|
|
&texture.scale[1],
|
|
|
|
&q2contents,
|
|
&q2surface,
|
|
&nDummy); // Pre-340 didn't have lightmap scale.
|
|
// Guarantee null-termination to keep /analyze happy.
|
|
szTexName[ ARRAYSIZE(szTexName) - 1 ] = 0;
|
|
|
|
if (nRead < 21)
|
|
{
|
|
bErrors = TRUE;
|
|
}
|
|
else if (nRead == 24)
|
|
{
|
|
// got q2 values - set them here
|
|
texture.q2contents = q2contents;
|
|
texture.q2surface = q2surface;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
COMPILE_TIME_ASSERT( ARRAYSIZE(szTexName) == 128 );
|
|
nRead = sscanf(szBuf,
|
|
"( %f %f %f ) ( %f %f %f ) ( %f %f %f ) "
|
|
"%127s "
|
|
"%f %f %f "
|
|
"%f %f %u %u %u",
|
|
|
|
&plane.planepts[0][0], &plane.planepts[0][1], &plane.planepts[0][2],
|
|
&plane.planepts[1][0], &plane.planepts[1][1], &plane.planepts[1][2],
|
|
&plane.planepts[2][0], &plane.planepts[2][1], &plane.planepts[2][2],
|
|
|
|
szTexName,
|
|
|
|
&texture.UAxis[3],
|
|
&texture.VAxis[3],
|
|
&texture.rotate,
|
|
&texture.scale[0],
|
|
&texture.scale[1],
|
|
|
|
&q2contents,
|
|
&q2surface,
|
|
&nDummy); // Pre-340 didn't have lightmap scale.
|
|
// Guarantee null-termination to keep /analyze happy.
|
|
szTexName[ ARRAYSIZE(szTexName) - 1 ] = 0;
|
|
|
|
if (nRead < 15)
|
|
{
|
|
bErrors = TRUE;
|
|
}
|
|
else if (nRead == 18)
|
|
{
|
|
// got q2 values - set them here
|
|
texture.q2contents = q2contents;
|
|
texture.q2surface = q2surface;
|
|
}
|
|
}
|
|
|
|
if (g_pGameConfig->GetTextureFormat() != tfVMT)
|
|
{
|
|
// reverse the slashes -- thank you id
|
|
for (int i = strlen(szTexName) - 1; i >= 0; i--)
|
|
{
|
|
if (szTexName[i] == '/')
|
|
szTexName[i] = '\\';
|
|
}
|
|
}
|
|
|
|
SetTexture(szTexName);
|
|
}
|
|
|
|
if (file.fail())
|
|
{
|
|
return fileOsError;
|
|
}
|
|
return fileOk;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : file -
|
|
// fIsStoring -
|
|
// Output : int
|
|
//-----------------------------------------------------------------------------
|
|
int MDkeyvalue::SerializeMAP(std::fstream& file, BOOL fIsStoring)
|
|
{
|
|
// load/save a keyvalue
|
|
char szBuf[1024];
|
|
|
|
if(fIsStoring)
|
|
{
|
|
// save a keyvalue
|
|
sprintf( szBuf,
|
|
"\"%s\" \"%s\"",
|
|
|
|
Key(), Value() );
|
|
|
|
file << szBuf << ENDLINE;
|
|
}
|
|
else
|
|
{
|
|
GetLine(file, szBuf);
|
|
if(szBuf[0] != '\"')
|
|
{
|
|
StuffLine(szBuf);
|
|
return fileDone;
|
|
}
|
|
char *p = strchr(szBuf, '\"');
|
|
p = strchr(p+1, '\"');
|
|
if(!p)
|
|
return fileError;
|
|
p[0] = 0;
|
|
V_strcpy_safe(szKey, szBuf+1);
|
|
|
|
// advance to start of value string
|
|
p = strchr(p+1, '\"');
|
|
if(!p)
|
|
return fileError;
|
|
// ocpy in value
|
|
strcpy(szValue, p+1);
|
|
// kill trailing "
|
|
p = strchr(szValue, '\"');
|
|
if(!p)
|
|
return fileError;
|
|
p[0] = 0;
|
|
}
|
|
|
|
return file.fail() ? fileOsError : fileOk;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : file -
|
|
// fIsStoring -
|
|
// Output : int
|
|
//-----------------------------------------------------------------------------
|
|
int CMapSolid::SerializeMAP(std::fstream& file, BOOL fIsStoring)
|
|
{
|
|
CMapClass::SerializeMAP(file, fIsStoring);
|
|
|
|
// load/save a brush
|
|
if (fIsStoring)
|
|
{
|
|
// save the brush
|
|
file << "{" << ENDLINE;
|
|
|
|
// serialize the Faces
|
|
int nFaces = Faces.GetCount();
|
|
for(int i = 0; i < nFaces; i++)
|
|
{
|
|
if(!Faces[i].Points)
|
|
continue;
|
|
if(Faces[i].SerializeMAP(file, fIsStoring) == fileError)
|
|
return fileError;
|
|
}
|
|
|
|
// terminator
|
|
file << "}" << ENDLINE;
|
|
}
|
|
else
|
|
{
|
|
// caller skipped delimiter
|
|
Faces.SetCount(0);
|
|
|
|
// read Faces
|
|
for(int i = 0; ; i++)
|
|
{
|
|
// extract plane
|
|
if (Faces[i].SerializeMAP(file, fIsStoring) == fileDone)
|
|
{
|
|
// when fileDone is returned, no face was loaded
|
|
break;
|
|
}
|
|
|
|
Faces[i].CalcPlane();
|
|
}
|
|
|
|
GetLine(file, NULL); // ignore line
|
|
|
|
if (!file.fail())
|
|
{
|
|
//
|
|
// Create the solid using the planes that were read from the MAP file.
|
|
//
|
|
if (CreateFromPlanes() == FALSE)
|
|
{
|
|
bErrors = TRUE;
|
|
return(fileError);
|
|
}
|
|
|
|
//
|
|
// If we are reading from an old map file, the texture axes will need to be set up.
|
|
// Leave the rotation and shifts alone; they were read from the MAP file.
|
|
//
|
|
if (uMapVersion < 220)
|
|
{
|
|
InitializeTextureAxes(TEXTURE_ALIGN_QUAKE, INIT_TEXTURE_AXES | INIT_TEXTURE_FORCE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return(fileOsError);
|
|
}
|
|
|
|
CalcBounds();
|
|
|
|
//
|
|
// Set solid type based on texture name.
|
|
//
|
|
m_eSolidType = HL1SolidTypeFromTextureName(Faces[0].texture.texture);
|
|
}
|
|
|
|
return(file.fail() ? fileOsError : fileOk);
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : file -
|
|
// fIsStoring -
|
|
// Output : int
|
|
//-----------------------------------------------------------------------------
|
|
int CMapEntity::SerializeMAP(std::fstream &file, BOOL fIsStoring)
|
|
{
|
|
CMapClass::SerializeMAP(file, fIsStoring);
|
|
|
|
// load/save an object
|
|
if (fIsStoring)
|
|
{
|
|
//
|
|
// If it's a solidentity but it doesn't have any solids,
|
|
// don't save it.
|
|
//
|
|
if (!IsPlaceholder() && !m_Children.Count())
|
|
{
|
|
return(fileOk);
|
|
}
|
|
|
|
//
|
|
// Save it.
|
|
//
|
|
file << "{" << ENDLINE;
|
|
|
|
//
|
|
// Save keyvalues & other data.
|
|
//
|
|
CEditGameClass::SerializeMAP(file, fIsStoring);
|
|
|
|
//
|
|
// If this is a placeholder and either has no class or is not a solid class,
|
|
// save our origin.
|
|
//
|
|
if (IsPlaceholder() && (!IsClass() || !IsSolidClass()))
|
|
{
|
|
MDkeyvalue tmpkv;
|
|
strcpy(tmpkv.szKey, "origin");
|
|
|
|
Vector Origin;
|
|
GetOrigin(Origin);
|
|
sprintf(tmpkv.szValue, "%.0f %.0f %.0f", Origin[0], Origin[1], Origin[2]);
|
|
tmpkv.SerializeMAP(file, fIsStoring);
|
|
}
|
|
|
|
if (!(IsPlaceholder()))
|
|
{
|
|
SaveSolidChildrenOf(this, file);
|
|
}
|
|
|
|
file << "}" << ENDLINE;
|
|
}
|
|
else
|
|
{
|
|
// load keyvalues
|
|
CEditGameClass::SerializeMAP(file, fIsStoring);
|
|
|
|
// solids
|
|
if (!ReadSolids(this, file))
|
|
{
|
|
flags |= flagPlaceholder;
|
|
}
|
|
|
|
// skip delimiter
|
|
GetLine(file, NULL);
|
|
}
|
|
|
|
return file.fail() ? fileOsError : fileOk;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : file -
|
|
// fIsStoring -
|
|
// Output : int
|
|
//-----------------------------------------------------------------------------
|
|
int CEditGameClass::SerializeMAP(std::fstream& file, BOOL fIsStoring)
|
|
{
|
|
int iRvl;
|
|
|
|
if (!fIsStoring)
|
|
{
|
|
// loading
|
|
if (PeekChar(file) == '\"')
|
|
{
|
|
// read kv pairs
|
|
MDkeyvalue newkv;
|
|
while (1)
|
|
{
|
|
if (newkv.SerializeMAP(file, fIsStoring) != fileOk)
|
|
{
|
|
// fileDone means the keyvalue was not loaded
|
|
break;
|
|
}
|
|
|
|
if (!strcmp(newkv.szKey, "classname"))
|
|
{
|
|
m_KeyValues.SetValue(newkv.szKey, newkv.szValue);
|
|
}
|
|
else if (!strcmp(newkv.szKey, "angle"))
|
|
{
|
|
ImportAngle(atoi(newkv.szValue));
|
|
}
|
|
else if (strcmp(newkv.szKey, "wad"))
|
|
{
|
|
//
|
|
// All other keys are simply added to the keyvalue list.
|
|
//
|
|
m_KeyValues.SetValue(newkv.szKey, newkv.szValue);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// save keyvalues
|
|
MDkeyvalue tmpkv;
|
|
|
|
if (GetKeyValue("classname") == NULL)
|
|
{
|
|
tmpkv.Set("classname", m_szClass);
|
|
tmpkv.SerializeMAP(file, fIsStoring);
|
|
}
|
|
|
|
//
|
|
// Determine whether we have a game data class. This will help us decide which keys
|
|
// to write.
|
|
//
|
|
GDclass *pGameDataClass = NULL;
|
|
if (pGD != NULL)
|
|
{
|
|
pGameDataClass = pGD->ClassForName(m_szClass);
|
|
}
|
|
|
|
//
|
|
// Consider all the keyvalues in this object for serialization.
|
|
//
|
|
for ( int z=m_KeyValues.GetFirst(); z != m_KeyValues.GetInvalidIndex(); z=m_KeyValues.GetNext( z ) )
|
|
{
|
|
MDkeyvalue &KeyValue = m_KeyValues.GetKeyValue(z);
|
|
|
|
iRvl = KeyValue.SerializeMAP(file, fIsStoring);
|
|
if (iRvl != fileOk)
|
|
{
|
|
return(iRvl);
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we have a base class, for each keyvalue in the class definition, write out all keys
|
|
// that are not present in the object and whose defaults are nonzero in the class definition.
|
|
//
|
|
if (pGameDataClass != NULL)
|
|
{
|
|
//
|
|
// For each variable from the base class...
|
|
//
|
|
int nVariableCount = pGameDataClass->GetVariableCount();
|
|
for (int i = 0; i < nVariableCount; i++)
|
|
{
|
|
GDinputvariable *pVar = pGameDataClass->GetVariableAt(i);
|
|
Assert(pVar != NULL);
|
|
|
|
if (pVar != NULL)
|
|
{
|
|
int iIndex;
|
|
MDkeyvalue *pKey;
|
|
LPCTSTR p = m_KeyValues.GetValue(pVar->GetName(), &iIndex);
|
|
|
|
//
|
|
// If the variable is not present in this object, write out the default value.
|
|
//
|
|
if (p == NULL)
|
|
{
|
|
pKey = &tmpkv;
|
|
pVar->ResetDefaults();
|
|
pVar->ToKeyValue(pKey);
|
|
|
|
//
|
|
// Only write the key value if it is non-zero.
|
|
//
|
|
if ((pKey->szKey[0] != 0) && (pKey->szValue[0] != 0) && (stricmp(pKey->szValue, "0")))
|
|
{
|
|
iRvl = pKey->SerializeMAP(file, fIsStoring);
|
|
if (iRvl != fileOk)
|
|
{
|
|
return(iRvl);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return(file.fail() ? fileOsError : fileOk);
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Purpose:
|
|
// Input : file -
|
|
// fIsStoring -
|
|
// pIntersecting -
|
|
// Output : int
|
|
//-----------------------------------------------------------------------------
|
|
int CMapWorld::SerializeMAP(std::fstream &file, BOOL fIsStoring, BoundBox *pIntersecting)
|
|
{
|
|
int iRvl;
|
|
|
|
bStuffed = FALSE;
|
|
bErrors = FALSE;
|
|
nInvalidSolids = 0;
|
|
|
|
// load/save a world
|
|
if (fIsStoring)
|
|
{
|
|
file << "{" << ENDLINE;
|
|
|
|
// save worldobject
|
|
CEditGameClass::SerializeMAP(file, fIsStoring);
|
|
|
|
if (MapFormat != mfQuake2)
|
|
{
|
|
MDkeyvalue tmpkv;
|
|
|
|
strcpy(tmpkv.szKey, "mapversion");
|
|
strcpy(tmpkv.szValue, "360");
|
|
tmpkv.SerializeMAP(file, fIsStoring);
|
|
|
|
// Save wad file line
|
|
strcpy(tmpkv.szKey, "wad");
|
|
|
|
// copy all texfiles into value
|
|
tmpkv.szValue[0] = 0;
|
|
BOOL bFirst = TRUE;
|
|
int nGraphicsFiles = g_Textures.FilesGetCount();
|
|
for (int i = 0; i < nGraphicsFiles; i++)
|
|
{
|
|
char szFile[MAX_PATH];
|
|
GRAPHICSFILESTRUCT gf;
|
|
|
|
g_Textures.FilesGetInfo(&gf, i);
|
|
|
|
//
|
|
// Don't save WAL files - they're never used.
|
|
//
|
|
if (gf.format != tfWAL)
|
|
{
|
|
//
|
|
// Also make sure this is the right kind of WAD file
|
|
// based on the game we're using.
|
|
//
|
|
if (gf.format == g_pGameConfig->GetTextureFormat())
|
|
{
|
|
//
|
|
// Append this WAD file to the WAD list.
|
|
//
|
|
strcpy(szFile, gf.filename);
|
|
|
|
// dvs: Strip off the path. This crashes VIS and QRAD!!
|
|
/*
|
|
char *pszSlash = strrchr(szFile, '\\');
|
|
if (pszSlash == NULL)
|
|
{
|
|
pszSlash = strrchr(szFile, '/');
|
|
}
|
|
|
|
if (pszSlash != NULL)
|
|
{
|
|
pszSlash++;
|
|
}
|
|
else
|
|
{
|
|
pszSlash = szFile;
|
|
}
|
|
*/
|
|
|
|
char *pszSlash = szFile;
|
|
|
|
// Strip off any drive letter.
|
|
if (pszSlash[1] == ':')
|
|
{
|
|
pszSlash += 2;
|
|
}
|
|
|
|
// WAD names are semicolon delimited.
|
|
if (!bFirst)
|
|
{
|
|
strcat(tmpkv.szValue, ";");
|
|
}
|
|
|
|
strcat(tmpkv.szValue, pszSlash);
|
|
bFirst = FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( tmpkv.szValue[0] != '\0' )
|
|
{
|
|
tmpkv.SerializeMAP(file, fIsStoring);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Save the brushes.
|
|
//
|
|
if (SaveSolidChildrenOf(this, file, pIntersecting) == fileOsError)
|
|
{
|
|
goto FatalError;
|
|
}
|
|
|
|
file << "}" << ENDLINE;
|
|
|
|
//
|
|
// Save the entities.
|
|
//
|
|
if (SaveEntityChildrenOf(this, file, pIntersecting) == fileOsError)
|
|
{
|
|
goto FatalError;
|
|
}
|
|
|
|
//
|
|
// Save paths (if paths are visible).
|
|
//
|
|
FOR_EACH_OBJ( m_Paths, pos )
|
|
{
|
|
CMapPath *pPath = m_Paths.Element(pos);
|
|
pPath->SerializeMAP(file, TRUE, pIntersecting);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pProgDlg = new CProgressDlg;
|
|
pProgDlg->Create();
|
|
pProgDlg->SetStep(1);
|
|
|
|
CString caption;
|
|
caption.LoadString(IDS_LOADINGFILE);
|
|
pProgDlg->SetWindowText(caption);
|
|
|
|
m_Render2DBox.ResetBounds();
|
|
|
|
// load world
|
|
GetLine(file, NULL); // ignore delimiter
|
|
CEditGameClass::SerializeMAP(file, fIsStoring);
|
|
|
|
const char* pszMapVersion;
|
|
|
|
pszMapVersion = m_KeyValues.GetValue("mapversion");
|
|
if (pszMapVersion != NULL)
|
|
{
|
|
uMapVersion = atoi(pszMapVersion);
|
|
}
|
|
else
|
|
{
|
|
uMapVersion = 0;
|
|
}
|
|
|
|
// read solids
|
|
if (ReadSolids(this, file) == fileOsError)
|
|
{
|
|
goto FatalError;
|
|
}
|
|
|
|
// skip end-of-entity marker
|
|
GetLine(file, NULL);
|
|
|
|
char szBuf[128];
|
|
|
|
// read entities
|
|
while (1)
|
|
{
|
|
GetLine(file, szBuf);
|
|
if (szBuf[0] != '{')
|
|
{
|
|
StuffLine(szBuf);
|
|
break;
|
|
}
|
|
|
|
if (PeekChar(file) == EOF)
|
|
{
|
|
break;
|
|
}
|
|
|
|
CMapEntity *pEntity;
|
|
|
|
pEntity = new CMapEntity;
|
|
iRvl = pEntity->SerializeMAP(file, fIsStoring);
|
|
AddChild(pEntity);
|
|
|
|
if (iRvl == fileError)
|
|
{
|
|
bErrors = TRUE;
|
|
}
|
|
else if (iRvl == fileOsError)
|
|
{
|
|
goto FatalError;
|
|
}
|
|
}
|
|
|
|
if (bErrors)
|
|
{
|
|
if (nInvalidSolids)
|
|
{
|
|
CString str;
|
|
str.Format("For your information, %d solids were not loaded\n"
|
|
"due to errors in the file.", nInvalidSolids);
|
|
AfxMessageBox(str);
|
|
}
|
|
else if (AfxMessageBox("There was a problem loading the MAP file. Do you\n"
|
|
"want to view the error report?", MB_YESNO) == IDYES)
|
|
{
|
|
CMapErrorsDlg dlg;
|
|
dlg.DoModal();
|
|
}
|
|
}
|
|
|
|
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" );
|
|
if( CMapDoc::GetActiveMapDoc() )
|
|
{
|
|
FileHandle_t searchReplaceFP = g_pFileSystem->Open( translationFilename, "r" );
|
|
if( searchReplaceFP )
|
|
{
|
|
CMapDoc::GetActiveMapDoc()->BatchReplaceTextures( searchReplaceFP );
|
|
g_pFileSystem->Close( searchReplaceFP );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pProgDlg)
|
|
{
|
|
pProgDlg->DestroyWindow();
|
|
delete pProgDlg;
|
|
pProgDlg = NULL;
|
|
}
|
|
|
|
return (bErrors && fIsStoring) ? -1 : 0;
|
|
|
|
FatalError:
|
|
|
|
// OS error.
|
|
CString str;
|
|
str.Format("The OS reported an error %s the file: %s", fIsStoring ? "saving" : "loading", strerror(errno));
|
|
AfxMessageBox(str);
|
|
|
|
if (pProgDlg != NULL)
|
|
{
|
|
pProgDlg->DestroyWindow();
|
|
delete pProgDlg;
|
|
pProgDlg = NULL;
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|