Procházet zdrojové kódy

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 před 2 týdny
rodič
revize
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
 directory will get bundled into the application package and you can load
 them using the standard functions in SDL_iostream.h.
 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
 There are also a few Android specific functions that allow you to get other
 useful paths for saving and loading data:
 useful paths for saving and loading data:
 * SDL_GetAndroidInternalStoragePath()
 * 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)
 static const APKNode *FindAPKNode(const char *constpath)
 {
 {
+    //SDL_Log("FindAPKNode('%s') ...", constpath);
+    if (SDL_strncmp(constpath, "assets://", 9) == 0) {
+        constpath += 9;
+    }
+
     APKNode *parent = APKRootNode;
     APKNode *parent = APKRootNode;
     if (!parent) {
     if (!parent) {
         return NULL;
         return NULL;
+    } else if (*constpath == '\0') {
+        return parent;
     }
     }
 
 
     const size_t pathlen = SDL_strlen(constpath);
     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)
 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;
     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;
         return NULL;
     }
     }
 
 
-    // if path ends with any slash, chop them off, so we don't confuse the pattern matcher later.
     char *pathcpy = NULL;
     char *pathcpy = NULL;
     size_t pathlen = SDL_strlen(path);
     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) {
     if (!pattern) {

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

@@ -31,7 +31,7 @@
 
 
 char *SDL_SYS_GetBasePath(void)
 char *SDL_SYS_GetBasePath(void)
 {
 {
-    return SDL_strdup("./");
+    return SDL_strdup("assets://");
 }
 }
 
 
 char *SDL_SYS_GetPrefPath(const char *org, const char *app)
 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 defined(SDL_PLATFORM_ANDROID) || defined(SDL_PLATFORM_IOS)
     if (*path != '/') {
     if (*path != '/') {
         #ifdef SDL_PLATFORM_ANDROID
         #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);
         SDL_asprintf(&apath, "%s/%s", SDL_GetAndroidInternalStoragePath(), path);
         #elif defined(SDL_PLATFORM_IOS)
         #elif defined(SDL_PLATFORM_IOS)
         char *base = SDL_GetPrefPath("", "");
         char *base = SDL_GetPrefPath("", "");
@@ -89,14 +96,13 @@ bool SDL_SYS_EnumerateDirectory(const char *path, SDL_EnumerateDirectoryCallback
 
 
     DIR *dir = opendir(pathwithsep);
     DIR *dir = opendir(pathwithsep);
     if (!dir) {
     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);
         const bool retval = Android_JNI_EnumerateAssetDirectory(pathwithsep + extralen, cb, userdata);
         SDL_free(pathwithsep);
         SDL_free(pathwithsep);
         return retval;
         return retval;
-#else
+#endif
         SDL_free(pathwithsep);
         SDL_free(pathwithsep);
         return SDL_SetError("Can't open directory: %s", strerror(errno));
         return SDL_SetError("Can't open directory: %s", strerror(errno));
-#endif
     }
     }
 
 
     SDL_EnumerationResult result = SDL_ENUM_CONTINUE;
     SDL_EnumerationResult result = SDL_ENUM_CONTINUE;
@@ -342,6 +348,8 @@ bool SDL_SYS_GetPathInfo(const char *path, SDL_PathInfo *info)
 #ifdef SDL_PLATFORM_ANDROID
 #ifdef SDL_PLATFORM_ANDROID
     if (*path == '/') {
     if (*path == '/') {
         rc = stat(path, &statbuf);
         rc = stat(path, &statbuf);
+    } else if (SDL_strncmp(path, "assets://", 9) == 0) {
+        return Android_JNI_GetAssetPathInfo(path, info);
     } else {
     } else {
         char *apath = NULL;
         char *apath = NULL;
         SDL_asprintf(&apath, "%s/%s", SDL_GetAndroidInternalStoragePath(), path);
         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);
         rc = stat(apath, &statbuf);
         SDL_free(apath);
         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);
         return Android_JNI_GetAssetPathInfo(path, info);
     }
     }
 #elif defined(SDL_PLATFORM_IOS)
 #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);
         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
         // Try opening it from internal storage if it's a relative path
         char *path = NULL;
         char *path = NULL;
         SDL_asprintf(&path, "%s/%s", SDL_GetAndroidInternalStoragePath(), file);
         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
 #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;
     void *iodata = NULL;
     if (!Android_JNI_FileOpen(&iodata, file, mode)) {
     if (!Android_JNI_FileOpen(&iodata, file, mode)) {
         return NULL;
         return NULL;