|
|
//========= 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; }
|