--- a/src/core/android/SDL_android.cpp Tue Jan 08 08:28:39 2013 -0300
+++ b/src/core/android/SDL_android.cpp Tue Jan 08 09:30:53 2013 -0300
@@ -37,6 +37,8 @@
#include <android/log.h>
#include <pthread.h>
+#include <sys/types.h>
+#include <unistd.h>
#define LOG_TAG "SDL_android"
//#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
//#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
@@ -559,6 +561,9 @@
jclass channels;
jobject readableByteChannel;
jstring fileNameJString;
+ jobject fd;
+ jclass fdCls;
+ jfieldID descriptor;
JNIEnv *mEnv = Android_JNI_GetEnv();
if (!refs.init(mEnv)) {
@@ -566,62 +571,98 @@
}
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);
- // inputStream = assetManager.open(<filename>);
- mid = mEnv->GetMethodID(mEnv->GetObjectClass(assetManager),
- "open", "(Ljava/lang/String;)Ljava/io/InputStream;");
+ /* 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()) {
- goto failure;
+ goto fallback;
+ }
+
+ ctx->hidden.androidio.assetFileDescriptorRef = mEnv->NewGlobalRef(inputStream);
+ mid = mEnv->GetMethodID(mEnv->GetObjectClass(inputStream), "getStartOffset", "()J");
+ ctx->hidden.androidio.offset = mEnv->CallLongMethod(inputStream, mid);
+ if (Android_JNI_ExceptionOccurred()) {
+ goto fallback;
+ }
+
+ mid = mEnv->GetMethodID(mEnv->GetObjectClass(inputStream), "getDeclaredLength", "()J");
+ ctx->hidden.androidio.size = mEnv->CallLongMethod(inputStream, mid);
+
+ if (Android_JNI_ExceptionOccurred()) {
+ goto fallback;
}
- ctx->hidden.androidio.inputStreamRef = mEnv->NewGlobalRef(inputStream);
+ 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);
- // 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
+ 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;
- // 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;
- }
+ // 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
- // 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;
+ // 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;
}
- 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;
@@ -636,6 +677,10 @@
mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.readableByteChannelRef);
}
+ if(ctx->hidden.androidio.assetFileDescriptorRef != NULL) {
+ mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.assetFileDescriptorRef);
+ }
+
}
return result;
@@ -660,6 +705,7 @@
ctx->hidden.androidio.inputStreamRef = NULL;
ctx->hidden.androidio.readableByteChannelRef = NULL;
ctx->hidden.androidio.readMethod = NULL;
+ ctx->hidden.androidio.assetFileDescriptorRef = NULL;
return Android_JNI_FileOpen(ctx);
}
@@ -668,40 +714,53 @@
size_t size, size_t maxnum)
{
LocalReferenceHolder refs(__FUNCTION__);
- 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;
- }
+ 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;
- jobject readableByteChannel = (jobject)ctx->hidden.androidio.readableByteChannelRef;
- jmethodID readMethod = (jmethodID)ctx->hidden.androidio.readMethod;
- jobject byteBuffer = mEnv->NewDirectByteBuffer(buffer, bytesRemaining);
+ /* Don't read more bytes than those that remain in the file, otherwise we get an exception */
+ if (bytesRemaining > bytesMax) bytesRemaining = bytesMax;
- while (bytesRemaining > 0) {
- // result = readableByteChannel.read(...);
- int result = mEnv->CallIntMethod(readableByteChannel, readMethod, byteBuffer);
-
- if (Android_JNI_ExceptionOccurred()) {
- return 0;
+ JNIEnv *mEnv = Android_JNI_GetEnv();
+ if (!refs.init(mEnv)) {
+ return -1;
}
- if (result < 0) {
- break;
- }
+ 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);
- bytesRemaining -= result;
- bytesRead += result;
- ctx->hidden.androidio.position += result;
- }
+ if (Android_JNI_ExceptionOccurred()) {
+ return 0;
+ }
- return bytesRead / size;
+ if (result < 0) {
+ break;
+ }
+
+ bytesRemaining -= result;
+ bytesRead += result;
+ ctx->hidden.androidio.position += result;
+ }
+ return bytesRead / size;
+ }
}
extern "C" size_t Android_JNI_FileWrite(SDL_RWops* ctx, const void* buffer,
@@ -727,16 +786,28 @@
mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.fileNameRef);
}
- jobject inputStream = (jobject)ctx->hidden.androidio.inputStreamRef;
+ if (ctx->hidden.androidio.assetFileDescriptorRef) {
+ jobject inputStream = (jobject)ctx->hidden.androidio.assetFileDescriptorRef;
+ jmethodID mid = mEnv->GetMethodID(mEnv->GetObjectClass(inputStream),
+ "close", "()V");
+ mEnv->CallVoidMethod(inputStream, mid);
+ mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.assetFileDescriptorRef);
+ if (Android_JNI_ExceptionOccurred()) {
+ result = -1;
+ }
+ }
+ else {
+ 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;
+ // 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) {
@@ -755,60 +826,86 @@
extern "C" Sint64 Android_JNI_FileSeek(SDL_RWops* ctx, Sint64 offset, int whence)
{
- Sint64 newPosition;
+ if (ctx->hidden.androidio.assetFileDescriptorRef) {
+ switch (whence) {
+ case RW_SEEK_SET:
+ if (ctx->hidden.androidio.size != -1 /*UNKNOWN_LENGTH*/ && offset > ctx->hidden.androidio.size) offset = ctx->hidden.androidio.size;
+ offset += ctx->hidden.androidio.offset;
+ break;
+ case RW_SEEK_CUR:
+ offset += ctx->hidden.androidio.position;
+ if (ctx->hidden.androidio.size != -1 /*UNKNOWN_LENGTH*/ && offset > ctx->hidden.androidio.size) offset = ctx->hidden.androidio.size;
+ offset += ctx->hidden.androidio.offset;
+ break;
+ case RW_SEEK_END:
+ offset = ctx->hidden.androidio.offset + ctx->hidden.androidio.size + offset;
+ break;
+ default:
+ SDL_SetError("Unknown value for 'whence'");
+ return -1;
+ }
+ whence = SEEK_SET;
- switch (whence) {
- case RW_SEEK_SET:
- newPosition = offset;
- break;
- case RW_SEEK_CUR:
- newPosition = ctx->hidden.androidio.position + offset;
- break;
- case RW_SEEK_END:
- newPosition = ctx->hidden.androidio.size + offset;
- break;
- default:
- SDL_SetError("Unknown value for 'whence'");
- return -1;
- }
+ off_t ret = lseek(ctx->hidden.androidio.fd, (off_t)offset, SEEK_SET);
+ if (ret == -1) return -1;
+ ctx->hidden.androidio.position = ret - ctx->hidden.androidio.offset;
+ } else {
+ Sint64 newPosition;
- /* Validate the new position */
- if (newPosition < 0) {
- SDL_Error(SDL_EFSEEK);
- return -1;
- }
- if (newPosition > ctx->hidden.androidio.size) {
- newPosition = ctx->hidden.androidio.size;
- }
+ switch (whence) {
+ case RW_SEEK_SET:
+ newPosition = offset;
+ break;
+ case RW_SEEK_CUR:
+ newPosition = ctx->hidden.androidio.position + offset;
+ break;
+ case RW_SEEK_END:
+ newPosition = ctx->hidden.androidio.size + offset;
+ break;
+ default:
+ SDL_SetError("Unknown value for 'whence'");
+ return -1;
+ }
- Sint64 movement = newPosition - ctx->hidden.androidio.position;
- if (movement > 0) {
- unsigned char buffer[4096];
+ /* Validate the new position */
+ if (newPosition < 0) {
+ SDL_Error(SDL_EFSEEK);
+ return -1;
+ }
+ if (newPosition > ctx->hidden.androidio.size) {
+ newPosition = ctx->hidden.androidio.size;
+ }
+
+ Sint64 movement = newPosition - ctx->hidden.androidio.position;
+ if (movement > 0) {
+ unsigned char buffer[4096];
- // The easy case where we're seeking forwards
- while (movement > 0) {
- Sint64 amount = sizeof (buffer);
- if (amount > movement) {
- amount = movement;
- }
- size_t result = Android_JNI_FileRead(ctx, buffer, 1, amount);
- if (result <= 0) {
- // Failed to read/skip the required amount, so fail
- return -1;
+ // The easy case where we're seeking forwards
+ while (movement > 0) {
+ Sint64 amount = sizeof (buffer);
+ if (amount > movement) {
+ amount = movement;
+ }
+ size_t result = Android_JNI_FileRead(ctx, buffer, 1, amount);
+ if (result <= 0) {
+ // Failed to read/skip the required amount, so fail
+ return -1;
+ }
+
+ movement -= result;
}
- movement -= result;
+ } else if (movement < 0) {
+ // We can't seek backwards so we have to reopen the file and seek
+ // forwards which obviously isn't very efficient
+ Android_JNI_FileClose(ctx, false);
+ Android_JNI_FileOpen(ctx);
+ Android_JNI_FileSeek(ctx, newPosition, RW_SEEK_SET);
}
-
- } else if (movement < 0) {
- // We can't seek backwards so we have to reopen the file and seek
- // forwards which obviously isn't very efficient
- Android_JNI_FileClose(ctx, false);
- Android_JNI_FileOpen(ctx);
- Android_JNI_FileSeek(ctx, newPosition, RW_SEEK_SET);
}
return ctx->hidden.androidio.position;
+
}
extern "C" int Android_JNI_FileClose(SDL_RWops* ctx)