Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

450 lines
14 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // The copyright to the contents herein is the property of Valve, L.L.C.
  4. // The contents may be used and/or copied only with the written permission of
  5. // Valve, L.L.C., or in accordance with the terms and conditions stipulated in
  6. // the agreement/contract under which the contents have been supplied.
  7. //
  8. // $Header: $
  9. // $NoKeywords: $
  10. //
  11. //=============================================================================
  12. // Valve includes
  13. #include "appframework/tier3app.h"
  14. #include "datamodel/idatamodel.h"
  15. #include "filesystem.h"
  16. #include "icommandline.h"
  17. #include "materialsystem/imaterialsystem.h"
  18. #include "istudiorender.h"
  19. #include "mathlib/mathlib.h"
  20. #include "tier2/p4helpers.h"
  21. #include "p4lib/ip4.h"
  22. #include "sfmobjects/sfmsession.h"
  23. #include "datacache/idatacache.h"
  24. #include "datacache/imdlcache.h"
  25. #include "vphysics_interface.h"
  26. #include "studio.h"
  27. #include "soundemittersystem/isoundemittersystembase.h"
  28. #include "tier2/soundutils.h"
  29. #include "tier2/fileutils.h"
  30. #include "tier3/choreoutils.h"
  31. #include "tier3/scenetokenprocessor.h"
  32. #include "soundchars.h"
  33. #include "choreoscene.h"
  34. #include "choreoactor.h"
  35. #include "choreochannel.h"
  36. #include "choreoevent.h"
  37. #include <ctype.h>
  38. #ifdef _DEBUG
  39. #include <windows.h>
  40. #undef GetCurrentDirectory
  41. #endif
  42. //-----------------------------------------------------------------------------
  43. // Standard spew functions
  44. //-----------------------------------------------------------------------------
  45. static SpewRetval_t SpewStdout( SpewType_t spewType, char const *pMsg )
  46. {
  47. if ( !pMsg )
  48. return SPEW_CONTINUE;
  49. #ifdef _DEBUG
  50. OutputDebugString( pMsg );
  51. #endif
  52. printf( pMsg );
  53. fflush( stdout );
  54. return ( spewType == SPEW_ASSERT ) ? SPEW_DEBUGGER : SPEW_CONTINUE;
  55. }
  56. //-----------------------------------------------------------------------------
  57. // The application object
  58. //-----------------------------------------------------------------------------
  59. class CVcdUpdateApp : public CTier3SteamApp
  60. {
  61. typedef CTier3SteamApp BaseClass;
  62. public:
  63. // Methods of IApplication
  64. virtual bool Create();
  65. virtual bool PreInit( );
  66. virtual int Main();
  67. virtual void Destroy() {}
  68. void PrintHelp( );
  69. private:
  70. struct VcdUpdateInfo_t
  71. {
  72. const char *m_pChangedWAVFile;
  73. const char *m_pChangedMDLFile;
  74. bool m_bWarnMissingMDL;
  75. bool m_bWarnNotMatchingMDL;
  76. };
  77. void UpdateVcdFiles( const VcdUpdateInfo_t& info );
  78. void UpdateVcd( const char *pFullPath, const VcdUpdateInfo_t& info, studiohdr_t *pStudioHdr, float flWavDuration );
  79. bool UpdateVcd( CChoreoScene *pScene, const VcdUpdateInfo_t& info, studiohdr_t *pStudioHdr, float flWavDuration );
  80. };
  81. DEFINE_CONSOLE_STEAM_APPLICATION_OBJECT( CVcdUpdateApp );
  82. //-----------------------------------------------------------------------------
  83. // The application object
  84. //-----------------------------------------------------------------------------
  85. bool CVcdUpdateApp::Create()
  86. {
  87. SpewOutputFunc( SpewStdout );
  88. AppSystemInfo_t appSystems[] =
  89. {
  90. { "materialsystem.dll", MATERIAL_SYSTEM_INTERFACE_VERSION },
  91. { "p4lib.dll", P4_INTERFACE_VERSION },
  92. { "datacache.dll", DATACACHE_INTERFACE_VERSION },
  93. { "datacache.dll", MDLCACHE_INTERFACE_VERSION },
  94. { "studiorender.dll", STUDIO_RENDER_INTERFACE_VERSION },
  95. { "vphysics.dll", VPHYSICS_INTERFACE_VERSION },
  96. { "soundemittersystem.dll", SOUNDEMITTERSYSTEM_INTERFACE_VERSION },
  97. { "", "" } // Required to terminate the list
  98. };
  99. AddSystems( appSystems );
  100. IMaterialSystem *pMaterialSystem = reinterpret_cast< IMaterialSystem * >( FindSystem( MATERIAL_SYSTEM_INTERFACE_VERSION ) );
  101. if ( !pMaterialSystem )
  102. {
  103. Error( "// ERROR: Unable to connect to material system interface!\n" );
  104. return false;
  105. }
  106. pMaterialSystem->SetShaderAPI( "shaderapiempty.dll" );
  107. return true;
  108. }
  109. //-----------------------------------------------------------------------------
  110. //
  111. //-----------------------------------------------------------------------------
  112. bool CVcdUpdateApp::PreInit( )
  113. {
  114. MathLib_Init( 2.2f, 2.2f, 0.0f, 2.0f, false, false, false, false );
  115. if ( !BaseClass::PreInit() )
  116. return false;
  117. if ( !g_pFullFileSystem || !g_pMDLCache )
  118. {
  119. Error( "// ERROR: vcdupdate is missing a required interface!\n" );
  120. return false;
  121. }
  122. // Add paths...
  123. if ( !SetupSearchPaths( NULL, false, true ) )
  124. return false;
  125. return true;
  126. }
  127. //-----------------------------------------------------------------------------
  128. // Print help
  129. //-----------------------------------------------------------------------------
  130. void CVcdUpdateApp::PrintHelp( )
  131. {
  132. Msg( "Usage: vcdupdate [-w <modified .wav file>] [-m <modified .mdl file>] [-e] [-n]\n" );
  133. Msg( " [-nop4] [-vproject <path to gameinfo.txt>]\n" );
  134. Msg( "vcdupdate will fixup all vcd files in the tree to account for other asset changes.\n" );
  135. Msg( "\t-w\t: Specifies the relative path to a .wav file that has changed.\n" );
  136. Msg( "\t-m\t: Specifies the relative path to a .mdl file that has changed.\n" );
  137. Msg( "\t-e\t: [Optional] Displays a warning about all .vcds that don't have actors w/ associated .mdls\n" );
  138. Msg( "\t\tSuch files cannot be auto-updated by vcdupdate and also can generate bugs.\n" );
  139. Msg( "\t-n\t: [Optional] Displays a warning about all .vcds whose actor names\n" );
  140. Msg( "\t\tare suspiciously different from the associated .mdl\n" );
  141. Msg( "\t-nop4\t: Disables auto perforce checkout/add.\n" );
  142. Msg( "\t-vproject\t: Specifies path to a gameinfo.txt file (which mod to build for).\n" );
  143. }
  144. //-----------------------------------------------------------------------------
  145. // Checks a single VCD to see if it needs to be updated or not.
  146. //-----------------------------------------------------------------------------
  147. bool CVcdUpdateApp::UpdateVcd( CChoreoScene *pScene, const VcdUpdateInfo_t& info, studiohdr_t *pStudioHdr, float flWavDuration )
  148. {
  149. CStudioHdr studioHdr( pStudioHdr, g_pMDLCache );
  150. float pPoseParameters[MAXSTUDIOPOSEPARAM];
  151. memset( pPoseParameters, 0, MAXSTUDIOPOSEPARAM * sizeof(float) );
  152. char pModelPath[MAX_PATH];
  153. if ( pStudioHdr )
  154. {
  155. g_pFullFileSystem->FullPathToRelativePathEx( info.m_pChangedMDLFile, "GAME", pModelPath, sizeof(pModelPath) );
  156. }
  157. bool bChanged = false;
  158. int c = pScene->GetNumEvents();
  159. for ( int i = 0; i < c; i++ )
  160. {
  161. CChoreoEvent *pEvent = pScene->GetEvent( i );
  162. if ( !pEvent )
  163. continue;
  164. switch ( pEvent->GetType() )
  165. {
  166. default:
  167. break;
  168. case CChoreoEvent::SPEAK:
  169. {
  170. if ( !info.m_pChangedWAVFile )
  171. continue;
  172. const char *pWavFile = GetSoundForEvent( pEvent, &studioHdr );
  173. if ( !pWavFile )
  174. continue;
  175. char pRelativeWAVFile[MAX_PATH];
  176. Q_ComposeFileName( "sound", pWavFile, pRelativeWAVFile, sizeof(pRelativeWAVFile) );
  177. char pFullWAVFile[MAX_PATH];
  178. g_pFullFileSystem->RelativePathToFullPath( pRelativeWAVFile, "GAME", pFullWAVFile, sizeof(pFullWAVFile) );
  179. if ( Q_stricmp( pFullWAVFile, info.m_pChangedWAVFile ) )
  180. continue;
  181. float flEndTime = pEvent->GetStartTime() + flWavDuration;
  182. if ( pEvent->GetEndTime() != flEndTime )
  183. {
  184. pEvent->SetEndTime( pEvent->GetStartTime() + flEndTime );
  185. bChanged = true;
  186. }
  187. }
  188. break;
  189. case CChoreoEvent::SEQUENCE:
  190. {
  191. if ( !pStudioHdr )
  192. continue;
  193. CChoreoActor *pActor = pEvent->GetActor();
  194. if ( pActor->GetName()[0] == '!' )
  195. continue;
  196. const char *pModelName = pActor->GetFacePoserModelName();
  197. if ( !pModelName || !pModelName[0] || Q_stricmp( pModelPath, pModelName) )
  198. continue;
  199. if ( UpdateSequenceLength( pEvent, &studioHdr, pPoseParameters, false, false ) )
  200. {
  201. bChanged = true;
  202. }
  203. }
  204. break;
  205. case CChoreoEvent::GESTURE:
  206. {
  207. if ( !pStudioHdr )
  208. continue;
  209. CChoreoActor *pActor = pEvent->GetActor();
  210. if ( pActor->GetName()[0] == '!' )
  211. continue;
  212. const char *pModelName = pActor->GetFacePoserModelName();
  213. if ( !pModelName || !pModelName[0] || Q_stricmp( pModelPath, pModelName ) )
  214. continue;
  215. if ( UpdateGestureLength( pEvent, &studioHdr, pPoseParameters, false ) )
  216. {
  217. bChanged = true;
  218. }
  219. if ( AutoAddGestureKeys( pEvent, &studioHdr, pPoseParameters, false ) )
  220. {
  221. bChanged = true;
  222. }
  223. }
  224. break;
  225. }
  226. }
  227. return bChanged;
  228. }
  229. //-----------------------------------------------------------------------------
  230. // Checks a single VCD to see if it needs to be updated or not.
  231. //-----------------------------------------------------------------------------
  232. void CVcdUpdateApp::UpdateVcd( const char *pFullPath, const VcdUpdateInfo_t& info, studiohdr_t *pStudioHdr, float flWavDuration )
  233. {
  234. CUtlBuffer buf;
  235. if ( !g_pFullFileSystem->ReadFile( pFullPath, NULL, buf ) )
  236. {
  237. Warning( "Unable to load file %s\n", pFullPath );
  238. return;
  239. }
  240. SetTokenProcessorBuffer( (char *)buf.Base() );
  241. CChoreoScene *pScene = ChoreoLoadScene( pFullPath, NULL, GetTokenProcessor(), NULL );
  242. if ( !pScene )
  243. {
  244. Warning( "Unable to parse file %s\n", pFullPath );
  245. return;
  246. }
  247. // Check for validity.
  248. if ( info.m_bWarnNotMatchingMDL || info.m_bWarnMissingMDL )
  249. {
  250. char pBaseModelName[MAX_PATH];
  251. int nActorCount = pScene->GetNumActors();
  252. for ( int i = 0; i < nActorCount; ++i )
  253. {
  254. CChoreoActor* pActor = pScene->GetActor( i );
  255. const char *pActorName = pActor->GetName();
  256. if ( pActorName[0] == '!' )
  257. continue;
  258. const char *pModelName = pActor->GetFacePoserModelName();
  259. if ( !pModelName || !pModelName[0] )
  260. {
  261. if ( info.m_bWarnMissingMDL )
  262. {
  263. Warning( "\t*** Missing .mdl association: File \"%s\"\tActor \"%s\"\n", pFullPath, pActorName );
  264. }
  265. continue;
  266. }
  267. if ( info.m_bWarnNotMatchingMDL )
  268. {
  269. Q_FileBase( pModelName, pBaseModelName, sizeof(pBaseModelName) );
  270. if ( !StringHasPrefix( pActorName, pBaseModelName ) )
  271. {
  272. Warning( "\t*** File \"%s\": Actor name and .mdl name suspiciously different:\n\t\tMDL \"%s\"\tActor \"%s\"\n", pFullPath, pModelName, pActorName );
  273. }
  274. }
  275. }
  276. }
  277. if ( UpdateVcd( pScene, info, pStudioHdr, flWavDuration ) )
  278. {
  279. Warning( "*** VCD %s requires update.\n", pFullPath );
  280. CP4AutoEditAddFile checkout( pFullPath );
  281. pScene->SaveToFile( pFullPath );
  282. }
  283. }
  284. //-----------------------------------------------------------------------------
  285. // Loads up the changed files and builds list of vcds to convert, then converts them
  286. //-----------------------------------------------------------------------------
  287. void CVcdUpdateApp::UpdateVcdFiles( const VcdUpdateInfo_t& info )
  288. {
  289. studiohdr_t *pStudioHdr = NULL;
  290. if ( info.m_pChangedMDLFile )
  291. {
  292. char pRelativeModelPath[MAX_PATH];
  293. g_pFullFileSystem->FullPathToRelativePathEx( info.m_pChangedMDLFile, "GAME", pRelativeModelPath, sizeof(pRelativeModelPath) );
  294. Q_SetExtension( pRelativeModelPath, ".mdl", sizeof(pRelativeModelPath) );
  295. MDLHandle_t hMDL = g_pMDLCache->FindMDL( pRelativeModelPath );
  296. if ( hMDL == MDLHANDLE_INVALID )
  297. {
  298. Warning( "vcdupdate: Model %s doesn't exist!\n", pRelativeModelPath );
  299. return;
  300. }
  301. pStudioHdr = g_pMDLCache->GetStudioHdr( hMDL );
  302. if ( !pStudioHdr || g_pMDLCache->IsErrorModel( hMDL ) )
  303. {
  304. Warning( "vcdupdate: Model %s doesn't exist!\n", pRelativeModelPath );
  305. return;
  306. }
  307. }
  308. float flWavDuration = -1.0f;
  309. if ( info.m_pChangedWAVFile )
  310. {
  311. flWavDuration = GetWavSoundDuration( info.m_pChangedWAVFile );
  312. }
  313. CUtlVector< CUtlString > dirs;
  314. CUtlVector< CUtlString > vcds;
  315. GetSearchPath( dirs, "GAME" );
  316. int nCount = dirs.Count();
  317. for ( int i = 0; i < nCount; ++i )
  318. {
  319. char pScenePath[MAX_PATH];
  320. Q_ComposeFileName( dirs[i], "scenes", pScenePath, sizeof(pScenePath) );
  321. AddFilesToList( vcds, pScenePath, NULL, "vcd" );
  322. }
  323. int nVCDCount = vcds.Count();
  324. Msg( "Found %d VCDs to check if update is necessary.\n", nVCDCount );
  325. for ( int i = 0; i < nVCDCount; ++i )
  326. {
  327. UpdateVcd( vcds[i], info, pStudioHdr, flWavDuration );
  328. }
  329. }
  330. //-----------------------------------------------------------------------------
  331. // The application object
  332. //-----------------------------------------------------------------------------
  333. int CVcdUpdateApp::Main()
  334. {
  335. // This bit of hackery allows us to access files on the harddrive
  336. g_pFullFileSystem->AddSearchPath( "", "LOCAL", PATH_ADD_TO_HEAD );
  337. if ( CommandLine()->CheckParm( "-h" ) || CommandLine()->CheckParm( "-help" ) )
  338. {
  339. PrintHelp();
  340. return 0;
  341. }
  342. VcdUpdateInfo_t info;
  343. info.m_pChangedWAVFile = CommandLine()->ParmValue( "-w" );
  344. info.m_pChangedMDLFile = CommandLine()->ParmValue( "-m" );
  345. info.m_bWarnMissingMDL = CommandLine()->ParmValue( "-e" ) ? true : false;
  346. info.m_bWarnNotMatchingMDL = CommandLine()->ParmValue( "-n" ) ? true : false;
  347. if ( !info.m_pChangedWAVFile && !info.m_pChangedMDLFile )
  348. {
  349. PrintHelp();
  350. return 0;
  351. }
  352. // Determine full paths
  353. char pFullMDLPath[MAX_PATH];
  354. char pFullWAVPath[MAX_PATH];
  355. if ( info.m_pChangedMDLFile )
  356. {
  357. char pRelativeMDLPath[MAX_PATH];
  358. if ( !Q_IsAbsolutePath( info.m_pChangedMDLFile ) )
  359. {
  360. Q_ComposeFileName( "models", info.m_pChangedMDLFile, pRelativeMDLPath, sizeof(pRelativeMDLPath) );
  361. g_pFullFileSystem->RelativePathToFullPath( pRelativeMDLPath, "GAME", pFullMDLPath, sizeof(pFullMDLPath) );
  362. }
  363. info.m_pChangedMDLFile = pFullMDLPath;
  364. }
  365. if ( info.m_pChangedWAVFile )
  366. {
  367. char pRelativeWAVPath[MAX_PATH];
  368. if ( !Q_IsAbsolutePath( info.m_pChangedWAVFile ) )
  369. {
  370. Q_ComposeFileName( "sound", info.m_pChangedWAVFile, pRelativeWAVPath, sizeof(pRelativeWAVPath) );
  371. g_pFullFileSystem->RelativePathToFullPath( pRelativeWAVPath, "GAME", pFullWAVPath, sizeof(pFullWAVPath) );
  372. }
  373. info.m_pChangedWAVFile = pFullWAVPath;
  374. }
  375. // Do Perforce Stuff
  376. if ( CommandLine()->FindParm( "-nop4" ) )
  377. {
  378. g_p4factory->SetDummyMode( true );
  379. }
  380. g_p4factory->SetOpenFileChangeList( "Automatically Updated VCD files" );
  381. g_pSoundEmitterSystem->ModInit();
  382. UpdateVcdFiles( info );
  383. g_pSoundEmitterSystem->ModShutdown();
  384. return -1;
  385. }