summaryrefslogtreecommitdiff
path: root/build
diff options
context:
space:
mode:
authorsapier <Sapier at GMX dot net>2014-04-21 14:10:59 +0200
committersapier <Sapier at GMX dot net>2014-06-29 18:17:56 +0200
commit1cc40c0a7c260f0562572bc99f39a666a12f1b09 (patch)
treec5af6b9787f4c69faa634e82f6484ca4540a7f88 /build
parentff36071d93266c1dd18708f8924d80aa1af5b33e (diff)
downloadminetest-1cc40c0a7c260f0562572bc99f39a666a12f1b09.tar.gz
minetest-1cc40c0a7c260f0562572bc99f39a666a12f1b09.tar.bz2
minetest-1cc40c0a7c260f0562572bc99f39a666a12f1b09.zip
Add support for Android 2.3+
There have been plenty of ppl involved in creating this version. I don't wanna mention names as I'm sure I'd forget someone so I just tell where help has been done: - The partial android versions done by various ppl - Testing on different android devices - reviewing code (especially the in core changes) - testing controls - reviewing texts A big thank you to everyone helping this to be completed!
Diffstat (limited to 'build')
-rw-r--r--build/android/AndroidManifest.xml.template35
-rw-r--r--build/android/Makefile724
-rw-r--r--build/android/build.xml16
-rw-r--r--build/android/irrlicht-back_button.patch19
-rw-r--r--build/android/irrlicht-texturehack.patch240
-rw-r--r--build/android/irrlicht-touchcount.patch30
-rw-r--r--build/android/jni/Android.mk310
-rw-r--r--build/android/jni/Application.mk8
-rw-r--r--build/android/libvorbis-libogg-fpu.patch37
-rw-r--r--build/android/project.properties1
-rw-r--r--build/android/res/drawable-hdpi/irr_icon.pngbin0 -> 5490 bytes
-rw-r--r--build/android/res/drawable-ldpi/irr_icon.pngbin0 -> 2262 bytes
-rw-r--r--build/android/res/drawable-mdpi/irr_icon.pngbin0 -> 3110 bytes
-rw-r--r--build/android/res/drawable-xhdpi/irr_icon.pngbin0 -> 7610 bytes
-rw-r--r--build/android/res/layout/assetcopy.xml22
-rw-r--r--build/android/res/values/styles.xml11
-rw-r--r--build/android/src/org/minetest/minetest/MinetestAssetCopy.java288
-rw-r--r--build/android/src/org/minetest/minetest/MinetestTextEntry.java91
-rw-r--r--build/android/src/org/minetest/minetest/MtNativeActivity.java93
19 files changed, 1925 insertions, 0 deletions
diff --git a/build/android/AndroidManifest.xml.template b/build/android/AndroidManifest.xml.template
new file mode 100644
index 000000000..dcffa35f6
--- /dev/null
+++ b/build/android/AndroidManifest.xml.template
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="org.minetest.minetest"
+ android:versionCode="###ANDROID_VERSION###"
+ android:versionName="###BASE_VERSION###.###ANDROID_VERSION###"
+ android:installLocation="auto">
+ <uses-sdk android:minSdkVersion="9"/>
+ <uses-feature android:glEsVersion="0x00010000" android:required="true"/>
+ <uses-permission android:name="android.permission.INTERNET" />
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ ###DEBUG_BUILD###
+ <application android:icon="@drawable/irr_icon" android:label="Minetest" android:theme="@android:style/Theme.NoTitleBar.Fullscreen" ###DEBUG_FLAG###>
+ <activity android:name=".MtNativeActivity"
+ android:label="Minetest"
+ android:launchMode="singleTask"
+ android:configChanges="orientation|keyboardHidden"
+ android:screenOrientation="landscape"
+ android:clearTaskOnLaunch="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ <meta-data android:name="android.app.lib_name" android:value="minetest" />
+ </activity>
+ <activity android:name=".MinetestTextEntry"
+ android:theme="@style/Theme.Transparent"
+ android:excludeFromRecents="true">
+ </activity>
+ <activity android:name=".MinetestAssetCopy"
+ android:theme="@style/Theme.Transparent"
+ android:excludeFromRecents="true">
+ </activity>
+ </application>
+</manifest>
diff --git a/build/android/Makefile b/build/android/Makefile
new file mode 100644
index 000000000..9e693432b
--- /dev/null
+++ b/build/android/Makefile
@@ -0,0 +1,724 @@
+# build options
+
+OS := $(shell uname)
+
+#automaticaly set number of jobs
+ifeq ($(OS),Linux)
+ PARALLEL := $(shell grep -c ^processor /proc/cpuinfo)
+else
+ PARALLEL := 1
+endif
+
+# compile with GPROF
+# GPROF = 1
+
+# build for build platform
+APP_PLATFORM = android-9
+
+# paths used for timestaps, dependencys, tree config and libs
+PATHCFGFILE = path.cfg
+
+ROOT = $(shell pwd)
+
+################################################################################
+# Android Version code
+# Increase for each build!
+################################################################################
+ANDROID_VERSION_CODE = 3
+
+################################################################################
+# toolchain config for arm old processors
+################################################################################
+TARGET_HOST = arm-linux
+TARGET_ABI = armeabi
+TARGET_LIBDIR = armeabi
+TARGET_TOOLCHAIN = arm-linux-androideabi-
+TARGET_CFLAGS_ADDON = -mfloat-abi=softfp -mfpu=vfp
+CROSS_PREFIX = arm-linux-androideabi-
+COMPILER_VERSION = 4.8
+
+################################################################################
+# toolchain config for arm new processors
+################################################################################
+#TARGET_HOST = arm-linux
+#TARGET_ABI = armeabi-v7a-hard
+#TARGET_LIBDIR = armeabi-v7a
+#TARGET_TOOLCHAIN = arm-linux-androideabi-
+#TARGET_CFLAGS_ADDON = -mfpu=vfpv3-d16 -D_NDK_MATH_NO_SOFTFP=1 \
+# -mfloat-abi=hard -march=armv7-a
+#TARGET_CXXFLAGS_ADDON = $(TARGET_CFLAGS_ADDON)
+#TARGET_LDFLAGS_ADDON = -Wl,--no-warn-mismatch -lm_hard
+#CROSS_PREFIX = arm-linux-androideabi-
+#COMPILER_VERSION = 4.8
+
+################################################################################
+# toolchain config for little endian mips
+################################################################################
+#TARGET_HOST = mipsel-linux
+#TARGET_ABI = mips
+#TARGET_LIBDIR = mips
+#TARGET_TOOLCHAIN = mipsel-linux-android-
+#CROSS_PREFIX = mipsel-linux-android-
+#COMPILER_VERSION = 4.8
+
+################################################################################
+# toolchain config for x86
+################################################################################
+#TARGET_HOST = x86-linux
+#TARGET_ABI = x86
+#TARGET_LIBDIR = x86
+#TARGET_TOOLCHAIN = x86-
+#CROSS_PREFIX = i686-linux-android-
+#COMPILER_VERSION = 4.8
+
+################################################################################
+ASSETS_TIMESTAMP = deps/assets_timestamp
+
+LEVELDB_DIR = $(ROOT)/deps/leveldb/
+LEVELDB_LIB = $(LEVELDB_DIR)libleveldb.a
+LEVELDB_TIMESTAMP = $(LEVELDB_DIR)/timestamp
+LEVELDB_TIMESTAMP_INT = $(ROOT)/deps/leveldb_timestamp
+LEVELDB_URL_GIT = https://code.google.com/p/leveldb/
+
+OPENAL_DIR = $(ROOT)/deps/openal-soft/
+OPENAL_LIB = $(OPENAL_DIR)libs/$(TARGET_ABI)/libopenal.so
+OPENAL_TIMESTAMP = $(OPENAL_DIR)/timestamp
+OPENAL_TIMESTAMP_INT = $(ROOT)/deps/openal_timestamp
+OPENAL_URL_GIT = https://github.com/apportable/openal-soft
+
+OGG_DIR = $(ROOT)/deps/libvorbis-libogg-android/
+OGG_LIB = $(OGG_DIR)libs/$(TARGET_ABI)/libogg.so
+VORBIS_LIB = $(OGG_DIR)libs/$(TARGET_ABI)/libogg.so
+OGG_TIMESTAMP = $(OGG_DIR)timestamp
+OGG_TIMESTAMP_INT = $(ROOT)/deps/ogg_timestamp
+OGG_URL_GIT = https://github.com/vincentjames501/libvorbis-libogg-android
+
+IRRLICHT_DIR = $(ROOT)/deps/irrlicht/
+IRRLICHT_LIB = $(IRRLICHT_DIR)lib/Android/libIrrlicht.a
+IRRLICHT_TIMESTAMP = $(IRRLICHT_DIR)timestamp
+IRRLICHT_TIMESTAMP_INT = $(ROOT)/deps/irrlicht_timestamp
+IRRLICHT_URL_SVN = http://svn.code.sf.net/p/irrlicht/code/branches/ogl-es/
+
+OPENSSL_BASEDIR = openssl-android
+OPENSSL_DIR = $(ROOT)/deps/$(OPENSSL_BASEDIR)/
+OPENSSL_LIB = $(OPENSSL_DIR)libs/$(TARGET_ABI)/libopenssl.so
+OPENSSL_TIMESTAMP = $(OPENSSL_DIR)timestamp
+OPENSSL_TIMESTAMP_INT = $(ROOT)/deps/openssl_timestamp
+OPENSSL_URL_GIT = https://github.com/wobbals/openssl-android
+
+CURL_VERSION = 7.35.0
+CURL_DIR = $(ROOT)/deps/curl-$(CURL_VERSION)
+CURL_LIB = $(CURL_DIR)/lib/.libs/libcurl.a
+CURL_TIMESTAMP = $(CURL_DIR)/timestamp
+CURL_TIMESTAMP_INT = $(ROOT)/deps/curl_timestamp
+CURL_URL_HTTP = http://curl.haxx.se/download/curl-${CURL_VERSION}.tar.bz2
+
+FREETYPE_DIR = $(ROOT)/deps/freetype2-android/
+FREETYPE_LIB = $(FREETYPE_DIR)/Android/obj/local/$(TARGER_ABI)/libfreetype2-static.a
+FREETYPE_TIMESTAMP = $(FREETYPE_DIR)timestamp
+FREETYPE_TIMESTAMP_INT = $(ROOT)/deps/freetype_timestamp
+FREETYPE_URL_GIT = https://github.com/cdave1/freetype2-android
+
+-include $(PATHCFGFILE)
+
+.PHONY : debug release reconfig delconfig \
+ leveldb_download clean_leveldb leveldb\
+ irrlicht_download clean_irrlicht irrlicht \
+ clean_assets assets \
+ freetype_download clean_freetype freetype \
+ apk clean_apk \
+ clean_all clean prep_srcdir \
+ install_debug install envpaths all \
+ manifest clean_manifest\
+ $(ASSETS_TIMESTAMP) $(LEVELDB_TIMESTAMP) \
+ $(OPENAL_TIMESTAMP) $(OGG_TIMESTAMP) \
+ $(IRRLICHT_TIMESTAMP) $(CURL_TIMESTAMP) \
+ $(OPENSSL_TIMESTAMP) curl_binary \
+ $(ROOT)/jni/src/android_version.h
+
+debug : $(PATHCFGFILE)
+ export NDEBUG=; \
+ export BUILD_TYPE=debug; \
+ $(MAKE) -j${PARALLEL} apk
+
+all : debug release
+
+release : $(PATHCFGFILE)
+ @export NDEBUG=1; \
+ export BUILD_TYPE=release; \
+ $(MAKE) -j${PARALLEL} apk
+
+reconfig: delconfig
+ @$(MAKE) -j${PARALLEL} $(PATHCFGFILE)
+
+delconfig :
+ $(RM) ${PATHCFGFILE}
+
+$(PATHCFGFILE) :
+ @echo "Please specify path of ANDROID NDK"; \
+ echo "e.g. /home/user/android-ndk-r9c/"; \
+ read ANDROID_NDK ; \
+ if [ ! -d $$ANDROID_NDK ] ; then \
+ echo "$$ANDROID_NDK is not a valid folder"; \
+ exit 1; \
+ fi; \
+ echo "ANDROID_NDK = $$ANDROID_NDK" > ${PATHCFGFILE}; \
+ echo "NDK_MODULE_PATH = $$ANDROID_NDK/tools" >> ${PATHCFGFILE}; \
+ echo "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++";\
+ echo "+ Note: NDK_MODULE_PATH is set to $$ANDROID_NDK/tools"; \
+ echo "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++";\
+ echo "Please specify path of ANDROID SDK"; \
+ echo "e.g. /home/user/adt-bundle-linux-x86_64-20131030/sdk/"; \
+ read SDKFLDR ; \
+ if [ ! -d $$SDKFLDR ] ; then \
+ echo "$$SDKFLDR is not a valid folder"; \
+ exit 1; \
+ fi; \
+ echo "SDKFOLDER = $$SDKFLDR" >> ${PATHCFGFILE};
+
+$(OPENAL_TIMESTAMP) : openal_download
+ @LAST_MODIF=$$(find ${OPENAL_DIR} -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -f2- -d" "); \
+ if [ $$(basename $$LAST_MODIF) != "timestamp" ] ; then \
+ touch ${OPENAL_TIMESTAMP}; \
+ fi
+
+openal_download :
+ @if [ ! -d ${OPENAL_DIR} ] ; then \
+ echo "openal sources missing, downloading..."; \
+ mkdir -p ${ROOT}/deps; \
+ cd ${ROOT}/deps ; \
+ git clone ${OPENAL_URL_GIT} || exit 1; \
+ fi
+
+openal : $(OPENAL_LIB)
+
+$(OPENAL_LIB): $(OPENAL_TIMESTAMP)
+ @REFRESH=0; \
+ if [ ! -e ${OPENAL_TIMESTAMP_INT} ] ; then \
+ REFRESH=1; \
+ fi; \
+ if [ ${OPENAL_TIMESTAMP} -nt ${OPENAL_TIMESTAMP_INT} ] ; then \
+ REFRESH=1; \
+ fi; \
+ if [ $$REFRESH -ne 0 ] ; then \
+ export PATH=$$PATH:${SDKFOLDER}/platform-tools:${ANDROID_NDK}; \
+ echo "changed timestamp for openal detected building..."; \
+ cd ${OPENAL_DIR}; \
+ ndk-build NDEBUG=${NDEBUG} NDK_MODULE_PATH=${NDK_MODULE_PATH} \
+ APP_ABI=${TARGET_ABI} APP_PLATFORM=${APP_PLATFORM} -j${PARALLEL} \
+ TARGET_CFLAGS+="${TARGET_CFLAGS_ADDON}" \
+ TARGET_LDFLAGS+="${TARGET_LDFLAGS_ADDON}" \
+ TARGET_CXXFLAGS+="${TARGET_CXXFLAGS_ADDON}" || exit 1; \
+ touch ${OPENAL_TIMESTAMP}; \
+ touch ${OPENAL_TIMESTAMP_INT}; \
+ else \
+ echo "nothing to be done for openal"; \
+ fi
+
+clean_openal :
+ $(RM) -rf ${OPENAL_DIR}
+
+$(OGG_TIMESTAMP) : ogg_download
+ @LAST_MODIF=$$(find ${OGG_DIR} -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -f2- -d" "); \
+ if [ $$(basename $$LAST_MODIF) != "timestamp" ] ; then \
+ touch ${OGG_TIMESTAMP}; \
+ fi
+
+ogg_download :
+ @if [ ! -d ${OGG_DIR} ] ; then \
+ echo "ogg sources missing, downloading..."; \
+ mkdir -p ${ROOT}/deps; \
+ cd ${ROOT}/deps ; \
+ git clone ${OGG_URL_GIT}|| exit 1; \
+ cd libvorbis-libogg-android ; \
+ patch -p1 < ../../libvorbis-libogg-fpu.patch || exit 1; \
+ fi
+
+ogg : $(OGG_LIB)
+
+$(OGG_LIB): $(OGG_TIMESTAMP)
+ @REFRESH=0; \
+ if [ ! -e ${OGG_TIMESTAMP_INT} ] ; then \
+ echo "${OGG_TIMESTAMP_INT} doesn't exist"; \
+ REFRESH=1; \
+ fi; \
+ if [ ${OGG_TIMESTAMP} -nt ${OGG_TIMESTAMP_INT} ] ; then \
+ REFRESH=1; \
+ fi; \
+ if [ $$REFRESH -ne 0 ] ; then \
+ export PATH=$$PATH:${SDKFOLDER}/platform-tools:${ANDROID_NDK}; \
+ echo "changed timestamp for ogg detected building..."; \
+ cd ${OGG_DIR}; \
+ ndk-build NDEBUG=${NDEBUG} NDK_MODULE_PATH=${NDK_MODULE_PATH} \
+ APP_ABI=${TARGET_ABI} APP_PLATFORM=${APP_PLATFORM} -j${PARALLEL} \
+ TARGET_CFLAGS+="${TARGET_CFLAGS_ADDON}" \
+ TARGET_LDFLAGS+="${TARGET_LDFLAGS_ADDON}" \
+ TARGET_CXXFLAGS+="${TARGET_CXXFLAGS_ADDON}" || exit 1; \
+ touch ${OGG_TIMESTAMP}; \
+ touch ${OGG_TIMESTAMP_INT}; \
+ else \
+ echo "nothing to be done for libogg/libvorbis"; \
+ fi
+
+clean_ogg :
+ $(RM) -rf ${OGG_DIR}
+
+$(OPENSSL_TIMESTAMP) : openssl_download
+ @LAST_MODIF=$$(find ${OPENSSL_DIR} -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -f2- -d" "); \
+ if [ $$(basename $$LAST_MODIF) != "timestamp" ] ; then \
+ touch ${OPENSSL_TIMESTAMP}; \
+ fi
+
+openssl_download :
+ @if [ ! -d ${OPENSSL_DIR} ] ; then \
+ echo "openssl sources missing, downloading..."; \
+ mkdir -p ${ROOT}/deps; \
+ cd ${ROOT}/deps ; \
+ git clone ${OPENSSL_URL_GIT} || exit 1; \
+ fi
+
+openssl : $(OPENSSL_LIB)
+
+$(OPENSSL_LIB): $(OPENSSL_TIMESTAMP)
+ @REFRESH=0; \
+ if [ ! -e ${OPENSSL_TIMESTAMP_INT} ] ; then \
+ echo "${OPENSSL_TIMESTAMP_INT} doesn't exist"; \
+ REFRESH=1; \
+ fi; \
+ if [ ${OPENSSL_TIMESTAMP} -nt ${OPENSSL_TIMESTAMP_INT} ] ; then \
+ REFRESH=1; \
+ fi; \
+ if [ $$REFRESH -ne 0 ] ; then \
+ export PATH=$$PATH:${SDKFOLDER}/platform-tools:${ANDROID_NDK}; \
+ echo "changed timestamp for openssl detected building..."; \
+ cd ${OPENSSL_DIR}; \
+ cat jni/Application.mk | grep -v NDK_TOOLCHAIN_VERSION >jni/Application.mk.new;\
+ mv jni/Application.mk.new jni/Application.mk; \
+ ndk-build NDEBUG=${NDEBUG} NDK_MODULE_PATH=${NDK_MODULE_PATH} \
+ APP_ABI=${TARGET_ABI} APP_PLATFORM=${APP_PLATFORM} -j${PARALLEL} \
+ TARGET_CFLAGS+="${TARGET_CFLAGS_ADDON}" \
+ TARGET_LDFLAGS+="${TARGET_LDFLAGS_ADDON}" \
+ TARGET_CXXFLAGS+="${TARGET_CXXFLAGS_ADDON}" || exit 1; \
+ touch ${OPENSSL_TIMESTAMP}; \
+ touch ${OPENSSL_TIMESTAMP_INT}; \
+ else \
+ echo "nothing to be done for openssl"; \
+ fi
+
+clean_openssl :
+ $(RM) -rf ${OPENSSL_DIR}
+
+$(LEVELDB_TIMESTAMP) : leveldb_download
+ @LAST_MODIF=$$(find ${LEVELDB_DIR} -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -f2- -d" "); \
+ if [ $$(basename $$LAST_MODIF) != "timestamp" ] ; then \
+ touch ${LEVELDB_TIMESTAMP}; \
+ fi
+
+leveldb_download :
+ @if [ ! -d ${LEVELDB_DIR} ] ; then \
+ echo "leveldb sources missing, downloading..."; \
+ mkdir -p ${ROOT}/deps; \
+ cd ${ROOT}/deps ; \
+ git clone ${LEVELDB_URL_GIT} || exit 1; \
+ fi
+
+leveldb : $(LEVELDB_LIB)
+
+$(LEVELDB_LIB): $(LEVELDB_TIMESTAMP)
+ @REFRESH=0; \
+ if [ ! -e ${LEVELDB_TIMESTAMP_INT} ] ; then \
+ REFRESH=1; \
+ fi; \
+ if [ ${LEVELDB_TIMESTAMP} -nt ${LEVELDB_TIMESTAMP_INT} ] ; then \
+ REFRESH=1; \
+ fi; \
+ if [ $$REFRESH -ne 0 ] ; then \
+ export PATH=$${PATH}:${SDKFOLDER}/platform-tools:${ANDROID_NDK}; \
+ echo "changed timestamp for leveldb detected building..."; \
+ cd deps/leveldb; \
+ export CROSS_PREFIX=${CROSS_PREFIX}; \
+ export TOOLCHAIN=/tmp/ndk-arm; \
+ ${ANDROID_NDK}/build/tools/make-standalone-toolchain.sh \
+ --toolchain=${TARGET_TOOLCHAIN}${COMPILER_VERSION} \
+ --install-dir=$${TOOLCHAIN} --system=linux-x86_64; \
+ export PATH="$${TOOLCHAIN}/bin:$${PATH}"; \
+ export CC=${CROSS_PREFIX}gcc; \
+ export CXX=${CROSS_PREFIX}g++; \
+ export CFLAGS="$${CFLAGS} ${TARGET_CFLAGS_ADDON}"; \
+ export CPPFLAGS="$${CPPFLAGS} ${TARGET_CFLAGS_ADDON}"; \
+ export LDFLAGS="$${LDFLAGS} ${TARGET_LDFLAGS_ADDON}"; \
+ export TARGET_OS=OS_ANDROID_CROSSCOMPILE; \
+ $(MAKE) -j${PARALLEL} -s || exit 1; \
+ touch ${LEVELDB_TIMESTAMP}; \
+ touch ${LEVELDB_TIMESTAMP_INT}; \
+ else \
+ echo "nothing to be done for leveldb"; \
+ fi
+
+clean_leveldb :
+ $(RM) -rf deps/leveldb
+
+$(FREETYPE_TIMESTAMP) : freetype_download
+ @LAST_MODIF=$$(find ${FREETYPE_DIR} -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -f2- -d" "); \
+ if [ $$(basename $$LAST_MODIF) != "timestamp" ] ; then \
+ touch ${FREETYPE_TIMESTAMP}; \
+ fi
+
+freetype_download :
+ @if [ ! -d ${FREETYPE_DIR} ] ; then \
+ echo "freetype sources missing, downloading..."; \
+ mkdir -p ${ROOT}/deps; \
+ cd deps; \
+ git clone ${FREETYPE_URL_GIT} || exit 1; \
+ fi
+
+$(IRRLICHT_TIMESTAMP) : irrlicht_download
+ @LAST_MODIF=$$(find ${IRRLICHT_DIR} -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -f2- -d" "); \
+ if [ $$(basename $$LAST_MODIF) != "timestamp" ] ; then \
+ touch ${IRRLICHT_TIMESTAMP}; \
+ fi
+
+freetype : $(FREETYPE_LIB)
+
+$(FREETYPE_LIB) : $(FREETYPE_TIMESTAMP)
+ @REFRESH=0; \
+ if [ ! -e ${FREETYPE_TIMESTAMP_INT} ] ; then \
+ REFRESH=1; \
+ fi; \
+ if [ ! -e ${FREETYPE_LIB} ] ; then \
+ REFRESH=1; \
+ fi; \
+ if [ ${FREETYPE_TIMESTAMP} -nt ${FREETYPE_TIMESTAMP_INT} ] ; then \
+ REFRESH=1; \
+ fi; \
+ if [ $$REFRESH -ne 0 ] ; then \
+ mkdir -p ${FREETYPE_DIR}; \
+ export PATH=$$PATH:${SDKFOLDER}/platform-tools:${ANDROID_NDK}; \
+ echo "changed timestamp for freetype detected building..."; \
+ cd ${FREETYPE_DIR}/Android/jni; \
+ ndk-build NDEBUG=${NDEBUG} NDK_MODULE_PATH=${NDK_MODULE_PATH} \
+ APP_PLATFORM=${APP_PLATFORM} APP_ABI=${TARGET_ABI} -j${PARALLEL} \
+ TARGET_CFLAGS+="${TARGET_CFLAGS_ADDON}" \
+ TARGET_LDFLAGS+="${TARGET_LDFLAGS_ADDON}" \
+ TARGET_CXXFLAGS+="${TARGET_CXXFLAGS_ADDON}" || exit 1; \
+ touch ${FREETYPE_TIMESTAMP}; \
+ touch ${FREETYPE_TIMESTAMP_INT}; \
+ else \
+ echo "nothing to be done for freetype"; \
+ fi
+
+clean_freetype :
+ $(RM) -rf ${FREETYPE_DIR}
+
+#Note: Texturehack patch is required for gpu's not supporting color format
+# correctly. Known bad GPU:
+# -geforce on emulator
+# -Vivante Corporation GC1000 core (e.g. Galaxy Tab 3)
+
+irrlicht_download :
+ @if [ ! -d "deps/irrlicht" ] ; then \
+ echo "irrlicht sources missing, downloading..."; \
+ mkdir -p ${ROOT}/deps; \
+ cd deps; \
+ svn co ${IRRLICHT_URL_SVN} irrlicht || exit 1; \
+ cd irrlicht; \
+ patch -p1 < ../../irrlicht-touchcount.patch || exit 1; \
+ patch -p1 < ../../irrlicht-back_button.patch || exit 1; \
+ patch -p1 < ../../irrlicht-texturehack.patch || exit 1; \
+ fi
+
+irrlicht : $(IRRLICHT_LIB)
+
+$(IRRLICHT_LIB): $(IRRLICHT_TIMESTAMP) $(FREETYPE_LIB)
+ @REFRESH=0; \
+ if [ ! -e ${IRRLICHT_TIMESTAMP_INT} ] ; then \
+ REFRESH=1; \
+ fi; \
+ if [ ! -e ${IRRLICHT_LIB} ] ; then \
+ REFRESH=1; \
+ fi; \
+ if [ ${IRRLICHT_TIMESTAMP} -nt ${IRRLICHT_TIMESTAMP_INT} ] ; then \
+ REFRESH=1; \
+ fi; \
+ if [ $$REFRESH -ne 0 ] ; then \
+ mkdir -p ${IRRLICHT_DIR}; \
+ export PATH=$$PATH:${SDKFOLDER}/platform-tools:${ANDROID_NDK}; \
+ echo "changed timestamp for irrlicht detected building..."; \
+ cd deps/irrlicht/source/Irrlicht/Android; \
+ ndk-build NDEBUG=${NDEBUG} NDK_MODULE_PATH=${NDK_MODULE_PATH} \
+ APP_ABI=${TARGET_ABI} APP_PLATFORM=${APP_PLATFORM} -j${PARALLEL} \
+ TARGET_CFLAGS+="${TARGET_CFLAGS_ADDON}" \
+ TARGET_LDFLAGS+="${TARGET_LDFLAGS_ADDON}" \
+ TARGET_CXXFLAGS+="${TARGET_CXXFLAGS_ADDON}" || exit 1; \
+ touch ${IRRLICHT_TIMESTAMP}; \
+ touch ${IRRLICHT_TIMESTAMP_INT}; \
+ else \
+ echo "nothing to be done for irrlicht"; \
+ fi
+
+clean_irrlicht :
+ $(RM) -rf deps/irrlicht
+
+$(CURL_TIMESTAMP) : curl_download
+ @LAST_MODIF=$$(find ${CURL_DIR} -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -f2- -d" "); \
+ if [ $$(basename $$LAST_MODIF) != "timestamp" ] ; then \
+ touch ${CURL_TIMESTAMP}; \
+ fi
+
+curl_download :
+ @if [ ! -d "deps/curl-${CURL_VERSION}" ] ; then \
+ echo "curl sources missing, downloading..."; \
+ mkdir -p ${ROOT}/deps; \
+ cd deps; \
+ wget ${CURL_URL_HTTP} || exit 1; \
+ tar -xjf curl-${CURL_VERSION}.tar.bz2 || exit 1; \
+ rm curl-${CURL_VERSION}.tar.bz2; \
+ fi
+
+curl : $(CURL_LIB)
+
+$(CURL_LIB): $(CURL_TIMESTAMP) $(OPENSSL_LIB)
+ @REFRESH=0; \
+ if [ ! -e ${CURL_TIMESTAMP_INT} ] ; then \
+ REFRESH=1; \
+ fi; \
+ if [ ! -e ${CURL_LIB} ] ; then \
+ REFRESH=1; \
+ fi; \
+ if [ ${CURL_TIMESTAMP} -nt ${CURL_TIMESTAMP_INT} ] ; then \
+ REFRESH=1; \
+ fi; \
+ if [ $$REFRESH -ne 0 ] ; then \
+ mkdir -p ${CURL_DIR}; \
+ export PATH="$${PATH}:${SDKFOLDER}/platform-tools:${ANDROID_NDK}"; \
+ echo "changed timestamp for curl detected building..."; \
+ cd deps/curl-${CURL_VERSION}; \
+ export CROSS_PREFIX=${CROSS_PREFIX}; \
+ export TOOLCHAIN=/tmp/ndk-arm; \
+ ${ANDROID_NDK}/build/tools/make-standalone-toolchain.sh \
+ --toolchain=${TARGET_TOOLCHAIN}${COMPILER_VERSION} \
+ --install-dir=$${TOOLCHAIN} --system=linux-x86_64; \
+ export PATH="$${TOOLCHAIN}/bin:$${PATH}"; \
+ export CC=${CROSS_PREFIX}gcc; \
+ export CXX=${CROSS_PREFIX}g++; \
+ export TARGET_OS=OS_ANDROID_CROSSCOMPILE; \
+ export CPPFLAGS="$${CPPFLAGS} -I${OPENSSL_DIR}/include \
+ -L${OPENSSL_DIR}/libs/${TARGET_ABI}/ ${TARGET_CFLAGS_ADDON}"; \
+ export CFLAGS="$${CFLAGS} ${TARGET_CFLAGS_ADDON}"; \
+ export LDFLAGS="$${LDFLAGS} -L${OPENSSL_DIR}/libs/${TARGET_ABI}/ \
+ ${TARGET_LDFLAGS_ADDON}"; \
+ ./configure --host=${TARGET_HOST} --disable-shared --enable-static --with-ssl; \
+ $(MAKE) -j${PARALLEL} -s || exit 1; \
+ touch ${CURL_TIMESTAMP}; \
+ touch ${CURL_TIMESTAMP_INT}; \
+ else \
+ echo "nothing to be done for curl"; \
+ fi
+
+clean_curl :
+ $(RM) -rf deps/curl-${CURL_VERSION}
+
+
+curl_binary:
+ @if [ ! -d "deps/curl-${CURL_VERSION_BINARY}" ] ; then \
+ echo "curl sources missing, downloading..."; \
+ mkdir -p ${ROOT}/deps; \
+ cd deps; \
+ wget http://curl.haxx.se/gknw.net/7.34.0/dist-android/curl-7.34.0-rtmp-ssh2-ssl-zlib-static-bin-android.tar.gz || exit 1;\
+ tar -xzf curl-7.34.0-rtmp-ssh2-ssl-zlib-static-bin-android.tar.gz || exit 1;\
+ mv curl-7.34.0-rtmp-ssh2-ssl-zlib-static-bin-android curl-${CURL_VERSION_BINARY};\
+ rm curl-7.34.0-rtmp-ssh2-ssl-zlib-static-bin-android.tar.gz; \
+ fi
+
+$(ASSETS_TIMESTAMP) : $(IRRLICHT_LIB)
+ @mkdir -p ${ROOT}/deps; \
+ LAST_MODIF=$$(find ${ROOT}/../../builtin -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -f2- -d" "); \
+ if [ $$(basename $$LAST_MODIF) != "timestamp" ] ; then \
+ touch ${ROOT}/../../builtin/timestamp; \
+ touch ${ASSETS_TIMESTAMP}; \
+ echo builtin changed $$LAST_MODIF; \
+ fi; \
+ LAST_MODIF=$$(find ${ROOT}/../../client -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -f2- -d" "); \
+ if [ $$(basename $$LAST_MODIF) != "timestamp" ] ; then \
+ touch ${ROOT}/../../client/timestamp; \
+ touch ${ASSETS_TIMESTAMP}; \
+ fi; \
+ LAST_MODIF=$$(find ${ROOT}/../../doc -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -f2- -d" "); \
+ if [ $$(basename $$LAST_MODIF) != "timestamp" ] ; then \
+ touch ${ROOT}/../../doc/timestamp; \
+ touch ${ASSETS_TIMESTAMP}; \
+ fi; \
+ LAST_MODIF=$$(find ${ROOT}/../../fonts -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -f2- -d" "); \
+ if [ $$(basename $$LAST_MODIF) != "timestamp" ] ; then \
+ touch ${ROOT}/../../fonts/timestamp; \
+ touch ${ASSETS_TIMESTAMP}; \
+ fi; \
+ LAST_MODIF=$$(find ${ROOT}/../../games -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -f2- -d" "); \
+ if [ $$(basename $$LAST_MODIF) != "timestamp" ] ; then \
+ touch ${ROOT}/../../games/timestamp; \
+ touch ${ASSETS_TIMESTAMP}; \
+ fi; \
+ LAST_MODIF=$$(find ${ROOT}/../../mods -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -f2- -d" "); \
+ if [ $$(basename $$LAST_MODIF) != "timestamp" ] ; then \
+ touch ${ROOT}/../../mods/timestamp; \
+ touch ${ASSETS_TIMESTAMP}; \
+ fi; \
+ LAST_MODIF=$$(find ${ROOT}/../../po -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -f2- -d" "); \
+ if [ $$(basename $$LAST_MODIF) != "timestamp" ] ; then \
+ touch ${ROOT}/../../po/timestamp; \
+ touch ${ASSETS_TIMESTAMP}; \
+ fi; \
+ LAST_MODIF=$$(find ${ROOT}/../../textures -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -f2- -d" "); \
+ if [ $$(basename $$LAST_MODIF) != "timestamp" ] ; then \
+ touch ${ROOT}/../../textures/timestamp; \
+ touch ${ASSETS_TIMESTAMP}; \
+ fi; \
+ LAST_MODIF=$$(find ${IRRLICHT_DIR}/media -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -f2- -d" "); \
+ if [ $$(basename $$LAST_MODIF) != "timestamp" ] ; then \
+ touch ${IRRLICHT_DIR}/media/timestamp; \
+ touch ${ASSETS_TIMESTAMP}; \
+ fi; \
+ if [ ${ROOT}/../../minetest.conf.example -nt ${ASSETS_TIMESTAMP} ] ; then \
+ echo "conf changed"; \
+ touch ${ASSETS_TIMESTAMP}; \
+ fi; \
+ if [ ${ROOT}/../../README.txt -nt ${ASSETS_TIMESTAMP} ] ; then \
+ touch ${ASSETS_TIMESTAMP}; \
+ fi; \
+ if [ ! -e $(ASSETS_TIMESTAMP) ] ; then \
+ touch $(ASSETS_TIMESTAMP); \
+ fi
+
+assets : $(ASSETS_TIMESTAMP)
+ @REFRESH=0; \
+ if [ ! -e ${ASSETS_TIMESTAMP}.old ] ; then \
+ REFRESH=1; \
+ fi; \
+ if [ ${ASSETS_TIMESTAMP} -nt ${ASSETS_TIMESTAMP}.old ] ; then \
+ REFRESH=1; \
+ fi; \
+ if [ ! -d ${ROOT}/assets ] ; then \
+ REFRESH=1; \
+ fi; \
+ if [ $$REFRESH -ne 0 ] ; then \
+ echo "assets changed, refreshing..."; \
+ $(MAKE) -j${PARALLEL} clean_assets; \
+ mkdir -p ${ROOT}/assets/Minetest; \
+ cp ${ROOT}/../../minetest.conf.example ${ROOT}/assets/Minetest; \
+ cp ${ROOT}/../../README.txt ${ROOT}/assets/Minetest; \
+ cp -r ${ROOT}/../../builtin ${ROOT}/assets/Minetest; \
+ cp -r ${ROOT}/../../client ${ROOT}/assets/Minetest; \
+ cp -r ${ROOT}/../../doc ${ROOT}/assets/Minetest; \
+ cp -r ${ROOT}/../../fonts ${ROOT}/assets/Minetest; \
+ cp -r ${ROOT}/../../games ${ROOT}/assets/Minetest; \
+ cp -r ${ROOT}/../../mods ${ROOT}/assets/Minetest; \
+ cp -r ${ROOT}/../../po ${ROOT}/assets/Minetest; \
+ cp -r ${ROOT}/../../textures ${ROOT}/assets/Minetest; \
+ mkdir -p ${ROOT}/assets/Minetest/media; \
+ cp -r ${IRRLICHT_DIR}/media/Shaders ${ROOT}/assets/Minetest/media; \
+ cd ${ROOT}/assets; \
+ find . -name "timestamp" -exec rm {} \; ; \
+ find . -name "*.blend" -exec rm {} \; ; \
+ ls -R | grep ":$$" | sed -e 's/:$$//' -e 's/\.//' -e 's/^\///' > "index.txt"; \
+ cp ${ROOT}/${ASSETS_TIMESTAMP} ${ROOT}/${ASSETS_TIMESTAMP}.old; \
+ else \
+ echo "nothing to be done for assets"; \
+ fi
+
+clean_assets :
+ @$(RM) -r assets
+
+apk: $(PATHCFGFILE) assets $(IRRLICHT_LIB) $(CURL_LIB) \
+ $(OPENAL_LIB) $(OGG_LIB) prep_srcdir $(ROOT)/jni/src/android_version.h
+ @export NDEBUG=$$NDEBUG; $(MAKE) -j${PARALLEL} manifest; \
+ export PATH=$$PATH:${SDKFOLDER}/platform-tools:${ANDROID_NDK}; \
+ export ANDROID_HOME=${SDKFOLDER}; \
+ mkdir -p ${ROOT}/src; \
+ ndk-build NDK_MODULE_PATH=${NDK_MODULE_PATH} -j${PARALLEL} \
+ GPROF=${GPROF} APP_ABI=${TARGET_ABI} \
+ APP_PLATFORM=${APP_PLATFORM} \
+ TARGET_LIBDIR=${TARGET_LIBDIR} \
+ TARGET_CFLAGS+="${TARGET_CFLAGS_ADDON}" \
+ TARGET_LDFLAGS+="${TARGET_LDFLAGS_ADDON}" \
+ TARGET_CXXFLAGS+="${TARGET_CXXFLAGS_ADDON}" && \
+ ant $$BUILD_TYPE && \
+ echo "++ Success!" && \
+ echo "APK: bin/Minetest-$$BUILD_TYPE.apk" && \
+ echo "You can install it with \`adb install -r bin/Minetest-$$BUILD_TYPE.apk\`"
+
+prep_srcdir :
+ @rm ${ROOT}/jni/src; \
+ ln -s ${ROOT}/../../src ${ROOT}/jni/src
+
+clean_apk : manifest
+ @export PATH=$$PATH:${SDKFOLDER}platform-tools:${ANDROID_NDK}; \
+ export ANDROID_HOME=${SDKFOLDER}; \
+ ant clean
+
+install_debug :
+ @export PATH=$$PATH:${SDKFOLDER}platform-tools:${ANDROID_NDK}; \
+ adb install -r ${ROOT}/bin/Minetest-debug.apk
+
+install :
+ @export PATH=$$PATH:${SDKFOLDER}platform-tools:${ANDROID_NDK}; \
+ adb install -r ${ROOT}/bin/Minetest-release.apk
+
+envpaths :
+ @echo "export PATH=$$PATH:${SDKFOLDER}platform-tools:${ANDROID_NDK}" > and_env;\
+ echo "export ANDROID_HOME=${SDKFOLDER}" >> and_env;
+
+clean_all :
+ @$(MAKE) -j${PARALLEL} clean_apk; \
+ $(MAKE) clean_assets clean_irrlicht clean_leveldb clean_curl clean_openssl \
+ clean_openal clean_ogg clean_manifest; \
+ sleep 1; \
+ $(RM) -r gen libs obj deps bin Debug and_env
+
+$(ROOT)/jni/src/android_version.h :
+ @echo "#define STR_HELPER(x) #x" \
+ >${ROOT}/jni/src/android_version.h; \
+ echo "#define STR(x) STR_HELPER(x)" \
+ >> ${ROOT}/jni/src/android_version.h; \
+ echo "#define VERSION_MAJOR $$(cat ${ROOT}/../../CMakeLists.txt | \
+ grep ^set\(VERSION_MAJOR\ | sed 's/)/ /' | awk '{print $$2;}')" \
+ >> ${ROOT}/jni/src/android_version.h; \
+ echo "#define VERSION_MINOR $$(cat ${ROOT}/../../CMakeLists.txt | \
+ grep ^set\(VERSION_MINOR\ | sed 's/)/ /' | awk '{print $$2;}')" \
+ >> ${ROOT}/jni/src/android_version.h; \
+ echo "#define VERSION_PATCH $$(cat ${ROOT}/../../CMakeLists.txt | \
+ grep ^set\(VERSION_PATCH\ | sed 's/)/ /' | awk '{print $$2;}')" \
+ >> ${ROOT}/jni/src/android_version.h; \
+ echo "#define VERSION_PATCH_ORIG $$(cat ${ROOT}/../../CMakeLists.txt | \
+ grep ^set\(VERSION_PATCH\ | sed 's/)/ /' | awk '{print $$2;}')" \
+ >> ${ROOT}/jni/src/android_version.h; \
+ echo "#define CMAKE_VERSION_GITHASH \"$$(git rev-parse --short=8 HEAD)\"" \
+ >> ${ROOT}/jni/src/android_version.h; \
+ echo "#define CMAKE_VERSION_STRING STR(VERSION_MAJOR)\".\"STR(VERSION_MINOR)\
+ \".\"STR(VERSION_PATCH)" \
+ >> ${ROOT}/jni/src/android_version.h;
+
+manifest :
+ @VERS_MAJOR=$$(cat ${ROOT}/../../CMakeLists.txt | \
+ grep ^set\(VERSION_MAJOR\ | sed 's/)/ /' | awk '{print $$2;}'); \
+ VERS_MINOR=$$(cat ${ROOT}/../../CMakeLists.txt | \
+ grep ^set\(VERSION_MINOR\ | sed 's/)/ /' | awk '{print $$2;}'); \
+ VERS_PATCH=$$(cat ${ROOT}/../../CMakeLists.txt | \
+ grep ^set\(VERSION_PATCH\ | sed 's/)/ /' | awk '{print $$2;}'); \
+ BASE_VERSION="$$VERS_MAJOR.$$VERS_MINOR.$$VERS_PATCH"; \
+ if [ "${NDEBUG}x" != "x" ] ; then \
+ DBG=''; \
+ DBG_FLAG="android:debuggable=\"false\""; \
+ else \
+ DBG="<uses-permission android:name=\"android.permission.SET_DEBUG_APP\" />"; \
+ DBG_FLAG="android:debuggable=\"true\""; \
+ fi; \
+ cat ${ROOT}/AndroidManifest.xml.template | \
+ sed s/###ANDROID_VERSION###/${ANDROID_VERSION_CODE}/g | \
+ sed s/###BASE_VERSION###/$$BASE_VERSION/g | \
+ sed -e "s@###DEBUG_BUILD###@$$DBG@g" | \
+ sed -e "s@###DEBUG_FLAG###@$$DBG_FLAG@g" >${ROOT}/AndroidManifest.xml
+
+clean_manifest :
+ rm -rf ${ROOT}/AndroidManifest.xml
+
+clean : clean_apk clean_assets
diff --git a/build/android/build.xml b/build/android/build.xml
new file mode 100644
index 000000000..50a3e95ac
--- /dev/null
+++ b/build/android/build.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project name="Minetest" default="help">
+ <property file="local.properties" />
+ <property file="ant.properties" />
+ <property environment="env" />
+ <condition property="sdk.dir" value="${env.ANDROID_HOME}">
+ <isset property="env.ANDROID_HOME" />
+ </condition>
+ <loadproperties srcFile="project.properties" />
+ <fail
+ message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through the ANDROID_HOME environment variable."
+ unless="sdk.dir"
+ />
+ <import file="custom_rules.xml" optional="true" />
+ <import file="${sdk.dir}/tools/ant/build.xml" />
+</project>
diff --git a/build/android/irrlicht-back_button.patch b/build/android/irrlicht-back_button.patch
new file mode 100644
index 000000000..227749ba7
--- /dev/null
+++ b/build/android/irrlicht-back_button.patch
@@ -0,0 +1,19 @@
+--- irrlicht/source/Irrlicht/Android/CIrrDeviceAndroid.cpp 2014-06-03 20:56:21.289559503 +0200
++++ irrlicht/source/Irrlicht/Android/CIrrDeviceAndroid.cpp.orig 2014-06-03 20:57:39.281556749 +0200
+@@ -423,6 +423,7 @@
+ }
+
+ device->postEventFromUser(event);
++ status = 1;
+ }
+ break;
+ default:
+@@ -479,7 +480,7 @@
+ KeyMap[1] = KEY_LBUTTON; // AKEYCODE_SOFT_LEFT
+ KeyMap[2] = KEY_RBUTTON; // AKEYCODE_SOFT_RIGHT
+ KeyMap[3] = KEY_HOME; // AKEYCODE_HOME
+- KeyMap[4] = KEY_BACK; // AKEYCODE_BACK
++ KeyMap[4] = KEY_CANCEL; // AKEYCODE_BACK
+ KeyMap[5] = KEY_UNKNOWN; // AKEYCODE_CALL
+ KeyMap[6] = KEY_UNKNOWN; // AKEYCODE_ENDCALL
+ KeyMap[7] = KEY_KEY_0; // AKEYCODE_0
diff --git a/build/android/irrlicht-texturehack.patch b/build/android/irrlicht-texturehack.patch
new file mode 100644
index 000000000..a458ede72
--- /dev/null
+++ b/build/android/irrlicht-texturehack.patch
@@ -0,0 +1,240 @@
+--- irrlicht/source/Irrlicht/COGLESTexture.cpp.orig 2014-06-22 17:01:13.266568869 +0200
++++ irrlicht/source/Irrlicht/COGLESTexture.cpp 2014-06-22 17:03:59.298572810 +0200
+@@ -366,112 +366,140 @@
+ void(*convert)(const void*, s32, void*) = 0;
+ getFormatParameters(ColorFormat, InternalFormat, filtering, PixelFormat, PixelType, convert);
+
+- // make sure we don't change the internal format of existing images
+- if (!newTexture)
+- InternalFormat = oldInternalFormat;
+-
+- Driver->setActiveTexture(0, this);
+-
+- if (Driver->testGLError())
+- os::Printer::log("Could not bind Texture", ELL_ERROR);
+-
+- // mipmap handling for main texture
+- if (!level && newTexture)
+- {
+- // auto generate if possible and no mipmap data is given
+- if (!IsCompressed && HasMipMaps && !mipmapData && Driver->queryFeature(EVDF_MIP_MAP_AUTO_UPDATE))
+- {
+- if (Driver->getTextureCreationFlag(ETCF_OPTIMIZED_FOR_SPEED))
+- glHint(GL_GENERATE_MIPMAP_HINT, GL_FASTEST);
+- else if (Driver->getTextureCreationFlag(ETCF_OPTIMIZED_FOR_QUALITY))
+- glHint(GL_GENERATE_MIPMAP_HINT, GL_NICEST);
+- else
+- glHint(GL_GENERATE_MIPMAP_HINT, GL_DONT_CARE);
++ bool retry = false;
++
++ do {
++ if (retry) {
++ InternalFormat = GL_RGBA;
++ PixelFormat = GL_RGBA;
++ convert = CColorConverter::convert_A8R8G8B8toA8B8G8R8;
++ }
++ // make sure we don't change the internal format of existing images
++ if (!newTexture)
++ InternalFormat = oldInternalFormat;
+
+- glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);
+- AutomaticMipmapUpdate=true;
+- }
++ Driver->setActiveTexture(0, this);
+
+- // enable bilinear filter without mipmaps
+- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
+- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
+- }
++ if (Driver->testGLError())
++ os::Printer::log("Could not bind Texture", ELL_ERROR);
+
+- // now get image data and upload to GPU
++ // mipmap handling for main texture
++ if (!level && newTexture)
++ {
++ // auto generate if possible and no mipmap data is given
++ if (!IsCompressed && HasMipMaps && !mipmapData && Driver->queryFeature(EVDF_MIP_MAP_AUTO_UPDATE))
++ {
++ if (Driver->getTextureCreationFlag(ETCF_OPTIMIZED_FOR_SPEED))
++ glHint(GL_GENERATE_MIPMAP_HINT, GL_FASTEST);
++ else if (Driver->getTextureCreationFlag(ETCF_OPTIMIZED_FOR_QUALITY))
++ glHint(GL_GENERATE_MIPMAP_HINT, GL_NICEST);
++ else
++ glHint(GL_GENERATE_MIPMAP_HINT, GL_DONT_CARE);
++
++ glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);
++ AutomaticMipmapUpdate=true;
++ }
++
++ // enable bilinear filter without mipmaps
++ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filtering);
++ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
++ }
+
+- u32 compressedImageSize = IImage::getCompressedImageSize(ColorFormat, image->getDimension().Width, image->getDimension().Height);
++ // now get image data and upload to GPU
+
+- void* source = image->lock();
++ u32 compressedImageSize = IImage::getCompressedImageSize(ColorFormat, image->getDimension().Width, image->getDimension().Height);
+
+- IImage* tmpImage = 0;
++ void* source = image->lock();
+
+- if (convert)
+- {
+- tmpImage = new CImage(image->getColorFormat(), image->getDimension());
+- void* dest = tmpImage->lock();
+- convert(source, image->getDimension().getArea(), dest);
+- image->unlock();
+- source = dest;
+- }
++ IImage* tmpImage = 0;
+
+- if (newTexture)
+- {
+- if (IsCompressed)
++ if (convert)
+ {
+- glCompressedTexImage2D(GL_TEXTURE_2D, 0, InternalFormat, image->getDimension().Width,
+- image->getDimension().Height, 0, compressedImageSize, source);
++ tmpImage = new CImage(image->getColorFormat(), image->getDimension());
++ void* dest = tmpImage->lock();
++ convert(source, image->getDimension().getArea(), dest);
++ image->unlock();
++ source = dest;
+ }
+- else
+- glTexImage2D(GL_TEXTURE_2D, level, InternalFormat, image->getDimension().Width,
+- image->getDimension().Height, 0, PixelFormat, PixelType, source);
+- }
+- else
+- {
+- if (IsCompressed)
++
++ if (newTexture)
+ {
+- glCompressedTexSubImage2D(GL_TEXTURE_2D, level, 0, 0, image->getDimension().Width,
+- image->getDimension().Height, PixelFormat, compressedImageSize, source);
++ if (IsCompressed)
++ {
++ glCompressedTexImage2D(GL_TEXTURE_2D, 0, InternalFormat, image->getDimension().Width,
++ image->getDimension().Height, 0, compressedImageSize, source);
++ }
++ else
++ glTexImage2D(GL_TEXTURE_2D, level, InternalFormat, image->getDimension().Width,
++ image->getDimension().Height, 0, PixelFormat, PixelType, source);
+ }
+ else
+- glTexSubImage2D(GL_TEXTURE_2D, level, 0, 0, image->getDimension().Width,
+- image->getDimension().Height, PixelFormat, PixelType, source);
+- }
+-
+- if (convert)
+- {
+- tmpImage->unlock();
+- tmpImage->drop();
+- }
+- else
+- image->unlock();
+-
+- if (!level && newTexture)
+- {
+- if (IsCompressed && !mipmapData)
+ {
+- if (image->hasMipMaps())
+- mipmapData = static_cast<u8*>(image->lock())+compressedImageSize;
++ if (IsCompressed)
++ {
++ glCompressedTexSubImage2D(GL_TEXTURE_2D, level, 0, 0, image->getDimension().Width,
++ image->getDimension().Height, PixelFormat, compressedImageSize, source);
++ }
+ else
+- HasMipMaps = false;
++ glTexSubImage2D(GL_TEXTURE_2D, level, 0, 0, image->getDimension().Width,
++ image->getDimension().Height, PixelFormat, PixelType, source);
+ }
+
+- regenerateMipMapLevels(mipmapData);
+-
+- if (HasMipMaps) // might have changed in regenerateMipMapLevels
++ if (convert)
+ {
+- // enable bilinear mipmap filter
+- GLint filteringMipMaps = GL_LINEAR_MIPMAP_NEAREST;
+-
+- if (filtering != GL_LINEAR)
+- filteringMipMaps = GL_NEAREST_MIPMAP_NEAREST;
++ tmpImage->unlock();
++ tmpImage->drop();
++ }
++ else
++ image->unlock();
++
++ if (glGetError() != GL_NO_ERROR) {
++ static bool warned = false;
++ if ((!retry) && (ColorFormat == ECF_A8R8G8B8)) {
++
++ if (!warned) {
++ os::Printer::log("Your driver claims to support GL_BGRA but fails on trying to upload a texture, converting to GL_RGBA and trying again", ELL_ERROR);
++ warned = true;
++ }
++ }
++ else if (retry) {
++ os::Printer::log("Neither uploading texture as GL_BGRA nor, converted one using GL_RGBA succeeded", ELL_ERROR);
++ }
++ retry = !retry;
++ continue;
++ } else {
++ retry = false;
++ }
+
+- glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filteringMipMaps);
+- glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
++ if (!level && newTexture)
++ {
++ if (IsCompressed && !mipmapData)
++ {
++ if (image->hasMipMaps())
++ mipmapData = static_cast<u8*>(image->lock())+compressedImageSize;
++ else
++ HasMipMaps = false;
++ }
++
++ regenerateMipMapLevels(mipmapData);
++
++ if (HasMipMaps) // might have changed in regenerateMipMapLevels
++ {
++ // enable bilinear mipmap filter
++ GLint filteringMipMaps = GL_LINEAR_MIPMAP_NEAREST;
++
++ if (filtering != GL_LINEAR)
++ filteringMipMaps = GL_NEAREST_MIPMAP_NEAREST;
++
++ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filteringMipMaps);
++ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filtering);
++ }
+ }
+- }
+
+- if (Driver->testGLError())
+- os::Printer::log("Could not glTexImage2D", ELL_ERROR);
++ if (Driver->testGLError())
++ os::Printer::log("Could not glTexImage2D", ELL_ERROR);
++ }
++ while(retry);
+ }
+
+
+--- irrlicht/source/Irrlicht/COGLESTexture.cpp.orig 2014-06-25 00:28:50.820501856 +0200
++++ irrlicht/source/Irrlicht/COGLESTexture.cpp 2014-06-25 00:08:37.712544692 +0200
+@@ -422,6 +422,9 @@
+ source = dest;
+ }
+
++ //clear old error
++ glGetError();
++
+ if (newTexture)
+ {
+ if (IsCompressed)
diff --git a/build/android/irrlicht-touchcount.patch b/build/android/irrlicht-touchcount.patch
new file mode 100644
index 000000000..d4e4b9c3e
--- /dev/null
+++ b/build/android/irrlicht-touchcount.patch
@@ -0,0 +1,30 @@
+--- irrlicht.orig/include/IEventReceiver.h 2014-06-03 19:43:50.433713133 +0200
++++ irrlicht/include/IEventReceiver.h 2014-06-03 19:44:36.993711489 +0200
+@@ -375,6 +375,9 @@
+ // Y position of simple touch.
+ s32 Y;
+
++ // number of current touches
++ s32 touchedCount;
++
+ //! Type of touch event.
+ ETOUCH_INPUT_EVENT Event;
+ };
+--- irrlicht.orig/source/Irrlicht/Android/CIrrDeviceAndroid.cpp 2014-06-03 19:43:50.505713130 +0200
++++ irrlicht/source/Irrlicht/Android/CIrrDeviceAndroid.cpp 2014-06-03 19:45:37.265709359 +0200
+@@ -315,6 +315,7 @@
+ event.TouchInput.ID = AMotionEvent_getPointerId(androidEvent, i);
+ event.TouchInput.X = AMotionEvent_getX(androidEvent, i);
+ event.TouchInput.Y = AMotionEvent_getY(androidEvent, i);
++ event.TouchInput.touchedCount = AMotionEvent_getPointerCount(androidEvent);
+
+ device->postEventFromUser(event);
+ }
+@@ -326,6 +327,7 @@
+ event.TouchInput.ID = AMotionEvent_getPointerId(androidEvent, pointerIndex);
+ event.TouchInput.X = AMotionEvent_getX(androidEvent, pointerIndex);
+ event.TouchInput.Y = AMotionEvent_getY(androidEvent, pointerIndex);
++ event.TouchInput.touchedCount = AMotionEvent_getPointerCount(androidEvent);
+
+ device->postEventFromUser(event);
+ }
diff --git a/build/android/jni/Android.mk b/build/android/jni/Android.mk
new file mode 100644
index 000000000..3559c2b6d
--- /dev/null
+++ b/build/android/jni/Android.mk
@@ -0,0 +1,310 @@
+LOCAL_PATH := $(call my-dir)/..
+
+#LOCAL_ADDRESS_SANITIZER:=true
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := Irrlicht
+LOCAL_SRC_FILES := deps/irrlicht/lib/Android/libIrrlicht.a
+include $(PREBUILT_STATIC_LIBRARY)
+
+#include $(CLEAR_VARS)
+#LOCAL_MODULE := LevelDB
+#LOCAL_SRC_FILES := deps/leveldb/libleveldb.a
+#include $(PREBUILT_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := curl
+LOCAL_SRC_FILES := deps/curl-7.35.0/lib/.libs/libcurl.a
+include $(PREBUILT_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := freetype
+LOCAL_SRC_FILES := deps/freetype2-android/Android/obj/local/$(TARGET_ARCH_ABI)/libfreetype2-static.a
+include $(PREBUILT_STATIC_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := openal
+LOCAL_SRC_FILES := deps/openal-soft/libs/$(TARGET_LIBDIR)/libopenal.so
+include $(PREBUILT_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := ogg
+LOCAL_SRC_FILES := deps/libvorbis-libogg-android/libs/$(TARGET_LIBDIR)/libogg.so
+include $(PREBUILT_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := vorbis
+LOCAL_SRC_FILES := deps/libvorbis-libogg-android/libs/$(TARGET_LIBDIR)/libvorbis.so
+include $(PREBUILT_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := ssl
+LOCAL_SRC_FILES := deps/openssl-android/libs/$(TARGET_LIBDIR)/libssl.so
+include $(PREBUILT_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := crypto
+LOCAL_SRC_FILES := deps/openssl-android/libs/$(TARGET_LIBDIR)/libcrypto.so
+include $(PREBUILT_SHARED_LIBRARY)
+
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := minetest
+
+LOCAL_CPP_FEATURES += exceptions
+
+ifdef GPROF
+GPROF_DEF=-DGPROF
+endif
+
+LOCAL_CFLAGS := -D_IRR_ANDROID_PLATFORM_ \
+ -DHAVE_TOUCHSCREENGUI \
+ -DUSE_CURL=1 \
+ -DUSE_SOUND=1 \
+ -DUSE_FREETYPE=1 \
+ $(GPROF_DEF) \
+ -pipe -fstrict-aliasing
+
+ifndef NDEBUG
+LOCAL_CFLAGS += -g -D_DEBUG -O0 -fno-omit-frame-pointer
+else
+LOCAL_CFLAGS += -fexpensive-optimizations -O3
+endif
+
+ifdef GPROF
+PROFILER_LIBS := android-ndk-profiler
+LOCAL_CFLAGS += -pg
+endif
+
+# LOCAL_CFLAGS += -fsanitize=address
+# LOCAL_LDFLAGS += -fsanitize=address
+
+ifeq ($(TARGET_ARCH_ABI),x86)
+LOCAL_CFLAGS += -fno-stack-protector
+endif
+
+LOCAL_C_INCLUDES := \
+ jni/src jni/src/sqlite \
+ jni/src/script \
+ jni/src/lua/src \
+ jni/src/json \
+ jni/src/cguittfont \
+ deps/irrlicht/include \
+ deps/freetype2-android/include \
+ deps/curl-7.35.0/include \
+ deps/openal-soft/jni/OpenAL/include \
+ deps/libvorbis-libogg-android/jni/include
+
+# deps/leveldb/include \
+
+LOCAL_SRC_FILES := \
+ jni/src/ban.cpp \
+ jni/src/base64.cpp \
+ jni/src/biome.cpp \
+ jni/src/camera.cpp \
+ jni/src/cavegen.cpp \
+ jni/src/chat.cpp \
+ jni/src/client.cpp \
+ jni/src/clientiface.cpp \
+ jni/src/clientmap.cpp \
+ jni/src/clientmedia.cpp \
+ jni/src/clientobject.cpp \
+ jni/src/clouds.cpp \
+ jni/src/collision.cpp \
+ jni/src/connection.cpp \
+ jni/src/content_abm.cpp \
+ jni/src/content_cao.cpp \
+ jni/src/content_cso.cpp \
+ jni/src/content_mapblock.cpp \
+ jni/src/content_mapnode.cpp \
+ jni/src/content_nodemeta.cpp \
+ jni/src/content_sao.cpp \
+ jni/src/convert_json.cpp \
+ jni/src/craftdef.cpp \
+ jni/src/database-dummy.cpp \
+ jni/src/database-sqlite3.cpp \
+ jni/src/database.cpp \
+ jni/src/debug.cpp \
+ jni/src/defaultsettings.cpp \
+ jni/src/drawscene.cpp \
+ jni/src/dungeongen.cpp \
+ jni/src/emerge.cpp \
+ jni/src/environment.cpp \
+ jni/src/filecache.cpp \
+ jni/src/filesys.cpp \
+ jni/src/game.cpp \
+ jni/src/genericobject.cpp \
+ jni/src/gettext.cpp \
+ jni/src/guiChatConsole.cpp \
+ jni/src/guiEngine.cpp \
+ jni/src/guiFileSelectMenu.cpp \
+ jni/src/guiFormSpecMenu.cpp \
+ jni/src/guiKeyChangeMenu.cpp \
+ jni/src/guiPasswordChange.cpp \
+ jni/src/guiTable.cpp \
+ jni/src/guiVolumeChange.cpp \
+ jni/src/httpfetch.cpp \
+ jni/src/hud.cpp \
+ jni/src/inventory.cpp \
+ jni/src/inventorymanager.cpp \
+ jni/src/itemdef.cpp \
+ jni/src/keycode.cpp \
+ jni/src/light.cpp \
+ jni/src/localplayer.cpp \
+ jni/src/log.cpp \
+ jni/src/main.cpp \
+ jni/src/map.cpp \
+ jni/src/mapblock.cpp \
+ jni/src/mapblock_mesh.cpp \
+ jni/src/mapgen.cpp \
+ jni/src/mapgen_indev.cpp \
+ jni/src/mapgen_math.cpp \
+ jni/src/mapgen_singlenode.cpp \
+ jni/src/mapgen_v6.cpp \
+ jni/src/mapgen_v7.cpp \
+ jni/src/mapnode.cpp \
+ jni/src/mapsector.cpp \
+ jni/src/mesh.cpp \
+ jni/src/mods.cpp \
+ jni/src/nameidmapping.cpp \
+ jni/src/nodedef.cpp \
+ jni/src/nodemetadata.cpp \
+ jni/src/nodetimer.cpp \
+ jni/src/noise.cpp \
+ jni/src/object_properties.cpp \
+ jni/src/particles.cpp \
+ jni/src/pathfinder.cpp \
+ jni/src/player.cpp \
+ jni/src/porting_android.cpp \
+ jni/src/porting.cpp \
+ jni/src/quicktune.cpp \
+ jni/src/rollback.cpp \
+ jni/src/rollback_interface.cpp \
+ jni/src/serialization.cpp \
+ jni/src/server.cpp \
+ jni/src/serverlist.cpp \
+ jni/src/serverobject.cpp \
+ jni/src/sha1.cpp \
+ jni/src/shader.cpp \
+ jni/src/sky.cpp \
+ jni/src/socket.cpp \
+ jni/src/sound.cpp \
+ jni/src/sound_openal.cpp \
+ jni/src/staticobject.cpp \
+ jni/src/subgame.cpp \
+ jni/src/test.cpp \
+ jni/src/tile.cpp \
+ jni/src/tool.cpp \
+ jni/src/treegen.cpp \
+ jni/src/version.cpp \
+ jni/src/voxel.cpp \
+ jni/src/voxelalgorithms.cpp \
+ jni/src/util/directiontables.cpp \
+ jni/src/util/numeric.cpp \
+ jni/src/util/pointedthing.cpp \
+ jni/src/util/serialize.cpp \
+ jni/src/util/string.cpp \
+ jni/src/util/timetaker.cpp \
+ jni/src/touchscreengui.cpp
+
+# jni/src/database-leveldb.cpp \
+
+# lua api
+LOCAL_SRC_FILES += \
+ jni/src/script/common/c_content.cpp \
+ jni/src/script/common/c_converter.cpp \
+ jni/src/script/common/c_internal.cpp \
+ jni/src/script/common/c_types.cpp \
+ jni/src/script/cpp_api/s_base.cpp \
+ jni/src/script/cpp_api/s_entity.cpp \
+ jni/src/script/cpp_api/s_env.cpp \
+ jni/src/script/cpp_api/s_inventory.cpp \
+ jni/src/script/cpp_api/s_item.cpp \
+ jni/src/script/cpp_api/s_mainmenu.cpp \
+ jni/src/script/cpp_api/s_node.cpp \
+ jni/src/script/cpp_api/s_nodemeta.cpp \
+ jni/src/script/cpp_api/s_player.cpp \
+ jni/src/script/cpp_api/s_server.cpp \
+ jni/src/script/cpp_api/s_async.cpp \
+ jni/src/script/lua_api/l_base.cpp \
+ jni/src/script/lua_api/l_craft.cpp \
+ jni/src/script/lua_api/l_env.cpp \
+ jni/src/script/lua_api/l_inventory.cpp \
+ jni/src/script/lua_api/l_item.cpp \
+ jni/src/script/lua_api/l_mainmenu.cpp \
+ jni/src/script/lua_api/l_mapgen.cpp \
+ jni/src/script/lua_api/l_nodemeta.cpp \
+ jni/src/script/lua_api/l_nodetimer.cpp \
+ jni/src/script/lua_api/l_noise.cpp \
+ jni/src/script/lua_api/l_object.cpp \
+ jni/src/script/lua_api/l_particles.cpp \
+ jni/src/script/lua_api/l_rollback.cpp \
+ jni/src/script/lua_api/l_server.cpp \
+ jni/src/script/lua_api/l_settings.cpp \
+ jni/src/script/lua_api/l_util.cpp \
+ jni/src/script/lua_api/l_vmanip.cpp \
+ jni/src/script/scripting_game.cpp \
+ jni/src/script/scripting_mainmenu.cpp
+
+#freetype2 support
+LOCAL_SRC_FILES += \
+ jni/src/cguittfont/xCGUITTFont.cpp
+
+# lua
+LOCAL_SRC_FILES += \
+ jni/src/lua/src/lapi.c \
+ jni/src/lua/src/lauxlib.c \
+ jni/src/lua/src/lbaselib.c \
+ jni/src/lua/src/lcode.c \
+ jni/src/lua/src/ldblib.c \
+ jni/src/lua/src/ldebug.c \
+ jni/src/lua/src/ldo.c \
+ jni/src/lua/src/ldump.c \
+ jni/src/lua/src/lfunc.c \
+ jni/src/lua/src/lgc.c \
+ jni/src/lua/src/linit.c \
+ jni/src/lua/src/liolib.c \
+ jni/src/lua/src/llex.c \
+ jni/src/lua/src/lmathlib.c \
+ jni/src/lua/src/lmem.c \
+ jni/src/lua/src/loadlib.c \
+ jni/src/lua/src/lobject.c \
+ jni/src/lua/src/lopcodes.c \
+ jni/src/lua/src/loslib.c \
+ jni/src/lua/src/lparser.c \
+ jni/src/lua/src/lstate.c \
+ jni/src/lua/src/lstring.c \
+ jni/src/lua/src/lstrlib.c \
+ jni/src/lua/src/ltable.c \
+ jni/src/lua/src/ltablib.c \
+ jni/src/lua/src/ltm.c \
+ jni/src/lua/src/lundump.c \
+ jni/src/lua/src/lvm.c \
+ jni/src/lua/src/lzio.c \
+ jni/src/lua/src/print.c
+
+# sqlite
+LOCAL_SRC_FILES += jni/src/sqlite/sqlite3.c
+
+# jthread
+LOCAL_SRC_FILES += \
+ jni/src/jthread/pthread/jevent.cpp \
+ jni/src/jthread/pthread/jmutex.cpp \
+ jni/src/jthread/pthread/jsemaphore.cpp \
+ jni/src/jthread/pthread/jthread.cpp
+
+# json
+LOCAL_SRC_FILES += jni/src/json/jsoncpp.cpp
+
+LOCAL_SHARED_LIBRARIES := openal ogg vorbis ssl crypto
+LOCAL_STATIC_LIBRARIES := Irrlicht freetype curl android_native_app_glue $(PROFILER_LIBS)
+# LevelDB
+LOCAL_LDLIBS := -lEGL -llog -lGLESv1_CM -lGLESv2 -lz -landroid
+
+include $(BUILD_SHARED_LIBRARY)
+
+# at the end of Android.mk
+ifdef GPROF
+$(call import-module,android-ndk-profiler)
+endif
+$(call import-module,android/native_app_glue)
diff --git a/build/android/jni/Application.mk b/build/android/jni/Application.mk
new file mode 100644
index 000000000..b7ffc56a1
--- /dev/null
+++ b/build/android/jni/Application.mk
@@ -0,0 +1,8 @@
+# NDK_TOOLCHAIN_VERSION := clang3.3
+
+APP_PLATFORM := android-9
+APP_MODULES := minetest
+APP_STL := gnustl_static
+
+APP_CPPFLAGS += -fexceptions
+APP_GNUSTL_FORCE_CPP_FEATURES := rtti
diff --git a/build/android/libvorbis-libogg-fpu.patch b/build/android/libvorbis-libogg-fpu.patch
new file mode 100644
index 000000000..52ab397ac
--- /dev/null
+++ b/build/android/libvorbis-libogg-fpu.patch
@@ -0,0 +1,37 @@
+--- libvorbis-libogg-android/jni/libvorbis-jni/Android.mk.orig 2014-06-17 19:22:50.621559073 +0200
++++ libvorbis-libogg-android/jni/libvorbis-jni/Android.mk 2014-06-17 19:38:20.641581140 +0200
+@@ -4,9 +4,6 @@
+
+ LOCAL_MODULE := vorbis-jni
+ LOCAL_CFLAGS += -I$(LOCAL_PATH)/../include -fsigned-char
+-ifeq ($(TARGET_ARCH),arm)
+- LOCAL_CFLAGS += -march=armv6 -marm -mfloat-abi=softfp -mfpu=vfp
+-endif
+
+ LOCAL_SHARED_LIBRARIES := libogg libvorbis
+
+--- libvorbis-libogg-android/jni/libvorbis/Android.mk.orig 2014-06-17 19:22:39.077558797 +0200
++++ libvorbis-libogg-android/jni/libvorbis/Android.mk 2014-06-17 19:38:52.121581887 +0200
+@@ -4,9 +4,6 @@
+
+ LOCAL_MODULE := libvorbis
+ LOCAL_CFLAGS += -I$(LOCAL_PATH)/../include -ffast-math -fsigned-char
+-ifeq ($(TARGET_ARCH),arm)
+- LOCAL_CFLAGS += -march=armv6 -marm -mfloat-abi=softfp -mfpu=vfp
+-endif
+ LOCAL_SHARED_LIBRARIES := libogg
+
+ LOCAL_SRC_FILES := \
+--- libvorbis-libogg-android/jni/libogg/Android.mk.orig 2014-06-17 19:22:33.965558675 +0200
++++ libvorbis-libogg-android/jni/libogg/Android.mk 2014-06-17 19:38:25.337581252 +0200
+@@ -4,10 +4,6 @@
+
+ LOCAL_MODULE := libogg
+ LOCAL_CFLAGS += -I$(LOCAL_PATH)/../include -ffast-math -fsigned-char
+-ifeq ($(TARGET_ARCH),arm)
+- LOCAL_CFLAGS += -march=armv6 -marm -mfloat-abi=softfp -mfpu=vfp
+-endif
+-
+
+ LOCAL_SRC_FILES := \
+ bitwise.c \
diff --git a/build/android/project.properties b/build/android/project.properties
new file mode 100644
index 000000000..cc2a7c5cf
--- /dev/null
+++ b/build/android/project.properties
@@ -0,0 +1 @@
+target=android-10
diff --git a/build/android/res/drawable-hdpi/irr_icon.png b/build/android/res/drawable-hdpi/irr_icon.png
new file mode 100644
index 000000000..0b6861a0d
--- /dev/null
+++ b/build/android/res/drawable-hdpi/irr_icon.png
Binary files differ
diff --git a/build/android/res/drawable-ldpi/irr_icon.png b/build/android/res/drawable-ldpi/irr_icon.png
new file mode 100644
index 000000000..b8c5d0177
--- /dev/null
+++ b/build/android/res/drawable-ldpi/irr_icon.png
Binary files differ
diff --git a/build/android/res/drawable-mdpi/irr_icon.png b/build/android/res/drawable-mdpi/irr_icon.png
new file mode 100644
index 000000000..951a7f8c1
--- /dev/null
+++ b/build/android/res/drawable-mdpi/irr_icon.png
Binary files differ
diff --git a/build/android/res/drawable-xhdpi/irr_icon.png b/build/android/res/drawable-xhdpi/irr_icon.png
new file mode 100644
index 000000000..2ec528ef7
--- /dev/null
+++ b/build/android/res/drawable-xhdpi/irr_icon.png
Binary files differ
diff --git a/build/android/res/layout/assetcopy.xml b/build/android/res/layout/assetcopy.xml
new file mode 100644
index 000000000..ade4b0c98
--- /dev/null
+++ b/build/android/res/layout/assetcopy.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:orientation="vertical" >
+
+ <ProgressBar
+ android:id="@+id/progressBar1"
+ style="?android:attr/progressBarStyleHorizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical" />
+
+ <TextView
+ android:id="@+id/textView1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:text="preparing media ..."
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+
+</LinearLayout>
diff --git a/build/android/res/values/styles.xml b/build/android/res/values/styles.xml
new file mode 100644
index 000000000..25b8df5a3
--- /dev/null
+++ b/build/android/res/values/styles.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <style name="Theme.Transparent" parent="android:Theme">
+ <item name="android:windowIsTranslucent">true</item>
+ <item name="android:windowBackground">@android:color/transparent</item>
+ <item name="android:windowContentOverlay">@null</item>
+ <item name="android:windowNoTitle">true</item>
+ <item name="android:windowIsFloating">true</item>
+ <item name="android:backgroundDimEnabled">false</item>
+ </style>
+</resources> \ No newline at end of file
diff --git a/build/android/src/org/minetest/minetest/MinetestAssetCopy.java b/build/android/src/org/minetest/minetest/MinetestAssetCopy.java
new file mode 100644
index 000000000..652a00831
--- /dev/null
+++ b/build/android/src/org/minetest/minetest/MinetestAssetCopy.java
@@ -0,0 +1,288 @@
+package org.minetest.minetest;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.util.Vector;
+
+import android.app.Activity;
+import android.content.res.AssetFileDescriptor;
+
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.os.Environment;
+import android.util.Log;
+import android.view.Display;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+
+public class MinetestAssetCopy extends Activity {
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.assetcopy);
+
+ m_ProgressBar = (ProgressBar) findViewById(R.id.progressBar1);
+ m_Filename = (TextView) findViewById(R.id.textView1);
+
+ Display display = getWindowManager().getDefaultDisplay();
+ m_ProgressBar.getLayoutParams().width = (int) (display.getWidth() * 0.8);
+ m_ProgressBar.invalidate();
+
+ m_AssetCopy = new copyAssetTask();
+ m_AssetCopy.execute();
+ }
+
+ ProgressBar m_ProgressBar;
+ TextView m_Filename;
+
+ copyAssetTask m_AssetCopy;
+
+ private class copyAssetTask extends AsyncTask<String, Integer, String>{
+
+ private void copyElement(String name, String path) {
+ String baseDir = Environment.getExternalStorageDirectory().getAbsolutePath();
+ String full_path;
+ if (path != "") {
+ full_path = path + "/" + name;
+ }
+ else {
+ full_path = name;
+ }
+ //is a folder read asset list
+ if (m_foldernames.contains(full_path)) {
+ m_Foldername = full_path;
+ publishProgress(0);
+ File current_folder = new File(baseDir + "/" + full_path);
+ if (!current_folder.exists()) {
+ if (!current_folder.mkdirs()) {
+ Log.w("MinetestAssetCopy","\t failed create folder: " + baseDir + "/" + full_path);
+ }
+ else {
+ Log.w("MinetestAssetCopy","\t created folder: " + baseDir + "/" + full_path);
+ }
+ }
+ try {
+ String[] current_assets = getAssets().list(full_path);
+ for(int i=0; i < current_assets.length; i++) {
+ copyElement(current_assets[i],full_path);
+ }
+ } catch (IOException e) {
+ Log.w("MinetestAssetCopy","\t failed to read contents of folder");
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+ //is a file just copy
+ else {
+ boolean refresh = true;
+
+ File testme = new File(baseDir + "/" + full_path);
+
+ long asset_filesize = -1;
+ long stored_filesize = -1;
+
+ if (testme.exists()) {
+ try {
+ AssetFileDescriptor fd = getAssets().openFd(full_path);
+ asset_filesize = fd.getLength();
+ fd.close();
+ } catch (IOException e) {
+ refresh = true;
+ m_asset_size_unknown.add(full_path);
+ }
+
+ stored_filesize = testme.length();
+
+ if (asset_filesize == stored_filesize) {
+ refresh = false;
+ }
+
+ }
+
+ if (refresh) {
+ m_tocopy.add(full_path);
+ }
+ }
+ }
+
+ private long getFullSize(String filename) {
+ long size = 0;
+ try {
+ InputStream src = getAssets().open(filename);
+ byte[] buf = new byte[1024];
+
+ int len = 0;
+ while ((len = src.read(buf)) > 0) {
+ size += len;
+ }
+ }
+ catch (IOException e) {
+ e.printStackTrace();
+ }
+ return size;
+ }
+
+ @Override
+ protected String doInBackground(String... files) {
+
+ m_foldernames = new Vector<String>();
+ m_tocopy = new Vector<String>();
+ m_asset_size_unknown = new Vector<String>();
+ String baseDir = Environment.getExternalStorageDirectory().getAbsolutePath() + "/";
+
+ File TempFolder = new File(baseDir + "Minetest/tmp/");
+
+ if (!TempFolder.exists()) {
+ TempFolder.mkdir();
+ }
+ else {
+ File[] todel = TempFolder.listFiles();
+
+ for(int i=0; i < todel.length; i++) {
+ Log.w("MinetestAssetCopy","deleting: " + todel[i].getAbsolutePath());
+ todel[i].delete();
+ }
+ }
+
+ // add a .nomedia file
+ try {
+ OutputStream dst = new FileOutputStream(baseDir + "Minetest/.nomedia");
+ dst.close();
+ } catch (IOException e) {
+ Log.w("MinetestAssetCopy","Failed to create .nomedia file");
+ e.printStackTrace();
+ }
+
+ try {
+ InputStream is = getAssets().open("index.txt");
+ BufferedReader reader = new BufferedReader(new InputStreamReader(is));
+
+ String line = reader.readLine();
+ while(line != null){
+ m_foldernames.add(line);
+ line = reader.readLine();
+ }
+ } catch (IOException e1) {
+ // TODO Auto-generated catch block
+ e1.printStackTrace();
+ }
+
+ copyElement("Minetest","");
+
+ m_copy_started = true;
+ m_ProgressBar.setMax(m_tocopy.size());
+
+ for (int i = 0; i < m_tocopy.size(); i++) {
+ try {
+ String filename = m_tocopy.get(i);
+ publishProgress(i);
+
+ boolean asset_size_unknown = false;
+ long filesize = -1;
+
+ if (m_asset_size_unknown.contains(filename)) {
+ File testme = new File(baseDir + "/" + filename);
+
+ if(testme.exists()) {
+ filesize = testme.length();
+ }
+ asset_size_unknown = true;
+ }
+
+ InputStream src;
+ try {
+ src = getAssets().open(filename);
+ } catch (IOException e) {
+ Log.w("MinetestAssetCopy","Copying file: " + filename + " FAILED (not in assets)");
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ continue;
+ }
+
+ // Transfer bytes from in to out
+ byte[] buf = new byte[1*1024];
+ int len = src.read(buf, 0, 1024);
+
+ /* following handling is crazy but we need to deal with */
+ /* compressed assets.Flash chips limited livetime sue to */
+ /* write operations, we can't allow large files to destroy */
+ /* users flash. */
+ if (asset_size_unknown) {
+ if ( (len > 0) && (len < buf.length) && (len == filesize)) {
+ src.close();
+ continue;
+ }
+
+ if (len == buf.length) {
+ src.close();
+ long size = getFullSize(filename);
+ if ( size == filesize) {
+ continue;
+ }
+ src = getAssets().open(filename);
+ len = src.read(buf, 0, 1024);
+ }
+ }
+ if (len > 0) {
+ int total_filesize = 0;
+ OutputStream dst;
+ try {
+ dst = new FileOutputStream(baseDir + "/" + filename);
+ } catch (IOException e) {
+ Log.w("MinetestAssetCopy","Copying file: " + baseDir +
+ "/" + filename + " FAILED (couldn't open output file)");
+ e.printStackTrace();
+ src.close();
+ continue;
+ }
+ dst.write(buf, 0, len);
+ total_filesize += len;
+
+ while ((len = src.read(buf)) > 0) {
+ dst.write(buf, 0, len);
+ total_filesize += len;
+ }
+
+ dst.close();
+ Log.w("MinetestAssetCopy","Copied file: " + m_tocopy.get(i) + " (" + total_filesize + " bytes)");
+ }
+ else if (len < 0) {
+ Log.w("MinetestAssetCopy","Copying file: " + m_tocopy.get(i) + " failed, size < 0");
+ }
+ src.close();
+ } catch (IOException e) {
+ Log.w("MinetestAssetCopy","Copying file: " + m_tocopy.get(i) + " failed");
+ e.printStackTrace();
+ }
+ }
+
+ return "";
+ }
+
+ protected void onProgressUpdate(Integer... progress) {
+ if (m_copy_started) {
+ m_ProgressBar.setProgress(progress[0]);
+ m_Filename.setText(m_tocopy.get(progress[0]));
+ }
+ else {
+ m_Filename.setText("scanning " + m_Foldername + " ...");
+ }
+ }
+
+ protected void onPostExecute (String result) {
+ finish();
+ }
+ boolean m_copy_started = false;
+ String m_Foldername = "media";
+ Vector<String> m_foldernames;
+ Vector<String> m_tocopy;
+ Vector<String> m_asset_size_unknown;
+ }
+}
diff --git a/build/android/src/org/minetest/minetest/MinetestTextEntry.java b/build/android/src/org/minetest/minetest/MinetestTextEntry.java
new file mode 100644
index 000000000..db175a483
--- /dev/null
+++ b/build/android/src/org/minetest/minetest/MinetestTextEntry.java
@@ -0,0 +1,91 @@
+package org.minetest.minetest;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.os.Bundle;
+import android.text.InputType;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.View.OnKeyListener;
+import android.widget.EditText;
+
+public class MinetestTextEntry extends Activity {
+ public AlertDialog mTextInputDialog;
+ public EditText mTextInputWidget;
+
+ private final int MultiLineTextInput = 1;
+ private final int SingleLineTextInput = 2;
+ private final int SingleLinePasswordInput = 3;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ Bundle b = getIntent().getExtras();
+ String acceptButton = b.getString("EnterButton");
+ String hint = b.getString("hint");
+ String current = b.getString("current");
+ int editType = b.getInt("editType");
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ mTextInputWidget = new EditText(this);
+ mTextInputWidget.setHint(hint);
+ mTextInputWidget.setText(current);
+ mTextInputWidget.setMinWidth(300);
+ if (editType == SingleLinePasswordInput) {
+ mTextInputWidget.setInputType(InputType.TYPE_CLASS_TEXT |
+ InputType.TYPE_TEXT_VARIATION_PASSWORD);
+ }
+ else {
+ mTextInputWidget.setInputType(InputType.TYPE_CLASS_TEXT);
+ }
+
+
+ builder.setView(mTextInputWidget);
+
+ if (editType == MultiLineTextInput) {
+ builder.setPositiveButton(acceptButton, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int whichButton)
+ { pushResult(mTextInputWidget.getText().toString()); }
+ });
+ }
+
+ builder.setOnCancelListener(new DialogInterface.OnCancelListener() {
+ public void onCancel(DialogInterface dialog) {
+ cancelDialog();
+ }
+ });
+
+ mTextInputWidget.setOnKeyListener(new OnKeyListener() {
+ @Override
+ public boolean onKey(View view, int KeyCode, KeyEvent event) {
+ if ( KeyCode == KeyEvent.KEYCODE_ENTER){
+
+ pushResult(mTextInputWidget.getText().toString());
+ return true;
+ }
+ return false;
+ }
+ });
+
+ mTextInputDialog = builder.create();
+ mTextInputDialog.show();
+ }
+
+ public void pushResult(String text) {
+ Intent resultData = new Intent();
+ resultData.putExtra("text", text);
+ setResult(Activity.RESULT_OK,resultData);
+ mTextInputDialog.dismiss();
+ finish();
+ }
+
+ public void cancelDialog() {
+ setResult(Activity.RESULT_CANCELED);
+ mTextInputDialog.dismiss();
+ finish();
+ }
+}
diff --git a/build/android/src/org/minetest/minetest/MtNativeActivity.java b/build/android/src/org/minetest/minetest/MtNativeActivity.java
new file mode 100644
index 000000000..ba7d62169
--- /dev/null
+++ b/build/android/src/org/minetest/minetest/MtNativeActivity.java
@@ -0,0 +1,93 @@
+package org.minetest.minetest;
+
+import android.app.NativeActivity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.WindowManager;
+
+public class MtNativeActivity extends NativeActivity {
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ m_MessagReturnCode = -1;
+ m_MessageReturnValue = "";
+
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ }
+
+
+ public void copyAssets() {
+ Intent intent = new Intent(this, MinetestAssetCopy.class);
+ startActivity(intent);
+ }
+
+ public void showDialog(String acceptButton, String hint, String current,
+ int editType) {
+
+ Intent intent = new Intent(this, MinetestTextEntry.class);
+ Bundle params = new Bundle();
+ params.putString("acceptButton", acceptButton);
+ params.putString("hint", hint);
+ params.putString("current", current);
+ params.putInt("editType", editType);
+ intent.putExtras(params);
+ startActivityForResult(intent, 101);
+ m_MessageReturnValue = "";
+ m_MessagReturnCode = -1;
+ }
+
+ public static native void putMessageBoxResult(String text);
+
+ /* ugly code to workaround putMessageBoxResult not beeing found */
+ public int getDialogState() {
+ return m_MessagReturnCode;
+ }
+
+ public String getDialogValue() {
+ m_MessagReturnCode = -1;
+ return m_MessageReturnValue;
+ }
+
+ public float getDensity() {
+ return getResources().getDisplayMetrics().density;
+ }
+
+ public int getDisplayWidth() {
+ return getResources().getDisplayMetrics().widthPixels;
+ }
+
+ public int getDisplayHeight() {
+ return getResources().getDisplayMetrics().heightPixels;
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode,
+ Intent data) {
+ if (requestCode == 101) {
+ if (resultCode == RESULT_OK) {
+ String text = data.getStringExtra("text");
+ m_MessagReturnCode = 0;
+ m_MessageReturnValue = text;
+ }
+ else {
+ m_MessagReturnCode = 1;
+ }
+ }
+ }
+
+ static {
+ System.loadLibrary("openal");
+ System.loadLibrary("ogg");
+ System.loadLibrary("vorbis");
+ System.loadLibrary("ssl");
+ System.loadLibrary("crypto");
+ }
+
+ private int m_MessagReturnCode;
+ private String m_MessageReturnValue;
+}