[android] Refactor source code port for Android

This commit is contained in:
mrxreverse@gmail.com
2026-01-17 03:51:55 -03:00
parent 7ca74c16da
commit 60620ac8bb
29 changed files with 3263 additions and 77 deletions
+62 -5
View File
@@ -20,15 +20,25 @@ header_directories(${PROJECT}_INCLUDES)
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/extras/GitSHA1.cpp.in" "${CMAKE_CURRENT_BINARY_DIR}/extras/GitSHA1.cpp" @ONLY)
list(APPEND ${PROJECT}_SOURCES "${CMAKE_CURRENT_BINARY_DIR}/extras/GitSHA1.cpp")
add_executable(${EXECUTABLE} WIN32
${${PROJECT}_SOURCES}
)
if(NOT ANDROID)
add_executable(${EXECUTABLE} WIN32 ${${PROJECT}_SOURCES})
elseif(ANDROID)
add_library(${EXECUTABLE} SHARED ${${PROJECT}_SOURCES})
endif()
target_link_libraries(${EXECUTABLE} PRIVATE
librw::librw
Threads::Threads
)
if(ANDROID)
target_link_libraries(${EXECUTABLE} PRIVATE
log
GLESv3
OpenSLES
)
endif()
target_include_directories(${EXECUTABLE}
PRIVATE
$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}>
@@ -49,8 +59,29 @@ if(LIBRW_PLATFORM_D3D9)
)
endif()
target_compile_definitions(${EXECUTABLE} PRIVATE CMAKE_BUILD)
target_compile_definitions(${EXECUTABLE} PRIVATE USE_OUR_VERSIONING)
if(ANDROID)
target_compile_definitions(${EXECUTABLE} PRIVATE
ANDROID
_ANDROID
LINUX
_LINUX
POSIX
_POSIX
NO_HOOK_MALLOC
AL_LIBTYPE_STATIC
GNUC
RW_GL3
LIBRW_SDL2
LIBRW
AUDIO_OAL
USE_OUR_VERSIONING
HAVE_EFFICIENT_UNALIGNED_ACCESS
VECTORIZE_SINCOS
)
else()
target_compile_definitions(${EXECUTABLE} PRIVATE CMAKE_BUILD)
target_compile_definitions(${EXECUTABLE} PRIVATE USE_OUR_VERSIONING)
endif()
if(${PROJECT}_AUDIO STREQUAL "OAL")
find_package(OpenAL REQUIRED)
@@ -91,6 +122,12 @@ target_compile_definitions(${EXECUTABLE} PRIVATE )
option(${PROJECT}_WITH_SANITIZERS "Use UB sanitizers (better crash log)" OFF)
option(${PROJECT}_WITH_ASAN "Use Address sanitizer (better crash log)" OFF)
option(${PROJECT}_DISABLE_WARNINGS "Disable build warnings" ON)
if(${PROJECT}_DISABLE_WARNINGS)
target_compile_options(${EXECUTABLE} PRIVATE
-w)
endif()
if(${PROJECT}_WITH_SANITIZERS)
target_compile_options(${EXECUTABLE} PUBLIC
@@ -105,6 +142,26 @@ if(${PROJECT}_WITH_ASAN)
endif()
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
if(ANDROID)
target_compile_options(${EXECUTABLE}
PRIVATE
-pthread
-std=c++11
-fno-strict-aliasing
-fexceptions
-Wcast-align
-Wuninitialized
-Winit-self
-Wstrict-aliasing
-Wno-reorder
-Wno-unknown-pragmas
-Wno-unused-function
-Wno-unused-but-set-variable
-Wno-unused-value
-Wno-unused-command-line-argument
-faligned-new
)
endif()
target_compile_options(${EXECUTABLE}
PRIVATE
"-Wall"
+6 -2
View File
@@ -499,11 +499,15 @@ _FindMP3s(void)
printf("MP3 folder path is too long, no place left for file names. MP3 finding aborted.\n");
return;
}
#if defined ANDROID
OutputDebugString("Finding MP3s...");
strcpy(path, _mp3DirectoryPath);
strcat(path, "/MP3/");
#else
OutputDebugString("Finding MP3s...");
strcpy(path, _mp3DirectoryPath);
strcat(path, "\\MP3\\");
#endif
#if !defined(_WIN32)
char *actualPath = casepath(path);
if (actualPath) {
+98 -24
View File
@@ -1,14 +1,22 @@
#ifndef _WIN32
#include "common.h"
#ifdef GTA_PC
#ifdef GTA_PC || GTA_MOBILE
#include "crossplatform.h"
#include <signal.h>
#include <pthread.h>
#ifndef ANDROID
#include <semaphore.h>
#endif
#include <sys/types.h>
#include <unistd.h>
#include <sys/time.h>
#ifndef ANDROID
#include <sys/statvfs.h>
#else
#include "AndroidMain.h"
#include <sys/vfs.h>
#define statvfs statfs
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
@@ -24,7 +32,7 @@
#include "MemoryMgr.h"
#define CDDEBUG(f, ...) debug ("%s: " f "\n", "cdvd_stream", ## __VA_ARGS__)
#define CDTRACE(f, ...) printf("%s: " f "\n", "cdvd_stream", ## __VA_ARGS__)
#define CDTRACE(f, ...) debug ("%s: " f "\n", "cdvd_stream trace", ## __VA_ARGS__)
#ifdef FLUSHABLE_STREAMING
bool flushStream[MAX_CDCHANNELS];
@@ -52,32 +60,32 @@ re3_sem_close(sem_t* sem)
free(sem);
}
#else
#elifndef ANDROID
#define RE3_SEM_OPEN re3_sem_open
sem_t*
re3_sem_open(const char* format, ...)
{
char semName[21];
va_list va;
va_start(va, format);
vsprintf(semName, format, va);
char semName[21];
va_list va;
va_start(va, format);
vsprintf(semName, format, va);
return sem_open(semName, O_CREAT, 0644, 1);
return sem_open(semName, O_CREAT, 0644, 1);
}
#define RE3_SEM_CLOSE re3_sem_close
void
re3_sem_close(sem_t* sem, const char* format, ...)
{
sem_close(sem);
sem_close(sem);
char semName[21];
va_list va;
va_start(va, format);
vsprintf(semName, format, va);
char semName[21];
va_list va;
va_start(va, format);
vsprintf(semName, format, va);
sem_unlink(semName);
sem_unlink(semName);
}
#endif
@@ -95,7 +103,11 @@ struct CdReadInfo
pthread_t pChannelThread;
sem_t *pStartSemaphore;
#endif
#ifndef ANDROID
sem_t *pDoneSemaphore; // used for CdStreamSync
#elif ANDROID
pthread_mutex_t pDoneSemaphore; // used for CdStreamSync
#endif
int32 hFile;
};
@@ -108,7 +120,11 @@ char *gImgNames[MAX_CDIMAGES];
#ifndef ONE_THREAD_PER_CHANNEL
pthread_t _gCdStreamThread;
#ifndef ANDROID
sem_t *gCdStreamSema; // released when we have new thing to read(so channel is set)
#elif ANDROID
pthread_mutex_t gCdStreamSema; // released when we have new thing to read(so channel is set)
#endif
int8 gCdStreamThreadStatus; // 0: created 1:priority set up 2:abort now
Queue gChannelRequestQ;
bool _gbCdStreamOverlapped;
@@ -132,21 +148,26 @@ CdStreamInitThread(void)
gChannelRequestQ.tail = 0;
gChannelRequestQ.size = gNumChannels + 1;
ASSERT(gChannelRequestQ.items != nil );
gCdStreamSema = RE3_SEM_OPEN("/semaphore_cd_stream");
#ifndef ANDROID
gCdStreamSema = RE3_SEM_OPEN("/semaphore_cd_stream");
if (gCdStreamSema == SEM_FAILED) {
CDTRACE("failed to create stream semaphore");
ASSERT(0);
return;
}
if (gCdStreamSema == SEM_FAILED) {
CDTRACE("failed to create stream semaphore");
ASSERT(0);
return;
}
#elif ANDROID
//pthread_mutex_unlock(&gCdStreamSema);
#endif
#endif
if ( gNumChannels > 0 )
{
for ( int32 i = 0; i < gNumChannels; i++ )
{
gpReadInfo[i].pDoneSemaphore = RE3_SEM_OPEN("/semaphore_done%d", i);
#ifndef ANDROID
gpReadInfo[i].pDoneSemaphore = RE3_SEM_OPEN("/semaphore_done%d", i);
if (gpReadInfo[i].pDoneSemaphore == SEM_FAILED)
{
@@ -154,6 +175,9 @@ CdStreamInitThread(void)
ASSERT(0);
return;
}
#elif ANDROID
//pthread_mutex_unlock(&gpReadInfo[i].doneMutex);
#endif
#ifdef ONE_THREAD_PER_CHANNEL
gpReadInfo[i].pStartSemaphore = RE3_SEM_OPEN("/semaphore_start%d", i);
@@ -199,14 +223,32 @@ void
CdStreamInit(int32 numChannels)
{
struct statvfs fsInfo;
#if defined ANDROID
char imgPath[MAX_PATH];
if(StorageRootBuffer == NULL) {
char pwd[128];
getcwd(pwd, 128);
setenv("STORAGE_ROOT", pwd, 1);
debug("%s\n", pwd);
}
debug("FILES %s\n", StorageRootBuffer);
strcpy(imgPath, StorageRootBuffer);
strcat(imgPath, "/models/gta3.img");
debug("%s\n", imgPath);
if((statvfs("models/gta3.img", &fsInfo)) < 0)
if((statvfs(imgPath, &fsInfo)) < 0)
#elifndef ANDROID
if((statvfs("models/gta3.img", &fsInfo)) < 0)
#endif
{
CDTRACE("can't get filesystem info");
ASSERT(0);
return;
}
#ifdef __linux__
#if defined ANDROID
_gdwCdStreamFlags = O_RDONLY;
#elif defined __linux__
_gdwCdStreamFlags = O_RDONLY | O_NOATIME;
#else
_gdwCdStreamFlags = O_RDONLY;
@@ -271,7 +313,11 @@ CdStreamShutdown(void)
// Destroying semaphores and free(gpReadInfo) will be done at threads
#ifndef ONE_THREAD_PER_CHANNEL
gCdStreamThreadStatus = 2;
#ifndef ANDROID
sem_post(gCdStreamSema);
#elif ANDROID
pthread_mutex_unlock(&gCdStreamSema);
#endif
pthread_join(_gCdStreamThread, nil);
#else
for ( int32 i = 0; i < gNumChannels; i++ ) {
@@ -318,7 +364,11 @@ CdStreamRead(int32 channel, void *buffer, uint32 offset, uint32 size)
#ifndef ONE_THREAD_PER_CHANNEL
AddToQueue(&gChannelRequestQ, channel);
if ( sem_post(gCdStreamSema) != 0 )
#if defined ANDROID
if ( pthread_mutex_unlock(&gCdStreamSema) != 0 )
#else
if ( sem_post(gCdStreamSema) != 0 )
#endif
printf("Signal Sema Error\n");
#else
if ( sem_post(pChannel->pStartSemaphore) != 0 )
@@ -387,7 +437,11 @@ CdStreamSync(int32 channel)
pthread_kill(_gCdStreamThread, SIGUSR1);
#endif
while (pChannel->bLocked)
#ifndef ANDROID
sem_wait(pChannel->pDoneSemaphore);
#elif ANDROID
pthread_mutex_lock(&pChannel->pDoneSemaphore);
#endif
}
pChannel->bReading = false;
flushStream[channel] = false;
@@ -399,7 +453,11 @@ CdStreamSync(int32 channel)
{
pChannel->bLocked = true;
while (pChannel->bLocked && pChannel->nSectorsToRead != 0){
#ifndef ANDROID
sem_wait(pChannel->pDoneSemaphore);
#elif ANDROID
pthread_mutex_lock(&pChannel->pDoneSemaphore);
#endif
}
pChannel->bLocked = false;
}
@@ -452,7 +510,11 @@ void *CdStreamThread(void *param)
#ifndef ONE_THREAD_PER_CHANNEL
while (gCdStreamThreadStatus != 2) {
#ifndef ANDROID
sem_wait(gCdStreamSema);
#elif ANDROID
pthread_mutex_lock(&gCdStreamSema);
#endif
int32 channel = GetFirstInQueue(&gChannelRequestQ);
@@ -510,7 +572,11 @@ void *CdStreamThread(void *param)
if ( pChannel->bLocked )
{
pChannel->bLocked = 0;
#ifndef ANDROID
sem_post(pChannel->pDoneSemaphore);
#else
pthread_mutex_unlock(&pChannel->pDoneSemaphore);
#endif
}
pChannel->bReading = false;
}
@@ -518,9 +584,17 @@ void *CdStreamThread(void *param)
#ifndef ONE_THREAD_PER_CHANNEL
for ( int32 i = 0; i < gNumChannels; i++ )
{
#ifndef ANDROID
RE3_SEM_CLOSE(gpReadInfo[i].pDoneSemaphore, "/semaphore_done%d", i);
#elif ANDROID
pthread_mutex_destroy(&gpReadInfo[i].pDoneSemaphore);
#endif
}
#ifndef ANDROID
RE3_SEM_CLOSE(gCdStreamSema, "/semaphore_cd_stream");
#elif ANDROID
pthread_mutex_destroy(&gCdStreamSema);
#endif
free(gChannelRequestQ.items);
#else
RE3_SEM_CLOSE(gpReadInfo[channel].pStartSemaphore, "/semaphore_start%d", channel);
+51
View File
@@ -26,6 +26,13 @@ CControllerConfigManager::CControllerConfigManager()
m_bFirstCapture = false;
m_bMouseAssociated = false;
m_lStickSensX = 1.0f;
m_lStickSensY = 1.0f;
m_rStickSensX = 1.0f;
m_rStickSensY = 1.0f;
m_lStickDeadzone = 0.3f;
m_rStickDeadzone = 0.3f;
MakeControllerActionsBlank();
InitDefaultControlConfiguration();
InitialiseControllerActionNameArray();
@@ -47,6 +54,7 @@ void CControllerConfigManager::MakeControllerActionsBlank()
#ifdef RW_GL3
int MapIdToButtonId(int mapId) {
#ifndef LIBRW_SDL2
switch (mapId) {
case GLFW_GAMEPAD_BUTTON_A: // Cross
return 2;
@@ -84,6 +92,45 @@ int MapIdToButtonId(int mapId) {
default:
return 0;
}
#else
switch (mapId) {
case SDL_CONTROLLER_BUTTON_A: // Cross
return 2;
case SDL_CONTROLLER_BUTTON_B: // Circle
return 1;
case SDL_CONTROLLER_BUTTON_X: // Square
return 3;
case SDL_CONTROLLER_BUTTON_Y: // Triangle
return 4;
case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
return 7;
case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
return 8;
case SDL_CONTROLLER_BUTTON_BACK:
return 9;
case SDL_CONTROLLER_BUTTON_START:
return 12;
case SDL_CONTROLLER_BUTTON_LEFTSTICK:
return 10;
case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
return 11;
case SDL_CONTROLLER_BUTTON_DPAD_UP:
return 13;
case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
return 14;
case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
return 15;
case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
return 16;
// SDL2 sends those as axes, so I added them here manually.
case 15: // Left trigger
return 5;
case 16: // Right trigger
return 6;
default:
return 0;
}
#endif
}
#endif
@@ -2807,7 +2854,11 @@ void CControllerConfigManager::UpdateJoyButtonState(int32 padnumber)
#elif defined RW_GL3
if (m_NewState.isGamepad) {
for (int32 i = 0; i < MAX_BUTTONS; i++) {
#ifndef LIBRW_SDL2
if (i == GLFW_GAMEPAD_BUTTON_GUIDE)
#else
if (i == SDL_CONTROLLER_BUTTON_GUIDE)
#endif
continue;
m_aButtonStates[MapIdToButtonId(i)-1] = m_NewState.mappedButtons[i];
+11 -9
View File
@@ -104,18 +104,18 @@ class CMouseControllerState;
class CControllerState;
#define JOY_BUTTONS 16
#define JOY_BUTTONS 32
#define MAX_BUTTONS (JOY_BUTTONS+1)
#define ACTIONNAME_LENGTH 40
#ifdef RW_GL3
struct GlfwJoyState {
int8 id;
bool isGamepad;
uint8 numButtons;
uint8* buttons;
bool mappedButtons[17];
struct JoyState {
int8 id;
bool isGamepad;
uint8 numButtons;
uint8 buttons[MAX_BUTTONS];
bool mappedButtons[MAX_BUTTONS];
};
#endif
@@ -136,8 +136,8 @@ public:
bool m_bFirstCapture;
#if defined RW_GL3
GlfwJoyState m_OldState;
GlfwJoyState m_NewState;
JoyState m_OldState;
JoyState m_NewState;
#else
DIJOYSTATE2 m_OldState;
DIJOYSTATE2 m_NewState;
@@ -147,6 +147,8 @@ public:
tControllerConfigBind m_aSettings[MAX_CONTROLLERACTIONS][MAX_CONTROLLERTYPES];
bool m_aSimCheckers[MAX_SIMS][MAX_CONTROLLERTYPES];
bool m_bMouseAssociated;
float m_lStickSensX, m_lStickSensY, m_rStickSensX, m_rStickSensY;
float m_lStickDeadzone, m_rStickDeadzone;
#ifdef LOAD_INI_SETTINGS
static uint32 ms_padButtonsInited;
+31 -3
View File
@@ -37,9 +37,21 @@ static myFILE myfiles[NUMFILES];
// Case-insensitivity on linux (from https://github.com/OneSadCookie/fcaseopen)
void mychdir(char const *path)
{
#if defined(ANDROID)
if(!path) {
return;
}
#endif
char* r = casepath(path, false);
if (r) {
#if defined(ANDROID)
char path[MAX_PATH];
strcpy(path, CFileMgr::GetRootDirName());
strcat(path, r);
chdir(path);
#else
chdir(r);
#endif
free(r);
} else {
errno = ENOENT;
@@ -200,8 +212,16 @@ char CFileMgr::ms_dirName[128];
void
CFileMgr::Initialise(void)
{
_getcwd(ms_rootDirName, 128);
#if defined(ANDROID)
if(getenv("STORAGE_ROOT") != NULL) {
strcpy(ms_rootDirName, getenv("STORAGE_ROOT"));
strcat(ms_rootDirName, "/");
debug("Android: Root Dir: %s\n", ms_rootDirName);
}
#else
_getcwd(ms_rootDirName, 128);
strcat(ms_rootDirName, "\\");
#endif
}
void
@@ -213,10 +233,13 @@ CFileMgr::ChangeDir(const char *dir)
}
if(*dir != '\0'){
strcat(ms_dirName, dir);
// BUG in the game it seems, it's off by one
#ifndef ANDROID
// BUG in the game it seems, it's off by one
if(dir[strlen(dir)-1] != '\\')
strcat(ms_dirName, "\\");
#endif
}
debug("CFileMgr::ChangeDir: %s", ms_dirName);
mychdir(ms_dirName);
}
@@ -226,10 +249,13 @@ CFileMgr::SetDir(const char *dir)
strcpy(ms_dirName, ms_rootDirName);
if(*dir != '\0'){
strcat(ms_dirName, dir);
// BUG in the game it seems, it's off by one
#ifndef ANDROID
// BUG in the game it seems, it's off by one
if(dir[strlen(dir)-1] != '\\')
strcat(ms_dirName, "\\");
#endif
}
debug("CFileMgr::SetDir: %s", ms_dirName);
mychdir(ms_dirName);
}
@@ -267,12 +293,14 @@ CFileMgr::LoadFile(const char *file, uint8 *buf, int maxlen, const char *mode)
int
CFileMgr::OpenFile(const char *file, const char *mode)
{
debug("CFileMgr::OpenFile: %s", file);
return myfopen(file, mode);
}
int
CFileMgr::OpenFileForWriting(const char *file)
{
debug("CFileMgr::OpenFileForWriting: %s", file);
return OpenFile(file, "wb");
}
+10 -2
View File
@@ -633,10 +633,11 @@ CMenuManager::CentreMousePointer()
Point.y = SCREEN_HEIGHT / 2;
ClientToScreen(PSGLOBAL(window), &Point);
SetCursorPos(Point.x, Point.y);
#elif defined RW_GL3
#elif defined RW_GL3 && !defined(LIBRW_SDL2)
glfwSetCursorPos(PSGLOBAL(window), SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2);
#elif defined(RW_GL3) && defined(LIBRW_SDL2)
SDL_WarpMouseInWindow(PSGLOBAL(window), SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2);
#endif
PSGLOBAL(lastMousePos.x) = SCREEN_WIDTH / 2;
PSGLOBAL(lastMousePos.y) = SCREEN_HEIGHT / 2;
}
@@ -4946,6 +4947,13 @@ CMenuManager::ProcessUserInput(uint8 goDown, uint8 goUp, uint8 optionSelected, u
PSGLOBAL(joy1)->GetCapabilities(&devCaps);
ControlsManager.InitDefaultControlConfigJoyPad(devCaps.dwButtons);
}
#elif defined(LIBRW_SDL2) // TODO SDL2 the part below seems unnecessary SDL2 (at least on Linux), remove in the future
/*if (PSGLOBAL(joy1id) != -1 && SDL_IsGameController(PSGLOBAL(joy1id))) {
SDL_Joystick* joy1 = SDL_JoystickOpen(PSGLOBAL(joy1id));
int count = SDL_JoystickNumButtons(joy1);
SDL_JoystickClose(joy1);
ControlsManager.InitDefaultControlConfigJoyPad(count);
}*/
#else
if (PSGLOBAL(joy1id) != -1 && glfwJoystickPresent(PSGLOBAL(joy1id))) {
int count;
+26 -10
View File
@@ -871,9 +871,13 @@ CMouseControllerState CMousePointerStateHelper::GetMouseSetUp()
}
#else
// It seems there is no way to get number of buttons on mouse, so assign all buttons if we have mouse.
double xpos = 1.0f, ypos;
#if !defined(LIBRW_SDL2)
double xpos = 1.0f, ypos;
glfwGetCursorPos(PSGLOBAL(window), &xpos, &ypos);
#else
int xpos = 1, ypos;
SDL_GetMouseState(&xpos, &ypos);
#endif
if (xpos != 0.f) {
state.MMB = true;
state.RMB = true;
@@ -931,8 +935,13 @@ void CPad::UpdateMouse()
#else
if ( IsForegroundApp() && PSGLOBAL(cursorIsInWindow) )
{
double xpos = 1.0f, ypos;
#ifdef LIBRW_SDL2
int xpos = 1, ypos;
int mouseState = SDL_GetMouseState(&xpos, &ypos);
#else
double xpos = 1.0f, ypos;
glfwGetCursorPos(PSGLOBAL(window), &xpos, &ypos);
#endif
if (xpos == 0.f)
return;
@@ -951,12 +960,19 @@ void CPad::UpdateMouse()
PCTempMouseControllerState.x = (float)(signX * (xpos - PSGLOBAL(lastMousePos.x)));
PCTempMouseControllerState.y = (float)(signy * (ypos - PSGLOBAL(lastMousePos.y)));
#ifdef LIBRW_SDL2
PCTempMouseControllerState.LMB = !!(mouseState & SDL_BUTTON(1));
PCTempMouseControllerState.RMB = !!(mouseState & SDL_BUTTON(3));
PCTempMouseControllerState.MMB = !!(mouseState & SDL_BUTTON(2));
PCTempMouseControllerState.MXB1 = !!(mouseState & SDL_BUTTON(4));
PCTempMouseControllerState.MXB2 = !!(mouseState & SDL_BUTTON(5));
#else
PCTempMouseControllerState.LMB = glfwGetMouseButton(PSGLOBAL(window), GLFW_MOUSE_BUTTON_LEFT);
PCTempMouseControllerState.RMB = glfwGetMouseButton(PSGLOBAL(window), GLFW_MOUSE_BUTTON_RIGHT);
PCTempMouseControllerState.MMB = glfwGetMouseButton(PSGLOBAL(window), GLFW_MOUSE_BUTTON_MIDDLE);
PCTempMouseControllerState.MXB1 = glfwGetMouseButton(PSGLOBAL(window), GLFW_MOUSE_BUTTON_4);
PCTempMouseControllerState.MXB2 = glfwGetMouseButton(PSGLOBAL(window), GLFW_MOUSE_BUTTON_5);
#endif
if (PSGLOBAL(mouseWheel) > 0)
PCTempMouseControllerState.WHEELUP = 1;
else if (PSGLOBAL(mouseWheel) < 0)
@@ -1631,14 +1647,14 @@ void CPad::AffectFromXinput(uint32 pad)
float rx = (float)xstate.Gamepad.sThumbRX / (float)0x7FFF;
float ry = (float)xstate.Gamepad.sThumbRY / (float)0x7FFF;
if (Abs(lx) > 0.3f || Abs(ly) > 0.3f) {
PCTempJoyState.LeftStickX = (int32)(lx * 128.0f);
PCTempJoyState.LeftStickY = (int32)(-ly * 128.0f);
if (Abs(lx) > ControlsManager.m_lStickDeadzone || Abs(ly) > ControlsManager.m_lStickDeadzone) {
PCTempJoyState.LeftStickX = (int32)(lx * 128.0f * ControlsManager.m_lStickSensX);
PCTempJoyState.LeftStickY = (int32)(-ly * 128.0f * ControlsManager.m_lStickSensY);
}
if (Abs(rx) > 0.3f || Abs(ry) > 0.3f) {
PCTempJoyState.RightStickX = (int32)(rx * 128.0f);
PCTempJoyState.RightStickY = (int32)(-ry * 128.0f);
if (Abs(rx) > ControlsManager.m_rStickDeadzone || Abs(ry) > ControlsManager.m_rStickDeadzone) {
PCTempJoyState.RightStickX = (int32)(rx * 128.0f * ControlsManager.m_rStickSensX);
PCTempJoyState.RightStickY = (int32)(-ry * 128.0f * ControlsManager.m_rStickSensY);
}
XINPUT_VIBRATION VibrationState;
+5 -1
View File
@@ -21,6 +21,10 @@
#include <string.h>
#include <math.h>
#if defined ANDROID
#include <android/log.h>
#endif
#ifdef __MWERKS__
#define AUDIO_MSS
#define RWLIBS // codewarrior doesn't support project level defines - so not even this is enough, but still catches most ifdefs
@@ -341,7 +345,7 @@ void re3_usererror(const char *format, ...);
#define DEBUGBREAK() __debugbreak();
// Switch to enable development messages.
#if 1
#if 0
#define DEV(f, ...)
#else
#define DEV(f, ...) re3_debug("[DEV]: " f, ## __VA_ARGS__)
+12 -4
View File
@@ -26,9 +26,11 @@ enum Config {
PEDMODELSIZE = 130,
VEHICLEMODELSIZE = 110,
TWODFXSIZE = 1210,
MAXVEHICLESLOADED = 50, // 70 on mobile
#if defined ANDROID
MAXVEHICLESLOADED = 70,
#else
MAXVEHICLESLOADED = 50, // 70 on mobile
#endif
NUMOBJECTINFO = 210,
// Pool sizes
@@ -154,12 +156,14 @@ enum Config {
#define GTA_PC
//#define GTA_PS2
//#define GTA_XBOX
#define GTA_MOBILE
// Version defines
#define GTAVC_PS2 400
#define GTAVC_PC_10 410
#define GTAVC_PC_11 411
#define GTAVC_PC_JAP 412
#define GTAVC_ANDROID 413 // ANDROID 413?
// TODO? maybe something for xbox or android?
#define GTA_VERSION GTAVC_PC_11
@@ -290,7 +294,7 @@ enum Config {
#define EXTENDED_OFFSCREEN_DESPAWN_RANGE // Use onscreen despawn range for offscreen peds and vehicles to avoid them despawning in the distance when you look
// away
#if defined(__LP64__) || defined(_WIN64)
#if defined(__LP64__) || defined(_WIN64) || defined(__aarch64__)
#define FIX_BUGS_64 // Must have fixes to be able to run 64 bit build
#endif
@@ -306,6 +310,10 @@ enum Config {
#define MISSION_SWITCHER // from debug menu
#endif
#ifdef ANDROID
#define USE_FILE_LOG
#endif
// Rendering/display
#define ASPECT_RATIO_SCALE // Not just makes everything scale with aspect ratio, also adds support for all aspect ratios
#define PROPER_SCALING // use original DEFAULT_SCREEN_WIDTH/DEFAULT_SCREEN_HEIGHT from PS2 instead of PC(R* changed HEIGHT here to make radar look better, but broke other hud elements aspect ratio).
+10 -2
View File
@@ -1084,8 +1084,10 @@ DisplayGameDebugText()
sprintf(verA,
#if defined _WIN32
"Win "
#elif defined __linux__
#elif defined __linux__ && !defined ANDROID
"Linux "
#elif defined(ANDROID)
"Android "
#elif defined __APPLE__
"Mac OS X "
#elif defined __FreeBSD__
@@ -1093,8 +1095,14 @@ DisplayGameDebugText()
#else
"Posix-compliant "
#endif
#if defined __LP64__ || defined _WIN64
#if defined __LP64__ || defined _WIN64 || defined __aarch64__
#if defined ANDROID
"arm64-v8a "
#else
"64-bit "
#endif
#elif defined ANDROID
"armeabi-v7a "
#else
"32-bit "
#endif
+16 -6
View File
@@ -136,6 +136,9 @@ CustomFrontendOptionsPopulate(void)
#ifdef EXTENDED_PIPELINES
const char *vehPipelineNames[] = { "FED_MFX", "FED_NEO" };
const char *off_on[] = { "FEM_OFF", "FEM_ON" };
#if defined ANDROID
CFileMgr::ChangeDir("\\");
#endif
fd = CFileMgr::OpenFile("neo/neo.txd","r");
if (fd) {
#ifdef GRAPHICS_MENU_OPTIONS
@@ -1212,7 +1215,7 @@ void re3_assert(const char *expr, const char *filename, unsigned int lineno, con
strcat_s(re3_buff, re3_buffsize, "(Press Retry to debug the application)");
nCode = ::MessageBoxA(nil, re3_buff, "REVC Assertion Failed!",
nCode = ::MessageBoxA(nil, re3_buff, "reVC Assertion Failed!",
MB_ABORTRETRYIGNORE|MB_ICONHAND|MB_SETFOREGROUND|MB_TASKMODAL);
if (nCode == IDABORT)
@@ -1233,7 +1236,7 @@ void re3_assert(const char *expr, const char *filename, unsigned int lineno, con
abort();
#else
// TODO
printf("\nREVC ASSERT FAILED\n\tFile: %s\n\tLine: %d\n\tFunction: %s\n\tExpression: %s\n",filename,lineno,func,expr);
printf("\nreVC ASSERT FAILED\n\tFile: %s\n\tLine: %d\n\tFunction: %s\n\tExpression: %s\n",filename,lineno,func,expr);
assert(false);
#endif
}
@@ -1250,7 +1253,9 @@ void re3_debug(const char *format, ...)
vsprintf(re3_buff, format, va);
#endif
va_end(va);
#if defined ANDROID
__android_log_print(ANDROID_LOG_DEBUG, "reVC-DEBUG", re3_buff);
#endif
printf("%s", re3_buff);
CDebug::DebugAddText(re3_buff);
#endif
@@ -1273,7 +1278,9 @@ void re3_trace(const char *filename, unsigned int lineno, const char *func, cons
sprintf(buff, "[%s.%s:%d]: %s", filename, func, lineno, re3_buff);
#endif
#if defined ANDROID
__android_log_print(ANDROID_LOG_DEBUG, "reVC-TRACE", buff);
#endif
OutputDebugString(buff);
}
#endif
@@ -1287,14 +1294,17 @@ void re3_usererror(const char *format, ...)
vsprintf_s(re3_buff, re3_buffsize, format, va);
va_end(va);
::MessageBoxA(nil, re3_buff, "REVC Error!",
::MessageBoxA(nil, re3_buff, "reVC Error!",
MB_OK|MB_ICONHAND|MB_SETFOREGROUND|MB_TASKMODAL);
raise(SIGABRT);
_exit(3);
#else
vsprintf(re3_buff, format, va);
printf("\nREVC Error!\n\t%s\n",re3_buff);
#if defined ANDROID
__android_log_print(ANDROID_LOG_ERROR, "reVC-ERROR", re3_buff);
#endif
printf("\nreVC Error!\n\t%s\n",re3_buff);
assert(false);
#endif
}
+6
View File
@@ -29,6 +29,9 @@
#include "SaveBuf.h"
int gBuildings;
#if defined ANDROID
int g_iLastRenderedObject;
#endif
CEntity::CEntity(void)
{
@@ -415,6 +418,9 @@ CEntity::PreRender(void)
void
CEntity::Render(void)
{
#if defined ANDROID
g_iLastRenderedObject = this->m_modelIndex;
#endif
if(m_rwObject){
bImBeingRendered = true;
if(RwObjectGetType(m_rwObject) == rpATOMIC)
+6 -1
View File
@@ -19,7 +19,12 @@ C_PcSave PcSaveHelper;
void
C_PcSave::SetSaveDirectory(const char *path)
{
sprintf(DefaultPCSaveFileName, "%s\\%s", path, "GTAVCsf");
#if defined ANDROID
sprintf(DefaultPCSaveFileName, "%s/%s", path, "GTAVCsf");
debug("SetSaveDirectory: %s", DefaultPCSaveFileName);
#else
sprintf(DefaultPCSaveFileName, "%s\\%s", path, "GTAVCsf");
#endif
}
bool
+432
View File
@@ -0,0 +1,432 @@
//
// Created by mrxenginner on 13/07/2025.
//
#if defined ANDROID
#include "AndroidMain.h"
#include "JavaWrapper.h"
#include "logger/log.h"
#include <SDL_hints.h>
#include <SDL_main.h>
#include <SignalHandler.h>
#include <patch/patch.h>
JavaVM* javaVM = NULL;
char* StorageRootBuffer = NULL;
uintptr_t g_libREVC = NULL;
bool AndWrapper::AppInitialized = false;
bool AndWrapper::AppStarted = false;
JAVA_WRAPPER Java_org_libsdl_app_SDLActivity_nativeSetupJNI(JNIEnv* env, jobject initGame)
{
if(!javaVM) {
env->GetJavaVM(&javaVM);
}
g_pJavaWrapper = new CJavaWrapper(env, initGame);
}
JAVA_WRAPPER Java_com_revc_game_core_REVC_setGamePath(JNIEnv *env, jobject obj, jstring value)
{
const char* root = env->GetStringUTFChars(value, NULL);
setenv("STORAGE_ROOT", root, 1);
StorageRootBuffer = getenv("STORAGE_ROOT");
debug("Storage Root: %s", StorageRootBuffer);
env->ReleaseStringUTFChars(value, root);
}
bool AndWrapper::InitLibraries() {
g_libREVC = Patch::FindLib("libreVC.so");
if (!g_libREVC) {
Logger::Log("[ERROR]: Required libraries not found!");
return false;
}
Logger::Log("[INFO]: libreVC base: 0x%X", g_libREVC);
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();
}
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");
if (!AndWrapper::InitLibraries()) return NULL;
int argc = 0;
char* argv[1] = { nullptr };
SDL_SetHint(SDL_HINT_ORIENTATIONS, "LandscapeLeft LandscapeRight");
// SDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS, "0");
CrashHandler::SetupSignalHandlers();
int result = SDL_main(argc, argv);
return result;
}
#endif
+39
View File
@@ -0,0 +1,39 @@
//
// Created by mrxenginner on 13/07/2025.
//
#ifndef REVC_ANDROIDMAIN_H
#define REVC_ANDROIDMAIN_H
#if defined ANDROID
#include "common.h"
#define JNI_WRAPPER extern "C" __attribute__ ((visibility("default")))
#define JAVA_WRAPPER extern "C" JNIEXPORT void JNICALL
extern uintptr_t g_libREVC;
extern char* StorageRootBuffer;
namespace AndWrapper {
extern bool AppInitialized;
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
#endif //REVC_ANDROIDMAIN_H
+73
View File
@@ -0,0 +1,73 @@
//
// Created by mrxenginner on 14/07/2025.
//
#include "common.h"
#include "JavaWrapper.h"
#if defined ANDROID
extern "C" JavaVM *javaVM;
JNIEnv* CJavaWrapper::GetEnv() {
JNIEnv* env = NULL;
int getEnvStat = javaVM->GetEnv((void**)&env, JNI_VERSION_1_6);
if (getEnvStat == JNI_EDETACHED) {
debug("GetEnv: not attached");
if (javaVM->AttachCurrentThread(&env, NULL) != 0) {
debug("Failed to attach");
return NULL;
}
}
if (getEnvStat == JNI_EVERSION) {
debug("GetEnv: version not supported");
return NULL;
}
if (getEnvStat == JNI_ERR) {
debug("GetEnv: JNI_ERR");
return NULL;
}
return env;
}
void CJavaWrapper::ExitGame() {
JNIEnv* env = GetEnv();
if (!env) {
debug("No env");
return;
}
env->CallVoidMethod(this->activity, this->s_ExitGame);
}
CJavaWrapper::CJavaWrapper(JNIEnv *env, jobject activity) {
this->activity = env->NewGlobalRef(activity);
jclass nvEventClass = env->GetObjectClass(activity);
if (!nvEventClass) {
debug("nvEventClass null");
return;
}
s_ExitGame = env->GetMethodID(nvEventClass, "exitGame", "()V");
env->DeleteLocalRef(nvEventClass);
env->DeleteLocalRef(activity);
}
CJavaWrapper::~CJavaWrapper() {
JNIEnv* pEnv = GetEnv();
if (pEnv) {
pEnv->DeleteGlobalRef(this->activity);
}
}
CJavaWrapper* g_pJavaWrapper = nullptr;
#endif
+39
View File
@@ -0,0 +1,39 @@
//
// Created by mrxenginner on 14/07/2025.
//
#ifndef REVC_CJAVAWRAPPER_H
#define REVC_CJAVAWRAPPER_H
#if defined ANDROID
#include <jni.h>
#include <string>
#define EXCEPTION_CHECK(env) \
if ((env)->ExceptionCheck()) \
{ \
(env)->ExceptionDescribe(); \
(env)->ExceptionClear(); \
return; \
}
class CJavaWrapper
{
jmethodID s_ExitGame;
public:
jobject activity;
CJavaWrapper(JNIEnv* env, jobject activity);
~CJavaWrapper();
void ExitGame();
// static methods
static JNIEnv* GetEnv();
};
extern CJavaWrapper* g_pJavaWrapper;
#endif
#endif //REVC_CJAVAWRAPPER_H
+65
View File
@@ -0,0 +1,65 @@
//
// Created by mrxenginner on 11/05/2025.
//
#if defined ANDROID
#include "common.h"
#include "log.h"
#include "AndroidMain.h"
extern char* StorageRootBuffer;
void Logger::Log(const char *fmt, ...)
{
static char buffer[512]{};
memset(buffer, 0, sizeof(buffer));
va_list arg;
va_start(arg, fmt);
vsnprintf(buffer, sizeof(buffer), fmt, arg);
va_end(arg);
#if defined ANDROID
__android_log_write(ANDROID_LOG_INFO, "LOG", buffer);
#endif
#ifdef USE_FILE_LOG
static FILE* flLog = nullptr;
if(flLog == nullptr && StorageRootBuffer != nullptr) {
sprintf(buffer, "%slog.txt", StorageRootBuffer);
flLog = fopen(buffer, "ab");
}
if(flLog == nullptr) return;
fprintf(flLog, "%s\n", buffer);
fflush(flLog);
#endif
}
void Logger::CrashLog(const char* fmt, ...)
{
static char buffer[512]{};
memset(buffer, 0, sizeof(buffer));
va_list arg;
va_start(arg, fmt);
vsnprintf(buffer, sizeof(buffer), fmt, arg);
va_end(arg);
#if defined ANDROID
__android_log_write(ANDROID_LOG_FATAL, "CRASH LOG", buffer);
#endif
#ifdef USE_FILE_LOG
static FILE* flLog = nullptr;
if (flLog == nullptr && StorageRootBuffer != nullptr) {
sprintf(buffer, "%scrash_log.txt", StorageRootBuffer);
flLog = fopen(buffer, "ab");
}
if (flLog == nullptr) return;
fprintf(flLog, "%s\n", buffer);
fflush(flLog);
#endif
}
#endif
+16
View File
@@ -0,0 +1,16 @@
//
// Created by mrxenginner on 11/05/2025.
//
#ifndef REVC_LOGGER_H
#define REVC_LOGGER_H
#if defined ANDROID
namespace Logger {
void Log(const char* fmt, ...);
void CrashLog(const char* fmt, ...);
}
#endif
#endif
+36
View File
@@ -0,0 +1,36 @@
//
// Created by mrxenginner on 14/07/2025.
//
#ifndef REVC_PATCH_H
#define REVC_PATCH_H
#if defined ANDROID
#include "common.h"
#include <dlfcn.h>
class Patch {
public:
static uintptr_t FindLib(const char* libName)
{
#if defined ANDROID
void* handle = dlopen(libName, RTLD_LAZY);
if (handle) {
void* symbol = dlsym(handle, "InitializeGame");
if (symbol) {
Dl_info info;
if (dladdr(symbol, &info) != 0) {
return reinterpret_cast<uintptr_t>(info.dli_fbase);
}
}
dlclose(handle);
}
#endif
return 0;
}
};
#endif
#endif // REVC_PATCH_H
@@ -0,0 +1,102 @@
//
// Created by mrxenginner on 11/05/2025.
//
#if defined ANDROID
#include "SignalHandler.h"
#include "../logger/log.h"
#include "StackTrace.h"
#include <csignal>
#include <string.h>
#include <time.h>
#include <ucontext.h>
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];
namespace CrashHandler {
static struct sigaction oldHandlers[4]; // 0: SEGV, 1: ABRT, 2: FPE, 3: BUS
void PrintBuildCrashInfo()
{
time_t currentTime = time(nullptr);
tm* timeInfo = localtime(&currentTime);
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"));
Logger::CrashLog("Last processed auto and entity: %d %d", g_usLastProcessedModelIndexAutomobile, g_iLastProcessedModelIndexAutoEnt);
Logger::CrashLog("Last rendered object: %d", g_iLastRenderedObject);
}
void SignalHandler(int signum, siginfo_t* info, void* contextPtr) {
auto* context = static_cast<ucontext_t*>(contextPtr);
struct sigaction* oldHandler = nullptr;
const char* signalName = nullptr;
switch (signum) {
case SIGSEGV:
oldHandler = &oldHandlers[0];
signalName = "SIGSEGV";
Logger::CrashLog(" ");
break;
case SIGABRT:
oldHandler = &oldHandlers[1];
signalName = "SIGABRT";
Logger::CrashLog(" ");
break;
case SIGFPE:
oldHandler = &oldHandlers[2];
signalName = "SIGFPE";
break;
case SIGBUS:
oldHandler = &oldHandlers[3];
signalName = "SIGBUS";
break;
default:
Logger::CrashLog("Unhandled signal: %d", signum);
return;
}
if (oldHandler && oldHandler->sa_sigaction) {
oldHandler->sa_sigaction(signum, info, contextPtr);
}
PrintBuildCrashInfo();
Logger::CrashLog("%s | Fault address: 0x%X", signalName, info->si_addr);
PRINT_CRASH_STATES(context);
CStackTrace::printBacktrace();
}
void SetupSignalHandlers() {
struct {
int signal;
int index;
} signals[] = {
{ SIGSEGV, 0 },
{ SIGABRT, 1 },
{ SIGFPE, 2 },
{ SIGBUS, 3 },
};
for (const auto& s : signals) {
struct sigaction act {};
act.sa_sigaction = SignalHandler;
sigemptyset(&act.sa_mask);
act.sa_flags = SA_SIGINFO;
sigaction(s.signal, &act, &oldHandlers[s.index]);
}
}
}
#endif
@@ -0,0 +1,17 @@
//
// Created by mrxenginner on 11/05/2025.
//
#ifndef REVC_SIGNALHANDLER_H
#define REVC_SIGNALHANDLER_H
#if defined ANDROID
#include "common.h"
namespace CrashHandler {
void SetupSignalHandlers();
}
#endif
#endif //REVC_SIGNALHANDLER_H
@@ -0,0 +1,67 @@
//
// Created by mrxenginner on 13/07/2025.
//
#ifndef REVC_STACKTRACE_H
#define REVC_STACKTRACE_H
#if defined ANDROID
#include <dlfcn.h>
#include <execinfo.h>
#include <unwind.h>
extern uintptr_t g_libREVC;
#if ANDROID_x32
#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); \
Logger::CrashLog("r4: 0x%x, r5: 0x%x, r6: 0x%x, r7: 0x%x", (context)->uc_mcontext.arm_r4, (context)->uc_mcontext.arm_r5, (context)->uc_mcontext.arm_r6, (context)->uc_mcontext.arm_r7); \
Logger::CrashLog("r8: 0x%x, r9: 0x%x, sl: 0x%x, fp: 0x%x", (context)->uc_mcontext.arm_r8, (context)->uc_mcontext.arm_r9, (context)->uc_mcontext.arm_r10, (context)->uc_mcontext.arm_fp); \
Logger::CrashLog("ip: 0x%x, sp: 0x%x, lr: 0x%x, pc: 0x%x", (context)->uc_mcontext.arm_ip, (context)->uc_mcontext.arm_sp, (context)->uc_mcontext.arm_lr, (context)->uc_mcontext.arm_pc); \
Logger::CrashLog("1: libreVC.so + 0x%X", context->uc_mcontext.arm_pc - g_libREVC); \
Logger::CrashLog("2: libreVC.so + 0x%X", context->uc_mcontext.arm_lr - g_libREVC);
#else
#define PRINT_CRASH_STATES(context) \
Logger::CrashLog("1: libreVC.so + 0x%llx", context->uc_mcontext.pc - g_libREVC); \
Logger::CrashLog("2: libreVC.so + 0x%llx", context->uc_mcontext.regs[30] - g_libREVC);
#endif
class CStackTrace
{
public:
static void printBacktrace()
{
Logger::CrashLog("------------ START BACKTRACE ------------");
Logger::CrashLog(" ");
PrintStackTrace();
}
private:
static _Unwind_Reason_Code TraceFunction(_Unwind_Context* context, void* arg) {
uintptr_t pc = _Unwind_GetIP(context);
Dl_info info;
if (dladdr(reinterpret_cast<void*>(pc), &info) && info.dli_sname != nullptr) {
Logger::CrashLog("[adr: %p reVC: %p] %s\n",
reinterpret_cast<void*>(pc),
reinterpret_cast<void*>(pc - g_libREVC),
info.dli_sname);
} else {
Logger::CrashLog("[adr: %p reVC: %p] name not found\n",
reinterpret_cast<void*>(pc),
reinterpret_cast<void*>(pc - g_libREVC));
}
return _URC_NO_REASON;
}
static void PrintStackTrace() {
_Unwind_Backtrace(TraceFunction, nullptr);
}
};
#endif
#endif
+23 -3
View File
@@ -1,5 +1,6 @@
#include "common.h"
#include "crossplatform.h"
#include "FileMgr.h"
// Codes compatible with Windows and Linux
#ifndef _WIN32
@@ -91,7 +92,11 @@ void GetDateFormat(int unused1, int unused2, SYSTEMTIME* in, int unused3, char*
linuxTime.tm_hour = in->wHour;
linuxTime.tm_min = in->wMinute;
linuxTime.tm_sec = in->wSecond;
strftime(out, size, nl_langinfo(D_FMT), &linuxTime);
#if defined ANDROID
strftime(out, size, "%Y-%m-%d", &linuxTime);
#else
strftime(out, size, nl_langinfo(D_FMT), &linuxTime);
#endif
}
void FileTimeToSystemTime(time_t* writeTime, SYSTEMTIME* out) {
@@ -212,6 +217,20 @@ char* casepath(char const* path, bool checkPathFirst)
}
else
#endif
#if defined(ANDROID) // TODO: Android is fuck!!!
char cwd[MAX_PATH];
getcwd(cwd, sizeof(cwd));
char* pos = strstr(p, CFileMgr::GetRootDirName());
if (pos != NULL) {
pos += strlen(CFileMgr::GetRootDirName());
p = pos;
}
d = opendir(cwd);
out[0] = '.';
out[1] = 0;
rl = 1;
#else
if (p[0] == '/' || p[0] == '\\')
{
d = opendir("/");
@@ -223,6 +242,7 @@ char* casepath(char const* path, bool checkPathFirst)
out[1] = 0;
rl = 1;
}
#endif
bool cantProceed = false; // just convert slashes in what's left in string, don't correct case of letters(because we can't)
bool mayBeTrailingSlash = false;
@@ -273,7 +293,7 @@ char* casepath(char const* path, bool checkPathFirst)
if (!e)
{
printf("casepath couldn't find dir/file \"%s\", full path was %s\n", c, path);
debug("casepath couldn't find dir/file \"%s\", full path was %s\n", c, path);
// No match, add original name and continue converting further slashes.
strcpy(out + rl, c);
rl += strlen(c);
@@ -288,7 +308,7 @@ char* casepath(char const* path, bool checkPathFirst)
}
if (rl > l + 2) {
printf("\n\ncasepath: Corrected path length is longer then original+2:\n\tOriginal: %s (%zu chars)\n\tCorrected: %s (%zu chars)\n\n", path, l, out, rl);
debug("\n\ncasepath: Corrected path length is longer then original+2:\n\tOriginal: %s (%zu chars)\n\tCorrected: %s (%zu chars)\n\n", path, l, out, rl);
}
return out;
}
+13 -3
View File
@@ -3,6 +3,10 @@
#include <time.h>
#include <limits.h>
#ifdef LIBRW_SDL2
#include <SDL.h>
#endif
// This is the common include for platform/renderer specific skeletons(glfw.cpp, win.cpp etc.) and using cross platform things (like Windows directories wrapper, platform specific global arrays etc.)
// Functions that's different on glfw and win but have same signature, should be located on platform.h.
@@ -62,16 +66,20 @@ int _caserename(const char *old_filename, const char *new_filename);
#define caserename _caserename
#endif
#ifdef RW_GL3
#if defined(RW_GL3)
typedef struct
{
#if defined(LIBRW_SDL2)
SDL_Window* window;
#else
GLFWwindow* window;
#endif
RwBool fullScreen;
RwV2d lastMousePos;
double mouseWheel; // glfw doesn't cache it
bool cursorIsInWindow;
RwInt8 joy1id;
RwInt8 joy2id;
RwInt8 joy1id;
RwInt8 joy2id;
}
psGlobalType;
@@ -166,6 +174,7 @@ void GetDateFormat(int, int, SYSTEMTIME*, int, char*, int);
#ifdef __SWITCH__
#ifndef LIBRW_SDL2
// tweak glfw values for switch to match expected pc bindings
#ifdef GLFW_GAMEPAD_BUTTON_A
#undef GLFW_GAMEPAD_BUTTON_A
@@ -186,6 +195,7 @@ void GetDateFormat(int, int, SYSTEMTIME*, int, char*, int);
#undef GLFW_GAMEPAD_BUTTON_Y
#endif
#define GLFW_GAMEPAD_BUTTON_Y 2
#endif
#endif
#endif
+1 -1
View File
@@ -774,7 +774,7 @@ HandlePadButtonUp(RsPadButtonStatus *padButtonStatus)
ControlsManager.UpdateJoyButtonState(padNumber);
for ( int32 i = 1; i < _TODOCONST(16); i++ )
for ( int32 i = 0; i < _TODOCONST(16); i++ ) // 1 > 0 TODO: Fixed a button handling for-loop?
{
RsPadButtons btn = rsPADNULL;
if ( ControlsManager.m_aButtonStates[i] == FALSE )
File diff suppressed because it is too large Load Diff
+9 -1
View File
@@ -54,6 +54,11 @@
bool bAllCarCheat;
#if defined ANDROID
int16 g_usLastProcessedModelIndexAutomobile = 0;
int g_iLastProcessedModelIndexAutoEnt = 0;
#endif
RwObject *GetCurrentAtomicObjectCB(RwObject *object, void *data);
bool CAutomobile::m_sAllTaxiLights;
@@ -3024,7 +3029,10 @@ CAutomobile::ProcessEntityCollision(CEntity *ent, CColPoint *colpoints)
{
int i;
CColModel *colModel;
#if defined ANDROID
g_usLastProcessedModelIndexAutomobile = ent->GetModelIndex();
g_iLastProcessedModelIndexAutoEnt = ent->GetModelIndex();
#endif
if(GetStatus() != STATUS_SIMPLE)
bVehicleColProcessed = true;