Browse Source

android: Parsing the APK's central dir is separate from the AAssetManager.

Only parse when necessary, which it isn't when opening a file, so this is now
separated from creating the cached AAssetManager object.
Ryan C. Gordon 1 week ago
parent
commit
9235ac4efd
1 changed files with 46 additions and 45 deletions
  1. 46 45
      src/core/android/SDL_android.c

+ 46 - 45
src/core/android/SDL_android.c

@@ -2236,37 +2236,58 @@ static bool CreateAPKNodes(const char *path)
 {
     SDL_Log("ANDROID: Parsing APK file '%s' ...", path);
 
-    SDL_PathInfo apkinfo;
-    SDL_assert(path[0] == '/');  // So SDL_GetPathInfo goes through the `stat` path and doesn't try to dig into the APK.
-    if (!SDL_GetPathInfo(path, &apkinfo)) {
-        SDL_zero(apkinfo);  // we just want the file times here, so oh well.
+    SDL_IOStream *io = SDL_IOFromFile(path, "rb");
+    if (!io) {
+        SDL_Log("ANDROID: Can't open APK '%s' for reading (%s). Filesystem enumeration will fail.", path, SDL_GetError());
+    } else {
+        ProcessZip(io, APKRootNode);
+        SDL_CloseIO(io);
     }
+    return true;
+}
 
-    if (!APKRootNode) {
+static bool PrepareAPK(void)
+{
+    // the assetmanager isn't useful for enumerating directories, so parse the APK directly for that info upfront.
+    bool retval = (APKRootNode != NULL);
+    if (!retval) {
+        // allocate this upfront, so if there's a failure, we'll not try again and just have an empty file tree.
         APKRootNode = (APKNode *) SDL_calloc(1, sizeof (*APKRootNode));
         if (!APKRootNode) {
-            SDL_Log("ANDROID: Can't open APK (out of memory). Filesystem enumeration will fail.");
-            return false;
+            return false;  // oh well.
         }
         APKRootNode->info.type = SDL_PATHTYPE_DIRECTORY;
-        APKRootNode->info.create_time = apkinfo.create_time;
-        APKRootNode->info.modify_time = apkinfo.modify_time;
-        APKRootNode->info.access_time = apkinfo.access_time;
-    }
 
-    SDL_IOStream *io = SDL_IOFromFile(path, "rb");
-    if (!io) {
-        SDL_Log("ANDROID: Can't open APK '%s' for reading (%s). Filesystem enumeration will fail.", path, SDL_GetError());
-    } else {
-        ProcessZip(io, APKRootNode);
-        SDL_CloseIO(io);
+        struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(SDL_FUNCTION);
+        JNIEnv *env = Android_JNI_GetEnv();
+        if (LocalReferenceHolder_Init(&refs, env)) {
+            jobject context = (*env)->CallStaticObjectMethod(env, mActivityClass, midGetContext);
+            jmethodID mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, context), "getPackageResourcePath", "()Ljava/lang/String;");
+            jstring jstr = (jstring)(*env)->CallObjectMethod(env, context, mid);
+            jthrowable jexception = (*env)->ExceptionOccurred(env);
+            if (jexception != NULL) {
+                (*env)->ExceptionClear(env);  // oh well
+            } else {
+                const char *apkpath = (*env)->GetStringUTFChars(env, jstr, NULL);
+                SDL_PathInfo apkinfo;
+                SDL_assert(apkpath[0] == '/');  // So SDL_GetPathInfo goes through the `stat` path and doesn't try to dig into the APK.
+                if (SDL_GetPathInfo(apkpath, &apkinfo)) {   // we just want the file times here, so oh well if it fails.
+                    APKRootNode->info.create_time = apkinfo.create_time;
+                    APKRootNode->info.modify_time = apkinfo.modify_time;
+                    APKRootNode->info.access_time = apkinfo.access_time;
+                }
+                CreateAPKNodes(apkpath);
+                (*env)->ReleaseStringUTFChars(env, jstr, apkpath);
+                retval = true;
+            }
+        }
+        LocalReferenceHolder_Cleanup(&refs);
     }
-    return true;  // even on failure, leave an empty root node so we have zero files and don't try to load the .zip again.
+    return retval;  // even on failure, leave an empty root node so we have zero files and don't try to load the .zip again.
 }
 
 static void Internal_Android_Create_AssetManager(void)
 {
-
     struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(SDL_FUNCTION);
     JNIEnv *env = Android_JNI_GetEnv();
     jmethodID mid;
@@ -2300,21 +2321,6 @@ static void Internal_Android_Create_AssetManager(void)
         Android_JNI_ExceptionOccurred(true);
     }
 
-    // the assetmanager isn't useful for enumerating directories, so parse the APK directly for that info upfront.
-    jthrowable jexception = 0;
-    jstring jstr = 0;
-
-    mid = (*env)->GetMethodID(env, (*env)->GetObjectClass(env, context), "getPackageResourcePath", "()Ljava/lang/String;");
-    jstr = (jstring)(*env)->CallObjectMethod(env, context, mid);
-    jexception = (*env)->ExceptionOccurred(env);
-    if (jexception != NULL) {
-        (*env)->ExceptionClear(env);  // oh well
-    } else {
-        const char *apkpath = (*env)->GetStringUTFChars(env, jstr, NULL);
-        CreateAPKNodes(apkpath);
-        (*env)->ReleaseStringUTFChars(env, jstr, apkpath);
-    }
-
     LocalReferenceHolder_Cleanup(&refs);
 }
 
@@ -2409,17 +2415,13 @@ bool Android_JNI_EnumerateAssetDirectory(const char *path, SDL_EnumerateDirector
 {
     SDL_assert(path != NULL);
 
-    if (!asset_manager) {
-        Internal_Android_Create_AssetManager();
-        if (!asset_manager) {
-            return SDL_SetError("Couldn't create asset manager");
-        }
+    if (!PrepareAPK()) {
+        return false;
     }
 
     SDL_EnumerationResult result = SDL_ENUM_CONTINUE;
     const char *asset_path = GetAssetPath(path);
 
-    // check our tree we built from the APK first.
     const APKNode *apknode = FindAPKNode(asset_path);
     if (!apknode) {
         return SDL_SetError("No such directory");
@@ -2436,11 +2438,10 @@ bool Android_JNI_EnumerateAssetDirectory(const char *path, SDL_EnumerateDirector
 
 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");
-        }
+    SDL_assert(path != NULL);
+
+    if (!PrepareAPK()) {
+        return false;
     }
 
     path = GetAssetPath(path);