Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,50 @@ jobs:
name: opengothic_${{ needs.setup.outputs.semver }}_linux_x64.deb
path: opengothic_${{ needs.setup.outputs.semver }}_linux_x64.deb

android-arm64:
needs: setup
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v6
with:
submodules: recursive

- uses: actions/setup-java@v5
with:
distribution: temurin
java-version: '17'

- uses: gradle/actions/setup-gradle@v5
with:
gradle-version: '8.9'

- uses: ./.github/actions/setup-vulkan
with:
os: Linux
vk-sdk: ${{ env.VK_SDK }}

- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y glslang-tools ninja-build
"$ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager" "platforms;android-35" "build-tools;35.0.0" "ndk;27.0.12077973" "cmake;3.22.1"
- name: Generate build version
run: |
echo '#pragma once' > game/build.h
echo "static const char* appBuild = \"${{ needs.setup.outputs.version }}\";" >> game/build.h
- name: Configure
run: |
"$ANDROID_HOME/cmake/3.22.1/bin/cmake" -S . -B build/android -G Ninja \
-DCMAKE_TOOLCHAIN_FILE="$ANDROID_HOME/ndk/27.0.12077973/build/cmake/android.toolchain.cmake" \
-DANDROID_ABI=arm64-v8a -DANDROID_PLATFORM=android-24 \
-DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }}
- name: Build APK
run: |
"$ANDROID_HOME/cmake/3.22.1/bin/cmake" --build build/android --target OpenGothic-apk
windows:
needs: setup
runs-on: windows-latest
Expand Down
25 changes: 21 additions & 4 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@ set(CMAKE_POSITION_INDEPENDENT_CODE ON)
set(CMAKE_SKIP_RPATH ON)

add_library(${PROJECT_NAME} STATIC)
add_executable(Gothic2Notr)
if(ANDROID)
add_library(Gothic2Notr SHARED)
else()
add_executable(Gothic2Notr)
endif()
add_executable(Spacer)

if(MSVC)
Expand Down Expand Up @@ -92,6 +96,8 @@ endif()

if(WIN32)
target_link_libraries(${PROJECT_NAME} shlwapi DbgHelp)
elseif(ANDROID)
target_link_libraries(${PROJECT_NAME} dl)
elseif(UNIX)
target_link_libraries(${PROJECT_NAME} -lpthread -ldl)
endif()
Expand Down Expand Up @@ -178,7 +184,7 @@ if(WIN32)
endif()

# in debug mode, enable sanitizers
if(${CMAKE_BUILD_TYPE} MATCHES "Debug")
if(${CMAKE_BUILD_TYPE} MATCHES "Debug" AND NOT ANDROID)
add_compile_options(-fsanitize=address)
add_compile_options(-fsanitize=leak)
add_link_options (-fsanitize=address)
Expand All @@ -197,13 +203,24 @@ if(IOS)
XCODE_ATTRIBUTE_CODE_SIGN_IDENTITY "iPhone Developer")
endif()

target_sources(Gothic2Notr PRIVATE game/main.cpp icon.rc)
target_sources(Gothic2Notr PRIVATE game/main.cpp)
if(WIN32)
target_sources(Gothic2Notr PRIVATE icon.rc)
endif()
target_link_libraries(Gothic2Notr PRIVATE OpenGothic)

# editor
target_sources(Spacer PRIVATE ${SPACER_SOURCES} spacer/main.cpp)
target_include_directories(Spacer PRIVATE "spacer")
target_link_libraries(Spacer PRIVATE OpenGothic SpacerAssets)

if(ANDROID)
add_android_apk(OpenGothic-apk
PACKAGE_NAME org.opengothic.app
CODE Gothic2Notr
MANIFEST android/AndroidManifest.xml)
endif()

# installation
install(TARGETS Gothic2Notr Spacer DESTINATION bin)
install(TARGETS Gothic2Notr DESTINATION bin)

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this change has no effect

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Restored the single install(TARGETS Gothic2Notr Spacer DESTINATION bin) call.

install(TARGETS Spacer DESTINATION bin)
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,10 @@ There are ongoing efforts to support parts of it to make at least some popular m
- Ninja

## Build Instructions
### Android

The basic ARM64 APK build and ADB game-file setup are documented in [android/README.md](android/README.md).
Comment thread
Try marked this conversation as resolved.
Outdated

### Linux
Install dependencies:
* Ubuntu 20.04/22.04 and their derived distros
Expand Down
17 changes: 17 additions & 0 deletions android/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-feature android:name="android.hardware.vulkan.level" android:version="0" android:required="true" />
<uses-feature android:name="android.hardware.vulkan.version" android:version="0x00401000" android:required="true" />
<application android:label="OpenGothic" android:hasCode="false" android:allowBackup="false"
android:theme="@android:style/Theme.Material.NoActionBar.Fullscreen">
<activity android:name="android.app.NativeActivity" android:exported="true"
android:screenOrientation="landscape"
android:configChanges="keyboard|keyboardHidden|navigation|orientation|screenSize|screenLayout|smallestScreenSize|density|uiMode">
<meta-data android:name="android.app.lib_name" android:value="Gothic2Notr" />
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
32 changes: 32 additions & 0 deletions android/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Android

An ARM64 NativeActivity APK using Tempest's Android backend and generated Gradle project. This is the basic platform integration; touch controls, controller input and game-file importing are not included yet. Android window destruction closes the application, including when backgrounding or locking destroys its surface.

## Build

Install JDK 17, Gradle 8.9, CMake 3.22.1 or newer, Ninja and the Android SDK with SDK 35, build-tools 35.0.0, NDK 27.0.12077973 and SDK CMake 3.22.1. Set `JAVA_HOME`, `ANDROID_HOME` and `VULKAN_SDK`. Put Gradle, Ninja and the host Vulkan SDK's `glslangValidator` on `PATH`. Vulkan headers come from the host SDK; Android links the NDK's Vulkan loader.

Clone with `git clone --recursive https://github.com/Try/OpenGothic.git`, or run `git submodule update --init --recursive` in an existing checkout. Replace `/path/to/ndk` below with the NDK installation directory. The same commands work in PowerShell and a Linux shell.

```sh
cmake -S . -B build/android -G Ninja -DCMAKE_TOOLCHAIN_FILE=/path/to/ndk/build/cmake/android.toolchain.cmake -DANDROID_ABI=arm64-v8a -DANDROID_PLATFORM=android-24 -DCMAKE_BUILD_TYPE=Release
cmake --build build/android --target OpenGothic-apk
```

APK: `build/android/OpenGothic-apk/build/outputs/apk/release/OpenGothic-apk-release.apk`. Use `-DCMAKE_BUILD_TYPE=Debug` for a debuggable APK under `outputs/apk/debug/OpenGothic-apk-debug.apk`. Desktop builds do not invoke the APK helper or require Android tools.

## Install and game files

Use a legally owned Gothic II: Night of the Raven installation. No game files are included in the APK. Copy the installation root, not only its `Data` directory: `Data`, `_work` and `System` must be directly inside `Gothic2`.

```sh
adb install -r build/android/OpenGothic-apk/build/outputs/apk/release/OpenGothic-apk-release.apk
adb shell mkdir -p /sdcard/Android/data/org.opengothic.app/files/Gothic2
adb push "/path/to/Gothic II/." /sdcard/Android/data/org.opengothic.app/files/Gothic2/
adb shell am start -n org.opengothic.app/android.app.NativeActivity
adb logcat -s OpenGothic AndroidRuntime DEBUG
```

Game files use app-specific external storage, without storage permissions. Logs, saves and writable settings use the app's internal files directory. Uninstalling removes both directories. Ray tracing and mesh shading are disabled for this initial Android build.

With a debug APK, read the file log using `adb shell run-as org.opengothic.app cat files/log.txt`.
2 changes: 1 addition & 1 deletion common/commandline.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ class CommandLine {
Benchmark isBenchmark = Benchmark::None;
bool isWindow = false;
bool isDebug = false;
#if defined(__OSX__)
#if defined(__OSX__) || defined(__ANDROID__)
bool isRQuery = false;
bool isMeshSh = false;
#else
Expand Down
40 changes: 36 additions & 4 deletions common/utils/crashlog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,17 @@
#include <fstream>
#include <cstring>

#if defined(__ANDROID__)
#include <cstdint>
#include <dlfcn.h> // dladdr
#include <unwind.h> // _Unwind_Backtrace
#endif

#if defined(__cpp_lib_stacktrace)
#include <stacktrace>
#endif

#if defined(__LINUX__) || defined(__APPLE__)
#if (defined(__LINUX__) && !defined(__ANDROID__)) || defined(__APPLE__)
#include <execinfo.h> // backtrace
#include <dlfcn.h> // dladdr
#include <cxxabi.h> // __cxa_demangle
Expand Down Expand Up @@ -136,7 +142,7 @@ void CrashLog::dumpStack(const char *sig, const char *extGpuLog) {
#elif defined(__WINDOWS__)
traceback.collect(0);
traceback.log(db, std::cout);
#elif defined(__LINUX__) || defined(__APPLE__)
#elif defined(__LINUX__) || defined(__ANDROID__) || defined(__APPLE__)
tracebackLinux(std::cout);
#endif
std::cout << std::endl;
Expand All @@ -150,7 +156,7 @@ void CrashLog::dumpStack(const char *sig, const char *extGpuLog) {
tracebackStd(fout);
#elif defined(__WINDOWS__)
traceback.log(db, fout);
#elif defined(__LINUX__) || defined(__APPLE__)
#elif defined(__LINUX__) || defined(__ANDROID__) || defined(__APPLE__)
tracebackLinux(fout);
#endif
fout.flush();
Expand All @@ -169,7 +175,33 @@ void CrashLog::tracebackStd(std::ostream &out) {
}

void CrashLog::tracebackLinux(std::ostream &out) {
#if defined(__LINUX__) || defined(__APPLE__)
#if defined(__ANDROID__)
Comment thread
Try marked this conversation as resolved.
struct Backtrace {
void* frames[64] = {};
size_t count = 0;
} trace;
_Unwind_Backtrace([](_Unwind_Context* context, void* arg) {
auto& trace = *static_cast<Backtrace*>(arg);
const uintptr_t pc = _Unwind_GetIP(context);
if(pc!=0)
trace.frames[trace.count++] = reinterpret_cast<void*>(pc);
return trace.count==64 ? _URC_END_OF_STACK : _URC_NO_REASON;
}, &trace);

for(size_t i=0; i<trace.count; ++i) {
Dl_info info = {};
out << "#" << i+1 << ": ";
if(dladdr(trace.frames[i], &info)) {
const uintptr_t offset = reinterpret_cast<uintptr_t>(trace.frames[i])-reinterpret_cast<uintptr_t>(info.dli_fbase);
out << (info.dli_sname ? info.dli_sname : "?") << " - " << (info.dli_fname ? info.dli_fname : "?");
out << " + 0x" << std::hex << offset << std::dec;
}
else {
out << trace.frames[i];
}
out << std::endl;
}
#elif defined(__LINUX__) || defined(__APPLE__)
// inspired by https://gist.github.com/fmela/591333/36faca4c2f68f7483cd0d3a357e8a8dd5f807edf (BSD)
void *callstack[64] = {};
char **symbols = nullptr;
Expand Down
21 changes: 21 additions & 0 deletions common/utils/installdetect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

#include <Tempest/Platform>

#ifdef __ANDROID__
#include <Tempest/AndroidApi>
#include <Tempest/TextCodec>
#include <filesystem>
#endif

#ifdef __WINDOWS__
#include "windows.h"
#include "shlobj.h"
Expand Down Expand Up @@ -31,11 +37,26 @@ std::u16string InstallDetect::detectG2() {
if(FileUtil::exists(appDir))
return appDir;
return u"";
#elif defined(__ANDROID__)
auto path = Tempest::AndroidApi::externalDataPath();
if(path.empty())
return u"";
path += "/Gothic2";
std::filesystem::create_directories(path);
return Tempest::TextCodec::toUtf16(path);
#else
return u"";
#endif
}

#ifdef __ANDROID__
std::u16string InstallDetect::applicationSupportDirectory() {
Comment thread
Try marked this conversation as resolved.
Outdated
auto path = Tempest::AndroidApi::internalDataPath();
std::filesystem::create_directories(path);
return Tempest::TextCodec::toUtf16(path);
}
#endif

std::u16string InstallDetect::detectG2(std::u16string pfiles) {
if(pfiles.empty())
return u"";
Expand Down
2 changes: 1 addition & 1 deletion common/utils/installdetect.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ class InstallDetect final {
InstallDetect();

std::u16string detectG2();
#if defined(__OSX__) || defined(__IOS__)
#if defined(__OSX__) || defined(__IOS__) || defined(__ANDROID__)
static std::u16string applicationSupportDirectory();
#endif

Expand Down
13 changes: 11 additions & 2 deletions game/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,13 @@
#include <Tempest/MetalApi>
#endif

#if defined(__IOS__)
#if defined(__IOS__) || defined(__ANDROID__)
#include "utils/installdetect.h"
#include <filesystem>
#endif

#if defined(__ANDROID__)
#include <android/log.h>
#endif

#include "utils/crashlog.h"
Expand Down Expand Up @@ -68,7 +73,7 @@ std::unique_ptr<Tempest::AbstractGraphicsApi> mkApi(const CommandLine& g) {
}

int main(int argc,const char** argv) {
#if defined(__IOS__)
#if defined(__IOS__) || defined(__ANDROID__)
{
auto appdir = InstallDetect::applicationSupportDirectory();
std::filesystem::current_path(appdir);
Expand All @@ -78,6 +83,10 @@ int main(int argc,const char** argv) {
try {
static Tempest::WFile logFile("log.txt");
Tempest::Log::setOutputCallback([](Tempest::Log::Mode mode, const char* text) {
#if defined(__ANDROID__)
const int priority = mode==Tempest::Log::Error ? ANDROID_LOG_ERROR : mode==Tempest::Log::Debug ? ANDROID_LOG_DEBUG : ANDROID_LOG_INFO;
__android_log_write(priority,"OpenGothic",text);

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tempest::Log already implements __android_log_print, so application never really need to relay on OS-specific api

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed the application-side Android logging call. Tempest::Log handles logcat output; the README now filters its app tag.

#endif
logFile.write(text,std::strlen(text));
logFile.write("\n",1);
if(mode==Tempest::Log::Error)
Expand Down
2 changes: 1 addition & 1 deletion shader/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ file(GLOB_RECURSE GLSL_SOURCES
"${CMAKE_SOURCE_DIR}/shader/*.glsl")

# GLSL to SPIRV compiler
find_program(GLSLANGVALIDATOR glslangValidator "/opt/homebrew/bin")
find_program(GLSLANGVALIDATOR glslangValidator "/opt/homebrew/bin" NO_CMAKE_FIND_ROOT_PATH)
if(NOT GLSLANGVALIDATOR)
message(FATAL_ERROR "glslangValidator required")
endif()
Expand Down
Loading