diff --git a/.gitattributes b/.gitattributes
index 60957e6..605224a 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -2,14 +2,23 @@
*.c text eol=lf
*.cc text eol=lf
*.cmake text eol=lf
+*.gradle text eol=lf
*.h text eol=lf
-*.md text eol=lf
+*.java text eol=lf
*.json text eol=lf
+*.md text eol=lf
*.plist text eol=lf
+*.pro text eol=lf
+*.properties text eol=lf
+*.xml text eol=lf
*.yml text eol=lf
.clang-format text eol=lf
.editorconfig text eol=lf
.gitattributes text eol=lf
.gitignore text eol=lf
+gradlew text eol=lf
CMakeLists.txt text eol=lf
LICENSE text eol=lf
+
+# Force CRLF
+*.bat text eol=crlf
diff --git a/.github/workflows/ci-build.yml b/.github/workflows/ci-build.yml
index f12d1c5..aefe72a 100644
--- a/.github/workflows/ci-build.yml
+++ b/.github/workflows/ci-build.yml
@@ -38,6 +38,49 @@ jobs:
- name: cppcheck
run: cppcheck --std=c++17 src/
+ android:
+ name: Android
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Clone
+ uses: actions/checkout@v3
+
+ - name: Setup Java
+ uses: actions/setup-java@v2
+ with:
+ distribution: temurin
+ java-version: 11
+ cache: gradle
+
+ - name: Cache cmake build
+ uses: actions/cache@v3
+ with:
+ path: os/android/app/.cxx
+ key: android-cmake-v1
+
+ - name: Setup signing config
+ run: |
+ cd os/android
+ echo "$KEYSTORE_FILE_BASE64" | base64 --decode > debug.keystore
+ echo "$KEYSTORE_PROPERTIES_FILE_BASE64" | base64 --decode > debug-keystore.properties
+ env:
+ KEYSTORE_FILE_BASE64: ${{ secrets.ANDROID_DEBUG_KEYSTORE_FILE_BASE64 }}
+ KEYSTORE_PROPERTIES_FILE_BASE64: ${{ secrets.ANDROID_DEBUG_KEYSTORE_PROPERTIES_FILE_BASE64 }}
+
+ - name: Build
+ run: |
+ cd os/android
+ ./gradlew assembleDebug
+
+ - name: Upload
+ uses: actions/upload-artifact@v3
+ with:
+ name: fallout2-ce-debug.apk
+ path: os/android/app/build/outputs/apk/debug/app-debug.apk
+ retention-days: 7
+
linux:
name: Linux (${{ matrix.arch }})
diff --git a/os/android/.gitignore b/os/android/.gitignore
index cf7a227..f0f2a29 100644
--- a/os/android/.gitignore
+++ b/os/android/.gitignore
@@ -7,3 +7,7 @@
/.idea/navEditor.xml
/.idea/workspace.xml
/local.properties
+/debug-keystore.properties
+/debug.keystore
+/release-keystore.properties
+/release.keystore
diff --git a/os/android/app/.gitignore b/os/android/app/.gitignore
index 8268dac..3a72371 100644
--- a/os/android/app/.gitignore
+++ b/os/android/app/.gitignore
@@ -1,2 +1,5 @@
/.cxx
/build
+
+# TODO: Cleanup root .gitignore
+!/src/debug
diff --git a/os/android/app/build.gradle b/os/android/app/build.gradle
index 633ebdf..e5f4ff0 100644
--- a/os/android/app/build.gradle
+++ b/os/android/app/build.gradle
@@ -26,10 +26,49 @@ android {
}
}
+ signingConfigs {
+ // Override default debug signing config to make sure every CI runner
+ // uses the same key for painless updates.
+ def debugKeystorePropertiesFile = rootProject.file('debug-keystore.properties')
+ if (debugKeystorePropertiesFile.exists()) {
+ def debugKeystoreProperties = new Properties()
+ debugKeystoreProperties.load(new FileInputStream(debugKeystorePropertiesFile))
+
+ debug {
+ storeFile rootProject.file(debugKeystoreProperties.getProperty('storeFile'))
+ storePassword debugKeystoreProperties.getProperty('storePassword')
+ keyAlias debugKeystoreProperties.getProperty('keyAlias')
+ keyPassword debugKeystoreProperties.getProperty('keyPassword')
+ }
+ }
+
+ def releaseKeystoreProperties = new Properties()
+ def releaseKeystorePropertiesFile = rootProject.file('release-keystore.properties')
+ if (releaseKeystorePropertiesFile.exists()) {
+ releaseKeystoreProperties.load(new FileInputStream(releaseKeystorePropertiesFile))
+
+ release {
+ storeFile rootProject.file(releaseKeystoreProperties.getProperty('storeFile'))
+ storePassword releaseKeystoreProperties.getProperty('storePassword')
+ keyAlias releaseKeystoreProperties.getProperty('keyAlias')
+ keyPassword releaseKeystoreProperties.getProperty('keyPassword')
+ }
+ }
+ }
+
buildTypes {
+ debug {
+ // Prevents signing keys clashes between debug and release versions
+ // for painless development.
+ applicationIdSuffix '.debug'
+ }
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+
+ // Release signing config is optional and might not be present in CI
+ // builds, hence `findByName`.
+ signingConfig signingConfigs.findByName('release')
}
}
diff --git a/os/android/app/src/debug/res/values/strings.xml b/os/android/app/src/debug/res/values/strings.xml
new file mode 100644
index 0000000..80ed3dc
--- /dev/null
+++ b/os/android/app/src/debug/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ Fallout 2 (Debug)
+
diff --git a/os/android/app/src/main/res/values/strings.xml b/os/android/app/src/main/res/values/strings.xml
index 6ea845c..03e515a 100644
--- a/os/android/app/src/main/res/values/strings.xml
+++ b/os/android/app/src/main/res/values/strings.xml
@@ -1,3 +1,3 @@
- Fallout 2 Community Edition
+ Fallout 2
diff --git a/src/core.cc b/src/core.cc
index 274c141..284d34f 100644
--- a/src/core.cc
+++ b/src/core.cc
@@ -1272,6 +1272,11 @@ void _GNW95_process_message()
// The data is accumulated in SDL itself and will be processed
// in `_mouse_info`.
break;
+ case SDL_FINGERDOWN:
+ case SDL_FINGERMOTION:
+ case SDL_FINGERUP:
+ handleTouchFingerEvent(&(e.tfinger));
+ break;
case SDL_KEYDOWN:
case SDL_KEYUP:
if (!keyboardIsDisabled()) {
diff --git a/src/dinput.cc b/src/dinput.cc
index b72987b..5272ccc 100644
--- a/src/dinput.cc
+++ b/src/dinput.cc
@@ -1,6 +1,18 @@
#include "dinput.h"
-#include
+static int gTouchMouseLastX = 0;
+static int gTouchMouseLastY = 0;
+static int gTouchMouseDeltaX = 0;
+static int gTouchMouseDeltaY = 0;
+
+static int gTouchFingers = 0;
+static unsigned int gTouchGestureLastTouchDownTimestamp = 0;
+static unsigned int gTouchGestureLastTouchUpTimestamp = 0;
+static int gTouchGestureTaps = 0;
+static bool gTouchGestureHandled = false;
+
+extern int screenGetWidth();
+extern int screenGetHeight();
// 0x4E0400
bool directInputInit()
@@ -47,9 +59,42 @@ bool mouseDeviceUnacquire()
// 0x4E053C
bool mouseDeviceGetData(MouseData* mouseState)
{
+#if __ANDROID__
+ mouseState->x = gTouchMouseDeltaX;
+ mouseState->y = gTouchMouseDeltaY;
+ mouseState->buttons[0] = 0;
+ mouseState->buttons[1] = 0;
+ gTouchMouseDeltaX = 0;
+ gTouchMouseDeltaY = 0;
+
+ if (gTouchFingers == 0) {
+ if (SDL_GetTicks() - gTouchGestureLastTouchUpTimestamp > 150) {
+ if (!gTouchGestureHandled) {
+ if (gTouchGestureTaps == 2) {
+ mouseState->buttons[0] = 1;
+ gTouchGestureHandled = true;
+ } else if (gTouchGestureTaps == 3) {
+ mouseState->buttons[1] = 1;
+ gTouchGestureHandled = true;
+ }
+ }
+ }
+ } else if (gTouchFingers == 1) {
+ if (SDL_GetTicks() - gTouchGestureLastTouchDownTimestamp > 150) {
+ if (gTouchGestureTaps == 1) {
+ mouseState->buttons[0] = 1;
+ gTouchGestureHandled = true;
+ } else if (gTouchGestureTaps == 2) {
+ mouseState->buttons[1] = 1;
+ gTouchGestureHandled = true;
+ }
+ }
+ }
+#else
Uint32 buttons = SDL_GetRelativeMouseState(&(mouseState->x), &(mouseState->y));
mouseState->buttons[0] = (buttons & SDL_BUTTON(SDL_BUTTON_LEFT)) != 0;
mouseState->buttons[1] = (buttons & SDL_BUTTON(SDL_BUTTON_RIGHT)) != 0;
+#endif
return true;
}
@@ -100,3 +145,44 @@ bool keyboardDeviceInit()
void keyboardDeviceFree()
{
}
+
+void handleTouchFingerEvent(SDL_TouchFingerEvent* event)
+{
+ int windowWidth = screenGetWidth();
+ int windowHeight = screenGetHeight();
+
+ if (event->type == SDL_FINGERDOWN) {
+ gTouchFingers++;
+
+ gTouchMouseLastX = (int)(event->x * windowWidth);
+ gTouchMouseLastY = (int)(event->y * windowHeight);
+ gTouchMouseDeltaX = 0;
+ gTouchMouseDeltaY = 0;
+
+ if (event->timestamp - gTouchGestureLastTouchDownTimestamp > 250) {
+ gTouchGestureTaps = 0;
+ gTouchGestureHandled = false;
+ }
+
+ gTouchGestureLastTouchDownTimestamp = event->timestamp;
+ } else if (event->type == SDL_FINGERMOTION) {
+ int prevX = gTouchMouseLastX;
+ int prevY = gTouchMouseLastY;
+ gTouchMouseLastX = (int)(event->x * windowWidth);
+ gTouchMouseLastY = (int)(event->y * windowHeight);
+ gTouchMouseDeltaX += gTouchMouseLastX - prevX;
+ gTouchMouseDeltaY += gTouchMouseLastY - prevY;
+ } else if (event->type == SDL_FINGERUP) {
+ gTouchFingers--;
+
+ int prevX = gTouchMouseLastX;
+ int prevY = gTouchMouseLastY;
+ gTouchMouseLastX = (int)(event->x * windowWidth);
+ gTouchMouseLastY = (int)(event->y * windowHeight);
+ gTouchMouseDeltaX += gTouchMouseLastX - prevX;
+ gTouchMouseDeltaY += gTouchMouseLastY - prevY;
+
+ gTouchGestureTaps++;
+ gTouchGestureLastTouchUpTimestamp = event->timestamp;
+ }
+}
diff --git a/src/dinput.h b/src/dinput.h
index 113e998..7cb8144 100644
--- a/src/dinput.h
+++ b/src/dinput.h
@@ -1,6 +1,8 @@
#ifndef DINPUT_H
#define DINPUT_H
+#include
+
typedef struct MouseData {
int x;
int y;
@@ -26,4 +28,6 @@ void mouseDeviceFree();
bool keyboardDeviceInit();
void keyboardDeviceFree();
+void handleTouchFingerEvent(SDL_TouchFingerEvent* event);
+
#endif /* DINPUT_H */
diff --git a/src/win32.cc b/src/win32.cc
index 70c6747..8b4ab30 100644
--- a/src/win32.cc
+++ b/src/win32.cc
@@ -40,6 +40,8 @@ int main(int argc, char* argv[])
#endif
#if __ANDROID__
+ SDL_SetHint(SDL_HINT_MOUSE_TOUCH_EVENTS, "0");
+ SDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS, "0");
chdir(SDL_AndroidGetExternalStoragePath());
#endif