mirror of
				https://github.com/KartKrewDev/RingRacers.git
				synced 2025-10-30 08:01:28 +00:00 
			
		
		
		
	 98016ef390
			
		
	
	
		98016ef390
		
	
	
	
	
		
			
			Replaced most drrr (mostly only used in system / exe stuff) with the more verbose "ringracers"
		
			
				
	
	
		
			2757 lines
		
	
	
	
		
			62 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			2757 lines
		
	
	
	
		
			62 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // 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 <graue@oceanbase.org> are in the public domain.
 | |
| //
 | |
| //-----------------------------------------------------------------------------
 | |
| /// \file
 | |
| /// \brief SRB2 system stuff for SDL
 | |
| 
 | |
| #ifdef CMAKECONFIG
 | |
| #include "config.h"
 | |
| #else
 | |
| #include "../config.h.in"
 | |
| #endif
 | |
| 
 | |
| #include <signal.h>
 | |
| 
 | |
| #ifdef _WIN32
 | |
| #define RPC_NO_WINDOWS_H
 | |
| #include <windows.h>
 | |
| #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 <stdio.h>
 | |
| #include <stdlib.h>
 | |
| #include <string.h>
 | |
| #ifdef __GNUC__
 | |
| #include <unistd.h>
 | |
| #elif defined (_MSC_VER)
 | |
| #include <direct.h>
 | |
| #endif
 | |
| #if defined (__unix__) || defined (UNIXCOMMON)
 | |
| #include <fcntl.h>
 | |
| #endif
 | |
| 
 | |
| #include <stdio.h>
 | |
| #ifdef _WIN32
 | |
| #include <conio.h>
 | |
| #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 <sys/vfs.h>
 | |
| #else
 | |
| #include <sys/param.h>
 | |
| #include <sys/mount.h>
 | |
| /*For meminfo*/
 | |
| #include <sys/types.h>
 | |
| #ifdef FREEBSD
 | |
| #include <kvm.h>
 | |
| #endif
 | |
| #include <nlist.h>
 | |
| #include <sys/vmmeter.h>
 | |
| #endif
 | |
| #endif
 | |
| 
 | |
| #if defined (__linux__) || (defined (UNIXCOMMON) && !defined (__HAIKU__))
 | |
| #ifndef NOTERMIOS
 | |
| #include <termios.h>
 | |
| #include <sys/ioctl.h> // ioctl
 | |
| #define HAVE_TERMIOS
 | |
| #endif
 | |
| #endif
 | |
| 
 | |
| #if (defined (__unix__) && !defined (_MSDOS)) || (defined (UNIXCOMMON) && !defined(__APPLE__))
 | |
| #include <errno.h>
 | |
| #include <sys/wait.h>
 | |
| #define NEWSIGNALHANDLER
 | |
| #endif
 | |
| 
 | |
| #ifndef NOMUMBLE
 | |
| #ifdef __linux__ // need -lrt
 | |
| #include <sys/mman.h>
 | |
| #ifdef MAP_FAILED
 | |
| #define HAVE_SHM
 | |
| #endif
 | |
| #include <wchar.h>
 | |
| #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 <errno.h>
 | |
| #endif
 | |
| 
 | |
| #if defined (__unix__) || defined(__APPLE__) || defined (UNIXCOMMON)
 | |
| #include <execinfo.h>
 | |
| #include <time.h>
 | |
| #define UNIXBACKTRACE
 | |
| #endif
 | |
| 
 | |
| // Locations for searching for main.kart
 | |
| #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 "main.kart"
 | |
| /**	\brief holds wad path
 | |
| */
 | |
| static char returnWadPath[256];
 | |
| 
 | |
| //Alam_GBC: SDL
 | |
| 
 | |
| #include "../doomdef.h"
 | |
| #include "../m_misc.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"
 | |
| 
 | |
| #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	The JoyReset function
 | |
| 
 | |
| 	\param	JoySet	Joystick info to reset
 | |
| 
 | |
| 	\return	void
 | |
| */
 | |
| static void JoyReset(SDLJoyInfo_t *JoySet)
 | |
| {
 | |
| 	if (JoySet->dev)
 | |
| 	{
 | |
| 		SDL_JoystickClose(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;
 | |
| 
 | |
| #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_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);
 | |
| 
 | |
| 	if (!M_CheckParm("-dedicated"))
 | |
| 		SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR,
 | |
| 			"Process killed by signal",
 | |
| 			sigmsg, NULL);
 | |
| }
 | |
| 
 | |
| #ifndef NEWSIGNALHANDLER
 | |
| FUNCNORETURN static ATTRNORETURN void signal_handler(INT32 num)
 | |
| {
 | |
| 	D_QuitNetGame(); // Fix server freezes
 | |
| 	CL_AbortDownloadResume();
 | |
| #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; i<tty_con.cursor; i++)
 | |
| 		{
 | |
| 			tty_Back();
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| }
 | |
| 
 | |
| // clear the display of the line currently edited
 | |
| // bring cursor back to beginning of line
 | |
| static inline void tty_Hide(void)
 | |
| {
 | |
| 	//I_Assert(consolevent);
 | |
| 	if (ttycon_hide)
 | |
| 	{
 | |
| 		ttycon_hide++;
 | |
| 		return;
 | |
| 	}
 | |
| 	tty_Clear();
 | |
| 	ttycon_hide++;
 | |
| }
 | |
| 
 | |
| // show the current line
 | |
| // FIXME TTimo need to position the cursor if needed??
 | |
| static inline void tty_Show(void)
 | |
| {
 | |
| 	size_t i;
 | |
| 	ssize_t d;
 | |
| 	//I_Assert(consolevent);
 | |
| 	I_Assert(ttycon_hide>0);
 | |
| 	ttycon_hide--;
 | |
| 	if (ttycon_hide == 0 && tty_con.cursor)
 | |
| 	{
 | |
| 		for (i=0; i<tty_con.cursor; i++)
 | |
| 		{
 | |
| 			d = write(STDOUT_FILENO, tty_con.buffer+i, 1);
 | |
| 		}
 | |
| 	}
 | |
| 	(void)d;
 | |
| }
 | |
| 
 | |
| // never exit without calling this, or your terminal will be left in a pretty bad state
 | |
| static void I_ShutdownConsole(void)
 | |
| {
 | |
| 	if (consolevent)
 | |
| 	{
 | |
| 		I_OutputMsg("Shutdown tty console\n");
 | |
| 		consolevent = SDL_FALSE;
 | |
| 		tcsetattr (STDIN_FILENO, TCSADRAIN, &tty_tc);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| static void I_StartupConsole(void)
 | |
| {
 | |
| 	struct termios tc;
 | |
| 
 | |
| 	// TTimo
 | |
| 	// https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=390 (404)
 | |
| 	// then SIGTTIN or SIGTOU is emitted, if not catched, turns into a SIGSTP
 | |
| 	signal(SIGTTIN, SIG_IGN);
 | |
| 	signal(SIGTTOU, SIG_IGN);
 | |
| 
 | |
| 	consolevent = !M_CheckParm("-noconsole");
 | |
| 	framebuffer = 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 = {0,0,0,0};
 | |
| 	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("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)
 | |
| {
 | |
| #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;
 | |
| 
 | |
| 	// 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_Joystick *dev)
 | |
| {
 | |
| 	INT32 i, count = SDL_NumJoysticks();
 | |
| 
 | |
| 	for (i = 0; dev && i < count; i++)
 | |
| 	{
 | |
| 		SDL_Joystick *test = SDL_JoystickOpen(i);
 | |
| 		if (test && test == dev)
 | |
| 			return i;
 | |
| 		else
 | |
| 		{
 | |
| 			UINT8 j;
 | |
| 
 | |
| 			for (j = 0; j < MAXSPLITSCREENPLAYERS; j++)
 | |
| 			{
 | |
| 				if (JoyInfo[j].dev == test)
 | |
| 					break;
 | |
| 			}
 | |
| 
 | |
| 			if (j == MAXSPLITSCREENPLAYERS)
 | |
| 				SDL_JoystickClose(test);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	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<JOYBUTTONS;i++)
 | |
| 	{
 | |
| 		event.data1=KEY_JOY1+i;
 | |
| 		D_PostEvent(&event);
 | |
| 	}
 | |
| 
 | |
| 	// emulate the up of all joystick hats
 | |
| 	for (i=0;i<JOYHATS*4;i++)
 | |
| 	{
 | |
| 		event.data1=KEY_HAT1+i;
 | |
| 		D_PostEvent(&event);
 | |
| 	}
 | |
| 
 | |
| 	// reset joystick position
 | |
| 	event.type = ev_joystick;
 | |
| 	for (i=0;i<JOYAXISSET; i++)
 | |
| 	{
 | |
| 		event.data1 = i;
 | |
| 		D_PostEvent(&event);
 | |
| 	}
 | |
| 
 | |
| 	joystick_started[index] = 0;
 | |
| 	JoyReset(&JoyInfo[index]);
 | |
| 
 | |
| 	// don't shut down the subsystem here, because hotplugging
 | |
| }
 | |
| 
 | |
| void I_GetJoystickEvents(UINT8 index)
 | |
| {
 | |
| 	static event_t event = {0,0,0,0};
 | |
| 	INT32 i = 0;
 | |
| 	UINT64 joyhats = 0;
 | |
| #if 0
 | |
| 	UINT64 joybuttons = 0;
 | |
| 	Sint16 axisx, axisy;
 | |
| #endif
 | |
| 
 | |
| 	if (!joystick_started[index]) return;
 | |
| 
 | |
| 	if (!JoyInfo[index].dev) //I_ShutdownJoystick(index);
 | |
| 		return;
 | |
| 
 | |
| #if 0
 | |
| 	//faB: look for as much buttons as g_input code supports,
 | |
| 	//  we don't use the others
 | |
| 	for (i = JoyInfo[index].buttons - 1; 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
 | |
| 
 | |
| 	for (i = JoyInfo[index].hats - 1; i >= 0; i--)
 | |
| 	{
 | |
| 		Uint8 hat = SDL_JoystickGetHat(JoyInfo[index].dev, i);
 | |
| 
 | |
| 		if (hat & SDL_HAT_UP   ) joyhats|=(UINT64)0x1<<(0 + 4*i);
 | |
| 		if (hat & SDL_HAT_DOWN ) joyhats|=(UINT64)0x1<<(1 + 4*i);
 | |
| 		if (hat & SDL_HAT_LEFT ) joyhats|=(UINT64)0x1<<(2 + 4*i);
 | |
| 		if (hat & SDL_HAT_RIGHT) joyhats|=(UINT64)0x1<<(3 + 4*i);
 | |
| 	}
 | |
| 
 | |
| 	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_Joystick *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 (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_JoystickOpen(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_JoystickGetAttached(JoyInfo[playerIndex].dev))) // we failed, but already have a working device
 | |
| 		{
 | |
| 			return JoyInfo[playerIndex].axises;
 | |
| 		}
 | |
| 
 | |
| 		// 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_JoystickName(JoyInfo[playerIndex].dev));
 | |
| 
 | |
| 		JoyInfo[playerIndex].axises = SDL_JoystickNumAxes(JoyInfo[playerIndex].dev);
 | |
| 		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_JoystickNumButtons(JoyInfo[playerIndex].dev);
 | |
| 		if (JoyInfo[playerIndex].buttons > JOYBUTTONS)
 | |
| 			JoyInfo[playerIndex].buttons = JOYBUTTONS;
 | |
| 
 | |
| 		JoyInfo[playerIndex].hats = SDL_JoystickNumHats(JoyInfo[playerIndex].dev);
 | |
| 		if (JoyInfo[playerIndex].hats > JOYHATS)
 | |
| 			JoyInfo[playerIndex].hats = JOYHATS;
 | |
| 
 | |
| 		JoyInfo[playerIndex].balls = SDL_JoystickNumBalls(JoyInfo[playerIndex].dev);
 | |
| 
 | |
| 		//JoyInfo[playerIndex].bGamepadStyle = !stricmp(SDL_JoystickName(JoyInfo[playerIndex].dev), "pad");
 | |
| 
 | |
| 		return JoyInfo[playerIndex].axises;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| //
 | |
| // I_InitJoystick
 | |
| //
 | |
| void I_InitJoystick(UINT8 index)
 | |
| {
 | |
| 	SDL_Joystick *newjoy = 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 (cv_usejoystick[index].value)
 | |
| 		newjoy = SDL_JoystickOpen(cv_usejoystick[index].value-1);
 | |
| 
 | |
| 	for (i = 0; i < MAXSPLITSCREENPLAYERS; i++)
 | |
| 	{
 | |
| 		if (i == index)
 | |
| 			continue;
 | |
| 
 | |
| 		if (JoyInfo[i].dev == newjoy)
 | |
| 			break;
 | |
| 	}
 | |
| 
 | |
| 	if (newjoy && i < MAXSPLITSCREENPLAYERS) // don't override an active device
 | |
| 	{
 | |
| 		cv_usejoystick[index].value = I_GetJoystickDeviceIndex(JoyInfo[index].dev) + 1;
 | |
| 	}
 | |
| 	else if (newjoy && 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 == newjoy)
 | |
| 			break;
 | |
| 	}
 | |
| 
 | |
| 	if (i == MAXSPLITSCREENPLAYERS)
 | |
| 	{
 | |
| 		// Joystick didn't end up being used
 | |
| 		SDL_JoystickClose(newjoy);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 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, 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;
 | |
| }
 | |
| 
 | |
| 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;
 | |
| 
 | |
| static double tic_frequency;
 | |
| static Uint64 tic_epoch;
 | |
| static double elapsed_tics;
 | |
| 
 | |
| static void UpdateElapsedTics(void)
 | |
| {
 | |
| 	const Uint64 now = SDL_GetPerformanceCounter();
 | |
| 
 | |
| 	elapsed_tics += (now - tic_epoch) / tic_frequency;
 | |
| 	tic_epoch = now; // moving epoch
 | |
| }
 | |
| 
 | |
| tic_t I_GetTime(void)
 | |
| {
 | |
| 	double f = 0.0;
 | |
| 	UpdateElapsedTics();
 | |
| 	f = floor(elapsed_tics);
 | |
| 	return (tic_t)f;
 | |
| }
 | |
| 
 | |
| float I_GetTimeFrac(void)
 | |
| {
 | |
| 	UpdateElapsedTics();
 | |
| 	return elapsed_tics;
 | |
| }
 | |
| 
 | |
| precise_t I_GetPreciseTime(void)
 | |
| {
 | |
| 	return SDL_GetPerformanceCounter();
 | |
| }
 | |
| 
 | |
| int I_PreciseToMicros(precise_t d)
 | |
| {
 | |
| 	// d is going to be converted into a double. So remove the highest bits
 | |
| 	// to avoid loss of precision in the lower bits, for the (probably rare) case
 | |
| 	// that the higher bits are actually used.
 | |
| 	d &= ((precise_t)1 << 53) - 1; // The mantissa of a double can handle 53 bits at most.
 | |
| 	// The resulting double from the calculation is converted first to UINT64 to avoid overflow,
 | |
| 	// which is undefined behaviour when converting floating point values to integers.
 | |
| 	return (int)(UINT64)(d / (timer_frequency / 1000000.0));
 | |
| }
 | |
| 
 | |
| //
 | |
| //I_StartupTimer
 | |
| //
 | |
| void I_StartupTimer(void)
 | |
| {
 | |
| 	timer_frequency = SDL_GetPerformanceFrequency();
 | |
| 	tic_epoch       = SDL_GetPerformanceCounter();
 | |
| 
 | |
| 	tic_frequency   = timer_frequency / (double)NEWTICRATE;
 | |
| 	elapsed_tics    = 0.0;
 | |
| }
 | |
| 
 | |
| void I_Sleep(void)
 | |
| {
 | |
| 	if (cv_sleep.value > 0)
 | |
| 		SDL_Delay(cv_sleep.value);
 | |
| }
 | |
| 
 | |
| boolean I_CheckFrameCap(precise_t start, precise_t end)
 | |
| {
 | |
| 	UINT32 capFrames = R_GetFramerateCap();
 | |
| 	int capMicros = 0;
 | |
| 
 | |
| 	int elapsed;
 | |
| 
 | |
| 	if (capFrames == 0)
 | |
| 	{
 | |
| 		// We don't want to cap.
 | |
| 		return false;
 | |
| 	}
 | |
| 
 | |
| 	elapsed = I_PreciseToMicros(end - start);
 | |
| 	capMicros = 1000000 / capFrames;
 | |
| 
 | |
| 	if (elapsed < capMicros)
 | |
| 	{
 | |
| 		// Wait to draw the next frame.
 | |
| 		UINT32 wait = ((capMicros - elapsed) / 1000);
 | |
| 
 | |
| 		if (cv_sleep.value > 1)
 | |
| 		{
 | |
| 			// 1 is the default, and in non-interpolated mode is just the bare minimum wait.
 | |
| 			// Since we're already adding some wait with an FPS cap, only apply when it's above 1.
 | |
| 			wait += cv_sleep.value - 1;
 | |
| 		}
 | |
| 
 | |
| 		// If the wait's greater than our granularity value,
 | |
| 		// we'll just burn the couple extra cycles in the main loop
 | |
| 		// in order to get to the next frame.
 | |
| 		// This makes us get to the exact FPS cap more often.
 | |
| 
 | |
| 		// Higher values have more wasted CPU cycles, but the in-game frame performance is better.
 | |
| 		// 10ms is the average clock tick of most OS scheduling.
 | |
| 		// 15ms is a little more than that, for leniency on slow machines. (This helps mine reach a stable 60, at least!)
 | |
| 		// (https://www.libsdl.org/release/SDL-1.2.15/docs/html/sdldelay.html)
 | |
| #define DELAY_GRANULARITY 15
 | |
| 		if (wait >= DELAY_GRANULARITY)
 | |
| 		{
 | |
| 			SDL_Delay(wait);
 | |
| 		}
 | |
| #undef DELAY_GRANULARITY
 | |
| 
 | |
| 		return true;
 | |
| 	}
 | |
| 
 | |
| 	// Waited enough to draw again.
 | |
| 	return false;
 | |
| }
 | |
| 
 | |
| #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
 | |
| 	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..
 | |
| #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];
 | |
| 
 | |
| 	// 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,
 | |
| 					"Dr. Robotnik's Ring Racers "VERSIONSTRING" Recursive Error",
 | |
| 					buffer, NULL);
 | |
| 
 | |
| 			W_Shutdown();
 | |
| 			exit(-1); // recursive errors detected
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	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.
 | |
| 
 | |
| 	/* 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();
 | |
| 	M_FreePlayerSetupColors();
 | |
| 	I_ShutdownMusic();
 | |
| 	I_ShutdownSound();
 | |
| 	// use this for 1.28 19990220 by Kin
 | |
| 	I_ShutdownGraphics();
 | |
| 	I_ShutdownInput();
 | |
| 	I_ShutdownSystem();
 | |
| 	SDL_Quit();
 | |
| 
 | |
| 	// 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" Error",
 | |
| 			buffer, 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.
 | |
| 
 | |
| 	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;
 | |
| 
 | |
| #ifndef NEWSIGNALHANDLER
 | |
| 	I_ShutdownConsole();
 | |
| #endif
 | |
| 
 | |
| 	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 main.kart 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.kart
 | |
| 
 | |
|   \return path to main.kart 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 CMAKECONFIG
 | |
| #ifndef NDEBUG
 | |
| 	I_OutputMsg(","CMAKE_ASSETS_DIR);
 | |
| 	strcpy(returnWadPath, CMAKE_ASSETS_DIR);
 | |
| 	if (isWadPathOk(returnWadPath))
 | |
| 	{
 | |
| 		return returnWadPath;
 | |
| 	}
 | |
| #endif
 | |
| #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.kart
 | |
| #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
 |