IFileSystemV009

From Valve Developer Community
Revision as of 18:57, 2 July 2016 by Solokiller (talk | contribs) (Created page to cover the IFileSystem V009 interface (GoldSource filesystem))

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

The SteamPipe update and subsequent Half-Life SDK release on Github came with a modified version of the IFileSystem interface that is designed to work with GoldSource's file searching procedures.

It provides a means to use the search paths defined by the engine to find files in the same way that the engine does, as well as allowing new search paths to be added to find content in other directories.

See IFileSystem for information on operations shared between the GoldSource and Source engine versions of the interface.

Path IDs

In GoldSource, the same file can exist in multiple SearchPaths. IFileSystem defines a few different access modes for determining precisely where it should look:

ROOT
The root directory. This is the location of the game's executable.
BASE
Same as ROOT.
DEFAULT
Default paths. Generally used when searching all paths.
PLATFORM
The game directory's platform directory.
GAME
Game directories, including localization, _addon and _hd.
GAMECONFIG
The directory containing game configuration files. This is the mod directory.
GAMEDOWNLOAD
The _downloads directory for the mod.
GAME_FALLBACK
If the fallback_dir option is provided in liblist.gam, this path ID is also set. Essentially, this enables you to easily specify that another game's content should also be mounted. It will add search paths for its _addon, _hd and localization directories as well. For example, Condition Zero defines its fallback directory to be "cstrike".

SteamPipe directories

The SteamPipe update also introduced a new set of directories that content is to be placed in. These are intended to prevent pollution of default content by custom content, and to prevent pollution of default and manually installed custom content by automatically downloaded content.

Content located in one of these directories will override the content located in the <base dir> and mod directories.

The following directories now exist:

<base dir>
The base directory is where all Half-Life content is normally loaded from. It defaults to valve/, but can be overridden by using the -basedir command line parameter.
<mod dir>
As before, the mod directory provides default content, the server and client libraries, and configuration files.
<mod dir>_addon
If the -addons command line parameter has been specified, this directory will be added as a GAME search path.
<mod dir>_downloads
This directory is where all content downloaded from game servers is stored at. Unlike _addon, this directory is not added to the GAME path ID. Presumably this is to prevent downloaded content from overriding default content in sensitive situations, like when server configurations are being executed.
<mod dir>_hd
If HD models are enabled, this directory is also added as a search path. It is intended to provide HD models that override default versions.

Instantiating an IFileSystem

There is only one instance of the IFileSystem interface, located in the filesystem_stdio library (filesystem_steam is obsolete).

Getting the instance takes a few steps (code modified from DMC's source):

#include "interface.h"
#include "FileSystem.h"

CSysModule* g_pFileSystemModule = NULL;
IFileSystem* g_pFileSystem = NULL;

bool LoadFileSystem()
{
	// Determine which filesystem to use.
#if defined ( _WIN32 )
	char *szFsModule = "filesystem_stdio.dll";
#elif defined(OSX)
	char *szFsModule = "filesystem_stdio.dylib";
#elif defined(LINUX)
	char *szFsModule = "filesystem_stdio.so";
#else
#error
#endif


	char szFSDir[ MAX_PATH ];
	szFSDir[ 0 ] = 0;
#ifdef CLIENT_DLL
	if( gEngfuncs.COM_ExpandFilename( szFsModule, szFSDir, sizeof( szFSDir ) ) == FALSE )
	{
		return false;
	}
#else
	//Just use the filename for the server. No COM_ExpandFilename here.
	strcpy( szFSDir, szFsModule );
#endif

	// Get filesystem interface.
	g_pFileSystemModule = Sys_LoadModule( szFSDir );
	assert( g_pFileSystemModule );
	if( !g_pFileSystemModule )
	{
		return false;
	}

	CreateInterfaceFn fileSystemFactory = Sys_GetFactory( g_pFileSystemModule );
	if( !fileSystemFactory )
	{
		return false;
	}

	g_pFileSystem = ( IFileSystem* ) fileSystemFactory( FILESYSTEM_INTERFACE_VERSION, nullptr );
	assert( g_pFileSystem );
	if( !g_pFileSystem )
	{
		return false;
	}

	return true;
}


Let's go over each step:

#include "interface.h"
#include "FileSystem.h"


In order to make use of the interface, its definition must be included. FileSystem.h includes that definition, and uses interface.h.

CSysModule* g_pFileSystemModule = NULL;
IFileSystem* g_pFileSystem = NULL;


These global variables will keep track of the filesystem module and the filesystem itself, respectively.

	// Determine which filesystem to use.
#if defined ( _WIN32 )
	char *szFsModule = "filesystem_stdio.dll";
#elif defined(OSX)
	char *szFsModule = "filesystem_stdio.dylib";
#elif defined(LINUX)
	char *szFsModule = "filesystem_stdio.so";
#else
#error
#endif


Based on the platform that you're compiling the game for, you'll need a specific library name and extension. The pointer szFsModule will point to the correct name.

	char szFSDir[ MAX_PATH ];
	szFSDir[ 0 ] = 0;
#ifdef CLIENT_DLL
	if( gEngfuncs.COM_ExpandFilename( szFsModule, szFSDir, sizeof( szFSDir ) ) == FALSE )
	{
		return false;
	}
#else
	//Just use the filename for the server. No COM_ExpandFilename here.
	strcpy( szFSDir, szFsModule );
#endif


The full name of the library file should now be determined. On the client side, you can use COM_ExpandFilename to get the absolute path of the file. The server does not expose this function (which actually accesses the filesystem itself to get this information), so you'll have to rely on the game using the correct working directory while loading it.

	// Get filesystem interface.
	g_pFileSystemModule = Sys_LoadModule( szFSDir );
	assert( g_pFileSystemModule );
	if( !g_pFileSystemModule )
	{
		return false;
	}


Next we load the filesystem library. The library will already have been loaded and initialized by the engine, so we're really just getting a handle to the library. If this fails, we stop and return false to indicate failure.

	CreateInterfaceFn fileSystemFactory = Sys_GetFactory( g_pFileSystemModule );
	if( !fileSystemFactory )
	{
		return false;
	}


In order to instantiate the filesystem, we need to have the library's CreateInterface function. If we fail to retrieve it, we stop and return false to indicate failure.

	g_pFileSystem = ( IFileSystem* ) fileSystemFactory( FILESYSTEM_INTERFACE_VERSION, nullptr );
	assert( g_pFileSystem );
	if( !g_pFileSystem )
	{
		return false;
	}


Now we instantiate the filesystem by querying the CreateInterface function for the interface version. If this fails, it can only mean that the filesystem isn't exposed for that version. In practice this should never happen, unless a breaking change is made to the interface that requires all users to upgrade, or if the library has been replaced by a third party.

	return true;


If we've reached this point, that means we've successfully instantiated the filesystem. We can now use it for any file operations, add new search paths, etc.

The LoadFileSystem function should be called in the Initialize function in the client library and either the GiveFnptrsToDll or GameDLLInit functions in the server library. Note that using GameDLLInit will require you to submit a "quit" server command in order to shut down the server in the event that the filesystem fails to load.

Notes

This version of the filesystem was originally designed for Steam (GCF) based content. As such, its interface contains methods to perform Steam specific actions. Since the SteamPipe update, these will no longer do anything.

The RemoveSearchPath method will cause an access violation regardless of which path you give it. This bug has been reported on Half-Life's bug tracker [1].