diff --git a/project.hxp b/project.hxp index 4836d19..5621298 100644 --- a/project.hxp +++ b/project.hxp @@ -32,6 +32,7 @@ class Project extends HXProject static final DISCORD_ALLOWED:CompileFlag = CompileFlag.get("DISCORD_ALLOWED"); static final MODCHARTS_ALLOWED:CompileFlag = CompileFlag.get("MODCHARTS_ALLOWED"); static final DCEBUILD:CompileFlag = CompileFlag.get("DCEBUILD"); + static final USE_OPENFL_FILESYSTEM:CompileFlag = CompileFlag.get("USE_OPENFL_FILESYSTEM"); static final ENABLE_ASCII_ART:Bool = true; @@ -213,6 +214,7 @@ class Project extends HXProject DISCORD_ALLOWED.integrate((isDesktop() && !isHashLink())); MODCHARTS_ALLOWED.integrate(/*DCEBUILD.isDisabled()*/false); DCEBUILD.integrate(false); + USE_OPENFL_FILESYSTEM.integrate(MODS_ALLOWED.isDisabled()/*|| isMobile()*/); setHaxedef("FLX_NO_FOCUS_LOST_SCREEN"); if (!isDebug()) diff --git a/source/engine/backend/io/File.hx b/source/engine/backend/io/File.hx index c82fdd1..a87a35b 100644 --- a/source/engine/backend/io/File.hx +++ b/source/engine/backend/io/File.hx @@ -24,6 +24,7 @@ class File return path; } + #if USE_OPENFL_FILESYSTEM static function openflcwd(path:String):String { @:privateAccess @@ -33,6 +34,7 @@ class File return path; } + #end public static function getContent(path:String):Null { @@ -50,8 +52,10 @@ class File #end #end + #if USE_OPENFL_FILESYSTEM if (Assets.exists(openflcwd(path))) return Assets.getText(openflcwd(path)); + #end return null; } @@ -72,6 +76,7 @@ class File #end #end + #if USE_OPENFL_FILESYSTEM if (Assets.exists(openflcwd(path))) switch (haxe.io.Path.extension(path).toLowerCase()) { @@ -80,6 +85,7 @@ class File default: return Assets.getBytes(openflcwd(path)); } + #end return null; } diff --git a/source/engine/backend/io/FileSystem.hx b/source/engine/backend/io/FileSystem.hx index 17f9083..541133d 100644 --- a/source/engine/backend/io/FileSystem.hx +++ b/source/engine/backend/io/FileSystem.hx @@ -23,6 +23,7 @@ class FileSystem return path; } + #if USE_OPENFL_FILESYSTEM static function openflcwd(path:String):String { @:privateAccess @@ -32,6 +33,7 @@ class FileSystem return path; } + #end public static function exists(path:String):Bool { @@ -49,10 +51,12 @@ class FileSystem #end #end - if (Assets.exists(openflcwd(path))) + #if USE_OPENFL_FILESYSTEM + if (Assets.exists(openflcwd(path)) || Assets.list().filter(asset -> asset.startsWith(path) && asset != path).length > 0) return true; + #end - return Assets.list().filter(asset -> asset.startsWith(path) && asset != path).length > 0; + return false; } public static function rename(path:String, newPath:String):Void @@ -139,7 +143,12 @@ class FileSystem #end #end - return Assets.list().filter(asset -> asset.startsWith(path) && asset != path).length > 0; + #if USE_OPENFL_FILESYSTEM + if (Assets.list().filter(asset -> asset.startsWith(path) && asset != path).length > 0) + return true; + #end + + return false; } public static function createDirectory(path:String):Void @@ -186,44 +195,54 @@ class FileSystem public static function readDirectory(path:String):Array { + var result:Array = null; + #if MODS_ALLOWED #if linux - var actualPath:String = cwd(path); - actualPath = getCaseInsensitivePath(path); - if (actualPath == null) - actualPath = path; + var actualPath = cwd(path); + actualPath = getCaseInsensitivePath(path) ?? path; if (SysFileSystem.exists(actualPath) && SysFileSystem.isDirectory(actualPath)) - return SysFileSystem.readDirectory(actualPath); + result = SysFileSystem.readDirectory(actualPath); #else if (SysFileSystem.exists(cwd(path)) && SysFileSystem.isDirectory(cwd(path))) - return SysFileSystem.readDirectory(cwd(path) ); + result = SysFileSystem.readDirectory(cwd(path)); #end #end - var filteredList:Array = Assets.list().filter(f -> f.startsWith(path)); - var results:Array = []; - for (i in filteredList.copy()) + #if USE_OPENFL_FILESYSTEM + if (result == null) { - var slashsCount:Int = path.split('/').length; - if (path.endsWith('/')) - slashsCount -= 1; + var filteredList = Assets.list().filter(f -> f.startsWith(path)); + var results:Array = []; - if (i.split('/').length - 1 != slashsCount) - filteredList.remove(i); - } - for (item in filteredList) - { - @:privateAccess - for (library in lime.utils.Assets.libraries.keys()) + for (i in filteredList.copy()) + { + var slashsCount = path.split('/').length; + if (path.endsWith('/')) + slashsCount--; + + if (i.split('/').length - 1 != slashsCount) + filteredList.remove(i); + } + + for (item in filteredList) { - var libPath:String = '$library:$item'; - if (library != 'default' && Assets.exists(libPath) && !results.contains(libPath)) - results.push(libPath); - else if (Assets.exists(item) && !results.contains(item)) - results.push(item); + @:privateAccess + for (library in lime.utils.Assets.libraries.keys()) + { + var libPath = '$library:$item'; + if (library != 'default' && Assets.exists(libPath) && !results.contains(libPath)) + results.push(libPath); + else if (Assets.exists(item) && !results.contains(item)) + results.push(item); + } } + + result = results.map(f -> f.substr(f.lastIndexOf("/") + 1)); } - return results.map(f -> f.substr(f.lastIndexOf("/") + 1)); + #end + + return result ?? []; } #if (linux && MODS_ALLOWED) diff --git a/source/engine/mobile/backend/io/Assets.hx b/source/engine/mobile/backend/io/Assets.hx new file mode 100644 index 0000000..8a0efba --- /dev/null +++ b/source/engine/mobile/backend/io/Assets.hx @@ -0,0 +1,9 @@ +package mobile.backend.io; + +#if android +typedef Assets = mobile.backend.io.android.Assets; +// #elseif ios +// typedef Assets = mobile.backend.io.ios.Assets; +#else +typedef Assets = Dynamic; +#end \ No newline at end of file diff --git a/source/engine/mobile/backend/io/android/Assets.hx b/source/engine/mobile/backend/io/android/Assets.hx new file mode 100644 index 0000000..e413552 --- /dev/null +++ b/source/engine/mobile/backend/io/android/Assets.hx @@ -0,0 +1,251 @@ +package mobile.backend.io.android; + +/** + * The code for this class is mostly taken from SDL2. + * This class implements IO methods from the Android NDK's AAssetManager to read bundled app assets. + */ + +#if android +import haxe.io.Bytes; +import lime.system.JNI; + +@:cppNamespaceCode(' + #include + #include + #include + + static jmethodID midGetContext; + static jclass mActivityClass; + static AAssetManager *asset_manager = NULL; + static jobject javaAssetManagerRef = 0; + + struct LocalReferenceHolder + { + JNIEnv *m_env; + const char *m_func; + }; + + static struct LocalReferenceHolder LocalReferenceHolder_Setup(const char *func) + { + struct LocalReferenceHolder refholder; + refholder.m_env = NULL; + refholder.m_func = func; + __android_log_print (ANDROID_LOG_DEBUG, "Shadow Engine", "Entering function %s", func); + return refholder; + } + + static bool LocalReferenceHolder_Init(struct LocalReferenceHolder *refholder, JNIEnv *env) + { + const int capacity = 16; + if ((*env).PushLocalFrame(capacity) < 0) + { + __android_log_print (ANDROID_LOG_ERROR, "Shadow Engine", "Failed to allocate enough JVM local references"); + return false; + } + refholder->m_env = env; + return true; + } + + static void LocalReferenceHolder_Cleanup(struct LocalReferenceHolder *refholder) + { + __android_log_print (ANDROID_LOG_DEBUG, "Shadow Engine", "Leaving function %s", refholder->m_func); + if (refholder->m_env) + { + JNIEnv *env = refholder->m_env; + (*env).PopLocalFrame(NULL); + } + } + + void Assets_obj::native_init(::Dynamic jni_env) + { + JNIEnv* env = (JNIEnv*)(uintptr_t)jni_env; + jclass cls = env->FindClass("org/libsdl/app/SDLActivity"); + mActivityClass = (jclass)((*env).NewGlobalRef(cls)); + + struct LocalReferenceHolder refs = LocalReferenceHolder_Setup(__FUNCTION__); + jmethodID mid; + jobject context; + jobject javaAssetManager; + + if (!LocalReferenceHolder_Init(&refs, env)) + { + LocalReferenceHolder_Cleanup(&refs); + return; + } + + // context = SDLActivity.getContext(); + midGetContext = (*env).GetStaticMethodID(mActivityClass, "getContext","()Landroid/content/Context;"); + context = (*env).CallStaticObjectMethod(mActivityClass, midGetContext); + + // javaAssetManager = context.getAssets(); + mid = (*env).GetMethodID((*env).GetObjectClass(context), + "getAssets", "()Landroid/content/res/AssetManager;"); + javaAssetManager = (*env).CallObjectMethod(context, mid); + + /** + * Given a Dalvik AssetManager object, obtain the corresponding native AAssetManager + * object. Note that the caller is responsible for obtaining and holding a VM reference + * to the jobject to prevent its being garbage collected while the native object is + * in use. + */ + javaAssetManagerRef = (*env).NewGlobalRef(javaAssetManager); + asset_manager = AAssetManager_fromJava(env, javaAssetManagerRef); + + if (asset_manager == NULL) + { + (*env).DeleteGlobalRef(javaAssetManagerRef); + __android_log_print (ANDROID_LOG_DEBUG, "Shadow Engine", "Failed to create Android Assets Manager"); + } + + LocalReferenceHolder_Cleanup(&refs); + } + + void Assets_obj::native_destroy(::Dynamic jni_env) + { + JNIEnv* env = (JNIEnv*)(uintptr_t)jni_env; + + if (asset_manager) + { + (*env).DeleteGlobalRef(javaAssetManagerRef); + asset_manager = NULL; + } + } + + bool Assets_obj::native_exists(::String file) + { + AAsset* asset = AAssetManager_open(asset_manager, file.__s, AASSET_MODE_UNKNOWN); + bool ret = asset != NULL; + + if (ret) + AAsset_close(asset); + + return ret; + } + + ::String Assets_obj::native_getContent(::String file) { + std::vector buffer; + + hx::EnterGCFreeZone(); + AAsset* asset = AAssetManager_open(asset_manager, file.__s, AASSET_MODE_BUFFER); + + if (!asset) + { + hx::ExitGCFreeZone(); + return ::String(null()); + } + + int len = AAsset_getLength(asset); + if (len <= 0) + { + AAsset_close(asset); + hx::ExitGCFreeZone(); + return ::String::emptyString; + } + + const char* src = (const char*)AAsset_getBuffer(asset); + + buffer.resize(len); + memcpy(&buffer[0], src, len); + + AAsset_close(asset); + hx::ExitGCFreeZone(); + + return ::String::create(&buffer[0], buffer.size()); + } + + Array Assets_obj::native_getBytes(::String file) { + hx::EnterGCFreeZone(); + AAsset* asset = AAssetManager_open(asset_manager, file.__s, AASSET_MODE_BUFFER); + + if (!asset) + { + hx::ExitGCFreeZone(); + return null(); + } + + int len = AAsset_getLength(asset); + const unsigned char* src = (const unsigned char*)AAsset_getBuffer(asset); + hx::ExitGCFreeZone(); + + Array buffer = Array_obj::__new(len, len); + if (len > 0) + { + hx::EnterGCFreeZone(); + memcpy(buffer->getBase(), src, len); + AAsset_close(asset); + hx::ExitGCFreeZone(); + } + else + { + hx::EnterGCFreeZone(); + AAsset_close(asset); + hx::ExitGCFreeZone(); + } + + return buffer; + } +') +@:headerClassCode(' + static void native_init(::Dynamic jni_env); + static void native_destroy(::Dynamic jni_env); + static bool native_exists(::String file); + static ::String native_getContent(::String file); + static Array native_getBytes(::String file); +') +class Assets +{ + public static function init():Void + { + __init(JNI.getEnv()); + } + + public static function destroy():Void + { + __destroy(JNI.getEnv()); + } + + public static function getContent(file:String):String + { + final content:String = __getContent(file); + + if (content == null) + throw 'file_contents, $file'; + + return content; + } + + public static function getBytes(file:String):Bytes + { + final data:Array = __getBytes(file); + + if (data == null || data.length <= 0) + throw 'file_contents, $file'; + + return Bytes.ofData(data); + } + + @:native('mobile::backend::io::android::Assets_obj::native_exists') + public static function exists(file:String):Bool + return false; + + @:noCompletion + @:native('mobile::backend::io::android::Assets_obj::native_init') + private static function __init(jni_env:Dynamic):Void + return; + + @:noCompletion + @:native('mobile::backend::io::android::Assets_obj::native_destroy') + private static function __destroy(jni_env:Dynamic):Void + return; + + @:noCompletion + @:native('mobile::backend::io::android::Assets_obj::native_getContent') + public static function __getContent(file:String):String + return null; + + @:noCompletion + @:native('mobile::backend::io::android::Assets_obj::native_getBytes') + private static function __getBytes(file:String):Array + return null; +} +#end