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

android: Change how apps access their APK's "assets" directory.

Now they can explicitly access it with "assets://" filenames, and
SDL_GetBasePath() returns "assets://"

Fixes #15347.
Fixes #5044.
Ryan C. Gordon 2 недель назад
Родитель
Сommit
9672f5b68b

+ 18 - 0
docs/README-android.md

@@ -212,6 +212,24 @@ Any files you put in the "app/src/main/assets" directory of your project
 directory will get bundled into the application package and you can load
 them using the standard functions in SDL_iostream.h.
 
+As of SDL 3.6.0, SDL APIs, such as SDL_EnumerateDirectory() and
+SDL_IOFromFile(), understand paths that are prefixed with "assets://" and will
+look for paths exclusively inside the APK's "assets" directory. Since this is
+where app-specific data files are meant to be located, SDL_GetBasePath() on
+Android now returns "assets://" to make this work as expected across platforms.
+Note that SDL 3.2.28 to 3.6.0 returned "./" on Android, and before that,
+SDL_GetBasePath() always returned NULL on this platform.
+
+Obviously, paths prefixed with "assets://" are only useful to SDL; other APIs,
+like fopen(), will not understand them at all.
+
+As an alternate approach: SDL APIs on Android treat relative paths in a
+special way. It will look for files under the path returned by
+SDL_GetAndroidInternalStoragePath() first, and failing that, will attempt to
+look for them as if they were prefixed by "assets://", with the relative path
+starting in the base of the assets tree. Absolute paths never check against
+internal storage or assets.
+
 There are also a few Android specific functions that allow you to get other
 useful paths for saving and loading data:
 * SDL_GetAndroidInternalStoragePath()

+ 20 - 5
src/core/android/SDL_android.c

@@ -1901,9 +1901,16 @@ static APKNode *FindAPKChildNode(APKNode *parent, const char *child)
 
 static const APKNode *FindAPKNode(const char *constpath)
 {
+    //SDL_Log("FindAPKNode('%s') ...", constpath);
+    if (SDL_strncmp(constpath, "assets://", 9) == 0) {
+        constpath += 9;
+    }
+
     APKNode *parent = APKRootNode;
     if (!parent) {
         return NULL;
+    } else if (*constpath == '\0') {
+        return parent;
     }
 
     const size_t pathlen = SDL_strlen(constpath);
@@ -2362,12 +2369,20 @@ static void Internal_Android_Destroy_AssetManager(void)
 
 static const char *GetAssetPath(const char *path)
 {
-    if (path && path[0] == '.' && path[1] == '/') {
-        path += 2;
-        while (*path == '/') {
-            ++path;
-        }
+    if (!path) {
+        return NULL;
     }
+
+    if (path[0] == '.' && ((path[1] == '/') || (path[1] == '\0'))) {
+        path++;
+    } else if (SDL_strncmp(path, "assets://", 9) == 0) {
+        path += 9;
+    }
+
+    while (*path == '/') {
+        ++path;
+    }
+
     return path;
 }
 

+ 19 - 11
src/filesystem/SDL_filesystem.c

@@ -371,20 +371,28 @@ char **SDL_InternalGlobDirectory(const char *path, const char *pattern, SDL_Glob
         return NULL;
     }
 
-    // if path ends with any slash, chop them off, so we don't confuse the pattern matcher later.
     char *pathcpy = NULL;
     size_t pathlen = SDL_strlen(path);
-    if ((pathlen > 1) && ((path[pathlen-1] == '/') || (path[pathlen-1] == '\\'))) {
-        pathcpy = SDL_strdup(path);
-        if (!pathcpy) {
-            return NULL;
-        }
-        char *ptr = &pathcpy[pathlen-1];
-        while ((ptr > pathcpy) && ((*ptr == '/') || (*ptr == '\\'))) {
-            *(ptr--) = '\0';
-            --pathlen;
+
+    // if path ends with any slash, chop them off, so we don't confuse the pattern matcher later.
+    #ifdef SDL_PLATFORM_ANDROID
+    if (SDL_strcmp(path, "assets://") == 0) {  // don't chop '//' off this if we're looking for the root of the asset tree.
+        pathlen--;  // we'll add a 1 again later.
+    } else
+    #endif
+    {
+        if ((pathlen > 1) && ((path[pathlen-1] == '/') || (path[pathlen-1] == '\\'))) {
+            pathcpy = SDL_strdup(path);
+            if (!pathcpy) {
+                return NULL;
+            }
+            char *ptr = &pathcpy[pathlen-1];
+            while ((ptr > pathcpy) && ((*ptr == '/') || (*ptr == '\\'))) {
+                *(ptr--) = '\0';
+                --pathlen;
+            }
+            path = pathcpy;
         }
-        path = pathcpy;
     }
 
     if (!pattern) {

+ 1 - 1
src/filesystem/android/SDL_sysfilesystem.c

@@ -31,7 +31,7 @@
 
 char *SDL_SYS_GetBasePath(void)
 {
-    return SDL_strdup("./");
+    return SDL_strdup("assets://");
 }
 
 char *SDL_SYS_GetPrefPath(const char *org, const char *app)

+ 12 - 4
src/filesystem/posix/SDL_sysfsops.c

@@ -47,6 +47,13 @@ bool SDL_SYS_EnumerateDirectory(const char *path, SDL_EnumerateDirectoryCallback
 #if defined(SDL_PLATFORM_ANDROID) || defined(SDL_PLATFORM_IOS)
     if (*path != '/') {
         #ifdef SDL_PLATFORM_ANDROID
+        if (SDL_strncmp(path, "assets://", 9) == 0) {
+            char *pathwithsep = NULL;
+            SDL_asprintf(&pathwithsep, "%s%s", path, (path[SDL_strlen(path) - 1] != '/') ? "/" : "");
+            const bool retval = pathwithsep ? Android_JNI_EnumerateAssetDirectory(pathwithsep, cb, userdata) : false;
+            SDL_free(pathwithsep);
+            return retval;
+        }
         SDL_asprintf(&apath, "%s/%s", SDL_GetAndroidInternalStoragePath(), path);
         #elif defined(SDL_PLATFORM_IOS)
         char *base = SDL_GetPrefPath("", "");
@@ -89,14 +96,13 @@ bool SDL_SYS_EnumerateDirectory(const char *path, SDL_EnumerateDirectoryCallback
 
     DIR *dir = opendir(pathwithsep);
     if (!dir) {
-#ifdef SDL_PLATFORM_ANDROID  // Maybe it's an asset...?
+#ifdef SDL_PLATFORM_ANDROID  // Maybe it's an asset... that didn't use an "assets://" URL?
         const bool retval = Android_JNI_EnumerateAssetDirectory(pathwithsep + extralen, cb, userdata);
         SDL_free(pathwithsep);
         return retval;
-#else
+#endif
         SDL_free(pathwithsep);
         return SDL_SetError("Can't open directory: %s", strerror(errno));
-#endif
     }
 
     SDL_EnumerationResult result = SDL_ENUM_CONTINUE;
@@ -342,6 +348,8 @@ bool SDL_SYS_GetPathInfo(const char *path, SDL_PathInfo *info)
 #ifdef SDL_PLATFORM_ANDROID
     if (*path == '/') {
         rc = stat(path, &statbuf);
+    } else if (SDL_strncmp(path, "assets://", 9) == 0) {
+        return Android_JNI_GetAssetPathInfo(path, info);
     } else {
         char *apath = NULL;
         SDL_asprintf(&apath, "%s/%s", SDL_GetAndroidInternalStoragePath(), path);
@@ -351,7 +359,7 @@ bool SDL_SYS_GetPathInfo(const char *path, SDL_PathInfo *info)
         rc = stat(apath, &statbuf);
         SDL_free(apath);
     }
-    if (rc < 0) {
+    if (rc < 0) {  // Maybe it's an asset... that didn't use an "assets://" URL?
         return Android_JNI_GetAssetPathInfo(path, info);
     }
 #elif defined(SDL_PLATFORM_IOS)

+ 2 - 3
src/io/SDL_iostream.c

@@ -1021,7 +1021,7 @@ SDL_IOStream *SDL_IOFromFile(const char *file, const char *mode)
         }
 
         return SDL_IOFromFP(fp, true);
-    } else {
+    } else if (SDL_strncmp(file, "assets://", 9) != 0) {
         // Try opening it from internal storage if it's a relative path
         char *path = NULL;
         SDL_asprintf(&path, "%s/%s", SDL_GetAndroidInternalStoragePath(), file);
@@ -1040,8 +1040,7 @@ SDL_IOStream *SDL_IOFromFile(const char *file, const char *mode)
     }
 #endif // HAVE_STDIO_H
 
-    // Try to open the file from the asset system
-
+    // Try to open the file from the asset system?
     void *iodata = NULL;
     if (!Android_JNI_FileOpen(&iodata, file, mode)) {
         return NULL;