summaryrefslogtreecommitdiff
path: root/hammer/loadsave_rmf.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /hammer/loadsave_rmf.cpp
downloadarchived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.tar.xz
archived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.zip
Diffstat (limited to 'hammer/loadsave_rmf.cpp')
-rw-r--r--hammer/loadsave_rmf.cpp926
1 files changed, 926 insertions, 0 deletions
diff --git a/hammer/loadsave_rmf.cpp b/hammer/loadsave_rmf.cpp
new file mode 100644
index 0000000..1cfe483
--- /dev/null
+++ b/hammer/loadsave_rmf.cpp
@@ -0,0 +1,926 @@
+//========= 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;
+}