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();
}