diff --git a/CMakeLists.txt b/CMakeLists.txt index 0b765474106..a16d9b265fe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -47,6 +47,15 @@ endif() PROJECT(FS2_Open LANGUAGES ${FSO_LANGUAGES}) +if (ANDROID) + message(STATUS "Building for Android (NDK)") + add_definitions(-DPLATFORM_ANDROID) + set(FSO_BUILD_INCLUDED_LIBS ON FORCE) + set(USING_PREBUILT_LIBS ON FORCE) + set(SDL2_USE_PRECOMPILED ON FORCE) + set(OPENAL_USE_PRECOMPILED ON FORCE) +endif() + # Check if the external modules exists IF(NOT IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/cmake/external/rpavlik-cmake-modules/launcher-templates") message(FATAL_ERROR "External submodules could not be found. Please make sure you have updated your submodules.") @@ -101,8 +110,8 @@ OPTION(FSO_BUILD_TESTS "Build unit tests" OFF) OPTION(FSO_DEVELOPMENT_MODE "Generate binaries in development mode, only use if you know what you're doing!" OFF) -IF(WIN32 OR APPLE) - # On windows and mac the default should be to always build the included libraries +IF(WIN32 OR APPLE OR ANDROID) + # On windows, mac and android the default should be to always build the included libraries SET(FSO_BUILD_INCLUDED_LIBS_DEFAULT ON) ELSE() SET(FSO_BUILD_INCLUDED_LIBS_DEFAULT OFF) diff --git a/code/cfile/cfile.cpp b/code/cfile/cfile.cpp index 12f6df421a9..cbc52a25c10 100644 --- a/code/cfile/cfile.cpp +++ b/code/cfile/cfile.cpp @@ -185,7 +185,12 @@ int cfile_init(const char *exe_dir, const char *cdrom_dir) char buf[CFILE_ROOT_DIRECTORY_LEN]; - strncpy(buf, exe_dir, CFILE_ROOT_DIRECTORY_LEN - 1); + #ifndef __ANDROID__ + strncpy(buf, exe_dir, CFILE_ROOT_DIRECTORY_LEN - 1); + #else + (void)exe_dir; + strncpy(buf, os_get_working_folder_path().c_str(), CFILE_ROOT_DIRECTORY_LEN - 1); + #endif buf[CFILE_ROOT_DIRECTORY_LEN - 1] = '\0'; diff --git a/code/cmdline/cmdline.cpp b/code/cmdline/cmdline.cpp index c68a29bdcd9..22b81dc53eb 100644 --- a/code/cmdline/cmdline.cpp +++ b/code/cmdline/cmdline.cpp @@ -74,6 +74,7 @@ const char *cmdline_arg_types[] = enum class flag_output_type { Binary, Json_V1, + Json_V2, }; // variables @@ -1352,51 +1353,58 @@ static void handle_unix_modlist(char **modlist, size_t *len) // external entry point into this modules -static json_t* json_get_v1() { - auto root = json_object(); +static void json_add_version(json_t* root) { + auto version_obj = json_object(); - { - auto version_obj = json_object(); + json_object_set_new(version_obj, "full", json_string(FS_VERSION_FULL)); + json_object_set_new(version_obj, "major", json_integer(FS_VERSION_MAJOR)); + json_object_set_new(version_obj, "minor", json_integer(FS_VERSION_MINOR)); + json_object_set_new(version_obj, "build", json_integer(FS_VERSION_BUILD)); + + json_object_set_new(version_obj, "has_revision", json_boolean(FS_VERSION_HAS_REVIS)); + json_object_set_new(version_obj, "revision", json_integer(FS_VERSION_REVIS)); + json_object_set_new(version_obj, "revision_str", json_string(FS_VERSION_REVIS_STR)); - json_object_set_new(version_obj, "full", json_string(FS_VERSION_FULL)); - json_object_set_new(version_obj, "major", json_integer(FS_VERSION_MAJOR)); - json_object_set_new(version_obj, "minor", json_integer(FS_VERSION_MINOR)); - json_object_set_new(version_obj, "build", json_integer(FS_VERSION_BUILD)); + json_object_set_new(root, "version", version_obj); +} - json_object_set_new(version_obj, "has_revision", json_boolean(FS_VERSION_HAS_REVIS)); - json_object_set_new(version_obj, "revision", json_integer(FS_VERSION_REVIS)); - json_object_set_new(version_obj, "revision_str", json_string(FS_VERSION_REVIS_STR)); +static void json_add_easy_flags(json_t* root) { + auto easy_array = json_array(); - json_object_set_new(root, "version", version_obj); + for (auto& easy_flag : easy_flags) { + json_array_append_new(easy_array, json_string(easy_flag.name)); } - { - auto easy_array = json_array(); - for (auto& easy_flag : easy_flags) { - json_array_append_new(easy_array, json_string(easy_flag.name)); - } + json_object_set_new(root, "easy_flags", easy_array); +} - json_object_set_new(root, "easy_flags", easy_array); +static void json_add_flags(json_t* root) { + auto flags_array = json_array(); + + for (auto& flag : exe_params) { + auto flag_obj = json_object(); + + json_object_set_new(flag_obj, "name", json_string(flag.name)); + json_object_set_new(flag_obj, "description", json_string(flag.desc)); + json_object_set_new(flag_obj, "fso_only", json_boolean(flag.fso_only)); + json_object_set_new(flag_obj, "on_flags", json_integer(flag.on_flags)); + json_object_set_new(flag_obj, "off_flags", json_integer(flag.off_flags)); + json_object_set_new(flag_obj, "type", json_string(flag.type)); + json_object_set_new(flag_obj, "web_url", json_string(flag.web_url)); + + json_array_append_new(flags_array, flag_obj); } - { - auto flags_array = json_array(); - for (auto& flag : exe_params) { - auto flag_obj = json_object(); + json_object_set_new(root, "flags", flags_array); +} - json_object_set_new(flag_obj, "name", json_string(flag.name)); - json_object_set_new(flag_obj, "description", json_string(flag.desc)); - json_object_set_new(flag_obj, "fso_only", json_boolean(flag.fso_only)); - json_object_set_new(flag_obj, "on_flags", json_integer(flag.on_flags)); - json_object_set_new(flag_obj, "off_flags", json_integer(flag.off_flags)); - json_object_set_new(flag_obj, "type", json_string(flag.type)); - json_object_set_new(flag_obj, "web_url", json_string(flag.web_url)); +static json_t* json_get_v1() { + auto root = json_object(); - json_array_append_new(flags_array, flag_obj); - } + json_add_version(root); + json_add_easy_flags(root); + json_add_flags(root); - json_object_set_new(root, "flags", flags_array); - } { auto caps_array = json_array(); @@ -1507,6 +1515,18 @@ static json_t* json_get_v1() { return root; } +// json_v2 is a minimal json string to report supported flags only +static json_t* json_get_v2() { + auto root = json_object(); + + json_add_version(root); + json_add_easy_flags(root); + json_add_flags(root); + json_object_set_new(root, "pref_path", json_string(os_get_config_path().c_str())); + + return root; +} + static void write_flags_file() { FILE *fp = fopen("flags.lch","w"); @@ -1564,6 +1584,8 @@ static flag_output_type get_flags_output_type() { return flag_output_type::Binary; } else if (type == "json_v1") { return flag_output_type::Json_V1; + } else if (type == "json_v2") { + return flag_output_type::Json_V2; } else { // This is supposed to make it easy for the launcher to recognize an unsupported type printf("OUTPUT TYPE NOT SUPPORTED!\n"); @@ -1574,17 +1596,23 @@ static flag_output_type get_flags_output_type() { static void write_flags() { auto type = get_flags_output_type(); - switch(type) { - case flag_output_type::Binary: - write_flags_file(); - break; - case flag_output_type::Json_V1: - json_t* root = json_get_v1(); - json_dumpf(root, stdout, JSON_INDENT(4)); - json_decref(root); - break; + if (type == flag_output_type::Binary) { + write_flags_file(); + return; } + + json_t* root = (type == flag_output_type::Json_V1) ? json_get_v1() : json_get_v2(); + +#ifdef __ANDROID__ + char* dumped = json_dumps(root, JSON_INDENT(4)); + os_set_flags_string(dumped ? dumped : ""); + free(dumped); +#else + json_dumpf(root, stdout, JSON_INDENT(4)); +#endif + + json_decref(root); } bool SetCmdlineParams() diff --git a/code/ddsutils/ddsutils.cpp b/code/ddsutils/ddsutils.cpp index 8bf48b198a6..3e3fcc0f2d7 100644 --- a/code/ddsutils/ddsutils.cpp +++ b/code/ddsutils/ddsutils.cpp @@ -74,6 +74,28 @@ static bool conversion_needed(const DDS_HEADER &dds_header) return false; } +#ifdef __ANDROID__ +// Auto-determine max texture size based on avail device ram for software decompression +// 128 for <4GB, 256 for < 8GB, 512 for < 12GB and finally 1024 +static size_t calculate_resize_max_size() +{ + size_t size = 1024; + size_t ram_mib = SDL_GetSystemRAM(); + + if (ram_mib > 0) + { + if (ram_mib < 4 * 1024) // < 4GB + return 128; + if (ram_mib < 8 * 1024) // < 8GB + return 256; + if (ram_mib < 12 * 1024) // < 12GB + return 512; + } + + return size; +} +#endif + // Memory usage for uncompressed textures is quite high. Some MVP assets can // require well over a GB of VRAM for a single ship after conversion. To help // alleviate this we need to resize those textures where possible. At 1024x1024 @@ -85,7 +107,11 @@ static bool conversion_needed(const DDS_HEADER &dds_header) // returns: number of mipmap levels to skip static uint conversion_resize(DDS_HEADER &dds_header) { +#ifdef __ANDROID__ + const size_t MAX_SIZE = calculate_resize_max_size(); +#else const size_t MAX_SIZE = 1024; +#endif uint width, height, depth, offset = 0; if (dds_header.dwMipMapCount <= 1) { diff --git a/code/external_dll/externalcode.h b/code/external_dll/externalcode.h index eebdee4cae2..1b82f340199 100644 --- a/code/external_dll/externalcode.h +++ b/code/external_dll/externalcode.h @@ -29,6 +29,23 @@ class SCP_ExternalCode return FALSE; m_library = SDL_LoadObject(externlib); + +#ifdef __ANDROID__ + if (m_library == NULL) { + auto android_path = SDL_AndroidGetInternalStoragePath(); + if (android_path != nullptr) { + SCP_string android_full_path = android_path; + if (android_full_path.back() != DIR_SEPARATOR_CHAR) { + android_full_path += DIR_SEPARATOR_CHAR; + } + android_full_path += SCP_string("natives") + DIR_SEPARATOR_CHAR + externlib; + #ifndef NDEBUG + mprintf(("Calling SDL_LoadObject with the following path: '%s'\n", android_full_path.c_str())); + #endif + m_library = SDL_LoadObject(android_full_path.c_str()); + } + } +#endif #ifndef NDEBUG if (m_library == NULL) diff --git a/code/graphics/2d.cpp b/code/graphics/2d.cpp index 0706d3da00d..b9c759e26a5 100644 --- a/code/graphics/2d.cpp +++ b/code/graphics/2d.cpp @@ -221,7 +221,11 @@ const auto LightingOption __UNUSED = options::OptionBuilder("Graphics.Light .parser(parse_lighting_func) .finish(); +#ifndef __ANDROID__ os::ViewportState Gr_configured_window_state = os::ViewportState::Fullscreen; +#else +os::ViewportState Gr_configured_window_state = os::ViewportState::Borderless; +#endif static bool mode_change_func(os::ViewportState state, bool initial) { diff --git a/code/graphics/opengl/gropengl.cpp b/code/graphics/opengl/gropengl.cpp index 1c0f10aed93..1bf40d4ca5b 100644 --- a/code/graphics/opengl/gropengl.cpp +++ b/code/graphics/opengl/gropengl.cpp @@ -6,7 +6,7 @@ #include #endif -#if !defined __APPLE_CC__ && defined SCP_UNIX +#if !defined __APPLE_CC__ && !defined __ANDROID__ && defined SCP_UNIX #include //Required because X defines none and always, which is used later #undef None @@ -1379,7 +1379,7 @@ bool gr_opengl_init(std::unique_ptr&& graphicsOps) Error(LOCATION, "Failed to load OpenGL!"); } -#if !defined __APPLE_CC__ && defined SCP_UNIX +#if !defined __APPLE_CC__ && !defined __ANDROID__ && defined SCP_UNIX if (!gladLoadGLXLoader(GL_context->getLoaderFunction(), nullptr, 0)) { Error(LOCATION, "Failed to load GLX!"); } diff --git a/code/osapi/osapi.cpp b/code/osapi/osapi.cpp index c80016a3385..a1ae90d2fff 100644 --- a/code/osapi/osapi.cpp +++ b/code/osapi/osapi.cpp @@ -28,6 +28,13 @@ #include #endif +#ifdef __ANDROID__ +#include +#include +#include +#include "options/Option.h" +#endif + namespace { const char* ORGANIZATION_NAME = "HardLightProductions"; @@ -840,3 +847,157 @@ SCP_string os_get_config_path(const SCP_string& subpath) return ss.str(); } +/* + Special functions for Android +*/ + +#ifdef __ANDROID__ +// Helper to get a static method from a java class and clear the exception +// if the method is not found. This is needed to avoid crashing on the next JNI request. +static jmethodID android_get_static_method(JNIEnv* e, jclass cls, const char* name, const char* sig) +{ + jmethodID m = e->GetStaticMethodID(cls, name, sig); + if (e->ExceptionCheck()) { + e->ExceptionClear(); + m = nullptr; + mprintf(("OSAPI : Method: %s. Not found on GameActivity! Signature: %s. \n", name, sig)); + } + return m; +} + +void os_set_flags_string(const char* json) +{ + //Get the JNI Environment pointer and current Activity instance via SDL + JNIEnv* env = (JNIEnv*)SDL_AndroidGetJNIEnv(); + jobject activity = (jobject)SDL_AndroidGetActivity(); + + if (env && activity) { + // Locate the Java class (FlagsActivity / GameActivity on KnossosNET) + jclass ga = env->GetObjectClass(activity); + if (ga) { + // void setFlagsJson(String) -> "(Ljava/lang/String;)V" + jmethodID methodId = android_get_static_method(env, ga, "setFlagsJson", "(Ljava/lang/String;)V"); + if (methodId) { + jstring jstr = env->NewStringUTF(json ? json : ""); + env->CallStaticVoidMethod(ga, methodId, jstr); + env->DeleteLocalRef(jstr); + } + else { + mprintf(("os_set_flags_string: Couldn't get the methodID.\n")); + } + env->DeleteLocalRef(ga); + } + else { + mprintf(("os_set_flags_string: Couldn't get java class.\n")); + } + } + else { + mprintf(("os_set_flags_string: Couldn't get JNI enviroment or activity.\n")); + } +} + +SCP_string os_get_working_folder_path() +{ + SCP_string wfp{}; + + //Get the JNI Environment pointer and current Activity instance via SDL + JNIEnv* env = (JNIEnv*)SDL_AndroidGetJNIEnv(); + jobject activity = (jobject)SDL_AndroidGetActivity(); + + if (env && activity) { + // Locate the Java class (GameActivity on KnossosNET) + jclass ga = env->GetObjectClass(activity); + if(ga) { + // Get the methodID (activity, methodName, signature); + // "()Ljava/lang/String;" means it takes no paramenters and returns a string + jmethodID methodId = android_get_static_method (env, ga, "getWorkingFolder", "()Ljava/lang/String;"); + if (methodId) { + jstring jString = (jstring)env->CallStaticObjectMethod(ga, methodId); + if (jString) { + const char* workingFolder = env->GetStringUTFChars(jString, 0); + wfp = SCP_string(workingFolder); + env->ReleaseStringUTFChars(jString, workingFolder); + env->DeleteLocalRef(jString); + } else { + mprintf(("os_get_working_folder_path: Couldn't get the jString.\n")); + } + } else { + mprintf(("os_get_working_folder_path: Couldn't get the methodID.\n")); + } + env->DeleteLocalRef(ga); + } else { + mprintf(("os_get_working_folder_path: Couldn't get java class.\n")); + } + } else { + mprintf(("os_get_working_folder_path: Couldn't get JNI enviroment or activity.\n")); + } + + if (wfp.empty()) { + mprintf(("Couldn't get working folder path from Java class, reverting to SDL default.\n")); + // Fallback to app space on internal storage + const char* fallbackPath = SDL_AndroidGetExternalStoragePath(); + if (fallbackPath) { + wfp = SCP_string(fallbackPath); + wfp += "/files/"; + } + } + + // Ensure path ends with a dir separator + if (!wfp.empty() && (wfp.back() != DIR_SEPARATOR_CHAR)) { + wfp += DIR_SEPARATOR_CHAR; + } + mprintf(("Using working folder: %s\n", wfp.c_str())); + return wfp; +} + +void os_touch_overlay_toggle(bool status) +{ + //Get the JNI Environment pointer and current Activity instance via SDL + JNIEnv* env = (JNIEnv*)SDL_AndroidGetJNIEnv(); + jobject activity = (jobject)SDL_AndroidGetActivity(); + + if (env && activity) { + // Locate the Java class (GameActivity on KnossosNET) + jclass ga = env->GetObjectClass(activity); + if(ga) { + jmethodID methodId = android_get_static_method (env, ga, status ? "enableOverlay" : "disableOverlay", "()V"); + if (methodId) { + env->CallStaticVoidMethod(ga, methodId); + } else { + mprintf(("os_touch_overlay_toggle: Couldn't get the methodID.\n")); + } + env->DeleteLocalRef(ga); + } else { + mprintf(("os_touch_overlay_toggle: Couldn't get java class.\n")); + } + } else { + mprintf(("os_touch_overlay_toggle: Couldn't get JNI enviroment or activity.\n")); + } +} + +static bool touch_ui_change(bool new_val, bool initial) +{ + if (initial) { + return false; + } + os_touch_overlay_toggle(new_val); + return true; +} + +static auto TouchOverlayOption = options::OptionBuilder("Input.TouchOverlay", + std::pair{"Touch Overlay", -1}, + std::pair{"Enable or disable the Touch Overlay", -1}) + .category(std::make_pair("Input", 1827)) + .level(options::ExpertLevel::Beginner) + .change_listener(touch_ui_change) + .default_val(true) + .importance(0) + .finish(); + +void os_touch_overlay_init() +{ + os_touch_overlay_toggle(TouchOverlayOption->getValue()); +} + +#endif + diff --git a/code/osapi/osapi.h b/code/osapi/osapi.h index 0d23b002ed3..6cb6b579008 100644 --- a/code/osapi/osapi.h +++ b/code/osapi/osapi.h @@ -91,6 +91,23 @@ bool os_is_legacy_mode(); */ SCP_string os_get_config_path(const SCP_string& subpath = ""); +/* + Special functions for Android +*/ +#ifdef __ANDROID__ +// Get working folder absolute path from Android Java Class +SCP_string os_get_working_folder_path(); + +// Calls to display the touch overlay depending on its last state +void os_touch_overlay_init(); + +// Enable or disable the touch UI overlay +void os_touch_overlay_toggle(bool status); + +// Send the JSON flags string to the Android Java layer +void os_set_flags_string(const char* json); +#endif + namespace os { /** diff --git a/code/sound/openal.h b/code/sound/openal.h index cd77a1bb290..ddf32509e68 100644 --- a/code/sound/openal.h +++ b/code/sound/openal.h @@ -3,7 +3,7 @@ #define _AL_H -#if defined(__APPLE__) +#if defined(__APPLE__) || defined(__ANDROID__) #include "al.h" #include "alc.h" #else diff --git a/code/windows_stub/stubs.cpp b/code/windows_stub/stubs.cpp index 1f45959c7e1..df4ae39b619 100644 --- a/code/windows_stub/stubs.cpp +++ b/code/windows_stub/stubs.cpp @@ -49,6 +49,7 @@ int filelength(int fd) SCP_string dump_stacktrace() { +#ifndef __ANDROID__ #ifdef SCP_HAVE_EXECINFO_H // The following is adapted from here: https://panthema.net/2008/0901-stacktrace-demangled/ const int ADDR_SIZE = 64; @@ -139,6 +140,9 @@ SCP_string dump_stacktrace() #else return "No stacktrace available"; #endif +#else +return "No stacktrace available"; +#endif } // retrieve the current working directory diff --git a/freespace2/CMakeLists.txt b/freespace2/CMakeLists.txt index aeb6afd9eae..580a8e0c708 100644 --- a/freespace2/CMakeLists.txt +++ b/freespace2/CMakeLists.txt @@ -19,7 +19,18 @@ IF(MSVC60) link_directories(${STLPORT_INCLUDE_LIB_DIRS}) ENDIF(MSVC60) -ADD_EXECUTABLE(Freespace2 ${EXE_GUI_TYPE} ${FREESPACE_SRC}) +# For android, build a library not a executable +if (ANDROID) + add_library(Freespace2 SHARED ${FREESPACE_SRC}) + target_compile_definitions(Freespace2 PRIVATE PLATFORM_ANDROID) + + # NDK base libs + find_library(log-lib log) + find_library(android-lib android) + target_link_libraries(Freespace2 PRIVATE ${log-lib} ${android-lib}) +else() + add_executable(Freespace2 ${EXE_GUI_TYPE} ${FREESPACE_SRC}) +endif() target_compile_features(Freespace2 PUBLIC cxx_std_17) @@ -27,9 +38,20 @@ include(resources.cmake) SET_TARGET_PROPERTIES(Freespace2 PROPERTIES OUTPUT_NAME "fs2_open_${FSO_BINARY_SUFFIX}") -TARGET_LINK_LIBRARIES(Freespace2 code) -TARGET_LINK_LIBRARIES(Freespace2 platform) -TARGET_LINK_LIBRARIES(Freespace2 compiler) +if (ANDROID) + # one call + target_link_libraries(Freespace2 + PRIVATE + code + platform + compiler + ${ANDROID_LIBS} + ) +else() + TARGET_LINK_LIBRARIES(Freespace2 code) + TARGET_LINK_LIBRARIES(Freespace2 platform) + TARGET_LINK_LIBRARIES(Freespace2 compiler) +endif() IF(WIN32) # Link sdl main only here as it interferes with fred @@ -51,6 +73,8 @@ IF(USE_STLPORT) INCLUDE_DIRECTORIES(BEFORE SYSTEM ${STLPORT_INCLUDE_DIRS}) ENDIF(USE_STLPORT) +# Do not install when building android +if (NOT ANDROID) # Add install script to copy the executable to the FreeSpace path if possible and wanted INSTALL( TARGETS Freespace2 @@ -64,6 +88,7 @@ if (FSO_INSTALL_DEBUG_FILES) OPTIONAL) endif() endif() +endif(NOT ANDROID) if(FSO_BUILD_APPIMAGE) configure_file("${CMAKE_CURRENT_LIST_DIR}/cmake/AppRun.in" "${CMAKE_CURRENT_BINARY_DIR}/AppRun.gen" @ONLY) @@ -89,6 +114,8 @@ enable_clang_tidy(Freespace2) INCLUDE(util) COPY_FILES_TO_TARGET(Freespace2) +# Do not launch when building android +if (NOT ANDROID) include(CreateLaunchers) create_target_launcher(Freespace2 WORKING_DIRECTORY ${FSO_FREESPACE_PATH} @@ -101,6 +128,7 @@ add_custom_target(launch_fso WORKING_DIRECTORY "${FSO_FREESPACE_PATH}" COMMENT "Launching FreeSpace" VERBATIM) +endif() # Ensure that Xcode generates debug symbols on macOS for Debug builds set_target_properties(Freespace2 PROPERTIES XCODE_ATTRIBUTE_GCC_GENERATE_DEBUGGING_SYMBOLS[variant=Debug] "YES") diff --git a/freespace2/SDLGraphicsOperations.cpp b/freespace2/SDLGraphicsOperations.cpp index 3b123d54636..3c0b40ea06a 100644 --- a/freespace2/SDLGraphicsOperations.cpp +++ b/freespace2/SDLGraphicsOperations.cpp @@ -201,6 +201,11 @@ std::unique_ptr SDLGraphicsOperations::createViewport(const os::Vi windowflags |= SDL_WINDOW_INPUT_GRABBED; } + #ifdef __ANDROID__ + SDL_SetHint(SDL_HINT_ORIENTATIONS, "LandscapeLeft LandscapeRight"); + os_touch_overlay_init(); + #endif + SDL_Rect bounds; if (SDL_GetDisplayBounds(props.display, &bounds) != 0) { mprintf(("Failed to get display bounds: %s\n", SDL_GetError())); diff --git a/freespace2/freespace.cpp b/freespace2/freespace.cpp index 746edd3d81c..c9933c36a71 100644 --- a/freespace2/freespace.cpp +++ b/freespace2/freespace.cpp @@ -8169,3 +8169,10 @@ int main(int argc, char *argv[]) return result; } + +#ifdef __ANDROID__ +extern "C" int android_main(int argc, char* argv[]) +{ + return main(argc, argv); +} +#endif \ No newline at end of file diff --git a/lib/FFmpeg.cmake b/lib/FFmpeg.cmake index 740ab4c1c1d..cfbbb367d92 100644 --- a/lib/FFmpeg.cmake +++ b/lib/FFmpeg.cmake @@ -48,7 +48,7 @@ IF(PLATFORM_WINDOWS) target_link_libraries(ffmpeg INTERFACE swscale) target_link_libraries(ffmpeg INTERFACE swresample) ELSE(WIN32) - if (PLATFORM_MAC) + if (PLATFORM_MAC OR ANDROID) option(FFMPEG_USE_PRECOMPILED "Use precompiled version of FFmpeg. If disabled the system libraries will be used." ON) else() option(FFMPEG_USE_PRECOMPILED "Use precompiled version of FFmpeg. If disabled the system libraries will be used." OFF) @@ -97,8 +97,8 @@ ELSE(WIN32) # Use our libraries find_library(${name}_LOCATION ${name} PATHS "${FFMPEG_PATH}/lib" - NO_DEFAULT_PATH) - + NO_DEFAULT_PATH + NO_CMAKE_FIND_ROOT_PATH) file(GLOB ${name}_LIBS "${FFMPEG_PATH}/lib/lib${name}*") get_filename_component(FULL_LIB_PATH "${${name}_LOCATION}" REALPATH) diff --git a/lib/OpenAL.cmake b/lib/OpenAL.cmake index 67adfd8c3a1..ac845fc441c 100644 --- a/lib/OpenAL.cmake +++ b/lib/OpenAL.cmake @@ -58,7 +58,7 @@ ELSE(WIN32) message(STATUS "Using pre-built OpenAL library.") unset(OpenAL_LOCATION CACHE) - find_library(OpenAL_LOCATION openal PATHS "${OpenAL_ROOT_DIR}/lib" NO_DEFAULT_PATH) + find_library(OpenAL_LOCATION openal PATHS "${OpenAL_ROOT_DIR}/lib" NO_DEFAULT_PATH NO_CMAKE_FIND_ROOT_PATH) get_filename_component(FULL_LIB_PATH "${OpenAL_LOCATION}" REALPATH) ADD_IMPORTED_LIB(openal "${OpenAL_ROOT_DIR}/include" "${FULL_LIB_PATH}") diff --git a/lib/SDL2.cmake b/lib/SDL2.cmake index d6d99fb06f4..2ca0c5a053f 100644 --- a/lib/SDL2.cmake +++ b/lib/SDL2.cmake @@ -70,12 +70,12 @@ else() message(STATUS "Using pre-built SDL2 library.") unset(SDL2_LOCATION CACHE) - find_library(SDL2_LOCATION NAMES SDL2 SDL2-2.0 PATHS "${SDL2_ROOT_DIR}/lib" NO_DEFAULT_PATH) + find_library(SDL2_LOCATION NAMES SDL2 SDL2-2.0 PATHS "${SDL2_ROOT_DIR}/lib" NO_DEFAULT_PATH NO_CMAKE_FIND_ROOT_PATH) get_filename_component(FULL_LIB_PATH "${SDL2_LOCATION}" REALPATH) ADD_IMPORTED_LIB(sdl2 "${SDL2_ROOT_DIR}/include" "${FULL_LIB_PATH}") - file(GLOB SDL2_LIBS "${SDL2_ROOT_DIR}/lib/libSDL2-2*") + file(GLOB SDL2_LIBS "${SDL2_ROOT_DIR}/lib/libSDL2*") add_target_copy_files("${SDL2_LIBS}") endif() endif() diff --git a/lib/freetype.cmake b/lib/freetype.cmake index cda855d3f5c..a64e5f85c87 100644 --- a/lib/freetype.cmake +++ b/lib/freetype.cmake @@ -1,5 +1,5 @@ -if(PLATFORM_WINDOWS OR PLATFORM_MAC OR CMAKE_CROSSCOMPILING) +if(PLATFORM_WINDOWS OR PLATFORM_MAC OR CMAKE_CROSSCOMPILING OR ANDROID) add_library(freetype INTERFACE) # We use prebuilt binaries for windows and mac @@ -10,7 +10,7 @@ if(PLATFORM_WINDOWS OR PLATFORM_MAC OR CMAKE_CROSSCOMPILING) set(SEARCH_PATH "${FREETYPE_ROOT_DIR}/lib") - if(PLATFORM_WINDOWS OR CMAKE_CROSSCOMPILING) + if((PLATFORM_WINDOWS OR CMAKE_CROSSCOMPILING) AND NOT ANDROID) set(LIBNAME "freetype*.dll") else() set(LIBNAME "libfreetype*") @@ -21,7 +21,8 @@ if(PLATFORM_WINDOWS OR PLATFORM_MAC OR CMAKE_CROSSCOMPILING) find_library(freetype_LOCATION NAMES freetype freetype281 PATHS "${SEARCH_PATH}" - NO_DEFAULT_PATH) + NO_DEFAULT_PATH + NO_CMAKE_FIND_ROOT_PATH) if (${freetype_LOCATION} STREQUAL "freetype_LOCATION-NOTFOUND" AND CMAKE_CROSSCOMPILING) #Just try the default here and see if we can make it work. diff --git a/lib/prebuilt.cmake b/lib/prebuilt.cmake index 94aae9ef0b0..d18b6e89ad5 100644 --- a/lib/prebuilt.cmake +++ b/lib/prebuilt.cmake @@ -34,6 +34,16 @@ function(get_prebuilt_path OUT_VAR) endif() elseif(PLATFORM_MAC) set(FILENAME "bin-mac.tar.gz") + elseif(ANDROID) + if(ANDROID_ABI STREQUAL "arm64-v8a") + set(FILENAME "bin-android-arm64.tar.gz") + elseif(ANDROID_ABI STREQUAL "x86_64") + set(FILENAME "bin-android-x64.tar.gz") + elseif(ANDROID_ABI STREQUAL "armeabi-v7a") + set(FILENAME "bin-android-arm32.tar.gz") + elseif(ANDROID_ABI STREQUAL "x86") + set(FILENAME "bin-android-x86.tar.gz") + endif() else() # Use Linux binaries... if (IS_ARM64) @@ -42,7 +52,11 @@ function(get_prebuilt_path OUT_VAR) set(FILENAME "bin-linux.tar.gz") endif() endif() + if(ANDROID) # TODO: REMOVE THIS ONCE ANDROID PREBUILTS ARE IN THE SCP-PREBUILT REPO + set(DOWNLOAD_URL "https://github.com/Shivansps/Fso-Android-Prebuilts/releases/download/prebuilts-cmake/${FILENAME}") + else() set(DOWNLOAD_URL "https://github.com/scp-fs2open/scp-prebuilt/releases/download/${TAG_NAME}/${FILENAME}") + endif() set(DOWNLOAD_FILE "${CURRENT_ROOT}/${FILENAME}") set(MAX_RETRIES 5)