[Scummvm-cvs-logs] SF.net SVN: scummvm:[49449] scummvm/trunk

fingolfin at users.sourceforge.net fingolfin at users.sourceforge.net
Sun Jun 6 11:34:37 CEST 2010


Revision: 49449
          http://scummvm.svn.sourceforge.net/scummvm/?rev=49449&view=rev
Author:   fingolfin
Date:     2010-06-06 09:34:36 +0000 (Sun, 06 Jun 2010)

Log Message:
-----------
Add Android backend from patch #2603856

Modified Paths:
--------------
    scummvm/trunk/base/commandLine.cpp
    scummvm/trunk/common/textconsole.cpp
    scummvm/trunk/configure
    scummvm/trunk/sound/decoders/vorbis.cpp
    scummvm/trunk/tools/update-version.pl

Added Paths:
-----------
    scummvm/trunk/backends/platform/android/
    scummvm/trunk/backends/platform/android/README.build
    scummvm/trunk/backends/platform/android/android.cpp
    scummvm/trunk/backends/platform/android/android.mk
    scummvm/trunk/backends/platform/android/asset-archive.cpp
    scummvm/trunk/backends/platform/android/asset-archive.h
    scummvm/trunk/backends/platform/android/module.mk
    scummvm/trunk/backends/platform/android/org/
    scummvm/trunk/backends/platform/android/org/inodes/
    scummvm/trunk/backends/platform/android/org/inodes/gus/
    scummvm/trunk/backends/platform/android/org/inodes/gus/scummvm/
    scummvm/trunk/backends/platform/android/org/inodes/gus/scummvm/EditableSurfaceView.java
    scummvm/trunk/backends/platform/android/org/inodes/gus/scummvm/Event.java
    scummvm/trunk/backends/platform/android/org/inodes/gus/scummvm/PluginProvider.java
    scummvm/trunk/backends/platform/android/org/inodes/gus/scummvm/ScummVM.java
    scummvm/trunk/backends/platform/android/org/inodes/gus/scummvm/ScummVMActivity.java
    scummvm/trunk/backends/platform/android/org/inodes/gus/scummvm/ScummVMApplication.java
    scummvm/trunk/backends/platform/android/org/inodes/gus/scummvm/Unpacker.java
    scummvm/trunk/backends/platform/android/scummvm-android-themeengine.patch
    scummvm/trunk/dists/android/
    scummvm/trunk/dists/android/mkmanifest.pl
    scummvm/trunk/dists/android/res/
    scummvm/trunk/dists/android/res/drawable/
    scummvm/trunk/dists/android/res/drawable/gradient.xml
    scummvm/trunk/dists/android/res/layout/
    scummvm/trunk/dists/android/res/layout/main.xml
    scummvm/trunk/dists/android/res/layout/splash.xml
    scummvm/trunk/dists/android/res/values/
    scummvm/trunk/dists/android/res/values/strings.xml

Added: scummvm/trunk/backends/platform/android/README.build
===================================================================
--- scummvm/trunk/backends/platform/android/README.build	                        (rev 0)
+++ scummvm/trunk/backends/platform/android/README.build	2010-06-06 09:34:36 UTC (rev 49449)
@@ -0,0 +1,84 @@
+Building the ScummVM Android port
+=================================
+
+You will need these things to build:
+1. Android EGL headers and library
+2. Android SDK
+3. An arm-android-eabi GCC toolchain
+
+In the example commands, we are going to build against the Android 1.5
+native ABI (but using the Android 1.6 SDK tools).  Other version
+combinations might/should be possible with a bit of tweaking.
+
+In detail:
+
+1. Android EGL headers and library
+
+You can build these from the full Android source, but it is far easier
+to just download the 3 Android EGL headers from here:
+ http://android.git.kernel.org/?p=platform/frameworks/base.git;a=tree;f=opengl/include/EGL;hb=HEAD
+ (copy them to a directory called "EGL" somewhere)
+
+... and grab libEGL.so off an existing phone/emulator:
+ adb pull /system/lib/libEGL.so /tmp
+
+2. Android SDK
+
+Download and install somewhere.
+
+3. arm-android-eabi GCC toolchain
+
+You have several choices for toolchains:
+
+- Use Google arm-eabi prebuilt toolchain.
+
+This is shipped with both the Android source release and Android NDK.
+The problem is that "arm-eabi-gcc" can't actually link anything
+successfully without extra command line flags.  To use this with the
+ScummVM configure/build environment you will need to create a family
+of shell wrapper scripts that convert "arm-android-eabi-foo" to
+"arm-eabi-foo -mandroid".
+
+For example, I use this script:
+ #!/bin/sh
+ exec arm-eabi-${0##*-} -mandroid -DANDROID "$@"
+
+... and create a family of symlinks/hardlinks pointing to it called
+arm-android-eabi-gcc, arm-android-eabi-g++, etc.  For tools that don't
+take a "-mandroid" argument - like arm-eabi-strip - I bypass the shell
+wrapper and just create an arm-android-eabi-strip symlink to the tool
+directly.
+
+- Build your own arm-android-eabi toolchain from GCC source.
+
+This is lots of fun.  I suggest my Android openembedded patches, see:
+  http://wiki.github.com/anguslees/openembedded-android/
+(You just need to have lots of disk space and type a few commands)
+If you get stuck, ask 
+
+Alternatively, do a websearch - there are several other cross-compile
+toolchains around.
+
+
+Building ScummVM
+================
+
+ export ANDROID_SDK=<root of Android SDK>
+
+ PATH=$ANDROID_SDK/platforms/android-1.6/tools:$ANDROID_SDK/tools:$PATH
+ # You also want to ensure your arm-android-eabi toolchain is in your $PATH
+
+ export ANDROID_TOP=<root of built Android source>
+
+ EGL_INC="-I<location of EGL/ header directory>"
+ EGL_LIBS="-L<location of libEGL.so>"
+
+ CPPFLAGS="$EGL_INC" \
+ LDFLAGS="-g $EGL_LIBS" \
+ ./configure --backend=android --host=android --enable-zlib #and any other flags
+ make scummvm.apk
+
+This will build a "monolithic" ScummVM package, with the engines
+statically linked in.  If you want to build separate engine packages,
+like on the market, add "--enable-plugins --default-dynamic" to
+configure and also make scummvm-engine-scumm.apk, etc.


Property changes on: scummvm/trunk/backends/platform/android/README.build
___________________________________________________________________
Added: svn:mime-type
   + text/plain
Added: svn:keywords
   + Date Rev Author URL Id
Added: svn:eol-style
   + native

Added: scummvm/trunk/backends/platform/android/android.cpp
===================================================================
--- scummvm/trunk/backends/platform/android/android.cpp	                        (rev 0)
+++ scummvm/trunk/backends/platform/android/android.cpp	2010-06-06 09:34:36 UTC (rev 49449)
@@ -0,0 +1,1413 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	 See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#include "backends/base-backend.h"
+#include "base/main.h"
+#include "graphics/surface.h"
+
+#include "backends/platform/android/video.h"
+
+#if defined(ANDROID_BACKEND)
+
+#define ANDROID_VERSION_GE(major,minor) \
+  (ANDROID_MAJOR_VERSION > (major) || \
+   (ANDROID_MAJOR_VERSION == (major) && ANDROID_MINOR_VERSION >= (minor)))
+
+#include <jni.h>
+
+#include <string.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <sys/time.h>
+#include <time.h>
+
+#include <GLES/gl.h>
+#include <GLES/glext.h>
+#include <EGL/egl.h>
+#include <android/log.h>
+
+#include "common/archive.h"
+#include "common/util.h"
+#include "common/rect.h"
+#include "common/queue.h"
+#include "common/mutex.h"
+#include "common/events.h"
+#include "common/config-manager.h"
+
+#include "backends/fs/posix/posix-fs-factory.h"
+#include "backends/keymapper/keymapper.h"
+#include "backends/saves/default/default-saves.h"
+#include "backends/timer/default/default-timer.h"
+#include "backends/plugins/posix/posix-provider.h"
+#include "sound/mixer_intern.h"
+
+#include "backends/platform/android/asset-archive.h"
+
+#undef LOG_TAG
+#define LOG_TAG "ScummVM"
+
+#if 0
+#define ENTER(args...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, args)
+#else
+#define ENTER(args...) /**/
+#endif
+
+// Fix JNIEXPORT declaration to actually do something useful
+#undef JNIEXPORT
+#define JNIEXPORT __attribute__ ((visibility("default")))
+
+static JavaVM *cached_jvm;
+static jfieldID FID_Event_type;
+static jfieldID FID_Event_synthetic;
+static jfieldID FID_Event_kbd_keycode;
+static jfieldID FID_Event_kbd_ascii;
+static jfieldID FID_Event_kbd_flags;
+static jfieldID FID_Event_mouse_x;
+static jfieldID FID_Event_mouse_y;
+static jfieldID FID_Event_mouse_relative;
+static jfieldID FID_ScummVM_nativeScummVM;
+static jmethodID MID_Object_wait;
+
+JNIEnv* JNU_GetEnv() {
+	JNIEnv* env;
+	bool version_unsupported =
+		cached_jvm->GetEnv((void**)&env, JNI_VERSION_1_2);
+	assert(! version_unsupported);
+	return env;
+}
+
+static void JNU_ThrowByName(JNIEnv* env, const char* name, const char* msg) {
+	jclass cls = env->FindClass(name);
+	// if cls is NULL, an exception has already been thrown
+	if (cls != NULL)
+		env->ThrowNew(cls, msg);
+	env->DeleteLocalRef(cls);
+}
+
+// floating point.	use sparingly.
+template <class T>
+static inline T scalef(T in, float numerator, float denominator) {
+	return static_cast<float>(in) * numerator / denominator;
+}
+
+static inline GLfixed xdiv(int numerator, int denominator) {
+	assert(numerator < (1<<16));
+	return (numerator << 16) / denominator;
+}
+
+#ifdef DYNAMIC_MODULES
+class AndroidPluginProvider : public POSIXPluginProvider {
+protected:
+	virtual void addCustomDirectories(Common::FSList &dirs) const;
+};
+#endif
+
+
+#if 0
+#define CHECK_GL_ERROR() checkGlError(__FILE__, __LINE__)
+static const char* getGlErrStr(GLenum error) {
+	switch (error) {
+	case GL_NO_ERROR:		   return "GL_NO_ERROR";
+	case GL_INVALID_ENUM:	   return "GL_INVALID_ENUM";
+	case GL_INVALID_OPERATION: return "GL_INVALID_OPERATION";
+	case GL_STACK_OVERFLOW:	   return "GL_STACK_OVERFLOW";
+	case GL_STACK_UNDERFLOW:   return "GL_STACK_UNDERFLOW";
+	case GL_OUT_OF_MEMORY:	   return "GL_OUT_OF_MEMORY";
+	}
+
+	static char buf[40];
+	snprintf(buf, sizeof(buf), "(Unknown GL error code 0x%x)", error);
+	return buf;
+}
+static void checkGlError(const char* file, int line) {
+	GLenum error = glGetError();
+	if (error != GL_NO_ERROR)
+		warning("%s:%d: GL error: %s", file, line, getGlErrStr(error));
+}
+#else
+#define CHECK_GL_ERROR() do {} while (false)
+#endif
+
+class OSystem_Android : public BaseBackend {
+private:
+	jobject _back_ptr;	// back pointer to (java) peer instance
+	jmethodID MID_displayMessageOnOSD;
+	jmethodID MID_setWindowCaption;
+	jmethodID MID_initBackend;
+	jmethodID MID_audioSampleRate;
+	jmethodID MID_showVirtualKeyboard;
+	jmethodID MID_getSysArchives;
+	jmethodID MID_getPluginDirectories;
+	jmethodID MID_setupScummVMSurface;
+	jmethodID MID_destroyScummVMSurface;
+
+	int _screen_changeid;
+	EGLDisplay _egl_display;
+	EGLSurface _egl_surface;
+	EGLint _egl_surface_width;
+	EGLint _egl_surface_height;
+
+	bool _force_redraw;
+
+	// Game layer
+	GLESPaletteTexture* _game_texture;
+	int _shake_offset;
+	bool _full_screen_dirty;
+	Common::Array<Common::Rect> _dirty_rects;
+
+	// Overlay layer
+	GLES4444Texture* _overlay_texture;
+	bool _show_overlay;
+
+	// Mouse layer
+	GLESPaletteATexture* _mouse_texture;
+	Common::Point _mouse_hotspot;
+	int _mouse_targetscale;
+	bool _show_mouse;
+	bool _use_mouse_palette;
+
+	Common::Queue<Common::Event> _event_queue;
+	MutexRef _event_queue_lock;
+
+	bool _timer_thread_exit;
+	pthread_t _timer_thread;
+	static void* timerThreadFunc(void* arg);
+
+	bool _virtkeybd_on;
+
+	Common::SaveFileManager *_savefile;
+	Audio::MixerImpl *_mixer;
+	Common::TimerManager *_timer;
+	FilesystemFactory *_fsFactory;
+	Common::Archive *_asset_archive;
+	timeval _startTime;
+
+	void setupScummVMSurface();
+	void destroyScummVMSurface();
+	void setupKeymapper();
+	void _setCursorPalette(const byte *colors, uint start, uint num);
+
+public:
+	OSystem_Android(jobject am);
+	virtual ~OSystem_Android();
+	bool initJavaHooks(JNIEnv* env, jobject self);
+
+	static OSystem_Android* fromJavaObject(JNIEnv* env, jobject obj);
+	virtual void initBackend();
+	void addPluginDirectories(Common::FSList &dirs) const;
+
+	virtual bool hasFeature(Feature f);
+	virtual void setFeatureState(Feature f, bool enable);
+	virtual bool getFeatureState(Feature f);
+	virtual const GraphicsMode *getSupportedGraphicsModes() const;
+	virtual int getDefaultGraphicsMode() const;
+	bool setGraphicsMode(const char *name);
+	virtual bool setGraphicsMode(int mode);
+	virtual int getGraphicsMode() const;
+	virtual void initSize(uint width, uint height,
+				  const Graphics::PixelFormat *format);
+	virtual int getScreenChangeID() const { return _screen_changeid; }
+	virtual int16 getHeight();
+	virtual int16 getWidth();
+	virtual void setPalette(const byte *colors, uint start, uint num);
+	virtual void grabPalette(byte *colors, uint start, uint num);
+	virtual void copyRectToScreen(const byte *buf, int pitch, int x, int y, int w, int h);
+	virtual void updateScreen();
+	virtual Graphics::Surface *lockScreen();
+	virtual void unlockScreen();
+	virtual void setShakePos(int shakeOffset);
+	virtual void fillScreen(uint32 col);
+	virtual void setFocusRectangle(const Common::Rect& rect);
+	virtual void clearFocusRectangle();
+
+	virtual void showOverlay();
+	virtual void hideOverlay();
+	virtual void clearOverlay();
+	virtual void grabOverlay(OverlayColor *buf, int pitch);
+	virtual void copyRectToOverlay(const OverlayColor *buf, int pitch, int x, int y, int w, int h);
+	virtual int16 getOverlayHeight();
+	virtual int16 getOverlayWidth();
+	virtual Graphics::PixelFormat getOverlayFormat() const {
+		// RGBA 4444
+		Graphics::PixelFormat format;
+		format.bytesPerPixel = 2;
+		format.rLoss = 8 - 4;
+		format.gLoss = 8 - 4;
+		format.bLoss = 8 - 4;
+		format.aLoss = 8 - 4;
+		format.rShift = 3*4;
+		format.gShift = 2*4;
+		format.bShift = 1*4;
+		format.aShift = 0*4;
+		return format;
+	}
+
+	virtual bool showMouse(bool visible);
+
+	virtual void warpMouse(int x, int y);
+	virtual void setMouseCursor(const byte *buf, uint w, uint h, int hotspotX, int hotspotY, uint32 keycolor, int cursorTargetScale, const Graphics::PixelFormat *format);
+	virtual void setCursorPalette(const byte *colors, uint start, uint num);
+	virtual void disableCursorPalette(bool disable);
+
+	virtual bool pollEvent(Common::Event &event);
+	void pushEvent(const Common::Event& event);
+	virtual uint32 getMillis();
+	virtual void delayMillis(uint msecs);
+
+	virtual MutexRef createMutex(void);
+	virtual void lockMutex(MutexRef mutex);
+	virtual void unlockMutex(MutexRef mutex);
+	virtual void deleteMutex(MutexRef mutex);
+
+	virtual void quit();
+
+	virtual void setWindowCaption(const char *caption);
+	virtual void displayMessageOnOSD(const char *msg);
+	virtual void showVirtualKeyboard(bool enable);
+
+	virtual Common::SaveFileManager *getSavefileManager();
+	virtual Audio::Mixer *getMixer();
+	virtual void getTimeAndDate(TimeDate &t) const;
+	virtual Common::TimerManager *getTimerManager();
+	virtual FilesystemFactory *getFilesystemFactory();
+	virtual void addSysArchivesToSearchSet(Common::SearchSet &s, int priority = 0);
+};
+
+OSystem_Android::OSystem_Android(jobject am)
+	: _back_ptr(0),
+	  _egl_display(EGL_NO_DISPLAY),
+	  _egl_surface(EGL_NO_SURFACE),
+	  _screen_changeid(0),
+	  _force_redraw(false),
+	  _game_texture(NULL),
+	  _overlay_texture(NULL),
+	  _mouse_texture(NULL),
+	  _use_mouse_palette(false),
+	  _show_mouse(false),
+	  _show_overlay(false),
+	  _savefile(0),
+	  _mixer(0),
+	  _timer(0),
+	  _fsFactory(new POSIXFilesystemFactory()),
+	  _asset_archive(new AndroidAssetArchive(am)),
+	  _shake_offset(0),
+	  _full_screen_dirty(false),
+	  _event_queue_lock(createMutex()) {
+}
+
+OSystem_Android::~OSystem_Android() {
+	ENTER("~OSystem_Android()");
+	delete _game_texture;
+	delete _overlay_texture;
+	delete _mouse_texture;
+	destroyScummVMSurface();
+	JNIEnv* env = JNU_GetEnv();
+	//env->DeleteWeakGlobalRef(_back_ptr);
+	env->DeleteGlobalRef(_back_ptr);
+	delete _savefile;
+	delete _mixer;
+	delete _timer;
+	delete _fsFactory;
+	delete _asset_archive;
+	deleteMutex(_event_queue_lock);
+}
+
+OSystem_Android* OSystem_Android::fromJavaObject(JNIEnv* env, jobject obj) {
+	jlong peer = env->GetLongField(obj, FID_ScummVM_nativeScummVM);
+	return (OSystem_Android*)peer;
+}
+
+bool OSystem_Android::initJavaHooks(JNIEnv* env, jobject self) {
+	// weak global ref to allow class to be unloaded
+	// ... except dalvik doesn't implement NewWeakGlobalRef (yet)
+	//_back_ptr = env->NewWeakGlobalRef(self);
+	_back_ptr = env->NewGlobalRef(self);
+
+	jclass cls = env->GetObjectClass(_back_ptr);
+
+#define FIND_METHOD(name, signature) do {						\
+		MID_ ## name = env->GetMethodID(cls, #name, signature); \
+		if (MID_ ## name == NULL)								\
+			return false;										\
+	} while (0)
+
+	FIND_METHOD(setWindowCaption, "(Ljava/lang/String;)V");
+	FIND_METHOD(displayMessageOnOSD, "(Ljava/lang/String;)V");
+	FIND_METHOD(initBackend, "()V");
+	FIND_METHOD(audioSampleRate, "()I");
+	FIND_METHOD(showVirtualKeyboard, "(Z)V");
+	FIND_METHOD(getSysArchives, "()[Ljava/lang/String;");
+	FIND_METHOD(getPluginDirectories, "()[Ljava/lang/String;");
+	FIND_METHOD(setupScummVMSurface, "()V");
+	FIND_METHOD(destroyScummVMSurface, "()V");
+
+#undef FIND_METHOD
+
+	return true;
+}
+
+static void ScummVM_create(JNIEnv* env, jobject self, jobject am) {
+	OSystem_Android* cpp_obj = new OSystem_Android(am);
+	if (!cpp_obj->initJavaHooks(env, self))
+		// Exception already thrown by initJavaHooks
+		return;
+
+	env->SetLongField(self, FID_ScummVM_nativeScummVM, (jlong)cpp_obj);
+
+#ifdef DYNAMIC_MODULES
+	PluginManager::instance().addPluginProvider(new AndroidPluginProvider());
+#endif
+}
+
+static void ScummVM_nativeDestroy(JNIEnv* env, jobject self) {
+	OSystem_Android* cpp_obj = OSystem_Android::fromJavaObject(env, self);
+	delete cpp_obj;
+}
+
+static void ScummVM_audioMixCallback(JNIEnv* env, jobject self,
+									 jbyteArray jbuf) {
+	OSystem_Android* cpp_obj = OSystem_Android::fromJavaObject(env, self);
+	jsize len = env->GetArrayLength(jbuf);
+	jbyte* buf = env->GetByteArrayElements(jbuf, NULL);
+	if (buf == NULL) {
+		warning("Unable to get Java audio byte array. Skipping.");
+		return;
+	}
+	Audio::MixerImpl* mixer =
+		static_cast<Audio::MixerImpl*>(cpp_obj->getMixer());
+	assert(mixer);
+	mixer->mixCallback(reinterpret_cast<byte*>(buf), len);
+	env->ReleaseByteArrayElements(jbuf, buf, 0);
+}
+
+static void ScummVM_setConfManInt(JNIEnv* env, jclass cls,
+				  jstring key_obj, jint value) {
+	ENTER("setConfManInt(%p, %d)", key_obj, (int)value);
+	const char* key = env->GetStringUTFChars(key_obj, NULL);
+	if (key == NULL)
+		return;
+	ConfMan.setInt(key, value);
+	env->ReleaseStringUTFChars(key_obj, key);
+}
+
+static void ScummVM_setConfManString(JNIEnv* env, jclass cls, jstring key_obj,
+					 jstring value_obj) {
+	ENTER("setConfManStr(%p, %p)", key_obj, value_obj);
+	const char* key = env->GetStringUTFChars(key_obj, NULL);
+	if (key == NULL)
+		return;
+	const char* value = env->GetStringUTFChars(value_obj, NULL);
+	if (value == NULL) {
+		env->ReleaseStringUTFChars(key_obj, key);
+		return;
+	}
+	ConfMan.set(key, value);
+	env->ReleaseStringUTFChars(value_obj, value);
+	env->ReleaseStringUTFChars(key_obj, key);
+}
+
+void* OSystem_Android::timerThreadFunc(void* arg) {
+	OSystem_Android* system = (OSystem_Android*)arg;
+	DefaultTimerManager* timer = (DefaultTimerManager*)(system->_timer);
+
+	struct timespec tv;
+	tv.tv_sec = 0;
+	tv.tv_nsec = 100 * 1000 * 1000;	// 100ms
+
+	while (!system->_timer_thread_exit) {
+		timer->handler();
+		nanosleep(&tv, NULL);
+	}
+
+	return NULL;
+}
+
+void OSystem_Android::initBackend() {
+	ENTER("initBackend()");
+	JNIEnv* env = JNU_GetEnv();
+
+	ConfMan.setInt("autosave_period", 0);
+	ConfMan.setInt("FM_medium_quality", true);
+
+	// must happen before creating TimerManager to avoid race in
+	// creating EventManager
+	setupKeymapper();
+
+	// BUG: "transient" ConfMan settings get nuked by the options
+	// screen.	Passing the savepath in this way makes it stick
+	// (via ConfMan.registerDefault)
+	_savefile = new DefaultSaveFileManager(ConfMan.get("savepath"));
+	_timer = new DefaultTimerManager();
+
+	gettimeofday(&_startTime, NULL);
+
+	jint sample_rate = env->CallIntMethod(_back_ptr, MID_audioSampleRate);
+	if (env->ExceptionCheck()) {
+		warning("Error finding audio sample rate - assuming 11025HZ");
+		env->ExceptionDescribe();
+		env->ExceptionClear();
+		sample_rate = 11025;
+	}
+	_mixer = new Audio::MixerImpl(this, sample_rate);
+	_mixer->setReady(true);
+
+	env->CallVoidMethod(_back_ptr, MID_initBackend);
+	if (env->ExceptionCheck()) {
+		error("Error in Java initBackend");
+		env->ExceptionDescribe();
+		env->ExceptionClear();
+	}
+
+	_timer_thread_exit = false;
+	pthread_create(&_timer_thread, NULL, timerThreadFunc, this);
+
+	OSystem::initBackend();
+
+	setupScummVMSurface();
+}
+
+void OSystem_Android::addPluginDirectories(Common::FSList &dirs) const {
+	ENTER("OSystem_Android::addPluginDirectories()");
+	JNIEnv* env = JNU_GetEnv();
+
+	jobjectArray array =
+		(jobjectArray)env->CallObjectMethod(_back_ptr, MID_getPluginDirectories);
+	if (env->ExceptionCheck()) {
+		warning("Error finding plugin directories");
+		env->ExceptionDescribe();
+		env->ExceptionClear();
+		return;
+	}
+
+	jsize size = env->GetArrayLength(array);
+	for (jsize i = 0; i < size; ++i) {
+		jstring path_obj = (jstring)env->GetObjectArrayElement(array, i);
+		if (path_obj == NULL)
+			continue;
+		const char* path = env->GetStringUTFChars(path_obj, NULL);
+		if (path == NULL) {
+			warning("Error getting string characters from plugin directory");
+			env->ExceptionClear();
+			env->DeleteLocalRef(path_obj);
+			continue;
+		}
+		dirs.push_back(Common::FSNode(path));
+		env->ReleaseStringUTFChars(path_obj, path);
+		env->DeleteLocalRef(path_obj);
+	}
+}
+
+bool OSystem_Android::hasFeature(Feature f) {
+	return (f == kFeatureCursorHasPalette ||
+		f == kFeatureVirtualKeyboard ||
+		f == kFeatureOverlaySupportsAlpha);
+}
+
+void OSystem_Android::setFeatureState(Feature f, bool enable) {
+	ENTER("setFeatureState(%d, %d)", f, enable);
+	switch (f) {
+	case kFeatureVirtualKeyboard:
+		_virtkeybd_on = enable;
+		showVirtualKeyboard(enable);
+		break;
+	default:
+		break;
+	}
+}
+
+bool OSystem_Android::getFeatureState(Feature f) {
+	switch (f) {
+	case kFeatureVirtualKeyboard:
+		return _virtkeybd_on;
+	default:
+		return false;
+	}
+}
+
+const OSystem::GraphicsMode* OSystem_Android::getSupportedGraphicsModes() const {
+	static const OSystem::GraphicsMode s_supportedGraphicsModes[] = {
+		{"default", "Default", 1},
+		{0, 0, 0},
+	};
+	return s_supportedGraphicsModes;
+}
+
+
+int OSystem_Android::getDefaultGraphicsMode() const {
+	return 1;
+}
+
+bool OSystem_Android::setGraphicsMode(const char *mode) {
+	ENTER("setGraphicsMode(%s)", mode);
+	return true;
+}
+
+bool OSystem_Android::setGraphicsMode(int mode) {
+	ENTER("setGraphicsMode(%d)", mode);
+	return true;
+}
+
+int OSystem_Android::getGraphicsMode() const {
+	return 1;
+}
+
+void OSystem_Android::setupScummVMSurface() {
+	JNIEnv* env = JNU_GetEnv();
+	env->CallVoidMethod(_back_ptr, MID_setupScummVMSurface);
+	if (env->ExceptionCheck())
+		return;
+
+	// EGL set up with a new surface.  Initialise OpenGLES context.
+
+	_egl_display = eglGetCurrentDisplay();
+	_egl_surface = eglGetCurrentSurface(EGL_DRAW);
+
+	static bool log_version = true;
+	if (log_version) {
+		__android_log_print(ANDROID_LOG_INFO, LOG_TAG,
+							"Using EGL %s (%s); GL %s/%s (%s)",
+							eglQueryString(_egl_display, EGL_VERSION),
+							eglQueryString(_egl_display, EGL_VENDOR),
+							glGetString(GL_VERSION),
+							glGetString(GL_RENDERER),
+							glGetString(GL_VENDOR));
+		log_version = false;		// only log this once
+	}
+
+	GLESTexture::initGLExtensions();
+
+	if (!eglQuerySurface(_egl_display, _egl_surface,
+						 EGL_WIDTH, &_egl_surface_width) ||
+		!eglQuerySurface(_egl_display, _egl_surface,
+						 EGL_HEIGHT, &_egl_surface_height)) {
+		JNU_ThrowByName(env, "java/lang/RuntimeException",
+						"Error fetching EGL surface width/height");
+		return;
+	}
+	__android_log_print(ANDROID_LOG_INFO, LOG_TAG,
+						"New surface is %dx%d",
+						_egl_surface_width, _egl_surface_height);
+
+	CHECK_GL_ERROR();
+
+	// Turn off anything that looks like 3D ;)
+	glDisable(GL_CULL_FACE);
+	glDisable(GL_DEPTH_TEST);
+	glDisable(GL_LIGHTING);
+	glDisable(GL_FOG);
+	glDisable(GL_DITHER);
+	glShadeModel(GL_FLAT);
+	glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST);
+
+	glEnable(GL_BLEND);
+	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+	glEnableClientState(GL_VERTEX_ARRAY);
+	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+
+	glEnable(GL_TEXTURE_2D);
+
+	if (!_game_texture)
+		_game_texture = new GLESPaletteTexture();
+	else
+		_game_texture->reinitGL();
+
+	if (!_overlay_texture)
+		_overlay_texture = new GLES4444Texture();
+	else
+		_overlay_texture->reinitGL();
+
+	if (!_mouse_texture)
+		_mouse_texture = new GLESPaletteATexture();
+	else
+		_mouse_texture->reinitGL();
+
+	glViewport(0, 0, _egl_surface_width, _egl_surface_height);
+	glMatrixMode(GL_PROJECTION);
+	glLoadIdentity();
+	glOrthof(0, _egl_surface_width, _egl_surface_height, 0, -1, 1);
+
+	glMatrixMode(GL_MODELVIEW);
+	glLoadIdentity();
+
+	CHECK_GL_ERROR();
+
+	_force_redraw = true;
+}
+
+void OSystem_Android::destroyScummVMSurface() {
+	_egl_surface = EGL_NO_SURFACE;
+	JNIEnv* env = JNU_GetEnv();
+	env->CallVoidMethod(_back_ptr, MID_destroyScummVMSurface);
+	// Can't use OpenGLES functions after this
+}
+
+void OSystem_Android::initSize(uint width, uint height,
+							   const Graphics::PixelFormat *format) {
+	ENTER("initSize(%d,%d,%p)", width, height, format);
+
+	_game_texture->allocBuffer(width, height);
+
+	// Cap at 320x200 or the ScummVM themes abort :/
+	GLuint overlay_width = MIN(_egl_surface_width, 320);
+	GLuint overlay_height = MIN(_egl_surface_height, 200);
+	_overlay_texture->allocBuffer(overlay_width, overlay_height);
+
+	// Don't know mouse size yet - it gets reallocated in
+	// setMouseCursor.	We need the palette allocated before
+	// setMouseCursor however, so just take a guess at the desired
+	// size (it's small).
+	_mouse_texture->allocBuffer(20, 20);
+}
+
+int16 OSystem_Android::getHeight() {
+	return _game_texture->height();
+}
+
+int16 OSystem_Android::getWidth() {
+	return _game_texture->width();
+}
+
+void OSystem_Android::setPalette(const byte* colors, uint start, uint num) {
+		ENTER("setPalette(%p, %u, %u)", colors, start, num);
+
+	if (!_use_mouse_palette)
+		_setCursorPalette(colors, start, num);
+
+	byte* palette = _game_texture->palette() + start*3;
+	do {
+		for (int i = 0; i < 3; ++i)
+			palette[i] = colors[i];
+		palette += 3;
+		colors += 4;
+	} while (--num);
+}	
+	
+void OSystem_Android::grabPalette(byte *colors, uint start, uint num) {
+		ENTER("grabPalette(%p, %u, %u)", colors, start, num);
+	const byte* palette = _game_texture->palette_const() + start*3;
+	do {
+		for (int i = 0; i < 3; ++i)
+			colors[i] = palette[i];
+		colors[3] = 0xff;  // alpha
+
+		palette += 3;
+		colors += 4;
+	} while (--num);
+}
+
+void OSystem_Android::copyRectToScreen(const byte *buf, int pitch,
+					   int x, int y, int w, int h) {
+	ENTER("copyRectToScreen(%p, %d, %d, %d, %d, %d)",
+		  buf, pitch, x, y, w, h);
+
+	_game_texture->updateBuffer(x, y, w, h, buf, pitch);
+}
+
+void OSystem_Android::updateScreen() {
+	//ENTER("updateScreen()");
+
+	if (!_force_redraw &&
+		!_game_texture->dirty() &&
+		!_overlay_texture->dirty() &&
+		!_mouse_texture->dirty())
+		return;
+
+	_force_redraw = false;
+
+	glPushMatrix();
+
+	if (_shake_offset != 0) {
+		// This is the only case where _game_texture doesn't
+		// cover the entire screen.
+		glClearColorx(0, 0, 0, 1 << 16);
+		glClear(GL_COLOR_BUFFER_BIT);
+
+		// Move everything up by _shake_offset (game) pixels
+		glTranslatex(0, -_shake_offset << 16, 0);
+	}
+
+	_game_texture->drawTexture(0, 0,
+				   _egl_surface_width, _egl_surface_height);
+
+	CHECK_GL_ERROR();
+
+	if (_show_overlay) {
+		_overlay_texture->drawTexture(0, 0,
+						  _egl_surface_width,
+						  _egl_surface_height);
+		CHECK_GL_ERROR();
+	}
+
+	if (_show_mouse) {
+		glPushMatrix();
+
+		glTranslatex(-_mouse_hotspot.x << 16,
+				 -_mouse_hotspot.y << 16,
+				 0);
+
+		// Scale up ScummVM -> OpenGL (pixel) coordinates
+		int texwidth, texheight;
+		if (_show_overlay) {
+			texwidth = getOverlayWidth();
+			texheight = getOverlayHeight();
+		} else {
+			texwidth = getWidth();
+			texheight = getHeight();
+		}
+		glScalex(xdiv(_egl_surface_width, texwidth),
+			 xdiv(_egl_surface_height, texheight),
+			 1 << 16);
+
+		// Note the extra half texel to position the mouse in
+		// the middle of the x,y square:
+		const Common::Point& mouse = getEventManager()->getMousePos();
+		glTranslatex((mouse.x << 16) | 1 << 15,
+					 (mouse.y << 16) | 1 << 15, 0);
+
+		// Mouse targetscale just seems to make the cursor way
+		// too big :/
+		//glScalex(_mouse_targetscale << 16, _mouse_targetscale << 16,
+		//	 1 << 16);
+
+		_mouse_texture->drawTexture();
+
+		glPopMatrix();
+	}
+
+	glPopMatrix();
+
+	CHECK_GL_ERROR();
+
+	if (!eglSwapBuffers(_egl_display, _egl_surface)) {
+		EGLint error = eglGetError();
+		warning("eglSwapBuffers exited with error 0x%x", error);
+		// Some errors mean we need to reinit GL
+		if (error == EGL_CONTEXT_LOST) {
+			destroyScummVMSurface();
+			setupScummVMSurface();
+		}
+	}
+}
+
+Graphics::Surface *OSystem_Android::lockScreen() {
+	ENTER("lockScreen()");
+	Graphics::Surface* surface = _game_texture->surface();
+	assert(surface->pixels);
+	return surface;
+}
+
+void OSystem_Android::unlockScreen() {
+	ENTER("unlockScreen()");
+	assert(_game_texture->dirty());
+}
+
+void OSystem_Android::setShakePos(int shake_offset) {
+	ENTER("setShakePos(%d)", shake_offset);
+	if (_shake_offset != shake_offset) {
+		_shake_offset = shake_offset;
+		_force_redraw = true;
+	}
+}
+
+void OSystem_Android::fillScreen(uint32 col) {
+	ENTER("fillScreen(%u)", col);
+	assert(col < 256);
+	_game_texture->fillBuffer(col);
+}
+
+void OSystem_Android::setFocusRectangle(const Common::Rect& rect) {
+	ENTER("setFocusRectangle(%d,%d,%d,%d)",
+		  rect.left, rect.top, rect.right, rect.bottom);
+#if 0
+	glMatrixMode(GL_PROJECTION);
+	glLoadIdentity();
+	glOrthof(rect.left, rect.right, rect.top, rect.bottom, 0, 1);
+	glMatrixMode(GL_MODELVIEW);
+
+	_force_redraw = true;
+#endif
+}
+
+void OSystem_Android::clearFocusRectangle() {
+	ENTER("clearFocusRectangle()");
+#if 0
+	glMatrixMode(GL_PROJECTION);
+	glLoadIdentity();
+	glOrthof(0, _egl_surface_width, _egl_surface_height, 0, -1, 1);
+	glMatrixMode(GL_MODELVIEW);
+
+	_force_redraw = true;
+#endif
+}
+
+void OSystem_Android::showOverlay() {
+	ENTER("showOverlay()");
+	_show_overlay = true;
+	_force_redraw = true;
+}
+
+void OSystem_Android::hideOverlay() {
+	ENTER("hideOverlay()");
+	_show_overlay = false;
+	_force_redraw = true;
+}
+
+void OSystem_Android::clearOverlay() {
+	ENTER("clearOverlay()");
+	_overlay_texture->fillBuffer(0);
+}
+
+void OSystem_Android::grabOverlay(OverlayColor *buf, int pitch) {
+	ENTER("grabOverlay(%p, %d)", buf, pitch);
+	// We support overlay alpha blending, so the pixel data here
+	// shouldn't actually be used.	Let's fill it with zeros, I'm sure
+	// it will be fine...
+	const Graphics::Surface* surface = _overlay_texture->surface_const();
+	assert(surface->bytesPerPixel == sizeof(buf[0]));
+	int h = surface->h;
+	do {
+		memset(buf, 0, surface->w * sizeof(buf[0]));
+		buf += pitch;  // This 'pitch' is pixels not bytes
+	} while (--h);
+}
+
+void OSystem_Android::copyRectToOverlay(const OverlayColor *buf, int pitch,
+					int x, int y, int w, int h) {
+	ENTER("copyRectToOverlay(%p, %d, %d, %d, %d, %d)",
+		 buf, pitch, x, y, w, h);
+	const Graphics::Surface* surface = _overlay_texture->surface_const();
+	assert(surface->bytesPerPixel == sizeof(buf[0]));
+
+	// This 'pitch' is pixels not bytes
+	_overlay_texture->updateBuffer(x, y, w, h, buf, pitch * sizeof(buf[0]));
+}
+
+int16 OSystem_Android::getOverlayHeight() {
+	return _overlay_texture->height();
+}
+
+int16 OSystem_Android::getOverlayWidth() {
+	return _overlay_texture->width();
+}
+
+bool OSystem_Android::showMouse(bool visible) {
+	ENTER("showMouse(%d)", visible);
+	_show_mouse = visible;
+	return true;
+}
+
+void OSystem_Android::warpMouse(int x, int y) {
+	ENTER("warpMouse(%d, %d)", x, y);
+	// We use only the eventmanager's idea of the current mouse
+	// position, so there is nothing extra to do here.
+}
+
+void OSystem_Android::setMouseCursor(const byte *buf, uint w, uint h,
+					 int hotspotX, int hotspotY,
+					 uint32 keycolor, int cursorTargetScale,
+					 const Graphics::PixelFormat *format) {
+	ENTER("setMouseCursor(%p, %u, %u, %d, %d, %d, %d, %p)",
+		  buf, w, h, hotspotX, hotspotY, (int)keycolor, cursorTargetScale,
+		  format);
+
+	assert(keycolor < 256);
+
+	_mouse_texture->allocBuffer(w, h);
+
+	// Update palette alpha based on keycolor
+	byte* palette = _mouse_texture->palette();
+	int i = 256;
+	do {
+		palette[3] = 0xff;
+		palette += 4;
+	} while (--i);
+	palette = _mouse_texture->palette();
+	palette[keycolor*4 + 3] = 0x00;
+	_mouse_texture->updateBuffer(0, 0, w, h, buf, w);
+
+	_mouse_hotspot = Common::Point(hotspotX, hotspotY);
+	_mouse_targetscale = cursorTargetScale;
+}
+
+void OSystem_Android::_setCursorPalette(const byte *colors,
+					uint start, uint num) {
+	byte* palette = _mouse_texture->palette() + start*4;
+	do {
+		for (int i = 0; i < 3; ++i)
+			palette[i] = colors[i];
+		// Leave alpha untouched to preserve keycolor
+
+		palette += 4;
+		colors += 4;
+	} while (--num);
+}
+
+void OSystem_Android::setCursorPalette(const byte *colors,
+					   uint start, uint num) {
+	ENTER("setCursorPalette(%p, %u, %u)", colors, start, num);
+	_setCursorPalette(colors, start, num);
+	_use_mouse_palette = true;
+}
+
+void OSystem_Android::disableCursorPalette(bool disable) {
+	ENTER("disableCursorPalette(%d)", disable);
+	_use_mouse_palette = !disable;
+}
+
+void OSystem_Android::setupKeymapper() {
+#ifdef ENABLE_KEYMAPPER
+	using namespace Common;
+
+	Keymapper *mapper = getEventManager()->getKeymapper();
+
+	HardwareKeySet *keySet = new HardwareKeySet();
+	keySet->addHardwareKey(
+		new HardwareKey("n", KeyState(KEYCODE_n), "n (vk)",
+				kTriggerLeftKeyType,
+				kVirtualKeyboardActionType));
+	mapper->registerHardwareKeySet(keySet);
+
+	Keymap *globalMap = new Keymap("global");
+	Action *act;
+
+	act = new Action(globalMap, "VIRT", "Display keyboard",
+			 kVirtualKeyboardActionType);
+	act->addKeyEvent(KeyState(KEYCODE_F7, ASCII_F7, 0));
+
+	mapper->addGlobalKeymap(globalMap);
+
+	mapper->pushKeymap("global");
+#endif
+}
+
+bool OSystem_Android::pollEvent(Common::Event &event) {
+	//ENTER("pollEvent()");
+	lockMutex(_event_queue_lock);
+	if (_event_queue.empty()) {
+		unlockMutex(_event_queue_lock);
+		return false;
+	}
+	event = _event_queue.pop();
+	unlockMutex(_event_queue_lock);
+
+	switch (event.type) {
+	case Common::EVENT_MOUSEMOVE:
+		// TODO: only dirty/redraw move bounds
+		_force_redraw = true;
+		// fallthrough
+	case Common::EVENT_LBUTTONDOWN:
+	case Common::EVENT_LBUTTONUP:
+	case Common::EVENT_RBUTTONDOWN:
+	case Common::EVENT_RBUTTONUP:
+	case Common::EVENT_WHEELUP:
+	case Common::EVENT_WHEELDOWN:
+	case Common::EVENT_MBUTTONDOWN:
+	case Common::EVENT_MBUTTONUP: {
+		if (event.kbd.flags == 1) { // relative mouse hack
+			// Relative (trackball) mouse hack.
+			const Common::Point& mouse_pos =
+				getEventManager()->getMousePos();
+			event.mouse.x += mouse_pos.x;
+			event.mouse.y += mouse_pos.y;
+			event.mouse.x = CLIP(event.mouse.x, (int16)0, _show_overlay ?
+								 getOverlayWidth() : getWidth());
+			event.mouse.y = CLIP(event.mouse.y, (int16)0, _show_overlay ?
+								 getOverlayHeight() : getHeight());
+		} else {
+			// Touchscreen events need to be converted
+			// from device to game coords first.
+			const GLESTexture* tex = _show_overlay
+				? static_cast<GLESTexture*>(_overlay_texture)
+				: static_cast<GLESTexture*>(_game_texture);
+			event.mouse.x = scalef(event.mouse.x, tex->width(),
+								   _egl_surface_width);
+			event.mouse.y = scalef(event.mouse.y, tex->height(),
+								   _egl_surface_height);
+			event.mouse.x -= _shake_offset;
+		}
+		break;
+	}
+	case Common::EVENT_SCREEN_CHANGED:
+		debug("EVENT_SCREEN_CHANGED");
+		_screen_changeid++;
+		destroyScummVMSurface();
+		setupScummVMSurface();
+		break;
+	default:
+		break;
+	}
+
+	return true;
+}
+
+void OSystem_Android::pushEvent(const Common::Event& event) {
+	lockMutex(_event_queue_lock);
+
+	// Try to combine multiple queued mouse move events
+	if (event.type == Common::EVENT_MOUSEMOVE &&
+		!_event_queue.empty() &&
+		_event_queue.back().type == Common::EVENT_MOUSEMOVE) {
+	  Common::Event tail = _event_queue.back();
+	  if (event.kbd.flags) {
+		// relative movement hack
+		tail.mouse.x += event.mouse.x;
+		tail.mouse.y += event.mouse.y;
+	  } else {
+		// absolute position
+		tail.kbd.flags = 0;	 // clear relative flag
+		tail.mouse.x = event.mouse.x;
+		tail.mouse.y = event.mouse.y;
+	  }
+	}
+	else
+	  _event_queue.push(event);
+
+	unlockMutex(_event_queue_lock);
+}
+
+static void ScummVM_pushEvent(JNIEnv* env, jobject self, jobject java_event) {
+	OSystem_Android* cpp_obj = OSystem_Android::fromJavaObject(env, self);
+
+	Common::Event event;
+	event.type = (Common::EventType)env->GetIntField(java_event,
+							 FID_Event_type);
+	event.synthetic =
+		env->GetBooleanField(java_event, FID_Event_synthetic);
+
+	switch (event.type) {
+	case Common::EVENT_KEYDOWN:
+	case Common::EVENT_KEYUP:
+		event.kbd.keycode = (Common::KeyCode)env->GetIntField(
+			java_event, FID_Event_kbd_keycode);
+		event.kbd.ascii = static_cast<int>(env->GetIntField(
+			java_event, FID_Event_kbd_ascii));
+		event.kbd.flags = static_cast<int>(env->GetIntField(
+			java_event, FID_Event_kbd_flags));
+		break;
+	case Common::EVENT_MOUSEMOVE:
+	case Common::EVENT_LBUTTONDOWN:
+	case Common::EVENT_LBUTTONUP:
+	case Common::EVENT_RBUTTONDOWN:
+	case Common::EVENT_RBUTTONUP:
+	case Common::EVENT_WHEELUP:
+	case Common::EVENT_WHEELDOWN:
+	case Common::EVENT_MBUTTONDOWN:
+	case Common::EVENT_MBUTTONUP:
+		event.mouse.x =
+			env->GetIntField(java_event, FID_Event_mouse_x);
+		event.mouse.y =
+			env->GetIntField(java_event, FID_Event_mouse_y);
+		// This is a terrible hack.	 We stash "relativeness"
+		// in the kbd.flags field until pollEvent() can work
+		// it out.
+		event.kbd.flags = env->GetBooleanField(
+			java_event, FID_Event_mouse_relative) ? 1 : 0;
+		break;
+	default:
+		break;
+	}
+
+	cpp_obj->pushEvent(event);
+}
+
+uint32 OSystem_Android::getMillis() {
+	timeval curTime;
+	gettimeofday(&curTime, NULL);
+	return (uint32)(((curTime.tv_sec - _startTime.tv_sec) * 1000) + \
+			((curTime.tv_usec - _startTime.tv_usec) / 1000));
+}
+
+void OSystem_Android::delayMillis(uint msecs) {
+	usleep(msecs * 1000);
+}
+
+OSystem::MutexRef OSystem_Android::createMutex() {
+	pthread_mutexattr_t attr;
+	pthread_mutexattr_init(&attr);
+	pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
+
+	pthread_mutex_t *mutex = new pthread_mutex_t;
+	if (pthread_mutex_init(mutex, &attr) != 0) {
+		warning("pthread_mutex_init() failed!");
+		delete mutex;
+		return NULL;
+	}
+	return (MutexRef)mutex;
+}
+
+void OSystem_Android::lockMutex(MutexRef mutex) {
+	if (pthread_mutex_lock((pthread_mutex_t*)mutex) != 0)
+		warning("pthread_mutex_lock() failed!");
+}
+
+void OSystem_Android::unlockMutex(MutexRef mutex) {
+	if (pthread_mutex_unlock((pthread_mutex_t*)mutex) != 0)
+		warning("pthread_mutex_unlock() failed!");
+}
+
+void OSystem_Android::deleteMutex(MutexRef mutex) {
+	pthread_mutex_t* m = (pthread_mutex_t*)mutex;
+	if (pthread_mutex_destroy(m) != 0)
+		warning("pthread_mutex_destroy() failed!");
+	else
+		delete m;
+}
+
+void OSystem_Android::quit() {
+	ENTER("quit()");
+
+	_timer_thread_exit = true;
+	pthread_join(_timer_thread, NULL);
+}
+
+void OSystem_Android::setWindowCaption(const char *caption) {
+	ENTER("setWindowCaption(%s)", caption);
+	JNIEnv* env = JNU_GetEnv();
+	jstring java_caption = env->NewStringUTF(caption);
+	env->CallVoidMethod(_back_ptr, MID_setWindowCaption, java_caption);
+	if (env->ExceptionCheck()) {
+		warning("Failed to set window caption");
+		env->ExceptionDescribe();
+		env->ExceptionClear();
+	}
+	env->DeleteLocalRef(java_caption);
+}
+
+void OSystem_Android::displayMessageOnOSD(const char *msg) {
+	ENTER("displayMessageOnOSD(%s)", msg);
+	JNIEnv* env = JNU_GetEnv();
+	jstring java_msg = env->NewStringUTF(msg);
+	env->CallVoidMethod(_back_ptr, MID_displayMessageOnOSD, java_msg);
+	if (env->ExceptionCheck()) {
+		warning("Failed to display OSD message");
+		env->ExceptionDescribe();
+		env->ExceptionClear();
+	}
+	env->DeleteLocalRef(java_msg);
+}
+
+void OSystem_Android::showVirtualKeyboard(bool enable) {
+	ENTER("showVirtualKeyboard(%d)", enable);
+	JNIEnv* env = JNU_GetEnv();
+	env->CallVoidMethod(_back_ptr, MID_showVirtualKeyboard, enable);
+	if (env->ExceptionCheck()) {
+		error("Error trying to show virtual keyboard");
+		env->ExceptionDescribe();
+		env->ExceptionClear();
+	}
+}
+
+Common::SaveFileManager *OSystem_Android::getSavefileManager() {
+	assert(_savefile);
+	return _savefile;
+}
+
+Audio::Mixer *OSystem_Android::getMixer() {
+	assert(_mixer);
+	return _mixer;
+}
+
+Common::TimerManager *OSystem_Android::getTimerManager() {
+	assert(_timer);
+	return _timer;
+}
+
+void OSystem_Android::getTimeAndDate(TimeDate &td) const {
+	struct tm tm;
+	const time_t curTime = time(NULL);
+	localtime_r(&curTime, &tm);
+	td.tm_sec = tm.tm_sec;
+	td.tm_min = tm.tm_min;
+	td.tm_hour = tm.tm_hour;
+	td.tm_mday = tm.tm_mday;
+	td.tm_mon = tm.tm_mon;
+	td.tm_year = tm.tm_year;
+}
+
+FilesystemFactory *OSystem_Android::getFilesystemFactory() {
+	return _fsFactory;
+}
+
+void OSystem_Android::addSysArchivesToSearchSet(Common::SearchSet &s,
+						int priority) {
+	s.add("ASSET", _asset_archive, priority, false);
+
+	JNIEnv* env = JNU_GetEnv();
+
+	jobjectArray array =
+		(jobjectArray)env->CallObjectMethod(_back_ptr, MID_getSysArchives);
+	if (env->ExceptionCheck()) {
+		warning("Error finding system archive path");
+		env->ExceptionDescribe();
+		env->ExceptionClear();
+		return;
+	}
+
+	jsize size = env->GetArrayLength(array);
+	for (jsize i = 0; i < size; ++i) {
+		jstring path_obj = (jstring)env->GetObjectArrayElement(array, i);
+		const char* path = env->GetStringUTFChars(path_obj, NULL);
+		if (path != NULL) {
+			s.addDirectory(path, path, priority);
+			env->ReleaseStringUTFChars(path_obj, path);
+		}
+		env->DeleteLocalRef(path_obj);
+	}
+}
+
+
+static jint ScummVM_scummVMMain(JNIEnv* env, jobject self, jobjectArray args) {
+	OSystem_Android* cpp_obj = OSystem_Android::fromJavaObject(env, self);
+
+	const int MAX_NARGS = 32;
+	int res = -1;
+
+	int argc = env->GetArrayLength(args);
+	if (argc > MAX_NARGS) {
+		JNU_ThrowByName(env, "java/lang/IllegalArgumentException",
+				"too many arguments");
+		return 0;
+	}
+
+	char* argv[MAX_NARGS];
+	int nargs;	// note use in cleanup loop below
+	for (nargs = 0; nargs < argc; ++nargs) {
+		jstring arg = (jstring)env->GetObjectArrayElement(args, nargs);
+		if (arg == NULL) {
+			argv[nargs] = NULL;
+		} else {
+			const char* cstr = env->GetStringUTFChars(arg, NULL);
+			argv[nargs] = const_cast<char*>(cstr);
+			if (cstr == NULL)
+				goto cleanup;  // exception already thrown
+		}
+		env->DeleteLocalRef(arg);
+	}
+
+	g_system = cpp_obj;
+	assert(g_system);
+	__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG,
+				"Entering scummvm_main with %d args", argc);
+	res = scummvm_main(argc, argv);
+	__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, "Exiting scummvm_main");
+	g_system->quit();
+
+cleanup:
+	nargs--;
+	for (int i = 0; i < nargs; ++i) {
+		if (argv[i] == NULL)
+			continue;
+		jstring arg = (jstring)env->GetObjectArrayElement(args, nargs);
+		if (arg == NULL)
+			// Exception already thrown
+			return res;
+		env->ReleaseStringUTFChars(arg, argv[i]);
+		env->DeleteLocalRef(arg);
+	}
+
+	return res;
+}
+
+#ifdef DYNAMIC_MODULES
+void AndroidPluginProvider::addCustomDirectories(Common::FSList &dirs) const {
+	OSystem_Android* g_system_android = (OSystem_Android*)g_system;
+	g_system_android->addPluginDirectories(dirs);
+}
+#endif
+
+const static JNINativeMethod gMethods[] = {
+	{ "create", "(Landroid/content/res/AssetManager;)V",
+	  (void*)ScummVM_create },
+	{ "nativeDestroy", "()V", (void*)ScummVM_nativeDestroy },
+	{ "scummVMMain", "([Ljava/lang/String;)I",
+	  (void*)ScummVM_scummVMMain },
+	{ "pushEvent", "(Lorg/inodes/gus/scummvm/Event;)V",
+	  (void*)ScummVM_pushEvent },
+	{ "audioMixCallback", "([B)V",
+	  (void*)ScummVM_audioMixCallback },
+	{ "setConfMan", "(Ljava/lang/String;I)V",
+	  (void*)ScummVM_setConfManInt },
+	{ "setConfMan", "(Ljava/lang/String;Ljava/lang/String;)V",
+	  (void*)ScummVM_setConfManString },
+};
+
+JNIEXPORT jint JNICALL
+JNI_OnLoad(JavaVM* jvm, void* reserved) {
+	cached_jvm = jvm;
+
+	JNIEnv* env;
+	if (jvm->GetEnv((void**)&env, JNI_VERSION_1_2))
+		return JNI_ERR;
+
+	jclass cls = env->FindClass("org/inodes/gus/scummvm/ScummVM");
+	if (cls == NULL)
+		return JNI_ERR;
+	if (env->RegisterNatives(cls, gMethods, ARRAYSIZE(gMethods)) < 0)
+		return JNI_ERR;
+
+	FID_ScummVM_nativeScummVM = env->GetFieldID(cls, "nativeScummVM", "J");
+	if (FID_ScummVM_nativeScummVM == NULL)
+		return JNI_ERR;
+
+	jclass event = env->FindClass("org/inodes/gus/scummvm/Event");
+	if (event == NULL)
+		return JNI_ERR;
+	FID_Event_type = env->GetFieldID(event, "type", "I");
+	if (FID_Event_type == NULL)
+		return JNI_ERR;
+	FID_Event_synthetic = env->GetFieldID(event, "synthetic", "Z");
+	if (FID_Event_synthetic == NULL)
+		return JNI_ERR;
+	FID_Event_kbd_keycode = env->GetFieldID(event, "kbd_keycode", "I");
+	if (FID_Event_kbd_keycode == NULL)
+		return JNI_ERR;
+	FID_Event_kbd_ascii = env->GetFieldID(event, "kbd_ascii", "I");
+	if (FID_Event_kbd_ascii == NULL)
+		return JNI_ERR;
+	FID_Event_kbd_flags = env->GetFieldID(event, "kbd_flags", "I");
+	if (FID_Event_kbd_flags == NULL)
+		return JNI_ERR;
+	FID_Event_mouse_x = env->GetFieldID(event, "mouse_x", "I");
+	if (FID_Event_mouse_x == NULL)
+		return JNI_ERR;
+	FID_Event_mouse_y = env->GetFieldID(event, "mouse_y", "I");
+	if (FID_Event_mouse_y == NULL)
+		return JNI_ERR;
+	FID_Event_mouse_relative = env->GetFieldID(event, "mouse_relative", "Z");
+	if (FID_Event_mouse_relative == NULL)
+		return JNI_ERR;
+
+	cls = env->FindClass("java/lang/Object");
+	if (cls == NULL)
+		return JNI_ERR;
+	MID_Object_wait = env->GetMethodID(cls, "wait", "()V");
+	if (MID_Object_wait == NULL)
+		return JNI_ERR;
+
+	return JNI_VERSION_1_2;
+}
+
+#endif


Property changes on: scummvm/trunk/backends/platform/android/android.cpp
___________________________________________________________________
Added: svn:mime-type
   + text/plain
Added: svn:keywords
   + Date Rev Author URL Id
Added: svn:eol-style
   + native

Added: scummvm/trunk/backends/platform/android/android.mk
===================================================================
--- scummvm/trunk/backends/platform/android/android.mk	                        (rev 0)
+++ scummvm/trunk/backends/platform/android/android.mk	2010-06-06 09:34:36 UTC (rev 49449)
@@ -0,0 +1,52 @@
+# Android specific build targets
+
+AAPT = aapt
+DX = dx
+APKBUILDER = apkbuilder
+ADB = adb -e
+ANDROID_JAR = $(ANDROID_SDK)/platforms/android-1.6/android.jar
+JAVAC ?= javac
+JAVACFLAGS = -source 1.5 -target 1.5
+
+# FIXME: find/mark plugin entry points and add all this back again:
+#LDFLAGS += -Wl,--gc-sections
+#CXXFLAGS += -ffunction-sections -fdata-sections -fvisibility=hidden -fvisibility-inlines-hidden
+
+scummvm.apk: build.tmp/libscummvm.so resources.ap_ classes.dex
+	# Package installer won't delete old libscummvm.so on upgrade so
+	# replace it with a zero size file
+	$(INSTALL) -d build.stage/common/lib/armeabi
+	touch build.stage/common/lib/armeabi/libscummvm.so
+	# We now handle the library unpacking ourselves from mylib/
+	$(INSTALL) -d build.stage/common/mylib/armeabi
+	$(INSTALL) -c -m 644 build.tmp/libscummvm.so build.stage/common/mylib/armeabi/
+	$(STRIP) build.stage/common/mylib/armeabi/libscummvm.so
+	# "-nf lib/armeabi/libscummvm.so" builds bogus paths?
+	$(APKBUILDER) $@ -z resources.ap_ -f classes.dex -rf build.stage/common || { $(RM) $@; exit 1; }
+
+scummvm-engine-%.apk: plugins/lib%.so build.tmp/%/resources.ap_ build.tmp/plugins/classes.dex
+	$(INSTALL) -d build.stage/$*/apk/mylib/armeabi/
+	$(INSTALL) -c -m 644 plugins/lib$*.so build.stage/$*/apk/mylib/armeabi/
+	$(STRIP) build.stage/$*/apk/mylib/armeabi/lib$*.so
+	$(APKBUILDER) $@ -z build.tmp/$*/resources.ap_ -f build.tmp/plugins/classes.dex -rf build.stage/$*/apk || { $(RM) $@; exit 1; }
+
+release/%.apk: %.apk
+	@$(MKDIR) -p $(@D)
+	@$(RM) $@
+	$(CP) $< $@.tmp
+	# remove debugging signature
+	zip -d $@.tmp META-INF/\*
+	jarsigner $(JARSIGNER_FLAGS) $@.tmp release
+	zipalign 4 $@.tmp $@
+	$(RM) $@.tmp
+
+androidrelease: release/scummvm.apk $(patsubst plugins/lib%.so,release/scummvm-engine-%.apk,$(PLUGINS))
+
+androidtest: scummvm.apk scummvm-engine-scumm.apk scummvm-engine-kyra.apk
+	@set -e; for apk in $^; do \
+	  echo $(ADB) install -r $$apk; \
+	  $(ADB) install -r $$apk; \
+	done
+	$(ADB) shell am start -a android.intent.action.MAIN -c android.intent.category.LAUNCHER -n org.inodes.gus.scummvm/.Unpacker
+
+.PHONY: androidrelease androidtest


Property changes on: scummvm/trunk/backends/platform/android/android.mk
___________________________________________________________________
Added: svn:mime-type
   + text/plain
Added: svn:eol-style
   + native

Added: scummvm/trunk/backends/platform/android/asset-archive.cpp
===================================================================
--- scummvm/trunk/backends/platform/android/asset-archive.cpp	                        (rev 0)
+++ scummvm/trunk/backends/platform/android/asset-archive.cpp	2010-06-06 09:34:36 UTC (rev 49449)
@@ -0,0 +1,414 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#if defined(ANDROID)
+
+#include <jni.h>
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "common/str.h"
+#include "common/stream.h"
+#include "common/util.h"
+#include "common/archive.h"
+#include "common/debug.h"
+
+#include "backends/platform/android/asset-archive.h"
+
+extern JNIEnv* JNU_GetEnv();
+
+// Must match android.content.res.AssetManager.ACCESS_*
+const jint ACCESS_UNKNOWN = 0;
+const jint ACCESS_RANDOM = 1;
+
+// This might be useful to someone else.  Assumes markSupported() == true.
+class JavaInputStream : public Common::SeekableReadStream {
+public:
+	JavaInputStream(JNIEnv* env, jobject is);
+	virtual ~JavaInputStream();
+	virtual bool eos() const { return _eos; }
+	virtual bool err() const { return _err; }
+	virtual void clearErr() { _eos = _err = false; }
+	virtual uint32 read(void *dataPtr, uint32 dataSize);
+	virtual int32 pos() const { return _pos; }
+	virtual int32 size() const { return _len; }
+	virtual bool seek(int32 offset, int whence = SEEK_SET);
+private:
+	void close(JNIEnv* env);
+	jmethodID MID_mark;
+	jmethodID MID_available;
+	jmethodID MID_close;
+	jmethodID MID_read;
+	jmethodID MID_reset;
+	jmethodID MID_skip;
+	jobject _input_stream;
+	jsize _buflen;
+	jbyteArray _buf;
+	uint32 _pos;
+	jint _len;
+	bool _eos;
+	bool _err;
+};
+
+JavaInputStream::JavaInputStream(JNIEnv* env, jobject is) :
+	_eos(false), _err(false), _pos(0)
+{
+	_input_stream = env->NewGlobalRef(is);
+	_buflen = 8192;
+	_buf = static_cast<jbyteArray>(env->NewGlobalRef(env->NewByteArray(_buflen)));
+
+	jclass cls = env->GetObjectClass(_input_stream);
+	MID_mark = env->GetMethodID(cls, "mark", "(I)V");
+	assert(MID_mark);
+	MID_available = env->GetMethodID(cls, "available", "()I");
+	assert(MID_mark);
+	MID_close = env->GetMethodID(cls, "close", "()V");
+	assert(MID_close);
+	MID_read = env->GetMethodID(cls, "read", "([BII)I");
+	assert(MID_read);
+	MID_reset = env->GetMethodID(cls, "reset", "()V");
+	assert(MID_reset);
+	MID_skip = env->GetMethodID(cls, "skip", "(J)J");
+	assert(MID_skip);
+
+	// Mark start of stream, so we can reset back to it.
+	// readlimit is set to something bigger than anything we might
+	// want to seek within.
+	env->CallVoidMethod(_input_stream, MID_mark, 10*1024*1024);
+	_len = env->CallIntMethod(_input_stream, MID_available);
+}
+
+JavaInputStream::~JavaInputStream() {
+	JNIEnv* env = JNU_GetEnv();
+	close(env);
+	env->DeleteGlobalRef(_buf);
+	env->DeleteGlobalRef(_input_stream);
+}
+
+void JavaInputStream::close(JNIEnv* env) {
+	env->CallVoidMethod(_input_stream, MID_close);
+	if (env->ExceptionCheck())
+		env->ExceptionClear();
+}
+
+uint32 JavaInputStream::read(void *dataPtr, uint32 dataSize) {
+	JNIEnv* env = JNU_GetEnv();
+
+	if (_buflen < dataSize) {
+		_buflen = dataSize;
+		env->DeleteGlobalRef(_buf);
+		_buf = static_cast<jbyteArray>(env->NewGlobalRef(env->NewByteArray(_buflen)));
+	}
+
+	jint ret = env->CallIntMethod(_input_stream, MID_read, _buf, 0, dataSize);
+	if (env->ExceptionCheck()) {
+		warning("Exception during JavaInputStream::read(%p, %d)",
+				dataPtr, dataSize);
+		env->ExceptionDescribe();
+		env->ExceptionClear();
+		_err = true;
+		ret = -1;
+	} else if (ret == -1) {
+		_eos = true;
+		ret = 0;
+	} else {
+		env->GetByteArrayRegion(_buf, 0, ret, static_cast<jbyte*>(dataPtr));
+		_pos += ret;
+	}
+	return ret;
+}
+
+bool JavaInputStream::seek(int32 offset, int whence) {
+	JNIEnv* env = JNU_GetEnv();
+	uint32 newpos;
+	switch (whence) {
+	case SEEK_SET:
+		newpos = offset;
+		break;
+	case SEEK_CUR:
+		newpos = _pos + offset;
+		break;
+	case SEEK_END:
+		newpos = _len + offset;
+		break;
+	default:
+		debug("Unknown 'whence' arg %d", whence);
+		return false;
+	}
+
+	jlong skip_bytes;
+	if (newpos > _pos) {
+		skip_bytes = newpos - _pos;
+	} else {
+		// Can't skip backwards, so jump back to start and skip from there.
+		env->CallVoidMethod(_input_stream, MID_reset);
+		if (env->ExceptionCheck()) {
+			warning("Failed to rewind to start of asset stream");
+			env->ExceptionDescribe();
+			env->ExceptionClear();
+			return false;
+		}
+		_pos = 0;
+		skip_bytes = newpos;
+	}
+
+	while (skip_bytes > 0) {
+		jlong ret = env->CallLongMethod(_input_stream, MID_skip, skip_bytes);
+		if (env->ExceptionCheck()) {
+			warning("Failed to skip %ld bytes into asset stream",
+					static_cast<long>(skip_bytes));
+			env->ExceptionDescribe();
+			env->ExceptionClear();
+			return false;
+		} else if (ret == 0) {
+			warning("InputStream->skip(%ld) didn't skip any bytes. Aborting seek.",
+					static_cast<long>(skip_bytes));
+			return false;  // No point looping forever...
+		}
+		_pos += ret;
+		skip_bytes -= ret;
+	}
+	_eos = false;
+	return true;
+}
+
+
+// Must match android.content.res.AssetFileDescriptor.UNKNOWN_LENGTH
+const jlong UNKNOWN_LENGTH = -1;
+
+// Reading directly from a fd is so much more efficient, that it is
+// worth optimising for.
+class AssetFdReadStream : public Common::SeekableReadStream {
+public:
+	AssetFdReadStream(JNIEnv* env, jobject assetfd);
+	virtual ~AssetFdReadStream();
+	virtual bool eos() const { return _eos; }
+	virtual bool err() const { return _err; }
+	virtual void clearErr() { _eos = _err = false; }
+	virtual uint32 read(void *dataPtr, uint32 dataSize);
+	virtual int32 pos() const { return _pos; }
+	virtual int32 size() const { return _declared_len; }
+	virtual bool seek(int32 offset, int whence = SEEK_SET);
+private:
+	void close(JNIEnv* env);
+	int _fd;
+	jmethodID MID_close;
+	jobject _assetfd;
+	jlong _start_off;
+	jlong _declared_len;
+	uint32 _pos;
+	bool _eos;
+	bool _err;
+};
+
+AssetFdReadStream::AssetFdReadStream(JNIEnv* env, jobject assetfd) :
+	_eos(false), _err(false), _pos(0)
+{
+	_assetfd = env->NewGlobalRef(assetfd);
+
+	jclass cls = env->GetObjectClass(_assetfd);
+	MID_close = env->GetMethodID(cls, "close", "()V");
+	assert(MID_close);
+
+	jmethodID MID_getStartOffset =
+		env->GetMethodID(cls, "getStartOffset", "()J");
+	assert(MID_getStartOffset);
+	_start_off = env->CallLongMethod(_assetfd, MID_getStartOffset);
+
+	jmethodID MID_getDeclaredLength =
+		env->GetMethodID(cls, "getDeclaredLength", "()J");
+	assert(MID_getDeclaredLength);
+	_declared_len = env->CallLongMethod(_assetfd, MID_getDeclaredLength);
+
+	jmethodID MID_getFileDescriptor =
+		env->GetMethodID(cls, "getFileDescriptor", "()Ljava/io/FileDescriptor;");
+	assert(MID_getFileDescriptor);
+	jobject javafd = env->CallObjectMethod(_assetfd, MID_getFileDescriptor);
+	assert(javafd);
+	jclass fd_cls = env->GetObjectClass(javafd);
+	jfieldID FID_descriptor = env->GetFieldID(fd_cls, "descriptor", "I");
+	assert(FID_descriptor);
+	_fd = env->GetIntField(javafd, FID_descriptor);
+}
+
+AssetFdReadStream::~AssetFdReadStream() {
+	JNIEnv* env = JNU_GetEnv();
+	env->CallVoidMethod(_assetfd, MID_close);
+	if (env->ExceptionCheck())
+		env->ExceptionClear();
+	env->DeleteGlobalRef(_assetfd);
+}
+
+uint32 AssetFdReadStream::read(void *dataPtr, uint32 dataSize) {
+	if (_declared_len != UNKNOWN_LENGTH) {
+		jlong cap = _declared_len - _pos;
+		if (dataSize > cap)
+			dataSize = cap;
+	}
+	int ret = ::read(_fd, dataPtr, dataSize);
+	if (ret == 0)
+		_eos = true;
+	else if (ret == -1)
+		_err = true;
+	else
+		_pos += ret;
+	return ret;
+}
+
+bool AssetFdReadStream::seek(int32 offset, int whence) {
+	if (whence == SEEK_SET) {
+		if (_declared_len != UNKNOWN_LENGTH && offset > _declared_len)
+			offset = _declared_len;
+		offset += _start_off;
+	} else if (whence == SEEK_END && _declared_len != UNKNOWN_LENGTH) {
+		whence = SEEK_SET;
+		offset = _start_off + _declared_len + offset;
+	}
+	int ret = lseek(_fd, offset, whence);
+	if (ret == -1)
+		return false;
+	_pos = ret - _start_off;
+	_eos = false;
+	return true;
+}
+
+AndroidAssetArchive::AndroidAssetArchive(jobject am) {
+	JNIEnv* env = JNU_GetEnv();
+	_am = env->NewGlobalRef(am);
+
+	jclass cls = env->GetObjectClass(_am);
+	MID_open = env->GetMethodID(cls, "open",
+								"(Ljava/lang/String;I)Ljava/io/InputStream;");
+	assert(MID_open);
+	MID_openFd = env->GetMethodID(cls, "openFd",
+								  "(Ljava/lang/String;)Landroid/content/res/AssetFileDescriptor;");
+	assert(MID_openFd);
+	MID_list = env->GetMethodID(cls, "list",
+								"(Ljava/lang/String;)[Ljava/lang/String;");
+	assert(MID_list);
+}
+
+AndroidAssetArchive::~AndroidAssetArchive() {
+	JNIEnv* env = JNU_GetEnv();
+	env->DeleteGlobalRef(_am);
+}
+
+bool AndroidAssetArchive::hasFile(const Common::String &name) {
+	JNIEnv* env = JNU_GetEnv();
+	jstring path = env->NewStringUTF(name.c_str());
+	jobject result = env->CallObjectMethod(_am, MID_open, path, ACCESS_UNKNOWN);
+	if (env->ExceptionCheck()) {
+		// Assume FileNotFoundException
+		//warning("Error while calling AssetManager->open(%s)", name.c_str());
+		//env->ExceptionDescribe();
+		env->ExceptionClear();
+		env->DeleteLocalRef(path);
+		return false;
+	}
+	env->DeleteLocalRef(result);
+	env->DeleteLocalRef(path);
+	return true;
+}
+
+int AndroidAssetArchive::listMembers(Common::ArchiveMemberList &member_list) {
+	JNIEnv* env = JNU_GetEnv();
+	Common::List<Common::String> dirlist;
+	dirlist.push_back("");
+
+	int count = 0;
+	while (!dirlist.empty()) {
+		const Common::String dir = dirlist.back();
+		dirlist.pop_back();
+
+		jstring jpath = env->NewStringUTF(dir.c_str());
+		jobjectArray jpathlist = static_cast<jobjectArray>(env->CallObjectMethod(_am, MID_list, jpath));
+		if (env->ExceptionCheck()) {
+			warning("Error while calling AssetManager->list(%s). Ignoring.",
+					dir.c_str());
+			env->ExceptionDescribe();
+			env->ExceptionClear();
+			continue;  // May as well keep going ...
+		}
+		env->DeleteLocalRef(jpath);
+
+		for (jsize i = 0; i < env->GetArrayLength(jpathlist); ++i) {
+			jstring elem = (jstring)env->GetObjectArrayElement(jpathlist, i);
+			const char* p = env->GetStringUTFChars(elem, NULL);
+			Common::String thispath = dir;
+			if (!thispath.empty())
+				thispath += "/";
+			thispath += p;
+
+			// Assume files have a . in them, and directories don't
+			if (strchr(p, '.')) {
+				member_list.push_back(getMember(thispath));
+				++count;
+			} else
+				dirlist.push_back(thispath);
+
+			env->ReleaseStringUTFChars(elem, p);
+			env->DeleteLocalRef(elem);
+		}
+
+		env->DeleteLocalRef(jpathlist);
+	}
+
+	return count;
+}
+
+Common::ArchiveMemberPtr AndroidAssetArchive::getMember(const Common::String &name) {
+	return Common::ArchiveMemberPtr(new Common::GenericArchiveMember(name, this));
+}
+
+Common::SeekableReadStream *AndroidAssetArchive::createReadStreamForMember(const Common::String &path) const {
+	JNIEnv* env = JNU_GetEnv();
+	jstring jpath = env->NewStringUTF(path.c_str());
+
+	// Try openFd() first ...
+	jobject afd = env->CallObjectMethod(_am, MID_openFd, jpath);
+	if (env->ExceptionCheck())
+		env->ExceptionClear();
+	else if (afd != NULL) {
+		// success :)
+		env->DeleteLocalRef(jpath);
+		return new AssetFdReadStream(env, afd);
+	}
+
+	// ... and fallback to normal open() if that doesn't work
+	jobject is = env->CallObjectMethod(_am, MID_open, jpath, ACCESS_RANDOM);
+	if (env->ExceptionCheck()) {
+		// Assume FileNotFoundException
+		//warning("Error opening %s", path.c_str());
+		//env->ExceptionDescribe();
+		env->ExceptionClear();
+		env->DeleteLocalRef(jpath);
+		return NULL;
+	}
+
+	return new JavaInputStream(env, is);
+}
+
+#endif


Property changes on: scummvm/trunk/backends/platform/android/asset-archive.cpp
___________________________________________________________________
Added: svn:mime-type
   + text/plain
Added: svn:keywords
   + Date Rev Author URL Id
Added: svn:eol-style
   + native

Added: scummvm/trunk/backends/platform/android/asset-archive.h
===================================================================
--- scummvm/trunk/backends/platform/android/asset-archive.h	                        (rev 0)
+++ scummvm/trunk/backends/platform/android/asset-archive.h	2010-06-06 09:34:36 UTC (rev 49449)
@@ -0,0 +1,53 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * $URL$
+ * $Id$
+ *
+ */
+
+#if defined(ANDROID)
+
+#include <jni.h>
+
+#include "common/str.h"
+#include "common/stream.h"
+#include "common/util.h"
+#include "common/archive.h"
+
+class AndroidAssetArchive : public Common::Archive {
+public:
+	AndroidAssetArchive(jobject am);
+	virtual ~AndroidAssetArchive();
+
+	virtual bool hasFile(const Common::String &name);
+	virtual int listMembers(Common::ArchiveMemberList &list);
+	virtual Common::ArchiveMemberPtr getMember(const Common::String &name);
+	virtual Common::SeekableReadStream *createReadStreamForMember(const Common::String &name) const;
+
+private:
+	jmethodID MID_open;
+	jmethodID MID_openFd;
+	jmethodID MID_list;
+
+	jobject _am;
+};
+
+#endif


Property changes on: scummvm/trunk/backends/platform/android/asset-archive.h
___________________________________________________________________
Added: svn:mime-type
   + text/plain
Added: svn:keywords
   + Date Rev Author URL Id
Added: svn:eol-style
   + native

Added: scummvm/trunk/backends/platform/android/module.mk
===================================================================
--- scummvm/trunk/backends/platform/android/module.mk	                        (rev 0)
+++ scummvm/trunk/backends/platform/android/module.mk	2010-06-06 09:34:36 UTC (rev 49449)
@@ -0,0 +1,85 @@
+MODULE := backends/platform/android
+
+MODULE_OBJS := \
+	android.o asset-archive.o video.o
+
+MODULE_DIRS += \
+	backends/platform/android/
+
+# We don't use the rules.mk here on purpose
+OBJS := $(addprefix $(MODULE)/, $(MODULE_OBJS)) $(OBJS)
+
+JAVA_SRC = \
+	$(MODULE)/org/inodes/gus/scummvm/ScummVM.java \
+	$(MODULE)/org/inodes/gus/scummvm/ScummVMApplication.java \
+	$(MODULE)/org/inodes/gus/scummvm/ScummVMActivity.java \
+	$(MODULE)/org/inodes/gus/scummvm/EditableSurfaceView.java \
+	$(MODULE)/org/inodes/gus/scummvm/Unpacker.java \
+	$(MODULE)/org/inodes/gus/scummvm/Manifest.java \
+	$(MODULE)/org/inodes/gus/scummvm/R.java
+
+JAVA_PLUGIN_SRC = \
+	$(MODULE)/org/inodes/gus/scummvm/PluginProvider.java
+
+RESOURCES = \
+	$(srcdir)/dists/android/res/values/strings.xml \
+	$(srcdir)/dists/android/res/layout/main.xml \
+	$(srcdir)/dists/android/res/layout/splash.xml \
+	$(srcdir)/dists/android/res/drawable/gradient.xml \
+	$(srcdir)/dists/android/res/drawable/scummvm.png \
+	$(srcdir)/dists/android/res/drawable/scummvm_big.png
+
+ASSETS = $(DIST_FILES_ENGINEDATA) $(DIST_FILES_THEMES)
+
+PLUGIN_RESOURCES = \
+	$(srcdir)/dists/android/res/values/strings.xml \
+	$(srcdir)/dists/android/res/drawable/scummvm.png
+
+# These must be incremented for each market upload
+#ANDROID_VERSIONCODE = 6  Specified in dists/android/AndroidManifest.xml.in
+ANDROID_PLUGIN_VERSIONCODE = 6
+
+# This library contains scummvm proper
+build.tmp/libscummvm.so: $(OBJS)
+	@$(MKDIR) -p $(@D)
+	$(CXX) $(PLUGIN_LDFLAGS) -shared $(LDFLAGS) -Wl,-soname,$(@F) -Wl,--no-undefined -o $@ $(PRE_OBJS_FLAGS) $(OBJS) $(POST_OBJS_FLAGS) $(LIBS)
+
+
+backends/platform/android/org/inodes/gus/scummvm/R.java backends/platform/android/org/inodes/gus/scummvm/Manifest.java: $(srcdir)/dists/android/AndroidManifest.xml $(filter %.xml,$(RESOURCES)) $(ANDROID_JAR)
+	$(AAPT) package -m -J backends/platform/android -M $< -S $(srcdir)/dists/android/res -I $(ANDROID_JAR)
+
+build.tmp/classes/%.class: $(srcdir)/backends/platform/android/%.java $(srcdir)/backends/platform/android/org/inodes/gus/scummvm/R.java
+	@$(MKDIR) -p $(@D)
+	$(JAVAC) $(JAVACFLAGS) -cp $(srcdir)/backends/platform/android -d build.tmp/classes -bootclasspath $(ANDROID_JAR) $<
+
+build.tmp/classes.plugin/%.class: $(srcdir)/backends/platform/android/%.java
+	@$(MKDIR) -p $(@D)
+	$(JAVAC) $(JAVACFLAGS) -cp $(srcdir)/backends/platform/android -d build.tmp/classes.plugin -bootclasspath $(ANDROID_JAR) $<
+
+classes.dex: $(JAVA_SRC:backends/platform/android/%.java=build.tmp/classes/%.class)
+	$(DX) --dex --output=$@ build.tmp/classes
+
+build.tmp/plugins/classes.dex: $(JAVA_PLUGIN_SRC:backends/platform/android/%.java=build.tmp/classes.plugin/%.class)
+	@$(MKDIR) -p $(@D)
+	$(DX) --dex --output=$@ build.tmp/classes.plugin
+
+resources.ap_: $(srcdir)/dists/android/AndroidManifest.xml $(RESOURCES) $(ASSETS) $(ANDROID_JAR) $(DIST_FILES_THEMES) $(DIST_FILES_ENGINEDATA)
+	$(INSTALL) -d build.tmp/assets/
+	$(INSTALL) -c -m 644 $(DIST_FILES_THEMES) $(DIST_FILES_ENGINEDATA) build.tmp/assets/
+	$(AAPT) package -f -M $< -S $(srcdir)/dists/android/res -A build.tmp/assets -I $(ANDROID_JAR) -F $@
+
+build.tmp/%/resources.ap_: build.tmp/%/AndroidManifest.xml build.stage/%/res/values/strings.xml build.stage/%/res/drawable/scummvm.png $(ANDROID_JAR)
+	$(AAPT) package -f -M $< -S build.stage/$*/res -I $(ANDROID_JAR) -F $@
+
+build.tmp/%/AndroidManifest.xml build.stage/%/res/values/strings.xml: dists/android/mkmanifest.pl configure dists/android/AndroidManifest.xml
+	dists/android/mkmanifest.pl --id=$* --configure=configure \
+	  --version-name=$(VERSION) \
+	  --version-code=$(ANDROID_PLUGIN_VERSIONCODE) \
+	  --stringres=build.stage/$*/res/values/strings.xml \
+	  --manifest=build.tmp/$*/AndroidManifest.xml \
+	  --master-manifest=dists/android/AndroidManifest.xml \
+	  --unpacklib=mylib/armeabi/lib$*.so
+
+build.stage/%/res/drawable/scummvm.png: dists/android/res/drawable/scummvm.png
+	@$(MKDIR) -p $(@D)
+	$(CP) $< $@


Property changes on: scummvm/trunk/backends/platform/android/module.mk
___________________________________________________________________
Added: svn:mime-type
   + text/plain
Added: svn:eol-style
   + native

Added: scummvm/trunk/backends/platform/android/org/inodes/gus/scummvm/EditableSurfaceView.java
===================================================================
--- scummvm/trunk/backends/platform/android/org/inodes/gus/scummvm/EditableSurfaceView.java	                        (rev 0)
+++ scummvm/trunk/backends/platform/android/org/inodes/gus/scummvm/EditableSurfaceView.java	2010-06-06 09:34:36 UTC (rev 49449)
@@ -0,0 +1,59 @@
+package org.inodes.gus.scummvm;
+
+import android.content.Context;
+import android.text.InputType;
+import android.util.AttributeSet;
+import android.view.SurfaceView;
+import android.view.inputmethod.BaseInputConnection;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputConnection;
+import android.view.inputmethod.InputMethodManager;
+
+public class EditableSurfaceView extends SurfaceView {
+	public EditableSurfaceView(Context context) {
+		super(context);
+	}
+
+	public EditableSurfaceView(Context context, AttributeSet attrs) {
+		super(context, attrs);
+	}
+
+	public EditableSurfaceView(Context context, AttributeSet attrs,
+							   int defStyle) {
+		super(context, attrs, defStyle);
+	}
+
+	@Override
+	public boolean onCheckIsTextEditor() {
+		return true;
+	}
+
+	private class MyInputConnection extends BaseInputConnection {
+		public MyInputConnection() {
+			super(EditableSurfaceView.this, false);
+		}
+
+		@Override
+		public boolean performEditorAction(int actionCode) {
+			if (actionCode == EditorInfo.IME_ACTION_DONE) {
+				InputMethodManager imm = (InputMethodManager)
+					getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
+				imm.hideSoftInputFromWindow(getWindowToken(), 0);
+			}
+			return super.performEditorAction(actionCode); // Sends enter key
+		}
+	}
+
+	@Override
+	public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
+		outAttrs.initialCapsMode = 0;
+		outAttrs.initialSelEnd = outAttrs.initialSelStart = -1;
+		outAttrs.inputType = (InputType.TYPE_CLASS_TEXT |
+							  InputType.TYPE_TEXT_VARIATION_NORMAL |
+							  InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE);
+		outAttrs.imeOptions = (EditorInfo.IME_ACTION_DONE |
+							   EditorInfo.IME_FLAG_NO_EXTRACT_UI);
+
+		return new MyInputConnection();
+	}
+}


Property changes on: scummvm/trunk/backends/platform/android/org/inodes/gus/scummvm/EditableSurfaceView.java
___________________________________________________________________
Added: svn:mime-type
   + text/plain
Added: svn:eol-style
   + native

Added: scummvm/trunk/backends/platform/android/org/inodes/gus/scummvm/Event.java
===================================================================
--- scummvm/trunk/backends/platform/android/org/inodes/gus/scummvm/Event.java	                        (rev 0)
+++ scummvm/trunk/backends/platform/android/org/inodes/gus/scummvm/Event.java	2010-06-06 09:34:36 UTC (rev 49449)
@@ -0,0 +1,330 @@
+package org.inodes.gus.scummvm;
+
+import android.view.KeyEvent;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+public class Event {
+	// Common::EventType enum.
+	// Must be kept in sync with common/events.h
+	public final static int EVENT_INVALID = 0;
+	public final static int EVENT_KEYDOWN = 1;
+	public final static int EVENT_KEYUP = 2;
+	public final static int EVENT_MOUSEMOVE = 3;
+	public final static int EVENT_LBUTTONDOWN = 4;
+	public final static int EVENT_LBUTTONUP = 5;
+	public final static int EVENT_RBUTTONDOWN = 6;
+	public final static int EVENT_RBUTTONUP = 7;
+	public final static int EVENT_WHEELUP = 8;
+	public final static int EVENT_WHEELDOWN = 9;
+	public final static int EVENT_QUIT = 10;
+	public final static int EVENT_SCREEN_CHANGED = 11;
+	public final static int EVENT_PREDICTIVE_DIALOG = 12;
+	public final static int EVENT_MBUTTONDOWN = 13;
+	public final static int EVENT_MBUTTONUP = 14;
+	public final static int EVENT_MAINMENU = 15;
+	public final static int EVENT_RTL = 16;
+
+	// common/keyboard.h
+	public final static int ASCII_F1 = 315;
+	public final static int ASCII_F2 = 316;
+	public final static int ASCII_F3 = 317;
+	public final static int ASCII_F4 = 318;
+	public final static int ASCII_F5 = 319;
+	public final static int ASCII_F6 = 320;
+	public final static int ASCII_F7 = 321;
+	public final static int ASCII_F8 = 322;
+	public final static int ASCII_F9 = 323;
+	public final static int ASCII_F10 = 324;
+	public final static int ASCII_F11 = 325;
+	public final static int ASCII_F12 = 326;
+	public final static int KBD_CTRL  = 1 << 0;
+	public final static int KBD_ALT	  = 1 << 1;
+	public final static int KBD_SHIFT = 1 << 2;
+
+	public final static int KEYCODE_INVALID = 0;
+	public final static int KEYCODE_BACKSPACE = 8;
+	public final static int KEYCODE_TAB = 9;
+	public final static int KEYCODE_CLEAR = 12;
+	public final static int KEYCODE_RETURN = 13;
+	public final static int KEYCODE_PAUSE = 19;
+	public final static int KEYCODE_ESCAPE = 27;
+	public final static int KEYCODE_SPACE = 32;
+	public final static int KEYCODE_EXCLAIM = 33;
+	public final static int KEYCODE_QUOTEDBL = 34;
+	public final static int KEYCODE_HASH = 35;
+	public final static int KEYCODE_DOLLAR = 36;
+	public final static int KEYCODE_AMPERSAND = 38;
+	public final static int KEYCODE_QUOTE = 39;
+	public final static int KEYCODE_LEFTPAREN = 40;
+	public final static int KEYCODE_RIGHTPAREN = 41;
+	public final static int KEYCODE_ASTERISK = 42;
+	public final static int KEYCODE_PLUS = 43;
+	public final static int KEYCODE_COMMA = 44;
+	public final static int KEYCODE_MINUS = 45;
+	public final static int KEYCODE_PERIOD = 46;
+	public final static int KEYCODE_SLASH = 47;
+	public final static int KEYCODE_0 = 48;
+	public final static int KEYCODE_1 = 49;
+	public final static int KEYCODE_2 = 50;
+	public final static int KEYCODE_3 = 51;
+	public final static int KEYCODE_4 = 52;
+	public final static int KEYCODE_5 = 53;
+	public final static int KEYCODE_6 = 54;
+	public final static int KEYCODE_7 = 55;
+	public final static int KEYCODE_8 = 56;
+	public final static int KEYCODE_9 = 57;
+	public final static int KEYCODE_COLON = 58;
+	public final static int KEYCODE_SEMICOLON = 59;
+	public final static int KEYCODE_LESS = 60;
+	public final static int KEYCODE_EQUALS = 61;
+	public final static int KEYCODE_GREATER = 62;
+	public final static int KEYCODE_QUESTION = 63;
+	public final static int KEYCODE_AT = 64;
+	public final static int KEYCODE_LEFTBRACKET = 91;
+	public final static int KEYCODE_BACKSLASH = 92;
+	public final static int KEYCODE_RIGHTBRACKET = 93;
+	public final static int KEYCODE_CARET = 94;
+	public final static int KEYCODE_UNDERSCORE = 95;
+	public final static int KEYCODE_BACKQUOTE = 96;
+	public final static int KEYCODE_a = 97;
+	public final static int KEYCODE_b = 98;
+	public final static int KEYCODE_c = 99;
+	public final static int KEYCODE_d = 100;
+	public final static int KEYCODE_e = 101;
+	public final static int KEYCODE_f = 102;
+	public final static int KEYCODE_g = 103;
+	public final static int KEYCODE_h = 104;
+	public final static int KEYCODE_i = 105;
+	public final static int KEYCODE_j = 106;
+	public final static int KEYCODE_k = 107;
+	public final static int KEYCODE_l = 108;
+	public final static int KEYCODE_m = 109;
+	public final static int KEYCODE_n = 110;
+	public final static int KEYCODE_o = 111;
+	public final static int KEYCODE_p = 112;
+	public final static int KEYCODE_q = 113;
+	public final static int KEYCODE_r = 114;
+	public final static int KEYCODE_s = 115;
+	public final static int KEYCODE_t = 116;
+	public final static int KEYCODE_u = 117;
+	public final static int KEYCODE_v = 118;
+	public final static int KEYCODE_w = 119;
+	public final static int KEYCODE_x = 120;
+	public final static int KEYCODE_y = 121;
+	public final static int KEYCODE_z = 122;
+	public final static int KEYCODE_DELETE = 127;
+	// Numeric keypad
+	public final static int KEYCODE_KP0 = 256;
+	public final static int KEYCODE_KP1 = 257;
+	public final static int KEYCODE_KP2 = 258;
+	public final static int KEYCODE_KP3 = 259;
+	public final static int KEYCODE_KP4 = 260;
+	public final static int KEYCODE_KP5 = 261;
+	public final static int KEYCODE_KP6 = 262;
+	public final static int KEYCODE_KP7 = 263;
+	public final static int KEYCODE_KP8 = 264;
+	public final static int KEYCODE_KP9 = 265;
+	public final static int KEYCODE_KP_PERIOD = 266;
+	public final static int KEYCODE_KP_DIVIDE = 267;
+	public final static int KEYCODE_KP_MULTIPLY = 268;
+	public final static int KEYCODE_KP_MINUS = 269;
+	public final static int KEYCODE_KP_PLUS = 270;
+	public final static int KEYCODE_KP_ENTER = 271;
+	public final static int KEYCODE_KP_EQUALS = 272;
+	// Arrows + Home/End pad
+	public final static int KEYCODE_UP = 273;
+	public final static int KEYCODE_DOWN = 274;
+	public final static int KEYCODE_RIGHT = 275;
+	public final static int KEYCODE_LEFT = 276;
+	public final static int KEYCODE_INSERT = 277;
+	public final static int KEYCODE_HOME = 278;
+	public final static int KEYCODE_END = 279;
+	public final static int KEYCODE_PAGEUP = 280;
+	public final static int KEYCODE_PAGEDOWN = 281;
+	// Function keys
+	public final static int KEYCODE_F1 = 282;
+	public final static int KEYCODE_F2 = 283;
+	public final static int KEYCODE_F3 = 284;
+	public final static int KEYCODE_F4 = 285;
+	public final static int KEYCODE_F5 = 286;
+	public final static int KEYCODE_F6 = 287;
+	public final static int KEYCODE_F7 = 288;
+	public final static int KEYCODE_F8 = 289;
+	public final static int KEYCODE_F9 = 290;
+	public final static int KEYCODE_F10 = 291;
+	public final static int KEYCODE_F11 = 292;
+	public final static int KEYCODE_F12 = 293;
+	public final static int KEYCODE_F13 = 294;
+	public final static int KEYCODE_F14 = 295;
+	public final static int KEYCODE_F15 = 296;
+	// Key state modifier keys
+	public final static int KEYCODE_NUMLOCK = 300;
+	public final static int KEYCODE_CAPSLOCK = 301;
+	public final static int KEYCODE_SCROLLOCK = 302;
+	public final static int KEYCODE_RSHIFT = 303;
+	public final static int KEYCODE_LSHIFT = 304;
+	public final static int KEYCODE_RCTRL = 305;
+	public final static int KEYCODE_LCTRL = 306;
+	public final static int KEYCODE_RALT = 307;
+	public final static int KEYCODE_LALT = 308;
+	public final static int KEYCODE_RMETA = 309;
+	public final static int KEYCODE_LMETA = 310;
+	public final static int KEYCODE_LSUPER = 311; // Left "Windows" key
+	public final static int KEYCODE_RSUPER = 312; // Right "Windows" key
+	public final static int KEYCODE_MODE = 313; // "Alt Gr" key
+	public final static int KEYCODE_COMPOSE = 314; // Multi-key compose key
+	// Miscellaneous function keys
+	public final static int KEYCODE_HELP = 315;
+	public final static int KEYCODE_PRINT = 316;
+	public final static int KEYCODE_SYSREQ = 317;
+	public final static int KEYCODE_BREAK = 318;
+	public final static int KEYCODE_MENU = 319;
+	public final static int KEYCODE_POWER = 320; // Power Macintosh power key
+	public final static int KEYCODE_EURO = 321; // Some european keyboards
+	public final static int KEYCODE_UNDO = 322; // Atari keyboard has Undo
+
+	// Android KeyEvent keycode -> ScummVM keycode
+	public final static Map<Integer, Integer> androidKeyMap;
+	static {
+		Map<Integer, Integer> map = new HashMap<Integer, Integer>();
+
+		map.put(KeyEvent.KEYCODE_DEL, KEYCODE_BACKSPACE);
+		map.put(KeyEvent.KEYCODE_TAB, KEYCODE_TAB);
+		map.put(KeyEvent.KEYCODE_CLEAR, KEYCODE_CLEAR);
+		map.put(KeyEvent.KEYCODE_ENTER, KEYCODE_RETURN);
+		//map.put(??, KEYCODE_PAUSE);
+		map.put(KeyEvent.KEYCODE_BACK, KEYCODE_ESCAPE);
+		map.put(KeyEvent.KEYCODE_SPACE, KEYCODE_SPACE);
+		//map.put(??, KEYCODE_EXCLAIM);
+		//map.put(??, KEYCODE_QUOTEDBL);
+		map.put(KeyEvent.KEYCODE_POUND, KEYCODE_HASH);
+		//map.put(??, KEYCODE_DOLLAR);
+		//map.put(??, KEYCODE_AMPERSAND);
+		map.put(KeyEvent.KEYCODE_APOSTROPHE, KEYCODE_QUOTE);
+		//map.put(??, KEYCODE_LEFTPAREN);
+		//map.put(??, KEYCODE_RIGHTPAREN);
+		//map.put(??, KEYCODE_ASTERISK);
+		map.put(KeyEvent.KEYCODE_PLUS, KEYCODE_PLUS);
+		map.put(KeyEvent.KEYCODE_COMMA, KEYCODE_COMMA);
+		map.put(KeyEvent.KEYCODE_MINUS, KEYCODE_MINUS);
+		map.put(KeyEvent.KEYCODE_PERIOD, KEYCODE_PERIOD);
+		map.put(KeyEvent.KEYCODE_SLASH, KEYCODE_SLASH);
+		map.put(KeyEvent.KEYCODE_0, KEYCODE_0);
+		map.put(KeyEvent.KEYCODE_1, KEYCODE_1);
+		map.put(KeyEvent.KEYCODE_2, KEYCODE_2);
+		map.put(KeyEvent.KEYCODE_3, KEYCODE_3);
+		map.put(KeyEvent.KEYCODE_4, KEYCODE_4);
+		map.put(KeyEvent.KEYCODE_5, KEYCODE_5);
+		map.put(KeyEvent.KEYCODE_6, KEYCODE_6);
+		map.put(KeyEvent.KEYCODE_7, KEYCODE_7);
+		map.put(KeyEvent.KEYCODE_8, KEYCODE_8);
+		map.put(KeyEvent.KEYCODE_9, KEYCODE_9);
+		//map.put(??, KEYCODE_COLON);
+		map.put(KeyEvent.KEYCODE_SEMICOLON, KEYCODE_SEMICOLON);
+		//map.put(??, KEYCODE_LESS);
+		map.put(KeyEvent.KEYCODE_EQUALS, KEYCODE_EQUALS);
+		//map.put(??, KEYCODE_GREATER);
+		//map.put(??, KEYCODE_QUESTION);
+		map.put(KeyEvent.KEYCODE_AT, KEYCODE_AT);
+		map.put(KeyEvent.KEYCODE_LEFT_BRACKET, KEYCODE_LEFTBRACKET);
+		map.put(KeyEvent.KEYCODE_BACKSLASH, KEYCODE_BACKSLASH);
+		map.put(KeyEvent.KEYCODE_RIGHT_BRACKET, KEYCODE_RIGHTBRACKET);
+		//map.put(??, KEYCODE_CARET);
+		//map.put(??, KEYCODE_UNDERSCORE);
+		//map.put(??, KEYCODE_BACKQUOTE);
+		map.put(KeyEvent.KEYCODE_A, KEYCODE_a);
+		map.put(KeyEvent.KEYCODE_B, KEYCODE_b);
+		map.put(KeyEvent.KEYCODE_C, KEYCODE_c);
+		map.put(KeyEvent.KEYCODE_D, KEYCODE_d);
+		map.put(KeyEvent.KEYCODE_E, KEYCODE_e);
+		map.put(KeyEvent.KEYCODE_F, KEYCODE_f);
+		map.put(KeyEvent.KEYCODE_G, KEYCODE_g);
+		map.put(KeyEvent.KEYCODE_H, KEYCODE_h);
+		map.put(KeyEvent.KEYCODE_I, KEYCODE_i);
+		map.put(KeyEvent.KEYCODE_J, KEYCODE_j);
+		map.put(KeyEvent.KEYCODE_K, KEYCODE_k);
+		map.put(KeyEvent.KEYCODE_L, KEYCODE_l);
+		map.put(KeyEvent.KEYCODE_M, KEYCODE_m);
+		map.put(KeyEvent.KEYCODE_N, KEYCODE_n);
+		map.put(KeyEvent.KEYCODE_O, KEYCODE_o);
+		map.put(KeyEvent.KEYCODE_P, KEYCODE_p);
+		map.put(KeyEvent.KEYCODE_Q, KEYCODE_q);
+		map.put(KeyEvent.KEYCODE_R, KEYCODE_r);
+		map.put(KeyEvent.KEYCODE_S, KEYCODE_s);
+		map.put(KeyEvent.KEYCODE_T, KEYCODE_t);
+		map.put(KeyEvent.KEYCODE_U, KEYCODE_u);
+		map.put(KeyEvent.KEYCODE_V, KEYCODE_v);
+		map.put(KeyEvent.KEYCODE_W, KEYCODE_w);
+		map.put(KeyEvent.KEYCODE_X, KEYCODE_x);
+		map.put(KeyEvent.KEYCODE_Y, KEYCODE_y);
+		map.put(KeyEvent.KEYCODE_Z, KEYCODE_z);
+		//map.put(KeyEvent.KEYCODE_DEL, KEYCODE_DELETE); use BACKSPACE instead
+		//map.put(??, KEYCODE_KP_*);
+		map.put(KeyEvent.KEYCODE_DPAD_UP, KEYCODE_UP);
+		map.put(KeyEvent.KEYCODE_DPAD_DOWN, KEYCODE_DOWN);
+		map.put(KeyEvent.KEYCODE_DPAD_RIGHT, KEYCODE_RIGHT);
+		map.put(KeyEvent.KEYCODE_DPAD_LEFT, KEYCODE_LEFT);
+		//map.put(??, KEYCODE_INSERT);
+		//map.put(??, KEYCODE_HOME);
+		//map.put(??, KEYCODE_END);
+		//map.put(??, KEYCODE_PAGEUP);
+		//map.put(??, KEYCODE_PAGEDOWN);
+		//map.put(??, KEYCODE_F{1-15});
+		map.put(KeyEvent.KEYCODE_NUM, KEYCODE_NUMLOCK);
+		//map.put(??, KEYCODE_CAPSLOCK);
+		//map.put(??, KEYCODE_SCROLLLOCK);
+		map.put(KeyEvent.KEYCODE_SHIFT_RIGHT, KEYCODE_RSHIFT);
+		map.put(KeyEvent.KEYCODE_SHIFT_LEFT, KEYCODE_LSHIFT);
+		//map.put(??, KEYCODE_RCTRL);
+		//map.put(??, KEYCODE_LCTRL);
+		map.put(KeyEvent.KEYCODE_ALT_RIGHT, KEYCODE_RALT);
+		map.put(KeyEvent.KEYCODE_ALT_LEFT, KEYCODE_LALT);
+		// ?? META, SUPER
+		// ?? MODE, COMPOSE
+		// ?? HELP, PRINT, SYSREQ, BREAK, EURO, UNDO
+		map.put(KeyEvent.KEYCODE_MENU, KEYCODE_MENU);
+		map.put(KeyEvent.KEYCODE_POWER, KEYCODE_POWER);
+
+		androidKeyMap = Collections.unmodifiableMap(map);
+	}
+
+	public int type;
+	public boolean synthetic;
+	public int kbd_keycode;
+	public int kbd_ascii;
+	public int kbd_flags;
+	public int mouse_x;
+	public int mouse_y;
+	public boolean mouse_relative;	// Used for trackball events
+
+	public Event() {
+		type = EVENT_INVALID;
+		synthetic = false;
+	}
+
+	public Event(int type) {
+		this.type = type;
+		synthetic = false;
+	}
+
+	public static Event KeyboardEvent(int type, int keycode, int ascii,
+									  int flags) {
+		Event e = new Event();
+		e.type = type;
+		e.kbd_keycode = keycode;
+		e.kbd_ascii = ascii;
+		e.kbd_flags = flags;
+		return e;
+	}
+
+	public static Event MouseEvent(int type, int x, int y) {
+		Event e = new Event();
+		e.type = type;
+		e.mouse_x = x;
+		e.mouse_y = y;
+		return e;
+	}
+}


Property changes on: scummvm/trunk/backends/platform/android/org/inodes/gus/scummvm/Event.java
___________________________________________________________________
Added: svn:mime-type
   + text/plain
Added: svn:eol-style
   + native

Added: scummvm/trunk/backends/platform/android/org/inodes/gus/scummvm/PluginProvider.java
===================================================================
--- scummvm/trunk/backends/platform/android/org/inodes/gus/scummvm/PluginProvider.java	                        (rev 0)
+++ scummvm/trunk/backends/platform/android/org/inodes/gus/scummvm/PluginProvider.java	2010-06-06 09:34:36 UTC (rev 49449)
@@ -0,0 +1,52 @@
+package org.inodes.gus.scummvm;
+
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.os.Bundle;
+import android.util.Log;
+
+import java.util.ArrayList;
+
+public class PluginProvider extends BroadcastReceiver {
+	public final static String META_UNPACK_LIB =
+		"org.inodes.gus.scummvm.meta.UNPACK_LIB";
+
+	public void onReceive(Context context, Intent intent) {
+		if (!intent.getAction().equals(ScummVMApplication.ACTION_PLUGIN_QUERY))
+			return;
+
+		Bundle extras = getResultExtras(true);
+
+		final ActivityInfo info;
+		try {
+			info = context.getPackageManager()
+				.getReceiverInfo(new ComponentName(context, this.getClass()),
+								 PackageManager.GET_META_DATA);
+		} catch (PackageManager.NameNotFoundException e) {
+			Log.e(this.toString(), "Error finding my own info?", e);
+			return;
+		}
+
+		String mylib = info.metaData.getString(META_UNPACK_LIB);
+		if (mylib != null) {
+			ArrayList<String> all_libs =
+				extras.getStringArrayList(ScummVMApplication.EXTRA_UNPACK_LIBS);
+
+			all_libs.add(new Uri.Builder()
+						 .scheme("plugin")
+						 .authority(context.getPackageName())
+						 .path(mylib)
+						 .toString());
+ 
+			extras.putStringArrayList(ScummVMApplication.EXTRA_UNPACK_LIBS,
+									  all_libs);
+		}
+
+		setResultExtras(extras);
+	}
+}


Property changes on: scummvm/trunk/backends/platform/android/org/inodes/gus/scummvm/PluginProvider.java
___________________________________________________________________
Added: svn:mime-type
   + text/plain
Added: svn:eol-style
   + native

Added: scummvm/trunk/backends/platform/android/org/inodes/gus/scummvm/ScummVM.java
===================================================================
--- scummvm/trunk/backends/platform/android/org/inodes/gus/scummvm/ScummVM.java	                        (rev 0)
+++ scummvm/trunk/backends/platform/android/org/inodes/gus/scummvm/ScummVM.java	2010-06-06 09:34:36 UTC (rev 49449)
@@ -0,0 +1,317 @@
+package org.inodes.gus.scummvm;
+
+import android.content.Context;
+import android.content.res.AssetManager;
+import android.media.AudioFormat;
+import android.media.AudioManager;
+import android.media.AudioTrack;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Process;
+import android.util.Log;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+
+import javax.microedition.khronos.egl.EGL10;
+import javax.microedition.khronos.egl.EGLConfig;
+import javax.microedition.khronos.egl.EGLContext;
+import javax.microedition.khronos.egl.EGLDisplay;
+import javax.microedition.khronos.egl.EGLSurface;
+
+import java.io.File;
+import java.util.concurrent.Semaphore;
+
+
+// At least in Android 2.1, eglCreateWindowSurface() requires an
+// EGLNativeWindowSurface object, which is hidden deep in the bowels
+// of libui.  Until EGL is properly exposed, it's probably safer to
+// use the Java versions of most EGL functions :(
+
+public class ScummVM implements SurfaceHolder.Callback {
+	private final static String LOG_TAG = "ScummVM.java";
+
+	private final int AUDIO_FRAME_SIZE = 2 * 2;  // bytes. 16bit audio * stereo
+	public static class AudioSetupException extends Exception {}
+
+	private long nativeScummVM;	// native code hangs itself here
+    boolean scummVMRunning = false;
+
+	private native void create(AssetManager am);
+
+	public ScummVM(Context context) {
+		create(context.getAssets());  // Init C++ code, set nativeScummVM
+	}
+
+	private native void nativeDestroy(); 
+
+	public synchronized void destroy() {
+		if (nativeScummVM != 0) {
+			nativeDestroy();
+			nativeScummVM = 0;
+		}
+	}
+	protected void finalize() {
+		destroy();
+	}
+
+    // Surface creation:
+    // GUI thread: create surface, release lock
+    // ScummVM thread: acquire lock (block), read surface
+    //
+    // Surface deletion:
+    // GUI thread: post event, acquire lock (block), return
+    // ScummVM thread: read event, free surface, release lock
+    //
+    // In other words, ScummVM thread does this:
+    //  acquire lock
+    //  setup surface
+    //  when SCREEN_CHANGED arrives:
+    //   destroy surface
+    //   release lock
+    //  back to acquire lock
+    static final int configSpec[] = {
+		EGL10.EGL_RED_SIZE, 5,
+		EGL10.EGL_GREEN_SIZE, 5,
+		EGL10.EGL_BLUE_SIZE, 5,
+		EGL10.EGL_DEPTH_SIZE, 0,
+		EGL10.EGL_SURFACE_TYPE, EGL10.EGL_WINDOW_BIT,
+		EGL10.EGL_NONE,
+    };
+    EGL10 egl;
+    EGLDisplay eglDisplay = EGL10.EGL_NO_DISPLAY;
+    EGLConfig eglConfig;
+    EGLContext eglContext = EGL10.EGL_NO_CONTEXT;
+    EGLSurface eglSurface = EGL10.EGL_NO_SURFACE;
+    Semaphore surfaceLock = new Semaphore(0, true);
+    SurfaceHolder nativeSurface;
+
+    public void surfaceCreated(SurfaceHolder holder) {
+		nativeSurface = holder;
+		surfaceLock.release();
+    }
+
+    public void surfaceChanged(SurfaceHolder holder, int format,
+							   int width, int height) {
+		// Disabled while I debug GL problems
+		//pushEvent(new Event(Event.EVENT_SCREEN_CHANGED));
+    }
+
+    public void surfaceDestroyed(SurfaceHolder holder) {
+		pushEvent(new Event(Event.EVENT_SCREEN_CHANGED));
+		try {
+			surfaceLock.acquire();
+		} catch (InterruptedException e) {
+			Log.e(this.toString(),
+				  "Interrupted while waiting for surface lock", e);
+		}
+    }
+
+    // Called by ScummVM thread (from initBackend)
+    private void createScummVMGLContext() {
+		egl = (EGL10)EGLContext.getEGL();
+		eglDisplay = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
+		int[] version = new int[2];
+		egl.eglInitialize(eglDisplay, version);
+		int[] num_config = new int[1];
+		egl.eglChooseConfig(eglDisplay, configSpec, null, 0, num_config);
+
+		final int numConfigs = num_config[0];
+		if (numConfigs <= 0)
+			throw new IllegalArgumentException("No configs match configSpec");
+
+		EGLConfig[] configs = new EGLConfig[numConfigs];
+		egl.eglChooseConfig(eglDisplay, configSpec, configs, numConfigs,
+							num_config);
+		eglConfig = configs[0];
+
+		eglContext = egl.eglCreateContext(eglDisplay, eglConfig,
+										  EGL10.EGL_NO_CONTEXT, null);
+    }
+
+    // Called by ScummVM thread
+    protected void setupScummVMSurface() {
+		try {
+			surfaceLock.acquire();
+		} catch (InterruptedException e) {
+			Log.e(this.toString(),
+				  "Interrupted while waiting for surface lock", e);
+			return;
+		}
+		eglSurface = egl.eglCreateWindowSurface(eglDisplay, eglConfig,
+												nativeSurface, null);
+		egl.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext);
+    }
+
+    // Called by ScummVM thread
+    protected void destroyScummVMSurface() {
+		if (eglSurface != null) {
+			egl.eglMakeCurrent(eglDisplay, EGL10.EGL_NO_SURFACE,
+							   EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
+			egl.eglDestroySurface(eglDisplay, eglSurface);
+			eglSurface = EGL10.EGL_NO_SURFACE;
+		}
+
+		surfaceLock.release();
+    }
+
+    public void setSurface(SurfaceHolder holder) {
+		holder.addCallback(this);
+    }
+
+	// Set scummvm config options
+	final public native static void loadConfigFile(String path);
+	final public native static void setConfMan(String key, int value);
+	final public native static void setConfMan(String key, String value);
+
+	// Feed an event to ScummVM.  Safe to call from other threads.
+	final public native void pushEvent(Event e);
+
+	final private native void audioMixCallback(byte[] buf);
+
+	// Runs the actual ScummVM program and returns when it does.
+	// This should not be called from multiple threads simultaneously...
+	final public native int scummVMMain(String[] argv);
+
+	// Callbacks from C++ peer instance
+	//protected GraphicsMode[] getSupportedGraphicsModes() {}
+	protected void displayMessageOnOSD(String msg) {}
+	protected void setWindowCaption(String caption) {}
+	protected void showVirtualKeyboard(boolean enable) {}
+	protected String[] getSysArchives() { return new String[0]; }
+	protected String[] getPluginDirectories() { return new String[0]; }
+    protected void initBackend() throws AudioSetupException {
+		createScummVMGLContext();
+		initAudio();
+    }
+
+	private static class AudioThread extends Thread {
+		final private int buf_size;
+		private boolean is_paused = false;
+		final private ScummVM scummvm;
+		final private AudioTrack audio_track;
+
+		AudioThread(ScummVM scummvm, AudioTrack audio_track, int buf_size) {
+			super("AudioThread");
+			this.scummvm = scummvm;
+			this.audio_track = audio_track;
+			this.buf_size = buf_size;
+			setPriority(Thread.MAX_PRIORITY);
+			setDaemon(true);
+		}
+
+		public void pauseAudio() {
+			synchronized (this) {
+				is_paused = true;
+			}
+			audio_track.pause();
+		}
+
+		public void resumeAudio() {
+			synchronized (this) {
+				is_paused = false;
+				notifyAll();
+			}
+			audio_track.play();
+		}
+
+		public void run() {
+			byte[] buf = new byte[buf_size];
+			audio_track.play();
+			int offset = 0;
+			try {
+				while (true) {
+					synchronized (this) {
+						while (is_paused)
+							wait();
+					}
+
+					if (offset == buf.length) {
+						// Grab new audio data
+						scummvm.audioMixCallback(buf);
+						offset = 0;
+					}
+					int len = buf.length - offset;
+					int ret = audio_track.write(buf, offset, len);
+					if (ret < 0) {
+						Log.w(LOG_TAG, String.format(
+							"AudioTrack.write(%dB) returned error %d",
+							buf.length, ret));
+						break;
+					} else if (ret != len) {
+						Log.w(LOG_TAG, String.format(
+							"Short audio write.  Wrote %dB, not %dB",
+							ret, buf.length));
+						// Buffer is full, so yield cpu for a while
+						Thread.sleep(100);
+					}
+					offset += ret;
+				}
+			} catch (InterruptedException e) {
+				Log.e(this.toString(), "Audio thread interrupted", e);
+			}
+		}
+	}
+	private AudioThread audio_thread;
+
+	final public int audioSampleRate() {
+		return AudioTrack.getNativeOutputSampleRate(AudioManager.STREAM_MUSIC);
+	}
+
+	private void initAudio() throws AudioSetupException {
+		int sample_rate = audioSampleRate();
+		int buf_size =
+			AudioTrack.getMinBufferSize(sample_rate,
+										AudioFormat.CHANNEL_CONFIGURATION_STEREO,
+										AudioFormat.ENCODING_PCM_16BIT);
+		if (buf_size < 0) {
+			int guess = AUDIO_FRAME_SIZE * sample_rate / 100;  // 10ms of audio
+			Log.w(LOG_TAG, String.format(
+										 "Unable to get min audio buffer size (error %d). Guessing %dB.",
+										 buf_size, guess));
+			buf_size = guess;
+		}
+		Log.d(LOG_TAG, String.format("Using %dB buffer for %dHZ audio",
+									 buf_size, sample_rate));
+		AudioTrack audio_track =
+			new AudioTrack(AudioManager.STREAM_MUSIC,
+						   sample_rate,
+						   AudioFormat.CHANNEL_CONFIGURATION_STEREO,
+						   AudioFormat.ENCODING_PCM_16BIT,
+						   buf_size,
+						   AudioTrack.MODE_STREAM);
+		if (audio_track.getState() != AudioTrack.STATE_INITIALIZED) {
+			Log.e(LOG_TAG, "Error initialising Android audio system.");
+			throw new AudioSetupException();
+		}
+
+		audio_thread = new AudioThread(this, audio_track, buf_size);
+		audio_thread.start();
+	}
+
+	public void pause() {
+		audio_thread.pauseAudio();
+		// TODO: need to pause engine too
+	}
+
+	public void resume() {
+		// TODO: need to resume engine too
+		audio_thread.resumeAudio();
+	}
+
+	static {
+		// For grabbing with gdb...
+		final boolean sleep_for_debugger = false;
+		if (sleep_for_debugger) {
+			try {
+				Thread.sleep(20*1000);
+			} catch (InterruptedException e) {
+			}
+		}
+
+		//System.loadLibrary("scummvm");
+		File cache_dir = ScummVMApplication.getLastCacheDir();
+		String libname = System.mapLibraryName("scummvm");
+		File libpath = new File(cache_dir, libname);
+		System.load(libpath.getPath());
+	}
+}


Property changes on: scummvm/trunk/backends/platform/android/org/inodes/gus/scummvm/ScummVM.java
___________________________________________________________________
Added: svn:mime-type
   + text/plain
Added: svn:eol-style
   + native

Added: scummvm/trunk/backends/platform/android/org/inodes/gus/scummvm/ScummVMActivity.java
===================================================================
--- scummvm/trunk/backends/platform/android/org/inodes/gus/scummvm/ScummVMActivity.java	                        (rev 0)
+++ scummvm/trunk/backends/platform/android/org/inodes/gus/scummvm/ScummVMActivity.java	2010-06-06 09:34:36 UTC (rev 49449)
@@ -0,0 +1,446 @@
+package org.inodes.gus.scummvm;
+
+import android.app.AlertDialog;
+import android.app.Activity;
+import android.content.DialogInterface;
+import android.content.res.Configuration;
+import android.media.AudioManager;
+import android.os.Bundle;
+import android.os.Environment;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+import android.view.inputmethod.InputMethodManager;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.SurfaceView;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.widget.Toast;
+
+import java.io.IOException;
+
+public class ScummVMActivity extends Activity {
+	private boolean _do_right_click;
+	private boolean _last_click_was_right;
+
+	// game pixels to move per trackball/dpad event.
+	// FIXME: replace this with proper mouse acceleration
+	private final static int TRACKBALL_SCALE = 2;
+
+	private class MyScummVM extends ScummVM {
+		private boolean scummvmRunning = false;
+
+		public MyScummVM() {
+			super(ScummVMActivity.this);
+		}
+
+		@Override
+		protected void initBackend() throws ScummVM.AudioSetupException {
+			synchronized (this) {
+				scummvmRunning = true;
+				notifyAll();
+			}
+			super.initBackend();
+		}
+
+		public void waitUntilRunning() throws InterruptedException {
+			synchronized (this) {
+				while (!scummvmRunning)
+					wait();
+			}
+		}
+
+		@Override
+		protected void displayMessageOnOSD(String msg) {
+			Log.i(this.toString(), "OSD: " + msg);
+			Toast.makeText(ScummVMActivity.this, msg, Toast.LENGTH_LONG).show();
+		}
+
+		@Override
+		protected void setWindowCaption(final String caption) {
+			runOnUiThread(new Runnable() {
+					public void run() {
+						setTitle(caption);
+					}
+				});
+		}
+
+		@Override
+		protected String[] getPluginDirectories() {
+			String[] dirs = new String[1];
+			dirs[0] = ScummVMApplication.getLastCacheDir().getPath();
+			return dirs;
+		}
+
+		@Override
+		protected void showVirtualKeyboard(final boolean enable) {
+			if (getResources().getConfiguration().keyboard ==
+				Configuration.KEYBOARD_NOKEYS) {
+				runOnUiThread(new Runnable() {
+						public void run() {
+							showKeyboard(enable);
+						}
+					});
+			}
+		}	
+	}
+	private MyScummVM scummvm;
+	private Thread scummvm_thread;
+
+	@Override
+	public void onCreate(Bundle savedInstanceState) {
+		super.onCreate(savedInstanceState);
+
+		_do_right_click = false;
+		setVolumeControlStream(AudioManager.STREAM_MUSIC);
+
+		setContentView(R.layout.main);
+		takeKeyEvents(true);
+
+		// This is a common enough error that we should warn about it
+		// explicitly.
+		if (!Environment.getExternalStorageDirectory().canRead()) {
+			new AlertDialog.Builder(this)
+				.setTitle(R.string.no_sdcard_title)
+				.setIcon(android.R.drawable.ic_dialog_alert)
+				.setMessage(R.string.no_sdcard)
+				.setNegativeButton(R.string.quit,
+								   new DialogInterface.OnClickListener() {
+									   public void onClick(DialogInterface dialog,
+														   int which) {
+										   finish();
+									   }
+								   })
+				.show();
+			return;
+		}
+
+		SurfaceView main_surface = (SurfaceView)findViewById(R.id.main_surface);
+		main_surface.setOnTouchListener(new View.OnTouchListener() {
+				public boolean onTouch(View v, MotionEvent event) {
+					return onTouchEvent(event);
+				}
+			});
+		main_surface.setOnKeyListener(new View.OnKeyListener() {
+				public boolean onKey(View v, int code, KeyEvent ev) {
+					return onKeyDown(code, ev);
+				}
+			});
+		main_surface.requestFocus();
+
+		// Start ScummVM
+		scummvm = new MyScummVM();
+		scummvm_thread = new Thread(new Runnable() {
+				public void run() {
+					try {
+						runScummVM();
+					} catch (Exception e) {
+						Log.e("ScummVM", "Fatal error in ScummVM thread", e);
+						new AlertDialog.Builder(ScummVMActivity.this)
+							.setTitle("Error")
+							.setMessage(e.toString())
+							.setIcon(android.R.drawable.ic_dialog_alert)
+							.show();
+						finish();
+					}
+				}
+			}, "ScummVM");
+		scummvm_thread.start();
+
+		// Block UI thread until ScummVM has started.  In particular,
+		// this means that surface and event callbacks should be safe
+		// after this point.
+		try {
+			scummvm.waitUntilRunning();
+		} catch (InterruptedException e) {
+			Log.e(this.toString(),
+				  "Interrupted while waiting for ScummVM.initBackend", e);
+			finish();
+		}
+
+		scummvm.setSurface(main_surface.getHolder());
+	}
+
+	// Runs in another thread
+	private void runScummVM() throws IOException {
+		getFilesDir().mkdirs();
+		String[] args = {
+			"ScummVM-lib",
+			"--config=" + getFileStreamPath("scummvmrc").getPath(),
+			"--path=" + Environment.getExternalStorageDirectory().getPath(),
+			"--gui-theme=scummmodern",
+			"--savepath=" + getDir("saves", 0).getPath(),
+		};
+
+		int ret = scummvm.scummVMMain(args);
+
+		// On exit, tear everything down for a fresh
+		// restart next time.
+		System.exit(ret);
+	}
+
+	private boolean was_paused = false;
+
+	@Override
+	public void onPause() {
+		if (scummvm != null) {
+			was_paused = true;
+			scummvm.pause();
+		}
+		super.onPause();
+	}
+
+	@Override
+	public void onResume() {
+		super.onResume();
+		if (scummvm != null && was_paused)
+			scummvm.resume();
+		was_paused = false;
+	}
+
+	@Override
+	public void onStop() {
+		if (scummvm != null) {
+			scummvm.pushEvent(new Event(Event.EVENT_QUIT));
+			try {
+				scummvm_thread.join(1000);	// 1s timeout
+			} catch (InterruptedException e) {
+				Log.i(this.toString(),
+					  "Error while joining ScummVM thread", e);
+			}
+		}
+		super.onStop();
+	}
+
+	static final int MSG_MENU_LONG_PRESS = 1;
+	private final Handler keycodeMenuTimeoutHandler = new Handler() {
+			@Override
+			public void handleMessage(Message msg) {
+				if (msg.what == MSG_MENU_LONG_PRESS) {
+					InputMethodManager imm = (InputMethodManager)
+						getSystemService(INPUT_METHOD_SERVICE);
+					if (imm != null)

@@ Diff output truncated at 100000 characters. @@

This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.




More information about the Scummvm-git-logs mailing list