void CScriptParser::SearchForFiles( const char *szWildcardPath ) { char filePath[FILE_PATH_MAX_LENGTH]; char basePath[FILE_PATH_MAX_LENGTH]; Q_strncpy( basePath, szWildcardPath, FILE_PATH_MAX_LENGTH ); V_StripFilename( basePath ); FileFindHandle_t findHandle; const char *fileName = filesystem->FindFirstEx( szWildcardPath, GetFSSearchPath(), &findHandle ); while ( fileName != NULL ) { Q_ComposeFileName( basePath, fileName, filePath, FILE_PATH_MAX_LENGTH ); if( !ParseFile( filePath ) ) { if ( m_bAlert ) DevWarning( "[script_parser] Unable to parse '%s'!\n", filePath ); } fileName = filesystem->FindNext( findHandle ); } filesystem->FindClose( findHandle ); }
//----------------------------------------------------------------------------- // The application object //----------------------------------------------------------------------------- void CSFMGenApp::GenerateSFMFiles( SFMGenInfo_t& info ) { char pRelativeModelPath[MAX_PATH]; Q_ComposeFileName( "models", info.m_pModelName, pRelativeModelPath, sizeof(pRelativeModelPath) ); Q_SetExtension( pRelativeModelPath, ".mdl", sizeof(pRelativeModelPath) ); MDLHandle_t hMDL = g_pMDLCache->FindMDL( pRelativeModelPath ); if ( hMDL == MDLHANDLE_INVALID ) { Warning( "sfmgen: Model %s doesn't exist!\n", pRelativeModelPath ); return; } studiohdr_t *pStudioHdr = g_pMDLCache->GetStudioHdr( hMDL ); if ( !pStudioHdr || g_pMDLCache->IsErrorModel( hMDL ) ) { Warning( "sfmgen: Model %s doesn't exist!\n", pRelativeModelPath ); return; } CUtlBuffer buf( 0, 0, CUtlBuffer::TEXT_BUFFER ); if ( !g_pFullFileSystem->ReadFile( info.m_pCSVFile, NULL, buf ) ) { Warning( "sfmgen: Unable to load file %s\n", info.m_pCSVFile ); return; } CUtlVector< SFMInfo_t > infoList; ParseCSVFile( buf, infoList, 1 ); int nCount = infoList.Count(); if ( nCount == 0 ) { Warning( "sfmgen: no files to create!\n" ); return; } UniqueifyNames( infoList ); // Construct full path to the output directories char pFullPath[MAX_PATH]; char pFullFacPathBuf[MAX_PATH]; const char *pExportFacPath = NULL; ComputeFullPath( info.m_pOutputDirectory, pFullPath, sizeof(pFullPath) ); if ( info.m_pExportFacDirectory ) { ComputeFullPath( info.m_pExportFacDirectory, pFullFacPathBuf, sizeof(pFullFacPathBuf) ); pExportFacPath = pFullFacPathBuf; } for ( int i = 0; i < nCount; ++i ) { GenerateSFMFile( info, infoList[i], pStudioHdr, pFullPath, pExportFacPath ); } }
//----------------------------------------------------------------------------- // Generates a sound clip for the game sound //----------------------------------------------------------------------------- CDmeSoundClip *CSFMGenApp::CreateSoundClip( CDmeFilmClip *pShot, const char *pAnimationSetName, const char *pGameSound, studiohdr_t *pStudioHdr, CDmeGameSound **ppGameSound ) { *ppGameSound = NULL; CDmeTrackGroup *pTrackGroup = pShot->FindOrAddTrackGroup( "audio" ); CDmeTrack *pTrack = pTrackGroup->FindOrAddTrack( pAnimationSetName, DMECLIP_SOUND ); // Get the gender for the model gender_t actorGender = g_pSoundEmitterSystem->GetActorGender( pStudioHdr->name ); // Get the wav file for the gamesound. CSoundParameters params; if ( !g_pSoundEmitterSystem->GetParametersForSound( pGameSound, params, actorGender ) ) { Warning( "Unable to determine .wav file for gamesound %s!\n", pGameSound ); return NULL; } // Get the sound duration char pFullPath[MAX_PATH]; char pRelativePath[MAX_PATH]; const char *pWavFile = PSkipSoundChars( params.soundname ); Q_ComposeFileName( "sound", pWavFile, pRelativePath, sizeof(pRelativePath) ); g_pFullFileSystem->RelativePathToFullPath( pRelativePath, "GAME", pFullPath, sizeof(pFullPath) ); DmeTime_t duration( GetWavSoundDuration( pFullPath ) ); CDmeGameSound *pDmeSound = CreateElement< CDmeGameSound >( pGameSound, pTrack->GetFileId() ); Assert( pDmeSound ); pDmeSound->m_GameSoundName = pGameSound; pDmeSound->m_SoundName = params.soundname; pDmeSound->m_Volume = params.volume; pDmeSound->m_Level = params.soundlevel; pDmeSound->m_Pitch = params.pitch; pDmeSound->m_IsStatic = true; pDmeSound->m_Channel = CHAN_STATIC; pDmeSound->m_Flags = 0; pDmeSound->m_Origin = vec3_origin; pDmeSound->m_Direction = vec3_origin; CDmeSoundClip *pSubClip = CreateElement< CDmeSoundClip >( pGameSound, pTrack->GetFileId() ); pSubClip->m_Sound = pDmeSound; pSubClip->SetStartTime( DMETIME_ZERO ); pSubClip->SetTimeOffset( DmeTime_t( -params.delay_msec / 1000.0f ) ); pSubClip->SetDuration( duration ); pTrack->AddClip( pSubClip ); *ppGameSound = pDmeSound; return pSubClip; }
//----------------------------------------------------------------------------- // 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 ); }
FSReturnCode_t FileSystem_LoadSearchPaths( CFSSearchPathsInit &initInfo ) { if ( !initInfo.m_pFileSystem || !initInfo.m_pDirectoryName ) return SetupFileSystemError( false, FS_INVALID_PARAMETERS, "FileSystem_LoadSearchPaths: Invalid parameters specified." ); KeyValues *pMainFile, *pFileSystemInfo, *pSearchPaths; FSReturnCode_t retVal = LoadGameInfoFile( initInfo.m_pDirectoryName, pMainFile, pFileSystemInfo, pSearchPaths ); if ( retVal != FS_OK ) return retVal; // All paths except those marked with |gameinfo_path| are relative to the base dir. char baseDir[MAX_PATH]; if ( !FileSystem_GetBaseDir( baseDir, sizeof( baseDir ) ) ) return SetupFileSystemError( false, FS_INVALID_PARAMETERS, "FileSystem_GetBaseDir failed." ); initInfo.m_ModPath[0] = 0; #define GAMEINFOPATH_TOKEN "|gameinfo_path|" #define BASESOURCEPATHS_TOKEN "|all_source_engine_paths|" bool bLowViolence = IsLowViolenceBuild(); bool bFirstGamePath = true; for ( KeyValues *pCur=pSearchPaths->GetFirstValue(); pCur; pCur=pCur->GetNextValue() ) { const char *pPathID = pCur->GetName(); const char *pLocation = pCur->GetString(); if ( Q_stristr( pLocation, GAMEINFOPATH_TOKEN ) == pLocation ) { pLocation += strlen( GAMEINFOPATH_TOKEN ); FileSystem_AddLoadedSearchPath( initInfo, pPathID, &bFirstGamePath, initInfo.m_pDirectoryName, pLocation, bLowViolence ); } else if ( Q_stristr( pLocation, BASESOURCEPATHS_TOKEN ) == pLocation ) { // This is a special identifier that tells it to add the specified path for all source engine versions equal to or prior to this version. // So in Orange Box, if they specified: // |all_source_engine_paths|hl2 // it would add the ep2\hl2 folder and the base (ep1-era) hl2 folder. // // We need a special identifier in the gameinfo.txt here because the base hl2 folder exists in different places. // In the case of a game or a Steam-launched dedicated server, all the necessary prior engine content is mapped in with the Steam depots, // so we can just use the path as-is. // In the case of an hldsupdatetool dedicated server, the base hl2 folder is "..\..\hl2" (since we're up in the 'orangebox' folder). pLocation += strlen( BASESOURCEPATHS_TOKEN ); // Add the Orange-box path (which also will include whatever the depots mapped in as well if we're // running a Steam-launched app). FileSystem_AddLoadedSearchPath( initInfo, pPathID, &bFirstGamePath, baseDir, pLocation, bLowViolence ); if ( FileSystem_IsHldsUpdateToolDedicatedServer() ) { // If we're using the hldsupdatetool dedicated server, then go up a directory to get the ep1-era files too. char ep1EraPath[MAX_PATH]; V_snprintf( ep1EraPath, sizeof( ep1EraPath ), "..%c%s", CORRECT_PATH_SEPARATOR, pLocation ); FileSystem_AddLoadedSearchPath( initInfo, pPathID, &bFirstGamePath, baseDir, ep1EraPath, bLowViolence ); } } else { FileSystem_AddLoadedSearchPath( initInfo, pPathID, &bFirstGamePath, baseDir, pLocation, bLowViolence ); } } pMainFile->deleteThis(); // // Set up search paths for add-ons // if ( IsPC() ) { #ifdef ENGINE_DLL FileSystem_UpdateAddonSearchPaths( initInfo.m_pFileSystem ); #endif } // these specialized tool paths are not used on 360 and cause a costly constant perf tax, so inhibited if ( IsPC() ) { // Create a content search path based on the game search path const char *pGameRoot = getenv( GAMEROOT_TOKEN ); const char *pContentRoot = getenv( CONTENTROOT_TOKEN ); if ( pGameRoot && pContentRoot ) { int nLen = initInfo.m_pFileSystem->GetSearchPath( "GAME", false, NULL, 0 ); char *pSearchPath = (char*)stackalloc( nLen * sizeof(char) ); initInfo.m_pFileSystem->GetSearchPath( "GAME", false, pSearchPath, nLen ); char *pPath = pSearchPath; while( pPath ) { char *pSemiColon = strchr( pPath, ';' ); if ( pSemiColon ) { *pSemiColon = 0; } Q_StripTrailingSlash( pPath ); Q_FixSlashes( pPath ); const char *pCurPath = pPath; pPath = pSemiColon ? pSemiColon + 1 : NULL; char pRelativePath[MAX_PATH]; char pContentPath[MAX_PATH]; if ( !Q_MakeRelativePath( pCurPath, pGameRoot, pRelativePath, sizeof(pRelativePath) ) ) continue; Q_ComposeFileName( pContentRoot, pRelativePath, pContentPath, sizeof(pContentPath) ); initInfo.m_pFileSystem->AddSearchPath( pContentPath, "CONTENT" ); } // Add the "platform" directory as a game searchable path char pPlatformPath[MAX_PATH]; Q_ComposeFileName( pGameRoot, "platform", pPlatformPath, sizeof(pPlatformPath) ); initInfo.m_pFileSystem->AddSearchPath( pPlatformPath, "GAME", PATH_ADD_TO_TAIL ); initInfo.m_pFileSystem->AddSearchPath( pContentRoot, "CONTENTROOT" ); initInfo.m_pFileSystem->AddSearchPath( pGameRoot, "GAMEROOT" ); } else { // Come up with some reasonable default int nLen = initInfo.m_pFileSystem->GetSearchPath( "MOD", false, NULL, 0 ); char *pSearchPath = (char*)stackalloc( nLen * sizeof(char) ); initInfo.m_pFileSystem->GetSearchPath( "MOD", false, pSearchPath, nLen ); char *pSemiColon = strchr( pSearchPath, ';' ); if ( pSemiColon ) { *pSemiColon = 0; } char pGameRootPath[MAX_PATH]; Q_strncpy( pGameRootPath, pSearchPath, sizeof(pGameRootPath) ); Q_StripTrailingSlash( pGameRootPath ); Q_StripFilename( pGameRootPath ); char pContentRootPath[MAX_PATH]; Q_strncpy( pContentRootPath, pGameRootPath, sizeof(pContentRootPath) ); char *pGame = Q_stristr( pContentRootPath, "game" ); if ( pGame ) { Q_strcpy( pGame, "content" ); } initInfo.m_pFileSystem->AddSearchPath( pContentRootPath, "CONTENTROOT" ); initInfo.m_pFileSystem->AddSearchPath( pGameRootPath, "GAMEROOT" ); } // Also, mark specific path IDs as "by request only". That way, we won't waste time searching in them // when people forget to specify a search path. initInfo.m_pFileSystem->MarkPathIDByRequestOnly( "contentroot", true ); initInfo.m_pFileSystem->MarkPathIDByRequestOnly( "gameroot", true ); initInfo.m_pFileSystem->MarkPathIDByRequestOnly( "content", true ); } // Also, mark specific path IDs as "by request only". That way, we won't waste time searching in them // when people forget to specify a search path. initInfo.m_pFileSystem->MarkPathIDByRequestOnly( "executable_path", true ); initInfo.m_pFileSystem->MarkPathIDByRequestOnly( "gamebin", true ); initInfo.m_pFileSystem->MarkPathIDByRequestOnly( "mod", true ); // Add the write path last. if ( initInfo.m_ModPath[0] != 0 ) { initInfo.m_pFileSystem->AddSearchPath( initInfo.m_ModPath, "DEFAULT_WRITE_PATH", PATH_ADD_TO_TAIL ); } #ifdef _DEBUG initInfo.m_pFileSystem->PrintSearchPaths(); #endif #if defined( ENABLE_RUNTIME_STACK_TRANSLATION ) && !defined( _X360 ) //copy search paths to stack tools so it can grab pdb's from all over. But only on P4 or Steam Beta builds if( (CommandLine()->FindParm( "-steam" ) == 0) || //not steam (CommandLine()->FindParm( "-internalbuild" ) != 0) ) //steam beta is ok { char szSearchPaths[4096]; //int CBaseFileSystem::GetSearchPath( const char *pathID, bool bGetPackFiles, char *pPath, int nMaxLen ) int iLength1 = initInfo.m_pFileSystem->GetSearchPath( "EXECUTABLE_PATH", false, szSearchPaths, 4096 ); if( iLength1 == 1 ) iLength1 = 0; int iLength2 = initInfo.m_pFileSystem->GetSearchPath( "GAMEBIN", false, szSearchPaths + iLength1, 4096 - iLength1 ); if( (iLength2 > 1) && (iLength1 > 1) ) { szSearchPaths[iLength1 - 1] = ';'; //replace first null terminator } const char *szAdditionalPath = CommandLine()->ParmValue( "-AdditionalPDBSearchPath" ); if( szAdditionalPath && szAdditionalPath[0] ) { int iLength = iLength1; if( iLength2 > 1 ) iLength += iLength2; if( iLength != 0 ) { szSearchPaths[iLength - 1] = ';'; //replaces null terminator } V_strncpy( &szSearchPaths[iLength], szAdditionalPath, 4096 - iLength ); } //Append the perforce symbol server last. Documentation says that "srv*\\perforce\symbols" should work, but it doesn't. //"symsrv*symsrv.dll*\\perforce\symbols" which the docs say is the same statement, works. { V_strncat( szSearchPaths, ";symsrv*symsrv.dll*\\\\perforce\\symbols", 4096 ); } SetStackTranslationSymbolSearchPath( szSearchPaths ); //MessageBox( NULL, szSearchPaths, "Search Paths", 0 ); } #endif return FS_OK; }
//----------------------------------------------------------------------------- // Creates a single sfm file //----------------------------------------------------------------------------- void CSFMGenApp::GenerateSFMFile( const SFMGenInfo_t& sfmGenInfo, const SFMInfo_t &info, studiohdr_t *pStudioHdr, const char *pOutputDirectory, const char *pExportFacPath ) { CSFMSession session; session.Init(); char pAnimationSetName[256]; Q_FileBase( pStudioHdr->name, pAnimationSetName, sizeof(pAnimationSetName) ); // Set the file id( necessary for phoneme extraction) DmFileId_t fileid = g_pDataModel->FindOrCreateFileId( pAnimationSetName ); session.Root()->SetFileId( fileid, TD_DEEP ); g_pModelPresetGroupMgr->AssociatePresetsWithFile( fileid ); // Get the shot. CDmeFilmClip *pMovie = session.Root()->GetValueElement<CDmeFilmClip>( "activeClip" ); CDmeFilmClip* pShot = CastElement< CDmeFilmClip >( pMovie->GetFilmTrack()->GetClip( 0 ) ); // Create a camera for the shot DmeCameraParams_t cameraParams( pAnimationSetName, vec3_origin, QAngle( 0, 180, 0 ) ); cameraParams.origin.x = pStudioHdr->hull_max.x + 20; cameraParams.origin.z = Lerp( 0.95f, pStudioHdr->hull_min.z, pStudioHdr->hull_max.z ); cameraParams.fov = 75.0f; CDmeCamera* pCamera = session.CreateCamera( cameraParams ); pShot->SetCamera( pCamera ); // Create a game model for the studio hdr CDmeGameModel *pGameModel = session.CreateEditorGameModel( pStudioHdr, vec3_origin, Quaternion( 0, 0, 0, 1 ) ); // Create a scene for the shot CDmeDag *pScene = session.FindOrCreateScene( pShot, pAnimationSetName ); pScene->AddChild( pGameModel ); // Create a sound clip CDmeGameSound *pGameSound; CDmeSoundClip *pSoundClip = CreateSoundClip( pMovie, pAnimationSetName, info.m_GameSound, pStudioHdr, &pGameSound ); pShot->SetDuration( pSoundClip->GetDuration() ); pMovie->SetDuration( pSoundClip->GetDuration() ); // Create an animation set CDmeAnimationSet *pAnimationSet = CreateAnimationSet( pMovie, pShot, pGameModel, pAnimationSetName, 0, false ); // Extract phonemes if ( pSoundClip ) { CExtractInfo extractInfo; extractInfo.m_pClip = pSoundClip; extractInfo.m_pSound = pGameSound; extractInfo.m_sHintText = info.m_Text; extractInfo.m_bUseSentence = sfmGenInfo.m_bUsePhonemesInWavs; ExtractDesc_t extractDesc; extractDesc.m_nExtractType = EXTRACT_WIPE_CLIP; extractDesc.m_pMovie = pMovie; extractDesc.m_pShot = pShot; extractDesc.m_pSet = pAnimationSet; extractDesc.m_flSampleRateHz = sfmGenInfo.m_flSampleRateHz; extractDesc.m_flSampleFilterSize = sfmGenInfo.m_flSampleFilterSize; extractDesc.m_WorkList.AddToTail( extractInfo ); BuildFacialControlList( pShot, pAnimationSet, extractDesc.m_ControlList ); sfm_phonemeextractor->Extract( SPEECH_API_LIPSINC, extractDesc, sfmGenInfo.m_bWritePhonemesInWavs ); CExtractInfo &results = extractDesc.m_WorkList[ 0 ]; CDmElement *pExtractionSettings = pGameSound->FindOrAddPhonemeExtractionSettings(); pExtractionSettings->SetValue< float >( "duration", results.m_flDuration ); // Store off phonemes if ( !pExtractionSettings->HasAttribute( "results" ) ) { pExtractionSettings->AddAttribute( "results", AT_ELEMENT_ARRAY ); } CDmrElementArray< CDmElement > resultsArray( pExtractionSettings, "results" ); resultsArray.RemoveAll(); for ( int i = 0; i < results.m_ApplyTags.Count(); ++i ) { CBasePhonemeTag *tag = results.m_ApplyTags[ i ]; CDmElement *tagElement = CreateElement< CDmElement >( ConvertPhoneme( tag->GetPhonemeCode() ), pGameSound->GetFileId() ); tagElement->SetValue< float >( "start", tag->GetStartTime() ); tagElement->SetValue< float >( "end", tag->GetEndTime() ); resultsArray.AddToTail( tagElement ); } if ( pExportFacPath ) { char pFACFileName[MAX_PATH]; Q_ComposeFileName( pExportFacPath, info.m_DMXFileName, pFACFileName, sizeof(pFACFileName) ); Q_SetExtension( pFACFileName, ".dmx", sizeof(pFACFileName) ); ExportFacialAnimation( pFACFileName, pMovie, pShot, pAnimationSet ); } } SaveSFMFile( session.Root(), pOutputDirectory, info.m_DMXFileName ); session.Shutdown(); }