diff --git a/src/SDL_internal.h b/src/SDL_internal.h index b25e917917..d86909e1ff 100644 --- a/src/SDL_internal.h +++ b/src/SDL_internal.h @@ -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 diff --git a/src/core/android/SDL_android.c b/src/core/android/SDL_android.c index aada458481..02462c1738 100644 --- a/src/core/android/SDL_android.c +++ b/src/core/android/SDL_android.c @@ -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(); diff --git a/src/core/android/SDL_android.h b/src/core/android/SDL_android.h index ec9d1dc863..b4b7b93013 100644 --- a/src/core/android/SDL_android.h +++ b/src/core/android/SDL_android.h @@ -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, diff --git a/src/filesystem/android/SDL_sysfilesystem.c b/src/filesystem/android/SDL_sysfilesystem.c index a6d0708841..b1a357a04e 100644 --- a/src/filesystem/android/SDL_sysfilesystem.c +++ b/src/filesystem/android/SDL_sysfilesystem.c @@ -26,8 +26,7 @@ // System dependent filesystem routines #include "../SDL_sysfilesystem.h" - -#include +#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) diff --git a/src/filesystem/cocoa/SDL_sysfilesystem.m b/src/filesystem/cocoa/SDL_sysfilesystem.m index 2995df4e5d..4cd9a563c7 100644 --- a/src/filesystem/cocoa/SDL_sysfilesystem.m +++ b/src/filesystem/cocoa/SDL_sysfilesystem.m @@ -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) diff --git a/src/filesystem/dos/SDL_sysfilesystem.c b/src/filesystem/dos/SDL_sysfilesystem.c index 244430c111..78aa8f8d9e 100644 --- a/src/filesystem/dos/SDL_sysfilesystem.c +++ b/src/filesystem/dos/SDL_sysfilesystem.c @@ -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) diff --git a/src/filesystem/haiku/SDL_sysfilesystem.cc b/src/filesystem/haiku/SDL_sysfilesystem.cc index 3d00c565b5..b4bef1acaf 100644 --- a/src/filesystem/haiku/SDL_sysfilesystem.cc +++ b/src/filesystem/haiku/SDL_sysfilesystem.cc @@ -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) diff --git a/src/filesystem/ngage/SDL_sysfilesystem.c b/src/filesystem/ngage/SDL_sysfilesystem.c index 73b8ddc0c7..8ce3ee1512 100644 --- a/src/filesystem/ngage/SDL_sysfilesystem.c +++ b/src/filesystem/ngage/SDL_sysfilesystem.c @@ -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) diff --git a/src/filesystem/ngage/SDL_sysfilesystem.cpp b/src/filesystem/ngage/SDL_sysfilesystem.cpp index 622c82e465..415fe55c4d 100644 --- a/src/filesystem/ngage/SDL_sysfilesystem.cpp +++ b/src/filesystem/ngage/SDL_sysfilesystem.cpp @@ -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 diff --git a/src/filesystem/riscos/SDL_sysfilesystem.c b/src/filesystem/riscos/SDL_sysfilesystem.c index 2a5ac9263f..b616864122 100644 --- a/src/filesystem/riscos/SDL_sysfilesystem.c +++ b/src/filesystem/riscos/SDL_sysfilesystem.c @@ -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, ®s, ®s); + 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) diff --git a/test/testfilesystem.c b/test/testfilesystem.c index 87ba39501f..3e522c3e60 100644 --- a/test/testfilesystem.c +++ b/test/testfilesystem.c @@ -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) {