Added PHYSFS_getPrefDir().

This commit is contained in:
Ryan C. Gordon 2012-03-21 23:30:50 -04:00
parent 584119a4a0
commit 24d6a925d1
12 changed files with 235 additions and 14 deletions

View File

@ -6,11 +6,6 @@ Some might be dupes, some might be done already, some might be bad ideas.
From http://icculus.org/pipermail/physfs/2009-March/000698.html ...
- Add an API to find the "pref path" ... this is the directory where an
app's configuration data is supposed to go...which is usually somewhere
under the user directory, but not always. As it is platform-dependent,
platform-version dependent and sometimes even user-dependent, this should be
handled by the library and not the app.
- Archives formats provided by the implementation.
- Write support for various archives. I haven't decided how to do this yet,
but I'd like to.
@ -50,7 +45,6 @@ From old TODO.txt...
- Use __cdecl in physfs.h?
- Look for FIXMEs (many marked with "!!!" in comments).
- Find some way to relax or remove the security model for external tools.
- OSX shouldn't use ~/.app for userdir.
- fscanf and fprintf support in extras dir.
- Why do we call it openArchive and dirClose?
- Sanity check byte order at runtime.

View File

@ -91,6 +91,7 @@
%rename(unmount) PHYSFS_unmount;
%rename(mountMemory) PHYSFS_mountMemory;
%rename(mountHandle) PHYSFS_mountHandle;
%rename(getPrefDir) PHYSFS_getPrefDir;
#endif
%include "../src/physfs.h"

View File

@ -135,6 +135,7 @@ static FileHandle *openWriteList = NULL;
static FileHandle *openReadList = NULL;
static char *baseDir = NULL;
static char *userDir = NULL;
static char *prefDir = NULL;
static int allowSymLinks = 0;
/* mutexes ... */
@ -1312,6 +1313,12 @@ int PHYSFS_deinit(void)
userDir = NULL;
} /* if */
if (prefDir != NULL)
{
allocator.Free(prefDir);
prefDir = NULL;
} /* if */
allowSymLinks = 0;
initialized = 0;
@ -1370,15 +1377,60 @@ void PHYSFS_getCdRomDirsCallback(PHYSFS_StringCallback callback, void *data)
} /* PHYSFS_getCdRomDirsCallback */
const char *PHYSFS_getPrefDir(const char *org, const char *app)
{
const char dirsep = __PHYSFS_platformDirSeparator;
char *ptr = NULL;
PHYSFS_Stat statbuf;
int exists = 0;
BAIL_IF_MACRO(!initialized, PHYSFS_ERR_NOT_INITIALIZED, 0);
BAIL_IF_MACRO(!org, PHYSFS_ERR_INVALID_ARGUMENT, NULL);
BAIL_IF_MACRO(*org == '\0', PHYSFS_ERR_INVALID_ARGUMENT, NULL);
BAIL_IF_MACRO(!app, PHYSFS_ERR_INVALID_ARGUMENT, NULL);
BAIL_IF_MACRO(*app == '\0', PHYSFS_ERR_INVALID_ARGUMENT, NULL);
allocator.Free(prefDir);
prefDir = __PHYSFS_platformCalcPrefDir(org, app);
BAIL_IF_MACRO(!prefDir, ERRPASS, NULL);
#if !PHYSFS_PLATFORM_WINDOWS /* Windows guarantees the dir exists here. */
if (__PHYSFS_platformStat(prefDir, &exists, &statbuf))
return prefDir;
for (ptr = strchr(prefDir, dirsep); ptr; ptr = strchr(ptr+1, dirsep))
{
*ptr = '\0';
__PHYSFS_platformMkDir(prefDir);
*ptr = dirsep;
} /* for */
if (!__PHYSFS_platformMkDir(prefDir))
{
allocator.Free(prefDir);
prefDir = NULL;
} /* if */
#endif
return prefDir;
} /* PHYSFS_getPrefDir */
const char *PHYSFS_getBaseDir(void)
{
return baseDir; /* this is calculated in PHYSFS_init()... */
} /* PHYSFS_getBaseDir */
const char *PHYSFS_getUserDir(void)
const char *__PHYSFS_getUserDir(void) /* not deprecated internal version. */
{
return userDir; /* this is calculated in PHYSFS_init()... */
} /* __PHYSFS_getUserDir */
const char *PHYSFS_getUserDir(void)
{
return __PHYSFS_getUserDir();
} /* PHYSFS_getUserDir */

View File

@ -111,7 +111,7 @@
* use the base dir for both searching and writing. There is a helper
* function (PHYSFS_setSaneConfig()) that puts together a basic configuration
* for you, based on a few parameters. Also see the comments on
* PHYSFS_getBaseDir(), and PHYSFS_getUserDir() for info on what those
* PHYSFS_getBaseDir(), and PHYSFS_getPrefDir() for info on what those
* are and how they can help you determine an optimal search path.
*
* PhysicsFS 2.0 adds the concept of "mounting" archives to arbitrary points
@ -765,7 +765,7 @@ PHYSFS_DECL char **PHYSFS_getCdRomDirs(void);
*
* \return READ ONLY string of base dir in platform-dependent notation.
*
* \sa PHYSFS_getUserDir
* \sa PHYSFS_getPrefDir
*/
PHYSFS_DECL const char *PHYSFS_getBaseDir(void);
@ -774,6 +774,8 @@ PHYSFS_DECL const char *PHYSFS_getBaseDir(void);
* \fn const char *PHYSFS_getUserDir(void)
* \brief Get the path where user's home directory resides.
*
* \deprecated As of PhysicsFS 2.1, you probably want PHYSFS_getPrefDir().
*
* Helper function.
*
* Get the "user dir". This is meant to be a suggestion of where a specific
@ -783,14 +785,12 @@ PHYSFS_DECL const char *PHYSFS_getBaseDir(void);
* where "username" will either be the login name, or "default" if the
* platform doesn't support multiple users, either.
*
* You should probably use the user dir as the basis for your write dir, and
* also put it near the beginning of your search path.
*
* \return READ ONLY string of user dir in platform-dependent notation.
*
* \sa PHYSFS_getBaseDir
* \sa PHYSFS_getPrefDir
*/
PHYSFS_DECL const char *PHYSFS_getUserDir(void);
PHYSFS_DECL const char *PHYSFS_getUserDir(void) PHYSFS_DEPRECATED;
/**
@ -3230,6 +3230,75 @@ PHYSFS_DECL const char *PHYSFS_getErrorByCode(PHYSFS_ErrorCode code);
*/
PHYSFS_DECL void PHYSFS_setErrorCode(PHYSFS_ErrorCode code);
/**
* \fn const char *PHYSFS_getPrefDir(const char *org, const char *app)
* \brief Get the user-and-app-specific path where files can be written.
*
* Helper function.
*
* Get the "pref dir". This is meant to be where users can write personal
* files (preferences and save games, etc) that are specific to your
* application. This directory is unique per user, per application.
*
* This function will decide the appropriate location in the native filesystem,
* create the directory if necessary, and return a string in
* platform-dependent notation, suitable for passing to PHYSFS_setWriteDir().
*
* On Windows, this might look like:
* "C:\\Users\\bob\\AppData\\Roaming\\My Company\\My Program Name"
*
* On Linux, this might look like:
* "/home/bob/.local/share/My Program Name"
*
* On Mac OS X, this might look like:
* "/Users/bob/Library/Application Support/My Program Name"
*
* (etc.)
*
* You should probably use the pref dir for your write dir, and also put it
* near the beginning of your search path. Older versions of PhysicsFS
* offered only PHYSFS_getUserDir() and left you to figure out where the
* files should go under that tree. This finds the correct location
* for whatever platform, which not only changes between operating systems,
* but also versions of the same operating system.
*
* You specify the name of your organization (if it's not a real organization,
* your name or an Internet domain you own might do) and the name of your
* application. These should be proper names.
*
* Both the (org) and (app) strings may become part of a directory name, so
* please follow these rules:
*
* - Try to use the same org string (including case-sensitivity) for
* all your applications that use this function.
* - Always use a unique app string for each one, and make sure it never
* changes for an app once you've decided on it.
* - Unicode characters are legal, as long as it's UTF-8 encoded, but...
* - ...only use letters, numbers, and spaces. Avoid punctuation like
* "Game Name 2: Bad Guy's Revenge!" ... "Game Name 2" is sufficient.
*
* The pointer returned by this function remains valid until you call this
* function again, or call PHYSFS_deinit(). This is not necessarily a fast
* call, though, so you should call this once at startup and copy the string
* if you need it.
*
* You should assume the path returned by this function is the only safe
* place to write files (and that PHYSFS_getUserDir() and PHYSFS_getBaseDir(),
* while they might be writable, or even parents of the returned path, aren't
* where you should be writing things).
*
* \param org The name of your organization.
* \param app The name of your application.
* \return READ ONLY string of user dir in platform-dependent notation. NULL
* if there's a problem (creating directory failed, etc).
*
* \sa PHYSFS_getBaseDir
* \sa PHYSFS_getUserDir
*/
PHYSFS_DECL const char *PHYSFS_getPrefDir(const char *org, const char *app);
/* Everything above this line is part of the PhysicsFS 2.1 API. */

View File

@ -633,13 +633,26 @@ char *__PHYSFS_platformCalcBaseDir(const char *argv0);
*/
char *__PHYSFS_platformGetUserName(void);
/*
/* !!! FIXME: should this be CalcUserDir, to match CalcBaseDir?
* Get the platform-specific user dir.
* Caller will allocator.Free() the retval if it's not NULL. If it's NULL,
* the userdir will default to basedir/username.
*/
char *__PHYSFS_platformGetUserDir(void);
/* This is the cached version from PHYSFS_init(). This is a fast call. */
const char *__PHYSFS_getUserDir(void); /* not deprecated internal version. */
/*
* Get the platform-specific pref dir.
* Caller will allocator.Free() the retval if it's not NULL. If it's NULL,
* it's a total failure. Caller will make missing directories if necessary;
* this just reports the final path.
*/
char *__PHYSFS_platformCalcPrefDir(const char *org, const char *app);
/*
* Return a pointer that uniquely identifies the current thread.
* On a platform without threading, (0x1) will suffice. These numbers are

View File

@ -183,6 +183,19 @@ char *__PHYSFS_platformCalcBaseDir(const char *argv0)
} /* __PHYSFS_platformCalcBaseDir */
char *__PHYSFS_platformCalcPrefDir(const char *org, const char *app)
{
/* !!! FIXME: there's a real API to determine this */
const char *userdir = __PHYSFS_getUserDir();
const char *append = "config/settings/";
const size_t len = strlen(userdir) + strlen(append) + strlen(app) + 1;
char *retval = allocator.Malloc(len);
BAIL_IF_MACRO(!retval, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
snprintf(retval, len, "%s%s%s", userdir, append, app);
return retval;
} /* __PHYSFS_platformCalcPrefDir */
void *__PHYSFS_platformGetThreadID(void)
{
return (void *) find_thread(NULL);

View File

@ -289,6 +289,19 @@ char *__PHYSFS_platformCalcBaseDir(const char *argv0)
} /* __PHYSFS_platformCalcBaseDir */
char *__PHYSFS_platformCalcPrefDir(const char *org, const char *app)
{
/* !!! FIXME: there's a real API to determine this */
const char *userdir = __PHYSFS_getUserDir();
const char *append = "Library/Application Support/";
const size_t len = strlen(userdir) + strlen(append) + strlen(app) + 1;
char *retval = allocator.Malloc(len);
BAIL_IF_MACRO(!retval, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
snprintf(retval, len, "%s%s%s", userdir, append, app);
return retval;
} /* __PHYSFS_platformCalcPrefDir */
/* Platform allocator uses default CFAllocator at PHYSFS_init() time. */
static CFAllocatorRef cfallocdef = NULL;

View File

@ -293,6 +293,36 @@ char *__PHYSFS_platformCalcBaseDir(const char *argv0)
} /* __PHYSFS_platformCalcBaseDir */
char *__PHYSFS_platformCalcPrefDir(const char *org, const char *app)
{
/*
* We use XDG's base directory spec, even if you're not on Linux.
* This isn't strictly correct, but the results are relatively sane
* in any case.
*
* http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
*/
const char *envr = getenv("XDG_DATA_HOME");
const char *append = "/";
char *retval = NULL;
size_t len = 0;
if (!envr)
{
/* You end up with "$HOME/.local/share/Game Name 2" */
envr = __PHYSFS_getUserDir();
BAIL_IF_MACRO(!envr, ERRPASS, NULL); /* oh well. */
append = ".local/share/";
} /* if */
len = strlen(envr) + strlen(append) + strlen(app) + 1;
retval = (char *) allocator.Malloc(len);
BAIL_IF_MACRO(!retval, PHYSFS_ERR_OUT_OF_MEMORY, NULL);
snprintf(retval, len, "%s%s%s", envr, append, app);
return retval;
} /* __PHYSFS_platformCalcPrefDir */
int __PHYSFS_platformSetDefaultAllocator(PHYSFS_Allocator *a)
{
return 0; /* just use malloc() and friends. */

View File

@ -88,6 +88,7 @@ typedef struct
} WinApiFile;
/* !!! FIXME: we cache userDir in physfs.c during PHYSFS_init(), too. */
static char *userDir = NULL;
static HANDLE libUserEnv = NULL;
static HANDLE detectCDThreadHandle = NULL;
@ -441,6 +442,22 @@ char *__PHYSFS_platformCalcBaseDir(const char *argv0)
} /* __PHYSFS_platformCalcBaseDir */
char *__PHYSFS_platformCalcPrefDir(const char *org, const char *app)
{
// Vista and later has a new API for this, but SHGetFolderPath works there,
// and apparently just wraps the new API. This is the new way to do it:
// SHGetKnownFolderPath(FOLDERID_RoamingAppData, KF_FLAG_CREATE,
// NULL, &wszPath);
WCHAR path[MAX_PATH];
if (!SUCCEEDED(SHGetFolderPathW(NULL, CSIDL_APPDATA | CSIDL_FLAG_CREATE,
NULL, 0, path)))
BAIL_MACRO(PHYSFS_ERR_OS_ERROR, NULL);
return unicodeToUtf8Heap(path);
} /* __PHYSFS_platformCalcPrefDir */
char *__PHYSFS_platformGetUserName(void)
{
DWORD bufsize = 0;

View File

@ -23,6 +23,9 @@
#include <time.h>
/* Define this, so the compiler doesn't complain about using old APIs. */
#define PHYSFS_DEPRECATED
#include "physfs.h"
#define TEST_VERSION_MAJOR 2
@ -386,6 +389,19 @@ static int cmd_getuserdir(char *args)
} /* cmd_getuserdir */
static int cmd_getprefdir(char *args)
{
char *org;
char *appName;
char *ptr = args;
org = ptr;
ptr = strchr(ptr, ' '); *ptr = '\0'; ptr++; appName = ptr;
printf("Pref dir is [%s].\n", PHYSFS_getPrefDir(org, appName));
return 1;
} /* cmd_getprefdir */
static int cmd_getwritedir(char *args)
{
printf("Write dir is [%s].\n", PHYSFS_getWriteDir());
@ -1130,6 +1146,7 @@ static const command_info commands[] =
{ "getsearchpath", cmd_getsearchpath, 0, NULL },
{ "getbasedir", cmd_getbasedir, 0, NULL },
{ "getuserdir", cmd_getuserdir, 0, NULL },
{ "getprefdir", cmd_getprefdir, 2, "<org> <app>" },
{ "getwritedir", cmd_getwritedir, 0, NULL },
{ "setwritedir", cmd_setwritedir, 1, "<newWriteDir>" },
{ "permitsymlinks", cmd_permitsyms, 1, "<1or0>" },

View File

@ -11,6 +11,7 @@ physfs::init("$0") or die("physfs::init('$0'): ".physfs::getLastError()."\n");
print "user dir: ", physfs::getUserDir(), "\n";
print "base dir: ", physfs::getBaseDir(), "\n";
print "pref dir: ", physfs::getPrefDir("icculus.org", "test_physfs"), "\n";
print "deinit...\n";
physfs::deinit();

View File

@ -9,6 +9,7 @@ Physfs.init($0)
puts "user dir: " + Physfs.getUserDir()
puts "base dir: " + Physfs.getBaseDir()
puts "pref dir: " + Physfs.getPrefDir("icculus.org", "test_physfs")
puts "deinit..."
Physfs.deinit()