aboutsummaryrefslogtreecommitdiff
path: root/code/sys
diff options
context:
space:
mode:
authorFathi Boudra <fabo@debian.org>2011-10-20 15:18:11 +0300
committerFathi Boudra <fabo@debian.org>2011-10-20 15:18:11 +0300
commit840643efa638de9dc44f45dc4f3f1c81c2985498 (patch)
treec4d2f3b70c4138f8f7df772b09e663d7794d9456 /code/sys
Initial import1772-openpandora
Diffstat (limited to 'code/sys')
-rw-r--r--code/sys/con_log.c129
-rw-r--r--code/sys/con_passive.c68
-rw-r--r--code/sys/con_tty.c476
-rw-r--r--code/sys/con_win32.c358
-rw-r--r--code/sys/sys_cocoa.m40
-rw-r--r--code/sys/sys_loadlib.h49
-rw-r--r--code/sys/sys_local.h56
-rw-r--r--code/sys/sys_main.c570
-rw-r--r--code/sys/sys_unix.c613
-rw-r--r--code/sys/sys_win32.c665
-rw-r--r--code/sys/win_resource.h44
-rw-r--r--code/sys/win_resource.rc76
12 files changed, 3144 insertions, 0 deletions
diff --git a/code/sys/con_log.c b/code/sys/con_log.c
new file mode 100644
index 0000000..c3a4a5b
--- /dev/null
+++ b/code/sys/con_log.c
@@ -0,0 +1,129 @@
+/*
+===========================================================================
+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"
+
+#define MAX_LOG 32768
+
+static char consoleLog[ MAX_LOG ];
+static unsigned int writePos = 0;
+static unsigned int readPos = 0;
+
+/*
+==================
+CON_LogSize
+==================
+*/
+unsigned int CON_LogSize( void )
+{
+ if( readPos <= writePos )
+ return writePos - readPos;
+ else
+ return writePos + MAX_LOG - readPos;
+}
+
+/*
+==================
+CON_LogFree
+==================
+*/
+static unsigned int CON_LogFree( void )
+{
+ return MAX_LOG - CON_LogSize( ) - 1;
+}
+
+/*
+==================
+CON_LogWrite
+==================
+*/
+unsigned int CON_LogWrite( const char *in )
+{
+ unsigned int length = strlen( in );
+ unsigned int firstChunk;
+ unsigned int secondChunk;
+
+ while( CON_LogFree( ) < length && CON_LogSize( ) > 0 )
+ {
+ // Free enough space
+ while( consoleLog[ readPos ] != '\n' && CON_LogSize( ) > 1 )
+ readPos = ( readPos + 1 ) % MAX_LOG;
+
+ // Skip past the '\n'
+ readPos = ( readPos + 1 ) % MAX_LOG;
+ }
+
+ if( CON_LogFree( ) < length )
+ return 0;
+
+ if( writePos + length > MAX_LOG )
+ {
+ firstChunk = MAX_LOG - writePos;
+ secondChunk = length - firstChunk;
+ }
+ else
+ {
+ firstChunk = length;
+ secondChunk = 0;
+ }
+
+ Com_Memcpy( consoleLog + writePos, in, firstChunk );
+ Com_Memcpy( consoleLog, in + firstChunk, secondChunk );
+
+ writePos = ( writePos + length ) % MAX_LOG;
+
+ return length;
+}
+
+/*
+==================
+CON_LogRead
+==================
+*/
+unsigned int CON_LogRead( char *out, unsigned int outSize )
+{
+ unsigned int firstChunk;
+ unsigned int secondChunk;
+
+ if( CON_LogSize( ) < outSize )
+ outSize = CON_LogSize( );
+
+ if( readPos + outSize > MAX_LOG )
+ {
+ firstChunk = MAX_LOG - readPos;
+ secondChunk = outSize - firstChunk;
+ }
+ else
+ {
+ firstChunk = outSize;
+ secondChunk = 0;
+ }
+
+ Com_Memcpy( out, consoleLog + readPos, firstChunk );
+ Com_Memcpy( out + firstChunk, out, secondChunk );
+
+ readPos = ( readPos + outSize ) % MAX_LOG;
+
+ return outSize;
+}
diff --git a/code/sys/con_passive.c b/code/sys/con_passive.c
new file mode 100644
index 0000000..794c895
--- /dev/null
+++ b/code/sys/con_passive.c
@@ -0,0 +1,68 @@
+/*
+===========================================================================
+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 <stdio.h>
+
+/*
+==================
+CON_Shutdown
+==================
+*/
+void CON_Shutdown( void )
+{
+}
+
+/*
+==================
+CON_Init
+==================
+*/
+void CON_Init( void )
+{
+}
+
+/*
+==================
+CON_Input
+==================
+*/
+char *CON_Input( void )
+{
+ return NULL;
+}
+
+/*
+==================
+CON_Print
+==================
+*/
+void CON_Print( const char *msg )
+{
+ if( com_ansiColor && com_ansiColor->integer )
+ Sys_AnsiColorPrint( msg );
+ else
+ fputs( msg, stderr );
+}
diff --git a/code/sys/con_tty.c b/code/sys/con_tty.c
new file mode 100644
index 0000000..7f5f631
--- /dev/null
+++ b/code/sys/con_tty.c
@@ -0,0 +1,476 @@
+/*
+===========================================================================
+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 <unistd.h>
+#include <signal.h>
+#include <termios.h>
+#include <fcntl.h>
+#include <sys/time.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 CON_Hide and CON_Show to be
+called before and after a stdout or stderr output
+=============================================================
+*/
+
+extern qboolean stdinIsATTY;
+static qboolean stdin_active;
+// 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 CON_HISTORY 32
+static field_t ttyEditLines[ CON_HISTORY ];
+static int hist_current = -1, hist_count = 0;
+
+/*
+==================
+CON_FlushIn
+
+Flush stdin, I suspect some terminals are sending a LOT of shit
+FIXME relevant?
+==================
+*/
+static void CON_FlushIn( void )
+{
+ char key;
+ while (read(STDIN_FILENO, &key, 1)!=-1);
+}
+
+/*
+==================
+CON_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 CON_Back( void )
+{
+ char key;
+ size_t size;
+
+ key = '\b';
+ size = write(STDOUT_FILENO, &key, 1);
+ key = ' ';
+ size = write(STDOUT_FILENO, &key, 1);
+ key = '\b';
+ size = write(STDOUT_FILENO, &key, 1);
+}
+
+/*
+==================
+CON_Hide
+
+Clear the display of the line currently edited
+bring cursor back to beginning of line
+==================
+*/
+static void CON_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++)
+ {
+ CON_Back();
+ }
+ }
+ CON_Back(); // Delete "]"
+ ttycon_hide++;
+ }
+}
+
+/*
+==================
+CON_Show
+
+Show the current line
+FIXME need to position the cursor if needed?
+==================
+*/
+static void CON_Show( void )
+{
+ if( ttycon_on )
+ {
+ int i;
+
+ assert(ttycon_hide>0);
+ ttycon_hide--;
+ if (ttycon_hide == 0)
+ {
+ size_t size;
+ size = write(STDOUT_FILENO, "]", 1);
+ if (TTY_con.cursor)
+ {
+ for (i=0; i<TTY_con.cursor; i++)
+ {
+ size = write(STDOUT_FILENO, TTY_con.buffer+i, 1);
+ }
+ }
+ }
+ }
+}
+
+/*
+==================
+CON_Shutdown
+
+Never exit without calling this, or your terminal will be left in a pretty bad state
+==================
+*/
+void CON_Shutdown( void )
+{
+ if (ttycon_on)
+ {
+ CON_Back(); // Delete "]"
+ tcsetattr (STDIN_FILENO, TCSADRAIN, &TTY_tc);
+ }
+
+ // Restore blocking to stdin reads
+ fcntl(STDIN_FILENO, F_SETFL, fcntl(STDIN_FILENO, F_GETFL, 0) & ~O_NONBLOCK);
+}
+
+/*
+==================
+Hist_Add
+==================
+*/
+void Hist_Add(field_t *field)
+{
+ int i;
+ assert(hist_count <= CON_HISTORY);
+ assert(hist_count >= 0);
+ assert(hist_current >= -1);
+ assert(hist_current <= hist_count);
+ // make some room
+ for (i=CON_HISTORY-1; i>0; i--)
+ {
+ ttyEditLines[i] = ttyEditLines[i-1];
+ }
+ ttyEditLines[0] = *field;
+ if (hist_count<CON_HISTORY)
+ {
+ hist_count++;
+ }
+ hist_current = -1; // re-init
+}
+
+/*
+==================
+Hist_Prev
+==================
+*/
+field_t *Hist_Prev( void )
+{
+ int hist_prev;
+ assert(hist_count <= CON_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 <= CON_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]);
+}
+
+/*
+==================
+CON_SigCont
+Reinitialize console input after receiving SIGCONT, as on Linux the terminal seems to lose all
+set attributes if user did CTRL+Z and then does fg again.
+==================
+*/
+
+void CON_SigCont(int signum)
+{
+ CON_Init();
+}
+
+/*
+==================
+CON_Init
+
+Initialize the console input (tty mode if possible)
+==================
+*/
+void CON_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);
+
+ // If SIGCONT is received, reinitialize console
+ signal(SIGCONT, CON_SigCont);
+
+ // Make stdin reads non-blocking
+ fcntl(STDIN_FILENO, F_SETFL, fcntl(STDIN_FILENO, F_GETFL, 0) | O_NONBLOCK );
+
+ if (!stdinIsATTY)
+ {
+ Com_Printf("tty console mode disabled\n");
+ ttycon_on = qfalse;
+ stdin_active = qtrue;
+ return;
+ }
+
+ Field_Clear(&TTY_con);
+ tcgetattr (STDIN_FILENO, &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 (STDIN_FILENO, TCSADRAIN, &tc);
+ ttycon_on = qtrue;
+}
+
+/*
+==================
+CON_Input
+==================
+*/
+char *CON_Input( void )
+{
+ // we use this when sending back commands
+ static char text[MAX_EDIT_LINE];
+ int avail;
+ char key;
+ field_t *history;
+ size_t size;
+
+ if(ttycon_on)
+ {
+ avail = read(STDIN_FILENO, &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';
+ CON_Back();
+ }
+ return NULL;
+ }
+ // check if this is a control char
+ if ((key) && (key) < ' ')
+ {
+ if (key == '\n')
+ {
+ // push it in history
+ Hist_Add(&TTY_con);
+ Q_strncpyz(text, TTY_con.buffer, sizeof(text));
+ Field_Clear(&TTY_con);
+ key = '\n';
+ size = write(1, &key, 1);
+ size = write( 1, "]", 1 );
+ return text;
+ }
+ if (key == '\t')
+ {
+ CON_Hide();
+ Field_AutoComplete( &TTY_con );
+ CON_Show();
+ return NULL;
+ }
+ avail = read(STDIN_FILENO, &key, 1);
+ if (avail != -1)
+ {
+ // VT 100 keys
+ if (key == '[' || key == 'O')
+ {
+ avail = read(STDIN_FILENO, &key, 1);
+ if (avail != -1)
+ {
+ switch (key)
+ {
+ case 'A':
+ history = Hist_Prev();
+ if (history)
+ {
+ CON_Hide();
+ TTY_con = *history;
+ CON_Show();
+ }
+ CON_FlushIn();
+ return NULL;
+ break;
+ case 'B':
+ history = Hist_Next();
+ CON_Hide();
+ if (history)
+ {
+ TTY_con = *history;
+ } else
+ {
+ Field_Clear(&TTY_con);
+ }
+ CON_Show();
+ CON_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);
+ CON_FlushIn();
+ return NULL;
+ }
+ if (TTY_con.cursor >= sizeof(text) - 1)
+ return NULL;
+ // push regular character
+ TTY_con.buffer[TTY_con.cursor] = key;
+ TTY_con.cursor++;
+ // print the current line (this is differential)
+ size = write(STDOUT_FILENO, &key, 1);
+ }
+
+ return NULL;
+ }
+ else if (stdin_active)
+ {
+ int len;
+ fd_set fdset;
+ struct timeval timeout;
+
+ FD_ZERO(&fdset);
+ FD_SET(STDIN_FILENO, &fdset); // stdin
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 0;
+ if(select (STDIN_FILENO + 1, &fdset, NULL, NULL, &timeout) == -1 || !FD_ISSET(STDIN_FILENO, &fdset))
+ return NULL;
+
+ len = read(STDIN_FILENO, 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;
+ }
+ return NULL;
+}
+
+/*
+==================
+CON_Print
+==================
+*/
+void CON_Print( const char *msg )
+{
+ CON_Hide( );
+
+ if( com_ansiColor && com_ansiColor->integer )
+ Sys_AnsiColorPrint( msg );
+ else
+ fputs( msg, stderr );
+
+ CON_Show( );
+}
diff --git a/code/sys/con_win32.c b/code/sys/con_win32.c
new file mode 100644
index 0000000..787c74f
--- /dev/null
+++ b/code/sys/con_win32.c
@@ -0,0 +1,358 @@
+/*
+===========================================================================
+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"
+
+#define QCONSOLE_HISTORY 32
+
+static WORD qconsole_attrib;
+
+// saved console status
+static DWORD qconsole_orig_mode;
+static CONSOLE_CURSOR_INFO qconsole_orig_cursorinfo;
+
+// cmd history
+static char qconsole_history[ QCONSOLE_HISTORY ][ MAX_EDIT_LINE ];
+static int qconsole_history_pos = -1;
+static int qconsole_history_oldest = 0;
+
+// current edit buffer
+static char qconsole_line[ MAX_EDIT_LINE ];
+static int qconsole_linelen = 0;
+
+static HANDLE qconsole_hout;
+static HANDLE qconsole_hin;
+
+/*
+==================
+CON_CtrlHandler
+
+The Windows Console doesn't use signals for terminating the application
+with Ctrl-C, logging off, window closing, etc. Instead it uses a special
+handler routine. Fortunately, the values for Ctrl signals don't seem to
+overlap with true signal codes that Windows provides, so calling
+Sys_SigHandler() with those numbers should be safe for generating unique
+shutdown messages.
+==================
+*/
+static BOOL WINAPI CON_CtrlHandler( DWORD sig )
+{
+ Sys_SigHandler( sig );
+ return TRUE;
+}
+
+/*
+==================
+CON_HistAdd
+==================
+*/
+static void CON_HistAdd( void )
+{
+ Q_strncpyz( qconsole_history[ qconsole_history_oldest ], qconsole_line,
+ sizeof( qconsole_history[ qconsole_history_oldest ] ) );
+
+ if( qconsole_history_oldest >= QCONSOLE_HISTORY - 1 )
+ qconsole_history_oldest = 0;
+ else
+ qconsole_history_oldest++;
+
+ qconsole_history_pos = qconsole_history_oldest;
+}
+
+/*
+==================
+CON_HistPrev
+==================
+*/
+static void CON_HistPrev( void )
+{
+ int pos;
+
+ pos = ( qconsole_history_pos < 1 ) ?
+ ( QCONSOLE_HISTORY - 1 ) : ( qconsole_history_pos - 1 );
+
+ // don' t allow looping through history
+ if( pos == qconsole_history_oldest )
+ return;
+
+ qconsole_history_pos = pos;
+ Q_strncpyz( qconsole_line, qconsole_history[ qconsole_history_pos ],
+ sizeof( qconsole_line ) );
+ qconsole_linelen = strlen( qconsole_line );
+}
+
+/*
+==================
+CON_HistNext
+==================
+*/
+static void CON_HistNext( void )
+{
+ int pos;
+
+ pos = ( qconsole_history_pos >= QCONSOLE_HISTORY - 1 ) ?
+ 0 : ( qconsole_history_pos + 1 );
+
+ // clear the edit buffer if they try to advance to a future command
+ if( pos == qconsole_history_oldest )
+ {
+ qconsole_line[ 0 ] = '\0';
+ qconsole_linelen = 0;
+ return;
+ }
+
+ qconsole_history_pos = pos;
+ Q_strncpyz( qconsole_line, qconsole_history[ qconsole_history_pos ],
+ sizeof( qconsole_line ) );
+ qconsole_linelen = strlen( qconsole_line );
+}
+
+
+/*
+==================
+CON_Show
+==================
+*/
+static void CON_Show( void )
+{
+ CONSOLE_SCREEN_BUFFER_INFO binfo;
+ COORD writeSize = { MAX_EDIT_LINE, 1 };
+ COORD writePos = { 0, 0 };
+ SMALL_RECT writeArea = { 0, 0, 0, 0 };
+ int i;
+ CHAR_INFO line[ MAX_EDIT_LINE ];
+
+ GetConsoleScreenBufferInfo( qconsole_hout, &binfo );
+
+ // if we're in the middle of printf, don't bother writing the buffer
+ if( binfo.dwCursorPosition.X != 0 )
+ return;
+
+ writeArea.Left = 0;
+ writeArea.Top = binfo.dwCursorPosition.Y;
+ writeArea.Bottom = binfo.dwCursorPosition.Y;
+ writeArea.Right = MAX_EDIT_LINE;
+
+ // build a space-padded CHAR_INFO array
+ for( i = 0; i < MAX_EDIT_LINE; i++ )
+ {
+ if( i < qconsole_linelen )
+ line[ i ].Char.AsciiChar = qconsole_line[ i ];
+ else
+ line[ i ].Char.AsciiChar = ' ';
+
+ line[ i ].Attributes = qconsole_attrib;
+ }
+
+ if( qconsole_linelen > binfo.srWindow.Right )
+ {
+ WriteConsoleOutput( qconsole_hout,
+ line + (qconsole_linelen - binfo.srWindow.Right ),
+ writeSize, writePos, &writeArea );
+ }
+ else
+ {
+ WriteConsoleOutput( qconsole_hout, line, writeSize,
+ writePos, &writeArea );
+ }
+}
+
+/*
+==================
+CON_Shutdown
+==================
+*/
+void CON_Shutdown( void )
+{
+ SetConsoleMode( qconsole_hin, qconsole_orig_mode );
+ SetConsoleCursorInfo( qconsole_hout, &qconsole_orig_cursorinfo );
+ CloseHandle( qconsole_hout );
+ CloseHandle( qconsole_hin );
+}
+
+/*
+==================
+CON_Init
+==================
+*/
+void CON_Init( void )
+{
+ CONSOLE_CURSOR_INFO curs;
+ CONSOLE_SCREEN_BUFFER_INFO info;
+ int i;
+
+ // handle Ctrl-C or other console termination
+ SetConsoleCtrlHandler( CON_CtrlHandler, TRUE );
+
+ qconsole_hin = GetStdHandle( STD_INPUT_HANDLE );
+ if( qconsole_hin == INVALID_HANDLE_VALUE )
+ return;
+
+ qconsole_hout = GetStdHandle( STD_OUTPUT_HANDLE );
+ if( qconsole_hout == INVALID_HANDLE_VALUE )
+ return;
+
+ GetConsoleMode( qconsole_hin, &qconsole_orig_mode );
+
+ // allow mouse wheel scrolling
+ SetConsoleMode( qconsole_hin,
+ qconsole_orig_mode & ~ENABLE_MOUSE_INPUT );
+
+ FlushConsoleInputBuffer( qconsole_hin );
+
+ GetConsoleScreenBufferInfo( qconsole_hout, &info );
+ qconsole_attrib = info.wAttributes;
+
+ SetConsoleTitle("ioquake3 Dedicated Server Console");
+
+ // make cursor invisible
+ GetConsoleCursorInfo( qconsole_hout, &qconsole_orig_cursorinfo );
+ curs.dwSize = 1;
+ curs.bVisible = FALSE;
+ SetConsoleCursorInfo( qconsole_hout, &curs );
+
+ // initialize history
+ for( i = 0; i < QCONSOLE_HISTORY; i++ )
+ qconsole_history[ i ][ 0 ] = '\0';
+}
+
+/*
+==================
+CON_Input
+==================
+*/
+char *CON_Input( void )
+{
+ INPUT_RECORD buff[ MAX_EDIT_LINE ];
+ DWORD count = 0, events = 0;
+ WORD key = 0;
+ int i;
+ int newlinepos = -1;
+
+ if( !GetNumberOfConsoleInputEvents( qconsole_hin, &events ) )
+ return NULL;
+
+ if( events < 1 )
+ return NULL;
+
+ // if we have overflowed, start dropping oldest input events
+ if( events >= MAX_EDIT_LINE )
+ {
+ ReadConsoleInput( qconsole_hin, buff, 1, &events );
+ return NULL;
+ }
+
+ if( !ReadConsoleInput( qconsole_hin, buff, events, &count ) )
+ return NULL;
+
+ FlushConsoleInputBuffer( qconsole_hin );
+
+ for( i = 0; i < count; i++ )
+ {
+ if( buff[ i ].EventType != KEY_EVENT )
+ continue;
+ if( !buff[ i ].Event.KeyEvent.bKeyDown )
+ continue;
+
+ key = buff[ i ].Event.KeyEvent.wVirtualKeyCode;
+
+ if( key == VK_RETURN )
+ {
+ newlinepos = i;
+ break;
+ }
+ else if( key == VK_UP )
+ {
+ CON_HistPrev();
+ break;
+ }
+ else if( key == VK_DOWN )
+ {
+ CON_HistNext();
+ break;
+ }
+ else if( key == VK_TAB )
+ {
+ field_t f;
+
+ Q_strncpyz( f.buffer, qconsole_line,
+ sizeof( f.buffer ) );
+ Field_AutoComplete( &f );
+ Q_strncpyz( qconsole_line, f.buffer,
+ sizeof( qconsole_line ) );
+ qconsole_linelen = strlen( qconsole_line );
+ break;
+ }
+
+ if( qconsole_linelen < sizeof( qconsole_line ) - 1 )
+ {
+ char c = buff[ i ].Event.KeyEvent.uChar.AsciiChar;
+
+ if( key == VK_BACK )
+ {
+ int pos = ( qconsole_linelen > 0 ) ?
+ qconsole_linelen - 1 : 0;
+
+ qconsole_line[ pos ] = '\0';
+ qconsole_linelen = pos;
+ }
+ else if( c )
+ {
+ qconsole_line[ qconsole_linelen++ ] = c;
+ qconsole_line[ qconsole_linelen ] = '\0';
+ }
+ }
+ }
+
+ CON_Show();
+
+ if( newlinepos < 0)
+ return NULL;
+
+ if( !qconsole_linelen )
+ {
+ Com_Printf( "\n" );
+ return NULL;
+ }
+
+ CON_HistAdd();
+ Com_Printf( "%s\n", qconsole_line );
+
+ qconsole_linelen = 0;
+
+ return qconsole_line;
+}
+
+/*
+==================
+CON_Print
+==================
+*/
+void CON_Print( const char *msg )
+{
+ fputs( msg, stderr );
+
+ CON_Show( );
+}
diff --git a/code/sys/sys_cocoa.m b/code/sys/sys_cocoa.m
new file mode 100644
index 0000000..cfc754d
--- /dev/null
+++ b/code/sys/sys_cocoa.m
@@ -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
+===========================================================================
+*/
+
+#ifndef MACOS_X
+#error This file is for Mac OS X only. You probably should not compile it.
+#endif
+
+// Please note that this file is just some Mac-specific bits. Most of the
+// Mac OS X code is shared with other Unix platforms in sys_unix.c ...
+
+#import <Cocoa/Cocoa.h>
+
+void Cocoa_MsgBox( const char *text )
+{
+ NSRunInformationalAlertPanel(@"ioquake3",
+ [NSString stringWithUTF8String:text],
+ @"OK", nil, nil);
+}
+
+// end of sys_cocoa.m ...
+
diff --git a/code/sys/sys_loadlib.h b/code/sys/sys_loadlib.h
new file mode 100644
index 0000000..994c5d3
--- /dev/null
+++ b/code/sys/sys_loadlib.h
@@ -0,0 +1,49 @@
+/*
+===========================================================================
+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
+# ifdef _WIN32
+# include <windows.h>
+# define Sys_LoadLibrary(f) (void*)LoadLibrary(f)
+# define Sys_UnloadLibrary(h) FreeLibrary((HMODULE)h)
+# define Sys_LoadFunction(h,fn) (void*)GetProcAddress((HMODULE)h,fn)
+# define Sys_LibraryError() "unknown"
+# else
+# 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()
+# endif
+#else
+# ifdef USE_LOCAL_HEADERS
+# include "SDL.h"
+# include "SDL_loadso.h"
+# else
+# include <SDL.h>
+# include <SDL_loadso.h>
+# endif
+# 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/code/sys/sys_local.h b/code/sys/sys_local.h
new file mode 100644
index 0000000..ee808d4
--- /dev/null
+++ b/code/sys/sys_local.h
@@ -0,0 +1,56 @@
+/*
+===========================================================================
+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"
+
+// Require a minimum version of SDL
+#define MINSDL_MAJOR 1
+#define MINSDL_MINOR 2
+#define MINSDL_PATCH 10
+
+// Input subsystem
+void IN_Init( void );
+void IN_Frame( void );
+void IN_Shutdown( void );
+void IN_Restart( void );
+
+// Console
+void CON_Shutdown( void );
+void CON_Init( void );
+char *CON_Input( void );
+void CON_Print( const char *message );
+
+unsigned int CON_LogSize( void );
+unsigned int CON_LogWrite( const char *in );
+unsigned int CON_LogRead( char *out, unsigned int outSize );
+
+#ifdef MACOS_X
+char *Sys_StripAppBundle( char *pwd );
+#endif
+
+void Sys_GLimpSafeInit( void );
+void Sys_GLimpInit( void );
+void Sys_PlatformInit( void );
+void Sys_SigHandler( int signal );
+void Sys_ErrorDialog( const char *error );
+void Sys_AnsiColorPrint( const char *msg );
diff --git a/code/sys/sys_main.c b/code/sys/sys_main.c
new file mode 100644
index 0000000..8f97ceb
--- /dev/null
+++ b/code/sys/sys_main.c
@@ -0,0 +1,570 @@
+/*
+===========================================================================
+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 <signal.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+
+#ifndef DEDICATED
+#ifdef USE_LOCAL_HEADERS
+# include "SDL.h"
+# include "SDL_cpuinfo.h"
+#else
+# include <SDL.h>
+# include <SDL_cpuinfo.h>
+#endif
+#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_DefaultAppPath
+=================
+*/
+char *Sys_DefaultAppPath(void)
+{
+ return Sys_BinaryPath();
+}
+
+/*
+=================
+Sys_In_Restart_f
+
+Restart the input subsystem
+=================
+*/
+void Sys_In_Restart_f( void )
+{
+ IN_Restart( );
+}
+
+/*
+=================
+Sys_ConsoleInput
+
+Handle new console input
+=================
+*/
+char *Sys_ConsoleInput(void)
+{
+ return CON_Input( );
+}
+
+/*
+=================
+Sys_Exit
+
+Single exit point (regular exit or in case of error)
+=================
+*/
+void Sys_Exit( int ex )
+{
+ CON_Shutdown( );
+
+#ifndef DEDICATED
+ SDL_Quit( );
+#endif
+
+#ifdef NDEBUG
+ 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( ) );
+}
+
+/*
+=================
+Sys_AnsiColorPrint
+
+Transform Q3 colour codes to ANSI escape sequences
+=================
+*/
+void Sys_AnsiColorPrint( const char *msg )
+{
+ static char buffer[ MAXPRINTMSG ];
+ int length = 0;
+ static int q3ToAnsi[ 8 ] =
+ {
+ 30, // COLOR_BLACK
+ 31, // COLOR_RED
+ 32, // COLOR_GREEN
+ 33, // COLOR_YELLOW
+ 34, // COLOR_BLUE
+ 36, // COLOR_CYAN
+ 35, // COLOR_MAGENTA
+ 0 // COLOR_WHITE
+ };
+
+ while( *msg )
+ {
+ if( Q_IsColorString( msg ) || *msg == '\n' )
+ {
+ // First empty the buffer
+ if( length > 0 )
+ {
+ buffer[ length ] = '\0';
+ fputs( buffer, stderr );
+ length = 0;
+ }
+
+ if( *msg == '\n' )
+ {
+ // Issue a reset and then the newline
+ fputs( "\033[0m\n", stderr );
+ msg++;
+ }
+ else
+ {
+ // Print the color code
+ Com_sprintf( buffer, sizeof( buffer ), "\033[%dm",
+ q3ToAnsi[ ColorIndex( *( msg + 1 ) ) ] );
+ fputs( buffer, stderr );
+ msg += 2;
+ }
+ }
+ else
+ {
+ if( length >= MAXPRINTMSG - 1 )
+ break;
+
+ buffer[ length ] = *msg;
+ length++;
+ msg++;
+ }
+ }
+
+ // Empty anything still left in the buffer
+ if( length > 0 )
+ {
+ buffer[ length ] = '\0';
+ fputs( buffer, stderr );
+ }
+}
+
+/*
+=================
+Sys_Print
+=================
+*/
+void Sys_Print( const char *msg )
+{
+ CON_LogWrite( msg );
+ CON_Print( msg );
+}
+
+/*
+=================
+Sys_Error
+=================
+*/
+void Sys_Error( const char *error, ... )
+{
+ va_list argptr;
+ char string[1024];
+
+ CL_Shutdown ();
+
+ va_start (argptr,error);
+ Q_vsnprintf (string, sizeof(string), error, argptr);
+ va_end (argptr);
+
+ Sys_ErrorDialog( 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);
+
+ CON_Print( va( "Warning: %s", string ) );
+}
+
+/*
+============
+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 in fs_homepath
+#2 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 fname[MAX_OSPATH];
+ char *basepath;
+ char *homepath;
+ char *gamedir;
+
+ assert( name );
+
+ Q_snprintf (fname, sizeof(fname), "%s" ARCH_STRING DLL_EXT, name);
+
+ // TODO: use fs_searchpaths from files.c
+ basepath = Cvar_VariableString( "fs_basepath" );
+ homepath = Cvar_VariableString( "fs_homepath" );
+ gamedir = Cvar_VariableString( "fs_game" );
+
+ 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_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
+=================
+*/
+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 };
+
+#ifndef DEDICATED
+ // SDL version check
+
+ // Compile time
+# if !SDL_VERSION_ATLEAST(MINSDL_MAJOR,MINSDL_MINOR,MINSDL_PATCH)
+# error A more recent version of SDL is required
+# endif
+
+ // Run time
+ const SDL_version *ver = SDL_Linked_Version( );
+
+#define MINSDL_VERSION \
+ XSTRING(MINSDL_MAJOR) "." \
+ XSTRING(MINSDL_MINOR) "." \
+ XSTRING(MINSDL_PATCH)
+
+ if( SDL_VERSIONNUM( ver->major, ver->minor, ver->patch ) <
+ SDL_VERSIONNUM( MINSDL_MAJOR, MINSDL_MINOR, MINSDL_PATCH ) )
+ {
+ Sys_Print( "SDL version " MINSDL_VERSION " or greater required\n" );
+ Sys_Exit( 1 );
+ }
+#endif
+
+ Sys_PlatformInit( );
+
+ // Set the initial time base
+ Sys_Milliseconds( );
+
+ 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++ )
+ {
+ const qboolean containsSpaces = strchr(argv[i], ' ') != NULL;
+ if (containsSpaces)
+ Q_strcat( commandLine, sizeof( commandLine ), "\"" );
+
+ Q_strcat( commandLine, sizeof( commandLine ), argv[ i ] );
+
+ if (containsSpaces)
+ Q_strcat( commandLine, sizeof( commandLine ), "\"" );
+
+ Q_strcat( commandLine, sizeof( commandLine ), " " );
+ }
+
+ Com_Init( commandLine );
+ NET_Init( );
+
+ CON_Init( );
+
+ signal( SIGILL, Sys_SigHandler );
+ signal( SIGFPE, Sys_SigHandler );
+ signal( SIGSEGV, Sys_SigHandler );
+ signal( SIGTERM, Sys_SigHandler );
+ signal( SIGINT, Sys_SigHandler );
+
+ while( 1 )
+ {
+ IN_Frame( );
+ Com_Frame( );
+ }
+
+ return 0;
+}
+
diff --git a/code/sys/sys_unix.c b/code/sys/sys_unix.c
new file mode 100644
index 0000000..c0b4ecb
--- /dev/null
+++ b/code/sys/sys_unix.c
@@ -0,0 +1,613 @@
+/*
+===========================================================================
+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 <signal.h>
+#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 <fcntl.h>
+
+qboolean stdinIsATTY;
+
+// 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
+ }
+ }
+
+ 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
+==================
+*/
+qboolean Sys_Mkdir( const char *path )
+{
+ int result = mkdir( path, 0750 );
+
+ if( result != 0 )
+ return errno == EEXIST;
+
+ return qtrue;
+}
+
+/*
+==================
+Sys_Cwd
+==================
+*/
+char *Sys_Cwd( void )
+{
+ static char cwd[MAX_OSPATH];
+
+ char *result = getcwd( cwd, sizeof( cwd ) - 1 );
+ if( result != cwd )
+ return NULL;
+
+ 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
+
+
+/*
+==================
+Sys_Sleep
+
+Block execution for msec or until input is recieved.
+==================
+*/
+void Sys_Sleep( int msec )
+{
+ if( msec == 0 )
+ return;
+
+ if( stdinIsATTY )
+ {
+ fd_set fdset;
+
+ FD_ZERO(&fdset);
+ FD_SET(STDIN_FILENO, &fdset);
+ if( msec < 0 )
+ {
+ select(STDIN_FILENO + 1, &fdset, NULL, NULL, NULL);
+ }
+ else
+ {
+ struct timeval timeout;
+
+ timeout.tv_sec = msec/1000;
+ timeout.tv_usec = (msec%1000)*1000;
+ select(STDIN_FILENO + 1, &fdset, NULL, NULL, &timeout);
+ }
+ }
+ else
+ {
+ // With nothing to select() on, we can't wait indefinitely
+ if( msec < 0 )
+ msec = 10;
+
+ usleep( msec * 1000 );
+ }
+}
+
+/*
+==============
+Sys_ErrorDialog
+
+Display an error message
+==============
+*/
+void Sys_ErrorDialog( const char *error )
+{
+ char buffer[ 1024 ];
+ unsigned int size;
+ int f = -1;
+ const char *homepath = Cvar_VariableString( "fs_homepath" );
+ const char *gamedir = Cvar_VariableString( "fs_gamedir" );
+ const char *fileName = "crashlog.txt";
+ char *ospath = FS_BuildOSPath( homepath, gamedir, fileName );
+
+ Sys_Print( va( "%s\n", error ) );
+
+#if defined(MACOS_X) && !DEDICATED
+ /* This function has to be in a separate file, compiled as Objective-C. */
+ extern void Cocoa_MsgBox( const char *text );
+ if (!com_dedicated || !com_dedicated->integer)
+ Cocoa_MsgBox(error);
+#endif
+
+ /* make sure the write path for the crashlog exists... */
+ if( FS_CreatePath( ospath ) ) {
+ Com_Printf( "ERROR: couldn't create path '%s' for crash log.\n", ospath );
+ return;
+ }
+
+ /* we might be crashing because we maxed out the Quake MAX_FILE_HANDLES,
+ which will come through here, so we don't want to recurse forever by
+ calling FS_FOpenFileWrite()...use the Unix system APIs instead. */
+ f = open(ospath, O_CREAT | O_TRUNC | O_WRONLY, 0640);
+ if( f == -1 )
+ {
+ Com_Printf( "ERROR: couldn't open %s\n", fileName );
+ return;
+ }
+
+ /* We're crashing, so we don't care much if write() or close() fails. */
+ while( ( size = CON_LogRead( buffer, sizeof( buffer ) ) ) > 0 ) {
+ if (write( f, buffer, size ) != size) {
+ Com_Printf( "ERROR: couldn't fully write to %s\n", fileName );
+ break;
+ }
+ }
+
+ close(f);
+}
+
+/*
+==============
+Sys_GLimpSafeInit
+
+Unix specific "safe" GL implementation initialisation
+==============
+*/
+void Sys_GLimpSafeInit( void )
+{
+ // NOP
+}
+
+/*
+==============
+Sys_GLimpInit
+
+Unix specific GL implementation initialisation
+==============
+*/
+void Sys_GLimpInit( void )
+{
+ // NOP
+}
+
+/*
+==============
+Sys_PlatformInit
+
+Unix specific initialisation
+==============
+*/
+void Sys_PlatformInit( void )
+{
+ const char* term = getenv( "TERM" );
+
+ signal( SIGHUP, Sys_SigHandler );
+ signal( SIGQUIT, Sys_SigHandler );
+ signal( SIGTRAP, Sys_SigHandler );
+ signal( SIGIOT, Sys_SigHandler );
+ signal( SIGBUS, Sys_SigHandler );
+
+ stdinIsATTY = isatty( STDIN_FILENO ) &&
+ !( term && ( !strcmp( term, "raw" ) || !strcmp( term, "dumb" ) ) );
+}
+
+/*
+==============
+Sys_SetEnv
+
+set/unset environment variables (empty value removes it)
+==============
+*/
+
+void Sys_SetEnv(const char *name, const char *value)
+{
+ if(value && *value)
+ setenv(name, value, 1);
+ else
+ unsetenv(name);
+}
diff --git a/code/sys/sys_win32.c b/code/sys/sys_win32.c
new file mode 100644
index 0000000..c428533
--- /dev/null
+++ b/code/sys/sys_win32.c
@@ -0,0 +1,665 @@
+/*
+===========================================================================
+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);
+ }
+
+ 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
+==============
+*/
+qboolean Sys_Mkdir( const char *path )
+{
+ if( !CreateDirectory( path, NULL ) )
+ {
+ if( GetLastError( ) != ERROR_ALREADY_EXISTS )
+ return qfalse;
+ }
+
+ return qtrue;
+}
+
+/*
+==============
+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 );
+}
+
+
+/*
+==============
+Sys_Sleep
+
+Block execution for msec or until input is received.
+==============
+*/
+void Sys_Sleep( int msec )
+{
+ if( msec == 0 )
+ return;
+
+#ifdef DEDICATED
+ if( msec < 0 )
+ WaitForSingleObject( GetStdHandle( STD_INPUT_HANDLE ), INFINITE );
+ else
+ WaitForSingleObject( GetStdHandle( STD_INPUT_HANDLE ), msec );
+#else
+ // Client Sys_Sleep doesn't support waiting on stdin
+ if( msec < 0 )
+ return;
+
+ Sleep( msec );
+#endif
+}
+
+/*
+==============
+Sys_ErrorDialog
+
+Display an error message
+==============
+*/
+void Sys_ErrorDialog( const char *error )
+{
+ if( MessageBox( NULL, va( "%s. Copy console log to clipboard?", error ),
+ NULL, MB_YESNO|MB_ICONERROR ) == IDYES )
+ {
+ HGLOBAL memoryHandle;
+ char *clipMemory;
+
+ memoryHandle = GlobalAlloc( GMEM_MOVEABLE|GMEM_DDESHARE, CON_LogSize( ) + 1 );
+ clipMemory = (char *)GlobalLock( memoryHandle );
+
+ if( clipMemory )
+ {
+ char *p = clipMemory;
+ char buffer[ 1024 ];
+ unsigned int size;
+
+ while( ( size = CON_LogRead( buffer, sizeof( buffer ) ) ) > 0 )
+ {
+ Com_Memcpy( p, buffer, size );
+ p += size;
+ }
+
+ *p = '\0';
+
+ if( OpenClipboard( NULL ) && EmptyClipboard( ) )
+ SetClipboardData( CF_TEXT, memoryHandle );
+
+ GlobalUnlock( clipMemory );
+ CloseClipboard( );
+ }
+ }
+}
+
+#ifndef DEDICATED
+static qboolean SDL_VIDEODRIVER_externallySet = qfalse;
+#endif
+
+/*
+==============
+Sys_GLimpSafeInit
+
+Windows specific "safe" GL implementation initialisation
+==============
+*/
+void Sys_GLimpSafeInit( void )
+{
+#ifndef DEDICATED
+ if( !SDL_VIDEODRIVER_externallySet )
+ {
+ // Here, we want to let SDL decide what do to unless
+ // explicitly requested otherwise
+ _putenv( "SDL_VIDEODRIVER=" );
+ }
+#endif
+}
+
+/*
+==============
+Sys_GLimpInit
+
+Windows specific GL implementation initialisation
+==============
+*/
+void Sys_GLimpInit( void )
+{
+#ifndef DEDICATED
+ if( !SDL_VIDEODRIVER_externallySet )
+ {
+ // It's a little bit weird having in_mouse control the
+ // video driver, but from ioq3's point of view they're
+ // virtually the same except for the mouse input anyway
+ if( Cvar_VariableIntegerValue( "in_mouse" ) == -1 )
+ {
+ // Use the windib SDL backend, which is closest to
+ // the behaviour of idq3 with in_mouse set to -1
+ _putenv( "SDL_VIDEODRIVER=windib" );
+ }
+ else
+ {
+ // Use the DirectX SDL backend
+ _putenv( "SDL_VIDEODRIVER=directx" );
+ }
+ }
+#endif
+}
+
+/*
+==============
+Sys_PlatformInit
+
+Windows specific initialisation
+==============
+*/
+void Sys_PlatformInit( void )
+{
+#ifndef DEDICATED
+ const char *SDL_VIDEODRIVER = getenv( "SDL_VIDEODRIVER" );
+
+ if( SDL_VIDEODRIVER )
+ {
+ Com_Printf( "SDL_VIDEODRIVER is externally set to \"%s\", "
+ "in_mouse -1 will have no effect\n", SDL_VIDEODRIVER );
+ SDL_VIDEODRIVER_externallySet = qtrue;
+ }
+ else
+ SDL_VIDEODRIVER_externallySet = qfalse;
+#endif
+}
+
+/*
+==============
+Sys_SetEnv
+
+set/unset environment variables (empty value removes it)
+==============
+*/
+
+void Sys_SetEnv(const char *name, const char *value)
+{
+ _putenv(va("%s=%s", name, value));
+}
diff --git a/code/sys/win_resource.h b/code/sys/win_resource.h
new file mode 100644
index 0000000..93c517e
--- /dev/null
+++ b/code/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/code/sys/win_resource.rc b/code/sys/win_resource.rc
new file mode 100644
index 0000000..b1c39d5
--- /dev/null
+++ b/code/sys/win_resource.rc
@@ -0,0 +1,76 @@
+//Microsoft Developer Studio generated resource script.
+//
+#include "win_resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include <winresrc.h>
+
+/////////////////////////////////////////////////////////////////////////////
+#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.
+#ifndef __MINGW32__
+IDI_ICON1 ICON DISCARDABLE "../quake3.ico"
+#else
+IDI_ICON1 ICON DISCARDABLE "misc/quake3.ico"
+#endif
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// String Table
+//
+
+STRINGTABLE DISCARDABLE
+BEGIN
+ IDS_STRING1 "Quake3"
+END
+
+#endif // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+