[Scummvm-git-logs] scummvm branch-2-1 -> 3ba8a1f3df394aa7ce03b496f5ca0e2ea74d11bb

antoniou79 a.antoniou79 at gmail.com
Mon Jan 13 12:43:16 UTC 2020


This automated email contains information about 11 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
0e028625dc OPENGL: Implement high DPI support on Android (#1895)
01b88d0651 ANDROID: Fix OSD message display crash or fail
4e07f9acd3 ANDROID: Disable support for split screen
f444e7f07b ANDROID: Override UTF-8 compliant definition of vsn_printf
d6b9edeb38 ANDROID: Remove duplicate header inclusion in android.h
d72cd2fe74 CONFIGURE: Detect SDL_net independently from SDL
6f333314f0 ANDROID: Add docs and port dist files to Assets
dad35969f2 ANDROID: Improve keyboard support (#1857)
f23c07b3ef ANDROID: Long press for back button has an alternate (menu button) function
45cc824813 ANDROID: Fix bad message id for long press back btn
3ba8a1f3df ANDROID: Fix crash due to adding '.' folder in SearchManager


Commit: 0e028625dca64444b1c99aed70deb6a6228db27f
    https://github.com/scummvm/scummvm/commit/0e028625dca64444b1c99aed70deb6a6228db27f
Author: Cameron Cawley (ccawley2011 at gmail.com)
Date: 2020-01-13T14:28:13+02:00

Commit Message:
OPENGL: Implement high DPI support on Android (#1895)

* OPENGL: Implement high DPI support on Android

* PSP2: Fix build

Changed paths:
    backends/graphics/opengl/opengl-graphics.cpp
    backends/graphics/opengl/opengl-graphics.h
    backends/graphics/openglsdl/openglsdl-graphics.cpp
    backends/graphics/openglsdl/openglsdl-graphics.h
    backends/graphics/psp2sdl/psp2sdl-graphics.cpp
    backends/graphics/sdl/sdl-graphics.cpp
    backends/graphics/sdl/sdl-graphics.h
    backends/graphics/surfacesdl/surfacesdl-graphics.cpp
    backends/graphics/surfacesdl/surfacesdl-graphics.h
    backends/graphics/windowed.h
    backends/platform/android/graphics.cpp


diff --git a/backends/graphics/opengl/opengl-graphics.cpp b/backends/graphics/opengl/opengl-graphics.cpp
index 32ae0fa..a01047e 100644
--- a/backends/graphics/opengl/opengl-graphics.cpp
+++ b/backends/graphics/opengl/opengl-graphics.cpp
@@ -905,7 +905,7 @@ void OpenGLGraphicsManager::grabPalette(byte *colors, uint start, uint num) cons
 	memcpy(colors, _gamePalette + start * 3, num * 3);
 }
 
-void OpenGLGraphicsManager::handleResizeImpl(const int width, const int height) {
+void OpenGLGraphicsManager::handleResizeImpl(const int width, const int height, const int xdpi, const int ydpi) {
 	// Setup backbuffer size.
 	_backBuffer.setDimensions(width, height);
 
@@ -938,6 +938,10 @@ void OpenGLGraphicsManager::handleResizeImpl(const int width, const int height)
 	overlayWidth = MAX<uint>(overlayWidth, 256);
 	overlayHeight = MAX<uint>(overlayHeight, 200);
 
+	// HACK: Reduce the size of the overlay on high DPI screens.
+	overlayWidth = fracToInt(overlayWidth * (intToFrac(90) / xdpi));
+	overlayHeight = fracToInt(overlayHeight * (intToFrac(90) / ydpi));
+
 	if (!_overlay || _overlay->getFormat() != _defaultFormatAlpha) {
 		delete _overlay;
 		_overlay = nullptr;
@@ -1004,7 +1008,7 @@ void OpenGLGraphicsManager::notifyContextCreate(const Graphics::PixelFormat &def
 
 	// Refresh the output screen dimensions if some are set up.
 	if (_windowWidth != 0 && _windowHeight != 0) {
-		handleResize(_windowWidth, _windowHeight);
+		handleResize(_windowWidth, _windowHeight, _xdpi, _ydpi);
 	}
 
 	// TODO: Should we try to convert textures into one of those formats if
@@ -1271,6 +1275,16 @@ void OpenGLGraphicsManager::recalculateCursorScaling() {
 
 		_cursorHotspotYScaled = fracToInt(_cursorHotspotYScaled * screenScaleFactorY);
 		_cursorHeightScaled   = fracToInt(_cursorHeightScaled   * screenScaleFactorY);
+	} else {
+		const frac_t screenScaleFactorX = intToFrac(90) / _xdpi;
+		const frac_t screenScaleFactorY = intToFrac(90) / _ydpi;
+
+		// FIXME: Replace this with integer maths
+		_cursorHotspotXScaled /= fracToDouble(screenScaleFactorX);
+		_cursorWidthScaled    /= fracToDouble(screenScaleFactorX);
+
+		_cursorHotspotYScaled /= fracToDouble(screenScaleFactorY);
+		_cursorHeightScaled   /= fracToDouble(screenScaleFactorY);
 	}
 }
 
diff --git a/backends/graphics/opengl/opengl-graphics.h b/backends/graphics/opengl/opengl-graphics.h
index f88315b..322f1d8 100644
--- a/backends/graphics/opengl/opengl-graphics.h
+++ b/backends/graphics/opengl/opengl-graphics.h
@@ -309,7 +309,7 @@ protected:
 
 	virtual bool gameNeedsAspectRatioCorrection() const override;
 	virtual void recalculateDisplayAreas() override;
-	virtual void handleResizeImpl(const int width, const int height) override;
+	virtual void handleResizeImpl(const int width, const int height, const int xdpi, const int ydpi) override;
 
 	/**
 	 * The default pixel format of the backend.
diff --git a/backends/graphics/openglsdl/openglsdl-graphics.cpp b/backends/graphics/openglsdl/openglsdl-graphics.cpp
index 2d2a1ba..d959e02 100644
--- a/backends/graphics/openglsdl/openglsdl-graphics.cpp
+++ b/backends/graphics/openglsdl/openglsdl-graphics.cpp
@@ -305,7 +305,8 @@ void OpenGLSdlGraphicsManager::notifyResize(const int width, const int height) {
 	getWindowSizeFromSdl(&currentWidth, &currentHeight);
 	if (width != currentWidth || height != currentHeight)
 		return;
-	handleResize(width, height);
+	// TODO: Implement high DPI support
+	handleResize(width, height, 90, 90);
 #else
 	if (!_ignoreResizeEvents && _hwScreen && !(_hwScreen->flags & SDL_FULLSCREEN)) {
 		// We save that we handled a resize event here. We need to know this
@@ -357,9 +358,9 @@ void *OpenGLSdlGraphicsManager::getProcAddress(const char *name) const {
 	return SDL_GL_GetProcAddress(name);
 }
 
-void OpenGLSdlGraphicsManager::handleResizeImpl(const int width, const int height) {
-	OpenGLGraphicsManager::handleResizeImpl(width, height);
-	SdlGraphicsManager::handleResizeImpl(width, height);
+void OpenGLSdlGraphicsManager::handleResizeImpl(const int width, const int height, const int xdpi, const int ydpi) {
+	OpenGLGraphicsManager::handleResizeImpl(width, height, xdpi, ydpi);
+	SdlGraphicsManager::handleResizeImpl(width, height, xdpi, ydpi);
 }
 
 bool OpenGLSdlGraphicsManager::saveScreenshot(const Common::String &filename) const {
@@ -463,7 +464,8 @@ bool OpenGLSdlGraphicsManager::setupMode(uint width, uint height) {
 	notifyContextCreate(rgba8888, rgba8888);
 	int actualWidth, actualHeight;
 	getWindowSizeFromSdl(&actualWidth, &actualHeight);
-	handleResize(actualWidth, actualHeight);
+	// TODO: Implement high DPI support
+	handleResize(actualWidth, actualHeight, 90, 90);
 	return true;
 #else
 	// WORKAROUND: Working around infamous SDL bugs when switching
@@ -510,7 +512,7 @@ bool OpenGLSdlGraphicsManager::setupMode(uint width, uint height) {
 
 	if (_hwScreen) {
 		notifyContextCreate(rgba8888, rgba8888);
-		handleResize(_hwScreen->w, _hwScreen->h);
+		handleResize(_hwScreen->w, _hwScreen->h, 90, 90);
 	}
 
 	// Ignore resize events (from SDL) for a few frames, if this isn't
diff --git a/backends/graphics/openglsdl/openglsdl-graphics.h b/backends/graphics/openglsdl/openglsdl-graphics.h
index 2729a52..929adc9 100644
--- a/backends/graphics/openglsdl/openglsdl-graphics.h
+++ b/backends/graphics/openglsdl/openglsdl-graphics.h
@@ -60,7 +60,7 @@ protected:
 
 	virtual void *getProcAddress(const char *name) const override;
 
-	virtual void handleResizeImpl(const int width, const int height) override;
+	virtual void handleResizeImpl(const int width, const int height, const int xdpi, const int ydpi) override;
 
 	virtual bool saveScreenshot(const Common::String &filename) const override;
 
diff --git a/backends/graphics/psp2sdl/psp2sdl-graphics.cpp b/backends/graphics/psp2sdl/psp2sdl-graphics.cpp
index f6333e2..2d26727 100644
--- a/backends/graphics/psp2sdl/psp2sdl-graphics.cpp
+++ b/backends/graphics/psp2sdl/psp2sdl-graphics.cpp
@@ -94,7 +94,7 @@ PSP2SdlGraphicsManager::PSP2SdlGraphicsManager(SdlEventSource *sdlEventSource, S
 	_shaders[0] = NULL;
 
 	/* Vita display size is always 960x544 (that's just the hardware) */
-	handleResize(960, 544);
+	handleResize(960, 544, 90, 90);
 }
 
 PSP2SdlGraphicsManager::~PSP2SdlGraphicsManager() {
diff --git a/backends/graphics/sdl/sdl-graphics.cpp b/backends/graphics/sdl/sdl-graphics.cpp
index 4f724ed..e5a49ce 100644
--- a/backends/graphics/sdl/sdl-graphics.cpp
+++ b/backends/graphics/sdl/sdl-graphics.cpp
@@ -231,7 +231,7 @@ void SdlGraphicsManager::setSystemMousePosition(const int x, const int y) {
 	}
 }
 
-void SdlGraphicsManager::handleResizeImpl(const int width, const int height) {
+void SdlGraphicsManager::handleResizeImpl(const int width, const int height, const int xdpi, const int ydpi) {
 	_eventSource->resetKeyboardEmulation(width - 1, height - 1);
 	_forceRedraw = true;
 }
diff --git a/backends/graphics/sdl/sdl-graphics.h b/backends/graphics/sdl/sdl-graphics.h
index 77c8d5d..d67557b 100644
--- a/backends/graphics/sdl/sdl-graphics.h
+++ b/backends/graphics/sdl/sdl-graphics.h
@@ -163,7 +163,7 @@ protected:
 
 	virtual void setSystemMousePosition(const int x, const int y) override;
 
-	virtual void handleResizeImpl(const int width, const int height) override;
+	virtual void handleResizeImpl(const int width, const int height, const int xdpi, const int ydpi) override;
 
 #if SDL_VERSION_ATLEAST(2, 0, 0)
 public:
diff --git a/backends/graphics/surfacesdl/surfacesdl-graphics.cpp b/backends/graphics/surfacesdl/surfacesdl-graphics.cpp
index 5332762..958ce51 100644
--- a/backends/graphics/surfacesdl/surfacesdl-graphics.cpp
+++ b/backends/graphics/surfacesdl/surfacesdl-graphics.cpp
@@ -1022,7 +1022,7 @@ bool SurfaceSdlGraphicsManager::loadGFXMode() {
 	}
 
 #if !SDL_VERSION_ATLEAST(2, 0, 0)
-	handleResize(_videoMode.hardwareWidth, _videoMode.hardwareHeight);
+	handleResize(_videoMode.hardwareWidth, _videoMode.hardwareHeight, 90, 90);
 #endif
 
 	//
@@ -2535,8 +2535,8 @@ void SurfaceSdlGraphicsManager::drawOSD() {
 
 #endif
 
-void SurfaceSdlGraphicsManager::handleResizeImpl(const int width, const int height) {
-	SdlGraphicsManager::handleResizeImpl(width, height);
+void SurfaceSdlGraphicsManager::handleResizeImpl(const int width, const int height, const int xdpi, const int ydpi) {
+	SdlGraphicsManager::handleResizeImpl(width, height, xdpi, ydpi);
 	recalculateDisplayAreas();
 }
 
@@ -2743,7 +2743,7 @@ void SurfaceSdlGraphicsManager::notifyVideoExpose() {
 
 void SurfaceSdlGraphicsManager::notifyResize(const int width, const int height) {
 #if SDL_VERSION_ATLEAST(2, 0, 0)
-	handleResize(width, height);
+	handleResize(width, height, _xdpi, _ydpi);
 #endif
 }
 
@@ -2790,8 +2790,9 @@ SDL_Surface *SurfaceSdlGraphicsManager::SDL_SetVideoMode(int width, int height,
 		return nullptr;
 	}
 
+	// TODO: Implement high DPI support
 	getWindowSizeFromSdl(&_windowWidth, &_windowHeight);
-	handleResize(_windowWidth, _windowHeight);
+	handleResize(_windowWidth, _windowHeight, 90, 90);
 
 	SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, _videoMode.filtering ? "linear" : "nearest");
 
diff --git a/backends/graphics/surfacesdl/surfacesdl-graphics.h b/backends/graphics/surfacesdl/surfacesdl-graphics.h
index 7888088..6b8ae36 100644
--- a/backends/graphics/surfacesdl/surfacesdl-graphics.h
+++ b/backends/graphics/surfacesdl/surfacesdl-graphics.h
@@ -185,7 +185,7 @@ protected:
 		return _videoMode.scaleFactor;
 	}
 
-	virtual void handleResizeImpl(const int width, const int height) override;
+	virtual void handleResizeImpl(const int width, const int height, const int xdpi, const int ydpi) override;
 
 	virtual int getGraphicsModeScale(int mode) const override;
 	virtual ScalerProc *getGraphicsScalerProc(int mode) const;
diff --git a/backends/graphics/windowed.h b/backends/graphics/windowed.h
index f94471d..9911527 100644
--- a/backends/graphics/windowed.h
+++ b/backends/graphics/windowed.h
@@ -50,6 +50,8 @@ public:
 		_cursorVisible(false),
 		_cursorX(0),
 		_cursorY(0),
+		_xdpi(90),
+		_ydpi(90),
 		_cursorNeedsRedraw(false),
 		_cursorLastInActiveArea(true) {}
 
@@ -95,7 +97,7 @@ protected:
 	 * Backend-specific implementation for updating internal surfaces that need
 	 * to reflect the new window size.
 	 */
-	virtual void handleResizeImpl(const int width, const int height) = 0;
+	virtual void handleResizeImpl(const int width, const int height, const int xdpi, const int ydpi) = 0;
 
 	/**
 	 * Converts the given point from the active virtual screen's coordinate
@@ -174,10 +176,12 @@ protected:
 	 * @param width The new width of the window, excluding window decoration.
 	 * @param height The new height of the window, excluding window decoration.
 	 */
-	void handleResize(const int width, const int height) {
+	void handleResize(const int width, const int height, const int xdpi, const int ydpi) {
 		_windowWidth = width;
 		_windowHeight = height;
-		handleResizeImpl(width, height);
+		_xdpi = xdpi;
+		_ydpi = ydpi;
+		handleResizeImpl(width, height, xdpi, ydpi);
 	}
 
 	/**
@@ -278,6 +282,11 @@ protected:
 	int _windowHeight;
 
 	/**
+	 * The DPI of the window.
+	 */
+	int _xdpi, _ydpi;
+
+	/**
 	 * Whether the overlay (i.e. launcher, including the out-of-game launcher)
 	 * is visible or not.
 	 */
diff --git a/backends/platform/android/graphics.cpp b/backends/platform/android/graphics.cpp
index c22e9bf..c359fcf 100644
--- a/backends/platform/android/graphics.cpp
+++ b/backends/platform/android/graphics.cpp
@@ -72,7 +72,9 @@ void AndroidGraphicsManager::initSurface() {
 	// mode we setup.
 	notifyContextCreate(Graphics::PixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0), Graphics::PixelFormat(2, 5, 5, 5, 1, 11, 6, 1, 0));
 
-	handleResize(JNI::egl_surface_width, JNI::egl_surface_height);
+	float dpi[2];
+	JNI::getDPI(dpi);
+	handleResize(JNI::egl_surface_width, JNI::egl_surface_height, dpi[0], dpi[1]);
 }
 
 void AndroidGraphicsManager::deinitSurface() {


Commit: 01b88d06512a070c1065bc99d8b5bb6b2ef791e3
    https://github.com/scummvm/scummvm/commit/01b88d06512a070c1065bc99d8b5bb6b2ef791e3
Author: Thanasis Antoniou (a.antoniou79 at gmail.com)
Date: 2020-01-13T14:28:23+02:00

Commit Message:
ANDROID: Fix OSD message display crash or fail

For translated message text. Android expected UTF-8 format for the message

Also makeToast for OSD needed to be run from the main UI thread

Changed paths:
    backends/platform/android/jni.cpp
    backends/platform/android/org/scummvm/scummvm/ScummVMActivity.java


diff --git a/backends/platform/android/jni.cpp b/backends/platform/android/jni.cpp
index 8c330c0..cedee38 100644
--- a/backends/platform/android/jni.cpp
+++ b/backends/platform/android/jni.cpp
@@ -46,6 +46,7 @@
 #include "common/error.h"
 #include "common/textconsole.h"
 #include "common/translation.h"
+#include "common/encoding.h"
 #include "engines/engine.h"
 
 #include "backends/platform/android/android.h"
@@ -225,8 +226,24 @@ void JNI::getDPI(float *values) {
 }
 
 void JNI::displayMessageOnOSD(const char *msg) {
+	// called from common/osd_message_queue, method: OSDMessageQueue::pollEvent()
 	JNIEnv *env = JNI::getEnv();
-	jstring java_msg = env->NewStringUTF(msg);
+//	LOGD("OSD orig MESSAGE: %s", msg);
+	Common::String fromEncoding = "ISO-8859-1";
+#ifdef USE_TRANSLATION
+	if (TransMan.getCurrentCharset() != "ASCII") {
+		fromEncoding = TransMan.getCurrentCharset();
+	}
+#endif
+	Common::Encoding converter("UTF-8", fromEncoding.c_str());
+
+	const char *utf8Msg = converter.convert(msg, converter.stringLength(msg, fromEncoding) );
+	if (utf8Msg == nullptr) {
+		LOGE("Failed to convert message to UTF-8 for OSD!");
+		return;
+	}
+//	LOGD("OSD target MESSAGE: %s", utf8Msg);
+	jstring java_msg = env->NewStringUTF(utf8Msg);
 
 	env->CallVoidMethod(_jobj, _MID_displayMessageOnOSD, java_msg);
 
@@ -693,6 +710,7 @@ void JNI::setPause(JNIEnv *env, jobject self, jboolean value) {
 jstring JNI::getCurrentCharset(JNIEnv *env, jobject self) {
 #ifdef USE_TRANSLATION
 	if (TransMan.getCurrentCharset() != "ASCII") {
+//		LOGD("getCurrentCharset: %s", TransMan.getCurrentCharset().c_str());
 		return env->NewStringUTF(TransMan.getCurrentCharset().c_str());
 	}
 #endif
diff --git a/backends/platform/android/org/scummvm/scummvm/ScummVMActivity.java b/backends/platform/android/org/scummvm/scummvm/ScummVMActivity.java
index dca52a9..f55639f 100644
--- a/backends/platform/android/org/scummvm/scummvm/ScummVMActivity.java
+++ b/backends/platform/android/org/scummvm/scummvm/ScummVMActivity.java
@@ -76,9 +76,15 @@ public class ScummVMActivity extends Activity {
 		}
 
 		@Override
-		protected void displayMessageOnOSD(String msg) {
-			Log.i(LOG_TAG, "OSD: " + msg);
-			Toast.makeText(ScummVMActivity.this, msg, Toast.LENGTH_LONG).show();
+		protected void displayMessageOnOSD(final String msg) {
+			if (msg != null) {
+				Log.i(LOG_TAG, "MessageOnOSD: " + msg + " " + getCurrentCharset());
+				runOnUiThread(new Runnable() {
+					public void run() {
+						Toast.makeText(ScummVMActivity.this, msg, Toast.LENGTH_SHORT).show();
+					}
+				});
+			}
 		}
 
 		@Override


Commit: 4e07f9acd3252201707094b8125b499ab1dea45f
    https://github.com/scummvm/scummvm/commit/4e07f9acd3252201707094b8125b499ab1dea45f
Author: Thanasis Antoniou (a.antoniou79 at gmail.com)
Date: 2020-01-13T14:28:31+02:00

Commit Message:
ANDROID: Disable support for split screen

Currently the ScummVM app will just exit in split-screen so that's not desireable

Disabled until we implement proper support, if deemed necessary

Changed paths:
    dists/android/AndroidManifest.xml
    dists/android/AndroidManifest.xml.in


diff --git a/dists/android/AndroidManifest.xml b/dists/android/AndroidManifest.xml
index 6abd3b8..9efb9aa 100644
--- a/dists/android/AndroidManifest.xml
+++ b/dists/android/AndroidManifest.xml
@@ -54,6 +54,7 @@
 			android:description="@string/app_desc"
 			android:allowBackup="true"
 			android:isGame="true"
+			android:resizeableActivity="false"
 			android:icon="@drawable/scummvm">
 		<activity android:name=".ScummVMActivity"
 				android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
diff --git a/dists/android/AndroidManifest.xml.in b/dists/android/AndroidManifest.xml.in
index 17757d0..3f8ccad 100644
--- a/dists/android/AndroidManifest.xml.in
+++ b/dists/android/AndroidManifest.xml.in
@@ -54,6 +54,7 @@
 			android:description="@string/app_desc"
 			android:allowBackup="true"
 			android:isGame="true"
+			android:resizeableActivity="false"
 			android:icon="@drawable/scummvm">
 		<activity android:name=".ScummVMActivity"
 				android:theme="@android:style/Theme.NoTitleBar.Fullscreen"


Commit: f444e7f07b07a8742369849441ada1a983eed091
    https://github.com/scummvm/scummvm/commit/f444e7f07b07a8742369849441ada1a983eed091
Author: Thanasis Antoniou (a.antoniou79 at gmail.com)
Date: 2020-01-13T14:28:41+02:00

Commit Message:
ANDROID: Override UTF-8 compliant definition of vsn_printf

This sets Android as a non-standard port in configure in order to override the definition for vsn_printf

The vsn_printf implementation is taken from https://github.com/weiss/c99-snprintf

Changed paths:
  A backends/platform/android/jni-android.cpp
  A backends/platform/android/jni-android.h
  A backends/platform/android/portdefs.h
  A backends/platform/android/snprintf.cpp
  R backends/platform/android/jni.cpp
  R backends/platform/android/jni.h
    backends/fs/posix/posix-fs.cpp
    backends/platform/android/android.cpp
    backends/platform/android/android.h
    backends/platform/android/asset-archive.cpp
    backends/platform/android/events.cpp
    backends/platform/android/graphics.cpp
    backends/platform/android/module.mk
    configure


diff --git a/backends/fs/posix/posix-fs.cpp b/backends/fs/posix/posix-fs.cpp
index fe72cfe..82ae597 100644
--- a/backends/fs/posix/posix-fs.cpp
+++ b/backends/fs/posix/posix-fs.cpp
@@ -58,7 +58,7 @@
 #endif
 
 #if defined(__ANDROID__) && !defined(ANDROIDSDL)
-#include "backends/platform/android/jni.h"
+#include "backends/platform/android/jni-android.h"
 #endif
 
 bool POSIXFilesystemNode::exists() const {
diff --git a/backends/platform/android/android.cpp b/backends/platform/android/android.cpp
index e16a6d4..78657d3 100644
--- a/backends/platform/android/android.cpp
+++ b/backends/platform/android/android.cpp
@@ -61,7 +61,7 @@
 #include "backends/saves/default/default-saves.h"
 #include "backends/timer/default/default-timer.h"
 
-#include "backends/platform/android/jni.h"
+#include "backends/platform/android/jni-android.h"
 #include "backends/platform/android/android.h"
 #include "backends/platform/android/graphics.h"
 
diff --git a/backends/platform/android/android.h b/backends/platform/android/android.h
index bd6f015..b5cb1d5 100644
--- a/backends/platform/android/android.h
+++ b/backends/platform/android/android.h
@@ -25,12 +25,14 @@
 
 #if defined(__ANDROID__)
 
+#include "backends/platform/android/portdefs.h"
 #include "common/fs.h"
 #include "common/archive.h"
 #include "audio/mixer_intern.h"
 #include "backends/modular-backend.h"
 #include "backends/plugins/posix/posix-provider.h"
 #include "backends/fs/posix/posix-fs-factory.h"
+#include "backends/fs/posix/posix-fs-factory.h"
 
 #include <pthread.h>
 
diff --git a/backends/platform/android/asset-archive.cpp b/backends/platform/android/asset-archive.cpp
index 0ee6efc..44e5ed8 100644
--- a/backends/platform/android/asset-archive.cpp
+++ b/backends/platform/android/asset-archive.cpp
@@ -32,7 +32,7 @@
 #include "common/debug.h"
 #include "common/textconsole.h"
 
-#include "backends/platform/android/jni.h"
+#include "backends/platform/android/jni-android.h"
 #include "backends/platform/android/asset-archive.h"
 
 #include <android/asset_manager.h>
diff --git a/backends/platform/android/events.cpp b/backends/platform/android/events.cpp
index da35fb0..2765b07 100644
--- a/backends/platform/android/events.cpp
+++ b/backends/platform/android/events.cpp
@@ -42,7 +42,7 @@
 #include "backends/platform/android/android.h"
 #include "backends/platform/android/graphics.h"
 #include "backends/platform/android/events.h"
-#include "backends/platform/android/jni.h"
+#include "backends/platform/android/jni-android.h"
 
 // floating point. use sparingly
 template<class T>
diff --git a/backends/platform/android/graphics.cpp b/backends/platform/android/graphics.cpp
index c359fcf..0412935 100644
--- a/backends/platform/android/graphics.cpp
+++ b/backends/platform/android/graphics.cpp
@@ -39,7 +39,7 @@
 
 #include "backends/platform/android/android.h"
 #include "backends/platform/android/graphics.h"
-#include "backends/platform/android/jni.h"
+#include "backends/platform/android/jni-android.h"
 
 //
 // AndroidGraphicsManager
diff --git a/backends/platform/android/jni-android.cpp b/backends/platform/android/jni-android.cpp
new file mode 100644
index 0000000..b4a4dac
--- /dev/null
+++ b/backends/platform/android/jni-android.cpp
@@ -0,0 +1,753 @@
+/* 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.
+ *
+ */
+
+#if defined(__ANDROID__)
+
+// Allow use of stuff in <time.h> and abort()
+#define FORBIDDEN_SYMBOL_EXCEPTION_time_h
+#define FORBIDDEN_SYMBOL_EXCEPTION_abort
+
+// Disable printf override in common/forbidden.h to avoid
+// clashes with log.h from the Android SDK.
+// That header file uses
+//   __attribute__ ((format(printf, 3, 4)))
+// which gets messed up by our override mechanism; this could
+// be avoided by either changing the Android SDK to use the equally
+// legal and valid
+//   __attribute__ ((format(printf, 3, 4)))
+// or by refining our printf override to use a varadic macro
+// (which then wouldn't be portable, though).
+// Anyway, for now we just disable the printf override globally
+// for the Android port
+#define FORBIDDEN_SYMBOL_EXCEPTION_printf
+
+#include "base/main.h"
+#include "base/version.h"
+#include "common/config-manager.h"
+#include "common/error.h"
+#include "common/textconsole.h"
+#include "common/translation.h"
+#include "common/encoding.h"
+#include "engines/engine.h"
+
+#include "backends/platform/android/android.h"
+#include "backends/platform/android/asset-archive.h"
+#include "backends/platform/android/jni-android.h"
+
+__attribute__ ((visibility("default")))
+jint JNICALL JNI_OnLoad(JavaVM *vm, void *) {
+	return JNI::onLoad(vm);
+}
+
+JavaVM *JNI::_vm = 0;
+jobject JNI::_jobj = 0;
+jobject JNI::_jobj_audio_track = 0;
+jobject JNI::_jobj_egl = 0;
+jobject JNI::_jobj_egl_display = 0;
+jobject JNI::_jobj_egl_surface = 0;
+
+Common::Archive *JNI::_asset_archive = 0;
+OSystem_Android *JNI::_system = 0;
+
+bool JNI::pause = false;
+sem_t JNI::pause_sem = { 0 };
+
+int JNI::surface_changeid = 0;
+int JNI::egl_surface_width = 0;
+int JNI::egl_surface_height = 0;
+bool JNI::_ready_for_events = 0;
+
+jmethodID JNI::_MID_getDPI = 0;
+jmethodID JNI::_MID_displayMessageOnOSD = 0;
+jmethodID JNI::_MID_openUrl = 0;
+jmethodID JNI::_MID_hasTextInClipboard = 0;
+jmethodID JNI::_MID_getTextFromClipboard = 0;
+jmethodID JNI::_MID_setTextInClipboard = 0;
+jmethodID JNI::_MID_isConnectionLimited = 0;
+jmethodID JNI::_MID_setWindowCaption = 0;
+jmethodID JNI::_MID_showVirtualKeyboard = 0;
+jmethodID JNI::_MID_showKeyboardControl = 0;
+jmethodID JNI::_MID_getSysArchives = 0;
+jmethodID JNI::_MID_getAllStorageLocations = 0;
+jmethodID JNI::_MID_initSurface = 0;
+jmethodID JNI::_MID_deinitSurface = 0;
+
+jmethodID JNI::_MID_EGL10_eglSwapBuffers = 0;
+
+jmethodID JNI::_MID_AudioTrack_flush = 0;
+jmethodID JNI::_MID_AudioTrack_pause = 0;
+jmethodID JNI::_MID_AudioTrack_play = 0;
+jmethodID JNI::_MID_AudioTrack_stop = 0;
+jmethodID JNI::_MID_AudioTrack_write = 0;
+
+const JNINativeMethod JNI::_natives[] = {
+	{ "create", "(Landroid/content/res/AssetManager;"
+				"Ljavax/microedition/khronos/egl/EGL10;"
+				"Ljavax/microedition/khronos/egl/EGLDisplay;"
+				"Landroid/media/AudioTrack;II)V",
+		(void *)JNI::create },
+	{ "destroy", "()V",
+		(void *)JNI::destroy },
+	{ "setSurface", "(II)V",
+		(void *)JNI::setSurface },
+	{ "main", "([Ljava/lang/String;)I",
+		(void *)JNI::main },
+	{ "pushEvent", "(IIIIIII)V",
+		(void *)JNI::pushEvent },
+	{ "setPause", "(Z)V",
+		(void *)JNI::setPause },
+	{ "getCurrentCharset", "()Ljava/lang/String;",
+		(void *)JNI::getCurrentCharset }
+};
+
+JNI::JNI() {
+}
+
+JNI::~JNI() {
+}
+
+jint JNI::onLoad(JavaVM *vm) {
+	_vm = vm;
+
+	JNIEnv *env;
+
+	if (_vm->GetEnv((void **)&env, JNI_VERSION_1_2))
+		return JNI_ERR;
+
+	jclass cls = env->FindClass("org/scummvm/scummvm/ScummVM");
+	if (cls == 0)
+		return JNI_ERR;
+
+	if (env->RegisterNatives(cls, _natives, ARRAYSIZE(_natives)) < 0)
+		return JNI_ERR;
+
+	return JNI_VERSION_1_2;
+}
+
+JNIEnv *JNI::getEnv() {
+	JNIEnv *env = 0;
+
+	jint res = _vm->GetEnv((void **)&env, JNI_VERSION_1_2);
+
+	if (res != JNI_OK) {
+		LOGE("GetEnv() failed: %d", res);
+		abort();
+	}
+
+	return env;
+}
+
+void JNI::attachThread() {
+	JNIEnv *env = 0;
+
+	jint res = _vm->AttachCurrentThread(&env, 0);
+
+	if (res != JNI_OK) {
+		LOGE("AttachCurrentThread() failed: %d", res);
+		abort();
+	}
+}
+
+void JNI::detachThread() {
+	jint res = _vm->DetachCurrentThread();
+
+	if (res != JNI_OK) {
+		LOGE("DetachCurrentThread() failed: %d", res);
+		abort();
+	}
+}
+
+void JNI::setReadyForEvents(bool ready) {
+	_ready_for_events = ready;
+}
+
+void JNI::throwByName(JNIEnv *env, const char *name, const char *msg) {
+	jclass cls = env->FindClass(name);
+
+	// if cls is 0, an exception has already been thrown
+	if (cls != 0)
+		env->ThrowNew(cls, msg);
+
+	env->DeleteLocalRef(cls);
+}
+
+void JNI::throwRuntimeException(JNIEnv *env, const char *msg) {
+	throwByName(env, "java/lang/RuntimeException", msg);
+}
+
+// calls to the dark side
+
+void JNI::getDPI(float *values) {
+	values[0] = 0.0;
+	values[1] = 0.0;
+
+	JNIEnv *env = JNI::getEnv();
+
+	jfloatArray array = env->NewFloatArray(2);
+
+	env->CallVoidMethod(_jobj, _MID_getDPI, array);
+
+	if (env->ExceptionCheck()) {
+		LOGE("Failed to get DPIs");
+
+		env->ExceptionDescribe();
+		env->ExceptionClear();
+	} else {
+		jfloat *res = env->GetFloatArrayElements(array, 0);
+
+		if (res) {
+			values[0] = res[0];
+			values[1] = res[1];
+
+			env->ReleaseFloatArrayElements(array, res, 0);
+		}
+	}
+
+	env->DeleteLocalRef(array);
+}
+
+void JNI::displayMessageOnOSD(const char *msg) {
+	// called from common/osd_message_queue, method: OSDMessageQueue::pollEvent()
+	JNIEnv *env = JNI::getEnv();
+	Common::String fromEncoding = "ISO-8859-1";
+#ifdef USE_TRANSLATION
+	if (TransMan.getCurrentCharset() != "ASCII") {
+		fromEncoding = TransMan.getCurrentCharset();
+	}
+#endif
+	Common::Encoding converter("UTF-8", fromEncoding.c_str());
+
+	const char *utf8Msg = converter.convert(msg, converter.stringLength(msg, fromEncoding) );
+	if (utf8Msg == nullptr) {
+		// Show a placeholder indicative of the translation error instead of silent failing
+		utf8Msg = "?";
+		LOGE("Failed to convert message to UTF-8 for OSD!");
+	}
+	jstring java_msg = env->NewStringUTF(utf8Msg);
+
+	env->CallVoidMethod(_jobj, _MID_displayMessageOnOSD, java_msg);
+
+	if (env->ExceptionCheck()) {
+		LOGE("Failed to display OSD message");
+
+		env->ExceptionDescribe();
+		env->ExceptionClear();
+	}
+
+	env->DeleteLocalRef(java_msg);
+}
+
+bool JNI::openUrl(const char *url) {
+	bool success = true;
+	JNIEnv *env = JNI::getEnv();
+	jstring javaUrl = env->NewStringUTF(url);
+
+	env->CallVoidMethod(_jobj, _MID_openUrl, javaUrl);
+
+	if (env->ExceptionCheck()) {
+		LOGE("Failed to open URL");
+
+		env->ExceptionDescribe();
+		env->ExceptionClear();
+		success = false;
+	}
+
+	env->DeleteLocalRef(javaUrl);
+	return success;
+}
+
+bool JNI::hasTextInClipboard() {
+	JNIEnv *env = JNI::getEnv();
+	bool hasText = env->CallBooleanMethod(_jobj, _MID_hasTextInClipboard);
+
+	if (env->ExceptionCheck()) {
+		LOGE("Failed to check the contents of the clipboard");
+
+		env->ExceptionDescribe();
+		env->ExceptionClear();
+		hasText = true;
+	}
+
+	return hasText;
+}
+
+Common::String JNI::getTextFromClipboard() {
+	JNIEnv *env = JNI::getEnv();
+
+	jbyteArray javaText = (jbyteArray)env->CallObjectMethod(_jobj, _MID_getTextFromClipboard);
+
+	if (env->ExceptionCheck()) {
+		LOGE("Failed to retrieve text from the clipboard");
+
+		env->ExceptionDescribe();
+		env->ExceptionClear();
+
+		return Common::String();
+	}
+
+	int len = env->GetArrayLength(javaText);
+	char* buf = new char[len];
+	env->GetByteArrayRegion(javaText, 0, len, reinterpret_cast<jbyte*>(buf));
+	Common::String text(buf, len);
+	delete[] buf;
+
+	return text;
+}
+
+bool JNI::setTextInClipboard(const Common::String &text) {
+	JNIEnv *env = JNI::getEnv();
+
+	jbyteArray javaText = env->NewByteArray(text.size());
+	env->SetByteArrayRegion(javaText, 0, text.size(), reinterpret_cast<const jbyte*>(text.c_str()));
+
+	bool success = env->CallBooleanMethod(_jobj, _MID_setTextInClipboard, javaText);
+
+	if (env->ExceptionCheck()) {
+		LOGE("Failed to add text to the clipboard");
+
+		env->ExceptionDescribe();
+		env->ExceptionClear();
+		success = false;
+	}
+
+	env->DeleteLocalRef(javaText);
+	return success;
+}
+
+bool JNI::isConnectionLimited() {
+	JNIEnv *env = JNI::getEnv();
+	bool limited = env->CallBooleanMethod(_jobj, _MID_isConnectionLimited);
+
+	if (env->ExceptionCheck()) {
+		LOGE("Failed to check whether connection's limited");
+
+		env->ExceptionDescribe();
+		env->ExceptionClear();
+		limited = true;
+	}
+
+	return limited;
+}
+
+void JNI::setWindowCaption(const char *caption) {
+	JNIEnv *env = JNI::getEnv();
+	jstring java_caption = env->NewStringUTF(caption);
+
+	env->CallVoidMethod(_jobj, _MID_setWindowCaption, java_caption);
+
+	if (env->ExceptionCheck()) {
+		LOGE("Failed to set window caption");
+
+		env->ExceptionDescribe();
+		env->ExceptionClear();
+	}
+
+	env->DeleteLocalRef(java_caption);
+}
+
+void JNI::showVirtualKeyboard(bool enable) {
+	JNIEnv *env = JNI::getEnv();
+
+	env->CallVoidMethod(_jobj, _MID_showVirtualKeyboard, enable);
+
+	if (env->ExceptionCheck()) {
+		LOGE("Error trying to show virtual keyboard");
+
+		env->ExceptionDescribe();
+		env->ExceptionClear();
+	}
+}
+
+void JNI::showKeyboardControl(bool enable) {
+	JNIEnv *env = JNI::getEnv();
+
+	env->CallVoidMethod(_jobj, _MID_showKeyboardControl, enable);
+
+	if (env->ExceptionCheck()) {
+		LOGE("Error trying to show virtual keyboard control");
+
+		env->ExceptionDescribe();
+		env->ExceptionClear();
+	}
+}
+
+void JNI::addSysArchivesToSearchSet(Common::SearchSet &s, int priority) {
+	JNIEnv *env = JNI::getEnv();
+
+	s.add("ASSET", _asset_archive, priority, false);
+
+	jobjectArray array =
+		(jobjectArray)env->CallObjectMethod(_jobj, _MID_getSysArchives);
+
+	if (env->ExceptionCheck()) {
+		LOGE("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, 0);
+
+		if (path != 0) {
+			s.addDirectory(path, path, priority);
+			env->ReleaseStringUTFChars(path_obj, path);
+		}
+
+		env->DeleteLocalRef(path_obj);
+	}
+}
+
+bool JNI::initSurface() {
+	JNIEnv *env = JNI::getEnv();
+
+	jobject obj = env->CallObjectMethod(_jobj, _MID_initSurface);
+
+	if (!obj || env->ExceptionCheck()) {
+		LOGE("initSurface failed");
+
+		env->ExceptionDescribe();
+		env->ExceptionClear();
+
+		return false;
+	}
+
+	_jobj_egl_surface = env->NewGlobalRef(obj);
+
+	return true;
+}
+
+void JNI::deinitSurface() {
+	JNIEnv *env = JNI::getEnv();
+
+	env->CallVoidMethod(_jobj, _MID_deinitSurface);
+
+	if (env->ExceptionCheck()) {
+		LOGE("deinitSurface failed");
+
+		env->ExceptionDescribe();
+		env->ExceptionClear();
+	}
+
+	env->DeleteGlobalRef(_jobj_egl_surface);
+	_jobj_egl_surface = 0;
+}
+
+void JNI::setAudioPause() {
+	JNIEnv *env = JNI::getEnv();
+
+	env->CallVoidMethod(_jobj_audio_track, _MID_AudioTrack_flush);
+
+	if (env->ExceptionCheck()) {
+		LOGE("Error flushing AudioTrack");
+
+		env->ExceptionDescribe();
+		env->ExceptionClear();
+	}
+
+	env->CallVoidMethod(_jobj_audio_track, _MID_AudioTrack_pause);
+
+	if (env->ExceptionCheck()) {
+		LOGE("Error setting AudioTrack: pause");
+
+		env->ExceptionDescribe();
+		env->ExceptionClear();
+	}
+}
+
+void JNI::setAudioPlay() {
+	JNIEnv *env = JNI::getEnv();
+
+	env->CallVoidMethod(_jobj_audio_track, _MID_AudioTrack_play);
+
+	if (env->ExceptionCheck()) {
+		LOGE("Error setting AudioTrack: play");
+
+		env->ExceptionDescribe();
+		env->ExceptionClear();
+	}
+}
+
+void JNI::setAudioStop() {
+	JNIEnv *env = JNI::getEnv();
+
+	env->CallVoidMethod(_jobj_audio_track, _MID_AudioTrack_stop);
+
+	if (env->ExceptionCheck()) {
+		LOGE("Error setting AudioTrack: stop");
+
+		env->ExceptionDescribe();
+		env->ExceptionClear();
+	}
+}
+
+// natives for the dark side
+
+void JNI::create(JNIEnv *env, jobject self, jobject asset_manager,
+				jobject egl, jobject egl_display,
+				jobject at, jint audio_sample_rate, jint audio_buffer_size) {
+	LOGI("%s", gScummVMFullVersion);
+
+	assert(!_system);
+
+	pause = false;
+	// initial value of zero!
+	sem_init(&pause_sem, 0, 0);
+
+	_asset_archive = new AndroidAssetArchive(asset_manager);
+	assert(_asset_archive);
+
+	_system = new OSystem_Android(audio_sample_rate, audio_buffer_size);
+	assert(_system);
+
+	// weak global ref to allow class to be unloaded
+	// ... except dalvik implements NewWeakGlobalRef only on froyo
+	//_jobj = env->NewWeakGlobalRef(self);
+
+	_jobj = env->NewGlobalRef(self);
+
+	jclass cls = env->GetObjectClass(_jobj);
+
+#define FIND_METHOD(prefix, name, signature) do {							\
+		_MID_ ## prefix ## name = env->GetMethodID(cls, #name, signature);	\
+		if (_MID_ ## prefix ## name == 0)									\
+			return;															\
+	} while (0)
+
+	FIND_METHOD(, setWindowCaption, "(Ljava/lang/String;)V");
+	FIND_METHOD(, getDPI, "([F)V");
+	FIND_METHOD(, displayMessageOnOSD, "(Ljava/lang/String;)V");
+	FIND_METHOD(, openUrl, "(Ljava/lang/String;)V");
+	FIND_METHOD(, hasTextInClipboard, "()Z");
+	FIND_METHOD(, getTextFromClipboard, "()[B");
+	FIND_METHOD(, setTextInClipboard, "([B)Z");
+	FIND_METHOD(, isConnectionLimited, "()Z");
+	FIND_METHOD(, showVirtualKeyboard, "(Z)V");
+	FIND_METHOD(, showKeyboardControl, "(Z)V");
+	FIND_METHOD(, getSysArchives, "()[Ljava/lang/String;");
+	FIND_METHOD(, getAllStorageLocations, "()[Ljava/lang/String;");
+	FIND_METHOD(, initSurface, "()Ljavax/microedition/khronos/egl/EGLSurface;");
+	FIND_METHOD(, deinitSurface, "()V");
+
+	_jobj_egl = env->NewGlobalRef(egl);
+	_jobj_egl_display = env->NewGlobalRef(egl_display);
+
+	cls = env->GetObjectClass(_jobj_egl);
+
+	FIND_METHOD(EGL10_, eglSwapBuffers,
+				"(Ljavax/microedition/khronos/egl/EGLDisplay;"
+				"Ljavax/microedition/khronos/egl/EGLSurface;)Z");
+
+	_jobj_audio_track = env->NewGlobalRef(at);
+
+	cls = env->GetObjectClass(_jobj_audio_track);
+
+	FIND_METHOD(AudioTrack_, flush, "()V");
+	FIND_METHOD(AudioTrack_, pause, "()V");
+	FIND_METHOD(AudioTrack_, play, "()V");
+	FIND_METHOD(AudioTrack_, stop, "()V");
+	FIND_METHOD(AudioTrack_, write, "([BII)I");
+
+#undef FIND_METHOD
+
+	g_system = _system;
+}
+
+void JNI::destroy(JNIEnv *env, jobject self) {
+	delete _asset_archive;
+	_asset_archive = 0;
+
+	// _system is a pointer of OSystem_Android <--- ModularBackend <--- BaseBacked <--- Common::OSystem
+	// It's better to call destroy() rather than just delete here
+	// to avoid mutex issues if a Common::String is used after this point
+	_system->destroy();
+
+	g_system = 0;
+	_system  = 0;
+
+	sem_destroy(&pause_sem);
+
+	// see above
+	//JNI::getEnv()->DeleteWeakGlobalRef(_jobj);
+
+	JNI::getEnv()->DeleteGlobalRef(_jobj_egl_display);
+	JNI::getEnv()->DeleteGlobalRef(_jobj_egl);
+	JNI::getEnv()->DeleteGlobalRef(_jobj_audio_track);
+	JNI::getEnv()->DeleteGlobalRef(_jobj);
+}
+
+void JNI::setSurface(JNIEnv *env, jobject self, jint width, jint height) {
+	egl_surface_width = width;
+	egl_surface_height = height;
+	surface_changeid++;
+}
+
+jint JNI::main(JNIEnv *env, jobject self, jobjectArray args) {
+	assert(_system);
+
+	const int MAX_NARGS = 32;
+	int res = -1;
+
+	int argc = env->GetArrayLength(args);
+	if (argc > MAX_NARGS) {
+		throwByName(env, "java/lang/IllegalArgumentException",
+					"too many arguments");
+		return 0;
+	}
+
+	char *argv[MAX_NARGS];
+
+	// note use in cleanup loop below
+	int nargs;
+
+	for (nargs = 0; nargs < argc; ++nargs) {
+		jstring arg = (jstring)env->GetObjectArrayElement(args, nargs);
+
+		if (arg == 0) {
+			argv[nargs] = 0;
+		} else {
+			const char *cstr = env->GetStringUTFChars(arg, 0);
+
+			argv[nargs] = const_cast<char *>(cstr);
+
+			// exception already thrown?
+			if (cstr == 0)
+				goto cleanup;
+		}
+
+		env->DeleteLocalRef(arg);
+	}
+
+	LOGI("Entering scummvm_main with %d args", argc);
+
+	res = scummvm_main(argc, argv);
+
+	LOGI("scummvm_main exited with code %d", res);
+
+	_system->quit();
+
+cleanup:
+	nargs--;
+
+	for (int i = 0; i < nargs; ++i) {
+		if (argv[i] == 0)
+			continue;
+
+		jstring arg = (jstring)env->GetObjectArrayElement(args, nargs);
+
+		// Exception already thrown?
+		if (arg == 0)
+			return res;
+
+		env->ReleaseStringUTFChars(arg, argv[i]);
+		env->DeleteLocalRef(arg);
+	}
+
+	return res;
+}
+
+void JNI::pushEvent(JNIEnv *env, jobject self, int type, int arg1, int arg2,
+					int arg3, int arg4, int arg5, int arg6) {
+	// drop events until we're ready and after we quit
+	if (!_ready_for_events) {
+		LOGW("dropping event");
+		return;
+	}
+
+	assert(_system);
+
+	_system->pushEvent(type, arg1, arg2, arg3, arg4, arg5, arg6);
+}
+
+void JNI::setPause(JNIEnv *env, jobject self, jboolean value) {
+	if (!_system)
+		return;
+
+	if (g_engine) {
+		LOGD("pauseEngine: %d", value);
+
+		g_engine->pauseEngine(value);
+
+		/*if (value &&
+				g_engine->hasFeature(Engine::kSupportsSavingDuringRuntime) &&
+				g_engine->canSaveGameStateCurrently())
+			g_engine->saveGameState(0, "Android parachute");*/
+	}
+
+	pause = value;
+
+	if (!pause) {
+		// wake up all threads
+		for (uint i = 0; i < 3; ++i)
+			sem_post(&pause_sem);
+	}
+}
+
+jstring JNI::getCurrentCharset(JNIEnv *env, jobject self) {
+#ifdef USE_TRANSLATION
+	if (TransMan.getCurrentCharset() != "ASCII") {
+//		LOGD("getCurrentCharset: %s", TransMan.getCurrentCharset().c_str());
+		return env->NewStringUTF(TransMan.getCurrentCharset().c_str());
+	}
+#endif
+	return env->NewStringUTF("ISO-8859-1");
+}
+
+Common::Array<Common::String> JNI::getAllStorageLocations() {
+	Common::Array<Common::String> *res = new Common::Array<Common::String>();
+
+	JNIEnv *env = JNI::getEnv();
+
+	jobjectArray array =
+		(jobjectArray)env->CallObjectMethod(_jobj, _MID_getAllStorageLocations);
+
+	if (env->ExceptionCheck()) {
+		LOGE("Error finding system archive path");
+
+		env->ExceptionDescribe();
+		env->ExceptionClear();
+
+		return *res;
+	}
+
+	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, 0);
+
+		if (path != 0) {
+			res->push_back(path);
+			env->ReleaseStringUTFChars(path_obj, path);
+		}
+
+		env->DeleteLocalRef(path_obj);
+	}
+
+	return *res;
+}
+
+
+#endif
diff --git a/backends/platform/android/jni-android.h b/backends/platform/android/jni-android.h
new file mode 100644
index 0000000..fe4b948
--- /dev/null
+++ b/backends/platform/android/jni-android.h
@@ -0,0 +1,161 @@
+/* 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.
+ *
+ */
+
+#ifndef _ANDROID_JNI_H_
+#define _ANDROID_JNI_H_
+
+#if defined(__ANDROID__)
+
+#include <jni.h>
+#include <semaphore.h>
+
+#include "common/fs.h"
+#include "common/archive.h"
+#include "common/array.h"
+
+class OSystem_Android;
+
+class JNI {
+private:
+	JNI();
+	virtual ~JNI();
+
+public:
+	static bool pause;
+	static sem_t pause_sem;
+
+	static int surface_changeid;
+	static int egl_surface_width;
+	static int egl_surface_height;
+
+	static jint onLoad(JavaVM *vm);
+
+	static JNIEnv *getEnv();
+
+	static void attachThread();
+	static void detachThread();
+
+	static void setReadyForEvents(bool ready);
+
+	static void setWindowCaption(const char *caption);
+	static void getDPI(float *values);
+	static void displayMessageOnOSD(const char *msg);
+	static bool openUrl(const char *url);
+	static bool hasTextInClipboard();
+	static Common::String getTextFromClipboard();
+	static bool setTextInClipboard(const Common::String &text);
+	static bool isConnectionLimited();
+	static void showVirtualKeyboard(bool enable);
+	static void showKeyboardControl(bool enable);
+	static void addSysArchivesToSearchSet(Common::SearchSet &s, int priority);
+
+	static inline bool haveSurface();
+	static inline bool swapBuffers();
+	static bool initSurface();
+	static void deinitSurface();
+
+	static void setAudioPause();
+	static void setAudioPlay();
+	static void setAudioStop();
+
+	static inline int writeAudio(JNIEnv *env, jbyteArray &data, int offset,
+									int size);
+
+	static Common::Array<Common::String> getAllStorageLocations();
+
+private:
+	static JavaVM *_vm;
+	// back pointer to (java) peer instance
+	static jobject _jobj;
+	static jobject _jobj_audio_track;
+	static jobject _jobj_egl;
+	static jobject _jobj_egl_display;
+	static jobject _jobj_egl_surface;
+
+	static Common::Archive *_asset_archive;
+	static OSystem_Android *_system;
+
+	static bool _ready_for_events;
+
+	static jmethodID _MID_getDPI;
+	static jmethodID _MID_displayMessageOnOSD;
+	static jmethodID _MID_openUrl;
+	static jmethodID _MID_hasTextInClipboard;
+	static jmethodID _MID_getTextFromClipboard;
+	static jmethodID _MID_setTextInClipboard;
+	static jmethodID _MID_isConnectionLimited;
+	static jmethodID _MID_setWindowCaption;
+	static jmethodID _MID_showVirtualKeyboard;
+	static jmethodID _MID_showKeyboardControl;
+	static jmethodID _MID_getSysArchives;
+	static jmethodID _MID_getAllStorageLocations;
+	static jmethodID _MID_initSurface;
+	static jmethodID _MID_deinitSurface;
+
+	static jmethodID _MID_EGL10_eglSwapBuffers;
+
+	static jmethodID _MID_AudioTrack_flush;
+	static jmethodID _MID_AudioTrack_pause;
+	static jmethodID _MID_AudioTrack_play;
+	static jmethodID _MID_AudioTrack_stop;
+	static jmethodID _MID_AudioTrack_write;
+
+	static const JNINativeMethod _natives[];
+
+	static void throwByName(JNIEnv *env, const char *name, const char *msg);
+	static void throwRuntimeException(JNIEnv *env, const char *msg);
+
+	// natives for the dark side
+	static void create(JNIEnv *env, jobject self, jobject asset_manager,
+						jobject egl, jobject egl_display,
+						jobject at, jint audio_sample_rate,
+						jint audio_buffer_size);
+	static void destroy(JNIEnv *env, jobject self);
+
+	static void setSurface(JNIEnv *env, jobject self, jint width, jint height);
+	static jint main(JNIEnv *env, jobject self, jobjectArray args);
+
+	static void pushEvent(JNIEnv *env, jobject self, int type, int arg1,
+							int arg2, int arg3, int arg4, int arg5, int arg6);
+	static void setPause(JNIEnv *env, jobject self, jboolean value);
+
+	static jstring getCurrentCharset(JNIEnv *env, jobject self);
+};
+
+inline bool JNI::haveSurface() {
+	return _jobj_egl_surface != 0;
+}
+
+inline bool JNI::swapBuffers() {
+	JNIEnv *env = JNI::getEnv();
+
+	return env->CallBooleanMethod(_jobj_egl, _MID_EGL10_eglSwapBuffers,
+									_jobj_egl_display, _jobj_egl_surface);
+}
+
+inline int JNI::writeAudio(JNIEnv *env, jbyteArray &data, int offset, int size) {
+	return env->CallIntMethod(_jobj_audio_track, _MID_AudioTrack_write, data,
+								offset, size);
+}
+
+#endif
+#endif
diff --git a/backends/platform/android/jni.cpp b/backends/platform/android/jni.cpp
deleted file mode 100644
index cedee38..0000000
--- a/backends/platform/android/jni.cpp
+++ /dev/null
@@ -1,754 +0,0 @@
-/* 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.
- *
- */
-
-#if defined(__ANDROID__)
-
-// Allow use of stuff in <time.h> and abort()
-#define FORBIDDEN_SYMBOL_EXCEPTION_time_h
-#define FORBIDDEN_SYMBOL_EXCEPTION_abort
-
-// Disable printf override in common/forbidden.h to avoid
-// clashes with log.h from the Android SDK.
-// That header file uses
-//   __attribute__ ((format(printf, 3, 4)))
-// which gets messed up by our override mechanism; this could
-// be avoided by either changing the Android SDK to use the equally
-// legal and valid
-//   __attribute__ ((format(printf, 3, 4)))
-// or by refining our printf override to use a varadic macro
-// (which then wouldn't be portable, though).
-// Anyway, for now we just disable the printf override globally
-// for the Android port
-#define FORBIDDEN_SYMBOL_EXCEPTION_printf
-
-#include "base/main.h"
-#include "base/version.h"
-#include "common/config-manager.h"
-#include "common/error.h"
-#include "common/textconsole.h"
-#include "common/translation.h"
-#include "common/encoding.h"
-#include "engines/engine.h"
-
-#include "backends/platform/android/android.h"
-#include "backends/platform/android/asset-archive.h"
-#include "backends/platform/android/jni.h"
-
-__attribute__ ((visibility("default")))
-jint JNICALL JNI_OnLoad(JavaVM *vm, void *) {
-	return JNI::onLoad(vm);
-}
-
-JavaVM *JNI::_vm = 0;
-jobject JNI::_jobj = 0;
-jobject JNI::_jobj_audio_track = 0;
-jobject JNI::_jobj_egl = 0;
-jobject JNI::_jobj_egl_display = 0;
-jobject JNI::_jobj_egl_surface = 0;
-
-Common::Archive *JNI::_asset_archive = 0;
-OSystem_Android *JNI::_system = 0;
-
-bool JNI::pause = false;
-sem_t JNI::pause_sem = { 0 };
-
-int JNI::surface_changeid = 0;
-int JNI::egl_surface_width = 0;
-int JNI::egl_surface_height = 0;
-bool JNI::_ready_for_events = 0;
-
-jmethodID JNI::_MID_getDPI = 0;
-jmethodID JNI::_MID_displayMessageOnOSD = 0;
-jmethodID JNI::_MID_openUrl = 0;
-jmethodID JNI::_MID_hasTextInClipboard = 0;
-jmethodID JNI::_MID_getTextFromClipboard = 0;
-jmethodID JNI::_MID_setTextInClipboard = 0;
-jmethodID JNI::_MID_isConnectionLimited = 0;
-jmethodID JNI::_MID_setWindowCaption = 0;
-jmethodID JNI::_MID_showVirtualKeyboard = 0;
-jmethodID JNI::_MID_showKeyboardControl = 0;
-jmethodID JNI::_MID_getSysArchives = 0;
-jmethodID JNI::_MID_getAllStorageLocations = 0;
-jmethodID JNI::_MID_initSurface = 0;
-jmethodID JNI::_MID_deinitSurface = 0;
-
-jmethodID JNI::_MID_EGL10_eglSwapBuffers = 0;
-
-jmethodID JNI::_MID_AudioTrack_flush = 0;
-jmethodID JNI::_MID_AudioTrack_pause = 0;
-jmethodID JNI::_MID_AudioTrack_play = 0;
-jmethodID JNI::_MID_AudioTrack_stop = 0;
-jmethodID JNI::_MID_AudioTrack_write = 0;
-
-const JNINativeMethod JNI::_natives[] = {
-	{ "create", "(Landroid/content/res/AssetManager;"
-				"Ljavax/microedition/khronos/egl/EGL10;"
-				"Ljavax/microedition/khronos/egl/EGLDisplay;"
-				"Landroid/media/AudioTrack;II)V",
-		(void *)JNI::create },
-	{ "destroy", "()V",
-		(void *)JNI::destroy },
-	{ "setSurface", "(II)V",
-		(void *)JNI::setSurface },
-	{ "main", "([Ljava/lang/String;)I",
-		(void *)JNI::main },
-	{ "pushEvent", "(IIIIIII)V",
-		(void *)JNI::pushEvent },
-	{ "setPause", "(Z)V",
-		(void *)JNI::setPause },
-	{ "getCurrentCharset", "()Ljava/lang/String;",
-		(void *)JNI::getCurrentCharset }
-};
-
-JNI::JNI() {
-}
-
-JNI::~JNI() {
-}
-
-jint JNI::onLoad(JavaVM *vm) {
-	_vm = vm;
-
-	JNIEnv *env;
-
-	if (_vm->GetEnv((void **)&env, JNI_VERSION_1_2))
-		return JNI_ERR;
-
-	jclass cls = env->FindClass("org/scummvm/scummvm/ScummVM");
-	if (cls == 0)
-		return JNI_ERR;
-
-	if (env->RegisterNatives(cls, _natives, ARRAYSIZE(_natives)) < 0)
-		return JNI_ERR;
-
-	return JNI_VERSION_1_2;
-}
-
-JNIEnv *JNI::getEnv() {
-	JNIEnv *env = 0;
-
-	jint res = _vm->GetEnv((void **)&env, JNI_VERSION_1_2);
-
-	if (res != JNI_OK) {
-		LOGE("GetEnv() failed: %d", res);
-		abort();
-	}
-
-	return env;
-}
-
-void JNI::attachThread() {
-	JNIEnv *env = 0;
-
-	jint res = _vm->AttachCurrentThread(&env, 0);
-
-	if (res != JNI_OK) {
-		LOGE("AttachCurrentThread() failed: %d", res);
-		abort();
-	}
-}
-
-void JNI::detachThread() {
-	jint res = _vm->DetachCurrentThread();
-
-	if (res != JNI_OK) {
-		LOGE("DetachCurrentThread() failed: %d", res);
-		abort();
-	}
-}
-
-void JNI::setReadyForEvents(bool ready) {
-	_ready_for_events = ready;
-}
-
-void JNI::throwByName(JNIEnv *env, const char *name, const char *msg) {
-	jclass cls = env->FindClass(name);
-
-	// if cls is 0, an exception has already been thrown
-	if (cls != 0)
-		env->ThrowNew(cls, msg);
-
-	env->DeleteLocalRef(cls);
-}
-
-void JNI::throwRuntimeException(JNIEnv *env, const char *msg) {
-	throwByName(env, "java/lang/RuntimeException", msg);
-}
-
-// calls to the dark side
-
-void JNI::getDPI(float *values) {
-	values[0] = 0.0;
-	values[1] = 0.0;
-
-	JNIEnv *env = JNI::getEnv();
-
-	jfloatArray array = env->NewFloatArray(2);
-
-	env->CallVoidMethod(_jobj, _MID_getDPI, array);
-
-	if (env->ExceptionCheck()) {
-		LOGE("Failed to get DPIs");
-
-		env->ExceptionDescribe();
-		env->ExceptionClear();
-	} else {
-		jfloat *res = env->GetFloatArrayElements(array, 0);
-
-		if (res) {
-			values[0] = res[0];
-			values[1] = res[1];
-
-			env->ReleaseFloatArrayElements(array, res, 0);
-		}
-	}
-
-	env->DeleteLocalRef(array);
-}
-
-void JNI::displayMessageOnOSD(const char *msg) {
-	// called from common/osd_message_queue, method: OSDMessageQueue::pollEvent()
-	JNIEnv *env = JNI::getEnv();
-//	LOGD("OSD orig MESSAGE: %s", msg);
-	Common::String fromEncoding = "ISO-8859-1";
-#ifdef USE_TRANSLATION
-	if (TransMan.getCurrentCharset() != "ASCII") {
-		fromEncoding = TransMan.getCurrentCharset();
-	}
-#endif
-	Common::Encoding converter("UTF-8", fromEncoding.c_str());
-
-	const char *utf8Msg = converter.convert(msg, converter.stringLength(msg, fromEncoding) );
-	if (utf8Msg == nullptr) {
-		LOGE("Failed to convert message to UTF-8 for OSD!");
-		return;
-	}
-//	LOGD("OSD target MESSAGE: %s", utf8Msg);
-	jstring java_msg = env->NewStringUTF(utf8Msg);
-
-	env->CallVoidMethod(_jobj, _MID_displayMessageOnOSD, java_msg);
-
-	if (env->ExceptionCheck()) {
-		LOGE("Failed to display OSD message");
-
-		env->ExceptionDescribe();
-		env->ExceptionClear();
-	}
-
-	env->DeleteLocalRef(java_msg);
-}
-
-bool JNI::openUrl(const char *url) {
-	bool success = true;
-	JNIEnv *env = JNI::getEnv();
-	jstring javaUrl = env->NewStringUTF(url);
-
-	env->CallVoidMethod(_jobj, _MID_openUrl, javaUrl);
-
-	if (env->ExceptionCheck()) {
-		LOGE("Failed to open URL");
-
-		env->ExceptionDescribe();
-		env->ExceptionClear();
-		success = false;
-	}
-
-	env->DeleteLocalRef(javaUrl);
-	return success;
-}
-
-bool JNI::hasTextInClipboard() {
-	JNIEnv *env = JNI::getEnv();
-	bool hasText = env->CallBooleanMethod(_jobj, _MID_hasTextInClipboard);
-
-	if (env->ExceptionCheck()) {
-		LOGE("Failed to check the contents of the clipboard");
-
-		env->ExceptionDescribe();
-		env->ExceptionClear();
-		hasText = true;
-	}
-
-	return hasText;
-}
-
-Common::String JNI::getTextFromClipboard() {
-	JNIEnv *env = JNI::getEnv();
-
-	jbyteArray javaText = (jbyteArray)env->CallObjectMethod(_jobj, _MID_getTextFromClipboard);
-
-	if (env->ExceptionCheck()) {
-		LOGE("Failed to retrieve text from the clipboard");
-
-		env->ExceptionDescribe();
-		env->ExceptionClear();
-
-		return Common::String();
-	}
-
-	int len = env->GetArrayLength(javaText);
-	char* buf = new char[len];
-	env->GetByteArrayRegion(javaText, 0, len, reinterpret_cast<jbyte*>(buf));
-	Common::String text(buf, len);
-	delete[] buf;
-
-	return text;
-}
-
-bool JNI::setTextInClipboard(const Common::String &text) {
-	JNIEnv *env = JNI::getEnv();
-
-	jbyteArray javaText = env->NewByteArray(text.size());
-	env->SetByteArrayRegion(javaText, 0, text.size(), reinterpret_cast<const jbyte*>(text.c_str()));
-
-	bool success = env->CallBooleanMethod(_jobj, _MID_setTextInClipboard, javaText);
-
-	if (env->ExceptionCheck()) {
-		LOGE("Failed to add text to the clipboard");
-
-		env->ExceptionDescribe();
-		env->ExceptionClear();
-		success = false;
-	}
-
-	env->DeleteLocalRef(javaText);
-	return success;
-}
-
-bool JNI::isConnectionLimited() {
-	JNIEnv *env = JNI::getEnv();
-	bool limited = env->CallBooleanMethod(_jobj, _MID_isConnectionLimited);
-
-	if (env->ExceptionCheck()) {
-		LOGE("Failed to check whether connection's limited");
-
-		env->ExceptionDescribe();
-		env->ExceptionClear();
-		limited = true;
-	}
-
-	return limited;
-}
-
-void JNI::setWindowCaption(const char *caption) {
-	JNIEnv *env = JNI::getEnv();
-	jstring java_caption = env->NewStringUTF(caption);
-
-	env->CallVoidMethod(_jobj, _MID_setWindowCaption, java_caption);
-
-	if (env->ExceptionCheck()) {
-		LOGE("Failed to set window caption");
-
-		env->ExceptionDescribe();
-		env->ExceptionClear();
-	}
-
-	env->DeleteLocalRef(java_caption);
-}
-
-void JNI::showVirtualKeyboard(bool enable) {
-	JNIEnv *env = JNI::getEnv();
-
-	env->CallVoidMethod(_jobj, _MID_showVirtualKeyboard, enable);
-
-	if (env->ExceptionCheck()) {
-		LOGE("Error trying to show virtual keyboard");
-
-		env->ExceptionDescribe();
-		env->ExceptionClear();
-	}
-}
-
-void JNI::showKeyboardControl(bool enable) {
-	JNIEnv *env = JNI::getEnv();
-
-	env->CallVoidMethod(_jobj, _MID_showKeyboardControl, enable);
-
-	if (env->ExceptionCheck()) {
-		LOGE("Error trying to show virtual keyboard control");
-
-		env->ExceptionDescribe();
-		env->ExceptionClear();
-	}
-}
-
-void JNI::addSysArchivesToSearchSet(Common::SearchSet &s, int priority) {
-	JNIEnv *env = JNI::getEnv();
-
-	s.add("ASSET", _asset_archive, priority, false);
-
-	jobjectArray array =
-		(jobjectArray)env->CallObjectMethod(_jobj, _MID_getSysArchives);
-
-	if (env->ExceptionCheck()) {
-		LOGE("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, 0);
-
-		if (path != 0) {
-			s.addDirectory(path, path, priority);
-			env->ReleaseStringUTFChars(path_obj, path);
-		}
-
-		env->DeleteLocalRef(path_obj);
-	}
-}
-
-bool JNI::initSurface() {
-	JNIEnv *env = JNI::getEnv();
-
-	jobject obj = env->CallObjectMethod(_jobj, _MID_initSurface);
-
-	if (!obj || env->ExceptionCheck()) {
-		LOGE("initSurface failed");
-
-		env->ExceptionDescribe();
-		env->ExceptionClear();
-
-		return false;
-	}
-
-	_jobj_egl_surface = env->NewGlobalRef(obj);
-
-	return true;
-}
-
-void JNI::deinitSurface() {
-	JNIEnv *env = JNI::getEnv();
-
-	env->CallVoidMethod(_jobj, _MID_deinitSurface);
-
-	if (env->ExceptionCheck()) {
-		LOGE("deinitSurface failed");
-
-		env->ExceptionDescribe();
-		env->ExceptionClear();
-	}
-
-	env->DeleteGlobalRef(_jobj_egl_surface);
-	_jobj_egl_surface = 0;
-}
-
-void JNI::setAudioPause() {
-	JNIEnv *env = JNI::getEnv();
-
-	env->CallVoidMethod(_jobj_audio_track, _MID_AudioTrack_flush);
-
-	if (env->ExceptionCheck()) {
-		LOGE("Error flushing AudioTrack");
-
-		env->ExceptionDescribe();
-		env->ExceptionClear();
-	}
-
-	env->CallVoidMethod(_jobj_audio_track, _MID_AudioTrack_pause);
-
-	if (env->ExceptionCheck()) {
-		LOGE("Error setting AudioTrack: pause");
-
-		env->ExceptionDescribe();
-		env->ExceptionClear();
-	}
-}
-
-void JNI::setAudioPlay() {
-	JNIEnv *env = JNI::getEnv();
-
-	env->CallVoidMethod(_jobj_audio_track, _MID_AudioTrack_play);
-
-	if (env->ExceptionCheck()) {
-		LOGE("Error setting AudioTrack: play");
-
-		env->ExceptionDescribe();
-		env->ExceptionClear();
-	}
-}
-
-void JNI::setAudioStop() {
-	JNIEnv *env = JNI::getEnv();
-
-	env->CallVoidMethod(_jobj_audio_track, _MID_AudioTrack_stop);
-
-	if (env->ExceptionCheck()) {
-		LOGE("Error setting AudioTrack: stop");
-
-		env->ExceptionDescribe();
-		env->ExceptionClear();
-	}
-}
-
-// natives for the dark side
-
-void JNI::create(JNIEnv *env, jobject self, jobject asset_manager,
-				jobject egl, jobject egl_display,
-				jobject at, jint audio_sample_rate, jint audio_buffer_size) {
-	LOGI("%s", gScummVMFullVersion);
-
-	assert(!_system);
-
-	pause = false;
-	// initial value of zero!
-	sem_init(&pause_sem, 0, 0);
-
-	_asset_archive = new AndroidAssetArchive(asset_manager);
-	assert(_asset_archive);
-
-	_system = new OSystem_Android(audio_sample_rate, audio_buffer_size);
-	assert(_system);
-
-	// weak global ref to allow class to be unloaded
-	// ... except dalvik implements NewWeakGlobalRef only on froyo
-	//_jobj = env->NewWeakGlobalRef(self);
-
-	_jobj = env->NewGlobalRef(self);
-
-	jclass cls = env->GetObjectClass(_jobj);
-
-#define FIND_METHOD(prefix, name, signature) do {							\
-		_MID_ ## prefix ## name = env->GetMethodID(cls, #name, signature);	\
-		if (_MID_ ## prefix ## name == 0)									\
-			return;															\
-	} while (0)
-
-	FIND_METHOD(, setWindowCaption, "(Ljava/lang/String;)V");
-	FIND_METHOD(, getDPI, "([F)V");
-	FIND_METHOD(, displayMessageOnOSD, "(Ljava/lang/String;)V");
-	FIND_METHOD(, openUrl, "(Ljava/lang/String;)V");
-	FIND_METHOD(, hasTextInClipboard, "()Z");
-	FIND_METHOD(, getTextFromClipboard, "()[B");
-	FIND_METHOD(, setTextInClipboard, "([B)Z");
-	FIND_METHOD(, isConnectionLimited, "()Z");
-	FIND_METHOD(, showVirtualKeyboard, "(Z)V");
-	FIND_METHOD(, showKeyboardControl, "(Z)V");
-	FIND_METHOD(, getSysArchives, "()[Ljava/lang/String;");
-	FIND_METHOD(, getAllStorageLocations, "()[Ljava/lang/String;");
-	FIND_METHOD(, initSurface, "()Ljavax/microedition/khronos/egl/EGLSurface;");
-	FIND_METHOD(, deinitSurface, "()V");
-
-	_jobj_egl = env->NewGlobalRef(egl);
-	_jobj_egl_display = env->NewGlobalRef(egl_display);
-
-	cls = env->GetObjectClass(_jobj_egl);
-
-	FIND_METHOD(EGL10_, eglSwapBuffers,
-				"(Ljavax/microedition/khronos/egl/EGLDisplay;"
-				"Ljavax/microedition/khronos/egl/EGLSurface;)Z");
-
-	_jobj_audio_track = env->NewGlobalRef(at);
-
-	cls = env->GetObjectClass(_jobj_audio_track);
-
-	FIND_METHOD(AudioTrack_, flush, "()V");
-	FIND_METHOD(AudioTrack_, pause, "()V");
-	FIND_METHOD(AudioTrack_, play, "()V");
-	FIND_METHOD(AudioTrack_, stop, "()V");
-	FIND_METHOD(AudioTrack_, write, "([BII)I");
-
-#undef FIND_METHOD
-
-	g_system = _system;
-}
-
-void JNI::destroy(JNIEnv *env, jobject self) {
-	delete _asset_archive;
-	_asset_archive = 0;
-
-	// _system is a pointer of OSystem_Android <--- ModularBackend <--- BaseBacked <--- Common::OSystem
-	// It's better to call destroy() rather than just delete here
-	// to avoid mutex issues if a Common::String is used after this point
-	_system->destroy();
-
-	g_system = 0;
-	_system  = 0;
-
-	sem_destroy(&pause_sem);
-
-	// see above
-	//JNI::getEnv()->DeleteWeakGlobalRef(_jobj);
-
-	JNI::getEnv()->DeleteGlobalRef(_jobj_egl_display);
-	JNI::getEnv()->DeleteGlobalRef(_jobj_egl);
-	JNI::getEnv()->DeleteGlobalRef(_jobj_audio_track);
-	JNI::getEnv()->DeleteGlobalRef(_jobj);
-}
-
-void JNI::setSurface(JNIEnv *env, jobject self, jint width, jint height) {
-	egl_surface_width = width;
-	egl_surface_height = height;
-	surface_changeid++;
-}
-
-jint JNI::main(JNIEnv *env, jobject self, jobjectArray args) {
-	assert(_system);
-
-	const int MAX_NARGS = 32;
-	int res = -1;
-
-	int argc = env->GetArrayLength(args);
-	if (argc > MAX_NARGS) {
-		throwByName(env, "java/lang/IllegalArgumentException",
-					"too many arguments");
-		return 0;
-	}
-
-	char *argv[MAX_NARGS];
-
-	// note use in cleanup loop below
-	int nargs;
-
-	for (nargs = 0; nargs < argc; ++nargs) {
-		jstring arg = (jstring)env->GetObjectArrayElement(args, nargs);
-
-		if (arg == 0) {
-			argv[nargs] = 0;
-		} else {
-			const char *cstr = env->GetStringUTFChars(arg, 0);
-
-			argv[nargs] = const_cast<char *>(cstr);
-
-			// exception already thrown?
-			if (cstr == 0)
-				goto cleanup;
-		}
-
-		env->DeleteLocalRef(arg);
-	}
-
-	LOGI("Entering scummvm_main with %d args", argc);
-
-	res = scummvm_main(argc, argv);
-
-	LOGI("scummvm_main exited with code %d", res);
-
-	_system->quit();
-
-cleanup:
-	nargs--;
-
-	for (int i = 0; i < nargs; ++i) {
-		if (argv[i] == 0)
-			continue;
-
-		jstring arg = (jstring)env->GetObjectArrayElement(args, nargs);
-
-		// Exception already thrown?
-		if (arg == 0)
-			return res;
-
-		env->ReleaseStringUTFChars(arg, argv[i]);
-		env->DeleteLocalRef(arg);
-	}
-
-	return res;
-}
-
-void JNI::pushEvent(JNIEnv *env, jobject self, int type, int arg1, int arg2,
-					int arg3, int arg4, int arg5, int arg6) {
-	// drop events until we're ready and after we quit
-	if (!_ready_for_events) {
-		LOGW("dropping event");
-		return;
-	}
-
-	assert(_system);
-
-	_system->pushEvent(type, arg1, arg2, arg3, arg4, arg5, arg6);
-}
-
-void JNI::setPause(JNIEnv *env, jobject self, jboolean value) {
-	if (!_system)
-		return;
-
-	if (g_engine) {
-		LOGD("pauseEngine: %d", value);
-
-		g_engine->pauseEngine(value);
-
-		/*if (value &&
-				g_engine->hasFeature(Engine::kSupportsSavingDuringRuntime) &&
-				g_engine->canSaveGameStateCurrently())
-			g_engine->saveGameState(0, "Android parachute");*/
-	}
-
-	pause = value;
-
-	if (!pause) {
-		// wake up all threads
-		for (uint i = 0; i < 3; ++i)
-			sem_post(&pause_sem);
-	}
-}
-
-jstring JNI::getCurrentCharset(JNIEnv *env, jobject self) {
-#ifdef USE_TRANSLATION
-	if (TransMan.getCurrentCharset() != "ASCII") {
-//		LOGD("getCurrentCharset: %s", TransMan.getCurrentCharset().c_str());
-		return env->NewStringUTF(TransMan.getCurrentCharset().c_str());
-	}
-#endif
-	return env->NewStringUTF("ISO-8859-1");
-}
-
-Common::Array<Common::String> JNI::getAllStorageLocations() {
-	Common::Array<Common::String> *res = new Common::Array<Common::String>();
-
-	JNIEnv *env = JNI::getEnv();
-
-	jobjectArray array =
-		(jobjectArray)env->CallObjectMethod(_jobj, _MID_getAllStorageLocations);
-
-	if (env->ExceptionCheck()) {
-		LOGE("Error finding system archive path");
-
-		env->ExceptionDescribe();
-		env->ExceptionClear();
-
-		return *res;
-	}
-
-	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, 0);
-
-		if (path != 0) {
-			res->push_back(path);
-			env->ReleaseStringUTFChars(path_obj, path);
-		}
-
-		env->DeleteLocalRef(path_obj);
-	}
-
-	return *res;
-}
-
-
-#endif
diff --git a/backends/platform/android/jni.h b/backends/platform/android/jni.h
deleted file mode 100644
index fe4b948..0000000
--- a/backends/platform/android/jni.h
+++ /dev/null
@@ -1,161 +0,0 @@
-/* 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.
- *
- */
-
-#ifndef _ANDROID_JNI_H_
-#define _ANDROID_JNI_H_
-
-#if defined(__ANDROID__)
-
-#include <jni.h>
-#include <semaphore.h>
-
-#include "common/fs.h"
-#include "common/archive.h"
-#include "common/array.h"
-
-class OSystem_Android;
-
-class JNI {
-private:
-	JNI();
-	virtual ~JNI();
-
-public:
-	static bool pause;
-	static sem_t pause_sem;
-
-	static int surface_changeid;
-	static int egl_surface_width;
-	static int egl_surface_height;
-
-	static jint onLoad(JavaVM *vm);
-
-	static JNIEnv *getEnv();
-
-	static void attachThread();
-	static void detachThread();
-
-	static void setReadyForEvents(bool ready);
-
-	static void setWindowCaption(const char *caption);
-	static void getDPI(float *values);
-	static void displayMessageOnOSD(const char *msg);
-	static bool openUrl(const char *url);
-	static bool hasTextInClipboard();
-	static Common::String getTextFromClipboard();
-	static bool setTextInClipboard(const Common::String &text);
-	static bool isConnectionLimited();
-	static void showVirtualKeyboard(bool enable);
-	static void showKeyboardControl(bool enable);
-	static void addSysArchivesToSearchSet(Common::SearchSet &s, int priority);
-
-	static inline bool haveSurface();
-	static inline bool swapBuffers();
-	static bool initSurface();
-	static void deinitSurface();
-
-	static void setAudioPause();
-	static void setAudioPlay();
-	static void setAudioStop();
-
-	static inline int writeAudio(JNIEnv *env, jbyteArray &data, int offset,
-									int size);
-
-	static Common::Array<Common::String> getAllStorageLocations();
-
-private:
-	static JavaVM *_vm;
-	// back pointer to (java) peer instance
-	static jobject _jobj;
-	static jobject _jobj_audio_track;
-	static jobject _jobj_egl;
-	static jobject _jobj_egl_display;
-	static jobject _jobj_egl_surface;
-
-	static Common::Archive *_asset_archive;
-	static OSystem_Android *_system;
-
-	static bool _ready_for_events;
-
-	static jmethodID _MID_getDPI;
-	static jmethodID _MID_displayMessageOnOSD;
-	static jmethodID _MID_openUrl;
-	static jmethodID _MID_hasTextInClipboard;
-	static jmethodID _MID_getTextFromClipboard;
-	static jmethodID _MID_setTextInClipboard;
-	static jmethodID _MID_isConnectionLimited;
-	static jmethodID _MID_setWindowCaption;
-	static jmethodID _MID_showVirtualKeyboard;
-	static jmethodID _MID_showKeyboardControl;
-	static jmethodID _MID_getSysArchives;
-	static jmethodID _MID_getAllStorageLocations;
-	static jmethodID _MID_initSurface;
-	static jmethodID _MID_deinitSurface;
-
-	static jmethodID _MID_EGL10_eglSwapBuffers;
-
-	static jmethodID _MID_AudioTrack_flush;
-	static jmethodID _MID_AudioTrack_pause;
-	static jmethodID _MID_AudioTrack_play;
-	static jmethodID _MID_AudioTrack_stop;
-	static jmethodID _MID_AudioTrack_write;
-
-	static const JNINativeMethod _natives[];
-
-	static void throwByName(JNIEnv *env, const char *name, const char *msg);
-	static void throwRuntimeException(JNIEnv *env, const char *msg);
-
-	// natives for the dark side
-	static void create(JNIEnv *env, jobject self, jobject asset_manager,
-						jobject egl, jobject egl_display,
-						jobject at, jint audio_sample_rate,
-						jint audio_buffer_size);
-	static void destroy(JNIEnv *env, jobject self);
-
-	static void setSurface(JNIEnv *env, jobject self, jint width, jint height);
-	static jint main(JNIEnv *env, jobject self, jobjectArray args);
-
-	static void pushEvent(JNIEnv *env, jobject self, int type, int arg1,
-							int arg2, int arg3, int arg4, int arg5, int arg6);
-	static void setPause(JNIEnv *env, jobject self, jboolean value);
-
-	static jstring getCurrentCharset(JNIEnv *env, jobject self);
-};
-
-inline bool JNI::haveSurface() {
-	return _jobj_egl_surface != 0;
-}
-
-inline bool JNI::swapBuffers() {
-	JNIEnv *env = JNI::getEnv();
-
-	return env->CallBooleanMethod(_jobj_egl, _MID_EGL10_eglSwapBuffers,
-									_jobj_egl_display, _jobj_egl_surface);
-}
-
-inline int JNI::writeAudio(JNIEnv *env, jbyteArray &data, int offset, int size) {
-	return env->CallIntMethod(_jobj_audio_track, _MID_AudioTrack_write, data,
-								offset, size);
-}
-
-#endif
-#endif
diff --git a/backends/platform/android/module.mk b/backends/platform/android/module.mk
index 6a0721b..d8cf647 100644
--- a/backends/platform/android/module.mk
+++ b/backends/platform/android/module.mk
@@ -1,11 +1,12 @@
 MODULE := backends/platform/android
 
 MODULE_OBJS := \
-	jni.o \
+	jni-android.o \
 	asset-archive.o \
 	android.o \
 	graphics.o \
-	events.o
+	events.o \
+	snprintf.o
 
 # We don't use rules.mk but rather manually update OBJS and MODULE_DIRS.
 MODULE_OBJS := $(addprefix $(MODULE)/, $(MODULE_OBJS))
diff --git a/backends/platform/android/portdefs.h b/backends/platform/android/portdefs.h
new file mode 100644
index 0000000..dda6a29
--- /dev/null
+++ b/backends/platform/android/portdefs.h
@@ -0,0 +1,48 @@
+/* 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.
+ *
+ */
+
+#ifndef _PORTDEFS_H_
+#define _PORTDEFS_H_
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <assert.h>
+#include <new>
+
+#define _USE_MATH_DEFINES
+#include <math.h>
+
+// This is defined in snprintf.c
+#ifdef __cplusplus
+extern "C" {
+#endif
+int rpl_vsnprintf(char *text, size_t maxlen, const char *fmt, va_list ap);
+#ifdef __cplusplus
+}
+#endif
+
+#define vsnprintf rpl_vsnprintf
+
+#endif // _PORTDEFS_H_
diff --git a/backends/platform/android/snprintf.cpp b/backends/platform/android/snprintf.cpp
new file mode 100644
index 0000000..c0b241c
--- /dev/null
+++ b/backends/platform/android/snprintf.cpp
@@ -0,0 +1,1323 @@
+/*
+ * Copyright (c) 1995 Patrick Powell.
+ *
+ * This code is based on code written by Patrick Powell <papowell at astart.com>.
+ * It may be used for any purpose as long as this notice remains intact on all
+ * source code distributions.
+ */
+
+/*
+ * Copyright (c) 2008 Holger Weiss.
+ *
+ * This version of the code is maintained by Holger Weiss <holger at jhweiss.de>.
+ * My changes to the code may freely be used, modified and/or redistributed for
+ * any purpose.  It would be nice if additions and fixes to this file (including
+ * trivial code cleanups) would be sent back in order to let me include them in
+ * the version available at <http://www.jhweiss.de/software/snprintf.html>.
+ * However, this is not a requirement for using or redistributing (possibly
+ * modified) versions of this file, nor is leaving this notice intact mandatory.
+ */
+
+/*
+ * History
+ *
+ * 2008-01-20 Holger Weiss <holger at jhweiss.de> for C99-snprintf 1.1:
+ *
+ * 	Fixed the detection of infinite floating point values on IRIX (and
+ * 	possibly other systems) and applied another few minor cleanups.
+ *
+ * 2008-01-06 Holger Weiss <holger at jhweiss.de> for C99-snprintf 1.0:
+ *
+ * 	Added a lot of new features, fixed many bugs, and incorporated various
+ * 	improvements done by Andrew Tridgell <tridge at samba.org>, Russ Allbery
+ * 	<rra at stanford.edu>, Hrvoje Niksic <hniksic at xemacs.org>, Damien Miller
+ * 	<djm at mindrot.org>, and others for the Samba, INN, Wget, and OpenSSH
+ * 	projects.  The additions include: support the "e", "E", "g", "G", and
+ * 	"F" conversion specifiers (and use conversion style "f" or "F" for the
+ * 	still unsupported "a" and "A" specifiers); support the "hh", "ll", "j",
+ * 	"t", and "z" length modifiers; support the "#" flag and the (non-C99)
+ * 	"'" flag; use localeconv(3) (if available) to get both the current
+ * 	locale's decimal point character and the separator between groups of
+ * 	digits; fix the handling of various corner cases of field width and
+ * 	precision specifications; fix various floating point conversion bugs;
+ * 	handle infinite and NaN floating point values; don't attempt to write to
+ * 	the output buffer (which may be NULL) if a size of zero was specified;
+ * 	check for integer overflow of the field width, precision, and return
+ * 	values and during the floating point conversion; use the OUTCHAR() macro
+ * 	instead of a function for better performance; provide asprintf(3) and
+ * 	vasprintf(3) functions; add new test cases.  The replacement functions
+ * 	have been renamed to use an "rpl_" prefix, the function calls in the
+ * 	main project (and in this file) must be redefined accordingly for each
+ * 	replacement function which is needed (by using Autoconf or other means).
+ * 	Various other minor improvements have been applied and the coding style
+ * 	was cleaned up for consistency.
+ *
+ * 2007-07-23 Holger Weiss <holger at jhweiss.de> for Mutt 1.5.13:
+ *
+ * 	C99 compliant snprintf(3) and vsnprintf(3) functions return the number
+ * 	of characters that would have been written to a sufficiently sized
+ * 	buffer (excluding the '\0').  The original code simply returned the
+ * 	length of the resulting output string, so that's been fixed.
+ *
+ * 1998-03-05 Michael Elkins <me at mutt.org> for Mutt 0.90.8:
+ *
+ * 	The original code assumed that both snprintf(3) and vsnprintf(3) were
+ * 	missing.  Some systems only have snprintf(3) but not vsnprintf(3), so
+ * 	the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF.
+ *
+ * 1998-01-27 Thomas Roessler <roessler at does-not-exist.org> for Mutt 0.89i:
+ *
+ * 	The PGP code was using unsigned hexadecimal formats.  Unfortunately,
+ * 	unsigned formats simply didn't work.
+ *
+ * 1997-10-22 Brandon Long <blong at fiction.net> for Mutt 0.87.1:
+ *
+ * 	Ok, added some minimal floating point support, which means this probably
+ * 	requires libm on most operating systems.  Don't yet support the exponent
+ * 	(e,E) and sigfig (g,G).  Also, fmtint() was pretty badly broken, it just
+ * 	wasn't being exercised in ways which showed it, so that's been fixed.
+ * 	Also, formatted the code to Mutt conventions, and removed dead code left
+ * 	over from the original.  Also, there is now a builtin-test, run with:
+ * 	gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm && ./snprintf
+ *
+ * 2996-09-15 Brandon Long <blong at fiction.net> for Mutt 0.43:
+ *
+ * 	This was ugly.  It is still ugly.  I opted out of floating point
+ * 	numbers, but the formatter understands just about everything from the
+ * 	normal C string format, at least as far as I can tell from the Solaris
+ * 	2.5 printf(3S) man page.
+ */
+
+/*
+ * ToDo
+ *
+ * - Add wide character support.
+ * - Add support for "%a" and "%A" conversions.
+ * - Create test routines which predefine the expected results.  Our test cases
+ *   usually expose bugs in system implementations rather than in ours :-)
+ */
+
+/*
+ * Usage
+ *
+ * 1) The following preprocessor macros should be defined to 1 if the feature or
+ *    file in question is available on the target system (by using Autoconf or
+ *    other means), though basic functionality should be available as long as
+ *    HAVE_STDARG_H and HAVE_STDLIB_H are defined correctly:
+ *
+ *    	HAVE_VSNPRINTF
+ *    	HAVE_SNPRINTF
+ *    	HAVE_VASPRINTF
+ *    	HAVE_ASPRINTF
+ *    	HAVE_STDARG_H
+ *    	HAVE_STDDEF_H
+ *    	HAVE_STDINT_H
+ *    	HAVE_STDLIB_H
+ *    	HAVE_FLOAT_H
+ *    	HAVE_INTTYPES_H
+ *    	HAVE_LOCALE_H
+ *    	HAVE_LOCALECONV
+ *    	HAVE_LCONV_DECIMAL_POINT
+ *    	HAVE_LCONV_THOUSANDS_SEP
+ *    	HAVE_LONG_DOUBLE
+ *    	HAVE_LONG_LONG_INT
+ *    	HAVE_UNSIGNED_LONG_LONG_INT
+ *    	HAVE_INTMAX_T
+ *    	HAVE_UINTMAX_T
+ *    	HAVE_UINTPTR_T
+ *    	HAVE_PTRDIFF_T
+ *    	HAVE_VA_COPY
+ *    	HAVE___VA_COPY
+ *
+ * 2) The calls to the functions which should be replaced must be redefined
+ *    throughout the project files (by using Autoconf or other means):
+ *
+ *    	#define vsnprintf rpl_vsnprintf
+ *    	#define snprintf rpl_snprintf
+ *    	#define vasprintf rpl_vasprintf
+ *    	#define asprintf rpl_asprintf
+ *
+ * 3) The required replacement functions should be declared in some header file
+ *    included throughout the project files:
+ *
+ *    	#if HAVE_CONFIG_H
+ *    	#include <config.h>
+ *    	#endif
+ *    	#if HAVE_STDARG_H
+ *    	#include <stdarg.h>
+ *    	#if !HAVE_VSNPRINTF
+ *    	int rpl_vsnprintf(char *, size_t, const char *, va_list);
+ *    	#endif
+ *    	#if !HAVE_SNPRINTF
+ *    	int rpl_snprintf(char *, size_t, const char *, ...);
+ *    	#endif
+ *    	#if !HAVE_VASPRINTF
+ *    	int rpl_vasprintf(char **, const char *, va_list);
+ *    	#endif
+ *    	#if !HAVE_ASPRINTF
+ *    	int rpl_asprintf(char **, const char *, ...);
+ *    	#endif
+ *    	#endif
+ *
+ * Autoconf macros for handling step 1 and step 2 are available at
+ * <http://www.jhweiss.de/software/snprintf.html>.
+ */
+/* 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.
+ *
+ */
+
+#if defined(__ANDROID__)
+
+// Allow use of stuff in <time.h>
+#define FORBIDDEN_SYMBOL_EXCEPTION_time_h
+
+// Disable printf override in common/forbidden.h to avoid
+// clashes with log.h from the Android SDK.
+// That header file uses
+//   __attribute__ ((format(printf, 3, 4)))
+// which gets messed up by our override mechanism; this could
+// be avoided by either changing the Android SDK to use the equally
+// legal and valid
+//   __attribute__ ((format(printf, 3, 4)))
+// or by refining our printf override to use a varadic macro
+// (which then wouldn't be portable, though).
+// Anyway, for now we just disable the printf override globally
+// for the Android port
+#define FORBIDDEN_SYMBOL_EXCEPTION_printf
+
+#include "backends/platform/android/portdefs.h"
+
+#include <errno.h>	/* For ERANGE and errno. */
+#include <limits.h>	/* For *_MAX. */
+#include <float.h>	/* For *DBL_{MIN,MAX}_10_EXP. */
+
+#if HAVE_INTTYPES_H
+#include <inttypes.h>	/* For intmax_t (if not defined in <stdint.h>). */
+#endif	/* HAVE_INTTYPES_H */
+#if HAVE_LOCALE_H
+#include <locale.h>	/* For localeconv(3). */
+#endif	/* HAVE_LOCALE_H */
+#if HAVE_STDDEF_H
+#include <stddef.h>	/* For ptrdiff_t. */
+#endif	/* HAVE_STDDEF_H */
+#if HAVE_STDINT_H
+#include <stdint.h>	/* For intmax_t. */
+#endif	/* HAVE_STDINT_H */
+
+/* Support for unsigned long long int.  We may also need ULLONG_MAX. */
+#ifndef ULONG_MAX	/* We may need ULONG_MAX as a fallback. */
+#ifdef UINT_MAX
+#define ULONG_MAX UINT_MAX
+#else
+#define ULONG_MAX INT_MAX
+#endif	/* defined(UINT_MAX) */
+#endif	/* !defined(ULONG_MAX) */
+#ifdef ULLONG
+#undef ULLONG
+#endif	/* defined(ULLONG) */
+#if HAVE_UNSIGNED_LONG_LONG_INT
+#define ULLONG unsigned long long int
+#ifndef ULLONG_MAX
+#define ULLONG_MAX ULONG_MAX
+#endif	/* !defined(ULLONG_MAX) */
+#else
+#define ULLONG unsigned long int
+#ifdef ULLONG_MAX
+#undef ULLONG_MAX
+#endif	/* defined(ULLONG_MAX) */
+#define ULLONG_MAX ULONG_MAX
+#endif	/* HAVE_LONG_LONG_INT */
+
+/* Support for uintmax_t.  We also need UINTMAX_MAX. */
+#ifdef UINTMAX_T
+#undef UINTMAX_T
+#endif	/* defined(UINTMAX_T) */
+#if HAVE_UINTMAX_T || defined(uintmax_t)
+#define UINTMAX_T uintmax_t
+#ifndef UINTMAX_MAX
+#define UINTMAX_MAX ULLONG_MAX
+#endif	/* !defined(UINTMAX_MAX) */
+#else
+#define UINTMAX_T ULLONG
+#ifdef UINTMAX_MAX
+#undef UINTMAX_MAX
+#endif	/* defined(UINTMAX_MAX) */
+#define UINTMAX_MAX ULLONG_MAX
+#endif	/* HAVE_UINTMAX_T || defined(uintmax_t) */
+
+/* Support for long double. */
+#ifndef LDOUBLE
+#if HAVE_LONG_DOUBLE
+#define LDOUBLE long double
+#define LDOUBLE_MIN_10_EXP LDBL_MIN_10_EXP
+#define LDOUBLE_MAX_10_EXP LDBL_MAX_10_EXP
+#else
+#define LDOUBLE double
+#define LDOUBLE_MIN_10_EXP DBL_MIN_10_EXP
+#define LDOUBLE_MAX_10_EXP DBL_MAX_10_EXP
+#endif	/* HAVE_LONG_DOUBLE */
+#endif	/* !defined(LDOUBLE) */
+
+/* Support for long long int. */
+#ifndef LLONG
+#if HAVE_LONG_LONG_INT
+#define LLONG long long int
+#else
+#define LLONG long int
+#endif	/* HAVE_LONG_LONG_INT */
+#endif	/* !defined(LLONG) */
+
+/* Support for intmax_t. */
+#ifndef INTMAX_T
+#if HAVE_INTMAX_T || defined(intmax_t)
+#define INTMAX_T intmax_t
+#else
+#define INTMAX_T LLONG
+#endif	/* HAVE_INTMAX_T || defined(intmax_t) */
+#endif	/* !defined(INTMAX_T) */
+
+/* Support for uintptr_t. */
+#ifndef UINTPTR_T
+#if HAVE_UINTPTR_T || defined(uintptr_t)
+#define UINTPTR_T uintptr_t
+#else
+#define UINTPTR_T unsigned long int
+#endif	/* HAVE_UINTPTR_T || defined(uintptr_t) */
+#endif	/* !defined(UINTPTR_T) */
+
+/* Support for ptrdiff_t. */
+#ifndef PTRDIFF_T
+#if HAVE_PTRDIFF_T || defined(ptrdiff_t)
+#define PTRDIFF_T ptrdiff_t
+#else
+#define PTRDIFF_T long int
+#endif	/* HAVE_PTRDIFF_T || defined(ptrdiff_t) */
+#endif	/* !defined(PTRDIFF_T) */
+
+/*
+ * We need an unsigned integer type corresponding to ptrdiff_t (cf. C99:
+ * 7.19.6.1, 7).  However, we'll simply use PTRDIFF_T and convert it to an
+ * unsigned type if necessary.  This should work just fine in practice.
+ */
+#ifndef UPTRDIFF_T
+#define UPTRDIFF_T PTRDIFF_T
+#endif	/* !defined(UPTRDIFF_T) */
+
+/*
+ * We need a signed integer type corresponding to size_t (cf. C99: 7.19.6.1, 7).
+ * However, we'll simply use size_t and convert it to a signed type if
+ * necessary.  This should work just fine in practice.
+ */
+#ifndef SSIZE_T
+#define SSIZE_T size_t
+#endif	/* !defined(SSIZE_T) */
+
+
+/*
+ * Buffer size to hold the octal string representation of UINT128_MAX without
+ * nul-termination ("3777777777777777777777777777777777777777777").
+ */
+#ifdef MAX_CONVERT_LENGTH
+#undef MAX_CONVERT_LENGTH
+#endif	/* defined(MAX_CONVERT_LENGTH) */
+#define MAX_CONVERT_LENGTH      43
+
+/* Format read states. */
+#define PRINT_S_DEFAULT         0
+#define PRINT_S_FLAGS           1
+#define PRINT_S_WIDTH           2
+#define PRINT_S_DOT             3
+#define PRINT_S_PRECISION       4
+#define PRINT_S_MOD             5
+#define PRINT_S_CONV            6
+
+/* Format flags. */
+#define PRINT_F_MINUS           (1 << 0)
+#define PRINT_F_PLUS            (1 << 1)
+#define PRINT_F_SPACE           (1 << 2)
+#define PRINT_F_NUM             (1 << 3)
+#define PRINT_F_ZERO            (1 << 4)
+#define PRINT_F_QUOTE           (1 << 5)
+#define PRINT_F_UP              (1 << 6)
+#define PRINT_F_UNSIGNED        (1 << 7)
+#define PRINT_F_TYPE_G          (1 << 8)
+#define PRINT_F_TYPE_E          (1 << 9)
+
+/* Conversion flags. */
+#define PRINT_C_CHAR            1
+#define PRINT_C_SHORT           2
+#define PRINT_C_LONG            3
+#define PRINT_C_LLONG           4
+#define PRINT_C_LDOUBLE         5
+#define PRINT_C_SIZE            6
+#define PRINT_C_PTRDIFF         7
+#define PRINT_C_INTMAX          8
+
+#ifndef MAX
+#define MAX(x, y) ((x >= y) ? x : y)
+#endif	/* !defined(MAX) */
+#ifndef CHARTOINT
+#define CHARTOINT(ch) (ch - '0')
+#endif	/* !defined(CHARTOINT) */
+#ifndef ISDIGIT
+#define ISDIGIT(ch) ('0' <= (unsigned char)ch && (unsigned char)ch <= '9')
+#endif	/* !defined(ISDIGIT) */
+#ifndef ISNAN
+#define ISNAN(x) (x != x)
+#endif	/* !defined(ISNAN) */
+#ifndef ISINF
+#define ISINF(x) ((x < -1 || x > 1) && x + x == x)
+#endif	/* !defined(ISINF) */
+
+#ifdef OUTCHAR
+#undef OUTCHAR
+#endif	/* defined(OUTCHAR) */
+#define OUTCHAR(str, len, size, ch)                                          \
+do {                                                                         \
+	if (len + 1 < size)                                                  \
+		str[len] = ch;                                               \
+	(len)++;                                                             \
+} while (/* CONSTCOND */ 0)
+
+static void fmtstr(char *, size_t *, size_t, const char *, int, int, int);
+static void fmtint(char *, size_t *, size_t, INTMAX_T, int, int, int, int);
+static void fmtflt(char *, size_t *, size_t, LDOUBLE, int, int, int, int *);
+static void printsep(char *, size_t *, size_t);
+static int getnumsep(int);
+static int getexponent(LDOUBLE);
+static int convert(UINTMAX_T, char *, size_t, int, int);
+static UINTMAX_T cast(LDOUBLE);
+static UINTMAX_T myround(LDOUBLE);
+static LDOUBLE mypow10(int);
+
+//extern int errno;
+
+int rpl_vsnprintf(char *str, size_t size, const char *format, va_list args) {
+	LDOUBLE fvalue;
+	INTMAX_T value;
+	unsigned char cvalue;
+	const char *strvalue;
+	INTMAX_T *intmaxptr;
+	PTRDIFF_T *ptrdiffptr;
+	SSIZE_T *sizeptr;
+	LLONG *llongptr;
+	long int *longptr;
+	int *intptr;
+	short int *shortptr;
+	signed char *charptr;
+	size_t len = 0;
+	int overflow = 0;
+	int base = 0;
+	int cflags = 0;
+	int flags = 0;
+	int width = 0;
+	int precision = -1;
+	int state = PRINT_S_DEFAULT;
+	char ch = *format++;
+
+	/*
+	 * C99 says: "If `n' is zero, nothing is written, and `s' may be a null
+	 * pointer." (7.19.6.5, 2)  We're forgiving and allow a NULL pointer
+	 * even if a size larger than zero was specified.  At least NetBSD's
+	 * snprintf(3) does the same, as well as other versions of this file.
+	 * (Though some of these versions will write to a non-NULL buffer even
+	 * if a size of zero was specified, which violates the standard.)
+	 */
+	if (str == NULL && size != 0)
+		size = 0;
+
+	while (ch != '\0')
+		switch (state) {
+		case PRINT_S_DEFAULT:
+			if (ch == '%')
+				state = PRINT_S_FLAGS;
+			else
+				OUTCHAR(str, len, size, ch);
+			ch = *format++;
+			break;
+		case PRINT_S_FLAGS:
+			switch (ch) {
+			case '-':
+				flags |= PRINT_F_MINUS;
+				ch = *format++;
+				break;
+			case '+':
+				flags |= PRINT_F_PLUS;
+				ch = *format++;
+				break;
+			case ' ':
+				flags |= PRINT_F_SPACE;
+				ch = *format++;
+				break;
+			case '#':
+				flags |= PRINT_F_NUM;
+				ch = *format++;
+				break;
+			case '0':
+				flags |= PRINT_F_ZERO;
+				ch = *format++;
+				break;
+			case '\'':	/* SUSv2 flag (not in C99). */
+				flags |= PRINT_F_QUOTE;
+				ch = *format++;
+				break;
+			default:
+				state = PRINT_S_WIDTH;
+				break;
+			}
+			break;
+		case PRINT_S_WIDTH:
+			if (ISDIGIT(ch)) {
+				ch = CHARTOINT(ch);
+				if (width > (INT_MAX - ch) / 10) {
+					overflow = 1;
+					goto out;
+				}
+				width = 10 * width + ch;
+				ch = *format++;
+			} else if (ch == '*') {
+				/*
+				 * C99 says: "A negative field width argument is
+				 * taken as a `-' flag followed by a positive
+				 * field width." (7.19.6.1, 5)
+				 */
+				if ((width = va_arg(args, int)) < 0) {
+					flags |= PRINT_F_MINUS;
+					width = -width;
+				}
+				ch = *format++;
+				state = PRINT_S_DOT;
+			} else
+				state = PRINT_S_DOT;
+			break;
+		case PRINT_S_DOT:
+			if (ch == '.') {
+				state = PRINT_S_PRECISION;
+				ch = *format++;
+			} else
+				state = PRINT_S_MOD;
+			break;
+		case PRINT_S_PRECISION:
+			if (precision == -1)
+				precision = 0;
+			if (ISDIGIT(ch)) {
+				ch = CHARTOINT(ch);
+				if (precision > (INT_MAX - ch) / 10) {
+					overflow = 1;
+					goto out;
+				}
+				precision = 10 * precision + ch;
+				ch = *format++;
+			} else if (ch == '*') {
+				/*
+				 * C99 says: "A negative precision argument is
+				 * taken as if the precision were omitted."
+				 * (7.19.6.1, 5)
+				 */
+				if ((precision = va_arg(args, int)) < 0)
+					precision = -1;
+				ch = *format++;
+				state = PRINT_S_MOD;
+			} else
+				state = PRINT_S_MOD;
+			break;
+		case PRINT_S_MOD:
+			switch (ch) {
+			case 'h':
+				ch = *format++;
+				if (ch == 'h') {	/* It's a char. */
+					ch = *format++;
+					cflags = PRINT_C_CHAR;
+				} else
+					cflags = PRINT_C_SHORT;
+				break;
+			case 'l':
+				ch = *format++;
+				if (ch == 'l') {	/* It's a long long. */
+					ch = *format++;
+					cflags = PRINT_C_LLONG;
+				} else
+					cflags = PRINT_C_LONG;
+				break;
+			case 'L':
+				cflags = PRINT_C_LDOUBLE;
+				ch = *format++;
+				break;
+			case 'j':
+				cflags = PRINT_C_INTMAX;
+				ch = *format++;
+				break;
+			case 't':
+				cflags = PRINT_C_PTRDIFF;
+				ch = *format++;
+				break;
+			case 'z':
+				cflags = PRINT_C_SIZE;
+				ch = *format++;
+				break;
+			}
+			state = PRINT_S_CONV;
+			break;
+		case PRINT_S_CONV:
+			switch (ch) {
+			case 'd':
+				/* FALLTHROUGH */
+			case 'i':
+				switch (cflags) {
+				case PRINT_C_CHAR:
+					value = (signed char)va_arg(args, int);
+					break;
+				case PRINT_C_SHORT:
+					value = (short int)va_arg(args, int);
+					break;
+				case PRINT_C_LONG:
+					value = va_arg(args, long int);
+					break;
+				case PRINT_C_LLONG:
+					value = va_arg(args, LLONG);
+					break;
+				case PRINT_C_SIZE:
+					value = va_arg(args, SSIZE_T);
+					break;
+				case PRINT_C_INTMAX:
+					value = va_arg(args, INTMAX_T);
+					break;
+				case PRINT_C_PTRDIFF:
+					value = va_arg(args, PTRDIFF_T);
+					break;
+				default:
+					value = va_arg(args, int);
+					break;
+				}
+				fmtint(str, &len, size, value, 10, width,
+				    precision, flags);
+				break;
+			case 'X':
+				flags |= PRINT_F_UP;
+				/* FALLTHROUGH */
+			case 'x':
+				base = 16;
+				/* FALLTHROUGH */
+			case 'o':
+				if (base == 0)
+					base = 8;
+				/* FALLTHROUGH */
+			case 'u':
+				if (base == 0)
+					base = 10;
+				flags |= PRINT_F_UNSIGNED;
+				switch (cflags) {
+				case PRINT_C_CHAR:
+					value = (unsigned char)va_arg(args,
+					    unsigned int);
+					break;
+				case PRINT_C_SHORT:
+					value = (unsigned short int)va_arg(args,
+					    unsigned int);
+					break;
+				case PRINT_C_LONG:
+					value = va_arg(args, unsigned long int);
+					break;
+				case PRINT_C_LLONG:
+					value = va_arg(args, ULLONG);
+					break;
+				case PRINT_C_SIZE:
+					value = va_arg(args, size_t);
+					break;
+				case PRINT_C_INTMAX:
+					value = va_arg(args, UINTMAX_T);
+					break;
+				case PRINT_C_PTRDIFF:
+					value = va_arg(args, UPTRDIFF_T);
+					break;
+				default:
+					value = va_arg(args, unsigned int);
+					break;
+				}
+				fmtint(str, &len, size, value, base, width,
+				    precision, flags);
+				break;
+			case 'A':
+				/* Not yet supported, we'll use "%F". */
+				/* FALLTHROUGH */
+			case 'E':
+				if (ch == 'E')
+					flags |= PRINT_F_TYPE_E;
+				/* FALLTHROUGH */
+			case 'G':
+				if (ch == 'G')
+					flags |= PRINT_F_TYPE_G;
+				/* FALLTHROUGH */
+			case 'F':
+				flags |= PRINT_F_UP;
+				/* FALLTHROUGH */
+			case 'a':
+				/* Not yet supported, we'll use "%f". */
+				/* FALLTHROUGH */
+			case 'e':
+				if (ch == 'e')
+					flags |= PRINT_F_TYPE_E;
+				/* FALLTHROUGH */
+			case 'g':
+				if (ch == 'g')
+					flags |= PRINT_F_TYPE_G;
+				/* FALLTHROUGH */
+			case 'f':
+				if (cflags == PRINT_C_LDOUBLE)
+					fvalue = va_arg(args, LDOUBLE);
+				else
+					fvalue = va_arg(args, double);
+				fmtflt(str, &len, size, fvalue, width,
+				    precision, flags, &overflow);
+				if (overflow)
+					goto out;
+				break;
+			case 'c':
+				cvalue = va_arg(args, int);
+				OUTCHAR(str, len, size, cvalue);
+				break;
+			case 's':
+				strvalue = va_arg(args, char *);
+				fmtstr(str, &len, size, strvalue, width,
+				    precision, flags);
+				break;
+			case 'p':
+				/*
+				 * C99 says: "The value of the pointer is
+				 * converted to a sequence of printing
+				 * characters, in an implementation-defined
+				 * manner." (C99: 7.19.6.1, 8)
+				 */
+				if ((strvalue = (const char *)va_arg(args, void *)) == NULL)
+					/*
+					 * We use the glibc format.  BSD prints
+					 * "0x0", SysV "0".
+					 */
+					fmtstr(str, &len, size, "(nil)", width,
+					    -1, flags);
+				else {
+					/*
+					 * We use the BSD/glibc format.  SysV
+					 * omits the "0x" prefix (which we emit
+					 * using the PRINT_F_NUM flag).
+					 */
+					flags |= PRINT_F_NUM;
+					flags |= PRINT_F_UNSIGNED;
+					fmtint(str, &len, size,
+					    (UINTPTR_T)strvalue, 16, width,
+					    precision, flags);
+				}
+				break;
+			case 'n':
+				switch (cflags) {
+				case PRINT_C_CHAR:
+					charptr = va_arg(args, signed char *);
+					*charptr = len;
+					break;
+				case PRINT_C_SHORT:
+					shortptr = va_arg(args, short int *);
+					*shortptr = len;
+					break;
+				case PRINT_C_LONG:
+					longptr = va_arg(args, long int *);
+					*longptr = len;
+					break;
+				case PRINT_C_LLONG:
+					llongptr = va_arg(args, LLONG *);
+					*llongptr = len;
+					break;
+				case PRINT_C_SIZE:
+					/*
+					 * C99 says that with the "z" length
+					 * modifier, "a following `n' conversion
+					 * specifier applies to a pointer to a
+					 * signed integer type corresponding to
+					 * size_t argument." (7.19.6.1, 7)
+					 */
+					sizeptr = va_arg(args, SSIZE_T *);
+					*sizeptr = len;
+					break;
+				case PRINT_C_INTMAX:
+					intmaxptr = va_arg(args, INTMAX_T *);
+					*intmaxptr = len;
+					break;
+				case PRINT_C_PTRDIFF:
+					ptrdiffptr = va_arg(args, PTRDIFF_T *);
+					*ptrdiffptr = len;
+					break;
+				default:
+					intptr = va_arg(args, int *);
+					*intptr = len;
+					break;
+				}
+				break;
+			case '%':	/* Print a "%" character verbatim. */
+				OUTCHAR(str, len, size, ch);
+				break;
+			default:	/* Skip other characters. */
+				break;
+			}
+			ch = *format++;
+			state = PRINT_S_DEFAULT;
+			base = cflags = flags = width = 0;
+			precision = -1;
+			break;
+		}
+out:
+	if (len < size)
+		str[len] = '\0';
+	else if (size > 0)
+		str[size - 1] = '\0';
+
+	if (overflow || len > INT_MAX) {
+		errno = EOVERFLOW;
+		return -1;
+	}
+	return (int)len;
+}
+
+static void fmtstr(char *str, size_t *len, size_t size, const char *value, int width, int precision, int flags) {
+	int padlen, strln;	/* Amount to pad. */
+	int noprecision = (precision == -1);
+
+	if (value == NULL)	/* We're forgiving. */
+		value = "(null)";
+
+	/* If a precision was specified, don't read the string past it. */
+	for (strln = 0; value[strln] != '\0' &&
+	    (noprecision || strln < precision); strln++)
+		continue;
+
+	if ((padlen = width - strln) < 0)
+		padlen = 0;
+	if (flags & PRINT_F_MINUS)	/* Left justify. */
+		padlen = -padlen;
+
+	while (padlen > 0) {	/* Leading spaces. */
+		OUTCHAR(str, *len, size, ' ');
+		padlen--;
+	}
+	while (*value != '\0' && (noprecision || precision-- > 0)) {
+		OUTCHAR(str, *len, size, *value);
+		value++;
+	}
+	while (padlen < 0) {	/* Trailing spaces. */
+		OUTCHAR(str, *len, size, ' ');
+		padlen++;
+	}
+}
+
+static void fmtint(char *str, size_t *len, size_t size, INTMAX_T value, int base, int width, int precision, int flags) {
+	UINTMAX_T uvalue;
+	char iconvert[MAX_CONVERT_LENGTH];
+	char sign = 0;
+	char hexprefix = 0;
+	int spadlen = 0;	/* Amount to space pad. */
+	int zpadlen = 0;	/* Amount to zero pad. */
+	int pos;
+	int separators = (flags & PRINT_F_QUOTE);
+	int noprecision = (precision == -1);
+
+	if (flags & PRINT_F_UNSIGNED)
+		uvalue = value;
+	else {
+		uvalue = (value >= 0) ? value : -value;
+		if (value < 0)
+			sign = '-';
+		else if (flags & PRINT_F_PLUS)	/* Do a sign. */
+			sign = '+';
+		else if (flags & PRINT_F_SPACE)
+			sign = ' ';
+	}
+
+	pos = convert(uvalue, iconvert, sizeof(iconvert), base,
+	    flags & PRINT_F_UP);
+
+	if (flags & PRINT_F_NUM && uvalue != 0) {
+		/*
+		 * C99 says: "The result is converted to an `alternative form'.
+		 * For `o' conversion, it increases the precision, if and only
+		 * if necessary, to force the first digit of the result to be a
+		 * zero (if the value and precision are both 0, a single 0 is
+		 * printed).  For `x' (or `X') conversion, a nonzero result has
+		 * `0x' (or `0X') prefixed to it." (7.19.6.1, 6)
+		 */
+		switch (base) {
+		case 8:
+			if (precision <= pos)
+				precision = pos + 1;
+			break;
+		case 16:
+			hexprefix = (flags & PRINT_F_UP) ? 'X' : 'x';
+			break;
+		}
+	}
+
+	if (separators)	/* Get the number of group separators we'll print. */
+		separators = getnumsep(pos);
+
+	zpadlen = precision - pos - separators;
+	spadlen = width                         /* Minimum field width. */
+	    - separators                        /* Number of separators. */
+	    - MAX(precision, pos)               /* Number of integer digits. */
+	    - ((sign != 0) ? 1 : 0)             /* Will we print a sign? */
+	    - ((hexprefix != 0) ? 2 : 0);       /* Will we print a prefix? */
+
+	if (zpadlen < 0)
+		zpadlen = 0;
+	if (spadlen < 0)
+		spadlen = 0;
+
+	/*
+	 * C99 says: "If the `0' and `-' flags both appear, the `0' flag is
+	 * ignored.  For `d', `i', `o', `u', `x', and `X' conversions, if a
+	 * precision is specified, the `0' flag is ignored." (7.19.6.1, 6)
+	 */
+	if (flags & PRINT_F_MINUS)	/* Left justify. */
+		spadlen = -spadlen;
+	else if (flags & PRINT_F_ZERO && noprecision) {
+		zpadlen += spadlen;
+		spadlen = 0;
+	}
+	while (spadlen > 0) {	/* Leading spaces. */
+		OUTCHAR(str, *len, size, ' ');
+		spadlen--;
+	}
+	if (sign != 0)	/* Sign. */
+		OUTCHAR(str, *len, size, sign);
+	if (hexprefix != 0) {	/* A "0x" or "0X" prefix. */
+		OUTCHAR(str, *len, size, '0');
+		OUTCHAR(str, *len, size, hexprefix);
+	}
+	while (zpadlen > 0) {	/* Leading zeros. */
+		OUTCHAR(str, *len, size, '0');
+		zpadlen--;
+	}
+	while (pos > 0) {	/* The actual digits. */
+		pos--;
+		OUTCHAR(str, *len, size, iconvert[pos]);
+		if (separators > 0 && pos > 0 && pos % 3 == 0)
+			printsep(str, len, size);
+	}
+	while (spadlen < 0) {	/* Trailing spaces. */
+		OUTCHAR(str, *len, size, ' ');
+		spadlen++;
+	}
+}
+
+static void fmtflt(char *str, size_t *len, size_t size, LDOUBLE fvalue, int width, int precision, int flags, int *overflow) {
+	LDOUBLE ufvalue;
+	UINTMAX_T intpart;
+	UINTMAX_T fracpart;
+	UINTMAX_T mask;
+	const char *infnan = NULL;
+	char iconvert[MAX_CONVERT_LENGTH];
+	char fconvert[MAX_CONVERT_LENGTH];
+	char econvert[5];	/* "e-300" (without nul-termination). */
+	char esign = 0;
+	char sign = 0;
+	int leadfraczeros = 0;
+	int exponent = 0;
+	int emitpoint = 0;
+	int omitzeros = 0;
+	int omitcount = 0;
+	int padlen = 0;
+	int epos = 0;
+	int fpos = 0;
+	int ipos = 0;
+	int separators = (flags & PRINT_F_QUOTE);
+	int estyle = (flags & PRINT_F_TYPE_E);
+#if HAVE_LOCALECONV && HAVE_LCONV_DECIMAL_POINT
+	struct lconv *lc = localeconv();
+#endif	/* HAVE_LOCALECONV && HAVE_LCONV_DECIMAL_POINT */
+
+	/*
+	 * AIX' man page says the default is 0, but C99 and at least Solaris'
+	 * and NetBSD's man pages say the default is 6, and sprintf(3) on AIX
+	 * defaults to 6.
+	 */
+	if (precision == -1)
+		precision = 6;
+
+	if (fvalue < 0.0)
+		sign = '-';
+	else if (flags & PRINT_F_PLUS)	/* Do a sign. */
+		sign = '+';
+	else if (flags & PRINT_F_SPACE)
+		sign = ' ';
+
+	if (ISNAN(fvalue))
+		infnan = (flags & PRINT_F_UP) ? "NAN" : "nan";
+	else if (ISINF(fvalue))
+		infnan = (flags & PRINT_F_UP) ? "INF" : "inf";
+
+	if (infnan != NULL) {
+		if (sign != 0)
+			iconvert[ipos++] = sign;
+		while (*infnan != '\0')
+			iconvert[ipos++] = *infnan++;
+		fmtstr(str, len, size, iconvert, width, ipos, flags);
+		return;
+	}
+
+	/* "%e" (or "%E") or "%g" (or "%G") conversion. */
+	if (flags & PRINT_F_TYPE_E || flags & PRINT_F_TYPE_G) {
+		if (flags & PRINT_F_TYPE_G) {
+			/*
+			 * If the precision is zero, it is treated as one (cf.
+			 * C99: 7.19.6.1, 8).
+			 */
+			if (precision == 0)
+				precision = 1;
+			/*
+			 * For "%g" (and "%G") conversions, the precision
+			 * specifies the number of significant digits, which
+			 * includes the digits in the integer part.  The
+			 * conversion will or will not be using "e-style" (like
+			 * "%e" or "%E" conversions) depending on the precision
+			 * and on the exponent.  However, the exponent can be
+			 * affected by rounding the converted value, so we'll
+			 * leave this decision for later.  Until then, we'll
+			 * assume that we're going to do an "e-style" conversion
+			 * (in order to get the exponent calculated).  For
+			 * "e-style", the precision must be decremented by one.
+			 */
+			precision--;
+			/*
+			 * For "%g" (and "%G") conversions, trailing zeros are
+			 * removed from the fractional portion of the result
+			 * unless the "#" flag was specified.
+			 */
+			if (!(flags & PRINT_F_NUM))
+				omitzeros = 1;
+		}
+		exponent = getexponent(fvalue);
+		estyle = 1;
+	}
+
+again:
+	/*
+	 * Sorry, we only support 9, 19, or 38 digits (that is, the number of
+	 * digits of the 32-bit, the 64-bit, or the 128-bit UINTMAX_MAX value
+	 * minus one) past the decimal point due to our conversion method.
+	 */
+	switch (sizeof(UINTMAX_T)) {
+	case 16:
+		if (precision > 38)
+			precision = 38;
+		break;
+	case 8:
+		if (precision > 19)
+			precision = 19;
+		break;
+	default:
+		if (precision > 9)
+			precision = 9;
+		break;
+	}
+
+	ufvalue = (fvalue >= 0.0) ? fvalue : -fvalue;
+	if (estyle)	/* We want exactly one integer digit. */
+		ufvalue /= mypow10(exponent);
+
+	if ((intpart = cast(ufvalue)) == UINTMAX_MAX) {
+		*overflow = 1;
+		return;
+	}
+
+	/*
+	 * Factor of ten with the number of digits needed for the fractional
+	 * part.  For example, if the precision is 3, the mask will be 1000.
+	 */
+	mask = mypow10(precision);
+	/*
+	 * We "cheat" by converting the fractional part to integer by
+	 * multiplying by a factor of ten.
+	 */
+	if ((fracpart = myround(mask * (ufvalue - intpart))) >= mask) {
+		/*
+		 * For example, ufvalue = 2.99962, intpart = 2, and mask = 1000
+		 * (because precision = 3).  Now, myround(1000 * 0.99962) will
+		 * return 1000.  So, the integer part must be incremented by one
+		 * and the fractional part must be set to zero.
+		 */
+		intpart++;
+		fracpart = 0;
+		if (estyle && intpart == 10) {
+			/*
+			 * The value was rounded up to ten, but we only want one
+			 * integer digit if using "e-style".  So, the integer
+			 * part must be set to one and the exponent must be
+			 * incremented by one.
+			 */
+			intpart = 1;
+			exponent++;
+		}
+	}
+
+	/*
+	 * Now that we know the real exponent, we can check whether or not to
+	 * use "e-style" for "%g" (and "%G") conversions.  If we don't need
+	 * "e-style", the precision must be adjusted and the integer and
+	 * fractional parts must be recalculated from the original value.
+	 *
+	 * C99 says: "Let P equal the precision if nonzero, 6 if the precision
+	 * is omitted, or 1 if the precision is zero.  Then, if a conversion
+	 * with style `E' would have an exponent of X:
+	 *
+	 * - if P > X >= -4, the conversion is with style `f' (or `F') and
+	 *   precision P - (X + 1).
+	 *
+	 * - otherwise, the conversion is with style `e' (or `E') and precision
+	 *   P - 1." (7.19.6.1, 8)
+	 *
+	 * Note that we had decremented the precision by one.
+	 */
+	if (flags & PRINT_F_TYPE_G && estyle &&
+	    precision + 1 > exponent && exponent >= -4) {
+		precision -= exponent;
+		estyle = 0;
+		goto again;
+	}
+
+	if (estyle) {
+		if (exponent < 0) {
+			exponent = -exponent;
+			esign = '-';
+		} else
+			esign = '+';
+
+		/*
+		 * Convert the exponent.  The sizeof(econvert) is 5.  So, the
+		 * econvert buffer can hold e.g. "e+999" and "e-999".  We don't
+		 * support an exponent which contains more than three digits.
+		 * Therefore, the following stores are safe.
+		 */
+		epos = convert(exponent, econvert, 3, 10, 0);
+		/*
+		 * C99 says: "The exponent always contains at least two digits,
+		 * and only as many more digits as necessary to represent the
+		 * exponent." (7.19.6.1, 8)
+		 */
+		if (epos == 1)
+			econvert[epos++] = '0';
+		econvert[epos++] = esign;
+		econvert[epos++] = (flags & PRINT_F_UP) ? 'E' : 'e';
+	}
+
+	/* Convert the integer part and the fractional part. */
+	ipos = convert(intpart, iconvert, sizeof(iconvert), 10, 0);
+	if (fracpart != 0)	/* convert() would return 1 if fracpart == 0. */
+		fpos = convert(fracpart, fconvert, sizeof(fconvert), 10, 0);
+
+	leadfraczeros = precision - fpos;
+
+	if (omitzeros) {
+		if (fpos > 0)	/* Omit trailing fractional part zeros. */
+			while (omitcount < fpos && fconvert[omitcount] == '0')
+				omitcount++;
+		else {	/* The fractional part is zero, omit it completely. */
+			omitcount = precision;
+			leadfraczeros = 0;
+		}
+		precision -= omitcount;
+	}
+
+	/*
+	 * Print a decimal point if either the fractional part is non-zero
+	 * and/or the "#" flag was specified.
+	 */
+	if (precision > 0 || flags & PRINT_F_NUM)
+		emitpoint = 1;
+	if (separators)	/* Get the number of group separators we'll print. */
+		separators = getnumsep(ipos);
+
+	padlen = width                  /* Minimum field width. */
+	    - ipos                      /* Number of integer digits. */
+	    - epos                      /* Number of exponent characters. */
+	    - precision                 /* Number of fractional digits. */
+	    - separators                /* Number of group separators. */
+	    - (emitpoint ? 1 : 0)       /* Will we print a decimal point? */
+	    - ((sign != 0) ? 1 : 0);    /* Will we print a sign character? */
+
+	if (padlen < 0)
+		padlen = 0;
+
+	/*
+	 * C99 says: "If the `0' and `-' flags both appear, the `0' flag is
+	 * ignored." (7.19.6.1, 6)
+	 */
+	if (flags & PRINT_F_MINUS)	/* Left justifty. */
+		padlen = -padlen;
+	else if (flags & PRINT_F_ZERO && padlen > 0) {
+		if (sign != 0) {	/* Sign. */
+			OUTCHAR(str, *len, size, sign);
+			sign = 0;
+		}
+		while (padlen > 0) {	/* Leading zeros. */
+			OUTCHAR(str, *len, size, '0');
+			padlen--;
+		}
+	}
+	while (padlen > 0) {	/* Leading spaces. */
+		OUTCHAR(str, *len, size, ' ');
+		padlen--;
+	}
+	if (sign != 0)	/* Sign. */
+		OUTCHAR(str, *len, size, sign);
+	while (ipos > 0) {	/* Integer part. */
+		ipos--;
+		OUTCHAR(str, *len, size, iconvert[ipos]);
+		if (separators > 0 && ipos > 0 && ipos % 3 == 0)
+			printsep(str, len, size);
+	}
+	if (emitpoint) {	/* Decimal point. */
+#if HAVE_LOCALECONV && HAVE_LCONV_DECIMAL_POINT
+		if (lc->decimal_point != NULL && *lc->decimal_point != '\0')
+			OUTCHAR(str, *len, size, *lc->decimal_point);
+		else	/* We'll always print some decimal point character. */
+#endif	/* HAVE_LOCALECONV && HAVE_LCONV_DECIMAL_POINT */
+			OUTCHAR(str, *len, size, '.');
+	}
+	while (leadfraczeros > 0) {	/* Leading fractional part zeros. */
+		OUTCHAR(str, *len, size, '0');
+		leadfraczeros--;
+	}
+	while (fpos > omitcount) {	/* The remaining fractional part. */
+		fpos--;
+		OUTCHAR(str, *len, size, fconvert[fpos]);
+	}
+	while (epos > 0) {	/* Exponent. */
+		epos--;
+		OUTCHAR(str, *len, size, econvert[epos]);
+	}
+	while (padlen < 0) {	/* Trailing spaces. */
+		OUTCHAR(str, *len, size, ' ');
+		padlen++;
+	}
+}
+
+static void printsep(char *str, size_t *len, size_t size) {
+#if HAVE_LOCALECONV && HAVE_LCONV_THOUSANDS_SEP
+	struct lconv *lc = localeconv();
+	int i;
+
+	if (lc->thousands_sep != NULL)
+		for (i = 0; lc->thousands_sep[i] != '\0'; i++)
+			OUTCHAR(str, *len, size, lc->thousands_sep[i]);
+	else
+#endif	/* HAVE_LOCALECONV && HAVE_LCONV_THOUSANDS_SEP */
+		OUTCHAR(str, *len, size, ',');
+}
+
+static int
+getnumsep(int digits)
+{
+	int separators = (digits - ((digits % 3 == 0) ? 1 : 0)) / 3;
+#if HAVE_LOCALECONV && HAVE_LCONV_THOUSANDS_SEP
+	int strln;
+	struct lconv *lc = localeconv();
+
+	/* We support an arbitrary separator length (including zero). */
+	if (lc->thousands_sep != NULL) {
+		for (strln = 0; lc->thousands_sep[strln] != '\0'; strln++)
+			continue;
+		separators *= strln;
+	}
+#endif	/* HAVE_LOCALECONV && HAVE_LCONV_THOUSANDS_SEP */
+	return separators;
+}
+
+static int getexponent(LDOUBLE value) {
+	LDOUBLE tmp = (value >= 0.0) ? value : -value;
+	int exponent = 0;
+
+	/*
+	 * We check for LDOUBLE_MAX_10_EXP >= exponent >= LDOUBLE_MIN_10_EXP in
+	 * order to work around possible endless loops which could happen (at
+	 * least) in the second loop (at least) if we're called with an infinite
+	 * value.  However, we checked for infinity before calling this function
+	 * using our ISINF() macro, so this might be somewhat paranoid.
+	 */
+	while (tmp < 1.0 && tmp > 0.0 && --exponent >= LDOUBLE_MIN_10_EXP)
+		tmp *= 10;
+	while (tmp >= 10.0 && ++exponent <= LDOUBLE_MAX_10_EXP)
+		tmp /= 10;
+
+	return exponent;
+}
+
+static int convert(UINTMAX_T value, char *buf, size_t size, int base, int caps) {
+	const char *digits = caps ? "0123456789ABCDEF" : "0123456789abcdef";
+	size_t pos = 0;
+
+	/* We return an unterminated buffer with the digits in reverse order. */
+	do {
+		buf[pos++] = digits[value % base];
+		value /= base;
+	} while (value != 0 && pos < size);
+
+	return (int)pos;
+}
+
+static UINTMAX_T cast(LDOUBLE value) {
+	UINTMAX_T result;
+
+	/*
+	 * We check for ">=" and not for ">" because if UINTMAX_MAX cannot be
+	 * represented exactly as an LDOUBLE value (but is less than LDBL_MAX),
+	 * it may be increased to the nearest higher representable value for the
+	 * comparison (cf. C99: 6.3.1.4, 2).  It might then equal the LDOUBLE
+	 * value although converting the latter to UINTMAX_T would overflow.
+	 */
+	if (value >= UINTMAX_MAX)
+		return UINTMAX_MAX;
+
+	result = value;
+	/*
+	 * At least on NetBSD/sparc64 3.0.2 and 4.99.30, casting long double to
+	 * an integer type converts e.g. 1.9 to 2 instead of 1 (which violates
+	 * the standard).  Sigh.
+	 */
+	return (result <= value) ? result : result - 1;
+}
+
+static UINTMAX_T myround(LDOUBLE value) {
+	UINTMAX_T intpart = cast(value);
+
+	return ((value -= intpart) < 0.5) ? intpart : intpart + 1;
+}
+
+static LDOUBLE mypow10(int exponent) {
+	LDOUBLE result = 1;
+
+	while (exponent > 0) {
+		result *= 10;
+		exponent--;
+	}
+	while (exponent < 0) {
+		result /= 10;
+		exponent++;
+	}
+	return result;
+}
+#endif // defined(__ANDROID__)
+/* vim: set joinspaces noexpandtab textwidth=80 cinoptions=(4,u0: */
diff --git a/configure b/configure
index 1a90845..939a4ca 100755
--- a/configure
+++ b/configure
@@ -3071,6 +3071,7 @@ if test -n "$_host"; then
 			_port_mk="backends/platform/3ds/3ds.mk"
 			;;
 		android | android-arm | android-v7a | android-arm-v7a | android-arm64-v8a | android-mips | android-mips64 | android-x86 | android-x86_64 | ouya)
+			# also __ANDROID__ is defined by Clang in the NDK
 			DEFINES="$DEFINES -D__ANDROID_PLAIN_PORT__ -DANDROID_PLAIN_PORT"
 			# we link a .so as default
 			append_var LDFLAGS "-shared"
@@ -3557,6 +3558,8 @@ case $_backend in
 		;;
 	android)
 		append_var DEFINES "-DREDUCE_MEMORY_USAGE"
+		append_var DEFINES "-DNONSTANDARD_PORT"
+		append_var INCLUDES '-I$(srcdir)/backends/platform/android'
 		append_var CXXFLAGS "-Wa,--noexecstack"
 		append_var LDFLAGS "-Wl,-z,noexecstack"
 		# removed the following directive - was causing compilation issues when not also explicitly using --disable-mt32emu


Commit: d6b9edeb3837a61a7adf6d55b204ef632d0ec6a0
    https://github.com/scummvm/scummvm/commit/d6b9edeb3837a61a7adf6d55b204ef632d0ec6a0
Author: Thanasis Antoniou (a.antoniou79 at gmail.com)
Date: 2020-01-13T14:28:49+02:00

Commit Message:
ANDROID: Remove duplicate header inclusion in android.h

Changed paths:
    backends/platform/android/android.h


diff --git a/backends/platform/android/android.h b/backends/platform/android/android.h
index b5cb1d5..1126b38 100644
--- a/backends/platform/android/android.h
+++ b/backends/platform/android/android.h
@@ -32,7 +32,6 @@
 #include "backends/modular-backend.h"
 #include "backends/plugins/posix/posix-provider.h"
 #include "backends/fs/posix/posix-fs-factory.h"
-#include "backends/fs/posix/posix-fs-factory.h"
 
 #include <pthread.h>
 


Commit: d72cd2fe7459883f3010718bdcf4fe128c989763
    https://github.com/scummvm/scummvm/commit/d72cd2fe7459883f3010718bdcf4fe128c989763
Author: Thanasis Antoniou (a.antoniou79 at gmail.com)
Date: 2020-01-13T14:28:58+02:00

Commit Message:
CONFIGURE: Detect SDL_net independently from SDL

Some platforms (eg. Android, iOS) use edited versions of SDL_net or SDL2_net that does not require SDL or SDL2

This should enable local server (LAN) mode for Android and probably iOS

Changed paths:
    configure


diff --git a/configure b/configure
index 939a4ca..1253776 100755
--- a/configure
+++ b/configure
@@ -3564,6 +3564,10 @@ case $_backend in
 		append_var LDFLAGS "-Wl,-z,noexecstack"
 		# removed the following directive - was causing compilation issues when not also explicitly using --disable-mt32emu
 #		append_var INCLUDES "-isystem $ANDROID_NDK/sources/cxx-stl/system/include"
+		_sdl=no
+		if test "$_host" = ouya; then
+			_sdlnet=no
+		fi
 		;;
 	androidsdl)
 		_sdl=auto
@@ -3788,6 +3792,8 @@ EOF
 		exit 1
 	fi
 fi
+
+_sdlMajorVersionNumber=0
 if test "$_sdl" = yes ; then
 	append_var DEFINES "-DSDL_BACKEND"
 	add_line_to_config_mk "SDL_BACKEND = 1"
@@ -3796,33 +3802,67 @@ if test "$_sdl" = yes ; then
 	case $_sdlversion in
 		2.0.*)
 			add_line_to_config_mk "USE_SDL2 = 1"
-			if test "$_pkg_config" = "yes" && $_pkgconfig --exists sdl2_net; then
-				append_var SDL_NET_LIBS "`$_pkgconfig --libs sdl2_net`"
-				append_var SDL_NET_CFLAGS "`$_pkgconfig --cflags sdl2_net`"
-			else
-				append_var SDL_NET_LIBS "-lSDL2_net"
-			fi
+			_sdlMajorVersionNumber=2
 			;;
 		*)
-			if test "$_pkg_config" = "yes" && $_pkgconfig --exists sdl_net; then
-				append_var SDL_NET_LIBS "`$_pkgconfig --libs sdl_net`"
-				append_var SDL_NET_CFLAGS "`$_pkgconfig --cflags sdl_net`"
-			else
-				append_var SDL_NET_LIBS "-lSDL_net"
-			fi
+			_sdlMajorVersionNumber=1
 			;;
 	esac
+fi
+
+#
+# Some platforms (eg. Android, iOS) may use an edited version
+# of SDL-net or SDL2-net that does not require SDL or SDL2 respectively
+#
+if test "$_sdlnet" = auto ; then
+	# If SDL2 was detected, then test for SDL2_net exclusively
+	# If SDL was detected, then test for SDL_net exclusively
+	# If neither SDL nor SDL2 detected, then test for both (SDL2_net success takes priority)
+	set_var SDL2_NET_LIBS   "$SDL_NET_LIBS"
+	set_var SDL2_NET_CFLAGS "$SDL_NET_CFLAGS"
+	set_var SDL1_NET_LIBS   "$SDL_NET_LIBS"
+	set_var SDL1_NET_CFLAGS "$SDL_NET_CFLAGS"
+
+	if test "$_sdl" = no || test "$_sdlMajorVersionNumber" = 2; then
+		if test "$_pkg_config" = "yes" && $_pkgconfig --exists SDL2_net; then
+			append_var SDL2_NET_LIBS "`$_pkgconfig --libs SDL2_net`"
+			append_var SDL2_NET_CFLAGS "`$_pkgconfig --cflags SDL2_net`"
+		else
+			append_var SDL2_NET_LIBS "-lSDL2_net"
+		fi
+	fi
+
+	if test "$_sdl" = no || test "$_sdlMajorVersionNumber" = 1; then
+		if test "$_pkg_config" = "yes" && $_pkgconfig --exists SDL_net; then
+			append_var SDL1_NET_LIBS "`$_pkgconfig --libs SDL_net`"
+			append_var SDL1_NET_CFLAGS "`$_pkgconfig --cflags SDL_net`"
+		else
+			append_var SDL1_NET_LIBS "-lSDL_net"
+		fi
+	fi
 
 	# Check for SDL_Net
 	echocheck "SDL_Net"
-	if test "$_sdlnet" = auto ; then
-		_sdlnet=no
+	_sdlnet=no
+	cat > $TMPC << EOF
+#include "SDL_net.h"
+int main(int argc, char *argv[]) { SDLNet_Init(); return 0; }
+EOF
+
+	cc_check $SDL2_NET_LIBS $LIBS $INCLUDES $SDL2_NET_CFLAGS && _sdlnet=yes
+	if test "$_sdlnet" = yes ; then
+		set_var SDL_NET_LIBS   "$SDL2_NET_LIBS"
+		set_var SDL_NET_CFLAGS "$SDL2_NET_CFLAGS"
+	else
 		cat > $TMPC << EOF
 #include "SDL_net.h"
 int main(int argc, char *argv[]) { SDLNet_Init(); return 0; }
 EOF
-		cc_check $SDL_NET_LIBS $LIBS $INCLUDES $SDL_NET_CFLAGS && _sdlnet=yes
+		cc_check $SDL1_NET_LIBS $LIBS $INCLUDES $SDL1_NET_CFLAGS && _sdlnet=yes
+		set_var SDL_NET_LIBS   "$SDL1_NET_LIBS"
+		set_var SDL_NET_CFLAGS "$SDL1_NET_CFLAGS"
 	fi
+
 	if test "$_sdlnet" = yes ; then
 		# Some platforms require SDL to be after SDL_Net, thus we prepend var
 		prepend_var LIBS "$SDL_NET_LIBS"


Commit: 6f333314f03bb386442ebc6e81aa265717bbaffa
    https://github.com/scummvm/scummvm/commit/6f333314f03bb386442ebc6e81aa265717bbaffa
Author: Thanasis Antoniou (a.antoniou79 at gmail.com)
Date: 2020-01-13T14:29:07+02:00

Commit Message:
ANDROID: Add docs and port dist files to Assets

Changed paths:
    backends/platform/android/android.mk


diff --git a/backends/platform/android/android.mk b/backends/platform/android/android.mk
index 5842db0..fa6cf46 100644
--- a/backends/platform/android/android.mk
+++ b/backends/platform/android/android.mk
@@ -63,9 +63,9 @@ $(PATH_BUILD)/libs/%: $(PATH_DIST)/libs/% | $(PATH_BUILD)
 	@$(MKDIR) -p $(@D)
 	$(CP) $< $@
 
-$(PATH_BUILD_ASSETS): $(DIST_FILES_THEMES) $(DIST_FILES_ENGINEDATA) $(DIST_FILES_NETWORKING) $(DIST_FILES_VKEYBD) $(DIST_BUILD_XML) | $(PATH_BUILD)
+$(PATH_BUILD_ASSETS): $(DIST_FILES_THEMES) $(DIST_FILES_ENGINEDATA) $(DIST_FILES_NETWORKING) $(DIST_FILES_VKEYBD) $(DIST_BUILD_XML) $(DIST_FILES_DOCS) $(PORT_DISTFILES) | $(PATH_BUILD)
 	$(INSTALL) -d $(PATH_BUILD_ASSETS)
-	$(INSTALL) -c -m 644 $(DIST_FILES_THEMES) $(DIST_FILES_ENGINEDATA) $(DIST_FILES_NETWORKING) $(DIST_FILES_VKEYBD) $(PATH_BUILD_ASSETS)/
+	$(INSTALL) -c -m 644 $(DIST_FILES_THEMES) $(DIST_FILES_ENGINEDATA) $(DIST_FILES_NETWORKING) $(DIST_FILES_VKEYBD) $(DIST_FILES_DOCS) $(PORT_DISTFILES) $(PATH_BUILD_ASSETS)/
 	$(INSTALL) -d $(PATH_BUILD)/jni
 	$(INSTALL) -c -m 644 $(DIST_ANDROID_MK) $(PATH_BUILD)/jni
 	$(INSTALL) -c -m 644 $(DIST_BUILD_XML) $(PATH_BUILD)


Commit: dad35969f2d1d6f27ef80a12303176c785e01fbe
    https://github.com/scummvm/scummvm/commit/dad35969f2d1d6f27ef80a12303176c785e01fbe
Author: Cameron Cawley (ccawley2011 at gmail.com)
Date: 2020-01-13T14:29:24+02:00

Commit Message:
ANDROID: Improve keyboard support (#1857)

Changed paths:
    backends/platform/android/events.cpp
    backends/platform/android/events.h
    backends/platform/android/org/scummvm/scummvm/ScummVMEvents.java


diff --git a/backends/platform/android/events.cpp b/backends/platform/android/events.cpp
index 2765b07..4503d1e 100644
--- a/backends/platform/android/events.cpp
+++ b/backends/platform/android/events.cpp
@@ -239,15 +239,15 @@ void OSystem_Android::pushEvent(int type, int arg1, int arg2, int arg3,
 			break;
 		}
 
-		if (arg4 & JMETA_SHIFT)
+		if (arg4 & JMETA_SHIFT_MASK)
 			e.kbd.flags |= Common::KBD_SHIFT;
 		// JMETA_ALT is Fn on physical keyboards!
 		// when mapping this to ALT - as we know it from PC keyboards - all
 		// Fn combos will be broken (like Fn+q, which needs to end as 1 and
 		// not ALT+1). Do not want.
-		//if (arg4 & JMETA_ALT)
+		//if (arg4 & JMETA_ALT_MASK)
 		//	e.kbd.flags |= Common::KBD_ALT;
-		if (arg4 & (JMETA_SYM | JMETA_CTRL))
+		if (arg4 & (JMETA_SYM_ON | JMETA_CTRL_MASK))
 			e.kbd.flags |= Common::KBD_CTRL;
 
 		pushEvent(e);
@@ -281,7 +281,7 @@ void OSystem_Android::pushEvent(int type, int arg1, int arg2, int arg3,
 
 				// the longer the button held, the faster the pointer is
 				// TODO put these values in some option dlg?
-				int f = CLIP(arg4, 1, 8) * _dpad_scale * 100 / s;
+				int f = CLIP(arg5, 1, 8) * _dpad_scale * 100 / s;
 
 				if (arg2 == JKEYCODE_DPAD_UP || arg2 == JKEYCODE_DPAD_LEFT)
 					*c -= f;
diff --git a/backends/platform/android/events.h b/backends/platform/android/events.h
index 1b0713c..4c114ce 100644
--- a/backends/platform/android/events.h
+++ b/backends/platform/android/events.h
@@ -28,6 +28,7 @@
 // $ANDROID_NDK/platforms/android-9/arch-arm/usr/include/android/keycodes.h
 // http://android.git.kernel.org/?p=platform/frameworks/base.git;a=blob;f=libs/ui/Input.cpp
 // http://android.git.kernel.org/?p=platform/frameworks/base.git;a=blob;f=core/java/android/view/KeyEvent.java
+// https://static.javadoc.io/io.appium/java-client/5.0.4/constant-values.html#io.appium.java_client.android.AndroidKeyCode.FLAG_SOFT_KEYBOARD
 
 // event type
 enum {
@@ -117,10 +118,28 @@ enum {
 
 // meta modifier
 enum {
-	JMETA_SHIFT = 0x01,
-	JMETA_ALT = 0x02,
-	JMETA_SYM = 0x04,
-	JMETA_CTRL = 0x1000
+	JMETA_SHIFT_ON = 0x01,
+	JMETA_ALT_ON = 0x02,
+	JMETA_SYM_ON = 0x04,
+	JMETA_FUNCTION_ON = 0x08,
+	JMETA_ALT_LEFT_ON = 0x10,
+	JMETA_ALT_RIGHT_ON = 0x20,
+	JMETA_SHIFT_LEFT_ON = 0x40,
+	JMETA_SHIFT_RIGHT_ON = 0x80,
+	JMETA_CTRL_ON = 0x1000,
+	JMETA_CTRL_LEFT_ON = 0x2000,
+	JMETA_CTRL_RIGHT_ON = 0x4000,
+	JMETA_META_ON = 0x10000,
+	JMETA_META_LEFT_ON = 0x20000,
+	JMETA_META_RIGHT_ON = 0x40000,
+	JMETA_CAPS_LOCK_ON = 0x100000,
+	JMETA_NUM_LOCK_ON = 0x200000,
+	JMETA_SCROLL_LOCK_ON = 0x400000,
+
+	JMETA_CTRL_MASK = JMETA_CTRL_ON | JMETA_CTRL_LEFT_ON | JMETA_CTRL_RIGHT_ON,
+	JMETA_META_MASK = JMETA_META_ON | JMETA_META_LEFT_ON | JMETA_META_RIGHT_ON,
+	JMETA_SHIFT_MASK = JMETA_SHIFT_ON | JMETA_SHIFT_LEFT_ON | JMETA_SHIFT_RIGHT_ON,
+	JMETA_ALT_MASK = JMETA_ALT_ON | JMETA_ALT_LEFT_ON | JMETA_ALT_RIGHT_ON
 };
 
 // map android key codes to our kbd codes
@@ -144,16 +163,16 @@ static const Common::KeyCode jkeymap[] = {
 	Common::KEYCODE_9, // KEYCODE_9
 	Common::KEYCODE_ASTERISK, // KEYCODE_STAR
 	Common::KEYCODE_HASH, // KEYCODE_POUND
-	Common::KEYCODE_INVALID, // KEYCODE_DPAD_UP
-	Common::KEYCODE_INVALID, // KEYCODE_DPAD_DOWN
-	Common::KEYCODE_INVALID, // KEYCODE_DPAD_LEFT
-	Common::KEYCODE_INVALID, // KEYCODE_DPAD_RIGHT
+	Common::KEYCODE_UP, // KEYCODE_DPAD_UP
+	Common::KEYCODE_DOWN, // KEYCODE_DPAD_DOWN
+	Common::KEYCODE_LEFT, // KEYCODE_DPAD_LEFT
+	Common::KEYCODE_RIGHT, // KEYCODE_DPAD_RIGHT
 	Common::KEYCODE_INVALID, // KEYCODE_DPAD_CENTER
 	Common::KEYCODE_INVALID, // KEYCODE_VOLUME_UP
 	Common::KEYCODE_INVALID, // KEYCODE_VOLUME_DOWN
 	Common::KEYCODE_INVALID, // KEYCODE_POWER
 	Common::KEYCODE_INVALID, // KEYCODE_CAMERA
-	Common::KEYCODE_INVALID, // KEYCODE_CLEAR
+	Common::KEYCODE_CLEAR, // KEYCODE_CLEAR
 	Common::KEYCODE_a, // KEYCODE_A
 	Common::KEYCODE_b, // KEYCODE_B
 	Common::KEYCODE_c, // KEYCODE_C
@@ -207,7 +226,7 @@ static const Common::KeyCode jkeymap[] = {
 	Common::KEYCODE_INVALID, // KEYCODE_HEADSETHOOK
 	Common::KEYCODE_INVALID, // KEYCODE_FOCUS
 	Common::KEYCODE_PLUS, // KEYCODE_PLUS
-	Common::KEYCODE_INVALID, // KEYCODE_MENU
+	Common::KEYCODE_MENU, // KEYCODE_MENU
 	Common::KEYCODE_INVALID, // KEYCODE_NOTIFICATION
 	Common::KEYCODE_INVALID, // KEYCODE_SEARCH
 	Common::KEYCODE_INVALID, // KEYCODE_MEDIA_PLAY_PAUSE
@@ -218,7 +237,174 @@ static const Common::KeyCode jkeymap[] = {
 	Common::KEYCODE_INVALID, // KEYCODE_MEDIA_FAST_FORWARD
 	Common::KEYCODE_INVALID, // KEYCODE_MUTE
 	Common::KEYCODE_PAGEUP, // KEYCODE_PAGE_UP
-	Common::KEYCODE_PAGEDOWN // KEYCODE_PAGE_DOWN
+	Common::KEYCODE_PAGEDOWN, // KEYCODE_PAGE_DOWN
+	Common::KEYCODE_INVALID, // KEYCODE_PICTSYMBOLS
+	Common::KEYCODE_INVALID, // KEYCODE_SWITCH_CHARSET
+	Common::KEYCODE_INVALID, // KEYCODE_BUTTON_A
+	Common::KEYCODE_INVALID, // KEYCODE_BUTTON_B
+	Common::KEYCODE_INVALID, // KEYCODE_BUTTON_C
+	Common::KEYCODE_INVALID, // KEYCODE_BUTTON_X
+	Common::KEYCODE_INVALID, // KEYCODE_BUTTON_Y
+	Common::KEYCODE_INVALID, // KEYCODE_BUTTON_Z
+	Common::KEYCODE_INVALID, // KEYCODE_BUTTON_L1
+	Common::KEYCODE_INVALID, // KEYCODE_BUTTON_R1
+	Common::KEYCODE_INVALID, // KEYCODE_BUTTON_L2
+	Common::KEYCODE_INVALID, // KEYCODE_BUTTON_R2
+	Common::KEYCODE_INVALID, // KEYCODE_BUTTON_THUMBL
+	Common::KEYCODE_INVALID, // KEYCODE_BUTTON_THUMBR
+	Common::KEYCODE_INVALID, // KEYCODE_BUTTON_START
+	Common::KEYCODE_INVALID, // KEYCODE_BUTTON_SELECT
+	Common::KEYCODE_INVALID, // KEYCODE_BUTTON_MODE
+	Common::KEYCODE_ESCAPE, // KEYCODE_ESCAPE
+	Common::KEYCODE_DELETE, // KEYCODE_FORWARD_DEL
+	Common::KEYCODE_LCTRL, // KEYCODE_CTRL_LEFT
+	Common::KEYCODE_RCTRL, // KEYCODE_CTRL_RIGHT
+	Common::KEYCODE_CAPSLOCK, // KEYCODE_CAPS_LOCK
+	Common::KEYCODE_SCROLLOCK, // KEYCODE_SCROLL_LOCK
+	Common::KEYCODE_LSUPER, // KEYCODE_META_LEFT
+	Common::KEYCODE_RSUPER, // KEYCODE_META_RIGHT
+	Common::KEYCODE_INVALID, // KEYCODE_FUNCTION
+	Common::KEYCODE_SYSREQ, // KEYCODE_SYSRQ
+	Common::KEYCODE_BREAK, // KEYCODE_BREAK
+	Common::KEYCODE_HOME, // KEYCODE_MOVE_HOME
+	Common::KEYCODE_END, // KEYCODE_MOVE_END
+	Common::KEYCODE_INSERT, // KEYCODE_INSERT
+	Common::KEYCODE_INVALID, // KEYCODE_FORWARD
+	Common::KEYCODE_INVALID, // KEYCODE_MEDIA_PLAY
+	Common::KEYCODE_INVALID, // KEYCODE_MEDIA_PAUSE
+	Common::KEYCODE_INVALID, // KEYCODE_MEDIA_CLOSE
+	Common::KEYCODE_INVALID, // KEYCODE_MEDIA_EJECT
+	Common::KEYCODE_INVALID, // KEYCODE_MEDIA_RECORD
+	Common::KEYCODE_F1, // KEYCODE_F1
+	Common::KEYCODE_F2, // KEYCODE_F2
+	Common::KEYCODE_F3, // KEYCODE_F3
+	Common::KEYCODE_F4, // KEYCODE_F4
+	Common::KEYCODE_F5, // KEYCODE_F5
+	Common::KEYCODE_F6, // KEYCODE_F6
+	Common::KEYCODE_F7, // KEYCODE_F7
+	Common::KEYCODE_F8, // KEYCODE_F8
+	Common::KEYCODE_F9, // KEYCODE_F9
+	Common::KEYCODE_F10, // KEYCODE_F10
+	Common::KEYCODE_F11, // KEYCODE_F11
+	Common::KEYCODE_F12, // KEYCODE_F12
+	Common::KEYCODE_NUMLOCK, // KEYCODE_NUM_LOCK
+	Common::KEYCODE_KP0, // KEYCODE_NUMPAD_0
+	Common::KEYCODE_KP1, // KEYCODE_NUMPAD_1
+	Common::KEYCODE_KP2, // KEYCODE_NUMPAD_2
+	Common::KEYCODE_KP3, // KEYCODE_NUMPAD_3
+	Common::KEYCODE_KP4, // KEYCODE_NUMPAD_4
+	Common::KEYCODE_KP5, // KEYCODE_NUMPAD_5
+	Common::KEYCODE_KP6, // KEYCODE_NUMPAD_6
+	Common::KEYCODE_KP7, // KEYCODE_NUMPAD_7
+	Common::KEYCODE_KP8, // KEYCODE_NUMPAD_8
+	Common::KEYCODE_KP9, // KEYCODE_NUMPAD_9
+	Common::KEYCODE_KP_DIVIDE, // KEYCODE_NUMPAD_DIVIDE
+	Common::KEYCODE_KP_MULTIPLY, // KEYCODE_NUMPAD_MULTIPLY
+	Common::KEYCODE_KP_MINUS, // KEYCODE_NUMPAD_SUBTRACT
+	Common::KEYCODE_KP_PLUS, // KEYCODE_NUMPAD_ADD
+	Common::KEYCODE_KP_PERIOD, // KEYCODE_NUMPAD_DOT
+	Common::KEYCODE_INVALID, // KEYCODE_NUMPAD_COMMA
+	Common::KEYCODE_KP_ENTER, // KEYCODE_NUMPAD_ENTER
+	Common::KEYCODE_KP_EQUALS, // KEYCODE_NUMPAD_EQUALS
+	Common::KEYCODE_INVALID, // KEYCODE_NUMPAD_LEFT_PAREN
+	Common::KEYCODE_INVALID, // KEYCODE_NUMPAD_RIGHT_PAREN
+	Common::KEYCODE_INVALID, // KEYCODE_VOLUME_MUTE
+	Common::KEYCODE_INVALID, // KEYCODE_INFO
+	Common::KEYCODE_INVALID, // KEYCODE_CHANNEL_UP
+	Common::KEYCODE_INVALID, // KEYCODE_CHANNEL_DOWN
+	Common::KEYCODE_INVALID, // KEYCODE_ZOOM_IN
+	Common::KEYCODE_INVALID, // KEYCODE_ZOOM_OUT
+	Common::KEYCODE_INVALID, // KEYCODE_TV
+	Common::KEYCODE_INVALID, // KEYCODE_WINDOW
+	Common::KEYCODE_INVALID, // KEYCODE_GUIDE
+	Common::KEYCODE_INVALID, // KEYCODE_DVR
+	Common::KEYCODE_INVALID, // KEYCODE_BOOKMARK
+	Common::KEYCODE_INVALID, // KEYCODE_CAPTIONS
+	Common::KEYCODE_INVALID, // KEYCODE_SETTINGS
+	Common::KEYCODE_INVALID, // KEYCODE_TV_POWER
+	Common::KEYCODE_INVALID, // KEYCODE_TV_INPUT
+	Common::KEYCODE_INVALID, // KEYCODE_STB_POWER
+	Common::KEYCODE_INVALID, // KEYCODE_STB_INPUT
+	Common::KEYCODE_INVALID, // KEYCODE_AVR_POWER
+	Common::KEYCODE_INVALID, // KEYCODE_AVR_INPUT
+	Common::KEYCODE_INVALID, // KEYCODE_PROG_RED
+	Common::KEYCODE_INVALID, // KEYCODE_PROG_GREEN
+	Common::KEYCODE_INVALID, // KEYCODE_PROG_YELLOW
+	Common::KEYCODE_INVALID, // KEYCODE_PROG_BLUE
+	Common::KEYCODE_INVALID, // KEYCODE_APP_SWITCH
+	Common::KEYCODE_INVALID, // KEYCODE_BUTTON_1
+	Common::KEYCODE_INVALID, // KEYCODE_BUTTON_2
+	Common::KEYCODE_INVALID, // KEYCODE_BUTTON_3
+	Common::KEYCODE_INVALID, // KEYCODE_BUTTON_4
+	Common::KEYCODE_INVALID, // KEYCODE_BUTTON_5
+	Common::KEYCODE_INVALID, // KEYCODE_BUTTON_6
+	Common::KEYCODE_INVALID, // KEYCODE_BUTTON_7
+	Common::KEYCODE_INVALID, // KEYCODE_BUTTON_8
+	Common::KEYCODE_INVALID, // KEYCODE_BUTTON_9
+	Common::KEYCODE_INVALID, // KEYCODE_BUTTON_10
+	Common::KEYCODE_INVALID, // KEYCODE_BUTTON_11
+	Common::KEYCODE_INVALID, // KEYCODE_BUTTON_12
+	Common::KEYCODE_INVALID, // KEYCODE_BUTTON_13
+	Common::KEYCODE_INVALID, // KEYCODE_BUTTON_14
+	Common::KEYCODE_INVALID, // KEYCODE_BUTTON_15
+	Common::KEYCODE_INVALID, // KEYCODE_BUTTON_16
+	Common::KEYCODE_INVALID, // KEYCODE_LANGUAGE_SWITCH
+	Common::KEYCODE_INVALID, // KEYCODE_MANNER_MODE
+	Common::KEYCODE_INVALID, // KEYCODE_3D_MODE
+	Common::KEYCODE_INVALID, // KEYCODE_CONTACTS
+	Common::KEYCODE_INVALID, // KEYCODE_CALENDAR
+	Common::KEYCODE_INVALID, // KEYCODE_MUSIC
+	Common::KEYCODE_INVALID, // KEYCODE_CALCULATOR
+	Common::KEYCODE_INVALID, // KEYCODE_ZENKAKU_HANKAKU
+	Common::KEYCODE_INVALID, // KEYCODE_EISU
+	Common::KEYCODE_INVALID, // KEYCODE_MUHENKAN
+	Common::KEYCODE_INVALID, // KEYCODE_HENKAN
+	Common::KEYCODE_INVALID, // KEYCODE_KATAKANA_HIRAGANA
+	Common::KEYCODE_INVALID, // KEYCODE_YEN
+	Common::KEYCODE_INVALID, // KEYCODE_RO
+	Common::KEYCODE_INVALID, // KEYCODE_KANA
+	Common::KEYCODE_INVALID, // KEYCODE_ASSIST
+	Common::KEYCODE_INVALID, // KEYCODE_BRIGHTNESS_DOWN
+	Common::KEYCODE_INVALID, // KEYCODE_BRIGHTNESS_UP
+	Common::KEYCODE_INVALID, // KEYCODE_MEDIA_AUDIO_TRACK
+	Common::KEYCODE_INVALID, // KEYCODE_SLEEP
+	Common::KEYCODE_INVALID, // KEYCODE_WAKEUP
+	Common::KEYCODE_INVALID, // KEYCODE_PAIRING
+	Common::KEYCODE_INVALID, // KEYCODE_MEDIA_TOP_MENU
+	Common::KEYCODE_INVALID, // KEYCODE_11
+	Common::KEYCODE_INVALID, // KEYCODE_12
+	Common::KEYCODE_INVALID, // KEYCODE_LAST_CHANNEL
+	Common::KEYCODE_INVALID, // KEYCODE_TV_DATA_SERVICE
+	Common::KEYCODE_INVALID, // KEYCODE_VOICE_ASSIST
+	Common::KEYCODE_INVALID, // KEYCODE_TV_RADIO_SERVICE
+	Common::KEYCODE_INVALID, // KEYCODE_TV_TELETEXT
+	Common::KEYCODE_INVALID, // KEYCODE_TV_NUMBER_ENTRY
+	Common::KEYCODE_INVALID, // KEYCODE_TV_TERRESTRIAL_ANALOG
+	Common::KEYCODE_INVALID, // KEYCODE_TV_TERRESTRIAL_DIGITAL
+	Common::KEYCODE_INVALID, // KEYCODE_TV_SATELLITE
+	Common::KEYCODE_INVALID, // KEYCODE_TV_SATELLITE_BS
+	Common::KEYCODE_INVALID, // KEYCODE_TV_SATELLITE_CS
+	Common::KEYCODE_INVALID, // KEYCODE_TV_SATELLITE_SERVICE
+	Common::KEYCODE_INVALID, // KEYCODE_TV_NETWORK
+	Common::KEYCODE_INVALID, // KEYCODE_TV_ANTENNA_CABLE
+	Common::KEYCODE_INVALID, // KEYCODE_TV_INPUT_HDMI_1
+	Common::KEYCODE_INVALID, // KEYCODE_TV_INPUT_HDMI_2
+	Common::KEYCODE_INVALID, // KEYCODE_TV_INPUT_HDMI_3
+	Common::KEYCODE_INVALID, // KEYCODE_TV_INPUT_HDMI_4
+	Common::KEYCODE_INVALID, // KEYCODE_TV_INPUT_COMPOSITE_1
+	Common::KEYCODE_INVALID, // KEYCODE_TV_INPUT_COMPOSITE_2
+	Common::KEYCODE_INVALID, // KEYCODE_TV_INPUT_COMPONENT_1
+	Common::KEYCODE_INVALID, // KEYCODE_TV_INPUT_COMPONENT_2
+	Common::KEYCODE_INVALID, // KEYCODE_TV_INPUT_VGA_1
+	Common::KEYCODE_INVALID, // KEYCODE_TV_AUDIO_DESCRIPTION
+	Common::KEYCODE_INVALID, // KEYCODE_TV_AUDIO_DESCRIPTION_MIX_UP
+	Common::KEYCODE_INVALID, // KEYCODE_TV_AUDIO_DESCRIPTION_MIX_DOWN
+	Common::KEYCODE_INVALID, // KEYCODE_TV_ZOOM_MODE
+	Common::KEYCODE_INVALID, // KEYCODE_TV_CONTENTS_MENU
+	Common::KEYCODE_INVALID, // KEYCODE_TV_MEDIA_CONTEXT_MENU
+	Common::KEYCODE_INVALID, // KEYCODE_TV_TIMER_PROGRAMMING
+	Common::KEYCODE_HELP // KEYCODE_HELP
+
 };
 
 #endif
diff --git a/backends/platform/android/org/scummvm/scummvm/ScummVMEvents.java b/backends/platform/android/org/scummvm/scummvm/ScummVMEvents.java
index 67609e0..4baadba 100644
--- a/backends/platform/android/org/scummvm/scummvm/ScummVMEvents.java
+++ b/backends/platform/android/org/scummvm/scummvm/ScummVMEvents.java
@@ -178,16 +178,19 @@ public class ScummVMEvents implements
 			return true;
 		}
 
+		int type;
 		switch (keyCode) {
 		case KeyEvent.KEYCODE_DPAD_UP:
 		case KeyEvent.KEYCODE_DPAD_DOWN:
 		case KeyEvent.KEYCODE_DPAD_LEFT:
 		case KeyEvent.KEYCODE_DPAD_RIGHT:
 		case KeyEvent.KEYCODE_DPAD_CENTER:
-			_scummvm.pushEvent(JE_DPAD, action, keyCode,
-								(int)(e.getEventTime() - e.getDownTime()),
-								e.getRepeatCount(), 0, 0);
-			return true;
+			if (e.getSource() == InputDevice.SOURCE_DPAD) {
+				type = JE_DPAD;
+			} else {
+				type = JE_KEY;
+			}
+			break;
 		case KeyEvent.KEYCODE_BUTTON_A:
 		case KeyEvent.KEYCODE_BUTTON_B:
 		case KeyEvent.KEYCODE_BUTTON_C:
@@ -203,15 +206,17 @@ public class ScummVMEvents implements
 		case KeyEvent.KEYCODE_BUTTON_START:
 		case KeyEvent.KEYCODE_BUTTON_SELECT:
 		case KeyEvent.KEYCODE_BUTTON_MODE:
-			_scummvm.pushEvent(JE_GAMEPAD, action, keyCode,
-								(int)(e.getEventTime() - e.getDownTime()),
-								e.getRepeatCount(), 0, 0);
-			return true;
+			type = JE_GAMEPAD;
+			break;
+		default:
+			type = JE_KEY;
+			break;
 		}
 
-		_scummvm.pushEvent(JE_KEY, action, keyCode,
+		_scummvm.pushEvent(type, action, keyCode,
 					e.getUnicodeChar() & KeyCharacterMap.COMBINING_ACCENT_MASK,
-					e.getMetaState(), e.getRepeatCount(), 0);
+					e.getMetaState(), e.getRepeatCount(),
+					(int)(e.getEventTime() - e.getDownTime()));
 
 		return true;
 	}


Commit: f23c07b3ef956722616a82cb6a3adfc01a2b12e9
    https://github.com/scummvm/scummvm/commit/f23c07b3ef956722616a82cb6a3adfc01a2b12e9
Author: Thanasis Antoniou (a.antoniou79 at gmail.com)
Date: 2020-01-13T14:29:30+02:00

Commit Message:
ANDROID: Long press for back button has an alternate (menu button) function

Changed paths:
    backends/platform/android/events.cpp
    backends/platform/android/org/scummvm/scummvm/ScummVMEvents.java


diff --git a/backends/platform/android/events.cpp b/backends/platform/android/events.cpp
index 4503d1e..f01bb80 100644
--- a/backends/platform/android/events.cpp
+++ b/backends/platform/android/events.cpp
@@ -97,7 +97,7 @@ void OSystem_Android::pushEvent(int type, int arg1, int arg2, int arg3,
 
 		switch (arg2) {
 
-		// special case. we'll only get it's up event
+		// special case. we'll only get its key-up event
 		case JKEYCODE_BACK:
 			if (_swap_menu_and_back) {
 				e.type = Common::EVENT_MAINMENU;
@@ -109,7 +109,7 @@ void OSystem_Android::pushEvent(int type, int arg1, int arg2, int arg3,
 			}
 			return;
 
-		// special case. we'll only get it's up event
+		// special case. we'll only get its key-up event
 		case JKEYCODE_MENU:
 			if (_swap_menu_and_back) {
 				e.kbd.keycode = Common::KEYCODE_ESCAPE;
diff --git a/backends/platform/android/org/scummvm/scummvm/ScummVMEvents.java b/backends/platform/android/org/scummvm/scummvm/ScummVMEvents.java
index 4baadba..4da87de 100644
--- a/backends/platform/android/org/scummvm/scummvm/ScummVMEvents.java
+++ b/backends/platform/android/org/scummvm/scummvm/ScummVMEvents.java
@@ -72,17 +72,20 @@ public class ScummVMEvents implements
 		return false;
 	}
 
-	final static int MSG_MENU_LONG_PRESS = 1;
+	final static int MSG_SMENU_LONG_PRESS = 1;
+	final static int MSG_SBACK_LONG_PRESS = 1;
 
 	final private Handler keyHandler = new Handler() {
 		@Override
 		public void handleMessage(Message msg) {
-			if (msg.what == MSG_MENU_LONG_PRESS) {
+			if (msg.what == MSG_SMENU_LONG_PRESS) {
 				InputMethodManager imm = (InputMethodManager)
 					_context.getSystemService(Context.INPUT_METHOD_SERVICE);
 
 				if (imm != null)
 					imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0);
+			} else if (msg.what == MSG_SBACK_LONG_PRESS) {
+				_scummvm.pushEvent(JE_SYS_KEY, KeyEvent.ACTION_UP, KeyEvent.KEYCODE_MENU, 0, 0, 0, 0);
 			}
 		}
 	};
@@ -98,15 +101,9 @@ public class ScummVMEvents implements
 		}
 
 		if (keyCode == KeyEvent.KEYCODE_BACK) {
-			if (action != KeyEvent.ACTION_UP) {
-				// only send event from back button on up event, since down event is sent on right mouse click and
-				// cannot be caught (thus rmb click would send escape key first)
-				return true;
-			}
-
 			if (_mouseHelper != null) {
 				if (_mouseHelper.getRmbGuard()) {
-					// right mouse button was just clicked which sends an extra back button press
+					// right mouse button was just clicked which sends an extra back button press (which should be ignored)
 					return true;
 				}
 			}
@@ -135,24 +132,43 @@ public class ScummVMEvents implements
 			// ourselves, since we are otherwise hijacking the menu key :(
 			// See com.android.internal.policy.impl.PhoneWindow.onKeyDownPanel()
 			// for the usual Android implementation of this feature.
-			if (keyCode == KeyEvent.KEYCODE_MENU) {
-				final boolean fired =
-					!keyHandler.hasMessages(MSG_MENU_LONG_PRESS);
+			//
+			// We adopt a similar behavior for the Back system button, as well.
+			if (keyCode == KeyEvent.KEYCODE_MENU || keyCode == KeyEvent.KEYCODE_BACK) {
+				//
+				// Upon pressing the system menu or system back key:
+				// (The example below assumes that system Back key was pressed)
+				// 1. keyHandler.hasMessages(MSG_SBACK_LONG_PRESS) = false, and thus: fired = true
+				// 2. Action will be KeyEvent.ACTION_DOWN, so a delayed message "MSG_SBACK_LONG_PRESS" will be sent to keyHandler after _longPress time
+				//    The "MSG_SBACK_LONG_PRESS" will be handled (and removed) in the keyHandler.
+				//    For the Back button, the keyHandler should forward a ACTION_UP for MENU (the alternate func of Back key!) to native)
+				//    But if the code enters this section before the "MSG_SBACK_LONG_PRESS" was handled in keyHandler (probably due to a ACTION_UP)
+				//        then fired = false and the message is removed from keyHandler, meaning we should treat the button press as a SHORT key press
+				final int typeOfLongPressMessage;
+				if (keyCode == KeyEvent.KEYCODE_MENU) {
+					typeOfLongPressMessage = MSG_SMENU_LONG_PRESS;
+				} else { // back button
+					typeOfLongPressMessage = MSG_SBACK_LONG_PRESS;
+				}
 
-				keyHandler.removeMessages(MSG_MENU_LONG_PRESS);
+				final boolean fired = !keyHandler.hasMessages(typeOfLongPressMessage);
+
+				keyHandler.removeMessages(typeOfLongPressMessage);
 
 				if (action == KeyEvent.ACTION_DOWN) {
 					keyHandler.sendMessageDelayed(keyHandler.obtainMessage(
-									MSG_MENU_LONG_PRESS), _longPress);
+									typeOfLongPressMessage), _longPress);
 					return true;
 				}
 
-				if (fired)
+				if (fired) {
 					return true;
+				}
 
-				// only send up events of the menu button to the native side
-				if (action != KeyEvent.ACTION_UP)
+				// only send up events of the menu or back button to the native side
+				if (action != KeyEvent.ACTION_UP) {
 					return true;
+				}
 			}
 
 			_scummvm.pushEvent(JE_SYS_KEY, action, keyCode, 0, 0, 0, 0);


Commit: 45cc824813a68190bfe936bb9e310c5484deb3e9
    https://github.com/scummvm/scummvm/commit/45cc824813a68190bfe936bb9e310c5484deb3e9
Author: Thanasis Antoniou (a.antoniou79 at gmail.com)
Date: 2020-01-13T14:29:37+02:00

Commit Message:
ANDROID: Fix bad message id for long press back btn

Changed paths:
    backends/platform/android/org/scummvm/scummvm/ScummVMEvents.java


diff --git a/backends/platform/android/org/scummvm/scummvm/ScummVMEvents.java b/backends/platform/android/org/scummvm/scummvm/ScummVMEvents.java
index 4da87de..9c8dd16 100644
--- a/backends/platform/android/org/scummvm/scummvm/ScummVMEvents.java
+++ b/backends/platform/android/org/scummvm/scummvm/ScummVMEvents.java
@@ -73,12 +73,14 @@ public class ScummVMEvents implements
 	}
 
 	final static int MSG_SMENU_LONG_PRESS = 1;
-	final static int MSG_SBACK_LONG_PRESS = 1;
+	final static int MSG_SBACK_LONG_PRESS = 2;
 
 	final private Handler keyHandler = new Handler() {
 		@Override
 		public void handleMessage(Message msg) {
 			if (msg.what == MSG_SMENU_LONG_PRESS) {
+				// this displays the android keyboard (see showVirtualKeyboard() in ScummVMActivity.java)
+				// when menu key is long-pressed
 				InputMethodManager imm = (InputMethodManager)
 					_context.getSystemService(Context.INPUT_METHOD_SERVICE);
 


Commit: 3ba8a1f3df394aa7ce03b496f5ca0e2ea74d11bb
    https://github.com/scummvm/scummvm/commit/3ba8a1f3df394aa7ce03b496f5ca0e2ea74d11bb
Author: Thanasis Antoniou (a.antoniou79 at gmail.com)
Date: 2020-01-13T14:29:46+02:00

Commit Message:
ANDROID: Fix crash due to adding '.' folder in SearchManager

Changed paths:
    backends/fs/posix/posix-fs-factory.cpp
    backends/platform/android/asset-archive.cpp
    common/archive.cpp


diff --git a/backends/fs/posix/posix-fs-factory.cpp b/backends/fs/posix/posix-fs-factory.cpp
index 9495992..e0eebbd 100644
--- a/backends/fs/posix/posix-fs-factory.cpp
+++ b/backends/fs/posix/posix-fs-factory.cpp
@@ -41,8 +41,22 @@ AbstractFSNode *POSIXFilesystemFactory::makeRootFileNode() const {
 }
 
 AbstractFSNode *POSIXFilesystemFactory::makeCurrentDirectoryFileNode() const {
+#if defined(__ANDROID__)
+	// For Android it does not make sense to have "." in Search Manager as a current directory file node, so we skip it here
+	// Otherwise this can potentially lead to a crash since, in Android getcwd() returns the root path "/"
+	// and when SearchMan is used (eg. SearchSet::createReadStreamForMember) and it tries to search root path (and calls POSIXFilesystemNode::getChildren())
+	// then a JNI call is made (JNI::getAllStorageLocations()) which leads to a crash if done from the wrong context -- and is also useless overhead as a call in that case.
+	// This fixes the error case: Loading "Beneath A Steel Sky" with Adlib or FluidSynth audio once, exiting to Launcher via in-game ScummVM menu and re-launching the game.
+	// Don't return NULL here, since it causes crash with other engines (eg. Blade Runner)
+	// Returning '.' here will cause POSIXFilesystemNode::getChildren() to ignore it
+	//
+	// We also skip adding the '.' directory to SearchManager (in archive.cpp, SearchManager::clear())
+	char buf[MAXPATHLEN] = {'.', '\0'};
+	return new POSIXFilesystemNode(buf);
+#else
 	char buf[MAXPATHLEN];
 	return getcwd(buf, MAXPATHLEN) ? new POSIXFilesystemNode(buf) : NULL;
+#endif
 }
 
 AbstractFSNode *POSIXFilesystemFactory::makeFileNodePath(const Common::String &path) const {
diff --git a/backends/platform/android/asset-archive.cpp b/backends/platform/android/asset-archive.cpp
index 44e5ed8..255262d 100644
--- a/backends/platform/android/asset-archive.cpp
+++ b/backends/platform/android/asset-archive.cpp
@@ -71,6 +71,9 @@ AssetInputStream::AssetInputStream(AAssetManager *as, const Common::String &path
 }
 
 AssetInputStream::~AssetInputStream() {
+	if (_asset != NULL) {
+		AAsset_close(_asset);
+	}
 }
 
 void AssetInputStream::close() {
diff --git a/common/archive.cpp b/common/archive.cpp
index 7e18965..71c4eb2 100644
--- a/common/archive.cpp
+++ b/common/archive.cpp
@@ -279,9 +279,12 @@ void SearchManager::clear() {
 	if (g_system)
 		g_system->addSysArchivesToSearchSet(*this, -1);
 
+#ifndef __ANDROID__
 	// Add the current dir as a very last resort.
 	// See also bug #2137680.
+	// But don't do this for Android platform, since it may lead to crashes
 	addDirectory(".", ".", -2);
+#endif
 }
 
 DECLARE_SINGLETON(SearchManager);




More information about the Scummvm-git-logs mailing list