// 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 _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 "SDL.h" #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 bios.pk3 #if defined (__unix__) || defined(__APPLE__) || defined (UNIXCOMMON) #define DEFAULTWADLOCATION1 "/usr/local/share/games/RingRacers" #define DEFAULTWADLOCATION2 "/usr/local/games/RingRacers" #define DEFAULTWADLOCATION3 "/usr/share/games/RingRacers" #define DEFAULTWADLOCATION4 "/usr/games/RingRacers" #define DEFAULTSEARCHPATH1 "/usr/local/games" #define DEFAULTSEARCHPATH2 "/usr/games" #define DEFAULTSEARCHPATH3 "/usr/local" #endif /** \brief WAD file to look for */ #define WADKEYWORD "bios.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 "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" #include "../k_menu.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 /** \brief SDL info about joysticks */ SDLJoyInfo_t JoyInfo[MAXSPLITSCREENPLAYERS]; SDL_bool consolevent = SDL_FALSE; SDL_bool framebuffer = SDL_FALSE; UINT8 keyboard_started = 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 Dr. Robotnik's Ring Racers! Send this stack trace to someone who can help!\n"; const char *error2 = "(Or find crash-log.txt in your Ring Racers 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), "Hee Ho!\n" "\n" "\"Dr. Robotnik's Ring Racers\" has encountered an unrecoverable error and needs to close.\n" "This is (usually) not your fault, but we encourage you to report it in the community. This should be done alongside your " "%s" "log file (%s).\n" "\n" "The following information is for a programmer (please be nice to them!) but\n" "may also be useful for server hosts and add-on creators.\n" "\n" "%s", dumpmade ? #if defined (UNIXBACKTRACE) "crash-log.txt" #elif defined (_WIN32) ".rpt crash dump" #endif " (very important!) and " : "", #ifdef LOGMESSAGES logfilename[0] ? logfilename : #endif "uh oh, one wasn't made!?", messagefordevelopers); // Rudementary word wrapping. // Simple and effective. Does not handle nonuniform letter sizes, etc. but who cares. // We can't use V_ScaledWordWrap, which this shares DNA with, because no guarantee // string character graphics exist as reference in the error handler... { 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 SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR, "Dr. Robotnik's Ring Racers "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) sprintf(msg, "%s (core dumped)", sigmsg); else 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) { D_QuitNetGame(); // Fix server freezes CL_AbortDownloadResume(); G_DirtyGameData(); #ifdef UNIXBACKTRACE write_backtrace(num); #endif I_ReportSignal(num, 0); I_ShutdownSystem(); signal(num, SIG_DFL); //default signal action raise(num); I_Quit(); } #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 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("Dr. Robotnik's Ring Racers 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) { #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) { G_DirtyGameData(); #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, 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; G_ResetAllDeviceResponding(); // return the first keypress from the event queue for (; eventtail != eventhead; eventtail = (eventtail+1)&(MAXEVENTS-1)) { ev = &events[eventtail]; HandleGamepadDeviceEvents(ev); 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; } void I_SetGamepadPlayerIndex(INT32 device_id, INT32 player) { #if !(SDL_VERSION_ATLEAST(2,0,12)) (void)device_id; (void)player; #else I_Assert(device_id > 0); // Gamepad devices are always ID 1 or higher I_Assert(player >= 0 && player < MAXSPLITSCREENPLAYERS); SDL_GameController *controller = SDL_GameControllerFromInstanceID(device_id - 1); if (controller == NULL) { return; } SDL_GameControllerSetPlayerIndex(controller, player); #endif } void I_SetGamepadIndicatorColor(INT32 device_id, UINT8 red, UINT8 green, UINT8 blue) { #if !(SDL_VERSION_ATLEAST(2,0,14)) (void)device_id; (void)player; #else I_Assert(device_id > 0); // Gamepad devices are always ID 1 or higher SDL_GameController *controller = SDL_GameControllerFromInstanceID(device_id - 1); if (controller == NULL) { return; } SDL_GameControllerSetLED(controller, red, green, blue); #endif } void I_GetGamepadGuid(INT32 device_id, char *out, int out_len) { SDL_GameController *controller; SDL_Joystick *joystick; SDL_JoystickGUID guid; I_Assert(device_id > 0); I_Assert(out != NULL); I_Assert(out_len > 0); if (out_len < 33) { out[0] = 0; return; } controller = SDL_GameControllerFromInstanceID(device_id - 1); if (controller == NULL) { out[0] = 0; return; } joystick = SDL_GameControllerGetJoystick(controller); if (joystick == NULL) { out[0] = 0; return; } guid = SDL_JoystickGetGUID(joystick); SDL_JoystickGetGUIDString(guid, out, out_len); } void I_GetGamepadName(INT32 device_id, char *out, int out_len) { SDL_GameController *controller; const char *name; int name_len; I_Assert(device_id > 0); I_Assert(out != NULL); I_Assert(out_len > 0); controller = SDL_GameControllerFromInstanceID(device_id - 1); if (controller == NULL) { out[0] = 0; return; } name = SDL_GameControllerName(controller); name_len = strlen(name) + 1; memcpy(out, name, out_len < name_len ? out_len : name_len); out[out_len - 1] = 0; } void I_GamepadRumble(INT32 device_id, UINT16 low_strength, UINT16 high_strength) { #if !(SDL_VERSION_ATLEAST(2,0,9)) (void)device_id; (void)low_strength; (void)high_strength; (void)length_ms; #else I_Assert(device_id > 0); // Gamepad devices are always ID 1 or higher SDL_GameController *controller = SDL_GameControllerFromInstanceID(device_id - 1); if (controller == NULL) { return; } SDL_GameControllerRumble(controller, low_strength, high_strength, 0); #endif } void I_GamepadRumbleTriggers(INT32 device_id, UINT16 left_strength, UINT16 right_strength) { #if !(SDL_VERSION_ATLEAST(2,0,14)) (void)device_id; (void)low_strength; (void)high_strength; (void)length_ms; #else I_Assert(device_id > 0); // Gamepad devices are always ID 1 or higher SDL_GameController *controller = SDL_GameControllerFromInstanceID(device_id - 1); if (controller == NULL) { return; } SDL_GameControllerRumbleTriggers(controller, left_strength, right_strength, 0); #endif } // // I_StartupInput // void I_StartupInput(void) { if (M_CheckParm("-nojoy")) return; { char dbpath[1024]; sprintf(dbpath, "%s" PATHSEP "gamecontrollerdb.txt", srb2path); SDL_GameControllerAddMappingsFromFile(dbpath); } { char dbpath[1024]; sprintf(dbpath, "%s" PATHSEP "gamecontrollerdb_user.txt", srb2home); SDL_GameControllerAddMappingsFromFile(dbpath); } if (SDL_WasInit(SDL_INIT_GAMECONTROLLER)) { 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); CONS_Printf("I_StartupInput()...\n"); if (SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) == -1) { CONS_Printf(M_GetText("Couldn't initialize game controllers: %s\n"), SDL_GetError()); return; } // Upon initialization, the gamecontroller subsystem will automatically dispatch controller device added events // for controllers connected before initialization. } static void I_ShutdownInput(void) { // The game code is now responsible for resetting its internal state based on ev_gamepad_device_removed events. // In practice, Input should never be shutdown and restarted during runtime. if (SDL_WasInit(SDL_INIT_GAMECONTROLLER) == SDL_INIT_GAMECONTROLLER) { CONS_Printf("Shutting down gamecontroller system\n"); SDL_QuitSubSystem(SDL_INIT_GAMECONTROLLER); I_OutputMsg("I_Joystick: SDL's Game Controller system has been shutdown\n"); } 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; SDL_Joystick* joystick; joyname[0] = 0; joyindex--; //SDL's Joystick System starts at 0, not 1 if (SDL_WasInit(SDL_INIT_JOYSTICK) != SDL_INIT_JOYSTICK) { return joyname; } // joyindex corresponds to the open joystick *instance* ID, not the joystick number joystick = SDL_JoystickFromInstanceID(joyindex); if (joystick == NULL) { return joyname; } tempname = SDL_JoystickNameForIndex(joyindex); if (tempname) { strncpy(joyname, tempname, 255); } 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 { #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 = 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 = 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"Dr. Robotnik's Ring Racers "VERSIONSTRINGW, 256); wcsncpy(mumble->description, L"Dr. Robotnik's Ring Racers 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; } // // 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); #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("Dr. Robotnik's Ring Racers: 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.. D_SaveBan(); // save the ban list M_SaveJoinedIPs(); // 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); #ifdef DEVELOP // Join up with thread if waiting R_PrintTextureDuplicates(); #endif D_QuitNetGame(); CL_AbortDownloadResume(); 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]; // 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_DirtyGameData(); // done first in case an error is in G_SaveGameData 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, "Dr. Robotnik's Ring Racers "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_etexpl); } 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.. D_SaveBan(); // save the ban list G_DirtyGameData(); // done first in case an error is in G_SaveGameData G_SaveGameData(); // Tails 12-08-2002 // Shutdown. Here might be other errors. /* Prevent segmentation fault if testers go to Record Attack... */ #ifndef TESTERS if (demo.recording) G_CheckDemoStatus(); if (metalrecording) G_StopMetalRecording(false); #endif D_QuitNetGame(); CL_AbortDownloadResume(); 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 (DEVELOP) *(INT32 *)0 = 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 = 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 bios.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 bios.pk3 \return path to bios.pk3 if any */ static const char *locateWad(void) { const char *envstr; const char *WadPath; I_OutputMsg("RINGRACERSWADDIR"); // does RINGRACERSWADDIR exist? if (((envstr = I_GetEnv("RINGRACERSWADDIR")) != 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 bios.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; 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