diff options
Diffstat (limited to 'hammer/loadsave_rmf.cpp')
| -rw-r--r-- | hammer/loadsave_rmf.cpp | 926 |
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; +} |