From e138e8ad527614739b72e82796cee5adacd276ac Mon Sep 17 00:00:00 2001 From: NepDisk Date: Sun, 29 Dec 2024 13:19:05 -0500 Subject: [PATCH] i_system.c -> cpp --- src/byteptr.h | 102 +- src/sdl/CMakeLists.txt | 2 +- src/sdl/Sourcefile | 2 +- src/sdl/{i_system.c => back/i_system.cpp} | 0 src/sdl/i_system.cpp | 2822 +++++++++++++++++++++ 5 files changed, 2885 insertions(+), 43 deletions(-) rename src/sdl/{i_system.c => back/i_system.cpp} (100%) create mode 100644 src/sdl/i_system.cpp diff --git a/src/byteptr.h b/src/byteptr.h index 8397fc0ae..e8deb88a2 100644 --- a/src/byteptr.h +++ b/src/byteptr.h @@ -1,7 +1,8 @@ -// SONIC ROBO BLAST 2 +// DR. ROBOTNIK'S RING RACERS //----------------------------------------------------------------------------- -// Copyright (C) 1998-2000 by DooM Legacy Team. -// Copyright (C) 1999-2020 by Sonic Team Junior. +// Copyright (C) 2024 by Kart Krew. +// Copyright (C) 2020 by Sonic Team Junior. +// Copyright (C) 2000 by DooM Legacy Team. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -14,6 +15,10 @@ #ifndef __BYTEPTR_H__ #define __BYTEPTR_H__ +#if defined (__alpha__) || defined (__arm__) || defined (__mips__) || defined (__ia64__) || defined (__clang__) +#define DEALIGNED +#endif + #include "endian.h" #ifdef __cplusplus @@ -24,36 +29,49 @@ extern "C" { // // Little-endian machines // -#define WRITEUINT8(p,b) do { UINT8 *p_tmp = (void *)p; const UINT8 tv = ( UINT8)(b); memcpy(p, &tv, sizeof( UINT8)); p_tmp++; p = (void *)p_tmp; } while (0) -#define WRITESINT8(p,b) do { SINT8 *p_tmp = (void *)p; const SINT8 tv = ( UINT8)(b); memcpy(p, &tv, sizeof( UINT8)); p_tmp++; p = (void *)p_tmp; } while (0) -#define WRITEINT16(p,b) do { INT16 *p_tmp = (void *)p; const INT16 tv = ( INT16)(b); memcpy(p, &tv, sizeof( INT16)); p_tmp++; p = (void *)p_tmp; } while (0) -#define WRITEUINT16(p,b) do { UINT16 *p_tmp = (void *)p; const UINT16 tv = ( UINT16)(b); memcpy(p, &tv, sizeof( UINT16)); p_tmp++; p = (void *)p_tmp; } while (0) -#define WRITEINT32(p,b) do { INT32 *p_tmp = (void *)p; const INT32 tv = ( INT32)(b); memcpy(p, &tv, sizeof( INT32)); p_tmp++; p = (void *)p_tmp; } while (0) -#define WRITEUINT32(p,b) do { UINT32 *p_tmp = (void *)p; const UINT32 tv = ( UINT32)(b); memcpy(p, &tv, sizeof( UINT32)); p_tmp++; p = (void *)p_tmp; } while (0) -#define WRITECHAR(p,b) do { char *p_tmp = (void *)p; const char tv = ( char)(b); memcpy(p, &tv, sizeof( char)); p_tmp++; p = (void *)p_tmp; } while (0) -#define WRITEFIXED(p,b) do { fixed_t *p_tmp = (void *)p; const fixed_t tv = (fixed_t)(b); memcpy(p, &tv, sizeof(fixed_t)); p_tmp++; p = (void *)p_tmp; } while (0) -#define WRITEANGLE(p,b) do { angle_t *p_tmp = (void *)p; const angle_t tv = (angle_t)(b); memcpy(p, &tv, sizeof(angle_t)); p_tmp++; p = (void *)p_tmp; } while (0) - -#ifdef __GNUC__ -#define READUINT8(p) ({ UINT8 *p_tmp = (void *)p; UINT8 b; memcpy(&b, p, sizeof( UINT8)); p_tmp++; p = (void *)p_tmp; b; }) -#define READSINT8(p) ({ SINT8 *p_tmp = (void *)p; SINT8 b; memcpy(&b, p, sizeof( SINT8)); p_tmp++; p = (void *)p_tmp; b; }) -#define READINT16(p) ({ INT16 *p_tmp = (void *)p; INT16 b; memcpy(&b, p, sizeof( INT16)); p_tmp++; p = (void *)p_tmp; b; }) -#define READUINT16(p) ({ UINT16 *p_tmp = (void *)p; UINT16 b; memcpy(&b, p, sizeof( UINT16)); p_tmp++; p = (void *)p_tmp; b; }) -#define READINT32(p) ({ INT32 *p_tmp = (void *)p; INT32 b; memcpy(&b, p, sizeof( INT32)); p_tmp++; p = (void *)p_tmp; b; }) -#define READUINT32(p) ({ UINT32 *p_tmp = (void *)p; UINT32 b; memcpy(&b, p, sizeof( UINT32)); p_tmp++; p = (void *)p_tmp; b; }) -#define READCHAR(p) ({ char *p_tmp = (void *)p; char b; memcpy(&b, p, sizeof( char)); p_tmp++; p = (void *)p_tmp; b; }) -#define READFIXED(p) ({ fixed_t *p_tmp = (void *)p; fixed_t b; memcpy(&b, p, sizeof(fixed_t)); p_tmp++; p = (void *)p_tmp; b; }) -#define READANGLE(p) ({ angle_t *p_tmp = (void *)p; angle_t b; memcpy(&b, p, sizeof(angle_t)); p_tmp++; p = (void *)p_tmp; b; }) +#ifdef DEALIGNED +#define WRITEUINT8(p,b) do { UINT8 *p_tmp = ( UINT8 *)p; const UINT8 tv = ( UINT8)(b); memcpy(p, &tv, sizeof( UINT8)); p_tmp++; *(void**)(&(p)) = (void *)p_tmp; } while (0) +#define WRITESINT8(p,b) do { SINT8 *p_tmp = ( SINT8 *)p; const SINT8 tv = ( UINT8)(b); memcpy(p, &tv, sizeof( UINT8)); p_tmp++; *(void**)(&(p)) = (void *)p_tmp; } while (0) +#define WRITEINT16(p,b) do { INT16 *p_tmp = ( INT16 *)p; const INT16 tv = ( INT16)(b); memcpy(p, &tv, sizeof( INT16)); p_tmp++; *(void**)(&(p)) = (void *)p_tmp; } while (0) +#define WRITEUINT16(p,b) do { UINT16 *p_tmp = ( UINT16 *)p; const UINT16 tv = ( UINT16)(b); memcpy(p, &tv, sizeof( UINT16)); p_tmp++; *(void**)(&(p)) = (void *)p_tmp; } while (0) +#define WRITEINT32(p,b) do { INT32 *p_tmp = ( INT32 *)p; const INT32 tv = ( INT32)(b); memcpy(p, &tv, sizeof( INT32)); p_tmp++; *(void**)(&(p)) = (void *)p_tmp; } while (0) +#define WRITEUINT32(p,b) do { UINT32 *p_tmp = ( UINT32 *)p; const UINT32 tv = ( UINT32)(b); memcpy(p, &tv, sizeof( UINT32)); p_tmp++; *(void**)(&(p)) = (void *)p_tmp; } while (0) +#define WRITECHAR(p,b) do { char *p_tmp = ( char *)p; const char tv = ( char)(b); memcpy(p, &tv, sizeof( char)); p_tmp++; *(void**)(&(p)) = (void *)p_tmp; } while (0) +#define WRITEFIXED(p,b) do { fixed_t *p_tmp = (fixed_t *)p; const fixed_t tv = (fixed_t)(b); memcpy(p, &tv, sizeof(fixed_t)); p_tmp++; *(void**)(&(p)) = (void *)p_tmp; } while (0) +#define WRITEANGLE(p,b) do { angle_t *p_tmp = (angle_t *)p; const angle_t tv = (angle_t)(b); memcpy(p, &tv, sizeof(angle_t)); p_tmp++; *(void**)(&(p)) = (void *)p_tmp; } while (0) #else -#define READUINT8(p) *(( UINT8 *)p)++ -#define READSINT8(p) *(( SINT8 *)p)++ -#define READINT16(p) *(( INT16 *)p)++ -#define READUINT16(p) *(( UINT16 *)p)++ -#define READINT32(p) *(( INT32 *)p)++ -#define READUINT32(p) *(( UINT32 *)p)++ -#define READCHAR(p) *(( char *)p)++ -#define READFIXED(p) *((fixed_t *)p)++ -#define READANGLE(p) *((angle_t *)p)++ +#define WRITEUINT8(p,b) do { UINT8 *p_tmp = ( UINT8 *)p; *p_tmp = ( UINT8)(b); p_tmp++; *(void**)(&(p)) = (void *)p_tmp; } while (0) +#define WRITESINT8(p,b) do { SINT8 *p_tmp = ( SINT8 *)p; *p_tmp = ( SINT8)(b); p_tmp++; *(void**)(&(p)) = (void *)p_tmp; } while (0) +#define WRITEINT16(p,b) do { INT16 *p_tmp = ( INT16 *)p; *p_tmp = ( INT16)(b); p_tmp++; *(void**)(&(p)) = (void *)p_tmp; } while (0) +#define WRITEUINT16(p,b) do { UINT16 *p_tmp = ( UINT16 *)p; *p_tmp = ( UINT16)(b); p_tmp++; *(void**)(&(p)) = (void *)p_tmp; } while (0) +#define WRITEINT32(p,b) do { INT32 *p_tmp = ( INT32 *)p; *p_tmp = ( INT32)(b); p_tmp++; *(void**)(&(p)) = (void *)p_tmp; } while (0) +#define WRITEUINT32(p,b) do { UINT32 *p_tmp = ( UINT32 *)p; *p_tmp = ( UINT32)(b); p_tmp++; *(void**)(&(p)) = (void *)p_tmp; } while (0) +#define WRITECHAR(p,b) do { char *p_tmp = ( char *)p; *p_tmp = ( char)(b); p_tmp++; *(void**)(&(p)) = (void *)p_tmp; } while (0) +#define WRITEFIXED(p,b) do { fixed_t *p_tmp = (fixed_t *)p; *p_tmp = (fixed_t)(b); p_tmp++; *(void**)(&(p)) = (void *)p_tmp; } while (0) +#define WRITEANGLE(p,b) do { angle_t *p_tmp = (angle_t *)p; *p_tmp = (angle_t)(b); p_tmp++; *(void**)(&(p)) = (void *)p_tmp; } while (0) +#endif + +// what is this? +#if defined (__GNUC__) && defined (DEALIGNED) +#define READUINT8(p) ({ const UINT8 *p_tmp = (const UINT8 *)p; UINT8 b; memcpy(&b, p, sizeof( UINT8)); p_tmp++; *(const void**)(&(p)) = (const void *)p_tmp; b; }) +#define READSINT8(p) ({ const SINT8 *p_tmp = (const SINT8 *)p; SINT8 b; memcpy(&b, p, sizeof( SINT8)); p_tmp++; *(const void**)(&(p)) = (const void *)p_tmp; b; }) +#define READINT16(p) ({ const INT16 *p_tmp = (const INT16 *)p; INT16 b; memcpy(&b, p, sizeof( INT16)); p_tmp++; *(const void**)(&(p)) = (const void *)p_tmp; b; }) +#define READUINT16(p) ({ const UINT16 *p_tmp = (const UINT16 *)p; UINT16 b; memcpy(&b, p, sizeof( UINT16)); p_tmp++; *(const void**)(&(p)) = (const void *)p_tmp; b; }) +#define READINT32(p) ({ const INT32 *p_tmp = (const INT32 *)p; INT32 b; memcpy(&b, p, sizeof( INT32)); p_tmp++; *(const void**)(&(p)) = (const void *)p_tmp; b; }) +#define READUINT32(p) ({ const UINT32 *p_tmp = (const UINT32 *)p; UINT32 b; memcpy(&b, p, sizeof( UINT32)); p_tmp++; *(const void**)(&(p)) = (const void *)p_tmp; b; }) +#define READCHAR(p) ({ const char *p_tmp = (const char *)p; char b; memcpy(&b, p, sizeof( char)); p_tmp++; *(const void**)(&(p)) = (const void *)p_tmp; b; }) +#define READFIXED(p) ({ const fixed_t *p_tmp = (const fixed_t *)p; fixed_t b; memcpy(&b, p, sizeof(fixed_t)); p_tmp++; *(const void**)(&(p)) = (const void *)p_tmp; b; }) +#define READANGLE(p) ({ const angle_t *p_tmp = (const angle_t *)p; angle_t b; memcpy(&b, p, sizeof(angle_t)); p_tmp++; *(const void**)(&(p)) = (const void *)p_tmp; b; }) +#else +#define READUINT8(p) ((const UINT8*)(*(const void**)(&(p)) = (const void*)&((const UINT8*)(p))[1]))[-1] +#define READSINT8(p) ((const SINT8*)(*(const void**)(&(p)) = (const void*)&((const SINT8*)(p))[1]))[-1] +#define READINT16(p) ((const INT16*)(*(const void**)(&(p)) = (const void*)&((const INT16*)(p))[1]))[-1] +#define READUINT16(p) ((const UINT16*)(*(const void**)(&(p)) = (const void*)&((const UINT16*)(p))[1]))[-1] +#define READINT32(p) ((const INT32*)(*(const void**)(&(p)) = (const void*)&((const INT32*)(p))[1]))[-1] +#define READUINT32(p) ((const UINT32*)(*(const void**)(&(p)) = (const void*)&((const UINT32*)(p))[1]))[-1] +#define READCHAR(p) ((const char*)(*(const void**)(&(p)) = (const void*)&((const char*)(p))[1]))[-1] +#define READFIXED(p) ((const fixed_t*)(*(const void**)(&(p)) = (const void*)&((const fixed_t*)(p))[1]))[-1] +#define READANGLE(p) ((const angle_t*)(*(const void**)(&(p)) = (const void*)&((const angle_t*)(p))[1]))[-1] #endif #else //SRB2_BIG_ENDIAN @@ -116,17 +134,19 @@ FUNCINLINE static ATTRINLINE UINT32 readulong(void *ptr) return (ucp[3] << 24) | (ucp[2] << 16) | (ucp[1] << 8) | ucp[0]; } -#define READUINT8(p) ((UINT8*)(p = (void*)&((UINT8*)p)[1]))[-1] -#define READSINT8(p) ((SINT8*)(p = (void*)&((SINT8*)p)[1]))[-1] -#define READINT16(p) readshort(&((INT16*)(p = (void*)&((INT16*)p)[1]))[-1]) -#define READUINT16(p) readushort(&((UINT16*)(p = (void*)&((UINT16*)p)[1]))[-1]) -#define READINT32(p) readlong(&((INT32*)(p = (void*)&((INT32*)p)[1]))[-1]) -#define READUINT32(p) readulong(&((UINT32*)(p = (void*)&((UINT32*)p)[1])) -#define READCHAR(p) ((char*)(p = (void*)&((char*)p)[1]))[-1] -#define READFIXED(p) readlong(&((fixed_t*)(p = (void*)&((fixed_t*)p)[1]))[-1]) -#define READANGLE(p) readulong(&((angle_t*)(p = (void*)&((angle_t*)p)[1]))[-1]) +#define READUINT8(p) ((const UINT8*)(p = (const void*)&((const UINT8*)p)[1]))[-1] +#define READSINT8(p) ((const SINT8*)(p = (const void*)&((const SINT8*)p)[1]))[-1] +#define READINT16(p) readshort(&((const INT16*)(p = (const void*)&((const INT16*)p)[1]))[-1]) +#define READUINT16(p) readushort(&((const UINT16*)(p = (const void*)&((const UINT16*)p)[1]))[-1]) +#define READINT32(p) readlong(&((const INT32*)(p = (const void*)&((const INT32*)p)[1]))[-1]) +#define READUINT32(p) readulong(&((const UINT32*)(p = (const void*)&((const UINT32*)p)[1])) +#define READCHAR(p) ((const char*)(p = (const void*)&((const char*)p)[1]))[-1] +#define READFIXED(p) readlong(&((const fixed_t*)(p = (const void*)&((const fixed_t*)p)[1]))[-1]) +#define READANGLE(p) readulong(&((const angle_t*)(p = (const void*)&((const angle_t*)p)[1]))[-1]) #endif //SRB2_BIG_ENDIAN +#undef DEALIGNED + #define WRITESTRINGN(p, s, n) do { \ size_t tmp_i; \ \ diff --git a/src/sdl/CMakeLists.txt b/src/sdl/CMakeLists.txt index ee9c4cddc..4413580bc 100644 --- a/src/sdl/CMakeLists.txt +++ b/src/sdl/CMakeLists.txt @@ -5,7 +5,7 @@ target_sources(SRB2SDL2 PRIVATE ogl_sdl.c i_threads.c i_net.c - i_system.c + i_system.cpp i_main.c i_video.c dosstr.c diff --git a/src/sdl/Sourcefile b/src/sdl/Sourcefile index 82d5ce073..f919a1334 100644 --- a/src/sdl/Sourcefile +++ b/src/sdl/Sourcefile @@ -1,5 +1,5 @@ i_net.c -i_system.c +i_system.cpp i_main.c i_video.c dosstr.c diff --git a/src/sdl/i_system.c b/src/sdl/back/i_system.cpp similarity index 100% rename from src/sdl/i_system.c rename to src/sdl/back/i_system.cpp diff --git a/src/sdl/i_system.cpp b/src/sdl/i_system.cpp new file mode 100644 index 000000000..972d657c5 --- /dev/null +++ b/src/sdl/i_system.cpp @@ -0,0 +1,2822 @@ +// Emacs style mode select -*- C++ -*- +// +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// +// Copyright (C) 1993-1996 by id Software, Inc. +// Portions Copyright (C) 1998-2000 by DooM Legacy Team. +// Copyright (C) 2014-2020 by Sonic Team Junior. +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License +// as published by the Free Software Foundation; either version 2 +// of the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// Changes by Graue are in the public domain. +// +//----------------------------------------------------------------------------- +/// \file +/// \brief SRB2 system stuff for SDL + +#include + +#ifdef CMAKECONFIG +#include "../config.h" +#else +#include "../config.h.in" +#endif +#include + +#ifdef _WIN32 +#define RPC_NO_WINDOWS_H +#include +#include "../doomtype.h" +typedef BOOL (WINAPI *p_GetDiskFreeSpaceExA)(LPCSTR, PULARGE_INTEGER, PULARGE_INTEGER, PULARGE_INTEGER); +typedef BOOL (WINAPI *p_IsProcessorFeaturePresent) (DWORD); +typedef DWORD (WINAPI *p_timeGetTime) (void); +typedef UINT (WINAPI *p_timeEndPeriod) (UINT); +typedef HANDLE (WINAPI *p_OpenFileMappingA) (DWORD, BOOL, LPCSTR); +typedef LPVOID (WINAPI *p_MapViewOfFile) (HANDLE, DWORD, DWORD, DWORD, SIZE_T); +#endif +#include +#include +#include +#ifdef __GNUC__ +#include +#elif defined (_MSC_VER) +#include +#endif +#if defined (__unix__) || defined (UNIXCOMMON) +#include +#endif + +#include +#ifdef _WIN32 +#include +#endif + +#ifdef _MSC_VER +#pragma warning(disable : 4214 4244) +#endif + +#ifdef HAVE_SDL +#define _MATH_DEFINES_DEFINED +#include + +#ifdef HAVE_TTF +#include "i_ttf.h" +#endif + +#ifdef _MSC_VER +#pragma warning(default : 4214 4244) +#endif + +#include "SDL_cpuinfo.h" +#define HAVE_SDLCPUINFO + +#if defined (__unix__) || defined(__APPLE__) || (defined (UNIXCOMMON) && !defined (__HAIKU__)) +#if defined (__linux__) +#include +#else +#include +#include +/*For meminfo*/ +#include +#ifdef FREEBSD +#include +#endif +#include +#include +#endif +#endif + +#if defined (__linux__) || (defined (UNIXCOMMON) && !defined (__HAIKU__)) +#ifndef NOTERMIOS +#include +#include // ioctl +#define HAVE_TERMIOS +#endif +#endif + +#if (defined (__unix__) && !defined (_MSDOS)) || (defined (UNIXCOMMON) && !defined(__APPLE__)) +#include +#include +#define NEWSIGNALHANDLER +#endif + +#ifndef NOMUMBLE +#ifdef __linux__ // need -lrt +#include +#ifdef MAP_FAILED +#define HAVE_SHM +#endif +#include +#endif + +#ifdef _WIN32 +#define HAVE_MUMBLE +#define WINMUMBLE +#elif defined (HAVE_SHM) +#define HAVE_MUMBLE +#endif +#endif // NOMUMBLE + +#ifndef O_BINARY +#define O_BINARY 0 +#endif + +#ifdef __APPLE__ +#include "macosx/mac_resources.h" +#endif + +#ifndef errno +#include +#endif + +#if defined (__unix__) || defined(__APPLE__) || defined (UNIXCOMMON) +#include +#include +#define UNIXBACKTRACE +#endif + +// Locations for searching for main.pk3 +#if defined (__unix__) || defined(__APPLE__) || defined (UNIXCOMMON) +#define DEFAULTWADLOCATION1 "/usr/local/share/games/SRB2Kart-V2" +#define DEFAULTWADLOCATION2 "/usr/local/games/SRB2Kart-V2" +#define DEFAULTWADLOCATION3 "/usr/share/games/SRB2Kart-V2" +#define DEFAULTWADLOCATION4 "/usr/games/SRB2Kart-V2" +#define DEFAULTSEARCHPATH1 "/usr/local/games" +#define DEFAULTSEARCHPATH2 "/usr/games" +#define DEFAULTSEARCHPATH3 "/usr/local" +#endif + +/** \brief WAD file to look for +*/ +#define WADKEYWORD "main.pk3" +/** \brief holds wad path +*/ +static char returnWadPath[256]; + +//Alam_GBC: SDL + +#include "../doomdef.h" +#include "../m_misc.h" +#include "../i_time.h" +#include "../i_video.h" +#include "../i_sound.h" +#include "../i_system.h" +#include "../i_threads.h" +#include "../screen.h" //vid.WndParent +#include "../d_net.h" +#include "../g_game.h" +#include "../filesrch.h" +#include "../s_sound.h" +#include "../core/thread_pool.h" +#include "endtxt.h" +#include "sdlmain.h" + +#include "../i_joy.h" + +#include "../m_argv.h" + +// SRB2Kart +#include "../k_pwrlv.h" +#include "../r_main.h" // Frame interpolation/uncapped +#include "../r_fps.h" + +#ifdef MAC_ALERT +#include "macosx/mac_alert.h" +#endif + +#include "../d_main.h" + +#if !defined(NOMUMBLE) && defined(HAVE_MUMBLE) +// Mumble context string +#include "../d_clisrv.h" +#include "../byteptr.h" +#endif + +static std::thread::id g_main_thread_id; + +/** \brief The JoyReset function + + \param JoySet Joystick info to reset + + \return void +*/ +static void JoyReset(SDLJoyInfo_t *JoySet) +{ + if (JoySet->dev) + { + SDL_GameControllerClose(JoySet->dev); + } + JoySet->dev = NULL; + JoySet->oldjoy = -1; + JoySet->axises = JoySet->buttons = JoySet->hats = JoySet->balls = 0; + //JoySet->scale +} + +/** \brief First joystick up and running +*/ +static INT32 joystick_started[MAXSPLITSCREENPLAYERS] = {0,0,0,0}; + +/** \brief SDL info about joystick 1 +*/ +SDLJoyInfo_t JoyInfo[MAXSPLITSCREENPLAYERS]; + +SDL_bool consolevent = SDL_FALSE; +SDL_bool framebuffer = SDL_FALSE; + +UINT8 keyboard_started = false; +boolean g_in_exiting_signal_handler = false; + +#ifdef UNIXBACKTRACE +#define STDERR_WRITE(string) if (fd != -1) I_OutputMsg("%s", string) +#define CRASHLOG_WRITE(string) if (fd != -1) write(fd, string, strlen(string)) +#define CRASHLOG_STDERR_WRITE(string) \ + if (fd != -1)\ + write(fd, string, strlen(string));\ + I_OutputMsg("%s", string) + +static void write_backtrace(INT32 signal) +{ + int fd = -1; + size_t size; + time_t rawtime; + struct tm timeinfo; + + enum { BT_SIZE = 1024, STR_SIZE = 32 }; + void *array[BT_SIZE]; + char timestr[STR_SIZE]; + + const char *error = "An error occurred within SRB2Kart! Send this stack trace to someone who can help!\n"; + const char *error2 = "(Or find crash-log.txt in your SRB2Kart directory.)\n"; // Shown only to stderr. + + fd = open(va("%s" PATHSEP "%s", srb2home, "crash-log.txt"), O_CREAT|O_APPEND|O_RDWR, S_IRUSR|S_IWUSR); + + if (fd == -1) + I_OutputMsg("\nWARNING: Couldn't open crash log for writing! Make sure your permissions are correct. Please save the below report!\n"); + + // Get the current time as a string. + time(&rawtime); + localtime_r(&rawtime, &timeinfo); + strftime(timestr, STR_SIZE, "%a, %d %b %Y %T %z", &timeinfo); + + CRASHLOG_WRITE("------------------------\n"); // Nice looking seperator + + CRASHLOG_STDERR_WRITE("\n"); // Newline to look nice for both outputs. + CRASHLOG_STDERR_WRITE(error); // "Oops, SRB2 crashed" message + STDERR_WRITE(error2); // Tell the user where the crash log is. + + // Tell the log when we crashed. + CRASHLOG_WRITE("Time of crash: "); + CRASHLOG_WRITE(timestr); + CRASHLOG_WRITE("\n"); + + // Give the crash log the cause and a nice 'Backtrace:' thing + // The signal is given to the user when the parent process sees we crashed. + CRASHLOG_WRITE("Cause: "); + CRASHLOG_WRITE(strsignal(signal)); + CRASHLOG_WRITE("\n"); // Newline for the signal name + + CRASHLOG_STDERR_WRITE("\nBacktrace:\n"); + + // Flood the output and log with the backtrace + size = backtrace(array, BT_SIZE); + backtrace_symbols_fd(array, size, fd); + backtrace_symbols_fd(array, size, STDERR_FILENO); + + CRASHLOG_WRITE("\n"); // Write another newline to the log so it looks nice :) + + close(fd); +} +#undef STDERR_WRITE +#undef CRASHLOG_WRITE +#undef CRASHLOG_STDERR_WRITE +#endif // UNIXBACKTRACE + +static void I_ShowErrorMessageBox(const char *messagefordevelopers, boolean dumpmade) +{ + static char finalmessage[2048]; + size_t firstimpressionsline = 3; // "Dr Robotnik's Ring Racers" has encountered... + + if (M_CheckParm("-dedicated")) + return; + + snprintf( + finalmessage, + sizeof(finalmessage), + "\"SRB2Kart V2\" has encountered an unrecoverable error and needs to close.\n" + "The %s log file is located at (%s).\n" + "\n" + "\n" + "%s", + dumpmade ? +#if defined (UNIXBACKTRACE) + "crash-log.txt" +#elif defined (_WIN32) + ".rpt crash dump" +#endif + : "", + logfilename, + messagefordevelopers); + + // Rudementary word wrapping. + // Simple and effective. Does not handle nonuniform letter sizes, etc. but who cares. + { + size_t max = 0, maxatstart = 0, start = 0, width = 0, i; + + for (i = 0; finalmessage[i]; i++) + { + if (finalmessage[i] == ' ') + { + start = i; + max += 4; + maxatstart = max; + } + else if (finalmessage[i] == '\n') + { + if (firstimpressionsline > 0) + { + firstimpressionsline--; + if (firstimpressionsline == 0) + { + width = max; + } + } + start = 0; + max = 0; + maxatstart = 0; + continue; + } + else + max += 8; + + // Start trying to wrap if presumed length exceeds the space we want. + if (width > 0 && max >= width && start > 0) + { + finalmessage[start] = '\n'; + max -= maxatstart; + start = 0; + } + } + } + + // Implement message box with SDL_ShowSimpleMessageBox, + // which should fail gracefully if it can't put a message box up + // on the target system + if (!M_CheckParm("-dedicated")) + SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, + "SRB2Kart V2 " VERSIONSTRING " Error", + finalmessage, NULL); + + // Note that SDL_ShowSimpleMessageBox does *not* require SDL to be + // initialized at the time, so calling it after SDL_Quit() is + // perfectly okay! In addition, we do this on purpose so the + // fullscreen window is closed before displaying the error message + // in case the fullscreen window blocks it for some absurd reason. +} + +static void I_ReportSignal(int num, int coredumped) +{ + //static char msg[] = "oh no! back to reality!\r\n"; + const char * sigmsg; + char msg[128]; + + switch (num) + { +// case SIGINT: +// sigmsg = "SIGINT - interrupted"; +// break; + case SIGILL: + sigmsg = "SIGILL - illegal instruction - invalid function image"; + break; + case SIGFPE: + sigmsg = "SIGFPE - mathematical exception"; + break; + case SIGSEGV: + sigmsg = "SIGSEGV - segment violation"; + break; +// case SIGTERM: +// sigmsg = "SIGTERM - Software termination signal from kill"; +// break; +// case SIGBREAK: +// sigmsg = "SIGBREAK - Ctrl-Break sequence"; +// break; + case SIGABRT: + sigmsg = "SIGABRT - abnormal termination triggered by abort call"; + break; + default: + sprintf(msg,"signal number %d", num); + if (coredumped) + sigmsg = 0; + else + sigmsg = msg; + } + + if (coredumped) + { + if (sigmsg) + strcpy(msg, sigmsg); + strcat(msg, " (core dumped)"); + + sigmsg = msg; + } + + I_OutputMsg("\nProcess killed by signal: %s\n\n", sigmsg); + + I_ShowErrorMessageBox(sigmsg, +#if defined (UNIXBACKTRACE) + true +#elif defined (_WIN32) + !M_CheckParm("-noexchndl") +#else + false +#endif + ); +} + +#ifndef NEWSIGNALHANDLER +FUNCNORETURN static ATTRNORETURN void signal_handler(INT32 num) +{ + g_in_exiting_signal_handler = true; + + if (g_main_thread_id != std::this_thread::get_id()) + { + // Do not attempt any sort of recovery if this signal triggers off the main thread + signal(num, SIG_DFL); + raise(num); + exit(-2); + } + + D_QuitNetGame(); // Fix server freezes + CL_AbortDownloadResume(); +#ifdef UNIXBACKTRACE + write_backtrace(num); +#endif + I_ReportSignal(num, 0); + signal(num, SIG_DFL); //default signal action + raise(num); +} +#endif + +FUNCNORETURN static ATTRNORETURN void quit_handler(int num) +{ + signal(num, SIG_DFL); //default signal action + raise(num); + I_Quit(); +} + +#ifdef HAVE_TERMIOS +// TERMIOS console code from Quake3: thank you! +SDL_bool stdin_active = SDL_TRUE; + +typedef struct +{ + size_t cursor; + char buffer[256]; +} feild_t; + +feild_t tty_con; + +// when printing general stuff to stdout stderr (Sys_Printf) +// we need to disable the tty console stuff +// this increments so we can recursively disable +static INT32 ttycon_hide = 0; +// some key codes that the terminal may be using +// TTimo NOTE: I'm not sure how relevant this is +static INT32 tty_erase; +static INT32 tty_eof; + +static struct termios tty_tc; + +// ============================================================= +// tty console routines +// NOTE: if the user is editing a line when something gets printed to the early console then it won't look good +// so we provide tty_Clear and tty_Show to be called before and after a stdout or stderr output +// ============================================================= + +// flush stdin, I suspect some terminals are sending a LOT of garbage +// FIXME TTimo relevant? +#if 0 +static inline void tty_FlushIn(void) +{ + char key; + while (read(STDIN_FILENO, &key, 1)!=-1); +} +#endif + +// do a backspace +// TTimo NOTE: it seems on some terminals just sending '\b' is not enough +// so for now, in any case we send "\b \b" .. yeah well .. +// (there may be a way to find out if '\b' alone would work though) +static void tty_Back(void) +{ + char key; + ssize_t d; + key = '\b'; + d = write(STDOUT_FILENO, &key, 1); + key = ' '; + d = write(STDOUT_FILENO, &key, 1); + key = '\b'; + d = write(STDOUT_FILENO, &key, 1); + (void)d; +} + +static void tty_Clear(void) +{ + size_t i; + if (tty_con.cursor>0) + { + for (i=0; i0); + ttycon_hide--; + if (ttycon_hide == 0 && tty_con.cursor) + { + for (i=0; i(!M_CheckParm("-noconsole")); + framebuffer = static_cast(M_CheckParm("-framebuffer")); + + if (framebuffer) + consolevent = SDL_FALSE; + + if (!consolevent) return; + + if (isatty(STDIN_FILENO)!=1) + { + I_OutputMsg("stdin is not a tty, tty console mode failed\n"); + consolevent = SDL_FALSE; + return; + } + memset(&tty_con, 0x00, sizeof(tty_con)); + tcgetattr (0, &tty_tc); + tty_erase = tty_tc.c_cc[VERASE]; + tty_eof = tty_tc.c_cc[VEOF]; + tc = tty_tc; + /* + ECHO: don't echo input characters + ICANON: enable canonical mode. This enables the special + characters EOF, EOL, EOL2, ERASE, KILL, REPRINT, + STATUS, and WERASE, and buffers by lines. + ISIG: when any of the characters INTR, QUIT, SUSP, or + DSUSP are received, generate the corresponding signal + */ + tc.c_lflag &= ~(ECHO | ICANON); + /* + ISTRIP strip off bit 8 + INPCK enable input parity checking + */ + tc.c_iflag &= ~(ISTRIP | INPCK); + tc.c_cc[VMIN] = 0; //1? + tc.c_cc[VTIME] = 0; + tcsetattr (0, TCSADRAIN, &tc); +} + +void I_GetConsoleEvents(void) +{ + // we use this when sending back commands + event_t ev = {}; + char key = 0; + ssize_t d; + + if (!consolevent) + return; + + ev.type = ev_console; + if (read(STDIN_FILENO, &key, 1) == -1 || !key) + return; + + // we have something + // backspace? + // NOTE TTimo testing a lot of values .. seems it's the only way to get it to work everywhere + if ((key == tty_erase) || (key == 127) || (key == 8)) + { + if (tty_con.cursor > 0) + { + tty_con.cursor--; + tty_con.buffer[tty_con.cursor] = '\0'; + tty_Back(); + } + ev.data1 = KEY_BACKSPACE; + } + else if (key < ' ') // check if this is a control char + { + if (key == '\n') + { + tty_Clear(); + tty_con.cursor = 0; + ev.data1 = KEY_ENTER; + } + else return; + } + else + { + // push regular character + ev.data1 = tty_con.buffer[tty_con.cursor] = key; + tty_con.cursor++; + // print the current line (this is differential) + d = write(STDOUT_FILENO, &key, 1); + } + if (ev.data1) D_PostEvent(&ev); + //tty_FlushIn(); + (void)d; +} + +#elif defined (_WIN32) +static BOOL I_ReadyConsole(HANDLE ci) +{ + DWORD gotinput; + if (ci == INVALID_HANDLE_VALUE) return FALSE; + if (WaitForSingleObject(ci,0) != WAIT_OBJECT_0) return FALSE; + if (GetFileType(ci) != FILE_TYPE_CHAR) return FALSE; + if (!GetConsoleMode(ci, &gotinput)) return FALSE; + return (GetNumberOfConsoleInputEvents(ci, &gotinput) && gotinput); +} + +static boolean entering_con_command = false; + +static void Impl_HandleKeyboardConsoleEvent(KEY_EVENT_RECORD evt, HANDLE co) +{ + event_t event; + CONSOLE_SCREEN_BUFFER_INFO CSBI; + DWORD t; + + memset(&event,0x00,sizeof (event)); + + if (evt.bKeyDown) + { + event.type = ev_console; + entering_con_command = true; + switch (evt.wVirtualKeyCode) + { + case VK_ESCAPE: + case VK_TAB: + event.data1 = KEY_NULL; + break; + case VK_RETURN: + entering_con_command = false; + /* FALLTHRU */ + default: + //event.data1 = MapVirtualKey(evt.wVirtualKeyCode,2); // convert in to char + event.data1 = evt.uChar.AsciiChar; + } + if (co != INVALID_HANDLE_VALUE && GetFileType(co) == FILE_TYPE_CHAR && GetConsoleMode(co, &t)) + { + if (event.data1 && event.data1 != KEY_LSHIFT && event.data1 != KEY_RSHIFT) + { +#ifdef _UNICODE + WriteConsole(co, &evt.uChar.UnicodeChar, 1, &t, NULL); +#else + WriteConsole(co, &evt.uChar.AsciiChar, 1 , &t, NULL); +#endif + } + if (evt.wVirtualKeyCode == VK_BACK + && GetConsoleScreenBufferInfo(co,&CSBI)) + { + WriteConsoleOutputCharacterA(co, " ",1, CSBI.dwCursorPosition, &t); + } + } + } + if (event.data1) D_PostEvent(&event); +} + +void I_GetConsoleEvents(void) +{ + HANDLE ci = GetStdHandle(STD_INPUT_HANDLE); + HANDLE co = GetStdHandle(STD_OUTPUT_HANDLE); + INPUT_RECORD input; + DWORD t; + + while (I_ReadyConsole(ci) && ReadConsoleInput(ci, &input, 1, &t) && t) + { + switch (input.EventType) + { + case KEY_EVENT: + Impl_HandleKeyboardConsoleEvent(input.Event.KeyEvent, co); + break; + case MOUSE_EVENT: + case WINDOW_BUFFER_SIZE_EVENT: + case MENU_EVENT: + case FOCUS_EVENT: + break; + } + } +} + +static void I_StartupConsole(void) +{ + HANDLE ci, co; + const INT32 ded = M_CheckParm("-dedicated"); + BOOL gotConsole = FALSE; + if (M_CheckParm("-console") || ded) + gotConsole = AllocConsole(); +#ifdef _DEBUG + else if (M_CheckParm("-noconsole") && !ded) +#else + else if (!M_CheckParm("-console") && !ded) +#endif + { + FreeConsole(); + gotConsole = FALSE; + } + + if (gotConsole) + { + SetConsoleTitleA("SRB2Kart Console"); + consolevent = SDL_TRUE; + } + + //Let get the real console HANDLE, because Mingw's Bash is bad! + ci = CreateFile(TEXT("CONIN$") , GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + co = CreateFile(TEXT("CONOUT$"), GENERIC_WRITE|GENERIC_READ, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (ci != INVALID_HANDLE_VALUE) + { + const DWORD CM = ENABLE_LINE_INPUT|ENABLE_ECHO_INPUT|ENABLE_PROCESSED_INPUT; + SetStdHandle(STD_INPUT_HANDLE, ci); + if (GetFileType(ci) == FILE_TYPE_CHAR) + SetConsoleMode(ci, CM); //default mode but no ENABLE_MOUSE_INPUT + } + if (co != INVALID_HANDLE_VALUE) + { + SetStdHandle(STD_OUTPUT_HANDLE, co); + SetStdHandle(STD_ERROR_HANDLE, co); + } +} +static inline void I_ShutdownConsole(void){} +#else +void I_GetConsoleEvents(void){} +static inline void I_StartupConsole(void) +{ +#ifdef _DEBUG + consolevent = !M_CheckParm("-noconsole"); +#else + consolevent = M_CheckParm("-console"); +#endif + + framebuffer = M_CheckParm("-framebuffer"); + + if (framebuffer) + consolevent = SDL_FALSE; +} +static inline void I_ShutdownConsole(void){} +#endif + +// +// StartupKeyboard +// +static void I_RegisterSignals (void) +{ + g_main_thread_id = std::this_thread::get_id(); + +#ifdef SIGINT + signal(SIGINT , quit_handler); +#endif +#ifdef SIGBREAK + signal(SIGBREAK , quit_handler); +#endif +#ifdef SIGTERM + signal(SIGTERM , quit_handler); +#endif + + // If these defines don't exist, + // then compilation would have failed above us... +#ifndef NEWSIGNALHANDLER + signal(SIGILL , signal_handler); + signal(SIGSEGV , signal_handler); + signal(SIGABRT , signal_handler); + signal(SIGFPE , signal_handler); +#endif +} + +#ifdef NEWSIGNALHANDLER +static void signal_handler_child(INT32 num) +{ +#ifdef UNIXBACKTRACE + write_backtrace(num); +#endif + + signal(num, SIG_DFL); //default signal action + raise(num); +} + +static void I_RegisterChildSignals(void) +{ + // If these defines don't exist, + // then compilation would have failed above us... + signal(SIGILL , signal_handler_child); + signal(SIGSEGV , signal_handler_child); + signal(SIGABRT , signal_handler_child); + signal(SIGFPE , signal_handler_child); +} +#endif + +// +//I_OutputMsg +// +void I_OutputMsg(const char *fmt, ...) +{ + size_t len; + char txt[8192]; + va_list argptr; + + va_start(argptr,fmt); + vsprintf(txt, fmt, argptr); + va_end(argptr); + +#ifdef HAVE_TTF + if (TTF_WasInit()) I_TTFDrawText(currentfont, solid, DEFAULTFONTFGR, DEFAULTFONTFGG, DEFAULTFONTFGB, DEFAULTFONTFGA, + DEFAULTFONTBGR, DEFAULTFONTBGG, DEFAULTFONTBGB, DEFAULTFONTBGA, txt); +#endif + +#if defined (_WIN32) && defined (_MSC_VER) + OutputDebugStringA(txt); +#endif + + len = strlen(txt); + +#ifdef LOGMESSAGES + if (logstream) + { + size_t d = fwrite(txt, len, 1, logstream); + fflush(logstream); + (void)d; + } +#endif + +#if defined (_WIN32) +#ifdef DEBUGFILE + if (debugfile != stderr) +#endif + { + HANDLE co = GetStdHandle(STD_OUTPUT_HANDLE); + DWORD bytesWritten; + + if (co == INVALID_HANDLE_VALUE) + return; + + if (GetFileType(co) == FILE_TYPE_CHAR && GetConsoleMode(co, &bytesWritten)) + { + static COORD coordNextWrite = {0,0}; + LPVOID oldLines = NULL; + INT oldLength; + CONSOLE_SCREEN_BUFFER_INFO csbi; + + // Save the lines that we're going to obliterate. + GetConsoleScreenBufferInfo(co, &csbi); + oldLength = csbi.dwSize.X * (csbi.dwCursorPosition.Y - coordNextWrite.Y) + csbi.dwCursorPosition.X - coordNextWrite.X; + + if (oldLength > 0) + { + LPVOID blank = malloc(oldLength); + if (!blank) return; + memset(blank, ' ', oldLength); // Blank out. + oldLines = malloc(oldLength*sizeof(TCHAR)); + if (!oldLines) + { + free(blank); + return; + } + + ReadConsoleOutputCharacter(co, (LPSTR)oldLines, oldLength, coordNextWrite, &bytesWritten); + + // Move to where we what to print - which is where we would've been, + // had console input not been in the way, + SetConsoleCursorPosition(co, coordNextWrite); + + WriteConsoleA(co, blank, oldLength, &bytesWritten, NULL); + free(blank); + + // And back to where we want to print again. + SetConsoleCursorPosition(co, coordNextWrite); + } + + // Actually write the string now! + WriteConsoleA(co, txt, (DWORD)len, &bytesWritten, NULL); + + // Next time, output where we left off. + GetConsoleScreenBufferInfo(co, &csbi); + coordNextWrite = csbi.dwCursorPosition; + + // Restore what was overwritten. + if (oldLines && entering_con_command) + WriteConsole(co, oldLines, oldLength, &bytesWritten, NULL); + if (oldLines) free(oldLines); + } + else // Redirected to a file. + WriteFile(co, txt, (DWORD)len, &bytesWritten, NULL); + } +#else +#ifdef HAVE_TERMIOS + if (consolevent) + { + tty_Hide(); + } +#endif + + if (!framebuffer) + fprintf(stderr, "%s", txt); +#ifdef HAVE_TERMIOS + if (consolevent) + { + tty_Show(); + } +#endif + + // 2004-03-03 AJR Since not all messages end in newline, some were getting displayed late. + if (!framebuffer) + fflush(stderr); + +#endif +} + +// +// I_GetKey +// +INT32 I_GetKey (void) +{ + // Warning: I_GetKey empties the event queue till next keypress + event_t *ev; + INT32 rc = 0; + + // return the first keypress from the event queue + for (; eventtail != eventhead; eventtail = (eventtail+1)&(MAXEVENTS-1)) + { + ev = &events[eventtail]; + if (ev->type == ev_keydown || ev->type == ev_console) + { + rc = ev->data1; + continue; + } + } + + return rc; +} + +void +I_CursedWindowMovement (int xd, int yd) +{ + SDL_SetWindowPosition(window, window_x + xd, window_y + yd); +} + +// +// I_JoyScale +// +void I_JoyScale(void) +{ + Joystick[0].bGamepadStyle = cv_joyscale[0].value==0; + JoyInfo[0].scale = Joystick[0].bGamepadStyle?1:cv_joyscale[0].value; +} + +void I_JoyScale2(void) +{ + Joystick[1].bGamepadStyle = cv_joyscale[1].value==0; + JoyInfo[1].scale = Joystick[1].bGamepadStyle?1:cv_joyscale[1].value; +} + +void I_JoyScale3(void) +{ + Joystick[2].bGamepadStyle = cv_joyscale[2].value==0; + JoyInfo[2].scale = Joystick[2].bGamepadStyle?1:cv_joyscale[2].value; +} + +void I_JoyScale4(void) +{ + Joystick[3].bGamepadStyle = cv_joyscale[3].value==0; + JoyInfo[3].scale = Joystick[3].bGamepadStyle?1:cv_joyscale[1].value; +} + +// Cheat to get the device index for a joystick handle +INT32 I_GetJoystickDeviceIndex(SDL_GameController *dev) +{ + SDL_Joystick *joystick = NULL; + + joystick = SDL_GameControllerGetJoystick(dev); + + if (joystick) + { + return SDL_JoystickInstanceID(joystick); + } + + return -1; +} + +void I_UpdateJoystickDeviceIndex(UINT8 player) +{ + /////////////////////////////////////////////// + // update this joystick's device index (wow) // + /////////////////////////////////////////////// + + if (JoyInfo[player].dev) + { + cv_usejoystick[player].value = I_GetJoystickDeviceIndex(JoyInfo[player].dev) + 1; + } + else + { + UINT8 joystickID, compareJoystick; + + for (joystickID = 0; joystickID < MAXSPLITSCREENPLAYERS; joystickID++) + { + // is this cv_usejoystick used? + const INT32 value = atoi(cv_usejoystick[joystickID].string); + + for (compareJoystick = 0; compareJoystick < MAXSPLITSCREENPLAYERS; compareJoystick++) + { + if (compareJoystick == player) + continue; + + if (value == JoyInfo[compareJoystick].oldjoy || value == cv_usejoystick[compareJoystick].value) + break; + } + + if (compareJoystick == MAXSPLITSCREENPLAYERS) + { + // We DID make it through the whole loop, so we can use this one! + cv_usejoystick[player].value = value; + break; + } + } + + if (joystickID == MAXSPLITSCREENPLAYERS) + { + // We DID NOT make it through the whole loop, so we can't assign this joystick to anything. + // When you try your best, but you don't succeed... + cv_usejoystick[player].value = 0; + } + } +} + +// Misleading function: updates device indices for all players BUT the one specified. +// Necessary for SDL_JOYDEVICEADDED events +void I_UpdateJoystickDeviceIndices(UINT8 excludePlayer) +{ + UINT8 player; + + for (player = 0; player < MAXSPLITSCREENPLAYERS; player++) + { + if (player == excludePlayer) + continue; + + I_UpdateJoystickDeviceIndex(player); + } +} + +/** \brief Joystick buttons states +*/ +static UINT64 lastjoybuttons[MAXSPLITSCREENPLAYERS] = {0,0,0,0}; + +/** \brief Joystick hats state +*/ +static UINT64 lastjoyhats[MAXSPLITSCREENPLAYERS] = {0,0,0,0}; + +/** \brief Shuts down joystick + \return void +*/ +void I_ShutdownJoystick(UINT8 index) +{ + INT32 i; + event_t event; + event.type=ev_keyup; + event.data2 = 0; + event.data3 = 0; + + lastjoybuttons[index] = lastjoyhats[index] = 0; + + // emulate the up of all joystick buttons + for (i=0;i= 0; i--) + { + joybuttons <<= 1; + if (SDL_JoystickGetButton(JoyInfo[index].dev,i)) + joybuttons |= 1; + } + + if (joybuttons != lastjoybuttons[index]) + { + INT64 j = 1; // keep only bits that changed since last time + INT64 newbuttons = joybuttons ^ lastjoybuttons[index]; + lastjoybuttons[index] = joybuttons; + + for (i = 0; i < JOYBUTTONS; i++, j <<= 1) + { + if (newbuttons & j) // button changed state? + { + if (joybuttons & j) + event.type = ev_keydown; + else + event.type = ev_keyup; + event.data1 = KEY_JOY1 + i; + D_PostEvent(&event); + } + } + } +#endif + + + joyhats |= SDL_GameControllerGetButton(JoyInfo[index].dev, SDL_CONTROLLER_BUTTON_DPAD_UP); + joyhats |= SDL_GameControllerGetButton(JoyInfo[index].dev, SDL_CONTROLLER_BUTTON_DPAD_DOWN) << 1; + joyhats |= SDL_GameControllerGetButton(JoyInfo[index].dev, SDL_CONTROLLER_BUTTON_DPAD_LEFT) << 2; + joyhats |= SDL_GameControllerGetButton(JoyInfo[index].dev, SDL_CONTROLLER_BUTTON_DPAD_RIGHT) << 3; + + + if (joyhats != lastjoyhats[index]) + { + INT64 j = 1; // keep only bits that changed since last time + INT64 newhats = joyhats ^ lastjoyhats[index]; + lastjoyhats[index] = joyhats; + + for (i = 0; i < JOYHATS*4; i++, j <<= 1) + { + if (newhats & j) // hat changed state? + { + if (joyhats & j) + event.type = ev_keydown; + else + event.type = ev_keyup; + event.data1 = KEY_HAT1 + i; + D_PostEvent(&event); + } + } + } + +#if 0 + // send joystick axis positions + event.type = ev_joystick; + + for (i = JOYAXISSET - 1; i >= 0; i--) + { + event.data1 = i; + if (i*2 + 1 <= JoyInfo[index].axises) + axisx = SDL_JoystickGetAxis(JoyInfo[index].dev, i*2 + 0); + else axisx = 0; + if (i*2 + 2 <= JoyInfo[index].axises) + axisy = SDL_JoystickGetAxis(JoyInfo[index].dev, i*2 + 1); + else axisy = 0; + + + // -32768 to 32767 + axisx = axisx/32; + axisy = axisy/32; + + + if (Joystick[index].bGamepadStyle) + { + // gamepad control type, on or off, live or die + if (axisx < -(JOYAXISRANGE/2)) + event.data2 = -1; + else if (axisx > (JOYAXISRANGE/2)) + event.data2 = 1; + else + event.data2 = 0; + if (axisy < -(JOYAXISRANGE/2)) + event.data3 = -1; + else if (axisy > (JOYAXISRANGE/2)) + event.data3 = 1; + else + event.data3 = 0; + } + else + { + + axisx = JoyInfo[index].scale?((axisx/JoyInfo[index].scale)*JoyInfo[index].scale):axisx; + axisy = JoyInfo[index].scale?((axisy/JoyInfo[index].scale)*JoyInfo[index].scale):axisy; + +#ifdef SDL_JDEADZONE + if (-SDL_JDEADZONE <= axisx && axisx <= SDL_JDEADZONE) axisx = 0; + if (-SDL_JDEADZONE <= axisy && axisy <= SDL_JDEADZONE) axisy = 0; +#endif + + // analog control style , just send the raw data + event.data2 = axisx; // x axis + event.data3 = axisy; // y axis + } + D_PostEvent(&event); + } +#endif +} + +/** \brief Open joystick handle + + \param fname name of joystick + + \return axises + + +*/ +static int joy_open(int playerIndex, int joyIndex) +{ + SDL_GameController *newdev = NULL; + int num_joy = 0; + + if (SDL_WasInit(SDL_INIT_JOYSTICK) == 0) + { + CONS_Printf(M_GetText("Joystick subsystem not started\n")); + return -1; + } + if (SDL_WasInit(SDL_INIT_GAMECONTROLLER) == 0) + { + CONS_Printf(M_GetText("Game Controller subsystem not started\n")); + return -1; + } + + + if (joyIndex <= 0) + return -1; + + num_joy = SDL_NumJoysticks(); + + if (num_joy == 0) + { + CONS_Printf("%s", M_GetText("Found no joysticks on this system\n")); + return -1; + } + + newdev = SDL_GameControllerOpen(joyIndex-1); + + // Handle the edge case where the device <-> joystick index assignment can change due to hotplugging + // This indexing is SDL's responsibility and there's not much we can do about it. + // + // Example: + // 1. Plug Controller A -> Index 0 opened + // 2. Plug Controller B -> Index 1 opened + // 3. Unplug Controller A -> Index 0 closed, Index 1 active + // 4. Unplug Controller B -> Index 0 inactive, Index 1 closed + // 5. Plug Controller B -> Index 0 opened + // 6. Plug Controller A -> Index 0 REPLACED, opened as Controller A; Index 1 is now Controller B + if (JoyInfo[playerIndex].dev) + { + if (JoyInfo[playerIndex].dev == newdev // same device, nothing to do + || (newdev == NULL && SDL_GameControllerGetAttached(JoyInfo[playerIndex].dev))) // we failed, but already have a working device + { + return SDL_CONTROLLER_AXIS_MAX; + } + + // Else, we're changing devices, so send neutral joy events + CONS_Debug(DBG_GAMELOGIC, "Joystick%d device is changing; resetting events...\n", playerIndex+1); + I_ShutdownJoystick(playerIndex); + } + + JoyInfo[playerIndex].dev = newdev; + + if (JoyInfo[playerIndex].dev == NULL) + { + CONS_Debug(DBG_GAMELOGIC, M_GetText("Joystick%d: Couldn't open device - %s\n"), playerIndex+1, SDL_GetError()); + return -1; + } + else + { + CONS_Debug(DBG_GAMELOGIC, M_GetText("Joystick%d: %s\n"), playerIndex+1, SDL_GameControllerName(JoyInfo[playerIndex].dev)); + + JoyInfo[playerIndex].axises = SDL_CONTROLLER_AXIS_MAX; + if (JoyInfo[playerIndex].axises > JOYAXISSET*2) + JoyInfo[playerIndex].axises = JOYAXISSET*2; + + /* + if (joyaxes<2) + { + I_OutputMsg("Not enought axes?\n"); + return 0; + } + */ + + JoyInfo[playerIndex].buttons = SDL_CONTROLLER_BUTTON_MAX; + if (JoyInfo[playerIndex].buttons > JOYBUTTONS) + JoyInfo[playerIndex].buttons = JOYBUTTONS; + + JoyInfo[playerIndex].hats = 4; + if (JoyInfo[playerIndex].hats > JOYHATS) + JoyInfo[playerIndex].hats = JOYHATS; + + JoyInfo[playerIndex].balls = 0; + + //JoyInfo[playerIndex].bGamepadStyle = !stricmp(SDL_JoystickName(JoyInfo[playerIndex].dev), "pad"); + + return JoyInfo[playerIndex].axises; + } +} + +// +// I_InitJoystick +// +void I_InitJoystick(UINT8 index) +{ + SDL_GameController *newcontroller = NULL; + UINT8 i; + + //I_ShutdownJoystick(); + //SDL_SetHintWithPriority("SDL_XINPUT_ENABLED", "0", SDL_HINT_OVERRIDE); + if (M_CheckParm("-nojoy")) + return; + + if (M_CheckParm("-noxinput")) + SDL_SetHintWithPriority("SDL_XINPUT_ENABLED", "0", SDL_HINT_OVERRIDE); + + if (M_CheckParm("-nohidapi")) + SDL_SetHintWithPriority("SDL_JOYSTICK_HIDAPI", "0", SDL_HINT_OVERRIDE); + + if (SDL_WasInit(SDL_INIT_JOYSTICK) == 0) + { + CONS_Printf("I_InitJoystick()...\n"); + + if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) == -1) + { + CONS_Printf(M_GetText("Couldn't initialize joystick: %s\n"), SDL_GetError()); + return; + } + } + if (SDL_WasInit(SDL_INIT_GAMECONTROLLER) == 0) + { + if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) == -1) + { + CONS_Printf(M_GetText("Couldn't initialize gamepads: %s\n"), SDL_GetError()); + return; + } + } + + + if (cv_usejoystick[index].value) + newcontroller = SDL_GameControllerOpen(cv_usejoystick[index].value-1); + + for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) + { + if (i == index) + continue; + + if (JoyInfo[i].dev == newcontroller) + break; + } + + if (newcontroller && i < MAXSPLITSCREENPLAYERS) // don't override an active device + { + cv_usejoystick[index].value = I_GetJoystickDeviceIndex(JoyInfo[index].dev) + 1; + } + else if (newcontroller && joy_open(index, cv_usejoystick[index].value) != -1) + { + // SDL's device indexes are unstable, so cv_usejoystick may not match + // the actual device index. So let's cheat a bit and find the device's current index. + JoyInfo[index].oldjoy = I_GetJoystickDeviceIndex(JoyInfo[index].dev) + 1; + joystick_started[index] = 1; + } + else + { + if (JoyInfo[index].oldjoy) + I_ShutdownJoystick(index); + cv_usejoystick[index].value = 0; + joystick_started[index] = 0; + } + + for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) + { + if (JoyInfo[i].dev == newcontroller) + break; + } + + if (i == MAXSPLITSCREENPLAYERS) + { + // Joystick didn't end up being used + SDL_GameControllerClose(newcontroller); + } +} + +void I_InitJoystick1(void) +{ + I_InitJoystick(0); +} + +void I_InitJoystick2(void) +{ + I_InitJoystick(1); +} + +void I_InitJoystick3(void) +{ + I_InitJoystick(2); +} + +void I_InitJoystick4(void) +{ + I_InitJoystick(3); +} + +static void I_ShutdownInput(void) +{ + UINT8 i; + + // Yes, the name is misleading: these send neutral events to + // clean up the unplugged joystick's input + // Note these methods are internal to this file, not called elsewhere. + + for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) + I_ShutdownJoystick(i); + + if (SDL_WasInit(SDL_INIT_JOYSTICK) == SDL_INIT_JOYSTICK) + { + CONS_Printf("Shutting down joy system\n"); + SDL_QuitSubSystem(SDL_INIT_JOYSTICK); + I_OutputMsg("I_Joystick: SDL's Joystick system has been shutdown\n"); + } +} + +INT32 I_NumJoys(void) +{ + INT32 numjoy = 0; + if (SDL_WasInit(SDL_INIT_JOYSTICK) == SDL_INIT_JOYSTICK) + numjoy = SDL_NumJoysticks(); + return numjoy; +} + +static char joyname[255]; // joystick name is straight from the driver + +const char *I_GetJoyName(INT32 joyindex) +{ + const char *tempname = NULL; + joyname[0] = 0; + joyindex--; //SDL's Joystick System starts at 0, not 1 + if (SDL_WasInit(SDL_INIT_JOYSTICK) == SDL_INIT_JOYSTICK) + { + tempname = SDL_JoystickNameForIndex(joyindex); + if (tempname) + { + strncpy(joyname, tempname, 254); + joyname[254] = 0; + } + } + return joyname; +} + +#ifndef NOMUMBLE +#ifdef HAVE_MUMBLE +// Best Mumble positional audio settings: +// Minimum distance 3.0 m +// Bloom 175% +// Maximum distance 80.0 m +// Minimum volume 50% +#define DEG2RAD (0.017453292519943295769236907684883l) // TAU/360 or PI/180 +#define MUMBLEUNIT (64.0f) // FRACUNITS in a Meter + +static struct mumble_s { +#ifdef WINMUMBLE + UINT32 uiVersion; + DWORD uiTick; +#else + Uint32 uiVersion; + Uint32 uiTick; +#endif + float fAvatarPosition[3]; + float fAvatarFront[3]; + float fAvatarTop[3]; // defaults to Y-is-up (only used for leaning) + wchar_t name[256]; // game name + float fCameraPosition[3]; + float fCameraFront[3]; + float fCameraTop[3]; // defaults to Y-is-up (only used for leaning) + wchar_t identity[256]; // player id +#ifdef WINMUMBLE + UINT32 context_len; +#else + Uint32 context_len; +#endif + unsigned char context[256]; // server/team + wchar_t description[2048]; // game description +} *mumble = NULL; +#endif // HAVE_MUMBLE + +static void I_SetupMumble(void) +{ +#ifdef WINMUMBLE + HANDLE hMap = OpenFileMappingW(FILE_MAP_ALL_ACCESS, FALSE, L"MumbleLink"); + if (!hMap) + return; + + mumble = static_cast(MapViewOfFile(hMap, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(*mumble))); + if (!mumble) + CloseHandle(hMap); +#elif defined (HAVE_SHM) + int shmfd; + char memname[256]; + + snprintf(memname, 256, "/MumbleLink.%d", getuid()); + shmfd = shm_open(memname, O_RDWR, S_IRUSR | S_IWUSR); + + if(shmfd < 0) + return; + + mumble = static_cast(mmap(NULL, sizeof(*mumble), PROT_READ | PROT_WRITE, MAP_SHARED, shmfd, 0)); + if (mumble == MAP_FAILED) + mumble = NULL; +#endif +} + +void I_UpdateMumble(const mobj_t *mobj, const listener_t listener) +{ +#ifdef HAVE_MUMBLE + double angle; + fixed_t anglef; + + if (!mumble) + return; + + if(mumble->uiVersion != 2) { + wcsncpy(mumble->name, L"SRB2Kart " VERSIONSTRINGW, 256); + wcsncpy(mumble->description, L"Sonic Robo Blast 2 Kart with integrated Mumble Link support.", 2048); + mumble->uiVersion = 2; + } + mumble->uiTick++; + + if (!netgame || gamestate != GS_LEVEL) { // Zero out, but never delink. + mumble->fAvatarPosition[0] = mumble->fAvatarPosition[1] = mumble->fAvatarPosition[2] = 0.0f; + mumble->fAvatarFront[0] = 1.0f; + mumble->fAvatarFront[1] = mumble->fAvatarFront[2] = 0.0f; + mumble->fCameraPosition[0] = mumble->fCameraPosition[1] = mumble->fCameraPosition[2] = 0.0f; + mumble->fCameraFront[0] = 1.0f; + mumble->fCameraFront[1] = mumble->fCameraFront[2] = 0.0f; + return; + } + + { + UINT8 *p = mumble->context; + WRITEMEM(p, server_context, 8); + WRITEINT16(p, gamemap); + mumble->context_len = (UINT32)(p - mumble->context); + } + + if (mobj) { + mumble->fAvatarPosition[0] = FIXED_TO_FLOAT(mobj->x) / MUMBLEUNIT; + mumble->fAvatarPosition[1] = FIXED_TO_FLOAT(mobj->z) / MUMBLEUNIT; + mumble->fAvatarPosition[2] = FIXED_TO_FLOAT(mobj->y) / MUMBLEUNIT; + + anglef = AngleFixed(mobj->angle); + angle = FIXED_TO_FLOAT(anglef)*DEG2RAD; + mumble->fAvatarFront[0] = (float)cos(angle); + mumble->fAvatarFront[1] = 0.0f; + mumble->fAvatarFront[2] = (float)sin(angle); + } else { + mumble->fAvatarPosition[0] = mumble->fAvatarPosition[1] = mumble->fAvatarPosition[2] = 0.0f; + mumble->fAvatarFront[0] = 1.0f; + mumble->fAvatarFront[1] = mumble->fAvatarFront[2] = 0.0f; + } + + mumble->fCameraPosition[0] = FIXED_TO_FLOAT(listener.x) / MUMBLEUNIT; + mumble->fCameraPosition[1] = FIXED_TO_FLOAT(listener.z) / MUMBLEUNIT; + mumble->fCameraPosition[2] = FIXED_TO_FLOAT(listener.y) / MUMBLEUNIT; + + anglef = AngleFixed(listener.angle); + angle = FIXED_TO_FLOAT(anglef)*DEG2RAD; + mumble->fCameraFront[0] = (float)cos(angle); + mumble->fCameraFront[1] = 0.0f; + mumble->fCameraFront[2] = (float)sin(angle); +#else + (void)mobj; + (void)listener; +#endif // HAVE_MUMBLE +} +#undef WINMUMBLE +#endif // NOMUMBLE + +// +// I_Tactile +// +void I_Tactile(FFType pFFType, const JoyFF_t *FFEffect) +{ + // UNUSED. + (void)pFFType; + (void)FFEffect; +} + +void I_Tactile2(FFType pFFType, const JoyFF_t *FFEffect) +{ + // UNUSED. + (void)pFFType; + (void)FFEffect; +} + +void I_Tactile3(FFType pFFType, const JoyFF_t *FFEffect) +{ + // UNUSED. + (void)pFFType; + (void)FFEffect; +} + +void I_Tactile4(FFType pFFType, const JoyFF_t *FFEffect) +{ + // UNUSED. + (void)pFFType; + (void)FFEffect; +} + +static ticcmd_t emptycmd[MAXSPLITSCREENPLAYERS]; + +/** \brief empty ticcmd for player 1 +*/ + +ticcmd_t *I_BaseTiccmd(void) +{ + return &emptycmd[0]; +} + +/** \brief empty ticcmd for player 2 +*/ + +ticcmd_t *I_BaseTiccmd2(void) +{ + return &emptycmd[1]; +} + +/** \brief empty ticcmd for player 3 +*/ + +ticcmd_t *I_BaseTiccmd3(void) +{ + return &emptycmd[2]; +} + +/** \brief empty ticcmd for player 4 +*/ + +ticcmd_t *I_BaseTiccmd4(void) +{ + return &emptycmd[3]; +} + +// +// I_GetTime +// returns time in 1/TICRATE second tics +// + +static Uint64 timer_frequency; + +precise_t I_GetPreciseTime(void) +{ + return SDL_GetPerformanceCounter(); +} + +UINT64 I_GetPrecisePrecision(void) +{ + return SDL_GetPerformanceFrequency(); +} + +static UINT32 frame_rate; + +static double frame_frequency; +static UINT64 frame_epoch; +static double elapsed_frames; + +static void I_InitFrameTime(const UINT64 now, const UINT32 cap) +{ + frame_rate = cap; + frame_epoch = now; + + //elapsed_frames = 0.0; + + if (frame_rate == 0) + { + // Shouldn't be used, but just in case...? + frame_frequency = 1.0; + return; + } + + frame_frequency = timer_frequency / (double)frame_rate; +} + +double I_GetFrameTime(void) +{ + const UINT64 now = SDL_GetPerformanceCounter(); + const UINT32 cap = R_GetFramerateCap(); + + if (cap != frame_rate) + { + // Maybe do this in a OnChange function for cv_fpscap? + I_InitFrameTime(now, cap); + } + + if (frame_rate == 0) + { + // Always advance a frame. + elapsed_frames += 1.0; + } + else + { + elapsed_frames += (now - frame_epoch) / frame_frequency; + } + + frame_epoch = now; // moving epoch + return elapsed_frames; +} + +// +// I_StartupTimer +// +void I_StartupTimer(void) +{ + timer_frequency = SDL_GetPerformanceFrequency(); + + I_InitFrameTime(0, R_GetFramerateCap()); + elapsed_frames = 0.0; +} + +void I_Sleep(UINT32 ms) +{ + SDL_Delay(ms); +} + +#ifdef NEWSIGNALHANDLER +static void newsignalhandler_Warn(const char *pr) +{ + char text[128]; + + snprintf(text, sizeof text, + "Error while setting up signal reporting: %s: %s", + pr, + strerror(errno) + ); + + I_OutputMsg("%s\n", text); + + if (!M_CheckParm("-dedicated")) + SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, + "Startup error", + text, NULL); + + I_ShutdownConsole(); + exit(-1); +} + +static void I_Fork(void) +{ + int child; + int status; + int signum; + int c; + + child = fork(); + + switch (child) + { + case -1: + newsignalhandler_Warn("fork()"); + break; + case 0: + I_RegisterChildSignals(); + break; + default: + if (logstream) + fclose(logstream);/* the child has this */ + + c = wait(&status); + +#ifdef LOGMESSAGES + /* By the way, exit closes files. */ + logstream = fopen(logfilename, "at"); +#else + logstream = 0; +#endif + + if (c == -1) + { + kill(child, SIGKILL); + newsignalhandler_Warn("wait()"); + } + else + { + if (WIFSIGNALED (status)) + { + signum = WTERMSIG (status); +#ifdef WCOREDUMP + I_ReportSignal(signum, WCOREDUMP (status)); +#else + I_ReportSignal(signum, 0); +#endif + status = 128 + signum; + } + else if (WIFEXITED (status)) + { + status = WEXITSTATUS (status); + } + + I_ShutdownConsole(); + exit(status); + } + } +} +#endif/*NEWSIGNALHANDLER*/ + +INT32 I_StartupSystem(void) +{ + SDL_version SDLcompiled; + SDL_version SDLlinked; + SDL_VERSION(&SDLcompiled) + SDL_GetVersion(&SDLlinked); +#ifdef HAVE_THREADS + I_start_threads(); + I_AddExitFunc(I_stop_threads); + I_ThreadPoolInit(); + I_AddExitFunc(I_ThreadPoolShutdown); +#endif + I_StartupConsole(); +#ifdef NEWSIGNALHANDLER + // This is useful when debugging. It lets GDB attach to + // the correct process easily. + if (!M_CheckParm("-nofork")) + I_Fork(); +#endif + I_RegisterSignals(); + I_OutputMsg("Compiled for SDL version: %d.%d.%d\n", + SDLcompiled.major, SDLcompiled.minor, SDLcompiled.patch); + I_OutputMsg("Linked with SDL version: %d.%d.%d\n", + SDLlinked.major, SDLlinked.minor, SDLlinked.patch); + if (SDL_Init(0) < 0) + I_Error("SRB2Kart: SDL System Error: %s", SDL_GetError()); //Alam: Oh no.... +#ifndef NOMUMBLE + I_SetupMumble(); +#endif + return 0; +} + +// +// I_Quit +// +void I_Quit(void) +{ + static SDL_bool quiting = SDL_FALSE; + + /* prevent recursive I_Quit() */ + if (quiting) goto death; + SDLforceUngrabMouse(); + quiting = SDL_FALSE; + M_SaveConfig(NULL); //save game config, cvars.. +#ifndef NONET + D_SaveBan(); // save the ban list +#endif + + // Make sure you lose points for ALT-F4 + if (Playing()) + K_PlayerForfeit(consoleplayer, true); + + G_SaveGameData(); // Tails 12-08-2002 + //added:16-02-98: when recording a demo, should exit using 'q' key, + // but sometimes we forget and use 'F10'.. so save here too. + + if (demo.recording) + G_CheckDemoStatus(); + if (metalrecording) + G_StopMetalRecording(false); + + D_QuitNetGame(); + CL_AbortDownloadResume(); + M_FreePlayerSetupColors(); + I_ShutdownMusic(); + I_ShutdownSound(); + // use this for 1.28 19990220 by Kin + I_ShutdownGraphics(); + I_ShutdownInput(); + I_ShutdownSystem(); + SDL_Quit(); + /* if option -noendtxt is set, don't print the text */ + if (!M_CheckParm("-noendtxt") && W_CheckNumForName("ENDOOM") != LUMPERROR) + { + printf("\r"); + ShowEndTxt(); + } + if (myargmalloc) + free(myargv); // Deallocate allocated memory +death: + W_Shutdown(); + exit(0); +} + +void I_WaitVBL(INT32 count) +{ + count = 1; + SDL_Delay(count); +} + +void I_BeginRead(void) +{ +} + +void I_EndRead(void) +{ +} + +// +// I_Error +// +/** \brief phuck recursive errors +*/ +static INT32 errorcount = 0; + +/** \brief recursive error detecting +*/ +static boolean shutdowning = false; + +void I_Error(const char *error, ...) +{ + va_list argptr; + char buffer[8192]; + + if (std::this_thread::get_id() != g_main_thread_id) + { + // Do not attempt a graceful shutdown. Errors off the main thread are unresolvable. + exit(-2); + } + + // recursive error detecting + if (shutdowning) + { + errorcount++; + if (errorcount == 1) + SDLforceUngrabMouse(); + // try to shutdown each subsystem separately + if (errorcount == 2) + I_ShutdownMusic(); + if (errorcount == 3) + I_ShutdownSound(); + if (errorcount == 4) + I_ShutdownGraphics(); + if (errorcount == 5) + I_ShutdownInput(); + if (errorcount == 6) + I_ShutdownSystem(); + if (errorcount == 7) + SDL_Quit(); + if (errorcount == 8) + { + M_SaveConfig(NULL); + G_SaveGameData(); + } + if (errorcount > 20) + { + va_start(argptr, error); + vsprintf(buffer, error, argptr); + va_end(argptr); + // Implement message box with SDL_ShowSimpleMessageBox, + // which should fail gracefully if it can't put a message box up + // on the target system + if (!M_CheckParm("-dedicated")) + SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, + "SRB2Kart V2 " VERSIONSTRING " Recursive Error", + buffer, NULL); + + W_Shutdown(); + exit(-1); // recursive errors detected + } + } + else + { + // This makes crashes funnier by stimulating the funnicampus of the brain + S_StopSounds(); + S_StartSound(NULL, sfx_s3k35); + } + + shutdowning = true; + + // Display error message in the console before we start shutting it down + va_start(argptr, error); + vsprintf(buffer, error, argptr); + va_end(argptr); + I_OutputMsg("\nI_Error(): %s\n", buffer); + // --- + + M_SaveConfig(NULL); // save game config, cvars.. +#ifndef NONET + D_SaveBan(); // save the ban list +#endif + G_SaveGameData(); // Tails 12-08-2002 + + // Shutdown. Here might be other errors. + + if (demo.recording) + G_CheckDemoStatus(); + if (metalrecording) + G_StopMetalRecording(false); + + D_QuitNetGame(); + CL_AbortDownloadResume(); + M_FreePlayerSetupColors(); + + I_ShutdownMusic(); + I_ShutdownGraphics(); + I_ShutdownInput(); + + I_ShowErrorMessageBox(buffer, false); + + // We wait until now to do this so the funny sound can be heard + I_ShutdownSound(); + // use this for 1.28 19990220 by Kin + I_ShutdownSystem(); + SDL_Quit(); + + W_Shutdown(); + +#if defined (PARANOIA) && defined (__CYGWIN__) + *(INT32 *)2 = 4; //Alam: Debug! +#endif + + exit(-1); +} + +/** \brief quit function table +*/ +static quitfuncptr quit_funcs[MAX_QUIT_FUNCS]; /* initialized to all bits 0 */ + +// +// Adds a function to the list that need to be called by I_SystemShutdown(). +// +void I_AddExitFunc(void (*func)()) +{ + INT32 c; + + for (c = 0; c < MAX_QUIT_FUNCS; c++) + { + if (!quit_funcs[c]) + { + quit_funcs[c] = func; + break; + } + } +} + + +// +// Removes a function from the list that need to be called by +// I_SystemShutdown(). +// +void I_RemoveExitFunc(void (*func)()) +{ + INT32 c; + + for (c = 0; c < MAX_QUIT_FUNCS; c++) + { + if (quit_funcs[c] == func) + { + while (c < MAX_QUIT_FUNCS-1) + { + quit_funcs[c] = quit_funcs[c+1]; + c++; + } + quit_funcs[MAX_QUIT_FUNCS-1] = NULL; + break; + } + } +} + +#if !(defined (__unix__) || defined(__APPLE__) || defined (UNIXCOMMON)) +static void Shittycopyerror(const char *name) +{ + I_OutputMsg( + "Error copying log file: %s: %s\n", + name, + strerror(errno) + ); +} + +static void Shittylogcopy(void) +{ + char buf[8192]; + FILE *fp; + size_t r; + if (fseek(logstream, 0, SEEK_SET) == -1) + { + Shittycopyerror("fseek"); + } + else if (( fp = fopen(logfilename, "wt") )) + { + while (( r = fread(buf, 1, sizeof buf, logstream) )) + { + if (fwrite(buf, 1, r, fp) < r) + { + Shittycopyerror("fwrite"); + break; + } + } + if (ferror(logstream)) + { + Shittycopyerror("fread"); + } + fclose(fp); + } + else + { + Shittycopyerror(logfilename); + } +} +#endif/*!(defined (__unix__) || defined(__APPLE__) || defined (UNIXCOMMON))*/ + +// +// Closes down everything. This includes restoring the initial +// palette and video mode, and removing whatever mouse, keyboard, and +// timer routines have been installed. +// +// NOTE: Shutdown user funcs are effectively called in reverse order. +// +void I_ShutdownSystem(void) +{ + INT32 c; + +#ifdef NEWSIGNALHANDLER + if (M_CheckParm("-nofork")) +#endif + I_ShutdownConsole(); + + for (c = MAX_QUIT_FUNCS-1; c >= 0; c--) + if (quit_funcs[c]) + (*quit_funcs[c])(); +#ifdef LOGMESSAGES + if (logstream) + { + I_OutputMsg("I_ShutdownSystem(): end of logstream.\n"); +#if !(defined (__unix__) || defined(__APPLE__) || defined (UNIXCOMMON)) + Shittylogcopy(); +#endif + fclose(logstream); + logstream = NULL; + } +#endif + +} + +void I_GetDiskFreeSpace(INT64 *freespace) +{ +#if defined (__unix__) || defined(__APPLE__) || defined (UNIXCOMMON) +#if defined (SOLARIS) || defined (__HAIKU__) + *freespace = INT32_MAX; + return; +#else // Both Linux and BSD have this, apparently. + struct statfs stfs; + if (statfs(srb2home, &stfs) == -1) + { + *freespace = INT32_MAX; + return; + } + *freespace = stfs.f_bavail * stfs.f_bsize; +#endif +#elif defined (_WIN32) + static p_GetDiskFreeSpaceExA pfnGetDiskFreeSpaceEx = NULL; + static boolean testwin95 = false; + ULARGE_INTEGER usedbytes, lfreespace; + + if (!testwin95) + { + *(void**)&pfnGetDiskFreeSpaceEx = FUNCPTRCAST(GetProcAddress(GetModuleHandleA("kernel32.dll"), "GetDiskFreeSpaceExA")); + testwin95 = true; + } + if (pfnGetDiskFreeSpaceEx) + { + if (pfnGetDiskFreeSpaceEx(srb2home, &lfreespace, &usedbytes, NULL)) + *freespace = lfreespace.QuadPart; + else + *freespace = INT32_MAX; + } + else + { + DWORD SectorsPerCluster, BytesPerSector, NumberOfFreeClusters, TotalNumberOfClusters; + GetDiskFreeSpace(NULL, &SectorsPerCluster, &BytesPerSector, + &NumberOfFreeClusters, &TotalNumberOfClusters); + *freespace = BytesPerSector*SectorsPerCluster*NumberOfFreeClusters; + } +#else // Dummy for platform independent; 1GB should be enough + *freespace = 1024*1024*1024; +#endif +} + +char *I_GetUserName(void) +{ + static char username[MAXPLAYERNAME+1]; + char *p; +#ifdef _WIN32 + DWORD i = MAXPLAYERNAME; + + if (!GetUserNameA(username, &i)) +#endif + { + p = I_GetEnv("USER"); + if (!p) + { + p = I_GetEnv("user"); + if (!p) + { + p = I_GetEnv("USERNAME"); + if (!p) + { + p = I_GetEnv("username"); + if (!p) + { + return NULL; + } + } + } + } + strncpy(username, p, MAXPLAYERNAME); + } + + + if (strcmp(username, "") != 0) + return username; + return NULL; // dummy for platform independent version +} + +INT32 I_mkdir(const char *dirname, INT32 unixright) +{ +//[segabor] +#if defined (__unix__) || defined(__APPLE__) || defined (UNIXCOMMON) || defined (__CYGWIN__) + return mkdir(dirname, unixright); +#elif defined (_WIN32) + UNREFERENCED_PARAMETER(unixright); /// \todo should implement ntright under nt... + return CreateDirectoryA(dirname, NULL); +#else + (void)dirname; + (void)unixright; + return false; +#endif +} + +char *I_GetEnv(const char *name) +{ +#ifdef NEED_SDL_GETENV + return SDL_getenv(name); +#else + return getenv(name); +#endif +} + +INT32 I_PutEnv(char *variable) +{ +#ifdef NEED_SDL_GETENV + return SDL_putenv(variable); +#else + return putenv(variable); +#endif +} + +INT32 I_ClipboardCopy(const char *data, size_t size) +{ + char storage[256]; + if (size > 255) + size = 255; + memcpy(storage, data, size); + storage[size] = 0; + + if (SDL_SetClipboardText(storage)) + return 0; + return -1; +} + +const char *I_ClipboardPaste(void) +{ + static char clipboard_modified[256]; + char *clipboard_contents, *i = clipboard_modified; + + if (!SDL_HasClipboardText()) + return NULL; + + clipboard_contents = SDL_GetClipboardText(); + strlcpy(clipboard_modified, clipboard_contents, 256); + SDL_free(clipboard_contents); + + while (*i) + { + if (*i == '\n' || *i == '\r') + { // End on newline + *i = 0; + break; + } + else if (*i == '\t') + *i = ' '; // Tabs become spaces + else if (*i < 32 || (unsigned)*i > 127) + *i = '?'; // Nonprintable chars become question marks + ++i; + } + return (const char *)&clipboard_modified; +} + +/** \brief The isWadPathOk function + + \param path string path to check + + \return if true, wad file found + + +*/ +static boolean isWadPathOk(const char *path) +{ + char *wad3path = static_cast(malloc(256)); + + if (!wad3path) + return false; + + sprintf(wad3path, pandf, path, WADKEYWORD); + + if (FIL_ReadFileOK(wad3path)) + { + free(wad3path); + return true; + } + + free(wad3path); + return false; +} + +static void pathonly(char *s) +{ + size_t j; + + for (j = strlen(s); j != (size_t)-1; j--) + if ((s[j] == '\\') || (s[j] == ':') || (s[j] == '/')) + { + if (s[j] == ':') s[j+1] = 0; + else s[j] = 0; + return; + } +} + +/** \brief search for main.pk3 in the given path + + \param searchDir starting path + + \return WAD path if not NULL + + +*/ +static const char *searchWad(const char *searchDir) +{ + static char tempsw[256] = ""; + filestatus_t fstemp; + + strcpy(tempsw, WADKEYWORD); + fstemp = filesearch(tempsw,searchDir,NULL,true,20); + if (fstemp == FS_FOUND) + { + pathonly(tempsw); + return tempsw; + } + + return NULL; +} + +/** \brief go through all possible paths and look for main.pk3 + + \return path to main.pk3 if any + +*/ +static const char *locateWad(void) +{ + const char *envstr; + const char *WadPath; + + I_OutputMsg("SRB2WADDIR"); + // does SRB2WADDIR exist? + if (((envstr = I_GetEnv("SRB2WADDIR")) != NULL) && isWadPathOk(envstr)) + return envstr; + +#ifndef NOCWD + I_OutputMsg(",."); + // examine current dir + strcpy(returnWadPath, "."); + if (isWadPathOk(returnWadPath)) + return NULL; +#endif + + +#ifdef DEFAULTDIR + I_OutputMsg(",HOME/" DEFAULTDIR); + // examine user jart directory + if ((envstr = I_GetEnv("HOME")) != NULL) + { + sprintf(returnWadPath, "%s" PATHSEP DEFAULTDIR, envstr); + if (isWadPathOk(returnWadPath)) + return returnWadPath; + } +#endif + +#ifdef __APPLE__ + OSX_GetResourcesPath(returnWadPath); + I_OutputMsg(",%s", returnWadPath); + if (isWadPathOk(returnWadPath)) + { + return returnWadPath; + } +#endif + + // examine default dirs +#ifdef DEFAULTWADLOCATION1 + I_OutputMsg("," DEFAULTWADLOCATION1); + strcpy(returnWadPath, DEFAULTWADLOCATION1); + if (isWadPathOk(returnWadPath)) + return returnWadPath; +#endif +#ifdef DEFAULTWADLOCATION2 + I_OutputMsg("," DEFAULTWADLOCATION2); + strcpy(returnWadPath, DEFAULTWADLOCATION2); + if (isWadPathOk(returnWadPath)) + return returnWadPath; +#endif +#ifdef DEFAULTWADLOCATION3 + I_OutputMsg("," DEFAULTWADLOCATION3); + strcpy(returnWadPath, DEFAULTWADLOCATION3); + if (isWadPathOk(returnWadPath)) + return returnWadPath; +#endif +#ifdef DEFAULTWADLOCATION4 + I_OutputMsg("," DEFAULTWADLOCATION4); + strcpy(returnWadPath, DEFAULTWADLOCATION4); + if (isWadPathOk(returnWadPath)) + return returnWadPath; +#endif +#ifdef DEFAULTWADLOCATION5 + I_OutputMsg("," DEFAULTWADLOCATION5); + strcpy(returnWadPath, DEFAULTWADLOCATION5); + if (isWadPathOk(returnWadPath)) + return returnWadPath; +#endif +#ifdef DEFAULTWADLOCATION6 + I_OutputMsg("," DEFAULTWADLOCATION6); + strcpy(returnWadPath, DEFAULTWADLOCATION6); + if (isWadPathOk(returnWadPath)) + return returnWadPath; +#endif +#ifdef DEFAULTWADLOCATION7 + I_OutputMsg("," DEFAULTWADLOCATION7); + strcpy(returnWadPath, DEFAULTWADLOCATION7); + if (isWadPathOk(returnWadPath)) + return returnWadPath; +#endif +#ifndef NOHOME + // find in $HOME + I_OutputMsg(",HOME"); + if ((envstr = I_GetEnv("HOME")) != NULL) + { + WadPath = searchWad(envstr); + if (WadPath) + return WadPath; + } +#endif +#ifdef DEFAULTSEARCHPATH1 + // find in /usr/local + I_OutputMsg(", in:" DEFAULTSEARCHPATH1); + WadPath = searchWad(DEFAULTSEARCHPATH1); + if (WadPath) + return WadPath; +#endif +#ifdef DEFAULTSEARCHPATH2 + // find in /usr/games + I_OutputMsg(", in:" DEFAULTSEARCHPATH2); + WadPath = searchWad(DEFAULTSEARCHPATH2); + if (WadPath) + return WadPath; +#endif +#ifdef DEFAULTSEARCHPATH3 + // find in ??? + I_OutputMsg(", in:" DEFAULTSEARCHPATH3); + WadPath = searchWad(DEFAULTSEARCHPATH3); + if (WadPath) + return WadPath; +#endif + // if nothing was found + return NULL; +} + +const char *I_LocateWad(void) +{ + const char *waddir; + + I_OutputMsg("Looking for WADs in: "); + waddir = locateWad(); + I_OutputMsg("\n"); + + if (waddir) + { + // change to the directory where we found main.pk3 +#if defined (_WIN32) + SetCurrentDirectoryA(waddir); +#else + if (chdir(waddir) == -1) + I_OutputMsg("Couldn't change working directory\n"); +#endif + } + return waddir; +} + +#ifdef __linux__ +#define MEMINFO_FILE "/proc/meminfo" +#define MEMTOTAL "MemTotal:" +#define MEMAVAILABLE "MemAvailable:" +#define MEMFREE "MemFree:" +#define CACHED "Cached:" +#define BUFFERS "Buffers:" +#define SHMEM "Shmem:" + +/* Parse the contents of /proc/meminfo (in buf), return value of "name" + * (example: MemTotal) */ +static long get_entry(const char* name, const char* buf) +{ + long val; + const char* hit = strstr(buf, name); + if (hit == NULL) { + return -1; + } + + errno = 0; + val = strtol(hit + strlen(name), NULL, 10); + if (errno != 0) { + CONS_Alert(CONS_ERROR, M_GetText("get_entry: strtol() failed: %s\n"), strerror(errno)); + return -1; + } + return val; +} +#endif + +// quick fix for compil +UINT32 I_GetFreeMem(UINT32 *total) +{ +#ifdef FREEBSD + struct vmmeter sum; + kvm_t *kd; + struct nlist namelist[] = + { +#define X_SUM 0 + {"_cnt"}, + {NULL} + }; + if ((kd = kvm_open(NULL, NULL, NULL, O_RDONLY, "kvm_open")) == NULL) + { + if (total) + *total = 0L; + return 0; + } + if (kvm_nlist(kd, namelist) != 0) + { + kvm_close (kd); + if (total) + *total = 0L; + return 0; + } + if (kvm_read(kd, namelist[X_SUM].n_value, &sum, + sizeof (sum)) != sizeof (sum)) + { + kvm_close(kd); + if (total) + *total = 0L; + return 0; + } + kvm_close(kd); + + if (total) + *total = sum.v_page_count * sum.v_page_size; + return sum.v_free_count * sum.v_page_size; +#elif defined (SOLARIS) + /* Just guess */ + if (total) + *total = 32 << 20; + return 32 << 20; +#elif defined (_WIN32) + MEMORYSTATUS info; + + info.dwLength = sizeof (MEMORYSTATUS); + GlobalMemoryStatus( &info ); + if (total) + *total = (UINT32)info.dwTotalPhys; + return (UINT32)info.dwAvailPhys; +#elif defined (__linux__) + /* Linux */ + char buf[1024]; + char *memTag; + UINT32 freeKBytes; + UINT32 totalKBytes; + INT32 n; + INT32 meminfo_fd = -1; + long Cached; + long MemFree; + long Buffers; + long Shmem; + long MemAvailable = -1; + + meminfo_fd = open(MEMINFO_FILE, O_RDONLY); + n = read(meminfo_fd, buf, 1023); + close(meminfo_fd); + + if (n < 0) + { + // Error + if (total) + *total = 0L; + return 0; + } + + buf[n] = '\0'; + if ((memTag = strstr(buf, MEMTOTAL)) == NULL) + { + // Error + if (total) + *total = 0L; + return 0; + } + + memTag += sizeof (MEMTOTAL); + totalKBytes = atoi(memTag); + + if ((memTag = strstr(buf, MEMAVAILABLE)) == NULL) + { + Cached = get_entry(CACHED, buf); + MemFree = get_entry(MEMFREE, buf); + Buffers = get_entry(BUFFERS, buf); + Shmem = get_entry(SHMEM, buf); + MemAvailable = Cached + MemFree + Buffers - Shmem; + + if (MemAvailable == -1) + { + // Error + if (total) + *total = 0L; + return 0; + } + freeKBytes = MemAvailable; + } + else + { + memTag += sizeof (MEMAVAILABLE); + freeKBytes = atoi(memTag); + } + + if (total) + *total = totalKBytes << 10; + return freeKBytes << 10; +#else + // Guess 48 MB. + if (total) + *total = 48<<20; + return 48<<20; +#endif +} + +const CPUInfoFlags *I_CPUInfo(void) +{ +#if defined (_WIN32) + static CPUInfoFlags WIN_CPUInfo; + SYSTEM_INFO SI; + p_IsProcessorFeaturePresent pfnCPUID; + *(void**)&pfnCPUID = FUNCPTRCAST(GetProcAddress(GetModuleHandleA("kernel32.dll"), "IsProcessorFeaturePresent")); + + ZeroMemory(&WIN_CPUInfo,sizeof (WIN_CPUInfo)); + if (pfnCPUID) + { + WIN_CPUInfo.FPPE = pfnCPUID( 0); //PF_FLOATING_POINT_PRECISION_ERRATA + WIN_CPUInfo.FPE = pfnCPUID( 1); //PF_FLOATING_POINT_EMULATED + WIN_CPUInfo.cmpxchg = pfnCPUID( 2); //PF_COMPARE_EXCHANGE_DOUBLE + WIN_CPUInfo.MMX = pfnCPUID( 3); //PF_MMX_INSTRUCTIONS_AVAILABLE + WIN_CPUInfo.PPCMM64 = pfnCPUID( 4); //PF_PPC_MOVEMEM_64BIT_OK + WIN_CPUInfo.ALPHAbyte = pfnCPUID( 5); //PF_ALPHA_BYTE_INSTRUCTIONS + WIN_CPUInfo.SSE = pfnCPUID( 6); //PF_XMMI_INSTRUCTIONS_AVAILABLE + WIN_CPUInfo.AMD3DNow = pfnCPUID( 7); //PF_3DNOW_INSTRUCTIONS_AVAILABLE + WIN_CPUInfo.RDTSC = pfnCPUID( 8); //PF_RDTSC_INSTRUCTION_AVAILABLE + WIN_CPUInfo.PAE = pfnCPUID( 9); //PF_PAE_ENABLED + WIN_CPUInfo.SSE2 = pfnCPUID(10); //PF_XMMI64_INSTRUCTIONS_AVAILABLE + //WIN_CPUInfo.blank = pfnCPUID(11); //PF_SSE_DAZ_MODE_AVAILABLE + WIN_CPUInfo.DEP = pfnCPUID(12); //PF_NX_ENABLED + WIN_CPUInfo.SSE3 = pfnCPUID(13); //PF_SSE3_INSTRUCTIONS_AVAILABLE + WIN_CPUInfo.cmpxchg16b = pfnCPUID(14); //PF_COMPARE_EXCHANGE128 + WIN_CPUInfo.cmp8xchg16 = pfnCPUID(15); //PF_COMPARE64_EXCHANGE128 + WIN_CPUInfo.PFC = pfnCPUID(16); //PF_CHANNELS_ENABLED + } +#ifdef HAVE_SDLCPUINFO + else + { + WIN_CPUInfo.RDTSC = SDL_HasRDTSC(); + WIN_CPUInfo.MMX = SDL_HasMMX(); + WIN_CPUInfo.AMD3DNow = SDL_Has3DNow(); + WIN_CPUInfo.SSE = SDL_HasSSE(); + WIN_CPUInfo.SSE2 = SDL_HasSSE2(); + WIN_CPUInfo.AltiVec = SDL_HasAltiVec(); + } + WIN_CPUInfo.MMXExt = SDL_FALSE; //SDL_HasMMXExt(); No longer in SDL2 + WIN_CPUInfo.AMD3DNowExt = SDL_FALSE; //SDL_Has3DNowExt(); No longer in SDL2 +#endif + GetSystemInfo(&SI); + WIN_CPUInfo.CPUs = SI.dwNumberOfProcessors; + WIN_CPUInfo.IA64 = (SI.dwProcessorType == 2200); // PROCESSOR_INTEL_IA64 + WIN_CPUInfo.AMD64 = (SI.dwProcessorType == 8664); // PROCESSOR_AMD_X8664 + return &WIN_CPUInfo; +#elif defined (HAVE_SDLCPUINFO) + static CPUInfoFlags SDL_CPUInfo; + memset(&SDL_CPUInfo,0,sizeof (CPUInfoFlags)); + SDL_CPUInfo.RDTSC = SDL_HasRDTSC(); + SDL_CPUInfo.MMX = SDL_HasMMX(); + SDL_CPUInfo.MMXExt = SDL_FALSE; //SDL_HasMMXExt(); No longer in SDL2 + SDL_CPUInfo.AMD3DNow = SDL_Has3DNow(); + SDL_CPUInfo.AMD3DNowExt = SDL_FALSE; //SDL_Has3DNowExt(); No longer in SDL2 + SDL_CPUInfo.SSE = SDL_HasSSE(); + SDL_CPUInfo.SSE2 = SDL_HasSSE2(); + SDL_CPUInfo.AltiVec = SDL_HasAltiVec(); + return &SDL_CPUInfo; +#else + return NULL; /// \todo CPUID asm +#endif +} + +// note CPUAFFINITY code used to reside here +void I_RegisterSysCommands(void) {} + +#endif // HAVE_SDL