hl2sdk/game/shared/tf/tf_proto_script_obj_def.cpp
2025-02-19 18:36:16 -05:00

870 lines
26 KiB
C++

//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "tier1/util_misc.h"
#include "tf_proto_script_obj_def.h"
#include "tf_quest_map_node.h"
#include <google/protobuf/text_format.h>
#include "filesystem.h"
#include "tier2/p4helpers.h"
#include "tier2/fileutils.h"
#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
#include "schemainitutils.h"
#ifdef CLIENT_DLL
#include "icommandline.h"
#include <vgui/ILocalize.h>
#endif
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
extern bool CheckValveSignature( const void *data, uint32 nDataSize, const void *signature, uint32 nSignatureSize );
const char* g_pszProtoPath = "scripts/protodefs/unencrypted/";
const char* g_pszProtoDefFile = "scripts/protodefs/proto_defs.vpd";
const char* g_pszProtoLocFileName = "resource/tf_proto_obj_defs_english.txt";
#ifdef CLIENT_DLL
ProtoDefTypeDesc_t g_ProtoDefTypeDescs[] = { { "Quest Map Node", false, NULL },
{ "Quest Map", false, NULL },
{ "Quest Theme", false, NULL },
{ "Quest Map Region", false, NULL },
{ "Quest", false, NULL },
{ "Quest Objective", false, NULL },
{ "Paintkit Variable", false, NULL },
{ "Paintkit Operation", false, NULL },
{ "Paintkit Item Def", false, NULL },
{ "Paintkit Def", false, NULL },
{ "Header", false, NULL },
{ "Quest Map Store Item", false, NULL },
{ "Quest Map Star Type", false, NULL } };
COMPILE_TIME_ASSERT( ARRAYSIZE( g_ProtoDefTypeDescs ) == ProtoDefTypes_ARRAYSIZE );
#endif
const IProtoBufScriptObjectDefinition* ProtoDefID_t::operator()() const
{
return GetProtoScriptObjDefManager()->GetDefinition( *this );
}
bool BMessagesTypesAreTheSame( const google::protobuf::Descriptor* pType1, const google::protobuf::Descriptor* pType2 )
{
return pType1->full_name() == pType2->full_name();
}
CUtlString GetProtoDefLocTokenForField( const IProtoBufScriptObjectDefinition* pDef, const CMsgFieldID& fieldID )
{
// We're going to craft a localization token that is unique for this field, in this definition, among all types.
// To do that we're going to glue a few things together.
// - To achieve uniqueness in the message use the field ID as a string
// - For uniqueness within a type use the defindex
// - For uniqueness among the types, use the type index
CUtlString strToken;
// Type
strToken.Append( CFmtStr( "%d_", pDef->GetDefType() ) );
// Defindex
strToken.Append( CFmtStr( "%d_", pDef->GetDefIndex() ) );
// Field ID
strToken.Append( fieldID.ShortDebugString().c_str() );
return strToken;
}
void SaveLocalizationValueToFile( const char* pszToken, wchar_t* pwszValue, bool bDeleted )
{
#ifdef CLIENT_DLL
tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__);
char szPathAndFileName[MAX_PATH];
if ( !GenerateFullPath( g_pszProtoLocFileName, "MOD", szPathAndFileName, ARRAYSIZE( szPathAndFileName ) ) )
{
Warning( "Failed to GenerateFullPath %s\n", g_pszProtoLocFileName );
return;
}
// The "deleted" file doesn't exist, but if we put the token into that file's map, then save out
// to tf_proto_obj_defs_english, the token and value won't be saved out.
const char* pszFile = bDeleted ? "deleted" : szPathAndFileName;
if ( pszToken[0] == '#' )
{
pszToken++;
}
g_pVGuiLocalize->AddString( pszToken, pwszValue, pszFile );
// Get the name of the file and p4 check it out
char szCorrectCaseFilePath[MAX_PATH];
g_pFullFileSystem->GetCaseCorrectFullPath( szPathAndFileName, szCorrectCaseFilePath );
CP4AutoEditFile a( szCorrectCaseFilePath );
g_pVGuiLocalize->SaveToFile( szPathAndFileName );
#endif
}
const CMsgProtoDefHeader& GetHeaderFromMessage( const google::protobuf::Message* pMsg )
{
tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__);
const google::protobuf::FieldDescriptor* pHeaderField = pMsg->GetDescriptor()->FindFieldByName( "header" );
const google::protobuf::Message& header = pMsg->GetReflection()->GetMessage( *pMsg, pHeaderField );
return *(CMsgProtoDefHeader*)(&header);
}
CMsgProtoDefHeader& GetMutableHeaderFromMessage( google::protobuf::Message* pMsg )
{
tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__);
const google::protobuf::FieldDescriptor* pHeaderField = pMsg->GetDescriptor()->FindFieldByName( "header" );
google::protobuf::Message* pHeader = pMsg->GetReflection()->MutableMessage( pMsg, pHeaderField );
return *(CMsgProtoDefHeader*)pHeader;
}
bool BMessagesAreEqual( const google::protobuf::Message* pMsg1, const google::protobuf::Message* pMsg2 )
{
// Quick check. Need to be the same type!
if ( pMsg1->GetDescriptor() != pMsg2->GetDescriptor() )
return false;
if ( pMsg1->IsInitialized() && pMsg2->IsInitialized() )
{
return pMsg1->SerializeAsString() == pMsg2->SerializeAsString();
}
else
{
return pMsg1->SerializePartialAsString() == pMsg2->SerializePartialAsString();
}
}
void SpewProtobufMessage( const google::protobuf::Message* pMsg )
{
std::string strMsg( pMsg->DebugString() );
for ( size_t i=0; i < strMsg.size(); ++i )
{
DevMsg( "%c", strMsg[ i ] );
}
}
bool UniversalFieldIDLess( const CMsgFieldID &lhs, const CMsgFieldID &rhs )
{
int nMaxDepth = Min( lhs.field_size(), rhs.field_size() );
for( int i=0; i < nMaxDepth; ++i )
{
const CMsgFieldID_CMsgField& lhsField = lhs.field( i );
const CMsgFieldID_CMsgField& rhsField = rhs.field( i );
if ( lhsField.field_number() < rhsField.field_number() )
return true;
if ( lhsField.field_number() > rhsField.field_number() )
return false;
if ( lhsField.repeated_index() < rhsField.repeated_index() )
return true;
if ( lhsField.repeated_index() > rhsField.repeated_index() )
return false;
// Same field, same repeated index. If we have multiple fields to
// dig into, it means we're going into nested messages. Check if
// the subsequent fields differ...
}
return lhs.field_size() < rhs.field_size();
}
void GetMessageFromFieldID( const CMsgFieldID& startingFieldID, const google::protobuf::Message* pStartingMsg,
CMsgFieldID_CMsgField& outerFieldID, const google::protobuf::Message** pOuterMsg )
{
// Very possible we're not going deeper
(*pOuterMsg) = pStartingMsg;
for( int i=0; i < startingFieldID.field_size(); ++i )
{
outerFieldID = startingFieldID.field( i );
auto pFieldDesc = (*pOuterMsg)->GetDescriptor()->FindFieldByNumber( outerFieldID.field_number() );
if ( pFieldDesc->type() == google::protobuf::FieldDescriptor::TYPE_MESSAGE )
{
Assert( i != startingFieldID.field_size() );
// Keep going!
(*pOuterMsg) = pFieldDesc->is_repeated() ? &(*pOuterMsg)->GetReflection()->GetRepeatedMessage( *(*pOuterMsg), pFieldDesc, outerFieldID.repeated_index() )
: &(*pOuterMsg)->GetReflection()->GetMessage( *(*pOuterMsg), pFieldDesc );
}
}
}
void SpewFieldID( const CMsgFieldID& fieldID, const google::protobuf::Message* pMsg )
{
for( int i=0; i < fieldID.field_size(); ++i )
{
const CMsgFieldID_CMsgField& field = fieldID.field( i );
auto pFieldDesc = pMsg->GetDescriptor()->FindFieldByNumber( field.field_number() );
// Spew the name
DevMsg( " %d:%s", pFieldDesc->number(), pFieldDesc->name().c_str() );
if ( pFieldDesc->is_repeated() )
{
DevMsg( ":%d", field.repeated_index() );
}
if ( pFieldDesc->type() == google::protobuf::FieldDescriptor::TYPE_MESSAGE )
{
// Keep going!
auto pNewMsg = GetSubMessage( pMsg, pFieldDesc, field.repeated_index() );
if ( !pNewMsg )
{
Assert( false );
pMsg = NULL;
return;
}
pMsg = pNewMsg;
}
else
{
std::string strFieldVal;
google::protobuf::TextFormat::PrintFieldValueToString( *pMsg, pFieldDesc, pFieldDesc->is_repeated() ? field.repeated_index() : -1, &strFieldVal );
DevMsg( " = %s", strFieldVal.c_str() );
}
}
}
void GetMutableMessageFromFieldID( const CMsgFieldID& startingFieldID, google::protobuf::Message* pStartingMsg,
CMsgFieldID_CMsgField& outerFieldID, google::protobuf::Message** pOuterMsg )
{
// Very possible we're not going deeper
(*pOuterMsg) = pStartingMsg;
for( int i=0; i < startingFieldID.field_size(); ++i )
{
outerFieldID = startingFieldID.field( i );
auto pFieldDesc = (*pOuterMsg)->GetDescriptor()->FindFieldByNumber( outerFieldID.field_number() );
if ( pFieldDesc->type() == google::protobuf::FieldDescriptor::TYPE_MESSAGE && i < startingFieldID.field_size() - 1 )
{
Assert( i != startingFieldID.field_size() );
if ( pFieldDesc->is_repeated() && (uint32)(*pOuterMsg)->GetReflection()->FieldSize( *(*pOuterMsg ), pFieldDesc ) <= outerFieldID.repeated_index() )
{
SpewFieldID( startingFieldID, pStartingMsg );
Assert( false );
return;
}
// Keep going!
(*pOuterMsg) = pFieldDesc->is_repeated() ? (*pOuterMsg)->GetReflection()->MutableRepeatedMessage( (*pOuterMsg), pFieldDesc, outerFieldID.repeated_index() )
: (*pOuterMsg)->GetReflection()->MutableMessage( (*pOuterMsg), pFieldDesc );
}
}
}
void SpewUniversalFieldID( const CMsgUniversalFieldID& universalFieldID )
{
auto pDef = GetProtoScriptObjDefManager()->GetDefinition( universalFieldID.defining_obj_id() );
DevMsg( "\"%s\" (%d): ", pDef->GetName()
, pDef->GetDefIndex() );
SpewFieldID( universalFieldID.field_id(), pDef->GetTopLayerMessage() );
}
const CMsgFieldID_CMsgField& GetFieldIDFromFieldID( const CMsgFieldID& fieldID )
{
return fieldID.field( fieldID.field_size() - 1 );
}
const google::protobuf::FieldDescriptor* GetMergingKeyField( const google::protobuf::Descriptor* pDesc )
{
for ( int i=0; i < pDesc->field_count(); ++i )
{
const google::protobuf::FieldDescriptor* pField = pDesc->field( i );
if ( pField->options().GetExtension( merging_key_field ) )
{
return pField;
}
}
return NULL;
}
void GetMessageHierarchyFromFieldID( const CMsgFieldID& startingFieldID, const google::protobuf::Message* pStartingMsg, ProtoMessageHierarchyVector_t& root )
{
root.RemoveAll();
const google::protobuf::Message *pNextMsg = pStartingMsg;
for ( int i = 0; i < startingFieldID.field_size(); ++i )
{
ProtoMessageHierarchy_t *pMsgNode = root.AddToTailGetPtr();
pMsgNode->m_msgField = startingFieldID.field( i );
pMsgNode->m_pFieldDesc = pNextMsg->GetDescriptor()->FindFieldByNumber( pMsgNode->m_msgField.field_number() );
if ( pMsgNode->m_pFieldDesc->type() == google::protobuf::FieldDescriptor::TYPE_MESSAGE )
{
Assert( i != startingFieldID.field_size() );
// Keep going!
pNextMsg = pMsgNode->m_pFieldDesc->is_repeated() ? &pNextMsg->GetReflection()->GetRepeatedMessage( *pNextMsg, pMsgNode->m_pFieldDesc, pMsgNode->m_msgField.repeated_index() )
: &pNextMsg->GetReflection()->GetMessage( *pNextMsg, pMsgNode->m_pFieldDesc );
pMsgNode->m_pMsg = pNextMsg;
}
else
{
pMsgNode->m_pMsg = NULL;
}
}
}
void GetDefinitionPath( const IProtoBufScriptObjectDefinition* pDef, CUtlString& strPath )
{
strPath = CFmtStr( "%s%s/%d_%s.txt", g_pszProtoPath, ProtoDefTypes_Name( (ProtoDefTypes)pDef->GetDefType() ).c_str(), pDef->GetDefIndex(), pDef->GetName() ).Get();
}
bool BDeleteDefinitionFile( const IProtoBufScriptObjectDefinition* pDef )
{
//
// Mark the definition source file as deleted with P4
//
CUtlString strExistingFile;
GetDefinitionPath( pDef, strExistingFile );
strExistingFile.ToLower();
bool bDeleted = false;
if ( g_pFullFileSystem->FileExists( strExistingFile.Get(), "MOD" ) )
{
char szCorrectCaseFilePath[MAX_PATH];
if ( !GenerateFullPath( strExistingFile.Get(), "MOD", szCorrectCaseFilePath, ARRAYSIZE( szCorrectCaseFilePath ) ) )
{
Warning( "Failed to GenerateFullPath to %s\n", strExistingFile.Get() );
}
CP4File p4File( szCorrectCaseFilePath );
P4FileState_t state = p4File.GetFileState();
switch ( state )
{
case P4FILE_OPENED_FOR_ADD:
{
// We're adding it, so just revert it
bDeleted = p4File.Revert();
break;
}
case P4FILE_OPENED_FOR_EDIT:
{
// File is already committed. Mark for delete
bDeleted = p4File.Delete();
break;
}
case P4FILE_UNOPENED:
{
bDeleted = p4File.Delete();
break;
}
default:
Assert( false );
}
}
if ( bDeleted )
{
g_pFullFileSystem->RemoveFile( strExistingFile, "MOD" );
}
return false;
}
IProtoBufScriptObjectDefinition::IProtoBufScriptObjectDefinition()
: m_mapFieldValueSources( UniversalFieldIDLess )
{}
void IProtoBufScriptObjectDefinition::SerializeToBuffer( CUtlBuffer& bufOut ) const
{
std::string strOut;
int nSize = GetMsg()->ByteSize();
void *buffer = malloc( nSize );
if ( nSize == 0 || !GetMsg()->SerializeToArray( buffer, nSize ) )
{
DevMsg( "Failed to serialize def %s - %s (%d) to buffer!\n", ProtoDefTypes_Name( GetDefType() ).c_str(), GetName(), GetDefIndex() );
SpewProtobufMessage( GetMsg() );
Assert( false );
}
// Write the size
bufOut.PutInt( nSize );
// Write the message
bufOut.Put( buffer, nSize );
free( buffer );
}
bool IProtoBufScriptObjectDefinition::BParseFromBuffer( CUtlBuffer& bufIn, CUtlVector<CUtlString> *pVecErrors )
{
tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__);
// Read the size
int nSize = bufIn.GetInt();
// Parse the message
GetMutableMsg()->ParsePartialFromArray( bufIn.PeekGet(), nSize );
bufIn.SeekGet( CUtlBuffer::SEEK_CURRENT, nSize );
m_bCompiledMessageLoaded = true;
m_bCompiledMessageDirty = false;
// Do anything implementation specific
return BOnDataLoaded( pVecErrors );
}
bool IProtoBufScriptObjectDefinition::BParseFromString( std::string& strIn, CUtlVector<CUtlString> *pVecErrors )
{
tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__);
// Parse the message
google::protobuf::Message* pMsg = GetMutableTopLayerMsg();
google::protobuf::io::ArrayInputStream stream( strIn.c_str(), strIn.length() );
// Parse!
google::protobuf::TextFormat::Parser().AllowPartialMessage( true );
google::protobuf::TextFormat::Parse( &stream, pMsg );
google::protobuf::TextFormat::Parser().AllowPartialMessage( false );
m_bSourceMessagesLoaded = true;
m_bCompiledMessageDirty = true;
// Do anything implementation specific
SCHEMA_INIT_CHECK( BOnDataLoaded( pVecErrors ), "Definition %s failed to BOnDataLoaded!", GetName() );
return SCHEMA_INIT_SUCCESS();
}
uint32 IProtoBufScriptObjectDefinition::GetDefIndex() const
{
// Use the header
return GetHeader().defindex();
}
const char* IProtoBufScriptObjectDefinition::GetName() const
{
// Use the header
return GetHeader().name().c_str();
}
CMsgProtoDefID IProtoBufScriptObjectDefinition::GetID() const
{
CMsgProtoDefID msgID;
msgID.set_type( GetDefType() );
msgID.set_defindex( GetDefIndex() );
return msgID;
}
const CMsgProtoDefHeader& IProtoBufScriptObjectDefinition::GetHeader() const
{
return GetHeaderFromMessage( m_bCompiledMessageLoaded ? GetMsg() : GetTopLayerMessage() );
}
bool IProtoBufScriptObjectDefinition::BPostDataLoaded( CUtlVector<CUtlString> *pVecErrors )
{
return true;
}
void ClearNonInheritedFields( ::google::protobuf::Message* pMsgPrefab )
{
tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__);
for( int i=0; i < pMsgPrefab->GetDescriptor()->field_count(); ++i )
{
const ::google::protobuf::FieldDescriptor* pField = pMsgPrefab->GetDescriptor()->field( i );
if ( pField->options().GetExtension( do_not_inherit ) )
{
pMsgPrefab->GetReflection()->ClearField( pMsgPrefab, pField );
}
else if ( pField->type() == ::google::protobuf::FieldDescriptor::TYPE_MESSAGE )
{
if ( pField->is_repeated() )
{
for( int j=0; j < pMsgPrefab->GetReflection()->FieldSize( *pMsgPrefab, pField ); ++j )
{
ClearNonInheritedFields( pMsgPrefab->GetReflection()->MutableRepeatedMessage( pMsgPrefab, pField, j ) );
}
}
else
{
if ( pMsgPrefab->GetReflection()->HasField( *pMsgPrefab, pField ) )
{
ClearNonInheritedFields( pMsgPrefab->GetReflection()->MutableMessage( pMsgPrefab, pField ) );
}
}
}
}
}
int GetRepeatedIndexForInheritedMessage( const google::protobuf::Message* pMsgToSearch,
const google::protobuf::Message* pMsgToInherit,
const google::protobuf::FieldDescriptor* pField )
{
const google::protobuf::FieldDescriptor* pKeyField = GetMergingKeyField( pMsgToInherit->GetDescriptor() );
if ( !pKeyField )
return -1;
if ( !pMsgToInherit->GetReflection()->HasField( *pMsgToInherit, pKeyField ) )
return -1;
auto pReflection = pMsgToSearch->GetReflection();
for( int i=0; i < pReflection->FieldSize( *pMsgToSearch, pField ); ++i )
{
const google::protobuf::Message* pMsgSearchRepeatedMessage = &pReflection->GetRepeatedMessage( *pMsgToSearch, pField, i );
// If we're both not less than each other, then we're equal.
if ( !GCSDK::IsProtoBufFieldLess( *pMsgToInherit, *pMsgSearchRepeatedMessage, pKeyField, pKeyField ) &&
!GCSDK::IsProtoBufFieldLess( *pMsgSearchRepeatedMessage, *pMsgToInherit, pKeyField, pKeyField ) )
{
// If we're equal, then THIS is the matching message that we want to merge with.
return i;
}
}
// No match
return -1;
}
CProtoBufScriptObjectDefinitionManager* GetProtoScriptObjDefManager()
{
static CProtoBufScriptObjectDefinitionManager* s_pMgr = NULL;
if ( s_pMgr == NULL )
{
s_pMgr = new CProtoBufScriptObjectDefinitionManager();
}
return s_pMgr;
}
void ProtoLogHandler( google::protobuf::LogLevel level, const char* filename, int line,
const std::string& message )
{
if ( level == google::protobuf::LOGLEVEL_DFATAL ||
level == google::protobuf::LOGLEVEL_ERROR )
{
AssertMsg( !"Protobuf Error", "%s", message.c_str() );
}
else
{
DevMsg( "%s", message.c_str() );
}
}
CProtoBufScriptObjectDefinitionManager::CProtoBufScriptObjectDefinitionManager()
: m_mapCopyCount( DefLessFunc( ProtoDefID_t ) )
{
m_mapFactories.SetLessFunc( DefLessFunc( ProtoDefTypes ) );
google::protobuf::SetLogHandler( ProtoLogHandler );
for( int nType=0; nType < ProtoDefTypes_MAX + 1; ++nType )
{
if ( !ProtoDefTypes_IsValid( nType ) )
continue;
m_arDefinitionsMaps[ nType ].SetLessFunc( DefLessFunc( uint32 ) );
}
}
bool CProtoBufScriptObjectDefinitionManager::Init()
{
#if CLIENT_DLL
Assert( g_pVGuiLocalize );
if (g_pVGuiLocalize)
{
g_pVGuiLocalize->AddFile( "resource/tf_proto_obj_defs_%language%.txt" );
}
#endif
{
BInitDefinitions();
return true;
}
}
void CProtoBufScriptObjectDefinitionManager::PostInit()
{
// PostInit is called automatically on the client/server due to this
// deriving from AutoGameSystem. The schema is also an AutoGameSystem and gets
// initialized in the Init() round. To make sure we go after the Schema,
// we need to init our defs on the PostInit() round.
BPostDefinitionsLoaded();
}
struct SpewOnDestruct
{
SpewOnDestruct( CUtlVector<CUtlString>& vecErrors ) : m_vecErrors( vecErrors ) {}
~SpewOnDestruct()
{
FOR_EACH_VEC( m_vecErrors, i )
{
DevMsg( 0, "%s\n", m_vecErrors[ i ].Get() );
}
Assert( m_vecErrors.Count() == 0 );
}
const CUtlVector<CUtlString>& m_vecErrors;
};
bool CProtoBufScriptObjectDefinitionManager::BInitDefinitions()
{
m_bDefinitionsLoaded = false;
m_bDefinitionsPostDataLoadedCalled = false;
CUtlVector<CUtlString> vecErrors;
CUtlVector<CUtlString>* pVecErrors = &vecErrors; // Silly, but makes the SCHEMA_INIT_ macros work
SpewOnDestruct spew( vecErrors );
tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s", __FUNCTION__);
// Clear out old definitions
for (int nType = 0; nType < ProtoDefTypes_MAX + 1; ++nType)
{
if ( !ProtoDefTypes_IsValid( nType ) )
continue;
tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s - Purging old definitions", __FUNCTION__ );
m_arDefinitionsMaps[ nType ].PurgeAndDeleteElements();
}
//
// Parse all the types and definitions from the compiled binary file
//
CUtlBuffer buffer;
bool bReadFileOK = g_pFullFileSystem->ReadFile( g_pszProtoDefFile, "GAME", buffer );
SCHEMA_INIT_CHECK( bReadFileOK, "Failed to load %s!", g_pszProtoDefFile );
// Do we need to check the signature?
#if defined(TF_DLL) || defined(TF_CLIENT_DLL)
{
// Load up the signature
CUtlString sSignatureFilename( g_pszProtoDefFile ); sSignatureFilename.Append( ".sig" );
CUtlBuffer bufSignatureBinary;
bool bReadSignatureOK = g_pFullFileSystem->ReadFile( sSignatureFilename.String(), "GAME", bufSignatureBinary );
SCHEMA_INIT_CHECK( bReadSignatureOK, "Cannot load file '%s'", sSignatureFilename.String() );
// Check it with the Valve public key
bool bSignatureValid = CheckValveSignature(
buffer.Base(), buffer.TellPut(),
bufSignatureBinary.Base(), bufSignatureBinary.TellPut()
);
// If they have a signature for a zero-byte file, that's OK, too.
// That's the secret code that is checked into P4 internally that
// let's us run with any items_game file
if ( !bSignatureValid )
{
bSignatureValid = CheckValveSignature(
"", 0,
bufSignatureBinary.Base(), bufSignatureBinary.TellPut()
);
}
SCHEMA_INIT_CHECK( bSignatureValid, "'%s' is corrupt. Please verify your local game files. (https://support.steampowered.com/kb_article.php?ref=2037-QEUH-3335)", g_pszProtoDefFile );
}
#endif
uint32 nBytesUsed = 0;
if ( buffer.TellPut() > buffer.TellGet() )
{
tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s - Parsing definitions", __FUNCTION__ );
while ( buffer.GetBytesRemaining() > 0 )
{
int nType = buffer.GetInt(); // Which type
int nNumDefs = buffer.GetInt(); // How many definitions there are
// Make sure there's a factory to create this type
auto idx = m_mapFactories.Find( (ProtoDefTypes)nType );
SCHEMA_INIT_CHECK( idx != m_mapFactories.InvalidIndex(), "No factory for type %s!", ProtoDefTypes_Name( (ProtoDefTypes)nType ).c_str() );
// Parse all the defs
while ( nNumDefs-- )
{
IProtoBufScriptObjectDefinition* pNewDef = m_mapFactories[ idx ]->CreateNewObject();
SCHEMA_INIT_SUBSTEP( pNewDef->BParseFromBuffer( buffer, &vecErrors ) );
InsertObjectIntoMap( pNewDef );
nBytesUsed += pNewDef->GetMsg()->SpaceUsed();
}
}
}
else
{
SCHEMA_INIT_CHECK( false, "Failed to load %s!", g_pszProtoDefFile );
}
m_bDefinitionsLoaded = true;
BPostDefinitionsLoaded();
Msg( "ProtoDefs loaded. %s used\n", V_pretifymem( nBytesUsed ) );
return SCHEMA_INIT_SUCCESS();
}
bool CProtoBufScriptObjectDefinitionManager::BPostDefinitionsLoaded()
{
if ( !m_bDefinitionsLoaded && !m_bSourceFilesLoaded )
{
AssertMsg( false, "BPostDefinitionsLoaded called before BInitDefinitions!\n" );
SCHEMA_INIT_SUBSTEP( BInitDefinitions() );
}
CUtlVector<CUtlString> vecErrors;
CUtlVector<CUtlString>* pVecErrors = &vecErrors; // Silly, buy makes the SCHEMA_INIT_ macros work
SpewOnDestruct spew( vecErrors );
// BPostDataLoaded after all defs have been loaded and BOnDataLoaded has
// been called on them.
m_bDefinitionsPostDataLoadedCalled = true;
for( int nType=0; nType < ProtoDefTypes_MAX + 1; ++nType )
{
if ( !ProtoDefTypes_IsValid( nType ) )
continue;
tmZone( TELEMETRY_LEVEL0, TMZF_NONE, "%s - BPostDataLoaded Type: %s", __FUNCTION__, ProtoDefTypes_Name( (ProtoDefTypes)nType ).c_str() );
FOR_EACH_MAP_FAST( m_arDefinitionsMaps[ nType ], nIndex )
{
IProtoBufScriptObjectDefinition* pDef = m_arDefinitionsMaps[ nType ][ nIndex ];
pDef->BPostDataLoaded( &vecErrors );
}
}
Msg( "ProtoDefs post data loaded.\n" );
return SCHEMA_INIT_SUCCESS();
}
const IProtoBufScriptObjectDefinition* CProtoBufScriptObjectDefinitionManager::GetDefinition( const ProtoDefID_t& defID ) const
{
return GetMutableDefinition( defID );
}
IProtoBufScriptObjectDefinition* CProtoBufScriptObjectDefinitionManager::GetMutableDefinition( const ProtoDefID_t& defID ) const
{
Assert( m_bDefinitionsLoaded );
Assert( m_bDefinitionsPostDataLoadedCalled );
auto& mapType = m_arDefinitionsMaps[ defID.GetType() ];
auto idx = mapType.Find( defID.GetDefindex() );
if ( idx != mapType.InvalidIndex() )
{
return mapType[ idx ];
}
return NULL;
}
void CProtoBufScriptObjectDefinitionManager::InsertObjectIntoMap( IProtoBufScriptObjectDefinition* pObject )
{
// Insert everything into the definitions map
auto& mapType = m_arDefinitionsMaps[ pObject->GetDefType() ];
auto defidx = mapType.Find( pObject->GetDefIndex() );
if ( defidx == mapType.InvalidIndex() )
{
mapType.Insert( pObject->GetDefIndex(), pObject );
}
else
{
AssertMsg2( false, "Attempted to double-insert proto script def %d:%s!", pObject->GetDefIndex(), pObject->GetName() );
// It had at least be us in that defindex slot
Assert( mapType[ defidx ] == pObject );
mapType[ defidx ] = pObject;
}
}
void CProtoBufScriptObjectDefinitionManager::RegisterTypeFactory( const IProtoBufScriptObjectFactory* pFactory )
{
auto idx = m_mapFactories.Find( pFactory->GetType() );
Assert( idx == m_mapFactories.InvalidIndex() );
if ( idx == m_mapFactories.InvalidIndex() )
{
m_mapFactories.Insert( pFactory->GetType(), pFactory );
}
}
//
// Editing relating functions
//
const ::google::protobuf::Message* GetSubMessage( const ::google::protobuf::Message* pMsg,
const ::google::protobuf::FieldDescriptor *pField,
int nIndex )
{
if ( pMsg )
{
if ( pField->is_repeated() )
{
if ( nIndex < pMsg->GetReflection()->FieldSize( *pMsg, pField ) )
{
return &pMsg->GetReflection()->GetRepeatedMessage( *pMsg, pField, nIndex );
}
}
else if ( pField->is_required() || ( pMsg->GetReflection()->HasField( *pMsg, pField ) && pField->is_optional() ) )
{
return &pMsg->GetReflection()->GetMessage( *pMsg, pField );
}
}
return NULL;
}
::google::protobuf::Message* GetMutableSubMessage( ::google::protobuf::Message* pMsg,
const ::google::protobuf::FieldDescriptor *pField,
int nIndex )
{
if ( pMsg )
{
if ( !pField->is_repeated() )
{
return pMsg->GetReflection()->MutableMessage( pMsg, pField );
}
else if ( nIndex < pMsg->GetReflection()->FieldSize( *pMsg, pField ) )
{
return pMsg->GetReflection()->MutableRepeatedMessage( pMsg, pField, nIndex );
}
Assert( false );
}
return NULL;
}
::google::protobuf::Message* AddSubMessage( ::google::protobuf::Message* pMsg, const ::google::protobuf::FieldDescriptor *pField )
{
::google::protobuf::Message* pSubMsg = pField->is_repeated() ? pMsg->GetReflection()->AddMessage( pMsg, pField )
: pMsg->GetReflection()->MutableMessage( pMsg, pField );
return pSubMsg;
}
class CHeaderOnlyDef : public CTypedProtoBufScriptObjectDefinition< CMsgHeaderOnly, DEF_TYPE_HEADER_ONLY >
{
public:
CHeaderOnlyDef() {}
virtual ~CHeaderOnlyDef() {}
};
REGISTER_PROTO_DEF_FACTORY( CHeaderOnlyDef, DEF_TYPE_HEADER_ONLY )