Kaynağa Gözat

android: If various POSIX fsops functions fail, try using AAssetManager.

This specifically affects SDL_EnumerateDirectory and SDL_GetPathInfo. Android
assets are read-only, so no need to do this for things like
SDL_CreateDirectory, etc, and the POSIX SDL_CopyFile() uses SDL_IOStream
behind the scenes, which already supports Android assets.

Fixes #13050.
Ryan C. Gordon 2 ay önce
ebeveyn
işleme
515433aa8a

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

@@ -1853,6 +1853,62 @@ bool Android_JNI_FileClose(void *userdata)
     return true;
 }
 
+bool Android_JNI_EnumerateAssetDirectory(const char *path, SDL_EnumerateDirectoryCallback cb, void *userdata)
+{
+    SDL_assert((*path == '\0') || (path[SDL_strlen(path) - 1] == '/'));  // SDL_SYS_EnumerateDirectory() should have verified this.
+
+    if (!asset_manager) {
+        Internal_Android_Create_AssetManager();
+        if (!asset_manager) {
+            return SDL_SetError("Couldn't create asset manager");
+        }
+    }
+
+    AAssetDir *adir = AAssetManager_openDir(asset_manager, path);
+    if (!adir) {
+        return SDL_SetError("AAssetManager_openDir failed");
+    }
+
+    SDL_EnumerationResult result = SDL_ENUM_CONTINUE;
+    const char *ent;
+    while ((result == SDL_ENUM_CONTINUE) && ((ent = AAssetDir_getNextFileName(adir)) != NULL)) {
+        result = cb(userdata, path, ent);
+    }
+
+    AAssetDir_close(adir);
+
+    return (result != SDL_ENUM_FAILURE);
+}
+
+bool Android_JNI_GetAssetPathInfo(const char *path, SDL_PathInfo *info)
+{
+    if (!asset_manager) {
+        Internal_Android_Create_AssetManager();
+        if (!asset_manager) {
+            return SDL_SetError("Couldn't create asset manager");
+        }
+    }
+
+    // this is sort of messy, but there isn't a stat()-like interface to the Assets.
+    AAssetDir *adir = AAssetManager_openDir(asset_manager, path);
+    if (adir) {  // it's a directory!
+        AAssetDir_close(adir);
+        info->type = SDL_PATHTYPE_DIRECTORY;
+        info->size = 0;
+        return true;
+    }
+
+    AAsset *aasset = AAssetManager_open(asset_manager, path, AASSET_MODE_UNKNOWN);
+    if (aasset) {  // it's a file!
+        info->type = SDL_PATHTYPE_FILE;
+        info->size = (Uint64) AAsset_getLength64(aasset);
+        AAsset_close(aasset);
+        return true;
+    }
+
+    return SDL_SetError("Couldn't open asset '%s'", path);
+}
+
 bool Android_JNI_SetClipboardText(const char *text)
 {
     JNIEnv *env = Android_JNI_GetEnv();

+ 2 - 0
src/core/android/SDL_android.h

@@ -88,6 +88,8 @@ Sint64 Android_JNI_FileSeek(void *userdata, Sint64 offset, SDL_IOWhence whence);
 size_t Android_JNI_FileRead(void *userdata, void *buffer, size_t size, SDL_IOStatus *status);
 size_t Android_JNI_FileWrite(void *userdata, const void *buffer, size_t size, SDL_IOStatus *status);
 bool Android_JNI_FileClose(void *userdata);
+bool Android_JNI_EnumerateAssetDirectory(const char *path, SDL_EnumerateDirectoryCallback cb, void *userdata);
+bool Android_JNI_GetAssetPathInfo(const char *path, SDL_PathInfo *info);
 
 // Environment support
 void Android_JNI_GetManifestEnvironmentVariables(void);

+ 15 - 1
src/filesystem/posix/SDL_sysfsops.c

@@ -35,6 +35,10 @@
 #include <sys/stat.h>
 #include <unistd.h>
 
+#ifdef SDL_PLATFORM_ANDROID
+#include "../../core/android/SDL_android.h"
+#endif
+
 bool SDL_SYS_EnumerateDirectory(const char *path, SDL_EnumerateDirectoryCallback cb, void *userdata)
 {
     char *pathwithsep = NULL;
@@ -51,8 +55,14 @@ bool SDL_SYS_EnumerateDirectory(const char *path, SDL_EnumerateDirectoryCallback
 
     DIR *dir = opendir(pathwithsep);
     if (!dir) {
+        #ifdef SDL_PLATFORM_ANDROID  // Maybe it's an asset...?
+        const bool retval = Android_JNI_EnumerateAssetDirectory(pathwithsep, cb, userdata);
+        SDL_free(pathwithsep);
+        return retval;
+        #else
         SDL_free(pathwithsep);
         return SDL_SetError("Can't open directory: %s", strerror(errno));
+        #endif
     }
 
     // make sure there's a path separator at the end now for the actual callback.
@@ -173,7 +183,11 @@ bool SDL_SYS_GetPathInfo(const char *path, SDL_PathInfo *info)
     struct stat statbuf;
     const int rc = stat(path, &statbuf);
     if (rc < 0) {
+        #ifdef SDL_PLATFORM_ANDROID  // Maybe it's an asset...?
+        return Android_JNI_GetAssetPathInfo(path, info);
+        #else
         return SDL_SetError("Can't stat: %s", strerror(errno));
+        #endif
     } else if (S_ISREG(statbuf.st_mode)) {
         info->type = SDL_PATHTYPE_FILE;
         info->size = (Uint64) statbuf.st_size;
@@ -203,7 +217,7 @@ bool SDL_SYS_GetPathInfo(const char *path, SDL_PathInfo *info)
     return true;
 }
 
-// Note that this isn't actually part of filesystem, not fsops, but everything that uses posix fsops uses this implementation, even with separate filesystem code.
+// Note that this is actually part of filesystem, not fsops, but everything that uses posix fsops uses this implementation, even with separate filesystem code.
 char *SDL_SYS_GetCurrentDirectory(void)
 {
     size_t buflen = 64;