From c99975c73ae2941751d86d3b8466454c5ad122c0 Mon Sep 17 00:00:00 2001
From: Tim Angus <tim@ngus.net>
Date: Wed, 5 Sep 2007 22:05:32 +0000
Subject: * Merge of ioq3-r1163   + SDL everywhere   + New R_LoadImage   +
 net_ip.c   + Window icon   + GUI based dedicated server support   + Compile
 time linked OpenGL * Remove ui_menuFiles cvar

---
 src/sys/sys_loadlib.h   |  36 +++
 src/sys/sys_local.h     |  40 +++
 src/sys/sys_main.c      | 646 ++++++++++++++++++++++++++++++++++++++++++++++++
 src/sys/sys_unix.c      | 450 +++++++++++++++++++++++++++++++++
 src/sys/sys_win32.c     | 510 ++++++++++++++++++++++++++++++++++++++
 src/sys/tty_console.c   | 440 +++++++++++++++++++++++++++++++++
 src/sys/win_resource.h  |  44 ++++
 src/sys/win_resource.rc |  75 ++++++
 8 files changed, 2241 insertions(+)
 create mode 100644 src/sys/sys_loadlib.h
 create mode 100644 src/sys/sys_local.h
 create mode 100644 src/sys/sys_main.c
 create mode 100644 src/sys/sys_unix.c
 create mode 100644 src/sys/sys_win32.c
 create mode 100644 src/sys/tty_console.c
 create mode 100644 src/sys/win_resource.h
 create mode 100644 src/sys/win_resource.rc

(limited to 'src/sys')

diff --git a/src/sys/sys_loadlib.h b/src/sys/sys_loadlib.h
new file mode 100644
index 00000000..29b21c7d
--- /dev/null
+++ b/src/sys/sys_loadlib.h
@@ -0,0 +1,36 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code 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.
+
+Quake III Arena source code 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.
+
+You should have received a copy of the GNU General Public License
+along with Quake III Arena source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+#ifdef DEDICATED
+#	include <dlfcn.h>
+#	define Sys_LoadLibrary(f) dlopen(f,RTLD_NOW)
+#	define Sys_UnloadLibrary(h) dlclose(h)
+#	define Sys_LoadFunction(h,fn) dlsym(h,fn)
+#	define Sys_LibraryError() dlerror()
+#else
+#	include "SDL.h"
+#	include "SDL_loadso.h"
+#	define Sys_LoadLibrary(f) SDL_LoadObject(f)
+#	define Sys_UnloadLibrary(h) SDL_UnloadObject(h)
+#	define Sys_LoadFunction(h,fn) SDL_LoadFunction(h,fn)
+#	define Sys_LibraryError() SDL_GetError()
+#endif
diff --git a/src/sys/sys_local.h b/src/sys/sys_local.h
new file mode 100644
index 00000000..2ccac5cf
--- /dev/null
+++ b/src/sys/sys_local.h
@@ -0,0 +1,40 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code 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.
+
+Quake III Arena source code 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.
+
+You should have received a copy of the GNU General Public License
+along with Quake III Arena source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+#include "../qcommon/q_shared.h"
+#include "../qcommon/qcommon.h"
+
+// Input subsystem
+void IN_Init (void);
+void IN_Frame (void);
+void IN_Shutdown (void);
+
+// TTY console
+void TTY_Hide( void );
+void TTY_Show( void );
+void TTY_Shutdown( void );
+void TTY_Init( void );
+char *TTY_ConsoleInput(void);
+
+#ifdef MACOS_X
+char *Sys_StripAppBundle( char *pwd );
+#endif
diff --git a/src/sys/sys_main.c b/src/sys/sys_main.c
new file mode 100644
index 00000000..df743733
--- /dev/null
+++ b/src/sys/sys_main.c
@@ -0,0 +1,646 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code 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.
+
+Quake III Arena source code 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.
+
+You should have received a copy of the GNU General Public License
+along with Quake III Arena source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+#include <unistd.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+
+#ifndef DEDICATED
+#include "SDL.h"
+#endif
+
+#include "sys_local.h"
+#include "sys_loadlib.h"
+
+#include "../qcommon/q_shared.h"
+#include "../qcommon/qcommon.h"
+
+static char binaryPath[ MAX_OSPATH ] = { 0 };
+static char installPath[ MAX_OSPATH ] = { 0 };
+
+/*
+=================
+Sys_SetBinaryPath
+=================
+*/
+void Sys_SetBinaryPath(const char *path)
+{
+	Q_strncpyz(binaryPath, path, sizeof(binaryPath));
+}
+
+/*
+=================
+Sys_BinaryPath
+=================
+*/
+char *Sys_BinaryPath(void)
+{
+	return binaryPath;
+}
+
+/*
+=================
+Sys_SetDefaultInstallPath
+=================
+*/
+void Sys_SetDefaultInstallPath(const char *path)
+{
+	Q_strncpyz(installPath, path, sizeof(installPath));
+}
+
+/*
+=================
+Sys_DefaultInstallPath
+=================
+*/
+char *Sys_DefaultInstallPath(void)
+{
+	if (*installPath)
+		return installPath;
+	else
+		return Sys_Cwd();
+}
+
+/*
+=================
+Sys_In_Restart_f
+
+Restart the input subsystem
+=================
+*/
+void Sys_In_Restart_f( void )
+{
+	IN_Shutdown();
+	IN_Init();
+}
+
+/*
+=================
+Sys_ConsoleInputInit
+
+Start the console input subsystem
+=================
+*/
+void Sys_ConsoleInputInit( void )
+{
+#ifdef DEDICATED
+	TTY_Init( );
+#endif
+}
+
+/*
+=================
+Sys_ConsoleInputShutdown
+
+Shutdown the console input subsystem
+=================
+*/
+void Sys_ConsoleInputShutdown( void )
+{
+#ifdef DEDICATED
+	TTY_Shutdown( );
+#endif
+}
+
+/*
+=================
+Sys_ConsoleInput
+
+Handle new console input
+=================
+*/
+char *Sys_ConsoleInput(void)
+{
+#ifdef DEDICATED
+	return TTY_ConsoleInput( );
+#endif
+
+	return NULL;
+}
+
+/*
+=================
+Sys_Exit
+
+Single exit point (regular exit or in case of error)
+=================
+*/
+void Sys_Exit( int ex )
+{
+	Sys_ConsoleInputShutdown();
+
+#ifndef DEDICATED
+	SDL_Quit( );
+#endif
+
+#ifdef NDEBUG
+	// _exit is called instead of exit since there are rumours of
+	// GL libraries installing atexit calls that we don't want to call
+	// FIXME: get some testing done with plain exit
+	_exit(ex);
+#else
+	// Cause a backtrace on error exits
+	assert( ex == 0 );
+	exit(ex);
+#endif
+}
+
+/*
+=================
+Sys_Quit
+=================
+*/
+void Sys_Quit (void)
+{
+	CL_Shutdown ();
+	Sys_Exit(0);
+}
+
+/*
+=================
+Sys_GetProcessorFeatures
+=================
+*/
+cpuFeatures_t Sys_GetProcessorFeatures( void )
+{
+	cpuFeatures_t features = 0;
+
+#ifndef DEDICATED
+	if( SDL_HasRDTSC( ) )    features |= CF_RDTSC;
+	if( SDL_HasMMX( ) )      features |= CF_MMX;
+	if( SDL_HasMMXExt( ) )   features |= CF_MMX_EXT;
+	if( SDL_Has3DNow( ) )    features |= CF_3DNOW;
+	if( SDL_Has3DNowExt( ) ) features |= CF_3DNOW_EXT;
+	if( SDL_HasSSE( ) )      features |= CF_SSE;
+	if( SDL_HasSSE2( ) )     features |= CF_SSE2;
+	if( SDL_HasAltiVec( ) )  features |= CF_ALTIVEC;
+#endif
+
+	return features;
+}
+
+/*
+=================
+Sys_Init
+=================
+*/
+void Sys_Init(void)
+{
+	Cmd_AddCommand( "in_restart", Sys_In_Restart_f );
+	Cvar_Set( "arch", OS_STRING " " ARCH_STRING );
+	Cvar_Set( "username", Sys_GetCurrentUser( ) );
+}
+
+static struct Q3ToAnsiColorTable_s
+{
+	char Q3color;
+	char *ANSIcolor;
+} TTY_colorTable[ ] =
+{
+	{ COLOR_BLACK,    "30" },
+	{ COLOR_RED,      "31" },
+	{ COLOR_GREEN,    "32" },
+	{ COLOR_YELLOW,   "33" },
+	{ COLOR_BLUE,     "34" },
+	{ COLOR_CYAN,     "36" },
+	{ COLOR_MAGENTA,  "35" },
+	{ COLOR_WHITE,    "0" }
+};
+
+static int TTY_colorTableSize =
+	sizeof( TTY_colorTable ) / sizeof( TTY_colorTable[ 0 ] );
+
+/*
+=================
+Sys_ANSIColorify
+
+Transform Q3 colour codes to ANSI escape sequences
+=================
+*/
+static void Sys_ANSIColorify( const char *msg, char *buffer, int bufferSize )
+{
+	int   msgLength, pos;
+	int   i, j;
+	char  *escapeCode;
+	char  tempBuffer[ 7 ];
+
+	if( !msg || !buffer )
+		return;
+
+	msgLength = strlen( msg );
+	pos = 0;
+	i = 0;
+	buffer[ 0 ] = '\0';
+
+	while( i < msgLength )
+	{
+		if( msg[ i ] == '\n' )
+		{
+			Com_sprintf( tempBuffer, 7, "%c[0m\n", 0x1B );
+			strncat( buffer, tempBuffer, bufferSize );
+			i++;
+		}
+		else if( msg[ i ] == Q_COLOR_ESCAPE )
+		{
+			i++;
+
+			if( i < msgLength )
+			{
+				escapeCode = NULL;
+				for( j = 0; j < TTY_colorTableSize; j++ )
+				{
+					if( msg[ i ] == TTY_colorTable[ j ].Q3color )
+					{
+						escapeCode = TTY_colorTable[ j ].ANSIcolor;
+						break;
+					}
+				}
+
+				if( escapeCode )
+				{
+					Com_sprintf( tempBuffer, 7, "%c[%sm", 0x1B, escapeCode );
+					strncat( buffer, tempBuffer, bufferSize );
+				}
+
+				i++;
+			}
+		}
+		else
+		{
+			Com_sprintf( tempBuffer, 7, "%c", msg[ i++ ] );
+			strncat( buffer, tempBuffer, bufferSize );
+		}
+	}
+}
+
+/*
+=================
+Sys_Print
+=================
+*/
+void Sys_Print( const char *msg )
+{
+#ifdef DEDICATED
+	TTY_Hide();
+#endif
+
+	if( com_ansiColor && com_ansiColor->integer )
+	{
+		char ansiColorString[ MAXPRINTMSG ];
+		Sys_ANSIColorify( msg, ansiColorString, MAXPRINTMSG );
+		fputs( ansiColorString, stderr );
+	}
+	else
+		fputs(msg, stderr);
+
+#ifdef DEDICATED
+	TTY_Show();
+#endif
+}
+
+/*
+=================
+Sys_Error
+=================
+*/
+void Sys_Error( const char *error, ... )
+{
+	va_list argptr;
+	char    string[1024];
+
+#ifdef DEDICATED
+	TTY_Hide();
+#endif
+
+	CL_Shutdown ();
+
+	va_start (argptr,error);
+	Q_vsnprintf (string, sizeof(string), error, argptr);
+	va_end (argptr);
+	fprintf(stderr, "Sys_Error: %s\n", string);
+
+	Sys_Exit( 1 );
+}
+
+/*
+=================
+Sys_Warn
+=================
+*/
+void Sys_Warn( char *warning, ... )
+{
+	va_list argptr;
+	char    string[1024];
+
+	va_start (argptr,warning);
+	Q_vsnprintf (string, sizeof(string), warning, argptr);
+	va_end (argptr);
+
+#ifdef DEDICATED
+	TTY_Hide();
+#endif
+
+	fprintf(stderr, "Warning: %s", string);
+
+#ifdef DEDICATED
+	TTY_Show();
+#endif
+}
+
+/*
+============
+Sys_FileTime
+
+returns -1 if not present
+============
+*/
+int Sys_FileTime( char *path )
+{
+	struct stat buf;
+
+	if (stat (path,&buf) == -1)
+		return -1;
+
+	return buf.st_mtime;
+}
+
+/*
+=================
+Sys_UnloadDll
+=================
+*/
+void Sys_UnloadDll( void *dllHandle )
+{
+	if( !dllHandle )
+	{
+		Com_Printf("Sys_UnloadDll(NULL)\n");
+		return;
+	}
+
+	Sys_UnloadLibrary(dllHandle);
+}
+
+/*
+=================
+Sys_TryLibraryLoad
+=================
+*/
+static void* Sys_TryLibraryLoad(const char* base, const char* gamedir, const char* fname, char* fqpath )
+{
+	void* libHandle;
+	char* fn;
+
+	*fqpath = 0;
+
+	fn = FS_BuildOSPath( base, gamedir, fname );
+	Com_Printf( "Sys_LoadDll(%s)... \n", fn );
+
+	libHandle = Sys_LoadLibrary(fn);
+
+	if(!libHandle) {
+		Com_Printf( "Sys_LoadDll(%s) failed:\n\"%s\"\n", fn, Sys_LibraryError() );
+		return NULL;
+	}
+
+	Com_Printf ( "Sys_LoadDll(%s): succeeded ...\n", fn );
+	Q_strncpyz ( fqpath , fn , MAX_QPATH ) ;
+
+	return libHandle;
+}
+
+/*
+=================
+Sys_LoadDll
+
+Used to load a development dll instead of a virtual machine
+#1 look down current path
+#2 look in fs_homepath
+#3 look in fs_basepath
+=================
+*/
+void *Sys_LoadDll( const char *name, char *fqpath ,
+	intptr_t (**entryPoint)(int, ...),
+	intptr_t (*systemcalls)(intptr_t, ...) )
+{
+	void  *libHandle;
+	void  (*dllEntry)( intptr_t (*syscallptr)(intptr_t, ...) );
+	char  curpath[MAX_OSPATH];
+	char  fname[MAX_OSPATH];
+	char  *basepath;
+	char  *homepath;
+	char  *pwdpath;
+	char  *gamedir;
+
+	assert( name );
+
+	getcwd(curpath, sizeof(curpath));
+	snprintf (fname, sizeof(fname), "%s" ARCH_STRING DLL_EXT, name);
+
+	// TODO: use fs_searchpaths from files.c
+	pwdpath = Sys_Cwd();
+	basepath = Cvar_VariableString( "fs_basepath" );
+	homepath = Cvar_VariableString( "fs_homepath" );
+	gamedir = Cvar_VariableString( "fs_game" );
+
+	libHandle = Sys_TryLibraryLoad(pwdpath, gamedir, fname, fqpath);
+
+	if(!libHandle && homepath)
+		libHandle = Sys_TryLibraryLoad(homepath, gamedir, fname, fqpath);
+
+	if(!libHandle && basepath)
+		libHandle = Sys_TryLibraryLoad(basepath, gamedir, fname, fqpath);
+
+	if(!libHandle) {
+		Com_Printf ( "Sys_LoadDll(%s) failed to load library\n", name );
+		return NULL;
+	}
+
+	dllEntry = Sys_LoadFunction( libHandle, "dllEntry" );
+	*entryPoint = Sys_LoadFunction( libHandle, "vmMain" );
+
+	if ( !*entryPoint || !dllEntry )
+	{
+		Com_Printf ( "Sys_LoadDll(%s) failed to find vmMain function:\n\"%s\" !\n", name, Sys_LibraryError( ) );
+		Sys_UnloadLibrary(libHandle);
+
+		return NULL;
+	}
+
+	Com_Printf ( "Sys_LoadDll(%s) found vmMain function at %p\n", name, *entryPoint );
+	dllEntry( systemcalls );
+
+	return libHandle;
+}
+
+/*
+=================
+Sys_Idle
+=================
+*/
+static void Sys_Idle( void )
+{
+#ifndef DEDICATED
+	int appState = SDL_GetAppState( );
+	int sleep = 0;
+
+	// If we have no input focus at all, sleep a bit
+	if( !( appState & ( SDL_APPMOUSEFOCUS | SDL_APPINPUTFOCUS ) ) )
+	{
+		Cvar_SetValue( "com_unfocused", 1 );
+		sleep += 16;
+	}
+	else
+		Cvar_SetValue( "com_unfocused", 0 );
+
+	// If we're minimised, sleep a bit more
+	if( !( appState & SDL_APPACTIVE ) )
+	{
+		Cvar_SetValue( "com_minimized", 1 );
+		sleep += 32;
+	}
+	else
+		Cvar_SetValue( "com_minimized", 0 );
+
+	if( !com_dedicated->integer && sleep )
+		SDL_Delay( sleep );
+#else
+	// Dedicated server idles via NET_Sleep
+#endif
+}
+
+/*
+=================
+Sys_ParseArgs
+=================
+*/
+void Sys_ParseArgs( int argc, char **argv )
+{
+	if( argc == 2 )
+	{
+		if( !strcmp( argv[1], "--version" ) ||
+				!strcmp( argv[1], "-v" ) )
+		{
+			const char* date = __DATE__;
+#ifdef DEDICATED
+			fprintf( stdout, Q3_VERSION " dedicated server (%s)\n", date );
+#else
+			fprintf( stdout, Q3_VERSION " client (%s)\n", date );
+#endif
+			Sys_Exit(0);
+		}
+	}
+}
+
+#ifndef DEFAULT_BASEDIR
+#	ifdef MACOS_X
+#		define DEFAULT_BASEDIR Sys_StripAppBundle(Sys_BinaryPath())
+#	else
+#		define DEFAULT_BASEDIR Sys_BinaryPath()
+#	endif
+#endif
+
+/*
+=================
+Sys_SigHandler
+=================
+*/
+static void Sys_SigHandler( int signal )
+{
+	static qboolean signalcaught = qfalse;
+
+	if( signalcaught )
+	{
+		fprintf( stderr, "DOUBLE SIGNAL FAULT: Received signal %d, exiting...\n",
+			signal );
+	}
+	else
+	{
+		signalcaught = qtrue;
+		fprintf( stderr, "Received signal %d, exiting...\n", signal );
+#ifndef DEDICATED
+		CL_Shutdown();
+#endif
+		SV_Shutdown( "Signal caught" );
+	}
+
+	Sys_Exit( 0 ); // Exit with 0 to avoid recursive signals
+}
+
+/*
+=================
+main
+=================
+*/
+int main( int argc, char **argv )
+{
+	int   i;
+	char  commandLine[ MAX_STRING_CHARS ] = { 0 };
+
+	Sys_ParseArgs( argc, argv );
+	Sys_SetBinaryPath( Sys_Dirname( argv[ 0 ] ) );
+	Sys_SetDefaultInstallPath( DEFAULT_BASEDIR );
+
+	// Concatenate the command line for passing to Com_Init
+	for( i = 1; i < argc; i++ )
+	{
+		Q_strcat( commandLine, sizeof( commandLine ), argv[ i ] );
+		Q_strcat( commandLine, sizeof( commandLine ), " " );
+	}
+
+	Com_Init( commandLine );
+	NET_Init();
+
+	Sys_ConsoleInputInit();
+
+#ifndef _WIN32
+	// Windows doesn't have these signals
+	signal( SIGHUP, Sys_SigHandler );
+	signal( SIGQUIT, Sys_SigHandler );
+	signal( SIGTRAP, Sys_SigHandler );
+	signal( SIGIOT, Sys_SigHandler );
+	signal( SIGBUS, Sys_SigHandler );
+#endif
+
+	signal( SIGILL, Sys_SigHandler );
+	signal( SIGFPE, Sys_SigHandler );
+	signal( SIGSEGV, Sys_SigHandler );
+	signal( SIGTERM, Sys_SigHandler );
+
+	while( 1 )
+	{
+		Sys_Idle( );
+		IN_Frame( );
+		Com_Frame( );
+	}
+
+	return 0;
+}
+
diff --git a/src/sys/sys_unix.c b/src/sys/sys_unix.c
new file mode 100644
index 00000000..8a8240bd
--- /dev/null
+++ b/src/sys/sys_unix.c
@@ -0,0 +1,450 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code 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.
+
+Quake III Arena source code 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.
+
+You should have received a copy of the GNU General Public License
+along with Quake III Arena source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <stdio.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <sys/time.h>
+#include <pwd.h>
+#include <libgen.h>
+
+#include "../qcommon/q_shared.h"
+#include "../qcommon/qcommon.h"
+
+// Used to determine where to store user-specific files
+static char homePath[ MAX_OSPATH ] = { 0 };
+
+/*
+==================
+Sys_DefaultHomePath
+==================
+*/
+char *Sys_DefaultHomePath(void)
+{
+	char *p;
+
+	if( !*homePath )
+	{
+		if( ( p = getenv( "HOME" ) ) != NULL )
+		{
+			Q_strncpyz( homePath, p, sizeof( homePath ) );
+#ifdef MACOS_X
+			Q_strcat( homePath, sizeof( homePath ), "/Library/Application Support/Quake3" );
+#else
+			Q_strcat( homePath, sizeof( homePath ), "/.q3a" );
+#endif
+			if( mkdir( homePath, 0777 ) )
+			{
+				if( errno != EEXIST )
+				{
+					Sys_Error( "Unable to create directory \"%s\", error is %s(%d)\n",
+							homePath, strerror( errno ), errno );
+				}
+			}
+		}
+	}
+
+	return homePath;
+}
+
+/*
+================
+Sys_Milliseconds
+================
+*/
+/* base time in seconds, that's our origin
+   timeval:tv_sec is an int:
+   assuming this wraps every 0x7fffffff - ~68 years since the Epoch (1970) - we're safe till 2038
+   using unsigned long data type to work right with Sys_XTimeToSysTime */
+unsigned long sys_timeBase = 0;
+/* current time in ms, using sys_timeBase as origin
+   NOTE: sys_timeBase*1000 + curtime -> ms since the Epoch
+     0x7fffffff ms - ~24 days
+   although timeval:tv_usec is an int, I'm not sure wether it is actually used as an unsigned int
+     (which would affect the wrap period) */
+int curtime;
+int Sys_Milliseconds (void)
+{
+	struct timeval tp;
+
+	gettimeofday(&tp, NULL);
+
+	if (!sys_timeBase)
+	{
+		sys_timeBase = tp.tv_sec;
+		return tp.tv_usec/1000;
+	}
+
+	curtime = (tp.tv_sec - sys_timeBase)*1000 + tp.tv_usec/1000;
+
+	return curtime;
+}
+
+#if !id386
+/*
+==================
+fastftol
+==================
+*/
+long fastftol( float f )
+{
+	return (long)f;
+}
+
+/*
+==================
+Sys_SnapVector
+==================
+*/
+void Sys_SnapVector( float *v )
+{
+	v[0] = rint(v[0]);
+	v[1] = rint(v[1]);
+	v[2] = rint(v[2]);
+}
+#endif
+
+
+/*
+==================
+Sys_RandomBytes
+==================
+*/
+qboolean Sys_RandomBytes( byte *string, int len )
+{
+	FILE *fp;
+
+	fp = fopen( "/dev/urandom", "r" );
+	if( !fp )
+		return qfalse;
+
+	if( !fread( string, sizeof( byte ), len, fp ) )
+	{
+		fclose( fp );
+		return qfalse;
+	}
+
+	fclose( fp );
+	return qtrue;
+}
+
+/*
+==================
+Sys_GetCurrentUser
+==================
+*/
+char *Sys_GetCurrentUser( void )
+{
+	struct passwd *p;
+
+	if ( (p = getpwuid( getuid() )) == NULL ) {
+		return "player";
+	}
+	return p->pw_name;
+}
+
+/*
+==================
+Sys_GetClipboardData
+==================
+*/
+char *Sys_GetClipboardData(void)
+{
+	return NULL;
+}
+
+#define MEM_THRESHOLD 96*1024*1024
+
+/*
+==================
+Sys_LowPhysicalMemory
+
+TODO
+==================
+*/
+qboolean Sys_LowPhysicalMemory( void )
+{
+	return qfalse;
+}
+
+/*
+==================
+Sys_Basename
+==================
+*/
+const char *Sys_Basename( char *path )
+{
+	return basename( path );
+}
+
+/*
+==================
+Sys_Dirname
+==================
+*/
+const char *Sys_Dirname( char *path )
+{
+	return dirname( path );
+}
+
+/*
+==================
+Sys_Mkdir
+==================
+*/
+void Sys_Mkdir( const char *path )
+{
+	mkdir( path, 0777 );
+}
+
+/*
+==================
+Sys_Cwd
+==================
+*/
+char *Sys_Cwd( void )
+{
+	static char cwd[MAX_OSPATH];
+
+	getcwd( cwd, sizeof( cwd ) - 1 );
+	cwd[MAX_OSPATH-1] = 0;
+
+	return cwd;
+}
+
+/*
+==============================================================
+
+DIRECTORY SCANNING
+
+==============================================================
+*/
+
+#define MAX_FOUND_FILES 0x1000
+
+/*
+==================
+Sys_ListFilteredFiles
+==================
+*/
+void Sys_ListFilteredFiles( const char *basedir, char *subdirs, char *filter, char **list, int *numfiles )
+{
+	char          search[MAX_OSPATH], newsubdirs[MAX_OSPATH];
+	char          filename[MAX_OSPATH];
+	DIR           *fdir;
+	struct dirent *d;
+	struct stat   st;
+
+	if ( *numfiles >= MAX_FOUND_FILES - 1 ) {
+		return;
+	}
+
+	if (strlen(subdirs)) {
+		Com_sprintf( search, sizeof(search), "%s/%s", basedir, subdirs );
+	}
+	else {
+		Com_sprintf( search, sizeof(search), "%s", basedir );
+	}
+
+	if ((fdir = opendir(search)) == NULL) {
+		return;
+	}
+
+	while ((d = readdir(fdir)) != NULL) {
+		Com_sprintf(filename, sizeof(filename), "%s/%s", search, d->d_name);
+		if (stat(filename, &st) == -1)
+			continue;
+
+		if (st.st_mode & S_IFDIR) {
+			if (Q_stricmp(d->d_name, ".") && Q_stricmp(d->d_name, "..")) {
+				if (strlen(subdirs)) {
+					Com_sprintf( newsubdirs, sizeof(newsubdirs), "%s/%s", subdirs, d->d_name);
+				}
+				else {
+					Com_sprintf( newsubdirs, sizeof(newsubdirs), "%s", d->d_name);
+				}
+				Sys_ListFilteredFiles( basedir, newsubdirs, filter, list, numfiles );
+			}
+		}
+		if ( *numfiles >= MAX_FOUND_FILES - 1 ) {
+			break;
+		}
+		Com_sprintf( filename, sizeof(filename), "%s/%s", subdirs, d->d_name );
+		if (!Com_FilterPath( filter, filename, qfalse ))
+			continue;
+		list[ *numfiles ] = CopyString( filename );
+		(*numfiles)++;
+	}
+
+	closedir(fdir);
+}
+
+/*
+==================
+Sys_ListFiles
+==================
+*/
+char **Sys_ListFiles( const char *directory, const char *extension, char *filter, int *numfiles, qboolean wantsubs )
+{
+	struct dirent *d;
+	DIR           *fdir;
+	qboolean      dironly = wantsubs;
+	char          search[MAX_OSPATH];
+	int           nfiles;
+	char          **listCopy;
+	char          *list[MAX_FOUND_FILES];
+	int           i;
+	struct stat   st;
+
+	int           extLen;
+
+	if (filter) {
+
+		nfiles = 0;
+		Sys_ListFilteredFiles( directory, "", filter, list, &nfiles );
+
+		list[ nfiles ] = NULL;
+		*numfiles = nfiles;
+
+		if (!nfiles)
+			return NULL;
+
+		listCopy = Z_Malloc( ( nfiles + 1 ) * sizeof( *listCopy ) );
+		for ( i = 0 ; i < nfiles ; i++ ) {
+			listCopy[i] = list[i];
+		}
+		listCopy[i] = NULL;
+
+		return listCopy;
+	}
+
+	if ( !extension)
+		extension = "";
+
+	if ( extension[0] == '/' && extension[1] == 0 ) {
+		extension = "";
+		dironly = qtrue;
+	}
+
+	extLen = strlen( extension );
+	
+	// search
+	nfiles = 0;
+
+	if ((fdir = opendir(directory)) == NULL) {
+		*numfiles = 0;
+		return NULL;
+	}
+
+	while ((d = readdir(fdir)) != NULL) {
+		Com_sprintf(search, sizeof(search), "%s/%s", directory, d->d_name);
+		if (stat(search, &st) == -1)
+			continue;
+		if ((dironly && !(st.st_mode & S_IFDIR)) ||
+			(!dironly && (st.st_mode & S_IFDIR)))
+			continue;
+
+		if (*extension) {
+			if ( strlen( d->d_name ) < strlen( extension ) ||
+				Q_stricmp(
+					d->d_name + strlen( d->d_name ) - strlen( extension ),
+					extension ) ) {
+				continue; // didn't match
+			}
+		}
+
+		if ( nfiles == MAX_FOUND_FILES - 1 )
+			break;
+		list[ nfiles ] = CopyString( d->d_name );
+		nfiles++;
+	}
+
+	list[ nfiles ] = NULL;
+
+	closedir(fdir);
+
+	// return a copy of the list
+	*numfiles = nfiles;
+
+	if ( !nfiles ) {
+		return NULL;
+	}
+
+	listCopy = Z_Malloc( ( nfiles + 1 ) * sizeof( *listCopy ) );
+	for ( i = 0 ; i < nfiles ; i++ ) {
+		listCopy[i] = list[i];
+	}
+	listCopy[i] = NULL;
+
+	return listCopy;
+}
+
+/*
+==================
+Sys_FreeFileList
+==================
+*/
+void Sys_FreeFileList( char **list )
+{
+	int i;
+
+	if ( !list ) {
+		return;
+	}
+
+	for ( i = 0 ; list[i] ; i++ ) {
+		Z_Free( list[i] );
+	}
+
+	Z_Free( list );
+}
+
+#ifdef MACOS_X
+/*
+=================
+Sys_StripAppBundle
+
+Discovers if passed dir is suffixed with the directory structure of a Mac OS X
+.app bundle. If it is, the .app directory structure is stripped off the end and
+the result is returned. If not, dir is returned untouched.
+=================
+*/
+char *Sys_StripAppBundle( char *dir )
+{
+	static char cwd[MAX_OSPATH];
+
+	Q_strncpyz(cwd, dir, sizeof(cwd));
+	if(strcmp(Sys_Basename(cwd), "MacOS"))
+		return dir;
+	Q_strncpyz(cwd, Sys_Dirname(cwd), sizeof(cwd));
+	if(strcmp(Sys_Basename(cwd), "Contents"))
+		return dir;
+	Q_strncpyz(cwd, Sys_Dirname(cwd), sizeof(cwd));
+	if(!strstr(Sys_Basename(cwd), ".app"))
+		return dir;
+	Q_strncpyz(cwd, Sys_Dirname(cwd), sizeof(cwd));
+	return cwd;
+}
+#endif // MACOS_X
diff --git a/src/sys/sys_win32.c b/src/sys/sys_win32.c
new file mode 100644
index 00000000..1ab3130b
--- /dev/null
+++ b/src/sys/sys_win32.c
@@ -0,0 +1,510 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code 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.
+
+Quake III Arena source code 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.
+
+You should have received a copy of the GNU General Public License
+along with Quake III Arena source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+#include "../qcommon/q_shared.h"
+#include "../qcommon/qcommon.h"
+#include "sys_local.h"
+
+#include <windows.h>
+#include <lmerr.h>
+#include <lmcons.h>
+#include <lmwksta.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <direct.h>
+#include <io.h>
+#include <conio.h>
+#include <wincrypt.h>
+#include <shlobj.h>
+
+// Used to determine where to store user-specific files
+static char homePath[ MAX_OSPATH ] = { 0 };
+
+/*
+================
+Sys_DefaultHomePath
+================
+*/
+char *Sys_DefaultHomePath( void )
+{
+	TCHAR szPath[MAX_PATH];
+	FARPROC qSHGetFolderPath;
+	HMODULE shfolder = LoadLibrary("shfolder.dll");
+	
+	if( !*homePath )
+	{
+		if(shfolder == NULL)
+		{
+			Com_Printf("Unable to load SHFolder.dll\n");
+			return NULL;
+		}
+
+		qSHGetFolderPath = GetProcAddress(shfolder, "SHGetFolderPathA");
+		if(qSHGetFolderPath == NULL)
+		{
+			Com_Printf("Unable to find SHGetFolderPath in SHFolder.dll\n");
+			FreeLibrary(shfolder);
+			return NULL;
+		}
+
+		if( !SUCCEEDED( qSHGetFolderPath( NULL, CSIDL_APPDATA,
+						NULL, 0, szPath ) ) )
+		{
+			Com_Printf("Unable to detect CSIDL_APPDATA\n");
+			FreeLibrary(shfolder);
+			return NULL;
+		}
+		Q_strncpyz( homePath, szPath, sizeof( homePath ) );
+		Q_strcat( homePath, sizeof( homePath ), "\\Quake3" );
+		FreeLibrary(shfolder);
+		if( !CreateDirectory( homePath, NULL ) )
+		{
+			if( GetLastError() != ERROR_ALREADY_EXISTS )
+			{
+				Com_Printf("Unable to create directory \"%s\"\n", homePath );
+				return NULL;
+			}
+		}
+	}
+
+	return homePath;
+}
+
+/*
+================
+Sys_Milliseconds
+================
+*/
+int sys_timeBase;
+int Sys_Milliseconds (void)
+{
+	int             sys_curtime;
+	static qboolean initialized = qfalse;
+
+	if (!initialized) {
+		sys_timeBase = timeGetTime();
+		initialized = qtrue;
+	}
+	sys_curtime = timeGetTime() - sys_timeBase;
+
+	return sys_curtime;
+}
+
+#ifndef __GNUC__ //see snapvectora.s
+/*
+================
+Sys_SnapVector
+================
+*/
+void Sys_SnapVector( float *v )
+{
+	int i;
+	float f;
+
+	f = *v;
+	__asm	fld		f;
+	__asm	fistp	i;
+	*v = i;
+	v++;
+	f = *v;
+	__asm	fld		f;
+	__asm	fistp	i;
+	*v = i;
+	v++;
+	f = *v;
+	__asm	fld		f;
+	__asm	fistp	i;
+	*v = i;
+}
+#endif
+
+/*
+================
+Sys_RandomBytes
+================
+*/
+qboolean Sys_RandomBytes( byte *string, int len )
+{
+	HCRYPTPROV  prov;
+
+	if( !CryptAcquireContext( &prov, NULL, NULL,
+		PROV_RSA_FULL, CRYPT_VERIFYCONTEXT ) )  {
+
+		return qfalse;
+	}
+
+	if( !CryptGenRandom( prov, len, (BYTE *)string ) )  {
+		CryptReleaseContext( prov, 0 );
+		return qfalse;
+	}
+	CryptReleaseContext( prov, 0 );
+	return qtrue;
+}
+
+/*
+================
+Sys_GetCurrentUser
+================
+*/
+char *Sys_GetCurrentUser( void )
+{
+	static char s_userName[1024];
+	unsigned long size = sizeof( s_userName );
+
+	if( !GetUserName( s_userName, &size ) )
+		strcpy( s_userName, "player" );
+
+	if( !s_userName[0] )
+	{
+		strcpy( s_userName, "player" );
+	}
+
+	return s_userName;
+}
+
+/*
+================
+Sys_GetClipboardData
+================
+*/
+char *Sys_GetClipboardData( void )
+{
+	char *data = NULL;
+	char *cliptext;
+
+	if ( OpenClipboard( NULL ) != 0 ) {
+		HANDLE hClipboardData;
+
+		if ( ( hClipboardData = GetClipboardData( CF_TEXT ) ) != 0 ) {
+			if ( ( cliptext = GlobalLock( hClipboardData ) ) != 0 ) {
+				data = Z_Malloc( GlobalSize( hClipboardData ) + 1 );
+				Q_strncpyz( data, cliptext, GlobalSize( hClipboardData ) );
+				GlobalUnlock( hClipboardData );
+				
+				strtok( data, "\n\r\b" );
+			}
+		}
+		CloseClipboard();
+	}
+	return data;
+}
+
+#define MEM_THRESHOLD 96*1024*1024
+
+/*
+==================
+Sys_LowPhysicalMemory
+==================
+*/
+qboolean Sys_LowPhysicalMemory( void )
+{
+	MEMORYSTATUS stat;
+	GlobalMemoryStatus (&stat);
+	return (stat.dwTotalPhys <= MEM_THRESHOLD) ? qtrue : qfalse;
+}
+
+/*
+==============
+Sys_Basename
+==============
+*/
+const char *Sys_Basename( char *path )
+{
+	static char base[ MAX_OSPATH ] = { 0 };
+	int length;
+
+	length = strlen( path ) - 1;
+
+	// Skip trailing slashes
+	while( length > 0 && path[ length ] == '\\' )
+		length--;
+
+	while( length > 0 && path[ length - 1 ] != '\\' )
+		length--;
+
+	Q_strncpyz( base, &path[ length ], sizeof( base ) );
+
+	length = strlen( base ) - 1;
+
+	// Strip trailing slashes
+	while( length > 0 && base[ length ] == '\\' )
+    base[ length-- ] = '\0';
+
+	return base;
+}
+
+/*
+==============
+Sys_Dirname
+==============
+*/
+const char *Sys_Dirname( char *path )
+{
+	static char dir[ MAX_OSPATH ] = { 0 };
+	int length;
+
+	Q_strncpyz( dir, path, sizeof( dir ) );
+	length = strlen( dir ) - 1;
+
+	while( length > 0 && dir[ length ] != '\\' )
+		length--;
+
+	dir[ length ] = '\0';
+
+	return dir;
+}
+
+/*
+==============
+Sys_Mkdir
+==============
+*/
+void Sys_Mkdir( const char *path )
+{
+	_mkdir (path);
+}
+
+/*
+==============
+Sys_Cwd
+==============
+*/
+char *Sys_Cwd( void ) {
+	static char cwd[MAX_OSPATH];
+
+	_getcwd( cwd, sizeof( cwd ) - 1 );
+	cwd[MAX_OSPATH-1] = 0;
+
+	return cwd;
+}
+
+/*
+==============================================================
+
+DIRECTORY SCANNING
+
+==============================================================
+*/
+
+#define MAX_FOUND_FILES 0x1000
+
+/*
+==============
+Sys_ListFilteredFiles
+==============
+*/
+void Sys_ListFilteredFiles( const char *basedir, char *subdirs, char *filter, char **list, int *numfiles )
+{
+	char		search[MAX_OSPATH], newsubdirs[MAX_OSPATH];
+	char		filename[MAX_OSPATH];
+	int			findhandle;
+	struct _finddata_t findinfo;
+
+	if ( *numfiles >= MAX_FOUND_FILES - 1 ) {
+		return;
+	}
+
+	if (strlen(subdirs)) {
+		Com_sprintf( search, sizeof(search), "%s\\%s\\*", basedir, subdirs );
+	}
+	else {
+		Com_sprintf( search, sizeof(search), "%s\\*", basedir );
+	}
+
+	findhandle = _findfirst (search, &findinfo);
+	if (findhandle == -1) {
+		return;
+	}
+
+	do {
+		if (findinfo.attrib & _A_SUBDIR) {
+			if (Q_stricmp(findinfo.name, ".") && Q_stricmp(findinfo.name, "..")) {
+				if (strlen(subdirs)) {
+					Com_sprintf( newsubdirs, sizeof(newsubdirs), "%s\\%s", subdirs, findinfo.name);
+				}
+				else {
+					Com_sprintf( newsubdirs, sizeof(newsubdirs), "%s", findinfo.name);
+				}
+				Sys_ListFilteredFiles( basedir, newsubdirs, filter, list, numfiles );
+			}
+		}
+		if ( *numfiles >= MAX_FOUND_FILES - 1 ) {
+			break;
+		}
+		Com_sprintf( filename, sizeof(filename), "%s\\%s", subdirs, findinfo.name );
+		if (!Com_FilterPath( filter, filename, qfalse ))
+			continue;
+		list[ *numfiles ] = CopyString( filename );
+		(*numfiles)++;
+	} while ( _findnext (findhandle, &findinfo) != -1 );
+
+	_findclose (findhandle);
+}
+
+/*
+==============
+strgtr
+==============
+*/
+static qboolean strgtr(const char *s0, const char *s1)
+{
+	int l0, l1, i;
+
+	l0 = strlen(s0);
+	l1 = strlen(s1);
+
+	if (l1<l0) {
+		l0 = l1;
+	}
+
+	for(i=0;i<l0;i++) {
+		if (s1[i] > s0[i]) {
+			return qtrue;
+		}
+		if (s1[i] < s0[i]) {
+			return qfalse;
+		}
+	}
+	return qfalse;
+}
+
+/*
+==============
+Sys_ListFiles
+==============
+*/
+char **Sys_ListFiles( const char *directory, const char *extension, char *filter, int *numfiles, qboolean wantsubs )
+{
+	char		search[MAX_OSPATH];
+	int			nfiles;
+	char		**listCopy;
+	char		*list[MAX_FOUND_FILES];
+	struct _finddata_t findinfo;
+	int			findhandle;
+	int			flag;
+	int			i;
+
+	if (filter) {
+
+		nfiles = 0;
+		Sys_ListFilteredFiles( directory, "", filter, list, &nfiles );
+
+		list[ nfiles ] = 0;
+		*numfiles = nfiles;
+
+		if (!nfiles)
+			return NULL;
+
+		listCopy = Z_Malloc( ( nfiles + 1 ) * sizeof( *listCopy ) );
+		for ( i = 0 ; i < nfiles ; i++ ) {
+			listCopy[i] = list[i];
+		}
+		listCopy[i] = NULL;
+
+		return listCopy;
+	}
+
+	if ( !extension) {
+		extension = "";
+	}
+
+	// passing a slash as extension will find directories
+	if ( extension[0] == '/' && extension[1] == 0 ) {
+		extension = "";
+		flag = 0;
+	} else {
+		flag = _A_SUBDIR;
+	}
+
+	Com_sprintf( search, sizeof(search), "%s\\*%s", directory, extension );
+
+	// search
+	nfiles = 0;
+
+	findhandle = _findfirst (search, &findinfo);
+	if (findhandle == -1) {
+		*numfiles = 0;
+		return NULL;
+	}
+
+	do {
+		if ( (!wantsubs && flag ^ ( findinfo.attrib & _A_SUBDIR )) || (wantsubs && findinfo.attrib & _A_SUBDIR) ) {
+			if ( nfiles == MAX_FOUND_FILES - 1 ) {
+				break;
+			}
+			list[ nfiles ] = CopyString( findinfo.name );
+			nfiles++;
+		}
+	} while ( _findnext (findhandle, &findinfo) != -1 );
+
+	list[ nfiles ] = 0;
+
+	_findclose (findhandle);
+
+	// return a copy of the list
+	*numfiles = nfiles;
+
+	if ( !nfiles ) {
+		return NULL;
+	}
+
+	listCopy = Z_Malloc( ( nfiles + 1 ) * sizeof( *listCopy ) );
+	for ( i = 0 ; i < nfiles ; i++ ) {
+		listCopy[i] = list[i];
+	}
+	listCopy[i] = NULL;
+
+	do {
+		flag = 0;
+		for(i=1; i<nfiles; i++) {
+			if (strgtr(listCopy[i-1], listCopy[i])) {
+				char *temp = listCopy[i];
+				listCopy[i] = listCopy[i-1];
+				listCopy[i-1] = temp;
+				flag = 1;
+			}
+		}
+	} while(flag);
+
+	return listCopy;
+}
+
+/*
+==============
+Sys_FreeFileList
+==============
+*/
+void Sys_FreeFileList( char **list )
+{
+	int i;
+
+	if ( !list ) {
+		return;
+	}
+
+	for ( i = 0 ; list[i] ; i++ ) {
+		Z_Free( list[i] );
+	}
+
+	Z_Free( list );
+}
diff --git a/src/sys/tty_console.c b/src/sys/tty_console.c
new file mode 100644
index 00000000..34a09d10
--- /dev/null
+++ b/src/sys/tty_console.c
@@ -0,0 +1,440 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code 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.
+
+Quake III Arena source code 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.
+
+You should have received a copy of the GNU General Public License
+along with Quake III Arena source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+
+#include "../qcommon/q_shared.h"
+#include "../qcommon/qcommon.h"
+
+#include <unistd.h>
+#include <signal.h>
+#include <termios.h>
+#include <fcntl.h>
+
+/*
+=============================================================
+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_Hide and TTY_Show to be
+called before and after a stdout or stderr output
+=============================================================
+*/
+
+// general flag to tell about tty console mode
+static qboolean ttycon_on = qfalse;
+static int ttycon_hide = 0;
+
+// some key codes that the terminal may be using, initialised on start up
+static int TTY_erase;
+static int TTY_eof;
+
+static struct termios TTY_tc;
+
+static field_t TTY_con;
+
+// This is somewhat of aduplicate of the graphical console history
+// but it's safer more modular to have our own here
+#define TTY_HISTORY 32
+static field_t ttyEditLines[ TTY_HISTORY ];
+static int hist_current = -1, hist_count = 0;
+
+/*
+==================
+TTY_FlushIn
+
+Flush stdin, I suspect some terminals are sending a LOT of shit
+FIXME relevant?
+==================
+*/
+static void TTY_FlushIn( void )
+{
+	char key;
+	while (read(0, &key, 1)!=-1);
+}
+
+/*
+==================
+TTY_Back
+
+Output a backspace
+
+NOTE: it seems on some terminals just sending '\b' is not enough so instead we
+send "\b \b"
+(FIXME there may be a way to find out if '\b' alone would work though)
+==================
+*/
+static void TTY_Back( void )
+{
+	char key;
+	key = '\b';
+	write(1, &key, 1);
+	key = ' ';
+	write(1, &key, 1);
+	key = '\b';
+	write(1, &key, 1);
+}
+
+/*
+==================
+TTY_Hide
+
+Clear the display of the line currently edited
+bring cursor back to beginning of line
+==================
+*/
+void TTY_Hide( void )
+{
+	if( ttycon_on )
+	{
+		int i;
+		if (ttycon_hide)
+		{
+			ttycon_hide++;
+			return;
+		}
+		if (TTY_con.cursor>0)
+		{
+			for (i=0; i<TTY_con.cursor; i++)
+			{
+				TTY_Back();
+			}
+		}
+		TTY_Back(); // Delete "]"
+		ttycon_hide++;
+	}
+}
+
+/*
+==================
+TTY_Show
+
+Show the current line
+FIXME need to position the cursor if needed?
+==================
+*/
+void TTY_Show( void )
+{
+	if( ttycon_on )
+	{
+		int i;
+
+		assert(ttycon_hide>0);
+		ttycon_hide--;
+		if (ttycon_hide == 0)
+		{
+			write( 1, "]", 1 );
+			if (TTY_con.cursor)
+			{
+				for (i=0; i<TTY_con.cursor; i++)
+				{
+					write(1, TTY_con.buffer+i, 1);
+				}
+			}
+		}
+	}
+}
+
+/*
+==================
+TTY_Shutdown
+
+Never exit without calling this, or your terminal will be left in a pretty bad state
+==================
+*/
+void TTY_Shutdown( void )
+{
+	if (ttycon_on)
+	{
+		TTY_Back(); // Delete "]"
+		tcsetattr (0, TCSADRAIN, &TTY_tc);
+
+		// Restore blocking to stdin reads
+		fcntl( 0, F_SETFL, fcntl( 0, F_GETFL, 0 ) & ~O_NDELAY );
+	}
+}
+
+/*
+==================
+Hist_Add
+==================
+*/
+void Hist_Add(field_t *field)
+{
+	int i;
+	assert(hist_count <= TTY_HISTORY);
+	assert(hist_count >= 0);
+	assert(hist_current >= -1);
+	assert(hist_current <= hist_count);
+	// make some room
+	for (i=TTY_HISTORY-1; i>0; i--)
+	{
+		ttyEditLines[i] = ttyEditLines[i-1];
+	}
+	ttyEditLines[0] = *field;
+	if (hist_count<TTY_HISTORY)
+	{
+		hist_count++;
+	}
+	hist_current = -1; // re-init
+}
+
+/*
+==================
+Hist_Prev
+==================
+*/
+field_t *Hist_Prev( void )
+{
+	int hist_prev;
+	assert(hist_count <= TTY_HISTORY);
+	assert(hist_count >= 0);
+	assert(hist_current >= -1);
+	assert(hist_current <= hist_count);
+	hist_prev = hist_current + 1;
+	if (hist_prev >= hist_count)
+	{
+		return NULL;
+	}
+	hist_current++;
+	return &(ttyEditLines[hist_current]);
+}
+
+/*
+==================
+Hist_Next
+==================
+*/
+field_t *Hist_Next( void )
+{
+	assert(hist_count <= TTY_HISTORY);
+	assert(hist_count >= 0);
+	assert(hist_current >= -1);
+	assert(hist_current <= hist_count);
+	if (hist_current >= 0)
+	{
+		hist_current--;
+	}
+	if (hist_current == -1)
+	{
+		return NULL;
+	}
+	return &(ttyEditLines[hist_current]);
+}
+
+/*
+==================
+TTY_Init
+
+Initialize the console input (tty mode if possible)
+==================
+*/
+void TTY_Init( void )
+{
+	struct termios tc;
+
+	// If the process is backgrounded (running non interactively)
+	// then SIGTTIN or SIGTOU is emitted, if not caught, turns into a SIGSTP
+	signal(SIGTTIN, SIG_IGN);
+	signal(SIGTTOU, SIG_IGN);
+
+	// Make stdin reads non-blocking
+	fcntl( 0, F_SETFL, fcntl( 0, F_GETFL, 0 ) | O_NDELAY );
+
+	if (isatty(STDIN_FILENO)!=1)
+	{
+		Com_Printf( "stdin is not a tty, tty console mode disabled\n");
+		ttycon_on = qfalse;
+		return;
+	}
+
+	Field_Clear(&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 sig�
+	nal
+	*/
+	tc.c_lflag &= ~(ECHO | ICANON);
+
+	/*
+	ISTRIP strip off bit 8
+	INPCK enable input parity checking
+	*/
+	tc.c_iflag &= ~(ISTRIP | INPCK);
+	tc.c_cc[VMIN] = 1;
+	tc.c_cc[VTIME] = 0;
+	tcsetattr (0, TCSADRAIN, &tc);
+	ttycon_on = qtrue;
+}
+
+/*
+==================
+TTY_ConsoleInput
+==================
+*/
+char *TTY_ConsoleInput( void )
+{
+	// we use this when sending back commands
+	static char text[256];
+	int avail;
+	char key;
+	field_t *history;
+
+	if( ttycon_on )
+	{
+		avail = read(0, &key, 1);
+		if (avail != -1)
+		{
+			// 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();
+				}
+				return NULL;
+			}
+			// check if this is a control char
+			if ((key) && (key) < ' ')
+			{
+				if (key == '\n')
+				{
+					// push it in history
+					Hist_Add(&TTY_con);
+					strcpy(text, TTY_con.buffer);
+					Field_Clear(&TTY_con);
+					key = '\n';
+					write(1, &key, 1);
+					write( 1, "]", 1 );
+					return text;
+				}
+				if (key == '\t')
+				{
+					TTY_Hide();
+					Field_AutoComplete( &TTY_con );
+					TTY_Show();
+					return NULL;
+				}
+				avail = read(0, &key, 1);
+				if (avail != -1)
+				{
+					// VT 100 keys
+					if (key == '[' || key == 'O')
+					{
+						avail = read(0, &key, 1);
+						if (avail != -1)
+						{
+							switch (key)
+							{
+								case 'A':
+									history = Hist_Prev();
+									if (history)
+									{
+										TTY_Hide();
+										TTY_con = *history;
+										TTY_Show();
+									}
+									TTY_FlushIn();
+									return NULL;
+									break;
+								case 'B':
+									history = Hist_Next();
+									TTY_Hide();
+									if (history)
+									{
+										TTY_con = *history;
+									} else
+									{
+										Field_Clear(&TTY_con);
+									}
+									TTY_Show();
+									TTY_FlushIn();
+									return NULL;
+									break;
+								case 'C':
+									return NULL;
+								case 'D':
+									return NULL;
+							}
+						}
+					}
+				}
+				Com_DPrintf("droping ISCTL sequence: %d, TTY_erase: %d\n", key, TTY_erase);
+				TTY_FlushIn();
+				return NULL;
+			}
+			// push regular character
+			TTY_con.buffer[TTY_con.cursor] = key;
+			TTY_con.cursor++;
+			// print the current line (this is differential)
+			write(1, &key, 1);
+		}
+
+		return NULL;
+	}
+	else
+	{
+		int     len;
+		fd_set  fdset;
+		struct timeval timeout;
+		static qboolean stdin_active;
+
+		if (!com_dedicated || !com_dedicated->value)
+			return NULL;
+
+		if (!stdin_active)
+			return NULL;
+
+		FD_ZERO(&fdset);
+		FD_SET(0, &fdset); // stdin
+		timeout.tv_sec = 0;
+		timeout.tv_usec = 0;
+		if (select (1, &fdset, NULL, NULL, &timeout) == -1 || !FD_ISSET(0, &fdset))
+		{
+			return NULL;
+		}
+
+		len = read (0, text, sizeof(text));
+		if (len == 0)
+		{ // eof!
+			stdin_active = qfalse;
+			return NULL;
+		}
+
+		if (len < 1)
+			return NULL;
+		text[len-1] = 0;    // rip off the /n and terminate
+
+		return text;
+	}
+}
diff --git a/src/sys/win_resource.h b/src/sys/win_resource.h
new file mode 100644
index 00000000..93c517ea
--- /dev/null
+++ b/src/sys/win_resource.h
@@ -0,0 +1,44 @@
+/*
+===========================================================================
+Copyright (C) 1999-2005 Id Software, Inc.
+
+This file is part of Quake III Arena source code.
+
+Quake III Arena source code 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.
+
+Quake III Arena source code 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.
+
+You should have received a copy of the GNU General Public License
+along with Quake III Arena source code; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+===========================================================================
+*/
+//{{NO_DEPENDENCIES}}
+// Microsoft Developer Studio generated include file.
+// Used by winquake.rc
+//
+#define IDS_STRING1                     1
+#define IDI_ICON1                       1
+#define IDB_BITMAP1                     1
+#define IDB_BITMAP2                     128
+#define IDC_CURSOR1                     129
+#define IDC_CURSOR2                     130
+#define IDC_CURSOR3                     131
+
+// Next default values for new objects
+// 
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NO_MFC                     1
+#define _APS_NEXT_RESOURCE_VALUE        132
+#define _APS_NEXT_COMMAND_VALUE         40001
+#define _APS_NEXT_CONTROL_VALUE         1005
+#define _APS_NEXT_SYMED_VALUE           101
+#endif
+#endif
diff --git a/src/sys/win_resource.rc b/src/sys/win_resource.rc
new file mode 100644
index 00000000..4b50adc2
--- /dev/null
+++ b/src/sys/win_resource.rc
@@ -0,0 +1,75 @@
+//Microsoft Developer Studio generated resource script.
+//
+#include "win_resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#ifndef __MINGW32__
+#include "winres.h"
+#else
+#include <winresrc.h>
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE DISCARDABLE 
+BEGIN
+    "resource.h\0"
+END
+
+2 TEXTINCLUDE DISCARDABLE 
+BEGIN
+    "#include ""winres.h""\r\n"
+    "\0"
+END
+
+3 TEXTINCLUDE DISCARDABLE 
+BEGIN
+    "\0"
+END
+
+#endif    // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+IDI_ICON1               ICON    DISCARDABLE     "misc/tremulous.ico"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// String Table
+//
+
+STRINGTABLE DISCARDABLE 
+BEGIN
+    IDS_STRING1             "Tremulous"
+END
+
+#endif    // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
-- 
cgit