extern "C" size_t Android_JNI_FileRead(SDL_RWops* ctx, void* buffer, size_t size, size_t maxnum) { LocalReferenceHolder refs; int bytesRemaining = size * maxnum; int bytesRead = 0; if (!refs.init(mEnv)) { return -1; } jobject readableByteChannel = (jobject)ctx->hidden.androidio.readableByteChannelRef; jmethodID readMethod = (jmethodID)ctx->hidden.androidio.readMethod; jobject byteBuffer = mEnv->NewDirectByteBuffer(buffer, bytesRemaining); while (bytesRemaining > 0) { // result = readableByteChannel.read(...); int result = mEnv->CallIntMethod(readableByteChannel, readMethod, byteBuffer); if (Android_JNI_ExceptionOccurred()) { return 0; } if (result < 0) { break; } bytesRemaining -= result; bytesRead += result; ctx->hidden.androidio.position += result; } return bytesRead / size; }
static int Android_JNI_FileClose(SDL_RWops* ctx, bool release) { LocalReferenceHolder refs; int result = 0; if (!refs.init(mEnv)) { SDL_SetError("Failed to allocate enough JVM local references"); return -1; } if (ctx) { if (release) { mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.fileNameRef); } jobject inputStream = (jobject)ctx->hidden.androidio.inputStreamRef; // inputStream.close(); jmethodID mid = mEnv->GetMethodID(mEnv->GetObjectClass(inputStream), "close", "()V"); mEnv->CallVoidMethod(inputStream, mid); mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.inputStreamRef); mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.readableByteChannelRef); if (Android_JNI_ExceptionOccurred()) { result = -1; } if (release) { SDL_FreeRW(ctx); } } return result; }
static int Android_JNI_FileClose(SDL_RWops* ctx, bool release) { int result = 0; if (ctx) { if (release) { mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.fileNameRef); } jobject inputStream = (jobject)ctx->hidden.androidio.inputStream; // inputStream.close(); jmethodID mid = mEnv->GetMethodID(mEnv->GetObjectClass(inputStream), "close", "()V"); mEnv->CallVoidMethod(inputStream, mid); mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.inputStreamRef); mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.readableByteChannelRef); if (Android_JNI_ExceptionOccurred()) { result = -1; } if (release) { SDL_FreeRW(ctx); } } return result; }
static int Android_JNI_FileClose(SDL_RWops* ctx, bool release) { JNIEnv *env = 0; int result = 0; if (mJavaVM->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) { LOGE("Failed to get the environment using GetEnv(); assuming main thread"); env = mEnv; } if (ctx) { if (release) { env->DeleteGlobalRef((jobject)ctx->hidden.androidio.fileNameRef); } jobject inputStream = (jobject)ctx->hidden.androidio.inputStreamRef; // inputStream.close(); jmethodID mid = env->GetMethodID(env->GetObjectClass(inputStream), "close", "()V"); env->CallVoidMethod(inputStream, mid); env->DeleteGlobalRef((jobject)ctx->hidden.androidio.inputStreamRef); env->DeleteGlobalRef((jobject)ctx->hidden.androidio.readableByteChannelRef); if (Android_JNI_ExceptionOccurred(env)) { result = -1; } if (release) { SDL_FreeRW(ctx); } } return result; }
extern "C" size_t Android_JNI_FileRead(SDL_RWops* ctx, void* buffer, size_t size, size_t maxnum) { LocalReferenceHolder refs(__FUNCTION__); if (ctx->hidden.androidio.assetFileDescriptorRef) { size_t bytesMax = size * maxnum; if (ctx->hidden.androidio.size != -1 /*UNKNOWN_LENGTH*/ && ctx->hidden.androidio.position + bytesMax > ctx->hidden.androidio.size) { bytesMax = ctx->hidden.androidio.size - ctx->hidden.androidio.position; } size_t result = read(ctx->hidden.androidio.fd, buffer, bytesMax ); if (result > 0) { ctx->hidden.androidio.position += result; return result / size; } return 0; } else { jlong bytesRemaining = (jlong) (size * maxnum); jlong bytesMax = (jlong) (ctx->hidden.androidio.size - ctx->hidden.androidio.position); int bytesRead = 0; /* Don't read more bytes than those that remain in the file, otherwise we get an exception */ if (bytesRemaining > bytesMax) bytesRemaining = bytesMax; JNIEnv *mEnv = Android_JNI_GetEnv(); if (!refs.init(mEnv)) { return -1; } jobject readableByteChannel = (jobject)ctx->hidden.androidio.readableByteChannelRef; jmethodID readMethod = (jmethodID)ctx->hidden.androidio.readMethod; jobject byteBuffer = mEnv->NewDirectByteBuffer(buffer, bytesRemaining); while (bytesRemaining > 0) { // result = readableByteChannel.read(...); int result = mEnv->CallIntMethod(readableByteChannel, readMethod, byteBuffer); if (Android_JNI_ExceptionOccurred()) { return 0; } if (result < 0) { break; } bytesRemaining -= result; bytesRead += result; ctx->hidden.androidio.position += result; } return bytesRead / size; } }
extern "C" size_t Android_JNI_FileRead(SDL_RWops* ctx, void* buffer, size_t size, size_t maxnum) { JNIEnv *env = 0; int bytesRemaining = size * maxnum; int bytesRead = 0; if (mJavaVM->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) { LOGE("Failed to get the environment using GetEnv(); assuming main thread"); env = mEnv; } jobject readableByteChannel = (jobject)ctx->hidden.androidio.readableByteChannelRef; jmethodID readMethod = (jmethodID)ctx->hidden.androidio.readMethod; jobject byteBuffer = env->NewDirectByteBuffer(buffer, bytesRemaining); while (bytesRemaining > 0) { // result = readableByteChannel.read(...); int result = env->CallIntMethod(readableByteChannel, readMethod, byteBuffer); if (Android_JNI_ExceptionOccurred(env)) { env->DeleteLocalRef(byteBuffer); return 0; } if (result < 0) { break; } bytesRemaining -= result; bytesRead += result; ctx->hidden.androidio.position += result; } env->DeleteLocalRef(byteBuffer); return bytesRead / size; }
static int Android_JNI_FileOpen(SDL_RWops* ctx) { LocalReferenceHolder refs; int result = 0; jmethodID mid; jobject context; jobject assetManager; jobject inputStream; jclass channels; jobject readableByteChannel; jstring fileNameJString; if (!refs.init(mEnv)) { goto failure; } fileNameJString = (jstring)ctx->hidden.androidio.fileNameRef; // context = SDLActivity.getContext(); mid = mEnv->GetStaticMethodID(mActivityClass, "getContext","()Landroid/content/Context;"); context = mEnv->CallStaticObjectMethod(mActivityClass, mid); // assetManager = context.getAssets(); mid = mEnv->GetMethodID(mEnv->GetObjectClass(context), "getAssets", "()Landroid/content/res/AssetManager;"); assetManager = mEnv->CallObjectMethod(context, mid); // inputStream = assetManager.open(<filename>); mid = mEnv->GetMethodID(mEnv->GetObjectClass(assetManager), "open", "(Ljava/lang/String;)Ljava/io/InputStream;"); inputStream = mEnv->CallObjectMethod(assetManager, mid, fileNameJString); if (Android_JNI_ExceptionOccurred()) { goto failure; } ctx->hidden.androidio.inputStreamRef = mEnv->NewGlobalRef(inputStream); // Despite all the visible documentation on [Asset]InputStream claiming // that the .available() method is not guaranteed to return the entire file // size, comments in <sdk>/samples/<ver>/ApiDemos/src/com/example/ ... // android/apis/content/ReadAsset.java imply that Android's // AssetInputStream.available() /will/ always return the total file size // size = inputStream.available(); mid = mEnv->GetMethodID(mEnv->GetObjectClass(inputStream), "available", "()I"); ctx->hidden.androidio.size = mEnv->CallIntMethod(inputStream, mid); if (Android_JNI_ExceptionOccurred()) { goto failure; } // readableByteChannel = Channels.newChannel(inputStream); channels = mEnv->FindClass("java/nio/channels/Channels"); mid = mEnv->GetStaticMethodID(channels, "newChannel", "(Ljava/io/InputStream;)Ljava/nio/channels/ReadableByteChannel;"); readableByteChannel = mEnv->CallStaticObjectMethod( channels, mid, inputStream); if (Android_JNI_ExceptionOccurred()) { goto failure; } ctx->hidden.androidio.readableByteChannelRef = mEnv->NewGlobalRef(readableByteChannel); // Store .read id for reading purposes mid = mEnv->GetMethodID(mEnv->GetObjectClass(readableByteChannel), "read", "(Ljava/nio/ByteBuffer;)I"); ctx->hidden.androidio.readMethod = mid; ctx->hidden.androidio.position = 0; if (false) { failure: result = -1; mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.fileNameRef); if(ctx->hidden.androidio.inputStreamRef != NULL) { mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.inputStreamRef); } if(ctx->hidden.androidio.readableByteChannelRef != NULL) { mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.readableByteChannelRef); } } return result; }
static int Android_JNI_FileOpen(SDL_RWops* ctx) { LocalReferenceHolder refs(__FUNCTION__); int result = 0; jmethodID mid; jobject context; jobject assetManager; jobject inputStream; jclass channels; jobject readableByteChannel; jstring fileNameJString; jobject fd; jclass fdCls; jfieldID descriptor; JNIEnv *mEnv = Android_JNI_GetEnv(); if (!refs.init(mEnv)) { goto failure; } fileNameJString = (jstring)ctx->hidden.androidio.fileNameRef; ctx->hidden.androidio.position = 0; // context = SDLActivity.getContext(); mid = mEnv->GetStaticMethodID(mActivityClass, "getContext","()Landroid/content/Context;"); context = mEnv->CallStaticObjectMethod(mActivityClass, mid); // assetManager = context.getAssets(); mid = mEnv->GetMethodID(mEnv->GetObjectClass(context), "getAssets", "()Landroid/content/res/AssetManager;"); assetManager = mEnv->CallObjectMethod(context, mid); /* First let's try opening the file to obtain an AssetFileDescriptor. * This method reads the files directly from the APKs using standard *nix calls */ mid = mEnv->GetMethodID(mEnv->GetObjectClass(assetManager), "openFd", "(Ljava/lang/String;)Landroid/content/res/AssetFileDescriptor;"); inputStream = mEnv->CallObjectMethod(assetManager, mid, fileNameJString); if (Android_JNI_ExceptionOccurred(true)) { goto fallback; } mid = mEnv->GetMethodID(mEnv->GetObjectClass(inputStream), "getStartOffset", "()J"); ctx->hidden.androidio.offset = mEnv->CallLongMethod(inputStream, mid); if (Android_JNI_ExceptionOccurred(true)) { goto fallback; } mid = mEnv->GetMethodID(mEnv->GetObjectClass(inputStream), "getDeclaredLength", "()J"); ctx->hidden.androidio.size = mEnv->CallLongMethod(inputStream, mid); if (Android_JNI_ExceptionOccurred(true)) { goto fallback; } mid = mEnv->GetMethodID(mEnv->GetObjectClass(inputStream), "getFileDescriptor", "()Ljava/io/FileDescriptor;"); fd = mEnv->CallObjectMethod(inputStream, mid); fdCls = mEnv->GetObjectClass(fd); descriptor = mEnv->GetFieldID(fdCls, "descriptor", "I"); ctx->hidden.androidio.fd = mEnv->GetIntField(fd, descriptor); ctx->hidden.androidio.assetFileDescriptorRef = mEnv->NewGlobalRef(inputStream); // Seek to the correct offset in the file. lseek(ctx->hidden.androidio.fd, (off_t)ctx->hidden.androidio.offset, SEEK_SET); if (false) { fallback: //__android_log_print(ANDROID_LOG_DEBUG, "SDL", "Falling back to legacy InputStream method for opening file"); /* Try the old method using InputStream */ ctx->hidden.androidio.assetFileDescriptorRef = NULL; // inputStream = assetManager.open(<filename>); mid = mEnv->GetMethodID(mEnv->GetObjectClass(assetManager), "open", "(Ljava/lang/String;I)Ljava/io/InputStream;"); inputStream = mEnv->CallObjectMethod(assetManager, mid, fileNameJString, 1 /*ACCESS_RANDOM*/); if (Android_JNI_ExceptionOccurred()) { goto failure; } ctx->hidden.androidio.inputStreamRef = mEnv->NewGlobalRef(inputStream); // Despite all the visible documentation on [Asset]InputStream claiming // that the .available() method is not guaranteed to return the entire file // size, comments in <sdk>/samples/<ver>/ApiDemos/src/com/example/ ... // android/apis/content/ReadAsset.java imply that Android's // AssetInputStream.available() /will/ always return the total file size // size = inputStream.available(); mid = mEnv->GetMethodID(mEnv->GetObjectClass(inputStream), "available", "()I"); ctx->hidden.androidio.size = (long)mEnv->CallIntMethod(inputStream, mid); if (Android_JNI_ExceptionOccurred()) { goto failure; } // readableByteChannel = Channels.newChannel(inputStream); channels = mEnv->FindClass("java/nio/channels/Channels"); mid = mEnv->GetStaticMethodID(channels, "newChannel", "(Ljava/io/InputStream;)Ljava/nio/channels/ReadableByteChannel;"); readableByteChannel = mEnv->CallStaticObjectMethod( channels, mid, inputStream); if (Android_JNI_ExceptionOccurred()) { goto failure; } ctx->hidden.androidio.readableByteChannelRef = mEnv->NewGlobalRef(readableByteChannel); // Store .read id for reading purposes mid = mEnv->GetMethodID(mEnv->GetObjectClass(readableByteChannel), "read", "(Ljava/nio/ByteBuffer;)I"); ctx->hidden.androidio.readMethod = mid; } if (false) { failure: result = -1; mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.fileNameRef); if(ctx->hidden.androidio.inputStreamRef != NULL) { mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.inputStreamRef); } if(ctx->hidden.androidio.readableByteChannelRef != NULL) { mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.readableByteChannelRef); } if(ctx->hidden.androidio.assetFileDescriptorRef != NULL) { mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.assetFileDescriptorRef); } } return result; }
static int Android_JNI_FileOpen(SDL_RWops* ctx) { JNIEnv *env = 0; int result = 0; jmethodID mid; jobject context; jobject assetManager; jobject inputStream; jclass channels; jobject readableByteChannel; jstring fileNameJString; bool allocatedLocalFrame = false; if (mJavaVM->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) { LOGE("Failed to get the environment using GetEnv(); assuming main thread"); env = mEnv; } if (env->PushLocalFrame(16) < 0) { SDL_SetError("Failed to allocate enough JVM local references"); goto failure; } else { allocatedLocalFrame = true; } fileNameJString = (jstring)ctx->hidden.androidio.fileNameRef; // context = SDLActivity.getContext(); mid = env->GetStaticMethodID(mActivityClass, "getContext","()Landroid/content/Context;"); context = env->CallStaticObjectMethod(mActivityClass, mid); // assetManager = context.getAssets(); mid = env->GetMethodID(env->GetObjectClass(context), "getAssets", "()Landroid/content/res/AssetManager;"); assetManager = env->CallObjectMethod(context, mid); // inputStream = assetManager.open(<filename>); mid = env->GetMethodID(env->GetObjectClass(assetManager), "open", "(Ljava/lang/String;)Ljava/io/InputStream;"); inputStream = env->CallObjectMethod(assetManager, mid, fileNameJString); if (Android_JNI_ExceptionOccurred(env)) { goto failure; } ctx->hidden.androidio.inputStream = inputStream; ctx->hidden.androidio.inputStreamRef = env->NewGlobalRef(inputStream); // Despite all the visible documentation on [Asset]InputStream claiming // that the .available() method is not guaranteed to return the entire file // size, comments in <sdk>/samples/<ver>/ApiDemos/src/com/example/ ... // android/apis/content/ReadAsset.java imply that Android's // AssetInputStream.available() /will/ always return the total file size // size = inputStream.available(); mid = env->GetMethodID(env->GetObjectClass(inputStream), "available", "()I"); ctx->hidden.androidio.size = env->CallIntMethod(inputStream, mid); if (Android_JNI_ExceptionOccurred(env)) { goto failure; } // readableByteChannel = Channels.newChannel(inputStream); channels = env->FindClass("java/nio/channels/Channels"); mid = env->GetStaticMethodID(channels, "newChannel", "(Ljava/io/InputStream;)Ljava/nio/channels/ReadableByteChannel;"); readableByteChannel = env->CallStaticObjectMethod( channels, mid, inputStream); if (Android_JNI_ExceptionOccurred(env)) { goto failure; } ctx->hidden.androidio.readableByteChannel = readableByteChannel; ctx->hidden.androidio.readableByteChannelRef = env->NewGlobalRef(readableByteChannel); // Store .read id for reading purposes mid = env->GetMethodID(env->GetObjectClass(readableByteChannel), "read", "(Ljava/nio/ByteBuffer;)I"); ctx->hidden.androidio.readMethod = mid; ctx->hidden.androidio.position = 0; if (false) { failure: result = -1; env->DeleteGlobalRef((jobject)ctx->hidden.androidio.fileNameRef); if(ctx->hidden.androidio.inputStreamRef != NULL) { env->DeleteGlobalRef((jobject)ctx->hidden.androidio.inputStreamRef); } } if (allocatedLocalFrame) { env->PopLocalFrame(NULL); } return result; }