diff options
Diffstat (limited to 'utils/vcdupdate')
| -rw-r--r-- | utils/vcdupdate/vcdupdate.cpp | 451 | ||||
| -rw-r--r-- | utils/vcdupdate/vcdupdate.vpc | 50 |
2 files changed, 501 insertions, 0 deletions
diff --git a/utils/vcdupdate/vcdupdate.cpp b/utils/vcdupdate/vcdupdate.cpp new file mode 100644 index 0000000..8a79453 --- /dev/null +++ b/utils/vcdupdate/vcdupdate.cpp @@ -0,0 +1,451 @@ +//========= 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/tier3app.h" +#include "datamodel/idatamodel.h" +#include "filesystem.h" +#include "icommandline.h" +#include "materialsystem/imaterialsystem.h" +#include "istudiorender.h" +#include "mathlib/mathlib.h" +#include "tier2/p4helpers.h" +#include "p4lib/ip4.h" +#include "sfmobjects/sfmsession.h" +#include "datacache/idatacache.h" +#include "datacache/imdlcache.h" +#include "vphysics_interface.h" +#include "studio.h" +#include "soundemittersystem/isoundemittersystembase.h" +#include "tier2/soundutils.h" +#include "tier2/fileutils.h" +#include "tier3/choreoutils.h" +#include "tier3/scenetokenprocessor.h" +#include "soundchars.h" +#include "choreoscene.h" +#include "choreoactor.h" +#include "choreochannel.h" +#include "choreoevent.h" +#include <ctype.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 CVcdUpdateApp : public CTier3SteamApp +{ + typedef CTier3SteamApp BaseClass; + +public: + // Methods of IApplication + virtual bool Create(); + virtual bool PreInit( ); + virtual int Main(); + virtual void Destroy() {} + + void PrintHelp( ); + +private: + struct VcdUpdateInfo_t + { + const char *m_pChangedWAVFile; + const char *m_pChangedMDLFile; + bool m_bWarnMissingMDL; + bool m_bWarnNotMatchingMDL; + }; + + void UpdateVcdFiles( const VcdUpdateInfo_t& info ); + void UpdateVcd( const char *pFullPath, const VcdUpdateInfo_t& info, studiohdr_t *pStudioHdr, float flWavDuration ); + bool UpdateVcd( CChoreoScene *pScene, const VcdUpdateInfo_t& info, studiohdr_t *pStudioHdr, float flWavDuration ); +}; + + +DEFINE_CONSOLE_STEAM_APPLICATION_OBJECT( CVcdUpdateApp ); + +//----------------------------------------------------------------------------- +// The application object +//----------------------------------------------------------------------------- +bool CVcdUpdateApp::Create() +{ + SpewOutputFunc( SpewStdout ); + + AppSystemInfo_t appSystems[] = + { + { "materialsystem.dll", MATERIAL_SYSTEM_INTERFACE_VERSION }, + { "p4lib.dll", P4_INTERFACE_VERSION }, + { "datacache.dll", DATACACHE_INTERFACE_VERSION }, + { "datacache.dll", MDLCACHE_INTERFACE_VERSION }, + { "studiorender.dll", STUDIO_RENDER_INTERFACE_VERSION }, + { "vphysics.dll", VPHYSICS_INTERFACE_VERSION }, + { "soundemittersystem.dll", SOUNDEMITTERSYSTEM_INTERFACE_VERSION }, + { "", "" } // Required to terminate the list + }; + + AddSystems( appSystems ); + + IMaterialSystem *pMaterialSystem = reinterpret_cast< IMaterialSystem * >( FindSystem( MATERIAL_SYSTEM_INTERFACE_VERSION ) ); + if ( !pMaterialSystem ) + { + Error( "// ERROR: Unable to connect to material system interface!\n" ); + return false; + } + + pMaterialSystem->SetShaderAPI( "shaderapiempty.dll" ); + return true; +} + + +//----------------------------------------------------------------------------- +// +//----------------------------------------------------------------------------- +bool CVcdUpdateApp::PreInit( ) +{ + MathLib_Init( 2.2f, 2.2f, 0.0f, 2.0f, false, false, false, false ); + + if ( !BaseClass::PreInit() ) + return false; + + if ( !g_pFullFileSystem || !g_pMDLCache ) + { + Error( "// ERROR: vcdupdate is missing a required interface!\n" ); + return false; + } + + // Add paths... + if ( !SetupSearchPaths( NULL, false, true ) ) + return false; + + return true; +} + + +//----------------------------------------------------------------------------- +// Print help +//----------------------------------------------------------------------------- +void CVcdUpdateApp::PrintHelp( ) +{ + Msg( "Usage: vcdupdate [-w <modified .wav file>] [-m <modified .mdl file>] [-e] [-n]\n" ); + Msg( " [-nop4] [-vproject <path to gameinfo.txt>]\n" ); + Msg( "vcdupdate will fixup all vcd files in the tree to account for other asset changes.\n" ); + Msg( "\t-w\t: Specifies the relative path to a .wav file that has changed.\n" ); + Msg( "\t-m\t: Specifies the relative path to a .mdl file that has changed.\n" ); + Msg( "\t-e\t: [Optional] Displays a warning about all .vcds that don't have actors w/ associated .mdls\n" ); + Msg( "\t\tSuch files cannot be auto-updated by vcdupdate and also can generate bugs.\n" ); + Msg( "\t-n\t: [Optional] Displays a warning about all .vcds whose actor names\n" ); + Msg( "\t\tare suspiciously different from the associated .mdl\n" ); + Msg( "\t-nop4\t: Disables auto perforce checkout/add.\n" ); + Msg( "\t-vproject\t: Specifies path to a gameinfo.txt file (which mod to build for).\n" ); +} + + +//----------------------------------------------------------------------------- +// Checks a single VCD to see if it needs to be updated or not. +//----------------------------------------------------------------------------- +bool CVcdUpdateApp::UpdateVcd( CChoreoScene *pScene, const VcdUpdateInfo_t& info, studiohdr_t *pStudioHdr, float flWavDuration ) +{ + CStudioHdr studioHdr( pStudioHdr, g_pMDLCache ); + float pPoseParameters[MAXSTUDIOPOSEPARAM]; + memset( pPoseParameters, 0, MAXSTUDIOPOSEPARAM * sizeof(float) ); + + char pModelPath[MAX_PATH]; + if ( pStudioHdr ) + { + g_pFullFileSystem->FullPathToRelativePathEx( info.m_pChangedMDLFile, "GAME", pModelPath, sizeof(pModelPath) ); + } + + bool bChanged = false; + int c = pScene->GetNumEvents(); + for ( int i = 0; i < c; i++ ) + { + CChoreoEvent *pEvent = pScene->GetEvent( i ); + if ( !pEvent ) + continue; + + switch ( pEvent->GetType() ) + { + default: + break; + case CChoreoEvent::SPEAK: + { + if ( !info.m_pChangedWAVFile ) + continue; + + const char *pWavFile = GetSoundForEvent( pEvent, &studioHdr ); + if ( !pWavFile ) + continue; + + char pRelativeWAVFile[MAX_PATH]; + Q_ComposeFileName( "sound", pWavFile, pRelativeWAVFile, sizeof(pRelativeWAVFile) ); + + char pFullWAVFile[MAX_PATH]; + g_pFullFileSystem->RelativePathToFullPath( pRelativeWAVFile, "GAME", pFullWAVFile, sizeof(pFullWAVFile) ); + + if ( Q_stricmp( pFullWAVFile, info.m_pChangedWAVFile ) ) + continue; + + float flEndTime = pEvent->GetStartTime() + flWavDuration; + if ( pEvent->GetEndTime() != flEndTime ) + { + pEvent->SetEndTime( pEvent->GetStartTime() + flEndTime ); + bChanged = true; + } + } + break; + + case CChoreoEvent::SEQUENCE: + { + if ( !pStudioHdr ) + continue; + + CChoreoActor *pActor = pEvent->GetActor(); + if ( pActor->GetName()[0] == '!' ) + continue; + const char *pModelName = pActor->GetFacePoserModelName(); + if ( !pModelName || !pModelName[0] || Q_stricmp( pModelPath, pModelName) ) + continue; + + if ( UpdateSequenceLength( pEvent, &studioHdr, pPoseParameters, false, false ) ) + { + bChanged = true; + } + } + break; + case CChoreoEvent::GESTURE: + { + if ( !pStudioHdr ) + continue; + + CChoreoActor *pActor = pEvent->GetActor(); + if ( pActor->GetName()[0] == '!' ) + continue; + const char *pModelName = pActor->GetFacePoserModelName(); + if ( !pModelName || !pModelName[0] || Q_stricmp( pModelPath, pModelName ) ) + continue; + + if ( UpdateGestureLength( pEvent, &studioHdr, pPoseParameters, false ) ) + { + bChanged = true; + } + if ( AutoAddGestureKeys( pEvent, &studioHdr, pPoseParameters, false ) ) + { + bChanged = true; + } + } + break; + } + } + + return bChanged; +} + + +//----------------------------------------------------------------------------- +// Checks a single VCD to see if it needs to be updated or not. +//----------------------------------------------------------------------------- +void CVcdUpdateApp::UpdateVcd( const char *pFullPath, const VcdUpdateInfo_t& info, studiohdr_t *pStudioHdr, float flWavDuration ) +{ + CUtlBuffer buf; + if ( !g_pFullFileSystem->ReadFile( pFullPath, NULL, buf ) ) + { + Warning( "Unable to load file %s\n", pFullPath ); + return; + } + + SetTokenProcessorBuffer( (char *)buf.Base() ); + CChoreoScene *pScene = ChoreoLoadScene( pFullPath, NULL, GetTokenProcessor(), NULL ); + if ( !pScene ) + { + Warning( "Unable to parse file %s\n", pFullPath ); + return; + } + + // Check for validity. + if ( info.m_bWarnNotMatchingMDL || info.m_bWarnMissingMDL ) + { + char pBaseModelName[MAX_PATH]; + int nActorCount = pScene->GetNumActors(); + for ( int i = 0; i < nActorCount; ++i ) + { + CChoreoActor* pActor = pScene->GetActor( i ); + const char *pActorName = pActor->GetName(); + if ( pActorName[0] == '!' ) + continue; + + const char *pModelName = pActor->GetFacePoserModelName(); + if ( !pModelName || !pModelName[0] ) + { + if ( info.m_bWarnMissingMDL ) + { + Warning( "\t*** Missing .mdl association: File \"%s\"\tActor \"%s\"\n", pFullPath, pActorName ); + } + continue; + } + + if ( info.m_bWarnNotMatchingMDL ) + { + Q_FileBase( pModelName, pBaseModelName, sizeof(pBaseModelName) ); + if ( !StringHasPrefix( pActorName, pBaseModelName ) ) + { + Warning( "\t*** File \"%s\": Actor name and .mdl name suspiciously different:\n\t\tMDL \"%s\"\tActor \"%s\"\n", pFullPath, pModelName, pActorName ); + } + } + } + } + + if ( UpdateVcd( pScene, info, pStudioHdr, flWavDuration ) ) + { + Warning( "*** VCD %s requires update.\n", pFullPath ); + CP4AutoEditAddFile checkout( pFullPath ); + pScene->SaveToFile( pFullPath ); + } +} + + +//----------------------------------------------------------------------------- +// Loads up the changed files and builds list of vcds to convert, then converts them +//----------------------------------------------------------------------------- +void CVcdUpdateApp::UpdateVcdFiles( const VcdUpdateInfo_t& info ) +{ + studiohdr_t *pStudioHdr = NULL; + if ( info.m_pChangedMDLFile ) + { + char pRelativeModelPath[MAX_PATH]; + g_pFullFileSystem->FullPathToRelativePathEx( info.m_pChangedMDLFile, "GAME", pRelativeModelPath, sizeof(pRelativeModelPath) ); + Q_SetExtension( pRelativeModelPath, ".mdl", sizeof(pRelativeModelPath) ); + MDLHandle_t hMDL = g_pMDLCache->FindMDL( pRelativeModelPath ); + if ( hMDL == MDLHANDLE_INVALID ) + { + Warning( "vcdupdate: Model %s doesn't exist!\n", pRelativeModelPath ); + return; + } + + pStudioHdr = g_pMDLCache->GetStudioHdr( hMDL ); + if ( !pStudioHdr || g_pMDLCache->IsErrorModel( hMDL ) ) + { + Warning( "vcdupdate: Model %s doesn't exist!\n", pRelativeModelPath ); + return; + } + } + + float flWavDuration = -1.0f; + if ( info.m_pChangedWAVFile ) + { + flWavDuration = GetWavSoundDuration( info.m_pChangedWAVFile ); + } + + CUtlVector< CUtlString > dirs; + CUtlVector< CUtlString > vcds; + GetSearchPath( dirs, "GAME" ); + int nCount = dirs.Count(); + for ( int i = 0; i < nCount; ++i ) + { + char pScenePath[MAX_PATH]; + Q_ComposeFileName( dirs[i], "scenes", pScenePath, sizeof(pScenePath) ); + AddFilesToList( vcds, pScenePath, NULL, "vcd" ); + } + + int nVCDCount = vcds.Count(); + Msg( "Found %d VCDs to check if update is necessary.\n", nVCDCount ); + for ( int i = 0; i < nVCDCount; ++i ) + { + UpdateVcd( vcds[i], info, pStudioHdr, flWavDuration ); + } +} + + +//----------------------------------------------------------------------------- +// The application object +//----------------------------------------------------------------------------- +int CVcdUpdateApp::Main() +{ + // 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" ) ) + { + PrintHelp(); + return 0; + } + + VcdUpdateInfo_t info; + info.m_pChangedWAVFile = CommandLine()->ParmValue( "-w" ); + info.m_pChangedMDLFile = CommandLine()->ParmValue( "-m" ); + info.m_bWarnMissingMDL = CommandLine()->ParmValue( "-e" ) ? true : false; + info.m_bWarnNotMatchingMDL = CommandLine()->ParmValue( "-n" ) ? true : false; + + if ( !info.m_pChangedWAVFile && !info.m_pChangedMDLFile ) + { + PrintHelp(); + return 0; + } + + // Determine full paths + char pFullMDLPath[MAX_PATH]; + char pFullWAVPath[MAX_PATH]; + if ( info.m_pChangedMDLFile ) + { + char pRelativeMDLPath[MAX_PATH]; + if ( !Q_IsAbsolutePath( info.m_pChangedMDLFile ) ) + { + Q_ComposeFileName( "models", info.m_pChangedMDLFile, pRelativeMDLPath, sizeof(pRelativeMDLPath) ); + g_pFullFileSystem->RelativePathToFullPath( pRelativeMDLPath, "GAME", pFullMDLPath, sizeof(pFullMDLPath) ); + } + info.m_pChangedMDLFile = pFullMDLPath; + } + if ( info.m_pChangedWAVFile ) + { + char pRelativeWAVPath[MAX_PATH]; + if ( !Q_IsAbsolutePath( info.m_pChangedWAVFile ) ) + { + Q_ComposeFileName( "sound", info.m_pChangedWAVFile, pRelativeWAVPath, sizeof(pRelativeWAVPath) ); + g_pFullFileSystem->RelativePathToFullPath( pRelativeWAVPath, "GAME", pFullWAVPath, sizeof(pFullWAVPath) ); + } + info.m_pChangedWAVFile = pFullWAVPath; + } + + // Do Perforce Stuff + if ( CommandLine()->FindParm( "-nop4" ) ) + { + g_p4factory->SetDummyMode( true ); + } + + g_p4factory->SetOpenFileChangeList( "Automatically Updated VCD files" ); + + g_pSoundEmitterSystem->ModInit(); + UpdateVcdFiles( info ); + g_pSoundEmitterSystem->ModShutdown(); + return -1; +}
\ No newline at end of file diff --git a/utils/vcdupdate/vcdupdate.vpc b/utils/vcdupdate/vcdupdate.vpc new file mode 100644 index 0000000..dc8e750 --- /dev/null +++ b/utils/vcdupdate/vcdupdate.vpc @@ -0,0 +1,50 @@ +//----------------------------------------------------------------------------- +// VCDUPDATE.VPC +// +// Project Script +//----------------------------------------------------------------------------- + +$Macro SRCDIR "..\.." +$Macro OUTBINDIR "$SRCDIR\..\game\bin" + +$Include "$SRCDIR\vpc_scripts\source_exe_con_base.vpc" + +$Configuration +{ + $Compiler + { + $AdditionalIncludeDirectories "$BASE,$SRCDIR\game\shared" + } +} + +$Project "vcdupdate" +{ + $Folder "Source Files" + { + $File "vcdupdate.cpp" + } + + $Folder "External" + { + $File "$SRCDIR\public\bone_setup.cpp" + $File "$SRCDIR\public\collisionutils.cpp" + $File "$SRCDIR\public\interpolatortypes.cpp" + $File "$SRCDIR\public\studio.cpp" + $File "$SRCDIR\public\sentence.cpp" + } + + $Folder "External Header Files" + { + $File "$SRCDIR\public\sentence.h" + $File "$SRCDIR\public\interpolatortypes.h" + } + + $Folder "Link Libraries" + { + $Lib appframework + $Lib choreoobjects + $Lib mathlib + $Lib tier2 + $Lib tier3 + } +} |