summaryrefslogtreecommitdiff
path: root/utils/mkentitypatch
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 /utils/mkentitypatch
downloadarchived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.tar.xz
archived-source-engine-2018-hl2-src-3bf9df6b2785fa6d951086978a3e66f49427166a.zip
Diffstat (limited to 'utils/mkentitypatch')
-rw-r--r--utils/mkentitypatch/mkentitypatch.cpp418
-rw-r--r--utils/mkentitypatch/mkentitypatch.vpc53
2 files changed, 471 insertions, 0 deletions
diff --git a/utils/mkentitypatch/mkentitypatch.cpp b/utils/mkentitypatch/mkentitypatch.cpp
new file mode 100644
index 0000000..1cef863
--- /dev/null
+++ b/utils/mkentitypatch/mkentitypatch.cpp
@@ -0,0 +1,418 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// The copyright to the contents herein is the property of Valve, L.L.C.
+// The contents may be used and/or copied only with the written permission of
+// Valve, L.L.C., or in accordance with the terms and conditions stipulated in
+// the agreement/contract under which the contents have been supplied.
+//
+// $Header: $
+// $NoKeywords: $
+//
+//=============================================================================
+
+
+// Valve includes
+#include "appframework/tier2app.h"
+#include "filesystem.h"
+#include "icommandline.h"
+#include "tier2/p4helpers.h"
+#include "p4lib/ip4.h"
+#include "tier1/KeyValues.h"
+#include "tier1/utlbuffer.h"
+#include "bsplib.h"
+#include "lumpfiles.h"
+#include "filesystem_tools.h"
+#include "cmdlib.h"
+
+#ifdef _DEBUG
+#include <windows.h>
+#undef GetCurrentDirectory
+#endif
+
+//-----------------------------------------------------------------------------
+// Standard spew functions
+//-----------------------------------------------------------------------------
+static SpewRetval_t SpewStdout( SpewType_t spewType, char const *pMsg )
+{
+ if ( !pMsg )
+ return SPEW_CONTINUE;
+
+#ifdef _DEBUG
+ OutputDebugString( pMsg );
+#endif
+
+ printf( pMsg );
+ fflush( stdout );
+
+ return ( spewType == SPEW_ASSERT ) ? SPEW_DEBUGGER : SPEW_CONTINUE;
+}
+
+
+//-----------------------------------------------------------------------------
+// The application object
+//-----------------------------------------------------------------------------
+class CMkEntityPatchApp : public CTier2SteamApp
+{
+ typedef CTier2SteamApp BaseClass;
+
+public:
+ // Methods of IApplication
+ virtual bool Create();
+ virtual bool PreInit( );
+ virtual int Main();
+ virtual void Destroy() {}
+
+ void PrintHelp( );
+
+private:
+};
+
+
+DEFINE_CONSOLE_STEAM_APPLICATION_OBJECT( CMkEntityPatchApp );
+
+//-----------------------------------------------------------------------------
+// The application object
+//-----------------------------------------------------------------------------
+bool CMkEntityPatchApp::Create()
+{
+ SpewOutputFunc( SpewStdout );
+
+ AppSystemInfo_t appSystems[] =
+ {
+ { "p4lib.dll", P4_INTERFACE_VERSION },
+ { "", "" } // Required to terminate the list
+ };
+
+ return AddSystems( appSystems );
+}
+
+
+//-----------------------------------------------------------------------------
+//
+//-----------------------------------------------------------------------------
+bool CMkEntityPatchApp::PreInit( )
+{
+ MathLib_Init( 2.2f, 2.2f, 0.0f, 2.0f, false, false, false, false );
+
+ if ( !BaseClass::PreInit() )
+ return false;
+
+ if ( !g_pFullFileSystem )
+ {
+ Error( "// ERROR: sfmgen is missing a required interface!\n" );
+ return false;
+ }
+
+ // Add paths...
+ if ( !SetupSearchPaths( NULL, false, true ) )
+ return false;
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Print help
+//-----------------------------------------------------------------------------
+void CMkEntityPatchApp::PrintHelp( )
+{
+ Msg( "Usage: mkentitypatch [-nop4] [-vproject <path to gameinfo.txt>] <in .bsp file>\n" );
+ Msg( "\t-nop4\t: [Optional] Disables auto perforce checkout/add.\n" );
+ Msg( "\t-vproject\t: [Optional] Specifies path to a gameinfo.txt file (which mod to build for).\n" );
+ Msg( "\t Source .BSP file whose entity lump you wish to patch.\n" );
+}
+
+
+//-----------------------------------------------------------------------------
+// Computes a full directory
+//-----------------------------------------------------------------------------
+static void ComputeFullPath( const char *pRelativeDir, char *pFullPath, int nBufLen )
+{
+ if ( !Q_IsAbsolutePath( pRelativeDir ) )
+ {
+ char pDir[MAX_PATH];
+ if ( g_pFullFileSystem->GetCurrentDirectory( pDir, sizeof(pDir) ) )
+ {
+ Q_ComposeFileName( pDir, pRelativeDir, pFullPath, nBufLen );
+ }
+ }
+ else
+ {
+ Q_strncpy( pFullPath, pRelativeDir, nBufLen );
+ }
+
+ Q_StripTrailingSlash( pFullPath );
+
+ // Ensure the output directory exists
+ g_pFullFileSystem->CreateDirHierarchy( pFullPath );
+}
+
+
+//-----------------------------------------------------------------------------
+// The application object
+//-----------------------------------------------------------------------------
+entity_t *FindEntity( KeyValues *pEntity )
+{
+ int nHammerId = pEntity->GetInt( "id", INT_MIN );
+ if ( nHammerId != INT_MIN )
+ {
+ // First, look for hammerid
+ for ( int i = 0; i < num_entities; ++i )
+ {
+ int nId = IntForKeyWithDefault( &entities[i], "hammerid", INT_MIN );
+ if ( nId == nHammerId )
+ return &entities[i];
+ }
+ }
+
+ // Unfortunately, hammmerid appears to be a relatively new feature. Now, we must
+ // look for target name
+ int nMatch = -1;
+ const char *pTargetName = pEntity->GetString( "targetname" );
+ if ( pTargetName && pTargetName[0] )
+ {
+ // First, look for hammerid
+ for ( int i = 0; i < num_entities; ++i )
+ {
+ const char *pMatchTargetName = ValueForKey( &entities[i], "targetname" );
+ if ( !pMatchTargetName || !pMatchTargetName[0] )
+ continue;
+ if ( !V_stricmp( pTargetName, pMatchTargetName ) )
+ {
+ if ( nMatch >= 0 )
+ {
+ //Warning( "Encountered multiple entities that matched targetname %s!\n", pTargetName );
+ //return false;
+ nMatch = -1; // force a fallback to scanning classname and origin
+ break;
+ }
+ else
+ nMatch = i;
+ }
+ }
+ }
+
+ if ( nMatch >= 0 )
+ return &entities[nMatch];
+
+ // No target name? Well, let's try classname and origin.
+ const char *pClassName = pEntity->GetString( "classname" );
+ if ( pClassName && pClassName[0] )
+ {
+ // First, look for hammerid
+ for ( int i = 0; i < num_entities; ++i )
+ {
+ const char *pMatchClassName = ValueForKey( &entities[i], "classname" );
+ if ( !pMatchClassName || !pMatchClassName[0] )
+ continue;
+ if ( V_stricmp( pClassName, pMatchClassName ) )
+ continue;
+
+ const char *pOrigin = "(na)";
+ if ( V_stricmp( pClassName, "worldspawn" ) ) // allow worldspawn to match all
+ {
+ pOrigin = pEntity->GetString( "origin" );
+ const char *pMatchOrigin = ValueForKey( &entities[i], "origin" );
+ if ( !pMatchOrigin || !pMatchOrigin[0] )
+ continue;
+ if ( V_stricmp( pOrigin, pMatchOrigin ) )
+ continue;
+ }
+
+ if ( nMatch >= 0 )
+ {
+ Warning( "Encountered multiple entities that matched classname %s, origin %s!\n", pClassName, pOrigin );
+ return false;
+ }
+ nMatch = i;
+ }
+ }
+
+ if ( nMatch >= 0 )
+ return &entities[nMatch];
+
+ return NULL;
+}
+
+bool InsertEntity( entity_t *pEntity, KeyValues *pEntityKeys )
+{
+ CUtlVector<KeyValues *> vecKVs;
+ for ( KeyValues *pKey = pEntityKeys->GetFirstValue(); pKey; pKey = pKey->GetNextValue() )
+ {
+ vecKVs.AddToTail( pKey );
+ }
+
+ FOR_EACH_VEC_BACK( vecKVs, i )
+ {
+ epair_t *e = (epair_t*)malloc( sizeof(epair_t) );
+ memset (e, 0, sizeof(epair_t));
+
+ const char *pName = vecKVs[i]->GetName();
+ if ( strlen(pName) >= MAX_KEY-1 )
+ {
+ Warning( "ParseEpar: token %s too long", pName );
+ return false;
+ }
+ e->key = copystring(pName);
+
+ const char *pValue = vecKVs[i]->GetString();
+ if ( strlen(pValue) >= MAX_VALUE-1 )
+ {
+ Warning( "ParseEpar: token %s too long", pValue );
+ return false;
+ }
+ e->value = copystring(pValue);
+
+ // strip trailing spaces
+ StripTrailing( e->key );
+ StripTrailing( e->value );
+ e->next = pEntity->epairs;
+ pEntity->epairs = e;
+ }
+
+ // Flatten everything ( specifically, 'connection' keys, necessary to
+ // make the patch file have the same format as the commentary files )
+ for ( KeyValues *pKey = pEntityKeys->GetFirstTrueSubKey(); pKey; pKey = pKey->GetNextTrueSubKey() )
+ {
+ InsertEntity( pEntity, pKey );
+ }
+
+ return true;
+}
+
+bool InsertEntity( KeyValues *pEntity )
+{
+ entity_t &entity = entities[ num_entities++ ];
+ return InsertEntity( &entity, pEntity );
+}
+
+bool ReplaceEntity( KeyValues *pEntity )
+{
+ entity_t *pReplace = FindEntity( pEntity );
+ if ( !pReplace )
+ {
+ Warning( "Tried to replace an entity %s, origin %s, but couldn't find the original!\n", pEntity->GetString( "classname" ), pEntity->GetString( "origin" ) );
+ return false;
+ }
+
+ epair_t *pNext;
+ for ( epair_t *e = pReplace->epairs; e; e = pNext )
+ {
+ pNext = e->next;
+ free( e->key );
+ free( e->value );
+ free( e );
+ }
+ pReplace->epairs = NULL;
+
+ return InsertEntity( pReplace, pEntity );
+}
+
+
+//-----------------------------------------------------------------------------
+// The application object
+//-----------------------------------------------------------------------------
+int CMkEntityPatchApp::Main()
+{
+ // Backward compat for bsplib
+ g_pFileSystem = g_pFullFileSystem;
+
+ // This bit of hackery allows us to access files on the harddrive
+ g_pFullFileSystem->AddSearchPath( "", "LOCAL", PATH_ADD_TO_HEAD );
+
+ if ( CommandLine()->CheckParm( "-h" ) || CommandLine()->CheckParm( "-help" ) || CommandLine()->ParmCount() == 1 )
+ {
+ PrintHelp();
+ return 0;
+ }
+
+ // The file name is the last argument
+ const char *pBSPFile = CommandLine()->GetParm( CommandLine()->ParmCount() - 1 );
+ if ( !pBSPFile || pBSPFile[0] == 0 || pBSPFile[0] == '-' )
+ {
+ PrintHelp();
+ return 0;
+ }
+
+ char pFullPath[MAX_PATH];
+ ComputeFullPath( pBSPFile, pFullPath, sizeof(pFullPath) );
+
+ char pBSPFileName[MAX_PATH];
+ char pPatchFileName[MAX_PATH];
+ char pOutputFileName[MAX_PATH];
+ V_strcpy( pBSPFileName, pFullPath );
+ V_strcpy( pPatchFileName, pFullPath );
+ V_SetExtension( pBSPFileName, ".bsp", sizeof(pBSPFileName) );
+ V_SetExtension( pPatchFileName, ".pat", sizeof(pPatchFileName) );
+ GenerateLumpFileName( pFullPath, pOutputFileName, sizeof(pOutputFileName), LUMP_ENTITIES );
+
+ if ( !g_pFullFileSystem->FileExists( pBSPFileName ) )
+ {
+ Warning( "BSP file %s doesn't exist!\n", pBSPFileName );
+ return 0;
+ }
+
+ if ( !g_pFullFileSystem->FileExists( pPatchFileName ) )
+ {
+ Warning( "BSP patch file %s doesn't exist!\n", pPatchFileName );
+ return 0;
+ }
+
+ KeyValues *pKeyValues = new KeyValues( "patch" );
+ if ( !pKeyValues->LoadFromFile( g_pFullFileSystem, pPatchFileName ) )
+ {
+ Warning( "Error parsing patch file %s!\n", pPatchFileName );
+ return 0;
+ }
+
+ LoadBSPFile( pFullPath );
+ ParseEntities();
+
+ for( int i = 0; i < num_entities; i++ )
+ {
+ entity_t *pCur = &entities[i];
+ epair_t *pNext = NULL;
+ epair_t *pPrev = NULL;
+ for ( epair_t *e = pCur->epairs; e; e = pNext )
+ {
+ pNext = e->next;
+ e->next = pPrev;
+ pPrev = e;
+ }
+ pCur->epairs = pPrev;
+ }
+
+ for ( KeyValues *pKey = pKeyValues->GetFirstTrueSubKey(); pKey; pKey = pKey->GetNextTrueSubKey() )
+ {
+ const char *pKeyName = pKey->GetName();
+ if ( !V_stricmp( pKeyName, "entity" ) )
+ {
+ if ( !InsertEntity( pKey ) )
+ return 0;
+ }
+ else if ( !V_stricmp( pKeyName, "replace_entity" ) )
+ {
+ if ( !ReplaceEntity( pKey ) )
+ return 0;
+ }
+ }
+
+ // Do Perforce Stuff
+ if ( CommandLine()->FindParm( "-nop4" ) )
+ {
+ g_p4factory->SetDummyMode( true );
+ }
+
+ g_p4factory->SetOpenFileChangeList( "Entity Patch Files" );
+
+ CP4AutoAddFile p4AddBSP( pBSPFileName );
+ CP4AutoAddFile p4AddPatch( pPatchFileName );
+ CP4AutoEditAddFile p4AddOutput( pOutputFileName );
+
+ UnparseEntities();
+ WriteLumpToFile( pBSPFileName, LUMP_ENTITIES, 0, dentdata.Base(), dentdata.Count() );
+
+ pKeyValues->deleteThis();
+
+ return -1;
+} \ No newline at end of file
diff --git a/utils/mkentitypatch/mkentitypatch.vpc b/utils/mkentitypatch/mkentitypatch.vpc
new file mode 100644
index 0000000..fe1a6af
--- /dev/null
+++ b/utils/mkentitypatch/mkentitypatch.vpc
@@ -0,0 +1,53 @@
+//-----------------------------------------------------------------------------
+// MKENTITYPATCH.VPC
+//
+// Project Script
+//-----------------------------------------------------------------------------
+
+$Macro SRCDIR "..\.."
+$Macro OUTBINDIR "$SRCDIR\..\game\bin"
+
+$Include "$SRCDIR\vpc_scripts\source_exe_con_base.vpc"
+
+$Configuration
+{
+ $Compiler
+ {
+ $AdditionalIncludeDirectories "$BASE,..\common"
+ $PreprocessorDefinitions "$BASE;DONT_PROTECT_FILEIO_FUNCTIONS"
+ }
+}
+
+$Project "mkentitypatch"
+{
+ $Folder "Source Files"
+ {
+ $File "mkentitypatch.cpp"
+ }
+
+ $Folder "External"
+ {
+ $File "$SRCDIR\utils\common\bsplib.cpp"
+ $File "$SRCDIR\utils\common\filesystem_tools.cpp"
+ $File "$SRCDIR\utils\common\scriplib.cpp"
+ $File "$SRCDIR\utils\common\cmdlib.cpp"
+ $File "$SRCDIR\public\filesystem_helpers.cpp"
+ $File "$SRCDIR\public\lumpfiles.cpp"
+ $File "$SRCDIR\public\zip_utils.cpp"
+ }
+
+ $Folder "External Header Files"
+ {
+ $File "$SRCDIR\utils\common\bsplib.h"
+ $File "$SRCDIR\public\lumpfiles.h"
+ $File "$SRCDIR\public\filesystem_helpers.h"
+ }
+
+ $Folder "Link Libraries"
+ {
+ $Lib appframework
+ $Lib mathlib
+ $Lib tier2
+ $Lib "$LIBCOMMON/lzma"
+ }
+}