1 /* |
1 /* |
2 SDL - Simple DirectMedia Layer |
2 Simple DirectMedia Layer |
3 Copyright (C) 1997-2011 Sam Lantinga |
3 Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org> |
4 |
4 |
5 This library is free software; you can redistribute it and/or |
5 This software is provided 'as-is', without any express or implied |
6 modify it under the terms of the GNU Lesser General Public |
6 warranty. In no event will the authors be held liable for any damages |
7 License as published by the Free Software Foundation; either |
7 arising from the use of this software. |
8 version 2.1 of the License, or (at your option) any later version. |
8 |
9 |
9 Permission is granted to anyone to use this software for any purpose, |
10 This library is distributed in the hope that it will be useful, |
10 including commercial applications, and to alter it and redistribute it |
11 but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 freely, subject to the following restrictions: |
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
12 |
13 Lesser General Public License for more details. |
13 1. The origin of this software must not be misrepresented; you must not |
14 |
14 claim that you wrote the original software. If you use this software |
15 You should have received a copy of the GNU Lesser General Public |
15 in a product, an acknowledgment in the product documentation would be |
16 License along with this library; if not, write to the Free Software |
16 appreciated but is not required. |
17 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
17 2. Altered source versions must be plainly marked as such, and must not be |
18 |
18 misrepresented as being the original software. |
19 Sam Lantinga |
19 3. This notice may not be removed or altered from any source distribution. |
20 slouken@libsdl.org |
|
21 */ |
20 */ |
22 |
|
23 #define org_hedgewars_hedgeroid org_hedgewars_hedgeroid |
|
24 |
|
25 #include "SDL_config.h" |
21 #include "SDL_config.h" |
26 #include "SDL_stdinc.h" |
22 #include "SDL_stdinc.h" |
|
23 |
|
24 #ifdef __ANDROID__ |
27 |
25 |
28 #include "SDL_android.h" |
26 #include "SDL_android.h" |
29 |
27 |
30 extern "C" { |
28 extern "C" { |
31 #include "../../events/SDL_events_c.h" |
29 #include "../../events/SDL_events_c.h" |
32 #include "../../video/android/SDL_androidkeyboard.h" |
30 #include "../../video/android/SDL_androidkeyboard.h" |
33 #include "../../video/android/SDL_androidtouch.h" |
31 #include "../../video/android/SDL_androidtouch.h" |
34 #include "../../video/android/SDL_androidvideo.h" |
32 #include "../../video/android/SDL_androidvideo.h" |
35 |
33 |
36 /* Impelemented in audio/android/SDL_androidaudio.c */ |
34 #include <android/log.h> |
|
35 #define LOG_TAG "SDL_android" |
|
36 //#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__) |
|
37 //#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__) |
|
38 #define LOGI(...) do {} while (false) |
|
39 #define LOGE(...) do {} while (false) |
|
40 |
|
41 |
|
42 /* Implemented in audio/android/SDL_androidaudio.c */ |
37 extern void Android_RunAudioThread(); |
43 extern void Android_RunAudioThread(); |
38 } // C |
44 } // C |
39 |
45 |
40 /******************************************************************************* |
46 /******************************************************************************* |
41 This file links the Java side of Android with libsdl |
47 This file links the Java side of Android with libsdl |
61 static jmethodID midAudioWriteByteBuffer; |
68 static jmethodID midAudioWriteByteBuffer; |
62 static jmethodID midAudioQuit; |
69 static jmethodID midAudioQuit; |
63 |
70 |
64 // Accelerometer data storage |
71 // Accelerometer data storage |
65 static float fLastAccelerometer[3]; |
72 static float fLastAccelerometer[3]; |
66 |
73 static bool bHasNewData; |
67 |
74 |
68 /******************************************************************************* |
75 /******************************************************************************* |
69 Functions called by JNI |
76 Functions called by JNI |
70 *******************************************************************************/ |
77 *******************************************************************************/ |
71 |
78 |
72 // Library init |
79 // Library init |
73 extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved) |
80 extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved) |
74 { |
81 { |
|
82 JNIEnv *env; |
|
83 mJavaVM = vm; |
|
84 LOGI("JNI_OnLoad called"); |
|
85 if (mJavaVM->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) { |
|
86 LOGE("Failed to get the environment using GetEnv()"); |
|
87 return -1; |
|
88 } |
|
89 |
75 return JNI_VERSION_1_4; |
90 return JNI_VERSION_1_4; |
76 } |
91 } |
77 |
92 |
78 // Called before SDL_main() to initialize JNI bindings |
93 // Called before SDL_main() to initialize JNI bindings |
79 extern "C" void SDL_Android_Init(JNIEnv* env, jclass cls) |
94 extern "C" void SDL_Android_Init(JNIEnv* env, jclass cls) |
80 { |
95 { |
81 __android_log_print(ANDROID_LOG_INFO, "SDL", "SDL_Android_Init()"); |
96 __android_log_print(ANDROID_LOG_INFO, "SDL", "SDL_Android_Init()"); |
82 |
97 |
83 mEnv = env; |
98 mEnv = env; |
84 mActivityClass = cls; |
99 mActivityClass = (jclass)env->NewGlobalRef(cls); |
85 |
100 |
86 midCreateGLContext = mEnv->GetStaticMethodID(mActivityClass, |
101 midCreateGLContext = mEnv->GetStaticMethodID(mActivityClass, |
87 "createGLContext","(II)Z"); |
102 "createGLContext","(II)Z"); |
88 midFlipBuffers = mEnv->GetStaticMethodID(mActivityClass, |
103 midFlipBuffers = mEnv->GetStaticMethodID(mActivityClass, |
89 "flipBuffers","()V"); |
104 "flipBuffers","()V"); |
138 JNIEnv* env, jclass jcls, |
156 JNIEnv* env, jclass jcls, |
139 jfloat x, jfloat y, jfloat z) |
157 jfloat x, jfloat y, jfloat z) |
140 { |
158 { |
141 fLastAccelerometer[0] = x; |
159 fLastAccelerometer[0] = x; |
142 fLastAccelerometer[1] = y; |
160 fLastAccelerometer[1] = y; |
143 fLastAccelerometer[2] = z; |
161 fLastAccelerometer[2] = z; |
|
162 bHasNewData = true; |
144 } |
163 } |
145 |
164 |
146 // Quit |
165 // Quit |
147 extern "C" void Java_org_hedgewars_hedgeroid_SDLActivity_nativeQuit( |
166 extern "C" void Java_org_hedgewars_hedgeroid_SDLActivity_nativeQuit( |
148 JNIEnv* env, jclass cls) |
167 JNIEnv* env, jclass cls) |
149 { |
168 { |
150 // Inject a SDL_QUIT event |
169 // Inject a SDL_QUIT event |
151 SDL_SendQuit(); |
170 SDL_SendQuit(); |
152 } |
171 } |
153 |
172 |
154 extern "C" void Java_org_hedgewars_hedgeroid_SDLActivity_nativeRunAudioThread( |
173 // Pause |
|
174 extern "C" void Java_org_libsdl_app_SDLActivity_nativePause( |
|
175 JNIEnv* env, jclass cls) |
|
176 { |
|
177 if (Android_Window) { |
|
178 SDL_SendWindowEvent(Android_Window, SDL_WINDOWEVENT_FOCUS_LOST, 0, 0); |
|
179 SDL_SendWindowEvent(Android_Window, SDL_WINDOWEVENT_MINIMIZED, 0, 0); |
|
180 } |
|
181 } |
|
182 |
|
183 // Resume |
|
184 extern "C" void Java_org_libsdl_app_SDLActivity_nativeResume( |
|
185 JNIEnv* env, jclass cls) |
|
186 { |
|
187 if (Android_Window) { |
|
188 SDL_SendWindowEvent(Android_Window, SDL_WINDOWEVENT_FOCUS_GAINED, 0, 0); |
|
189 SDL_SendWindowEvent(Android_Window, SDL_WINDOWEVENT_RESTORED, 0, 0); |
|
190 } |
|
191 } |
|
192 |
|
193 extern "C" void Java_org_libsdl_app_SDLActivity_nativeRunAudioThread( |
155 JNIEnv* env, jclass cls) |
194 JNIEnv* env, jclass cls) |
156 { |
195 { |
157 /* This is the audio thread, with a different environment */ |
196 /* This is the audio thread, with a different environment */ |
158 mAudioEnv = env; |
197 mAudioEnv = env; |
159 |
198 |
206 |
253 |
207 extern "C" int Android_JNI_OpenAudioDevice(int sampleRate, int is16Bit, int channelCount, int desiredBufferFrames) |
254 extern "C" int Android_JNI_OpenAudioDevice(int sampleRate, int is16Bit, int channelCount, int desiredBufferFrames) |
208 { |
255 { |
209 int audioBufferFrames; |
256 int audioBufferFrames; |
210 |
257 |
|
258 int status; |
|
259 JNIEnv *env; |
|
260 static bool isAttached = false; |
|
261 status = mJavaVM->GetEnv((void **) &env, JNI_VERSION_1_4); |
|
262 if(status < 0) { |
|
263 LOGE("callback_handler: failed to get JNI environment, assuming native thread"); |
|
264 status = mJavaVM->AttachCurrentThread(&env, NULL); |
|
265 if(status < 0) { |
|
266 LOGE("callback_handler: failed to attach current thread"); |
|
267 return 0; |
|
268 } |
|
269 isAttached = true; |
|
270 } |
|
271 |
|
272 |
211 __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "SDL audio: opening device"); |
273 __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "SDL audio: opening device"); |
212 audioBuffer16Bit = is16Bit; |
274 audioBuffer16Bit = is16Bit; |
213 audioBufferStereo = channelCount > 1; |
275 audioBufferStereo = channelCount > 1; |
214 |
276 |
215 audioBuffer = mEnv->CallStaticObjectMethod(mActivityClass, midAudioInit, sampleRate, audioBuffer16Bit, audioBufferStereo, desiredBufferFrames); |
277 audioBuffer = env->CallStaticObjectMethod(mActivityClass, midAudioInit, sampleRate, audioBuffer16Bit, audioBufferStereo, desiredBufferFrames); |
216 |
278 |
217 if (audioBuffer == NULL) { |
279 if (audioBuffer == NULL) { |
218 __android_log_print(ANDROID_LOG_WARN, "SDL", "SDL audio: didn't get back a good audio buffer!"); |
280 __android_log_print(ANDROID_LOG_WARN, "SDL", "SDL audio: didn't get back a good audio buffer!"); |
219 return 0; |
281 return 0; |
220 } |
282 } |
221 audioBuffer = mEnv->NewGlobalRef(audioBuffer); |
283 audioBuffer = env->NewGlobalRef(audioBuffer); |
222 |
284 |
223 jboolean isCopy = JNI_FALSE; |
285 jboolean isCopy = JNI_FALSE; |
224 if (audioBuffer16Bit) { |
286 if (audioBuffer16Bit) { |
225 audioBufferPinned = mEnv->GetShortArrayElements((jshortArray)audioBuffer, &isCopy); |
287 audioBufferPinned = env->GetShortArrayElements((jshortArray)audioBuffer, &isCopy); |
226 audioBufferFrames = mEnv->GetArrayLength((jshortArray)audioBuffer); |
288 audioBufferFrames = env->GetArrayLength((jshortArray)audioBuffer); |
227 } else { |
289 } else { |
228 audioBufferPinned = mEnv->GetByteArrayElements((jbyteArray)audioBuffer, &isCopy); |
290 audioBufferPinned = env->GetByteArrayElements((jbyteArray)audioBuffer, &isCopy); |
229 audioBufferFrames = mEnv->GetArrayLength((jbyteArray)audioBuffer); |
291 audioBufferFrames = env->GetArrayLength((jbyteArray)audioBuffer); |
230 } |
292 } |
231 if (audioBufferStereo) { |
293 if (audioBufferStereo) { |
232 audioBufferFrames /= 2; |
294 audioBufferFrames /= 2; |
|
295 } |
|
296 |
|
297 if (isAttached) { |
|
298 mJavaVM->DetachCurrentThread(); |
233 } |
299 } |
234 |
300 |
235 return audioBufferFrames; |
301 return audioBufferFrames; |
236 } |
302 } |
237 |
303 |
253 /* JNI_COMMIT means the changes are committed to the VM but the buffer remains pinned */ |
319 /* JNI_COMMIT means the changes are committed to the VM but the buffer remains pinned */ |
254 } |
320 } |
255 |
321 |
256 extern "C" void Android_JNI_CloseAudioDevice() |
322 extern "C" void Android_JNI_CloseAudioDevice() |
257 { |
323 { |
258 mEnv->CallStaticVoidMethod(mActivityClass, midAudioQuit); |
324 int status; |
|
325 JNIEnv *env; |
|
326 static bool isAttached = false; |
|
327 status = mJavaVM->GetEnv((void **) &env, JNI_VERSION_1_4); |
|
328 if(status < 0) { |
|
329 LOGE("callback_handler: failed to get JNI environment, assuming native thread"); |
|
330 status = mJavaVM->AttachCurrentThread(&env, NULL); |
|
331 if(status < 0) { |
|
332 LOGE("callback_handler: failed to attach current thread"); |
|
333 return; |
|
334 } |
|
335 isAttached = true; |
|
336 } |
|
337 |
|
338 env->CallStaticVoidMethod(mActivityClass, midAudioQuit); |
259 |
339 |
260 if (audioBuffer) { |
340 if (audioBuffer) { |
261 mEnv->DeleteGlobalRef(audioBuffer); |
341 env->DeleteGlobalRef(audioBuffer); |
262 audioBuffer = NULL; |
342 audioBuffer = NULL; |
263 audioBufferPinned = NULL; |
343 audioBufferPinned = NULL; |
264 } |
344 } |
265 } |
345 |
|
346 if (isAttached) { |
|
347 mJavaVM->DetachCurrentThread(); |
|
348 } |
|
349 } |
|
350 |
|
351 // Test for an exception and call SDL_SetError with its detail if one occurs |
|
352 static bool Android_JNI_ExceptionOccurred() |
|
353 { |
|
354 jthrowable exception = mEnv->ExceptionOccurred(); |
|
355 if (exception != NULL) { |
|
356 jmethodID mid; |
|
357 |
|
358 // Until this happens most JNI operations have undefined behaviour |
|
359 mEnv->ExceptionClear(); |
|
360 |
|
361 jclass exceptionClass = mEnv->GetObjectClass(exception); |
|
362 jclass classClass = mEnv->FindClass("java/lang/Class"); |
|
363 |
|
364 mid = mEnv->GetMethodID(classClass, "getName", "()Ljava/lang/String;"); |
|
365 jstring exceptionName = (jstring)mEnv->CallObjectMethod(exceptionClass, mid); |
|
366 const char* exceptionNameUTF8 = mEnv->GetStringUTFChars(exceptionName, 0); |
|
367 |
|
368 mid = mEnv->GetMethodID(exceptionClass, "getMessage", "()Ljava/lang/String;"); |
|
369 jstring exceptionMessage = (jstring)mEnv->CallObjectMethod(exception, mid); |
|
370 |
|
371 if (exceptionMessage != NULL) { |
|
372 const char* exceptionMessageUTF8 = mEnv->GetStringUTFChars( |
|
373 exceptionMessage, 0); |
|
374 SDL_SetError("%s: %s", exceptionNameUTF8, exceptionMessageUTF8); |
|
375 mEnv->ReleaseStringUTFChars(exceptionMessage, exceptionMessageUTF8); |
|
376 mEnv->DeleteLocalRef(exceptionMessage); |
|
377 } else { |
|
378 SDL_SetError("%s", exceptionNameUTF8); |
|
379 } |
|
380 |
|
381 mEnv->ReleaseStringUTFChars(exceptionName, exceptionNameUTF8); |
|
382 mEnv->DeleteLocalRef(exceptionName); |
|
383 mEnv->DeleteLocalRef(classClass); |
|
384 mEnv->DeleteLocalRef(exceptionClass); |
|
385 mEnv->DeleteLocalRef(exception); |
|
386 |
|
387 return true; |
|
388 } |
|
389 |
|
390 return false; |
|
391 } |
|
392 |
|
393 static int Android_JNI_FileOpen(SDL_RWops* ctx) |
|
394 { |
|
395 int result = 0; |
|
396 |
|
397 jmethodID mid; |
|
398 jobject context; |
|
399 jobject assetManager; |
|
400 jobject inputStream; |
|
401 jclass channels; |
|
402 jobject readableByteChannel; |
|
403 jstring fileNameJString; |
|
404 |
|
405 bool allocatedLocalFrame = false; |
|
406 |
|
407 if (mEnv->PushLocalFrame(16) < 0) { |
|
408 SDL_SetError("Failed to allocate enough JVM local references"); |
|
409 goto failure; |
|
410 } else { |
|
411 allocatedLocalFrame = true; |
|
412 } |
|
413 |
|
414 fileNameJString = (jstring)ctx->hidden.androidio.fileName; |
|
415 |
|
416 // context = SDLActivity.getContext(); |
|
417 mid = mEnv->GetStaticMethodID(mActivityClass, |
|
418 "getContext","()Landroid/content/Context;"); |
|
419 context = mEnv->CallStaticObjectMethod(mActivityClass, mid); |
|
420 |
|
421 // assetManager = context.getAssets(); |
|
422 mid = mEnv->GetMethodID(mEnv->GetObjectClass(context), |
|
423 "getAssets", "()Landroid/content/res/AssetManager;"); |
|
424 assetManager = mEnv->CallObjectMethod(context, mid); |
|
425 |
|
426 // inputStream = assetManager.open(<filename>); |
|
427 mid = mEnv->GetMethodID(mEnv->GetObjectClass(assetManager), |
|
428 "open", "(Ljava/lang/String;)Ljava/io/InputStream;"); |
|
429 inputStream = mEnv->CallObjectMethod(assetManager, mid, fileNameJString); |
|
430 if (Android_JNI_ExceptionOccurred()) { |
|
431 goto failure; |
|
432 } |
|
433 |
|
434 ctx->hidden.androidio.inputStream = inputStream; |
|
435 ctx->hidden.androidio.inputStreamRef = mEnv->NewGlobalRef(inputStream); |
|
436 |
|
437 // Despite all the visible documentation on [Asset]InputStream claiming |
|
438 // that the .available() method is not guaranteed to return the entire file |
|
439 // size, comments in <sdk>/samples/<ver>/ApiDemos/src/com/example/ ... |
|
440 // android/apis/content/ReadAsset.java imply that Android's |
|
441 // AssetInputStream.available() /will/ always return the total file size |
|
442 |
|
443 // size = inputStream.available(); |
|
444 mid = mEnv->GetMethodID(mEnv->GetObjectClass(inputStream), |
|
445 "available", "()I"); |
|
446 ctx->hidden.androidio.size = mEnv->CallIntMethod(inputStream, mid); |
|
447 if (Android_JNI_ExceptionOccurred()) { |
|
448 goto failure; |
|
449 } |
|
450 |
|
451 // readableByteChannel = Channels.newChannel(inputStream); |
|
452 channels = mEnv->FindClass("java/nio/channels/Channels"); |
|
453 mid = mEnv->GetStaticMethodID(channels, |
|
454 "newChannel", |
|
455 "(Ljava/io/InputStream;)Ljava/nio/channels/ReadableByteChannel;"); |
|
456 readableByteChannel = mEnv->CallStaticObjectMethod( |
|
457 channels, mid, inputStream); |
|
458 if (Android_JNI_ExceptionOccurred()) { |
|
459 goto failure; |
|
460 } |
|
461 |
|
462 ctx->hidden.androidio.readableByteChannel = readableByteChannel; |
|
463 ctx->hidden.androidio.readableByteChannelRef = |
|
464 mEnv->NewGlobalRef(readableByteChannel); |
|
465 |
|
466 // Store .read id for reading purposes |
|
467 mid = mEnv->GetMethodID(mEnv->GetObjectClass(readableByteChannel), |
|
468 "read", "(Ljava/nio/ByteBuffer;)I"); |
|
469 ctx->hidden.androidio.readMethod = mid; |
|
470 |
|
471 ctx->hidden.androidio.position = 0; |
|
472 |
|
473 if (false) { |
|
474 failure: |
|
475 result = -1; |
|
476 |
|
477 mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.fileNameRef); |
|
478 |
|
479 if(ctx->hidden.androidio.inputStreamRef != NULL) { |
|
480 mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.inputStreamRef); |
|
481 } |
|
482 } |
|
483 |
|
484 if (allocatedLocalFrame) { |
|
485 mEnv->PopLocalFrame(NULL); |
|
486 } |
|
487 |
|
488 return result; |
|
489 } |
|
490 |
|
491 extern "C" int Android_JNI_FileOpen(SDL_RWops* ctx, |
|
492 const char* fileName, const char*) |
|
493 { |
|
494 if (!ctx) { |
|
495 return -1; |
|
496 } |
|
497 |
|
498 jstring fileNameJString = mEnv->NewStringUTF(fileName); |
|
499 ctx->hidden.androidio.fileName = fileNameJString; |
|
500 ctx->hidden.androidio.fileNameRef = mEnv->NewGlobalRef(fileNameJString); |
|
501 ctx->hidden.androidio.inputStreamRef = NULL; |
|
502 mEnv->DeleteLocalRef(fileNameJString); |
|
503 |
|
504 return Android_JNI_FileOpen(ctx); |
|
505 } |
|
506 |
|
507 extern "C" size_t Android_JNI_FileRead(SDL_RWops* ctx, void* buffer, |
|
508 size_t size, size_t maxnum) |
|
509 { |
|
510 int bytesRemaining = size * maxnum; |
|
511 int bytesRead = 0; |
|
512 |
|
513 jobject readableByteChannel = (jobject)ctx->hidden.androidio.readableByteChannel; |
|
514 jmethodID readMethod = (jmethodID)ctx->hidden.androidio.readMethod; |
|
515 jobject byteBuffer = mEnv->NewDirectByteBuffer(buffer, bytesRemaining); |
|
516 |
|
517 while (bytesRemaining > 0) { |
|
518 // result = readableByteChannel.read(...); |
|
519 int result = mEnv->CallIntMethod(readableByteChannel, readMethod, byteBuffer); |
|
520 |
|
521 if (Android_JNI_ExceptionOccurred()) { |
|
522 mEnv->DeleteLocalRef(byteBuffer); |
|
523 return 0; |
|
524 } |
|
525 |
|
526 if (result < 0) { |
|
527 break; |
|
528 } |
|
529 |
|
530 bytesRemaining -= result; |
|
531 bytesRead += result; |
|
532 ctx->hidden.androidio.position += result; |
|
533 } |
|
534 |
|
535 mEnv->DeleteLocalRef(byteBuffer); |
|
536 |
|
537 return bytesRead / size; |
|
538 } |
|
539 |
|
540 extern "C" size_t Android_JNI_FileWrite(SDL_RWops* ctx, const void* buffer, |
|
541 size_t size, size_t num) |
|
542 { |
|
543 SDL_SetError("Cannot write to Android package filesystem"); |
|
544 return 0; |
|
545 } |
|
546 |
|
547 static int Android_JNI_FileClose(SDL_RWops* ctx, bool release) |
|
548 { |
|
549 int result = 0; |
|
550 |
|
551 if (ctx) { |
|
552 if (release) { |
|
553 mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.fileNameRef); |
|
554 } |
|
555 |
|
556 jobject inputStream = (jobject)ctx->hidden.androidio.inputStream; |
|
557 |
|
558 // inputStream.close(); |
|
559 jmethodID mid = mEnv->GetMethodID(mEnv->GetObjectClass(inputStream), |
|
560 "close", "()V"); |
|
561 mEnv->CallVoidMethod(inputStream, mid); |
|
562 mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.inputStreamRef); |
|
563 mEnv->DeleteGlobalRef((jobject)ctx->hidden.androidio.readableByteChannelRef); |
|
564 if (Android_JNI_ExceptionOccurred()) { |
|
565 result = -1; |
|
566 } |
|
567 |
|
568 if (release) { |
|
569 SDL_FreeRW(ctx); |
|
570 } |
|
571 } |
|
572 |
|
573 return result; |
|
574 } |
|
575 |
|
576 |
|
577 extern "C" long Android_JNI_FileSeek(SDL_RWops* ctx, long offset, int whence) |
|
578 { |
|
579 long newPosition; |
|
580 |
|
581 switch (whence) { |
|
582 case RW_SEEK_SET: |
|
583 newPosition = offset; |
|
584 break; |
|
585 case RW_SEEK_CUR: |
|
586 newPosition = ctx->hidden.androidio.position + offset; |
|
587 break; |
|
588 case RW_SEEK_END: |
|
589 newPosition = ctx->hidden.androidio.size + offset; |
|
590 break; |
|
591 default: |
|
592 SDL_SetError("Unknown value for 'whence'"); |
|
593 return -1; |
|
594 } |
|
595 if (newPosition < 0) { |
|
596 newPosition = 0; |
|
597 } |
|
598 if (newPosition > ctx->hidden.androidio.size) { |
|
599 newPosition = ctx->hidden.androidio.size; |
|
600 } |
|
601 |
|
602 long movement = newPosition - ctx->hidden.androidio.position; |
|
603 jobject inputStream = (jobject)ctx->hidden.androidio.inputStream; |
|
604 |
|
605 if (movement > 0) { |
|
606 unsigned char buffer[1024]; |
|
607 |
|
608 // The easy case where we're seeking forwards |
|
609 while (movement > 0) { |
|
610 long amount = (long) sizeof (buffer); |
|
611 if (amount > movement) { |
|
612 amount = movement; |
|
613 } |
|
614 size_t result = Android_JNI_FileRead(ctx, buffer, 1, amount); |
|
615 |
|
616 if (result <= 0) { |
|
617 // Failed to read/skip the required amount, so fail |
|
618 return -1; |
|
619 } |
|
620 |
|
621 movement -= result; |
|
622 } |
|
623 } else if (movement < 0) { |
|
624 // We can't seek backwards so we have to reopen the file and seek |
|
625 // forwards which obviously isn't very efficient |
|
626 Android_JNI_FileClose(ctx, false); |
|
627 Android_JNI_FileOpen(ctx); |
|
628 Android_JNI_FileSeek(ctx, newPosition, RW_SEEK_SET); |
|
629 } |
|
630 |
|
631 ctx->hidden.androidio.position = newPosition; |
|
632 |
|
633 return ctx->hidden.androidio.position; |
|
634 } |
|
635 |
|
636 extern "C" int Android_JNI_FileClose(SDL_RWops* ctx) |
|
637 { |
|
638 return Android_JNI_FileClose(ctx, true); |
|
639 } |
|
640 |
|
641 #endif /* __ANDROID__ */ |
266 |
642 |
267 /* vi: set ts=4 sw=4 expandtab: */ |
643 /* vi: set ts=4 sw=4 expandtab: */ |