Jump to content


PhysicsFS (Not a Physics System)

Recommended Posts

PhysicsFS or (PhysFS for short) can be found at: http://icculus.org/physfs/


I now use this in the cgame for read/write of most of my text-format files. As well as for the game monkey script reading.


I also have it on the server side as well for Shrubbot, my weapon config, the holocron point files, bot waypoint files, banlist, as well as others but just to name a few.


If you look on the websvn you will also notice i posted a patch for 0-length file opening. :p


I use this instead of the regular trap calls because theoretically you can get around some things that you are not supposed to be able to. Plus this allows for non-allowed extensions in pure servers (though most people don't use pure in JKA because of skins, models, sabers, etc)


Also, it is completely virtual like the quake 3 engine is. How it works basically is you call init to the directory where jamp is. Mount fs_game directory and base. Change the zip extension to pk3 and then you can mount all pk3s and read files from pk3s as well. Note: you are only able to have 1 write directory so you would want to set that to fs_game, also if fs_game is "" you will want to forward it to base and not do both checks.


The way I have mine set up is, an extra read cmd that asks for an enum of:


MODPATH_BASE or MODPATH_FS_GAME if it gets MODPATH_BASE it looks in base/ if it gets MODPATH_FS_GAME it looks in fs_game/ fs_game being whatever that cvar is.


You can also chain it if say you are loading a map to check if it exists, check in base and if that fails, try fs_game, and if that fails then fail completely. I'm actually using C++ to do my internal functions in ©g_physfs.cpp.


Basically I have:


A cvar retrieve function that returns the buffer of the string in a std::string format.


And, I use new and delete for char buffers for the game monkey portion, otherwise i just use my CG_Alloc / G_Alloc and CG_Free / G_Free.

Link to post
Share on other sites

A snippet of PhysFS starting up on my server:


SV_PhysFS: Initializing...
SV_PhysFS: Mounted: /home/jeremy/.jediacademy/ensimod to ensimod
SV_PhysFS: Mounted: /home/jeremy/.jediacademy/base to base
SV_PhysFS: Mounted: 21 PK3s in base
SV_PhysFS: Mounted: 2 PK3s in ensimod
SV_PhysFS: Set: WriteDir to: /home/jeremy/.jediacademy/ensimod


... And shutdown:


SV_PhysFS: Shutting Down...

Link to post
Share on other sites

NOTE: Cannot be used to open any visuals or shaders that would be used by renderer or sounds, etc... If you are just making a test of file exists, that is fine but otherwise, no don't use for that or it cannot really communicate with renderer portion of engine.

Link to post
Share on other sites
  • 2 months later...

I've successfully added this to the UI module now as well.


I fixed PK3 loading in that it sorts them in the correct order so that it loads in the same order as the engine does.


New Functions:

- PFS_isOpen

- PFS_writeSync

- PFS_seek

- PFS_tell

- PFS_eof

- PFS_setBuffer

- PFS_flush

- PFS_fileLength

- PFS_GetFileList (Very haxor, but works nicely)

- PFS_getLastError


GetFileList is far more advanced than the like of the regular trap_FS_GetFileList. In that it does not use a huge buffer to store each file. It uses a struct with an array of 2 buffers per item found, plus if it was in base or in the current mod folder (which is base if you are running from base).


So... to properly load all the .bot files in scripts/ in g_bot.c normally looks something like so:


static void G_LoadBots( void ) {
vmCvar_t	botsFile;
int			numdirs;
char		filename[128];
char		dirlist[1024];
char*		dirptr;
int			i;
int			dirlen;

if ( !trap_Cvar_VariableIntegerValue( "bot_enable" ) ) {

g_numBots = 0;

trap_Cvar_Register( &botsFile, "g_botsFile", "", CVAR_INIT|CVAR_ROM );
if( *botsFile.string ) {
else {

// get all bots from .bot files
numdirs = trap_FS_GetFileList("scripts", ".bot", dirlist, 1024 );
dirptr  = dirlist;
for (i = 0; i < numdirs; i++, dirptr += dirlen+1) {
	dirlen = strlen(dirptr);
	Q_strncpyz(filename, "scripts/", sizeof(filename));
	Q_strcat(filename, sizeof(filename), dirptr);
//	G_Printf( "%i bots parsed\n", g_numBots );


However, all that seems rather dirty...


static void G_LoadBots( void ) {
vmCvar_t botsFile;
int i = 0;
FindInfo bots;

if (!trap_Cvar_VariableIntegerValue("bot_enable")) {

bots.i_NumFiles = 0;

g_numBots = 0;

trap_Cvar_Register(&botsFile, "g_botsFile", "", CVAR_INIT|CVAR_ROM);
if( botsFile.string[0] ) {
} else {

PFS_GetFileList("scripts", ".bot", &bots);
for(i = 0; i < bots.i_NumFiles && bots.m_Files[i].m_Real; ++i) {
//	G_Printf( "%i bots parsed\n", g_numBots );


m_Real includes the base/ or ensimod/ or whatever fs_game is; m_Virtual does not. There are cases where, even in the older method you needed to strip the extension. Sometimes you need to strip off the rest of the path too, but I've only seen the path one once (in UI loading of demolist & roqlist probably too)


FindInfo struct:


typedef struct
struct File
	char	m_Real[256];
	char	m_Virtual[256];
	ModPath	i_MountPoint;
} m_Files[4096];
int		i_NumFiles;
char	m_Ext[10];
} FindInfo;

Link to post
Share on other sites
  • Create New...