diff --git a/.gitignore b/.gitignore index c55a27cea..5d64770cc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,21 @@ +# Android / Gradle +.gradle/ +android/launcher/app/build/ +android/launcher/app/jniLibs/ +android/launcher/build/ +android/launcher/.cxx/ +android/launcher/local.properties +*.apk +*.aab +*.jks + +# IDE +.idea/ +*.iml +.vscode/ + +# CLAUDE.md is intentionally tracked + ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. ## diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 000000000..002ab90eb --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,149 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project + +reVC is a fully reverse-engineered Grand Theft Auto: Vice City (`miami` branch). It builds and runs on Windows, Linux, macOS, FreeBSD, and Android on x86, amd64, arm, and arm64. + +## Build Commands + +### Linux (Conan + CMake — recommended) +``` +conan export vendor/librw librw/master@ +mkdir build && cd build +conan install .. reVC/miami@ -if build -o reVC:audio=openal -o librw:platform=gl3 -o librw:gl3_gfxlib=glfw --build missing -s reVC:build_type=RelWithDebInfo -s librw:build_type=RelWithDebInfo +conan build .. -if build -bf build -pf package +``` + +### Linux (Premake) +```sh +./premake5Linux --with-librw gmake2 +cd build && make config=release_x86_64 +``` + +### Windows (Premake + Visual Studio) +```bat +premake-vs2022.cmd +:: Open build/reVC.sln in Visual Studio, build reVC project +``` + +### macOS (Premake) +```sh +./premake5Linux --with-librw --os=macosx gmake2 +cd build && make config=release_x86_64 +``` + +### Build options +- `--with-librw` — build librw alongside reVC (vs. using system/external) +- `--with-lto` — enable Link Time Optimization +- `--with-asan` — enable Address Sanitizer +- `--with-opus` — enable Opus audio codec support +- `--no-git-hash` / `--no-full-paths` — reproducible build options + +### CMake options +``` +-DREVC_AUDIO=OAL|MSS # audio backend (default OAL) +-DREVC_WITH_OPUS=ON|OFF # Opus support +-DREVC_WITH_SANITIZERS=ON # UBSan +-DREVC_WITH_ASAN=ON # ASan +``` + +## Architecture + +reVC is a single monolithic executable. All game code lives under `src/`, organized by subsystem: + +| Directory | Purpose | +|-----------|---------| +| `src/core/` | Core loop, world manager, streaming, pools, frontend, zones, config | +| `src/renderer/` | Rendering: HUD, coronas, particles, shadows, weather, timecycle | +| `src/collision/` | Collision detection and response | +| `src/entities/` | Entity base classes and managers | +| `src/peds/` | Pedestrian AI, animation blending, player control | +| `src/vehicles/` | Vehicle physics, heli, boat, car control | +| `src/weapons/` | Weapon types, bullet traces, explosions | +| `src/objects/` | Dynamic world objects | +| `src/buildings/` | Static building rendering and LOD | +| `src/animation/` | Animation system (AnimBlend) | +| `src/audio/` | Audio system with backends in `audio/oal/` (OpenAL), `audio/eax/` | +| `src/control/` | Gamepad, controller config | +| `src/math/` | Vector, matrix, quaternion math | +| `src/modelinfo/` | Model type information registry | +| `src/save/` | Save/load game state | +| `src/text/` | GXT text system | +| `src/rw/` | RenderWare helper/utility code | + +### Platform abstraction (`src/skel/`) + +The platform layer follows the RenderWare skeleton pattern. Entry point is `main.cpp` → `WinMain`/`main` → `Game::InitialiseOnceAfterRW()`. + +- **`skeleton.h`** — public API (events, input, timer, camera). All platform backends implement this interface. +- **`platform.h`** — platform-specific function declarations (`psInitialize`, `psTimer`, etc.). +- **`glfw/glfw.cpp`** — primary cross-platform backend (Windows OpenGL, Linux, macOS, BSD) +- **`sdl2/sdl2.cpp`** — SDL2 backend used on Android (`LIBRW_SDL2` define) +- **`android/`** — Android-specific JNI bridge (`AndroidMain.cpp`, `JavaWrapper`) +- **`win/`** — Windows-specific D3D8/D3D9 backend (`RWLIBS` or `USE_D3D9`) +- **`crossplatform.cpp/.h`** — filesystem path utilities, locale/language detection, `casepath` + +### RenderWare abstraction (`src/fakerw/`) + +When building with librw (`LIBRW` define), stub headers in `fakerw/` redirect RenderWare API calls through librw. When building with original RW (`RWLIBS`), the real RenderWare SDK headers are used directly. + +### Rendering backends (preprocessor-controlled) + +- `RWLIBS` — original RenderWare D3D8 (Windows only) +- `LIBRW` + `RW_D3D9` — librw with Direct3D 9 (Windows only) +- `LIBRW` + `RW_GL3` — librw with OpenGL 3.x via GLFW or SDL2 (all platforms) + +### Audio backends + +- `AUDIO_MSS` — Miles Sound System (Windows, requires original MSS DLLs) +- `AUDIO_OAL` — OpenAL (cross-platform, default) + +### Key configuration + +`src/core/config.h` defines all pool sizes, entity limits, and compile-time options. Feature toggles and bugfix guards (e.g., `FIX_BUGS`) are also here. Runtime settings live in `reVC.ini` (not the original `gta_vc.set`). + +## Coding Conventions + +- **C++ standard**: C++11 maximum — do not use C++14 or later features +- **Types**: Always use project typedefs (`int8`, `uint8`, `int16`, `uint16`, `int32`, `uint32`, `bool`). Never use `unsigned` bare, `char` for non-characters, or Win32 types (`BYTE`, `DWORD`) outside platform-specific code. +- **Pointers**: `int *ptr;` style (asterisk attached to variable, not type) +- **Indentation**: TABS only +- **Brace style**: K&R-ish — brace on next line for functions/structs, same line for control flow. No braces for single-statement bodies. `else` on same line with closing brace. +- **Variable naming**: Hungarian-notation influenced — `f` prefix for float, `i`/`n` for integer, `b` for boolean, `m_` for private members, `ms_` for private static members. +- **Magic numbers**: Avoid. Use enums even when the exact meaning is unknown (`FOOBAR_TYPE_4` over `4`). + +## PR Guidelines + +Accepted contributions: +- Features that existed in at least one original GTA version +- Bug fixes (behind `FIX_BUGS` preprocessor guard if fixing original behavior) +- Un-reversed platform-specific or unused code +- Making reversed code more accurate (matching original assembly) +- Cross-platform skeleton/compatibility layer improvements +- Translation fixes for original languages + +No custom/gameplay features unless guarded by preprocessor conditions, and no mod-like additions. + +## Android Port (`android-port` branch) + +### Build +```sh +cd android/launcher && ./gradlew assembleDebug +``` +Requires NDK 30+, SDK 35+. Prebuilt `.so` files (SDL2, OpenAL, mpg123) are in `vendor/` — run `android/setup_libs.sh` to copy to `jniLibs/`. + +### First-launch flow +1. SAF folder picker → user selects GTA VC directory +2. Files validated (`models/gta3.img` must exist) +3. Background copy to `getExternalFilesDir()` via `DocumentsContract` (app-owned files) +4. Game loads from app-private storage — no permissions needed +5. Subsequent launches skip picker + +### Key changes from upstream +- `FileMgr.cpp` — `\` → `/` conversion (Windows paths) +- `sdl2.cpp` — SDL hints, `STORAGE_ROOT` via `SDL_AndroidGetExternalStoragePath` +- `crossplatform.cpp` — `casepath` does `opendir`/`readdir`; works because FUSE is case-insensitive +- `AndroidManifest.xml` — `extractNativeLibs=true` (SDL2 needs filesystem .so), landscape, gamepad-only +- `CdStream_posix.cpp` — Android uses `statfs` not `statvfs`, pthread mutex instead of semaphore diff --git a/android/launcher/app/CMakeLists.txt b/android/launcher/app/CMakeLists.txt index 2d3ee95bb..d1595cdbe 100644 --- a/android/launcher/app/CMakeLists.txt +++ b/android/launcher/app/CMakeLists.txt @@ -3,18 +3,17 @@ # # Description: # This file defines the root CMake configuration for the reVC – Mobile Edition, -# targeting the Android platform via the Android NDK. It initializes the build system -# for the mobile game, integrates shared cross-platform sources, configures compiler settings, -# and handles dependency resolution. +# targeting the Android platform via the Android NDK. It sets up prebuilt libraries +# (SDL2, OpenAL, mpg123) and then includes the main game CMake build. # ================================================================================================== cmake_minimum_required(VERSION 3.22.1 FATAL_ERROR) message(" ") -message("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━") +message("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━") message(" * 🌐 reVC – Build Configuration Initialized") message(" * 🔧 Preparing Android platform modules and environment configuration...") -message("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━") +message("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━") message(" ") # ──────────────────────────────────────────────────────────────────────────────── @@ -38,17 +37,54 @@ get_filename_component(REVC_ROOT_DIR "${CMAKE_CURRENT_SOURCE_DIR}" ABSOLUTE) set(REVC_GAME_DIR "${REVC_ROOT_DIR}/../../../") message(" * 📂 Game source directory set to: ${REVC_GAME_DIR}") +# ──────────────────────────────────────────────────────────────────────────────── +# 📦 Prebuilt Native Libraries for Android +# ──────────────────────────────────────────────────────────────────────────────── + +# SDL2 +add_library(SDL2::SDL2 SHARED IMPORTED) +set_target_properties(SDL2::SDL2 PROPERTIES + IMPORTED_LOCATION "${REVC_GAME_DIR}/vendor/sdl2/libs/Android/${ANDROID_ABI}/libSDL2.so" + INTERFACE_INCLUDE_DIRECTORIES "${REVC_GAME_DIR}/vendor/sdl2/include" +) +message(" * 📦 SDL2 prebuilt: ${ANDROID_ABI}") + +# OpenAL +add_library(OpenAL::OpenAL SHARED IMPORTED) +set_target_properties(OpenAL::OpenAL PROPERTIES + IMPORTED_LOCATION "${REVC_GAME_DIR}/vendor/openal-soft/libs/Android/${ANDROID_ABI}/libopenal.so" + INTERFACE_INCLUDE_DIRECTORIES "${REVC_GAME_DIR}/vendor/openal-soft/include" +) +message(" * 📦 OpenAL prebuilt: ${ANDROID_ABI}") + +# mpg123 +add_library(MPG123::libmpg123 SHARED IMPORTED) +set_target_properties(MPG123::libmpg123 PROPERTIES + IMPORTED_LOCATION "${REVC_GAME_DIR}/vendor/mpg123/lib/Android/${ANDROID_ABI}/libmpg123.so" + INTERFACE_INCLUDE_DIRECTORIES "${REVC_GAME_DIR}/vendor/mpg123/include" +) +message(" * 📦 mpg123 prebuilt: ${ANDROID_ABI}") + # ──────────────────────────────────────────────────────────────────────────────── # ➕ Add Game Subdirectory If Available # ──────────────────────────────────────────────────────────────────────────────── if(IS_DIRECTORY "${REVC_GAME_DIR}" AND EXISTS "${REVC_GAME_DIR}/CMakeLists.txt") message(" * ➕ Including Game module from: ${REVC_GAME_DIR}") message(" ") - message("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━") + message("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━") message(" * 🚀 Initiating Game module build configuration...") - message("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━") + message("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━") message(" ") - #add_subdirectory(${REVC_GAME_DIR} ${CMAKE_BINARY_DIR}/game_build) + + set(REVC_AUDIO "OAL" CACHE STRING "Audio" FORCE) + set(REVC_VENDORED_LIBRW ON CACHE BOOL "Use vendored librw" FORCE) + set(REVC_INSTALL OFF CACHE BOOL "Install" FORCE) + + add_subdirectory(${REVC_GAME_DIR} ${CMAKE_BINARY_DIR}/game_build) + + # Force lowercase output name — System.loadLibrary("revc") looks for librevc.so + set_target_properties(reVC PROPERTIES LIBRARY_OUTPUT_NAME revc) + else() message(" * ⚠️ Game source directory or CMakeLists.txt not found at: ${REVC_GAME_DIR}") message(FATAL_ERROR " * ❗ Please ensure the game module sources are available for the build.") diff --git a/android/launcher/app/build.gradle b/android/launcher/app/build.gradle index e4a404468..1dc2ac28a 100644 --- a/android/launcher/app/build.gradle +++ b/android/launcher/app/build.gradle @@ -9,17 +9,17 @@ android { defaultConfig { applicationId "com.revc.game" - minSdk 24 + minSdk 26 targetSdk 35 versionCode 1 versionName "reVC-0.0.1" - ndkVersion '27.2.12479018' + ndkVersion '30.0.15729638' testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" ndk { - abiFilters "armeabi-v7a"/*, "arm64-v8a"*/ + abiFilters "armeabi-v7a", "arm64-v8a" } } @@ -37,7 +37,6 @@ android { minifyEnabled true shrinkResources true jniDebuggable false - renderscriptDebuggable false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } @@ -54,7 +53,6 @@ android { debuggable true minifyEnabled false jniDebuggable true - renderscriptDebuggable true } } diff --git a/android/launcher/app/src/main/AndroidManifest.xml b/android/launcher/app/src/main/AndroidManifest.xml index 54669c7da..6fdf00de3 100644 --- a/android/launcher/app/src/main/AndroidManifest.xml +++ b/android/launcher/app/src/main/AndroidManifest.xml @@ -36,17 +36,22 @@ android:glEsVersion="0x00020000" android:required="true"/> - + - + + + - - - - - - - - + diff --git a/android/launcher/app/src/main/java/com/revc/game/MainActivity.java b/android/launcher/app/src/main/java/com/revc/game/MainActivity.java deleted file mode 100644 index 1b8558ee0..000000000 --- a/android/launcher/app/src/main/java/com/revc/game/MainActivity.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.revc.game; - -import androidx.appcompat.app.AppCompatActivity; - -import android.os.Bundle; -import android.widget.TextView; - -import com.revc.game.databinding.ActivityMainBinding; - -public class MainActivity extends AppCompatActivity { - - // Used to load the 'game' library on application startup. - static { - //System.loadLibrary("revc"); - } - - private ActivityMainBinding binding; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - binding = ActivityMainBinding.inflate(getLayoutInflater()); - setContentView(binding.getRoot()); - - // Example of a call to a native method - TextView tv = binding.sampleText; - // tv.setText(stringFromJNI()); - } - - /** - * A native method that is implemented by the 'game' native library, - * which is packaged with this application. - */ - //public native String stringFromJNI(); -} \ No newline at end of file diff --git a/android/launcher/app/src/main/java/org/libsdl/app/SDLActivity.java b/android/launcher/app/src/main/java/org/libsdl/app/SDLActivity.java index e6622294b..0ee13377e 100644 --- a/android/launcher/app/src/main/java/org/libsdl/app/SDLActivity.java +++ b/android/launcher/app/src/main/java/org/libsdl/app/SDLActivity.java @@ -4,6 +4,7 @@ import android.app.AlertDialog; import android.app.Dialog; import android.app.UiModeManager; +import android.content.ActivityNotFoundException; import android.content.ClipboardManager; import android.content.ClipData; import android.content.Context; @@ -50,6 +51,16 @@ import android.widget.TextView; import android.widget.Toast; +import android.app.ProgressDialog; +import android.content.SharedPreferences; +import android.database.Cursor; +import android.net.Uri; +import android.os.AsyncTask; +import android.provider.DocumentsContract; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; import java.util.Hashtable; import java.util.Locale; @@ -62,6 +73,8 @@ public class SDLActivity extends Activity implements View.OnSystemUiVisibilityCh private static final int SDL_MAJOR_VERSION = 2; private static final int SDL_MINOR_VERSION = 32; private static final int SDL_MICRO_VERSION = 8; + private static final int REQUEST_GAME_DIR = 1000; + private static final String PREF_GAME_READY = "game_ready"; /* // Display InputType.SOURCE/CLASS of events and devices // @@ -325,6 +338,23 @@ protected void onCreate(Bundle savedInstanceState) { Log.v(TAG, "modify thread properties failed " + e.toString()); } + // First launch: SAF picker → copy to app-private + SharedPreferences prefs = getSharedPreferences(PREF_GAME_READY, MODE_PRIVATE); + if (!prefs.getBoolean("ready", false)) { + Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE); + try { + startActivityForResult(intent, REQUEST_GAME_DIR); + } catch (ActivityNotFoundException e) { + // Google TV ships no SAF document picker. Material Files (or any + // file manager exposing a DocumentsProvider) supplies one. + Toast.makeText(this, + "No file picker found. Install a file manager (e.g. Material Files) to select the game folder.", + Toast.LENGTH_LONG).show(); + finish(); + } + return; + } + // Load shared libraries String errorMsgBrokenLib = ""; try { @@ -409,7 +439,7 @@ public void onClick(DialogInterface dialog,int id) { setContentView(mLayout); - setWindowStyle(false); + setWindowStyle(true); getWindow().getDecorView().setOnSystemUiVisibilityChangeListener(this); @@ -446,6 +476,77 @@ protected void resumeNativeThread() { SDLActivity.handleNativeState(); } + // SAF picker result + copy + @Override + protected void onActivityResult(int req, int res, Intent data) { + super.onActivityResult(req, res, data); + if (req != REQUEST_GAME_DIR || res != RESULT_OK || data == null) { finish(); return; } + Uri treeUri = data.getData(); + if (treeUri == null) { finish(); return; } + getContentResolver().takePersistableUriPermission(treeUri, + Intent.FLAG_GRANT_READ_URI_PERMISSION); + new CopyGameTask().execute(treeUri); + } + + private class CopyGameTask extends AsyncTask { + private ProgressDialog dialog; + protected void onPreExecute() { + dialog = new ProgressDialog(SDLActivity.this); + dialog.setTitle("Copying game files"); + dialog.setMessage("Please wait..."); + dialog.setProgressStyle(ProgressDialog.STYLE_SPINNER); + dialog.setCancelable(false); + dialog.show(); + } + protected Boolean doInBackground(Uri... uris) { + File dst = getExternalFilesDir(null); + if (dst == null) return false; + dst.mkdirs(); + try { copyTree(uris[0], "", dst); return true; } + catch (IOException e) { Log.e(TAG, "Copy failed: " + e); return false; } + } + protected void onPostExecute(Boolean ok) { + dialog.dismiss(); + if (ok) { + getSharedPreferences(PREF_GAME_READY, MODE_PRIVATE) + .edit().putBoolean("ready", true).apply(); + recreate(); + } else { + new AlertDialog.Builder(SDLActivity.this) + .setTitle("Error").setMessage("Failed to copy game files.") + .setPositiveButton("Exit", (d, w) -> finish()).show(); + } + } + private void copyTree(Uri treeUri, String path, File dstDir) throws IOException { + String docId = DocumentsContract.getTreeDocumentId(treeUri); + String childId = path.isEmpty() ? docId : docId + "/" + path; + Uri dirUri = DocumentsContract.buildChildDocumentsUriUsingTree(treeUri, childId); + try (Cursor c = getContentResolver().query(dirUri, + new String[]{DocumentsContract.Document.COLUMN_DOCUMENT_ID, + DocumentsContract.Document.COLUMN_DISPLAY_NAME, + DocumentsContract.Document.COLUMN_MIME_TYPE}, + null, null, null)) { + if (c == null) return; + while (c.moveToNext()) { + String name = c.getString(1); + String mime = c.getString(2); + String childDocId = c.getString(0); + if (DocumentsContract.Document.MIME_TYPE_DIR.equals(mime)) { + File sub = new File(dstDir, name); sub.mkdirs(); + copyTree(treeUri, (path.isEmpty() ? "" : path + "/") + name, sub); + } else { + Uri fileUri = DocumentsContract.buildDocumentUriUsingTree(treeUri, childDocId); + try (InputStream in = getContentResolver().openInputStream(fileUri); + FileOutputStream out = new FileOutputStream(new File(dstDir, name))) { + byte[] buf = new byte[65536]; int n; + while ((n = in.read(buf)) > 0) out.write(buf, 0, n); + } + } + } + } + } + } + // Events @Override protected void onPause() { @@ -606,6 +707,9 @@ protected void onDestroy() { SDLActivity.nativeQuit(); super.onDestroy(); + + // Ensure clean process state for next launch + android.os.Process.killProcess(android.os.Process.myPid()); } @Override diff --git a/android/launcher/app/src/main/res/drawable-xhdpi/banner.png b/android/launcher/app/src/main/res/drawable-xhdpi/banner.png new file mode 100644 index 000000000..9dccf05c0 Binary files /dev/null and b/android/launcher/app/src/main/res/drawable-xhdpi/banner.png differ diff --git a/android/launcher/app/src/main/res/drawable/ic_launcher_background.xml b/android/launcher/app/src/main/res/drawable/ic_launcher_background.xml deleted file mode 100644 index 07d5da9cb..000000000 --- a/android/launcher/app/src/main/res/drawable/ic_launcher_background.xml +++ /dev/null @@ -1,170 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/android/launcher/app/src/main/res/drawable/ic_launcher_foreground.xml b/android/launcher/app/src/main/res/drawable/ic_launcher_foreground.xml deleted file mode 100644 index 2b068d114..000000000 --- a/android/launcher/app/src/main/res/drawable/ic_launcher_foreground.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/android/launcher/app/src/main/res/layout/activity_main.xml b/android/launcher/app/src/main/res/layout/activity_main.xml deleted file mode 100644 index 0fdf98529..000000000 --- a/android/launcher/app/src/main/res/layout/activity_main.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/android/launcher/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/android/launcher/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml index 6f3b755bf..345888d26 100644 --- a/android/launcher/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +++ b/android/launcher/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -1,6 +1,6 @@ - - - + + + \ No newline at end of file diff --git a/android/launcher/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/android/launcher/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml index 6f3b755bf..345888d26 100644 --- a/android/launcher/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +++ b/android/launcher/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -1,6 +1,6 @@ - - - + + + \ No newline at end of file diff --git a/android/launcher/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/launcher/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 000000000..19fe524ef Binary files /dev/null and b/android/launcher/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/android/launcher/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/android/launcher/app/src/main/res/mipmap-hdpi/ic_launcher.webp deleted file mode 100644 index c209e78ec..000000000 Binary files a/android/launcher/app/src/main/res/mipmap-hdpi/ic_launcher.webp and /dev/null differ diff --git a/android/launcher/app/src/main/res/mipmap-hdpi/ic_launcher_background.png b/android/launcher/app/src/main/res/mipmap-hdpi/ic_launcher_background.png new file mode 100644 index 000000000..58bfe2670 Binary files /dev/null and b/android/launcher/app/src/main/res/mipmap-hdpi/ic_launcher_background.png differ diff --git a/android/launcher/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png b/android/launcher/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png new file mode 100644 index 000000000..fc692ae5e Binary files /dev/null and b/android/launcher/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png differ diff --git a/android/launcher/app/src/main/res/mipmap-hdpi/ic_launcher_monochrome.png b/android/launcher/app/src/main/res/mipmap-hdpi/ic_launcher_monochrome.png new file mode 100644 index 000000000..fc692ae5e Binary files /dev/null and b/android/launcher/app/src/main/res/mipmap-hdpi/ic_launcher_monochrome.png differ diff --git a/android/launcher/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/android/launcher/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp deleted file mode 100644 index b2dfe3d1b..000000000 Binary files a/android/launcher/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp and /dev/null differ diff --git a/android/launcher/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/launcher/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 000000000..ecad188ea Binary files /dev/null and b/android/launcher/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/android/launcher/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/android/launcher/app/src/main/res/mipmap-mdpi/ic_launcher.webp deleted file mode 100644 index 4f0f1d64e..000000000 Binary files a/android/launcher/app/src/main/res/mipmap-mdpi/ic_launcher.webp and /dev/null differ diff --git a/android/launcher/app/src/main/res/mipmap-mdpi/ic_launcher_background.png b/android/launcher/app/src/main/res/mipmap-mdpi/ic_launcher_background.png new file mode 100644 index 000000000..93fb1ef98 Binary files /dev/null and b/android/launcher/app/src/main/res/mipmap-mdpi/ic_launcher_background.png differ diff --git a/android/launcher/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png b/android/launcher/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png new file mode 100644 index 000000000..50c9ae3b4 Binary files /dev/null and b/android/launcher/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png differ diff --git a/android/launcher/app/src/main/res/mipmap-mdpi/ic_launcher_monochrome.png b/android/launcher/app/src/main/res/mipmap-mdpi/ic_launcher_monochrome.png new file mode 100644 index 000000000..50c9ae3b4 Binary files /dev/null and b/android/launcher/app/src/main/res/mipmap-mdpi/ic_launcher_monochrome.png differ diff --git a/android/launcher/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/android/launcher/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp deleted file mode 100644 index 62b611da0..000000000 Binary files a/android/launcher/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp and /dev/null differ diff --git a/android/launcher/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/android/launcher/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 000000000..70a85f72d Binary files /dev/null and b/android/launcher/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/android/launcher/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/android/launcher/app/src/main/res/mipmap-xhdpi/ic_launcher.webp deleted file mode 100644 index 948a3070f..000000000 Binary files a/android/launcher/app/src/main/res/mipmap-xhdpi/ic_launcher.webp and /dev/null differ diff --git a/android/launcher/app/src/main/res/mipmap-xhdpi/ic_launcher_background.png b/android/launcher/app/src/main/res/mipmap-xhdpi/ic_launcher_background.png new file mode 100644 index 000000000..b8aa7bb22 Binary files /dev/null and b/android/launcher/app/src/main/res/mipmap-xhdpi/ic_launcher_background.png differ diff --git a/android/launcher/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png b/android/launcher/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png new file mode 100644 index 000000000..690e5d902 Binary files /dev/null and b/android/launcher/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png differ diff --git a/android/launcher/app/src/main/res/mipmap-xhdpi/ic_launcher_monochrome.png b/android/launcher/app/src/main/res/mipmap-xhdpi/ic_launcher_monochrome.png new file mode 100644 index 000000000..690e5d902 Binary files /dev/null and b/android/launcher/app/src/main/res/mipmap-xhdpi/ic_launcher_monochrome.png differ diff --git a/android/launcher/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/android/launcher/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp deleted file mode 100644 index 1b9a6956b..000000000 Binary files a/android/launcher/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp and /dev/null differ diff --git a/android/launcher/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/launcher/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 000000000..272101480 Binary files /dev/null and b/android/launcher/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/android/launcher/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/android/launcher/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp deleted file mode 100644 index 28d4b77f9..000000000 Binary files a/android/launcher/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp and /dev/null differ diff --git a/android/launcher/app/src/main/res/mipmap-xxhdpi/ic_launcher_background.png b/android/launcher/app/src/main/res/mipmap-xxhdpi/ic_launcher_background.png new file mode 100644 index 000000000..d21b0b8e9 Binary files /dev/null and b/android/launcher/app/src/main/res/mipmap-xxhdpi/ic_launcher_background.png differ diff --git a/android/launcher/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png b/android/launcher/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png new file mode 100644 index 000000000..08a3839c5 Binary files /dev/null and b/android/launcher/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png differ diff --git a/android/launcher/app/src/main/res/mipmap-xxhdpi/ic_launcher_monochrome.png b/android/launcher/app/src/main/res/mipmap-xxhdpi/ic_launcher_monochrome.png new file mode 100644 index 000000000..08a3839c5 Binary files /dev/null and b/android/launcher/app/src/main/res/mipmap-xxhdpi/ic_launcher_monochrome.png differ diff --git a/android/launcher/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/android/launcher/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp deleted file mode 100644 index 9287f5083..000000000 Binary files a/android/launcher/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp and /dev/null differ diff --git a/android/launcher/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/launcher/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 000000000..afcf0f92d Binary files /dev/null and b/android/launcher/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/android/launcher/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/android/launcher/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp deleted file mode 100644 index aa7d6427e..000000000 Binary files a/android/launcher/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp and /dev/null differ diff --git a/android/launcher/app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.png b/android/launcher/app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.png new file mode 100644 index 000000000..6e53e59e2 Binary files /dev/null and b/android/launcher/app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.png differ diff --git a/android/launcher/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/android/launcher/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png new file mode 100644 index 000000000..9f7078488 Binary files /dev/null and b/android/launcher/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png differ diff --git a/android/launcher/app/src/main/res/mipmap-xxxhdpi/ic_launcher_monochrome.png b/android/launcher/app/src/main/res/mipmap-xxxhdpi/ic_launcher_monochrome.png new file mode 100644 index 000000000..9f7078488 Binary files /dev/null and b/android/launcher/app/src/main/res/mipmap-xxxhdpi/ic_launcher_monochrome.png differ diff --git a/android/launcher/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/android/launcher/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp deleted file mode 100644 index 9126ae37c..000000000 Binary files a/android/launcher/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp and /dev/null differ diff --git a/android/launcher/app/src/main/res/values/themes.xml b/android/launcher/app/src/main/res/values/themes.xml index 606a05210..a29a9e328 100644 --- a/android/launcher/app/src/main/res/values/themes.xml +++ b/android/launcher/app/src/main/res/values/themes.xml @@ -1,16 +1,16 @@ - - \ No newline at end of file diff --git a/android/setup_libs.sh b/android/setup_libs.sh new file mode 100644 index 000000000..c9a1f4654 --- /dev/null +++ b/android/setup_libs.sh @@ -0,0 +1,22 @@ +#!/usr/bin/env bash +# Setup prebuilt native libraries for Android build. +# Copies .so files from vendor/ to jniLibs/ for Gradle packaging. +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +APP_DIR="$SCRIPT_DIR/launcher/app" +VENDOR_DIR="$SCRIPT_DIR/../vendor" + +abis=("arm64-v8a" "armeabi-v7a") + +for abi in "${abis[@]}"; do + mkdir -p "$APP_DIR/jniLibs/$abi" + + cp "$VENDOR_DIR/sdl2/libs/Android/$abi/libSDL2.so" "$APP_DIR/jniLibs/$abi/" + cp "$VENDOR_DIR/openal-soft/libs/Android/$abi/libopenal.so" "$APP_DIR/jniLibs/$abi/" + cp "$VENDOR_DIR/mpg123/lib/Android/$abi/libmpg123.so" "$APP_DIR/jniLibs/$abi/" + + echo "✔ $abi: libSDL2.so, libopenal.so, libmpg123.so" +done + +echo "Done. Prebuilt libs copied to jniLibs/" diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 91d3b8f69..d44325f26 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -84,7 +84,9 @@ else() endif() if(${PROJECT}_AUDIO STREQUAL "OAL") - find_package(OpenAL REQUIRED) + if(NOT TARGET OpenAL::OpenAL) + find_package(OpenAL REQUIRED) + endif() if(TARGET OpenAL::OpenAL) target_link_libraries(${EXECUTABLE} PRIVATE OpenAL::OpenAL) else() @@ -99,7 +101,9 @@ elseif(${PROJECT}_AUDIO STREQUAL "MSS") target_link_libraries(${EXECUTABLE} PRIVATE MilesSDK::MilesSDK) endif() -find_package(mpg123 REQUIRED) +if(NOT TARGET MPG123::libmpg123) + find_package(mpg123 REQUIRED) +endif() target_link_libraries(${EXECUTABLE} PRIVATE MPG123::libmpg123 ) @@ -161,6 +165,9 @@ if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang -Wno-unused-command-line-argument -faligned-new ) + target_link_options(${EXECUTABLE} PRIVATE + -Wl,-z,max-page-size=16384 + ) endif() target_compile_options(${EXECUTABLE} PRIVATE diff --git a/src/core/CdStream_posix.cpp b/src/core/CdStream_posix.cpp index 0287adc5d..ef9fd7ef6 100644 --- a/src/core/CdStream_posix.cpp +++ b/src/core/CdStream_posix.cpp @@ -229,9 +229,10 @@ CdStreamInit(int32 numChannels) char pwd[128]; getcwd(pwd, 128); setenv("STORAGE_ROOT", pwd, 1); - debug("%s\n", pwd); + StorageRootBuffer = getenv("STORAGE_ROOT"); + debug("%s\n", pwd); } - + debug("FILES %s\n", StorageRootBuffer); strcpy(imgPath, StorageRootBuffer); strcat(imgPath, "/models/gta3.img"); @@ -242,8 +243,9 @@ CdStreamInit(int32 numChannels) if((statvfs("models/gta3.img", &fsInfo)) < 0) #endif { - CDTRACE("can't get filesystem info"); - ASSERT(0); + CDTRACE("can't get filesystem info for %s", imgPath); + debug("ERROR: Cannot find game assets at %s\n", imgPath); + debug("Please copy GTA VC files to this directory\n"); return; } #if defined ANDROID diff --git a/src/core/FileMgr.cpp b/src/core/FileMgr.cpp index 124423529..69c2f97ad 100644 --- a/src/core/FileMgr.cpp +++ b/src/core/FileMgr.cpp @@ -294,7 +294,15 @@ int CFileMgr::OpenFile(const char *file, const char *mode) { debug("CFileMgr::OpenFile: %s", file); +#if defined(ANDROID) + char cleanFile[MAX_PATH]; + strcpy(cleanFile, file); + for (char *p = cleanFile; *p; p++) + if (*p == '\\') *p = '/'; + return myfopen(cleanFile, mode); +#else return myfopen(file, mode); +#endif } int diff --git a/src/core/main.cpp b/src/core/main.cpp index 7cfb29cf5..42ebcd3b4 100644 --- a/src/core/main.cpp +++ b/src/core/main.cpp @@ -531,11 +531,13 @@ Initialise3D(void *param) { POP_MEMID(); + #ifdef DEBUGMENU DebugMenuInit(); DebugMenuPopulate(); #endif // !DEBUGMENU - return CGame::InitialiseRenderWare(); + bool rwResult = CGame::InitialiseRenderWare(); + return rwResult; } POP_MEMID(); diff --git a/src/skel/android/AndroidMain.cpp b/src/skel/android/AndroidMain.cpp index ba2da0237..9cb3d7e6a 100644 --- a/src/skel/android/AndroidMain.cpp +++ b/src/skel/android/AndroidMain.cpp @@ -51,13 +51,6 @@ bool AndWrapper::InitLibraries() { return true; } -void AndWrapper::TimeInitialize() { - struct timeval v0; - - gettimeofday(&v0, NULL); - // base_time = (double)v0.tv_usec / 1000000.0 + (double)v0.tv_sec; -} - void* AndWrapper::GetJNI() { return CJavaWrapper::GetEnv(); } @@ -66,352 +59,6 @@ void* AndWrapper::GetJNIFunc() { return CJavaWrapper::GetEnv(); } -void* AndWrapper::GetObj() { - //return s_event_globalThiz; -} - -const char* AndWrapper::GetAppId() -{ - /*JNIEnv* CurrentJNIEnv; // x19 - jobject v1; // x20 - const char *v2; // x21 - jboolean v4[4]; // [xsp+Ch] [xbp-14h] BYREF - - if (!staticAppId[0]) { - CurrentJNIEnv = CJavaWrapper::GetEnv(); - - v1 = _JNIEnv::CallObjectMethod(CurrentJNIEnv, s_event_globalThiz, s_GetAppId); - v2 = CurrentJNIEnv->functions->GetStringUTFChars(CurrentJNIEnv, (jstring)v1, v4); - strcpy(staticAppId, v2); - CurrentJNIEnv->functions->ReleaseStringUTFChars(CurrentJNIEnv, (jstring)v1, v2); - CurrentJNIEnv->functions->DeleteLocalRef(CurrentJNIEnv, v1); - } - return staticAppId;*/ -} - -const char* AndWrapper::GetDeviceID() -{ - /*JNIEnv *CurrentJNIEnv; // x19 - jobject v1; // x20 - const char *v2; // x21 - jboolean v4[4]; // [xsp+Ch] [xbp-24h] BYREF - - CurrentJNIEnv = NVThreadGetCurrentJNIEnv(); - v1 = JNIEnv::CallObjectMethod(CurrentJNIEnv, s_event_globalThiz, s_GetDeviceID); - v2 = CurrentJNIEnv->functions->GetStringUTFChars(CurrentJNIEnv, (jstring)v1, v4); - strncpy(staticDeviceID, v2, 0x80uLL); - CurrentJNIEnv->functions->ReleaseStringUTFChars(CurrentJNIEnv, (jstring)v1, v2); - CurrentJNIEnv->functions->DeleteLocalRef(CurrentJNIEnv, v1); - return staticDeviceID;*/ -} - -int AndWrapper::GetDeviceInfo(int index) -{ - //JNIEnv *CurrentJNIEnv; // x0 - - //CurrentJNIEnv = NVThreadGetCurrentJNIEnv(); - //return _JNIEnv::CallIntMethod(CurrentJNIEnv, s_event_globalThiz, s_GetDeviceInfo, (unsigned int)index); -} - -bool AndWrapper::IsAppInstalled(const char* app) -{ - /*_JNIEnv *CurrentJNIEnv; // x20 - __int64 v3; // x19 - bool v4; // w21 - - CurrentJNIEnv = NVThreadGetCurrentJNIEnv(); - v3 = (__int64)CurrentJNIEnv->functions->NewStringUTF(CurrentJNIEnv, app); - v4 = _JNIEnv::CallBooleanMethod(CurrentJNIEnv, s_event_globalThiz, s_IsAppInstalled, v3) != 0; - CurrentJNIEnv->functions->DeleteLocalRef(CurrentJNIEnv, (jobject)v3); - return v4;*/ -} - -void AndWrapper::OpenLink(const char* link) -{ - /*_JNIEnv *CurrentJNIEnv; // x20 - __int64 v3; // x19 - - CurrentJNIEnv = NVThreadGetCurrentJNIEnv(); - v3 = (__int64)CurrentJNIEnv->functions->NewStringUTF(CurrentJNIEnv, link); - _JNIEnv::CallVoidMethod(CurrentJNIEnv, s_event_globalThiz, s_OpenLink, v3); - CurrentJNIEnv->functions->DeleteLocalRef(CurrentJNIEnv, (jobject)v3);*/ -} - -bool AndWrapper::DeviceIsTV() -{ - //_JNIEnv *CurrentJNIEnv; // x0 - - //CurrentJNIEnv = NVThreadGetCurrentJNIEnv(); - //return _JNIEnv::CallBooleanMethod(CurrentJNIEnv, s_event_globalThiz, s_IsTV) != 0; -} - -int AndWrapper::DeviceLocale() -{ - // _JNIEnv *CurrentJNIEnv; // x0 - - // CurrentJNIEnv = NVThreadGetCurrentJNIEnv(); - // return _JNIEnv::CallIntMethod(CurrentJNIEnv, s_event_globalThiz, s_GetDeviceLocale); -} - -int AndWrapper::DeviceType() -{ - //_JNIEnv *CurrentJNIEnv; // x0 - - //CurrentJNIEnv = NVThreadGetCurrentJNIEnv(); - //return _JNIEnv::CallIntMethod(CurrentJNIEnv, s_event_globalThiz, s_GetDeviceType); -} - -void AndWrapper::SystemInitialize() -{ - -} -/* -int32_t __fastcall NVEventAppMain(int32_t argc, char **argv) -{ - _QWORD *v4; // x8 - pthread_key_t v5; // w0 - __int64 v6; // x21 - _BOOL4 v7; // w21 - _JNIEnv *CurrentJNIEnv; // x0 - _JNIEnv *v9; // x0 - jint v10; // w0 - __int64 v11; // x22 - __int64 v12; // x22 - _JNIEnv *v13; // x0 - int v14; // w8 - _JNIEnv *v15; // x0 - _JNIEnv *v16; // x0 - double v17; // d8 - bool v18; // w19 - double v19; // d1 - double v20; // d0 - double v21; // d12 - double v22; // d8 - float v23; // s0 - char v24; // w20 - pthread_mutexattr_t *v25; // x19 - pthread_mutexattr_t *v26; // x19 - pthread_mutexattr_t *v27; // x19 - struct timeval v29; // [xsp+0h] [xbp-90h] BYREF - int data; // [xsp+1Ch] [xbp-74h] BYREF - - OS_ApplicationPreinit(); - AND_KeyboardInitialize(); - memset(lastGamepadAxis, 0, sizeof(lastGamepadAxis)); - gettimeofday(&v29, 0LL); - base_time = (double)v29.tv_usec / 1000000.0 + (double)v29.tv_sec; - if ( !ANDThread_Initted ) - { - pthread_key_create(&ANDThreadStorageKey, ANDThreadData::Destroy); - v4 = malloc(0x18uLL); - v5 = ANDThreadStorageKey; - v4[1] = 0LL; - v4[2] = 0LL; - *v4 = 0LL; - pthread_setspecific(v5, v4); - ANDThread_Initted = 1; - } - v6 = operator new(0x30uLL); - pthread_mutexattr_init((pthread_mutexattr_t *)(v6 + 40)); - pthread_mutexattr_settype((pthread_mutexattr_t *)(v6 + 40), 1); - pthread_mutex_init((pthread_mutex_t *)v6, (const pthread_mutexattr_t *)(v6 + 40)); - fileMutex = (OSMutex)v6; - if ( DoInitGraphics ) - initGraphics(); - v7 = 0; - if ( IsInitGraphics ) - goto LABEL_8; - while ( IsAndroidPaused || !v7 ) - { - while ( 1 ) - { - ++NVEventAppMain(int,char **)::iter; - v7 = ProcessEvents(0); - if ( !IsInitGraphics ) - break; - LABEL_8: - if ( !IsAndroidPaused ) - goto LABEL_12; - } - } - LABEL_12: - pthread_mutex_lock((pthread_mutex_t *)AndroidEGLContext); - CurrentJNIEnv = NVThreadGetCurrentJNIEnv(); - if ( CurrentJNIEnv && s_event_globalThiz ) - { - if ( !_JNIEnv::CallBooleanMethod(CurrentJNIEnv, s_event_globalThiz, s_makeCurrent) ) - __android_log_print(3, "NVEvent", "Error: MakeCurrent failed"); - } - else - { - __android_log_print(3, "NVEvent", "Error: No valid JNI env in MakeCurrent"); - } - AND_WRAPPER::SystemInitialize(); - v9 = NVThreadGetCurrentJNIEnv(); - v10 = _JNIEnv::CallIntMethod(v9, s_event_globalThiz, s_GetDeviceType); - isLowMemoryDevice = (((unsigned int)v10 >> 1) & 1) == 0 || v10 >> 6 < 250; - v11 = operator new(0x30uLL); - pthread_mutexattr_init((pthread_mutexattr_t *)(v11 + 40)); - pthread_mutexattr_settype((pthread_mutexattr_t *)(v11 + 40), 1); - pthread_mutex_init((pthread_mutex_t *)v11, (const pthread_mutexattr_t *)(v11 + 40)); - billingMutex = (OSMutex)v11; - v12 = operator new(0x30uLL); - pthread_mutexattr_init((pthread_mutexattr_t *)(v12 + 40)); - pthread_mutexattr_settype((pthread_mutexattr_t *)(v12 + 40), 1); - pthread_mutex_init((pthread_mutex_t *)v12, (const pthread_mutexattr_t *)(v12 + 40)); - gameServiceMutex = (OSMutex)v12; - s_conflictHandler = 0LL; - if ( OS_ApplicationInitialize(argc, (const char **)argv) ) - { - data = 0; - AND_AppInitialized = 1; - v13 = NVThreadGetCurrentJNIEnv(); - if ( _JNIEnv::CallBooleanMethod(v13, s_event_globalThiz, s_IsWifiAvailable) ) - { - v14 = 2; - } - else - { - v15 = NVThreadGetCurrentJNIEnv(); - if ( !_JNIEnv::CallBooleanMethod(v15, s_event_globalThiz, s_IsNetworkAvailable) ) - goto LABEL_23; - v14 = 1; - } - data = v14; - LABEL_23: - OS_ApplicationEvent(OSET_NetworkChanged, &data); - v16 = NVThreadGetCurrentJNIEnv(); - if ( v16 && s_event_globalThiz ) - { - if ( !_JNIEnv::CallBooleanMethod(v16, s_event_globalThiz, s_unMakeCurrent) ) - __android_log_print(3, "NVEvent", "Error: UnMakeCurrent failed"); - } - else - { - __android_log_print(3, "NVEvent", "Error: No valid JNI env in UnMakeCurrent"); - } - pthread_mutex_unlock((pthread_mutex_t *)AndroidEGLContext); - if ( OS_ApplicationStartup(windowSize[0], windowSize[1], argc, (const char **)argv) ) - { - AND_AppStarted = 1; - gettimeofday(&v29, 0LL); - if ( !v7 ) - { - v17 = (double)v29.tv_usec / 1000000.0 + (double)v29.tv_sec; - do - { - v18 = ProcessEvents(0); - while ( !v18 ) - { - if ( !IsAndroidPaused ) - break; - if ( IsAndroidInMultiplayer ) - break; - v18 = ProcessEvents(0); - usleep(0x61A8u); - } - gettimeofday(&v29, 0LL); - if ( v29.tv_usec > 1000000 || v29.tv_usec < 0 ) - v19 = OS_TimeAccurate(void)::last_current_time - - (double)(unsigned int)OS_TimeAccurate(void)::last_current_time - + 0.00033; - else - v19 = (double)v29.tv_usec / 1000000.0; - v20 = v19 + (double)v29.tv_sec; - OS_TimeAccurate(void)::last_current_time = v20; - if ( v20 - OS_TimeAccurate(void)::lastPrint > 5.0 ) - OS_TimeAccurate(void)::lastPrint = v19 + (double)v29.tv_sec; - v21 = v20 - base_time; - v22 = v20 - base_time - v17; - v23 = v22; - v24 = OS_ApplicationTick(v23); - AND_GamepadUpdate(); - AND_FileUpdate(v22); - AND_BillingUpdate(0); - v17 = v21; - } - while ( !v18 && (v24 & 1) != 0 ); - } - OS_ApplicationEvent(OSET_RequestExit, 0LL); - pthread_key_delete(ANDThreadStorageKey); - if ( items ) - { - free(items); - items = 0LL; - numItems = 0; - } - v25 = (pthread_mutexattr_t *)billingMutex; - if ( billingMutex ) - { - pthread_mutex_destroy((pthread_mutex_t *)billingMutex); - pthread_mutexattr_destroy(v25 + 5); - operator delete(v25); - } - billingMutex = 0LL; - AND_ClearAchievementData(1); - v26 = (pthread_mutexattr_t *)billingMutex; - if ( billingMutex ) - { - pthread_mutex_destroy((pthread_mutex_t *)billingMutex); - pthread_mutexattr_destroy(v26 + 5); - operator delete(v26); - } - v27 = (pthread_mutexattr_t *)fileMutex; - if ( fileMutex ) - { - pthread_mutex_destroy((pthread_mutex_t *)fileMutex); - pthread_mutexattr_destroy(v27 + 5); - operator delete(v27); - } - fileMutex = 0LL; - } - else - { - OS_ApplicationEvent(OSET_RequestExit, 0LL); - } - } - return 0; -}*/ -/* -void __fastcall OS_ApplicationEvent(OSEventType type, void *data) -{ - switch ( type ) - { - case OSET_RequestExit: - RsGlobal.quit = TRUE; - //OS_ThreadWait(mainThread); - break; - - case OSET_Pause: - SaveGameForPause(eSaveTypes::eExitSave, 0LL); - CTimer::StartUserPause(); - if ( !CPad::GetPad(0)->DisablePlayerControls - && !gMobileMenu.screenStack.numEntries - && !CCutsceneMgr::IntroTextIsActiveHack - && !CCutsceneMgr::ms_running - && !gMobileMenu.pendingScreen - && !CTouchInterface::AnyWidgetsUsingAltBack() ) - { - if (FindPlayerPed()) - bPendingPause = 1; - } - CAEAudioHardware::PauseOpenAL((CAEAudioHardware *)&AEAudioHardware, 1); - break; - case OSET_Resume: - OS_ThreadUnmakeCurrent(); - CTimer::Update(); - if ( !gMobileMenu.screenStack.numEntries && !gMobileMenu.pendingScreen ) - CTimer::EndUserPause(); - CAEAudioHardware::PauseOpenAL((CAEAudioHardware *)&AEAudioHardware, 0); - break; - case OSET_LowMemory: - //DoLowMemoryCleanup = 1; - break; - default: - return; - } -}*/ - JNI_WRAPPER int InitializeGame() { debug("Initialize Game"); @@ -421,7 +68,6 @@ JNI_WRAPPER int InitializeGame() { char* argv[1] = { nullptr }; SDL_SetHint(SDL_HINT_ORIENTATIONS, "LandscapeLeft LandscapeRight"); - // SDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS, "0"); CrashHandler::SetupSignalHandlers(); diff --git a/src/skel/android/AndroidMain.h b/src/skel/android/AndroidMain.h index dc851f3f1..6e7c1929b 100644 --- a/src/skel/android/AndroidMain.h +++ b/src/skel/android/AndroidMain.h @@ -20,19 +20,8 @@ namespace AndWrapper { extern bool AppStarted; bool InitLibraries(); - void SystemInitialize(); - void TimeInitialize(); void* GetJNI(); void* GetJNIFunc(); - void* GetObj(); - const char* GetAppId(); - const char* GetDeviceID(); - int GetDeviceInfo(int index); - bool IsAppInstalled(const char* app); - void OpenLink(const char* link); - bool DeviceIsTV(); - int DeviceLocale(); - int DeviceType(); } #endif diff --git a/src/skel/android/signalhandler/SignalHandler.cpp b/src/skel/android/signalhandler/SignalHandler.cpp index fcc23abc3..46e23a711 100644 --- a/src/skel/android/signalhandler/SignalHandler.cpp +++ b/src/skel/android/signalhandler/SignalHandler.cpp @@ -15,13 +15,7 @@ extern int16 g_usLastProcessedModelIndexAutomobile; extern int g_iLastProcessedModelIndexAutoEnt; - -extern int g_iLastProcessedSkinCollision; -extern int g_iLastProcessedEntityCollision; -extern char lastFile[123]; extern int g_iLastRenderedObject; -extern int lastNvEvent; -extern CVector lastPos; char g_iLastBlock[123]; char streamimgState[255]; @@ -34,7 +28,11 @@ namespace CrashHandler { tm* timeInfo = localtime(¤tTime); Logger::CrashLog("Crash time: %d:%d:%d %d:%d:%d", timeInfo->tm_mday, timeInfo->tm_mon, timeInfo->tm_year, timeInfo->tm_hour, timeInfo->tm_min, timeInfo->tm_sec); - Logger::CrashLog("Build times: %s %s. ABI: %s", __TIME__, __DATE__, (ANDROID_x32 ? "armeabi-v7a" : "arm64-v8a")); +#ifdef __arm__ + Logger::CrashLog("Build times: %s %s. ABI: %s", __TIME__, __DATE__, "armeabi-v7a"); +#else + Logger::CrashLog("Build times: %s %s. ABI: %s", __TIME__, __DATE__, "arm64-v8a"); +#endif Logger::CrashLog("Last processed auto and entity: %d %d", g_usLastProcessedModelIndexAutomobile, g_iLastProcessedModelIndexAutoEnt); Logger::CrashLog("Last rendered object: %d", g_iLastRenderedObject); } diff --git a/src/skel/android/signalhandler/StackTrace.h b/src/skel/android/signalhandler/StackTrace.h index 47d6c8970..2a58b1431 100644 --- a/src/skel/android/signalhandler/StackTrace.h +++ b/src/skel/android/signalhandler/StackTrace.h @@ -13,7 +13,7 @@ extern uintptr_t g_libREVC; -#if ANDROID_x32 +#ifdef __arm__ #define PRINT_CRASH_STATES(context) \ Logger::CrashLog("register states:"); \ Logger::CrashLog("r0: 0x%X, r1: 0x%X, r2: 0x%X, r3: 0x%X", (context)->uc_mcontext.arm_r0, (context)->uc_mcontext.arm_r1, (context)->uc_mcontext.arm_r2, (context)->uc_mcontext.arm_r3); \ diff --git a/src/skel/crossplatform.cpp b/src/skel/crossplatform.cpp index 99ec599d2..9873302d3 100644 --- a/src/skel/crossplatform.cpp +++ b/src/skel/crossplatform.cpp @@ -217,7 +217,7 @@ char* casepath(char const* path, bool checkPathFirst) } else #endif -#if defined(ANDROID) // TODO: Android is fuck!!! +#if defined(ANDROID) // Android FUSE resolves relative paths differently — strip the root dir prefix and open CWD char cwd[MAX_PATH]; getcwd(cwd, sizeof(cwd)); diff --git a/src/skel/sdl2/sdl2.cpp b/src/skel/sdl2/sdl2.cpp index 47b943f39..71f96b375 100644 --- a/src/skel/sdl2/sdl2.cpp +++ b/src/skel/sdl2/sdl2.cpp @@ -83,7 +83,14 @@ void _psCreateFolder(const char *path) #if defined(ANDROID) const char* pathroot = StorageRootBuffer; char dbPath[1024]; - snprintf(dbPath, sizeof(dbPath), "%s%s", pathroot, path); + if (path[0] == '/' && pathroot) { + // Path is already absolute, use as-is + snprintf(dbPath, sizeof(dbPath), "%s", path); + } else if (pathroot) { + snprintf(dbPath, sizeof(dbPath), "%s/%s", pathroot, path); + } else { + snprintf(dbPath, sizeof(dbPath), "%s", path); + } mkdir(dbPath, 0755); debug("Creating Folder Path: %s", dbPath); #else @@ -107,8 +114,18 @@ void _psCreateFolder(const char *path) const char *_psGetUserFilesFolder() { static char szUserFiles[256]; +#if defined(ANDROID) + const char *root = getenv("STORAGE_ROOT"); + if (root) { + snprintf(szUserFiles, sizeof(szUserFiles), "%s/userfiles", root); + _psCreateFolder(szUserFiles); + } else { + strcpy(szUserFiles, "userfiles"); + } +#else strcpy(szUserFiles, "userfiles"); _psCreateFolder(szUserFiles); +#endif return szUserFiles; } @@ -473,6 +490,7 @@ psSelectDevice() RwBool modeFound = FALSE; + if (!useDefault) { GnumSubSystems = RwEngineGetNumSubSystems(); @@ -592,8 +610,10 @@ psSelectDevice() } if(bestFsMode < 0){ - printf("WARNING: Cannot find desired video mode, selecting device cancelled\n"); - return FALSE; + // Fallback to windowed mode — happens on Android/GLES where + // there are no SDL fullscreen display modes + bestFsMode = bestWndMode >= 0 ? bestWndMode : 0; + FrontEndMenuManager.m_nPrefsWindowed = 1; } GcurSelVM = bestFsMode; @@ -701,7 +721,7 @@ void _InputInitialiseJoys() #if defined ANDROID const char* pathRoot = getenv("STORAGE_ROOT"); char SDL_GAMEPAD_DB_PATH[MAX_PATH]; - snprintf(SDL_GAMEPAD_DB_PATH, sizeof(SDL_GAMEPAD_DB_PATH), "%s%s", pathRoot, "gamecontrollerdb.txt"); + snprintf(SDL_GAMEPAD_DB_PATH, sizeof(SDL_GAMEPAD_DB_PATH), "%s/%s", pathRoot, "gamecontrollerdb.txt"); #else const char* SDL_GAMEPAD_DB_PATH = "gamecontrollerdb.txt"; #endif @@ -712,8 +732,9 @@ void _InputInitialiseJoys() } } - // TODO SDL2 the part below seems unnecessary SDL2 (at least on Linux), remove in the future - /*for (int i = 0; i <= SDL_NumJoysticks(); i++) { + // Detect already-connected joysticks during init. + // Needed on Android where gamepad may already be connected before SDL init. + for (int i = 0; i < SDL_NumJoysticks(); i++) { if (!IsThisJoystickBlacklisted(i)) { if (PSGLOBAL(joy1id) == -1) PSGLOBAL(joy1id) = i; @@ -732,7 +753,7 @@ void _InputInitialiseJoys() strncpy(gSelectedJoystickName, SDL_JoystickNameForIndex(PSGLOBAL(joy1id)), sizeof(gSelectedJoystickName)); #endif ControlsManager.InitDefaultControlConfigJoyPad(count); - }*/ + } } #if 0 @@ -1320,6 +1341,20 @@ main(int argc, char *argv[]) InitMemoryMgr(); #endif +#if defined(ANDROID) + // Gamepad-only: disable touch-to-mouse emulation + SDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS, "0"); + // Force landscape orientation regardless of auto-rotate + SDL_SetHint(SDL_HINT_ORIENTATIONS, "LandscapeLeft LandscapeRight"); + + // Game assets are copied to app-private storage by Java SAF picker. + if (!getenv("STORAGE_ROOT")) { + const char *extPath = SDL_AndroidGetExternalStoragePath(); + if (extPath) setenv("STORAGE_ROOT", extPath, 1); + } + StorageRootBuffer = getenv("STORAGE_ROOT"); +#endif + struct sigaction act; act.sa_sigaction = terminateHandler; act.sa_flags = SA_SIGINFO; @@ -1372,7 +1407,6 @@ main(int argc, char *argv[]) if( rsEVENTERROR == RsEventHandler(rsRWINITIALIZE, &openParams) ) { RsEventHandler(rsTERMINATE, nil); - return 0; } diff --git a/src/skel/skeleton.cpp b/src/skel/skeleton.cpp index 2bb234605..f4ee2e4b1 100644 --- a/src/skel/skeleton.cpp +++ b/src/skel/skeleton.cpp @@ -318,7 +318,7 @@ RsRwInitialize(void *displayID) * Install any platform specific file systems... */ psInstallFileSystem(); - + /* * Initialize debug message handling... */ @@ -339,7 +339,7 @@ RsRwInitialize(void *displayID) { return (FALSE); } - + openParams.displayID = displayID; if (!RwEngineOpen(&openParams)) @@ -347,14 +347,14 @@ RsRwInitialize(void *displayID) RwEngineTerm(); return (FALSE); } - + if (RsEventHandler(rsSELECTDEVICE, displayID) == rsEVENTERROR) { RwEngineClose(); RwEngineTerm(); return (FALSE); } - + if (!RwEngineStart()) { RwEngineClose(); diff --git a/src/weapons/WeaponInfo.cpp b/src/weapons/WeaponInfo.cpp index 1f78b7d45..6c2219331 100644 --- a/src/weapons/WeaponInfo.cpp +++ b/src/weapons/WeaponInfo.cpp @@ -30,7 +30,7 @@ uint16 CWeaponInfo::ms_aReloadSampleTime[WEAPONTYPE_TOTALWEAPONS] = 0, // ROCKET 250, // COLT45 250, // PYTHON - 650, // SHOTGUN + 150, // SHOTGUN 650, // SPAS12 SHOTGUN 650, // STUBBY SHOTGUN 400, // TEC9 diff --git a/vendor/mpg123/lib/Android/arm64-v8a/libmpg123.so b/vendor/mpg123/lib/Android/arm64-v8a/libmpg123.so index 36dcbe5db..c7f465738 100644 Binary files a/vendor/mpg123/lib/Android/arm64-v8a/libmpg123.so and b/vendor/mpg123/lib/Android/arm64-v8a/libmpg123.so differ diff --git a/vendor/mpg123/lib/Android/armeabi-v7a/libmpg123.so b/vendor/mpg123/lib/Android/armeabi-v7a/libmpg123.so index 2b2e32b94..a3b075712 100644 Binary files a/vendor/mpg123/lib/Android/armeabi-v7a/libmpg123.so and b/vendor/mpg123/lib/Android/armeabi-v7a/libmpg123.so differ diff --git a/vendor/sdl2/libs/Android/arm64-v8a/libSDL2.so b/vendor/sdl2/libs/Android/arm64-v8a/libSDL2.so index fc7f72cc9..2a986d5bf 100644 Binary files a/vendor/sdl2/libs/Android/arm64-v8a/libSDL2.so and b/vendor/sdl2/libs/Android/arm64-v8a/libSDL2.so differ diff --git a/vendor/sdl2/libs/Android/armeabi-v7a/libSDL2.so b/vendor/sdl2/libs/Android/armeabi-v7a/libSDL2.so index 9013ffc04..6a5966d8c 100644 Binary files a/vendor/sdl2/libs/Android/armeabi-v7a/libSDL2.so and b/vendor/sdl2/libs/Android/armeabi-v7a/libSDL2.so differ