Просмотр исходного кода

filesystem: Implement SDL_GetExeName() for all platforms.

(cherry picked from commit d7ba3efe6bd80c5c49911798649b698a96999e7f)
Ryan C. Gordon 11 часов назад
Родитель
Сommit
b8601dc8a7

+ 3 - 1
src/SDL_internal.h

@@ -266,7 +266,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

+ 37 - 0
src/core/android/SDL_android.c

@@ -2848,6 +2848,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();

+ 2 - 0
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_OpenFileDialog(SDL_DialogFileCallback callback, void *userdata,
     const SDL_DialogFileFilter *filters, int nfilters, bool forwrite,

+ 2 - 3
src/filesystem/android/SDL_sysfilesystem.c

@@ -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)

+ 19 - 3
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)

+ 7 - 2
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)

+ 4 - 2
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)

+ 17 - 0
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

+ 19 - 2
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, &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)

+ 3 - 0
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) {