filesystem: Implement SDL_GetExeName() for all platforms.

This commit is contained in:
Ryan C. Gordon 2026-05-27 15:41:51 -04:00
parent 6b780c5ff9
commit d7ba3efe6b
No known key found for this signature in database
GPG key ID: FA148B892AB48044
11 changed files with 141 additions and 30 deletions

View file

@ -267,7 +267,9 @@ extern "C" {
anything calling it without an extremely good reason. */
extern SDL_NORETURN void SDL_ExitProcess(int exitcode);
// Get just the process's binary name, no path. Calculates and caches the string on first call. String lives until SDL_Quit(). This is not a public API right now!
// Get just the process's binary name, no path. NULL if it doesn't make sense for a platform.
// Can be something not a file, like a package ID on Android. Meant to be human-readable, not appended to a path, etc.
// Calculates and caches the string on first call. String lives until SDL_Quit(). This is not a public API right now!
extern const char *SDL_GetExeName(void);
#ifdef HAVE_LIBC

View file

@ -2886,6 +2886,43 @@ int SDL_GetAndroidSDKVersion(void)
return sdk_version;
}
char *SDL_GetAndroidPackageName(void)
{
// this doesn't currently cache this, because it's only used by SDL_GetExeName, which _does_ cache it.
struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(SDL_FUNCTION);
JNIEnv *env = Android_JNI_GetEnv();
if (!LocalReferenceHolder_Init(&refs, env)) {
LocalReferenceHolder_Cleanup(&refs);
return NULL;
}
// context = SDLActivity.getContext();
jobject context = (*env)->CallStaticObjectMethod(env, mActivityClass, midGetContext);
if (!context) {
SDL_SetError("Couldn't get Android context!");
LocalReferenceHolder_Cleanup(&refs);
return NULL;
}
// fileObj = context.getFilesDir();
jmethodID mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, context), "getPackageName", "()Ljava/lang/String;");
jstring jstr = (jstring)(*env)->CallObjectMethod(env, context, mid);
if (Android_JNI_ExceptionOccurred(false)) {
LocalReferenceHolder_Cleanup(&refs);
return NULL;
}
const char *cstr = (*env)->GetStringUTFChars(env, jstr, NULL);
char *retval = cstr ? SDL_strdup(cstr) : NULL;
(*env)->ReleaseStringUTFChars(env, jstr, cstr);
LocalReferenceHolder_Cleanup(&refs);
return retval;
}
bool SDL_IsAndroidTablet(void)
{
JNIEnv *env = Android_JNI_GetEnv();

View file

@ -153,6 +153,8 @@ int SDL_GetAndroidSDKVersion(void);
bool SDL_IsAndroidTablet(void);
bool SDL_IsAndroidTV(void);
char *SDL_GetAndroidPackageName(void); // this is a SDL_malloc'd string the caller will own.
// File Dialogs
bool Android_JNI_ShowFileDialog(SDL_DialogFileCallback callback, void *userdata,
const SDL_DialogFileFilter *filters, int nfilters, SDL_FileDialogType type,

View file

@ -26,8 +26,7 @@
// System dependent filesystem routines
#include "../SDL_sysfilesystem.h"
#include <unistd.h>
#include "../../core/android/SDL_android.h"
char *SDL_SYS_GetBasePath(void)
{
@ -36,7 +35,7 @@ char *SDL_SYS_GetBasePath(void)
char *SDL_SYS_GetExeName(void)
{
return NULL; // !!! FIXME: probably just use the Linux path?
return SDL_GetAndroidPackageName();
}
char *SDL_SYS_GetPrefPath(const char *org, const char *app)

View file

@ -47,7 +47,7 @@ char *SDL_SYS_GetBasePath(void)
} else if (SDL_strcasecmp(baseType, "parent") == 0) {
base = [[[bundle bundlePath] stringByDeletingLastPathComponent] fileSystemRepresentation];
} else {
// this returns the exedir for non-bundled and the resourceDir for bundled apps
// this returns the exedir for non-bundled and the resourceDir for bundled apps
base = [[bundle resourcePath] fileSystemRepresentation];
}
@ -65,8 +65,24 @@ char *SDL_SYS_GetBasePath(void)
char *SDL_SYS_GetExeName(void)
{
SDL_Unsupported(); // !!! FIXME
return NULL;
@autoreleasepool {
NSBundle *bundle = [NSBundle mainBundle];
const char *name = [[[bundle infoDictionary] objectForKey:@"CFBundleIdentifier"] UTF8String];
if (!name) {
name = [[[bundle infoDictionary] objectForKey:@"CFBundleDisplayName"] UTF8String];
if (!name) {
name = [[[bundle infoDictionary] objectForKey:@"CFBundleName"] UTF8String];
if (!name) {
name = [[[bundle infoDictionary] objectForKey:@"CFBundleExecutable"] UTF8String];
if (!name) {
name = [[[NSProcessInfo processInfo] processName] UTF8String]; // oh well.
}
}
}
}
return name ? SDL_strdup(name) : NULL;
}
}
char *SDL_SYS_GetPrefPath(const char *org, const char *app)

View file

@ -31,7 +31,7 @@
#include "../SDL_sysfilesystem.h"
char *SDL_SYS_GetBasePath(void)
static char *GetExePath(void)
{
/* As of MS-DOS 3.0, you can get the full path to the EXE from the very end of the
environment table, which is discovered through the PSP:
@ -80,34 +80,45 @@ char *SDL_SYS_GetBasePath(void)
slen++; /* count the null terminator. */
char *lastbackslash = NULL;
char *retval = (char *) SDL_malloc(slen);
if (retval) {
for (int i = 0; i < slen; i++) {
const char ch = (char) _farpeekb(envsel, offset + i);
if (ch == '\\') {
retval[i] = '/'; // I don't know if this is a good idea. Drop DOS path separators, use Unix style instead.
lastbackslash = &retval[i];
} else {
retval[i] = ch;
}
retval[i] = (ch == '\\') ? '/' : ch; // I don't know if this is a good idea. Drop DOS path separators, use Unix style instead.
}
}
if (lastbackslash) {
lastbackslash[1] = '\0'; /* chop off exe name, just leave path */
} else { // uh...should have been a full path...?!
SDL_free(retval);
retval = NULL;
}
return retval;
}
char *SDL_SYS_GetBasePath(void)
{
char *path = GetExePath(); // look up full path of the current process's EXE file.
if (!path) {
return NULL; // error message was already set.
}
char *ptr = SDL_strrchr(path, '/');
SDL_assert(ptr != NULL); // Should have been an absolute path.
ptr[1] = '\0'; // chop off filename, leave '/'.
ptr = (char *) SDL_realloc(path, ((size_t) (ptr - path)) + 2); // try to shrink this allocation down a little.
return ptr ? ptr : path; // return shrunk buffer if shrink worked out, unchanged original buffer if not.
}
char *SDL_SYS_GetExeName(void)
{
SDL_Unsupported(); // !!! FIXME: Move most of SDL_SYS_GetBasePath to a separate function and reuse it here.
return NULL;
char *path = GetExePath(); // look up full path of the current process's EXE file.
if (!path) {
return NULL; // error message was already set.
}
char *ptr = SDL_strrchr(path, '/');
const size_t slen = SDL_strlen(ptr); // counts null terminator because we're still sitting on path separator.
SDL_memmove(path, ptr + 1, slen); // move filename string to start of SDL_realloc'd region.
ptr = (char *) SDL_realloc(path, slen); // try to shrink this allocation down a little.
return ptr ? ptr : path; // return shrunk buffer if shrink worked out, unchanged original buffer if not.
}
char *SDL_SYS_GetPrefPath(const char *org, const char *app)

View file

@ -44,6 +44,7 @@ char *SDL_SYS_GetBasePath(void)
return NULL;
}
// !!! FIXME: if find_path promises an absolute path, can we dump this and just do SDL_strrchr(name, '/')?
BEntry entry(name, true);
BPath path;
status_t rc = entry.GetPath(&path); // (path) now has binary's path.
@ -66,8 +67,12 @@ char *SDL_SYS_GetBasePath(void)
char *SDL_SYS_GetExeName(void)
{
SDL_Unsupported(); // !!! FIXME: Move most of SDL_SYS_GetBasePath to a separate function and reuse it here.
return NULL;
char name[MAXPATHLEN];
if (find_path(B_APP_IMAGE_SYMBOL, B_FIND_PATH_IMAGE_PATH, NULL, name, sizeof(name)) != B_OK) {
return NULL;
}
char *ptr = SDL_strrchr(name, '/');
return SDL_strdup(ptr ? ptr + 1 : name);
}
char *SDL_SYS_GetPrefPath(const char *org, const char *app)

View file

@ -21,6 +21,7 @@
#include "SDL_internal.h"
extern void NGAGE_GetAppPath(char *path);
extern void NGAGE_GetExeName(char *path);
char *SDL_SYS_GetBasePath(void)
{
@ -32,8 +33,9 @@ char *SDL_SYS_GetBasePath(void)
char *SDL_SYS_GetExeName(void)
{
SDL_Unsupported(); // !!! FIXME: see code in NGAGE_GetAppPath
return NULL;
char exe_name[512];
NGAGE_GetExeName(exe_name);
return SDL_strdup(exe_name);
}
char *SDL_SYS_GetPrefPath(const char *org, const char *app)

View file

@ -63,6 +63,23 @@ void NGAGE_GetAppPath(char *path)
}
}
void NGAGE_GetExeName(char *path)
{
TBuf<512> aPath;
TFileName fullExePath = RProcess().FileName();
TParsePtrC parser(fullExePath);
aPath.Copy(parser.NameAndExt());
TBuf8<512> utf8Path; // Temporary buffer for UTF-8 data.
CnvUtfConverter::ConvertFromUnicodeToUtf8(utf8Path, aPath);
// Copy UTF-8 data to the provided char* buffer.
strncpy(path, (const char *)utf8Path.Ptr(), utf8Path.Length());
path[utf8Path.Length()] = '\0';
}
#ifdef __cplusplus
}
#endif

View file

@ -154,8 +154,25 @@ char *SDL_SYS_GetBasePath(void)
char *SDL_SYS_GetExeName(void)
{
SDL_Unsupported(); // !!! FIXME: see code in SDL_SYS_GetBasePath.
return NULL;
_kernel_swi_regs regs;
_kernel_oserror *error;
char *canon, *ptr, *retval;
error = _kernel_swi(OS_GetEnv, &regs, &regs);
if (error) {
return NULL;
}
canon = canonicalisePath((const char *)regs.r[0], "Run$Path");
if (!canon) {
return NULL;
}
// find filename.
ptr = SDL_strrchr(canon, '.');
retval = SDL_strdup(ptr ? ptr + 1 : canon);
SDL_free(canon);
return retval;
}
char *SDL_SYS_GetPrefPath(const char *org, const char *app)

View file

@ -94,6 +94,9 @@ int main(int argc, char *argv[])
char *curdir;
const char *base_path;
/* this will be SDL's best guess at the human-readable exe name (or bundle id, or whatever) by default. */
SDL_Log("Default app name: '%s'", SDL_GetAppMetadataProperty(SDL_PROP_APP_METADATA_NAME_STRING));
/* Initialize test framework */
state = SDLTest_CommonCreateState(argv, 0);
if (!state) {