Android: Use APK assets for HostInterface::OpenPackagePath

This commit is contained in:
Connor McLaughlin 2020-11-27 23:10:02 +10:00
parent feadc83050
commit 12caa79178
3 changed files with 65 additions and 0 deletions

View File

@ -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<GrowableMemoryByteStream> ReadInputStreamToMemory(JNIEnv* env, jobject obj, u32 chunk_size/* = 65536*/)
{
std::unique_ptr<GrowableMemoryByteStream> bs = std::make_unique<GrowableMemoryByteStream>(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<u32>(bytes_read)) > bs->GetMemorySize())
{
const u32 new_size = std::max<u32>(bs->GetMemorySize() * 2, position + static_cast<u32>(bytes_read));
bs->ResizeMemory(new_size);
}
env->GetByteArrayRegion(temp, 0, bytes_read, reinterpret_cast<jbyte*>(bs->GetMemoryPointer() + position));
position += static_cast<u32>(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<ByteStream> 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 ||

View File

@ -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 <array>
#include <atomic>
@ -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<ByteStream> 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<GrowableMemoryByteStream> ReadInputStreamToMemory(JNIEnv* env, jobject obj, u32 chunk_size = 65536);
} // namespace AndroidHelpers

View File

@ -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);