Skip to content

android: Parse the APK directory tree, replace AAssetManager enum/getpathinfo.#15441

Open
icculus wants to merge 2 commits intolibsdl-org:mainfrom
icculus:sdl3-android-apk-parsing
Open

android: Parse the APK directory tree, replace AAssetManager enum/getpathinfo.#15441
icculus wants to merge 2 commits intolibsdl-org:mainfrom
icculus:sdl3-android-apk-parsing

Conversation

@icculus
Copy link
Copy Markdown
Collaborator

@icculus icculus commented Apr 23, 2026

  • I confirm that I am the author of this code and release it to the SDL project under the Zlib license. This contribution does not contain code from other sources, including code generated by a Large Language Model ("AI").

The AAssetManager won't supply this information to us, but it's just a zip file, so we can read its central directory ourselves. We can still use AAssetManager for file i/o to the APK's assets, so we don't have to deal with compression and other zipfile features, but now we can get a correct view of the filesystem for SDL_EnumerateDirectory() and SDL_GetPathInfo().

This was tested on an Android phone by stuffing enum_callback from testfilesystem.c into testsprite.c and seeing what it spits out when I added a bunch of files to the app's asset tree when building. As SDL currently operates, it can correctly enumerate subdirectories of the "assets" folder, but not the root of it and not if something matching is in the internal storage (see #15347 for discussion on that. We can build on top of this to solve that issue, but I left SDL behaving as before, for now).

The ZipDosTimeToSDLTime() function was lifted from PhysicsFS's zip_dos_time_to_physfs_time(), but that was entirely written by me and released under the same license as SDL. All the rest is from scratch.

Fixes #15220.
Reference Issue #15347.

@icculus icculus added this to the 3.4.6 milestone Apr 23, 2026
@icculus icculus self-assigned this Apr 23, 2026
@1bsyl
Copy link
Copy Markdown
Contributor

1bsyl commented Apr 24, 2026

maybe the part that initiates the call to CreateAPKNodes() could be moved (and executed the first time) in JNI_GetAssetPathInfo() so that apps scan the APK only if they need it.

@Sackzement
Copy link
Copy Markdown
Contributor

Sackzement commented Apr 24, 2026

SDL_GetPathInfo("assets")
returns a pathinfo with type SDL_PATHTYPE_FILE

I assume because it's a root node?

That was because I had a file "assets_file1.txt" in the assets dir

@icculus
Copy link
Copy Markdown
Collaborator Author

icculus commented Apr 24, 2026

maybe the part that initiates the call to CreateAPKNodes() could be moved (and executed the first time) in JNI_GetAssetPathInfo() so that apps scan the APK only if they need it.

I think it only does it the first time it's needed already, but I'll double-check.

@icculus
Copy link
Copy Markdown
Collaborator Author

icculus commented Apr 24, 2026

That was because I had a file "assets_file1.txt" in the assets dir

That sounds like a bug, I'll check it.

@icculus icculus force-pushed the sdl3-android-apk-parsing branch from 1334a10 to b1c89f6 Compare April 24, 2026 14:22
@Sackzement
Copy link
Copy Markdown
Contributor

Sackzement commented Apr 24, 2026

That was because I had a file "assets_file1.txt" in the assets dir

That sounds like a bug, I'll check it.

Something like this could fix it:

diff --git a/src/core/android/SDL_android.c b/src/core/android/SDL_android.c
index d63572e..9e8c101 100644
--- a/src/core/android/SDL_android.c
+++ b/src/core/android/SDL_android.c
@@ -1883,10 +1883,12 @@ static const APKNode *FindAPKNode(const char *path)
             break;
         }
         const char *ptr = SDL_strchr(path, '/');
-        const size_t slen = ptr ? ((size_t) (ptr - path)) : ((size_t) SDL_strlen(path) - 1);
+        const size_t slen = ptr ? ((size_t) (ptr - path)) : SDL_strlen(path);
         const APKNode *node;
         for (node = parent->children; node; node = node->next_sibling) {
-            if (SDL_strncmp(path, node->name, slen) == 0) {
+            size_t nlen = SDL_strlen(node->name);
+            size_t max_len = SDL_max(slen, nlen);
+            if (SDL_strncmp(path, node->name, max_len) == 0) {
                 break;
             }
         }
@@ -1896,7 +1898,7 @@ static const APKNode *FindAPKNode(const char *path)
         }
 
         parent = node;
-        path += slen + 1;
+        path += slen;
     }
 
     return parent;

Edit:
I think SDL_strncmp() isn't even needed here. Maybe just SDL_strcmp() is good enough.
It is actually needed if the path still has slashes. It is not needed if it doesn't. The above patch is ok I think.

@icculus
Copy link
Copy Markdown
Collaborator Author

icculus commented Apr 24, 2026

I think it only does it the first time it's needed already, but I'll double-check.

If there's a call to SDL_GetPathInfo or SDL_EnumerateDirectory that needs the assets directory, it'll pull the APK info in on the first request, but it doesn't until then, including if you use those APIs and don't need the assets dir.

Specifically, we do it in Internal_Android_Create_AssetManager, which Android_JNI_GetAssetPathInfo, etc, call if needed.

icculus added 2 commits April 24, 2026 14:51
…pathinfo.

The AAssetManager won't supply this information to us, but it's just a zip
file, so we can read its central directory ourselves. We can still use
AAssetManager for file i/o to the APK's assets, so we don't have to deal with
compression and other zipfile features.

Fixes libsdl-org#15220.
Reference Issue libsdl-org#15347.
@icculus icculus force-pushed the sdl3-android-apk-parsing branch from b1c89f6 to 0d5c870 Compare April 24, 2026 18:51
@icculus
Copy link
Copy Markdown
Collaborator Author

icculus commented Apr 24, 2026

Something like this could fix it:

Just pushed a fix that was a little like this, thanks!

@Sackzement
Copy link
Copy Markdown
Contributor

A minor issue: An existing file with a trailing slash:
SDL_GetPathInfo("file.txt/")
On PC: Fails with: Can't stat: Not a directory
On Android: Returns a valid path info with type SDL_PATHTYPE_FILE.

@1bsyl
Copy link
Copy Markdown
Contributor

1bsyl commented Apr 24, 2026

If there's a call to SDL_GetPathInfo or SDL_EnumerateDirectory that needs the assets directory, it'll pull the APK info in on the first request, but it doesn't until then, including if you use those APIs and don't need the assets dir.

It's called only once, yes, that's ok.

But I mean't:
Internal_Android_Create_AssetManager() is also called in Android_JNI_FileOpen(). So the first time we open a file, it will pull the APK info. maybe this shoud be done only for to SDL_GetPathInfo and SDL_EnumerateDirectory

@icculus
Copy link
Copy Markdown
Collaborator Author

icculus commented Apr 24, 2026

Oh, if that's happening, that's a good point. (But the better question is why we are creating the assetmanager when we don't need it...I'll take a look at that.)

@icculus
Copy link
Copy Markdown
Collaborator Author

icculus commented Apr 24, 2026

A minor issue: An existing file with a trailing slash:

String handling in C, still awesome. :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

SDL_EnumerateDirectory doesn't provide folders located inside the apk to the callback function on Android

3 participants