1 // Emacs style mode select   -*- C++ -*-
2 //
3 // SONIC ROBO BLAST 2
4 //-----------------------------------------------------------------------------
5 //
6 // Copyright (C) 1993-1996 by id Software, Inc.
7 // Portions Copyright (C) 1998-2000 by DooM Legacy Team.
8 // Copyright (C) 2014-2020 by Sonic Team Junior.
9 //
10 // This program is free software; you can redistribute it and/or
11 // modify it under the terms of the GNU General Public License
12 // as published by the Free Software Foundation; either version 2
13 // of the License, or (at your option) any later version.
14 //
15 // This program is distributed in the hope that it will be useful,
16 // but WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18 // GNU General Public License for more details.
19 //
20 // Changes by Graue <graue@oceanbase.org> are in the public domain.
21 //
22 //-----------------------------------------------------------------------------
23 /// \file
24 /// \brief SRB2 system stuff for SDL
25 
26 #ifdef CMAKECONFIG
27 #include "config.h"
28 #else
29 #include "../config.h.in"
30 #endif
31 
32 #include <signal.h>
33 
34 #ifdef _WIN32
35 #define RPC_NO_WINDOWS_H
36 #include <windows.h>
37 #include "../doomtype.h"
38 typedef BOOL (WINAPI *p_GetDiskFreeSpaceExA)(LPCSTR, PULARGE_INTEGER, PULARGE_INTEGER, PULARGE_INTEGER);
39 typedef BOOL (WINAPI *p_IsProcessorFeaturePresent) (DWORD);
40 typedef DWORD (WINAPI *p_timeGetTime) (void);
41 typedef UINT (WINAPI *p_timeEndPeriod) (UINT);
42 typedef HANDLE (WINAPI *p_OpenFileMappingA) (DWORD, BOOL, LPCSTR);
43 typedef LPVOID (WINAPI *p_MapViewOfFile) (HANDLE, DWORD, DWORD, DWORD, SIZE_T);
44 #endif
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48 #ifdef __GNUC__
49 #include <unistd.h>
50 #elif defined (_MSC_VER)
51 #include <direct.h>
52 #endif
53 #if defined (__unix__) || defined (UNIXCOMMON)
54 #include <fcntl.h>
55 #endif
56 
57 #include <stdio.h>
58 #ifdef _WIN32
59 #include <conio.h>
60 #endif
61 
62 #ifdef _MSC_VER
63 #pragma warning(disable : 4214 4244)
64 #endif
65 
66 #ifdef HAVE_SDL
67 #define _MATH_DEFINES_DEFINED
68 #include "SDL.h"
69 
70 #ifdef HAVE_TTF
71 #include "i_ttf.h"
72 #endif
73 
74 #ifdef _MSC_VER
75 #pragma warning(default : 4214 4244)
76 #endif
77 
78 #include "SDL_cpuinfo.h"
79 #define HAVE_SDLCPUINFO
80 
81 #if defined (__unix__) || defined(__APPLE__) || (defined (UNIXCOMMON) && !defined (__HAIKU__))
82 #if defined (__linux__)
83 #include <sys/vfs.h>
84 #else
85 #include <sys/param.h>
86 #include <sys/mount.h>
87 /*For meminfo*/
88 #include <sys/types.h>
89 #ifdef FREEBSD
90 #include <kvm.h>
91 #endif
92 #include <nlist.h>
93 #include <sys/vmmeter.h>
94 #endif
95 #endif
96 
97 #if defined (__linux__) || (defined (UNIXCOMMON) && !defined (__HAIKU__))
98 #ifndef NOTERMIOS
99 #include <termios.h>
100 #include <sys/ioctl.h> // ioctl
101 #define HAVE_TERMIOS
102 #endif
103 #endif
104 
105 #if (defined (__unix__) && !defined (_MSDOS)) || (defined (UNIXCOMMON) && !defined(__APPLE__))
106 #include <errno.h>
107 #include <sys/wait.h>
108 #define NEWSIGNALHANDLER
109 #endif
110 
111 #ifndef NOMUMBLE
112 #ifdef __linux__ // need -lrt
113 #include <sys/mman.h>
114 #ifdef MAP_FAILED
115 #define HAVE_SHM
116 #endif
117 #include <wchar.h>
118 #endif
119 
120 #ifdef _WIN32
121 #define HAVE_MUMBLE
122 #define WINMUMBLE
123 #elif defined (HAVE_SHM)
124 #define HAVE_MUMBLE
125 #endif
126 #endif // NOMUMBLE
127 
128 #ifndef O_BINARY
129 #define O_BINARY 0
130 #endif
131 
132 #ifdef __APPLE__
133 #include "macosx/mac_resources.h"
134 #endif
135 
136 #ifndef errno
137 #include <errno.h>
138 #endif
139 
140 #if defined (__unix__) || defined(__APPLE__) || defined (UNIXCOMMON)
141 #include <execinfo.h>
142 #include <time.h>
143 #define UNIXBACKTRACE
144 #endif
145 
146 // Locations for searching the srb2.pk3
147 #if defined (__unix__) || defined(__APPLE__) || defined (UNIXCOMMON)
148 #define DEFAULTWADLOCATION1 "/usr/local/share/games/SRB2"
149 #define DEFAULTWADLOCATION2 "/usr/local/games/SRB2"
150 #define DEFAULTWADLOCATION3 "/usr/share/games/SRB2"
151 #define DEFAULTWADLOCATION4 "/usr/games/SRB2"
152 #define DEFAULTSEARCHPATH1 "/usr/local/games"
153 #define DEFAULTSEARCHPATH2 "/usr/games"
154 #define DEFAULTSEARCHPATH3 "/usr/local"
155 #elif defined (_WIN32)
156 #define DEFAULTWADLOCATION1 "c:\\games\\srb2"
157 #define DEFAULTWADLOCATION2 "\\games\\srb2"
158 #define DEFAULTSEARCHPATH1 "c:\\games"
159 #define DEFAULTSEARCHPATH2 "\\games"
160 #endif
161 
162 /**	\brief WAD file to look for
163 */
164 #define WADKEYWORD1 "srb2.pk3"
165 /**	\brief holds wad path
166 */
167 static char returnWadPath[256];
168 
169 //Alam_GBC: SDL
170 
171 #include "../doomdef.h"
172 #include "../m_misc.h"
173 #include "../i_video.h"
174 #include "../i_sound.h"
175 #include "../i_system.h"
176 #include "../i_threads.h"
177 #include "../screen.h" //vid.WndParent
178 #include "../d_net.h"
179 #include "../g_game.h"
180 #include "../filesrch.h"
181 #include "endtxt.h"
182 #include "sdlmain.h"
183 
184 #include "../i_joy.h"
185 
186 #include "../m_argv.h"
187 
188 #include "../m_menu.h"
189 
190 #ifdef MAC_ALERT
191 #include "macosx/mac_alert.h"
192 #endif
193 
194 #include "../d_main.h"
195 
196 #if !defined(NOMUMBLE) && defined(HAVE_MUMBLE)
197 // Mumble context string
198 #include "../d_clisrv.h"
199 #include "../byteptr.h"
200 #endif
201 
202 /**	\brief	The JoyReset function
203 
204 	\param	JoySet	Joystick info to reset
205 
206 	\return	void
207 */
JoyReset(SDLJoyInfo_t * JoySet)208 static void JoyReset(SDLJoyInfo_t *JoySet)
209 {
210 	if (JoySet->dev)
211 	{
212 		SDL_JoystickClose(JoySet->dev);
213 	}
214 	JoySet->dev = NULL;
215 	JoySet->oldjoy = -1;
216 	JoySet->axises = JoySet->buttons = JoySet->hats = JoySet->balls = 0;
217 	//JoySet->scale
218 }
219 
220 /**	\brief First joystick up and running
221 */
222 static INT32 joystick_started  = 0;
223 
224 /**	\brief SDL info about joystick 1
225 */
226 SDLJoyInfo_t JoyInfo;
227 
228 
229 /**	\brief Second joystick up and running
230 */
231 static INT32 joystick2_started = 0;
232 
233 /**	\brief SDL inof about joystick 2
234 */
235 SDLJoyInfo_t JoyInfo2;
236 
237 #ifdef HAVE_TERMIOS
238 static INT32 fdmouse2 = -1;
239 static INT32 mouse2_started = 0;
240 #endif
241 
242 SDL_bool consolevent = SDL_FALSE;
243 SDL_bool framebuffer = SDL_FALSE;
244 
245 UINT8 keyboard_started = false;
246 
247 #ifdef UNIXBACKTRACE
248 #define STDERR_WRITE(string) if (fd != -1) I_OutputMsg("%s", string)
249 #define CRASHLOG_WRITE(string) if (fd != -1) write(fd, string, strlen(string))
250 #define CRASHLOG_STDERR_WRITE(string) \
251 	if (fd != -1)\
252 		write(fd, string, strlen(string));\
253 	I_OutputMsg("%s", string)
254 
write_backtrace(INT32 signal)255 static void write_backtrace(INT32 signal)
256 {
257 	int fd = -1;
258 	size_t size;
259 	time_t rawtime;
260 	struct tm timeinfo;
261 
262 	enum { BT_SIZE = 1024, STR_SIZE = 32 };
263 	void *array[BT_SIZE];
264 	char timestr[STR_SIZE];
265 
266 	const char *error = "An error occurred within SRB2! Send this stack trace to someone who can help!\n";
267 	const char *error2 = "(Or find crash-log.txt in your SRB2 directory.)\n"; // Shown only to stderr.
268 
269 	fd = open(va("%s" PATHSEP "%s", srb2home, "crash-log.txt"), O_CREAT|O_APPEND|O_RDWR, S_IRUSR|S_IWUSR);
270 
271 	if (fd == -1)
272 		I_OutputMsg("\nWARNING: Couldn't open crash log for writing! Make sure your permissions are correct. Please save the below report!\n");
273 
274 	// Get the current time as a string.
275 	time(&rawtime);
276 	localtime_r(&rawtime, &timeinfo);
277 	strftime(timestr, STR_SIZE, "%a, %d %b %Y %T %z", &timeinfo);
278 
279 	CRASHLOG_WRITE("------------------------\n"); // Nice looking seperator
280 
281 	CRASHLOG_STDERR_WRITE("\n"); // Newline to look nice for both outputs.
282 	CRASHLOG_STDERR_WRITE(error); // "Oops, SRB2 crashed" message
283 	STDERR_WRITE(error2); // Tell the user where the crash log is.
284 
285 	// Tell the log when we crashed.
286 	CRASHLOG_WRITE("Time of crash: ");
287 	CRASHLOG_WRITE(timestr);
288 	CRASHLOG_WRITE("\n");
289 
290 	// Give the crash log the cause and a nice 'Backtrace:' thing
291 	// The signal is given to the user when the parent process sees we crashed.
292 	CRASHLOG_WRITE("Cause: ");
293 	CRASHLOG_WRITE(strsignal(signal));
294 	CRASHLOG_WRITE("\n"); // Newline for the signal name
295 
296 	CRASHLOG_STDERR_WRITE("\nBacktrace:\n");
297 
298 	// Flood the output and log with the backtrace
299 	size = backtrace(array, BT_SIZE);
300 	backtrace_symbols_fd(array, size, fd);
301 	backtrace_symbols_fd(array, size, STDERR_FILENO);
302 
303 	CRASHLOG_WRITE("\n"); // Write another newline to the log so it looks nice :)
304 
305 	close(fd);
306 }
307 #undef STDERR_WRITE
308 #undef CRASHLOG_WRITE
309 #undef CRASHLOG_STDERR_WRITE
310 #endif // UNIXBACKTRACE
311 
I_ReportSignal(int num,int coredumped)312 static void I_ReportSignal(int num, int coredumped)
313 {
314 	//static char msg[] = "oh no! back to reality!\r\n";
315 	const char *      sigmsg;
316 	char msg[128];
317 
318 	switch (num)
319 	{
320 //	case SIGINT:
321 //		sigmsg = "SIGINT - interrupted";
322 //		break;
323 	case SIGILL:
324 		sigmsg = "SIGILL - illegal instruction - invalid function image";
325 		break;
326 	case SIGFPE:
327 		sigmsg = "SIGFPE - mathematical exception";
328 		break;
329 	case SIGSEGV:
330 		sigmsg = "SIGSEGV - segment violation";
331 		break;
332 //	case SIGTERM:
333 //		sigmsg = "SIGTERM - Software termination signal from kill";
334 //		break;
335 //	case SIGBREAK:
336 //		sigmsg = "SIGBREAK - Ctrl-Break sequence";
337 //		break;
338 	case SIGABRT:
339 		sigmsg = "SIGABRT - abnormal termination triggered by abort call";
340 		break;
341 	default:
342 		sprintf(msg,"signal number %d", num);
343 		if (coredumped)
344 			sigmsg = 0;
345 		else
346 			sigmsg = msg;
347 	}
348 
349 	if (coredumped)
350 	{
351 		if (sigmsg)
352 			sprintf(msg, "%s (core dumped)", sigmsg);
353 		else
354 			strcat(msg, " (core dumped)");
355 
356 		sigmsg = msg;
357 	}
358 
359 	I_OutputMsg("\nProcess killed by signal: %s\n\n", sigmsg);
360 
361 	SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR,
362 		"Process killed by signal",
363 		sigmsg, NULL);
364 }
365 
366 #ifndef NEWSIGNALHANDLER
signal_handler(INT32 num)367 FUNCNORETURN static ATTRNORETURN void signal_handler(INT32 num)
368 {
369 	D_QuitNetGame(); // Fix server freezes
370 	CL_AbortDownloadResume();
371 #ifdef UNIXBACKTRACE
372 	write_backtrace(num);
373 #endif
374 	I_ReportSignal(num, 0);
375 	I_ShutdownSystem();
376 	signal(num, SIG_DFL);               //default signal action
377 	raise(num);
378 	I_Quit();
379 }
380 #endif
381 
quit_handler(int num)382 FUNCNORETURN static ATTRNORETURN void quit_handler(int num)
383 {
384 	signal(num, SIG_DFL); //default signal action
385 	raise(num);
386 	I_Quit();
387 }
388 
389 #ifdef HAVE_TERMIOS
390 // TERMIOS console code from Quake3: thank you!
391 SDL_bool stdin_active = SDL_TRUE;
392 
393 typedef struct
394 {
395 	size_t cursor;
396 	char buffer[256];
397 } feild_t;
398 
399 feild_t tty_con;
400 
401 // when printing general stuff to stdout stderr (Sys_Printf)
402 //   we need to disable the tty console stuff
403 // this increments so we can recursively disable
404 static INT32 ttycon_hide = 0;
405 // some key codes that the terminal may be using
406 // TTimo NOTE: I'm not sure how relevant this is
407 static INT32 tty_erase;
408 static INT32 tty_eof;
409 
410 static struct termios tty_tc;
411 
412 // =============================================================
413 // tty console routines
414 // NOTE: if the user is editing a line when something gets printed to the early console then it won't look good
415 //   so we provide tty_Clear and tty_Show to be called before and after a stdout or stderr output
416 // =============================================================
417 
418 // flush stdin, I suspect some terminals are sending a LOT of garbage
419 // FIXME TTimo relevant?
420 #if 0
421 static inline void tty_FlushIn(void)
422 {
423 	char key;
424 	while (read(STDIN_FILENO, &key, 1)!=-1);
425 }
426 #endif
427 
428 // do a backspace
429 // TTimo NOTE: it seems on some terminals just sending '\b' is not enough
430 //   so for now, in any case we send "\b \b" .. yeah well ..
431 //   (there may be a way to find out if '\b' alone would work though)
tty_Back(void)432 static void tty_Back(void)
433 {
434 	char key;
435 	ssize_t d;
436 	key = '\b';
437 	d = write(STDOUT_FILENO, &key, 1);
438 	key = ' ';
439 	d = write(STDOUT_FILENO, &key, 1);
440 	key = '\b';
441 	d = write(STDOUT_FILENO, &key, 1);
442 	(void)d;
443 }
444 
tty_Clear(void)445 static void tty_Clear(void)
446 {
447 	size_t i;
448 	if (tty_con.cursor>0)
449 	{
450 		for (i=0; i<tty_con.cursor; i++)
451 		{
452 			tty_Back();
453 		}
454 	}
455 
456 }
457 
458 // clear the display of the line currently edited
459 // bring cursor back to beginning of line
tty_Hide(void)460 static inline void tty_Hide(void)
461 {
462 	//I_Assert(consolevent);
463 	if (ttycon_hide)
464 	{
465 		ttycon_hide++;
466 		return;
467 	}
468 	tty_Clear();
469 	ttycon_hide++;
470 }
471 
472 // show the current line
473 // FIXME TTimo need to position the cursor if needed??
tty_Show(void)474 static inline void tty_Show(void)
475 {
476 	size_t i;
477 	ssize_t d;
478 	//I_Assert(consolevent);
479 	I_Assert(ttycon_hide>0);
480 	ttycon_hide--;
481 	if (ttycon_hide == 0 && tty_con.cursor)
482 	{
483 		for (i=0; i<tty_con.cursor; i++)
484 		{
485 			d = write(STDOUT_FILENO, tty_con.buffer+i, 1);
486 		}
487 	}
488 	(void)d;
489 }
490 
491 // never exit without calling this, or your terminal will be left in a pretty bad state
I_ShutdownConsole(void)492 static void I_ShutdownConsole(void)
493 {
494 	if (consolevent)
495 	{
496 		I_OutputMsg("Shutdown tty console\n");
497 		consolevent = SDL_FALSE;
498 		tcsetattr (STDIN_FILENO, TCSADRAIN, &tty_tc);
499 	}
500 }
501 
I_StartupConsole(void)502 static void I_StartupConsole(void)
503 {
504 	struct termios tc;
505 
506 	// TTimo
507 	// https://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=390 (404)
508 	// then SIGTTIN or SIGTOU is emitted, if not catched, turns into a SIGSTP
509 	signal(SIGTTIN, SIG_IGN);
510 	signal(SIGTTOU, SIG_IGN);
511 
512 	consolevent = !M_CheckParm("-noconsole");
513 	framebuffer = M_CheckParm("-framebuffer");
514 
515 	if (framebuffer)
516 		consolevent = SDL_FALSE;
517 
518 	if (!consolevent) return;
519 
520 	if (isatty(STDIN_FILENO)!=1)
521 	{
522 		I_OutputMsg("stdin is not a tty, tty console mode failed\n");
523 		consolevent = SDL_FALSE;
524 		return;
525 	}
526 	memset(&tty_con, 0x00, sizeof(tty_con));
527 	tcgetattr (0, &tty_tc);
528 	tty_erase = tty_tc.c_cc[VERASE];
529 	tty_eof = tty_tc.c_cc[VEOF];
530 	tc = tty_tc;
531 	/*
532 	 ECHO: don't echo input characters
533 	 ICANON: enable canonical mode.  This  enables  the  special
534 	  characters  EOF,  EOL,  EOL2, ERASE, KILL, REPRINT,
535 	  STATUS, and WERASE, and buffers by lines.
536 	 ISIG: when any of the characters  INTR,  QUIT,  SUSP,  or
537 	  DSUSP are received, generate the corresponding signal
538 	*/
539 	tc.c_lflag &= ~(ECHO | ICANON);
540 	/*
541 	 ISTRIP strip off bit 8
542 	 INPCK enable input parity checking
543 	 */
544 	tc.c_iflag &= ~(ISTRIP | INPCK);
545 	tc.c_cc[VMIN] = 0; //1?
546 	tc.c_cc[VTIME] = 0;
547 	tcsetattr (0, TCSADRAIN, &tc);
548 }
549 
I_GetConsoleEvents(void)550 void I_GetConsoleEvents(void)
551 {
552 	// we use this when sending back commands
553 	event_t ev = {0,0,0,0};
554 	char key = 0;
555 	ssize_t d;
556 
557 	if (!consolevent)
558 		return;
559 
560 	ev.type = ev_console;
561 	if (read(STDIN_FILENO, &key, 1) == -1 || !key)
562 		return;
563 
564 	// we have something
565 	// backspace?
566 	// NOTE TTimo testing a lot of values .. seems it's the only way to get it to work everywhere
567 	if ((key == tty_erase) || (key == 127) || (key == 8))
568 	{
569 		if (tty_con.cursor > 0)
570 		{
571 			tty_con.cursor--;
572 			tty_con.buffer[tty_con.cursor] = '\0';
573 			tty_Back();
574 		}
575 		ev.data1 = KEY_BACKSPACE;
576 	}
577 	else if (key < ' ') // check if this is a control char
578 	{
579 		if (key == '\n')
580 		{
581 			tty_Clear();
582 			tty_con.cursor = 0;
583 			ev.data1 = KEY_ENTER;
584 		}
585 		else return;
586 	}
587 	else
588 	{
589 		// push regular character
590 		ev.data1 = tty_con.buffer[tty_con.cursor] = key;
591 		tty_con.cursor++;
592 		// print the current line (this is differential)
593 		d = write(STDOUT_FILENO, &key, 1);
594 	}
595 	if (ev.data1) D_PostEvent(&ev);
596 	//tty_FlushIn();
597 	(void)d;
598 }
599 
600 #elif defined (_WIN32)
I_ReadyConsole(HANDLE ci)601 static BOOL I_ReadyConsole(HANDLE ci)
602 {
603 	DWORD gotinput;
604 	if (ci == INVALID_HANDLE_VALUE) return FALSE;
605 	if (WaitForSingleObject(ci,0) != WAIT_OBJECT_0) return FALSE;
606 	if (GetFileType(ci) != FILE_TYPE_CHAR) return FALSE;
607 	if (!GetConsoleMode(ci, &gotinput)) return FALSE;
608 	return (GetNumberOfConsoleInputEvents(ci, &gotinput) && gotinput);
609 }
610 
611 static boolean entering_con_command = false;
612 
Impl_HandleKeyboardConsoleEvent(KEY_EVENT_RECORD evt,HANDLE co)613 static void Impl_HandleKeyboardConsoleEvent(KEY_EVENT_RECORD evt, HANDLE co)
614 {
615 	event_t event;
616 	CONSOLE_SCREEN_BUFFER_INFO CSBI;
617 	DWORD t;
618 
619 	memset(&event,0x00,sizeof (event));
620 
621 	if (evt.bKeyDown)
622 	{
623 		event.type = ev_console;
624 		entering_con_command = true;
625 		switch (evt.wVirtualKeyCode)
626 		{
627 			case VK_ESCAPE:
628 			case VK_TAB:
629 				event.data1 = KEY_NULL;
630 				break;
631 			case VK_RETURN:
632 				entering_con_command = false;
633 				/* FALLTHRU */
634 			default:
635 				//event.data1 = MapVirtualKey(evt.wVirtualKeyCode,2); // convert in to char
636 				event.data1 = evt.uChar.AsciiChar;
637 		}
638 		if (co != INVALID_HANDLE_VALUE && GetFileType(co) == FILE_TYPE_CHAR && GetConsoleMode(co, &t))
639 		{
640 			if (event.data1 && event.data1 != KEY_LSHIFT && event.data1 != KEY_RSHIFT)
641 			{
642 #ifdef _UNICODE
643 				WriteConsole(co, &evt.uChar.UnicodeChar, 1, &t, NULL);
644 #else
645 				WriteConsole(co, &evt.uChar.AsciiChar, 1 , &t, NULL);
646 #endif
647 			}
648 			if (evt.wVirtualKeyCode == VK_BACK
649 				&& GetConsoleScreenBufferInfo(co,&CSBI))
650 			{
651 				WriteConsoleOutputCharacterA(co, " ",1, CSBI.dwCursorPosition, &t);
652 			}
653 		}
654 	}
655 	if (event.data1) D_PostEvent(&event);
656 }
657 
I_GetConsoleEvents(void)658 void I_GetConsoleEvents(void)
659 {
660 	HANDLE ci = GetStdHandle(STD_INPUT_HANDLE);
661 	HANDLE co = GetStdHandle(STD_OUTPUT_HANDLE);
662 	INPUT_RECORD input;
663 	DWORD t;
664 
665 	while (I_ReadyConsole(ci) && ReadConsoleInput(ci, &input, 1, &t) && t)
666 	{
667 		switch (input.EventType)
668 		{
669 			case KEY_EVENT:
670 				Impl_HandleKeyboardConsoleEvent(input.Event.KeyEvent, co);
671 				break;
672 			case MOUSE_EVENT:
673 			case WINDOW_BUFFER_SIZE_EVENT:
674 			case MENU_EVENT:
675 			case FOCUS_EVENT:
676 				break;
677 		}
678 	}
679 }
680 
I_StartupConsole(void)681 static void I_StartupConsole(void)
682 {
683 	HANDLE ci, co;
684 	const INT32 ded = M_CheckParm("-dedicated");
685 	BOOL gotConsole = FALSE;
686 	if (M_CheckParm("-console") || ded)
687 		gotConsole = AllocConsole();
688 #ifdef _DEBUG
689 	else if (M_CheckParm("-noconsole") && !ded)
690 #else
691 	else if (!M_CheckParm("-console") && !ded)
692 #endif
693 	{
694 		FreeConsole();
695 		gotConsole = FALSE;
696 	}
697 
698 	if (gotConsole)
699 	{
700 		SetConsoleTitleA("SRB2 Console");
701 		consolevent = SDL_TRUE;
702 	}
703 
704 	//Let get the real console HANDLE, because Mingw's Bash is bad!
705 	ci = CreateFile(TEXT("CONIN$") ,               GENERIC_READ, FILE_SHARE_READ,  NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
706 	co = CreateFile(TEXT("CONOUT$"), GENERIC_WRITE|GENERIC_READ, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
707 	if (ci != INVALID_HANDLE_VALUE)
708 	{
709 		const DWORD CM = ENABLE_LINE_INPUT|ENABLE_ECHO_INPUT|ENABLE_PROCESSED_INPUT;
710 		SetStdHandle(STD_INPUT_HANDLE, ci);
711 		if (GetFileType(ci) == FILE_TYPE_CHAR)
712 			SetConsoleMode(ci, CM); //default mode but no ENABLE_MOUSE_INPUT
713 	}
714 	if (co != INVALID_HANDLE_VALUE)
715 	{
716 		SetStdHandle(STD_OUTPUT_HANDLE, co);
717 		SetStdHandle(STD_ERROR_HANDLE, co);
718 	}
719 }
I_ShutdownConsole(void)720 static inline void I_ShutdownConsole(void){}
721 #else
I_GetConsoleEvents(void)722 void I_GetConsoleEvents(void){}
I_StartupConsole(void)723 static inline void I_StartupConsole(void)
724 {
725 #ifdef _DEBUG
726 	consolevent = !M_CheckParm("-noconsole");
727 #else
728 	consolevent = M_CheckParm("-console");
729 #endif
730 
731 	framebuffer = M_CheckParm("-framebuffer");
732 
733 	if (framebuffer)
734 		consolevent = SDL_FALSE;
735 }
I_ShutdownConsole(void)736 static inline void I_ShutdownConsole(void){}
737 #endif
738 
739 //
740 // StartupKeyboard
741 //
I_RegisterSignals(void)742 static void I_RegisterSignals (void)
743 {
744 #ifdef SIGINT
745 	signal(SIGINT , quit_handler);
746 #endif
747 #ifdef SIGBREAK
748 	signal(SIGBREAK , quit_handler);
749 #endif
750 #ifdef SIGTERM
751 	signal(SIGTERM , quit_handler);
752 #endif
753 
754 	// If these defines don't exist,
755 	// then compilation would have failed above us...
756 #ifndef NEWSIGNALHANDLER
757 	signal(SIGILL , signal_handler);
758 	signal(SIGSEGV , signal_handler);
759 	signal(SIGABRT , signal_handler);
760 	signal(SIGFPE , signal_handler);
761 #endif
762 }
763 
764 #ifdef NEWSIGNALHANDLER
signal_handler_child(INT32 num)765 static void signal_handler_child(INT32 num)
766 {
767 #ifdef UNIXBACKTRACE
768 	write_backtrace(num);
769 #endif
770 
771 	signal(num, SIG_DFL);               //default signal action
772 	raise(num);
773 }
774 
I_RegisterChildSignals(void)775 static void I_RegisterChildSignals(void)
776 {
777 	// If these defines don't exist,
778 	// then compilation would have failed above us...
779 	signal(SIGILL , signal_handler_child);
780 	signal(SIGSEGV , signal_handler_child);
781 	signal(SIGABRT , signal_handler_child);
782 	signal(SIGFPE , signal_handler_child);
783 }
784 #endif
785 
786 //
787 //I_OutputMsg
788 //
I_OutputMsg(const char * fmt,...)789 void I_OutputMsg(const char *fmt, ...)
790 {
791 	size_t len;
792 	char txt[8192];
793 	va_list  argptr;
794 
795 	va_start(argptr,fmt);
796 	vsprintf(txt, fmt, argptr);
797 	va_end(argptr);
798 
799 #ifdef HAVE_TTF
800 	if (TTF_WasInit()) I_TTFDrawText(currentfont, solid, DEFAULTFONTFGR, DEFAULTFONTFGG, DEFAULTFONTFGB,  DEFAULTFONTFGA,
801 	DEFAULTFONTBGR, DEFAULTFONTBGG, DEFAULTFONTBGB, DEFAULTFONTBGA, txt);
802 #endif
803 
804 #if defined (_WIN32) && defined (_MSC_VER)
805 	OutputDebugStringA(txt);
806 #endif
807 
808 	len = strlen(txt);
809 
810 #ifdef LOGMESSAGES
811 	if (logstream)
812 	{
813 		size_t d = fwrite(txt, len, 1, logstream);
814 		fflush(logstream);
815 		(void)d;
816 	}
817 #endif
818 
819 #if defined (_WIN32)
820 #ifdef DEBUGFILE
821 	if (debugfile != stderr)
822 #endif
823 	{
824 		HANDLE co = GetStdHandle(STD_OUTPUT_HANDLE);
825 		DWORD bytesWritten;
826 
827 		if (co == INVALID_HANDLE_VALUE)
828 			return;
829 
830 		if (GetFileType(co) == FILE_TYPE_CHAR && GetConsoleMode(co, &bytesWritten))
831 		{
832 			static COORD coordNextWrite = {0,0};
833 			LPVOID oldLines = NULL;
834 			INT oldLength;
835 			CONSOLE_SCREEN_BUFFER_INFO csbi;
836 
837 			// Save the lines that we're going to obliterate.
838 			GetConsoleScreenBufferInfo(co, &csbi);
839 			oldLength = csbi.dwSize.X * (csbi.dwCursorPosition.Y - coordNextWrite.Y) + csbi.dwCursorPosition.X - coordNextWrite.X;
840 
841 			if (oldLength > 0)
842 			{
843 				LPVOID blank = malloc(oldLength);
844 				if (!blank) return;
845 				memset(blank, ' ', oldLength); // Blank out.
846 				oldLines = malloc(oldLength*sizeof(TCHAR));
847 				if (!oldLines)
848 				{
849 					free(blank);
850 					return;
851 				}
852 
853 				ReadConsoleOutputCharacter(co, oldLines, oldLength, coordNextWrite, &bytesWritten);
854 
855 				// Move to where we what to print - which is where we would've been,
856 				// had console input not been in the way,
857 				SetConsoleCursorPosition(co, coordNextWrite);
858 
859 				WriteConsoleA(co, blank, oldLength, &bytesWritten, NULL);
860 				free(blank);
861 
862 				// And back to where we want to print again.
863 				SetConsoleCursorPosition(co, coordNextWrite);
864 			}
865 
866 			// Actually write the string now!
867 			WriteConsoleA(co, txt, (DWORD)len, &bytesWritten, NULL);
868 
869 			// Next time, output where we left off.
870 			GetConsoleScreenBufferInfo(co, &csbi);
871 			coordNextWrite = csbi.dwCursorPosition;
872 
873 			// Restore what was overwritten.
874 			if (oldLines && entering_con_command)
875 				WriteConsole(co, oldLines, oldLength, &bytesWritten, NULL);
876 			if (oldLines) free(oldLines);
877 		}
878 		else // Redirected to a file.
879 			WriteFile(co, txt, (DWORD)len, &bytesWritten, NULL);
880 	}
881 #else
882 #ifdef HAVE_TERMIOS
883 	if (consolevent)
884 	{
885 		tty_Hide();
886 	}
887 #endif
888 
889 	if (!framebuffer)
890 		fprintf(stderr, "%s", txt);
891 #ifdef HAVE_TERMIOS
892 	if (consolevent)
893 	{
894 		tty_Show();
895 	}
896 #endif
897 
898 	// 2004-03-03 AJR Since not all messages end in newline, some were getting displayed late.
899 	if (!framebuffer)
900 		fflush(stderr);
901 
902 #endif
903 }
904 
905 //
906 // I_GetKey
907 //
I_GetKey(void)908 INT32 I_GetKey (void)
909 {
910 	// Warning: I_GetKey empties the event queue till next keypress
911 	event_t *ev;
912 	INT32 rc = 0;
913 
914 	// return the first keypress from the event queue
915 	for (; eventtail != eventhead; eventtail = (eventtail+1)&(MAXEVENTS-1))
916 	{
917 		ev = &events[eventtail];
918 		if (ev->type == ev_keydown || ev->type == ev_console)
919 		{
920 			rc = ev->data1;
921 			continue;
922 		}
923 	}
924 
925 	return rc;
926 }
927 
928 //
929 // I_JoyScale
930 //
I_JoyScale(void)931 void I_JoyScale(void)
932 {
933 	Joystick.bGamepadStyle = cv_joyscale.value==0;
934 	JoyInfo.scale = Joystick.bGamepadStyle?1:cv_joyscale.value;
935 }
936 
I_JoyScale2(void)937 void I_JoyScale2(void)
938 {
939 	Joystick2.bGamepadStyle = cv_joyscale2.value==0;
940 	JoyInfo2.scale = Joystick2.bGamepadStyle?1:cv_joyscale2.value;
941 }
942 
943 // Cheat to get the device index for a joystick handle
I_GetJoystickDeviceIndex(SDL_Joystick * dev)944 INT32 I_GetJoystickDeviceIndex(SDL_Joystick *dev)
945 {
946 	INT32 i, count = SDL_NumJoysticks();
947 
948 	for (i = 0; dev && i < count; i++)
949 	{
950 		SDL_Joystick *test = SDL_JoystickOpen(i);
951 		if (test && test == dev)
952 			return i;
953 		else if (JoyInfo.dev != test && JoyInfo2.dev != test)
954 			SDL_JoystickClose(test);
955 	}
956 
957 	return -1;
958 }
959 
960 /**	\brief Joystick 1 buttons states
961 */
962 static UINT64 lastjoybuttons = 0;
963 
964 /**	\brief Joystick 1 hats state
965 */
966 static UINT64 lastjoyhats = 0;
967 
968 /**	\brief	Shuts down joystick 1
969 
970 
971 	\return void
972 
973 
974 */
I_ShutdownJoystick(void)975 void I_ShutdownJoystick(void)
976 {
977 	INT32 i;
978 	event_t event;
979 	event.type=ev_keyup;
980 	event.data2 = 0;
981 	event.data3 = 0;
982 
983 	lastjoybuttons = lastjoyhats = 0;
984 
985 	// emulate the up of all joystick buttons
986 	for (i=0;i<JOYBUTTONS;i++)
987 	{
988 		event.data1=KEY_JOY1+i;
989 		D_PostEvent(&event);
990 	}
991 
992 	// emulate the up of all joystick hats
993 	for (i=0;i<JOYHATS*4;i++)
994 	{
995 		event.data1=KEY_HAT1+i;
996 		D_PostEvent(&event);
997 	}
998 
999 	// reset joystick position
1000 	event.type = ev_joystick;
1001 	for (i=0;i<JOYAXISSET; i++)
1002 	{
1003 		event.data1 = i;
1004 		D_PostEvent(&event);
1005 	}
1006 
1007 	joystick_started = 0;
1008 	JoyReset(&JoyInfo);
1009 
1010 	// don't shut down the subsystem here, because hotplugging
1011 }
1012 
I_GetJoystickEvents(void)1013 void I_GetJoystickEvents(void)
1014 {
1015 	static event_t event = {0,0,0,0};
1016 	INT32 i = 0;
1017 	UINT64 joyhats = 0;
1018 #if 0
1019 	UINT64 joybuttons = 0;
1020 	Sint16 axisx, axisy;
1021 #endif
1022 
1023 	if (!joystick_started) return;
1024 
1025 	if (!JoyInfo.dev) //I_ShutdownJoystick();
1026 		return;
1027 
1028 #if 0
1029 	//faB: look for as much buttons as g_input code supports,
1030 	//  we don't use the others
1031 	for (i = JoyInfo.buttons - 1; i >= 0; i--)
1032 	{
1033 		joybuttons <<= 1;
1034 		if (SDL_JoystickGetButton(JoyInfo.dev,i))
1035 			joybuttons |= 1;
1036 	}
1037 
1038 	if (joybuttons != lastjoybuttons)
1039 	{
1040 		INT64 j = 1; // keep only bits that changed since last time
1041 		INT64 newbuttons = joybuttons ^ lastjoybuttons;
1042 		lastjoybuttons = joybuttons;
1043 
1044 		for (i = 0; i < JOYBUTTONS; i++, j <<= 1)
1045 		{
1046 			if (newbuttons & j) // button changed state?
1047 			{
1048 				if (joybuttons & j)
1049 					event.type = ev_keydown;
1050 				else
1051 					event.type = ev_keyup;
1052 				event.data1 = KEY_JOY1 + i;
1053 				D_PostEvent(&event);
1054 			}
1055 		}
1056 	}
1057 #endif
1058 
1059 	for (i = JoyInfo.hats - 1; i >= 0; i--)
1060 	{
1061 		Uint8 hat = SDL_JoystickGetHat(JoyInfo.dev, i);
1062 
1063 		if (hat & SDL_HAT_UP   ) joyhats|=(UINT64)0x1<<(0 + 4*i);
1064 		if (hat & SDL_HAT_DOWN ) joyhats|=(UINT64)0x1<<(1 + 4*i);
1065 		if (hat & SDL_HAT_LEFT ) joyhats|=(UINT64)0x1<<(2 + 4*i);
1066 		if (hat & SDL_HAT_RIGHT) joyhats|=(UINT64)0x1<<(3 + 4*i);
1067 	}
1068 
1069 	if (joyhats != lastjoyhats)
1070 	{
1071 		INT64 j = 1; // keep only bits that changed since last time
1072 		INT64 newhats = joyhats ^ lastjoyhats;
1073 		lastjoyhats = joyhats;
1074 
1075 		for (i = 0; i < JOYHATS*4; i++, j <<= 1)
1076 		{
1077 			if (newhats & j) // hat changed state?
1078 			{
1079 				if (joyhats & j)
1080 					event.type = ev_keydown;
1081 				else
1082 					event.type = ev_keyup;
1083 				event.data1 = KEY_HAT1 + i;
1084 				D_PostEvent(&event);
1085 			}
1086 		}
1087 	}
1088 
1089 #if 0
1090 	// send joystick axis positions
1091 	event.type = ev_joystick;
1092 
1093 	for (i = JOYAXISSET - 1; i >= 0; i--)
1094 	{
1095 		event.data1 = i;
1096 		if (i*2 + 1 <= JoyInfo.axises)
1097 			axisx = SDL_JoystickGetAxis(JoyInfo.dev, i*2 + 0);
1098 		else axisx = 0;
1099 		if (i*2 + 2 <= JoyInfo.axises)
1100 			axisy = SDL_JoystickGetAxis(JoyInfo.dev, i*2 + 1);
1101 		else axisy = 0;
1102 
1103 
1104 		// -32768 to 32767
1105 		axisx = axisx/32;
1106 		axisy = axisy/32;
1107 
1108 
1109 		if (Joystick.bGamepadStyle)
1110 		{
1111 			// gamepad control type, on or off, live or die
1112 			if (axisx < -(JOYAXISRANGE/2))
1113 				event.data2 = -1;
1114 			else if (axisx > (JOYAXISRANGE/2))
1115 				event.data2 = 1;
1116 			else event.data2 = 0;
1117 			if (axisy < -(JOYAXISRANGE/2))
1118 				event.data3 = -1;
1119 			else if (axisy > (JOYAXISRANGE/2))
1120 				event.data3 = 1;
1121 			else event.data3 = 0;
1122 		}
1123 		else
1124 		{
1125 
1126 			axisx = JoyInfo.scale?((axisx/JoyInfo.scale)*JoyInfo.scale):axisx;
1127 			axisy = JoyInfo.scale?((axisy/JoyInfo.scale)*JoyInfo.scale):axisy;
1128 
1129 #ifdef SDL_JDEADZONE
1130 			if (-SDL_JDEADZONE <= axisx && axisx <= SDL_JDEADZONE) axisx = 0;
1131 			if (-SDL_JDEADZONE <= axisy && axisy <= SDL_JDEADZONE) axisy = 0;
1132 #endif
1133 
1134 			// analog control style , just send the raw data
1135 			event.data2 = axisx; // x axis
1136 			event.data3 = axisy; // y axis
1137 		}
1138 		D_PostEvent(&event);
1139 	}
1140 #endif
1141 }
1142 
1143 /**	\brief	Open joystick handle
1144 
1145 	\param	fname	name of joystick
1146 
1147 	\return	axises
1148 
1149 
1150 */
joy_open(int joyindex)1151 static int joy_open(int joyindex)
1152 {
1153 	SDL_Joystick *newdev = NULL;
1154 	int num_joy = 0;
1155 
1156 	if (SDL_WasInit(SDL_INIT_JOYSTICK) == 0)
1157 	{
1158 		CONS_Printf(M_GetText("Joystick subsystem not started\n"));
1159 		return -1;
1160 	}
1161 
1162 	if (joyindex <= 0)
1163 		return -1;
1164 
1165 	num_joy = SDL_NumJoysticks();
1166 
1167 	if (num_joy == 0)
1168 	{
1169 		CONS_Printf("%s", M_GetText("Found no joysticks on this system\n"));
1170 		return -1;
1171 	}
1172 
1173 	newdev = SDL_JoystickOpen(joyindex-1);
1174 
1175 	// Handle the edge case where the device <-> joystick index assignment can change due to hotplugging
1176 	// This indexing is SDL's responsibility and there's not much we can do about it.
1177 	//
1178 	// Example:
1179 	// 1. Plug Controller A   -> Index 0 opened
1180 	// 2. Plug Controller B   -> Index 1 opened
1181 	// 3. Unplug Controller A -> Index 0 closed, Index 1 active
1182 	// 4. Unplug Controller B -> Index 0 inactive, Index 1 closed
1183 	// 5. Plug Controller B   -> Index 0 opened
1184 	// 6. Plug Controller A   -> Index 0 REPLACED, opened as Controller A; Index 1 is now Controller B
1185 	if (JoyInfo.dev)
1186 	{
1187 		if (JoyInfo.dev == newdev // same device, nothing to do
1188 			|| (newdev == NULL && SDL_JoystickGetAttached(JoyInfo.dev))) // we failed, but already have a working device
1189 			return JoyInfo.axises;
1190 		// Else, we're changing devices, so send neutral joy events
1191 		CONS_Debug(DBG_GAMELOGIC, "Joystick1 device is changing; resetting events...\n");
1192 		I_ShutdownJoystick();
1193 	}
1194 
1195 	JoyInfo.dev = newdev;
1196 
1197 	if (JoyInfo.dev == NULL)
1198 	{
1199 		CONS_Debug(DBG_GAMELOGIC, M_GetText("Joystick1: Couldn't open device - %s\n"), SDL_GetError());
1200 		return -1;
1201 	}
1202 	else
1203 	{
1204 		CONS_Debug(DBG_GAMELOGIC, M_GetText("Joystick1: %s\n"), SDL_JoystickName(JoyInfo.dev));
1205 		JoyInfo.axises = SDL_JoystickNumAxes(JoyInfo.dev);
1206 		if (JoyInfo.axises > JOYAXISSET*2)
1207 			JoyInfo.axises = JOYAXISSET*2;
1208 	/*		if (joyaxes<2)
1209 		{
1210 			I_OutputMsg("Not enought axes?\n");
1211 			return 0;
1212 		}*/
1213 
1214 		JoyInfo.buttons = SDL_JoystickNumButtons(JoyInfo.dev);
1215 		if (JoyInfo.buttons > JOYBUTTONS)
1216 			JoyInfo.buttons = JOYBUTTONS;
1217 
1218 		JoyInfo.hats = SDL_JoystickNumHats(JoyInfo.dev);
1219 		if (JoyInfo.hats > JOYHATS)
1220 			JoyInfo.hats = JOYHATS;
1221 
1222 		JoyInfo.balls = SDL_JoystickNumBalls(JoyInfo.dev);
1223 
1224 		//Joystick.bGamepadStyle = !stricmp(SDL_JoystickName(JoyInfo.dev), "pad");
1225 
1226 		return JoyInfo.axises;
1227 	}
1228 }
1229 
1230 //Joystick2
1231 
1232 /**	\brief Joystick 2 buttons states
1233 */
1234 static UINT64 lastjoy2buttons = 0;
1235 
1236 /**	\brief Joystick 2 hats state
1237 */
1238 static UINT64 lastjoy2hats = 0;
1239 
1240 /**	\brief	Shuts down joystick 2
1241 
1242 
1243 	\return	void
1244 */
I_ShutdownJoystick2(void)1245 void I_ShutdownJoystick2(void)
1246 {
1247 	INT32 i;
1248 	event_t event;
1249 	event.type = ev_keyup;
1250 	event.data2 = 0;
1251 	event.data3 = 0;
1252 
1253 	lastjoy2buttons = lastjoy2hats = 0;
1254 
1255 	// emulate the up of all joystick buttons
1256 	for (i = 0; i < JOYBUTTONS; i++)
1257 	{
1258 		event.data1 = KEY_2JOY1 + i;
1259 		D_PostEvent(&event);
1260 	}
1261 
1262 	// emulate the up of all joystick hats
1263 	for (i = 0; i < JOYHATS*4; i++)
1264 	{
1265 		event.data1 = KEY_2HAT1 + i;
1266 		D_PostEvent(&event);
1267 	}
1268 
1269 	// reset joystick position
1270 	event.type = ev_joystick2;
1271 	for (i = 0; i < JOYAXISSET; i++)
1272 	{
1273 		event.data1 = i;
1274 		D_PostEvent(&event);
1275 	}
1276 
1277 	joystick2_started = 0;
1278 	JoyReset(&JoyInfo2);
1279 
1280 	// don't shut down the subsystem here, because hotplugging
1281 }
1282 
I_GetJoystick2Events(void)1283 void I_GetJoystick2Events(void)
1284 {
1285 	static event_t event = {0,0,0,0};
1286 	INT32 i = 0;
1287 	UINT64 joyhats = 0;
1288 #if 0
1289 	INT64 joybuttons = 0;
1290 	INT32 axisx, axisy;
1291 #endif
1292 
1293 	if (!joystick2_started)
1294 		return;
1295 
1296 	if (!JoyInfo2.dev) //I_ShutdownJoystick2();
1297 		return;
1298 
1299 
1300 #if 0
1301 	//faB: look for as much buttons as g_input code supports,
1302 	//  we don't use the others
1303 	for (i = JoyInfo2.buttons - 1; i >= 0; i--)
1304 	{
1305 		joybuttons <<= 1;
1306 		if (SDL_JoystickGetButton(JoyInfo2.dev,i))
1307 			joybuttons |= 1;
1308 	}
1309 
1310 	if (joybuttons != lastjoy2buttons)
1311 	{
1312 		INT64 j = 1; // keep only bits that changed since last time
1313 		INT64 newbuttons = joybuttons ^ lastjoy2buttons;
1314 		lastjoy2buttons = joybuttons;
1315 
1316 		for (i = 0; i < JOYBUTTONS; i++, j <<= 1)
1317 		{
1318 			if (newbuttons & j) // button changed state?
1319 			{
1320 				if (joybuttons & j)
1321 					event.type = ev_keydown;
1322 				else
1323 					event.type = ev_keyup;
1324 				event.data1 = KEY_2JOY1 + i;
1325 				D_PostEvent(&event);
1326 			}
1327 		}
1328 	}
1329 #endif
1330 
1331 	for (i = JoyInfo2.hats - 1; i >= 0; i--)
1332 	{
1333 		Uint8 hat = SDL_JoystickGetHat(JoyInfo2.dev, i);
1334 
1335 		if (hat & SDL_HAT_UP   ) joyhats|=(UINT64)0x1<<(0 + 4*i);
1336 		if (hat & SDL_HAT_DOWN ) joyhats|=(UINT64)0x1<<(1 + 4*i);
1337 		if (hat & SDL_HAT_LEFT ) joyhats|=(UINT64)0x1<<(2 + 4*i);
1338 		if (hat & SDL_HAT_RIGHT) joyhats|=(UINT64)0x1<<(3 + 4*i);
1339 	}
1340 
1341 	if (joyhats != lastjoy2hats)
1342 	{
1343 		INT64 j = 1; // keep only bits that changed since last time
1344 		INT64 newhats = joyhats ^ lastjoy2hats;
1345 		lastjoy2hats = joyhats;
1346 
1347 		for (i = 0; i < JOYHATS*4; i++, j <<= 1)
1348 		{
1349 			if (newhats & j) // hat changed state?
1350 			{
1351 				if (joyhats & j)
1352 					event.type = ev_keydown;
1353 				else
1354 					event.type = ev_keyup;
1355 				event.data1 = KEY_2HAT1 + i;
1356 				D_PostEvent(&event);
1357 			}
1358 		}
1359 	}
1360 
1361 #if 0
1362 	// send joystick axis positions
1363 	event.type = ev_joystick2;
1364 
1365 	for (i = JOYAXISSET - 1; i >= 0; i--)
1366 	{
1367 		event.data1 = i;
1368 		if (i*2 + 1 <= JoyInfo2.axises)
1369 			axisx = SDL_JoystickGetAxis(JoyInfo2.dev, i*2 + 0);
1370 		else axisx = 0;
1371 		if (i*2 + 2 <= JoyInfo2.axises)
1372 			axisy = SDL_JoystickGetAxis(JoyInfo2.dev, i*2 + 1);
1373 		else axisy = 0;
1374 
1375 		// -32768 to 32767
1376 		axisx = axisx/32;
1377 		axisy = axisy/32;
1378 
1379 		if (Joystick2.bGamepadStyle)
1380 		{
1381 			// gamepad control type, on or off, live or die
1382 			if (axisx < -(JOYAXISRANGE/2))
1383 				event.data2 = -1;
1384 			else if (axisx > (JOYAXISRANGE/2))
1385 				event.data2 = 1;
1386 			else
1387 				event.data2 = 0;
1388 			if (axisy < -(JOYAXISRANGE/2))
1389 				event.data3 = -1;
1390 			else if (axisy > (JOYAXISRANGE/2))
1391 				event.data3 = 1;
1392 			else
1393 				event.data3 = 0;
1394 		}
1395 		else
1396 		{
1397 
1398 			axisx = JoyInfo2.scale?((axisx/JoyInfo2.scale)*JoyInfo2.scale):axisx;
1399 			axisy = JoyInfo2.scale?((axisy/JoyInfo2.scale)*JoyInfo2.scale):axisy;
1400 
1401 #ifdef SDL_JDEADZONE
1402 			if (-SDL_JDEADZONE <= axisx && axisx <= SDL_JDEADZONE) axisx = 0;
1403 			if (-SDL_JDEADZONE <= axisy && axisy <= SDL_JDEADZONE) axisy = 0;
1404 #endif
1405 
1406 			// analog control style , just send the raw data
1407 			event.data2 = axisx; // x axis
1408 			event.data3 = axisy; // y axis
1409 		}
1410 		D_PostEvent(&event);
1411 	}
1412 #endif
1413 }
1414 
1415 /**	\brief	Open joystick handle
1416 
1417 	\param	fname	name of joystick
1418 
1419 	\return	axises
1420 
1421 
1422 */
joy_open2(int joyindex)1423 static int joy_open2(int joyindex)
1424 {
1425 	SDL_Joystick *newdev = NULL;
1426 	int num_joy = 0;
1427 
1428 	if (SDL_WasInit(SDL_INIT_JOYSTICK) == 0)
1429 	{
1430 		CONS_Printf(M_GetText("Joystick subsystem not started\n"));
1431 		return -1;
1432 	}
1433 
1434 	if (joyindex <= 0)
1435 		return -1;
1436 
1437 	num_joy = SDL_NumJoysticks();
1438 
1439 	if (num_joy == 0)
1440 	{
1441 		CONS_Printf("%s", M_GetText("Found no joysticks on this system\n"));
1442 		return -1;
1443 	}
1444 
1445 	newdev = SDL_JoystickOpen(joyindex-1);
1446 
1447 	// Handle the edge case where the device <-> joystick index assignment can change due to hotplugging
1448 	// This indexing is SDL's responsibility and there's not much we can do about it.
1449 	//
1450 	// Example:
1451 	// 1. Plug Controller A   -> Index 0 opened
1452 	// 2. Plug Controller B   -> Index 1 opened
1453 	// 3. Unplug Controller A -> Index 0 closed, Index 1 active
1454 	// 4. Unplug Controller B -> Index 0 inactive, Index 1 closed
1455 	// 5. Plug Controller B   -> Index 0 opened
1456 	// 6. Plug Controller A   -> Index 0 REPLACED, opened as Controller A; Index 1 is now Controller B
1457 	if (JoyInfo2.dev)
1458 	{
1459 		if (JoyInfo2.dev == newdev // same device, nothing to do
1460 			|| (newdev == NULL && SDL_JoystickGetAttached(JoyInfo2.dev))) // we failed, but already have a working device
1461 			return JoyInfo.axises;
1462 		// Else, we're changing devices, so send neutral joy events
1463 		CONS_Debug(DBG_GAMELOGIC, "Joystick2 device is changing; resetting events...\n");
1464 		I_ShutdownJoystick2();
1465 	}
1466 
1467 	JoyInfo2.dev = newdev;
1468 
1469 	if (JoyInfo2.dev == NULL)
1470 	{
1471 		CONS_Debug(DBG_GAMELOGIC, M_GetText("Joystick2: couldn't open device - %s\n"), SDL_GetError());
1472 		return -1;
1473 	}
1474 	else
1475 	{
1476 		CONS_Debug(DBG_GAMELOGIC, M_GetText("Joystick2: %s\n"), SDL_JoystickName(JoyInfo2.dev));
1477 		JoyInfo2.axises = SDL_JoystickNumAxes(JoyInfo2.dev);
1478 		if (JoyInfo2.axises > JOYAXISSET*2)
1479 			JoyInfo2.axises = JOYAXISSET*2;
1480 /*		if (joyaxes<2)
1481 		{
1482 			I_OutputMsg("Not enought axes?\n");
1483 			return 0;
1484 		}*/
1485 
1486 		JoyInfo2.buttons = SDL_JoystickNumButtons(JoyInfo2.dev);
1487 		if (JoyInfo2.buttons > JOYBUTTONS)
1488 			JoyInfo2.buttons = JOYBUTTONS;
1489 
1490 		JoyInfo2.hats = SDL_JoystickNumHats(JoyInfo2.dev);
1491 		if (JoyInfo2.hats > JOYHATS)
1492 			JoyInfo2.hats = JOYHATS;
1493 
1494 		JoyInfo2.balls = SDL_JoystickNumBalls(JoyInfo2.dev);
1495 
1496 		//Joystick.bGamepadStyle = !stricmp(SDL_JoystickName(JoyInfo2.dev), "pad");
1497 
1498 		return JoyInfo2.axises;
1499 	}
1500 }
1501 
1502 //
1503 // I_InitJoystick
1504 //
I_InitJoystick(void)1505 void I_InitJoystick(void)
1506 {
1507 	SDL_Joystick *newjoy = NULL;
1508 
1509 	//I_ShutdownJoystick();
1510 	if (M_CheckParm("-nojoy"))
1511 		return;
1512 
1513 	if (M_CheckParm("-noxinput"))
1514 		SDL_SetHintWithPriority("SDL_XINPUT_ENABLED", "0", SDL_HINT_OVERRIDE);
1515 
1516 	if (M_CheckParm("-nohidapi"))
1517 		SDL_SetHintWithPriority("SDL_JOYSTICK_HIDAPI", "0", SDL_HINT_OVERRIDE);
1518 
1519 	if (SDL_WasInit(SDL_INIT_JOYSTICK) == 0)
1520 	{
1521 		CONS_Printf("I_InitJoystick()...\n");
1522 
1523 		if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) == -1)
1524 		{
1525 			CONS_Printf(M_GetText("Couldn't initialize joystick: %s\n"), SDL_GetError());
1526 			return;
1527 		}
1528 	}
1529 
1530 	if (cv_usejoystick.value)
1531 		newjoy = SDL_JoystickOpen(cv_usejoystick.value-1);
1532 
1533 	if (newjoy && JoyInfo2.dev == newjoy) // don't override an active device
1534 		cv_usejoystick.value = I_GetJoystickDeviceIndex(JoyInfo.dev) + 1;
1535 	else if (newjoy && joy_open(cv_usejoystick.value) != -1)
1536 	{
1537 		// SDL's device indexes are unstable, so cv_usejoystick may not match
1538 		// the actual device index. So let's cheat a bit and find the device's current index.
1539 		JoyInfo.oldjoy = I_GetJoystickDeviceIndex(JoyInfo.dev) + 1;
1540 		joystick_started = 1;
1541 	}
1542 	else
1543 	{
1544 		if (JoyInfo.oldjoy)
1545 			I_ShutdownJoystick();
1546 		cv_usejoystick.value = 0;
1547 		joystick_started = 0;
1548 	}
1549 
1550 	if (JoyInfo.dev != newjoy && JoyInfo2.dev != newjoy)
1551 		SDL_JoystickClose(newjoy);
1552 }
1553 
I_InitJoystick2(void)1554 void I_InitJoystick2(void)
1555 {
1556 	SDL_Joystick *newjoy = NULL;
1557 
1558 	//I_ShutdownJoystick2();
1559 	if (M_CheckParm("-nojoy"))
1560 		return;
1561 
1562 	if (M_CheckParm("-noxinput"))
1563 		SDL_SetHintWithPriority("SDL_XINPUT_ENABLED", "0", SDL_HINT_OVERRIDE);
1564 
1565 	if (M_CheckParm("-nohidapi"))
1566 		SDL_SetHintWithPriority("SDL_JOYSTICK_HIDAPI", "0", SDL_HINT_OVERRIDE);
1567 
1568 	if (SDL_WasInit(SDL_INIT_JOYSTICK) == 0)
1569 	{
1570 		CONS_Printf("I_InitJoystick2()...\n");
1571 
1572 		if (SDL_InitSubSystem(SDL_INIT_JOYSTICK) == -1)
1573 		{
1574 			CONS_Printf(M_GetText("Couldn't initialize joystick: %s\n"), SDL_GetError());
1575 			return;
1576 		}
1577 	}
1578 
1579 	if (cv_usejoystick2.value)
1580 		newjoy = SDL_JoystickOpen(cv_usejoystick2.value-1);
1581 
1582 	if (newjoy && JoyInfo.dev == newjoy) // don't override an active device
1583 		cv_usejoystick2.value = I_GetJoystickDeviceIndex(JoyInfo2.dev) + 1;
1584 	else if (newjoy && joy_open2(cv_usejoystick2.value) != -1)
1585 	{
1586 		// SDL's device indexes are unstable, so cv_usejoystick may not match
1587 		// the actual device index. So let's cheat a bit and find the device's current index.
1588 		JoyInfo2.oldjoy = I_GetJoystickDeviceIndex(JoyInfo2.dev) + 1;
1589 		joystick2_started = 1;
1590 	}
1591 	else
1592 	{
1593 		if (JoyInfo2.oldjoy)
1594 			I_ShutdownJoystick2();
1595 		cv_usejoystick2.value = 0;
1596 		joystick2_started = 0;
1597 	}
1598 
1599 	if (JoyInfo.dev != newjoy && JoyInfo2.dev != newjoy)
1600 		SDL_JoystickClose(newjoy);
1601 }
1602 
I_ShutdownInput(void)1603 static void I_ShutdownInput(void)
1604 {
1605 	// Yes, the name is misleading: these send neutral events to
1606 	// clean up the unplugged joystick's input
1607 	// Note these methods are internal to this file, not called elsewhere.
1608 	I_ShutdownJoystick();
1609 	I_ShutdownJoystick2();
1610 
1611 	if (SDL_WasInit(SDL_INIT_JOYSTICK) == SDL_INIT_JOYSTICK)
1612 	{
1613 		CONS_Printf("Shutting down joy system\n");
1614 		SDL_QuitSubSystem(SDL_INIT_JOYSTICK);
1615 		I_OutputMsg("I_Joystick: SDL's Joystick system has been shutdown\n");
1616 	}
1617 }
1618 
I_NumJoys(void)1619 INT32 I_NumJoys(void)
1620 {
1621 	INT32 numjoy = 0;
1622 	if (SDL_WasInit(SDL_INIT_JOYSTICK) == SDL_INIT_JOYSTICK)
1623 		numjoy = SDL_NumJoysticks();
1624 	return numjoy;
1625 }
1626 
1627 static char joyname[255]; // joystick name is straight from the driver
1628 
I_GetJoyName(INT32 joyindex)1629 const char *I_GetJoyName(INT32 joyindex)
1630 {
1631 	const char *tempname = NULL;
1632 	joyname[0] = 0;
1633 	joyindex--; //SDL's Joystick System starts at 0, not 1
1634 	if (SDL_WasInit(SDL_INIT_JOYSTICK) == SDL_INIT_JOYSTICK)
1635 	{
1636 		tempname = SDL_JoystickNameForIndex(joyindex);
1637 		if (tempname)
1638 			strncpy(joyname, tempname, 255);
1639 	}
1640 	return joyname;
1641 }
1642 
1643 #ifndef NOMUMBLE
1644 #ifdef HAVE_MUMBLE
1645 // Best Mumble positional audio settings:
1646 // Minimum distance 3.0 m
1647 // Bloom 175%
1648 // Maximum distance 80.0 m
1649 // Minimum volume 50%
1650 #define DEG2RAD (0.017453292519943295769236907684883l) // TAU/360 or PI/180
1651 #define MUMBLEUNIT (64.0f) // FRACUNITS in a Meter
1652 
1653 static struct {
1654 #ifdef WINMUMBLE
1655 	UINT32 uiVersion;
1656 	DWORD uiTick;
1657 #else
1658 	Uint32 uiVersion;
1659 	Uint32 uiTick;
1660 #endif
1661 	float fAvatarPosition[3];
1662 	float fAvatarFront[3];
1663 	float fAvatarTop[3]; // defaults to Y-is-up (only used for leaning)
1664 	wchar_t name[256]; // game name
1665 	float fCameraPosition[3];
1666 	float fCameraFront[3];
1667 	float fCameraTop[3]; // defaults to Y-is-up (only used for leaning)
1668 	wchar_t identity[256]; // player id
1669 #ifdef WINMUMBLE
1670 	UINT32 context_len;
1671 #else
1672 	Uint32 context_len;
1673 #endif
1674 	unsigned char context[256]; // server/team
1675 	wchar_t description[2048]; // game description
1676 } *mumble = NULL;
1677 #endif // HAVE_MUMBLE
1678 
I_SetupMumble(void)1679 static void I_SetupMumble(void)
1680 {
1681 #ifdef WINMUMBLE
1682 	HANDLE hMap = OpenFileMappingW(FILE_MAP_ALL_ACCESS, FALSE, L"MumbleLink");
1683 	if (!hMap)
1684 		return;
1685 
1686 	mumble = MapViewOfFile(hMap, FILE_MAP_ALL_ACCESS, 0, 0, sizeof(*mumble));
1687 	if (!mumble)
1688 		CloseHandle(hMap);
1689 #elif defined (HAVE_SHM)
1690 	int shmfd;
1691 	char memname[256];
1692 
1693 	snprintf(memname, 256, "/MumbleLink.%d", getuid());
1694 	shmfd = shm_open(memname, O_RDWR, S_IRUSR | S_IWUSR);
1695 
1696 	if(shmfd < 0)
1697 		return;
1698 
1699 	mumble = mmap(NULL, sizeof(*mumble), PROT_READ | PROT_WRITE, MAP_SHARED, shmfd, 0);
1700 	if (mumble == MAP_FAILED)
1701 		mumble = NULL;
1702 #endif
1703 }
1704 
I_UpdateMumble(const mobj_t * mobj,const listener_t listener)1705 void I_UpdateMumble(const mobj_t *mobj, const listener_t listener)
1706 {
1707 #ifdef HAVE_MUMBLE
1708 	double angle;
1709 	fixed_t anglef;
1710 
1711 	if (!mumble)
1712 		return;
1713 
1714 	if(mumble->uiVersion != 2) {
1715 		wcsncpy(mumble->name, L"SRB2 "VERSIONSTRINGW, 256);
1716 		wcsncpy(mumble->description, L"Sonic Robo Blast 2 with integrated Mumble Link support.", 2048);
1717 		mumble->uiVersion = 2;
1718 	}
1719 	mumble->uiTick++;
1720 
1721 	if (!netgame || gamestate != GS_LEVEL) { // Zero out, but never delink.
1722 		mumble->fAvatarPosition[0] = mumble->fAvatarPosition[1] = mumble->fAvatarPosition[2] = 0.0f;
1723 		mumble->fAvatarFront[0] = 1.0f;
1724 		mumble->fAvatarFront[1] = mumble->fAvatarFront[2] = 0.0f;
1725 		mumble->fCameraPosition[0] = mumble->fCameraPosition[1] = mumble->fCameraPosition[2] = 0.0f;
1726 		mumble->fCameraFront[0] = 1.0f;
1727 		mumble->fCameraFront[1] = mumble->fCameraFront[2] = 0.0f;
1728 		return;
1729 	}
1730 
1731 	{
1732 		UINT8 *p = mumble->context;
1733 		WRITEMEM(p, server_context, 8);
1734 		WRITEINT16(p, gamemap);
1735 		mumble->context_len = (UINT32)(p - mumble->context);
1736 	}
1737 
1738 	if (mobj) {
1739 		mumble->fAvatarPosition[0] = FIXED_TO_FLOAT(mobj->x) / MUMBLEUNIT;
1740 		mumble->fAvatarPosition[1] = FIXED_TO_FLOAT(mobj->z) / MUMBLEUNIT;
1741 		mumble->fAvatarPosition[2] = FIXED_TO_FLOAT(mobj->y) / MUMBLEUNIT;
1742 
1743 		anglef = AngleFixed(mobj->angle);
1744 		angle = FIXED_TO_FLOAT(anglef)*DEG2RAD;
1745 		mumble->fAvatarFront[0] = (float)cos(angle);
1746 		mumble->fAvatarFront[1] = 0.0f;
1747 		mumble->fAvatarFront[2] = (float)sin(angle);
1748 	} else {
1749 		mumble->fAvatarPosition[0] = mumble->fAvatarPosition[1] = mumble->fAvatarPosition[2] = 0.0f;
1750 		mumble->fAvatarFront[0] = 1.0f;
1751 		mumble->fAvatarFront[1] = mumble->fAvatarFront[2] = 0.0f;
1752 	}
1753 
1754 	mumble->fCameraPosition[0] = FIXED_TO_FLOAT(listener.x) / MUMBLEUNIT;
1755 	mumble->fCameraPosition[1] = FIXED_TO_FLOAT(listener.z) / MUMBLEUNIT;
1756 	mumble->fCameraPosition[2] = FIXED_TO_FLOAT(listener.y) / MUMBLEUNIT;
1757 
1758 	anglef = AngleFixed(listener.angle);
1759 	angle = FIXED_TO_FLOAT(anglef)*DEG2RAD;
1760 	mumble->fCameraFront[0] = (float)cos(angle);
1761 	mumble->fCameraFront[1] = 0.0f;
1762 	mumble->fCameraFront[2] = (float)sin(angle);
1763 #else
1764 	(void)mobj;
1765 	(void)listener;
1766 #endif // HAVE_MUMBLE
1767 }
1768 #undef WINMUMBLE
1769 #endif // NOMUMBLE
1770 
1771 #ifdef HAVE_TERMIOS
1772 
I_GetMouseEvents(void)1773 void I_GetMouseEvents(void)
1774 {
1775 	static UINT8 mdata[5];
1776 	static INT32 i = 0,om2b = 0;
1777 	INT32 di, j, mlp, button;
1778 	event_t event;
1779 	const INT32 mswap[8] = {0, 4, 1, 5, 2, 6, 3, 7};
1780 
1781 	if (!mouse2_started) return;
1782 	for (mlp = 0; mlp < 20; mlp++)
1783 	{
1784 		for (; i < 5; i++)
1785 		{
1786 			di = read(fdmouse2, mdata+i, 1);
1787 			if (di == -1) return;
1788 		}
1789 		if ((mdata[0] & 0xf8) != 0x80)
1790 		{
1791 			for (j = 1; j < 5; j++)
1792 				if ((mdata[j] & 0xf8) == 0x80)
1793 					for (i = 0; i < 5-j; i++) // shift
1794 						mdata[i] = mdata[i+j];
1795 			if (i < 5) continue;
1796 		}
1797 		else
1798 		{
1799 			button = mswap[~mdata[0] & 0x07];
1800 			for (j = 0; j < MOUSEBUTTONS; j++)
1801 			{
1802 				if (om2b & (1<<j))
1803 				{
1804 					if (!(button & (1<<j))) //keyup
1805 					{
1806 						event.type = ev_keyup;
1807 						event.data1 = KEY_2MOUSE1+j;
1808 						D_PostEvent(&event);
1809 						om2b ^= 1 << j;
1810 					}
1811 				}
1812 				else
1813 				{
1814 					if (button & (1<<j))
1815 					{
1816 						event.type = ev_keydown;
1817 						event.data1 = KEY_2MOUSE1+j;
1818 						D_PostEvent(&event);
1819 						om2b ^= 1 << j;
1820 					}
1821 				}
1822 			}
1823 			event.data2 = ((SINT8)mdata[1])+((SINT8)mdata[3]);
1824 			event.data3 = ((SINT8)mdata[2])+((SINT8)mdata[4]);
1825 			if (event.data2 && event.data3)
1826 			{
1827 				event.type = ev_mouse2;
1828 				event.data1 = 0;
1829 				D_PostEvent(&event);
1830 			}
1831 		}
1832 		i = 0;
1833 	}
1834 }
1835 
1836 //
1837 // I_ShutdownMouse2
1838 //
I_ShutdownMouse2(void)1839 static void I_ShutdownMouse2(void)
1840 {
1841 	if (fdmouse2 != -1) close(fdmouse2);
1842 	mouse2_started = 0;
1843 }
1844 #elif defined (_WIN32)
1845 
1846 static HANDLE mouse2filehandle = INVALID_HANDLE_VALUE;
1847 
I_ShutdownMouse2(void)1848 static void I_ShutdownMouse2(void)
1849 {
1850 	event_t event;
1851 	INT32 i;
1852 
1853 	if (mouse2filehandle == INVALID_HANDLE_VALUE)
1854 		return;
1855 
1856 	SetCommMask(mouse2filehandle, 0);
1857 
1858 	EscapeCommFunction(mouse2filehandle, CLRDTR);
1859 	EscapeCommFunction(mouse2filehandle, CLRRTS);
1860 
1861 	PurgeComm(mouse2filehandle, PURGE_TXABORT | PURGE_RXABORT |
1862 			  PURGE_TXCLEAR | PURGE_RXCLEAR);
1863 
1864 	CloseHandle(mouse2filehandle);
1865 
1866 	// emulate the up of all mouse buttons
1867 	for (i = 0; i < MOUSEBUTTONS; i++)
1868 	{
1869 		event.type = ev_keyup;
1870 		event.data1 = KEY_2MOUSE1+i;
1871 		D_PostEvent(&event);
1872 	}
1873 
1874 	mouse2filehandle = INVALID_HANDLE_VALUE;
1875 }
1876 
1877 #define MOUSECOMBUFFERSIZE 256
1878 static INT32 handlermouse2x,handlermouse2y,handlermouse2buttons;
1879 
I_PoolMouse2(void)1880 static void I_PoolMouse2(void)
1881 {
1882 	UINT8 buffer[MOUSECOMBUFFERSIZE];
1883 	COMSTAT ComStat;
1884 	DWORD dwErrorFlags;
1885 	DWORD dwLength;
1886 	char dx,dy;
1887 
1888 	static INT32 bytenum;
1889 	static UINT8 combytes[4];
1890 	DWORD i;
1891 
1892 	ClearCommError(mouse2filehandle, &dwErrorFlags, &ComStat);
1893 	dwLength = min(MOUSECOMBUFFERSIZE, ComStat.cbInQue);
1894 
1895 	if (dwLength <= 0)
1896 		return;
1897 
1898 	if (!ReadFile(mouse2filehandle, buffer, dwLength, &dwLength, NULL))
1899 	{
1900 		CONS_Alert(CONS_WARNING, "%s", M_GetText("Read Error on secondary mouse port\n"));
1901 		return;
1902 	}
1903 
1904 	// parse the mouse packets
1905 	for (i = 0; i < dwLength; i++)
1906 	{
1907 		if ((buffer[i] & 64)== 64)
1908 			bytenum = 0;
1909 
1910 		if (bytenum < 4)
1911 			combytes[bytenum] = buffer[i];
1912 		bytenum++;
1913 
1914 		if (bytenum == 1)
1915 		{
1916 			handlermouse2buttons &= ~3;
1917 			handlermouse2buttons |= ((combytes[0] & (32+16)) >> 4);
1918 		}
1919 		else if (bytenum == 3)
1920 		{
1921 			dx = (char)((combytes[0] &  3) << 6);
1922 			dy = (char)((combytes[0] & 12) << 4);
1923 			dx = (char)(dx + combytes[1]);
1924 			dy = (char)(dy + combytes[2]);
1925 			handlermouse2x+= dx;
1926 			handlermouse2y+= dy;
1927 		}
1928 		else if (bytenum == 4) // fourth UINT8 (logitech mouses)
1929 		{
1930 			if (buffer[i] & 32)
1931 				handlermouse2buttons |= 4;
1932 			else
1933 				handlermouse2buttons &= ~4;
1934 		}
1935 	}
1936 }
1937 
I_GetMouseEvents(void)1938 void I_GetMouseEvents(void)
1939 {
1940 	static UINT8 lastbuttons2 = 0; //mouse movement
1941 	event_t event;
1942 
1943 	if (mouse2filehandle == INVALID_HANDLE_VALUE)
1944 		return;
1945 
1946 	I_PoolMouse2();
1947 	// post key event for buttons
1948 	if (handlermouse2buttons != lastbuttons2)
1949 	{
1950 		INT32 i, j = 1, k;
1951 		k = (handlermouse2buttons ^ lastbuttons2); // only changed bit to 1
1952 		lastbuttons2 = (UINT8)handlermouse2buttons;
1953 
1954 		for (i = 0; i < MOUSEBUTTONS; i++, j <<= 1)
1955 			if (k & j)
1956 			{
1957 				if (handlermouse2buttons & j)
1958 					event.type = ev_keydown;
1959 				else
1960 					event.type = ev_keyup;
1961 				event.data1 = KEY_2MOUSE1+i;
1962 				D_PostEvent(&event);
1963 			}
1964 	}
1965 
1966 	if (handlermouse2x != 0 || handlermouse2y != 0)
1967 	{
1968 		event.type = ev_mouse2;
1969 		event.data1 = 0;
1970 //		event.data1 = buttons; // not needed
1971 		event.data2 = handlermouse2x << 1;
1972 		event.data3 = -handlermouse2y << 1;
1973 		handlermouse2x = 0;
1974 		handlermouse2y = 0;
1975 
1976 		D_PostEvent(&event);
1977 	}
1978 }
1979 #else
I_GetMouseEvents(void)1980 void I_GetMouseEvents(void){};
1981 #endif
1982 
1983 //
1984 // I_StartupMouse2
1985 //
I_StartupMouse2(void)1986 void I_StartupMouse2(void)
1987 {
1988 #ifdef HAVE_TERMIOS
1989 	struct termios m2tio;
1990 	size_t i;
1991 	INT32 dtr = -1, rts = -1;;
1992 	I_ShutdownMouse2();
1993 	if (cv_usemouse2.value == 0) return;
1994 	if ((fdmouse2 = open(cv_mouse2port.string, O_RDONLY|O_NONBLOCK|O_NOCTTY)) == -1)
1995 	{
1996 		CONS_Printf(M_GetText("Error opening %s!\n"), cv_mouse2port.string);
1997 		return;
1998 	}
1999 	tcflush(fdmouse2, TCIOFLUSH);
2000 	m2tio.c_iflag = IGNBRK;
2001 	m2tio.c_oflag = 0;
2002 	m2tio.c_cflag = CREAD|CLOCAL|HUPCL|CS8|CSTOPB|B1200;
2003 	m2tio.c_lflag = 0;
2004 	m2tio.c_cc[VTIME] = 0;
2005 	m2tio.c_cc[VMIN] = 1;
2006 	tcsetattr(fdmouse2, TCSANOW, &m2tio);
2007 	for (i = 0; i < strlen(cv_mouse2opt.string); i++)
2008 	{
2009 		if (toupper(cv_mouse2opt.string[i]) == 'D')
2010 		{
2011 			if (cv_mouse2opt.string[i+1] == '-')
2012 				dtr = 0;
2013 			else
2014 				dtr = 1;
2015 		}
2016 		if (toupper(cv_mouse2opt.string[i]) == 'R')
2017 		{
2018 			if (cv_mouse2opt.string[i+1] == '-')
2019 				rts = 0;
2020 			else
2021 				rts = 1;
2022 		}
2023 		if (dtr != -1 || rts != -1)
2024 		{
2025 			INT32 c;
2026 			if (!ioctl(fdmouse2, TIOCMGET, &c))
2027 			{
2028 				if (!dtr)
2029 					c &= ~TIOCM_DTR;
2030 				else if (dtr > 0)
2031 					c |= TIOCM_DTR;
2032 			}
2033 			if (!rts)
2034 				c &= ~TIOCM_RTS;
2035 			else if (rts > 0)
2036 				c |= TIOCM_RTS;
2037 			ioctl(fdmouse2, TIOCMSET, &c);
2038 		}
2039 	}
2040 	mouse2_started = 1;
2041 	I_AddExitFunc(I_ShutdownMouse2);
2042 #elif defined (_WIN32)
2043 	DCB dcb;
2044 
2045 	if (mouse2filehandle != INVALID_HANDLE_VALUE)
2046 		I_ShutdownMouse2();
2047 
2048 	if (cv_usemouse2.value == 0)
2049 		return;
2050 
2051 	if (mouse2filehandle == INVALID_HANDLE_VALUE)
2052 	{
2053 		// COM file handle
2054 		mouse2filehandle = CreateFileA(cv_mouse2port.string, GENERIC_READ | GENERIC_WRITE,
2055 									   0,                     // exclusive access
2056 									   NULL,                  // no security attrs
2057 									   OPEN_EXISTING,
2058 									   FILE_ATTRIBUTE_NORMAL,
2059 									   NULL);
2060 		if (mouse2filehandle == INVALID_HANDLE_VALUE)
2061 		{
2062 			INT32 e = GetLastError();
2063 			if (e == 5)
2064 				CONS_Alert(CONS_ERROR, M_GetText("Can't open %s: Access denied\n"), cv_mouse2port.string);
2065 			else
2066 				CONS_Alert(CONS_ERROR, M_GetText("Can't open %s: error %d\n"), cv_mouse2port.string, e);
2067 			return;
2068 		}
2069 	}
2070 
2071 	// getevent when somthing happens
2072 	//SetCommMask(mouse2filehandle, EV_RXCHAR);
2073 
2074 	// buffers
2075 	SetupComm(mouse2filehandle, MOUSECOMBUFFERSIZE, MOUSECOMBUFFERSIZE);
2076 
2077 	// purge buffers
2078 	PurgeComm(mouse2filehandle, PURGE_TXABORT | PURGE_RXABORT
2079 			  | PURGE_TXCLEAR | PURGE_RXCLEAR);
2080 
2081 	// setup port to 1200 7N1
2082 	dcb.DCBlength = sizeof (DCB);
2083 
2084 	GetCommState(mouse2filehandle, &dcb);
2085 
2086 	dcb.BaudRate = CBR_1200;
2087 	dcb.ByteSize = 7;
2088 	dcb.Parity = NOPARITY;
2089 	dcb.StopBits = ONESTOPBIT;
2090 
2091 	dcb.fDtrControl = DTR_CONTROL_ENABLE;
2092 	dcb.fRtsControl = RTS_CONTROL_ENABLE;
2093 
2094 	dcb.fBinary = TRUE;
2095 	dcb.fParity = TRUE;
2096 
2097 	SetCommState(mouse2filehandle, &dcb);
2098 	I_AddExitFunc(I_ShutdownMouse2);
2099 #endif
2100 }
2101 
2102 //
2103 // I_Tactile
2104 //
I_Tactile(FFType pFFType,const JoyFF_t * FFEffect)2105 void I_Tactile(FFType pFFType, const JoyFF_t *FFEffect)
2106 {
2107 	// UNUSED.
2108 	(void)pFFType;
2109 	(void)FFEffect;
2110 }
2111 
I_Tactile2(FFType pFFType,const JoyFF_t * FFEffect)2112 void I_Tactile2(FFType pFFType, const JoyFF_t *FFEffect)
2113 {
2114 	// UNUSED.
2115 	(void)pFFType;
2116 	(void)FFEffect;
2117 }
2118 
2119 /**	\brief empty ticcmd for player 1
2120 */
2121 static ticcmd_t emptycmd;
2122 
I_BaseTiccmd(void)2123 ticcmd_t *I_BaseTiccmd(void)
2124 {
2125 	return &emptycmd;
2126 }
2127 
2128 /**	\brief empty ticcmd for player 2
2129 */
2130 static ticcmd_t emptycmd2;
2131 
I_BaseTiccmd2(void)2132 ticcmd_t *I_BaseTiccmd2(void)
2133 {
2134 	return &emptycmd2;
2135 }
2136 
2137 //
2138 // I_GetTime
2139 // returns time in 1/TICRATE second tics
2140 //
2141 
2142 static Uint64 timer_frequency;
2143 
2144 static double tic_frequency;
2145 static Uint64 tic_epoch;
2146 
I_GetTime(void)2147 tic_t I_GetTime(void)
2148 {
2149 	static double elapsed;
2150 
2151 	const Uint64 now = SDL_GetPerformanceCounter();
2152 
2153 	elapsed += (now - tic_epoch) / tic_frequency;
2154 	tic_epoch = now; // moving epoch
2155 
2156 	return (tic_t)elapsed;
2157 }
2158 
I_GetPreciseTime(void)2159 precise_t I_GetPreciseTime(void)
2160 {
2161 	return SDL_GetPerformanceCounter();
2162 }
2163 
I_PreciseToMicros(precise_t d)2164 int I_PreciseToMicros(precise_t d)
2165 {
2166 	return (int)(d / (timer_frequency / 1000000.0));
2167 }
2168 
2169 //
2170 //I_StartupTimer
2171 //
I_StartupTimer(void)2172 void I_StartupTimer(void)
2173 {
2174 	timer_frequency = SDL_GetPerformanceFrequency();
2175 	tic_epoch       = SDL_GetPerformanceCounter();
2176 
2177 	tic_frequency   = timer_frequency / (double)NEWTICRATE;
2178 }
2179 
I_Sleep(void)2180 void I_Sleep(void)
2181 {
2182 	if (cv_sleep.value != -1)
2183 		SDL_Delay(cv_sleep.value);
2184 }
2185 
2186 #ifdef NEWSIGNALHANDLER
newsignalhandler_Warn(const char * pr)2187 static void newsignalhandler_Warn(const char *pr)
2188 {
2189 	char text[128];
2190 
2191 	snprintf(text, sizeof text,
2192 			"Error while setting up signal reporting: %s: %s",
2193 			pr,
2194 			strerror(errno)
2195 	);
2196 
2197 	I_OutputMsg("%s\n", text);
2198 
2199 	SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR,
2200 		"Startup error",
2201 		text, NULL);
2202 
2203 	I_ShutdownConsole();
2204 	exit(-1);
2205 }
2206 
I_Fork(void)2207 static void I_Fork(void)
2208 {
2209 	int child;
2210 	int status;
2211 	int signum;
2212 	int c;
2213 
2214 	child = fork();
2215 
2216 	switch (child)
2217 	{
2218 		case -1:
2219 			newsignalhandler_Warn("fork()");
2220 			break;
2221 		case 0:
2222 			I_RegisterChildSignals();
2223 			break;
2224 		default:
2225 			if (logstream)
2226 				fclose(logstream);/* the child has this */
2227 
2228 			c = wait(&status);
2229 
2230 #ifdef LOGMESSAGES
2231 			/* By the way, exit closes files. */
2232 			logstream = fopen(logfilename, "at");
2233 #else
2234 			logstream = 0;
2235 #endif
2236 
2237 			if (c == -1)
2238 			{
2239 				kill(child, SIGKILL);
2240 				newsignalhandler_Warn("wait()");
2241 			}
2242 			else
2243 			{
2244 				if (WIFSIGNALED (status))
2245 				{
2246 					signum = WTERMSIG (status);
2247 #ifdef WCOREDUMP
2248 					I_ReportSignal(signum, WCOREDUMP (status));
2249 #else
2250 					I_ReportSignal(signum, 0);
2251 #endif
2252 					status = 128 + signum;
2253 				}
2254 				else if (WIFEXITED (status))
2255 				{
2256 					status = WEXITSTATUS (status);
2257 				}
2258 
2259 				I_ShutdownConsole();
2260 				exit(status);
2261 			}
2262 	}
2263 }
2264 #endif/*NEWSIGNALHANDLER*/
2265 
I_StartupSystem(void)2266 INT32 I_StartupSystem(void)
2267 {
2268 	SDL_version SDLcompiled;
2269 	SDL_version SDLlinked;
2270 	SDL_VERSION(&SDLcompiled)
2271 	SDL_GetVersion(&SDLlinked);
2272 #ifdef HAVE_THREADS
2273 	I_start_threads();
2274 	I_AddExitFunc(I_stop_threads);
2275 #endif
2276 	I_StartupConsole();
2277 #ifdef NEWSIGNALHANDLER
2278 	I_Fork();
2279 #endif
2280 	I_RegisterSignals();
2281 	I_OutputMsg("Compiled for SDL version: %d.%d.%d\n",
2282 	 SDLcompiled.major, SDLcompiled.minor, SDLcompiled.patch);
2283 	I_OutputMsg("Linked with SDL version: %d.%d.%d\n",
2284 	 SDLlinked.major, SDLlinked.minor, SDLlinked.patch);
2285 	if (SDL_Init(0) < 0)
2286 		I_Error("SRB2: SDL System Error: %s", SDL_GetError()); //Alam: Oh no....
2287 #ifndef NOMUMBLE
2288 	I_SetupMumble();
2289 #endif
2290 	return 0;
2291 }
2292 
2293 //
2294 // I_Quit
2295 //
I_Quit(void)2296 void I_Quit(void)
2297 {
2298 	static SDL_bool quiting = SDL_FALSE;
2299 
2300 	/* prevent recursive I_Quit() */
2301 	if (quiting) goto death;
2302 	SDLforceUngrabMouse();
2303 	quiting = SDL_FALSE;
2304 	M_SaveConfig(NULL); //save game config, cvars..
2305 #ifndef NONET
2306 	D_SaveBan(); // save the ban list
2307 #endif
2308 	G_SaveGameData(); // Tails 12-08-2002
2309 	//added:16-02-98: when recording a demo, should exit using 'q' key,
2310 	//        but sometimes we forget and use 'F10'.. so save here too.
2311 
2312 	if (demorecording)
2313 		G_CheckDemoStatus();
2314 	if (metalrecording)
2315 		G_StopMetalRecording(false);
2316 
2317 	D_QuitNetGame();
2318 	CL_AbortDownloadResume();
2319 	M_FreePlayerSetupColors();
2320 	I_ShutdownMusic();
2321 	I_ShutdownSound();
2322 	// use this for 1.28 19990220 by Kin
2323 	I_ShutdownGraphics();
2324 	I_ShutdownInput();
2325 	I_ShutdownSystem();
2326 	SDL_Quit();
2327 	/* if option -noendtxt is set, don't print the text */
2328 	if (!M_CheckParm("-noendtxt") && W_CheckNumForName("ENDOOM") != LUMPERROR)
2329 	{
2330 		printf("\r");
2331 		ShowEndTxt();
2332 	}
2333 	if (myargmalloc)
2334 		free(myargv); // Deallocate allocated memory
2335 death:
2336 	W_Shutdown();
2337 	exit(0);
2338 }
2339 
I_WaitVBL(INT32 count)2340 void I_WaitVBL(INT32 count)
2341 {
2342 	count = 1;
2343 	SDL_Delay(count);
2344 }
2345 
I_BeginRead(void)2346 void I_BeginRead(void)
2347 {
2348 }
2349 
I_EndRead(void)2350 void I_EndRead(void)
2351 {
2352 }
2353 
2354 //
2355 // I_Error
2356 //
2357 /**	\brief phuck recursive errors
2358 */
2359 static INT32 errorcount = 0;
2360 
2361 /**	\brief recursive error detecting
2362 */
2363 static boolean shutdowning = false;
2364 
I_Error(const char * error,...)2365 void I_Error(const char *error, ...)
2366 {
2367 	va_list argptr;
2368 	char buffer[8192];
2369 
2370 	// recursive error detecting
2371 	if (shutdowning)
2372 	{
2373 		errorcount++;
2374 		if (errorcount == 1)
2375 			SDLforceUngrabMouse();
2376 		// try to shutdown each subsystem separately
2377 		if (errorcount == 2)
2378 			I_ShutdownMusic();
2379 		if (errorcount == 3)
2380 			I_ShutdownSound();
2381 		if (errorcount == 4)
2382 			I_ShutdownGraphics();
2383 		if (errorcount == 5)
2384 			I_ShutdownInput();
2385 		if (errorcount == 6)
2386 			I_ShutdownSystem();
2387 		if (errorcount == 7)
2388 			SDL_Quit();
2389 		if (errorcount == 8)
2390 		{
2391 			M_SaveConfig(NULL);
2392 			G_SaveGameData();
2393 		}
2394 		if (errorcount > 20)
2395 		{
2396 			va_start(argptr, error);
2397 			vsprintf(buffer, error, argptr);
2398 			va_end(argptr);
2399 			// Implement message box with SDL_ShowSimpleMessageBox,
2400 			// which should fail gracefully if it can't put a message box up
2401 			// on the target system
2402 			SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR,
2403 				"SRB2 "VERSIONSTRING" Recursive Error",
2404 				buffer, NULL);
2405 
2406 			W_Shutdown();
2407 			exit(-1); // recursive errors detected
2408 		}
2409 	}
2410 
2411 	shutdowning = true;
2412 
2413 	// Display error message in the console before we start shutting it down
2414 	va_start(argptr, error);
2415 	vsprintf(buffer, error, argptr);
2416 	va_end(argptr);
2417 	I_OutputMsg("\nI_Error(): %s\n", buffer);
2418 	// ---
2419 
2420 	M_SaveConfig(NULL); // save game config, cvars..
2421 #ifndef NONET
2422 	D_SaveBan(); // save the ban list
2423 #endif
2424 	G_SaveGameData(); // Tails 12-08-2002
2425 
2426 	// Shutdown. Here might be other errors.
2427 	if (demorecording)
2428 		G_CheckDemoStatus();
2429 	if (metalrecording)
2430 		G_StopMetalRecording(false);
2431 
2432 	D_QuitNetGame();
2433 	CL_AbortDownloadResume();
2434 	M_FreePlayerSetupColors();
2435 	I_ShutdownMusic();
2436 	I_ShutdownSound();
2437 	// use this for 1.28 19990220 by Kin
2438 	I_ShutdownGraphics();
2439 	I_ShutdownInput();
2440 	I_ShutdownSystem();
2441 	SDL_Quit();
2442 
2443 	// Implement message box with SDL_ShowSimpleMessageBox,
2444 	// which should fail gracefully if it can't put a message box up
2445 	// on the target system
2446 	SDL_ShowSimpleMessageBox(SDL_MESSAGEBOX_ERROR,
2447 		"SRB2 "VERSIONSTRING" Error",
2448 		buffer, NULL);
2449 	// Note that SDL_ShowSimpleMessageBox does *not* require SDL to be
2450 	// initialized at the time, so calling it after SDL_Quit() is
2451 	// perfectly okay! In addition, we do this on purpose so the
2452 	// fullscreen window is closed before displaying the error message
2453 	// in case the fullscreen window blocks it for some absurd reason.
2454 
2455 	W_Shutdown();
2456 
2457 #if defined (PARANOIA) && defined (__CYGWIN__)
2458 	*(INT32 *)2 = 4; //Alam: Debug!
2459 #endif
2460 
2461 	exit(-1);
2462 }
2463 
2464 /**	\brief quit function table
2465 */
2466 static quitfuncptr quit_funcs[MAX_QUIT_FUNCS]; /* initialized to all bits 0 */
2467 
2468 //
2469 //  Adds a function to the list that need to be called by I_SystemShutdown().
2470 //
I_AddExitFunc(void (* func)())2471 void I_AddExitFunc(void (*func)())
2472 {
2473 	INT32 c;
2474 
2475 	for (c = 0; c < MAX_QUIT_FUNCS; c++)
2476 	{
2477 		if (!quit_funcs[c])
2478 		{
2479 			quit_funcs[c] = func;
2480 			break;
2481 		}
2482 	}
2483 }
2484 
2485 
2486 //
2487 //  Removes a function from the list that need to be called by
2488 //   I_SystemShutdown().
2489 //
I_RemoveExitFunc(void (* func)())2490 void I_RemoveExitFunc(void (*func)())
2491 {
2492 	INT32 c;
2493 
2494 	for (c = 0; c < MAX_QUIT_FUNCS; c++)
2495 	{
2496 		if (quit_funcs[c] == func)
2497 		{
2498 			while (c < MAX_QUIT_FUNCS-1)
2499 			{
2500 				quit_funcs[c] = quit_funcs[c+1];
2501 				c++;
2502 			}
2503 			quit_funcs[MAX_QUIT_FUNCS-1] = NULL;
2504 			break;
2505 		}
2506 	}
2507 }
2508 
2509 #if !(defined (__unix__) || defined(__APPLE__) || defined (UNIXCOMMON))
Shittycopyerror(const char * name)2510 static void Shittycopyerror(const char *name)
2511 {
2512 	I_OutputMsg(
2513 			"Error copying log file: %s: %s\n",
2514 			name,
2515 			strerror(errno)
2516 	);
2517 }
2518 
Shittylogcopy(void)2519 static void Shittylogcopy(void)
2520 {
2521 	char buf[8192];
2522 	FILE *fp;
2523 	size_t r;
2524 	if (fseek(logstream, 0, SEEK_SET) == -1)
2525 	{
2526 		Shittycopyerror("fseek");
2527 	}
2528 	else if (( fp = fopen(logfilename, "wt") ))
2529 	{
2530 		while (( r = fread(buf, 1, sizeof buf, logstream) ))
2531 		{
2532 			if (fwrite(buf, 1, r, fp) < r)
2533 			{
2534 				Shittycopyerror("fwrite");
2535 				break;
2536 			}
2537 		}
2538 		if (ferror(logstream))
2539 		{
2540 			Shittycopyerror("fread");
2541 		}
2542 		fclose(fp);
2543 	}
2544 	else
2545 	{
2546 		Shittycopyerror(logfilename);
2547 	}
2548 }
2549 #endif/*!(defined (__unix__) || defined(__APPLE__) || defined (UNIXCOMMON))*/
2550 
2551 //
2552 //  Closes down everything. This includes restoring the initial
2553 //  palette and video mode, and removing whatever mouse, keyboard, and
2554 //  timer routines have been installed.
2555 //
2556 //  NOTE: Shutdown user funcs are effectively called in reverse order.
2557 //
I_ShutdownSystem(void)2558 void I_ShutdownSystem(void)
2559 {
2560 	INT32 c;
2561 
2562 #ifndef NEWSIGNALHANDLER
2563 	I_ShutdownConsole();
2564 #endif
2565 
2566 	for (c = MAX_QUIT_FUNCS-1; c >= 0; c--)
2567 		if (quit_funcs[c])
2568 			(*quit_funcs[c])();
2569 #ifdef LOGMESSAGES
2570 	if (logstream)
2571 	{
2572 		I_OutputMsg("I_ShutdownSystem(): end of logstream.\n");
2573 #if !(defined (__unix__) || defined(__APPLE__) || defined (UNIXCOMMON))
2574 		Shittylogcopy();
2575 #endif
2576 		fclose(logstream);
2577 		logstream = NULL;
2578 	}
2579 #endif
2580 
2581 }
2582 
I_GetDiskFreeSpace(INT64 * freespace)2583 void I_GetDiskFreeSpace(INT64 *freespace)
2584 {
2585 #if defined (__unix__) || defined(__APPLE__) || defined (UNIXCOMMON)
2586 #if defined (SOLARIS) || defined (__HAIKU__)
2587 	*freespace = INT32_MAX;
2588 	return;
2589 #else // Both Linux and BSD have this, apparently.
2590 	struct statfs stfs;
2591 	if (statfs(srb2home, &stfs) == -1)
2592 	{
2593 		*freespace = INT32_MAX;
2594 		return;
2595 	}
2596 	*freespace = stfs.f_bavail * stfs.f_bsize;
2597 #endif
2598 #elif defined (_WIN32)
2599 	static p_GetDiskFreeSpaceExA pfnGetDiskFreeSpaceEx = NULL;
2600 	static boolean testwin95 = false;
2601 	ULARGE_INTEGER usedbytes, lfreespace;
2602 
2603 	if (!testwin95)
2604 	{
2605 		pfnGetDiskFreeSpaceEx = (p_GetDiskFreeSpaceExA)(LPVOID)GetProcAddress(GetModuleHandleA("kernel32.dll"), "GetDiskFreeSpaceExA");
2606 		testwin95 = true;
2607 	}
2608 	if (pfnGetDiskFreeSpaceEx)
2609 	{
2610 		if (pfnGetDiskFreeSpaceEx(srb2home, &lfreespace, &usedbytes, NULL))
2611 			*freespace = lfreespace.QuadPart;
2612 		else
2613 			*freespace = INT32_MAX;
2614 	}
2615 	else
2616 	{
2617 		DWORD SectorsPerCluster, BytesPerSector, NumberOfFreeClusters, TotalNumberOfClusters;
2618 		GetDiskFreeSpace(NULL, &SectorsPerCluster, &BytesPerSector,
2619 						 &NumberOfFreeClusters, &TotalNumberOfClusters);
2620 		*freespace = BytesPerSector*SectorsPerCluster*NumberOfFreeClusters;
2621 	}
2622 #else // Dummy for platform independent; 1GB should be enough
2623 	*freespace = 1024*1024*1024;
2624 #endif
2625 }
2626 
I_GetUserName(void)2627 char *I_GetUserName(void)
2628 {
2629 	static char username[MAXPLAYERNAME+1];
2630 	char *p;
2631 #ifdef _WIN32
2632 	DWORD i = MAXPLAYERNAME;
2633 
2634 	if (!GetUserNameA(username, &i))
2635 #endif
2636 	{
2637 		p = I_GetEnv("USER");
2638 		if (!p)
2639 		{
2640 			p = I_GetEnv("user");
2641 			if (!p)
2642 			{
2643 				p = I_GetEnv("USERNAME");
2644 				if (!p)
2645 				{
2646 					p = I_GetEnv("username");
2647 					if (!p)
2648 					{
2649 						return NULL;
2650 					}
2651 				}
2652 			}
2653 		}
2654 		strncpy(username, p, MAXPLAYERNAME);
2655 	}
2656 
2657 
2658 	if (strcmp(username, "") != 0)
2659 		return username;
2660 	return NULL; // dummy for platform independent version
2661 }
2662 
I_mkdir(const char * dirname,INT32 unixright)2663 INT32 I_mkdir(const char *dirname, INT32 unixright)
2664 {
2665 //[segabor]
2666 #if defined (__unix__) || defined(__APPLE__) || defined (UNIXCOMMON) || defined (__CYGWIN__)
2667 	return mkdir(dirname, unixright);
2668 #elif defined (_WIN32)
2669 	UNREFERENCED_PARAMETER(unixright); /// \todo should implement ntright under nt...
2670 	return CreateDirectoryA(dirname, NULL);
2671 #else
2672 	(void)dirname;
2673 	(void)unixright;
2674 	return false;
2675 #endif
2676 }
2677 
I_GetEnv(const char * name)2678 char *I_GetEnv(const char *name)
2679 {
2680 #ifdef NEED_SDL_GETENV
2681 	return SDL_getenv(name);
2682 #else
2683 	return getenv(name);
2684 #endif
2685 }
2686 
I_PutEnv(char * variable)2687 INT32 I_PutEnv(char *variable)
2688 {
2689 #ifdef NEED_SDL_GETENV
2690 	return SDL_putenv(variable);
2691 #else
2692 	return putenv(variable);
2693 #endif
2694 }
2695 
I_ClipboardCopy(const char * data,size_t size)2696 INT32 I_ClipboardCopy(const char *data, size_t size)
2697 {
2698 	char storage[256];
2699 	if (size > 255)
2700 		size = 255;
2701 	memcpy(storage, data, size);
2702 	storage[size] = 0;
2703 
2704 	if (SDL_SetClipboardText(storage))
2705 		return 0;
2706 	return -1;
2707 }
2708 
I_ClipboardPaste(void)2709 const char *I_ClipboardPaste(void)
2710 {
2711 	static char clipboard_modified[256];
2712 	char *clipboard_contents, *i = clipboard_modified;
2713 
2714 	if (!SDL_HasClipboardText())
2715 		return NULL;
2716 
2717 	clipboard_contents = SDL_GetClipboardText();
2718 	strlcpy(clipboard_modified, clipboard_contents, 256);
2719 	SDL_free(clipboard_contents);
2720 
2721 	while (*i)
2722 	{
2723 		if (*i == '\n' || *i == '\r')
2724 		{ // End on newline
2725 			*i = 0;
2726 			break;
2727 		}
2728 		else if (*i == '\t')
2729 			*i = ' '; // Tabs become spaces
2730 		else if (*i < 32 || (unsigned)*i > 127)
2731 			*i = '?'; // Nonprintable chars become question marks
2732 		++i;
2733 	}
2734 	return (const char *)&clipboard_modified;
2735 }
2736 
2737 /**	\brief	The isWadPathOk function
2738 
2739 	\param	path	string path to check
2740 
2741 	\return if true, wad file found
2742 
2743 
2744 */
isWadPathOk(const char * path)2745 static boolean isWadPathOk(const char *path)
2746 {
2747 	char *wad3path = malloc(256);
2748 
2749 	if (!wad3path)
2750 		return false;
2751 
2752 	sprintf(wad3path, pandf, path, WADKEYWORD1);
2753 
2754 	if (FIL_ReadFileOK(wad3path))
2755 	{
2756 		free(wad3path);
2757 		return true;
2758 	}
2759 
2760 	free(wad3path);
2761 	return false;
2762 }
2763 
pathonly(char * s)2764 static void pathonly(char *s)
2765 {
2766 	size_t j;
2767 
2768 	for (j = strlen(s); j != (size_t)-1; j--)
2769 		if ((s[j] == '\\') || (s[j] == ':') || (s[j] == '/'))
2770 		{
2771 			if (s[j] == ':') s[j+1] = 0;
2772 			else s[j] = 0;
2773 			return;
2774 		}
2775 }
2776 
2777 /**	\brief	search for srb2.pk3 in the given path
2778 
2779 	\param	searchDir	starting path
2780 
2781 	\return	WAD path if not NULL
2782 
2783 
2784 */
searchWad(const char * searchDir)2785 static const char *searchWad(const char *searchDir)
2786 {
2787 	static char tempsw[256] = "";
2788 	filestatus_t fstemp;
2789 
2790 	strcpy(tempsw, WADKEYWORD1);
2791 	fstemp = filesearch(tempsw,searchDir,NULL,true,20);
2792 	if (fstemp == FS_FOUND)
2793 	{
2794 		pathonly(tempsw);
2795 		return tempsw;
2796 	}
2797 
2798 	return NULL;
2799 }
2800 
2801 /**	\brief go through all possible paths and look for srb2.pk3
2802 
2803   \return path to srb2.pk3 if any
2804 */
locateWad(void)2805 static const char *locateWad(void)
2806 {
2807 	const char *envstr;
2808 	const char *WadPath;
2809 
2810 	I_OutputMsg("SRB2WADDIR");
2811 	// does SRB2WADDIR exist?
2812 	if (((envstr = I_GetEnv("SRB2WADDIR")) != NULL) && isWadPathOk(envstr))
2813 		return envstr;
2814 
2815 #ifndef NOCWD
2816 	I_OutputMsg(",.");
2817 	// examine current dir
2818 	strcpy(returnWadPath, ".");
2819 	if (isWadPathOk(returnWadPath))
2820 		return NULL;
2821 #endif
2822 
2823 
2824 #ifdef CMAKECONFIG
2825 #ifndef NDEBUG
2826 	I_OutputMsg(","CMAKE_ASSETS_DIR);
2827 	strcpy(returnWadPath, CMAKE_ASSETS_DIR);
2828 	if (isWadPathOk(returnWadPath))
2829 	{
2830 		return returnWadPath;
2831 	}
2832 #endif
2833 #endif
2834 
2835 #ifdef __APPLE__
2836 	OSX_GetResourcesPath(returnWadPath);
2837 	I_OutputMsg(",%s", returnWadPath);
2838 	if (isWadPathOk(returnWadPath))
2839 	{
2840 		return returnWadPath;
2841 	}
2842 #endif
2843 
2844 	// examine default dirs
2845 #ifdef DEFAULTWADLOCATION1
2846 	I_OutputMsg(","DEFAULTWADLOCATION1);
2847 	strcpy(returnWadPath, DEFAULTWADLOCATION1);
2848 	if (isWadPathOk(returnWadPath))
2849 		return returnWadPath;
2850 #endif
2851 #ifdef DEFAULTWADLOCATION2
2852 	I_OutputMsg(","DEFAULTWADLOCATION2);
2853 	strcpy(returnWadPath, DEFAULTWADLOCATION2);
2854 	if (isWadPathOk(returnWadPath))
2855 		return returnWadPath;
2856 #endif
2857 #ifdef DEFAULTWADLOCATION3
2858 	I_OutputMsg(","DEFAULTWADLOCATION3);
2859 	strcpy(returnWadPath, DEFAULTWADLOCATION3);
2860 	if (isWadPathOk(returnWadPath))
2861 		return returnWadPath;
2862 #endif
2863 #ifdef DEFAULTWADLOCATION4
2864 	I_OutputMsg(","DEFAULTWADLOCATION4);
2865 	strcpy(returnWadPath, DEFAULTWADLOCATION4);
2866 	if (isWadPathOk(returnWadPath))
2867 		return returnWadPath;
2868 #endif
2869 #ifdef DEFAULTWADLOCATION5
2870 	I_OutputMsg(","DEFAULTWADLOCATION5);
2871 	strcpy(returnWadPath, DEFAULTWADLOCATION5);
2872 	if (isWadPathOk(returnWadPath))
2873 		return returnWadPath;
2874 #endif
2875 #ifdef DEFAULTWADLOCATION6
2876 	I_OutputMsg(","DEFAULTWADLOCATION6);
2877 	strcpy(returnWadPath, DEFAULTWADLOCATION6);
2878 	if (isWadPathOk(returnWadPath))
2879 		return returnWadPath;
2880 #endif
2881 #ifdef DEFAULTWADLOCATION7
2882 	I_OutputMsg(","DEFAULTWADLOCATION7);
2883 	strcpy(returnWadPath, DEFAULTWADLOCATION7);
2884 	if (isWadPathOk(returnWadPath))
2885 		return returnWadPath;
2886 #endif
2887 #ifndef NOHOME
2888 	// find in $HOME
2889 	I_OutputMsg(",HOME");
2890 	if ((envstr = I_GetEnv("HOME")) != NULL)
2891 	{
2892 		WadPath = searchWad(envstr);
2893 		if (WadPath)
2894 			return WadPath;
2895 	}
2896 #endif
2897 #ifdef DEFAULTSEARCHPATH1
2898 	// find in /usr/local
2899 	I_OutputMsg(", in:"DEFAULTSEARCHPATH1);
2900 	WadPath = searchWad(DEFAULTSEARCHPATH1);
2901 	if (WadPath)
2902 		return WadPath;
2903 #endif
2904 #ifdef DEFAULTSEARCHPATH2
2905 	// find in /usr/games
2906 	I_OutputMsg(", in:"DEFAULTSEARCHPATH2);
2907 	WadPath = searchWad(DEFAULTSEARCHPATH2);
2908 	if (WadPath)
2909 		return WadPath;
2910 #endif
2911 #ifdef DEFAULTSEARCHPATH3
2912 	// find in ???
2913 	I_OutputMsg(", in:"DEFAULTSEARCHPATH3);
2914 	WadPath = searchWad(DEFAULTSEARCHPATH3);
2915 	if (WadPath)
2916 		return WadPath;
2917 #endif
2918 	// if nothing was found
2919 	return NULL;
2920 }
2921 
I_LocateWad(void)2922 const char *I_LocateWad(void)
2923 {
2924 	const char *waddir;
2925 
2926 	I_OutputMsg("Looking for WADs in: ");
2927 	waddir = locateWad();
2928 	I_OutputMsg("\n");
2929 
2930 	if (waddir)
2931 	{
2932 		// change to the directory where we found srb2.pk3
2933 #if defined (_WIN32)
2934 		SetCurrentDirectoryA(waddir);
2935 #else
2936 		if (chdir(waddir) == -1)
2937 			I_OutputMsg("Couldn't change working directory\n");
2938 #endif
2939 	}
2940 	return waddir;
2941 }
2942 
2943 #ifdef __linux__
2944 #define MEMINFO_FILE "/proc/meminfo"
2945 #define MEMTOTAL "MemTotal:"
2946 #define MEMAVAILABLE "MemAvailable:"
2947 #define MEMFREE "MemFree:"
2948 #define CACHED "Cached:"
2949 #define BUFFERS "Buffers:"
2950 #define SHMEM "Shmem:"
2951 
2952 /* Parse the contents of /proc/meminfo (in buf), return value of "name"
2953  * (example: MemTotal) */
get_entry(const char * name,const char * buf)2954 static long get_entry(const char* name, const char* buf)
2955 {
2956 	long val;
2957 	char* hit = strstr(buf, name);
2958 	if (hit == NULL) {
2959 		return -1;
2960 	}
2961 
2962 	errno = 0;
2963 	val = strtol(hit + strlen(name), NULL, 10);
2964 	if (errno != 0) {
2965 		CONS_Alert(CONS_ERROR, M_GetText("get_entry: strtol() failed: %s\n"), strerror(errno));
2966 		return -1;
2967 	}
2968 	return val;
2969 }
2970 #endif
2971 
2972 // quick fix for compil
I_GetFreeMem(UINT32 * total)2973 UINT32 I_GetFreeMem(UINT32 *total)
2974 {
2975 #ifdef FREEBSD
2976 	struct vmmeter sum;
2977 	kvm_t *kd;
2978 	struct nlist namelist[] =
2979 	{
2980 #define X_SUM   0
2981 		{"_cnt"},
2982 		{NULL}
2983 	};
2984 	if ((kd = kvm_open(NULL, NULL, NULL, O_RDONLY, "kvm_open")) == NULL)
2985 	{
2986 		if (total)
2987 			*total = 0L;
2988 		return 0;
2989 	}
2990 	if (kvm_nlist(kd, namelist) != 0)
2991 	{
2992 		kvm_close (kd);
2993 		if (total)
2994 			*total = 0L;
2995 		return 0;
2996 	}
2997 	if (kvm_read(kd, namelist[X_SUM].n_value, &sum,
2998 		sizeof (sum)) != sizeof (sum))
2999 	{
3000 		kvm_close(kd);
3001 		if (total)
3002 			*total = 0L;
3003 		return 0;
3004 	}
3005 	kvm_close(kd);
3006 
3007 	if (total)
3008 		*total = sum.v_page_count * sum.v_page_size;
3009 	return sum.v_free_count * sum.v_page_size;
3010 #elif defined (SOLARIS)
3011 	/* Just guess */
3012 	if (total)
3013 		*total = 32 << 20;
3014 	return 32 << 20;
3015 #elif defined (_WIN32)
3016 	MEMORYSTATUS info;
3017 
3018 	info.dwLength = sizeof (MEMORYSTATUS);
3019 	GlobalMemoryStatus( &info );
3020 	if (total)
3021 		*total = (UINT32)info.dwTotalPhys;
3022 	return (UINT32)info.dwAvailPhys;
3023 #elif defined (__linux__)
3024 	/* Linux */
3025 	char buf[1024];
3026 	char *memTag;
3027 	UINT32 freeKBytes;
3028 	UINT32 totalKBytes;
3029 	INT32 n;
3030 	INT32 meminfo_fd = -1;
3031 	long Cached;
3032 	long MemFree;
3033 	long Buffers;
3034 	long Shmem;
3035 	long MemAvailable = -1;
3036 
3037 	meminfo_fd = open(MEMINFO_FILE, O_RDONLY);
3038 	n = read(meminfo_fd, buf, 1023);
3039 	close(meminfo_fd);
3040 
3041 	if (n < 0)
3042 	{
3043 		// Error
3044 		if (total)
3045 			*total = 0L;
3046 		return 0;
3047 	}
3048 
3049 	buf[n] = '\0';
3050 	if ((memTag = strstr(buf, MEMTOTAL)) == NULL)
3051 	{
3052 		// Error
3053 		if (total)
3054 			*total = 0L;
3055 		return 0;
3056 	}
3057 
3058 	memTag += sizeof (MEMTOTAL);
3059 	totalKBytes = atoi(memTag);
3060 
3061 	if ((memTag = strstr(buf, MEMAVAILABLE)) == NULL)
3062 	{
3063 		Cached = get_entry(CACHED, buf);
3064 		MemFree = get_entry(MEMFREE, buf);
3065 		Buffers = get_entry(BUFFERS, buf);
3066 		Shmem = get_entry(SHMEM, buf);
3067 		MemAvailable = Cached + MemFree + Buffers - Shmem;
3068 
3069 		if (MemAvailable == -1)
3070 		{
3071 			// Error
3072 			if (total)
3073 				*total = 0L;
3074 			return 0;
3075 		}
3076 		freeKBytes = MemAvailable;
3077 	}
3078 	else
3079 	{
3080 		memTag += sizeof (MEMAVAILABLE);
3081 		freeKBytes = atoi(memTag);
3082 	}
3083 
3084 	if (total)
3085 		*total = totalKBytes << 10;
3086 	return freeKBytes << 10;
3087 #else
3088 	// Guess 48 MB.
3089 	if (total)
3090 		*total = 48<<20;
3091 	return 48<<20;
3092 #endif
3093 }
3094 
I_CPUInfo(void)3095 const CPUInfoFlags *I_CPUInfo(void)
3096 {
3097 #if defined (_WIN32)
3098 	static CPUInfoFlags WIN_CPUInfo;
3099 	SYSTEM_INFO SI;
3100 	p_IsProcessorFeaturePresent pfnCPUID = (p_IsProcessorFeaturePresent)(LPVOID)GetProcAddress(GetModuleHandleA("kernel32.dll"), "IsProcessorFeaturePresent");
3101 
3102 	ZeroMemory(&WIN_CPUInfo,sizeof (WIN_CPUInfo));
3103 	if (pfnCPUID)
3104 	{
3105 		WIN_CPUInfo.FPPE       = pfnCPUID( 0); //PF_FLOATING_POINT_PRECISION_ERRATA
3106 		WIN_CPUInfo.FPE        = pfnCPUID( 1); //PF_FLOATING_POINT_EMULATED
3107 		WIN_CPUInfo.cmpxchg    = pfnCPUID( 2); //PF_COMPARE_EXCHANGE_DOUBLE
3108 		WIN_CPUInfo.MMX        = pfnCPUID( 3); //PF_MMX_INSTRUCTIONS_AVAILABLE
3109 		WIN_CPUInfo.PPCMM64    = pfnCPUID( 4); //PF_PPC_MOVEMEM_64BIT_OK
3110 		WIN_CPUInfo.ALPHAbyte  = pfnCPUID( 5); //PF_ALPHA_BYTE_INSTRUCTIONS
3111 		WIN_CPUInfo.SSE        = pfnCPUID( 6); //PF_XMMI_INSTRUCTIONS_AVAILABLE
3112 		WIN_CPUInfo.AMD3DNow   = pfnCPUID( 7); //PF_3DNOW_INSTRUCTIONS_AVAILABLE
3113 		WIN_CPUInfo.RDTSC      = pfnCPUID( 8); //PF_RDTSC_INSTRUCTION_AVAILABLE
3114 		WIN_CPUInfo.PAE        = pfnCPUID( 9); //PF_PAE_ENABLED
3115 		WIN_CPUInfo.SSE2       = pfnCPUID(10); //PF_XMMI64_INSTRUCTIONS_AVAILABLE
3116 		//WIN_CPUInfo.blank    = pfnCPUID(11); //PF_SSE_DAZ_MODE_AVAILABLE
3117 		WIN_CPUInfo.DEP        = pfnCPUID(12); //PF_NX_ENABLED
3118 		WIN_CPUInfo.SSE3       = pfnCPUID(13); //PF_SSE3_INSTRUCTIONS_AVAILABLE
3119 		WIN_CPUInfo.cmpxchg16b = pfnCPUID(14); //PF_COMPARE_EXCHANGE128
3120 		WIN_CPUInfo.cmp8xchg16 = pfnCPUID(15); //PF_COMPARE64_EXCHANGE128
3121 		WIN_CPUInfo.PFC        = pfnCPUID(16); //PF_CHANNELS_ENABLED
3122 	}
3123 #ifdef HAVE_SDLCPUINFO
3124 	else
3125 	{
3126 		WIN_CPUInfo.RDTSC       = SDL_HasRDTSC();
3127 		WIN_CPUInfo.MMX         = SDL_HasMMX();
3128 		WIN_CPUInfo.AMD3DNow    = SDL_Has3DNow();
3129 		WIN_CPUInfo.SSE         = SDL_HasSSE();
3130 		WIN_CPUInfo.SSE2        = SDL_HasSSE2();
3131 		WIN_CPUInfo.AltiVec     = SDL_HasAltiVec();
3132 	}
3133 	WIN_CPUInfo.MMXExt      = SDL_FALSE; //SDL_HasMMXExt(); No longer in SDL2
3134 	WIN_CPUInfo.AMD3DNowExt = SDL_FALSE; //SDL_Has3DNowExt(); No longer in SDL2
3135 #endif
3136 	GetSystemInfo(&SI);
3137 	WIN_CPUInfo.CPUs = SI.dwNumberOfProcessors;
3138 	WIN_CPUInfo.IA64 = (SI.dwProcessorType == 2200); // PROCESSOR_INTEL_IA64
3139 	WIN_CPUInfo.AMD64 = (SI.dwProcessorType == 8664); // PROCESSOR_AMD_X8664
3140 	return &WIN_CPUInfo;
3141 #elif defined (HAVE_SDLCPUINFO)
3142 	static CPUInfoFlags SDL_CPUInfo;
3143 	memset(&SDL_CPUInfo,0,sizeof (CPUInfoFlags));
3144 	SDL_CPUInfo.RDTSC       = SDL_HasRDTSC();
3145 	SDL_CPUInfo.MMX         = SDL_HasMMX();
3146 	SDL_CPUInfo.MMXExt      = SDL_FALSE; //SDL_HasMMXExt(); No longer in SDL2
3147 	SDL_CPUInfo.AMD3DNow    = SDL_Has3DNow();
3148 	SDL_CPUInfo.AMD3DNowExt = SDL_FALSE; //SDL_Has3DNowExt(); No longer in SDL2
3149 	SDL_CPUInfo.SSE         = SDL_HasSSE();
3150 	SDL_CPUInfo.SSE2        = SDL_HasSSE2();
3151 	SDL_CPUInfo.AltiVec     = SDL_HasAltiVec();
3152 	return &SDL_CPUInfo;
3153 #else
3154 	return NULL; /// \todo CPUID asm
3155 #endif
3156 }
3157 
3158 // note CPUAFFINITY code used to reside here
I_RegisterSysCommands(void)3159 void I_RegisterSysCommands(void) {}
3160 #endif
3161