//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
// $NoKeywords: $
//=============================================================================//

#include "basefilesystem.h"
#include "steamcommon.h"
#include "SteamInterface.h"
#include "tier0/dbg.h"
#include "tier0/icommandline.h"
#include "steam/steam_api.h"
#ifdef POSIX
#include <fcntl.h>
#ifdef LINUX
#include <sys/file.h>
#endif
#include <dlfcn.h>
#define _S_IWRITE S_IWRITE 
#define _S_IWRITE S_IWRITE
#define _S_IFREG S_IFREG
#define FILE_ATTRIBUTE_OFFLINE 0x1000
#endif

#ifdef _WIN32
	extern "C"
	{
		__declspec(dllimport) int __stdcall IsDebuggerPresent();
	}
#endif

ISteamInterface *steam = NULL;
static SteamHandle_t	g_pLastErrorFile; 
static TSteamError		g_tLastError;
static TSteamError g_tLastErrorNoFile;

void CheckError( SteamHandle_t fp, TSteamError & steamError)
{
	if (steamError.eSteamError == eSteamErrorContentServerConnect)
	{
		// fatal error
#ifdef WIN32		
		// kill the current window so the user can see the error
		HWND hwnd = GetForegroundWindow();
		if (hwnd)
		{
			DestroyWindow(hwnd);
		}
		
		// show the error
		MessageBox(NULL, "Could not acquire necessary game files because the connection to Steam servers was lost.", "Source - Fatal Error", MB_OK | MB_ICONEXCLAMATION);

		// get out of here immediately
		TerminateProcess(GetCurrentProcess(), 0);
#else
		fprintf( stderr, "Could not acquire necessary game files because the connection to Steam servers was lost." );
		exit(-1);
#endif
		return;
	}

	if (fp)
	{
		if (steamError.eSteamError != eSteamErrorNone || g_tLastError.eSteamError != eSteamErrorNone)
		{
			g_pLastErrorFile = fp;
			g_tLastError = steamError;
		}
	}
	else
	{
		// write to the NULL error checker
		if (steamError.eSteamError != eSteamErrorNone || g_tLastErrorNoFile.eSteamError != eSteamErrorNone)
		{
			g_tLastErrorNoFile = steamError;
		}
	}
}


#ifdef POSIX
class CSteamFile
{
public:
	explicit CSteamFile( SteamHandle_t file, bool bWriteable, const char *pchName ) : m_File( file ), m_bWriteable( bWriteable ), m_FileName(pchName) {}
	~CSteamFile() {} 
	SteamHandle_t Handle() { return m_File; }
	bool BWriteable() { return m_bWriteable; }
	CUtlSymbol GetFileName() { return m_FileName; }
private:
	SteamHandle_t m_File;
	bool m_bWriteable;
	CUtlSymbol m_FileName;
};
#endif


class CFileSystem_Steam : public CBaseFileSystem
{
public:
	CFileSystem_Steam();
	~CFileSystem_Steam();

	// Methods of IAppSystem
	virtual InitReturnVal_t Init();
	virtual void			Shutdown();
	virtual void *			QueryInterface( const char *pInterfaceName );

	// Higher level filesystem methods requiring specific behavior
	virtual void GetLocalCopy( const char *pFileName );
	virtual int	HintResourceNeed( const char *hintlist, int forgetEverything );
	virtual CSysModule * LoadModule( const char *pFileName, const char *pPathID, bool bValidatedDllOnly );
	virtual bool IsFileImmediatelyAvailable(const char *pFileName);

	// resource waiting
	virtual WaitForResourcesHandle_t WaitForResources( const char *resourcelist );
	virtual bool GetWaitForResourcesProgress( WaitForResourcesHandle_t handle, float *progress /* out */ , bool *complete /* out */ );
	virtual void CancelWaitForResources( WaitForResourcesHandle_t handle );
	virtual bool IsSteam() const { return true; }
	virtual	FilesystemMountRetval_t MountSteamContent( int nExtraAppId = -1 );

protected:
	// implementation of CBaseFileSystem virtual functions
	virtual FILE *FS_fopen( const char *filename, const char *options, unsigned flags, int64 *size, CFileLoadInfo *pInfo );
	virtual void FS_setbufsize( FILE *fp, unsigned nBytes );
	virtual void FS_fclose( FILE *fp );
	virtual void FS_fseek( FILE *fp, int64 pos, int seekType );
	virtual long FS_ftell( FILE *fp );
	virtual int FS_feof( FILE *fp );
	virtual size_t FS_fread( void *dest, size_t destSize, size_t size, FILE *fp );
	virtual size_t FS_fwrite( const void *src, size_t size, FILE *fp );
	virtual size_t FS_vfprintf( FILE *fp, const char *fmt, va_list list );
	virtual int FS_ferror( FILE *fp );
	virtual int FS_fflush( FILE *fp );
	virtual char *FS_fgets( char *dest, int destSize, FILE *fp );
	virtual int FS_stat( const char *path, struct _stat *buf, bool *pbLoadedFromSteamCache=NULL );
	virtual int FS_chmod( const char *path, int pmode );
	virtual HANDLE FS_FindFirstFile(const char *findname, WIN32_FIND_DATA *dat);
	virtual bool FS_FindNextFile(HANDLE handle, WIN32_FIND_DATA *dat);
	virtual bool FS_FindClose(HANDLE handle);

private:
	bool IsFileInSteamCache( const char *file );
	bool IsFileInSteamCache2( const char *file );
	void ViewSteamCache( const char* szDir, bool bRecurse );
	bool m_bSteamInitialized;
	bool m_bCurrentlyLoading;
	bool m_bAssertFilesImmediatelyAvailable;
	bool m_bCanAsync;
	bool m_bSelfMounted;
	bool m_bContentLoaded;
	bool m_bSDKToolMode;

	SteamCallHandle_t m_hWaitForResourcesCallHandle;
	int m_iCurrentReturnedCallHandle;
	HMODULE m_hSteamDLL;
	void LoadAndStartSteam();
#ifdef POSIX
	static CUtlMap< int, CInterlockedInt > m_LockedFDMap;
#endif	
};

#ifdef POSIX
CUtlMap< int, CInterlockedInt> CFileSystem_Steam::m_LockedFDMap;
#endif


//-----------------------------------------------------------------------------
// singleton
//-----------------------------------------------------------------------------
static CFileSystem_Steam g_FileSystem_Steam;
#if defined(DEDICATED)
CBaseFileSystem *BaseFileSystem_Steam( void )
{
	return &g_FileSystem_Steam;
}
#endif

#ifdef DEDICATED // "hack" to allow us to not export a stdio version of the FILESYSTEM_INTERFACE_VERSION anywhere

IFileSystem *g_pFileSystemSteam = &g_FileSystem_Steam;
IBaseFileSystem *g_pBaseFileSystemSteam = &g_FileSystem_Steam;

#else

EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CFileSystem_Steam, IFileSystem, FILESYSTEM_INTERFACE_VERSION, g_FileSystem_Steam );
EXPOSE_SINGLE_INTERFACE_GLOBALVAR( CFileSystem_Steam, IBaseFileSystem, BASEFILESYSTEM_INTERFACE_VERSION, g_FileSystem_Steam );

#endif


//-----------------------------------------------------------------------------
// constructor
//-----------------------------------------------------------------------------
CFileSystem_Steam::CFileSystem_Steam()
{
	m_bSteamInitialized = false;
	m_bCurrentlyLoading = false;
	m_bAssertFilesImmediatelyAvailable = false;
	m_bCanAsync = true;
	m_bContentLoaded = false;
	m_hWaitForResourcesCallHandle = STEAM_INVALID_CALL_HANDLE;
	m_iCurrentReturnedCallHandle = 1;
	m_hSteamDLL = NULL;
	m_bSDKToolMode = false;
#ifdef POSIX
	SetDefLessFunc( m_LockedFDMap );
#endif
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
CFileSystem_Steam::~CFileSystem_Steam()
{
	m_bSteamInitialized = false;
}

bool CFileSystem_Steam::IsFileInSteamCache2( const char *file )
{
	if ( !m_bContentLoaded || m_bSDKToolMode)
	{
		return true;
	}

	// see if the file exists
	TSteamElemInfo info;
	TSteamError error;
	
	SteamHandle_t h = steam->FindFirst( file, eSteamFindRemoteOnly, &info, &error );
	if ( h == STEAM_INVALID_HANDLE )
	{
		return false;
	}
	else
	{
		steam->FindClose( h, &error );
	}

	return true;
}


void MountDependencies( int iAppId, CUtlVector<unsigned int> &depList )
{
	TSteamError steamError;

	// Setup the buffers for the TSteamApp structure.
	char buffers[4][2048];
	TSteamApp steamApp;
	steamApp.szName = buffers[0];
	steamApp.uMaxNameChars = sizeof( buffers[0] );
	steamApp.szLatestVersionLabel = buffers[1];
	steamApp.uMaxLatestVersionLabelChars = sizeof( buffers[1] );
	steamApp.szCurrentVersionLabel = buffers[2];
	steamApp.uMaxCurrentVersionLabelChars = sizeof( buffers[2] );
	steamApp.szInstallDirName = buffers[3];
	steamApp.uMaxInstallDirNameChars = sizeof( buffers[3] );
	
	// Ask how many caches depend on this app ID.
	steam->EnumerateApp( iAppId, &steamApp, &steamError );
	if ( steamError.eSteamError != eSteamErrorNone )
		Error( "EnumerateApp( %d ) failed: %s", iAppId, steamError.szDesc );

	// Mount each cache.
	for ( int i=0; i < (int)steamApp.uNumDependencies; i++ )
	{
		TSteamAppDependencyInfo appDependencyInfo;
		steam->EnumerateAppDependency( iAppId, i, &appDependencyInfo, &steamError );
		if ( steamError.eSteamError != eSteamErrorNone )
			Error( "EnumerateAppDependency( %d, %d ) failed: %s", iAppId, i, steamError.szDesc );

		if ( depList.Find( appDependencyInfo.uAppId ) == -1 )
		{
			depList.AddToTail( appDependencyInfo.uAppId );

			// Make sure that the user owns the app before attempting to mount it
			int isSubscribed = false, isPending = false;
			steam->IsAppSubscribed( appDependencyInfo.uAppId, &isSubscribed, &isPending, &steamError );
			if ( isSubscribed ) 
			{
				steam->MountFilesystem( appDependencyInfo.uAppId, "", &steamError );
				if ( steamError.eSteamError != eSteamErrorNone && steamError.eSteamError != eSteamErrorNotSubscribed )
				{
					Error( "MountFilesystem( %d ) failed: %s", appDependencyInfo.uAppId, steamError.szDesc );
				}
			}
		}
	}
}


//-----------------------------------------------------------------------------
// QueryInterface: 
//-----------------------------------------------------------------------------
void *CFileSystem_Steam::QueryInterface( const char *pInterfaceName )
{
	// We also implement the IMatSystemSurface interface
	if (!Q_strncmp(	pInterfaceName, FILESYSTEM_INTERFACE_VERSION, Q_strlen(FILESYSTEM_INTERFACE_VERSION) + 1))
		return (IFileSystem*)this;

	return CBaseFileSystem::QueryInterface( pInterfaceName );
}


//-----------------------------------------------------------------------------
// Methods of IAppSystem
//-----------------------------------------------------------------------------
InitReturnVal_t CFileSystem_Steam::Init()
{
	m_bSteamInitialized = true;
	m_bSelfMounted = false;

	LoadAndStartSteam();

	return CBaseFileSystem::Init();
}

void CFileSystem_Steam::Shutdown()
{
	Assert( m_bSteamInitialized );

	if ( !steam )
		return;


	TSteamError steamError;

	// If we're not running Steam in local mode, remove all mount points from the STEAM VFS.
	if ( !CommandLine()->CheckParm("-steamlocal") && !m_bSelfMounted && !steam->UnmountAppFilesystem(&steamError) )
	{
#ifdef WIN32
		OutputDebugString(steamError.szDesc);
#endif
		Assert(!("STEAM VFS failed to unmount"));

		// just continue on as if nothing happened
		// ::MessageBox(NULL, szErrorMsg, "Half-Life FileSystem_Steam Error", MB_OK);
		// exit( -1 );
	}

	steam->Cleanup(&steamError);

	if ( m_hSteamDLL )
	{
		Sys_UnloadModule( (CSysModule *)m_hSteamDLL );
		m_hSteamDLL = NULL; 
	}
	m_bSteamInitialized = false;
}


void CFileSystem_Steam::LoadAndStartSteam()
{
	if ( !m_hSteamDLL )
	{
		const char *pchSteamInstallPath = SteamAPI_GetSteamInstallPath();
		if ( pchSteamInstallPath )
		{
			char szSteamDLLPath[ MAX_PATH ];
#ifdef WIN32
			V_ComposeFileName( pchSteamInstallPath, "steam" DLL_EXT_STRING, szSteamDLLPath, Q_ARRAYSIZE(szSteamDLLPath) );
#elif defined(POSIX)
			V_ComposeFileName( pchSteamInstallPath, "libsteam" DLL_EXT_STRING, szSteamDLLPath, Q_ARRAYSIZE(szSteamDLLPath) );			
#else
#error
#endif
			// try to load the steam.dll from the running steam process first
			m_hSteamDLL = (HMODULE)Sys_LoadModule( szSteamDLLPath );
		}

		if ( !m_hSteamDLL )
#ifdef WIN32			
			m_hSteamDLL = (HMODULE)Sys_LoadModule( "steam" DLL_EXT_STRING );
#elif defined(POSIX)
			m_hSteamDLL = (HMODULE)Sys_LoadModule( "libsteam" DLL_EXT_STRING );
#else
#error
#endif
	}

	if ( m_hSteamDLL )
	{
		typedef void *(*PFSteamCreateInterface)( const char *pchSteam );
#ifdef WIN32
		PFSteamCreateInterface pfnSteamCreateInterface = (PFSteamCreateInterface)GetProcAddress( m_hSteamDLL, "_f" );
#else
		PFSteamCreateInterface pfnSteamCreateInterface = (PFSteamCreateInterface)dlsym( (void *)m_hSteamDLL, "_f" );
#endif
		if ( pfnSteamCreateInterface )
			steam = (ISteamInterface *)pfnSteamCreateInterface( STEAM_INTERFACE_VERSION );	
	}

	if ( !steam )
	{
		Error("CFileSystem_Steam::Init() failed: failed to find steam interface\n");
#ifdef WIN32
		::DestroyWindow( GetForegroundWindow() );
		::MessageBox(NULL, "CFileSystem_Steam::Init() failed: failed to find steam interface", "Half-Life FileSystem_Steam Error", MB_OK);
#endif
		_exit( -1 );
	}

	TSteamError steamError;
	if (!steam->Startup(STEAM_USING_FILESYSTEM | STEAM_USING_LOGGING | STEAM_USING_USERID | STEAM_USING_ACCOUNT, &steamError))
	{
		Error("SteamStartup() failed: %s\n", steamError.szDesc);
#ifdef WIN32
		::DestroyWindow( GetForegroundWindow() );
		::MessageBox(NULL, steamError.szDesc, "Half-Life FileSystem_Steam Error", MB_OK);
#endif
		_exit( -1 );
	}
}


//-----------------------------------------------------------------------------
// Methods of IAppSystem
//-----------------------------------------------------------------------------
FilesystemMountRetval_t CFileSystem_Steam::MountSteamContent( int nExtraAppId )
{
	m_bContentLoaded = true;
	FilesystemMountRetval_t retval = FILESYSTEM_MOUNT_OK;

	// MWD: This is here because of Hammer's funky startup sequence that requires MountSteamContent() be called in CHammerApp::PreInit(). Once that root problem is addressed this will be removed;
	if ( NULL == steam )
	{
		LoadAndStartSteam();
	}

	// only mount if we're already logged in
	// if we're not logged in, assume the app will login & mount the cache itself
	// this enables both the game and the platform to use this same code, even though they mount caches at different times
	int loggedIn = 0;
	TSteamError steamError;
	int result = steam->IsLoggedIn(&loggedIn, &steamError);
	if (!result || loggedIn)
	{
		if ( nExtraAppId != -1 )
		{
			m_bSDKToolMode = true;

			CUtlVector<unsigned int> depList;
			if ( nExtraAppId < -1 )
			{
				// Special way to tell them to mount a specific App ID's depots.
				MountDependencies( -nExtraAppId, depList );
				return FILESYSTEM_MOUNT_OK;
			}
			else
			{
				const char *pMainAppId = NULL;

				// If they specified extra app IDs they want to mount after the main one, then we mount 
				// the caches manually here.
#ifdef _WIN32
				// Use GetEnvironmentVariable instead of getenv because getenv doesn't pick up changes
				// to the process environment after the DLL was loaded.
				char szMainAppId[128];
				if ( GetEnvironmentVariable( "steamappid", szMainAppId, sizeof( szMainAppId ) ) != 0 )
				{
					pMainAppId = szMainAppId;
				}
#else
				// LINUX BUG: see above
				pMainAppId = getenv( "SteamAppId" );
#endif // _WIN32				

				if ( !pMainAppId )
					Error( "Extra App ID set to %d, but no SteamAppId.", nExtraAppId );

				//swapping this mount order ensures the most current engine binaries are used by tools
				MountDependencies( nExtraAppId, depList );
				MountDependencies( atoi( pMainAppId ), depList );				
				return FILESYSTEM_MOUNT_OK;
			}	
		}
		else if (!steam->MountAppFilesystem(&steamError))
		{
			Error("MountAppFilesystem() failed: %s\n", steamError.szDesc);
#ifdef WIN32
			::DestroyWindow( GetForegroundWindow() );
			::MessageBox(NULL, steamError.szDesc, "Half-Life FileSystem_Steam Error", MB_OK);
#endif
			_exit( -1 );
		}

	}
	else
	{
		m_bSelfMounted = true;
	}

	return retval;
}



//-----------------------------------------------------------------------------
// Purpose: low-level filesystem wrapper
//-----------------------------------------------------------------------------
FILE *CFileSystem_Steam::FS_fopen( const char *filenameT, const char *options, unsigned flags, int64 *size, CFileLoadInfo *pInfo )
{
	char filename[MAX_PATH];

	FixUpPath ( filenameT, filename, sizeof( filename ) );

	// make sure the file is immediately available
	if (m_bAssertFilesImmediatelyAvailable && !m_bCurrentlyLoading)
	{
		if (!IsFileImmediatelyAvailable(filename))
		{
			Msg("Steam FS: '%s' not immediately available when not in loading dialog", filename);
		}
	}

	if ( !steam )
	{
		AssertMsg( 0, "CFileSystem_Steam::FS_fopen used with null steam interface!" );
		return NULL;
	}

	CFileLoadInfo dummyInfo;
	if ( !pInfo )
	{
		dummyInfo.m_bSteamCacheOnly = false;
		pInfo = &dummyInfo;
	}
	
	SteamHandle_t f = 0;

#ifdef POSIX
	FILE *pFile = NULL;
	bool bWriteable = false;
	if ( strchr(options,'w') || strchr(options,'a') )
		bWriteable = true;
	
	if (  bWriteable )
	{
		pFile = fopen( filename, options );
		if (pFile && size)
		{
			// todo: replace with filelength()? 
			struct _stat buf;
			int rt = _stat( filename, &buf );
			if (rt == 0)
			{
				*size = buf.st_size;
			}
		}
		if ( pFile )
		{
			
			// Win32 has an undocumented feature that is serialized ALL writes to a file across threads (i.e only 1 thread can open a file at a time)
			// so use flock here to mimic that behavior
			
			ThreadId_t curThread = ThreadGetCurrentId();
			
			{
				CThreadFastMutex Locklock;
				AUTO_LOCK( Locklock );
				int fd = fileno_unlocked( pFile );
				int iLockID = m_LockedFDMap.Find( fd );
				int ret = flock( fd, LOCK_EX | LOCK_NB );
				if ( ret < 0 )
				{
					if ( errno == EWOULDBLOCK  )
					{
						if ( iLockID != m_LockedFDMap.InvalidIndex() && 
							m_LockedFDMap[iLockID] != -1 && 
							curThread != m_LockedFDMap[iLockID] )
						{
							ret = flock( fd, LOCK_EX );
							if ( ret < 0 )
							{
								fclose( pFile );
								return NULL;						
							}
						}
					}
					else 
					{
						fclose( pFile );
						return NULL;
					}
				}

				if ( iLockID != m_LockedFDMap.InvalidIndex() )
					m_LockedFDMap[iLockID] = curThread;
				else
					m_LockedFDMap.Insert( fd, curThread );

			}
			rewind( pFile );
		}
	}
	else 
	{
#endif
	
	TSteamError steamError;
	unsigned int fileSize;
	int bLocal = 0;
	f = steam->OpenFileEx(filename, options, pInfo->m_bSteamCacheOnly, &fileSize, &bLocal, &steamError);

	pInfo->m_bLoadedFromSteamCache = (bLocal == 0);
	if (size)
	{
		*size = fileSize;
	}

	CheckError( f, steamError );

#ifdef POSIX
	}
	
	if ( f || pFile )
	{
		CSteamFile *steamFile = new CSteamFile( pFile ? (SteamHandle_t)pFile : f, bWriteable, filename );
		f = (SteamHandle_t)steamFile;
	}
#endif
	return (FILE *)f;
}

//-----------------------------------------------------------------------------
// Purpose: low-level filesystem wrapper
//-----------------------------------------------------------------------------
void CFileSystem_Steam::FS_setbufsize( FILE *fp, unsigned nBytes )
{
}


//-----------------------------------------------------------------------------
// Purpose: steam call, unnecessary in stdio
//-----------------------------------------------------------------------------
WaitForResourcesHandle_t CFileSystem_Steam::WaitForResources( const char *resourcelist )
{
	char szResourceList[MAX_PATH];
	Q_strncpy( szResourceList, resourcelist, sizeof(szResourceList) );
	Q_DefaultExtension( szResourceList, ".lst", sizeof(szResourceList) );

	// cancel any old call
	TSteamError steamError;
	m_hWaitForResourcesCallHandle = steam->WaitForResources(szResourceList, &steamError);
	if (steamError.eSteamError == eSteamErrorNone)
	{
		// return a new call handle
		return (WaitForResourcesHandle_t)(++m_iCurrentReturnedCallHandle);
	}

	Msg("SteamWaitForResources() failed: %s\n", steamError.szDesc);
	return (WaitForResourcesHandle_t)FILESYSTEM_INVALID_HANDLE;
}

//-----------------------------------------------------------------------------
// Purpose: steam call, unnecessary in stdio
//-----------------------------------------------------------------------------
bool CFileSystem_Steam::GetWaitForResourcesProgress( WaitForResourcesHandle_t handle, float *progress /* out */ , bool *complete /* out */ )
{
	// clear the input
	*progress = 0.0f;
	*complete = true;

	// check to see if they're using an old handle
	if (m_iCurrentReturnedCallHandle != handle)
		return false;
	if (m_hWaitForResourcesCallHandle == STEAM_INVALID_CALL_HANDLE)
		return false;

	// get the progress
	TSteamError steamError;
	TSteamProgress steamProgress;
	int result = steam->ProcessCall(m_hWaitForResourcesCallHandle, &steamProgress, &steamError);
	if (result && steamError.eSteamError == eSteamErrorNone)
	{
		// we've finished successfully
		m_hWaitForResourcesCallHandle = STEAM_INVALID_CALL_HANDLE;
		*complete = true;
		*progress = 1.0f;
		return true;
	}
	else if (steamError.eSteamError != eSteamErrorNotFinishedProcessing)
	{
		// we have an error, just call it done
		m_hWaitForResourcesCallHandle = STEAM_INVALID_CALL_HANDLE;
		Msg("SteamProcessCall(SteamWaitForResources()) failed: %s\n", steamError.szDesc);
		return false;
	}

	// return the progress
	if (steamProgress.bValid)
	{
		*progress = (float)steamProgress.uPercentDone / (100.0f * STEAM_PROGRESS_PERCENT_SCALE);
	}
	else
	{
		*progress = 0;
	}
	*complete = false;

	return (steamProgress.bValid != false);
}

//-----------------------------------------------------------------------------
// Purpose: steam call, unnecessary in stdio
//-----------------------------------------------------------------------------
void CFileSystem_Steam::CancelWaitForResources( WaitForResourcesHandle_t handle )
{
	// check to see if they're using an old handle
	if (m_iCurrentReturnedCallHandle != handle)
		return;
	if (m_hWaitForResourcesCallHandle == STEAM_INVALID_CALL_HANDLE)
		return;

	TSteamError steamError;
	steam->AbortCall(m_hWaitForResourcesCallHandle, &steamError);
	m_hWaitForResourcesCallHandle = STEAM_INVALID_CALL_HANDLE;
}


//-----------------------------------------------------------------------------
// Purpose: helper for posix file handle wrapper
//-----------------------------------------------------------------------------
#ifdef POSIX
FILE *GetFileHandle( CSteamFile *steamFile )
{
	if ( !steamFile )
		return NULL;
	
	return (FILE *)steamFile->Handle();	
}
bool BWriteable( CSteamFile *steamFile )
{
	return steamFile && steamFile->BWriteable();
}
#endif


//-----------------------------------------------------------------------------
// Purpose: low-level filesystem wrapper
//-----------------------------------------------------------------------------
void CFileSystem_Steam::FS_fclose( FILE *fp )
{
#ifdef POSIX
	CSteamFile *steamFile = (CSteamFile *)fp;
	fp = GetFileHandle( steamFile );
	if ( BWriteable( steamFile ) )
	{
		int fd = fileno_unlocked( fp );
		fflush( fp );
		flock( fd, LOCK_UN );
		int iLockID = m_LockedFDMap.Find( fd );
		if ( iLockID != m_LockedFDMap.InvalidIndex() )
			m_LockedFDMap[ iLockID ] = -1;

		fclose( fp );
	}
	else
	{
#endif
	TSteamError steamError;
	steam->CloseFile((SteamHandle_t)fp, &steamError);
	CheckError( (SteamHandle_t)fp, steamError );
#ifdef POSIX
	}
#endif
}


//-----------------------------------------------------------------------------
// Purpose: low-level filesystem wrapper
//-----------------------------------------------------------------------------
void CFileSystem_Steam::FS_fseek( FILE *fp, int64 pos, int seekType )
{
#ifdef POSIX
	CSteamFile *steamFile = (CSteamFile *)fp;
	fp = GetFileHandle( steamFile );
	if ( BWriteable( steamFile ) )
	{
		fseek( fp, pos, seekType );
	}
	else
	{
#endif
	TSteamError steamError;
	int result;
	result = steam->SeekFile((SteamHandle_t)fp, (int32)pos, (ESteamSeekMethod)seekType, &steamError);
	CheckError((SteamHandle_t)fp, steamError);
#ifdef POSIX
	}
#endif
}

//-----------------------------------------------------------------------------
// Purpose: low-level filesystem wrapper
//-----------------------------------------------------------------------------
long CFileSystem_Steam::FS_ftell( FILE *fp )
{
#ifdef POSIX
	CSteamFile *steamFile = (CSteamFile *)fp;
	fp = GetFileHandle( steamFile );
	if ( BWriteable( steamFile ) )
	{
		return ftell(fp);
	}
	else
	{
#endif
	long steam_offset;
	TSteamError steamError;

	steam_offset = steam->TellFile((SteamHandle_t)fp, &steamError);
	if ( steamError.eSteamError != eSteamErrorNone )
	{
		CheckError((SteamHandle_t)fp, steamError);
		return -1L;
	}

	return steam_offset;
#ifdef POSIX
	}
#endif
}

//-----------------------------------------------------------------------------
// Purpose: low-level filesystem wrapper
//-----------------------------------------------------------------------------
int CFileSystem_Steam::FS_feof( FILE *fp )
{
#ifdef POSIX
	CSteamFile *steamFile = (CSteamFile *)fp;
	fp = GetFileHandle( steamFile );
	if ( BWriteable( steamFile ) )
	{
		return feof(fp);		
	}
	else
	{
#endif
	long orig_pos;
	
	// Figure out where in the file we currently are...
	orig_pos = FS_ftell(fp);
	
	if ( (SteamHandle_t)fp == g_pLastErrorFile && g_tLastError.eSteamError == eSteamErrorEOF )
		return 1;

	if ( g_tLastError.eSteamError != eSteamErrorNone )
		return 0;

	// Jump to the end...
	FS_fseek(fp, 0L, SEEK_END);

	// If we were already at the end, return true
	if ( orig_pos == FS_ftell(fp) )
		return 1;

	// Otherwise, go back to the original spot and return false.
	FS_fseek(fp, orig_pos, SEEK_SET);
	return 0;
#ifdef POSIX
	}
#endif
}

//-----------------------------------------------------------------------------
// Purpose: low-level filesystem wrapper
//-----------------------------------------------------------------------------
size_t CFileSystem_Steam::FS_fread( void *dest, size_t destSize, size_t size, FILE *fp )
{
#ifdef POSIX
	CSteamFile *steamFile = (CSteamFile *)fp;
	fp = GetFileHandle( steamFile );
	if ( BWriteable( steamFile ) )
	{
		return fread( dest, 1, size, fp );
	}
	else
	{
#endif
	TSteamError steamError;
	int blocksRead = steam->ReadFile(dest, 1, size, (SteamHandle_t)fp, &steamError);
	CheckError((SteamHandle_t)fp, steamError);
	return blocksRead; // steam reads in atomic blocks of "size" bytes
#ifdef POSIX
	}
#endif
}

//-----------------------------------------------------------------------------
// Purpose: low-level filesystem wrapper
//-----------------------------------------------------------------------------
size_t CFileSystem_Steam::FS_fwrite( const void *src, size_t size, FILE *fp )
{
#ifdef POSIX
	CSteamFile *steamFile = (CSteamFile *)fp;
	fp = GetFileHandle( steamFile );
	if ( BWriteable( steamFile ) )
	{
#define WRITE_CHUNK		(256 * 1024)
		if ( size > WRITE_CHUNK )
		{
			size_t remaining = size;
			const byte* current = (const byte *) src;
			size_t total = 0;
			
			while ( remaining > 0 )
			{
				size_t bytesToCopy = min(remaining, WRITE_CHUNK);
				
				total += fwrite(current, 1, bytesToCopy, fp);
				
				remaining -= bytesToCopy;
				current += bytesToCopy;
			}
			
			Assert( total == size );
			return total;
		}
		
		return fwrite(src, 1, size, fp);// return number of bytes written (because we have size = 1, count = bytes, so it return bytes)		
	}
	else
	{
#endif
	TSteamError steamError;
	int result = steam->WriteFile(src, 1, size, (SteamHandle_t)fp, &steamError);
	CheckError((SteamHandle_t)fp, steamError);
	return result;
#ifdef POSIX
	}
#endif
}

//-----------------------------------------------------------------------------
// Purpose: low-level filesystem wrapper
//-----------------------------------------------------------------------------
size_t CFileSystem_Steam::FS_vfprintf( FILE *fp, const char *fmt, va_list list )
{
#ifdef POSIX
	CSteamFile *steamFile = (CSteamFile *)fp;
	fp = GetFileHandle( steamFile );
	if ( BWriteable( steamFile ) )
	{
		return vfprintf(fp, fmt, list);		
	}
	else
	{
#endif
	int blen, plen;
	char *buf;

	if ( !fp || !fmt )
		return 0;

	// Open the null device...used by vfprintf to determine the length of
	// the formatted string.
	FILE *nullDeviceFP = fopen("nul:", "w");
	if ( !nullDeviceFP )
		return 0;

	// Figure out how long the formatted string will be...dump formatted
	// string to the bit bucket.
	blen = vfprintf(nullDeviceFP, fmt, list);
	fclose(nullDeviceFP);
	if ( !blen )
	{
		return 0;
	}

	// Get buffer in which to build the formatted string.
	buf = (char *)malloc(blen+1);
	if ( !buf )
	{
		return 0;
	}

	// Build the formatted string.
	plen = _vsnprintf(buf, blen, fmt, list);
	va_end(list);
	if ( plen != blen )
	{
		free(buf);
		return 0;
	}

	buf[ blen ] = 0;

	// Write out the formatted string.
	if ( plen != (int)FS_fwrite(buf, plen, fp) )
	{
		free(buf);
		return 0;
	}

	free(buf);
	return plen;
#ifdef POSIX
	}
#endif
}

//-----------------------------------------------------------------------------
// Purpose: low-level filesystem wrapper
//-----------------------------------------------------------------------------
int CFileSystem_Steam::FS_ferror( FILE *fp )
{
	if (fp)
	{
#ifdef POSIX
		CSteamFile *steamFile = (CSteamFile *)fp;
		fp = GetFileHandle( steamFile );
		if ( BWriteable( steamFile ) )
		{
			return ferror(fp);			
		}
		else
		{
#endif
		if ((SteamHandle_t)fp != g_pLastErrorFile)
		{
			// it's asking for an error for a previous file, return no error
			return 0;
		}

		return ( g_tLastError.eSteamError != eSteamErrorNone );
#ifdef POSIX
		}
#endif
	}
	return g_tLastErrorNoFile.eSteamError != eSteamErrorNone;
}

//-----------------------------------------------------------------------------
// Purpose: low-level filesystem wrapper
//-----------------------------------------------------------------------------
int CFileSystem_Steam::FS_fflush( FILE *fp )
{
#ifdef POSIX
	CSteamFile *steamFile = (CSteamFile *)fp;
	fp = GetFileHandle( steamFile );
	if ( BWriteable( steamFile ) )
	{
		return fflush(fp);		
	}
	else
	{
#endif
	TSteamError steamError;
	int result = steam->FlushFile((SteamHandle_t)fp, &steamError);
	CheckError((SteamHandle_t)fp, steamError);
	return result;
#ifdef POSIX
	}
#endif
}

//-----------------------------------------------------------------------------
// Purpose: low-level filesystem wrapper
//-----------------------------------------------------------------------------
char *CFileSystem_Steam::FS_fgets( char *dest, int destSize, FILE *fp )
{
#ifdef POSIX
	CSteamFile *steamFile = (CSteamFile *)fp;
	fp = GetFileHandle( steamFile );
	if ( BWriteable( steamFile ) )
	{
		return fgets(dest, destSize, fp);
	}
	else
	{
#endif
	unsigned char c;
	int numCharRead = 0;
	
	// Read at most n chars from the file or until a newline
	*dest = c = '\0';
	while ( (numCharRead < destSize-1) && (c != '\n') )
	{
		// Read in the next char...
		if ( FS_fread(&c, 1, 1, fp) != 1 )
		{
			if ( g_tLastError.eSteamError != eSteamErrorEOF || numCharRead == 0 )
			{
				return NULL;	// If we hit an error, return NULL.
			}
			
			numCharRead = destSize;	// Hit EOF, no more to read, all done...
		}

		else
		{
			*dest++ = c;		// add the char to the string and point to the next pos
			*dest = '\0';		// append NULL
			numCharRead++;		// count the char read
		}
	}
	return dest; // Has a NULL termination...
#ifdef POSIX
	}
#endif
}

//-----------------------------------------------------------------------------
// Purpose: low-level filesystem wrapper
//-----------------------------------------------------------------------------
int CFileSystem_Steam::FS_stat( const char *path, struct _stat *buf, bool *pbLoadedFromSteamCache )
{
	TSteamElemInfo Info;
	TSteamError steamError;

	if ( pbLoadedFromSteamCache )
		*pbLoadedFromSteamCache = false;

	if ( !steam )
	{
		// The dedicated server gets here once at startup. When setting up the executable path before loading
		// base modules like engine.dll, the filesystem looks for zipX.zip but we haven't mounted steam content
		// yet so steam is null.
#if !defined( DEDICATED )		
		AssertMsg( 0, "CFileSystem_Steam::FS_stat used with null steam interface!" );
#endif
		return -1;
	}

	memset(buf, 0, sizeof(struct _stat));
	int returnVal= steam->Stat(path, &Info, &steamError);
	if ( returnVal == 0 )
	{
		if (Info.bIsDir )
		{
			buf->st_mode |= _S_IFDIR;
			buf->st_size = 0;
		}
		else
		{
			if ( pbLoadedFromSteamCache )
				*pbLoadedFromSteamCache = ( Info.bIsLocal == 0 );

			// Now we want to know if it's writable or not. First see if there is a local copy.
			struct _stat testBuf;
			int rt = _stat( path, &testBuf );
			if ( rt == 0 )
			{
				// Ok, there's a local copy. Now check if the copy on our HD is writable.
				if ( testBuf.st_mode & _S_IWRITE )
					buf->st_mode |= _S_IWRITE;
			}

			buf->st_mode |= _S_IFREG;
			buf->st_size = Info.uSizeOrCount;
		}

		buf->st_atime = Info.lLastAccessTime;
		buf->st_mtime = Info.lLastModificationTime;
		buf->st_ctime = Info.lCreationTime;
	}

	CheckError(NULL, steamError);
	return returnVal;
}

#ifdef _WIN32
#include <io.h>
#endif

//-----------------------------------------------------------------------------
// Purpose: low-level filesystem wrapper
//-----------------------------------------------------------------------------
int CFileSystem_Steam::FS_chmod( const char *path, int pmode )
{
	return _chmod( path, pmode );
}

//-----------------------------------------------------------------------------
// Purpose: low-level filesystem wrapper
//-----------------------------------------------------------------------------
HANDLE CFileSystem_Steam::FS_FindFirstFile(const char *findname, WIN32_FIND_DATA *dat)
{
	TSteamElemInfo steamFindInfo;
	HANDLE hResult = INVALID_HANDLE_VALUE;
	SteamHandle_t steamResult;
	TSteamError steamError;

	steamResult = steam->FindFirst(findname, eSteamFindAll, &steamFindInfo, &steamError);
	CheckError(NULL, steamError);

	if ( steamResult == STEAM_INVALID_HANDLE )
	{
		hResult = INVALID_HANDLE_VALUE;
	}
	else
	{
		hResult = (HANDLE)steamResult;
		strcpy(dat->cFileName, steamFindInfo.cszName);
		
// NEED TO DEAL WITH THIS STUFF!!!  FORTUNATELY HALF-LIFE DOESN'T USE ANY OF IT
// AND ARCANUM USES _findfirst() etc.
//
//		findInfo->ftLastWriteTime = steamFindInfo.lLastModificationTime;
//		findInfo->ftCreationTime = steamFindInfo.lCreationTime;
//		findInfo->ftLastAccessTime = steamFindInfo.lLastAccessTime;
//		findInfo->nFileSizeHigh = ;
//		findInfo->nFileSizeLow = ;

		// Determine if the found object is a directory...
		if ( steamFindInfo.bIsDir )
			dat->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
		else
			dat->dwFileAttributes &= ~FILE_ATTRIBUTE_DIRECTORY;

		// Determine if the found object was local or remote.
		// ***NOTE*** we are hijacking the FILE_ATTRIBUTE_OFFLINE bit and using it in a different
		//            (but similar) manner than the WIN32 documentation indicates ***NOTE***
		if ( steamFindInfo.bIsLocal )
			dat->dwFileAttributes &= ~FILE_ATTRIBUTE_OFFLINE;
		else
			dat->dwFileAttributes |= FILE_ATTRIBUTE_OFFLINE;
	}
	
	return hResult;
}

//-----------------------------------------------------------------------------
// Purpose: low-level filesystem wrapper
//-----------------------------------------------------------------------------
bool CFileSystem_Steam::FS_FindNextFile(HANDLE handle, WIN32_FIND_DATA *dat)
{
	TSteamElemInfo steamFindInfo;
	bool result;
	TSteamError steamError;

	result = (steam->FindNext((SteamHandle_t)handle, &steamFindInfo, &steamError) == 0);
	CheckError(NULL, steamError);

	if ( result )
	{
		strcpy(dat->cFileName, steamFindInfo.cszName);
		if ( steamFindInfo.bIsDir )
			dat->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
		else
			dat->dwFileAttributes &= ~FILE_ATTRIBUTE_DIRECTORY;
	}
	return result;
}

//-----------------------------------------------------------------------------
// Purpose: low-level filesystem wrapper
//-----------------------------------------------------------------------------
bool CFileSystem_Steam::FS_FindClose(HANDLE handle)
{
	TSteamError steamError;
	int result = (steam->FindClose((SteamHandle_t)handle, &steamError) == 0); 
	CheckError(NULL, steamError);
	return result != 0;
}

//-----------------------------------------------------------------------------
// Purpose: files are always immediately available on disk
//-----------------------------------------------------------------------------
bool CFileSystem_Steam::IsFileImmediatelyAvailable(const char *pFileName)
{
	TSteamError steamError;
	return (steam->IsFileImmediatelyAvailable(pFileName, &steamError) != 0);
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CFileSystem_Steam::GetLocalCopy( const char *pFileName )
{
	// Now try to find the dll under Steam so we can do a GetLocalCopy() on it
	TSteamError steamError;

/*
#ifdef WIN32
	struct _stat StatBuf;
	if ( FS_stat(pFileName, &StatBuf) == -1 )
	{
		// Use the environment search path to try and find it
		char* pPath = getenv("PATH");
	
		// Use the .EXE name to determine the root directory
		char srchPath[ MAX_PATH ];
#ifdef WIN32
		HINSTANCE hInstance = ( HINSTANCE )GetModuleHandle( 0 );
		if ( !GetModuleFileName( hInstance, srchPath, MAX_PATH ) )
		{
			::MessageBox( 0, "Failed calling GetModuleFileName", "Half-Life Steam Filesystem Error", MB_OK );
			return;
		}
#else
		srchPath[0] = '.';
		srchPath[1] = '\0';
#endif
		
		// Get the length of the root directory the .exe is in
		char* pSeperator = strrchr( srchPath, CORRECT_PATH_SEPARATOR );
		int nBaseLen = 0;
		if ( pSeperator )
		{
			nBaseLen = pSeperator - srchPath;
		}
		
		// Extract each section of the path
		char* pStart = pPath;
		char* pEnd = 0;
		bool bSearch = true;
		while ( bSearch )
		{
#ifdef WIN32
#define PATH_SEP ";"
#else
#define PATH_SEP ":"
#endif			
			pEnd = strstr( pStart, PATH_SEP );
			int nSize = pEnd - pStart;
			if ( !pEnd )
			{
				bSearch = false;
				// If pEnd is NULL then nSize will be rubbish, so calculate
				// it sensibly.
				nSize = strlen( pStart );
			}
		
			// Is this path even potentially in the base directory?
			if ( nSize > nBaseLen )
			{
				// Create a new path (relative to the base directory) by stripping off
				// nBaseLen characters and therefore doing FS_stat relative to the current
				// directory, which should be the base directory.
				Assert( sizeof(srchPath) > nBaseLen + strlen(pFileName) + 2 );
				nSize -= nBaseLen+1;
				memcpy( srchPath, pStart+nBaseLen+1, nSize );
				memcpy( srchPath+nSize, pFileName, strlen(pFileName)+1 );
				// If the path starts with a directory separator then we won't get a
				// relative path, so skip the check.
				if ( srchPath[0] != CORRECT_PATH_SEPARATOR )
				{
					if ( FS_stat(srchPath, &StatBuf) == 0 )
					{
						steam->GetLocalFileCopy(srchPath, &steamError);
						break;
					}
				}
			}
			pStart = pEnd+1;
		}
	}
	else
#endif
*/
	{
		// Convert _srv.so to .so...
		const char *pDllStringExtension = V_GetFileExtension( DLL_EXT_STRING );
		const char *pModuleExtension = pDllStringExtension ? ( pDllStringExtension - 1 ) : DLL_EXT_STRING;

		// If we got an extension, and this filename has it, then check if it's loaded.
		if( pModuleExtension && V_stristr( pFileName, pModuleExtension ) )
		{
			// We can't be copying files over the top of .so files if they're already loaded
			//	in memory. mmap2( ... MAP_PRIVATE ... ) says "it is unspecified whether changes
			//	made to the file after the mmap() call are visible in the mapped region." Testing
			//	and lots of debugging (thanks Pierre-Loup!) has shown that they are, in fact,
			//	blasted right over your nicely loaded and fixed up object.
			CSysModule *module = Sys_LoadModule( pFileName, SYS_NOLOAD );

			if( module )
			{
				// Sys_LoadModule( SYS_NOLOAD ) increments the refcount, so bump that back down.
				Sys_UnloadModule( module );
				return;
			}
		}

		steam->GetLocalFileCopy(pFileName, &steamError);
	}	
}

//-----------------------------------------------------------------------------
// Purpose: Load a DLL
// Input  : *path 
//-----------------------------------------------------------------------------
CSysModule * CFileSystem_Steam::LoadModule( const char *pFileName, const char *pPathID, bool bValidatedDllOnly )
{
	char szNewPath[ MAX_PATH ];
	CBaseFileSystem::ParsePathID( pFileName, pPathID, szNewPath );

	// File must end in .dll
	char szExtension[] = DLL_EXT_STRING;
	Assert( Q_strlen(pFileName) < sizeof(szNewPath) );
	
	Q_strncpy( szNewPath, pFileName, sizeof( szNewPath ) );
	if ( !Q_stristr(szNewPath, szExtension) )
	{
		Assert( strlen(pFileName) + sizeof(szExtension) < sizeof(szNewPath) );
		Q_strncat( szNewPath, szExtension, sizeof( szNewPath ), COPY_ALL_CHARACTERS );
	}

	LogFileAccess( szNewPath );
	if ( !pPathID )
	{
		pPathID = "EXECUTABLE_PATH"; // default to the bin dir
	}

	CUtlSymbol lookup = g_PathIDTable.AddString( pPathID );

	// a pathID has been specified, find the first match in the path list
	int c = m_SearchPaths.Count();
	for (int i = 0; i < c; i++)
	{
		// pak files are not allowed to be written to...
		if (m_SearchPaths[i].GetPackFile())
			continue;

		if ( m_SearchPaths[i].GetPathID() == lookup )
		{
			char newPathName[MAX_PATH];
			Q_snprintf( newPathName, sizeof(newPathName), "%s%s", m_SearchPaths[i].GetPathString(), szNewPath ); // append the path to this dir.

			// make sure the file exists, and is in the Steam cache
			
			if ( bValidatedDllOnly && !IsFileInSteamCache(newPathName) )
				continue;

			// Get a local copy from Steam
			bool bGetLocalCopy = true;

			if ( m_bSDKToolMode )
				bGetLocalCopy = false;
#ifdef _WIN32
			if ( IsDebuggerPresent() )
				bGetLocalCopy = false;
#endif
			if ( bGetLocalCopy )
				GetLocalCopy( newPathName );

			CSysModule *module = Sys_LoadModule( newPathName );
			if ( module ) // we found the binary in one of our search paths
			{
				if ( bValidatedDllOnly && !IsFileInSteamCache2(newPathName) )
				{
					return NULL;
				}
				else
				{
					return module;
				}
			}
		}
	}

	if ( bValidatedDllOnly && IsFileInSteamCache(szNewPath) )
	{
		// couldn't load it from any of the search paths, let LoadLibrary try
		return Sys_LoadModule( szNewPath ); 
	}

	return NULL;
}

void CFileSystem_Steam::ViewSteamCache(const char* szDir, bool bRecurse)
{
	TSteamElemInfo info;
	TSteamError error;
	char szPath[MAX_PATH];

	V_snprintf( szPath, sizeof(szPath),"%s%c*.*", szDir, CORRECT_PATH_SEPARATOR );

	SteamHandle_t h = steam->FindFirst( szPath, eSteamFindRemoteOnly, &info, &error );
	int ret = 0;

	if ( h != STEAM_INVALID_HANDLE )
	{
		do 
		{
			Msg( "View Steam Cache: '%s%c%s' \n", szDir, CORRECT_PATH_SEPARATOR, info.cszName );

			if ( bRecurse && info.bIsDir && (0 == V_stristr( info.cszName, "." ) ) )
			{
				V_snprintf( szPath, sizeof(szPath),"%s%c%s", szDir, CORRECT_PATH_SEPARATOR, info.cszName );
				ViewSteamCache( szPath, true );
			}

			ret = steam->FindNext( h, &info, &error );

		} while( 0 == ret );

		steam->FindClose( h, &error );
	}
}


// HACK HACK - to allow IsFileInSteamCache() to use the old C exported interface
extern "C" SteamHandle_t	SteamFindFirst( const char *cszPattern, ESteamFindFilter eFilter, TSteamElemInfo *pFindInfo, TSteamError *pError );
extern "C" int				SteamFindClose( SteamHandle_t hDirectory, TSteamError *pError );

//-----------------------------------------------------------------------------
// Purpose: returns true if the file exists and is in a mounted Steam cache
//-----------------------------------------------------------------------------
bool CFileSystem_Steam::IsFileInSteamCache( const char *file )
{
	if ( !m_bContentLoaded || m_bSDKToolMode )
	{
		return true;
	}

	// see if the file exists
	TSteamElemInfo info;
	TSteamError error;
	
	SteamHandle_t h = steam->FindFirst( file, eSteamFindRemoteOnly, &info, &error );
	if ( h == STEAM_INVALID_HANDLE )
	{
		return false;
	}
	else
	{
		steam->FindClose( h, &error );
	}

	return true;
}


int CFileSystem_Steam::HintResourceNeed( const char *hintlist, int forgetEverything )
{
	TSteamError steamError;
	int result = steam->HintResourceNeed( hintlist, forgetEverything, &steamError );
	CheckError(NULL, steamError);
	return result;
}