diff options
Diffstat (limited to 'dmserializers/importvmf.cpp')
| -rw-r--r-- | dmserializers/importvmf.cpp | 629 |
1 files changed, 629 insertions, 0 deletions
diff --git a/dmserializers/importvmf.cpp b/dmserializers/importvmf.cpp new file mode 100644 index 0000000..7f57c6d --- /dev/null +++ b/dmserializers/importvmf.cpp @@ -0,0 +1,629 @@ +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +//============================================================================= + +#include "importkeyvaluebase.h" +#include "dmserializers.h" +#include "datamodel/idatamodel.h" +#include "datamodel/dmelement.h" +#include "tier1/KeyValues.h" +#include "tier1/utlbuffer.h" +#include "datamodel/dmattribute.h" + + +//----------------------------------------------------------------------------- +// Serialization class for VMF files (map files) +//----------------------------------------------------------------------------- +class CImportVMF : public CImportKeyValueBase +{ +public: + virtual const char *GetName() const { return "vmf"; } + virtual const char *GetDescription() const { return "Valve Map File"; } + virtual int GetCurrentVersion() const { return 0; } // doesn't store a version + + bool Serialize( CUtlBuffer &outBuf, CDmElement *pRoot ); + CDmElement* UnserializeFromKeyValues( KeyValues *pKeyValues ); + +private: + // Reads a single entity + bool UnserializeEntityKey( CDmAttribute *pEntities, KeyValues *pKeyValues ); + + // Reads entity editor keys + bool UnserializeEntityEditorKey( CDmAttribute *pEditor, KeyValues *pKeyValues ); + + // Reads keys that we currently do nothing with + bool UnserializeUnusedKeys( DmElementHandle_t hOther, KeyValues *pKeyValues ); + + // Writes out all everything other than entities + bool SerializeOther( CUtlBuffer &buf, CDmAttribute *pOther, const char **ppFilter = 0 ); + + // Writes out all entities + bool SerializeEntities( CUtlBuffer &buf, CDmAttribute *pEntities ); + + // Writes out a single attribute recursively + bool SerializeAttribute( CUtlBuffer &buf, CDmAttribute *pAttribute, bool bElementArrays ); + + // Writes entity editor keys + bool SerializeEntityEditorKey( CUtlBuffer &buf, DmElementHandle_t hEditor ); + + // Updates the max hammer id + void UpdateMaxHammerId( KeyValues *pKeyValue ); + + // Max id read from the file + int m_nMaxHammerId; +}; + + +//----------------------------------------------------------------------------- +// Singleton instance +//----------------------------------------------------------------------------- +static CImportVMF s_ImportVMF; + +void InstallVMFImporter( IDataModel *pFactory ) +{ + pFactory->AddSerializer( &s_ImportVMF ); +} + + +//----------------------------------------------------------------------------- +// Deals with poorly-named key values for the DME system +//----------------------------------------------------------------------------- +static const char *s_pKeyRemapNames[][2] = +{ + { "id", "__id" }, + { "name", "__name" }, + { "type", "__type" }, + { NULL, NULL }, +}; + + +//----------------------------------------------------------------------------- +// Gets remap name for unserialization/serailzation +//----------------------------------------------------------------------------- +static const char *GetRemapName( const char *pName, bool bSerialization ) +{ + for ( int i = 0; s_pKeyRemapNames[i][0]; ++i ) + { + if ( !Q_stricmp( pName, s_pKeyRemapNames[i][bSerialization] ) ) + return s_pKeyRemapNames[i][1 - bSerialization]; + } + return pName; +} + + +//----------------------------------------------------------------------------- +// Writes out a single attribute recursively +//----------------------------------------------------------------------------- +bool CImportVMF::SerializeAttribute( CUtlBuffer &buf, CDmAttribute *pAttribute, bool bElementArrays ) +{ + if ( pAttribute->IsFlagSet( FATTRIB_STANDARD | FATTRIB_DONTSAVE ) ) + return true; + + const char *pFieldName = GetRemapName( pAttribute->GetName(), true ); + if ( !Q_stricmp( pFieldName, "editorType" ) ) + return true; + + if ( !IsArrayType( pAttribute->GetType() ) ) + { + if ( !bElementArrays ) + { + buf.Printf( "\"%s\" ", pFieldName ); + if ( pAttribute->GetType() != AT_STRING ) + { + buf.Printf( "\"" ); + } + g_pDataModel->SetSerializationDelimiter( GetCStringCharConversion() ); + pAttribute->Serialize( buf ); + g_pDataModel->SetSerializationDelimiter( NULL ); + if ( pAttribute->GetType() != AT_STRING ) + { + buf.Printf( "\"" ); + } + buf.Printf( "\n" ); + } + } + else + { + if ( bElementArrays ) + { + Assert( pAttribute->GetType() == AT_ELEMENT_ARRAY ); + if ( !SerializeOther( buf, pAttribute ) ) + return false; + } + } + + return true; +} + + +//----------------------------------------------------------------------------- +// Writes out all everything other than entities +//----------------------------------------------------------------------------- +bool CImportVMF::SerializeOther( CUtlBuffer &buf, CDmAttribute *pOther, const char **ppFilter ) +{ + CDmrElementArray<> array( pOther ); + int nCount = array.Count(); + for ( int i = 0; i < nCount; ++i ) + { + CDmElement *pElement = array[i]; + const char *pElementName = pElement->GetName(); + if ( ppFilter ) + { + int j; + for ( j = 0; ppFilter[j]; ++j ) + { + if ( !Q_stricmp( pElementName, ppFilter[j] ) ) + break; + } + + if ( !ppFilter[j] ) + continue; + } + + int nLen = Q_strlen( pElementName ) + 1; + char *pTemp = (char*)_alloca( nLen ); + Q_strncpy( pTemp, pElementName, nLen ); + Q_strlower( pTemp ); + buf.Printf( "%s\n", pTemp ); + buf.Printf( "{\n" ); + buf.PushTab(); + + // Normal attributes first + for( CDmAttribute *pAttribute = pElement->FirstAttribute(); pAttribute; pAttribute = pAttribute->NextAttribute() ) + { + if ( !SerializeAttribute( buf, pAttribute, false ) ) + return false; + } + + // Subkeys later + for( CDmAttribute *pAttribute = pElement->FirstAttribute(); pAttribute; pAttribute = pAttribute->NextAttribute() ) + { + if ( !SerializeAttribute( buf, pAttribute, true ) ) + return false; + } + + buf.PopTab(); + buf.Printf( "}\n" ); + } + + return true; +} + + +//----------------------------------------------------------------------------- +// Writes entity editor keys +//----------------------------------------------------------------------------- +bool CImportVMF::SerializeEntityEditorKey( CUtlBuffer &buf, DmElementHandle_t hEditor ) +{ + CDmElement *pEditorElement = g_pDataModel->GetElement( hEditor ); + if ( !pEditorElement ) + return true; + + buf.Printf( "editor\n" ); + buf.Printf( "{\n" ); + buf.PushTab(); + + { + CDmAttribute *pAttribute = pEditorElement->GetAttribute( "color" ); + if ( pAttribute ) + { + Color c = pAttribute->GetValue<Color>(); + buf.Printf( "\"color\" \"%d %d %d\"\n", c.r(), c.g(), c.b() ); + } + } + PrintIntAttribute( pEditorElement, buf, "id" ); // FIXME - id is a DmObjectId_t!!! This should never print anything! + PrintStringAttribute( pEditorElement, buf, "comments" ); + PrintBoolAttribute( pEditorElement, buf, "visgroupshown" ); + PrintBoolAttribute( pEditorElement, buf, "visgroupautoshown" ); + + for ( CDmAttribute *pAttribute = pEditorElement->FirstAttribute(); pAttribute != NULL; pAttribute = pAttribute->NextAttribute() ) + { + if ( pAttribute->IsFlagSet( FATTRIB_STANDARD | FATTRIB_DONTSAVE ) ) + continue; + + const char *pKeyName = pAttribute->GetName(); + if ( Q_stricmp( pKeyName, "color" ) && Q_stricmp( pKeyName, "id" ) && + Q_stricmp( pKeyName, "comments" ) && Q_stricmp( pKeyName, "visgroupshown" ) && + Q_stricmp( pKeyName, "visgroupautoshown" ) ) + { + PrintStringAttribute( pEditorElement, buf, pKeyName ); + } + } + + buf.PopTab(); + buf.Printf( "}\n" ); + + return true; +} + + +//----------------------------------------------------------------------------- +// Writes out all entities +//----------------------------------------------------------------------------- +bool CImportVMF::SerializeEntities( CUtlBuffer &buf, CDmAttribute *pEntities ) +{ + // FIXME: Make this serialize in the order in which it appears in the FGD + // to minimize diffs + CDmrElementArray<> array( pEntities ); + + int nCount = array.Count(); + for ( int i = 0; i < nCount; ++i ) + { + CDmElement *pElement = array[i]; + buf.Printf( "entity\n" ); + buf.Printf( "{\n" ); + buf.PushTab(); + buf.Printf( "\"id\" \"%s\"\n", pElement->GetName() ); + + for( CDmAttribute *pAttribute = pElement->FirstAttribute(); pAttribute; pAttribute = pAttribute->NextAttribute() ) + { + // Do 'editor' at the end to preserve ordering and not make terrible diffs + if ( !Q_stricmp( pAttribute->GetName(), "editor" ) ) + continue; + + if ( !SerializeAttribute( buf, pAttribute, false ) ) + return false; + } + + for( CDmAttribute *pAttribute = pElement->FirstAttribute(); pAttribute; pAttribute = pAttribute->NextAttribute() ) + { + // Do 'editor' at the end to preserve ordering and not make terrible diffs + if ( !Q_stricmp( pAttribute->GetName(), "editor" ) ) + continue; + + if ( !SerializeAttribute( buf, pAttribute, true ) ) + return false; + } + + // Do the 'editor' + CDmAttribute *pEditor = pElement->GetAttribute( "editor" ); + if ( pEditor ) + { + SerializeEntityEditorKey( buf, pEditor->GetValue<DmElementHandle_t>() ); + } + + buf.PopTab(); + buf.Printf( "}\n" ); + } + + return true; +} + + +//----------------------------------------------------------------------------- +// Writes out a new VMF file +//----------------------------------------------------------------------------- +bool CImportVMF::Serialize( CUtlBuffer &buf, CDmElement *pRoot ) +{ + // This is done in this strange way (namely, serializing other twice) to minimize diffs + const char *pOtherFilter1[] = + { + "versioninfo", "visgroups", "viewsettings", "world", NULL + }; + + const char *pOtherFilter2[] = + { + "cameras", "cordon", "hidden", NULL + }; + + CDmAttribute *pOther = pRoot->GetAttribute( "other" ); + if ( pOther && pOther->GetType() == AT_ELEMENT_ARRAY ) + { + if ( !SerializeOther( buf, pOther, pOtherFilter1 ) ) + return false; + } + + // Serialize entities + CDmAttribute *pEntities = pRoot->GetAttribute( "entities" ); + if ( pEntities && pEntities->GetType() == AT_ELEMENT_ARRAY ) + { + if ( !SerializeEntities( buf, pEntities ) ) + return false; + } + + if ( pOther && pOther->GetType() == AT_ELEMENT_ARRAY ) + { + if ( !SerializeOther( buf, pOther, pOtherFilter2 ) ) + return false; + } + + return true; +} + + +//----------------------------------------------------------------------------- +// Updates the max hammer id +//----------------------------------------------------------------------------- +void CImportVMF::UpdateMaxHammerId( KeyValues *pField ) +{ + if ( !Q_stricmp( pField->GetName(), "id" ) ) + { + int nId = atoi( pField->GetString() ); + if ( nId > m_nMaxHammerId ) + { + m_nMaxHammerId = nId; + } + } +} + + +//----------------------------------------------------------------------------- +// Reads entity editor keys +//----------------------------------------------------------------------------- +bool CImportVMF::UnserializeEntityEditorKey( CDmAttribute *pEditorAttribute, KeyValues *pKeyValues ) +{ + CDmElement *pEditor; + DmElementHandle_t hEditor = pEditorAttribute->GetValue<DmElementHandle_t>(); + if ( hEditor == DMELEMENT_HANDLE_INVALID ) + { + pEditor = CreateDmElement( "DmElement", "editor", NULL );; + if ( !pEditor ) + return false; + hEditor = pEditor->GetHandle(); + pEditorAttribute->SetValue( hEditor ); + } + else + { + pEditor = g_pDataModel->GetElement( hEditor ); + } + + int r, g, b; + if ( sscanf( pKeyValues->GetString( "color", "" ), "%d %d %d", &r, &g, &b ) == 3 ) + { + Color c( r, g, b, 255 ); + if ( !pEditor->SetValue( "color", c ) ) + return false; + } + KeyValues *pIdKey = pKeyValues->FindKey( "id" ); + if ( pIdKey ) + { + UpdateMaxHammerId( pIdKey ); + } + AddIntAttribute( pEditor, pKeyValues, "id" ); + AddStringAttribute( pEditor, pKeyValues, "comments" ); + AddBoolAttribute( pEditor, pKeyValues, "visgroupshown" ); + AddBoolAttribute( pEditor, pKeyValues, "visgroupautoshown" ); + + for ( KeyValues *pUserKey = pKeyValues->GetFirstValue(); pUserKey != NULL; pUserKey = pUserKey->GetNextValue() ) + { + const char *pKeyName = pUserKey->GetName(); + if ( Q_stricmp( pKeyName, "color" ) && Q_stricmp( pKeyName, "id" ) && + Q_stricmp( pKeyName, "comments" ) && Q_stricmp( pKeyName, "visgroupshown" ) && + Q_stricmp( pKeyName, "visgroupautoshown" ) ) + { + AddStringAttribute( pEditor, pKeyValues, pKeyName ); + } + } + + return true; +} + + +//----------------------------------------------------------------------------- +// Reads a single entity +//----------------------------------------------------------------------------- +bool CImportVMF::UnserializeEntityKey( CDmAttribute *pEntities, KeyValues *pKeyValues ) +{ + CDmElement *pEntity = CreateDmElement( "DmeVMFEntity", pKeyValues->GetString( "id", "-1" ), NULL ); + if ( !pEntity ) + return false; + + CDmrElementArray<> array( pEntities ); + array.AddToTail( pEntity ); + + // Each act busy needs to have an editortype associated with it so it displays nicely in editors + pEntity->SetValue( "editorType", "vmfEntity" ); + + const char *pClassName = pKeyValues->GetString( "classname", NULL ); + if ( !pClassName ) + return false; + + // Read the actual fields + for ( KeyValues *pField = pKeyValues->GetFirstValue(); pField != NULL; pField = pField->GetNextValue() ) + { + // FIXME: Knowing the FGD here would be useful for type determination. + // Look up the field by name based on class name + // In the meantime, just use the keyvalues type? + char pFieldName[512]; + Q_strncpy( pFieldName, pField->GetName(), sizeof(pFieldName) ); + Q_strlower( pFieldName ); + + // Don't do id: it's used as the name + // Not to mention it's a protected name + if ( !Q_stricmp( pFieldName, "id" ) ) + { + UpdateMaxHammerId( pField ); + continue; + } + + // Type, name, and editortype are protected names + Assert( Q_stricmp( pFieldName, "type" ) && Q_stricmp( pFieldName, "name" ) && Q_stricmp( pFieldName, "editortype" ) ); + + switch( pField->GetDataType() ) + { + case KeyValues::TYPE_INT: + if ( !AddIntAttributeFlags( pEntity, pKeyValues, pFieldName, FATTRIB_USERDEFINED ) ) + return false; + break; + + case KeyValues::TYPE_FLOAT: + if ( !AddFloatAttributeFlags( pEntity, pKeyValues, pFieldName, FATTRIB_USERDEFINED ) ) + return false; + break; + + case KeyValues::TYPE_STRING: + { + const char* pString = pField->GetString(); + if (!pString || !pString[0]) + return false; + + // Look for vectors + Vector4D v; + if ( sscanf( pString, "%f %f %f %f", &v.x, &v.y, &v.z, &v.w ) == 4 ) + { + if ( !pEntity->SetValue( pFieldName, v ) ) + return false; + CDmAttribute *pAttribute = pEntity->GetAttribute( pFieldName ); + pAttribute->AddFlag( FATTRIB_USERDEFINED ); + } + else if ( sscanf( pString, "%f %f %f", &v.x, &v.y, &v.z ) == 3 ) + { + if ( !pEntity->SetValue( pFieldName, v.AsVector3D() ) ) + { + QAngle ang( v.x, v.y, v.z ); + if ( !pEntity->SetValue( pFieldName, ang ) ) + return false; + } + CDmAttribute *pAttribute = pEntity->GetAttribute( pFieldName ); + pAttribute->AddFlag( FATTRIB_USERDEFINED ); + } + else + { + if ( !AddStringAttributeFlags( pEntity, pKeyValues, pFieldName, FATTRIB_USERDEFINED ) ) + return false; + } + } + break; + } + } + + // Read the subkeys + CDmAttribute *pEditor = pEntity->AddAttribute( "editor", AT_ELEMENT ); + CDmrElementArray<> otherKeys( pEntity->AddAttribute( "other", AT_ELEMENT_ARRAY ) ); + for ( KeyValues *pSubKey = pKeyValues->GetFirstTrueSubKey(); pSubKey != NULL; pSubKey = pSubKey->GetNextTrueSubKey() ) + { + bool bOk = false; + if ( !Q_stricmp( pSubKey->GetName(), "editor" ) ) + { + bOk = UnserializeEntityEditorKey( pEditor, pSubKey ); + } + else + { + // We don't currently do anything with the other keys + CDmElement *pOther = CreateDmElement( "DmElement", pSubKey->GetName(), NULL ); + otherKeys.AddToTail( pOther ); + bOk = UnserializeUnusedKeys( pOther->GetHandle(), pSubKey ); + } + + if ( !bOk ) + return false; + } + + return true; +} + + +//----------------------------------------------------------------------------- +// Reads keys that we currently do nothing with +//----------------------------------------------------------------------------- +bool CImportVMF::UnserializeUnusedKeys( DmElementHandle_t hOther, KeyValues *pKeyValues ) +{ + CDmElement *pOther = g_pDataModel->GetElement( hOther ); + + // Read the actual fields + for ( KeyValues *pField = pKeyValues->GetFirstValue(); pField != NULL; pField = pField->GetNextValue() ) + { + UpdateMaxHammerId( pField ); + const char *pFieldName = GetRemapName( pField->GetName(), false ); + pOther->SetValue( pFieldName, pField->GetString() ); + } + + // Read the subkeys + CDmrElementArray<> subKeys( pOther->AddAttribute( "subkeys", AT_ELEMENT_ARRAY ) ); + for ( KeyValues *pSubKey = pKeyValues->GetFirstTrueSubKey(); pSubKey != NULL; pSubKey = pSubKey->GetNextTrueSubKey() ) + { + CDmElement *pSubElement = CreateDmElement( "DmElement", pSubKey->GetName(), NULL ); + subKeys.AddToTail( pSubElement ); + if ( !UnserializeUnusedKeys( pSubElement->GetHandle(), pSubKey ) ) + return false; + } + return true; +} + + +/* +//----------------------------------------------------------------------------- +// Reads the cordon data +//----------------------------------------------------------------------------- +bool CImportVMF::UnserializeCordonKey( IDmAttributeElement *pCordon, KeyValues *pKeyValues ) +{ + DmElementHandle_t hCordon = pCordon->GetValue().Get(); + if ( hCordon == DMELEMENT_HANDLE_INVALID ) + { + hCordon = CreateDmElement( "DmElement", "cordon", NULL ); + if ( hCordon == DMELEMENT_HANDLE_INVALID ) + return false; + pCordon->SetValue( hCordon ); + } + + AddBoolAttribute( hCordon, pKeyValues, "active" ); + + Vector v; + if ( sscanf( pKeyValues->GetString( "mins", "" ), "(%f %f %f)", &v.x, &v.y, &v.z ) == 3 ) + { + if ( !DmElementAddAttribute( hCordon, "mins", v ) ) + return false; + } + if ( sscanf( pKeyValues->GetString( "maxs", "" ), "(%f %f %f)", &v.x, &v.y, &v.z ) == 3 ) + { + if ( !DmElementAddAttribute( hCordon, "maxs", v ) ) + return false; + } + return true; +} +*/ + + +//----------------------------------------------------------------------------- +// Main entry point for the unserialization +//----------------------------------------------------------------------------- +CDmElement* CImportVMF::UnserializeFromKeyValues( KeyValues *pKeyValues ) +{ + m_nMaxHammerId = 0; + + // Create the main element + CDmElement *pElement = CreateDmElement( "DmElement", "VMF", NULL ); + if ( !pElement ) + return NULL; + + // Each vmf needs to have an editortype associated with it so it displays nicely in editors + pElement->SetValue( "editorType", "VMF" ); + + // The VMF is a series of keyvalue blocks; either + // 'entity', 'cameras', 'cordon', 'world', 'versioninfo', or 'viewsettings' + CDmAttribute *pEntityArray = pElement->AddAttribute( "entities", AT_ELEMENT_ARRAY ); + + // All main keys are root keys + CDmrElementArray<> otherKeys( pElement->AddAttribute( "other", AT_ELEMENT_ARRAY ) ); + for ( ; pKeyValues != NULL; pKeyValues = pKeyValues->GetNextKey() ) + { + bool bOk = false; + if ( !Q_stricmp( pKeyValues->GetName(), "entity" ) ) + { + bOk = UnserializeEntityKey( pEntityArray, pKeyValues ); + } + else + { + // We don't currently do anything with + CDmElement *pOther = CreateDmElement( "DmElement", pKeyValues->GetName(), NULL ); + otherKeys.AddToTail( pOther ); + bOk = UnserializeUnusedKeys( pOther->GetHandle(), pKeyValues ); + } + + if ( !bOk ) + { + Warning( "Error importing VMF element %s\n", pKeyValues->GetName() ); + return NULL; + } + } + + // Resolve all element references recursively + RecursivelyResolveElement( pElement ); + + // Add the max id read in from the file to the root entity + pElement->SetValue( "maxHammerId", m_nMaxHammerId ); + + return pElement; +} |