mirror of
https://github.com/love2d/love.git
synced 2026-08-12 08:30:56 +02:00
Use GameActivity.buildFileLookup via JNI to build file tree.
Turns out AAssetDir_getNextFilename() intentionally excludes directories, thus the custom archive backend only sees the top-level files.
This commit is contained in:
+112
-55
@@ -333,24 +333,59 @@ void showRecordingPermissionMissingDialog()
|
||||
env->DeleteLocalRef(activity);
|
||||
}
|
||||
|
||||
/* A container for AssetManager Java object */
|
||||
class AssetManagerObject
|
||||
{
|
||||
public:
|
||||
AssetManagerObject()
|
||||
{
|
||||
JNIEnv *env = (JNIEnv *) SDL_AndroidGetJNIEnv();
|
||||
jobject am = getLocalAssetManager(env);
|
||||
|
||||
assetManager = env->NewGlobalRef(am);
|
||||
env->DeleteLocalRef(am);
|
||||
}
|
||||
|
||||
~AssetManagerObject()
|
||||
{
|
||||
JNIEnv *env = (JNIEnv *) SDL_AndroidGetJNIEnv();
|
||||
env->DeleteGlobalRef(assetManager);
|
||||
}
|
||||
|
||||
static jobject getLocalAssetManager(JNIEnv *env) {
|
||||
jobject self = (jobject) SDL_AndroidGetActivity();
|
||||
jclass activity = env->GetObjectClass(self);
|
||||
jmethodID method = env->GetMethodID(activity, "getAssets", "()Landroid/content/res/AssetManager;");
|
||||
jobject am = env->CallObjectMethod(self, method);
|
||||
|
||||
env->DeleteLocalRef(self);
|
||||
env->DeleteLocalRef(activity);
|
||||
return am;
|
||||
}
|
||||
|
||||
explicit operator jobject()
|
||||
{
|
||||
return assetManager;
|
||||
};
|
||||
private:
|
||||
jobject assetManager;
|
||||
};
|
||||
|
||||
/*
|
||||
* Helper functions to aid new fusing method
|
||||
*/
|
||||
|
||||
// This returns *global* reference, no need to free it.
|
||||
static jobject getJavaAssetManager()
|
||||
{
|
||||
static AssetManagerObject assetManager;
|
||||
return (jobject) assetManager;
|
||||
}
|
||||
|
||||
static AAssetManager *getAssetManager()
|
||||
{
|
||||
JNIEnv *env = (JNIEnv*) SDL_AndroidGetJNIEnv();
|
||||
jobject self = (jobject) SDL_AndroidGetActivity();
|
||||
jclass activity = env->GetObjectClass(self);
|
||||
jmethodID method = env->GetMethodID(activity, "getAssetManager", "()Landroid/content/res/AssetManager;");
|
||||
|
||||
jobject assetManager = env->CallObjectMethod(self, method);
|
||||
AAssetManager *assetManagerObject = AAssetManager_fromJava(env, assetManager);
|
||||
|
||||
env->DeleteLocalRef(assetManager);
|
||||
env->DeleteLocalRef(activity);
|
||||
env->DeleteLocalRef(self);
|
||||
|
||||
return assetManagerObject;
|
||||
return AAssetManager_fromJava(env, (jobject) getJavaAssetManager());
|
||||
}
|
||||
|
||||
namespace aasset
|
||||
@@ -368,35 +403,6 @@ struct AssetInfo
|
||||
|
||||
static std::unordered_map<std::string, PHYSFS_FileType> fileTree;
|
||||
|
||||
// Workaround AAsset can't detect whetever something is a directory or doesn't exist
|
||||
static void buildFileLookup(
|
||||
AAssetManager *assetManager,
|
||||
std::unordered_map<std::string, PHYSFS_FileType> &out,
|
||||
const std::string &path = ""
|
||||
)
|
||||
{
|
||||
if (!path.empty())
|
||||
{
|
||||
AAsset *test = AAssetManager_open(assetManager, path.c_str(), AASSET_MODE_STREAMING);
|
||||
if (test)
|
||||
{
|
||||
AAsset_close(test);
|
||||
out[path] = PHYSFS_FILETYPE_REGULAR;
|
||||
return;
|
||||
}
|
||||
|
||||
out[path] = PHYSFS_FILETYPE_DIRECTORY;
|
||||
}
|
||||
|
||||
AAssetDir *dir = AAssetManager_openDir(assetManager, path.c_str());
|
||||
const char *file;
|
||||
|
||||
while ((file = AAssetDir_getNextFileName(dir)) != nullptr)
|
||||
buildFileLookup(assetManager, out, file);
|
||||
|
||||
AAssetDir_close(dir);
|
||||
}
|
||||
|
||||
PHYSFS_sint64 read(PHYSFS_Io *io, void *buf, PHYSFS_uint64 len)
|
||||
{
|
||||
AAsset *asset = ((AssetInfo *) io->opaque)->asset;
|
||||
@@ -505,7 +511,31 @@ void *openArchive(PHYSFS_Io *io, const char *name, int forWrite, int *claimed)
|
||||
AAssetManager *assetManager = getAssetManager();
|
||||
|
||||
if (io::fileTree.empty())
|
||||
io::buildFileLookup(assetManager, io::fileTree);
|
||||
{
|
||||
// AAssetDir_getNextFileName intentionally excludes directories, so
|
||||
// we have to use JNI that calls AssetManager.list() recursively.
|
||||
JNIEnv *env = (JNIEnv *) SDL_AndroidGetJNIEnv();
|
||||
jobject activity = (jobject) SDL_AndroidGetActivity();
|
||||
jclass clazz = env->GetObjectClass(activity);
|
||||
|
||||
jmethodID method = env->GetMethodID(clazz, "buildFileTree", "()[Ljava/lang/String;");
|
||||
jobjectArray list = (jobjectArray) env->CallObjectMethod(activity, method);
|
||||
|
||||
for (jsize i = 0; i < env->GetArrayLength(list); i++)
|
||||
{
|
||||
jstring jstr = (jstring) env->GetObjectArrayElement(list, i);
|
||||
const char *str = env->GetStringUTFChars(jstr, nullptr);
|
||||
|
||||
io::fileTree[str + 1] = str[0] == 'd' ? PHYSFS_FILETYPE_DIRECTORY : PHYSFS_FILETYPE_REGULAR;
|
||||
|
||||
env->ReleaseStringUTFChars(jstr, str);
|
||||
env->DeleteLocalRef(jstr);
|
||||
}
|
||||
|
||||
env->DeleteLocalRef(list);
|
||||
env->DeleteLocalRef(clazz);
|
||||
env->DeleteLocalRef(activity);
|
||||
}
|
||||
|
||||
return assetManager;
|
||||
}
|
||||
@@ -518,33 +548,60 @@ PHYSFS_EnumerateCallbackResult enumerate(
|
||||
void *callbackdata
|
||||
)
|
||||
{
|
||||
using FileTreeIterator = std::unordered_map<std::string, PHYSFS_FileType>::iterator;
|
||||
LOVE_UNUSED(opaque);
|
||||
|
||||
const char *path = dirname;
|
||||
if (path == nullptr || (path[0] == '/' && path[1] == 0))
|
||||
path = "";
|
||||
|
||||
AAssetManager *assetManager = (AAssetManager *) opaque;
|
||||
AAssetDir *dir = AAssetManager_openDir(assetManager, path);
|
||||
|
||||
if (dir == nullptr)
|
||||
if (path[0] != 0)
|
||||
{
|
||||
PHYSFS_setErrorCode(PHYSFS_ERR_NOT_FOUND);
|
||||
return PHYSFS_ENUM_ERROR;
|
||||
FileTreeIterator result = io::fileTree.find(path);
|
||||
|
||||
if (result == io::fileTree.end() || result->second != PHYSFS_FILETYPE_DIRECTORY)
|
||||
{
|
||||
PHYSFS_setErrorCode(PHYSFS_ERR_NOT_FOUND);
|
||||
return PHYSFS_ENUM_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
JNIEnv *env = (JNIEnv *) SDL_AndroidGetJNIEnv();
|
||||
jobject assetManager = getJavaAssetManager();
|
||||
jclass clazz = env->GetObjectClass(assetManager);
|
||||
jmethodID method = env->GetMethodID(clazz, "list", "(Ljava/lang/String;)[Ljava/lang/String");
|
||||
|
||||
jstring jstringDir = env->NewStringUTF(path);
|
||||
jobjectArray dir = (jobjectArray) env->CallObjectMethod(assetManager, method, jstringDir);
|
||||
|
||||
PHYSFS_EnumerateCallbackResult ret = PHYSFS_ENUM_OK;
|
||||
|
||||
while (ret == PHYSFS_ENUM_OK)
|
||||
if (env->ExceptionCheck())
|
||||
{
|
||||
const char *name = AAssetDir_getNextFileName(dir);
|
||||
// IOException occured
|
||||
ret = PHYSFS_ENUM_ERROR;
|
||||
env->ExceptionClear();
|
||||
}
|
||||
else
|
||||
{
|
||||
jsize i = 0;
|
||||
jsize len = env->GetArrayLength(dir);
|
||||
|
||||
// No more files?
|
||||
if (name == nullptr)
|
||||
break;
|
||||
while (ret == PHYSFS_ENUM_OK && i < len) {
|
||||
jstring jstr = (jstring) env->GetObjectArrayElement(dir, i++);
|
||||
const char *name = env->GetStringUTFChars(jstr, nullptr);
|
||||
|
||||
ret = cb(callbackdata, origdir, name);
|
||||
ret = cb(callbackdata, origdir, name);
|
||||
|
||||
env->ReleaseStringUTFChars(jstr, name);
|
||||
env->DeleteLocalRef(jstr);
|
||||
}
|
||||
|
||||
env->DeleteLocalRef(dir);
|
||||
}
|
||||
|
||||
AAssetDir_close(dir);
|
||||
env->DeleteLocalRef(jstringDir);
|
||||
env->DeleteLocalRef(clazz);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user