diff --git a/android/app/src/cpp/android_host_interface.cpp b/android/app/src/cpp/android_host_interface.cpp index 16ad84361..adb3c8393 100644 --- a/android/app/src/cpp/android_host_interface.cpp +++ b/android/app/src/cpp/android_host_interface.cpp @@ -35,6 +35,7 @@ static jmethodID s_AndroidHostInterface_constructor; static jfieldID s_AndroidHostInterface_field_mNativePointer; static jmethodID s_AndroidHostInterface_method_reportError; static jmethodID s_AndroidHostInterface_method_reportMessage; +static jmethodID s_AndroidHostInterface_method_openAssetStream; static jmethodID s_EmulationActivity_method_reportError; static jmethodID s_EmulationActivity_method_reportMessage; static jmethodID s_EmulationActivity_method_onEmulationStarted; @@ -78,6 +79,36 @@ std::string JStringToString(JNIEnv* env, jstring str) return ret; } + +std::unique_ptr ReadInputStreamToMemory(JNIEnv* env, jobject obj, u32 chunk_size/* = 65536*/) +{ + std::unique_ptr bs = std::make_unique(nullptr, 0); + u32 position = 0; + + jclass cls = env->GetObjectClass(obj); + jmethodID read_method = env->GetMethodID(cls, "read", "([B)I"); + Assert(read_method); + + jbyteArray temp = env->NewByteArray(chunk_size); + for (;;) + { + int bytes_read = env->CallIntMethod(obj, read_method, temp); + if (bytes_read <= 0) + break; + + if ((position + static_cast(bytes_read)) > bs->GetMemorySize()) + { + const u32 new_size = std::max(bs->GetMemorySize() * 2, position + static_cast(bytes_read)); + bs->ResizeMemory(new_size); + } + + env->GetByteArrayRegion(temp, 0, bytes_read, reinterpret_cast(bs->GetMemoryPointer() + position)); + position += static_cast(bytes_read); + } + + bs->Resize(position); + return bs; +} } // namespace AndroidHelpers AndroidHostInterface::AndroidHostInterface(jobject java_object, jobject context_object, std::string user_directory) @@ -159,6 +190,23 @@ float AndroidHostInterface::GetFloatSettingValue(const char* section, const char return m_settings_interface.GetFloatValue(section, key, default_value); } +std::unique_ptr AndroidHostInterface::OpenPackageFile(const char *path, u32 flags) +{ + Log_DevPrintf("OpenPackageFile(%s, %x)", path, flags); + if (flags & (BYTESTREAM_OPEN_CREATE | BYTESTREAM_OPEN_WRITE)) + return {}; + + JNIEnv* env = AndroidHelpers::GetJNIEnv(); + jobject stream = env->CallObjectMethod(m_java_object, s_AndroidHostInterface_method_openAssetStream, env->NewStringUTF(path)); + if (!stream) + { + Log_ErrorPrintf("Package file '%s' not found", path); + return {}; + } + + return AndroidHelpers::ReadInputStreamToMemory(env, stream, 65536); +} + void AndroidHostInterface::SetUserDirectory() { // Already set in constructor. @@ -707,6 +755,8 @@ extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved) env->GetMethodID(s_AndroidHostInterface_class, "reportError", "(Ljava/lang/String;)V")) == nullptr || (s_AndroidHostInterface_method_reportMessage = env->GetMethodID(s_AndroidHostInterface_class, "reportMessage", "(Ljava/lang/String;)V")) == nullptr || + (s_AndroidHostInterface_method_openAssetStream = + env->GetMethodID(s_AndroidHostInterface_class, "openAssetStream", "(Ljava/lang/String;)Ljava/io/InputStream;")) == nullptr || (emulation_activity_class = env->FindClass("com/github/stenzek/duckstation/EmulationActivity")) == nullptr || (s_EmulationActivity_method_reportError = env->GetMethodID(emulation_activity_class, "reportError", "(Ljava/lang/String;)V")) == nullptr || diff --git a/android/app/src/cpp/android_host_interface.h b/android/app/src/cpp/android_host_interface.h index 4ec0fbe99..4bedd9b41 100644 --- a/android/app/src/cpp/android_host_interface.h +++ b/android/app/src/cpp/android_host_interface.h @@ -2,6 +2,7 @@ #include "android_settings_interface.h" #include "common/event.h" #include "common/progress_callback.h" +#include "common/byte_stream.h" #include "frontend-common/common_host_interface.h" #include #include @@ -35,6 +36,7 @@ public: bool GetBoolSettingValue(const char* section, const char* key, bool default_value = false) override; int GetIntSettingValue(const char* section, const char* key, int default_value = 0) override; float GetFloatSettingValue(const char* section, const char* key, float default_value = 0.0f) override; + std::unique_ptr OpenPackageFile(const char* path, u32 flags) override; bool IsEmulationThreadRunning() const { return m_emulation_thread.joinable(); } bool IsEmulationThreadPaused() const; @@ -105,4 +107,5 @@ namespace AndroidHelpers { JNIEnv* GetJNIEnv(); AndroidHostInterface* GetNativeClass(JNIEnv* env, jobject obj); std::string JStringToString(JNIEnv* env, jstring str); +std::unique_ptr ReadInputStreamToMemory(JNIEnv* env, jobject obj, u32 chunk_size = 65536); } // namespace AndroidHelpers diff --git a/android/app/src/main/java/com/github/stenzek/duckstation/AndroidHostInterface.java b/android/app/src/main/java/com/github/stenzek/duckstation/AndroidHostInterface.java index 262c67ba6..2a06b8d18 100644 --- a/android/app/src/main/java/com/github/stenzek/duckstation/AndroidHostInterface.java +++ b/android/app/src/main/java/com/github/stenzek/duckstation/AndroidHostInterface.java @@ -1,11 +1,15 @@ package com.github.stenzek.duckstation; import android.content.Context; +import android.content.res.AssetManager; import android.os.Environment; import android.util.Log; import android.view.Surface; import android.widget.Toast; +import java.io.IOException; +import java.io.InputStream; + public class AndroidHostInterface { public final static int DISPLAY_ALIGNMENT_TOP_OR_LEFT = 0; public final static int DISPLAY_ALIGNMENT_CENTER = 1; @@ -30,6 +34,14 @@ public class AndroidHostInterface { Toast.makeText(mContext, message, Toast.LENGTH_SHORT).show(); } + public InputStream openAssetStream(String path) { + try { + return mContext.getAssets().open(path, AssetManager.ACCESS_STREAMING); + } catch (IOException e) { + return null; + } + } + public native boolean isEmulationThreadRunning(); public native boolean startEmulationThread(EmulationActivity emulationActivity, Surface surface, String filename, boolean resumeState, String state_filename);