diff options
| author | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
|---|---|---|
| committer | FluorescentCIAAfricanAmerican <[email protected]> | 2020-04-22 12:56:21 -0400 |
| commit | 3bf9df6b2785fa6d951086978a3e66f49427166a (patch) | |
| tree | 2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /utils/mkentitypatch | |
| download | archived-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.cpp | 418 | ||||
| -rw-r--r-- | utils/mkentitypatch/mkentitypatch.vpc | 53 |
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" + } +} |