From 8a4fa58cd42b7cca4a86fe2d9913b839b554bf10 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?eray=20or=C3=A7unus?= Date: Mon, 11 May 2020 05:55:57 +0300 Subject: Linux build support --- src/skel/crossplatform.cpp | 125 ++++++++++++++++++++++++++++++-- src/skel/crossplatform.h | 81 ++++++++++++--------- src/skel/glfw/glfw.cpp | 175 ++++++++++++++++++++++++++++++++------------- src/skel/platform.h | 18 ++++- src/skel/skeleton.cpp | 6 +- src/skel/skeleton.h | 6 +- src/skel/win/win.h | 1 - 7 files changed, 321 insertions(+), 91 deletions(-) (limited to 'src/skel') diff --git a/src/skel/crossplatform.cpp b/src/skel/crossplatform.cpp index f9464bb6..9971d2ae 100644 --- a/src/skel/crossplatform.cpp +++ b/src/skel/crossplatform.cpp @@ -1,5 +1,4 @@ #include "common.h" -#define USEALTERNATIVEWINFUNCS #include "crossplatform.h" // For internal use @@ -20,7 +19,8 @@ void GetLocalTime_CP(SYSTEMTIME *out) { tmToSystemTime(localTm, out); } -#if !defined _WIN32 || defined __MINGW32__ +// Compatible with Linux/POSIX and MinGW on Windows +#ifndef _WIN32 HANDLE FindFirstFile(const char* pathname, WIN32_FIND_DATA* firstfile) { char newpathname[32]; strncpy(newpathname, pathname, 32); @@ -34,7 +34,7 @@ HANDLE FindFirstFile(const char* pathname, WIN32_FIND_DATA* firstfile) { strncpy(firstfile->extension, "", sizeof(firstfile->extension)); HANDLE d; - if ((d = opendir(path)) == NULL || !FindNextFile(d, firstfile)) + if ((d = (HANDLE)opendir(path)) == NULL || !FindNextFile(d, firstfile)) return NULL; return d; @@ -45,7 +45,7 @@ bool FindNextFile(HANDLE d, WIN32_FIND_DATA* finddata) { static struct stat fileStats; static char path[PATH_MAX], relativepath[NAME_MAX + sizeof(finddata->folder) + 1]; int extensionLen = strlen(finddata->extension); - while ((file = readdir(d)) != NULL) { + while ((file = readdir((DIR*)d)) != NULL) { // We only want "DT_REG"ular Files, but reportedly some FS and OSes gives DT_UNKNOWN as type. if ((file->d_type == DT_UNKNOWN || file->d_type == DT_REG) && @@ -78,4 +78,119 @@ void FileTimeToSystemTime(time_t* writeTime, SYSTEMTIME* out) { tm *ptm = gmtime(writeTime); tmToSystemTime(ptm, out); } -#endif \ No newline at end of file +#endif + +#ifndef _WIN32 +char *strupr(char *s) { + char* tmp = s; + + for (;*tmp;++tmp) { + *tmp = toupper((unsigned char) *tmp); + } + + return s; +} +char *strlwr(char *s) { + char* tmp = s; + + for (;*tmp;++tmp) { + *tmp = tolower((unsigned char) *tmp); + } + + return s; +} + +char *trim(char *s) { + char *ptr; + if (!s) + return NULL; // handle NULL string + if (!*s) + return s; // handle empty string + for (ptr = s + strlen(s) - 1; (ptr >= s) && isspace(*ptr); --ptr); + ptr[1] = '\0'; + return s; +} + +// Case-insensitivity on linux (from https://github.com/OneSadCookie/fcaseopen) +// r must have strlen(path) + 2 bytes +int casepath(char const *path, char *r) +{ + size_t l = strlen(path); + char *p = (char*)alloca(l + 1); + strcpy(p, path); + + // my addon: change \'s with / + char *nextBs; + while(nextBs = strstr(p, "\\")){ + *nextBs = '/'; + } + + // my addon: linux doesn't handle filenames with spaces at the end nicely + p = trim(p); + + size_t rl = 0; + + DIR *d; + if (p[0] == '/') + { + d = opendir("/"); + p = p + 1; + } + else + { + d = opendir("."); + r[0] = '.'; + r[1] = 0; + rl = 1; + } + + int last = 0; + char *c = strsep(&p, "/"); + while (c) + { + if (!d) + { + return 0; + } + + if (last) + { + closedir(d); + return 0; + } + + r[rl] = '/'; + rl += 1; + r[rl] = 0; + + struct dirent *e = readdir(d); + while (e) + { + if (strcasecmp(c, e->d_name) == 0) + { + strcpy(r + rl, e->d_name); + rl += strlen(e->d_name); + + closedir(d); + d = opendir(r); + + break; + } + + e = readdir(d); + } + + if (!e) + { + strcpy(r + rl, c); + rl += strlen(c); + last = 1; + } + + c = strsep(&p, "/"); + } + + if (d) closedir(d); + return 1; +} +#endif diff --git a/src/skel/crossplatform.h b/src/skel/crossplatform.h index 342aab4e..f6a3408b 100644 --- a/src/skel/crossplatform.h +++ b/src/skel/crossplatform.h @@ -1,10 +1,38 @@ #include -// This is the common include for platform/renderer specific skeletons(glfw, win etc.) and cross platform things (like Windows directories wrapper, platform specific global arrays etc.) +// 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. -// This only has as Win header. #ifdef _WIN32 +// This only has as Win header. #include "win.h" +extern DWORD _dwOperatingSystemVersion; +#else +char *strupr(char *str); +char *strlwr(char *str); +enum { + OS_WIN98, + OS_WIN2000, + OS_WINNT, + OS_WINXP, +}; + +enum { + LANG_OTHER, + LANG_GERMAN, + LANG_FRENCH, + LANG_ENGLISH, + LANG_ITALIAN, + LANG_SPANISH, +}; + +enum { + SUBLANG_OTHER, + SUBLANG_ENGLISH_AUS +}; + +extern long _dwOperatingSystemVersion; +int casepath(char const *path, char *r); #endif #ifdef RW_GL3 @@ -14,8 +42,8 @@ typedef struct RwBool fullScreen; RwV2d lastMousePos; double mouseWheel; // glfw doesn't cache it - int8 joy1id; - int8 joy2id; + RwInt8 joy1id; + RwInt8 joy2id; } psGlobalType; @@ -44,17 +72,6 @@ enum eGameState extern RwUInt32 gGameState; RwBool IsForegroundApp(); -void InitialiseLanguage(); -RwBool _psSetVideoMode(RwInt32 subSystem, RwInt32 videoMode); - -RwChar** _psGetVideoModeList(); -RwInt32 _psGetNumVideModes(); - -void _psSelectScreenVM(RwInt32 videoMode); -void HandleExit(); -void _InputTranslateShiftKeyUpDown(RsKeyCodes* rs); - -// Mostly wrappers around Windows functions #ifndef MAX_PATH #if !defined _WIN32 || defined __MINGW32__ @@ -64,39 +81,39 @@ void _InputTranslateShiftKeyUpDown(RsKeyCodes* rs); #endif #endif -// TODO: Remove USEALTERNATIVEWINFUNCS and don't use it anywhere when re3 becomes fully cross-platform, this is for testing // Codes compatible with Windows and Linux -#if defined USEALTERNATIVEWINFUNCS || !defined _WIN32 || defined __MINGW32__ +#if !defined _WIN32 #define DeleteFile unlink // Needed for save games struct SYSTEMTIME { - uint16 wYear; - uint16 wMonth; - uint16 wDayOfWeek; - uint16 wDay; - uint16 wHour; - uint16 wMinute; - uint16 wSecond; - uint16 wMilliseconds; + RwUInt16 wYear; + RwUInt16 wMonth; + RwUInt16 wDayOfWeek; + RwUInt16 wDay; + RwUInt16 wHour; + RwUInt16 wMinute; + RwUInt16 wSecond; + RwUInt16 wMilliseconds; }; void GetLocalTime_CP(SYSTEMTIME* out); #define GetLocalTime GetLocalTime_CP -#define OutputDebugString(s) re3_debug("[DBG-2]: " s "\n") +#define OutputDebugString(s) re3_debug("[DBG-2]: %s\n",s) #endif -// Only runs on GNU/POSIX/etc. -#if !defined _WIN32 || defined __MINGW32__ +// Compatible with Linux/POSIX and MinGW on Windows +#if !defined _WIN32 #include #include #include #include #include +#include -typedef DIR* HANDLE; +typedef void* HANDLE; #define INVALID_HANDLE_VALUE NULL -#define FindClose closedir +#define FindClose(h) closedir((DIR*)h) #define LOCALE_USER_DEFAULT 0 #define DATE_SHORTDATE 0 @@ -107,8 +124,8 @@ struct WIN32_FIND_DATA { time_t ftLastWriteTime; }; -HANDLE FindFirstFile(char*, WIN32_FIND_DATA*); +HANDLE FindFirstFile(const char*, WIN32_FIND_DATA*); bool FindNextFile(HANDLE, WIN32_FIND_DATA*); void FileTimeToSystemTime(time_t*, SYSTEMTIME*); void GetDateFormat(int, int, SYSTEMTIME*, int, char*, int); -#endif \ No newline at end of file +#endif diff --git a/src/skel/glfw/glfw.cpp b/src/skel/glfw/glfw.cpp index 63ebccee..b30bda7b 100644 --- a/src/skel/glfw/glfw.cpp +++ b/src/skel/glfw/glfw.cpp @@ -7,14 +7,11 @@ #pragma warning( disable : 4005) #pragma warning( pop ) -#pragma comment( lib, "Winmm.lib" ) // Needed for time - #if (defined(_MSC_VER)) #include #endif /* (defined(_MSC_VER)) */ #include #include "rwcore.h" -#include "resource.h" #include "skeleton.h" #include "platform.h" #include "crossplatform.h" @@ -69,14 +66,20 @@ static psGlobalType PsGlobal; #define JIF(x) if (FAILED(hr=(x))) \ {debug(TEXT("FAILED(hr=0x%x) in ") TEXT(#x) TEXT("\n"), hr); return;} +long _dwMemAvailPhys; +RwUInt32 gGameState; -// TODO: This is used on selecting video mode, so either think something or remove it completely -DWORD _dwMemTotalVideo = 1024 * (1024 * 1024); // 1024 MB as placeholder -DWORD _dwMemAvailPhys; - +#ifdef _WIN32 DWORD _dwOperatingSystemVersion; - -RwUInt32 gGameState; +#include "resource.h" +#else +long _dwOperatingSystemVersion; +#include +#include +#include +#include +#include +#endif /* ***************************************************************************** */ @@ -144,7 +147,7 @@ const char *_psGetUserFilesFolder() strcpy(szUserFiles, "data"); return szUserFiles; #else - static CHAR szUserFiles[256]; + static char szUserFiles[256]; strcpy(szUserFiles, "userfiles"); _psCreateFolder(szUserFiles); return szUserFiles; @@ -185,6 +188,8 @@ psCameraShowRaster(RwCamera *camera) /* ***************************************************************************** */ +#ifdef _WIN32 +#pragma comment( lib, "Winmm.lib" ) // Needed for time RwUInt32 psTimer(void) { @@ -202,6 +207,16 @@ psTimer(void) return time; } +#else +double +psTimer(void) +{ + struct timespec start; + clock_gettime(CLOCK_MONOTONIC_RAW, &start); + return start.tv_sec * 1000.0 + start.tv_nsec/1000000.0; +} +#endif + /* ***************************************************************************** @@ -209,12 +224,7 @@ psTimer(void) void psMouseSetPos(RwV2d *pos) { - POINT point; - - point.x = (RwInt32) pos->x; - point.y = (RwInt32) pos->y; - - glfwSetCursorPos(PSGLOBAL(window), point.x, point.y); + glfwSetCursorPos(PSGLOBAL(window), pos->x, pos->y); PSGLOBAL(lastMousePos.x) = (RwInt32)pos->x; @@ -285,7 +295,7 @@ psInitialise(void) gGameState = GS_START_UP; TRACE("gGameState = GS_START_UP"); - +#ifdef _WIN32 OSVERSIONINFO verInfo; verInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); @@ -330,14 +340,19 @@ psInitialise(void) _dwMemAvailPhys = memstats.dwAvailPhys; -#ifdef FIX_BUGS debug("Physical memory size %u\n", memstats.dwTotalPhys); debug("Available physical memory %u\n", memstats.dwAvailPhys); #else - debug("Physical memory size %d\n", memstats.dwTotalPhys); - debug("Available physical memory %d\n", memstats.dwAvailPhys); -#endif + struct sysinfo systemInfo; + sysinfo(&systemInfo); + + _dwMemAvailPhys = systemInfo.freeram; + _dwOperatingSystemVersion = OS_WINXP; // To fool other classes + + debug("Physical memory size %u\n", systemInfo.totalram); + debug("Available physical memory %u\n", systemInfo.freeram); +#endif TheText.Unload(); return TRUE; @@ -413,18 +428,8 @@ RwChar **_psGetVideoModeList() if ( vm.flags & rwVIDEOMODEEXCLUSIVE ) { - if ( vm.width >= 640 - && vm.height >= 480 - && (vm.width == 640 - && vm.height == 480) - || !(vm.flags & rwVIDEOMODEEXCLUSIVE) - || (_dwMemTotalVideo - vm.depth * vm.height * vm.width / 8) > (12 * 1024 * 1024)/*12 MB*/ ) - { - _VMList[i] = (RwChar*)RwCalloc(100, sizeof(RwChar)); - rwsprintf(_VMList[i],"%lu X %lu X %lu", vm.width, vm.height, vm.depth); - } - else - _VMList[i] = nil; + _VMList[i] = (RwChar*)RwCalloc(100, sizeof(RwChar)); + rwsprintf(_VMList[i],"%lu X %lu X %lu", vm.width, vm.height, vm.depth); } else _VMList[i] = nil; @@ -445,6 +450,8 @@ void _psSelectScreenVM(RwInt32 videoMode) if (!_psSetVideoMode(RwEngineGetCurrentSubSystem(), videoMode)) { RsGlobal.quit = TRUE; + + printf("ERROR: Failed to select new screen resolution\n"); } else FrontEndMenuManager.LoadAllTextures(); @@ -589,7 +596,7 @@ psSelectDevice() #ifdef DEFAULT_NATIVE_RESOLUTION GcurSelVM = 1; #else - MessageBox(nil, "Cannot find 640x480 video mode", "GTA3", MB_OK); + printf("WARNING: Cannot find 640x480 video mode, selecting device cancelled\n"); return FALSE; #endif } @@ -602,8 +609,9 @@ psSelectDevice() FrontEndMenuManager.m_nPrefsHeight == 0 || FrontEndMenuManager.m_nPrefsDepth == 0){ // Defaults if nothing specified - FrontEndMenuManager.m_nPrefsWidth = GetSystemMetrics(SM_CXSCREEN); - FrontEndMenuManager.m_nPrefsHeight = GetSystemMetrics(SM_CYSCREEN); + const GLFWvidmode *mode = glfwGetVideoMode(glfwGetPrimaryMonitor()); + FrontEndMenuManager.m_nPrefsWidth = mode->width; + FrontEndMenuManager.m_nPrefsHeight = mode->height; FrontEndMenuManager.m_nPrefsDepth = 32; FrontEndMenuManager.m_nPrefsWindowed = 0; } @@ -632,7 +640,7 @@ psSelectDevice() } if(bestFsMode < 0){ - MessageBox(nil, "Cannot find desired video mode", "GTA3", MB_OK); + printf("WARNING: Cannot find desired video mode, selecting device cancelled\n"); return FALSE; } GcurSelVM = bestFsMode; @@ -886,6 +894,30 @@ CommandLineToArgv(RwChar *cmdLine, RwInt32 *argCount) */ void InitialiseLanguage() { +#ifndef _WIN32 + // Mandatory for Linux(Unix? Posix?) to set lang. to environment lang. + setlocale(LC_ALL, ""); + + char *systemLang, *keyboardLang; + + systemLang = setlocale (LC_ALL, NULL); + keyboardLang = setlocale (LC_CTYPE, NULL); + + short primUserLCID, primSystemLCID; + primUserLCID = primSystemLCID = !strncmp(systemLang, "fr_",3) ? LANG_FRENCH : + !strncmp(systemLang, "de_",3) ? LANG_GERMAN : + !strncmp(systemLang, "en_",3) ? LANG_ENGLISH : + !strncmp(systemLang, "it_",3) ? LANG_ITALIAN : + !strncmp(systemLang, "es_",3) ? LANG_SPANISH : + LANG_OTHER; + + short primLayout = !strncmp(keyboardLang, "fr_",3) ? LANG_FRENCH : (!strncmp(keyboardLang, "de_",3) ? LANG_GERMAN : LANG_ENGLISH); + + short subUserLCID, subSystemLCID; + subUserLCID = subSystemLCID = !strncmp(systemLang, "en_AU",5) ? SUBLANG_ENGLISH_AUS : SUBLANG_OTHER; + short subLayout = !strncmp(keyboardLang, "en_AU",5) ? SUBLANG_ENGLISH_AUS : SUBLANG_OTHER; + +#else WORD primUserLCID = PRIMARYLANGID(GetSystemDefaultLCID()); WORD primSystemLCID = PRIMARYLANGID(GetUserDefaultLCID()); WORD primLayout = PRIMARYLANGID((DWORD)GetKeyboardLayout(0)); @@ -893,7 +925,7 @@ void InitialiseLanguage() WORD subUserLCID = SUBLANGID(GetSystemDefaultLCID()); WORD subSystemLCID = SUBLANGID(GetUserDefaultLCID()); WORD subLayout = SUBLANGID((DWORD)GetKeyboardLayout(0)); - +#endif if ( primUserLCID == LANG_GERMAN || primSystemLCID == LANG_GERMAN || primLayout == LANG_GERMAN ) @@ -987,13 +1019,22 @@ void InitialiseLanguage() TheText.Unload(); TheText.Load(); + +#ifndef _WIN32 + // TODO this is needed for strcasecmp to work correctly across all languages, but can these cause other problems?? + setlocale(LC_CTYPE, "C"); + setlocale(LC_COLLATE, "C"); + setlocale(LC_NUMERIC, "C"); +#endif } /* ***************************************************************************** */ + void HandleExit() { +#ifdef _WIN32 MSG message; while ( PeekMessage(&message, nil, 0U, 0U, PM_REMOVE|PM_NOYIELD) ) { @@ -1007,8 +1048,21 @@ void HandleExit() DispatchMessage(&message); } } +#else + // We now handle terminate message always, why handle on some cases? + return; +#endif } - + +#ifndef _WIN32 +void terminateHandler(int sig, siginfo_t *info, void *ucontext) { + RsGlobal.quit = TRUE; +} + +void dummyHandler(int sig){ +} +#endif + void resizeCB(GLFWwindow* window, int width, int height) { /* * Handle event to ensure window contents are displayed during re-size @@ -1210,17 +1264,36 @@ cursorCB(GLFWwindow* window, double xpos, double ypos) { /* ***************************************************************************** */ +#ifdef _WIN32 int PASCAL WinMain(HINSTANCE instance, HINSTANCE prevInstance __RWUNUSED__, CMDSTR cmdLine, int cmdShow) { - RwV2d pos; - RwInt32 argc, i; + + RwInt32 argc; RwChar** argv; - StaticPatcher::Apply(); SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, nil, SPIF_SENDCHANGE); +#else +int +main(int argc, char *argv[]) +{ +#endif + RwV2d pos; + RwInt32 i; +// StaticPatcher::Apply(); + +#ifndef _WIN32 + struct sigaction act; + act.sa_sigaction = terminateHandler; + act.sa_flags = SA_SIGINFO; + sigaction(SIGTERM, &act, NULL); + struct sigaction sa; + sa.sa_handler = dummyHandler; + sa.sa_flags = 0; + sigaction(SIGINT, &sa, NULL); // Needed for CdStreamPosix +#endif /* * Initialize the platform independent data. @@ -1231,7 +1304,7 @@ WinMain(HINSTANCE instance, return FALSE; } - +#ifdef _WIN32 /* * Get proper command line params, cmdLine passed to us does not * work properly under all circumstances... @@ -1248,6 +1321,7 @@ WinMain(HINSTANCE instance, * Parse command line parameters (except program name) one at * a time BEFORE RenderWare initialization... */ +#endif for(i=1; i