Discussion:
[SDL] Feature request related to SDL_GetBasePath
Coriiander
2017-01-12 22:28:08 UTC
Permalink
Hello, and thank you for accepting my recent community membership request.
(Just to introduce: My name is Cor Schols, version 39 though still bugged, compiled in The Netherlands)

Feature request
The purpose of this forum post is to file a feature request for SDL2. An example implementation meeting the request's purpose is included. With the release of SDL 2.0.1 a new function was introduced: SDL_GetBasePath() (https://wiki.libsdl.org/SDL_GetBasePath). The function retrieves the base directory where the application resides in. While this may possibly be considered to be a minor utility function, I really like its presence. In fact, I've filed bug reports on it before (fixed now). While a nice feature, I think there's bigger potential possible here., with just a trivial change or addition. As follows:

Like retrieving an application's base directory path, I think it would be great to also be able to retrieve the application's filename. The filename can be used for numerous purposes, for example to set a default window/dialog title or have a default log name, etc. This would be especially convenient for gamedev libraries/frameworks that use SDL in its core. As it stands, while using SDL, I find myself still having to write platform-dependant code to obtain the application's filename myself. This while SDL already had access to it while retrieving the base path. It's inefficient, duplicate, inconsistent.

Suggestions
[1] Introduce a new function "SDL_GetBasePathExt", which takes an argument indicating which part of the base path to return (i.e. full, directory-only, filename-only, filename-without-extension).
[2] Do not introduce above new function, but change existing SDL_GetBasePath function to include said additional argument.
[3] Complete overhaul of SDL_filesystem, turning it into a subsystem (like audio, timer, video). It could allow for a new function "SDL_GetPath" which would not only be able to return (parts of) the application's base directory, but also the user directory, pref path, the system's temporary directory, a temporary file name to write to, etc. It could be extended with allowing users to register their own named paths (e.g. content/asset-path). The filesystem could offer functionality to work with path symbols (e.g. %prefpath%, %basedir%). Anyway, this third option is outside the scope of this post - return to option 1 or 2, before we start incorporating Ryan's PhysFS and whatnot.

Example implementation
Below you find my example implementation (Win32) which shows how SDL_GetBasePath could very easily be changed to return subparts of the base path. As I see no options here to include post attachments I'll copy/paste it here, and also provide download links to the edited source code files:

SDL_filesystem.h (http://www.coriiander.com/ext/sdl/forum/SDL_filesystem.h)
Added 1 new enum typedef and 1 new function declaration.

Code:
typedef enum
{
SDL_PATH_APP_FULL = 1, /* full app directory and filename */
SDL_PATH_APP_DIRECTORY = 2, /* app directory only */
SDL_PATH_APP_FILENAME = 3, /* app filename only */
SDL_PATH_APP_FILENAME_NO_EXT = 4 /* app filename without extension */
} SDL_PathID;

/**
* \brief Returns the requested path.
*
* \return UTF-8 string of requested path in platform-dependent notation,
* or NULL on ary error.
*
* \sa SDL_GetBasePath, SDL_GetPrefPath
*/
extern DECLSPEC char *SDLCALL SDL_GetPath(Uint32 pathId);




SDL_sysfilesystem.c (http://www.coriiander.com/ext/sdl/forum/SDL_sysfilesystem.c)
Added 1 new function definition, changed SDL_GetBasePath to call this function.
Basically the code was copied over from SDL_GetBasePath, and then altered with a few lines of codes to enable my feature request. Simple, trivial.

Code:
char *
SDL_GetBasePath(void)
{
return SDL_GetPath(SDL_PATH_APP_DIRECTORY);
}

char *
SDL_GetPath(Uint32 pathId)
{
typedef DWORD (WINAPI *GetModuleFileNameExW_t)(HANDLE, HMODULE, LPWSTR, DWORD);
GetModuleFileNameExW_t pGetModuleFileNameExW;
WCHAR *path = NULL;
char *retval;
HANDLE psapi = LoadLibrary(L"psapi.dll");
DWORD buflen = 128;
DWORD len = 0;
int iDirEnd;
int iExtPos;

if (!psapi) {
WIN_SetError("Couldn't load psapi.dll");
return NULL;
}

pGetModuleFileNameExW = (GetModuleFileNameExW_t)GetProcAddress(psapi, "GetModuleFileNameExW");
if (!pGetModuleFileNameExW) {
WIN_SetError("Couldn't find GetModuleFileNameExW");
FreeLibrary(psapi);
return NULL;
}

while (32768 >= buflen) {
void *ptr = SDL_realloc(path, buflen * sizeof (WCHAR));
if (!ptr) {
SDL_free(path);
FreeLibrary(psapi);
SDL_OutOfMemory();
return NULL;
}

path = (WCHAR *) ptr;

len = pGetModuleFileNameExW(GetCurrentProcess(), NULL, path, buflen);
if (len != buflen) {
break;
}

/* buffer too small? Try again. */
buflen *= 2;
}

FreeLibrary(psapi);

if (len == 0) {
SDL_free(path);
WIN_SetError("Couldn't locate our .exe");
return NULL;
}

/* Full path requested? We have the full path right now; return it */
if (SDL_PATH_APP_FULL == pathId)
{
retval = WIN_StringToUTF8(path);
SDL_free(path);
return retval;
}

/* Either the directory or the filename was requested.
Find out where the directory part ends and the filename begins. */
for (iDirEnd = len-1; iDirEnd > 0; iDirEnd--) {
if (path[iDirEnd] == '\\') {
break;
}
}

SDL_assert(iDirEnd > 0); /* Should have been an absolute path. */

/* Directory path requested? Return path with filename chopped of. */
if (SDL_PATH_APP_DIRECTORY == pathId)
{
path[iDirEnd+1] = '\0';
retval = WIN_StringToUTF8(path);
SDL_free(path);
return retval;
}

/* Directory part no longer needed. */
++iDirEnd;

/* Filename without extension requested? Chop off extension. */
if (SDL_PATH_APP_FILENAME_NO_EXT == pathId)
{
/* NOTE: Takes the approach of extensions to be single-dotted.
(like .NET and Windows itself do as wel).
Changing this to support multi-dot instead is trivial.
Optionally some new hint or compile-time flag could be
introduced to allow the user to specificy desired approach. */
for (iExtPos = len-1; iExtPos > iDirEnd; iExtPos--) {
if (path[iExtPos] == '.') {
path[iExtPos] = '\0';
break;
}
}
}

/* Filename (with or without extension) requested?
Return path with directory stripped off. */
if (SDL_PATH_APP_FILENAME == pathId ||
SDL_PATH_APP_FILENAME_NO_EXT == pathId)
{
retval = WIN_StringToUTF8(&path[iDirEnd]);
SDL_free(path);
return retval;
}

/* Invalid path id requested. */
SDL_free(path);
SDL_SetError("Invalid path id");
return NULL;
}




Thank you for your time. Feel free to contact me if there are any questions. You can also contact me if you need additional help on working on SDL. Reasons of my (potential full-time) availability are outside the scope of this forum post.

Kind regards,
Cor Schols, alias Coriiander
Sik the hedgehog
2017-01-15 04:40:39 UTC
Permalink
SDL_GetBasePath should retrieve the path *of* the executable (i.e.
which directory it resides in). A function that retrieves the filename
should not include "path" in it. That probably solves a lot of the
dilemma here :P

SDL_GetBaseFileName? SDL_GetExecFileName?

Not sure whether a variant without extension is worth the effort (just
find the last dot and replace it with a '\0' if it exists), but I'm
leaving that up to the rest to decide.

Loading...