[dhewm3/el7] Add ExcludeArch for ppc due to missing ppc_intrinsics.h
by Simone Caronni
commit d9d9fd820a8a9c8f5d64489d443119257ff94da8
Author: leigh123linux <leigh123linux(a)googlemail.com>
Date: Sat Mar 25 13:19:24 2017 +0000
Add ExcludeArch for ppc due to missing ppc_intrinsics.h
dhewm3.spec | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
---
diff --git a/dhewm3.spec b/dhewm3.spec
index aace5a8..9de888e 100644
--- a/dhewm3.spec
+++ b/dhewm3.spec
@@ -3,7 +3,7 @@
Name: dhewm3
Version: 1.4.1rc1
-Release: 2.%{?shortcommit0}%{?dist}
+Release: 3.%{?shortcommit0}%{?dist}
Summary: Dhewm's Doom 3 engine
License: GPLv3+ with exceptions
URL: https://github.com/dhewm/%{name}
@@ -16,6 +16,8 @@ Patch0: %{name}-no-cdkey.patch
Patch1: %{name}-def-fixedtic.patch
Patch2: %{name}-carmack.patch
+ExcludeArch: ppc64 ppc64le
+
# Generic provider for Doom 3 engine based games
Provides: doom3-engine = 1.3.1.1304
@@ -75,6 +77,9 @@ fi
%{_libdir}/%{name}
%changelog
+* Sat Mar 25 2017 Leigh Scott <leigh123linux(a)googlemail.com> - 1.4.1rc1-3.89f227b
+- Add ExcludeArch for ppc due to missing ppc_intrinsics.h
+
* Sun Mar 19 2017 RPM Fusion Release Engineering <kwizart(a)rpmfusion.org> - 1.4.1rc1-2.89f227b
- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild
7 years, 7 months
[dhewm3/el7] - Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild
by Simone Caronni
commit c72612179588c09cf2a6e870905a2850ed77ac1d
Author: Nicolas Chauvet <kwizart(a)gmail.com>
Date: Sun Mar 19 09:51:40 2017 +0100
- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild
dhewm3.spec | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
---
diff --git a/dhewm3.spec b/dhewm3.spec
index 6136661..aace5a8 100644
--- a/dhewm3.spec
+++ b/dhewm3.spec
@@ -3,7 +3,7 @@
Name: dhewm3
Version: 1.4.1rc1
-Release: 1.%{?shortcommit0}%{?dist}
+Release: 2.%{?shortcommit0}%{?dist}
Summary: Dhewm's Doom 3 engine
License: GPLv3+ with exceptions
URL: https://github.com/dhewm/%{name}
@@ -75,6 +75,9 @@ fi
%{_libdir}/%{name}
%changelog
+* Sun Mar 19 2017 RPM Fusion Release Engineering <kwizart(a)rpmfusion.org> - 1.4.1rc1-2.89f227b
+- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild
+
* Sat Jan 23 2016 Simone Caronni <negativo17(a)gmail.com> - 1.4.1rc1-1.89f227b
- Update to latest 1.4.1rc1.
- Drop RHEL 6 support, provided libjpeg is too old and would need to have a
7 years, 7 months
[dhewm3/el7] Update to latest 1.4.1rc1
by Simone Caronni
commit 11b1fcea55e2b69507199a3f7e65be2567d7b900
Author: Simone Caronni <negativo17(a)gmail.com>
Date: Wed Feb 24 19:18:31 2016 +0100
Update to latest 1.4.1rc1
.gitignore | 1 +
dhewm3-README.txt | 35 +-
dhewm3-carmack.patch | 26 +-
dhewm3-def-eax-on.patch | 12 -
dhewm3-no-cdkey.patch | 14225 ++--------------------------------------------
dhewm3.spec | 45 +-
sources | 2 +-
7 files changed, 652 insertions(+), 13694 deletions(-)
---
diff --git a/.gitignore b/.gitignore
index 14710bc..79fcf54 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,2 @@
dhewm3-1.3.1.1304-657ad99.tar.gz
+dhewm3-89f227b.tar.gz
diff --git a/dhewm3-README.txt b/dhewm3-README.txt
index b0f5497..7ee0fc9 100644
--- a/dhewm3-README.txt
+++ b/dhewm3-README.txt
@@ -1,15 +1,15 @@
-____ ____ ____ _____ ____ _____ ____
-| _ \| __ )| _ \ ___ ___ _ __ ___ |___ /| __ )| ___/ ___|
-| |_) | _ \| | | |/ _ \ / _ \| '_ ` _ \ |_ \| _ \| |_ | | _
-| _ <| |_) | |_| | (_) | (_) | | | | | |___) | |_) | _|| |_| |
-|_| \_\____/|____/ \___/ \___/|_| |_| |_|____/|____/|_| \____|
-
-** Doom 3 BFG Edition GPL source modification
+ _ _ _____
+ __| | |__ _____ ___ __ ___ |___ /
+ / _` | '_ \ / _ \ \ /\ / / '_ ` _ \ |_ \
+| (_| | | | | __/\ V V /| | | | | |___) |
+ \__,_|_| |_|\___| \_/\_/ |_| |_| |_|____/
+
+** Doom 3 GPL source modification
For the additional features and fixes introduced by this great modification,
please see the original page at:
- https://github.com/RobertBeckebans/RBDOOM-3-BFG
+ https://github.com/dhewm/dhewm3
======== Game data
@@ -19,7 +19,7 @@ yourself buying the game.
To launch the engine pointing at your data files you can use the following
command:
- doom3bfg-engine +set fs_basepath "/path/to/game/content" "$@"
+ doom3-engine +set fs_basepath "/path/to/game/content" "$@"
A sample nosrc rpm that can be used to build a binary rpm using the DVD data
files is at: http://slaanesh.fedorapeople.org/
@@ -29,12 +29,12 @@ To rebuild it, add the required files to your SOURCES directory and remove the
A high resolution icon has been generated from the game assets, to have a look
at how the resulting menu entries look like see:
-http://slaanesh.fedorapeople.org/doom3bfg-menus.png
+http://slaanesh.fedorapeople.org/doom3-menus.png
You should end up with something like this:
$ rpm -qa dhewm3* doom3*
- dhewm3-1.3.1.1304-19.gitfa231898.fc19.x86_64
+ dhewm3-1.4.1rc1-1.git.89f227b.fc23.x86_64
doom3-1.3.1.1304-4.fc18.noarch
doom3-roe-1.3.1.1304-4.fc18.noarch
@@ -46,15 +46,6 @@ You should end up with something like this:
Ogg Vorbis
SDL
zlib
-- Uses SDL2 on Fedora 19+ and RHEL/CentOS 7+.
-- Provides a doom3bfg-engine symlink through the alternatives system to easily
+- Uses SDL2 on Fedora and RHEL/CentOS 7+.
+- Provides a doom3-engine symlink through the alternatives system to easily
switch between additional variations.
-- Removes the bundled libraries and tools required for compiling Doom Classic
- (not working in the current source code).
-
-======== Notes
-
-- The Doom Classic icons in the main menu do not work; they are not selectable.
- This is by design.
-- Bink Videos do not work, the code is patented and not present in the current
- source code.
diff --git a/dhewm3-carmack.patch b/dhewm3-carmack.patch
index fa109b2..4914b06 100644
--- a/dhewm3-carmack.patch
+++ b/dhewm3-carmack.patch
@@ -1,16 +1,15 @@
-diff --git a/neo/CMakeLists.txt b/neo/CMakeLists.txt
-index 57d27d4..7ed0ab0 100644
---- a/neo/CMakeLists.txt
-+++ b/neo/CMakeLists.txt
-@@ -30,6 +30,7 @@ option(D3XP "Build the d3xp game code" ON)
+diff -Naur dhewm3-89f227b365c2086dbe8818d82324f074a8ab4792.old/neo/CMakeLists.txt dhewm3-89f227b365c2086dbe8818d82324f074a8ab4792/neo/CMakeLists.txt
+--- dhewm3-89f227b365c2086dbe8818d82324f074a8ab4792.old/neo/CMakeLists.txt 2016-01-23 10:16:16.845525612 +0100
++++ dhewm3-89f227b365c2086dbe8818d82324f074a8ab4792/neo/CMakeLists.txt 2016-01-23 10:23:06.175713253 +0100
+@@ -39,6 +39,7 @@
option(DEDICATED "Build the dedicated server" OFF)
option(ONATIVE "Optimize for the host CPU" OFF)
- option(SDL2 "Use SDL2 instead of SDL1.2" OFF)
-+option(ZFAIL "Use Carmack's reverse" OFF)
+ option(SDL2 "Use SDL2 instead of SDL1.2" ON)
++option(ZFAIL "Use Carmack's reverse" OFF)
if(NOT CMAKE_SYSTEM_PROCESSOR)
message(FATAL_ERROR "No target CPU architecture set")
-@@ -738,6 +739,9 @@ if(CORE)
+@@ -752,6 +753,9 @@
${src_sys_base}
${src_sys_core}
)
@@ -20,11 +19,10 @@ index 57d27d4..7ed0ab0 100644
set_target_properties(dhewm3 PROPERTIES COMPILE_DEFINITIONS "__DOOM_DLL__")
set_target_properties(dhewm3 PROPERTIES LINK_FLAGS "${ldflags}")
-diff --git a/neo/renderer/draw_common.cpp b/neo/renderer/draw_common.cpp
-index 70bcc1d..ce48ea1 100644
---- a/neo/renderer/draw_common.cpp
-+++ b/neo/renderer/draw_common.cpp
-@@ -1140,6 +1140,7 @@ static void RB_T_Shadow( const drawSurf_t *surf ) {
+diff -Naur dhewm3-89f227b365c2086dbe8818d82324f074a8ab4792.old/neo/renderer/draw_common.cpp dhewm3-89f227b365c2086dbe8818d82324f074a8ab4792/neo/renderer/draw_common.cpp
+--- dhewm3-89f227b365c2086dbe8818d82324f074a8ab4792.old/neo/renderer/draw_common.cpp 2016-01-23 10:16:17.172529795 +0100
++++ dhewm3-89f227b365c2086dbe8818d82324f074a8ab4792/neo/renderer/draw_common.cpp 2016-01-23 10:21:43.729669761 +0100
+@@ -1140,6 +1140,7 @@
return;
}
@@ -32,7 +30,7 @@ index 70bcc1d..ce48ea1 100644
// patent-free work around
if ( !external ) {
// "preload" the stencil buffer with the number of volumes
-@@ -1151,6 +1152,17 @@ static void RB_T_Shadow( const drawSurf_t *surf ) {
+@@ -1151,6 +1152,17 @@
GL_Cull( CT_BACK_SIDED );
RB_DrawShadowElementsWithCounters( tri, numIndexes );
}
diff --git a/dhewm3-no-cdkey.patch b/dhewm3-no-cdkey.patch
index 8f87484..20c2e8f 100644
--- a/dhewm3-no-cdkey.patch
+++ b/dhewm3-no-cdkey.patch
@@ -1,6 +1,6 @@
-diff -Naur dhewm3.old/neo/framework/async/AsyncClient.cpp dhewm3/neo/framework/async/AsyncClient.cpp
---- dhewm3.old/neo/framework/async/AsyncClient.cpp 2013-08-15 23:27:20.000000000 +0200
-+++ dhewm3/neo/framework/async/AsyncClient.cpp 2013-08-22 13:23:32.995219604 +0200
+diff -Naur dhewm3-89f227b365c2086dbe8818d82324f074a8ab4792.old/neo/framework/async/AsyncClient.cpp dhewm3-89f227b365c2086dbe8818d82324f074a8ab4792/neo/framework/async/AsyncClient.cpp
+--- dhewm3-89f227b365c2086dbe8818d82324f074a8ab4792.old/neo/framework/async/AsyncClient.cpp 2016-01-23 10:16:16.945526891 +0100
++++ dhewm3-89f227b365c2086dbe8818d82324f074a8ab4792/neo/framework/async/AsyncClient.cpp 2016-01-23 10:16:31.525713410 +0100
@@ -1262,93 +1262,6 @@
/*
@@ -151,13621 +151,598 @@ diff -Naur dhewm3.old/neo/framework/async/AsyncClient.cpp dhewm3/neo/framework/a
===============
idAsyncClient::CheckTimeout
===============
-diff -Naur dhewm3.old/neo/framework/async/AsyncClient.cpp.orig dhewm3/neo/framework/async/AsyncClient.cpp.orig
---- dhewm3.old/neo/framework/async/AsyncClient.cpp.orig 1970-01-01 01:00:00.000000000 +0100
-+++ dhewm3/neo/framework/async/AsyncClient.cpp.orig 2013-08-22 13:23:32.996219594 +0200
-@@ -0,0 +1,2317 @@
-+/*
-+===========================================================================
-+
-+Doom 3 GPL Source Code
-+Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
-+
-+This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
-+
-+Doom 3 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 3 of the License, or
-+(at your option) any later version.
-+
-+Doom 3 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 Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
-+
-+In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
-+
-+If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
-+
-+===========================================================================
-+*/
-+
-+#include "sys/platform.h"
-+#include "idlib/LangDict.h"
-+#include "framework/async/AsyncNetwork.h"
-+#include "framework/Licensee.h"
-+#include "framework/Game.h"
-+#include "framework/Session_local.h"
-+#include "sound/sound.h"
-+
-+#include "framework/async/AsyncClient.h"
-+
-+const int SETUP_CONNECTION_RESEND_TIME = 1000;
-+const int EMPTY_RESEND_TIME = 500;
-+const int PREDICTION_FAST_ADJUST = 4;
-+
-+/*
-+==================
-+idAsyncClient::idAsyncClient
-+==================
-+*/
-+idAsyncClient::idAsyncClient( void ) {
-+ guiNetMenu = NULL;
-+ updateState = UPDATE_NONE;
-+ Clear();
-+}
-+
-+/*
-+==================
-+idAsyncClient::Clear
-+==================
-+*/
-+void idAsyncClient::Clear( void ) {
-+ active = false;
-+ realTime = 0;
-+ clientTime = 0;
-+ clientId = 0;
-+ clientDataChecksum = 0;
-+ clientNum = 0;
-+ clientState = CS_DISCONNECTED;
-+ clientPrediction = 0;
-+ clientPredictTime = 0;
-+ serverId = 0;
-+ serverChallenge = 0;
-+ serverMessageSequence = 0;
-+ lastConnectTime = -9999;
-+ lastEmptyTime = -9999;
-+ lastPacketTime = -9999;
-+ lastSnapshotTime = -9999;
-+ snapshotGameFrame = 0;
-+ snapshotGameTime = 0;
-+ snapshotSequence = 0;
-+ gameInitId = GAME_INIT_ID_INVALID;
-+ gameFrame = 0;
-+ gameTimeResidual = 0;
-+ gameTime = 0;
-+ memset( userCmds, 0, sizeof( userCmds ) );
-+ backgroundDownload.completed = true;
-+ lastRconTime = 0;
-+ showUpdateMessage = false;
-+ lastFrameDelta = 0;
-+
-+ dlRequest = -1;
-+ dlCount = -1;
-+ memset( dlChecksums, 0, sizeof( int ) * MAX_PURE_PAKS );
-+ currentDlSize = 0;
-+ totalDlSize = 0;
-+}
-+
-+/*
-+==================
-+idAsyncClient::Shutdown
-+==================
-+*/
-+void idAsyncClient::Shutdown( void ) {
-+ guiNetMenu = NULL;
-+ updateMSG.Clear();
-+ updateURL.Clear();
-+ updateFile.Clear();
-+ updateFallback.Clear();
-+ backgroundDownload.url.url.Clear();
-+ dlList.Clear();
-+}
-+
-+/*
-+==================
-+idAsyncClient::InitPort
-+==================
-+*/
-+bool idAsyncClient::InitPort( void ) {
-+ // if this is the first time we connect to a server, open the UDP port
-+ if ( !clientPort.GetPort() ) {
-+ if ( !clientPort.InitForPort( PORT_ANY ) ) {
-+ common->Printf( "Couldn't open client network port.\n" );
-+ return false;
-+ }
-+ }
-+ // maintain it valid between connects and ui manager reloads
-+ guiNetMenu = uiManager->FindGui( "guis/netmenu.gui", true, false, true );
-+
-+ return true;
-+}
-+
-+/*
-+==================
-+idAsyncClient::ClosePort
-+==================
-+*/
-+void idAsyncClient::ClosePort( void ) {
-+ clientPort.Close();
-+}
-+
-+/*
-+==================
-+idAsyncClient::ClearPendingPackets
-+==================
-+*/
-+void idAsyncClient::ClearPendingPackets( void ) {
-+ int size;
-+ byte msgBuf[MAX_MESSAGE_SIZE];
-+ netadr_t from;
-+
-+ while( clientPort.GetPacket( from, msgBuf, size, sizeof( msgBuf ) ) ) {
-+ }
-+}
-+
-+/*
-+==================
-+idAsyncClient::HandleGuiCommandInternal
-+==================
-+*/
-+const char* idAsyncClient::HandleGuiCommandInternal( const char *cmd ) {
-+ if ( !idStr::Cmp( cmd, "abort" ) || !idStr::Cmp( cmd, "pure_abort" ) ) {
-+ common->DPrintf( "connection aborted\n" );
-+ cmdSystem->BufferCommandText( CMD_EXEC_NOW, "disconnect" );
-+ return "";
-+ } else {
-+ common->DWarning( "idAsyncClient::HandleGuiCommand: unknown cmd %s", cmd );
-+ }
-+ return NULL;
-+}
-+
-+/*
-+==================
-+idAsyncClient::HandleGuiCommand
-+==================
-+*/
-+const char* idAsyncClient::HandleGuiCommand( const char *cmd ) {
-+ return idAsyncNetwork::client.HandleGuiCommandInternal( cmd );
-+}
-+
-+/*
-+==================
-+idAsyncClient::ConnectToServer
-+==================
-+*/
-+void idAsyncClient::ConnectToServer( const netadr_t adr ) {
-+ // shutdown any current game. that includes network disconnect
-+ session->Stop();
-+
-+ if ( !InitPort() ) {
-+ return;
-+ }
-+
-+ if ( cvarSystem->GetCVarBool( "net_serverDedicated" ) ) {
-+ common->Printf( "Can't connect to a server as dedicated\n" );
-+ return;
-+ }
-+
-+ // trash any currently pending packets
-+ ClearPendingPackets();
-+
-+ serverAddress = adr;
-+
-+ // clear the client state
-+ Clear();
-+
-+ // get a pseudo random client id, but don't use the id which is reserved for connectionless packets
-+ clientId = Sys_Milliseconds() & CONNECTIONLESS_MESSAGE_ID_MASK;
-+
-+ // calculate a checksum on some of the essential data used
-+ clientDataChecksum = declManager->GetChecksum();
-+
-+ // start challenging the server
-+ clientState = CS_CHALLENGING;
-+
-+ active = true;
-+
-+ guiNetMenu = uiManager->FindGui( "guis/netmenu.gui", true, false, true );
-+ guiNetMenu->SetStateString( "status", va( common->GetLanguageDict()->GetString( "#str_06749" ), Sys_NetAdrToString( adr ) ) );
-+ session->SetGUI( guiNetMenu, HandleGuiCommand );
-+}
-+
-+/*
-+==================
-+idAsyncClient::Reconnect
-+==================
-+*/
-+void idAsyncClient::Reconnect( void ) {
-+ ConnectToServer( serverAddress );
-+}
-+
-+/*
-+==================
-+idAsyncClient::ConnectToServer
-+==================
-+*/
-+void idAsyncClient::ConnectToServer( const char *address ) {
-+ int serverNum;
-+ netadr_t adr;
-+
-+ if ( idStr::IsNumeric( address ) ) {
-+ serverNum = atoi( address );
-+ if ( serverNum < 0 || serverNum >= serverList.Num() ) {
-+ session->MessageBox( MSG_OK, va( common->GetLanguageDict()->GetString( "#str_06733" ), serverNum ), common->GetLanguageDict()->GetString( "#str_06735" ), true );
-+ return;
-+ }
-+ adr = serverList[ serverNum ].adr;
-+ } else {
-+ if ( !Sys_StringToNetAdr( address, &adr, true ) ) {
-+ session->MessageBox( MSG_OK, va( common->GetLanguageDict()->GetString( "#str_06734" ), address ), common->GetLanguageDict()->GetString( "#str_06735" ), true );
-+ return;
-+ }
-+ }
-+ if ( !adr.port ) {
-+ adr.port = PORT_SERVER;
-+ }
-+
-+ common->Printf( "\"%s\" resolved to %s\n", address, Sys_NetAdrToString( adr ) );
-+
-+ ConnectToServer( adr );
-+}
-+
-+/*
-+==================
-+idAsyncClient::DisconnectFromServer
-+==================
-+*/
-+void idAsyncClient::DisconnectFromServer( void ) {
-+ idBitMsg msg;
-+ byte msgBuf[MAX_MESSAGE_SIZE];
-+
-+ if ( clientState >= CS_CONNECTED ) {
-+ // if we were actually connected, clear the pure list
-+ fileSystem->ClearPureChecksums();
-+
-+ // send reliable disconnect to server
-+ msg.Init( msgBuf, sizeof( msgBuf ) );
-+ msg.WriteByte( CLIENT_RELIABLE_MESSAGE_DISCONNECT );
-+ msg.WriteString( "disconnect" );
-+
-+ if ( !channel.SendReliableMessage( msg ) ) {
-+ common->Error( "client->server reliable messages overflow\n" );
-+ }
-+
-+ SendEmptyToServer( true );
-+ SendEmptyToServer( true );
-+ SendEmptyToServer( true );
-+ }
-+
-+ if ( clientState != CS_PURERESTART ) {
-+ channel.Shutdown();
-+ clientState = CS_DISCONNECTED;
-+ }
-+
-+ active = false;
-+}
-+
-+/*
-+==================
-+idAsyncClient::GetServerInfo
-+==================
-+*/
-+void idAsyncClient::GetServerInfo( const netadr_t adr ) {
-+ idBitMsg msg;
-+ byte msgBuf[MAX_MESSAGE_SIZE];
-+
-+ if ( !InitPort() ) {
-+ return;
-+ }
-+
-+ msg.Init( msgBuf, sizeof( msgBuf ) );
-+ msg.WriteShort( CONNECTIONLESS_MESSAGE_ID );
-+ msg.WriteString( "getInfo" );
-+ msg.WriteInt( serverList.GetChallenge() ); // challenge
-+
-+ clientPort.SendPacket( adr, msg.GetData(), msg.GetSize() );
-+}
-+
-+/*
-+==================
-+idAsyncClient::GetServerInfo
-+==================
-+*/
-+void idAsyncClient::GetServerInfo( const char *address ) {
-+ netadr_t adr;
-+
-+ if ( address && *address != '\0' ) {
-+ if ( !Sys_StringToNetAdr( address, &adr, true ) ) {
-+ common->Printf( "Couldn't get server address for \"%s\"\n", address );
-+ return;
-+ }
-+ } else if ( active ) {
-+ adr = serverAddress;
-+ } else if ( idAsyncNetwork::server.IsActive() ) {
-+ // used to be a Sys_StringToNetAdr( "localhost", &adr, true ); and send a packet over loopback
-+ // but this breaks with net_ip ( typically, for multi-homed servers )
-+ idAsyncNetwork::server.PrintLocalServerInfo();
-+ return;
-+ } else {
-+ common->Printf( "no server found\n" );
-+ return;
-+ }
-+
-+ if ( !adr.port ) {
-+ adr.port = PORT_SERVER;
-+ }
-+
-+ GetServerInfo( adr );
-+}
-+
-+/*
-+==================
-+idAsyncClient::GetLANServers
-+==================
-+*/
-+void idAsyncClient::GetLANServers( void ) {
-+ int i;
-+ idBitMsg msg;
-+ byte msgBuf[MAX_MESSAGE_SIZE];
-+ netadr_t broadcastAddress;
-+
-+ if ( !InitPort() ) {
-+ return;
-+ }
-+
-+ idAsyncNetwork::LANServer.SetBool( true );
-+
-+ serverList.SetupLANScan();
-+
-+ msg.Init( msgBuf, sizeof( msgBuf ) );
-+ msg.WriteShort( CONNECTIONLESS_MESSAGE_ID );
-+ msg.WriteString( "getInfo" );
-+ msg.WriteInt( serverList.GetChallenge() );
-+
-+ broadcastAddress.type = NA_BROADCAST;
-+ for ( i = 0; i < MAX_SERVER_PORTS; i++ ) {
-+ broadcastAddress.port = PORT_SERVER + i;
-+ clientPort.SendPacket( broadcastAddress, msg.GetData(), msg.GetSize() );
-+ }
-+}
-+
-+/*
-+==================
-+idAsyncClient::GetNETServers
-+==================
-+*/
-+void idAsyncClient::GetNETServers( void ) {
-+ idBitMsg msg;
-+ byte msgBuf[MAX_MESSAGE_SIZE];
-+
-+ idAsyncNetwork::LANServer.SetBool( false );
-+
-+ // NetScan only clears GUI and results, not the stored list
-+ serverList.Clear( );
-+ serverList.NetScan( );
-+ serverList.StartServers( true );
-+
-+ msg.Init( msgBuf, sizeof( msgBuf ) );
-+ msg.WriteShort( CONNECTIONLESS_MESSAGE_ID );
-+ msg.WriteString( "getServers" );
-+ msg.WriteInt( ASYNC_PROTOCOL_VERSION );
-+ msg.WriteString( cvarSystem->GetCVarString( "fs_game" ) );
-+ msg.WriteBits( cvarSystem->GetCVarInteger( "gui_filter_password" ), 2 );
-+ msg.WriteBits( cvarSystem->GetCVarInteger( "gui_filter_players" ), 2 );
-+ msg.WriteBits( cvarSystem->GetCVarInteger( "gui_filter_gameType" ), 2 );
-+
-+ netadr_t adr;
-+ if ( idAsyncNetwork::GetMasterAddress( 0, adr ) ) {
-+ clientPort.SendPacket( adr, msg.GetData(), msg.GetSize() );
-+ }
-+}
-+
-+/*
-+==================
-+idAsyncClient::ListServers
-+==================
-+*/
-+void idAsyncClient::ListServers( void ) {
-+ int i;
-+
-+ for ( i = 0; i < serverList.Num(); i++ ) {
-+ common->Printf( "%3d: %s %dms (%s)\n", i, serverList[i].serverInfo.GetString( "si_name" ), serverList[ i ].ping, Sys_NetAdrToString( serverList[i].adr ) );
-+ }
-+}
-+
-+/*
-+==================
-+idAsyncClient::ClearServers
-+==================
-+*/
-+void idAsyncClient::ClearServers( void ) {
-+ serverList.Clear();
-+}
-+
-+/*
-+==================
-+idAsyncClient::RemoteConsole
-+==================
-+*/
-+void idAsyncClient::RemoteConsole( const char *command ) {
-+ netadr_t adr;
-+ idBitMsg msg;
-+ byte msgBuf[MAX_MESSAGE_SIZE];
-+
-+ if ( !InitPort() ) {
-+ return;
-+ }
-+
-+ if ( active ) {
-+ adr = serverAddress;
-+ } else {
-+ Sys_StringToNetAdr( idAsyncNetwork::clientRemoteConsoleAddress.GetString(), &adr, true );
-+ }
-+
-+ if ( !adr.port ) {
-+ adr.port = PORT_SERVER;
-+ }
-+
-+ lastRconAddress = adr;
-+ lastRconTime = realTime;
-+
-+ msg.Init( msgBuf, sizeof( msgBuf ) );
-+ msg.WriteShort( CONNECTIONLESS_MESSAGE_ID );
-+ msg.WriteString( "rcon" );
-+ msg.WriteString( idAsyncNetwork::clientRemoteConsolePassword.GetString() );
-+ msg.WriteString( command );
-+
-+ clientPort.SendPacket( adr, msg.GetData(), msg.GetSize() );
-+}
-+
-+/*
-+==================
-+idAsyncClient::GetPrediction
-+==================
-+*/
-+int idAsyncClient::GetPrediction( void ) const {
-+ if ( clientState < CS_CONNECTED ) {
-+ return -1;
-+ } else {
-+ return clientPrediction;
-+ }
-+}
-+
-+/*
-+==================
-+idAsyncClient::GetTimeSinceLastPacket
-+==================
-+*/
-+int idAsyncClient::GetTimeSinceLastPacket( void ) const {
-+ if ( clientState < CS_CONNECTED ) {
-+ return -1;
-+ } else {
-+ return clientTime - lastPacketTime;
-+ }
-+}
-+
-+/*
-+==================
-+idAsyncClient::GetOutgoingRate
-+==================
-+*/
-+int idAsyncClient::GetOutgoingRate( void ) const {
-+ if ( clientState < CS_CONNECTED ) {
-+ return -1;
-+ } else {
-+ return channel.GetOutgoingRate();
-+ }
-+}
-+
-+/*
-+==================
-+idAsyncClient::GetIncomingRate
-+==================
-+*/
-+int idAsyncClient::GetIncomingRate( void ) const {
-+ if ( clientState < CS_CONNECTED ) {
-+ return -1;
-+ } else {
-+ return channel.GetIncomingRate();
-+ }
-+}
-+
-+/*
-+==================
-+idAsyncClient::GetOutgoingCompression
-+==================
-+*/
-+float idAsyncClient::GetOutgoingCompression( void ) const {
-+ if ( clientState < CS_CONNECTED ) {
-+ return 0.0f;
-+ } else {
-+ return channel.GetOutgoingCompression();
-+ }
-+}
-+
-+/*
-+==================
-+idAsyncClient::GetIncomingCompression
-+==================
-+*/
-+float idAsyncClient::GetIncomingCompression( void ) const {
-+ if ( clientState < CS_CONNECTED ) {
-+ return 0.0f;
-+ } else {
-+ return channel.GetIncomingCompression();
-+ }
-+}
-+
-+/*
-+==================
-+idAsyncClient::GetIncomingPacketLoss
-+==================
-+*/
-+float idAsyncClient::GetIncomingPacketLoss( void ) const {
-+ if ( clientState < CS_CONNECTED ) {
-+ return 0.0f;
-+ } else {
-+ return channel.GetIncomingPacketLoss();
-+ }
-+}
-+
-+/*
-+==================
-+idAsyncClient::DuplicateUsercmds
-+==================
-+*/
-+void idAsyncClient::DuplicateUsercmds( int frame, int time ) {
-+ int i, previousIndex, currentIndex;
-+
-+ previousIndex = ( frame - 1 ) & ( MAX_USERCMD_BACKUP - 1 );
-+ currentIndex = frame & ( MAX_USERCMD_BACKUP - 1 );
-+
-+ // duplicate previous user commands if no new commands are available for a client
-+ for ( i = 0; i < MAX_ASYNC_CLIENTS; i++ ) {
-+ idAsyncNetwork::DuplicateUsercmd( userCmds[previousIndex][i], userCmds[currentIndex][i], frame, time );
-+ }
-+}
-+
-+/*
-+==================
-+idAsyncClient::SendUserInfoToServer
-+==================
-+*/
-+void idAsyncClient::SendUserInfoToServer( void ) {
-+ idBitMsg msg;
-+ byte msgBuf[MAX_MESSAGE_SIZE];
-+ idDict info;
-+
-+ if ( clientState < CS_CONNECTED ) {
-+ return;
-+ }
-+
-+ info = *cvarSystem->MoveCVarsToDict( CVAR_USERINFO );
-+
-+ // send reliable client info to server
-+ msg.Init( msgBuf, sizeof( msgBuf ) );
-+ msg.WriteByte( CLIENT_RELIABLE_MESSAGE_CLIENTINFO );
-+ msg.WriteDeltaDict( info, &sessLocal.mapSpawnData.userInfo[ clientNum ] );
-+
-+ if ( !channel.SendReliableMessage( msg ) ) {
-+ common->Error( "client->server reliable messages overflow\n" );
-+ }
-+
-+ sessLocal.mapSpawnData.userInfo[clientNum] = info;
-+}
-+
-+/*
-+==================
-+idAsyncClient::SendEmptyToServer
-+==================
-+*/
-+void idAsyncClient::SendEmptyToServer( bool force, bool mapLoad ) {
-+ idBitMsg msg;
-+ byte msgBuf[MAX_MESSAGE_SIZE];
-+
-+ if ( lastEmptyTime > realTime ) {
-+ lastEmptyTime = realTime;
-+ }
-+
-+ if ( !force && ( realTime - lastEmptyTime < EMPTY_RESEND_TIME ) ) {
-+ return;
-+ }
-+
-+ if ( idAsyncNetwork::verbose.GetInteger() ) {
-+ common->Printf( "sending empty to server, gameInitId = %d\n", mapLoad ? GAME_INIT_ID_MAP_LOAD : gameInitId );
-+ }
-+
-+ msg.Init( msgBuf, sizeof( msgBuf ) );
-+ msg.WriteInt( serverMessageSequence );
-+ msg.WriteInt( mapLoad ? GAME_INIT_ID_MAP_LOAD : gameInitId );
-+ msg.WriteInt( snapshotSequence );
-+ msg.WriteByte( CLIENT_UNRELIABLE_MESSAGE_EMPTY );
-+
-+ channel.SendMessage( clientPort, clientTime, msg );
-+
-+ while( channel.UnsentFragmentsLeft() ) {
-+ channel.SendNextFragment( clientPort, clientTime );
-+ }
-+
-+ lastEmptyTime = realTime;
-+}
-+
-+/*
-+==================
-+idAsyncClient::SendPingResponseToServer
-+==================
-+*/
-+void idAsyncClient::SendPingResponseToServer( int time ) {
-+ idBitMsg msg;
-+ byte msgBuf[MAX_MESSAGE_SIZE];
-+
-+ if ( idAsyncNetwork::verbose.GetInteger() == 2 ) {
-+ common->Printf( "sending ping response to server, gameInitId = %d\n", gameInitId );
-+ }
-+
-+ msg.Init( msgBuf, sizeof( msgBuf ) );
-+ msg.WriteInt( serverMessageSequence );
-+ msg.WriteInt( gameInitId );
-+ msg.WriteInt( snapshotSequence );
-+ msg.WriteByte( CLIENT_UNRELIABLE_MESSAGE_PINGRESPONSE );
-+ msg.WriteInt( time );
-+
-+ channel.SendMessage( clientPort, clientTime, msg );
-+ while( channel.UnsentFragmentsLeft() ) {
-+ channel.SendNextFragment( clientPort, clientTime );
-+ }
-+}
-+
-+/*
-+==================
-+idAsyncClient::SendUsercmdsToServer
-+==================
-+*/
-+void idAsyncClient::SendUsercmdsToServer( void ) {
-+ int i, numUsercmds, index;
-+ idBitMsg msg;
-+ byte msgBuf[MAX_MESSAGE_SIZE];
-+ usercmd_t * last;
-+
-+ if ( idAsyncNetwork::verbose.GetInteger() == 2 ) {
-+ common->Printf( "sending usercmd to server: gameInitId = %d, gameFrame = %d, gameTime = %d\n", gameInitId, gameFrame, gameTime );
-+ }
-+
-+ // generate user command for this client
-+ index = gameFrame & ( MAX_USERCMD_BACKUP - 1 );
-+ userCmds[index][clientNum] = usercmdGen->GetDirectUsercmd();
-+ userCmds[index][clientNum].gameFrame = gameFrame;
-+ userCmds[index][clientNum].gameTime = gameTime;
-+
-+ // send the user commands to the server
-+ msg.Init( msgBuf, sizeof( msgBuf ) );
-+ msg.WriteInt( serverMessageSequence );
-+ msg.WriteInt( gameInitId );
-+ msg.WriteInt( snapshotSequence );
-+ msg.WriteByte( CLIENT_UNRELIABLE_MESSAGE_USERCMD );
-+ msg.WriteShort( clientPrediction );
-+
-+ numUsercmds = idMath::ClampInt( 0, 10, idAsyncNetwork::clientUsercmdBackup.GetInteger() ) + 1;
-+
-+ // write the user commands
-+ msg.WriteInt( gameFrame );
-+ msg.WriteByte( numUsercmds );
-+ for ( last = NULL, i = gameFrame - numUsercmds + 1; i <= gameFrame; i++ ) {
-+ index = i & ( MAX_USERCMD_BACKUP - 1 );
-+ idAsyncNetwork::WriteUserCmdDelta( msg, userCmds[index][clientNum], last );
-+ last = &userCmds[index][clientNum];
-+ }
-+
-+ channel.SendMessage( clientPort, clientTime, msg );
-+ while( channel.UnsentFragmentsLeft() ) {
-+ channel.SendNextFragment( clientPort, clientTime );
-+ }
-+}
-+
-+/*
-+==================
-+idAsyncClient::InitGame
-+==================
-+*/
-+void idAsyncClient::InitGame( int serverGameInitId, int serverGameFrame, int serverGameTime, const idDict &serverSI ) {
-+ gameInitId = serverGameInitId;
-+ gameFrame = snapshotGameFrame = serverGameFrame;
-+ gameTime = snapshotGameTime = serverGameTime;
-+ gameTimeResidual = 0;
-+ memset( userCmds, 0, sizeof( userCmds ) );
-+
-+ for ( int i = 0; i < MAX_ASYNC_CLIENTS; i++ ) {
-+ sessLocal.mapSpawnData.userInfo[ i ].Clear();
-+ }
-+
-+ sessLocal.mapSpawnData.serverInfo = serverSI;
-+}
-+
-+/*
-+==================
-+idAsyncClient::ProcessUnreliableServerMessage
-+==================
-+*/
-+void idAsyncClient::ProcessUnreliableServerMessage( const idBitMsg &msg ) {
-+ int i, j, index, id, numDuplicatedUsercmds, aheadOfServer, numUsercmds, delta;
-+ int serverGameInitId, serverGameFrame, serverGameTime;
-+ idDict serverSI;
-+ usercmd_t *last;
-+ bool pureWait;
-+
-+ serverGameInitId = msg.ReadInt();
-+
-+ id = msg.ReadByte();
-+ switch( id ) {
-+ case SERVER_UNRELIABLE_MESSAGE_EMPTY: {
-+ if ( idAsyncNetwork::verbose.GetInteger() ) {
-+ common->Printf( "received empty message from server\n" );
-+ }
-+ break;
-+ }
-+ case SERVER_UNRELIABLE_MESSAGE_PING: {
-+ if ( idAsyncNetwork::verbose.GetInteger() == 2 ) {
-+ common->Printf( "received ping message from server\n" );
-+ }
-+ SendPingResponseToServer( msg.ReadInt() );
-+ break;
-+ }
-+ case SERVER_UNRELIABLE_MESSAGE_GAMEINIT: {
-+ serverGameFrame = msg.ReadInt();
-+ serverGameTime = msg.ReadInt();
-+ msg.ReadDeltaDict( serverSI, NULL );
-+ pureWait = serverSI.GetBool( "si_pure" );
-+
-+ InitGame( serverGameInitId, serverGameFrame, serverGameTime, serverSI );
-+
-+ channel.ResetRate();
-+
-+ if ( idAsyncNetwork::verbose.GetInteger() ) {
-+ common->Printf( "received gameinit, gameInitId = %d, gameFrame = %d, gameTime = %d\n", gameInitId, gameFrame, gameTime );
-+ }
-+
-+ // mute sound
-+ soundSystem->SetMute( true );
-+
-+ // ensure chat icon goes away when the GUI is changed...
-+ //cvarSystem->SetCVarBool( "ui_chat", false );
-+
-+ if ( pureWait ) {
-+ guiNetMenu = uiManager->FindGui( "guis/netmenu.gui", true, false, true );
-+ session->SetGUI( guiNetMenu, HandleGuiCommand );
-+ session->MessageBox( MSG_ABORT, common->GetLanguageDict()->GetString ( "#str_04317" ), common->GetLanguageDict()->GetString ( "#str_04318" ), false, "pure_abort" );
-+ } else {
-+ // load map
-+ session->SetGUI( NULL, NULL );
-+ sessLocal.ExecuteMapChange();
-+ }
-+
-+ break;
-+ }
-+ case SERVER_UNRELIABLE_MESSAGE_SNAPSHOT: {
-+ // if the snapshot is from a different game
-+ if ( serverGameInitId != gameInitId ) {
-+ if ( idAsyncNetwork::verbose.GetInteger() ) {
-+ common->Printf( "ignoring snapshot with != gameInitId\n" );
-+ }
-+ break;
-+ }
-+
-+ snapshotSequence = msg.ReadInt();
-+ snapshotGameFrame = msg.ReadInt();
-+ snapshotGameTime = msg.ReadInt();
-+ numDuplicatedUsercmds = msg.ReadByte();
-+ aheadOfServer = msg.ReadShort();
-+
-+ // read the game snapshot
-+ game->ClientReadSnapshot( clientNum, snapshotSequence, snapshotGameFrame, snapshotGameTime, numDuplicatedUsercmds, aheadOfServer, msg );
-+
-+ // read user commands of other clients from the snapshot
-+ for ( last = NULL, i = msg.ReadByte(); i < MAX_ASYNC_CLIENTS; i = msg.ReadByte() ) {
-+ numUsercmds = msg.ReadByte();
-+ if ( numUsercmds > MAX_USERCMD_RELAY ) {
-+ common->Error( "snapshot %d contains too many user commands for client %d", snapshotSequence, i );
-+ break;
-+ }
-+ for ( j = 0; j < numUsercmds; j++ ) {
-+ index = ( snapshotGameFrame + j ) & ( MAX_USERCMD_BACKUP - 1 );
-+ idAsyncNetwork::ReadUserCmdDelta( msg, userCmds[index][i], last );
-+ userCmds[index][i].gameFrame = snapshotGameFrame + j;
-+ userCmds[index][i].duplicateCount = 0;
-+ last = &userCmds[index][i];
-+ }
-+ // clear all user commands after the ones just read from the snapshot
-+ for ( j = numUsercmds; j < MAX_USERCMD_BACKUP; j++ ) {
-+ index = ( snapshotGameFrame + j ) & ( MAX_USERCMD_BACKUP - 1 );
-+ userCmds[index][i].gameFrame = 0;
-+ userCmds[index][i].gameTime = 0;
-+ }
-+ }
-+
-+ // if this is the first snapshot after a game init was received
-+ if ( clientState == CS_CONNECTED ) {
-+ gameTimeResidual = 0;
-+ clientState = CS_INGAME;
-+ assert( !sessLocal.GetActiveMenu( ) );
-+ if ( idAsyncNetwork::verbose.GetInteger() ) {
-+ common->Printf( "received first snapshot, gameInitId = %d, gameFrame %d gameTime %d\n", gameInitId, snapshotGameFrame, snapshotGameTime );
-+ }
-+ }
-+
-+ // if the snapshot is newer than the clients current game time
-+ if ( gameTime < snapshotGameTime || gameTime > snapshotGameTime + idAsyncNetwork::clientMaxPrediction.GetInteger() ) {
-+ gameFrame = snapshotGameFrame;
-+ gameTime = snapshotGameTime;
-+ gameTimeResidual = idMath::ClampInt( -idAsyncNetwork::clientMaxPrediction.GetInteger(), idAsyncNetwork::clientMaxPrediction.GetInteger(), gameTimeResidual );
-+ clientPredictTime = idMath::ClampInt( -idAsyncNetwork::clientMaxPrediction.GetInteger(), idAsyncNetwork::clientMaxPrediction.GetInteger(), clientPredictTime );
-+ }
-+
-+ // adjust the client prediction time based on the snapshot time
-+ clientPrediction -= ( 1 - ( INTSIGNBITSET( aheadOfServer - idAsyncNetwork::clientPrediction.GetInteger() ) << 1 ) );
-+ clientPrediction = idMath::ClampInt( idAsyncNetwork::clientPrediction.GetInteger(), idAsyncNetwork::clientMaxPrediction.GetInteger(), clientPrediction );
-+ delta = gameTime - ( snapshotGameTime + clientPrediction );
-+ clientPredictTime -= ( delta / PREDICTION_FAST_ADJUST ) + ( 1 - ( INTSIGNBITSET( delta ) << 1 ) );
-+
-+ lastSnapshotTime = clientTime;
-+
-+ if ( idAsyncNetwork::verbose.GetInteger() == 2 ) {
-+ common->Printf( "received snapshot, gameInitId = %d, gameFrame = %d, gameTime = %d\n", gameInitId, gameFrame, gameTime );
-+ }
-+
-+ if ( numDuplicatedUsercmds && ( idAsyncNetwork::verbose.GetInteger() == 2 ) ) {
-+ common->Printf( "server duplicated %d user commands before snapshot %d\n", numDuplicatedUsercmds, snapshotGameFrame );
-+ }
-+ break;
-+ }
-+ default: {
-+ common->Printf( "unknown unreliable server message %d\n", id );
-+ break;
-+ }
-+ }
-+}
-+
-+/*
-+==================
-+idAsyncClient::ProcessReliableMessagePure
-+==================
-+*/
-+void idAsyncClient::ProcessReliableMessagePure( const idBitMsg &msg ) {
-+ idBitMsg outMsg;
-+ byte msgBuf[ MAX_MESSAGE_SIZE ];
-+ int inChecksums[ MAX_PURE_PAKS ];
-+ int i;
-+ int serverGameInitId;
-+
-+ session->SetGUI( NULL, NULL );
-+
-+ serverGameInitId = msg.ReadInt();
-+
-+ if ( serverGameInitId != gameInitId ) {
-+ common->DPrintf( "ignoring pure server checksum from an outdated gameInitId (%d)\n", serverGameInitId );
-+ return;
-+ }
-+
-+ if ( !ValidatePureServerChecksums( serverAddress, msg ) ) {
-+
-+ return;
-+ }
-+
-+ if ( idAsyncNetwork::verbose.GetInteger() ) {
-+ common->Printf( "received new pure server info. ExecuteMapChange and report back\n" );
-+ }
-+
-+ // it is now ok to load the next map with updated pure checksums
-+ sessLocal.ExecuteMapChange( true );
-+
-+ // upon receiving our pure list, the server will send us SCS_INGAME and we'll start getting snapshots
-+ fileSystem->GetPureServerChecksums( inChecksums );
-+ outMsg.Init( msgBuf, sizeof( msgBuf ) );
-+ outMsg.WriteByte( CLIENT_RELIABLE_MESSAGE_PURE );
-+
-+ outMsg.WriteInt( gameInitId );
-+
-+ i = 0;
-+ while ( inChecksums[ i ] ) {
-+ outMsg.WriteInt( inChecksums[ i++ ] );
-+ }
-+ outMsg.WriteInt( 0 );
-+
-+ if ( !channel.SendReliableMessage( outMsg ) ) {
-+ common->Error( "client->server reliable messages overflow\n" );
-+ }
-+}
-+
-+/*
-+===============
-+idAsyncClient::ReadLocalizedServerString
-+===============
-+*/
-+void idAsyncClient::ReadLocalizedServerString( const idBitMsg &msg, char *out, int maxLen ) {
-+ msg.ReadString( out, maxLen );
-+ // look up localized string. if the message is not an #str_ format, we'll just get it back unchanged
-+ idStr::snPrintf( out, maxLen - 1, "%s", common->GetLanguageDict()->GetString( out ) );
-+}
-+
-+/*
-+==================
-+idAsyncClient::ProcessReliableServerMessages
-+==================
-+*/
-+void idAsyncClient::ProcessReliableServerMessages( void ) {
-+ idBitMsg msg;
-+ byte msgBuf[MAX_MESSAGE_SIZE];
-+ byte id;
-+
-+ msg.Init( msgBuf, sizeof( msgBuf ) );
-+
-+ while ( channel.GetReliableMessage( msg ) ) {
-+ id = msg.ReadByte();
-+ switch( id ) {
-+ case SERVER_RELIABLE_MESSAGE_CLIENTINFO: {
-+ int clientNum;
-+ clientNum = msg.ReadByte();
-+
-+ idDict &info = sessLocal.mapSpawnData.userInfo[ clientNum ];
-+ bool haveBase = ( msg.ReadBits( 1 ) != 0 );
-+
-+#if ID_CLIENTINFO_TAGS
-+ int checksum = info.Checksum();
-+ int srv_checksum = msg.ReadInt();
-+ if ( checksum != srv_checksum ) {
-+ common->DPrintf( "SERVER_RELIABLE_MESSAGE_CLIENTINFO %d (haveBase: %s): != checksums srv: 0x%x local: 0x%x\n", clientNum, haveBase ? "true" : "false", checksum, srv_checksum );
-+ info.Print();
-+ } else {
-+ common->DPrintf( "SERVER_RELIABLE_MESSAGE_CLIENTINFO %d (haveBase: %s): checksums ok 0x%x\n", clientNum, haveBase ? "true" : "false", checksum );
-+ }
-+#endif
-+
-+ if ( haveBase ) {
-+ msg.ReadDeltaDict( info, &info );
-+ } else {
-+ msg.ReadDeltaDict( info, NULL );
-+ }
-+
-+ // server forces us to a different userinfo
-+ if ( clientNum == idAsyncClient::clientNum ) {
-+ common->DPrintf( "local user info modified by server\n" );
-+ cvarSystem->SetCVarsFromDict( info );
-+ cvarSystem->ClearModifiedFlags( CVAR_USERINFO ); // don't emit back
-+ }
-+ game->SetUserInfo( clientNum, info, true, false );
-+ break;
-+ }
-+ case SERVER_RELIABLE_MESSAGE_SYNCEDCVARS: {
-+ idDict &info = sessLocal.mapSpawnData.syncedCVars;
-+ msg.ReadDeltaDict( info, &info );
-+ cvarSystem->SetCVarsFromDict( info );
-+ if ( !idAsyncNetwork::allowCheats.GetBool() ) {
-+ cvarSystem->ResetFlaggedVariables( CVAR_CHEAT );
-+ }
-+ break;
-+ }
-+ case SERVER_RELIABLE_MESSAGE_PRINT: {
-+ char string[MAX_STRING_CHARS];
-+ msg.ReadString( string, MAX_STRING_CHARS );
-+ common->Printf( "%s\n", string );
-+ break;
-+ }
-+ case SERVER_RELIABLE_MESSAGE_DISCONNECT: {
-+ int clientNum;
-+ char string[MAX_STRING_CHARS];
-+ clientNum = msg.ReadInt( );
-+ ReadLocalizedServerString( msg, string, MAX_STRING_CHARS );
-+ if ( clientNum == idAsyncClient::clientNum ) {
-+ session->Stop();
-+ session->MessageBox( MSG_OK, string, common->GetLanguageDict()->GetString ( "#str_04319" ), true );
-+ session->StartMenu();
-+ } else {
-+ common->Printf( "client %d %s\n", clientNum, string );
-+ cmdSystem->BufferCommandText( CMD_EXEC_NOW, va( "addChatLine \"%s^0 %s\"", sessLocal.mapSpawnData.userInfo[ clientNum ].GetString( "ui_name" ), string ) );
-+ sessLocal.mapSpawnData.userInfo[ clientNum ].Clear();
-+ }
-+ break;
-+ }
-+ case SERVER_RELIABLE_MESSAGE_APPLYSNAPSHOT: {
-+ int sequence;
-+ sequence = msg.ReadInt();
-+ if ( !game->ClientApplySnapshot( clientNum, sequence ) ) {
-+ session->Stop();
-+ common->Error( "couldn't apply snapshot %d", sequence );
-+ }
-+ break;
-+ }
-+ case SERVER_RELIABLE_MESSAGE_PURE: {
-+ ProcessReliableMessagePure( msg );
-+ break;
-+ }
-+ case SERVER_RELIABLE_MESSAGE_RELOAD: {
-+ if ( idAsyncNetwork::verbose.GetBool() ) {
-+ common->Printf( "got MESSAGE_RELOAD from server\n" );
-+ }
-+ // simply reconnect, so that if the server restarts in pure mode we can get the right list and avoid spurious reloads
-+ cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "reconnect\n" );
-+ break;
-+ }
-+ case SERVER_RELIABLE_MESSAGE_ENTERGAME: {
-+ SendUserInfoToServer();
-+ game->SetUserInfo( clientNum, sessLocal.mapSpawnData.userInfo[ clientNum ], true, false );
-+ cvarSystem->ClearModifiedFlags( CVAR_USERINFO );
-+ break;
-+ }
-+ default: {
-+ // pass reliable message on to game code
-+ game->ClientProcessReliableMessage( clientNum, msg );
-+ break;
-+ }
-+ }
-+ }
-+}
-+
-+/*
-+==================
-+idAsyncClient::ProcessChallengeResponseMessage
-+==================
-+*/
-+void idAsyncClient::ProcessChallengeResponseMessage( const netadr_t from, const idBitMsg &msg ) {
-+ char serverGame[ MAX_STRING_CHARS ], serverGameBase[ MAX_STRING_CHARS ];
-+
-+ if ( clientState != CS_CHALLENGING ) {
-+ common->Printf( "Unwanted challenge response received.\n" );
-+ return;
-+ }
-+
-+ serverChallenge = msg.ReadInt();
-+ serverId = msg.ReadShort();
-+ msg.ReadString( serverGameBase, MAX_STRING_CHARS );
-+ msg.ReadString( serverGame, MAX_STRING_CHARS );
-+
-+ // the server is running a different game... we need to reload in the correct fs_game
-+ // even pure pak checks would fail if we didn't, as there are files we may not even see atm
-+ // NOTE: we could read the pure list from the server at the same time and set it up for the restart
-+ // ( if the client can restart directly with the right pak order, then we avoid an extra reloadEngine later.. )
-+ if ( idStr::Icmp( cvarSystem->GetCVarString( "fs_game_base" ), serverGameBase ) ||
-+ idStr::Icmp( cvarSystem->GetCVarString( "fs_game" ), serverGame ) ) {
-+ // bug #189 - if the server is running ROE and ROE is not locally installed, refuse to connect or we might crash
-+ if ( !fileSystem->HasD3XP() && ( !idStr::Icmp( serverGameBase, "d3xp" ) || !idStr::Icmp( serverGame, "d3xp" ) ) ) {
-+ common->Printf( "The server is running Doom3: Resurrection of Evil expansion pack. RoE is not installed on this client. Aborting the connection..\n" );
-+ cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "disconnect\n" );
-+ return;
-+ }
-+ common->Printf( "The server is running a different mod (%s-%s). Restarting..\n", serverGameBase, serverGame );
-+ cvarSystem->SetCVarString( "fs_game_base", serverGameBase );
-+ cvarSystem->SetCVarString( "fs_game", serverGame );
-+ cmdSystem->BufferCommandText( CMD_EXEC_NOW, "reloadEngine" );
-+ cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "reconnect\n" );
-+ return;
-+ }
-+
-+ common->Printf( "received challenge response 0x%x from %s\n", serverChallenge, Sys_NetAdrToString( from ) );
-+
-+ // start sending connect packets instead of challenge request packets
-+ clientState = CS_CONNECTING;
-+ lastConnectTime = -9999;
-+
-+ // take this address as the new server address. This allows
-+ // a server proxy to hand off connections to multiple servers
-+ serverAddress = from;
-+}
-+
-+/*
-+==================
-+idAsyncClient::ProcessConnectResponseMessage
-+==================
-+*/
-+void idAsyncClient::ProcessConnectResponseMessage( const netadr_t from, const idBitMsg &msg ) {
-+ int serverGameInitId, serverGameFrame, serverGameTime;
-+ idDict serverSI;
-+
-+ if ( clientState >= CS_CONNECTED ) {
-+ common->Printf( "Duplicate connect received.\n" );
-+ return;
-+ }
-+ if ( clientState != CS_CONNECTING ) {
-+ common->Printf( "Connect response packet while not connecting.\n" );
-+ return;
-+ }
-+ if ( !Sys_CompareNetAdrBase( from, serverAddress ) ) {
-+ common->Printf( "Connect response from a different server.\n" );
-+ common->Printf( "%s should have been %s\n", Sys_NetAdrToString( from ), Sys_NetAdrToString( serverAddress ) );
-+ return;
-+ }
-+
-+ common->Printf( "received connect response from %s\n", Sys_NetAdrToString( from ) );
-+
-+ channel.Init( from, clientId );
-+ clientNum = msg.ReadInt();
-+ clientState = CS_CONNECTED;
-+ lastPacketTime = -9999;
-+
-+ serverGameInitId = msg.ReadInt();
-+ serverGameFrame = msg.ReadInt();
-+ serverGameTime = msg.ReadInt();
-+ msg.ReadDeltaDict( serverSI, NULL );
-+
-+ InitGame( serverGameInitId, serverGameFrame, serverGameTime, serverSI );
-+
-+ // load map
-+ session->SetGUI( NULL, NULL );
-+ sessLocal.ExecuteMapChange();
-+
-+ clientPredictTime = clientPrediction = idMath::ClampInt( 0, idAsyncNetwork::clientMaxPrediction.GetInteger(), clientTime - lastConnectTime );
-+}
-+
-+/*
-+==================
-+idAsyncClient::ProcessDisconnectMessage
-+==================
-+*/
-+void idAsyncClient::ProcessDisconnectMessage( const netadr_t from, const idBitMsg &msg ) {
-+ if ( clientState == CS_DISCONNECTED ) {
-+ common->Printf( "Disconnect packet while not connected.\n" );
-+ return;
-+ }
-+ if ( !Sys_CompareNetAdrBase( from, serverAddress ) ) {
-+ common->Printf( "Disconnect packet from unknown server.\n" );
-+ return;
-+ }
-+ session->Stop();
-+ session->MessageBox( MSG_OK, common->GetLanguageDict()->GetString ( "#str_04320" ), NULL, true );
-+ session->StartMenu();
-+}
-+
-+/*
-+==================
-+idAsyncClient::ProcessInfoResponseMessage
-+==================
-+*/
-+void idAsyncClient::ProcessInfoResponseMessage( const netadr_t from, const idBitMsg &msg ) {
-+ int i, protocol, index;
-+ networkServer_t serverInfo;
-+ bool verbose = false;
-+
-+ if ( from.type == NA_LOOPBACK || cvarSystem->GetCVarBool( "developer" ) ) {
-+ verbose = true;
-+ }
-+
-+ serverInfo.clients = 0;
-+ serverInfo.adr = from;
-+ serverInfo.challenge = msg.ReadInt(); // challenge
-+ protocol = msg.ReadInt();
-+ if ( protocol != ASYNC_PROTOCOL_VERSION ) {
-+ common->Printf( "server %s ignored - protocol %d.%d, expected %d.%d\n", Sys_NetAdrToString( serverInfo.adr ), protocol >> 16, protocol & 0xffff, ASYNC_PROTOCOL_MAJOR, ASYNC_PROTOCOL_MINOR );
-+ return;
-+ }
-+ msg.ReadDeltaDict( serverInfo.serverInfo, NULL );
-+
-+ if ( verbose ) {
-+ common->Printf( "server IP = %s\n", Sys_NetAdrToString( serverInfo.adr ) );
-+ serverInfo.serverInfo.Print();
-+ }
-+ for ( i = msg.ReadByte(); i < MAX_ASYNC_CLIENTS; i = msg.ReadByte() ) {
-+ serverInfo.pings[ serverInfo.clients ] = msg.ReadShort();
-+ serverInfo.rate[ serverInfo.clients ] = msg.ReadInt();
-+ msg.ReadString( serverInfo.nickname[ serverInfo.clients ], MAX_NICKLEN );
-+ if ( verbose ) {
-+ common->Printf( "client %2d: %s, ping = %d, rate = %d\n", i, serverInfo.nickname[ serverInfo.clients ], serverInfo.pings[ serverInfo.clients ], serverInfo.rate[ serverInfo.clients ] );
-+ }
-+ serverInfo.clients++;
-+ }
-+ index = serverList.InfoResponse( serverInfo );
-+
-+ common->Printf( "%d: server %s - protocol %d.%d - %s\n", index, Sys_NetAdrToString( serverInfo.adr ), protocol >> 16, protocol & 0xffff, serverInfo.serverInfo.GetString( "si_name" ) );
-+}
-+
-+/*
-+==================
-+idAsyncClient::ProcessPrintMessage
-+==================
-+*/
-+void idAsyncClient::ProcessPrintMessage( const netadr_t from, const idBitMsg &msg ) {
-+ char string[ MAX_STRING_CHARS ];
-+ int opcode;
-+ int game_opcode = ALLOW_YES;
-+ const char *retpass;
-+
-+ opcode = msg.ReadInt();
-+ if ( opcode == SERVER_PRINT_GAMEDENY ) {
-+ game_opcode = msg.ReadInt();
-+ }
-+ ReadLocalizedServerString( msg, string, MAX_STRING_CHARS );
-+ common->Printf( "%s\n", string );
-+ guiNetMenu->SetStateString( "status", string );
-+ if ( opcode == SERVER_PRINT_GAMEDENY ) {
-+ if ( game_opcode == ALLOW_BADPASS ) {
-+ retpass = session->MessageBox( MSG_PROMPT, common->GetLanguageDict()->GetString ( "#str_04321" ), string, true, "passprompt_ok" );
-+ ClearPendingPackets();
-+ guiNetMenu->SetStateString( "status", common->GetLanguageDict()->GetString ( "#str_04322" ));
-+ if ( retpass ) {
-+ // #790
-+ cvarSystem->SetCVarString( "password", "" );
-+ cvarSystem->SetCVarString( "password", retpass );
-+ } else {
-+ cmdSystem->BufferCommandText( CMD_EXEC_NOW, "disconnect" );
-+ }
-+ } else if ( game_opcode == ALLOW_NO ) {
-+ session->MessageBox( MSG_OK, string, common->GetLanguageDict()->GetString ( "#str_04323" ), true );
-+ ClearPendingPackets();
-+ cmdSystem->BufferCommandText( CMD_EXEC_NOW, "disconnect" );
-+ }
-+ // ALLOW_NOTYET just keeps running as usual. The GUI has an abort button
-+ } else if ( opcode == SERVER_PRINT_BADCHALLENGE && clientState >= CS_CONNECTING ) {
-+ cmdSystem->BufferCommandText( CMD_EXEC_NOW, "reconnect" );
-+ }
-+}
-+
-+/*
-+==================
-+idAsyncClient::ProcessServersListMessage
-+==================
-+*/
-+void idAsyncClient::ProcessServersListMessage( const netadr_t from, const idBitMsg &msg ) {
-+ if ( !Sys_CompareNetAdrBase( idAsyncNetwork::GetMasterAddress(), from ) ) {
-+ common->DPrintf( "received a server list from %s - not a valid master\n", Sys_NetAdrToString( from ) );
-+ return;
-+ }
-+ while ( msg.GetRemaingData() ) {
-+ int a,b,c,d;
-+ a = msg.ReadByte(); b = msg.ReadByte(); c = msg.ReadByte(); d = msg.ReadByte();
-+ serverList.AddServer( serverList.Num(), va( "%i.%i.%i.%i:%i", a, b, c, d, msg.ReadShort() ) );
-+ }
-+}
-+
-+/*
-+==================
-+idAsyncClient::ProcessAuthKeyMessage
-+==================
-+*/
-+void idAsyncClient::ProcessAuthKeyMessage( const netadr_t from, const idBitMsg &msg ) {
-+ authKeyMsg_t authMsg;
-+ char read_string[ MAX_STRING_CHARS ];
-+ const char *retkey;
-+ authBadKeyStatus_t authBadStatus;
-+ int key_index;
-+ bool valid[ 2 ];
-+ idStr auth_msg;
-+
-+ if ( clientState != CS_CONNECTING && !session->WaitingForGameAuth() ) {
-+ common->Printf( "clientState != CS_CONNECTING, not waiting for game auth, authKey ignored\n" );
-+ return;
-+ }
-+
-+ authMsg = (authKeyMsg_t)msg.ReadByte();
-+ if ( authMsg == AUTHKEY_BADKEY ) {
-+ valid[ 0 ] = valid[ 1 ] = true;
-+ key_index = 0;
-+ authBadStatus = (authBadKeyStatus_t)msg.ReadByte();
-+ switch ( authBadStatus ) {
-+ case AUTHKEY_BAD_INVALID:
-+ valid[ 0 ] = ( msg.ReadByte() == 1 );
-+ valid[ 1 ] = ( msg.ReadByte() == 1 );
-+ idAsyncNetwork::BuildInvalidKeyMsg( auth_msg, valid );
-+ break;
-+ case AUTHKEY_BAD_BANNED:
-+ key_index = msg.ReadByte();
-+ auth_msg = common->GetLanguageDict()->GetString( va( "#str_0719%1d", 6 + key_index ) );
-+ auth_msg += "\n";
-+ auth_msg += common->GetLanguageDict()->GetString( "#str_04304" );
-+ valid[ key_index ] = false;
-+ break;
-+ case AUTHKEY_BAD_INUSE:
-+ key_index = msg.ReadByte();
-+ auth_msg = common->GetLanguageDict()->GetString( va( "#str_0719%1d", 8 + key_index ) );
-+ auth_msg += "\n";
-+ auth_msg += common->GetLanguageDict()->GetString( "#str_04304" );
-+ valid[ key_index ] = false;
-+ break;
-+ case AUTHKEY_BAD_MSG:
-+ // a general message explaining why this key is denied
-+ // no specific use for this atm. let's not clear the keys either
-+ msg.ReadString( read_string, MAX_STRING_CHARS );
-+ auth_msg = read_string;
-+ break;
-+ }
-+ common->DPrintf( "auth deny: %s\n", auth_msg.c_str() );
-+
-+ // keys to be cleared. applies to both net connect and game auth
-+ session->ClearCDKey( valid );
-+
-+ // get rid of the bad key - at least that's gonna annoy people who stole a fake key
-+ if ( clientState == CS_CONNECTING ) {
-+ while ( 1 ) {
-+ // here we use the auth status message
-+ retkey = session->MessageBox( MSG_CDKEY, auth_msg, common->GetLanguageDict()->GetString( "#str_04325" ), true );
-+ if ( retkey ) {
-+ if ( session->CheckKey( retkey, true, valid ) ) {
-+ cmdSystem->BufferCommandText( CMD_EXEC_NOW, "reconnect" );
-+ } else {
-+ // build a more precise message about the offline check failure
-+ idAsyncNetwork::BuildInvalidKeyMsg( auth_msg, valid );
-+ session->MessageBox( MSG_OK, auth_msg.c_str(), common->GetLanguageDict()->GetString( "#str_04327" ), true );
-+ continue;
-+ }
-+ } else {
-+ cmdSystem->BufferCommandText( CMD_EXEC_NOW, "disconnect" );
-+ }
-+ break;
-+ }
-+ } else {
-+ // forward the auth status information to the session code
-+ session->CDKeysAuthReply( false, auth_msg );
-+ }
-+ } else {
-+ msg.ReadString( read_string, MAX_STRING_CHARS );
-+ cvarSystem->SetCVarString( "com_guid", read_string );
-+ common->Printf( "guid set to %s\n", read_string );
-+ session->CDKeysAuthReply( true, NULL );
-+ }
-+}
-+
-+/*
-+==================
-+idAsyncClient::ProcessVersionMessage
-+==================
-+*/
-+void idAsyncClient::ProcessVersionMessage( const netadr_t from, const idBitMsg &msg ) {
-+ char string[ MAX_STRING_CHARS ];
-+
-+ if ( updateState != UPDATE_SENT ) {
-+ common->Printf( "ProcessVersionMessage: version reply, != UPDATE_SENT\n" );
-+ return;
-+ }
-+
-+ common->Printf( "A new version is available\n" );
-+ msg.ReadString( string, MAX_STRING_CHARS );
-+ updateMSG = string;
-+ updateDirectDownload = ( msg.ReadByte() != 0 );
-+ msg.ReadString( string, MAX_STRING_CHARS );
-+ updateURL = string;
-+ updateMime = (dlMime_t)msg.ReadByte();
-+ msg.ReadString( string, MAX_STRING_CHARS );
-+ updateFallback = string;
-+ updateState = UPDATE_READY;
-+}
-+
-+/*
-+==================
-+idAsyncClient::ValidatePureServerChecksums
-+==================
-+*/
-+bool idAsyncClient::ValidatePureServerChecksums( const netadr_t from, const idBitMsg &msg ) {
-+ int i, numChecksums, numMissingChecksums;
-+ int inChecksums[ MAX_PURE_PAKS ];
-+ int missingChecksums[ MAX_PURE_PAKS ];
-+ idBitMsg dlmsg;
-+ byte msgBuf[MAX_MESSAGE_SIZE];
-+
-+ // read checksums
-+ // pak checksums, in a 0-terminated list
-+ numChecksums = 0;
-+ do {
-+ i = msg.ReadInt( );
-+ inChecksums[ numChecksums++ ] = i;
-+ // just to make sure a broken message doesn't crash us
-+ if ( numChecksums >= MAX_PURE_PAKS ) {
-+ common->Warning( "MAX_PURE_PAKS ( %d ) exceeded in idAsyncClient::ProcessPureMessage\n", MAX_PURE_PAKS );
-+ return false;
-+ }
-+ } while ( i );
-+ inChecksums[ numChecksums ] = 0;
-+
-+ fsPureReply_t reply = fileSystem->SetPureServerChecksums( inChecksums, missingChecksums );
-+ switch ( reply ) {
-+ case PURE_RESTART:
-+ // need to restart the filesystem with a different pure configuration
-+ cmdSystem->BufferCommandText( CMD_EXEC_NOW, "disconnect" );
-+ // restart with the right FS configuration and get back to the server
-+ clientState = CS_PURERESTART;
-+ fileSystem->SetRestartChecksums( inChecksums );
-+ cmdSystem->BufferCommandText( CMD_EXEC_NOW, "reloadEngine" );
-+ return false;
-+ case PURE_MISSING: {
-+
-+ idStr checksums;
-+
-+ i = 0;
-+ while ( missingChecksums[ i ] ) {
-+ checksums += va( "0x%x ", missingChecksums[ i++ ] );
-+ }
-+ numMissingChecksums = i;
-+
-+ if ( idAsyncNetwork::clientDownload.GetInteger() == 0 ) {
-+ // never any downloads
-+ idStr message = va( common->GetLanguageDict()->GetString( "#str_07210" ), Sys_NetAdrToString( from ) );
-+
-+ if ( numMissingChecksums > 0 ) {
-+ message += va( common->GetLanguageDict()->GetString( "#str_06751" ), numMissingChecksums, checksums.c_str() );
-+ }
-+
-+ common->Printf( message );
-+ cmdSystem->BufferCommandText( CMD_EXEC_NOW, "disconnect" );
-+ session->MessageBox( MSG_OK, message, common->GetLanguageDict()->GetString( "#str_06735" ), true );
-+ } else {
-+ if ( clientState >= CS_CONNECTED ) {
-+ // we are already connected, reconnect to negociate the paks in connectionless mode
-+ cmdSystem->BufferCommandText( CMD_EXEC_NOW, "reconnect" );
-+ return false;
-+ }
-+ // ask the server to send back download info
-+ common->DPrintf( "missing %d paks: %s\n", numMissingChecksums, checksums.c_str() );
-+ // store the requested downloads
-+ GetDownloadRequest( missingChecksums, numMissingChecksums );
-+ // build the download request message
-+ // NOTE: in a specific function?
-+ dlmsg.Init( msgBuf, sizeof( msgBuf ) );
-+ dlmsg.WriteShort( CONNECTIONLESS_MESSAGE_ID );
-+ dlmsg.WriteString( "downloadRequest" );
-+ dlmsg.WriteInt( serverChallenge );
-+ dlmsg.WriteShort( clientId );
-+ // used to make sure the server replies to the same download request
-+ dlmsg.WriteInt( dlRequest );
-+ // special case the code pak - if we have a 0 checksum then we don't need to download it
-+ // 0-terminated list of missing paks
-+ i = 0;
-+ while ( missingChecksums[ i ] ) {
-+ dlmsg.WriteInt( missingChecksums[ i++ ] );
-+ }
-+ dlmsg.WriteInt( 0 );
-+ clientPort.SendPacket( from, dlmsg.GetData(), dlmsg.GetSize() );
-+ }
-+
-+ return false;
-+ }
-+ default:
-+ break;
-+ }
-+
-+ return true;
-+}
-+
-+/*
-+==================
-+idAsyncClient::ProcessPureMessage
-+==================
-+*/
-+void idAsyncClient::ProcessPureMessage( const netadr_t from, const idBitMsg &msg ) {
-+ idBitMsg outMsg;
-+ byte msgBuf[ MAX_MESSAGE_SIZE ];
-+ int i;
-+ int inChecksums[ MAX_PURE_PAKS ];
-+
-+ if ( clientState != CS_CONNECTING ) {
-+ common->Printf( "clientState != CS_CONNECTING, pure msg ignored\n" );
-+ return;
-+ }
-+
-+ if ( !ValidatePureServerChecksums( from, msg ) ) {
-+ return;
-+ }
-+
-+ fileSystem->GetPureServerChecksums( inChecksums );
-+ outMsg.Init( msgBuf, sizeof( msgBuf ) );
-+ outMsg.WriteShort( CONNECTIONLESS_MESSAGE_ID );
-+ outMsg.WriteString( "pureClient" );
-+ outMsg.WriteInt( serverChallenge );
-+ outMsg.WriteShort( clientId );
-+ i = 0;
-+ while ( inChecksums[ i ] ) {
-+ outMsg.WriteInt( inChecksums[ i++ ] );
-+ }
-+ outMsg.WriteInt( 0 );
-+
-+ clientPort.SendPacket( from, outMsg.GetData(), outMsg.GetSize() );
-+}
-+
-+/*
-+==================
-+idAsyncClient::ConnectionlessMessage
-+==================
-+*/
-+void idAsyncClient::ConnectionlessMessage( const netadr_t from, const idBitMsg &msg ) {
-+ char string[MAX_STRING_CHARS*2]; // M. Quinn - Even Balance - PB packets can go beyond 1024
-+
-+ msg.ReadString( string, sizeof( string ) );
-+
-+ // info response from a server, are accepted from any source
-+ if ( idStr::Icmp( string, "infoResponse" ) == 0 ) {
-+ ProcessInfoResponseMessage( from, msg );
-+ return;
-+ }
-+
-+ // from master server:
-+ if ( Sys_CompareNetAdrBase( from, idAsyncNetwork::GetMasterAddress( ) ) ) {
-+ // server list
-+ if ( idStr::Icmp( string, "servers" ) == 0 ) {
-+ ProcessServersListMessage( from, msg );
-+ return;
-+ }
-+
-+ if ( idStr::Icmp( string, "authKey" ) == 0 ) {
-+ ProcessAuthKeyMessage( from, msg );
-+ return;
-+ }
-+
-+ if ( idStr::Icmp( string, "newVersion" ) == 0 ) {
-+ ProcessVersionMessage( from, msg );
-+ return;
-+ }
-+ }
-+
-+ // ignore if not from the current/last server
-+ if ( !Sys_CompareNetAdrBase( from, serverAddress ) && ( lastRconTime + 10000 < realTime || !Sys_CompareNetAdrBase( from, lastRconAddress ) ) ) {
-+ common->DPrintf( "got message '%s' from bad source: %s\n", string, Sys_NetAdrToString( from ) );
-+ return;
-+ }
-+
-+ // challenge response from the server we are connecting to
-+ if ( idStr::Icmp( string, "challengeResponse" ) == 0 ) {
-+ ProcessChallengeResponseMessage( from, msg );
-+ return;
-+ }
-+
-+ // connect response from the server we are connecting to
-+ if ( idStr::Icmp( string, "connectResponse" ) == 0 ) {
-+ ProcessConnectResponseMessage( from, msg );
-+ return;
-+ }
-+
-+ // a disconnect message from the server, which will happen if the server
-+ // dropped the connection but is still getting packets from this client
-+ if ( idStr::Icmp( string, "disconnect" ) == 0 ) {
-+ ProcessDisconnectMessage( from, msg );
-+ return;
-+ }
-+
-+ // print request from server
-+ if ( idStr::Icmp( string, "print" ) == 0 ) {
-+ ProcessPrintMessage( from, msg );
-+ return;
-+ }
-+
-+ // server pure list
-+ if ( idStr::Icmp( string, "pureServer" ) == 0 ) {
-+ ProcessPureMessage( from, msg );
-+ return;
-+ }
-+
-+ if ( idStr::Icmp( string, "downloadInfo" ) == 0 ) {
-+ ProcessDownloadInfoMessage( from, msg );
-+ }
-+
-+ if ( idStr::Icmp( string, "authrequired" ) == 0 ) {
-+ // server telling us that he's expecting an auth mode connect, just in case we're trying to connect in LAN mode
-+ if ( idAsyncNetwork::LANServer.GetBool() ) {
-+ common->Warning( "server %s requests master authorization for this client. Turning off LAN mode\n", Sys_NetAdrToString( from ) );
-+ idAsyncNetwork::LANServer.SetBool( false );
-+ }
-+ }
-+
-+ common->DPrintf( "ignored message from %s: %s\n", Sys_NetAdrToString( from ), string );
-+}
-+
-+/*
-+=================
-+idAsyncClient::ProcessMessage
-+=================
-+*/
-+void idAsyncClient::ProcessMessage( const netadr_t from, idBitMsg &msg ) {
-+ int id;
-+
-+ id = msg.ReadShort();
-+
-+ // check for a connectionless packet
-+ if ( id == CONNECTIONLESS_MESSAGE_ID ) {
-+ ConnectionlessMessage( from, msg );
-+ return;
-+ }
-+
-+ if ( clientState < CS_CONNECTED ) {
-+ return; // can't be a valid sequenced packet
-+ }
-+
-+ if ( msg.GetRemaingData() < 4 ) {
-+ common->DPrintf( "%s: tiny packet\n", Sys_NetAdrToString( from ) );
-+ return;
-+ }
-+
-+ // is this a packet from the server
-+ if ( !Sys_CompareNetAdrBase( from, channel.GetRemoteAddress() ) || id != serverId ) {
-+ common->DPrintf( "%s: sequenced server packet without connection\n", Sys_NetAdrToString( from ) );
-+ return;
-+ }
-+
-+ if ( !channel.Process( from, clientTime, msg, serverMessageSequence ) ) {
-+ return; // out of order, duplicated, fragment, etc.
-+ }
-+
-+ lastPacketTime = clientTime;
-+ ProcessReliableServerMessages();
-+ ProcessUnreliableServerMessage( msg );
-+}
-+
-+/*
-+==================
-+idAsyncClient::SetupConnection
-+==================
-+*/
-+void idAsyncClient::SetupConnection( void ) {
-+ idBitMsg msg;
-+ byte msgBuf[MAX_MESSAGE_SIZE];
-+
-+ if ( clientTime - lastConnectTime < SETUP_CONNECTION_RESEND_TIME ) {
-+ return;
-+ }
-+
-+ if ( clientState == CS_CHALLENGING ) {
-+ common->Printf( "sending challenge to %s\n", Sys_NetAdrToString( serverAddress ) );
-+ msg.Init( msgBuf, sizeof( msgBuf ) );
-+ msg.WriteShort( CONNECTIONLESS_MESSAGE_ID );
-+ msg.WriteString( "challenge" );
-+ msg.WriteInt( clientId );
-+ clientPort.SendPacket( serverAddress, msg.GetData(), msg.GetSize() );
-+ } else if ( clientState == CS_CONNECTING ) {
-+ common->Printf( "sending connect to %s with challenge 0x%x\n", Sys_NetAdrToString( serverAddress ), serverChallenge );
-+ msg.Init( msgBuf, sizeof( msgBuf ) );
-+ msg.WriteShort( CONNECTIONLESS_MESSAGE_ID );
-+ msg.WriteString( "connect" );
-+ msg.WriteInt( ASYNC_PROTOCOL_VERSION );
-+ msg.WriteInt( clientDataChecksum );
-+ msg.WriteInt( serverChallenge );
-+ msg.WriteShort( clientId );
-+ msg.WriteInt( cvarSystem->GetCVarInteger( "net_clientMaxRate" ) );
-+ msg.WriteString( cvarSystem->GetCVarString( "com_guid" ) );
-+ msg.WriteString( cvarSystem->GetCVarString( "password" ), -1, false );
-+ // do not make the protocol depend on PB
-+ msg.WriteShort( 0 );
-+ clientPort.SendPacket( serverAddress, msg.GetData(), msg.GetSize() );
-+#ifdef ID_ENFORCE_KEY_CLIENT
-+ if ( idAsyncNetwork::LANServer.GetBool() ) {
-+ common->Printf( "net_LANServer is set, connecting in LAN mode\n" );
-+ } else {
-+ // emit a cd key authorization request
-+ // modified at protocol 1.37 for XP key addition
-+ msg.BeginWriting();
-+ msg.WriteShort( CONNECTIONLESS_MESSAGE_ID );
-+ msg.WriteString( "clAuth" );
-+ msg.WriteInt( ASYNC_PROTOCOL_VERSION );
-+ msg.WriteNetadr( serverAddress );
-+ // if we don't have a com_guid, this will request a direct reply from auth with it
-+ msg.WriteByte( cvarSystem->GetCVarString( "com_guid" )[0] ? 1 : 0 );
-+ // send the main key, and flag an extra byte to add XP key
-+ msg.WriteString( session->GetCDKey( false ) );
-+ const char *xpkey = session->GetCDKey( true );
-+ msg.WriteByte( xpkey ? 1 : 0 );
-+ if ( xpkey ) {
-+ msg.WriteString( xpkey );
-+ }
-+ clientPort.SendPacket( idAsyncNetwork::GetMasterAddress(), msg.GetData(), msg.GetSize() );
-+ }
-+#else
-+ if (! Sys_IsLANAddress( serverAddress ) ) {
-+ common->Printf( "Build Does not have CD Key Enforcement enabled. The Server ( %s ) is not within the lan addresses. Attemting to connect.\n", Sys_NetAdrToString( serverAddress ) );
-+ }
-+ common->Printf( "Not Testing key.\n" );
-+#endif
-+ } else {
-+ return;
-+ }
-+
-+ lastConnectTime = clientTime;
-+}
-+
-+/*
-+==================
-+idAsyncClient::SendReliableGameMessage
-+==================
-+*/
-+void idAsyncClient::SendReliableGameMessage( const idBitMsg &msg ) {
-+ idBitMsg outMsg;
-+ byte msgBuf[MAX_MESSAGE_SIZE];
-+
-+ if ( clientState < CS_INGAME ) {
-+ return;
-+ }
-+
-+ outMsg.Init( msgBuf, sizeof( msgBuf ) );
-+ outMsg.WriteByte( CLIENT_RELIABLE_MESSAGE_GAME );
-+ outMsg.WriteData( msg.GetData(), msg.GetSize() );
-+ if ( !channel.SendReliableMessage( outMsg ) ) {
-+ common->Error( "client->server reliable messages overflow\n" );
-+ }
-+}
-+
-+/*
-+==================
-+idAsyncClient::Idle
-+==================
-+*/
-+void idAsyncClient::Idle( void ) {
-+ // also need to read mouse for the connecting guis
-+ usercmdGen->GetDirectUsercmd();
-+
-+ SendEmptyToServer();
-+}
-+
-+/*
-+==================
-+idAsyncClient::UpdateTime
-+==================
-+*/
-+int idAsyncClient::UpdateTime( int clamp ) {
-+ int time, msec;
-+
-+ time = Sys_Milliseconds();
-+ msec = idMath::ClampInt( 0, clamp, time - realTime );
-+ realTime = time;
-+ clientTime += msec;
-+ return msec;
-+}
-+
-+/*
-+==================
-+idAsyncClient::RunFrame
-+==================
-+*/
-+void idAsyncClient::RunFrame( void ) {
-+ int msec, size;
-+ bool newPacket;
-+ idBitMsg msg;
-+ byte msgBuf[MAX_MESSAGE_SIZE];
-+ netadr_t from;
-+
-+ msec = UpdateTime( 100 );
-+
-+ if ( !clientPort.GetPort() ) {
-+ return;
-+ }
-+
-+ // handle ongoing pk4 downloads and patch downloads
-+ HandleDownloads();
-+
-+ gameTimeResidual += msec;
-+
-+ // spin in place processing incoming packets until enough time lapsed to run a new game frame
-+ do {
-+
-+ do {
-+
-+ // blocking read with game time residual timeout
-+ newPacket = clientPort.GetPacketBlocking( from, msgBuf, size, sizeof( msgBuf ), USERCMD_MSEC - ( gameTimeResidual + clientPredictTime ) - 1 );
-+ if ( newPacket ) {
-+ msg.Init( msgBuf, sizeof( msgBuf ) );
-+ msg.SetSize( size );
-+ msg.BeginReading();
-+ ProcessMessage( from, msg );
-+ }
-+
-+ msec = UpdateTime( 100 );
-+ gameTimeResidual += msec;
-+
-+ } while( newPacket );
-+
-+ } while( gameTimeResidual + clientPredictTime < USERCMD_MSEC );
-+
-+ // update server list
-+ serverList.RunFrame();
-+
-+ if ( clientState == CS_DISCONNECTED ) {
-+ usercmdGen->GetDirectUsercmd();
-+ gameTimeResidual = USERCMD_MSEC - 1;
-+ clientPredictTime = 0;
-+ return;
-+ }
-+
-+ if ( clientState == CS_PURERESTART ) {
-+ clientState = CS_DISCONNECTED;
-+ Reconnect();
-+ gameTimeResidual = USERCMD_MSEC - 1;
-+ clientPredictTime = 0;
-+ return;
-+ }
-+
-+ // if not connected setup a connection
-+ if ( clientState < CS_CONNECTED ) {
-+ // also need to read mouse for the connecting guis
-+ usercmdGen->GetDirectUsercmd();
-+ SetupConnection();
-+ gameTimeResidual = USERCMD_MSEC - 1;
-+ clientPredictTime = 0;
-+ return;
-+ }
-+
-+ if ( CheckTimeout() ) {
-+ return;
-+ }
-+
-+ // if not yet in the game send empty messages to keep data flowing through the channel
-+ if ( clientState < CS_INGAME ) {
-+ Idle();
-+ gameTimeResidual = 0;
-+ return;
-+ }
-+
-+ // check for user info changes
-+ if ( cvarSystem->GetModifiedFlags() & CVAR_USERINFO ) {
-+ game->ThrottleUserInfo( );
-+ SendUserInfoToServer( );
-+ game->SetUserInfo( clientNum, sessLocal.mapSpawnData.userInfo[ clientNum ], true, false );
-+ cvarSystem->ClearModifiedFlags( CVAR_USERINFO );
-+ }
-+
-+ if ( gameTimeResidual + clientPredictTime >= USERCMD_MSEC ) {
-+ lastFrameDelta = 0;
-+ }
-+
-+ // generate user commands for the predicted time
-+ while ( gameTimeResidual + clientPredictTime >= USERCMD_MSEC ) {
-+
-+ // send the user commands of this client to the server
-+ SendUsercmdsToServer();
-+
-+ // update time
-+ gameFrame++;
-+ gameTime += USERCMD_MSEC;
-+ gameTimeResidual -= USERCMD_MSEC;
-+
-+ // run from the snapshot up to the local game frame
-+ while ( snapshotGameFrame < gameFrame ) {
-+
-+ lastFrameDelta++;
-+
-+ // duplicate usercmds for clients if no new ones are available
-+ DuplicateUsercmds( snapshotGameFrame, snapshotGameTime );
-+
-+ // indicate the last prediction frame before a render
-+ bool lastPredictFrame = ( snapshotGameFrame + 1 >= gameFrame && gameTimeResidual + clientPredictTime < USERCMD_MSEC );
-+
-+ // run client prediction
-+ gameReturn_t ret = game->ClientPrediction( clientNum, userCmds[ snapshotGameFrame & ( MAX_USERCMD_BACKUP - 1 ) ], lastPredictFrame );
-+
-+ idAsyncNetwork::ExecuteSessionCommand( ret.sessionCommand );
-+
-+ snapshotGameFrame++;
-+ snapshotGameTime += USERCMD_MSEC;
-+ }
-+ }
-+}
-+
-+/*
-+==================
-+idAsyncClient::PacifierUpdate
-+==================
-+*/
-+void idAsyncClient::PacifierUpdate( void ) {
-+ if ( !IsActive() ) {
-+ return;
-+ }
-+ realTime = Sys_Milliseconds();
-+ SendEmptyToServer( false, true );
-+}
-+
-+/*
-+==================
-+idAsyncClient::SendVersionCheck
-+==================
-+*/
-+void idAsyncClient::SendVersionCheck( bool fromMenu ) {
-+ idBitMsg msg;
-+ byte msgBuf[MAX_MESSAGE_SIZE];
-+
-+ if ( updateState != UPDATE_NONE && !fromMenu ) {
-+ common->DPrintf( "up-to-date check was already performed\n" );
-+ return;
-+ }
-+
-+ InitPort();
-+ msg.Init( msgBuf, sizeof( msgBuf ) );
-+ msg.WriteShort( CONNECTIONLESS_MESSAGE_ID );
-+ msg.WriteString( "versionCheck" );
-+ msg.WriteInt( ASYNC_PROTOCOL_VERSION );
-+ msg.WriteString( cvarSystem->GetCVarString( "si_version" ) );
-+ msg.WriteString( cvarSystem->GetCVarString( "com_guid" ) );
-+ clientPort.SendPacket( idAsyncNetwork::GetMasterAddress(), msg.GetData(), msg.GetSize() );
-+
-+ common->DPrintf( "sent a version check request\n" );
-+
-+ updateState = UPDATE_SENT;
-+ updateSentTime = clientTime;
-+ showUpdateMessage = fromMenu;
-+}
-+
-+/*
-+==================
-+idAsyncClient::SendVersionDLUpdate
-+
-+sending those packets is not strictly necessary. just a way to tell the update server
-+about what is going on. allows the update server to have a more precise view of the overall
-+network load for the updates
-+==================
-+*/
-+void idAsyncClient::SendVersionDLUpdate( int state ) {
-+ idBitMsg msg;
-+ byte msgBuf[MAX_MESSAGE_SIZE];
-+
-+ msg.Init( msgBuf, sizeof( msgBuf ) );
-+ msg.WriteShort( CONNECTIONLESS_MESSAGE_ID );
-+ msg.WriteString( "versionDL" );
-+ msg.WriteInt( ASYNC_PROTOCOL_VERSION );
-+ msg.WriteShort( state );
-+ clientPort.SendPacket( idAsyncNetwork::GetMasterAddress(), msg.GetData(), msg.GetSize() );
-+}
-+
-+/*
-+==================
-+idAsyncClient::HandleDownloads
-+==================
-+*/
-+void idAsyncClient::HandleDownloads( void ) {
-+
-+ if ( updateState == UPDATE_SENT && clientTime > updateSentTime + 2000 ) {
-+ // timing out on no reply
-+ updateState = UPDATE_DONE;
-+ if ( showUpdateMessage ) {
-+ session->MessageBox( MSG_OK, common->GetLanguageDict()->GetString ( "#str_04839" ), common->GetLanguageDict()->GetString ( "#str_04837" ), true );
-+ showUpdateMessage = false;
-+ }
-+ common->DPrintf( "No update available\n" );
-+ } else if ( backgroundDownload.completed ) {
-+ // only enter these if the download slot is free
-+ if ( updateState == UPDATE_READY ) {
-+ //
-+ if ( session->MessageBox( MSG_YESNO, updateMSG, common->GetLanguageDict()->GetString ( "#str_04330" ), true, "yes" )[0] ) {
-+ if ( !updateDirectDownload ) {
-+ sys->OpenURL( updateURL, true );
-+ updateState = UPDATE_DONE;
-+ } else {
-+
-+ // we're just creating the file at toplevel inside fs_savepath
-+ updateURL.ExtractFileName( updateFile );
-+ idFile_Permanent *f = static_cast< idFile_Permanent *>( fileSystem->OpenFileWrite( updateFile ) );
-+ dltotal = 0;
-+ dlnow = 0;
-+
-+ backgroundDownload.completed = false;
-+ backgroundDownload.opcode = DLTYPE_URL;
-+ backgroundDownload.f = f;
-+ backgroundDownload.url.status = DL_WAIT;
-+ backgroundDownload.url.dlnow = 0;
-+ backgroundDownload.url.dltotal = 0;
-+ backgroundDownload.url.url = updateURL;
-+ fileSystem->BackgroundDownload( &backgroundDownload );
-+
-+ updateState = UPDATE_DLING;
-+ SendVersionDLUpdate( 0 );
-+ session->DownloadProgressBox( &backgroundDownload, va( "Downloading %s\n", updateFile.c_str() ) );
-+ updateState = UPDATE_DONE;
-+ if ( backgroundDownload.url.status == DL_DONE ) {
-+ SendVersionDLUpdate( 1 );
-+ idStr fullPath = f->GetFullPath();
-+ fileSystem->CloseFile( f );
-+ if ( session->MessageBox( MSG_YESNO, common->GetLanguageDict()->GetString ( "#str_04331" ), common->GetLanguageDict()->GetString ( "#str_04332" ), true, "yes" )[0] ) {
-+ if ( updateMime == DL_FILE_EXEC ) {
-+ sys->StartProcess( fullPath, true );
-+ } else {
-+ sys->OpenURL( va( "file://%s", fullPath.c_str() ), true );
-+ }
-+ } else {
-+ session->MessageBox( MSG_OK, va( common->GetLanguageDict()->GetString ( "#str_04333" ), fullPath.c_str() ), common->GetLanguageDict()->GetString ( "#str_04334" ), true );
-+ }
-+ } else {
-+ if ( backgroundDownload.url.dlerror[ 0 ] ) {
-+ common->Warning( "update download failed. curl error: %s", backgroundDownload.url.dlerror );
-+ }
-+ SendVersionDLUpdate( 2 );
-+ idStr name = f->GetName();
-+ fileSystem->CloseFile( f );
-+ fileSystem->RemoveFile( name );
-+ session->MessageBox( MSG_OK, common->GetLanguageDict()->GetString ( "#str_04335" ), common->GetLanguageDict()->GetString ( "#str_04336" ), true );
-+ if ( updateFallback.Length() ) {
-+ sys->OpenURL( updateFallback.c_str(), true );
-+ } else {
-+ common->Printf( "no fallback URL\n" );
-+ }
-+ }
-+ }
-+ } else {
-+ updateState = UPDATE_DONE;
-+ }
-+ } else if ( dlList.Num() ) {
-+
-+ int numPaks = dlList.Num();
-+ int pakCount = 1;
-+ int progress_start, progress_end;
-+ currentDlSize = 0;
-+
-+ do {
-+
-+ if ( dlList[ 0 ].url[ 0 ] == '\0' ) {
-+ // ignore empty files
-+ dlList.RemoveIndex( 0 );
-+ continue;
-+ }
-+ common->Printf( "start download for %s\n", dlList[ 0 ].url.c_str() );
-+
-+ idFile_Permanent *f = static_cast< idFile_Permanent *>( fileSystem->MakeTemporaryFile( ) );
-+ if ( !f ) {
-+ common->Warning( "could not create temporary file" );
-+ dlList.Clear();
-+ return;
-+ }
-+
-+ backgroundDownload.completed = false;
-+ backgroundDownload.opcode = DLTYPE_URL;
-+ backgroundDownload.f = f;
-+ backgroundDownload.url.status = DL_WAIT;
-+ backgroundDownload.url.dlnow = 0;
-+ backgroundDownload.url.dltotal = dlList[ 0 ].size;
-+ backgroundDownload.url.url = dlList[ 0 ].url;
-+ fileSystem->BackgroundDownload( &backgroundDownload );
-+ idStr dltitle;
-+ // "Downloading %s"
-+ sprintf( dltitle, common->GetLanguageDict()->GetString( "#str_07213" ), dlList[ 0 ].filename.c_str() );
-+ if ( numPaks > 1 ) {
-+ dltitle += va( " (%d/%d)", pakCount, numPaks );
-+ }
-+ if ( totalDlSize ) {
-+ progress_start = (int)( (float)currentDlSize * 100.0f / (float)totalDlSize );
-+ progress_end = (int)( (float)( currentDlSize + dlList[ 0 ].size ) * 100.0f / (float)totalDlSize );
-+ } else {
-+ progress_start = 0;
-+ progress_end = 100;
-+ }
-+ session->DownloadProgressBox( &backgroundDownload, dltitle, progress_start, progress_end );
-+ if ( backgroundDownload.url.status == DL_DONE ) {
-+ idFile *saveas;
-+ const int CHUNK_SIZE = 1024 * 1024;
-+ byte *buf;
-+ int remainlen;
-+ int readlen;
-+ int retlen;
-+ int checksum;
-+
-+ common->Printf( "file downloaded\n" );
-+ idStr finalPath = cvarSystem->GetCVarString( "fs_savepath" );
-+ finalPath.AppendPath( dlList[ 0 ].filename );
-+ fileSystem->CreateOSPath( finalPath );
-+ // do the final copy ourselves so we do by small chunks in case the file is big
-+ saveas = fileSystem->OpenExplicitFileWrite( finalPath );
-+ buf = (byte*)Mem_Alloc( CHUNK_SIZE );
-+ f->Seek( 0, FS_SEEK_END );
-+ remainlen = f->Tell();
-+ f->Seek( 0, FS_SEEK_SET );
-+ while ( remainlen ) {
-+ readlen = Min( remainlen, CHUNK_SIZE );
-+ retlen = f->Read( buf, readlen );
-+ if ( retlen != readlen ) {
-+ common->FatalError( "short read %d of %d in idFileSystem::HandleDownload", retlen, readlen );
-+ }
-+ retlen = saveas->Write( buf, readlen );
-+ if ( retlen != readlen ) {
-+ common->FatalError( "short write %d of %d in idFileSystem::HandleDownload", retlen, readlen );
-+ }
-+ remainlen -= readlen;
-+ }
-+ fileSystem->CloseFile( f );
-+ fileSystem->CloseFile( saveas );
-+ common->Printf( "saved as %s\n", finalPath.c_str() );
-+ Mem_Free( buf );
-+
-+ // add that file to our paks list
-+ checksum = fileSystem->AddZipFile( dlList[ 0 ].filename );
-+
-+ // verify the checksum to be what the server says
-+ if ( !checksum || checksum != dlList[ 0 ].checksum ) {
-+ // "pak is corrupted ( checksum 0x%x, expected 0x%x )"
-+ session->MessageBox( MSG_OK, va( common->GetLanguageDict()->GetString( "#str_07214" ) , checksum, dlList[0].checksum ), "Download failed", true );
-+ fileSystem->RemoveFile( dlList[ 0 ].filename );
-+ dlList.Clear();
-+ return;
-+ }
-+
-+ currentDlSize += dlList[ 0 ].size;
-+
-+ } else {
-+ common->Warning( "download failed: %s", dlList[ 0 ].url.c_str() );
-+ if ( backgroundDownload.url.dlerror[ 0 ] ) {
-+ common->Warning( "curl error: %s", backgroundDownload.url.dlerror );
-+ }
-+ // "The download failed or was cancelled"
-+ // "Download failed"
-+ session->MessageBox( MSG_OK, common->GetLanguageDict()->GetString( "#str_07215" ), common->GetLanguageDict()->GetString( "#str_07216" ), true );
-+ dlList.Clear();
-+ return;
-+ }
-+
-+ pakCount++;
-+ dlList.RemoveIndex( 0 );
-+ } while ( dlList.Num() );
-+
-+ // all downloads successful - do the dew
-+ cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "reconnect\n" );
-+ }
-+ }
-+}
-+
-+/*
-+===============
-+idAsyncClient::SendAuthCheck
-+===============
-+*/
-+bool idAsyncClient::SendAuthCheck( const char *cdkey, const char *xpkey ) {
-+ idBitMsg msg;
-+ byte msgBuf[MAX_MESSAGE_SIZE];
-+
-+ msg.Init( msgBuf, sizeof( msgBuf ) );
-+ msg.WriteShort( CONNECTIONLESS_MESSAGE_ID );
-+ msg.WriteString( "gameAuth" );
-+ msg.WriteInt( ASYNC_PROTOCOL_VERSION );
-+ msg.WriteByte( cdkey ? 1 : 0 );
-+ msg.WriteString( cdkey ? cdkey : "" );
-+ msg.WriteByte( xpkey ? 1 : 0 );
-+ msg.WriteString( xpkey ? xpkey : "" );
-+ InitPort();
-+ clientPort.SendPacket( idAsyncNetwork::GetMasterAddress(), msg.GetData(), msg.GetSize() );
-+ return true;
-+}
-+
-+/*
-+===============
-+idAsyncClient::CheckTimeout
-+===============
-+*/
-+bool idAsyncClient::CheckTimeout( void ) {
-+ if ( lastPacketTime > 0 && ( lastPacketTime + idAsyncNetwork::clientServerTimeout.GetInteger()*1000 < clientTime ) ) {
-+ session->StopBox();
-+ session->MessageBox( MSG_OK, common->GetLanguageDict()->GetString ( "#str_04328" ), common->GetLanguageDict()->GetString ( "#str_04329" ), true );
-+ cmdSystem->BufferCommandText( CMD_EXEC_NOW, "disconnect" );
-+ return true;
-+ }
-+ return false;
-+}
-+
-+/*
-+===============
-+idAsyncClient::ProcessDownloadInfoMessage
-+===============
-+*/
-+void idAsyncClient::ProcessDownloadInfoMessage( const netadr_t from, const idBitMsg &msg ) {
-+ char buf[ MAX_STRING_CHARS ];
-+ int srvDlRequest = msg.ReadInt();
-+ int infoType = msg.ReadByte();
-+ int pakDl;
-+ int pakIndex;
-+
-+ pakDlEntry_t entry;
-+ bool gotAllFiles = true;
-+ idStr sizeStr;
-+ bool gotGame = false;
-+
-+ if ( dlRequest == -1 || srvDlRequest != dlRequest ) {
-+ common->Warning( "bad download id from server, ignored" );
-+ return;
-+ }
-+ // mark the dlRequest as dead now whatever how we process it
-+ dlRequest = -1;
-+
-+ if ( infoType == SERVER_DL_REDIRECT ) {
-+ msg.ReadString( buf, MAX_STRING_CHARS );
-+ cmdSystem->BufferCommandText( CMD_EXEC_NOW, "disconnect" );
-+ // "You are missing required pak files to connect to this server.\nThe server gave a web page though:\n%s\nDo you want to go there now?"
-+ // "Missing required files"
-+ if ( session->MessageBox( MSG_YESNO, va( common->GetLanguageDict()->GetString( "#str_07217" ), buf ),
-+ common->GetLanguageDict()->GetString( "#str_07218" ), true, "yes" )[ 0 ] ) {
-+ sys->OpenURL( buf, true );
-+ }
-+ } else if ( infoType == SERVER_DL_LIST ) {
-+ cmdSystem->BufferCommandText( CMD_EXEC_NOW, "disconnect" );
-+ if ( dlList.Num() ) {
-+ common->Warning( "tried to process a download list while already busy downloading things" );
-+ return;
-+ }
-+ // read the URLs, check against what we requested, prompt for download
-+ pakIndex = -1;
-+ totalDlSize = 0;
-+ do {
-+ pakIndex++;
-+ pakDl = msg.ReadByte();
-+ if ( pakDl == SERVER_PAK_YES ) {
-+ if ( pakIndex == 0 ) {
-+ gotGame = true;
-+ }
-+ msg.ReadString( buf, MAX_STRING_CHARS );
-+ entry.filename = buf;
-+ msg.ReadString( buf, MAX_STRING_CHARS );
-+ entry.url = buf;
-+ entry.size = msg.ReadInt();
-+ // checksums are not transmitted, we read them from the dl request we sent
-+ entry.checksum = dlChecksums[ pakIndex ];
-+ totalDlSize += entry.size;
-+ dlList.Append( entry );
-+ common->Printf( "download %s from %s ( 0x%x )\n", entry.filename.c_str(), entry.url.c_str(), entry.checksum );
-+ } else if ( pakDl == SERVER_PAK_NO ) {
-+ msg.ReadString( buf, MAX_STRING_CHARS );
-+ entry.filename = buf;
-+ entry.url = "";
-+ entry.size = 0;
-+ entry.checksum = 0;
-+ dlList.Append( entry );
-+ // first pak is game pak, only fail it if we actually requested it
-+ if ( pakIndex != 0 || dlChecksums[ 0 ] != 0 ) {
-+ common->Printf( "no download offered for %s ( 0x%x )\n", entry.filename.c_str(), dlChecksums[ pakIndex ] );
-+ gotAllFiles = false;
-+ }
-+ } else {
-+ assert( pakDl == SERVER_PAK_END );
-+ }
-+ } while ( pakDl != SERVER_PAK_END );
-+ if ( dlList.Num() < dlCount ) {
-+ common->Printf( "%d files were ignored by the server\n", dlCount - dlList.Num() );
-+ gotAllFiles = false;
-+ }
-+ sizeStr.BestUnit( "%.2f", totalDlSize, MEASURE_SIZE );
-+ cmdSystem->BufferCommandText( CMD_EXEC_NOW, "disconnect" );
-+ if ( totalDlSize == 0 ) {
-+ // was no downloadable stuff for us
-+ // "Can't connect to the pure server: no downloads offered"
-+ // "Missing required files"
-+ dlList.Clear();
-+ session->MessageBox( MSG_OK, common->GetLanguageDict()->GetString( "#str_07219" ), common->GetLanguageDict()->GetString( "#str_07218" ), true );
-+ return;
-+ }
-+ bool asked = false;
-+ if ( gotGame ) {
-+ asked = true;
-+ // "You need to download game code to connect to this server. Are you sure? You should only answer yes if you trust the server administrators."
-+ // "Missing game binaries"
-+ if ( !session->MessageBox( MSG_YESNO, common->GetLanguageDict()->GetString( "#str_07220" ), common->GetLanguageDict()->GetString( "#str_07221" ), true, "yes" )[ 0 ] ) {
-+ dlList.Clear();
-+ return;
-+ }
-+ }
-+ if ( !gotAllFiles ) {
-+ asked = true;
-+ // "The server only offers to download some of the files required to connect ( %s ). Download anyway?"
-+ // "Missing required files"
-+ if ( !session->MessageBox( MSG_YESNO, va( common->GetLanguageDict()->GetString( "#str_07222" ), sizeStr.c_str() ),
-+ common->GetLanguageDict()->GetString( "#str_07218" ), true, "yes" )[ 0 ] ) {
-+ dlList.Clear();
-+ return;
-+ }
-+ }
-+ if ( !asked && idAsyncNetwork::clientDownload.GetInteger() == 1 ) {
-+ // "You need to download some files to connect to this server ( %s ), proceed?"
-+ // "Missing required files"
-+ if ( !session->MessageBox( MSG_YESNO, va( common->GetLanguageDict()->GetString( "#str_07224" ), sizeStr.c_str() ),
-+ common->GetLanguageDict()->GetString( "#str_07218" ), true, "yes" )[ 0 ] ) {
-+ dlList.Clear();
-+ return;
-+ }
-+ }
-+ } else {
-+ cmdSystem->BufferCommandText( CMD_EXEC_NOW, "disconnect" );
-+ // "You are missing some files to connect to this server, and the server doesn't provide downloads."
-+ // "Missing required files"
-+ session->MessageBox( MSG_OK, common->GetLanguageDict()->GetString( "#str_07223" ), common->GetLanguageDict()->GetString( "#str_07218" ), true );
-+ }
-+}
-+
-+/*
-+===============
-+idAsyncClient::GetDownloadRequest
-+===============
-+*/
-+int idAsyncClient::GetDownloadRequest( const int checksums[ MAX_PURE_PAKS ], int count ) {
-+ assert( !checksums[ count ] ); // 0-terminated
-+ if ( memcmp( dlChecksums, checksums, sizeof( int ) * count ) ) {
-+ idRandom newreq;
-+
-+ memcpy( dlChecksums, checksums, sizeof( int ) * MAX_PURE_PAKS );
-+
-+ newreq.SetSeed( Sys_Milliseconds() );
-+ dlRequest = newreq.RandomInt();
-+ dlCount = count;
-+ return dlRequest;
-+ }
-+ // this is the same dlRequest, we haven't heard from the server. keep the same id
-+ return dlRequest;
-+}
-diff -Naur dhewm3.old/neo/framework/async/AsyncClient.h dhewm3/neo/framework/async/AsyncClient.h
---- dhewm3.old/neo/framework/async/AsyncClient.h 2013-08-15 23:27:20.000000000 +0200
-+++ dhewm3/neo/framework/async/AsyncClient.h 2013-08-22 13:23:32.996219594 +0200
-@@ -114,9 +114,6 @@
- void SendReliableGameMessage( const idBitMsg &msg );
-
- void SendVersionCheck( bool fromMenu = false );
-- // pass NULL for the keys you don't care to auth for
-- // returns false if internet link doesn't appear to be available
-- bool SendAuthCheck( const char *cdkey, const char *xpkey );
-
- void PacifierUpdate( void );
-
-@@ -201,7 +198,6 @@
- void ProcessInfoResponseMessage( const netadr_t from, const idBitMsg &msg );
- void ProcessPrintMessage( const netadr_t from, const idBitMsg &msg );
- void ProcessServersListMessage( const netadr_t from, const idBitMsg &msg );
-- void ProcessAuthKeyMessage( const netadr_t from, const idBitMsg &msg );
- void ProcessVersionMessage( const netadr_t from, const idBitMsg &msg );
- void ConnectionlessMessage( const netadr_t from, const idBitMsg &msg );
- void ProcessMessage( const netadr_t from, idBitMsg &msg );
-diff -Naur dhewm3.old/neo/framework/BuildDefines.h dhewm3/neo/framework/BuildDefines.h
---- dhewm3.old/neo/framework/BuildDefines.h 2013-08-15 23:27:20.000000000 +0200
-+++ dhewm3/neo/framework/BuildDefines.h 2013-08-22 13:23:32.997219582 +0200
-@@ -75,24 +75,6 @@
- #define ID_ALLOW_TOOLS
- #endif
-
--#define ID_ENFORCE_KEY_CLIENT 0
--
--#ifndef ID_ENFORCE_KEY
--# if !defined( ID_DEDICATED )
--# define ID_ENFORCE_KEY 1
--# else
--# define ID_ENFORCE_KEY 0
--# endif
--#endif
--
--#ifndef ID_ENFORCE_KEY_CLIENT
--# if ID_ENFORCE_KEY
--# define ID_ENFORCE_KEY_CLIENT 1
--# else
--# define ID_ENFORCE_KEY_CLIENT 0
--# endif
--#endif
--
- // async network
-
- /*
-diff -Naur dhewm3.old/neo/framework/BuildDefines.h.rej dhewm3/neo/framework/BuildDefines.h.rej
---- dhewm3.old/neo/framework/BuildDefines.h.rej 1970-01-01 01:00:00.000000000 +0100
-+++ dhewm3/neo/framework/BuildDefines.h.rej 2013-08-22 13:23:32.997219582 +0200
-@@ -0,0 +1,17 @@
-+--- dhewm3.old/neo/framework/BuildDefines.h 2012-07-28 12:54:25.000000000 +0200
-++++ dhewm3/neo/framework/BuildDefines.h 2012-08-29 15:07:41.788513805 +0200
-+@@ -75,14 +75,6 @@
-+ #define ID_ALLOW_TOOLS
-+ #endif
-+
-+-#ifndef ID_ENFORCE_KEY
-+-# if !defined( ID_DEDICATED )
-+-# define ID_ENFORCE_KEY 1
-+-# else
-+-# define ID_ENFORCE_KEY 0
-+-# endif
-+-#endif
-+-
-+ // async network
-+
-+ /*
-diff -Naur dhewm3.old/neo/framework/Common.cpp dhewm3/neo/framework/Common.cpp
---- dhewm3.old/neo/framework/Common.cpp 2013-08-15 23:27:20.000000000 +0200
-+++ dhewm3/neo/framework/Common.cpp 2013-08-22 13:23:32.997219582 +0200
-@@ -1049,7 +1049,6 @@
- com_developer.SetBool( false );
-
- WriteConfigToFile( CONFIG_FILE );
-- session->WriteCDKey( );
-
- // restore the developer cvar
- com_developer.SetBool( developer );
-@@ -2832,11 +2831,7 @@
- InitGame();
-
- // don't add startup commands if no CD key is present
--#if ID_ENFORCE_KEY
-- if ( !session->CDKeysAreValid( false ) || !AddStartupCommands() ) {
--#else
- if ( !AddStartupCommands() ) {
--#endif
- // if the user didn't give any commands, run default action
- session->StartMenu( true );
- }
-diff -Naur dhewm3.old/neo/framework/Common.cpp.orig dhewm3/neo/framework/Common.cpp.orig
---- dhewm3.old/neo/framework/Common.cpp.orig 1970-01-01 01:00:00.000000000 +0100
-+++ dhewm3/neo/framework/Common.cpp.orig 2013-08-22 13:23:32.999219558 +0200
-@@ -0,0 +1,3109 @@
-+/*
-+===========================================================================
-+
-+Doom 3 GPL Source Code
-+Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
-+
-+This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
-+
-+Doom 3 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 3 of the License, or
-+(at your option) any later version.
-+
-+Doom 3 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 Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
-+
-+In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
-+
-+If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
-+
-+===========================================================================
-+*/
-+
-+#include <SDL.h>
-+
-+#include "sys/platform.h"
-+#include "idlib/containers/HashTable.h"
-+#include "idlib/LangDict.h"
-+#include "idlib/MapFile.h"
-+#include "cm/CollisionModel.h"
-+#include "framework/async/AsyncNetwork.h"
-+#include "framework/async/NetworkSystem.h"
-+#include "framework/BuildVersion.h"
-+#include "framework/Licensee.h"
-+#include "framework/Console.h"
-+#include "framework/Session.h"
-+#include "framework/Game.h"
-+#include "framework/KeyInput.h"
-+#include "framework/EventLoop.h"
-+#include "renderer/Image.h"
-+#include "renderer/Model.h"
-+#include "renderer/ModelManager.h"
-+#include "renderer/RenderSystem.h"
-+#include "tools/compilers/compiler_public.h"
-+#include "tools/compilers/aas/AASFileManager.h"
-+
-+#include "framework/Common.h"
-+
-+#define MAX_PRINT_MSG_SIZE 4096
-+#define MAX_WARNING_LIST 256
-+
-+typedef enum {
-+ ERP_NONE,
-+ ERP_FATAL, // exit the entire game with a popup window
-+ ERP_DROP, // print to console and disconnect from game
-+ ERP_DISCONNECT // don't kill server
-+} errorParm_t;
-+
-+#if defined( _DEBUG )
-+ #define BUILD_DEBUG "-debug"
-+#else
-+ #define BUILD_DEBUG ""
-+#endif
-+
-+struct version_s {
-+ version_s( void ) { sprintf( string, "%s.%d%s %s-%s %s %s", ENGINE_VERSION, BUILD_NUMBER, BUILD_DEBUG, BUILD_OS, BUILD_CPU, __DATE__, __TIME__ ); }
-+ char string[256];
-+} version;
-+
-+idCVar com_version( "si_version", version.string, CVAR_SYSTEM|CVAR_ROM|CVAR_SERVERINFO, "engine version" );
-+idCVar com_skipRenderer( "com_skipRenderer", "0", CVAR_BOOL|CVAR_SYSTEM, "skip the renderer completely" );
-+idCVar com_machineSpec( "com_machineSpec", "-1", CVAR_INTEGER | CVAR_ARCHIVE | CVAR_SYSTEM, "hardware classification, -1 = not detected, 0 = low quality, 1 = medium quality, 2 = high quality, 3 = ultra quality" );
-+idCVar com_purgeAll( "com_purgeAll", "0", CVAR_BOOL | CVAR_ARCHIVE | CVAR_SYSTEM, "purge everything between level loads" );
-+idCVar com_memoryMarker( "com_memoryMarker", "-1", CVAR_INTEGER | CVAR_SYSTEM | CVAR_INIT, "used as a marker for memory stats" );
-+idCVar com_preciseTic( "com_preciseTic", "1", CVAR_BOOL|CVAR_SYSTEM, "run one game tick every async thread update" );
-+idCVar com_asyncInput( "com_asyncInput", "0", CVAR_BOOL|CVAR_SYSTEM, "sample input from the async thread" );
-+#define ASYNCSOUND_INFO "0: mix sound inline, 1: memory mapped async mix, 2: callback mixing, 3: write async mix"
-+#if defined( __unix__ ) && !defined( MACOS_X )
-+idCVar com_asyncSound( "com_asyncSound", "3", CVAR_INTEGER|CVAR_SYSTEM|CVAR_ROM, ASYNCSOUND_INFO );
-+#else
-+idCVar com_asyncSound( "com_asyncSound", "1", CVAR_INTEGER|CVAR_SYSTEM, ASYNCSOUND_INFO, 0, 1 );
-+#endif
-+idCVar com_forceGenericSIMD( "com_forceGenericSIMD", "0", CVAR_BOOL | CVAR_SYSTEM | CVAR_NOCHEAT, "force generic platform independent SIMD" );
-+idCVar com_developer( "developer", "0", CVAR_BOOL|CVAR_SYSTEM|CVAR_NOCHEAT, "developer mode" );
-+idCVar com_allowConsole( "com_allowConsole", "0", CVAR_BOOL | CVAR_SYSTEM | CVAR_NOCHEAT, "allow toggling console with the tilde key" );
-+idCVar com_speeds( "com_speeds", "0", CVAR_BOOL|CVAR_SYSTEM|CVAR_NOCHEAT, "show engine timings" );
-+idCVar com_showFPS( "com_showFPS", "0", CVAR_BOOL|CVAR_SYSTEM|CVAR_ARCHIVE|CVAR_NOCHEAT, "show frames rendered per second" );
-+idCVar com_showMemoryUsage( "com_showMemoryUsage", "0", CVAR_BOOL|CVAR_SYSTEM|CVAR_NOCHEAT, "show total and per frame memory usage" );
-+idCVar com_showAsyncStats( "com_showAsyncStats", "0", CVAR_BOOL|CVAR_SYSTEM|CVAR_NOCHEAT, "show async network stats" );
-+idCVar com_showSoundDecoders( "com_showSoundDecoders", "0", CVAR_BOOL|CVAR_SYSTEM|CVAR_NOCHEAT, "show sound decoders" );
-+idCVar com_timestampPrints( "com_timestampPrints", "0", CVAR_SYSTEM, "print time with each console print, 1 = msec, 2 = sec", 0, 2, idCmdSystem::ArgCompletion_Integer<0,2> );
-+idCVar com_timescale( "timescale", "1", CVAR_SYSTEM | CVAR_FLOAT, "scales the time", 0.1f, 10.0f );
-+idCVar com_makingBuild( "com_makingBuild", "0", CVAR_BOOL | CVAR_SYSTEM, "1 when making a build" );
-+idCVar com_updateLoadSize( "com_updateLoadSize", "0", CVAR_BOOL | CVAR_SYSTEM | CVAR_NOCHEAT, "update the load size after loading a map" );
-+
-+idCVar com_product_lang_ext( "com_product_lang_ext", "1", CVAR_INTEGER | CVAR_SYSTEM | CVAR_ARCHIVE, "Extension to use when creating language files." );
-+
-+// com_speeds times
-+int time_gameFrame;
-+int time_gameDraw;
-+int time_frontend; // renderSystem frontend time
-+int time_backend; // renderSystem backend time
-+
-+int com_frameTime; // time for the current frame in milliseconds
-+int com_frameNumber; // variable frame number
-+volatile int com_ticNumber; // 60 hz tics
-+int com_editors; // currently opened editor(s)
-+bool com_editorActive; // true if an editor has focus
-+
-+#ifdef _WIN32
-+HWND com_hwndMsg = NULL;
-+bool com_outputMsg = false;
-+unsigned int com_msgID = -1;
-+#endif
-+
-+#ifdef __DOOM_DLL__
-+idGame * game = NULL;
-+idGameEdit * gameEdit = NULL;
-+#endif
-+
-+// writes si_version to the config file - in a kinda obfuscated way
-+//#define ID_WRITE_VERSION
-+
-+class idCommonLocal : public idCommon {
-+public:
-+ idCommonLocal( void );
-+
-+ virtual void Init( int argc, char **argv );
-+ virtual void Shutdown( void );
-+ virtual void Quit( void );
-+ virtual bool IsInitialized( void ) const;
-+ virtual void Frame( void );
-+ virtual void GUIFrame( bool execCmd, bool network );
-+ virtual void Async( void );
-+ virtual void StartupVariable( const char *match, bool once );
-+ virtual void InitTool( const toolFlag_t tool, const idDict *dict );
-+ virtual void ActivateTool( bool active );
-+ virtual void WriteConfigToFile( const char *filename );
-+ virtual void WriteFlaggedCVarsToFile( const char *filename, int flags, const char *setCmd );
-+ virtual void BeginRedirect( char *buffer, int buffersize, void (*flush)( const char * ) );
-+ virtual void EndRedirect( void );
-+ virtual void SetRefreshOnPrint( bool set );
-+ virtual void Printf( const char *fmt, ... ) id_attribute((format(printf,2,3)));
-+ virtual void VPrintf( const char *fmt, va_list arg );
-+ virtual void DPrintf( const char *fmt, ... ) id_attribute((format(printf,2,3)));
-+ virtual void Warning( const char *fmt, ... ) id_attribute((format(printf,2,3)));
-+ virtual void DWarning( const char *fmt, ...) id_attribute((format(printf,2,3)));
-+ virtual void PrintWarnings( void );
-+ virtual void ClearWarnings( const char *reason );
-+ virtual void Error( const char *fmt, ... ) id_attribute((format(printf,2,3)));
-+ virtual void FatalError( const char *fmt, ... ) id_attribute((format(printf,2,3)));
-+ virtual const idLangDict * GetLanguageDict( void );
-+
-+ virtual const char * KeysFromBinding( const char *bind );
-+ virtual const char * BindingFromKey( const char *key );
-+
-+ virtual int ButtonState( int key );
-+ virtual int KeyState( int key );
-+
-+ void InitGame( void );
-+ void ShutdownGame( bool reloading );
-+
-+ // localization
-+ void InitLanguageDict( void );
-+ void LocalizeGui( const char *fileName, idLangDict &langDict );
-+ void LocalizeMapData( const char *fileName, idLangDict &langDict );
-+ void LocalizeSpecificMapData( const char *fileName, idLangDict &langDict, const idLangDict &replaceArgs );
-+
-+ void SetMachineSpec( void );
-+
-+private:
-+ void InitCommands( void );
-+ void InitRenderSystem( void );
-+ void InitSIMD( void );
-+ bool AddStartupCommands( void );
-+ void ParseCommandLine( int argc, char **argv );
-+ void ClearCommandLine( void );
-+ bool SafeMode( void );
-+ void CheckToolMode( void );
-+ void WriteConfiguration( void );
-+ void DumpWarnings( void );
-+ void SingleAsyncTic( void );
-+ void LoadGameDLL( void );
-+ void LoadGameDLLbyName( const char *dll, idStr& s );
-+ void UnloadGameDLL( void );
-+ void PrintLoadingMessage( const char *msg );
-+ void FilterLangList( idStrList* list, idStr lang );
-+
-+ bool com_fullyInitialized;
-+ bool com_refreshOnPrint; // update the screen every print for dmap
-+ int com_errorEntered; // 0, ERP_DROP, etc
-+
-+ char errorMessage[MAX_PRINT_MSG_SIZE];
-+
-+ char * rd_buffer;
-+ int rd_buffersize;
-+ void (*rd_flush)( const char *buffer );
-+
-+ idStr warningCaption;
-+ idStrList warningList;
-+ idStrList errorList;
-+
-+ uintptr_t gameDLL;
-+
-+ idLangDict languageDict;
-+
-+#ifdef ID_WRITE_VERSION
-+ idCompressor * config_compressor;
-+#endif
-+
-+ SDL_TimerID async_timer;
-+};
-+
-+idCommonLocal commonLocal;
-+idCommon * common = &commonLocal;
-+
-+/*
-+==================
-+idCommonLocal::idCommonLocal
-+==================
-+*/
-+idCommonLocal::idCommonLocal( void ) {
-+ com_fullyInitialized = false;
-+ com_refreshOnPrint = false;
-+ com_errorEntered = 0;
-+
-+ strcpy( errorMessage, "" );
-+
-+ rd_buffer = NULL;
-+ rd_buffersize = 0;
-+ rd_flush = NULL;
-+
-+ gameDLL = 0;
-+
-+#ifdef ID_WRITE_VERSION
-+ config_compressor = NULL;
-+#endif
-+
-+ async_timer = 0;
-+}
-+
-+/*
-+==================
-+idCommonLocal::BeginRedirect
-+==================
-+*/
-+void idCommonLocal::BeginRedirect( char *buffer, int buffersize, void (*flush)( const char *) ) {
-+ if ( !buffer || !buffersize || !flush ) {
-+ return;
-+ }
-+ rd_buffer = buffer;
-+ rd_buffersize = buffersize;
-+ rd_flush = flush;
-+
-+ *rd_buffer = 0;
-+}
-+
-+/*
-+==================
-+idCommonLocal::EndRedirect
-+==================
-+*/
-+void idCommonLocal::EndRedirect( void ) {
-+ if ( rd_flush && rd_buffer[ 0 ] ) {
-+ rd_flush( rd_buffer );
-+ }
-+
-+ rd_buffer = NULL;
-+ rd_buffersize = 0;
-+ rd_flush = NULL;
-+}
-+
-+#ifdef _WIN32
-+
-+/*
-+==================
-+EnumWindowsProc
-+==================
-+*/
-+BOOL CALLBACK EnumWindowsProc( HWND hwnd, LPARAM lParam ) {
-+ char buff[1024];
-+
-+ ::GetWindowText( hwnd, buff, sizeof( buff ) );
-+ if ( idStr::Icmpn( buff, EDITOR_WINDOWTEXT, strlen( EDITOR_WINDOWTEXT ) ) == 0 ) {
-+ com_hwndMsg = hwnd;
-+ return FALSE;
-+ }
-+ return TRUE;
-+}
-+
-+/*
-+==================
-+FindEditor
-+==================
-+*/
-+bool FindEditor( void ) {
-+ com_hwndMsg = NULL;
-+ EnumWindows( EnumWindowsProc, 0 );
-+ return !( com_hwndMsg == NULL );
-+}
-+
-+#endif
-+
-+/*
-+==================
-+idCommonLocal::SetRefreshOnPrint
-+==================
-+*/
-+void idCommonLocal::SetRefreshOnPrint( bool set ) {
-+ com_refreshOnPrint = set;
-+}
-+
-+/*
-+==================
-+idCommonLocal::VPrintf
-+
-+A raw string should NEVER be passed as fmt, because of "%f" type crashes.
-+==================
-+*/
-+void idCommonLocal::VPrintf( const char *fmt, va_list args ) {
-+ char msg[MAX_PRINT_MSG_SIZE];
-+ int timeLength;
-+
-+ // if the cvar system is not initialized
-+ if ( !cvarSystem->IsInitialized() ) {
-+ return;
-+ }
-+
-+ // optionally put a timestamp at the beginning of each print,
-+ // so we can see how long different init sections are taking
-+ if ( com_timestampPrints.GetInteger() ) {
-+ int t = Sys_Milliseconds();
-+ if ( com_timestampPrints.GetInteger() == 1 ) {
-+ t /= 1000;
-+ }
-+ sprintf( msg, "[%i]", t );
-+ timeLength = strlen( msg );
-+ } else {
-+ timeLength = 0;
-+ }
-+
-+ // don't overflow
-+ if ( idStr::vsnPrintf( msg+timeLength, MAX_PRINT_MSG_SIZE-timeLength-1, fmt, args ) < 0 ) {
-+ msg[sizeof(msg)-2] = '\n'; msg[sizeof(msg)-1] = '\0'; // avoid output garbling
-+ Sys_Printf( "idCommon::VPrintf: truncated to %zd characters\n", strlen(msg)-1 );
-+ }
-+
-+ if ( rd_buffer ) {
-+ if ( (int)( strlen( msg ) + strlen( rd_buffer ) ) > ( rd_buffersize - 1 ) ) {
-+ rd_flush( rd_buffer );
-+ *rd_buffer = 0;
-+ }
-+ strcat( rd_buffer, msg );
-+ return;
-+ }
-+
-+ // echo to console buffer
-+ console->Print( msg );
-+
-+ // remove any color codes
-+ idStr::RemoveColors( msg );
-+
-+ // echo to dedicated console and early console
-+ Sys_Printf( "%s", msg );
-+
-+ // print to script debugger server
-+ // DebuggerServerPrint( msg );
-+
-+#if 0 // !@#
-+#if defined(_DEBUG) && defined(WIN32)
-+ if ( strlen( msg ) < 512 ) {
-+ TRACE( msg );
-+ }
-+#endif
-+#endif
-+
-+ // don't trigger any updates if we are in the process of doing a fatal error
-+ if ( com_errorEntered != ERP_FATAL ) {
-+ // update the console if we are in a long-running command, like dmap
-+ if ( com_refreshOnPrint ) {
-+ session->UpdateScreen();
-+ }
-+
-+ // let session redraw the animated loading screen if necessary
-+ session->PacifierUpdate();
-+ }
-+
-+#ifdef _WIN32
-+
-+ if ( com_outputMsg ) {
-+ if ( com_msgID == -1 ) {
-+ com_msgID = ::RegisterWindowMessage( DMAP_MSGID );
-+ if ( !FindEditor() ) {
-+ com_outputMsg = false;
-+ } else {
-+ Sys_ShowWindow( false );
-+ }
-+ }
-+ if ( com_hwndMsg ) {
-+ ATOM atom = ::GlobalAddAtom( msg );
-+ ::PostMessage( com_hwndMsg, com_msgID, 0, static_cast<LPARAM>(atom) );
-+ }
-+ }
-+
-+#endif
-+}
-+
-+/*
-+==================
-+idCommonLocal::Printf
-+
-+Both client and server can use this, and it will output to the appropriate place.
-+
-+A raw string should NEVER be passed as fmt, because of "%f" type crashers.
-+==================
-+*/
-+void idCommonLocal::Printf( const char *fmt, ... ) {
-+ va_list argptr;
-+ va_start( argptr, fmt );
-+ VPrintf( fmt, argptr );
-+ va_end( argptr );
-+}
-+
-+/*
-+==================
-+idCommonLocal::DPrintf
-+
-+prints message that only shows up if the "developer" cvar is set
-+==================
-+*/
-+void idCommonLocal::DPrintf( const char *fmt, ... ) {
-+ va_list argptr;
-+ char msg[MAX_PRINT_MSG_SIZE];
-+
-+ if ( !cvarSystem->IsInitialized() || !com_developer.GetBool() ) {
-+ return; // don't confuse non-developers with techie stuff...
-+ }
-+
-+ va_start( argptr, fmt );
-+ idStr::vsnPrintf( msg, sizeof(msg), fmt, argptr );
-+ va_end( argptr );
-+ msg[sizeof(msg)-1] = '\0';
-+
-+ // never refresh the screen, which could cause reentrency problems
-+ bool temp = com_refreshOnPrint;
-+ com_refreshOnPrint = false;
-+
-+ Printf( S_COLOR_RED"%s", msg );
-+
-+ com_refreshOnPrint = temp;
-+}
-+
-+/*
-+==================
-+idCommonLocal::DWarning
-+
-+prints warning message in yellow that only shows up if the "developer" cvar is set
-+==================
-+*/
-+void idCommonLocal::DWarning( const char *fmt, ... ) {
-+ va_list argptr;
-+ char msg[MAX_PRINT_MSG_SIZE];
-+
-+ if ( !com_developer.GetBool() ) {
-+ return; // don't confuse non-developers with techie stuff...
-+ }
-+
-+ va_start( argptr, fmt );
-+ idStr::vsnPrintf( msg, sizeof(msg), fmt, argptr );
-+ va_end( argptr );
-+ msg[sizeof(msg)-1] = '\0';
-+
-+ Printf( S_COLOR_YELLOW"WARNING: %s\n", msg );
-+}
-+
-+/*
-+==================
-+idCommonLocal::Warning
-+
-+prints WARNING %s and adds the warning message to a queue to be printed later on
-+==================
-+*/
-+void idCommonLocal::Warning( const char *fmt, ... ) {
-+ va_list argptr;
-+ char msg[MAX_PRINT_MSG_SIZE];
-+
-+ va_start( argptr, fmt );
-+ idStr::vsnPrintf( msg, sizeof(msg), fmt, argptr );
-+ va_end( argptr );
-+ msg[sizeof(msg)-1] = 0;
-+
-+ Printf( S_COLOR_YELLOW "WARNING: " S_COLOR_RED "%s\n", msg );
-+
-+ if ( warningList.Num() < MAX_WARNING_LIST ) {
-+ warningList.AddUnique( msg );
-+ }
-+}
-+
-+/*
-+==================
-+idCommonLocal::PrintWarnings
-+==================
-+*/
-+void idCommonLocal::PrintWarnings( void ) {
-+ int i;
-+
-+ if ( !warningList.Num() ) {
-+ return;
-+ }
-+
-+ warningList.Sort();
-+
-+ Printf( "----- Warnings -----\n" );
-+ Printf( "during %s...\n", warningCaption.c_str() );
-+
-+ for ( i = 0; i < warningList.Num(); i++ ) {
-+ Printf( S_COLOR_YELLOW "WARNING: " S_COLOR_RED "%s\n", warningList[i].c_str() );
-+ }
-+ if ( warningList.Num() ) {
-+ if ( warningList.Num() >= MAX_WARNING_LIST ) {
-+ Printf( "more than %d warnings\n", MAX_WARNING_LIST );
-+ } else {
-+ Printf( "%d warnings\n", warningList.Num() );
-+ }
-+ }
-+}
-+
-+/*
-+==================
-+idCommonLocal::ClearWarnings
-+==================
-+*/
-+void idCommonLocal::ClearWarnings( const char *reason ) {
-+ warningCaption = reason;
-+ warningList.Clear();
-+}
-+
-+/*
-+==================
-+idCommonLocal::DumpWarnings
-+==================
-+*/
-+void idCommonLocal::DumpWarnings( void ) {
-+ int i;
-+ idFile *warningFile;
-+
-+ if ( !warningList.Num() ) {
-+ return;
-+ }
-+
-+ warningFile = fileSystem->OpenFileWrite( "warnings.txt", "fs_savepath" );
-+ if ( warningFile ) {
-+
-+ warningFile->Printf( "----- Warnings -----\n\n" );
-+ warningFile->Printf( "during %s...\n", warningCaption.c_str() );
-+ warningList.Sort();
-+ for ( i = 0; i < warningList.Num(); i++ ) {
-+ warningList[i].RemoveColors();
-+ warningFile->Printf( "WARNING: %s\n", warningList[i].c_str() );
-+ }
-+ if ( warningList.Num() >= MAX_WARNING_LIST ) {
-+ warningFile->Printf( "\nmore than %d warnings!\n", MAX_WARNING_LIST );
-+ } else {
-+ warningFile->Printf( "\n%d warnings.\n", warningList.Num() );
-+ }
-+
-+ warningFile->Printf( "\n\n----- Errors -----\n\n" );
-+ errorList.Sort();
-+ for ( i = 0; i < errorList.Num(); i++ ) {
-+ errorList[i].RemoveColors();
-+ warningFile->Printf( "ERROR: %s", errorList[i].c_str() );
-+ }
-+
-+ warningFile->ForceFlush();
-+
-+ fileSystem->CloseFile( warningFile );
-+
-+#if defined(_WIN32) && !defined(_DEBUG)
-+ idStr osPath;
-+ osPath = fileSystem->RelativePathToOSPath( "warnings.txt", "fs_savepath" );
-+ WinExec( va( "Notepad.exe %s", osPath.c_str() ), SW_SHOW );
-+#endif
-+ }
-+}
-+
-+/*
-+==================
-+idCommonLocal::Error
-+==================
-+*/
-+void idCommonLocal::Error( const char *fmt, ... ) {
-+ va_list argptr;
-+ static int lastErrorTime;
-+ static int errorCount;
-+ int currentTime;
-+
-+ int code = ERP_DROP;
-+
-+ // always turn this off after an error
-+ com_refreshOnPrint = false;
-+
-+ // when we are running automated scripts, make sure we
-+ // know if anything failed
-+ if ( cvarSystem->GetCVarInteger( "fs_copyfiles" ) ) {
-+ code = ERP_FATAL;
-+ }
-+
-+ // if we don't have GL running, make it a fatal error
-+ if ( !renderSystem->IsOpenGLRunning() ) {
-+ code = ERP_FATAL;
-+ }
-+
-+ // if we got a recursive error, make it fatal
-+ if ( com_errorEntered ) {
-+ // if we are recursively erroring while exiting
-+ // from a fatal error, just kill the entire
-+ // process immediately, which will prevent a
-+ // full screen rendering window covering the
-+ // error dialog
-+ if ( com_errorEntered == ERP_FATAL ) {
-+ Sys_Quit();
-+ }
-+ code = ERP_FATAL;
-+ }
-+
-+ // if we are getting a solid stream of ERP_DROP, do an ERP_FATAL
-+ currentTime = Sys_Milliseconds();
-+ if ( currentTime - lastErrorTime < 100 ) {
-+ if ( ++errorCount > 3 ) {
-+ code = ERP_FATAL;
-+ }
-+ } else {
-+ errorCount = 0;
-+ }
-+ lastErrorTime = currentTime;
-+
-+ com_errorEntered = code;
-+
-+ va_start (argptr,fmt);
-+ idStr::vsnPrintf( errorMessage, sizeof(errorMessage), fmt, argptr );
-+ va_end (argptr);
-+ errorMessage[sizeof(errorMessage)-1] = '\0';
-+
-+ // copy the error message to the clip board
-+ Sys_SetClipboardData( errorMessage );
-+
-+ // add the message to the error list
-+ errorList.AddUnique( errorMessage );
-+
-+ // Dont shut down the session for gui editor or debugger
-+ if ( !( com_editors & ( EDITOR_GUI | EDITOR_DEBUGGER ) ) ) {
-+ session->Stop();
-+ }
-+
-+ if ( code == ERP_DISCONNECT ) {
-+ com_errorEntered = 0;
-+ throw idException( errorMessage );
-+ // The gui editor doesnt want thing to com_error so it handles exceptions instead
-+ } else if( com_editors & ( EDITOR_GUI | EDITOR_DEBUGGER ) ) {
-+ com_errorEntered = 0;
-+ throw idException( errorMessage );
-+ } else if ( code == ERP_DROP ) {
-+ Printf( "********************\nERROR: %s\n********************\n", errorMessage );
-+ com_errorEntered = 0;
-+ throw idException( errorMessage );
-+ } else {
-+ Printf( "********************\nERROR: %s\n********************\n", errorMessage );
-+ }
-+
-+ if ( cvarSystem->GetCVarBool( "r_fullscreen" ) ) {
-+ cmdSystem->BufferCommandText( CMD_EXEC_NOW, "vid_restart partial windowed\n" );
-+ }
-+
-+ Shutdown();
-+
-+ Sys_Error( "%s", errorMessage );
-+}
-+
-+/*
-+==================
-+idCommonLocal::FatalError
-+
-+Dump out of the game to a system dialog
-+==================
-+*/
-+void idCommonLocal::FatalError( const char *fmt, ... ) {
-+ va_list argptr;
-+
-+ // if we got a recursive error, make it fatal
-+ if ( com_errorEntered ) {
-+ // if we are recursively erroring while exiting
-+ // from a fatal error, just kill the entire
-+ // process immediately, which will prevent a
-+ // full screen rendering window covering the
-+ // error dialog
-+
-+ Sys_Printf( "FATAL: recursed fatal error:\n%s\n", errorMessage );
-+
-+ va_start( argptr, fmt );
-+ idStr::vsnPrintf( errorMessage, sizeof(errorMessage), fmt, argptr );
-+ va_end( argptr );
-+ errorMessage[sizeof(errorMessage)-1] = '\0';
-+
-+ Sys_Printf( "%s\n", errorMessage );
-+
-+ // write the console to a log file?
-+ Sys_Quit();
-+ }
-+ com_errorEntered = ERP_FATAL;
-+
-+ va_start( argptr, fmt );
-+ idStr::vsnPrintf( errorMessage, sizeof(errorMessage), fmt, argptr );
-+ va_end( argptr );
-+ errorMessage[sizeof(errorMessage)-1] = '\0';
-+
-+ if ( cvarSystem->GetCVarBool( "r_fullscreen" ) ) {
-+ cmdSystem->BufferCommandText( CMD_EXEC_NOW, "vid_restart partial windowed\n" );
-+ }
-+
-+ Sys_Printf( "shutting down: %s\n", errorMessage );
-+
-+ Shutdown();
-+
-+ Sys_Error( "%s", errorMessage );
-+}
-+
-+/*
-+==================
-+idCommonLocal::Quit
-+==================
-+*/
-+void idCommonLocal::Quit( void ) {
-+
-+#ifdef ID_ALLOW_TOOLS
-+ if ( com_editors & EDITOR_RADIANT ) {
-+ RadiantInit();
-+ return;
-+ }
-+#endif
-+
-+ // don't try to shutdown if we are in a recursive error
-+ if ( !com_errorEntered ) {
-+ Shutdown();
-+ }
-+
-+ Sys_Quit();
-+}
-+
-+
-+/*
-+============================================================================
-+
-+COMMAND LINE FUNCTIONS
-+
-++ characters separate the commandLine string into multiple console
-+command lines.
-+
-+All of these are valid:
-+
-+doom +set test blah +map test
-+doom set test blah+map test
-+doom set test blah + map test
-+
-+============================================================================
-+*/
-+
-+#define MAX_CONSOLE_LINES 32
-+int com_numConsoleLines;
-+idCmdArgs com_consoleLines[MAX_CONSOLE_LINES];
-+
-+/*
-+==================
-+idCommonLocal::ParseCommandLine
-+==================
-+*/
-+void idCommonLocal::ParseCommandLine( int argc, char **argv ) {
-+ int i;
-+
-+ com_numConsoleLines = 0;
-+ // API says no program path
-+ for ( i = 0; i < argc; i++ ) {
-+ if ( argv[ i ][ 0 ] == '+' ) {
-+ com_numConsoleLines++;
-+ com_consoleLines[ com_numConsoleLines-1 ].AppendArg( argv[ i ] + 1 );
-+ } else {
-+ if ( !com_numConsoleLines ) {
-+ com_numConsoleLines++;
-+ }
-+ com_consoleLines[ com_numConsoleLines-1 ].AppendArg( argv[ i ] );
-+ }
-+ }
-+}
-+
-+/*
-+==================
-+idCommonLocal::ClearCommandLine
-+==================
-+*/
-+void idCommonLocal::ClearCommandLine( void ) {
-+ com_numConsoleLines = 0;
-+}
-+
-+/*
-+==================
-+idCommonLocal::SafeMode
-+
-+Check for "safe" on the command line, which will
-+skip loading of config file (DoomConfig.cfg)
-+==================
-+*/
-+bool idCommonLocal::SafeMode( void ) {
-+ int i;
-+
-+ for ( i = 0 ; i < com_numConsoleLines ; i++ ) {
-+ if ( !idStr::Icmp( com_consoleLines[ i ].Argv(0), "safe" )
-+ || !idStr::Icmp( com_consoleLines[ i ].Argv(0), "cvar_restart" ) ) {
-+ com_consoleLines[ i ].Clear();
-+ return true;
-+ }
-+ }
-+ return false;
-+}
-+
-+/*
-+==================
-+idCommonLocal::CheckToolMode
-+
-+Check for "renderbump", "dmap", or "editor" on the command line,
-+and force fullscreen off in those cases
-+==================
-+*/
-+void idCommonLocal::CheckToolMode( void ) {
-+ int i;
-+
-+ for ( i = 0 ; i < com_numConsoleLines ; i++ ) {
-+ if ( !idStr::Icmp( com_consoleLines[ i ].Argv(0), "guieditor" ) ) {
-+ com_editors |= EDITOR_GUI;
-+ }
-+ else if ( !idStr::Icmp( com_consoleLines[ i ].Argv(0), "debugger" ) ) {
-+ com_editors |= EDITOR_DEBUGGER;
-+ }
-+ else if ( !idStr::Icmp( com_consoleLines[ i ].Argv(0), "editor" ) ) {
-+ com_editors |= EDITOR_RADIANT;
-+ }
-+ // Nerve: Add support for the material editor
-+ else if ( !idStr::Icmp( com_consoleLines[ i ].Argv(0), "materialEditor" ) ) {
-+ com_editors |= EDITOR_MATERIAL;
-+ }
-+
-+ if ( !idStr::Icmp( com_consoleLines[ i ].Argv(0), "renderbump" )
-+ || !idStr::Icmp( com_consoleLines[ i ].Argv(0), "editor" )
-+ || !idStr::Icmp( com_consoleLines[ i ].Argv(0), "guieditor" )
-+ || !idStr::Icmp( com_consoleLines[ i ].Argv(0), "debugger" )
-+ || !idStr::Icmp( com_consoleLines[ i ].Argv(0), "dmap" )
-+ || !idStr::Icmp( com_consoleLines[ i ].Argv(0), "materialEditor" )
-+ ) {
-+ cvarSystem->SetCVarBool( "r_fullscreen", false );
-+ return;
-+ }
-+ }
-+}
-+
-+/*
-+==================
-+idCommonLocal::StartupVariable
-+
-+Searches for command line parameters that are set commands.
-+If match is not NULL, only that cvar will be looked for.
-+That is necessary because cddir and basedir need to be set
-+before the filesystem is started, but all other sets should
-+be after execing the config and default.
-+==================
-+*/
-+void idCommonLocal::StartupVariable( const char *match, bool once ) {
-+ int i;
-+ const char *s;
-+
-+ i = 0;
-+ while ( i < com_numConsoleLines ) {
-+ if ( strcmp( com_consoleLines[ i ].Argv( 0 ), "set" ) ) {
-+ i++;
-+ continue;
-+ }
-+
-+ s = com_consoleLines[ i ].Argv(1);
-+
-+ if ( !match || !idStr::Icmp( s, match ) ) {
-+ cvarSystem->SetCVarString( s, com_consoleLines[ i ].Argv( 2 ) );
-+ if ( once ) {
-+ // kill the line
-+ int j = i + 1;
-+ while ( j < com_numConsoleLines ) {
-+ com_consoleLines[ j - 1 ] = com_consoleLines[ j ];
-+ j++;
-+ }
-+ com_numConsoleLines--;
-+ continue;
-+ }
-+ }
-+ i++;
-+ }
-+}
-+
-+/*
-+==================
-+idCommonLocal::AddStartupCommands
-+
-+Adds command line parameters as script statements
-+Commands are separated by + signs
-+
-+Returns true if any late commands were added, which
-+will keep the demoloop from immediately starting
-+==================
-+*/
-+bool idCommonLocal::AddStartupCommands( void ) {
-+ int i;
-+ bool added;
-+
-+ added = false;
-+ // quote every token, so args with semicolons can work
-+ for ( i = 0; i < com_numConsoleLines; i++ ) {
-+ if ( !com_consoleLines[i].Argc() ) {
-+ continue;
-+ }
-+
-+ // set commands won't override menu startup
-+ if ( idStr::Icmpn( com_consoleLines[i].Argv(0), "set", 3 ) ) {
-+ added = true;
-+ }
-+ // directly as tokenized so nothing gets screwed
-+ cmdSystem->BufferCommandArgs( CMD_EXEC_APPEND, com_consoleLines[i] );
-+ }
-+
-+ return added;
-+}
-+
-+/*
-+=================
-+idCommonLocal::InitTool
-+=================
-+*/
-+void idCommonLocal::InitTool( const toolFlag_t tool, const idDict *dict ) {
-+#ifdef ID_ALLOW_TOOLS
-+ if ( tool & EDITOR_SOUND ) {
-+ SoundEditorInit( dict );
-+ } else if ( tool & EDITOR_LIGHT ) {
-+ LightEditorInit( dict );
-+ } else if ( tool & EDITOR_PARTICLE ) {
-+ ParticleEditorInit( dict );
-+ } else if ( tool & EDITOR_AF ) {
-+ AFEditorInit( dict );
-+ }
-+#endif
-+}
-+
-+/*
-+==================
-+idCommonLocal::ActivateTool
-+
-+Activates or Deactivates a tool
-+==================
-+*/
-+void idCommonLocal::ActivateTool( bool active ) {
-+ com_editorActive = active;
-+ Sys_GrabMouseCursor( !active );
-+}
-+
-+/*
-+==================
-+idCommonLocal::WriteFlaggedCVarsToFile
-+==================
-+*/
-+void idCommonLocal::WriteFlaggedCVarsToFile( const char *filename, int flags, const char *setCmd ) {
-+ idFile *f;
-+
-+ f = fileSystem->OpenFileWrite( filename, "fs_configpath" );
-+ if ( !f ) {
-+ Printf( "Couldn't write %s.\n", filename );
-+ return;
-+ }
-+ cvarSystem->WriteFlaggedVariables( flags, setCmd, f );
-+ fileSystem->CloseFile( f );
-+}
-+
-+/*
-+==================
-+idCommonLocal::WriteConfigToFile
-+==================
-+*/
-+void idCommonLocal::WriteConfigToFile( const char *filename ) {
-+ idFile *f;
-+#ifdef ID_WRITE_VERSION
-+ ID_TIME_T t;
-+ char *curtime;
-+ idStr runtag;
-+ idFile_Memory compressed( "compressed" );
-+ idBase64 out;
-+#endif
-+
-+ f = fileSystem->OpenFileWrite( filename, "fs_configpath" );
-+ if ( !f ) {
-+ Printf ("Couldn't write %s.\n", filename );
-+ return;
-+ }
-+
-+#ifdef ID_WRITE_VERSION
-+ assert( config_compressor );
-+ t = time( NULL );
-+ curtime = ctime( &t );
-+ sprintf( runtag, "%s - %s", cvarSystem->GetCVarString( "si_version" ), curtime );
-+ config_compressor->Init( &compressed, true, 8 );
-+ config_compressor->Write( runtag.c_str(), runtag.Length() );
-+ config_compressor->FinishCompress( );
-+ out.Encode( (const byte *)compressed.GetDataPtr(), compressed.Length() );
-+ f->Printf( "// %s\n", out.c_str() );
-+#endif
-+
-+ idKeyInput::WriteBindings( f );
-+ cvarSystem->WriteFlaggedVariables( CVAR_ARCHIVE, "seta", f );
-+ fileSystem->CloseFile( f );
-+}
-+
-+/*
-+===============
-+idCommonLocal::WriteConfiguration
-+
-+Writes key bindings and archived cvars to config file if modified
-+===============
-+*/
-+void idCommonLocal::WriteConfiguration( void ) {
-+ // if we are quiting without fully initializing, make sure
-+ // we don't write out anything
-+ if ( !com_fullyInitialized ) {
-+ return;
-+ }
-+
-+ if ( !( cvarSystem->GetModifiedFlags() & CVAR_ARCHIVE ) ) {
-+ return;
-+ }
-+ cvarSystem->ClearModifiedFlags( CVAR_ARCHIVE );
-+
-+ // disable printing out the "Writing to:" message
-+ bool developer = com_developer.GetBool();
-+ com_developer.SetBool( false );
-+
-+ WriteConfigToFile( CONFIG_FILE );
-+ session->WriteCDKey( );
-+
-+ // restore the developer cvar
-+ com_developer.SetBool( developer );
-+}
-+
-+/*
-+===============
-+KeysFromBinding()
-+Returns the key bound to the command
-+===============
-+*/
-+const char* idCommonLocal::KeysFromBinding( const char *bind ) {
-+ return idKeyInput::KeysFromBinding( bind );
-+}
-+
-+/*
-+===============
-+BindingFromKey()
-+Returns the binding bound to key
-+===============
-+*/
-+const char* idCommonLocal::BindingFromKey( const char *key ) {
-+ return idKeyInput::BindingFromKey( key );
-+}
-+
-+/*
-+===============
-+ButtonState()
-+Returns the state of the button
-+===============
-+*/
-+int idCommonLocal::ButtonState( int key ) {
-+ return usercmdGen->ButtonState(key);
-+}
-+
-+/*
-+===============
-+ButtonState()
-+Returns the state of the key
-+===============
-+*/
-+int idCommonLocal::KeyState( int key ) {
-+ return usercmdGen->KeyState(key);
-+}
-+
-+//============================================================================
-+
-+#ifdef ID_ALLOW_TOOLS
-+/*
-+==================
-+Com_Editor_f
-+
-+ we can start the editor dynamically, but we won't ever get back
-+==================
-+*/
-+static void Com_Editor_f( const idCmdArgs &args ) {
-+ RadiantInit();
-+}
-+
-+/*
-+=============
-+Com_ScriptDebugger_f
-+=============
-+*/
-+static void Com_ScriptDebugger_f( const idCmdArgs &args ) {
-+ // Make sure it wasnt on the command line
-+ if ( !( com_editors & EDITOR_DEBUGGER ) ) {
-+ common->Printf( "Script debugger is currently disabled\n" );
-+ // DebuggerClientLaunch();
-+ }
-+}
-+
-+/*
-+=============
-+Com_EditGUIs_f
-+=============
-+*/
-+static void Com_EditGUIs_f( const idCmdArgs &args ) {
-+ GUIEditorInit();
-+}
-+
-+/*
-+=============
-+Com_MaterialEditor_f
-+=============
-+*/
-+static void Com_MaterialEditor_f( const idCmdArgs &args ) {
-+ // Turn off sounds
-+ soundSystem->SetMute( true );
-+ MaterialEditorInit();
-+}
-+#endif // ID_ALLOW_TOOLS
-+
-+/*
-+============
-+idCmdSystemLocal::PrintMemInfo_f
-+
-+This prints out memory debugging data
-+============
-+*/
-+static void PrintMemInfo_f( const idCmdArgs &args ) {
-+ MemInfo_t mi;
-+
-+ memset( &mi, 0, sizeof( mi ) );
-+ mi.filebase = session->GetCurrentMapName();
-+
-+ renderSystem->PrintMemInfo( &mi ); // textures and models
-+ soundSystem->PrintMemInfo( &mi ); // sounds
-+
-+ common->Printf( " Used image memory: %s bytes\n", idStr::FormatNumber( mi.imageAssetsTotal ).c_str() );
-+ mi.assetTotals += mi.imageAssetsTotal;
-+
-+ common->Printf( " Used model memory: %s bytes\n", idStr::FormatNumber( mi.modelAssetsTotal ).c_str() );
-+ mi.assetTotals += mi.modelAssetsTotal;
-+
-+ common->Printf( " Used sound memory: %s bytes\n", idStr::FormatNumber( mi.soundAssetsTotal ).c_str() );
-+ mi.assetTotals += mi.soundAssetsTotal;
-+
-+ common->Printf( " Used asset memory: %s bytes\n", idStr::FormatNumber( mi.assetTotals ).c_str() );
-+
-+ // write overview file
-+ idFile *f;
-+
-+ f = fileSystem->OpenFileAppend( "maps/printmeminfo.txt" );
-+ if ( !f ) {
-+ return;
-+ }
-+
-+ f->Printf( "total(%s ) image(%s ) model(%s ) sound(%s ): %s\n", idStr::FormatNumber( mi.assetTotals ).c_str(), idStr::FormatNumber( mi.imageAssetsTotal ).c_str(),
-+ idStr::FormatNumber( mi.modelAssetsTotal ).c_str(), idStr::FormatNumber( mi.soundAssetsTotal ).c_str(), mi.filebase.c_str() );
-+
-+ fileSystem->CloseFile( f );
-+}
-+
-+#ifdef ID_ALLOW_TOOLS
-+/*
-+==================
-+Com_EditLights_f
-+==================
-+*/
-+static void Com_EditLights_f( const idCmdArgs &args ) {
-+ LightEditorInit( NULL );
-+ cvarSystem->SetCVarInteger( "g_editEntityMode", 1 );
-+}
-+
-+/*
-+==================
-+Com_EditSounds_f
-+==================
-+*/
-+static void Com_EditSounds_f( const idCmdArgs &args ) {
-+ SoundEditorInit( NULL );
-+ cvarSystem->SetCVarInteger( "g_editEntityMode", 2 );
-+}
-+
-+/*
-+==================
-+Com_EditDecls_f
-+==================
-+*/
-+static void Com_EditDecls_f( const idCmdArgs &args ) {
-+ DeclBrowserInit( NULL );
-+}
-+
-+/*
-+==================
-+Com_EditAFs_f
-+==================
-+*/
-+static void Com_EditAFs_f( const idCmdArgs &args ) {
-+ AFEditorInit( NULL );
-+}
-+
-+/*
-+==================
-+Com_EditParticles_f
-+==================
-+*/
-+static void Com_EditParticles_f( const idCmdArgs &args ) {
-+ ParticleEditorInit( NULL );
-+}
-+
-+/*
-+==================
-+Com_EditScripts_f
-+==================
-+*/
-+static void Com_EditScripts_f( const idCmdArgs &args ) {
-+ ScriptEditorInit( NULL );
-+}
-+
-+/*
-+==================
-+Com_EditPDAs_f
-+==================
-+*/
-+static void Com_EditPDAs_f( const idCmdArgs &args ) {
-+ PDAEditorInit( NULL );
-+}
-+#endif // ID_ALLOW_TOOLS
-+
-+/*
-+==================
-+Com_Error_f
-+
-+Just throw a fatal error to test error shutdown procedures.
-+==================
-+*/
-+static void Com_Error_f( const idCmdArgs &args ) {
-+ if ( !com_developer.GetBool() ) {
-+ commonLocal.Printf( "error may only be used in developer mode\n" );
-+ return;
-+ }
-+
-+ if ( args.Argc() > 1 ) {
-+ commonLocal.FatalError( "Testing fatal error" );
-+ } else {
-+ commonLocal.Error( "Testing drop error" );
-+ }
-+}
-+
-+/*
-+==================
-+Com_Freeze_f
-+
-+Just freeze in place for a given number of seconds to test error recovery.
-+==================
-+*/
-+static void Com_Freeze_f( const idCmdArgs &args ) {
-+ float s;
-+ int start, now;
-+
-+ if ( args.Argc() != 2 ) {
-+ commonLocal.Printf( "freeze <seconds>\n" );
-+ return;
-+ }
-+
-+ if ( !com_developer.GetBool() ) {
-+ commonLocal.Printf( "freeze may only be used in developer mode\n" );
-+ return;
-+ }
-+
-+ s = atof( args.Argv(1) );
-+
-+ start = eventLoop->Milliseconds();
-+
-+ while ( 1 ) {
-+ now = eventLoop->Milliseconds();
-+ if ( ( now - start ) * 0.001f > s ) {
-+ break;
-+ }
-+ }
-+}
-+
-+/*
-+=================
-+Com_Crash_f
-+
-+A way to force a bus error for development reasons
-+=================
-+*/
-+static void Com_Crash_f( const idCmdArgs &args ) {
-+ if ( !com_developer.GetBool() ) {
-+ commonLocal.Printf( "crash may only be used in developer mode\n" );
-+ return;
-+ }
-+
-+#ifdef __GNUC__
-+ __builtin_trap();
-+#else
-+ * ( int * ) 0 = 0x12345678;
-+#endif
-+}
-+
-+/*
-+=================
-+Com_Quit_f
-+=================
-+*/
-+static void Com_Quit_f( const idCmdArgs &args ) {
-+ commonLocal.Quit();
-+}
-+
-+/*
-+===============
-+Com_WriteConfig_f
-+
-+Write the config file to a specific name
-+===============
-+*/
-+void Com_WriteConfig_f( const idCmdArgs &args ) {
-+ idStr filename;
-+
-+ if ( args.Argc() != 2 ) {
-+ commonLocal.Printf( "Usage: writeconfig <filename>\n" );
-+ return;
-+ }
-+
-+ filename = args.Argv(1);
-+ filename.DefaultFileExtension( ".cfg" );
-+ commonLocal.Printf( "Writing %s.\n", filename.c_str() );
-+ commonLocal.WriteConfigToFile( filename );
-+}
-+
-+/*
-+=================
-+Com_SetMachineSpecs_f
-+=================
-+*/
-+void Com_SetMachineSpec_f( const idCmdArgs &args ) {
-+ commonLocal.SetMachineSpec();
-+}
-+
-+/*
-+=================
-+Com_ExecMachineSpecs_f
-+=================
-+*/
-+#ifdef MACOS_X
-+void OSX_GetVideoCard( int& outVendorId, int& outDeviceId );
-+bool OSX_GetCPUIdentification( int& cpuId, bool& oldArchitecture );
-+#endif
-+void Com_ExecMachineSpec_f( const idCmdArgs &args ) {
-+ if ( com_machineSpec.GetInteger() == 3 ) {
-+ cvarSystem->SetCVarInteger( "image_anisotropy", 1, CVAR_ARCHIVE );
-+ cvarSystem->SetCVarInteger( "image_lodbias", 0, CVAR_ARCHIVE );
-+ cvarSystem->SetCVarInteger( "image_forceDownSize", 0, CVAR_ARCHIVE );
-+ cvarSystem->SetCVarInteger( "image_roundDown", 1, CVAR_ARCHIVE );
-+ cvarSystem->SetCVarInteger( "image_preload", 1, CVAR_ARCHIVE );
-+ cvarSystem->SetCVarInteger( "image_useAllFormats", 1, CVAR_ARCHIVE );
-+ cvarSystem->SetCVarInteger( "image_downSizeSpecular", 0, CVAR_ARCHIVE );
-+ cvarSystem->SetCVarInteger( "image_downSizeBump", 0, CVAR_ARCHIVE );
-+ cvarSystem->SetCVarInteger( "image_downSizeSpecularLimit", 64, CVAR_ARCHIVE );
-+ cvarSystem->SetCVarInteger( "image_downSizeBumpLimit", 256, CVAR_ARCHIVE );
-+ cvarSystem->SetCVarInteger( "image_usePrecompressedTextures", 0, CVAR_ARCHIVE );
-+ cvarSystem->SetCVarInteger( "image_downsize", 0 , CVAR_ARCHIVE );
-+ cvarSystem->SetCVarString( "image_filter", "GL_LINEAR_MIPMAP_LINEAR", CVAR_ARCHIVE );
-+ cvarSystem->SetCVarInteger( "image_anisotropy", 8, CVAR_ARCHIVE );
-+ cvarSystem->SetCVarInteger( "image_useCompression", 0, CVAR_ARCHIVE );
-+ cvarSystem->SetCVarInteger( "image_ignoreHighQuality", 0, CVAR_ARCHIVE );
-+ cvarSystem->SetCVarInteger( "s_maxSoundsPerShader", 0, CVAR_ARCHIVE );
-+ cvarSystem->SetCVarInteger( "r_mode", 5, CVAR_ARCHIVE );
-+ cvarSystem->SetCVarInteger( "image_useNormalCompression", 0, CVAR_ARCHIVE );
-+ cvarSystem->SetCVarInteger( "r_multiSamples", 0, CVAR_ARCHIVE );
-+ } else if ( com_machineSpec.GetInteger() == 2 ) {
-+ cvarSystem->SetCVarString( "image_filter", "GL_LINEAR_MIPMAP_LINEAR", CVAR_ARCHIVE );
-+ cvarSystem->SetCVarInteger( "image_anisotropy", 1, CVAR_ARCHIVE );
-+ cvarSystem->SetCVarInteger( "image_lodbias", 0, CVAR_ARCHIVE );
-+ cvarSystem->SetCVarInteger( "image_forceDownSize", 0, CVAR_ARCHIVE );
-+ cvarSystem->SetCVarInteger( "image_roundDown", 1, CVAR_ARCHIVE );
-+ cvarSystem->SetCVarInteger( "image_preload", 1, CVAR_ARCHIVE );
-+ cvarSystem->SetCVarInteger( "image_useAllFormats", 1, CVAR_ARCHIVE );
-+ cvarSystem->SetCVarInteger( "image_downSizeSpecular", 0, CVAR_ARCHIVE );
-+ cvarSystem->SetCVarInteger( "image_downSizeBump", 0, CVAR_ARCHIVE );
-+ cvarSystem->SetCVarInteger( "image_downSizeSpecularLimit", 64, CVAR_ARCHIVE );
-+ cvarSystem->SetCVarInteger( "image_downSizeBumpLimit", 256, CVAR_ARCHIVE );
-+ cvarSystem->SetCVarInteger( "image_usePrecompressedTextures", 1, CVAR_ARCHIVE );
-+ cvarSystem->SetCVarInteger( "image_downsize", 0, CVAR_ARCHIVE );
-+ cvarSystem->SetCVarInteger( "image_anisotropy", 8, CVAR_ARCHIVE );
-+ cvarSystem->SetCVarInteger( "image_useCompression", 1, CVAR_ARCHIVE );
-+ cvarSystem->SetCVarInteger( "image_ignoreHighQuality", 0, CVAR_ARCHIVE );
-+ cvarSystem->SetCVarInteger( "s_maxSoundsPerShader", 0, CVAR_ARCHIVE );
-+ cvarSystem->SetCVarInteger( "image_useNormalCompression", 0, CVAR_ARCHIVE );
-+ cvarSystem->SetCVarInteger( "r_mode", 4, CVAR_ARCHIVE );
-+ cvarSystem->SetCVarInteger( "r_multiSamples", 0, CVAR_ARCHIVE );
-+ } else if ( com_machineSpec.GetInteger() == 1 ) {
-+ cvarSystem->SetCVarString( "image_filter", "GL_LINEAR_MIPMAP_LINEAR", CVAR_ARCHIVE );
-+ cvarSystem->SetCVarInteger( "image_anisotropy", 1, CVAR_ARCHIVE );
-+ cvarSystem->SetCVarInteger( "image_lodbias", 0, CVAR_ARCHIVE );
-+ cvarSystem->SetCVarInteger( "image_downSize", 0, CVAR_ARCHIVE );
-+ cvarSystem->SetCVarInteger( "image_forceDownSize", 0, CVAR_ARCHIVE );
-+ cvarSystem->SetCVarInteger( "image_roundDown", 1, CVAR_ARCHIVE );
-+ cvarSystem->SetCVarInteger( "image_preload", 1, CVAR_ARCHIVE );
-+ cvarSystem->SetCVarInteger( "image_useCompression", 1, CVAR_ARCHIVE );
-+ cvarSystem->SetCVarInteger( "image_useAllFormats", 1, CVAR_ARCHIVE );
-+ cvarSystem->SetCVarInteger( "image_usePrecompressedTextures", 1, CVAR_ARCHIVE );
-+ cvarSystem->SetCVarInteger( "image_downSizeSpecular", 0, CVAR_ARCHIVE );
-+ cvarSystem->SetCVarInteger( "image_downSizeBump", 0, CVAR_ARCHIVE );
-+ cvarSystem->SetCVarInteger( "image_downSizeSpecularLimit", 64, CVAR_ARCHIVE );
-+ cvarSystem->SetCVarInteger( "image_downSizeBumpLimit", 256, CVAR_ARCHIVE );
-+ cvarSystem->SetCVarInteger( "image_useNormalCompression", 2, CVAR_ARCHIVE );
-+ cvarSystem->SetCVarInteger( "r_mode", 3, CVAR_ARCHIVE );
-+ cvarSystem->SetCVarInteger( "r_multiSamples", 0, CVAR_ARCHIVE );
-+ } else {
-+ cvarSystem->SetCVarString( "image_filter", "GL_LINEAR_MIPMAP_LINEAR", CVAR_ARCHIVE );
-+ cvarSystem->SetCVarInteger( "image_anisotropy", 1, CVAR_ARCHIVE );
-+ cvarSystem->SetCVarInteger( "image_lodbias", 0, CVAR_ARCHIVE );
-+ cvarSystem->SetCVarInteger( "image_roundDown", 1, CVAR_ARCHIVE );
-+ cvarSystem->SetCVarInteger( "image_preload", 1, CVAR_ARCHIVE );
-+ cvarSystem->SetCVarInteger( "image_useAllFormats", 1, CVAR_ARCHIVE );
-+ cvarSystem->SetCVarInteger( "image_usePrecompressedTextures", 1, CVAR_ARCHIVE );
-+ cvarSystem->SetCVarInteger( "image_downSize", 1, CVAR_ARCHIVE );
-+ cvarSystem->SetCVarInteger( "image_anisotropy", 0, CVAR_ARCHIVE );
-+ cvarSystem->SetCVarInteger( "image_useCompression", 1, CVAR_ARCHIVE );
-+ cvarSystem->SetCVarInteger( "image_ignoreHighQuality", 1, CVAR_ARCHIVE );
-+ cvarSystem->SetCVarInteger( "s_maxSoundsPerShader", 1, CVAR_ARCHIVE );
-+ cvarSystem->SetCVarInteger( "image_downSizeSpecular", 1, CVAR_ARCHIVE );
-+ cvarSystem->SetCVarInteger( "image_downSizeBump", 1, CVAR_ARCHIVE );
-+ cvarSystem->SetCVarInteger( "image_downSizeSpecularLimit", 64, CVAR_ARCHIVE );
-+ cvarSystem->SetCVarInteger( "image_downSizeBumpLimit", 256, CVAR_ARCHIVE );
-+ cvarSystem->SetCVarInteger( "r_mode", 3 , CVAR_ARCHIVE );
-+ cvarSystem->SetCVarInteger( "image_useNormalCompression", 2, CVAR_ARCHIVE );
-+ cvarSystem->SetCVarInteger( "r_multiSamples", 0, CVAR_ARCHIVE );
-+ }
-+
-+ cvarSystem->SetCVarBool( "com_purgeAll", false, CVAR_ARCHIVE );
-+ cvarSystem->SetCVarBool( "r_forceLoadImages", false, CVAR_ARCHIVE );
-+
-+ cvarSystem->SetCVarBool( "g_decals", true, CVAR_ARCHIVE );
-+ cvarSystem->SetCVarBool( "g_projectileLights", true, CVAR_ARCHIVE );
-+ cvarSystem->SetCVarBool( "g_doubleVision", true, CVAR_ARCHIVE );
-+ cvarSystem->SetCVarBool( "g_muzzleFlash", true, CVAR_ARCHIVE );
-+
-+#if MACOS_X
-+ // On low settings, G4 systems & 64MB FX5200/NV34 Systems should default shadows off
-+ bool oldArch;
-+ int vendorId, deviceId, cpuId;
-+ OSX_GetVideoCard( vendorId, deviceId );
-+ OSX_GetCPUIdentification( cpuId, oldArch );
-+ bool isFX5200 = vendorId == 0x10DE && ( deviceId & 0x0FF0 ) == 0x0320;
-+ if ( oldArch && ( com_machineSpec.GetInteger() == 0 ) ) {
-+ cvarSystem->SetCVarBool( "r_shadows", false, CVAR_ARCHIVE );
-+ } else {
-+ cvarSystem->SetCVarBool( "r_shadows", true, CVAR_ARCHIVE );
-+ }
-+#endif
-+}
-+
-+/*
-+=================
-+Com_ReloadEngine_f
-+=================
-+*/
-+void Com_ReloadEngine_f( const idCmdArgs &args ) {
-+ bool menu = false;
-+
-+ if ( !commonLocal.IsInitialized() ) {
-+ return;
-+ }
-+
-+ if ( args.Argc() > 1 && idStr::Icmp( args.Argv( 1 ), "menu" ) == 0 ) {
-+ menu = true;
-+ }
-+
-+ common->Printf( "============= ReloadEngine start =============\n" );
-+ if ( !menu ) {
-+ Sys_ShowConsole( 1, false );
-+ }
-+ commonLocal.ShutdownGame( true );
-+ commonLocal.InitGame();
-+ if ( !menu && !idAsyncNetwork::serverDedicated.GetBool() ) {
-+ Sys_ShowConsole( 0, false );
-+ }
-+ common->Printf( "============= ReloadEngine end ===============\n" );
-+
-+ if ( !cmdSystem->PostReloadEngine() ) {
-+ if ( menu ) {
-+ session->StartMenu( );
-+ }
-+ }
-+}
-+
-+/*
-+===============
-+idCommonLocal::GetLanguageDict
-+===============
-+*/
-+const idLangDict *idCommonLocal::GetLanguageDict( void ) {
-+ return &languageDict;
-+}
-+
-+/*
-+===============
-+idCommonLocal::FilterLangList
-+===============
-+*/
-+void idCommonLocal::FilterLangList( idStrList* list, idStr lang ) {
-+
-+ idStr temp;
-+ for( int i = 0; i < list->Num(); i++ ) {
-+ temp = (*list)[i];
-+ temp = temp.Right(temp.Length()-strlen("strings/"));
-+ temp = temp.Left(lang.Length());
-+ if(idStr::Icmp(temp, lang) != 0) {
-+ list->RemoveIndex(i);
-+ i--;
-+ }
-+ }
-+}
-+
-+/*
-+===============
-+idCommonLocal::InitLanguageDict
-+===============
-+*/
-+void idCommonLocal::InitLanguageDict( void ) {
-+ idStr fileName;
-+ languageDict.Clear();
-+
-+ //D3XP: Instead of just loading a single lang file for each language
-+ //we are going to load all files that begin with the language name
-+ //similar to the way pak files work. So you can place english001.lang
-+ //to add new strings to the english language dictionary
-+ idFileList* langFiles;
-+ langFiles = fileSystem->ListFilesTree( "strings", ".lang", true );
-+
-+ idStrList langList = langFiles->GetList();
-+
-+ StartupVariable( "sys_lang", false ); // let it be set on the command line - this is needed because this init happens very early
-+ idStr langName = cvarSystem->GetCVarString( "sys_lang" );
-+
-+ //Loop through the list and filter
-+ idStrList currentLangList = langList;
-+ FilterLangList(¤tLangList, langName);
-+
-+ if ( currentLangList.Num() == 0 ) {
-+ // reset cvar to default and try to load again
-+ cmdSystem->BufferCommandText( CMD_EXEC_NOW, "reset sys_lang" );
-+ langName = cvarSystem->GetCVarString( "sys_lang" );
-+ currentLangList = langList;
-+ FilterLangList(¤tLangList, langName);
-+ }
-+
-+ for( int i = 0; i < currentLangList.Num(); i++ ) {
-+ //common->Printf("%s\n", currentLangList[i].c_str());
-+ languageDict.Load( currentLangList[i], false );
-+ }
-+
-+ fileSystem->FreeFileList(langFiles);
-+
-+ Sys_InitScanTable();
-+}
-+
-+/*
-+===============
-+idCommonLocal::LocalizeSpecificMapData
-+===============
-+*/
-+void idCommonLocal::LocalizeSpecificMapData( const char *fileName, idLangDict &langDict, const idLangDict &replaceArgs ) {
-+ idStr out, ws, work;
-+
-+ idMapFile map;
-+ if ( map.Parse( fileName, false, false ) ) {
-+ int count = map.GetNumEntities();
-+ for ( int i = 0; i < count; i++ ) {
-+ idMapEntity *ent = map.GetEntity( i );
-+ if ( ent ) {
-+ for ( int j = 0; j < replaceArgs.GetNumKeyVals(); j++ ) {
-+ const idLangKeyValue *kv = replaceArgs.GetKeyVal( j );
-+ const char *temp = ent->epairs.GetString( kv->key );
-+ if ( temp && *temp ) {
-+ idStr val = kv->value;
-+ if ( val == temp ) {
-+ ent->epairs.Set( kv->key, langDict.AddString( temp ) );
-+ }
-+ }
-+ }
-+ }
-+ }
-+ map.Write( fileName, ".map" );
-+ }
-+}
-+
-+/*
-+===============
-+idCommonLocal::LocalizeMapData
-+===============
-+*/
-+void idCommonLocal::LocalizeMapData( const char *fileName, idLangDict &langDict ) {
-+ const char *buffer = NULL;
-+ idLexer src( LEXFL_NOFATALERRORS | LEXFL_NOSTRINGCONCAT | LEXFL_ALLOWMULTICHARLITERALS | LEXFL_ALLOWBACKSLASHSTRINGCONCAT );
-+
-+ common->SetRefreshOnPrint( true );
-+
-+ if ( fileSystem->ReadFile( fileName, (void**)&buffer ) > 0 ) {
-+ src.LoadMemory( buffer, strlen(buffer), fileName );
-+ if ( src.IsLoaded() ) {
-+ common->Printf( "Processing %s\n", fileName );
-+ idStr mapFileName;
-+ idToken token, token2;
-+ idLangDict replaceArgs;
-+ while ( src.ReadToken( &token ) ) {
-+ mapFileName = token;
-+ replaceArgs.Clear();
-+ src.ExpectTokenString( "{" );
-+ while ( src.ReadToken( &token) ) {
-+ if ( token == "}" ) {
-+ break;
-+ }
-+ if ( src.ReadToken( &token2 ) ) {
-+ if ( token2 == "}" ) {
-+ break;
-+ }
-+ replaceArgs.AddKeyVal( token, token2 );
-+ }
-+ }
-+ common->Printf( " localizing map %s...\n", mapFileName.c_str() );
-+ LocalizeSpecificMapData( mapFileName, langDict, replaceArgs );
-+ }
-+ }
-+ fileSystem->FreeFile( (void*)buffer );
-+ }
-+
-+ common->SetRefreshOnPrint( false );
-+}
-+
-+/*
-+===============
-+idCommonLocal::LocalizeGui
-+===============
-+*/
-+void idCommonLocal::LocalizeGui( const char *fileName, idLangDict &langDict ) {
-+ idStr out, ws, work;
-+ const char *buffer = NULL;
-+ out.Empty();
-+ int k;
-+ char ch;
-+ char slash = '\\';
-+ char tab = 't';
-+ char nl = 'n';
-+ idLexer src( LEXFL_NOFATALERRORS | LEXFL_NOSTRINGCONCAT | LEXFL_ALLOWMULTICHARLITERALS | LEXFL_ALLOWBACKSLASHSTRINGCONCAT );
-+ if ( fileSystem->ReadFile( fileName, (void**)&buffer ) > 0 ) {
-+ src.LoadMemory( buffer, strlen(buffer), fileName );
-+ if ( src.IsLoaded() ) {
-+ idFile *outFile = fileSystem->OpenFileWrite( fileName );
-+ common->Printf( "Processing %s\n", fileName );
-+ session->UpdateScreen();
-+ idToken token;
-+ while( src.ReadToken( &token ) ) {
-+ src.GetLastWhiteSpace( ws );
-+ out += ws;
-+ if ( token.type == TT_STRING ) {
-+ out += va( "\"%s\"", token.c_str() );
-+ } else {
-+ out += token;
-+ }
-+ if ( out.Length() > 200000 ) {
-+ outFile->Write( out.c_str(), out.Length() );
-+ out = "";
-+ }
-+ work = token.Right( 6 );
-+ if ( token.Icmp( "text" ) == 0 || work.Icmp( "::text" ) == 0 || token.Icmp( "choices" ) == 0 ) {
-+ if ( src.ReadToken( &token ) ) {
-+ // see if already exists, if so save that id to this position in this file
-+ // otherwise add this to the list and save the id to this position in this file
-+ src.GetLastWhiteSpace( ws );
-+ out += ws;
-+ token = langDict.AddString( token );
-+ out += "\"";
-+ for ( k = 0; k < token.Length(); k++ ) {
-+ ch = token[k];
-+ if ( ch == '\t' ) {
-+ out += slash;
-+ out += tab;
-+ } else if ( ch == '\n' || ch == '\r' ) {
-+ out += slash;
-+ out += nl;
-+ } else {
-+ out += ch;
-+ }
-+ }
-+ out += "\"";
-+ }
-+ } else if ( token.Icmp( "comment" ) == 0 ) {
-+ if ( src.ReadToken( &token ) ) {
-+ // need to write these out by hand to preserve any \n's
-+ // see if already exists, if so save that id to this position in this file
-+ // otherwise add this to the list and save the id to this position in this file
-+ src.GetLastWhiteSpace( ws );
-+ out += ws;
-+ out += "\"";
-+ for ( k = 0; k < token.Length(); k++ ) {
-+ ch = token[k];
-+ if ( ch == '\t' ) {
-+ out += slash;
-+ out += tab;
-+ } else if ( ch == '\n' || ch == '\r' ) {
-+ out += slash;
-+ out += nl;
-+ } else {
-+ out += ch;
-+ }
-+ }
-+ out += "\"";
-+ }
-+ }
-+ }
-+ outFile->Write( out.c_str(), out.Length() );
-+ fileSystem->CloseFile( outFile );
-+ }
-+ fileSystem->FreeFile( (void*)buffer );
-+ }
-+}
-+
-+/*
-+=================
-+ReloadLanguage_f
-+=================
-+*/
-+void Com_ReloadLanguage_f( const idCmdArgs &args ) {
-+ commonLocal.InitLanguageDict();
-+}
-+
-+typedef idHashTable<idStrList> ListHash;
-+void LoadMapLocalizeData(ListHash& listHash) {
-+
-+ idStr fileName = "map_localize.cfg";
-+ const char *buffer = NULL;
-+ idLexer src( LEXFL_NOFATALERRORS | LEXFL_NOSTRINGCONCAT | LEXFL_ALLOWMULTICHARLITERALS | LEXFL_ALLOWBACKSLASHSTRINGCONCAT );
-+
-+ if ( fileSystem->ReadFile( fileName, (void**)&buffer ) > 0 ) {
-+ src.LoadMemory( buffer, strlen(buffer), fileName );
-+ if ( src.IsLoaded() ) {
-+ idStr classname;
-+ idToken token;
-+
-+
-+
-+ while ( src.ReadToken( &token ) ) {
-+ classname = token;
-+ src.ExpectTokenString( "{" );
-+
-+ idStrList list;
-+ while ( src.ReadToken( &token) ) {
-+ if ( token == "}" ) {
-+ break;
-+ }
-+ list.Append(token);
-+ }
-+
-+ listHash.Set(classname, list);
-+ }
-+ }
-+ fileSystem->FreeFile( (void*)buffer );
-+ }
-+
-+}
-+
-+void LoadGuiParmExcludeList(idStrList& list) {
-+
-+ idStr fileName = "guiparm_exclude.cfg";
-+ const char *buffer = NULL;
-+ idLexer src( LEXFL_NOFATALERRORS | LEXFL_NOSTRINGCONCAT | LEXFL_ALLOWMULTICHARLITERALS | LEXFL_ALLOWBACKSLASHSTRINGCONCAT );
-+
-+ if ( fileSystem->ReadFile( fileName, (void**)&buffer ) > 0 ) {
-+ src.LoadMemory( buffer, strlen(buffer), fileName );
-+ if ( src.IsLoaded() ) {
-+ idStr classname;
-+ idToken token;
-+
-+
-+
-+ while ( src.ReadToken( &token ) ) {
-+ list.Append(token);
-+ }
-+ }
-+ fileSystem->FreeFile( (void*)buffer );
-+ }
-+}
-+
-+bool TestMapVal(idStr& str) {
-+ //Already Localized?
-+ if(str.Find("#str_") != -1) {
-+ return false;
-+ }
-+
-+ return true;
-+}
-+
-+bool TestGuiParm(const char* parm, const char* value, idStrList& excludeList) {
-+
-+ idStr testVal = value;
-+
-+ //Already Localized?
-+ if(testVal.Find("#str_") != -1) {
-+ return false;
-+ }
-+
-+ //Numeric
-+ if(testVal.IsNumeric()) {
-+ return false;
-+ }
-+
-+ //Contains ::
-+ if(testVal.Find("::") != -1) {
-+ return false;
-+ }
-+
-+ //Contains /
-+ if(testVal.Find("/") != -1) {
-+ return false;
-+ }
-+
-+ if(excludeList.Find(testVal)) {
-+ return false;
-+ }
-+
-+ return true;
-+}
-+
-+void GetFileList(const char* dir, const char* ext, idStrList& list) {
-+
-+ //Recurse Subdirectories
-+ idStrList dirList;
-+ Sys_ListFiles(dir, "/", dirList);
-+ for(int i = 0; i < dirList.Num(); i++) {
-+ if(dirList[i] == "." || dirList[i] == "..") {
-+ continue;
-+ }
-+ idStr fullName = va("%s/%s", dir, dirList[i].c_str());
-+ GetFileList(fullName, ext, list);
-+ }
-+
-+ idStrList fileList;
-+ Sys_ListFiles(dir, ext, fileList);
-+ for(int i = 0; i < fileList.Num(); i++) {
-+ idStr fullName = va("%s/%s", dir, fileList[i].c_str());
-+ list.Append(fullName);
-+ }
-+}
-+
-+int LocalizeMap(const char* mapName, idLangDict &langDict, ListHash& listHash, idStrList& excludeList, bool writeFile) {
-+
-+ common->Printf("Localizing Map '%s'\n", mapName);
-+
-+ int strCount = 0;
-+
-+ idMapFile map;
-+ if ( map.Parse(mapName, false, false ) ) {
-+ int count = map.GetNumEntities();
-+ for ( int j = 0; j < count; j++ ) {
-+ idMapEntity *ent = map.GetEntity( j );
-+ if ( ent ) {
-+
-+ idStr classname = ent->epairs.GetString("classname");
-+
-+ //Hack: for info_location
-+ bool hasLocation = false;
-+
-+ idStrList* list;
-+ listHash.Get(classname, &list);
-+ if(list) {
-+
-+ for(int k = 0; k < list->Num(); k++) {
-+
-+ idStr val = ent->epairs.GetString((*list)[k], "");
-+
-+ if(val.Length() && classname == "info_location" && (*list)[k] == "location") {
-+ hasLocation = true;
-+ }
-+
-+ if(val.Length() && TestMapVal(val)) {
-+
-+ if(!hasLocation || (*list)[k] == "location") {
-+ //Localize it!!!
-+ strCount++;
-+ ent->epairs.Set( (*list)[k], langDict.AddString( val ) );
-+ }
-+ }
-+ }
-+ }
-+
-+ listHash.Get("all", &list);
-+ if(list) {
-+ for(int k = 0; k < list->Num(); k++) {
-+ idStr val = ent->epairs.GetString((*list)[k], "");
-+ if(val.Length() && TestMapVal(val)) {
-+ //Localize it!!!
-+ strCount++;
-+ ent->epairs.Set( (*list)[k], langDict.AddString( val ) );
-+ }
-+ }
-+ }
-+
-+ //Localize the gui_parms
-+ const idKeyValue* kv = ent->epairs.MatchPrefix("gui_parm");
-+ while( kv ) {
-+ if(TestGuiParm(kv->GetKey(), kv->GetValue(), excludeList)) {
-+ //Localize It!
-+ strCount++;
-+ ent->epairs.Set( kv->GetKey(), langDict.AddString( kv->GetValue() ) );
-+ }
-+ kv = ent->epairs.MatchPrefix( "gui_parm", kv );
-+ }
-+ }
-+ }
-+ if(writeFile && strCount > 0) {
-+ //Before we write the map file lets make a backup of the original
-+ idStr file = fileSystem->RelativePathToOSPath(mapName);
-+ idStr bak = file.Left(file.Length() - 4);
-+ bak.Append(".bak_loc");
-+ fileSystem->CopyFile( file, bak );
-+
-+ map.Write( mapName, ".map" );
-+ }
-+ }
-+
-+ common->Printf("Count: %d\n", strCount);
-+ return strCount;
-+}
-+
-+/*
-+=================
-+LocalizeMaps_f
-+=================
-+*/
-+void Com_LocalizeMaps_f( const idCmdArgs &args ) {
-+ if ( args.Argc() < 2 ) {
-+ common->Printf( "Usage: localizeMaps <count | dictupdate | all> <map>\n" );
-+ return;
-+ }
-+
-+ int strCount = 0;
-+
-+ bool count = false;
-+ bool dictUpdate = false;
-+ bool write = false;
-+
-+ if ( idStr::Icmp( args.Argv(1), "count" ) == 0 ) {
-+ count = true;
-+ } else if ( idStr::Icmp( args.Argv(1), "dictupdate" ) == 0 ) {
-+ count = true;
-+ dictUpdate = true;
-+ } else if ( idStr::Icmp( args.Argv(1), "all" ) == 0 ) {
-+ count = true;
-+ dictUpdate = true;
-+ write = true;
-+ } else {
-+ common->Printf( "Invalid Command\n" );
-+ common->Printf( "Usage: localizeMaps <count | dictupdate | all>\n" );
-+ return;
-+
-+ }
-+
-+ idLangDict strTable;
-+ idStr filename = va("strings/english%.3i.lang", com_product_lang_ext.GetInteger());
-+ if(strTable.Load( filename ) == false) {
-+ //This is a new file so set the base index
-+ strTable.SetBaseID(com_product_lang_ext.GetInteger()*100000);
-+ }
-+
-+ common->SetRefreshOnPrint( true );
-+
-+ ListHash listHash;
-+ LoadMapLocalizeData(listHash);
-+
-+ idStrList excludeList;
-+ LoadGuiParmExcludeList(excludeList);
-+
-+ if(args.Argc() == 3) {
-+ strCount += LocalizeMap(args.Argv(2), strTable, listHash, excludeList, write);
-+ } else {
-+ idStrList files;
-+ GetFileList("z:/d3xp/d3xp/maps/game", "*.map", files);
-+ for ( int i = 0; i < files.Num(); i++ ) {
-+ idStr file = fileSystem->OSPathToRelativePath(files[i]);
-+ strCount += LocalizeMap(file, strTable, listHash, excludeList, write);
-+ }
-+ }
-+
-+ if(count) {
-+ common->Printf("Localize String Count: %d\n", strCount);
-+ }
-+
-+ common->SetRefreshOnPrint( false );
-+
-+ if(dictUpdate) {
-+ strTable.Save( filename );
-+ }
-+}
-+
-+/*
-+=================
-+LocalizeGuis_f
-+=================
-+*/
-+void Com_LocalizeGuis_f( const idCmdArgs &args ) {
-+
-+ if ( args.Argc() != 2 ) {
-+ common->Printf( "Usage: localizeGuis <all | gui>\n" );
-+ return;
-+ }
-+
-+ idLangDict strTable;
-+
-+ idStr filename = va("strings/english%.3i.lang", com_product_lang_ext.GetInteger());
-+ if(strTable.Load( filename ) == false) {
-+ //This is a new file so set the base index
-+ strTable.SetBaseID(com_product_lang_ext.GetInteger()*100000);
-+ }
-+
-+ idFileList *files;
-+ if ( idStr::Icmp( args.Argv(1), "all" ) == 0 ) {
-+ idStr game = cvarSystem->GetCVarString( "fs_game" );
-+ if(game.Length()) {
-+ files = fileSystem->ListFilesTree( "guis", "*.gui", true, game );
-+ } else {
-+ files = fileSystem->ListFilesTree( "guis", "*.gui", true );
-+ }
-+ for ( int i = 0; i < files->GetNumFiles(); i++ ) {
-+ commonLocal.LocalizeGui( files->GetFile( i ), strTable );
-+ }
-+ fileSystem->FreeFileList( files );
-+
-+ if(game.Length()) {
-+ files = fileSystem->ListFilesTree( "guis", "*.pd", true, game );
-+ } else {
-+ files = fileSystem->ListFilesTree( "guis", "*.pd", true, "d3xp" );
-+ }
-+
-+ for ( int i = 0; i < files->GetNumFiles(); i++ ) {
-+ commonLocal.LocalizeGui( files->GetFile( i ), strTable );
-+ }
-+ fileSystem->FreeFileList( files );
-+
-+ } else {
-+ commonLocal.LocalizeGui( args.Argv(1), strTable );
-+ }
-+ strTable.Save( filename );
-+}
-+
-+void Com_LocalizeGuiParmsTest_f( const idCmdArgs &args ) {
-+
-+ common->SetRefreshOnPrint( true );
-+
-+ idFile *localizeFile = fileSystem->OpenFileWrite( "gui_parm_localize.csv" );
-+ idFile *noLocalizeFile = fileSystem->OpenFileWrite( "gui_parm_nolocalize.csv" );
-+
-+ idStrList excludeList;
-+ LoadGuiParmExcludeList(excludeList);
-+
-+ idStrList files;
-+ GetFileList("z:/d3xp/d3xp/maps/game", "*.map", files);
-+
-+ for ( int i = 0; i < files.Num(); i++ ) {
-+
-+ common->Printf("Testing Map '%s'\n", files[i].c_str());
-+ idMapFile map;
-+
-+ idStr file = fileSystem->OSPathToRelativePath(files[i]);
-+ if ( map.Parse(file, false, false ) ) {
-+ int count = map.GetNumEntities();
-+ for ( int j = 0; j < count; j++ ) {
-+ idMapEntity *ent = map.GetEntity( j );
-+ if ( ent ) {
-+ const idKeyValue* kv = ent->epairs.MatchPrefix("gui_parm");
-+ while( kv ) {
-+ if(TestGuiParm(kv->GetKey(), kv->GetValue(), excludeList)) {
-+ idStr out = va("%s,%s,%s\r\n", kv->GetValue().c_str(), kv->GetKey().c_str(), file.c_str());
-+ localizeFile->Write( out.c_str(), out.Length() );
-+ } else {
-+ idStr out = va("%s,%s,%s\r\n", kv->GetValue().c_str(), kv->GetKey().c_str(), file.c_str());
-+ noLocalizeFile->Write( out.c_str(), out.Length() );
-+ }
-+ kv = ent->epairs.MatchPrefix( "gui_parm", kv );
-+ }
-+ }
-+ }
-+ }
-+ }
-+
-+ fileSystem->CloseFile( localizeFile );
-+ fileSystem->CloseFile( noLocalizeFile );
-+
-+ common->SetRefreshOnPrint( false );
-+}
-+
-+
-+void Com_LocalizeMapsTest_f( const idCmdArgs &args ) {
-+
-+ ListHash listHash;
-+ LoadMapLocalizeData(listHash);
-+
-+
-+ common->SetRefreshOnPrint( true );
-+
-+ idFile *localizeFile = fileSystem->OpenFileWrite( "map_localize.csv" );
-+
-+ idStrList files;
-+ GetFileList("z:/d3xp/d3xp/maps/game", "*.map", files);
-+
-+ for ( int i = 0; i < files.Num(); i++ ) {
-+
-+ common->Printf("Testing Map '%s'\n", files[i].c_str());
-+ idMapFile map;
-+
-+ idStr file = fileSystem->OSPathToRelativePath(files[i]);
-+ if ( map.Parse(file, false, false ) ) {
-+ int count = map.GetNumEntities();
-+ for ( int j = 0; j < count; j++ ) {
-+ idMapEntity *ent = map.GetEntity( j );
-+ if ( ent ) {
-+
-+ //Temp code to get a list of all entity key value pairs
-+ /*idStr classname = ent->epairs.GetString("classname");
-+ if(classname == "worldspawn" || classname == "func_static" || classname == "light" || classname == "speaker" || classname.Left(8) == "trigger_") {
-+ continue;
-+ }
-+ for( int i = 0; i < ent->epairs.GetNumKeyVals(); i++) {
-+ const idKeyValue* kv = ent->epairs.GetKeyVal(i);
-+ idStr out = va("%s,%s,%s,%s\r\n", classname.c_str(), kv->GetKey().c_str(), kv->GetValue().c_str(), file.c_str());
-+ localizeFile->Write( out.c_str(), out.Length() );
-+ }*/
-+
-+ idStr classname = ent->epairs.GetString("classname");
-+
-+ //Hack: for info_location
-+ bool hasLocation = false;
-+
-+ idStrList* list;
-+ listHash.Get(classname, &list);
-+ if(list) {
-+
-+ for(int k = 0; k < list->Num(); k++) {
-+
-+ idStr val = ent->epairs.GetString((*list)[k], "");
-+
-+ if(classname == "info_location" && (*list)[k] == "location") {
-+ hasLocation = true;
-+ }
-+
-+ if(val.Length() && TestMapVal(val)) {
-+
-+ if(!hasLocation || (*list)[k] == "location") {
-+ idStr out = va("%s,%s,%s\r\n", val.c_str(), (*list)[k].c_str(), file.c_str());
-+ localizeFile->Write( out.c_str(), out.Length() );
-+ }
-+ }
-+ }
-+ }
-+
-+ listHash.Get("all", &list);
-+ if(list) {
-+ for(int k = 0; k < list->Num(); k++) {
-+ idStr val = ent->epairs.GetString((*list)[k], "");
-+ if(val.Length() && TestMapVal(val)) {
-+ idStr out = va("%s,%s,%s\r\n", val.c_str(), (*list)[k].c_str(), file.c_str());
-+ localizeFile->Write( out.c_str(), out.Length() );
-+ }
-+ }
-+ }
-+ }
-+ }
-+ }
-+ }
-+
-+ fileSystem->CloseFile( localizeFile );
-+
-+ common->SetRefreshOnPrint( false );
-+}
-+
-+/*
-+=================
-+Com_StartBuild_f
-+=================
-+*/
-+void Com_StartBuild_f( const idCmdArgs &args ) {
-+ globalImages->StartBuild();
-+}
-+
-+/*
-+=================
-+Com_FinishBuild_f
-+=================
-+*/
-+void Com_FinishBuild_f( const idCmdArgs &args ) {
-+ if ( game ) {
-+ game->CacheDictionaryMedia( NULL );
-+ }
-+ globalImages->FinishBuild( ( args.Argc() > 1 ) );
-+}
-+
-+/*
-+==============
-+Com_Help_f
-+==============
-+*/
-+void Com_Help_f( const idCmdArgs &args ) {
-+ common->Printf( "\nCommonly used commands:\n" );
-+ common->Printf( " spawnServer - start the server.\n" );
-+ common->Printf( " disconnect - shut down the server.\n" );
-+ common->Printf( " listCmds - list all console commands.\n" );
-+ common->Printf( " listCVars - list all console variables.\n" );
-+ common->Printf( " kick - kick a client by number.\n" );
-+ common->Printf( " gameKick - kick a client by name.\n" );
-+ common->Printf( " serverNextMap - immediately load next map.\n" );
-+ common->Printf( " serverMapRestart - restart the current map.\n" );
-+ common->Printf( " serverForceReady - force all players to ready status.\n" );
-+ common->Printf( "\nCommonly used variables:\n" );
-+ common->Printf( " si_name - server name (change requires a restart to see)\n" );
-+ common->Printf( " si_gametype - type of game.\n" );
-+ common->Printf( " si_fragLimit - max kills to win (or lives in Last Man Standing).\n" );
-+ common->Printf( " si_timeLimit - maximum time a game will last.\n" );
-+ common->Printf( " si_warmup - do pre-game warmup.\n" );
-+ common->Printf( " si_pure - pure server.\n" );
-+ common->Printf( " g_mapCycle - name of .scriptcfg file for cycling maps.\n" );
-+ common->Printf( "See mapcycle.scriptcfg for an example of a mapcyle script.\n\n" );
-+}
-+
-+/*
-+=================
-+idCommonLocal::InitCommands
-+=================
-+*/
-+void idCommonLocal::InitCommands( void ) {
-+ cmdSystem->AddCommand( "error", Com_Error_f, CMD_FL_SYSTEM|CMD_FL_CHEAT, "causes an error" );
-+ cmdSystem->AddCommand( "crash", Com_Crash_f, CMD_FL_SYSTEM|CMD_FL_CHEAT, "causes a crash" );
-+ cmdSystem->AddCommand( "freeze", Com_Freeze_f, CMD_FL_SYSTEM|CMD_FL_CHEAT, "freezes the game for a number of seconds" );
-+ cmdSystem->AddCommand( "quit", Com_Quit_f, CMD_FL_SYSTEM, "quits the game" );
-+ cmdSystem->AddCommand( "exit", Com_Quit_f, CMD_FL_SYSTEM, "exits the game" );
-+ cmdSystem->AddCommand( "writeConfig", Com_WriteConfig_f, CMD_FL_SYSTEM, "writes a config file" );
-+ cmdSystem->AddCommand( "reloadEngine", Com_ReloadEngine_f, CMD_FL_SYSTEM, "reloads the engine down to including the file system" );
-+ cmdSystem->AddCommand( "setMachineSpec", Com_SetMachineSpec_f, CMD_FL_SYSTEM, "detects system capabilities and sets com_machineSpec to appropriate value" );
-+ cmdSystem->AddCommand( "execMachineSpec", Com_ExecMachineSpec_f, CMD_FL_SYSTEM, "execs the appropriate config files and sets cvars based on com_machineSpec" );
-+
-+#if !defined( ID_DEDICATED )
-+ // compilers
-+ cmdSystem->AddCommand( "dmap", Dmap_f, CMD_FL_TOOL, "compiles a map", idCmdSystem::ArgCompletion_MapName );
-+ cmdSystem->AddCommand( "renderbump", RenderBump_f, CMD_FL_TOOL, "renders a bump map", idCmdSystem::ArgCompletion_ModelName );
-+ cmdSystem->AddCommand( "renderbumpFlat", RenderBumpFlat_f, CMD_FL_TOOL, "renders a flat bump map", idCmdSystem::ArgCompletion_ModelName );
-+ cmdSystem->AddCommand( "runAAS", RunAAS_f, CMD_FL_TOOL, "compiles an AAS file for a map", idCmdSystem::ArgCompletion_MapName );
-+ cmdSystem->AddCommand( "runAASDir", RunAASDir_f, CMD_FL_TOOL, "compiles AAS files for all maps in a folder", idCmdSystem::ArgCompletion_MapName );
-+ cmdSystem->AddCommand( "runReach", RunReach_f, CMD_FL_TOOL, "calculates reachability for an AAS file", idCmdSystem::ArgCompletion_MapName );
-+ cmdSystem->AddCommand( "roq", RoQFileEncode_f, CMD_FL_TOOL, "encodes a roq file" );
-+#endif
-+
-+#ifdef ID_ALLOW_TOOLS
-+ // editors
-+ cmdSystem->AddCommand( "editor", Com_Editor_f, CMD_FL_TOOL, "launches the level editor Radiant" );
-+ cmdSystem->AddCommand( "editLights", Com_EditLights_f, CMD_FL_TOOL, "launches the in-game Light Editor" );
-+ cmdSystem->AddCommand( "editSounds", Com_EditSounds_f, CMD_FL_TOOL, "launches the in-game Sound Editor" );
-+ cmdSystem->AddCommand( "editDecls", Com_EditDecls_f, CMD_FL_TOOL, "launches the in-game Declaration Editor" );
-+ cmdSystem->AddCommand( "editAFs", Com_EditAFs_f, CMD_FL_TOOL, "launches the in-game Articulated Figure Editor" );
-+ cmdSystem->AddCommand( "editParticles", Com_EditParticles_f, CMD_FL_TOOL, "launches the in-game Particle Editor" );
-+ cmdSystem->AddCommand( "editScripts", Com_EditScripts_f, CMD_FL_TOOL, "launches the in-game Script Editor" );
-+ cmdSystem->AddCommand( "editGUIs", Com_EditGUIs_f, CMD_FL_TOOL, "launches the GUI Editor" );
-+ cmdSystem->AddCommand( "editPDAs", Com_EditPDAs_f, CMD_FL_TOOL, "launches the in-game PDA Editor" );
-+ cmdSystem->AddCommand( "debugger", Com_ScriptDebugger_f, CMD_FL_TOOL, "launches the Script Debugger" );
-+
-+ //BSM Nerve: Add support for the material editor
-+ cmdSystem->AddCommand( "materialEditor", Com_MaterialEditor_f, CMD_FL_TOOL, "launches the Material Editor" );
-+#endif
-+
-+ cmdSystem->AddCommand( "printMemInfo", PrintMemInfo_f, CMD_FL_SYSTEM, "prints memory debugging data" );
-+
-+ // idLib commands
-+ cmdSystem->AddCommand( "memoryDump", Mem_Dump_f, CMD_FL_SYSTEM|CMD_FL_CHEAT, "creates a memory dump" );
-+ cmdSystem->AddCommand( "memoryDumpCompressed", Mem_DumpCompressed_f, CMD_FL_SYSTEM|CMD_FL_CHEAT, "creates a compressed memory dump" );
-+ cmdSystem->AddCommand( "showStringMemory", idStr::ShowMemoryUsage_f, CMD_FL_SYSTEM, "shows memory used by strings" );
-+ cmdSystem->AddCommand( "showDictMemory", idDict::ShowMemoryUsage_f, CMD_FL_SYSTEM, "shows memory used by dictionaries" );
-+ cmdSystem->AddCommand( "listDictKeys", idDict::ListKeys_f, CMD_FL_SYSTEM|CMD_FL_CHEAT, "lists all keys used by dictionaries" );
-+ cmdSystem->AddCommand( "listDictValues", idDict::ListValues_f, CMD_FL_SYSTEM|CMD_FL_CHEAT, "lists all values used by dictionaries" );
-+ cmdSystem->AddCommand( "testSIMD", idSIMD::Test_f, CMD_FL_SYSTEM|CMD_FL_CHEAT, "test SIMD code" );
-+
-+ // localization
-+ cmdSystem->AddCommand( "localizeGuis", Com_LocalizeGuis_f, CMD_FL_SYSTEM|CMD_FL_CHEAT, "localize guis" );
-+ cmdSystem->AddCommand( "localizeMaps", Com_LocalizeMaps_f, CMD_FL_SYSTEM|CMD_FL_CHEAT, "localize maps" );
-+ cmdSystem->AddCommand( "reloadLanguage", Com_ReloadLanguage_f, CMD_FL_SYSTEM, "reload language dict" );
-+
-+ //D3XP Localization
-+ cmdSystem->AddCommand( "localizeGuiParmsTest", Com_LocalizeGuiParmsTest_f, CMD_FL_SYSTEM, "Create test files that show gui parms localized and ignored." );
-+ cmdSystem->AddCommand( "localizeMapsTest", Com_LocalizeMapsTest_f, CMD_FL_SYSTEM, "Create test files that shows which strings will be localized." );
-+
-+ // build helpers
-+ cmdSystem->AddCommand( "startBuild", Com_StartBuild_f, CMD_FL_SYSTEM|CMD_FL_CHEAT, "prepares to make a build" );
-+ cmdSystem->AddCommand( "finishBuild", Com_FinishBuild_f, CMD_FL_SYSTEM|CMD_FL_CHEAT, "finishes the build process" );
-+
-+#ifdef ID_DEDICATED
-+ cmdSystem->AddCommand( "help", Com_Help_f, CMD_FL_SYSTEM, "shows help" );
-+#endif
-+}
-+
-+/*
-+=================
-+idCommonLocal::InitRenderSystem
-+=================
-+*/
-+void idCommonLocal::InitRenderSystem( void ) {
-+ if ( com_skipRenderer.GetBool() ) {
-+ return;
-+ }
-+
-+ renderSystem->InitOpenGL();
-+ PrintLoadingMessage( common->GetLanguageDict()->GetString( "#str_04343" ) );
-+}
-+
-+/*
-+=================
-+idCommonLocal::PrintLoadingMessage
-+=================
-+*/
-+void idCommonLocal::PrintLoadingMessage( const char *msg ) {
-+ if ( !( msg && *msg ) ) {
-+ return;
-+ }
-+ renderSystem->BeginFrame( renderSystem->GetScreenWidth(), renderSystem->GetScreenHeight() );
-+ renderSystem->DrawStretchPic( 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0, 1, 1, declManager->FindMaterial( "splashScreen" ) );
-+ int len = strlen( msg );
-+ renderSystem->DrawSmallStringExt( ( 640 - len * SMALLCHAR_WIDTH ) / 2, 410, msg, idVec4( 0.0f, 0.81f, 0.94f, 1.0f ), true, declManager->FindMaterial( "textures/bigchars" ) );
-+ renderSystem->EndFrame( NULL, NULL );
-+}
-+
-+/*
-+=================
-+idCommonLocal::InitSIMD
-+=================
-+*/
-+void idCommonLocal::InitSIMD( void ) {
-+ idSIMD::InitProcessor( "doom", com_forceGenericSIMD.GetBool() );
-+ com_forceGenericSIMD.ClearModified();
-+}
-+
-+/*
-+=================
-+idCommonLocal::Frame
-+=================
-+*/
-+void idCommonLocal::Frame( void ) {
-+ try {
-+
-+ // pump all the events
-+ Sys_GenerateEvents();
-+
-+ // write config file if anything changed
-+ WriteConfiguration();
-+
-+ // change SIMD implementation if required
-+ if ( com_forceGenericSIMD.IsModified() ) {
-+ InitSIMD();
-+ }
-+
-+ eventLoop->RunEventLoop();
-+
-+ com_frameTime = com_ticNumber * USERCMD_MSEC;
-+
-+ idAsyncNetwork::RunFrame();
-+
-+ if ( idAsyncNetwork::IsActive() ) {
-+ if ( idAsyncNetwork::serverDedicated.GetInteger() != 1 ) {
-+ session->GuiFrameEvents();
-+ session->UpdateScreen( false );
-+ }
-+ } else {
-+ session->Frame();
-+
-+ // normal, in-sequence screen update
-+ session->UpdateScreen( false );
-+ }
-+
-+ // report timing information
-+ if ( com_speeds.GetBool() ) {
-+ static int lastTime;
-+ int nowTime = Sys_Milliseconds();
-+ int com_frameMsec = nowTime - lastTime;
-+ lastTime = nowTime;
-+ Printf( "frame:%i all:%3i gfr:%3i rf:%3i bk:%3i\n", com_frameNumber, com_frameMsec, time_gameFrame, time_frontend, time_backend );
-+ time_gameFrame = 0;
-+ time_gameDraw = 0;
-+ }
-+
-+ com_frameNumber++;
-+
-+ // set idLib frame number for frame based memory dumps
-+ idLib::frameNumber = com_frameNumber;
-+ }
-+
-+ catch( idException & ) {
-+ return; // an ERP_DROP was thrown
-+ }
-+}
-+
-+/*
-+=================
-+idCommonLocal::GUIFrame
-+=================
-+*/
-+void idCommonLocal::GUIFrame( bool execCmd, bool network ) {
-+ Sys_GenerateEvents();
-+ eventLoop->RunEventLoop( execCmd ); // and execute any commands
-+ com_frameTime = com_ticNumber * USERCMD_MSEC;
-+ if ( network ) {
-+ idAsyncNetwork::RunFrame();
-+ }
-+ session->Frame();
-+ session->UpdateScreen( false );
-+}
-+
-+/*
-+=================
-+idCommonLocal::SingleAsyncTic
-+
-+The system will asyncronously call this function 60 times a second to
-+handle the time-critical functions that we don't want limited to
-+the frame rate:
-+
-+sound mixing
-+user input generation (conditioned by com_asyncInput)
-+packet server operation
-+packet client operation
-+
-+We are not using thread safe libraries, so any functionality put here must
-+be VERY VERY careful about what it calls.
-+=================
-+*/
-+
-+typedef struct {
-+ int milliseconds; // should always be incremeting by 60hz
-+ int deltaMsec; // should always be 16
-+ int timeConsumed; // msec spent in Com_AsyncThread()
-+ int clientPacketsReceived;
-+ int serverPacketsReceived;
-+ int mostRecentServerPacketSequence;
-+} asyncStats_t;
-+
-+static const int MAX_ASYNC_STATS = 1024;
-+asyncStats_t com_asyncStats[MAX_ASYNC_STATS]; // indexed by com_ticNumber
-+int prevAsyncMsec;
-+int lastTicMsec;
-+
-+void idCommonLocal::SingleAsyncTic( void ) {
-+ // main thread code can prevent this from happening while modifying
-+ // critical data structures
-+ Sys_EnterCriticalSection();
-+
-+ asyncStats_t *stat = &com_asyncStats[com_ticNumber & (MAX_ASYNC_STATS-1)];
-+ memset( stat, 0, sizeof( *stat ) );
-+ stat->milliseconds = Sys_Milliseconds();
-+ stat->deltaMsec = stat->milliseconds - com_asyncStats[(com_ticNumber - 1) & (MAX_ASYNC_STATS-1)].milliseconds;
-+
-+ if ( usercmdGen && com_asyncInput.GetBool() ) {
-+ usercmdGen->UsercmdInterrupt();
-+ }
-+
-+ switch ( com_asyncSound.GetInteger() ) {
-+ case 1:
-+ soundSystem->AsyncUpdate( stat->milliseconds );
-+ break;
-+ case 3:
-+ soundSystem->AsyncUpdateWrite( stat->milliseconds );
-+ break;
-+ }
-+
-+ // we update com_ticNumber after all the background tasks
-+ // have completed their work for this tic
-+ com_ticNumber++;
-+
-+ stat->timeConsumed = Sys_Milliseconds() - stat->milliseconds;
-+
-+ Sys_LeaveCriticalSection();
-+}
-+
-+/*
-+=================
-+idCommonLocal::Async
-+=================
-+*/
-+void idCommonLocal::Async( void ) {
-+ int msec = Sys_Milliseconds();
-+ if ( !lastTicMsec ) {
-+ lastTicMsec = msec - USERCMD_MSEC;
-+ }
-+
-+ if ( !com_preciseTic.GetBool() ) {
-+ // just run a single tic, even if the exact msec isn't precise
-+ SingleAsyncTic();
-+ return;
-+ }
-+
-+ int ticMsec = USERCMD_MSEC;
-+
-+ // the number of msec per tic can be varies with the timescale cvar
-+ float timescale = com_timescale.GetFloat();
-+ if ( timescale != 1.0f ) {
-+ ticMsec /= timescale;
-+ if ( ticMsec < 1 ) {
-+ ticMsec = 1;
-+ }
-+ }
-+
-+ // don't skip too many
-+ if ( timescale == 1.0f ) {
-+ if ( lastTicMsec + 10 * USERCMD_MSEC < msec ) {
-+ lastTicMsec = msec - 10*USERCMD_MSEC;
-+ }
-+ }
-+
-+ while ( lastTicMsec + ticMsec <= msec ) {
-+ SingleAsyncTic();
-+ lastTicMsec += ticMsec;
-+ }
-+}
-+
-+/*
-+=================
-+idCommonLocal::LoadGameDLLbyName
-+
-+Helper for LoadGameDLL() to make it less painfull to try different dll names.
-+=================
-+*/
-+void idCommonLocal::LoadGameDLLbyName( const char *dll, idStr& s ) {
-+ s.CapLength(0);
-+ // try next to the binary first (build tree)
-+ if (Sys_GetPath(PATH_EXE, s)) {
-+ // "s = " seems superfluous, but works around g++ 4.7 bug else StripFilename()
-+ // (and possibly even CapLength()) seems to be "optimized" away and the string contains garbage
-+ s = s.StripFilename();
-+ s.AppendPath(dll);
-+ gameDLL = sys->DLL_Load(s);
-+ }
-+
-+ #if defined(_WIN32)
-+ // then the lib/ dir relative to the binary on windows
-+ if (!gameDLL && Sys_GetPath(PATH_EXE, s)) {
-+ s.StripFilename();
-+ s.AppendPath("lib");
-+ s.AppendPath(dll);
-+ gameDLL = sys->DLL_Load(s);
-+ }
-+ #elif defined(MACOS_X)
-+ // then the binary dir in the bundle on osx
-+ if (!gameDLL && Sys_GetPath(PATH_EXE, s)) {
-+ s.StripFilename();
-+ s.AppendPath(dll);
-+ gameDLL = sys->DLL_Load(s);
-+ }
-+ #else
-+ // then the install folder on *nix
-+ if (!gameDLL) {
-+ s = BUILD_LIBDIR;
-+ s.AppendPath(dll);
-+ gameDLL = sys->DLL_Load(s);
-+ }
-+ #endif
-+}
-+
-+/*
-+=================
-+idCommonLocal::LoadGameDLL
-+=================
-+*/
-+void idCommonLocal::LoadGameDLL( void ) {
-+#ifdef __DOOM_DLL__
-+ const char *fs_game;
-+ char dll[MAX_OSPATH];
-+ idStr s;
-+
-+ gameImport_t gameImport;
-+ gameExport_t gameExport;
-+ GetGameAPI_t GetGameAPI;
-+
-+ fs_game = cvarSystem->GetCVarString("fs_game");
-+ if (!fs_game || !fs_game[0])
-+ fs_game = BASE_GAMEDIR;
-+
-+ gameDLL = 0;
-+
-+ sys->DLL_GetFileName(fs_game, dll, sizeof(dll));
-+ LoadGameDLLbyName(dll, s);
-+
-+ // there was no gamelib for this mod, use default one from base game
-+ if (!gameDLL) {
-+ common->Warning( "couldn't load mod-specific %s, defaulting to base game's library!", dll );
-+ sys->DLL_GetFileName(BASE_GAMEDIR, dll, sizeof(dll));
-+ LoadGameDLLbyName(dll, s);
-+ }
-+
-+ if ( !gameDLL ) {
-+ common->FatalError( "couldn't load game dynamic library" );
-+ return;
-+ }
-+
-+ common->Printf("loaded game library '%s'.\n", s.c_str());
-+
-+ GetGameAPI = (GetGameAPI_t) Sys_DLL_GetProcAddress( gameDLL, "GetGameAPI" );
-+ if ( !GetGameAPI ) {
-+ Sys_DLL_Unload( gameDLL );
-+ gameDLL = 0;
-+ common->FatalError( "couldn't find game DLL API" );
-+ return;
-+ }
-+
-+ gameImport.version = GAME_API_VERSION;
-+ gameImport.sys = ::sys;
-+ gameImport.common = ::common;
-+ gameImport.cmdSystem = ::cmdSystem;
-+ gameImport.cvarSystem = ::cvarSystem;
-+ gameImport.fileSystem = ::fileSystem;
-+ gameImport.networkSystem = ::networkSystem;
-+ gameImport.renderSystem = ::renderSystem;
-+ gameImport.soundSystem = ::soundSystem;
-+ gameImport.renderModelManager = ::renderModelManager;
-+ gameImport.uiManager = ::uiManager;
-+ gameImport.declManager = ::declManager;
-+ gameImport.AASFileManager = ::AASFileManager;
-+ gameImport.collisionModelManager = ::collisionModelManager;
-+
-+ gameExport = *GetGameAPI( &gameImport );
-+
-+ if ( gameExport.version != GAME_API_VERSION ) {
-+ Sys_DLL_Unload( gameDLL );
-+ gameDLL = 0;
-+ common->FatalError( "wrong game DLL API version" );
-+ return;
-+ }
-+
-+ game = gameExport.game;
-+ gameEdit = gameExport.gameEdit;
-+
-+#endif
-+
-+ // initialize the game object
-+ if ( game != NULL ) {
-+ game->Init();
-+ }
-+}
-+
-+/*
-+=================
-+idCommonLocal::UnloadGameDLL
-+=================
-+*/
-+void idCommonLocal::UnloadGameDLL( void ) {
-+
-+ // shut down the game object
-+ if ( game != NULL ) {
-+ game->Shutdown();
-+ }
-+
-+#ifdef __DOOM_DLL__
-+
-+ if ( gameDLL ) {
-+ Sys_DLL_Unload( gameDLL );
-+ gameDLL = 0;
-+ }
-+ game = NULL;
-+ gameEdit = NULL;
-+
-+#endif
-+}
-+
-+/*
-+=================
-+idCommonLocal::IsInitialized
-+=================
-+*/
-+bool idCommonLocal::IsInitialized( void ) const {
-+ return com_fullyInitialized;
-+}
-+
-+/*
-+=================
-+idCommonLocal::SetMachineSpec
-+=================
-+*/
-+void idCommonLocal::SetMachineSpec( void ) {
-+ int sysRam = Sys_GetSystemRam();
-+
-+ Printf( "Detected\n\t%i MB of System memory\n\n", sysRam );
-+
-+ if ( sysRam >= 1024 ) {
-+ Printf( "This system qualifies for Ultra quality!\n" );
-+ com_machineSpec.SetInteger( 3 );
-+ } else if ( sysRam >= 512 ) {
-+ Printf( "This system qualifies for High quality!\n" );
-+ com_machineSpec.SetInteger( 2 );
-+ } else if ( sysRam >= 384 ) {
-+ Printf( "This system qualifies for Medium quality.\n" );
-+ com_machineSpec.SetInteger( 1 );
-+ } else {
-+ Printf( "This system qualifies for Low quality.\n" );
-+ com_machineSpec.SetInteger( 0 );
-+ }
-+}
-+
-+static unsigned int AsyncTimer(unsigned int interval, void *) {
-+ common->Async();
-+ Sys_TriggerEvent(TRIGGER_EVENT_ONE);
-+
-+ // calculate the next interval to get as close to 60fps as possible
-+ unsigned int now = SDL_GetTicks();
-+ unsigned int tick = com_ticNumber * USERCMD_MSEC;
-+
-+ if (now >= tick)
-+ return 1;
-+
-+ return tick - now;
-+}
-+
-+/*
-+=================
-+idCommonLocal::Init
-+=================
-+*/
-+void idCommonLocal::Init( int argc, char **argv ) {
-+#ifdef ID_DEDICATED
-+ // we want to use the SDL event queue for dedicated servers. That
-+ // requires video to be initialized, so we just use the dummy
-+ // driver for headless boxen
-+#if SDL_VERSION_ATLEAST(2, 0, 0)
-+ SDL_setenv("SDL_VIDEODRIVER", "dummy", 1);
-+#else
-+ char dummy[] = "SDL_VIDEODRIVER=dummy\0";
-+ SDL_putenv(dummy);
-+#endif
-+#endif
-+
-+ if (SDL_Init(SDL_INIT_TIMER | SDL_INIT_VIDEO))
-+ Sys_Error("Error while initializing SDL: %s", SDL_GetError());
-+
-+ Sys_InitThreads();
-+
-+ try {
-+
-+ // set interface pointers used by idLib
-+ idLib::sys = sys;
-+ idLib::common = common;
-+ idLib::cvarSystem = cvarSystem;
-+ idLib::fileSystem = fileSystem;
-+
-+ // initialize idLib
-+ idLib::Init();
-+
-+ // clear warning buffer
-+ ClearWarnings( GAME_NAME " initialization" );
-+
-+ // parse command line options
-+ ParseCommandLine( argc, argv );
-+
-+ // init console command system
-+ cmdSystem->Init();
-+
-+ // init CVar system
-+ cvarSystem->Init();
-+
-+ // start file logging right away, before early console or whatever
-+ StartupVariable( "win_outputDebugString", false );
-+
-+ // register all static CVars
-+ idCVar::RegisterStaticVars();
-+
-+ // print engine version
-+#if SDL_VERSION_ATLEAST(2, 0, 0)
-+ SDL_version sdlv;
-+ SDL_GetVersion(&sdlv);
-+#else
-+ SDL_version sdlv = *SDL_Linked_Version();
-+#endif
-+ Printf( "%s using SDL v%u.%u.%u\n",
-+ version.string, sdlv.major, sdlv.minor, sdlv.patch );
-+
-+ // initialize key input/binding, done early so bind command exists
-+ idKeyInput::Init();
-+
-+ // init the console so we can take prints
-+ console->Init();
-+
-+ // get architecture info
-+ Sys_Init();
-+
-+ // initialize networking
-+ Sys_InitNetworking();
-+
-+ // override cvars from command line
-+ StartupVariable( NULL, false );
-+
-+ // set fpu double extended precision
-+ Sys_FPU_SetPrecision();
-+
-+ // initialize processor specific SIMD implementation
-+ InitSIMD();
-+
-+ // init commands
-+ InitCommands();
-+
-+#ifdef ID_WRITE_VERSION
-+ config_compressor = idCompressor::AllocArithmetic();
-+#endif
-+
-+ // game specific initialization
-+ InitGame();
-+
-+ // don't add startup commands if no CD key is present
-+#if ID_ENFORCE_KEY
-+ if ( !session->CDKeysAreValid( false ) || !AddStartupCommands() ) {
-+#else
-+ if ( !AddStartupCommands() ) {
-+#endif
-+ // if the user didn't give any commands, run default action
-+ session->StartMenu( true );
-+ }
-+
-+ // print all warnings queued during initialization
-+ PrintWarnings();
-+
-+#ifdef ID_DEDICATED
-+ Printf( "\nType 'help' for dedicated server info.\n\n" );
-+#endif
-+
-+ // remove any prints from the notify lines
-+ console->ClearNotifyLines();
-+
-+ ClearCommandLine();
-+
-+ // load the persistent console history
-+ console->LoadHistory();
-+
-+ com_fullyInitialized = true;
-+ }
-+
-+ catch( idException & ) {
-+ Sys_Error( "Error during initialization" );
-+ }
-+
-+ async_timer = SDL_AddTimer(USERCMD_MSEC, AsyncTimer, NULL);
-+
-+ if (!async_timer)
-+ Sys_Error("Error while starting the async timer: %s", SDL_GetError());
-+}
-+
-+
-+/*
-+=================
-+idCommonLocal::Shutdown
-+=================
-+*/
-+void idCommonLocal::Shutdown( void ) {
-+ if (async_timer) {
-+ SDL_RemoveTimer(async_timer);
-+ async_timer = 0;
-+ }
-+
-+ idAsyncNetwork::server.Kill();
-+ idAsyncNetwork::client.Shutdown();
-+
-+ // save persistent console history
-+ console->SaveHistory();
-+
-+ // game specific shut down
-+ ShutdownGame( false );
-+
-+ // shut down non-portable system services
-+ Sys_Shutdown();
-+
-+ // shut down the console
-+ console->Shutdown();
-+
-+ // shut down the key system
-+ idKeyInput::Shutdown();
-+
-+ // shut down the cvar system
-+ cvarSystem->Shutdown();
-+
-+ // shut down the console command system
-+ cmdSystem->Shutdown();
-+
-+#ifdef ID_WRITE_VERSION
-+ delete config_compressor;
-+ config_compressor = NULL;
-+#endif
-+
-+ // free any buffered warning messages
-+ ClearWarnings( GAME_NAME " shutdown" );
-+ warningCaption.Clear();
-+ errorList.Clear();
-+
-+ // free language dictionary
-+ languageDict.Clear();
-+
-+ // enable leak test
-+ Mem_EnableLeakTest( "doom" );
-+
-+ // shutdown idLib
-+ idLib::ShutDown();
-+
-+ Sys_ShutdownThreads();
-+
-+ SDL_Quit();
-+}
-+
-+/*
-+=================
-+idCommonLocal::InitGame
-+=================
-+*/
-+void idCommonLocal::InitGame( void ) {
-+ // initialize the file system
-+ fileSystem->Init();
-+
-+ // initialize the declaration manager
-+ declManager->Init();
-+
-+ // force r_fullscreen 0 if running a tool
-+ CheckToolMode();
-+
-+ idFile *file = fileSystem->OpenExplicitFileRead( fileSystem->RelativePathToOSPath( CONFIG_SPEC, "fs_configpath" ) );
-+ bool sysDetect = ( file == NULL );
-+ if ( file ) {
-+ fileSystem->CloseFile( file );
-+ } else {
-+ file = fileSystem->OpenFileWrite( CONFIG_SPEC, "fs_configpath" );
-+ fileSystem->CloseFile( file );
-+ }
-+
-+ idCmdArgs args;
-+ if ( sysDetect ) {
-+ SetMachineSpec();
-+ Com_ExecMachineSpec_f( args );
-+ }
-+
-+ // initialize the renderSystem data structures, but don't start OpenGL yet
-+ renderSystem->Init();
-+
-+ // initialize string database right off so we can use it for loading messages
-+ InitLanguageDict();
-+
-+ PrintLoadingMessage( common->GetLanguageDict()->GetString( "#str_04344" ) );
-+
-+ // load the font, etc
-+ console->LoadGraphics();
-+
-+ // init journalling, etc
-+ eventLoop->Init();
-+
-+ PrintLoadingMessage( common->GetLanguageDict()->GetString( "#str_04345" ) );
-+
-+ // exec the startup scripts
-+ cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "exec editor.cfg\n" );
-+ cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "exec default.cfg\n" );
-+
-+ // skip the config file if "safe" is on the command line
-+ if ( !SafeMode() ) {
-+ cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "exec " CONFIG_FILE "\n" );
-+ }
-+ cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "exec autoexec.cfg\n" );
-+
-+ // reload the language dictionary now that we've loaded config files
-+ cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "reloadLanguage\n" );
-+
-+ // run cfg execution
-+ cmdSystem->ExecuteCommandBuffer();
-+
-+ // re-override anything from the config files with command line args
-+ StartupVariable( NULL, false );
-+
-+ // if any archived cvars are modified after this, we will trigger a writing of the config file
-+ cvarSystem->ClearModifiedFlags( CVAR_ARCHIVE );
-+
-+ // init the user command input code
-+ usercmdGen->Init();
-+
-+ PrintLoadingMessage( common->GetLanguageDict()->GetString( "#str_04346" ) );
-+
-+ // start the sound system, but don't do any hardware operations yet
-+ soundSystem->Init();
-+
-+ PrintLoadingMessage( common->GetLanguageDict()->GetString( "#str_04347" ) );
-+
-+ // init async network
-+ idAsyncNetwork::Init();
-+
-+#ifdef ID_DEDICATED
-+ idAsyncNetwork::server.InitPort();
-+ cvarSystem->SetCVarBool( "s_noSound", true );
-+#else
-+ if ( idAsyncNetwork::serverDedicated.GetInteger() == 1 ) {
-+ idAsyncNetwork::server.InitPort();
-+ cvarSystem->SetCVarBool( "s_noSound", true );
-+ } else {
-+ // init OpenGL, which will open a window and connect sound and input hardware
-+ PrintLoadingMessage( common->GetLanguageDict()->GetString( "#str_04348" ) );
-+ InitRenderSystem();
-+ }
-+#endif
-+
-+ PrintLoadingMessage( common->GetLanguageDict()->GetString( "#str_04349" ) );
-+
-+ // initialize the user interfaces
-+ uiManager->Init();
-+
-+ // startup the script debugger
-+ // DebuggerServerInit();
-+
-+ PrintLoadingMessage( common->GetLanguageDict()->GetString( "#str_04350" ) );
-+
-+ // load the game dll
-+ LoadGameDLL();
-+
-+ PrintLoadingMessage( common->GetLanguageDict()->GetString( "#str_04351" ) );
-+
-+ // init the session
-+ session->Init();
-+
-+ // have to do this twice.. first one sets the correct r_mode for the renderer init
-+ // this time around the backend is all setup correct.. a bit fugly but do not want
-+ // to mess with all the gl init at this point.. an old vid card will never qualify for
-+ if ( sysDetect ) {
-+ SetMachineSpec();
-+ Com_ExecMachineSpec_f( args );
-+ cvarSystem->SetCVarInteger( "s_numberOfSpeakers", 6 );
-+ cmdSystem->BufferCommandText( CMD_EXEC_NOW, "s_restart\n" );
-+ cmdSystem->ExecuteCommandBuffer();
-+ }
-+}
-+
-+/*
-+=================
-+idCommonLocal::ShutdownGame
-+=================
-+*/
-+void idCommonLocal::ShutdownGame( bool reloading ) {
-+
-+ // kill sound first
-+ idSoundWorld *sw = soundSystem->GetPlayingSoundWorld();
-+ if ( sw ) {
-+ sw->StopAllSounds();
-+ }
-+
-+ // shutdown the script debugger
-+ // DebuggerServerShutdown();
-+
-+ idAsyncNetwork::client.Shutdown();
-+
-+ // shut down the session
-+ session->Shutdown();
-+
-+ // shut down the user interfaces
-+ uiManager->Shutdown();
-+
-+ // shut down the sound system
-+ soundSystem->Shutdown();
-+
-+ // shut down async networking
-+ idAsyncNetwork::Shutdown();
-+
-+ // shut down the user command input code
-+ usercmdGen->Shutdown();
-+
-+ // shut down the event loop
-+ eventLoop->Shutdown();
-+
-+ // shut down the renderSystem
-+ renderSystem->Shutdown();
-+
-+ // shutdown the decl manager
-+ declManager->Shutdown();
-+
-+ // unload the game dll
-+ UnloadGameDLL();
-+
-+ // dump warnings to "warnings.txt"
-+#ifdef DEBUG
-+ DumpWarnings();
-+#endif
-+
-+ // shut down the file system
-+ fileSystem->Shutdown( reloading );
-+}
-diff -Naur dhewm3.old/neo/framework/FileSystem.cpp dhewm3/neo/framework/FileSystem.cpp
---- dhewm3.old/neo/framework/FileSystem.cpp 2013-08-15 23:27:20.000000000 +0200
-+++ dhewm3/neo/framework/FileSystem.cpp 2013-08-22 13:23:33.000219548 +0200
-@@ -2969,12 +2969,6 @@
- return NULL;
- }
-
-- // make sure the doomkey file is only readable by game at initialization
-- // any other time the key should only be accessed in memory using the provided functions
-- if( common->IsInitialized() && ( idStr::Icmp( relativePath, CDKEY_FILE ) == 0 || idStr::Icmp( relativePath, XPKEY_FILE ) == 0 ) ) {
-- return NULL;
-- }
--
- //
- // search through the path, one element at a time
- //
-diff -Naur dhewm3.old/neo/framework/FileSystem.cpp.orig dhewm3/neo/framework/FileSystem.cpp.orig
---- dhewm3.old/neo/framework/FileSystem.cpp.orig 1970-01-01 01:00:00.000000000 +0100
-+++ dhewm3/neo/framework/FileSystem.cpp.orig 2013-08-15 23:27:20.000000000 +0200
-@@ -0,0 +1,3914 @@
-+/*
-+===========================================================================
-+
-+Doom 3 GPL Source Code
-+Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
-+
-+This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
-+
-+Doom 3 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 3 of the License, or
-+(at your option) any later version.
-+
-+Doom 3 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 Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
-+
-+In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
-+
-+If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
-+
-+===========================================================================
-+*/
-+
-+#ifdef WIN32
-+ #include <io.h> // for _read
-+#else
-+ #include <sys/types.h>
-+ #include <sys/stat.h>
-+ #include <unistd.h>
-+#endif
-+
-+#include "sys/platform.h"
-+
-+#ifdef ID_ENABLE_CURL
-+ #include <curl/curl.h>
-+#endif
-+
-+#include "idlib/hashing/MD4.h"
-+#include "framework/Licensee.h"
-+#include "framework/Unzip.h"
-+#include "framework/EventLoop.h"
-+#include "framework/DeclEntityDef.h"
-+#include "framework/DeclManager.h"
-+
-+#include "framework/FileSystem.h"
-+
-+/*
-+=============================================================================
-+
-+DOOM FILESYSTEM
-+
-+All of Doom's data access is through a hierarchical file system, but the contents of
-+the file system can be transparently merged from several sources.
-+
-+A "relativePath" is a reference to game file data, which must include a terminating zero.
-+"..", "\\", and ":" are explicitly illegal in qpaths to prevent any references
-+outside the Doom directory system.
-+
-+The "base path" is the path to the directory holding all the game directories and
-+usually the executable. It defaults to the current directory, but can be overridden
-+with "+set fs_basepath c:\doom" on the command line. The base path cannot be modified
-+at all after startup.
-+
-+The "save path" is the path to the directory where game files will be saved. It defaults
-+to the base path, but can be overridden with a "+set fs_savepath c:\doom" on the
-+command line. Any files that are created during the game (demos, screenshots, etc.) will
-+be created reletive to the save path.
-+
-+The "cd path" is the path to an alternate hierarchy that will be searched if a file
-+is not located in the base path. A user can do a partial install that copies some
-+data to a base path created on their hard drive and leave the rest on the cd. It defaults
-+to the current directory, but it can be overridden with "+set fs_cdpath g:\doom" on the
-+command line.
-+
-+The "dev path" is the path to an alternate hierarchy where the editors and tools used
-+during development (Radiant, AF editor, dmap, runAAS) will write files to. It defaults to
-+the cd path, but can be overridden with a "+set fs_devpath c:\doom" on the command line.
-+
-+If a user runs the game directly from a CD, the base path would be on the CD. This
-+should still function correctly, but all file writes will fail (harmlessly).
-+
-+The "base game" is the directory under the paths where data comes from by default, and
-+can be either "base" or "demo".
-+
-+The "current game" may be the same as the base game, or it may be the name of another
-+directory under the paths that should be searched for files before looking in the base
-+game. The game directory is set with "+set fs_game myaddon" on the command line. This is
-+the basis for addons.
-+
-+No other directories outside of the base game and current game will ever be referenced by
-+filesystem functions.
-+
-+To save disk space and speed up file loading, directory trees can be collapsed into zip
-+files. The files use a ".pk4" extension to prevent users from unzipping them accidentally,
-+but otherwise they are simply normal zip files. A game directory can have multiple zip
-+files of the form "pak0.pk4", "pak1.pk4", etc. Zip files are searched in decending order
-+from the highest number to the lowest, and will always take precedence over the filesystem.
-+This allows a pk4 distributed as a patch to override all existing data.
-+
-+Because we will have updated executables freely available online, there is no point to
-+trying to restrict demo / oem versions of the game with code changes. Demo / oem versions
-+should be exactly the same executables as release versions, but with different data that
-+automatically restricts where game media can come from to prevent add-ons from working.
-+
-+After the paths are initialized, Doom will look for the product.txt file. If not found
-+and verified, the game will run in restricted mode. In restricted mode, only files
-+contained in demo/pak0.pk4 will be available for loading, and only if the zip header is
-+verified to not have been modified. A single exception is made for DoomConfig.cfg. Files
-+can still be written out in restricted mode, so screenshots and demos are allowed.
-+Restricted mode can be tested by setting "+set fs_restrict 1" on the command line, even
-+if there is a valid product.txt under the basepath or cdpath.
-+
-+If the "fs_copyfiles" cvar is set to 1, then every time a file is sourced from the cd
-+path, it will be copied over to the save path. This is a development aid to help build
-+test releases and to copy working sets of files.
-+
-+If the "fs_copyfiles" cvar is set to 2, any file found in fs_cdpath that is newer than
-+it's fs_savepath version will be copied to fs_savepath (in addition to the fs_copyfiles 1
-+behaviour).
-+
-+If the "fs_copyfiles" cvar is set to 3, files from both basepath and cdpath will be copied
-+over to the save path. This is useful when copying working sets of files mainly from base
-+path with an additional cd path (which can be a slower network drive for instance).
-+
-+If the "fs_copyfiles" cvar is set to 4, files that exist in the cd path but NOT the base path
-+will be copied to the save path
-+
-+NOTE: fs_copyfiles and case sensitivity. On fs_caseSensitiveOS 0 filesystems ( win32 ), the
-+copied files may change casing when copied over.
-+
-+The relative path "sound/newstuff/test.wav" would be searched for in the following places:
-+
-+for save path, dev path, base path, cd path:
-+ for current game, base game:
-+ search directory
-+ search zip files
-+
-+downloaded files, to be written to save path + current game's directory
-+
-+The filesystem can be safely shutdown and reinitialized with different
-+basedir / cddir / game combinations, but all other subsystems that rely on it
-+(sound, video) must also be forced to restart.
-+
-+
-+"fs_caseSensitiveOS":
-+This cvar is set on operating systems that use case sensitive filesystems (Linux and OSX)
-+It is a common situation to have the media reference filenames, whereas the file on disc
-+only matches in a case-insensitive way. When "fs_caseSensitiveOS" is set, the filesystem
-+will always do a case insensitive search.
-+IMPORTANT: This only applies to files, and not to directories. There is no case-insensitive
-+matching of directories. All directory names should be lowercase, when "com_developer" is 1,
-+the filesystem will warn when it catches bad directory situations (regardless of the
-+"fs_caseSensitiveOS" setting)
-+When bad casing in directories happen and "fs_caseSensitiveOS" is set, BuildOSPath will
-+attempt to correct the situation by forcing the path to lowercase. This assumes the media
-+is stored all lowercase.
-+
-+"additional mod path search":
-+fs_game_base can be used to set an additional search path
-+in search order, fs_game, fs_game_base, BASEGAME
-+for instance to base a mod of D3 + D3XP assets, fs_game mymod, fs_game_base d3xp
-+
-+=============================================================================
-+*/
-+
-+
-+
-+// define to fix special-cases for GetPackStatus so that files that shipped in
-+// the wrong place for Doom 3 don't break pure servers.
-+#define DOOM3_PURE_SPECIAL_CASES
-+
-+typedef bool (*pureExclusionFunc_t)( const struct pureExclusion_s &excl, int l, const idStr &name );
-+
-+typedef struct pureExclusion_s {
-+ int nameLen;
-+ int extLen;
-+ const char * name;
-+ const char * ext;
-+ pureExclusionFunc_t func;
-+} pureExclusion_t;
-+
-+bool excludeExtension( const pureExclusion_t &excl, int l, const idStr &name ) {
-+ if ( l > excl.extLen && !idStr::Icmp( name.c_str() + l - excl.extLen, excl.ext ) ) {
-+ return true;
-+ }
-+ return false;
-+}
-+
-+bool excludePathPrefixAndExtension( const pureExclusion_t &excl, int l, const idStr &name ) {
-+ if ( l > excl.nameLen && !idStr::Icmp( name.c_str() + l - excl.extLen, excl.ext ) && !name.IcmpPrefixPath( excl.name ) ) {
-+ return true;
-+ }
-+ return false;
-+}
-+
-+bool excludeFullName( const pureExclusion_t &excl, int l, const idStr &name ) {
-+ if ( l == excl.nameLen && !name.Icmp( excl.name ) ) {
-+ return true;
-+ }
-+ return false;
-+}
-+
-+static pureExclusion_t pureExclusions[] = {
-+ { 0, 0, NULL, "/", excludeExtension },
-+ { 0, 0, NULL, "\\", excludeExtension },
-+ { 0, 0, NULL, ".pda", excludeExtension },
-+ { 0, 0, NULL, ".gui", excludeExtension },
-+ { 0, 0, NULL, ".pd", excludeExtension },
-+ { 0, 0, NULL, ".lang", excludeExtension },
-+ { 0, 0, "sound/VO", ".ogg", excludePathPrefixAndExtension },
-+ { 0, 0, "sound/VO", ".wav", excludePathPrefixAndExtension },
-+#if defined DOOM3_PURE_SPECIAL_CASES
-+ // add any special-case files or paths for pure servers here
-+ { 0, 0, "sound/ed/marscity/vo_intro_cutscene.ogg", NULL, excludeFullName },
-+ { 0, 0, "sound/weapons/soulcube/energize_01.ogg", NULL, excludeFullName },
-+ { 0, 0, "sound/xian/creepy/vocal_fx", ".ogg", excludePathPrefixAndExtension },
-+ { 0, 0, "sound/xian/creepy/vocal_fx", ".wav", excludePathPrefixAndExtension },
-+ { 0, 0, "sound/feedback", ".ogg", excludePathPrefixAndExtension },
-+ { 0, 0, "sound/feedback", ".wav", excludePathPrefixAndExtension },
-+ { 0, 0, "guis/assets/mainmenu/chnote.tga", NULL, excludeFullName },
-+ { 0, 0, "sound/levels/alphalabs2/uac_better_place.ogg", NULL, excludeFullName },
-+ { 0, 0, "textures/bigchars.tga", NULL, excludeFullName },
-+ { 0, 0, "dds/textures/bigchars.dds", NULL, excludeFullName },
-+ { 0, 0, "fonts", ".tga", excludePathPrefixAndExtension },
-+ { 0, 0, "dds/fonts", ".dds", excludePathPrefixAndExtension },
-+ { 0, 0, "default.cfg", NULL, excludeFullName },
-+ // russian zpak001.pk4
-+ { 0, 0, "fonts", ".dat", excludePathPrefixAndExtension },
-+ { 0, 0, "guis/temp.guied", NULL, excludeFullName },
-+#endif
-+ { 0, 0, NULL, NULL, NULL }
-+};
-+
-+// ensures that lengths for pure exclusions are correct
-+class idInitExclusions {
-+public:
-+ idInitExclusions() {
-+ for ( int i = 0; pureExclusions[i].func != NULL; i++ ) {
-+ if ( pureExclusions[i].name ) {
-+ pureExclusions[i].nameLen = idStr::Length( pureExclusions[i].name );
-+ }
-+ if ( pureExclusions[i].ext ) {
-+ pureExclusions[i].extLen = idStr::Length( pureExclusions[i].ext );
-+ }
-+ }
-+ }
-+};
-+
-+static idInitExclusions initExclusions;
-+
-+#define MAX_ZIPPED_FILE_NAME 2048
-+#define FILE_HASH_SIZE 1024
-+
-+typedef struct fileInPack_s {
-+ idStr name; // name of the file
-+ ZPOS64_T pos; // file info position in zip
-+ struct fileInPack_s * next; // next file in the hash
-+} fileInPack_t;
-+
-+typedef enum {
-+ PURE_UNKNOWN = 0, // need to run the pak through GetPackStatus
-+ PURE_NEUTRAL, // neutral regarding pureness. gets in the pure list if referenced
-+ PURE_ALWAYS, // always referenced - for pak* named files, unless NEVER
-+ PURE_NEVER // VO paks. may be referenced, won't be in the pure lists
-+} pureStatus_t;
-+
-+typedef struct {
-+ idList<int> depends;
-+ idList<idDict *> mapDecls;
-+} addonInfo_t;
-+
-+typedef struct {
-+ idStr pakFilename; // c:\doom\base\pak0.pk4
-+ unzFile handle;
-+ int checksum;
-+ int numfiles;
-+ int length;
-+ bool referenced;
-+ bool addon; // this is an addon pack - addon_search tells if it's 'active'
-+ bool addon_search; // is in the search list
-+ addonInfo_t *addon_info;
-+ pureStatus_t pureStatus;
-+ bool isNew; // for downloaded paks
-+ fileInPack_t *hashTable[FILE_HASH_SIZE];
-+ fileInPack_t *buildBuffer;
-+} pack_t;
-+
-+typedef struct {
-+ idStr path; // c:\doom
-+ idStr gamedir; // base
-+} directory_t;
-+
-+typedef struct searchpath_s {
-+ pack_t * pack; // only one of pack / dir will be non NULL
-+ directory_t * dir;
-+ struct searchpath_s *next;
-+} searchpath_t;
-+
-+// search flags when opening a file
-+#define FSFLAG_SEARCH_DIRS ( 1 << 0 )
-+#define FSFLAG_SEARCH_PAKS ( 1 << 1 )
-+#define FSFLAG_PURE_NOREF ( 1 << 2 )
-+#define FSFLAG_SEARCH_ADDONS ( 1 << 4 )
-+
-+// 3 search path (fs_savepath fs_basepath fs_cdpath)
-+// + .jpg and .tga
-+#define MAX_CACHED_DIRS 6
-+
-+// how many OSes to handle game paks for ( we don't have to know them precisely )
-+#define BINARY_CONFIG "binary.conf"
-+#define ADDON_CONFIG "addon.conf"
-+
-+class idDEntry : public idStrList {
-+public:
-+ idDEntry() {}
-+ virtual ~idDEntry() {}
-+
-+ bool Matches( const char *directory, const char *extension ) const;
-+ void Init( const char *directory, const char *extension, const idStrList &list );
-+ void Clear( void );
-+
-+private:
-+ idStr directory;
-+ idStr extension;
-+};
-+
-+class idFileSystemLocal : public idFileSystem {
-+public:
-+ idFileSystemLocal( void );
-+
-+ virtual void Init( void );
-+ virtual void StartBackgroundDownloadThread( void );
-+ virtual void Restart( void );
-+ virtual void Shutdown( bool reloading );
-+ virtual bool IsInitialized( void ) const;
-+ virtual bool PerformingCopyFiles( void ) const;
-+ virtual idModList * ListMods( void );
-+ virtual void FreeModList( idModList *modList );
-+ virtual idFileList * ListFiles( const char *relativePath, const char *extension, bool sort = false, bool fullRelativePath = false, const char* gamedir = NULL );
-+ virtual idFileList * ListFilesTree( const char *relativePath, const char *extension, bool sort = false, const char* gamedir = NULL );
-+ virtual void FreeFileList( idFileList *fileList );
-+ virtual const char * OSPathToRelativePath( const char *OSPath );
-+ virtual const char * RelativePathToOSPath( const char *relativePath, const char *basePath );
-+ virtual const char * BuildOSPath( const char *base, const char *game, const char *relativePath );
-+ virtual void CreateOSPath( const char *OSPath );
-+ virtual bool FileIsInPAK( const char *relativePath );
-+ virtual void UpdatePureServerChecksums( void );
-+ virtual fsPureReply_t SetPureServerChecksums( const int pureChecksums[ MAX_PURE_PAKS ], int missingChecksums[ MAX_PURE_PAKS ] );
-+ virtual void GetPureServerChecksums( int checksums[ MAX_PURE_PAKS ] );
-+ virtual void SetRestartChecksums( const int pureChecksums[ MAX_PURE_PAKS ] );
-+ virtual void ClearPureChecksums( void );
-+ virtual int ReadFile( const char *relativePath, void **buffer, ID_TIME_T *timestamp );
-+ virtual void FreeFile( void *buffer );
-+ virtual int WriteFile( const char *relativePath, const void *buffer, int size, const char *basePath = "fs_savepath" );
-+ virtual void RemoveFile( const char *relativePath );
-+ virtual idFile * OpenFileReadFlags( const char *relativePath, int searchFlags, pack_t **foundInPak = NULL, bool allowCopyFiles = true, const char* gamedir = NULL );
-+ virtual idFile * OpenFileRead( const char *relativePath, bool allowCopyFiles = true, const char* gamedir = NULL );
-+ virtual idFile * OpenFileWrite( const char *relativePath, const char *basePath = "fs_savepath" );
-+ virtual idFile * OpenFileAppend( const char *relativePath, bool sync = false, const char *basePath = "fs_basepath" );
-+ virtual idFile * OpenFileByMode( const char *relativePath, fsMode_t mode );
-+ virtual idFile * OpenExplicitFileRead( const char *OSPath );
-+ virtual idFile * OpenExplicitFileWrite( const char *OSPath );
-+ virtual void CloseFile( idFile *f );
-+ virtual void BackgroundDownload( backgroundDownload_t *bgl );
-+ virtual void ResetReadCount( void ) { readCount = 0; }
-+ virtual void AddToReadCount( int c ) { readCount += c; }
-+ virtual int GetReadCount( void ) { return readCount; }
-+ virtual void FindDLL( const char *basename, char dllPath[ MAX_OSPATH ] );
-+ virtual void ClearDirCache( void );
-+ virtual bool HasD3XP( void );
-+ virtual bool RunningD3XP( void );
-+ virtual void CopyFile( const char *fromOSPath, const char *toOSPath );
-+ virtual int ValidateDownloadPakForChecksum( int checksum, char path[ MAX_STRING_CHARS ] );
-+ virtual idFile * MakeTemporaryFile( void );
-+ virtual int AddZipFile( const char *path );
-+ virtual findFile_t FindFile( const char *path, bool scheduleAddons );
-+ virtual int GetNumMaps();
-+ virtual const idDict * GetMapDecl( int i );
-+ virtual void FindMapScreenshot( const char *path, char *buf, int len );
-+ virtual bool FilenameCompare( const char *s1, const char *s2 ) const;
-+
-+ static void Dir_f( const idCmdArgs &args );
-+ static void DirTree_f( const idCmdArgs &args );
-+ static void Path_f( const idCmdArgs &args );
-+ static void TouchFile_f( const idCmdArgs &args );
-+ static void TouchFileList_f( const idCmdArgs &args );
-+
-+private:
-+ friend int BackgroundDownloadThread( void *pexit );
-+
-+ searchpath_t * searchPaths;
-+ int readCount; // total bytes read
-+ int loadCount; // total files read
-+ int loadStack; // total files in memory
-+ idStr gameFolder; // this will be a single name without separators
-+
-+ searchpath_t *addonPaks; // not loaded up, but we saw them
-+
-+ idDict mapDict; // for GetMapDecl
-+
-+ static idCVar fs_debug;
-+ static idCVar fs_restrict;
-+ static idCVar fs_copyfiles;
-+ static idCVar fs_basepath;
-+ static idCVar fs_configpath;
-+ static idCVar fs_savepath;
-+ static idCVar fs_cdpath;
-+ static idCVar fs_devpath;
-+ static idCVar fs_game;
-+ static idCVar fs_game_base;
-+ static idCVar fs_caseSensitiveOS;
-+ static idCVar fs_searchAddons;
-+
-+ backgroundDownload_t * backgroundDownloads;
-+ backgroundDownload_t defaultBackgroundDownload;
-+ xthreadInfo backgroundThread;
-+ bool backgroundThread_exit;
-+
-+ idList<pack_t *> serverPaks;
-+ bool loadedFileFromDir; // set to true once a file was loaded from a directory - can't switch to pure anymore
-+ idList<int> restartChecksums; // used during a restart to set things in right order
-+ idList<int> addonChecksums; // list of checksums that should go to the search list directly ( for restarts )
-+
-+ idDEntry dir_cache[ MAX_CACHED_DIRS ]; // fifo
-+ int dir_cache_index;
-+ int dir_cache_count;
-+
-+ int d3xp; // 0: didn't check, -1: not installed, 1: installed
-+
-+private:
-+ void ReplaceSeparators( idStr &path, char sep = PATHSEPERATOR_CHAR );
-+ int HashFileName( const char *fname ) const;
-+ int ListOSFiles( const char *directory, const char *extension, idStrList &list );
-+ FILE * OpenOSFile( const char *name, const char *mode, idStr *caseSensitiveName = NULL );
-+ FILE * OpenOSFileCorrectName( idStr &path, const char *mode );
-+ int DirectFileLength( FILE *o );
-+ void CopyFile( idFile *src, const char *toOSPath );
-+ int AddUnique( const char *name, idStrList &list, idHashIndex &hashIndex ) const;
-+ void GetExtensionList( const char *extension, idStrList &extensionList ) const;
-+ int GetFileList( const char *relativePath, const idStrList &extensions, idStrList &list, idHashIndex &hashIndex, bool fullRelativePath, const char* gamedir = NULL );
-+
-+ int GetFileListTree( const char *relativePath, const idStrList &extensions, idStrList &list, idHashIndex &hashIndex, const char* gamedir = NULL );
-+ pack_t * LoadZipFile( const char *zipfile );
-+ void AddGameDirectory( const char *path, const char *dir );
-+ void SetupGameDirectories( const char *gameName );
-+ void Startup( void );
-+ // some files can be obtained from directories without compromising si_pure
-+ bool FileAllowedFromDir( const char *path );
-+ // searches all the paks, no pure check
-+ pack_t * GetPackForChecksum( int checksum, bool searchAddons = false );
-+ // searches all the paks, no pure check
-+ pack_t * FindPakForFileChecksum( const char *relativePath, int fileChecksum, bool bReference );
-+ idFile_InZip * ReadFileFromZip( pack_t *pak, fileInPack_t *pakFile, const char *relativePath );
-+ int GetFileChecksum( idFile *file );
-+ pureStatus_t GetPackStatus( pack_t *pak );
-+ addonInfo_t * ParseAddonDef( const char *buf, const int len );
-+ void FollowAddonDependencies( pack_t *pak );
-+
-+ static size_t CurlWriteFunction( void *ptr, size_t size, size_t nmemb, void *stream );
-+ // curl_progress_callback in curl.h
-+ static int CurlProgressFunction( void *clientp, double dltotal, double dlnow, double ultotal, double ulnow );
-+};
-+
-+idCVar idFileSystemLocal::fs_restrict( "fs_restrict", "", CVAR_SYSTEM | CVAR_INIT | CVAR_BOOL, "" );
-+idCVar idFileSystemLocal::fs_debug( "fs_debug", "0", CVAR_SYSTEM | CVAR_INTEGER, "", 0, 2, idCmdSystem::ArgCompletion_Integer<0,2> );
-+idCVar idFileSystemLocal::fs_copyfiles( "fs_copyfiles", "0", CVAR_SYSTEM | CVAR_INIT | CVAR_INTEGER, "", 0, 4, idCmdSystem::ArgCompletion_Integer<0,3> );
-+idCVar idFileSystemLocal::fs_basepath( "fs_basepath", "", CVAR_SYSTEM | CVAR_INIT, "" );
-+idCVar idFileSystemLocal::fs_configpath( "fs_configpath", "", CVAR_SYSTEM | CVAR_INIT, "" );
-+idCVar idFileSystemLocal::fs_savepath( "fs_savepath", "", CVAR_SYSTEM | CVAR_INIT, "" );
-+idCVar idFileSystemLocal::fs_cdpath( "fs_cdpath", "", CVAR_SYSTEM | CVAR_INIT, "" );
-+idCVar idFileSystemLocal::fs_devpath( "fs_devpath", "", CVAR_SYSTEM | CVAR_INIT, "" );
-+idCVar idFileSystemLocal::fs_game( "fs_game", "", CVAR_SYSTEM | CVAR_INIT | CVAR_SERVERINFO, "mod path" );
-+idCVar idFileSystemLocal::fs_game_base( "fs_game_base", "", CVAR_SYSTEM | CVAR_INIT | CVAR_SERVERINFO, "alternate mod path, searched after the main fs_game path, before the basedir" );
-+#ifdef WIN32
-+idCVar idFileSystemLocal::fs_caseSensitiveOS( "fs_caseSensitiveOS", "0", CVAR_SYSTEM | CVAR_BOOL, "" );
-+#else
-+idCVar idFileSystemLocal::fs_caseSensitiveOS( "fs_caseSensitiveOS", "1", CVAR_SYSTEM | CVAR_BOOL, "" );
-+#endif
-+idCVar idFileSystemLocal::fs_searchAddons( "fs_searchAddons", "0", CVAR_SYSTEM | CVAR_BOOL, "search all addon pk4s ( disables addon functionality )" );
-+
-+idFileSystemLocal fileSystemLocal;
-+idFileSystem * fileSystem = &fileSystemLocal;
-+
-+/*
-+================
-+idFileSystemLocal::idFileSystemLocal
-+================
-+*/
-+idFileSystemLocal::idFileSystemLocal( void ) {
-+ searchPaths = NULL;
-+ readCount = 0;
-+ loadCount = 0;
-+ loadStack = 0;
-+ dir_cache_index = 0;
-+ dir_cache_count = 0;
-+ d3xp = 0;
-+ loadedFileFromDir = false;
-+ memset( &backgroundThread, 0, sizeof( backgroundThread ) );
-+ backgroundThread_exit = false;
-+ addonPaks = NULL;
-+}
-+
-+/*
-+================
-+idFileSystemLocal::HashFileName
-+
-+return a hash value for the filename
-+================
-+*/
-+int idFileSystemLocal::HashFileName( const char *fname ) const {
-+ int i;
-+ int hash;
-+ char letter;
-+
-+ hash = 0;
-+ i = 0;
-+ while( fname[i] != '\0' ) {
-+ letter = idStr::ToLower( fname[i] );
-+ if ( letter == '.' ) {
-+ break; // don't include extension
-+ }
-+ if ( letter == '\\' ) {
-+ letter = '/'; // damn path names
-+ }
-+ hash += (int)(letter) * (i+119);
-+ i++;
-+ }
-+ hash &= (FILE_HASH_SIZE-1);
-+ return hash;
-+}
-+
-+/*
-+===========
-+idFileSystemLocal::FilenameCompare
-+
-+Ignore case and separator char distinctions
-+
-+RETURNS false WHEN EQUAL THIS IS SO STUPID IT HURTS!
-+===========
-+*/
-+bool idFileSystemLocal::FilenameCompare( const char *s1, const char *s2 ) const {
-+ int c1, c2;
-+
-+ do {
-+ c1 = *s1++;
-+ c2 = *s2++;
-+
-+ if ( c1 >= 'a' && c1 <= 'z' ) {
-+ c1 -= ('a' - 'A');
-+ }
-+ if ( c2 >= 'a' && c2 <= 'z' ) {
-+ c2 -= ('a' - 'A');
-+ }
-+
-+ if ( c1 == '\\' || c1 == ':' ) {
-+ c1 = '/';
-+ }
-+ if ( c2 == '\\' || c2 == ':' ) {
-+ c2 = '/';
-+ }
-+
-+ if ( c1 != c2 ) {
-+ return true; // strings not equal
-+ }
-+ } while( c1 );
-+
-+ return false; // strings are equal
-+}
-+
-+/*
-+================
-+idFileSystemLocal::OpenOSFile
-+optional caseSensitiveName is set to case sensitive file name as found on disc (fs_caseSensitiveOS only)
-+================
-+*/
-+FILE *idFileSystemLocal::OpenOSFile( const char *fileName, const char *mode, idStr *caseSensitiveName ) {
-+ int i;
-+ FILE *fp;
-+ idStr fpath, entry;
-+ idStrList list;
-+
-+#ifndef WIN32
-+ // some systems will let you fopen a directory
-+ struct stat buf;
-+ if ( stat( fileName, &buf ) != -1 && !S_ISREG(buf.st_mode) ) {
-+ return NULL;
-+ }
-+#endif
-+ fp = fopen( fileName, mode );
-+ if ( !fp && fs_caseSensitiveOS.GetBool() ) {
-+ fpath = fileName;
-+ fpath.StripFilename();
-+ fpath.StripTrailing( PATHSEPERATOR_CHAR );
-+ if ( ListOSFiles( fpath, NULL, list ) == -1 ) {
-+ return NULL;
-+ }
-+
-+ for ( i = 0; i < list.Num(); i++ ) {
-+ entry = fpath + PATHSEPERATOR_CHAR + list[i];
-+ if ( !entry.Icmp( fileName ) ) {
-+ fp = fopen( entry, mode );
-+ if ( fp ) {
-+ if ( caseSensitiveName ) {
-+ *caseSensitiveName = entry;
-+ caseSensitiveName->StripPath();
-+ }
-+ if ( fs_debug.GetInteger() ) {
-+ common->Printf( "idFileSystemLocal::OpenFileRead: changed %s to %s\n", fileName, entry.c_str() );
-+ }
-+ break;
-+ } else {
-+ // not supposed to happen if ListOSFiles is doing it's job correctly
-+ common->Warning( "idFileSystemLocal::OpenFileRead: fs_caseSensitiveOS 1 could not open %s", entry.c_str() );
-+ }
-+ }
-+ }
-+ } else if ( caseSensitiveName ) {
-+ *caseSensitiveName = fileName;
-+ caseSensitiveName->StripPath();
-+ }
-+ return fp;
-+}
-+
-+/*
-+================
-+idFileSystemLocal::OpenOSFileCorrectName
-+================
-+*/
-+FILE *idFileSystemLocal::OpenOSFileCorrectName( idStr &path, const char *mode ) {
-+ idStr caseName;
-+ FILE *f = OpenOSFile( path.c_str(), mode, &caseName );
-+ if ( f ) {
-+ path.StripFilename();
-+ path += PATHSEPERATOR_STR;
-+ path += caseName;
-+ }
-+ return f;
-+}
-+
-+/*
-+================
-+idFileSystemLocal::DirectFileLength
-+================
-+*/
-+int idFileSystemLocal::DirectFileLength( FILE *o ) {
-+ int pos;
-+ int end;
-+
-+ pos = ftell( o );
-+ fseek( o, 0, SEEK_END );
-+ end = ftell( o );
-+ fseek( o, pos, SEEK_SET );
-+ return end;
-+}
-+
-+/*
-+============
-+idFileSystemLocal::CreateOSPath
-+
-+Creates any directories needed to store the given filename
-+============
-+*/
-+void idFileSystemLocal::CreateOSPath( const char *OSPath ) {
-+ char *ofs;
-+
-+ // make absolutely sure that it can't back up the path
-+ // FIXME: what about c: ?
-+ if ( strstr( OSPath, ".." ) || strstr( OSPath, "::" ) ) {
-+#ifdef _DEBUG
-+ common->DPrintf( "refusing to create relative path \"%s\"\n", OSPath );
-+#endif
-+ return;
-+ }
-+
-+ idStr path( OSPath );
-+ for( ofs = &path[ 1 ]; *ofs ; ofs++ ) {
-+ if ( *ofs == PATHSEPERATOR_CHAR ) {
-+ // create the directory
-+ *ofs = 0;
-+ Sys_Mkdir( path );
-+ *ofs = PATHSEPERATOR_CHAR;
-+ }
-+ }
-+}
-+
-+/*
-+=================
-+idFileSystemLocal::CopyFile
-+
-+Copy a fully specified file from one place to another
-+=================
-+*/
-+void idFileSystemLocal::CopyFile( const char *fromOSPath, const char *toOSPath ) {
-+ FILE *f;
-+ int len;
-+ byte *buf;
-+
-+ common->Printf( "copy %s to %s\n", fromOSPath, toOSPath );
-+ f = OpenOSFile( fromOSPath, "rb" );
-+ if ( !f ) {
-+ return;
-+ }
-+ fseek( f, 0, SEEK_END );
-+ len = ftell( f );
-+ fseek( f, 0, SEEK_SET );
-+
-+ buf = (byte *)Mem_Alloc( len );
-+ if ( fread( buf, 1, len, f ) != (unsigned int)len ) {
-+ common->FatalError( "short read in idFileSystemLocal::CopyFile()\n" );
-+ }
-+ fclose( f );
-+
-+ CreateOSPath( toOSPath );
-+ f = OpenOSFile( toOSPath, "wb" );
-+ if ( !f ) {
-+ common->Printf( "could not create destination file\n" );
-+ Mem_Free( buf );
-+ return;
-+ }
-+ if ( fwrite( buf, 1, len, f ) != (unsigned int)len ) {
-+ common->FatalError( "short write in idFileSystemLocal::CopyFile()\n" );
-+ }
-+ fclose( f );
-+ Mem_Free( buf );
-+}
-+
-+/*
-+=================
-+idFileSystemLocal::CopyFile
-+=================
-+*/
-+void idFileSystemLocal::CopyFile( idFile *src, const char *toOSPath ) {
-+ FILE *f;
-+ int len;
-+ byte *buf;
-+
-+ common->Printf( "copy %s to %s\n", src->GetName(), toOSPath );
-+ src->Seek( 0, FS_SEEK_END );
-+ len = src->Tell();
-+ src->Seek( 0, FS_SEEK_SET );
-+
-+ buf = (byte *)Mem_Alloc( len );
-+ if ( src->Read( buf, len ) != len ) {
-+ common->FatalError( "Short read in idFileSystemLocal::CopyFile()\n" );
-+ }
-+
-+ CreateOSPath( toOSPath );
-+ f = OpenOSFile( toOSPath, "wb" );
-+ if ( !f ) {
-+ common->Printf( "could not create destination file\n" );
-+ Mem_Free( buf );
-+ return;
-+ }
-+ if ( fwrite( buf, 1, len, f ) != (unsigned int)len ) {
-+ common->FatalError( "Short write in idFileSystemLocal::CopyFile()\n" );
-+ }
-+ fclose( f );
-+ Mem_Free( buf );
-+}
-+
-+/*
-+====================
-+idFileSystemLocal::ReplaceSeparators
-+
-+Fix things up differently for win/unix/mac
-+====================
-+*/
-+void idFileSystemLocal::ReplaceSeparators( idStr &path, char sep ) {
-+ char *s;
-+
-+ for( s = &path[ 0 ]; *s ; s++ ) {
-+ if ( *s == '/' || *s == '\\' ) {
-+ *s = sep;
-+ }
-+ }
-+}
-+
-+/*
-+===================
-+idFileSystemLocal::BuildOSPath
-+===================
-+*/
-+const char *idFileSystemLocal::BuildOSPath( const char *base, const char *game, const char *relativePath ) {
-+ static char OSPath[MAX_STRING_CHARS];
-+ idStr newPath;
-+
-+ if ( fs_caseSensitiveOS.GetBool() || com_developer.GetBool() ) {
-+ // extract the path, make sure it's all lowercase
-+ idStr testPath, fileName;
-+
-+ sprintf( testPath, "%s/%s", game , relativePath );
-+ testPath.StripFilename();
-+
-+ if ( testPath.HasUpper() ) {
-+
-+ common->DPrintf( "Non-portable: path contains uppercase characters: %s", testPath.c_str() );
-+
-+ // attempt a fixup on the fly
-+ if ( fs_caseSensitiveOS.GetBool() ) {
-+ testPath.ToLower();
-+ fileName = relativePath;
-+ fileName.StripPath();
-+ sprintf( newPath, "%s/%s/%s", base, testPath.c_str(), fileName.c_str() );
-+ ReplaceSeparators( newPath );
-+ common->DPrintf( "Fixed up to %s\n", newPath.c_str() );
-+ idStr::Copynz( OSPath, newPath, sizeof( OSPath ) );
-+ return OSPath;
-+ }
-+ }
-+ }
-+
-+ idStr strBase = base;
-+ strBase.StripTrailing( '/' );
-+ strBase.StripTrailing( '\\' );
-+ sprintf( newPath, "%s/%s/%s", strBase.c_str(), game, relativePath );
-+ ReplaceSeparators( newPath );
-+ idStr::Copynz( OSPath, newPath, sizeof( OSPath ) );
-+ return OSPath;
-+}
-+
-+/*
-+================
-+idFileSystemLocal::OSPathToRelativePath
-+
-+takes a full OS path, as might be found in data from a media creation
-+program, and converts it to a relativePath by stripping off directories
-+
-+Returns false if the osPath tree doesn't match any of the existing
-+search paths.
-+
-+================
-+*/
-+const char *idFileSystemLocal::OSPathToRelativePath( const char *OSPath ) {
-+ static char relativePath[MAX_STRING_CHARS];
-+ const char *s, *base;
-+
-+ // skip a drive letter?
-+
-+ // search for anything with "base" in it
-+ // Ase files from max may have the form of:
-+ // "//Purgatory/purgatory/doom/base/models/mapobjects/bitch/hologirl.tga"
-+ // which won't match any of our drive letter based search paths
-+ // look for the first complete directory name
-+ base = strstr( OSPath, BASE_GAMEDIR );
-+ while ( base ) {
-+ char c1 = '\0', c2;
-+ if ( base > OSPath ) {
-+ c1 = *(base - 1);
-+ }
-+ c2 = *( base + strlen( BASE_GAMEDIR ) );
-+ if ( ( c1 == '/' || c1 == '\\' ) && ( c2 == '/' || c2 == '\\' ) ) {
-+ break;
-+ }
-+ base = strstr( base + 1, BASE_GAMEDIR );
-+ }
-+
-+ // fs_game and fs_game_base support - look for first complete name with a mod path
-+ // ( fs_game searched before fs_game_base )
-+ const char *fsgame = NULL;
-+ int igame = 0;
-+ for ( igame = 0; igame < 2; igame++ ) {
-+ if ( igame == 0 ) {
-+ fsgame = fs_game.GetString();
-+ } else if ( igame == 1 ) {
-+ fsgame = fs_game_base.GetString();
-+ }
-+ if ( base == NULL && fsgame && strlen( fsgame ) ) {
-+ base = strstr( OSPath, fsgame );
-+ while ( base ) {
-+ char c1 = '\0', c2;
-+ if ( base > OSPath ) {
-+ c1 = *(base - 1);
-+ }
-+ c2 = *( base + strlen( fsgame ) );
-+ if ( ( c1 == '/' || c1 == '\\' ) && ( c2 == '/' || c2 == '\\' ) ) {
-+ break;
-+ }
-+ base = strstr( base + 1, fsgame );
-+ }
-+ }
-+ }
-+
-+ if ( base ) {
-+ s = strstr( base, "/" );
-+ if ( !s ) {
-+ s = strstr( base, "\\" );
-+ }
-+ if ( s ) {
-+ strcpy( relativePath, s + 1 );
-+ if ( fs_debug.GetInteger() > 1 ) {
-+ common->Printf( "idFileSystem::OSPathToRelativePath: %s becomes %s\n", OSPath, relativePath );
-+ }
-+ return relativePath;
-+ }
-+ }
-+
-+ common->Warning( "idFileSystem::OSPathToRelativePath failed on %s", OSPath );
-+
-+ strcpy( relativePath, "" );
-+ return relativePath;
-+}
-+
-+/*
-+=====================
-+idFileSystemLocal::RelativePathToOSPath
-+
-+Returns a fully qualified path that can be used with stdio libraries
-+=====================
-+*/
-+const char *idFileSystemLocal::RelativePathToOSPath( const char *relativePath, const char *basePath ) {
-+ const char *path = cvarSystem->GetCVarString( basePath );
-+ if ( !path[0] ) {
-+ path = fs_savepath.GetString();
-+ }
-+ return BuildOSPath( path, gameFolder, relativePath );
-+}
-+
-+/*
-+=================
-+idFileSystemLocal::RemoveFile
-+=================
-+*/
-+void idFileSystemLocal::RemoveFile( const char *relativePath ) {
-+ idStr OSPath;
-+
-+ if ( fs_devpath.GetString()[0] ) {
-+ OSPath = BuildOSPath( fs_devpath.GetString(), gameFolder, relativePath );
-+ remove( OSPath );
-+ }
-+
-+ OSPath = BuildOSPath( fs_savepath.GetString(), gameFolder, relativePath );
-+ remove( OSPath );
-+
-+ ClearDirCache();
-+}
-+
-+/*
-+================
-+idFileSystemLocal::FileIsInPAK
-+================
-+*/
-+bool idFileSystemLocal::FileIsInPAK( const char *relativePath ) {
-+ searchpath_t *search;
-+ pack_t *pak;
-+ fileInPack_t *pakFile;
-+ int hash;
-+
-+ if ( !searchPaths ) {
-+ common->FatalError( "Filesystem call made without initialization\n" );
-+ }
-+
-+ if ( !relativePath ) {
-+ common->FatalError( "idFileSystemLocal::FileIsInPAK: NULL 'relativePath' parameter passed\n" );
-+ }
-+
-+ // qpaths are not supposed to have a leading slash
-+ if ( relativePath[0] == '/' || relativePath[0] == '\\' ) {
-+ relativePath++;
-+ }
-+
-+ // make absolutely sure that it can't back up the path.
-+ // The searchpaths do guarantee that something will always
-+ // be prepended, so we don't need to worry about "c:" or "//limbo"
-+ if ( strstr( relativePath, ".." ) || strstr( relativePath, "::" ) ) {
-+ return false;
-+ }
-+
-+ //
-+ // search through the path, one element at a time
-+ //
-+
-+ hash = HashFileName( relativePath );
-+
-+ for ( search = searchPaths; search; search = search->next ) {
-+ // is the element a pak file?
-+ if ( search->pack && search->pack->hashTable[hash] ) {
-+
-+ // disregard if it doesn't match one of the allowed pure pak files - or is a localization file
-+ if ( serverPaks.Num() ) {
-+ GetPackStatus( search->pack );
-+ if ( search->pack->pureStatus != PURE_NEVER && !serverPaks.Find( search->pack ) ) {
-+ continue; // not on the pure server pak list
-+ }
-+ }
-+
-+ // look through all the pak file elements
-+ pak = search->pack;
-+ pakFile = pak->hashTable[hash];
-+ do {
-+ // case and separator insensitive comparisons
-+ if ( !FilenameCompare( pakFile->name, relativePath ) ) {
-+ return true;
-+ }
-+ pakFile = pakFile->next;
-+ } while( pakFile != NULL );
-+ }
-+ }
-+ return false;
-+}
-+
-+/*
-+============
-+idFileSystemLocal::ReadFile
-+
-+Filename are relative to the search path
-+a null buffer will just return the file length and time without loading
-+timestamp can be NULL if not required
-+============
-+*/
-+int idFileSystemLocal::ReadFile( const char *relativePath, void **buffer, ID_TIME_T *timestamp ) {
-+ idFile * f;
-+ byte * buf;
-+ int len;
-+ bool isConfig;
-+
-+ if ( !searchPaths ) {
-+ common->FatalError( "Filesystem call made without initialization\n" );
-+ }
-+
-+ if ( !relativePath || !relativePath[0] ) {
-+ common->FatalError( "idFileSystemLocal::ReadFile with empty name\n" );
-+ }
-+
-+ if ( timestamp ) {
-+ *timestamp = FILE_NOT_FOUND_TIMESTAMP;
-+ }
-+
-+ if ( buffer ) {
-+ *buffer = NULL;
-+ }
-+
-+ buf = NULL; // quiet compiler warning
-+
-+ // if this is a .cfg file and we are playing back a journal, read
-+ // it from the journal file
-+ if ( strstr( relativePath, ".cfg" ) == relativePath + strlen( relativePath ) - 4 ) {
-+ isConfig = true;
-+ if ( eventLoop && eventLoop->JournalLevel() == 2 ) {
-+ int r;
-+
-+ loadCount++;
-+ loadStack++;
-+
-+ common->DPrintf( "Loading %s from journal file.\n", relativePath );
-+ len = 0;
-+ r = eventLoop->com_journalDataFile->Read( &len, sizeof( len ) );
-+ if ( r != sizeof( len ) ) {
-+ *buffer = NULL;
-+ return -1;
-+ }
-+ buf = (byte *)Mem_ClearedAlloc(len+1);
-+ *buffer = buf;
-+ r = eventLoop->com_journalDataFile->Read( buf, len );
-+ if ( r != len ) {
-+ common->FatalError( "Read from journalDataFile failed" );
-+ }
-+
-+ // guarantee that it will have a trailing 0 for string operations
-+ buf[len] = 0;
-+
-+ return len;
-+ }
-+ } else {
-+ isConfig = false;
-+ }
-+
-+ // look for it in the filesystem or pack files
-+ f = OpenFileRead( relativePath, ( buffer != NULL ) );
-+ if ( f == NULL ) {
-+ if ( buffer ) {
-+ *buffer = NULL;
-+ }
-+ return -1;
-+ }
-+ len = f->Length();
-+
-+ if ( timestamp ) {
-+ *timestamp = f->Timestamp();
-+ }
-+
-+ if ( !buffer ) {
-+ CloseFile( f );
-+ return len;
-+ }
-+
-+ loadCount++;
-+ loadStack++;
-+
-+ buf = (byte *)Mem_ClearedAlloc(len+1);
-+ *buffer = buf;
-+
-+ f->Read( buf, len );
-+
-+ // guarantee that it will have a trailing 0 for string operations
-+ buf[len] = 0;
-+ CloseFile( f );
-+
-+ // if we are journalling and it is a config file, write it to the journal file
-+ if ( isConfig && eventLoop && eventLoop->JournalLevel() == 1 ) {
-+ common->DPrintf( "Writing %s to journal file.\n", relativePath );
-+ eventLoop->com_journalDataFile->Write( &len, sizeof( len ) );
-+ eventLoop->com_journalDataFile->Write( buf, len );
-+ eventLoop->com_journalDataFile->Flush();
-+ }
-+
-+ return len;
-+}
-+
-+/*
-+=============
-+idFileSystemLocal::FreeFile
-+=============
-+*/
-+void idFileSystemLocal::FreeFile( void *buffer ) {
-+ if ( !searchPaths ) {
-+ common->FatalError( "Filesystem call made without initialization\n" );
-+ }
-+ if ( !buffer ) {
-+ common->FatalError( "idFileSystemLocal::FreeFile( NULL )" );
-+ }
-+ loadStack--;
-+
-+ Mem_Free( buffer );
-+}
-+
-+/*
-+============
-+idFileSystemLocal::WriteFile
-+
-+Filenames are relative to the search path
-+============
-+*/
-+int idFileSystemLocal::WriteFile( const char *relativePath, const void *buffer, int size, const char *basePath ) {
-+ idFile *f;
-+
-+ if ( !searchPaths ) {
-+ common->FatalError( "Filesystem call made without initialization\n" );
-+ }
-+
-+ if ( !relativePath || !buffer ) {
-+ common->FatalError( "idFileSystemLocal::WriteFile: NULL parameter" );
-+ }
-+
-+ f = idFileSystemLocal::OpenFileWrite( relativePath, basePath );
-+ if ( !f ) {
-+ common->Printf( "Failed to open %s\n", relativePath );
-+ return -1;
-+ }
-+
-+ size = f->Write( buffer, size );
-+
-+ CloseFile( f );
-+
-+ return size;
-+}
-+
-+/*
-+=================
-+idFileSystemLocal::ParseAddonDef
-+=================
-+*/
-+addonInfo_t *idFileSystemLocal::ParseAddonDef( const char *buf, const int len ) {
-+ idLexer src;
-+ idToken token, token2;
-+ addonInfo_t *info;
-+
-+ src.LoadMemory( buf, len, "<addon.conf>" );
-+ src.SetFlags( DECL_LEXER_FLAGS );
-+ if ( !src.SkipUntilString( "addonDef" ) ) {
-+ src.Warning( "ParseAddonDef: no addonDef" );
-+ return NULL;
-+ }
-+ if ( !src.ReadToken( &token ) ) {
-+ src.Warning( "Expected {" );
-+ return NULL;
-+ }
-+ info = new addonInfo_t;
-+ // read addonDef
-+ while ( 1 ) {
-+ if ( !src.ReadToken( &token ) ) {
-+ delete info;
-+ return NULL;
-+ }
-+ if ( !token.Icmp( "}" ) ) {
-+ break;
-+ }
-+ if ( token.type != TT_STRING ) {
-+ src.Warning( "Expected quoted string, but found '%s'", token.c_str() );
-+ delete info;
-+ return NULL;
-+ }
-+ int checksum;
-+ if ( sscanf( token.c_str(), "0x%x", &checksum ) != 1 && sscanf( token.c_str(), "%x", &checksum ) != 1 ) {
-+ src.Warning( "Could not parse checksum '%s'", token.c_str() );
-+ delete info;
-+ return NULL;
-+ }
-+ info->depends.Append( checksum );
-+ }
-+ // read any number of mapDef entries
-+ while ( 1 ) {
-+ if ( !src.SkipUntilString( "mapDef" ) ) {
-+ return info;
-+ }
-+ if ( !src.ReadToken( &token ) ) {
-+ src.Warning( "Expected map path" );
-+ info->mapDecls.DeleteContents( true );
-+ delete info;
-+ return NULL;
-+ }
-+ idDict *dict = new idDict;
-+ dict->Set( "path", token.c_str() );
-+ if ( !src.ReadToken( &token ) ) {
-+ src.Warning( "Expected {" );
-+ info->mapDecls.DeleteContents( true );
-+ delete dict;
-+ delete info;
-+ return NULL;
-+ }
-+ while ( 1 ) {
-+ if ( !src.ReadToken( &token ) ) {
-+ break;
-+ }
-+ if ( !token.Icmp( "}" ) ) {
-+ break;
-+ }
-+ if ( token.type != TT_STRING ) {
-+ src.Warning( "Expected quoted string, but found '%s'", token.c_str() );
-+ info->mapDecls.DeleteContents( true );
-+ delete dict;
-+ delete info;
-+ return NULL;
-+ }
-+
-+ if ( !src.ReadToken( &token2 ) ) {
-+ src.Warning( "Unexpected end of file" );
-+ info->mapDecls.DeleteContents( true );
-+ delete dict;
-+ delete info;
-+ return NULL;
-+ }
-+
-+ if ( dict->FindKey( token ) ) {
-+ src.Warning( "'%s' already defined", token.c_str() );
-+ }
-+ dict->Set( token, token2 );
-+ }
-+ info->mapDecls.Append( dict );
-+ }
-+ assert( false );
-+ return NULL;
-+}
-+
-+/*
-+=================
-+idFileSystemLocal::LoadZipFile
-+=================
-+*/
-+pack_t *idFileSystemLocal::LoadZipFile( const char *zipfile ) {
-+ fileInPack_t * buildBuffer;
-+ pack_t * pack;
-+ unzFile uf;
-+ int err;
-+ unz_global_info64 gi;
-+ char filename_inzip[MAX_ZIPPED_FILE_NAME];
-+ unz_file_info64 file_info;
-+ int i;
-+ int hash;
-+ int fs_numHeaderLongs;
-+ int * fs_headerLongs;
-+ FILE *f;
-+ int len;
-+ int confHash;
-+ fileInPack_t *pakFile;
-+
-+ f = OpenOSFile( zipfile, "rb" );
-+ if ( !f ) {
-+ return NULL;
-+ }
-+ fseek( f, 0, SEEK_END );
-+ len = ftell( f );
-+ fclose( f );
-+
-+ fs_numHeaderLongs = 0;
-+
-+ uf = unzOpen( zipfile );
-+ err = unzGetGlobalInfo64( uf, &gi );
-+
-+ if ( err != UNZ_OK ) {
-+ return NULL;
-+ }
-+
-+ buildBuffer = new fileInPack_t[gi.number_entry];
-+ pack = new pack_t;
-+ for( i = 0; i < FILE_HASH_SIZE; i++ ) {
-+ pack->hashTable[i] = NULL;
-+ }
-+
-+ pack->pakFilename = zipfile;
-+ pack->handle = uf;
-+ pack->numfiles = gi.number_entry;
-+ pack->buildBuffer = buildBuffer;
-+ pack->referenced = false;
-+ pack->addon = false;
-+ pack->addon_search = false;
-+ pack->addon_info = NULL;
-+ pack->pureStatus = PURE_UNKNOWN;
-+ pack->isNew = false;
-+
-+ pack->length = len;
-+
-+ unzGoToFirstFile(uf);
-+ fs_headerLongs = (int *)Mem_ClearedAlloc( gi.number_entry * sizeof(int) );
-+ for ( i = 0; i < (int)gi.number_entry; i++ ) {
-+ err = unzGetCurrentFileInfo64( uf, &file_info, filename_inzip, sizeof(filename_inzip), NULL, 0, NULL, 0 );
-+ if ( err != UNZ_OK ) {
-+ break;
-+ }
-+ if ( file_info.uncompressed_size > 0 ) {
-+ fs_headerLongs[fs_numHeaderLongs++] = LittleInt( file_info.crc );
-+ }
-+ hash = HashFileName( filename_inzip );
-+ buildBuffer[i].name = filename_inzip;
-+ buildBuffer[i].name.ToLower();
-+ buildBuffer[i].name.BackSlashesToSlashes();
-+ // store the file position in the zip
-+ buildBuffer[i].pos = unzGetOffset64( uf );
-+ // add the file to the hash
-+ buildBuffer[i].next = pack->hashTable[hash];
-+ pack->hashTable[hash] = &buildBuffer[i];
-+ // go to the next file in the zip
-+ unzGoToNextFile(uf);
-+ }
-+
-+ // ignore all binary paks
-+ confHash = HashFileName(BINARY_CONFIG);
-+ for (pakFile = pack->hashTable[confHash]; pakFile; pakFile = pakFile->next) {
-+ if (!FilenameCompare(pakFile->name, BINARY_CONFIG)) {
-+ unzClose(uf);
-+ delete[] buildBuffer;
-+ delete pack;
-+
-+ return NULL;
-+ }
-+ }
-+
-+ // check if this is an addon pak
-+ pack->addon = false;
-+ confHash = HashFileName( ADDON_CONFIG );
-+ for ( pakFile = pack->hashTable[confHash]; pakFile; pakFile = pakFile->next ) {
-+ if ( !FilenameCompare( pakFile->name, ADDON_CONFIG ) ) {
-+ pack->addon = true;
-+ idFile_InZip *file = ReadFileFromZip( pack, pakFile, ADDON_CONFIG );
-+ // may be just an empty file if you don't bother about the mapDef
-+ if ( file && file->Length() ) {
-+ char *buf;
-+ buf = new char[ file->Length() + 1 ];
-+ file->Read( (void *)buf, file->Length() );
-+ buf[ file->Length() ] = '\0';
-+ pack->addon_info = ParseAddonDef( buf, file->Length() );
-+ delete[] buf;
-+ }
-+ if ( file ) {
-+ CloseFile( file );
-+ }
-+ break;
-+ }
-+ }
-+
-+ pack->checksum = MD4_BlockChecksum( fs_headerLongs, 4 * fs_numHeaderLongs );
-+ pack->checksum = LittleInt( pack->checksum );
-+
-+ Mem_Free( fs_headerLongs );
-+
-+ return pack;
-+}
-+
-+/*
-+===============
-+idFileSystemLocal::AddZipFile
-+adds a downloaded pak file to the list so we can work out what we have and what we still need
-+the isNew flag is set to true, indicating that we cannot add this pak to the search lists without a restart
-+===============
-+*/
-+int idFileSystemLocal::AddZipFile( const char *path ) {
-+ idStr fullpath = fs_savepath.GetString();
-+ pack_t *pak;
-+ searchpath_t *search, *last;
-+
-+ fullpath.AppendPath( path );
-+ pak = LoadZipFile( fullpath );
-+ if ( !pak ) {
-+ common->Warning( "AddZipFile %s failed\n", path );
-+ return 0;
-+ }
-+ // insert the pak at the end of the search list - temporary until we restart
-+ pak->isNew = true;
-+ search = new searchpath_t;
-+ search->dir = NULL;
-+ search->pack = pak;
-+ search->next = NULL;
-+ last = searchPaths;
-+ while ( last->next ) {
-+ last = last->next;
-+ }
-+ last->next = search;
-+ common->Printf( "Appended pk4 %s with checksum 0x%x\n", pak->pakFilename.c_str(), pak->checksum );
-+ return pak->checksum;
-+}
-+
-+/*
-+===============
-+idFileSystemLocal::AddUnique
-+===============
-+*/
-+int idFileSystemLocal::AddUnique( const char *name, idStrList &list, idHashIndex &hashIndex ) const {
-+ int i, hashKey;
-+
-+ hashKey = hashIndex.GenerateKey( name );
-+ for ( i = hashIndex.First( hashKey ); i >= 0; i = hashIndex.Next( i ) ) {
-+ if ( list[i].Icmp( name ) == 0 ) {
-+ return i;
-+ }
-+ }
-+ i = list.Append( name );
-+ hashIndex.Add( hashKey, i );
-+ return i;
-+}
-+
-+/*
-+===============
-+idFileSystemLocal::GetExtensionList
-+===============
-+*/
-+void idFileSystemLocal::GetExtensionList( const char *extension, idStrList &extensionList ) const {
-+ int s, e, l;
-+
-+ l = idStr::Length( extension );
-+ s = 0;
-+ while( 1 ) {
-+ e = idStr::FindChar( extension, '|', s, l );
-+ if ( e != -1 ) {
-+ extensionList.Append( idStr( extension, s, e ) );
-+ s = e + 1;
-+ } else {
-+ extensionList.Append( idStr( extension, s, l ) );
-+ break;
-+ }
-+ }
-+}
-+
-+/*
-+===============
-+idFileSystemLocal::GetFileList
-+
-+Does not clear the list first so this can be used to progressively build a file list.
-+When 'sort' is true only the new files added to the list are sorted.
-+===============
-+*/
-+int idFileSystemLocal::GetFileList( const char *relativePath, const idStrList &extensions, idStrList &list, idHashIndex &hashIndex, bool fullRelativePath, const char* gamedir ) {
-+ searchpath_t * search;
-+ fileInPack_t * buildBuffer;
-+ int i, j;
-+ int pathLength;
-+ int length;
-+ const char * name;
-+ pack_t * pak;
-+ idStr work;
-+
-+ if ( !searchPaths ) {
-+ common->FatalError( "Filesystem call made without initialization\n" );
-+ }
-+
-+ if ( !extensions.Num() ) {
-+ return 0;
-+ }
-+
-+ if ( !relativePath ) {
-+ return 0;
-+ }
-+ pathLength = strlen( relativePath );
-+ if ( pathLength ) {
-+ pathLength++; // for the trailing '/'
-+ }
-+
-+ // search through the path, one element at a time, adding to list
-+ for( search = searchPaths; search != NULL; search = search->next ) {
-+ if ( search->dir ) {
-+ if(gamedir && strlen(gamedir)) {
-+ if(search->dir->gamedir != gamedir) {
-+ continue;
-+ }
-+ }
-+
-+ idStrList sysFiles;
-+ idStr netpath;
-+
-+ netpath = BuildOSPath( search->dir->path, search->dir->gamedir, relativePath );
-+
-+ for ( i = 0; i < extensions.Num(); i++ ) {
-+
-+ // scan for files in the filesystem
-+ ListOSFiles( netpath, extensions[i], sysFiles );
-+
-+ // if we are searching for directories, remove . and ..
-+ if ( extensions[i][0] == '/' && extensions[i][1] == 0 ) {
-+ sysFiles.Remove( "." );
-+ sysFiles.Remove( ".." );
-+ }
-+
-+ for( j = 0; j < sysFiles.Num(); j++ ) {
-+ // unique the match
-+ if ( fullRelativePath ) {
-+ work = relativePath;
-+ work += "/";
-+ work += sysFiles[j];
-+ AddUnique( work, list, hashIndex );
-+ }
-+ else {
-+ AddUnique( sysFiles[j], list, hashIndex );
-+ }
-+ }
-+ }
-+ } else if ( search->pack ) {
-+ // look through all the pak file elements
-+
-+ // exclude any extra packs if we have server paks to search
-+ if ( serverPaks.Num() ) {
-+ GetPackStatus( search->pack );
-+ if ( search->pack->pureStatus != PURE_NEVER && !serverPaks.Find( search->pack ) ) {
-+ continue; // not on the pure server pak list
-+ }
-+ }
-+
-+ pak = search->pack;
-+ buildBuffer = pak->buildBuffer;
-+ for( i = 0; i < pak->numfiles; i++ ) {
-+
-+ length = buildBuffer[i].name.Length();
-+
-+ // if the name is not long anough to at least contain the path
-+ if ( length <= pathLength ) {
-+ continue;
-+ }
-+
-+ name = buildBuffer[i].name;
-+
-+
-+ // check for a path match without the trailing '/'
-+ if ( pathLength && idStr::Icmpn( name, relativePath, pathLength - 1 ) != 0 ) {
-+ continue;
-+ }
-+
-+ // ensure we have a path, and not just a filename containing the path
-+ if ( name[ pathLength ] == '\0' || name[pathLength - 1] != '/' ) {
-+ continue;
-+ }
-+
-+ // make sure the file is not in a subdirectory
-+ for ( j = pathLength; name[j+1] != '\0'; j++ ) {
-+ if ( name[j] == '/' ) {
-+ break;
-+ }
-+ }
-+ if ( name[j+1] ) {
-+ continue;
-+ }
-+
-+ // check for extension match
-+ for ( j = 0; j < extensions.Num(); j++ ) {
-+ if ( length >= extensions[j].Length() && extensions[j].Icmp( name + length - extensions[j].Length() ) == 0 ) {
-+ break;
-+ }
-+ }
-+ if ( j >= extensions.Num() ) {
-+ continue;
-+ }
-+
-+ // unique the match
-+ if ( fullRelativePath ) {
-+ work = relativePath;
-+ work += "/";
-+ work += name + pathLength;
-+ work.StripTrailing( '/' );
-+ AddUnique( work, list, hashIndex );
-+ } else {
-+ work = name + pathLength;
-+ work.StripTrailing( '/' );
-+ AddUnique( work, list, hashIndex );
-+ }
-+ }
-+ }
-+ }
-+
-+ return list.Num();
-+}
-+
-+/*
-+===============
-+idFileSystemLocal::ListFiles
-+===============
-+*/
-+idFileList *idFileSystemLocal::ListFiles( const char *relativePath, const char *extension, bool sort, bool fullRelativePath, const char* gamedir ) {
-+ idHashIndex hashIndex( 4096, 4096 );
-+ idStrList extensionList;
-+
-+ idFileList *fileList = new idFileList;
-+ fileList->basePath = relativePath;
-+
-+ GetExtensionList( extension, extensionList );
-+
-+ GetFileList( relativePath, extensionList, fileList->list, hashIndex, fullRelativePath, gamedir );
-+
-+ if ( sort ) {
-+ idStrListSortPaths( fileList->list );
-+ }
-+
-+ return fileList;
-+}
-+
-+/*
-+===============
-+idFileSystemLocal::GetFileListTree
-+===============
-+*/
-+int idFileSystemLocal::GetFileListTree( const char *relativePath, const idStrList &extensions, idStrList &list, idHashIndex &hashIndex, const char* gamedir ) {
-+ int i;
-+ idStrList slash, folders( 128 );
-+ idHashIndex folderHashIndex( 1024, 128 );
-+
-+ // recurse through the subdirectories
-+ slash.Append( "/" );
-+ GetFileList( relativePath, slash, folders, folderHashIndex, true, gamedir );
-+ for ( i = 0; i < folders.Num(); i++ ) {
-+ if ( folders[i][0] == '.' ) {
-+ continue;
-+ }
-+ if ( folders[i].Icmp( relativePath ) == 0 ){
-+ continue;
-+ }
-+ GetFileListTree( folders[i], extensions, list, hashIndex, gamedir );
-+ }
-+
-+ // list files in the current directory
-+ GetFileList( relativePath, extensions, list, hashIndex, true, gamedir );
-+
-+ return list.Num();
-+}
-+
-+/*
-+===============
-+idFileSystemLocal::ListFilesTree
-+===============
-+*/
-+idFileList *idFileSystemLocal::ListFilesTree( const char *relativePath, const char *extension, bool sort, const char* gamedir ) {
-+ idHashIndex hashIndex( 4096, 4096 );
-+ idStrList extensionList;
-+
-+ idFileList *fileList = new idFileList();
-+ fileList->basePath = relativePath;
-+ fileList->list.SetGranularity( 4096 );
-+
-+ GetExtensionList( extension, extensionList );
-+
-+ GetFileListTree( relativePath, extensionList, fileList->list, hashIndex, gamedir );
-+
-+ if ( sort ) {
-+ idStrListSortPaths( fileList->list );
-+ }
-+
-+ return fileList;
-+}
-+
-+/*
-+===============
-+idFileSystemLocal::FreeFileList
-+===============
-+*/
-+void idFileSystemLocal::FreeFileList( idFileList *fileList ) {
-+ delete fileList;
-+}
-+
-+/*
-+===============
-+idFileSystemLocal::ListMods
-+===============
-+*/
-+idModList *idFileSystemLocal::ListMods( void ) {
-+ int i;
-+ const int MAX_DESCRIPTION = 256;
-+ char desc[ MAX_DESCRIPTION ];
-+
-+ idStrList dirs;
-+ idStrList pk4s;
-+
-+ idModList *list = new idModList;
-+
-+ const char *search[ 4 ];
-+ int isearch;
-+
-+ search[0] = fs_savepath.GetString();
-+ search[1] = fs_devpath.GetString();
-+ search[2] = fs_basepath.GetString();
-+ search[3] = fs_cdpath.GetString();
-+
-+ for ( isearch = 0; isearch < 4; isearch++ ) {
-+
-+ dirs.Clear();
-+ pk4s.Clear();
-+
-+ // scan for directories
-+ ListOSFiles( search[ isearch ], "/", dirs );
-+
-+ dirs.Remove( "." );
-+ dirs.Remove( ".." );
-+ dirs.Remove( "base" );
-+ dirs.Remove( "pb" );
-+
-+ // see if there are any pk4 files in each directory
-+ for( i = 0; i < dirs.Num(); i++ ) {
-+ idStr gamepath = BuildOSPath( search[ isearch ], dirs[ i ], "" );
-+ ListOSFiles( gamepath, ".pk4", pk4s );
-+ if ( pk4s.Num() ) {
-+ if ( !list->mods.Find( dirs[ i ] ) ) {
-+ // D3 1.3 #31, only list d3xp if the pak is present
-+ if ( dirs[ i ].Icmp( "d3xp" ) || HasD3XP() ) {
-+ list->mods.Append( dirs[ i ] );
-+ }
-+ }
-+ }
-+ }
-+ }
-+
-+ list->mods.Sort();
-+
-+ // read the descriptions for each mod - search all paths
-+ for ( i = 0; i < list->mods.Num(); i++ ) {
-+
-+ for ( isearch = 0; isearch < 4; isearch++ ) {
-+
-+ idStr descfile = BuildOSPath( search[ isearch ], list->mods[ i ], "description.txt" );
-+ FILE *f = OpenOSFile( descfile, "r" );
-+ if ( f ) {
-+ if ( fgets( desc, MAX_DESCRIPTION, f ) ) {
-+ list->descriptions.Append( desc );
-+ fclose( f );
-+ break;
-+ } else {
-+ common->DWarning( "Error reading %s", descfile.c_str() );
-+ fclose( f );
-+ continue;
-+ }
-+ }
-+ }
-+
-+ if ( isearch == 4 ) {
-+ list->descriptions.Append( list->mods[ i ] );
-+ }
-+ }
-+
-+ list->mods.Insert( "" );
-+ list->descriptions.Insert( "dhewm 3" );
-+
-+ assert( list->mods.Num() == list->descriptions.Num() );
-+
-+ return list;
-+}
-+
-+/*
-+===============
-+idFileSystemLocal::FreeModList
-+===============
-+*/
-+void idFileSystemLocal::FreeModList( idModList *modList ) {
-+ delete modList;
-+}
-+
-+/*
-+===============
-+idDEntry::Matches
-+===============
-+*/
-+bool idDEntry::Matches(const char *directory, const char *extension) const {
-+ if ( !idDEntry::directory.Icmp( directory ) && !idDEntry::extension.Icmp( extension ) ) {
-+ return true;
-+ }
-+ return false;
-+}
-+
-+/*
-+===============
-+idDEntry::Init
-+===============
-+*/
-+void idDEntry::Init( const char *directory, const char *extension, const idStrList &list ) {
-+ idDEntry::directory = directory;
-+ idDEntry::extension = extension;
-+ idStrList::operator=(list);
-+}
-+
-+/*
-+===============
-+idDEntry::Clear
-+===============
-+*/
-+void idDEntry::Clear( void ) {
-+ directory.Clear();
-+ extension.Clear();
-+ idStrList::Clear();
-+}
-+
-+/*
-+===============
-+idFileSystemLocal::ListOSFiles
-+
-+ call to the OS for a listing of files in an OS directory
-+ optionally, perform some caching of the entries
-+===============
-+*/
-+int idFileSystemLocal::ListOSFiles( const char *directory, const char *extension, idStrList &list ) {
-+ int i, j, ret;
-+
-+ if ( !extension ) {
-+ extension = "";
-+ }
-+
-+ if ( !fs_caseSensitiveOS.GetBool() ) {
-+ return Sys_ListFiles( directory, extension, list );
-+ }
-+
-+ // try in cache
-+ i = dir_cache_index - 1;
-+ while( i >= dir_cache_index - dir_cache_count ) {
-+ j = (i+MAX_CACHED_DIRS) % MAX_CACHED_DIRS;
-+ if ( dir_cache[j].Matches( directory, extension ) ) {
-+ if ( fs_debug.GetInteger() ) {
-+ //common->Printf( "idFileSystemLocal::ListOSFiles: cache hit: %s\n", directory );
-+ }
-+ list = dir_cache[j];
-+ return list.Num();
-+ }
-+ i--;
-+ }
-+
-+ if ( fs_debug.GetInteger() ) {
-+ //common->Printf( "idFileSystemLocal::ListOSFiles: cache miss: %s\n", directory );
-+ }
-+
-+ ret = Sys_ListFiles( directory, extension, list );
-+
-+ if ( ret == -1 ) {
-+ return -1;
-+ }
-+
-+ // push a new entry
-+ dir_cache[dir_cache_index].Init( directory, extension, list );
-+ dir_cache_index = (dir_cache_index + 1) % MAX_CACHED_DIRS;
-+ if ( dir_cache_count < MAX_CACHED_DIRS ) {
-+ dir_cache_count++;
-+ }
-+
-+ return ret;
-+}
-+
-+/*
-+================
-+idFileSystemLocal::Dir_f
-+================
-+*/
-+void idFileSystemLocal::Dir_f( const idCmdArgs &args ) {
-+ idStr relativePath;
-+ idStr extension;
-+ idFileList *fileList;
-+ int i;
-+
-+ if ( args.Argc() < 2 || args.Argc() > 3 ) {
-+ common->Printf( "usage: dir <directory> [extension]\n" );
-+ return;
-+ }
-+
-+ if ( args.Argc() == 2 ) {
-+ relativePath = args.Argv( 1 );
-+ extension = "";
-+ }
-+ else {
-+ relativePath = args.Argv( 1 );
-+ extension = args.Argv( 2 );
-+ if ( extension[0] != '.' ) {
-+ common->Warning( "extension should have a leading dot" );
-+ }
-+ }
-+ relativePath.BackSlashesToSlashes();
-+ relativePath.StripTrailing( '/' );
-+
-+ common->Printf( "Listing of %s/*%s\n", relativePath.c_str(), extension.c_str() );
-+ common->Printf( "---------------\n" );
-+
-+ fileList = fileSystemLocal.ListFiles( relativePath, extension );
-+
-+ for ( i = 0; i < fileList->GetNumFiles(); i++ ) {
-+ common->Printf( "%s\n", fileList->GetFile( i ) );
-+ }
-+ common->Printf( "%d files\n", fileList->list.Num() );
-+
-+ fileSystemLocal.FreeFileList( fileList );
-+}
-+
-+/*
-+================
-+idFileSystemLocal::DirTree_f
-+================
-+*/
-+void idFileSystemLocal::DirTree_f( const idCmdArgs &args ) {
-+ idStr relativePath;
-+ idStr extension;
-+ idFileList *fileList;
-+ int i;
-+
-+ if ( args.Argc() < 2 || args.Argc() > 3 ) {
-+ common->Printf( "usage: dirtree <directory> [extension]\n" );
-+ return;
-+ }
-+
-+ if ( args.Argc() == 2 ) {
-+ relativePath = args.Argv( 1 );
-+ extension = "";
-+ }
-+ else {
-+ relativePath = args.Argv( 1 );
-+ extension = args.Argv( 2 );
-+ if ( extension[0] != '.' ) {
-+ common->Warning( "extension should have a leading dot" );
-+ }
-+ }
-+ relativePath.BackSlashesToSlashes();
-+ relativePath.StripTrailing( '/' );
-+
-+ common->Printf( "Listing of %s/*%s /s\n", relativePath.c_str(), extension.c_str() );
-+ common->Printf( "---------------\n" );
-+
-+ fileList = fileSystemLocal.ListFilesTree( relativePath, extension );
-+
-+ for ( i = 0; i < fileList->GetNumFiles(); i++ ) {
-+ common->Printf( "%s\n", fileList->GetFile( i ) );
-+ }
-+ common->Printf( "%d files\n", fileList->list.Num() );
-+
-+ fileSystemLocal.FreeFileList( fileList );
-+}
-+
-+/*
-+============
-+idFileSystemLocal::Path_f
-+============
-+*/
-+void idFileSystemLocal::Path_f( const idCmdArgs &args ) {
-+ searchpath_t *sp;
-+ idStr status;
-+
-+ common->Printf( "Current search path:\n" );
-+ for ( sp = fileSystemLocal.searchPaths; sp; sp = sp->next ) {
-+ if ( sp->pack ) {
-+ if ( com_developer.GetBool() ) {
-+ sprintf( status, "%s (%i files - 0x%x %s", sp->pack->pakFilename.c_str(), sp->pack->numfiles, sp->pack->checksum, sp->pack->referenced ? "referenced" : "not referenced" );
-+ if ( sp->pack->addon ) {
-+ status += " - addon)\n";
-+ } else {
-+ status += ")\n";
-+ }
-+ common->Printf( status.c_str() );
-+ } else {
-+ common->Printf( "%s (%i files)\n", sp->pack->pakFilename.c_str(), sp->pack->numfiles );
-+ }
-+ if ( fileSystemLocal.serverPaks.Num() ) {
-+ if ( fileSystemLocal.serverPaks.Find( sp->pack ) ) {
-+ common->Printf( " on the pure list\n" );
-+ } else {
-+ common->Printf( " not on the pure list\n" );
-+ }
-+ }
-+ } else {
-+ common->Printf( "%s/%s\n", sp->dir->path.c_str(), sp->dir->gamedir.c_str() );
-+ }
-+ }
-+
-+ // show addon packs that are *not* in the search lists
-+ common->Printf( "Addon pk4s:\n" );
-+ for ( sp = fileSystemLocal.addonPaks; sp; sp = sp->next ) {
-+ if ( com_developer.GetBool() ) {
-+ common->Printf( "%s (%i files - 0x%x)\n", sp->pack->pakFilename.c_str(), sp->pack->numfiles, sp->pack->checksum );
-+ } else {
-+ common->Printf( "%s (%i files)\n", sp->pack->pakFilename.c_str(), sp->pack->numfiles );
-+ }
-+ }
-+}
-+
-+/*
-+============
-+idFileSystemLocal::TouchFile_f
-+
-+The only purpose of this function is to allow game script files to copy
-+arbitrary files furing an "fs_copyfiles 1" run.
-+============
-+*/
-+void idFileSystemLocal::TouchFile_f( const idCmdArgs &args ) {
-+ idFile *f;
-+
-+ if ( args.Argc() != 2 ) {
-+ common->Printf( "Usage: touchFile <file>\n" );
-+ return;
-+ }
-+
-+ f = fileSystemLocal.OpenFileRead( args.Argv( 1 ) );
-+ if ( f ) {
-+ fileSystemLocal.CloseFile( f );
-+ }
-+}
-+
-+/*
-+============
-+idFileSystemLocal::TouchFileList_f
-+
-+Takes a text file and touches every file in it, use one file per line.
-+============
-+*/
-+void idFileSystemLocal::TouchFileList_f( const idCmdArgs &args ) {
-+
-+ if ( args.Argc() != 2 ) {
-+ common->Printf( "Usage: touchFileList <filename>\n" );
-+ return;
-+ }
-+
-+ const char *buffer = NULL;
-+ idParser src( LEXFL_NOFATALERRORS | LEXFL_NOSTRINGCONCAT | LEXFL_ALLOWMULTICHARLITERALS | LEXFL_ALLOWBACKSLASHSTRINGCONCAT );
-+ if ( fileSystem->ReadFile( args.Argv( 1 ), ( void** )&buffer, NULL ) && buffer ) {
-+ src.LoadMemory( buffer, strlen( buffer ), args.Argv( 1 ) );
-+ if ( src.IsLoaded() ) {
-+ idToken token;
-+ while( src.ReadToken( &token ) ) {
-+ common->Printf( "%s\n", token.c_str() );
-+ session->UpdateScreen();
-+ idFile *f = fileSystemLocal.OpenFileRead( token );
-+ if ( f ) {
-+ fileSystemLocal.CloseFile( f );
-+ }
-+ }
-+ }
-+ }
-+
-+}
-+
-+
-+/*
-+================
-+idFileSystemLocal::AddGameDirectory
-+
-+Sets gameFolder, adds the directory to the head of the search paths, then loads any pk4 files.
-+================
-+*/
-+void idFileSystemLocal::AddGameDirectory( const char *path, const char *dir ) {
-+ int i;
-+ searchpath_t * search;
-+ pack_t * pak;
-+ idStr pakfile;
-+ idStrList pakfiles;
-+
-+ // check if the search path already exists
-+ for ( search = searchPaths; search; search = search->next ) {
-+ // if this element is a pak file
-+ if ( !search->dir ) {
-+ continue;
-+ }
-+ if ( search->dir->path.Cmp( path ) == 0 && search->dir->gamedir.Cmp( dir ) == 0 ) {
-+ return;
-+ }
-+ }
-+
-+ gameFolder = dir;
-+
-+ //
-+ // add the directory to the search path
-+ //
-+ search = new searchpath_t;
-+ search->dir = new directory_t;
-+ search->pack = NULL;
-+
-+ search->dir->path = path;
-+ search->dir->gamedir = dir;
-+ search->next = searchPaths;
-+ searchPaths = search;
-+
-+ // find all pak files in this directory
-+ pakfile = BuildOSPath( path, dir, "" );
-+ pakfile[ pakfile.Length() - 1 ] = 0; // strip the trailing slash
-+
-+ ListOSFiles( pakfile, ".pk4", pakfiles );
-+
-+ // sort them so that later alphabetic matches override
-+ // earlier ones. This makes pak1.pk4 override pak0.pk4
-+ pakfiles.Sort();
-+
-+ for ( i = 0; i < pakfiles.Num(); i++ ) {
-+ pakfile = BuildOSPath( path, dir, pakfiles[i] );
-+ pak = LoadZipFile( pakfile );
-+ if ( !pak ) {
-+ continue;
-+ }
-+ // insert the pak after the directory it comes from
-+ search = new searchpath_t;
-+ search->dir = NULL;
-+ search->pack = pak;
-+ search->next = searchPaths->next;
-+ searchPaths->next = search;
-+ common->Printf( "Loaded pk4 %s with checksum 0x%x\n", pakfile.c_str(), pak->checksum );
-+ }
-+}
-+
-+/*
-+================
-+idFileSystemLocal::SetupGameDirectories
-+
-+ Takes care of the correct search order.
-+================
-+*/
-+void idFileSystemLocal::SetupGameDirectories( const char *gameName ) {
-+ // setup cdpath
-+ if ( fs_cdpath.GetString()[0] ) {
-+ AddGameDirectory( fs_cdpath.GetString(), gameName );
-+ }
-+
-+ // setup basepath
-+ if ( fs_basepath.GetString()[0] ) {
-+ AddGameDirectory( fs_basepath.GetString(), gameName );
-+ }
-+
-+ // setup devpath
-+ if ( fs_devpath.GetString()[0] ) {
-+ AddGameDirectory( fs_devpath.GetString(), gameName );
-+ }
-+
-+ // setup savepath
-+ if ( fs_savepath.GetString()[0] ) {
-+ AddGameDirectory( fs_savepath.GetString(), gameName );
-+ }
-+
-+ // setup configpath
-+ if ( fs_configpath.GetString()[0] ) {
-+ AddGameDirectory( fs_configpath.GetString(), gameName );
-+ }
-+}
-+
-+/*
-+===============
-+idFileSystemLocal::FollowDependencies
-+===============
-+*/
-+void idFileSystemLocal::FollowAddonDependencies( pack_t *pak ) {
-+ assert( pak );
-+ if ( !pak->addon_info || !pak->addon_info->depends.Num() ) {
-+ return;
-+ }
-+ int i, num = pak->addon_info->depends.Num();
-+ for ( i = 0; i < num; i++ ) {
-+ pack_t *deppak = GetPackForChecksum( pak->addon_info->depends[ i ], true );
-+ if ( deppak ) {
-+ // make sure it hasn't been marked for search already
-+ if ( !deppak->addon_search ) {
-+ // must clean addonChecksums as we go
-+ int addon_index = addonChecksums.FindIndex( deppak->checksum );
-+ if ( addon_index >= 0 ) {
-+ addonChecksums.RemoveIndex( addon_index );
-+ }
-+ deppak->addon_search = true;
-+ common->Printf( "Addon pk4 %s 0x%x depends on pak %s 0x%x, will be searched\n",
-+ pak->pakFilename.c_str(), pak->checksum,
-+ deppak->pakFilename.c_str(), deppak->checksum );
-+ FollowAddonDependencies( deppak );
-+ }
-+ } else {
-+ common->Printf( "Addon pk4 %s 0x%x depends on unknown pak 0x%x\n",
-+ pak->pakFilename.c_str(), pak->checksum, pak->addon_info->depends[ i ] );
-+ }
-+ }
-+}
-+
-+/*
-+================
-+idFileSystemLocal::Startup
-+================
-+*/
-+void idFileSystemLocal::Startup( void ) {
-+ searchpath_t **search;
-+ int i;
-+ pack_t *pak;
-+ int addon_index;
-+
-+ common->Printf( "----- Initializing File System -----\n" );
-+
-+ if ( restartChecksums.Num() ) {
-+ common->Printf( "restarting in pure mode with %d pak files\n", restartChecksums.Num() );
-+ }
-+ if ( addonChecksums.Num() ) {
-+ common->Printf( "restarting filesystem with %d addon pak file(s) to include\n", addonChecksums.Num() );
-+ }
-+
-+ SetupGameDirectories( BASE_GAMEDIR );
-+
-+ // fs_game_base override
-+ if ( fs_game_base.GetString()[0] &&
-+ idStr::Icmp( fs_game_base.GetString(), BASE_GAMEDIR ) ) {
-+ SetupGameDirectories( fs_game_base.GetString() );
-+ }
-+
-+ // fs_game override
-+ if ( fs_game.GetString()[0] &&
-+ idStr::Icmp( fs_game.GetString(), BASE_GAMEDIR ) &&
-+ idStr::Icmp( fs_game.GetString(), fs_game_base.GetString() ) ) {
-+ SetupGameDirectories( fs_game.GetString() );
-+ }
-+
-+ // currently all addons are in the search list - deal with filtering out and dependencies now
-+ // scan through and deal with dependencies
-+ search = &searchPaths;
-+ while ( *search ) {
-+ if ( !( *search )->pack || !( *search )->pack->addon ) {
-+ search = &( ( *search )->next );
-+ continue;
-+ }
-+ pak = ( *search )->pack;
-+ if ( fs_searchAddons.GetBool() ) {
-+ // when we have fs_searchAddons on we should never have addonChecksums
-+ assert( !addonChecksums.Num() );
-+ pak->addon_search = true;
-+ search = &( ( *search )->next );
-+ continue;
-+ }
-+ addon_index = addonChecksums.FindIndex( pak->checksum );
-+ if ( addon_index >= 0 ) {
-+ assert( !pak->addon_search ); // any pak getting flagged as addon_search should also have been removed from addonChecksums already
-+ pak->addon_search = true;
-+ addonChecksums.RemoveIndex( addon_index );
-+ FollowAddonDependencies( pak );
-+ }
-+ search = &( ( *search )->next );
-+ }
-+
-+ // now scan to filter out addons not marked addon_search
-+ search = &searchPaths;
-+ while ( *search ) {
-+ if ( !( *search )->pack || !( *search )->pack->addon ) {
-+ search = &( ( *search )->next );
-+ continue;
-+ }
-+ assert( !( *search )->dir );
-+ pak = ( *search )->pack;
-+ if ( pak->addon_search ) {
-+ common->Printf( "Addon pk4 %s with checksum 0x%x is on the search list\n",
-+ pak->pakFilename.c_str(), pak->checksum );
-+ search = &( ( *search )->next );
-+ } else {
-+ // remove from search list, put in addons list
-+ searchpath_t *paksearch = *search;
-+ *search = ( *search )->next;
-+ paksearch->next = addonPaks;
-+ addonPaks = paksearch;
-+ common->Printf( "Addon pk4 %s with checksum 0x%x is on addon list\n",
-+ pak->pakFilename.c_str(), pak->checksum );
-+ }
-+ }
-+
-+ // all addon paks found and accounted for
-+ assert( !addonChecksums.Num() );
-+ addonChecksums.Clear(); // just in case
-+
-+ if ( restartChecksums.Num() ) {
-+ search = &searchPaths;
-+ while ( *search ) {
-+ if ( !( *search )->pack ) {
-+ search = &( ( *search )->next );
-+ continue;
-+ }
-+ if ( ( i = restartChecksums.FindIndex( ( *search )->pack->checksum ) ) != -1 ) {
-+ if ( i == 0 ) {
-+ // this pak is the next one in the pure search order
-+ serverPaks.Append( ( *search )->pack );
-+ restartChecksums.RemoveIndex( 0 );
-+ if ( !restartChecksums.Num() ) {
-+ break; // early out, we're done
-+ }
-+ search = &( ( *search )->next );
-+ continue;
-+ } else {
-+ // this pak will be on the pure list, but order is not right yet
-+ searchpath_t *aux;
-+ aux = ( *search )->next;
-+ if ( !aux ) {
-+ // last of the list can't be swapped back
-+ if ( fs_debug.GetBool() ) {
-+ common->Printf( "found pure checksum %x at index %d, but the end of search path is reached\n", ( *search )->pack->checksum, i );
-+ idStr checks;
-+ checks.Clear();
-+ for ( i = 0; i < serverPaks.Num(); i++ ) {
-+ checks += va( "%p ", serverPaks[ i ] );
-+ }
-+ common->Printf( "%d pure paks - %s \n", serverPaks.Num(), checks.c_str() );
-+ checks.Clear();
-+ for ( i = 0; i < restartChecksums.Num(); i++ ) {
-+ checks += va( "%x ", restartChecksums[ i ] );
-+ }
-+ common->Printf( "%d paks left - %s\n", restartChecksums.Num(), checks.c_str() );
-+ }
-+ common->FatalError( "Failed to restart with pure mode restrictions for server connect" );
-+ }
-+ // put this search path at the end of the list
-+ searchpath_t *search_end;
-+ search_end = ( *search )->next;
-+ while ( search_end->next ) {
-+ search_end = search_end->next;
-+ }
-+ search_end->next = *search;
-+ *search = ( *search )->next;
-+ search_end->next->next = NULL;
-+ continue;
-+ }
-+ }
-+ // this pak is not on the pure list
-+ search = &( ( *search )->next );
-+ }
-+ // the list must be empty
-+ if ( restartChecksums.Num() ) {
-+ if ( fs_debug.GetBool() ) {
-+ idStr checks;
-+ checks.Clear();
-+ for ( i = 0; i < serverPaks.Num(); i++ ) {
-+ checks += va( "%p ", serverPaks[ i ] );
-+ }
-+ common->Printf( "%d pure paks - %s \n", serverPaks.Num(), checks.c_str() );
-+ checks.Clear();
-+ for ( i = 0; i < restartChecksums.Num(); i++ ) {
-+ checks += va( "%x ", restartChecksums[ i ] );
-+ }
-+ common->Printf( "%d paks left - %s\n", restartChecksums.Num(), checks.c_str() );
-+ }
-+ common->FatalError( "Failed to restart with pure mode restrictions for server connect" );
-+ }
-+ }
-+
-+ // add our commands
-+ cmdSystem->AddCommand( "dir", Dir_f, CMD_FL_SYSTEM, "lists a folder", idCmdSystem::ArgCompletion_FileName );
-+ cmdSystem->AddCommand( "dirtree", DirTree_f, CMD_FL_SYSTEM, "lists a folder with subfolders" );
-+ cmdSystem->AddCommand( "path", Path_f, CMD_FL_SYSTEM, "lists search paths" );
-+ cmdSystem->AddCommand( "touchFile", TouchFile_f, CMD_FL_SYSTEM, "touches a file" );
-+ cmdSystem->AddCommand( "touchFileList", TouchFileList_f, CMD_FL_SYSTEM, "touches a list of files" );
-+
-+ // print the current search paths
-+ Path_f( idCmdArgs() );
-+}
-+
-+/*
-+=====================
-+idFileSystemLocal::UpdatePureServerChecksums
-+=====================
-+*/
-+void idFileSystemLocal::UpdatePureServerChecksums( void ) {
-+ searchpath_t *search;
-+ int i;
-+ pureStatus_t status;
-+
-+ serverPaks.Clear();
-+ for ( search = searchPaths; search; search = search->next ) {
-+ // is the element a referenced pak file?
-+ if ( !search->pack ) {
-+ continue;
-+ }
-+ status = GetPackStatus( search->pack );
-+ if ( status == PURE_NEVER ) {
-+ continue;
-+ }
-+ if ( status == PURE_NEUTRAL && !search->pack->referenced ) {
-+ continue;
-+ }
-+ serverPaks.Append( search->pack );
-+ if ( serverPaks.Num() >= MAX_PURE_PAKS ) {
-+ common->FatalError( "MAX_PURE_PAKS ( %d ) exceeded\n", MAX_PURE_PAKS );
-+ }
-+ }
-+ if ( fs_debug.GetBool() ) {
-+ idStr checks;
-+ for ( i = 0; i < serverPaks.Num(); i++ ) {
-+ checks += va( "%x ", serverPaks[ i ]->checksum );
-+ }
-+ common->Printf( "set pure list - %d paks ( %s)\n", serverPaks.Num(), checks.c_str() );
-+ }
-+}
-+
-+/*
-+=====================
-+idFileSystemLocal::GetPackForChecksum
-+=====================
-+*/
-+pack_t* idFileSystemLocal::GetPackForChecksum( int checksum, bool searchAddons ) {
-+ searchpath_t *search;
-+ for ( search = searchPaths; search; search = search->next ) {
-+ if ( !search->pack ) {
-+ continue;
-+ }
-+ if ( search->pack->checksum == checksum ) {
-+ return search->pack;
-+ }
-+ }
-+ if ( searchAddons ) {
-+ for ( search = addonPaks; search; search = search->next ) {
-+ assert( search->pack && search->pack->addon );
-+ if ( search->pack->checksum == checksum ) {
-+ return search->pack;
-+ }
-+ }
-+ }
-+ return NULL;
-+}
-+
-+/*
-+===============
-+idFileSystemLocal::ValidateDownloadPakForChecksum
-+===============
-+*/
-+int idFileSystemLocal::ValidateDownloadPakForChecksum( int checksum, char path[ MAX_STRING_CHARS ] ) {
-+ int i;
-+ idStrList testList;
-+ idStr name;
-+ idStr relativePath;
-+ pack_t *pak = GetPackForChecksum( checksum );
-+
-+ if ( !pak ) {
-+ return 0;
-+ }
-+
-+ // validate this pak for a potential download
-+ // ignore pak*.pk4 for download. those are reserved to distribution and cannot be downloaded
-+ name = pak->pakFilename;
-+ name.StripPath();
-+ if ( strstr( name.c_str(), "pak" ) == name.c_str() ) {
-+ common->DPrintf( "%s is not a donwloadable pak\n", pak->pakFilename.c_str() );
-+ return 0;
-+ }
-+
-+ // extract a path that includes the fs_game: != OSPathToRelativePath
-+ testList.Append( fs_savepath.GetString() );
-+ testList.Append( fs_devpath.GetString() );
-+ testList.Append( fs_basepath.GetString() );
-+ testList.Append( fs_cdpath.GetString() );
-+ for ( i = 0; i < testList.Num(); i ++ ) {
-+ if ( testList[ i ].Length() && !testList[ i ].Icmpn( pak->pakFilename, testList[ i ].Length() ) ) {
-+ relativePath = pak->pakFilename.c_str() + testList[ i ].Length() + 1;
-+ break;
-+ }
-+ }
-+ if ( i == testList.Num() ) {
-+ common->Warning( "idFileSystem::ValidateDownloadPak: failed to extract relative path for %s", pak->pakFilename.c_str() );
-+ return 0;
-+ }
-+ idStr::Copynz( path, relativePath, MAX_STRING_CHARS );
-+ return pak->length;
-+}
-+
-+/*
-+=====================
-+idFileSystemLocal::ClearPureChecksums
-+=====================
-+*/
-+void idFileSystemLocal::ClearPureChecksums( void ) {
-+ common->DPrintf( "Cleared pure server lock\n" );
-+ serverPaks.Clear();
-+}
-+
-+/*
-+=====================
-+idFileSystemLocal::SetPureServerChecksums
-+set the pure paks according to what the server asks
-+if that's not possible, identify why and build an answer
-+can be:
-+ loadedFileFromDir - some files were loaded from directories instead of paks (a restart in pure pak-only is required)
-+ missing/wrong checksums - some pak files would need to be installed/updated (downloaded for instance)
-+ some pak files currently referenced are not referenced by the server
-+ wrong order - if the pak order doesn't match, means some stuff could have been loaded from somewhere else
-+server referenced files are prepended to the list if possible ( that doesn't break pureness )
-+DLL:
-+ the checksum of the pak containing the DLL is maintained seperately, the server can send different replies by OS
-+=====================
-+*/
-+fsPureReply_t idFileSystemLocal::SetPureServerChecksums( const int pureChecksums[ MAX_PURE_PAKS ], int missingChecksums[ MAX_PURE_PAKS ] ) {
-+ pack_t *pack;
-+ int i, j, imissing;
-+ bool success = true;
-+ bool canPrepend = true;
-+
-+ imissing = 0;
-+ missingChecksums[ 0 ] = 0;
-+
-+ if ( pureChecksums[ 0 ] == 0 ) {
-+ ClearPureChecksums();
-+ return PURE_OK;
-+ }
-+
-+ if ( !serverPaks.Num() ) {
-+ // there was no pure lockdown yet - lock to what we already have
-+ UpdatePureServerChecksums();
-+ }
-+ i = 0; j = 0;
-+ while ( pureChecksums[ i ] ) {
-+ if ( j < serverPaks.Num() && serverPaks[ j ]->checksum == pureChecksums[ i ] ) {
-+ canPrepend = false; // once you start matching into the list there is no prepending anymore
-+ i++; j++; // the pak is matched, is in the right order, continue..
-+ } else {
-+ pack = GetPackForChecksum( pureChecksums[ i ], true );
-+ if ( pack && pack->addon && !pack->addon_search ) {
-+ // this is an addon pack, and it's not on our current search list
-+ // setting success to false meaning that a restart including this addon is required
-+ if ( fs_debug.GetBool() ) {
-+ common->Printf( "pak %s checksumed 0x%x is on addon list. Restart required.\n", pack->pakFilename.c_str(), pack->checksum );
-+ }
-+ success = false;
-+ }
-+ if ( pack && pack->isNew ) {
-+ // that's a downloaded pack, we will need to restart
-+ if ( fs_debug.GetBool() ) {
-+ common->Printf( "pak %s checksumed 0x%x is a newly downloaded file. Restart required.\n", pack->pakFilename.c_str(), pack->checksum );
-+ }
-+ success = false;
-+ }
-+ if ( pack ) {
-+ if ( canPrepend ) {
-+ // we still have a chance
-+ if ( fs_debug.GetBool() ) {
-+ common->Printf( "prepend pak %s checksumed 0x%x at index %d\n", pack->pakFilename.c_str(), pack->checksum, j );
-+ }
-+ // NOTE: there is a light possibility this adds at the end of the list if UpdatePureServerChecksums didn't set anything
-+ serverPaks.Insert( pack, j );
-+ i++; j++; // continue..
-+ } else {
-+ success = false;
-+ if ( fs_debug.GetBool() ) {
-+ // verbose the situation
-+ if ( serverPaks.Find( pack ) ) {
-+ common->Printf( "pak %s checksumed 0x%x is in the pure list at wrong index. Current index is %d, found at %d\n", pack->pakFilename.c_str(), pack->checksum, j, serverPaks.FindIndex( pack ) );
-+ } else {
-+ common->Printf( "pak %s checksumed 0x%x can't be added to pure list because of search order\n", pack->pakFilename.c_str(), pack->checksum );
-+ }
-+ }
-+ i++; // advance server checksums only
-+ }
-+ } else {
-+ // didn't find a matching checksum
-+ success = false;
-+ missingChecksums[ imissing++ ] = pureChecksums[ i ];
-+ missingChecksums[ imissing ] = 0;
-+ if ( fs_debug.GetBool() ) {
-+ common->Printf( "checksum not found - 0x%x\n", pureChecksums[ i ] );
-+ }
-+ i++; // advance the server checksums only
-+ }
-+ }
-+ }
-+ while ( j < serverPaks.Num() ) {
-+ success = false; // just in case some extra pak files are referenced at the end of our local list
-+ if ( fs_debug.GetBool() ) {
-+ common->Printf( "pak %s checksumed 0x%x is an extra reference at the end of local pure list\n", serverPaks[ j ]->pakFilename.c_str(), serverPaks[ j ]->checksum );
-+ }
-+ j++;
-+ }
-+
-+ // we reply to missing after DLL check so it can be part of the list
-+ if ( imissing ) {
-+ return PURE_MISSING;
-+ }
-+
-+ // one last check
-+ if ( loadedFileFromDir ) {
-+ success = false;
-+ if ( fs_debug.GetBool() ) {
-+ common->Printf( "SetPureServerChecksums: there are files loaded from dir\n" );
-+ }
-+ }
-+ return ( success ? PURE_OK : PURE_RESTART );
-+}
-+
-+/*
-+=====================
-+idFileSystemLocal::GetPureServerChecksums
-+=====================
-+*/
-+void idFileSystemLocal::GetPureServerChecksums( int checksums[ MAX_PURE_PAKS ] ) {
-+ int i;
-+
-+ for ( i = 0; i < serverPaks.Num(); i++ ) {
-+ checksums[ i ] = serverPaks[ i ]->checksum;
-+ }
-+ checksums[ i ] = 0;
-+}
-+
-+/*
-+=====================
-+idFileSystemLocal::SetRestartChecksums
-+=====================
-+*/
-+void idFileSystemLocal::SetRestartChecksums( const int pureChecksums[ MAX_PURE_PAKS ] ) {
-+ int i;
-+ pack_t *pack;
-+
-+ restartChecksums.Clear();
-+ i = 0;
-+ while ( pureChecksums[ i ] ) {
-+ pack = GetPackForChecksum( pureChecksums[ i ], true );
-+ if ( !pack ) {
-+ common->FatalError( "SetRestartChecksums failed: no pak for checksum 0x%x\n", pureChecksums[i] );
-+ }
-+ if ( pack->addon && addonChecksums.FindIndex( pack->checksum ) < 0 ) {
-+ // can't mark it pure if we're not even gonna search it :-)
-+ addonChecksums.Append( pack->checksum );
-+ }
-+ restartChecksums.Append( pureChecksums[ i ] );
-+ i++;
-+ }
-+}
-+
-+/*
-+================
-+idFileSystemLocal::Init
-+
-+Called only at inital startup, not when the filesystem
-+is resetting due to a game change
-+================
-+*/
-+void idFileSystemLocal::Init( void ) {
-+ // allow command line parms to override our defaults
-+ // we have to specially handle this, because normal command
-+ // line variable sets don't happen until after the filesystem
-+ // has already been initialized
-+ common->StartupVariable( "fs_basepath", false );
-+ common->StartupVariable( "fs_configpath", false );
-+ common->StartupVariable( "fs_savepath", false );
-+ common->StartupVariable( "fs_cdpath", false );
-+ common->StartupVariable( "fs_devpath", false );
-+ common->StartupVariable( "fs_game", false );
-+ common->StartupVariable( "fs_game_base", false );
-+ common->StartupVariable( "fs_copyfiles", false );
-+ common->StartupVariable( "fs_restrict", false );
-+ common->StartupVariable( "fs_searchAddons", false );
-+
-+ idStr path;
-+ if (fs_basepath.GetString()[0] == '\0' && Sys_GetPath(PATH_BASE, path))
-+ fs_basepath.SetString(path);
-+
-+ if (fs_savepath.GetString()[0] == '\0' && Sys_GetPath(PATH_SAVE, path))
-+ fs_savepath.SetString(path);
-+
-+ if (fs_configpath.GetString()[0] == '\0' && Sys_GetPath(PATH_CONFIG, path))
-+ fs_configpath.SetString(path);
-+
-+ if ( fs_devpath.GetString()[0] == '\0' ) {
-+#ifdef WIN32
-+ fs_devpath.SetString( fs_cdpath.GetString()[0] ? fs_cdpath.GetString() : fs_basepath.GetString() );
-+#else
-+ fs_devpath.SetString( fs_savepath.GetString() );
-+#endif
-+ }
-+
-+ // try to start up normally
-+ Startup( );
-+
-+ // spawn a thread to handle background file reads
-+ StartBackgroundDownloadThread();
-+
-+ // if we can't find default.cfg, assume that the paths are
-+ // busted and error out now, rather than getting an unreadable
-+ // graphics screen when the font fails to load
-+ // Dedicated servers can run with no outside files at all
-+ if ( ReadFile( "default.cfg", NULL, NULL ) <= 0 ) {
-+ common->FatalError( "Couldn't load default.cfg" );
-+ }
-+}
-+
-+/*
-+================
-+idFileSystemLocal::Restart
-+================
-+*/
-+void idFileSystemLocal::Restart( void ) {
-+ // free anything we currently have loaded
-+ Shutdown( true );
-+
-+ Startup( );
-+
-+ // if we can't find default.cfg, assume that the paths are
-+ // busted and error out now, rather than getting an unreadable
-+ // graphics screen when the font fails to load
-+ if ( ReadFile( "default.cfg", NULL, NULL ) <= 0 ) {
-+ common->FatalError( "Couldn't load default.cfg" );
-+ }
-+}
-+
-+/*
-+================
-+idFileSystemLocal::Shutdown
-+
-+Frees all resources and closes all files
-+================
-+*/
-+void idFileSystemLocal::Shutdown( bool reloading ) {
-+ searchpath_t *sp, *next, *loop;
-+
-+ backgroundThread_exit = true;
-+ Sys_TriggerEvent();
-+ Sys_DestroyThread(backgroundThread);
-+ backgroundThread_exit = false;
-+
-+ gameFolder.Clear();
-+
-+ serverPaks.Clear();
-+ if ( !reloading ) {
-+ restartChecksums.Clear();
-+ addonChecksums.Clear();
-+ }
-+ loadedFileFromDir = false;
-+
-+ ClearDirCache();
-+
-+ // free everything - loop through searchPaths and addonPaks
-+ for ( loop = searchPaths; loop; loop == searchPaths ? loop = addonPaks : loop = NULL ) {
-+ for ( sp = loop; sp; sp = next ) {
-+ next = sp->next;
-+
-+ if ( sp->pack ) {
-+ unzClose( sp->pack->handle );
-+ delete [] sp->pack->buildBuffer;
-+ if ( sp->pack->addon_info ) {
-+ sp->pack->addon_info->mapDecls.DeleteContents( true );
-+ delete sp->pack->addon_info;
-+ }
-+ delete sp->pack;
-+ }
-+ if ( sp->dir ) {
-+ delete sp->dir;
-+ }
-+ delete sp;
-+ }
-+ }
-+
-+ // any FS_ calls will now be an error until reinitialized
-+ searchPaths = NULL;
-+ addonPaks = NULL;
-+
-+ cmdSystem->RemoveCommand( "path" );
-+ cmdSystem->RemoveCommand( "dir" );
-+ cmdSystem->RemoveCommand( "dirtree" );
-+ cmdSystem->RemoveCommand( "touchFile" );
-+
-+ mapDict.Clear();
-+}
-+
-+/*
-+================
-+idFileSystemLocal::IsInitialized
-+================
-+*/
-+bool idFileSystemLocal::IsInitialized( void ) const {
-+ return ( searchPaths != NULL );
-+}
-+
-+
-+/*
-+=================================================================================
-+
-+Opening files
-+
-+=================================================================================
-+*/
-+
-+/*
-+===========
-+idFileSystemLocal::FileAllowedFromDir
-+===========
-+*/
-+bool idFileSystemLocal::FileAllowedFromDir( const char *path ) {
-+ unsigned int l;
-+
-+ l = strlen( path );
-+
-+ if ( !strcmp( path + l - 4, ".cfg" ) // for config files
-+ || !strcmp( path + l - 4, ".dat" ) // for journal files
-+ || !strcmp( path + l - 4, ".dll" ) // dynamic modules are handled a different way for pure
-+ || !strcmp( path + l - 3, ".so" )
-+ || ( l > 6 && !strcmp( path + l - 6, ".dylib" ) )
-+ || ( l > 10 && !strcmp( path + l - 10, ".scriptcfg" ) ) // configuration script, such as map cycle
-+#if ID_PURE_ALLOWDDS
-+ || !strcmp( path + l - 4, ".dds" )
-+#endif
-+ ) {
-+ // note: cd and xp keys, as well as config.spec are opened through an explicit OS path and don't hit this
-+ return true;
-+ }
-+ // savegames
-+ if ( strstr( path, "savegames" ) == path &&
-+ ( !strcmp( path + l - 4, ".tga" ) || !strcmp( path + l -4, ".txt" ) || !strcmp( path + l - 5, ".save" ) ) ) {
-+ return true;
-+ }
-+ // screen shots
-+ if ( strstr( path, "screenshots" ) == path && !strcmp( path + l - 4, ".tga" ) ) {
-+ return true;
-+ }
-+ // objective tgas
-+ if ( strstr( path, "maps/game" ) == path &&
-+ !strcmp( path + l - 4, ".tga" ) ) {
-+ return true;
-+ }
-+ // splash screens extracted from addons
-+ if ( strstr( path, "guis/assets/splash/addon" ) == path &&
-+ !strcmp( path + l -4, ".tga" ) ) {
-+ return true;
-+ }
-+
-+ return false;
-+}
-+
-+/*
-+===========
-+idFileSystemLocal::GetPackStatus
-+===========
-+*/
-+pureStatus_t idFileSystemLocal::GetPackStatus( pack_t *pak ) {
-+ int i, l, hashindex;
-+ fileInPack_t *file;
-+ bool abrt;
-+ idStr name;
-+
-+ if ( pak->pureStatus != PURE_UNKNOWN ) {
-+ return pak->pureStatus;
-+ }
-+
-+ // check content for PURE_NEVER
-+ i = 0;
-+ file = pak->buildBuffer;
-+ for ( hashindex = 0; hashindex < FILE_HASH_SIZE; hashindex++ ) {
-+ abrt = false;
-+ file = pak->hashTable[ hashindex ];
-+ while ( file ) {
-+ abrt = true;
-+ l = file->name.Length();
-+ for ( int j = 0; pureExclusions[j].func != NULL; j++ ) {
-+ if ( pureExclusions[j].func( pureExclusions[j], l, file->name ) ) {
-+ abrt = false;
-+ break;
-+ }
-+ }
-+ if ( abrt ) {
-+ common->DPrintf( "pak '%s' candidate for pure: '%s'\n", pak->pakFilename.c_str(), file->name.c_str() );
-+ break;
-+ }
-+ file = file->next;
-+ i++;
-+ }
-+ if ( abrt ) {
-+ break;
-+ }
-+ }
-+ if ( i == pak->numfiles ) {
-+ pak->pureStatus = PURE_NEVER;
-+ return PURE_NEVER;
-+ }
-+
-+ // check pak name for PURE_ALWAYS
-+ pak->pakFilename.ExtractFileName( name );
-+ if ( !name.IcmpPrefixPath( "pak" ) ) {
-+ pak->pureStatus = PURE_ALWAYS;
-+ return PURE_ALWAYS;
-+ }
-+
-+ pak->pureStatus = PURE_NEUTRAL;
-+ return PURE_NEUTRAL;
-+}
-+
-+/*
-+===========
-+idFileSystemLocal::ReadFileFromZip
-+===========
-+*/
-+idFile_InZip * idFileSystemLocal::ReadFileFromZip( pack_t *pak, fileInPack_t *pakFile, const char *relativePath ) {
-+ // relativePath == pakFile->name according to FilenameCompare()
-+ // pakFile->Pos is position of that file within the zip
-+
-+ // set position in pk4 file to the file (in the zip/pk4) we want a handle on
-+ unzSetOffset64( pak->handle, pakFile->pos );
-+
-+ // clone handle and assign a new internal filestream to zip file to it
-+ unzFile uf = unzReOpen( pak->pakFilename, pak->handle );
-+ if ( uf == NULL ) {
-+ common->FatalError( "Couldn't reopen %s", pak->pakFilename.c_str() );
-+ }
-+
-+ // the following stuff is needed to get the uncompress filesize (for file->fileSize)
-+ char filename_inzip[MAX_ZIPPED_FILE_NAME];
-+ unz_file_info64 file_info;
-+ int err = unzGetCurrentFileInfo64( uf, &file_info, filename_inzip, sizeof(filename_inzip), NULL, 0, NULL, 0 );
-+ if ( err != UNZ_OK ) {
-+ common->FatalError( "Couldn't get file info for %s in %s, pos %llu", relativePath, pak->pakFilename.c_str(), pakFile->pos );
-+ }
-+
-+ // create idFile_InZip and set fields accordingly
-+ idFile_InZip *file = new idFile_InZip();
-+ file->z = uf;
-+ file->name = relativePath;
-+ file->fullPath = pak->pakFilename + "/" + relativePath;
-+ file->zipFilePos = pakFile->pos;
-+ file->fileSize = file_info.uncompressed_size;
-+
-+ return file;
-+}
-+
-+/*
-+===========
-+idFileSystemLocal::OpenFileReadFlags
-+
-+Finds the file in the search path, following search flag recommendations
-+Returns filesize and an open FILE pointer.
-+Used for streaming data out of either a
-+separate file or a ZIP file.
-+===========
-+*/
-+idFile *idFileSystemLocal::OpenFileReadFlags( const char *relativePath, int searchFlags, pack_t **foundInPak, bool allowCopyFiles, const char* gamedir ) {
-+ searchpath_t * search;
-+ idStr netpath;
-+ pack_t * pak;
-+ fileInPack_t * pakFile;
-+ directory_t * dir;
-+ int hash;
-+ FILE * fp;
-+
-+ if ( !searchPaths ) {
-+ common->FatalError( "Filesystem call made without initialization\n" );
-+ }
-+
-+ if ( !relativePath ) {
-+ common->FatalError( "idFileSystemLocal::OpenFileRead: NULL 'relativePath' parameter passed\n" );
-+ }
-+
-+ if ( foundInPak ) {
-+ *foundInPak = NULL;
-+ }
-+
-+ // qpaths are not supposed to have a leading slash
-+ if ( relativePath[0] == '/' || relativePath[0] == '\\' ) {
-+ relativePath++;
-+ }
-+
-+ // make absolutely sure that it can't back up the path.
-+ // The searchpaths do guarantee that something will always
-+ // be prepended, so we don't need to worry about "c:" or "//limbo"
-+ if ( strstr( relativePath, ".." ) || strstr( relativePath, "::" ) ) {
-+ return NULL;
-+ }
-+
-+ // edge case
-+ if ( relativePath[0] == '\0' ) {
-+ return NULL;
-+ }
-+
-+ // make sure the doomkey file is only readable by game at initialization
-+ // any other time the key should only be accessed in memory using the provided functions
-+ if( common->IsInitialized() && ( idStr::Icmp( relativePath, CDKEY_FILE ) == 0 || idStr::Icmp( relativePath, XPKEY_FILE ) == 0 ) ) {
-+ return NULL;
-+ }
-+
-+ //
-+ // search through the path, one element at a time
-+ //
-+
-+ hash = HashFileName( relativePath );
-+
-+ for ( search = searchPaths; search; search = search->next ) {
-+ if ( search->dir && ( searchFlags & FSFLAG_SEARCH_DIRS ) ) {
-+ // check a file in the directory tree
-+
-+ // if we are running restricted, the only files we
-+ // will allow to come from the directory are .cfg files
-+ if ( fs_restrict.GetBool() || serverPaks.Num() ) {
-+ if ( !FileAllowedFromDir( relativePath ) ) {
-+ continue;
-+ }
-+ }
-+
-+ dir = search->dir;
-+
-+ if(gamedir && strlen(gamedir)) {
-+ if(dir->gamedir != gamedir) {
-+ continue;
-+ }
-+ }
-+
-+ netpath = BuildOSPath( dir->path, dir->gamedir, relativePath );
-+ fp = OpenOSFileCorrectName( netpath, "rb" );
-+ if ( !fp ) {
-+ continue;
-+ }
-+
-+ idFile_Permanent *file = new idFile_Permanent();
-+ file->o = fp;
-+ file->name = relativePath;
-+ file->fullPath = netpath;
-+ file->mode = ( 1 << FS_READ );
-+ file->fileSize = DirectFileLength( file->o );
-+ if ( fs_debug.GetInteger() ) {
-+ common->Printf( "idFileSystem::OpenFileRead: %s (found in '%s/%s')\n", relativePath, dir->path.c_str(), dir->gamedir.c_str() );
-+ }
-+
-+ if ( !loadedFileFromDir && !FileAllowedFromDir( relativePath ) ) {
-+ if ( restartChecksums.Num() ) {
-+ common->FatalError( "'%s' loaded from directory: Failed to restart with pure mode restrictions for server connect", relativePath );
-+ }
-+ common->DPrintf( "filesystem: switching to pure mode will require a restart. '%s' loaded from directory.\n", relativePath );
-+ loadedFileFromDir = true;
-+ }
-+
-+ // if fs_copyfiles is set
-+ if ( allowCopyFiles && fs_copyfiles.GetInteger() ) {
-+
-+ idStr copypath;
-+ idStr name;
-+ copypath = BuildOSPath( fs_savepath.GetString(), dir->gamedir, relativePath );
-+ netpath.ExtractFileName( name );
-+ copypath.StripFilename( );
-+ copypath += PATHSEPERATOR_STR;
-+ copypath += name;
-+
-+ bool isFromCDPath = !dir->path.Cmp( fs_cdpath.GetString() );
-+ bool isFromSavePath = !dir->path.Cmp( fs_savepath.GetString() );
-+ bool isFromBasePath = !dir->path.Cmp( fs_basepath.GetString() );
-+
-+ switch ( fs_copyfiles.GetInteger() ) {
-+ case 1:
-+ // copy from cd path only
-+ if ( isFromCDPath ) {
-+ CopyFile( netpath, copypath );
-+ }
-+ break;
-+ case 2:
-+ // from cd path + timestamps
-+ if ( isFromCDPath ) {
-+ CopyFile( netpath, copypath );
-+ } else if ( isFromSavePath || isFromBasePath ) {
-+ idStr sourcepath;
-+ sourcepath = BuildOSPath( fs_cdpath.GetString(), dir->gamedir, relativePath );
-+ FILE *f1 = OpenOSFile( sourcepath, "r" );
-+ if ( f1 ) {
-+ ID_TIME_T t1 = Sys_FileTimeStamp( f1 );
-+ fclose( f1 );
-+ FILE *f2 = OpenOSFile( copypath, "r" );
-+ if ( f2 ) {
-+ ID_TIME_T t2 = Sys_FileTimeStamp( f2 );
-+ fclose( f2 );
-+ if ( t1 > t2 ) {
-+ CopyFile( sourcepath, copypath );
-+ }
-+ }
-+ }
-+ }
-+ break;
-+ case 3:
-+ if ( isFromCDPath || isFromBasePath ) {
-+ CopyFile( netpath, copypath );
-+ }
-+ break;
-+ case 4:
-+ if ( isFromCDPath && !isFromBasePath ) {
-+ CopyFile( netpath, copypath );
-+ }
-+ break;
-+ }
-+ }
-+
-+ return file;
-+ } else if ( search->pack && ( searchFlags & FSFLAG_SEARCH_PAKS ) ) {
-+
-+ if ( !search->pack->hashTable[hash] ) {
-+ continue;
-+ }
-+
-+ // disregard if it doesn't match one of the allowed pure pak files
-+ if ( serverPaks.Num() ) {
-+ GetPackStatus( search->pack );
-+ if ( search->pack->pureStatus != PURE_NEVER && !serverPaks.Find( search->pack ) ) {
-+ continue; // not on the pure server pak list
-+ }
-+ }
-+
-+ // look through all the pak file elements
-+ pak = search->pack;
-+ for ( pakFile = pak->hashTable[hash]; pakFile; pakFile = pakFile->next ) {
-+ // case and separator insensitive comparisons
-+ if ( !FilenameCompare( pakFile->name, relativePath ) ) {
-+ idFile_InZip *file = ReadFileFromZip( pak, pakFile, relativePath );
-+
-+ if ( foundInPak ) {
-+ *foundInPak = pak;
-+ }
-+
-+ if ( !pak->referenced && !( searchFlags & FSFLAG_PURE_NOREF ) ) {
-+ // mark this pak referenced
-+ if ( fs_debug.GetInteger( ) ) {
-+ common->Printf( "idFileSystem::OpenFileRead: %s -> adding %s to referenced paks\n", relativePath, pak->pakFilename.c_str() );
-+ }
-+ pak->referenced = true;
-+ }
-+
-+ if ( fs_debug.GetInteger( ) ) {
-+ common->Printf( "idFileSystem::OpenFileRead: %s (found in '%s')\n", relativePath, pak->pakFilename.c_str() );
-+ }
-+ return file;
-+ }
-+ }
-+ }
-+ }
-+
-+ if ( searchFlags & FSFLAG_SEARCH_ADDONS ) {
-+ for ( search = addonPaks; search; search = search->next ) {
-+ assert( search->pack );
-+ fileInPack_t *pakFile;
-+ pak = search->pack;
-+ for ( pakFile = pak->hashTable[hash]; pakFile; pakFile = pakFile->next ) {
-+ if ( !FilenameCompare( pakFile->name, relativePath ) ) {
-+ idFile_InZip *file = ReadFileFromZip( pak, pakFile, relativePath );
-+ if ( foundInPak ) {
-+ *foundInPak = pak;
-+ }
-+ // we don't toggle pure on paks found in addons - they can't be used without a reloadEngine anyway
-+ if ( fs_debug.GetInteger( ) ) {
-+ common->Printf( "idFileSystem::OpenFileRead: %s (found in addon pk4 '%s')\n", relativePath, search->pack->pakFilename.c_str() );
-+ }
-+ return file;
-+ }
-+ }
-+ }
-+ }
-+
-+ if ( fs_debug.GetInteger( ) ) {
-+ common->Printf( "Can't find %s\n", relativePath );
-+ }
-+
-+ return NULL;
-+}
-+
-+/*
-+===========
-+idFileSystemLocal::OpenFileRead
-+===========
-+*/
-+idFile *idFileSystemLocal::OpenFileRead( const char *relativePath, bool allowCopyFiles, const char* gamedir ) {
-+ return OpenFileReadFlags( relativePath, FSFLAG_SEARCH_DIRS | FSFLAG_SEARCH_PAKS, NULL, allowCopyFiles, gamedir );
-+}
-+
-+/*
-+===========
-+idFileSystemLocal::OpenFileWrite
-+===========
-+*/
-+idFile *idFileSystemLocal::OpenFileWrite( const char *relativePath, const char *basePath ) {
-+ const char *path;
-+ idStr OSpath;
-+ idFile_Permanent *f;
-+
-+ if ( !searchPaths ) {
-+ common->FatalError( "Filesystem call made without initialization\n" );
-+ }
-+
-+ path = cvarSystem->GetCVarString( basePath );
-+ if ( !path[0] ) {
-+ path = fs_savepath.GetString();
-+ }
-+
-+ OSpath = BuildOSPath( path, gameFolder, relativePath );
-+
-+ if ( fs_debug.GetInteger() ) {
-+ common->Printf( "idFileSystem::OpenFileWrite: %s\n", OSpath.c_str() );
-+ }
-+
-+ // if the dir we are writing to is in our current list, it will be outdated
-+ // so just flush everything
-+ ClearDirCache();
-+
-+ common->DPrintf( "writing to: %s\n", OSpath.c_str() );
-+ CreateOSPath( OSpath );
-+
-+ f = new idFile_Permanent();
-+ f->o = OpenOSFile( OSpath, "wb" );
-+ if ( !f->o ) {
-+ delete f;
-+ return NULL;
-+ }
-+ f->name = relativePath;
-+ f->fullPath = OSpath;
-+ f->mode = ( 1 << FS_WRITE );
-+ f->handleSync = false;
-+ f->fileSize = 0;
-+
-+ return f;
-+}
-+
-+/*
-+===========
-+idFileSystemLocal::OpenExplicitFileRead
-+===========
-+*/
-+idFile *idFileSystemLocal::OpenExplicitFileRead( const char *OSPath ) {
-+ idFile_Permanent *f;
-+
-+ if ( !searchPaths ) {
-+ common->FatalError( "Filesystem call made without initialization\n" );
-+ }
-+
-+ if ( fs_debug.GetInteger() ) {
-+ common->Printf( "idFileSystem::OpenExplicitFileRead: %s\n", OSPath );
-+ }
-+
-+ common->DPrintf( "idFileSystem::OpenExplicitFileRead - reading from: %s\n", OSPath );
-+
-+ f = new idFile_Permanent();
-+ f->o = OpenOSFile( OSPath, "rb" );
-+ if ( !f->o ) {
-+ delete f;
-+ return NULL;
-+ }
-+ f->name = OSPath;
-+ f->fullPath = OSPath;
-+ f->mode = ( 1 << FS_READ );
-+ f->handleSync = false;
-+ f->fileSize = DirectFileLength( f->o );
-+
-+ return f;
-+}
-+
-+/*
-+===========
-+idFileSystemLocal::OpenExplicitFileWrite
-+===========
-+*/
-+idFile *idFileSystemLocal::OpenExplicitFileWrite( const char *OSPath ) {
-+ idFile_Permanent *f;
-+
-+ if ( !searchPaths ) {
-+ common->FatalError( "Filesystem call made without initialization\n" );
-+ }
-+
-+ if ( fs_debug.GetInteger() ) {
-+ common->Printf( "idFileSystem::OpenExplicitFileWrite: %s\n", OSPath );
-+ }
-+
-+ common->DPrintf( "writing to: %s\n", OSPath );
-+ CreateOSPath( OSPath );
-+
-+ f = new idFile_Permanent();
-+ f->o = OpenOSFile( OSPath, "wb" );
-+ if ( !f->o ) {
-+ delete f;
-+ return NULL;
-+ }
-+ f->name = OSPath;
-+ f->fullPath = OSPath;
-+ f->mode = ( 1 << FS_WRITE );
-+ f->handleSync = false;
-+ f->fileSize = 0;
-+
-+ return f;
-+}
-+
-+/*
-+===========
-+idFileSystemLocal::OpenFileAppend
-+===========
-+*/
-+idFile *idFileSystemLocal::OpenFileAppend( const char *relativePath, bool sync, const char *basePath ) {
-+ const char *path;
-+ idStr OSpath;
-+ idFile_Permanent *f;
-+
-+ if ( !searchPaths ) {
-+ common->FatalError( "Filesystem call made without initialization\n" );
-+ }
-+
-+ path = cvarSystem->GetCVarString( basePath );
-+ if ( !path[0] ) {
-+ path = fs_savepath.GetString();
-+ }
-+
-+ OSpath = BuildOSPath( path, gameFolder, relativePath );
-+ CreateOSPath( OSpath );
-+
-+ if ( fs_debug.GetInteger() ) {
-+ common->Printf( "idFileSystem::OpenFileAppend: %s\n", OSpath.c_str() );
-+ }
-+
-+ f = new idFile_Permanent();
-+ f->o = OpenOSFile( OSpath, "ab" );
-+ if ( !f->o ) {
-+ delete f;
-+ return NULL;
-+ }
-+ f->name = relativePath;
-+ f->fullPath = OSpath;
-+ f->mode = ( 1 << FS_WRITE ) + ( 1 << FS_APPEND );
-+ f->handleSync = sync;
-+ f->fileSize = DirectFileLength( f->o );
-+
-+ return f;
-+}
-+
-+/*
-+================
-+idFileSystemLocal::OpenFileByMode
-+================
-+*/
-+idFile *idFileSystemLocal::OpenFileByMode( const char *relativePath, fsMode_t mode ) {
-+ if ( mode == FS_READ ) {
-+ return OpenFileRead( relativePath );
-+ }
-+ if ( mode == FS_WRITE ) {
-+ return OpenFileWrite( relativePath );
-+ }
-+ if ( mode == FS_APPEND ) {
-+ return OpenFileAppend( relativePath, true );
-+ }
-+ common->FatalError( "idFileSystemLocal::OpenFileByMode: bad mode" );
-+ return NULL;
-+}
-+
-+/*
-+==============
-+idFileSystemLocal::CloseFile
-+==============
-+*/
-+void idFileSystemLocal::CloseFile( idFile *f ) {
-+ if ( !searchPaths ) {
-+ common->FatalError( "Filesystem call made without initialization\n" );
-+ }
-+ delete f;
-+}
-+
-+
-+/*
-+=================================================================================
-+
-+back ground loading
-+
-+=================================================================================
-+*/
-+
-+/*
-+=================
-+idFileSystemLocal::CurlWriteFunction
-+=================
-+*/
-+size_t idFileSystemLocal::CurlWriteFunction( void *ptr, size_t size, size_t nmemb, void *stream ) {
-+ backgroundDownload_t *bgl = (backgroundDownload_t *)stream;
-+ if ( !bgl->f ) {
-+ return size * nmemb;
-+ }
-+ #ifdef _WIN32
-+ return _write( static_cast<idFile_Permanent*>(bgl->f)->GetFilePtr()->_file, ptr, size * nmemb );
-+ #else
-+ return fwrite( ptr, size, nmemb, static_cast<idFile_Permanent*>(bgl->f)->GetFilePtr() );
-+ #endif
-+}
-+
-+/*
-+=================
-+idFileSystemLocal::CurlProgressFunction
-+=================
-+*/
-+int idFileSystemLocal::CurlProgressFunction( void *clientp, double dltotal, double dlnow, double ultotal, double ulnow ) {
-+ backgroundDownload_t *bgl = (backgroundDownload_t *)clientp;
-+ if ( bgl->url.status == DL_ABORTING ) {
-+ return 1;
-+ }
-+ bgl->url.dltotal = dltotal;
-+ bgl->url.dlnow = dlnow;
-+ return 0;
-+}
-+
-+/*
-+===================
-+BackgroundDownload
-+
-+Reads part of a file from a background thread.
-+===================
-+*/
-+int BackgroundDownloadThread( void *pexit ) {
-+ bool *exit = (bool *)pexit;
-+
-+ while (!(*exit)) {
-+ Sys_EnterCriticalSection();
-+ backgroundDownload_t *bgl = fileSystemLocal.backgroundDownloads;
-+ if ( !bgl ) {
-+ Sys_LeaveCriticalSection();
-+ Sys_WaitForEvent();
-+ continue;
-+ }
-+ // remove this from the list
-+ fileSystemLocal.backgroundDownloads = bgl->next;
-+ Sys_LeaveCriticalSection();
-+
-+ bgl->next = NULL;
-+
-+ if ( bgl->opcode == DLTYPE_FILE ) {
-+ // use the low level read function, because fread may allocate memory
-+ #if defined(WIN32)
-+ _read( static_cast<idFile_Permanent*>(bgl->f)->GetFilePtr()->_file, bgl->file.buffer, bgl->file.length );
-+ #else
-+ fread( bgl->file.buffer, bgl->file.length, 1, static_cast<idFile_Permanent*>(bgl->f)->GetFilePtr() );
-+ #endif
-+ bgl->completed = true;
-+ } else {
-+#ifdef ID_ENABLE_CURL
-+ // DLTYPE_URL
-+ // use a local buffer for curl error since the size define is local
-+ char error_buf[ CURL_ERROR_SIZE ];
-+ bgl->url.dlerror[ 0 ] = '\0';
-+ CURL *session = curl_easy_init();
-+ CURLcode ret;
-+ if ( !session ) {
-+ bgl->url.dlstatus = CURLE_FAILED_INIT;
-+ bgl->url.status = DL_FAILED;
-+ bgl->completed = true;
-+ continue;
-+ }
-+ ret = curl_easy_setopt( session, CURLOPT_ERRORBUFFER, error_buf );
-+ if ( ret ) {
-+ bgl->url.dlstatus = ret;
-+ bgl->url.status = DL_FAILED;
-+ bgl->completed = true;
-+ continue;
-+ }
-+ ret = curl_easy_setopt( session, CURLOPT_URL, bgl->url.url.c_str() );
-+ if ( ret ) {
-+ bgl->url.dlstatus = ret;
-+ bgl->url.status = DL_FAILED;
-+ bgl->completed = true;
-+ continue;
-+ }
-+ ret = curl_easy_setopt( session, CURLOPT_FAILONERROR, 1 );
-+ if ( ret ) {
-+ bgl->url.dlstatus = ret;
-+ bgl->url.status = DL_FAILED;
-+ bgl->completed = true;
-+ continue;
-+ }
-+ ret = curl_easy_setopt( session, CURLOPT_WRITEFUNCTION, idFileSystemLocal::CurlWriteFunction );
-+ if ( ret ) {
-+ bgl->url.dlstatus = ret;
-+ bgl->url.status = DL_FAILED;
-+ bgl->completed = true;
-+ continue;
-+ }
-+ ret = curl_easy_setopt( session, CURLOPT_WRITEDATA, bgl );
-+ if ( ret ) {
-+ bgl->url.dlstatus = ret;
-+ bgl->url.status = DL_FAILED;
-+ bgl->completed = true;
-+ continue;
-+ }
-+ ret = curl_easy_setopt( session, CURLOPT_NOPROGRESS, 0 );
-+ if ( ret ) {
-+ bgl->url.dlstatus = ret;
-+ bgl->url.status = DL_FAILED;
-+ bgl->completed = true;
-+ continue;
-+ }
-+ ret = curl_easy_setopt( session, CURLOPT_PROGRESSFUNCTION, idFileSystemLocal::CurlProgressFunction );
-+ if ( ret ) {
-+ bgl->url.dlstatus = ret;
-+ bgl->url.status = DL_FAILED;
-+ bgl->completed = true;
-+ continue;
-+ }
-+ ret = curl_easy_setopt( session, CURLOPT_PROGRESSDATA, bgl );
-+ if ( ret ) {
-+ bgl->url.dlstatus = ret;
-+ bgl->url.status = DL_FAILED;
-+ bgl->completed = true;
-+ continue;
-+ }
-+ bgl->url.dlnow = 0;
-+ bgl->url.dltotal = 0;
-+ bgl->url.status = DL_INPROGRESS;
-+ ret = curl_easy_perform( session );
-+ if ( ret ) {
-+ Sys_Printf( "curl_easy_perform failed: %s\n", error_buf );
-+ idStr::Copynz( bgl->url.dlerror, error_buf, MAX_STRING_CHARS );
-+ bgl->url.dlstatus = ret;
-+ bgl->url.status = DL_FAILED;
-+ bgl->completed = true;
-+ continue;
-+ }
-+ bgl->url.status = DL_DONE;
-+ bgl->completed = true;
-+#else
-+ bgl->url.status = DL_FAILED;
-+ bgl->completed = true;
-+#endif
-+ }
-+ }
-+ return 0;
-+}
-+
-+/*
-+=================
-+idFileSystemLocal::StartBackgroundReadThread
-+=================
-+*/
-+void idFileSystemLocal::StartBackgroundDownloadThread() {
-+ if ( !backgroundThread.threadHandle ) {
-+ Sys_CreateThread( BackgroundDownloadThread, &backgroundThread_exit, backgroundThread, "backgroundDownload" );
-+ } else {
-+ common->Printf( "background thread already running\n" );
-+ }
-+}
-+
-+/*
-+=================
-+idFileSystemLocal::BackgroundDownload
-+=================
-+*/
-+void idFileSystemLocal::BackgroundDownload( backgroundDownload_t *bgl ) {
-+ if ( bgl->opcode == DLTYPE_FILE ) {
-+ if ( dynamic_cast<idFile_Permanent *>(bgl->f) ) {
-+ // add the bgl to the background download list
-+ Sys_EnterCriticalSection();
-+ bgl->next = backgroundDownloads;
-+ backgroundDownloads = bgl;
-+ Sys_TriggerEvent();
-+ Sys_LeaveCriticalSection();
-+ } else {
-+ // read zipped file directly
-+ bgl->f->Seek( bgl->file.position, FS_SEEK_SET );
-+ bgl->f->Read( bgl->file.buffer, bgl->file.length );
-+ bgl->completed = true;
-+ }
-+ } else {
-+ Sys_EnterCriticalSection();
-+ bgl->next = backgroundDownloads;
-+ backgroundDownloads = bgl;
-+ Sys_TriggerEvent();
-+ Sys_LeaveCriticalSection();
-+ }
-+}
-+
-+/*
-+=================
-+idFileSystemLocal::PerformingCopyFiles
-+=================
-+*/
-+bool idFileSystemLocal::PerformingCopyFiles( void ) const {
-+ return fs_copyfiles.GetInteger() > 0;
-+}
-+
-+/*
-+=================
-+idFileSystemLocal::FindPakForFileChecksum
-+=================
-+*/
-+pack_t *idFileSystemLocal::FindPakForFileChecksum( const char *relativePath, int findChecksum, bool bReference ) {
-+ searchpath_t *search;
-+ pack_t *pak;
-+ fileInPack_t *pakFile;
-+ int hash;
-+ assert( !serverPaks.Num() );
-+ hash = HashFileName( relativePath );
-+ for ( search = searchPaths; search; search = search->next ) {
-+ if ( search->pack && search->pack->hashTable[ hash ] ) {
-+ pak = search->pack;
-+ for ( pakFile = pak->hashTable[ hash ]; pakFile; pakFile = pakFile->next ) {
-+ if ( !FilenameCompare( pakFile->name, relativePath ) ) {
-+ idFile_InZip *file = ReadFileFromZip( pak, pakFile, relativePath );
-+ if ( findChecksum == GetFileChecksum( file ) ) {
-+ if ( fs_debug.GetBool() ) {
-+ common->Printf( "found '%s' with checksum 0x%x in pak '%s'\n", relativePath, findChecksum, pak->pakFilename.c_str() );
-+ }
-+ if ( bReference ) {
-+ pak->referenced = true;
-+ // FIXME: use dependencies for pak references
-+ }
-+ CloseFile( file );
-+ return pak;
-+ } else if ( fs_debug.GetBool() ) {
-+ common->Printf( "'%s' in pak '%s' has != checksum %x\n", relativePath, pak->pakFilename.c_str(), GetFileChecksum( file ) );
-+ }
-+ CloseFile( file );
-+ }
-+ }
-+ }
-+ }
-+ if ( fs_debug.GetBool() ) {
-+ common->Printf( "no pak file found for '%s' checksumed %x\n", relativePath, findChecksum );
-+ }
-+ return NULL;
-+}
-+
-+/*
-+=================
-+idFileSystemLocal::GetFileChecksum
-+=================
-+*/
-+int idFileSystemLocal::GetFileChecksum( idFile *file ) {
-+ int len, ret;
-+ byte *buf;
-+
-+ file->Seek( 0, FS_SEEK_END );
-+ len = file->Tell();
-+ file->Seek( 0, FS_SEEK_SET );
-+ buf = (byte *)Mem_Alloc( len );
-+ if ( file->Read( buf, len ) != len ) {
-+ common->FatalError( "Short read in idFileSystemLocal::GetFileChecksum()\n" );
-+ }
-+ ret = MD4_BlockChecksum( buf, len );
-+ Mem_Free( buf );
-+ return ret;
-+}
-+
-+/*
-+=================
-+idFileSystemLocal::FindDLL
-+=================
-+*/
-+void idFileSystemLocal::FindDLL( const char *name, char _dllPath[ MAX_OSPATH ] ) {
-+ idFile *dllFile = NULL;
-+ char dllName[MAX_OSPATH];
-+ idStr dllPath;
-+
-+ sys->DLL_GetFileName( name, dllName, MAX_OSPATH );
-+
-+ if (!serverPaks.Num() && Sys_GetPath(PATH_EXE, dllPath)) {
-+ // from executable directory first - this is handy for developement
-+ dllPath.StripFilename( );
-+ dllPath.AppendPath( dllName );
-+ dllFile = OpenExplicitFileRead( dllPath );
-+ }
-+
-+ if ( !dllFile && !serverPaks.Num() )
-+ dllFile = OpenFileReadFlags( dllName, FSFLAG_SEARCH_DIRS );
-+
-+ if ( dllFile ) {
-+ dllPath = dllFile->GetFullPath( );
-+ CloseFile( dllFile );
-+ dllFile = NULL;
-+ } else {
-+ dllPath = "";
-+ }
-+ idStr::snPrintf( _dllPath, MAX_OSPATH, dllPath.c_str() );
-+}
-+
-+/*
-+================
-+idFileSystemLocal::ClearDirCache
-+================
-+*/
-+void idFileSystemLocal::ClearDirCache( void ) {
-+ int i;
-+
-+ dir_cache_index = 0;
-+ dir_cache_count = 0;
-+ for( i = 0; i < MAX_CACHED_DIRS; i++ ) {
-+ dir_cache[ i ].Clear();
-+ }
-+}
-+
-+/*
-+===============
-+idFileSystemLocal::HasD3XP
-+===============
-+*/
-+bool idFileSystemLocal::HasD3XP( void ) {
-+ int i;
-+ idStrList dirs, pk4s;
-+ idStr gamepath;
-+
-+ if ( d3xp == -1 ) {
-+ return false;
-+ } else if ( d3xp == 1 ) {
-+ return true;
-+ }
-+
-+#if 0
-+ // check for a d3xp directory with a pk4 file
-+ // copied over from ListMods - only looks in basepath
-+ ListOSFiles( fs_basepath.GetString(), "/", dirs );
-+ for ( i = 0; i < dirs.Num(); i++ ) {
-+ if ( dirs[i].Icmp( "d3xp" ) == 0 ) {
-+ gamepath = BuildOSPath( fs_basepath.GetString(), dirs[ i ], "" );
-+ ListOSFiles( gamepath, ".pk4", pk4s );
-+ if ( pk4s.Num() ) {
-+ d3xp = 1;
-+ return true;
-+ }
-+ }
-+ }
-+#else
-+ // check for d3xp's d3xp/pak000.pk4 in any search path
-+ // checking wether the pak is loaded by checksum wouldn't be enough:
-+ // we may have a different fs_game right now but still need to reply that it's installed
-+ const char *search[4];
-+ idFile *pakfile;
-+ search[0] = fs_savepath.GetString();
-+ search[1] = fs_devpath.GetString();
-+ search[2] = fs_basepath.GetString();
-+ search[3] = fs_cdpath.GetString();
-+ for ( i = 0; i < 4; i++ ) {
-+ pakfile = OpenExplicitFileRead( BuildOSPath( search[ i ], "d3xp", "pak000.pk4" ) );
-+ if ( pakfile ) {
-+ CloseFile( pakfile );
-+ d3xp = 1;
-+ return true;
-+ }
-+ }
-+#endif
-+
-+ // if we didn't find a pk4 file then the user might have unpacked so look for default.cfg file
-+ // that's the old way mostly used during developement. don't think it hurts to leave it there
-+ ListOSFiles( fs_basepath.GetString(), "/", dirs );
-+ for ( i = 0; i < dirs.Num(); i++ ) {
-+ if ( dirs[i].Icmp( "d3xp" ) == 0 ) {
-+
-+ gamepath = BuildOSPath( fs_configpath.GetString(), dirs[ i ], "default.cfg" );
-+ idFile* cfg = OpenExplicitFileRead(gamepath);
-+ if(cfg) {
-+ CloseFile(cfg);
-+ d3xp = 1;
-+ return true;
-+ }
-+ }
-+ }
-+
-+ d3xp = -1;
-+ return false;
-+}
-+
-+/*
-+===============
-+idFileSystemLocal::RunningD3XP
-+===============
-+*/
-+bool idFileSystemLocal::RunningD3XP( void ) {
-+ // TODO: mark the checksum of the gold XP and check for it being referenced ( for double mod support )
-+ // a simple fs_game check should be enough for now..
-+ if ( !idStr::Icmp( fs_game.GetString(), "d3xp" ) ||
-+ !idStr::Icmp( fs_game_base.GetString(), "d3xp" ) ) {
-+ return true;
-+ }
-+ return false;
-+}
-+
-+/*
-+===============
-+idFileSystemLocal::MakeTemporaryFile
-+===============
-+*/
-+idFile * idFileSystemLocal::MakeTemporaryFile( void ) {
-+ FILE *f = tmpfile();
-+ if ( !f ) {
-+ common->Warning( "idFileSystem::MakeTemporaryFile failed: %s", strerror( errno ) );
-+ return NULL;
-+ }
-+ idFile_Permanent *file = new idFile_Permanent();
-+ file->o = f;
-+ file->name = "<tempfile>";
-+ file->fullPath = "<tempfile>";
-+ file->mode = ( 1 << FS_READ ) + ( 1 << FS_WRITE );
-+ file->fileSize = 0;
-+ return file;
-+}
-+
-+/*
-+===============
-+idFileSystemLocal::FindFile
-+===============
-+*/
-+ findFile_t idFileSystemLocal::FindFile( const char *path, bool scheduleAddons ) {
-+ pack_t *pak;
-+ idFile *f = OpenFileReadFlags( path, FSFLAG_SEARCH_DIRS | FSFLAG_SEARCH_PAKS | FSFLAG_SEARCH_ADDONS, &pak );
-+ if ( !f ) {
-+ return FIND_NO;
-+ }
-+ if ( !pak ) {
-+ // found in FS, not even in paks
-+ return FIND_YES;
-+ }
-+ // marking addons for inclusion on reload - may need to do that even when already in the search path
-+ if ( scheduleAddons && pak->addon && addonChecksums.FindIndex( pak->checksum ) < 0 ) {
-+ addonChecksums.Append( pak->checksum );
-+ }
-+ // an addon that's not on search list yet? that will require a restart
-+ if ( pak->addon && !pak->addon_search ) {
-+ delete f;
-+ return FIND_ADDON;
-+ }
-+ delete f;
-+ return FIND_YES;
-+}
-+
-+/*
-+===============
-+idFileSystemLocal::GetNumMaps
-+account for actual decls and for addon maps
-+===============
-+*/
-+int idFileSystemLocal::GetNumMaps() {
-+ int i;
-+ searchpath_t *search = NULL;
-+ int ret = declManager->GetNumDecls( DECL_MAPDEF );
-+
-+ // add to this all addon decls - coming from all addon packs ( searched or not )
-+ for ( i = 0; i < 2; i++ ) {
-+ if ( i == 0 ) {
-+ search = searchPaths;
-+ } else if ( i == 1 ) {
-+ search = addonPaks;
-+ }
-+ for ( ; search ; search = search->next ) {
-+ if ( !search->pack || !search->pack->addon || !search->pack->addon_info ) {
-+ continue;
-+ }
-+ ret += search->pack->addon_info->mapDecls.Num();
-+ }
-+ }
-+ return ret;
-+}
-+
-+/*
-+===============
-+idFileSystemLocal::GetMapDecl
-+retrieve the decl dictionary, add a 'path' value
-+===============
-+*/
-+const idDict * idFileSystemLocal::GetMapDecl( int idecl ) {
-+ int i;
-+ const idDecl *mapDecl;
-+ const idDeclEntityDef *mapDef;
-+ int numdecls = declManager->GetNumDecls( DECL_MAPDEF );
-+ searchpath_t *search = NULL;
-+
-+ if ( idecl < numdecls ) {
-+ mapDecl = declManager->DeclByIndex( DECL_MAPDEF, idecl );
-+ mapDef = static_cast<const idDeclEntityDef *>( mapDecl );
-+ if ( !mapDef ) {
-+ common->Error( "idFileSystemLocal::GetMapDecl %d: not found\n", idecl );
-+ }
-+ mapDict = mapDef->dict;
-+ mapDict.Set( "path", mapDef->GetName() );
-+ return &mapDict;
-+ }
-+ idecl -= numdecls;
-+ for ( i = 0; i < 2; i++ ) {
-+ if ( i == 0 ) {
-+ search = searchPaths;
-+ } else if ( i == 1 ) {
-+ search = addonPaks;
-+ }
-+ for ( ; search ; search = search->next ) {
-+ if ( !search->pack || !search->pack->addon || !search->pack->addon_info ) {
-+ continue;
-+ }
-+ // each addon may have a bunch of map decls
-+ if ( idecl < search->pack->addon_info->mapDecls.Num() ) {
-+ mapDict = *search->pack->addon_info->mapDecls[ idecl ];
-+ return &mapDict;
-+ }
-+ idecl -= search->pack->addon_info->mapDecls.Num();
-+ assert( idecl >= 0 );
-+ }
-+ }
-+ return NULL;
-+}
-+
-+/*
-+===============
-+idFileSystemLocal::FindMapScreenshot
-+===============
-+*/
-+void idFileSystemLocal::FindMapScreenshot( const char *path, char *buf, int len ) {
-+ idFile *file;
-+ idStr mapname = path;
-+
-+ mapname.StripPath();
-+ mapname.StripFileExtension();
-+
-+ idStr::snPrintf( buf, len, "guis/assets/splash/%s.tga", mapname.c_str() );
-+ if ( ReadFile( buf, NULL, NULL ) == -1 ) {
-+ // try to extract from an addon
-+ file = OpenFileReadFlags( buf, FSFLAG_SEARCH_ADDONS );
-+ if ( file ) {
-+ // save it out to an addon splash directory
-+ int dlen = file->Length();
-+ char *data = new char[ dlen ];
-+ file->Read( data, dlen );
-+ CloseFile( file );
-+ idStr::snPrintf( buf, len, "guis/assets/splash/addon/%s.tga", mapname.c_str() );
-+ WriteFile( buf, data, dlen );
-+ delete[] data;
-+ } else {
-+ idStr::Copynz( buf, "guis/assets/splash/pdtempa", len );
-+ }
-+ }
-+}
-diff -Naur dhewm3.old/neo/framework/Licensee.h dhewm3/neo/framework/Licensee.h
---- dhewm3.old/neo/framework/Licensee.h 2013-08-15 23:27:20.000000000 +0200
-+++ dhewm3/neo/framework/Licensee.h 2013-08-22 13:23:33.000219548 +0200
-@@ -92,12 +92,4 @@
- // Linux info
- #define LINUX_DEFAULT_PATH "/usr/local/games/doom3"
-
--// CD Key file info
--// goes into BASE_GAMEDIR whatever the fs_game is set to
--// two distinct files for easier win32 installer job
--#define CDKEY_FILE "doomkey"
--#define XPKEY_FILE "xpkey"
--#define CDKEY_TEXT "\n// Do not give this file to ANYONE.\n" \
-- "// id Software or Zenimax will NEVER ask you to send this file to them.\n"
--
- #define CONFIG_SPEC "config.spec"
-diff -Naur dhewm3.old/neo/framework/Session.cpp dhewm3/neo/framework/Session.cpp
---- dhewm3.old/neo/framework/Session.cpp 2013-08-15 23:27:20.000000000 +0200
-+++ dhewm3/neo/framework/Session.cpp 2013-08-22 13:23:33.001219540 +0200
-@@ -210,86 +210,6 @@
- }
-
- /*
--===============
--idSessionLocal::MaybeWaitOnCDKey
--===============
--*/
--bool idSessionLocal::MaybeWaitOnCDKey( void ) {
-- if ( authEmitTimeout > 0 ) {
-- authWaitBox = true;
-- sessLocal.MessageBox( MSG_WAIT, common->GetLanguageDict()->GetString( "#str_07191" ), NULL, true, NULL, NULL, true );
-- return true;
-- }
-- return false;
--}
--
--/*
--===================
--Session_PromptKey_f
--===================
--*/
--static void Session_PromptKey_f( const idCmdArgs &args ) {
-- const char *retkey;
-- bool valid[ 2 ];
-- static bool recursed = false;
--
-- if ( recursed ) {
-- common->Warning( "promptKey recursed - aborted" );
-- return;
-- }
-- recursed = true;
--
-- do {
-- // in case we're already waiting for an auth to come back to us ( may happen exceptionally )
-- if ( sessLocal.MaybeWaitOnCDKey() ) {
-- if ( sessLocal.CDKeysAreValid( true ) ) {
-- recursed = false;
-- return;
-- }
-- }
-- // the auth server may have replied and set an error message, otherwise use a default
-- const char *prompt_msg = sessLocal.GetAuthMsg();
-- if ( prompt_msg[ 0 ] == '\0' ) {
-- prompt_msg = common->GetLanguageDict()->GetString( "#str_04308" );
-- }
-- retkey = sessLocal.MessageBox( MSG_CDKEY, prompt_msg, common->GetLanguageDict()->GetString( "#str_04305" ), true, NULL, NULL, true );
-- if ( retkey ) {
-- if ( sessLocal.CheckKey( retkey, false, valid ) ) {
-- // if all went right, then we may have sent an auth request to the master ( unless the prompt is used during a net connect )
-- bool canExit = true;
-- if ( sessLocal.MaybeWaitOnCDKey() ) {
-- // wait on auth reply, and got denied, prompt again
-- if ( !sessLocal.CDKeysAreValid( true ) ) {
-- // server says key is invalid - MaybeWaitOnCDKey was interrupted by a CDKeysAuthReply call, which has set the right error message
-- // the invalid keys have also been cleared in the process
-- sessLocal.MessageBox( MSG_OK, sessLocal.GetAuthMsg(), common->GetLanguageDict()->GetString( "#str_04310" ), true, NULL, NULL, true );
-- canExit = false;
-- }
-- }
-- if ( canExit ) {
-- // make sure that's saved on file
-- sessLocal.WriteCDKey();
-- sessLocal.MessageBox( MSG_OK, common->GetLanguageDict()->GetString( "#str_04307" ), common->GetLanguageDict()->GetString( "#str_04305" ), true, NULL, NULL, true );
-- break;
-- }
-- } else {
-- // offline check sees key invalid
-- // build a message about keys being wrong. do not attempt to change the current key state though
-- // ( the keys may be valid, but user would have clicked on the dialog anyway, that kind of thing )
-- idStr msg;
-- idAsyncNetwork::BuildInvalidKeyMsg( msg, valid );
-- sessLocal.MessageBox( MSG_OK, msg, common->GetLanguageDict()->GetString( "#str_04310" ), true, NULL, NULL, true );
-- }
-- } else if ( args.Argc() == 2 && idStr::Icmp( args.Argv(1), "force" ) == 0 ) {
-- // cancelled in force mode
-- cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "quit\n" );
-- cmdSystem->ExecuteCommandBuffer();
-- }
-- } while ( retkey );
-- recursed = false;
--}
--
--/*
- ===============================================================================
-
- SESSION LOCAL
-@@ -1151,23 +1071,6 @@
- common->Printf( "Dedicated servers cannot start singleplayer games.\n" );
- return;
- #else
--#if ID_ENFORCE_KEY
-- // strict check. don't let a game start without a definitive answer
-- if ( !CDKeysAreValid( true ) ) {
-- bool prompt = true;
-- if ( MaybeWaitOnCDKey() ) {
-- // check again, maybe we just needed more time
-- if ( CDKeysAreValid( true ) ) {
-- // can continue directly
-- prompt = false;
-- }
-- }
-- if ( prompt ) {
-- cmdSystem->BufferCommandText( CMD_EXEC_NOW, "promptKey force" );
-- cmdSystem->ExecuteCommandBuffer();
-- }
-- }
--#endif
- if ( idAsyncNetwork::server.IsActive() ) {
- common->Printf("Server running, use si_map / serverMapRestart\n");
- return;
-@@ -2599,16 +2502,9 @@
- StopBox();
- authWaitBox = false;
- }
-- if ( cdkey_state == CDKEY_CHECKING ) {
-- cdkey_state = CDKEY_OK;
-- }
-- if ( xpkey_state == CDKEY_CHECKING ) {
-- xpkey_state = CDKEY_OK;
-- }
- // maintain this empty as it's set by auth denials
- authMsg.Empty();
- authEmitTimeout = 0;
-- SetCDKeyGuiVars();
- }
- }
-
-@@ -2867,8 +2763,6 @@
-
- cmdSystem->AddCommand( "rescanSI", Session_RescanSI_f, CMD_FL_SYSTEM, "internal - rescan serverinfo cvars and tell game" );
-
-- cmdSystem->AddCommand( "promptKey", Session_PromptKey_f, CMD_FL_SYSTEM, "prompt and sets the CD Key" );
--
- cmdSystem->AddCommand( "hitch", Session_Hitch_f, CMD_FL_SYSTEM|CMD_FL_CHEAT, "hitches the game" );
-
- // the same idRenderWorld will be used for all games
-@@ -2897,8 +2791,6 @@
-
- guiActive = NULL;
- guiHandle = NULL;
--
-- ReadCDKey();
- }
-
- /*
-@@ -2948,335 +2840,6 @@
- }
-
- /*
--=================
--idSessionLocal::ReadCDKey
--=================
--*/
--void idSessionLocal::ReadCDKey( void ) {
-- idStr filename;
-- idFile *f;
-- char buffer[32];
--
-- cdkey_state = CDKEY_UNKNOWN;
--
-- filename = "../" BASE_GAMEDIR "/" CDKEY_FILE;
-- f = fileSystem->OpenExplicitFileRead( fileSystem->RelativePathToOSPath( filename, "fs_configpath" ) );
--
-- // try the install path, which is where the cd installer and steam put it
-- if ( !f )
-- f = fileSystem->OpenExplicitFileRead( fileSystem->RelativePathToOSPath( filename, "fs_basepath" ) );
--
-- if ( !f ) {
-- common->Printf( "Couldn't read %s.\n", filename.c_str() );
-- cdkey[ 0 ] = '\0';
-- } else {
-- memset( buffer, 0, sizeof(buffer) );
-- f->Read( buffer, CDKEY_BUF_LEN - 1 );
-- fileSystem->CloseFile( f );
-- idStr::Copynz( cdkey, buffer, CDKEY_BUF_LEN );
-- }
--
-- xpkey_state = CDKEY_UNKNOWN;
--
-- filename = "../" BASE_GAMEDIR "/" XPKEY_FILE;
-- f = fileSystem->OpenExplicitFileRead( fileSystem->RelativePathToOSPath( filename, "fs_configpath" ) );
--
-- // try the install path, which is where the cd installer and steam put it
-- if ( !f )
-- f = fileSystem->OpenExplicitFileRead( fileSystem->RelativePathToOSPath( filename, "fs_basepath" ) );
--
-- if ( !f ) {
-- common->Printf( "Couldn't read %s.\n", filename.c_str() );
-- xpkey[ 0 ] = '\0';
-- } else {
-- memset( buffer, 0, sizeof(buffer) );
-- f->Read( buffer, CDKEY_BUF_LEN - 1 );
-- fileSystem->CloseFile( f );
-- idStr::Copynz( xpkey, buffer, CDKEY_BUF_LEN );
-- }
--}
--
--/*
--================
--idSessionLocal::WriteCDKey
--================
--*/
--void idSessionLocal::WriteCDKey( void ) {
-- idStr filename;
-- idFile *f;
-- const char *OSPath;
--
-- filename = "../" BASE_GAMEDIR "/" CDKEY_FILE;
-- // OpenFileWrite advertises creating directories to the path if needed, but that won't work with a '..' in the path
-- // occasionally on windows, but mostly on Linux and OSX, the fs_configpath/base may not exist in full
-- OSPath = fileSystem->BuildOSPath( cvarSystem->GetCVarString( "fs_configpath" ), BASE_GAMEDIR, CDKEY_FILE );
-- fileSystem->CreateOSPath( OSPath );
-- f = fileSystem->OpenFileWrite( filename, "fs_configpath" );
-- if ( !f ) {
-- common->Printf( "Couldn't write %s.\n", filename.c_str() );
-- return;
-- }
-- f->Printf( "%s%s", cdkey, CDKEY_TEXT );
-- fileSystem->CloseFile( f );
--
-- filename = "../" BASE_GAMEDIR "/" XPKEY_FILE;
-- f = fileSystem->OpenFileWrite( filename, "fs_configpath" );
-- if ( !f ) {
-- common->Printf( "Couldn't write %s.\n", filename.c_str() );
-- return;
-- }
-- f->Printf( "%s%s", xpkey, CDKEY_TEXT );
-- fileSystem->CloseFile( f );
--}
--
--/*
--===============
--idSessionLocal::ClearKey
--===============
--*/
--void idSessionLocal::ClearCDKey( bool valid[ 2 ] ) {
-- if ( !valid[ 0 ] ) {
-- memset( cdkey, 0, CDKEY_BUF_LEN );
-- cdkey_state = CDKEY_UNKNOWN;
-- } else if ( cdkey_state == CDKEY_CHECKING ) {
-- // if a key was in checking and not explicitely asked for clearing, put it back to ok
-- cdkey_state = CDKEY_OK;
-- }
-- if ( !valid[ 1 ] ) {
-- memset( xpkey, 0, CDKEY_BUF_LEN );
-- xpkey_state = CDKEY_UNKNOWN;
-- } else if ( xpkey_state == CDKEY_CHECKING ) {
-- xpkey_state = CDKEY_OK;
-- }
-- WriteCDKey( );
--}
--
--/*
--================
--idSessionLocal::GetCDKey
--================
--*/
--const char *idSessionLocal::GetCDKey( bool xp ) {
-- if ( !xp ) {
-- return cdkey;
-- }
-- if ( xpkey_state == CDKEY_OK || xpkey_state == CDKEY_CHECKING ) {
-- return xpkey;
-- }
-- return NULL;
--}
--
--// digits to letters table
--#define CDKEY_DIGITS "TWSBJCGD7PA23RLH"
--
--/*
--===============
--idSessionLocal::EmitGameAuth
--we toggled some key state to CDKEY_CHECKING. send a standalone auth packet to validate
--===============
--*/
--void idSessionLocal::EmitGameAuth( void ) {
-- // make sure the auth reply is empty, we use it to indicate an auth reply
-- authMsg.Empty();
-- if ( idAsyncNetwork::client.SendAuthCheck( cdkey_state == CDKEY_CHECKING ? cdkey : NULL, xpkey_state == CDKEY_CHECKING ? xpkey : NULL ) ) {
-- authEmitTimeout = Sys_Milliseconds() + CDKEY_AUTH_TIMEOUT;
-- common->DPrintf( "authing with the master..\n" );
-- } else {
-- // net is not available
-- common->DPrintf( "sendAuthCheck failed\n" );
-- if ( cdkey_state == CDKEY_CHECKING ) {
-- cdkey_state = CDKEY_OK;
-- }
-- if ( xpkey_state == CDKEY_CHECKING ) {
-- xpkey_state = CDKEY_OK;
-- }
-- }
--}
--
--/*
--================
--idSessionLocal::CheckKey
--the function will only modify keys to _OK or _CHECKING if the offline checks are passed
--if the function returns false, the offline checks failed, and offline_valid holds which keys are bad
--================
--*/
--bool idSessionLocal::CheckKey( const char *key, bool netConnect, bool offline_valid[ 2 ] ) {
-- char lkey[ 2 ][ CDKEY_BUF_LEN ];
-- char l_chk[ 2 ][ 3 ];
-- char s_chk[ 3 ];
-- int imax,i_key;
-- unsigned int checksum, chk8;
-- bool edited_key[ 2 ];
--
-- // make sure have a right input string
-- assert( strlen( key ) == ( CDKEY_BUF_LEN - 1 ) * 2 + 4 + 3 + 4 );
--
-- edited_key[ 0 ] = ( key[0] == '1' );
-- idStr::Copynz( lkey[0], key + 2, CDKEY_BUF_LEN );
-- idStr::ToUpper( lkey[0] );
-- idStr::Copynz( l_chk[0], key + CDKEY_BUF_LEN + 2, 3 );
-- idStr::ToUpper( l_chk[0] );
-- edited_key[ 1 ] = ( key[ CDKEY_BUF_LEN + 2 + 3 ] == '1' );
-- idStr::Copynz( lkey[1], key + CDKEY_BUF_LEN + 7, CDKEY_BUF_LEN );
-- idStr::ToUpper( lkey[1] );
-- idStr::Copynz( l_chk[1], key + CDKEY_BUF_LEN * 2 + 7, 3 );
-- idStr::ToUpper( l_chk[1] );
--
-- if ( fileSystem->HasD3XP() ) {
-- imax = 2;
-- } else {
-- imax = 1;
-- }
-- offline_valid[ 0 ] = offline_valid[ 1 ] = true;
-- for( i_key = 0; i_key < imax; i_key++ ) {
-- // check that the characters are from the valid set
-- int i;
-- for ( i = 0; i < CDKEY_BUF_LEN - 1; i++ ) {
-- if ( !strchr( CDKEY_DIGITS, lkey[i_key][i] ) ) {
-- offline_valid[ i_key ] = false;
-- continue;
-- }
-- }
--
-- if ( edited_key[ i_key ] ) {
-- // verify the checksum for edited keys only
-- checksum = CRC32_BlockChecksum( lkey[i_key], CDKEY_BUF_LEN - 1 );
-- chk8 = ( checksum & 0xff ) ^ ( ( ( checksum & 0xff00 ) >> 8 ) ^ ( ( ( checksum & 0xff0000 ) >> 16 ) ^ ( ( checksum & 0xff000000 ) >> 24 ) ) );
-- idStr::snPrintf( s_chk, 3, "%02X", chk8 );
-- if ( idStr::Icmp( l_chk[i_key], s_chk ) != 0 ) {
-- offline_valid[ i_key ] = false;
-- continue;
-- }
-- }
-- }
--
-- if ( !offline_valid[ 0 ] || !offline_valid[1] ) {
-- return false;
-- }
--
-- // offline checks passed, we'll return true and optionally emit key check requests
-- // the function should only modify the key states if the offline checks passed successfully
--
-- // set the keys, don't send a game auth if we are net connecting
-- idStr::Copynz( cdkey, lkey[0], CDKEY_BUF_LEN );
-- netConnect ? cdkey_state = CDKEY_OK : cdkey_state = CDKEY_CHECKING;
-- if ( fileSystem->HasD3XP() ) {
-- idStr::Copynz( xpkey, lkey[1], CDKEY_BUF_LEN );
-- netConnect ? xpkey_state = CDKEY_OK : xpkey_state = CDKEY_CHECKING;
-- } else {
-- xpkey_state = CDKEY_NA;
-- }
-- if ( !netConnect ) {
-- EmitGameAuth();
-- }
-- SetCDKeyGuiVars();
--
-- return true;
--}
--
--/*
--===============
--idSessionLocal::CDKeysAreValid
--checking that the key is present and uses only valid characters
--if d3xp is installed, check for a valid xpkey as well
--emit an auth packet to the master if possible and needed
--===============
--*/
--bool idSessionLocal::CDKeysAreValid( bool strict ) {
-- int i;
-- bool emitAuth = false;
--
-- if ( cdkey_state == CDKEY_UNKNOWN ) {
-- if ( strlen( cdkey ) != CDKEY_BUF_LEN - 1 ) {
-- cdkey_state = CDKEY_INVALID;
-- } else {
-- for ( i = 0; i < CDKEY_BUF_LEN-1; i++ ) {
-- if ( !strchr( CDKEY_DIGITS, cdkey[i] ) ) {
-- cdkey_state = CDKEY_INVALID;
-- break;
-- }
-- }
-- }
-- if ( cdkey_state == CDKEY_UNKNOWN ) {
-- cdkey_state = CDKEY_CHECKING;
-- emitAuth = true;
-- }
-- }
-- if ( xpkey_state == CDKEY_UNKNOWN ) {
-- if ( fileSystem->HasD3XP() ) {
-- if ( strlen( xpkey ) != CDKEY_BUF_LEN -1 ) {
-- xpkey_state = CDKEY_INVALID;
-- } else {
-- for ( i = 0; i < CDKEY_BUF_LEN-1; i++ ) {
-- if ( !strchr( CDKEY_DIGITS, xpkey[i] ) ) {
-- xpkey_state = CDKEY_INVALID;
-- }
-- }
-- }
-- if ( xpkey_state == CDKEY_UNKNOWN ) {
-- xpkey_state = CDKEY_CHECKING;
-- emitAuth = true;
-- }
-- } else {
-- xpkey_state = CDKEY_NA;
-- }
-- }
-- if ( emitAuth ) {
-- EmitGameAuth();
-- }
-- // make sure to keep the mainmenu gui up to date in case we made state changes
-- SetCDKeyGuiVars();
-- if ( strict ) {
-- return cdkey_state == CDKEY_OK && ( xpkey_state == CDKEY_OK || xpkey_state == CDKEY_NA );
-- } else {
-- return ( cdkey_state == CDKEY_OK || cdkey_state == CDKEY_CHECKING ) && ( xpkey_state == CDKEY_OK || xpkey_state == CDKEY_CHECKING || xpkey_state == CDKEY_NA );
-- }
--}
--
--/*
--===============
--idSessionLocal::WaitingForGameAuth
--===============
--*/
--bool idSessionLocal::WaitingForGameAuth( void ) {
-- return authEmitTimeout != 0;
--}
--
--/*
--===============
--idSessionLocal::CDKeysAuthReply
--===============
--*/
--void idSessionLocal::CDKeysAuthReply( bool valid, const char *auth_msg ) {
-- //assert( authEmitTimeout > 0 );
-- if ( authWaitBox ) {
-- // close the wait box
-- StopBox();
-- authWaitBox = false;
-- }
-- if ( !valid ) {
-- common->DPrintf( "auth key is invalid\n" );
-- authMsg = auth_msg;
-- if ( cdkey_state == CDKEY_CHECKING ) {
-- cdkey_state = CDKEY_INVALID;
-- }
-- if ( xpkey_state == CDKEY_CHECKING ) {
-- xpkey_state = CDKEY_INVALID;
-- }
-- } else {
-- common->DPrintf( "client is authed in\n" );
-- if ( cdkey_state == CDKEY_CHECKING ) {
-- cdkey_state = CDKEY_OK;
-- }
-- if ( xpkey_state == CDKEY_CHECKING ) {
-- xpkey_state = CDKEY_OK;
-- }
-- }
-- authEmitTimeout = 0;
-- SetCDKeyGuiVars();
--}
--
--/*
- ===============
- idSessionLocal::GetCurrentMapName
- ===============
-diff -Naur dhewm3.old/neo/framework/Session.cpp.orig dhewm3/neo/framework/Session.cpp.orig
---- dhewm3.old/neo/framework/Session.cpp.orig 1970-01-01 01:00:00.000000000 +0100
-+++ dhewm3/neo/framework/Session.cpp.orig 2013-08-22 13:23:33.003219524 +0200
-@@ -0,0 +1,3304 @@
-+/*
-+===========================================================================
-+
-+Doom 3 GPL Source Code
-+Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
-+
-+This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
-+
-+Doom 3 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 3 of the License, or
-+(at your option) any later version.
-+
-+Doom 3 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 Doom 3 Source Code. If not, see <http://www.gnu.org/licenses/>.
-+
-+In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code. If not, please request a copy in writing from id Software at the address below.
-+
-+If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
-+
-+===========================================================================
-+*/
-+
-+#include "sys/platform.h"
-+#include "idlib/hashing/CRC32.h"
-+#include "idlib/LangDict.h"
-+#include "framework/async/AsyncNetwork.h"
-+#include "framework/Console.h"
-+#include "framework/Game.h"
-+#include "framework/EventLoop.h"
-+#include "renderer/ModelManager.h"
-+
-+#include "framework/Session_local.h"
-+
-+idCVar idSessionLocal::com_showAngles( "com_showAngles", "0", CVAR_SYSTEM | CVAR_BOOL, "" );
-+idCVar idSessionLocal::com_minTics( "com_minTics", "1", CVAR_SYSTEM, "" );
-+idCVar idSessionLocal::com_showTics( "com_showTics", "0", CVAR_SYSTEM | CVAR_BOOL, "" );
-+idCVar idSessionLocal::com_fixedTic( "com_fixedTic", "0", CVAR_SYSTEM | CVAR_INTEGER, "", 0, 10 );
-+idCVar idSessionLocal::com_showDemo( "com_showDemo", "0", CVAR_SYSTEM | CVAR_BOOL, "" );
-+idCVar idSessionLocal::com_skipGameDraw( "com_skipGameDraw", "0", CVAR_SYSTEM | CVAR_BOOL, "" );
-+idCVar idSessionLocal::com_aviDemoSamples( "com_aviDemoSamples", "16", CVAR_SYSTEM, "" );
-+idCVar idSessionLocal::com_aviDemoWidth( "com_aviDemoWidth", "256", CVAR_SYSTEM, "" );
-+idCVar idSessionLocal::com_aviDemoHeight( "com_aviDemoHeight", "256", CVAR_SYSTEM, "" );
-+idCVar idSessionLocal::com_aviDemoTics( "com_aviDemoTics", "2", CVAR_SYSTEM | CVAR_INTEGER, "", 1, 60 );
-+idCVar idSessionLocal::com_wipeSeconds( "com_wipeSeconds", "1", CVAR_SYSTEM, "" );
-+idCVar idSessionLocal::com_guid( "com_guid", "", CVAR_SYSTEM | CVAR_ARCHIVE | CVAR_ROM, "" );
-+
-+idSessionLocal sessLocal;
-+idSession *session = &sessLocal;
-+
-+// these must be kept up to date with window Levelshot in guis/mainmenu.gui
-+const int PREVIEW_X = 211;
-+const int PREVIEW_Y = 31;
-+const int PREVIEW_WIDTH = 398;
-+const int PREVIEW_HEIGHT = 298;
-+
-+void RandomizeStack( void ) {
-+ // attempt to force uninitialized stack memory bugs
-+ int bytes = 4000000;
-+ byte *buf = (byte *)_alloca( bytes );
-+
-+ int fill = rand()&255;
-+ for ( int i = 0 ; i < bytes ; i++ ) {
-+ buf[i] = fill;
-+ }
-+}
-+
-+/*
-+=================
-+Session_RescanSI_f
-+=================
-+*/
-+void Session_RescanSI_f( const idCmdArgs &args ) {
-+ sessLocal.mapSpawnData.serverInfo = *cvarSystem->MoveCVarsToDict( CVAR_SERVERINFO );
-+ if ( game && idAsyncNetwork::server.IsActive() ) {
-+ game->SetServerInfo( sessLocal.mapSpawnData.serverInfo );
-+ }
-+}
-+
-+#ifndef ID_DEDICATED
-+/*
-+==================
-+Session_Map_f
-+
-+Restart the server on a different map
-+==================
-+*/
-+static void Session_Map_f( const idCmdArgs &args ) {
-+ idStr map, string;
-+ findFile_t ff;
-+ idCmdArgs rl_args;
-+
-+ map = args.Argv(1);
-+ if ( !map.Length() ) {
-+ return;
-+ }
-+ map.StripFileExtension();
-+
-+ // make sure the level exists before trying to change, so that
-+ // a typo at the server console won't end the game
-+ // handle addon packs through reloadEngine
-+ sprintf( string, "maps/%s.map", map.c_str() );
-+ ff = fileSystem->FindFile( string, true );
-+ switch ( ff ) {
-+ case FIND_NO:
-+ common->Printf( "Can't find map %s\n", string.c_str() );
-+ return;
-+ case FIND_ADDON:
-+ common->Printf( "map %s is in an addon pak - reloading\n", string.c_str() );
-+ rl_args.AppendArg( "map" );
-+ rl_args.AppendArg( map );
-+ cmdSystem->SetupReloadEngine( rl_args );
-+ return;
-+ default:
-+ break;
-+ }
-+
-+ cvarSystem->SetCVarBool( "developer", false );
-+ sessLocal.StartNewGame( map, true );
-+}
-+
-+/*
-+==================
-+Session_DevMap_f
-+
-+Restart the server on a different map in developer mode
-+==================
-+*/
-+static void Session_DevMap_f( const idCmdArgs &args ) {
-+ idStr map, string;
-+ findFile_t ff;
-+ idCmdArgs rl_args;
-+
-+ map = args.Argv(1);
-+ if ( !map.Length() ) {
-+ return;
-+ }
-+ map.StripFileExtension();
-+
-+ // make sure the level exists before trying to change, so that
-+ // a typo at the server console won't end the game
-+ // handle addon packs through reloadEngine
-+ sprintf( string, "maps/%s.map", map.c_str() );
-+ ff = fileSystem->FindFile( string, true );
-+ switch ( ff ) {
-+ case FIND_NO:
-+ common->Printf( "Can't find map %s\n", string.c_str() );
-+ return;
-+ case FIND_ADDON:
-+ common->Printf( "map %s is in an addon pak - reloading\n", string.c_str() );
-+ rl_args.AppendArg( "devmap" );
-+ rl_args.AppendArg( map );
-+ cmdSystem->SetupReloadEngine( rl_args );
-+ return;
-+ default:
-+ break;
-+ }
-+
-+ cvarSystem->SetCVarBool( "developer", true );
-+ sessLocal.StartNewGame( map, true );
-+}
-+
-+/*
-+==================
-+Session_TestMap_f
-+==================
-+*/
-+static void Session_TestMap_f( const idCmdArgs &args ) {
-+ idStr map, string;
-+
-+ map = args.Argv(1);
-+ if ( !map.Length() ) {
-+ return;
-+ }
-+ map.StripFileExtension();
-+
-+ cmdSystem->BufferCommandText( CMD_EXEC_NOW, "disconnect" );
-+
-+ sprintf( string, "dmap maps/%s.map", map.c_str() );
-+ cmdSystem->BufferCommandText( CMD_EXEC_NOW, string );
-+
-+ sprintf( string, "devmap %s", map.c_str() );
-+ cmdSystem->BufferCommandText( CMD_EXEC_NOW, string );
-+}
-+#endif
-+
-+/*
-+==================
-+Sess_WritePrecache_f
-+==================
-+*/
-+static void Sess_WritePrecache_f( const idCmdArgs &args ) {
-+ if ( args.Argc() != 2 ) {
-+ common->Printf( "USAGE: writePrecache <execFile>\n" );
-+ return;
-+ }
-+ idStr str = args.Argv(1);
-+ str.DefaultFileExtension( ".cfg" );
-+ idFile *f = fileSystem->OpenFileWrite( str, "fs_configpath" );
-+ declManager->WritePrecacheCommands( f );
-+ renderModelManager->WritePrecacheCommands( f );
-+ uiManager->WritePrecacheCommands( f );
-+
-+ fileSystem->CloseFile( f );
-+}
-+
-+/*
-+===============
-+idSessionLocal::MaybeWaitOnCDKey
-+===============
-+*/
-+bool idSessionLocal::MaybeWaitOnCDKey( void ) {
-+ if ( authEmitTimeout > 0 ) {
-+ authWaitBox = true;
-+ sessLocal.MessageBox( MSG_WAIT, common->GetLanguageDict()->GetString( "#str_07191" ), NULL, true, NULL, NULL, true );
-+ return true;
-+ }
-+ return false;
-+}
-+
-+/*
-+===================
-+Session_PromptKey_f
-+===================
-+*/
-+static void Session_PromptKey_f( const idCmdArgs &args ) {
-+ const char *retkey;
-+ bool valid[ 2 ];
-+ static bool recursed = false;
-+
-+ if ( recursed ) {
-+ common->Warning( "promptKey recursed - aborted" );
-+ return;
-+ }
-+ recursed = true;
-+
-+ do {
-+ // in case we're already waiting for an auth to come back to us ( may happen exceptionally )
-+ if ( sessLocal.MaybeWaitOnCDKey() ) {
-+ if ( sessLocal.CDKeysAreValid( true ) ) {
-+ recursed = false;
-+ return;
-+ }
-+ }
-+ // the auth server may have replied and set an error message, otherwise use a default
-+ const char *prompt_msg = sessLocal.GetAuthMsg();
-+ if ( prompt_msg[ 0 ] == '\0' ) {
-+ prompt_msg = common->GetLanguageDict()->GetString( "#str_04308" );
-+ }
-+ retkey = sessLocal.MessageBox( MSG_CDKEY, prompt_msg, common->GetLanguageDict()->GetString( "#str_04305" ), true, NULL, NULL, true );
-+ if ( retkey ) {
-+ if ( sessLocal.CheckKey( retkey, false, valid ) ) {
-+ // if all went right, then we may have sent an auth request to the master ( unless the prompt is used during a net connect )
-+ bool canExit = true;
-+ if ( sessLocal.MaybeWaitOnCDKey() ) {
-+ // wait on auth reply, and got denied, prompt again
-+ if ( !sessLocal.CDKeysAreValid( true ) ) {
-+ // server says key is invalid - MaybeWaitOnCDKey was interrupted by a CDKeysAuthReply call, which has set the right error message
-+ // the invalid keys have also been cleared in the process
-+ sessLocal.MessageBox( MSG_OK, sessLocal.GetAuthMsg(), common->GetLanguageDict()->GetString( "#str_04310" ), true, NULL, NULL, true );
-+ canExit = false;
-+ }
-+ }
-+ if ( canExit ) {
-+ // make sure that's saved on file
-+ sessLocal.WriteCDKey();
-+ sessLocal.MessageBox( MSG_OK, common->GetLanguageDict()->GetString( "#str_04307" ), common->GetLanguageDict()->GetString( "#str_04305" ), true, NULL, NULL, true );
-+ break;
-+ }
-+ } else {
-+ // offline check sees key invalid
-+ // build a message about keys being wrong. do not attempt to change the current key state though
-+ // ( the keys may be valid, but user would have clicked on the dialog anyway, that kind of thing )
-+ idStr msg;
-+ idAsyncNetwork::BuildInvalidKeyMsg( msg, valid );
-+ sessLocal.MessageBox( MSG_OK, msg, common->GetLanguageDict()->GetString( "#str_04310" ), true, NULL, NULL, true );
-+ }
-+ } else if ( args.Argc() == 2 && idStr::Icmp( args.Argv(1), "force" ) == 0 ) {
-+ // cancelled in force mode
-+ cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "quit\n" );
-+ cmdSystem->ExecuteCommandBuffer();
-+ }
-+ } while ( retkey );
-+ recursed = false;
-+}
-+
-+/*
-+===============================================================================
-+
-+SESSION LOCAL
-+
-+===============================================================================
-+*/
-+
-+/*
-+===============
-+idSessionLocal::Clear
-+===============
-+*/
-+void idSessionLocal::Clear() {
-+
-+ insideUpdateScreen = false;
-+ insideExecuteMapChange = false;
-+
-+ loadingSaveGame = false;
-+ savegameFile = NULL;
-+ savegameVersion = 0;
-+
-+ currentMapName.Clear();
-+ aviDemoShortName.Clear();
-+ msgFireBack[ 0 ].Clear();
-+ msgFireBack[ 1 ].Clear();
-+
-+ timeHitch = 0;
-+
-+ rw = NULL;
-+ sw = NULL;
-+ menuSoundWorld = NULL;
-+ readDemo = NULL;
-+ writeDemo = NULL;
-+ renderdemoVersion = 0;
-+ cmdDemoFile = NULL;
-+
-+ syncNextGameFrame = false;
-+ mapSpawned = false;
-+ guiActive = NULL;
-+ aviCaptureMode = false;
-+ timeDemo = TD_NO;
-+ waitingOnBind = false;
-+ lastPacifierTime = 0;
-+
-+ msgRunning = false;
-+ guiMsgRestore = NULL;
-+ msgIgnoreButtons = false;
-+
-+ bytesNeededForMapLoad = 0;
-+
-+#if ID_CONSOLE_LOCK
-+ emptyDrawCount = 0;
-+#endif
-+ ClearWipe();
-+
-+ loadGameList.Clear();
-+ modsList.Clear();
-+
-+ authEmitTimeout = 0;
-+ authWaitBox = false;
-+
-+ authMsg.Clear();
-+}
-+
-+/*
-+===============
-+idSessionLocal::idSessionLocal
-+===============
-+*/
-+idSessionLocal::idSessionLocal() {
-+ guiInGame = guiMainMenu = guiIntro \
-+ = guiRestartMenu = guiLoading = guiGameOver = guiActive \
-+ = guiTest = guiMsg = guiMsgRestore = guiTakeNotes = NULL;
-+
-+ menuSoundWorld = NULL;
-+
-+ Clear();
-+}
-+
-+/*
-+===============
-+idSessionLocal::~idSessionLocal
-+===============
-+*/
-+idSessionLocal::~idSessionLocal() {
-+}
-+
-+/*
-+===============
-+idSessionLocal::Stop
-+
-+called on errors and game exits
-+===============
-+*/
-+void idSessionLocal::Stop() {
-+ ClearWipe();
-+
-+ // clear mapSpawned and demo playing flags
-+ UnloadMap();
-+
-+ // disconnect async client
-+ idAsyncNetwork::client.DisconnectFromServer();
-+
-+ // kill async server
-+ idAsyncNetwork::server.Kill();
-+
-+ if ( sw ) {
-+ sw->StopAllSounds();
-+ }
-+
-+ insideUpdateScreen = false;
-+ insideExecuteMapChange = false;
-+
-+ // drop all guis
-+ SetGUI( NULL, NULL );
-+}
-+
-+/*
-+===============
-+idSessionLocal::Shutdown
-+===============
-+*/
-+void idSessionLocal::Shutdown() {
-+ int i;
-+
-+ if ( aviCaptureMode ) {
-+ EndAVICapture();
-+ }
-+
-+ if(timeDemo == TD_YES) {
-+ // else the game freezes when showing the timedemo results
-+ timeDemo = TD_YES_THEN_QUIT;
-+ }
-+
-+ Stop();
-+
-+ if ( rw ) {
-+ delete rw;
-+ rw = NULL;
-+ }
-+
-+ if ( sw ) {
-+ delete sw;
-+ sw = NULL;
-+ }
-+
-+ if ( menuSoundWorld ) {
-+ delete menuSoundWorld;
-+ menuSoundWorld = NULL;
-+ }
-+
-+ mapSpawnData.serverInfo.Clear();
-+ mapSpawnData.syncedCVars.Clear();
-+ for ( i = 0; i < MAX_ASYNC_CLIENTS; i++ ) {
-+ mapSpawnData.userInfo[i].Clear();
-+ mapSpawnData.persistentPlayerInfo[i].Clear();
-+ }
-+
-+ if ( guiMainMenu_MapList != NULL ) {
-+ guiMainMenu_MapList->Shutdown();
-+ uiManager->FreeListGUI( guiMainMenu_MapList );
-+ guiMainMenu_MapList = NULL;
-+ }
-+
-+ Clear();
-+}
-+
-+/*
-+===============
-+idSessionLocal::IsMultiplayer
-+===============
-+*/
-+bool idSessionLocal::IsMultiplayer() {
-+ return idAsyncNetwork::IsActive();
-+}
-+
-+/*
-+================
-+idSessionLocal::StartWipe
-+
-+Draws and captures the current state, then starts a wipe with that image
-+================
-+*/
-+void idSessionLocal::StartWipe( const char *_wipeMaterial, bool hold ) {
-+ console->Close();
-+
-+ // render the current screen into a texture for the wipe model
-+ renderSystem->CropRenderSize( 640, 480, true );
-+
-+ Draw();
-+
-+ renderSystem->CaptureRenderToImage( "_scratch");
-+ renderSystem->UnCrop();
-+
-+ wipeMaterial = declManager->FindMaterial( _wipeMaterial, false );
-+
-+ wipeStartTic = com_ticNumber;
-+ wipeStopTic = wipeStartTic + 1000.0f / USERCMD_MSEC * com_wipeSeconds.GetFloat();
-+ wipeHold = hold;
-+}
-+
-+/*
-+================
-+idSessionLocal::CompleteWipe
-+================
-+*/
-+void idSessionLocal::CompleteWipe() {
-+ if ( com_ticNumber == 0 ) {
-+ // if the async thread hasn't started, we would hang here
-+ wipeStopTic = 0;
-+ UpdateScreen( true );
-+ return;
-+ }
-+ while ( com_ticNumber < wipeStopTic ) {
-+#if ID_CONSOLE_LOCK
-+ emptyDrawCount = 0;
-+#endif
-+ UpdateScreen( true );
-+ }
-+}
-+
-+/*
-+================
-+idSessionLocal::ShowLoadingGui
-+================
-+*/
-+void idSessionLocal::ShowLoadingGui() {
-+ if ( com_ticNumber == 0 ) {
-+ return;
-+ }
-+ console->Close();
-+
-+ // introduced in D3XP code. don't think it actually fixes anything, but doesn't hurt either
-+#if 1
-+ // Try and prevent the while loop from being skipped over (long hitch on the main thread?)
-+ int stop = Sys_Milliseconds() + 1000;
-+ int force = 10;
-+ while ( Sys_Milliseconds() < stop || force-- > 0 ) {
-+ com_frameTime = com_ticNumber * USERCMD_MSEC;
-+ session->Frame();
-+ session->UpdateScreen( false );
-+ }
-+#else
-+ int stop = com_ticNumber + 1000.0f / USERCMD_MSEC * 1.0f;
-+ while ( com_ticNumber < stop ) {
-+ com_frameTime = com_ticNumber * USERCMD_MSEC;
-+ session->Frame();
-+ session->UpdateScreen( false );
-+ }
-+#endif
-+}
-+
-+
-+
-+/*
-+================
-+idSessionLocal::ClearWipe
-+================
-+*/
-+void idSessionLocal::ClearWipe( void ) {
-+ wipeHold = false;
-+ wipeStopTic = 0;
-+ wipeStartTic = wipeStopTic + 1;
-+}
-+
-+/*
-+================
-+Session_TestGUI_f
-+================
-+*/
-+static void Session_TestGUI_f( const idCmdArgs &args ) {
-+ sessLocal.TestGUI( args.Argv(1) );
-+}
-+
-+/*
-+================
-+idSessionLocal::TestGUI
-+================
-+*/
-+void idSessionLocal::TestGUI( const char *guiName ) {
-+ if ( guiName && *guiName ) {
-+ guiTest = uiManager->FindGui( guiName, true, false, true );
-+ } else {
-+ guiTest = NULL;
-+ }
-+}
-+
-+/*
-+================
-+FindUnusedFileName
-+================
-+*/
-+static idStr FindUnusedFileName( const char *format ) {
-+ int i;
-+ char filename[1024];
-+
-+ for ( i = 0 ; i < 999 ; i++ ) {
-+ sprintf( filename, format, i );
-+ int len = fileSystem->ReadFile( filename, NULL, NULL );
-+ if ( len <= 0 ) {
-+ return filename; // file doesn't exist
-+ }
-+ }
-+
-+ return filename;
-+}
-+
-+/*
-+================
-+Session_DemoShot_f
-+================
-+*/
-+static void Session_DemoShot_f( const idCmdArgs &args ) {
-+ if ( args.Argc() != 2 ) {
-+ idStr filename = FindUnusedFileName( "demos/shot%03i.demo" );
-+ sessLocal.DemoShot( filename );
-+ } else {
-+ sessLocal.DemoShot( va( "demos/shot_%s.demo", args.Argv(1) ) );
-+ }
-+}
-+
-+#ifndef ID_DEDICATED
-+/*
-+================
-+Session_RecordDemo_f
-+================
-+*/
-+static void Session_RecordDemo_f( const idCmdArgs &args ) {
-+ if ( args.Argc() != 2 ) {
-+ idStr filename = FindUnusedFileName( "demos/demo%03i.demo" );
-+ sessLocal.StartRecordingRenderDemo( filename );
-+ } else {
-+ sessLocal.StartRecordingRenderDemo( va( "demos/%s.demo", args.Argv(1) ) );
-+ }
-+}
-+
-+/*
-+================
-+Session_CompressDemo_f
-+================
-+*/
-+static void Session_CompressDemo_f( const idCmdArgs &args ) {
-+ if ( args.Argc() == 2 ) {
-+ sessLocal.CompressDemoFile( "2", args.Argv(1) );
-+ } else if ( args.Argc() == 3 ) {
-+ sessLocal.CompressDemoFile( args.Argv(2), args.Argv(1) );
-+ } else {
-+ common->Printf("use: CompressDemo <file> [scheme]\nscheme is the same as com_compressDemo, defaults to 2" );
-+ }
-+}
-+
-+/*
-+================
-+Session_StopRecordingDemo_f
-+================
-+*/
-+static void Session_StopRecordingDemo_f( const idCmdArgs &args ) {
-+ sessLocal.StopRecordingRenderDemo();
-+}
-+
-+/*
-+================
-+Session_PlayDemo_f
-+================
-+*/
-+static void Session_PlayDemo_f( const idCmdArgs &args ) {
-+ if ( args.Argc() >= 2 ) {
-+ sessLocal.StartPlayingRenderDemo( va( "demos/%s", args.Argv(1) ) );
-+ }
-+}
-+
-+/*
-+================
-+Session_TimeDemo_f
-+================
-+*/
-+static void Session_TimeDemo_f( const idCmdArgs &args ) {
-+ if ( args.Argc() >= 2 ) {
-+ sessLocal.TimeRenderDemo( va( "demos/%s", args.Argv(1) ), ( args.Argc() > 2 ) );
-+ }
-+}
-+
-+/*
-+================
-+Session_TimeDemoQuit_f
-+================
-+*/
-+static void Session_TimeDemoQuit_f( const idCmdArgs &args ) {
-+ sessLocal.TimeRenderDemo( va( "demos/%s", args.Argv(1) ) );
-+ if ( sessLocal.timeDemo == TD_YES ) {
-+ // this allows hardware vendors to automate some testing
-+ sessLocal.timeDemo = TD_YES_THEN_QUIT;
-+ }
-+}
-+
-+/*
-+================
-+Session_AVIDemo_f
-+================
-+*/
-+static void Session_AVIDemo_f( const idCmdArgs &args ) {
-+ sessLocal.AVIRenderDemo( va( "demos/%s", args.Argv(1) ) );
-+}
-+
-+/*
-+================
-+Session_AVIGame_f
-+================
-+*/
-+static void Session_AVIGame_f( const idCmdArgs &args ) {
-+ sessLocal.AVIGame( args.Argv(1) );
-+}
-+
-+/*
-+================
-+Session_AVICmdDemo_f
-+================
-+*/
-+static void Session_AVICmdDemo_f( const idCmdArgs &args ) {
-+ sessLocal.AVICmdDemo( args.Argv(1) );
-+}
-+
-+/*
-+================
-+Session_WriteCmdDemo_f
-+================
-+*/
-+static void Session_WriteCmdDemo_f( const idCmdArgs &args ) {
-+ if ( args.Argc() == 1 ) {
-+ idStr filename = FindUnusedFileName( "demos/cmdDemo%03i.cdemo" );
-+ sessLocal.WriteCmdDemo( filename );
-+ } else if ( args.Argc() == 2 ) {
-+ sessLocal.WriteCmdDemo( va( "demos/%s.cdemo", args.Argv( 1 ) ) );
-+ } else {
-+ common->Printf( "usage: writeCmdDemo [demoName]\n" );
-+ }
-+}
-+
-+/*
-+================
-+Session_PlayCmdDemo_f
-+================
-+*/
-+static void Session_PlayCmdDemo_f( const idCmdArgs &args ) {
-+ sessLocal.StartPlayingCmdDemo( args.Argv(1) );
-+}
-+
-+/*
-+================
-+Session_TimeCmdDemo_f
-+================
-+*/
-+static void Session_TimeCmdDemo_f( const idCmdArgs &args ) {
-+ sessLocal.TimeCmdDemo( args.Argv(1) );
-+}
-+#endif
-+
-+/*
-+================
-+Session_Disconnect_f
-+================
-+*/
-+static void Session_Disconnect_f( const idCmdArgs &args ) {
-+ sessLocal.Stop();
-+ sessLocal.StartMenu();
-+ if ( soundSystem ) {
-+ soundSystem->SetMute( false );
-+ }
-+}
-+
-+#ifndef ID_DEDICATED
-+/*
-+================
-+Session_ExitCmdDemo_f
-+================
-+*/
-+static void Session_ExitCmdDemo_f( const idCmdArgs &args ) {
-+ if ( !sessLocal.cmdDemoFile ) {
-+ common->Printf( "not reading from a cmdDemo\n" );
-+ return;
-+ }
-+ fileSystem->CloseFile( sessLocal.cmdDemoFile );
-+ common->Printf( "Command demo exited at logIndex %i\n", sessLocal.logIndex );
-+ sessLocal.cmdDemoFile = NULL;
-+}
-+#endif
-+
-+/*
-+================
-+idSessionLocal::StartRecordingRenderDemo
-+================
-+*/
-+void idSessionLocal::StartRecordingRenderDemo( const char *demoName ) {
-+ if ( writeDemo ) {
-+ // allow it to act like a toggle
-+ StopRecordingRenderDemo();
-+ return;
-+ }
-+
-+ if ( !demoName[0] ) {
-+ common->Printf( "idSessionLocal::StartRecordingRenderDemo: no name specified\n" );
-+ return;
-+ }
-+
-+ console->Close();
-+
-+ writeDemo = new idDemoFile;
-+ if ( !writeDemo->OpenForWriting( demoName ) ) {
-+ common->Printf( "error opening %s\n", demoName );
-+ delete writeDemo;
-+ writeDemo = NULL;
-+ return;
-+ }
-+
-+ common->Printf( "recording to %s\n", writeDemo->GetName() );
-+
-+ writeDemo->WriteInt( DS_VERSION );
-+ writeDemo->WriteInt( RENDERDEMO_VERSION );
-+
-+ // if we are in a map already, dump the current state
-+ sw->StartWritingDemo( writeDemo );
-+ rw->StartWritingDemo( writeDemo );
-+}
-+
-+/*
-+================
-+idSessionLocal::StopRecordingRenderDemo
-+================
-+*/
-+void idSessionLocal::StopRecordingRenderDemo() {
-+ if ( !writeDemo ) {
-+ common->Printf( "idSessionLocal::StopRecordingRenderDemo: not recording\n" );
-+ return;
-+ }
-+ sw->StopWritingDemo();
-+ rw->StopWritingDemo();
-+
-+ writeDemo->Close();
-+ common->Printf( "stopped recording %s.\n", writeDemo->GetName() );
-+ delete writeDemo;
-+ writeDemo = NULL;
-+}
-+
-+/*
-+================
-+idSessionLocal::StopPlayingRenderDemo
-+
-+Reports timeDemo numbers and finishes any avi recording
-+================
-+*/
-+void idSessionLocal::StopPlayingRenderDemo() {
-+ if ( !readDemo ) {
-+ timeDemo = TD_NO;
-+ return;
-+ }
-+
-+ // Record the stop time before doing anything that could be time consuming
-+ int timeDemoStopTime = Sys_Milliseconds();
-+
-+ EndAVICapture();
-+
-+ readDemo->Close();
-+
-+ sw->StopAllSounds();
-+ soundSystem->SetPlayingSoundWorld( menuSoundWorld );
-+
-+ common->Printf( "stopped playing %s.\n", readDemo->GetName() );
-+ delete readDemo;
-+ readDemo = NULL;
-+
-+ if ( timeDemo ) {
-+ // report the stats
-+ float demoSeconds = ( timeDemoStopTime - timeDemoStartTime ) * 0.001f;
-+ float demoFPS = numDemoFrames / demoSeconds;
-+ idStr message = va( "%i frames rendered in %3.1f seconds = %3.1f fps\n", numDemoFrames, demoSeconds, demoFPS );
-+
-+ common->Printf( message );
-+ if ( timeDemo == TD_YES_THEN_QUIT ) {
-+ cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "quit\n" );
-+ } else {
-+ soundSystem->SetMute( true );
-+ MessageBox( MSG_OK, message, "Time Demo Results", true );
-+ soundSystem->SetMute( false );
-+ }
-+ timeDemo = TD_NO;
-+ }
-+}
-+
-+/*
-+================
-+idSessionLocal::DemoShot
-+
-+A demoShot is a single frame demo
-+================
-+*/
-+void idSessionLocal::DemoShot( const char *demoName ) {
-+ StartRecordingRenderDemo( demoName );
-+
-+ // force draw one frame
-+ UpdateScreen();
-+
-+ StopRecordingRenderDemo();
-+}
-+
-+/*
-+================
-+idSessionLocal::StartPlayingRenderDemo
-+================
-+*/
-+void idSessionLocal::StartPlayingRenderDemo( idStr demoName ) {
-+ if ( !demoName[0] ) {
-+ common->Printf( "idSessionLocal::StartPlayingRenderDemo: no name specified\n" );
-+ return;
-+ }
-+
-+ // make sure localSound / GUI intro music shuts up
-+ sw->StopAllSounds();
-+ sw->PlayShaderDirectly( "", 0 );
-+ menuSoundWorld->StopAllSounds();
-+ menuSoundWorld->PlayShaderDirectly( "", 0 );
-+
-+ // exit any current game
-+ Stop();
-+
-+ // automatically put the console away
-+ console->Close();
-+
-+ // bring up the loading screen manually, since demos won't
-+ // call ExecuteMapChange()
-+ guiLoading = uiManager->FindGui( "guis/map/loading.gui", true, false, true );
-+ guiLoading->SetStateString( "demo", common->GetLanguageDict()->GetString( "#str_02087" ) );
-+ readDemo = new idDemoFile;
-+ demoName.DefaultFileExtension( ".demo" );
-+ if ( !readDemo->OpenForReading( demoName ) ) {
-+ common->Printf( "couldn't open %s\n", demoName.c_str() );
-+ delete readDemo;
-+ readDemo = NULL;
-+ Stop();
-+ StartMenu();
-+ soundSystem->SetMute( false );
-+ return;
-+ }
-+
-+ insideExecuteMapChange = true;
-+ UpdateScreen();
-+ insideExecuteMapChange = false;
-+ guiLoading->SetStateString( "demo", "" );
-+
-+ // setup default render demo settings
-+ // that's default for <= Doom3 v1.1
-+ renderdemoVersion = 1;
-+ savegameVersion = 16;
-+
-+ AdvanceRenderDemo( true );
-+
-+ numDemoFrames = 1;
-+
-+ lastDemoTic = -1;
-+ timeDemoStartTime = Sys_Milliseconds();
-+}
-+
-+/*
-+================
-+idSessionLocal::TimeRenderDemo
-+================
-+*/
-+void idSessionLocal::TimeRenderDemo( const char *demoName, bool twice ) {
-+ idStr demo = demoName;
-+
-+ // no sound in time demos
-+ soundSystem->SetMute( true );
-+
-+ StartPlayingRenderDemo( demo );
-+
-+ if ( twice && readDemo ) {
-+ // cycle through once to precache everything
-+ guiLoading->SetStateString( "demo", common->GetLanguageDict()->GetString( "#str_04852" ) );
-+ guiLoading->StateChanged( com_frameTime );
-+ while ( readDemo ) {
-+ insideExecuteMapChange = true;
-+ UpdateScreen();
-+ insideExecuteMapChange = false;
-+ AdvanceRenderDemo( true );
-+ }
-+ guiLoading->SetStateString( "demo", "" );
-+ StartPlayingRenderDemo( demo );
-+ }
-+
-+
-+ if ( !readDemo ) {
-+ return;
-+ }
-+
-+ timeDemo = TD_YES;
-+}
-+
-+
-+/*
-+================
-+idSessionLocal::BeginAVICapture
-+================
-+*/
-+void idSessionLocal::BeginAVICapture( const char *demoName ) {
-+ idStr name = demoName;
-+ name.ExtractFileBase( aviDemoShortName );
-+ aviCaptureMode = true;
-+ aviDemoFrameCount = 0;
-+ aviTicStart = 0;
-+ sw->AVIOpen( va( "demos/%s/", aviDemoShortName.c_str() ), aviDemoShortName.c_str() );
-+}
-+
-+/*
-+================
-+idSessionLocal::EndAVICapture
-+================
-+*/
-+void idSessionLocal::EndAVICapture() {
-+ if ( !aviCaptureMode ) {
-+ return;
-+ }
-+
-+ sw->AVIClose();
-+
-+ // write a .roqParam file so the demo can be converted to a roq file
-+ idFile *f = fileSystem->OpenFileWrite( va( "demos/%s/%s.roqParam",
-+ aviDemoShortName.c_str(), aviDemoShortName.c_str() ) );
-+ f->Printf( "INPUT_DIR demos/%s\n", aviDemoShortName.c_str() );
-+ f->Printf( "FILENAME demos/%s/%s.RoQ\n", aviDemoShortName.c_str(), aviDemoShortName.c_str() );
-+ f->Printf( "\nINPUT\n" );
-+ f->Printf( "%s_*.tga [00000-%05i]\n", aviDemoShortName.c_str(), (int)( aviDemoFrameCount-1 ) );
-+ f->Printf( "END_INPUT\n" );
-+ delete f;
-+
-+ common->Printf( "captured %i frames for %s.\n", ( int )aviDemoFrameCount, aviDemoShortName.c_str() );
-+
-+ aviCaptureMode = false;
-+}
-+
-+
-+/*
-+================
-+idSessionLocal::AVIRenderDemo
-+================
-+*/
-+void idSessionLocal::AVIRenderDemo( const char *_demoName ) {
-+ idStr demoName = _demoName; // copy off from va() buffer
-+
-+ StartPlayingRenderDemo( demoName );
-+ if ( !readDemo ) {
-+ return;
-+ }
-+
-+ BeginAVICapture( demoName.c_str() ) ;
-+
-+ // I don't understand why I need to do this twice, something
-+ // strange with the nvidia swapbuffers?
-+ UpdateScreen();
-+}
-+
-+/*
-+================
-+idSessionLocal::AVICmdDemo
-+================
-+*/
-+void idSessionLocal::AVICmdDemo( const char *demoName ) {
-+ StartPlayingCmdDemo( demoName );
-+
-+ BeginAVICapture( demoName ) ;
-+}
-+
-+/*
-+================
-+idSessionLocal::AVIGame
-+
-+Start AVI recording the current game session
-+================
-+*/
-+void idSessionLocal::AVIGame( const char *demoName ) {
-+ if ( aviCaptureMode ) {
-+ EndAVICapture();
-+ return;
-+ }
-+
-+ if ( !mapSpawned ) {
-+ common->Printf( "No map spawned.\n" );
-+ }
-+
-+ if ( !demoName || !demoName[0] ) {
-+ idStr filename = FindUnusedFileName( "demos/game%03i.game" );
-+ demoName = filename.c_str();
-+
-+ // write a one byte stub .game file just so the FindUnusedFileName works,
-+ fileSystem->WriteFile( demoName, demoName, 1 );
-+ }
-+
-+ BeginAVICapture( demoName ) ;
-+}
-+
-+/*
-+================
-+idSessionLocal::CompressDemoFile
-+================
-+*/
-+void idSessionLocal::CompressDemoFile( const char *scheme, const char *demoName ) {
-+ idStr fullDemoName = "demos/";
-+ fullDemoName += demoName;
-+ fullDemoName.DefaultFileExtension( ".demo" );
-+ idStr compressedName = fullDemoName;
-+ compressedName.StripFileExtension();
-+ compressedName.Append( "_compressed.demo" );
-+
-+ int savedCompression = cvarSystem->GetCVarInteger("com_compressDemos");
-+ bool savedPreload = cvarSystem->GetCVarBool("com_preloadDemos");
-+ cvarSystem->SetCVarBool( "com_preloadDemos", false );
-+ cvarSystem->SetCVarInteger("com_compressDemos", atoi(scheme) );
-+
-+ idDemoFile demoread, demowrite;
-+ if ( !demoread.OpenForReading( fullDemoName ) ) {
-+ common->Printf( "Could not open %s for reading\n", fullDemoName.c_str() );
-+ return;
-+ }
-+ if ( !demowrite.OpenForWriting( compressedName ) ) {
-+ common->Printf( "Could not open %s for writing\n", compressedName.c_str() );
-+ demoread.Close();
-+ cvarSystem->SetCVarBool( "com_preloadDemos", savedPreload );
-+ cvarSystem->SetCVarInteger("com_compressDemos", savedCompression);
-+ return;
-+ }
-+ common->SetRefreshOnPrint( true );
-+ common->Printf( "Compressing %s to %s...\n", fullDemoName.c_str(), compressedName.c_str() );
-+
-+ static const int bufferSize = 65535;
-+ char buffer[bufferSize];
-+ int bytesRead;
-+ while ( 0 != (bytesRead = demoread.Read( buffer, bufferSize ) ) ) {
-+ demowrite.Write( buffer, bytesRead );
-+ common->Printf( "." );
-+ }
-+
-+ demoread.Close();
-+ demowrite.Close();
-+
-+ cvarSystem->SetCVarBool( "com_preloadDemos", savedPreload );
-+ cvarSystem->SetCVarInteger("com_compressDemos", savedCompression);
-+
-+ common->Printf( "Done\n" );
-+ common->SetRefreshOnPrint( false );
-+
-+}
-+
-+
-+/*
-+===============
-+idSessionLocal::StartNewGame
-+===============
-+*/
-+void idSessionLocal::StartNewGame( const char *mapName, bool devmap ) {
-+#ifdef ID_DEDICATED
-+ common->Printf( "Dedicated servers cannot start singleplayer games.\n" );
-+ return;
-+#else
-+#if ID_ENFORCE_KEY
-+ // strict check. don't let a game start without a definitive answer
-+ if ( !CDKeysAreValid( true ) ) {
-+ bool prompt = true;
-+ if ( MaybeWaitOnCDKey() ) {
-+ // check again, maybe we just needed more time
-+ if ( CDKeysAreValid( true ) ) {
-+ // can continue directly
-+ prompt = false;
-+ }
-+ }
-+ if ( prompt ) {
-+ cmdSystem->BufferCommandText( CMD_EXEC_NOW, "promptKey force" );
-+ cmdSystem->ExecuteCommandBuffer();
-+ }
-+ }
-+#endif
-+ if ( idAsyncNetwork::server.IsActive() ) {
-+ common->Printf("Server running, use si_map / serverMapRestart\n");
-+ return;
-+ }
-+ if ( idAsyncNetwork::client.IsActive() ) {
-+ common->Printf("Client running, disconnect from server first\n");
-+ return;
-+ }
-+
-+ // clear the userInfo so the player starts out with the defaults
-+ mapSpawnData.userInfo[0].Clear();
-+ mapSpawnData.persistentPlayerInfo[0].Clear();
-+ mapSpawnData.userInfo[0] = *cvarSystem->MoveCVarsToDict( CVAR_USERINFO );
-+
-+ mapSpawnData.serverInfo.Clear();
-+ mapSpawnData.serverInfo = *cvarSystem->MoveCVarsToDict( CVAR_SERVERINFO );
-+ mapSpawnData.serverInfo.Set( "si_gameType", "singleplayer" );
-+
-+ // set the devmap key so any play testing items will be given at
-+ // spawn time to set approximately the right weapons and ammo
-+ if(devmap) {
-+ mapSpawnData.serverInfo.Set( "devmap", "1" );
-+ }
-+
-+ mapSpawnData.syncedCVars.Clear();
-+ mapSpawnData.syncedCVars = *cvarSystem->MoveCVarsToDict( CVAR_NETWORKSYNC );
-+
-+ MoveToNewMap( mapName );
-+#endif
-+}
-+
-+/*
-+===============
-+idSessionLocal::GetAutoSaveName
-+===============
-+*/
-+idStr idSessionLocal::GetAutoSaveName( const char *mapName ) const {
-+ const idDecl *mapDecl = declManager->FindType( DECL_MAPDEF, mapName, false );
-+ const idDeclEntityDef *mapDef = static_cast<const idDeclEntityDef *>( mapDecl );
-+ if ( mapDef ) {
-+ mapName = common->GetLanguageDict()->GetString( mapDef->dict.GetString( "name", mapName ) );
-+ }
-+ // Fixme: Localization
-+ return va( "^3AutoSave:^0 %s", mapName );
-+}
-+
-+/*
-+===============
-+idSessionLocal::MoveToNewMap
-+
-+Leaves the existing userinfo and serverinfo
-+===============
-+*/
-+void idSessionLocal::MoveToNewMap( const char *mapName ) {
-+ mapSpawnData.serverInfo.Set( "si_map", mapName );
-+
-+ ExecuteMapChange();
-+
-+ if ( !mapSpawnData.serverInfo.GetBool("devmap") ) {
-+ // Autosave at the beginning of the level
-+ SaveGame( GetAutoSaveName( mapName ), true );
-+ }
-+
-+ SetGUI( NULL, NULL );
-+}
-+
-+/*
-+==============
-+SaveCmdDemoFromFile
-+==============
-+*/
-+void idSessionLocal::SaveCmdDemoToFile( idFile *file ) {
-+
-+ mapSpawnData.serverInfo.WriteToFileHandle( file );
-+
-+ for ( int i = 0 ; i < MAX_ASYNC_CLIENTS ; i++ ) {
-+ mapSpawnData.userInfo[i].WriteToFileHandle( file );
-+ mapSpawnData.persistentPlayerInfo[i].WriteToFileHandle( file );
-+ }
-+
-+ file->Write( &mapSpawnData.mapSpawnUsercmd, sizeof( mapSpawnData.mapSpawnUsercmd ) );
-+
-+ if ( numClients < 1 ) {
-+ numClients = 1;
-+ }
-+ file->Write( loggedUsercmds, numClients * logIndex * sizeof( loggedUsercmds[0] ) );
-+}
-+
-+/*
-+==============
-+idSessionLocal::LoadCmdDemoFromFile
-+==============
-+*/
-+void idSessionLocal::LoadCmdDemoFromFile( idFile *file ) {
-+
-+ mapSpawnData.serverInfo.ReadFromFileHandle( file );
-+
-+ for ( int i = 0 ; i < MAX_ASYNC_CLIENTS ; i++ ) {
-+ mapSpawnData.userInfo[i].ReadFromFileHandle( file );
-+ mapSpawnData.persistentPlayerInfo[i].ReadFromFileHandle( file );
-+ }
-+ file->Read( &mapSpawnData.mapSpawnUsercmd, sizeof( mapSpawnData.mapSpawnUsercmd ) );
-+}
-+
-+/*
-+==============
-+idSessionLocal::WriteCmdDemo
-+
-+Dumps the accumulated commands for the current level.
-+This should still work after disconnecting from a level
-+==============
-+*/
-+void idSessionLocal::WriteCmdDemo( const char *demoName, bool save ) {
-+
-+ if ( !demoName[0] ) {
-+ common->Printf( "idSessionLocal::WriteCmdDemo: no name specified\n" );
-+ return;
-+ }
-+
-+ idStr statsName;
-+ if (save) {
-+ statsName = demoName;
-+ statsName.StripFileExtension();
-+ statsName.DefaultFileExtension(".stats");
-+ }
-+
-+ common->Printf( "writing save data to %s\n", demoName );
-+
-+ idFile *cmdDemoFile = fileSystem->OpenFileWrite( demoName );
-+ if ( !cmdDemoFile ) {
-+ common->Printf( "Couldn't open for writing %s\n", demoName );
-+ return;
-+ }
-+
-+ if ( save ) {
-+ cmdDemoFile->Write( &logIndex, sizeof( logIndex ) );
-+ }
-+
-+ SaveCmdDemoToFile( cmdDemoFile );
-+
-+ if ( save ) {
-+ idFile *statsFile = fileSystem->OpenFileWrite( statsName );
-+ if ( statsFile ) {
-+ statsFile->Write( &statIndex, sizeof( statIndex ) );
-+ statsFile->Write( loggedStats, numClients * statIndex * sizeof( loggedStats[0] ) );
-+ fileSystem->CloseFile( statsFile );
-+ }
-+ }
-+
-+ fileSystem->CloseFile( cmdDemoFile );
-+}
-+
-+/*
-+===============
-+idSessionLocal::FinishCmdLoad
-+===============
-+*/
-+void idSessionLocal::FinishCmdLoad() {
-+}
-+
-+/*
-+===============
-+idSessionLocal::StartPlayingCmdDemo
-+===============
-+*/
-+void idSessionLocal::StartPlayingCmdDemo(const char *demoName) {
-+ // exit any current game
-+ Stop();
-+
-+ idStr fullDemoName = "demos/";
-+ fullDemoName += demoName;
-+ fullDemoName.DefaultFileExtension( ".cdemo" );
-+ cmdDemoFile = fileSystem->OpenFileRead(fullDemoName);
-+
-+ if ( cmdDemoFile == NULL ) {
-+ common->Printf( "Couldn't open %s\n", fullDemoName.c_str() );
-+ return;
-+ }
-+
-+ guiLoading = uiManager->FindGui( "guis/map/loading.gui", true, false, true );
-+ //cmdDemoFile->Read(&loadGameTime, sizeof(loadGameTime));
-+
-+ LoadCmdDemoFromFile(cmdDemoFile);
-+
-+ // start the map
-+ ExecuteMapChange();
-+
-+ cmdDemoFile = fileSystem->OpenFileRead(fullDemoName);
-+
-+ // have to do this twice as the execmapchange clears the cmddemofile
-+ LoadCmdDemoFromFile(cmdDemoFile);
-+
-+ // run one frame to get the view angles correct
-+ RunGameTic();
-+}
-+
-+/*
-+===============
-+idSessionLocal::TimeCmdDemo
-+===============
-+*/
-+void idSessionLocal::TimeCmdDemo( const char *demoName ) {
-+ StartPlayingCmdDemo( demoName );
-+ ClearWipe();
-+ UpdateScreen();
-+
-+ int startTime = Sys_Milliseconds();
-+ int count = 0;
-+ int minuteStart, minuteEnd;
-+ float sec;
-+
-+ // run all the frames in sequence
-+ minuteStart = startTime;
-+
-+ while( cmdDemoFile ) {
-+ RunGameTic();
-+ count++;
-+
-+ if ( count / 3600 != ( count - 1 ) / 3600 ) {
-+ minuteEnd = Sys_Milliseconds();
-+ sec = ( minuteEnd - minuteStart ) / 1000.0;
-+ minuteStart = minuteEnd;
-+ common->Printf( "minute %i took %3.1f seconds\n", count / 3600, sec );
-+ UpdateScreen();
-+ }
-+ }
-+
-+ int endTime = Sys_Milliseconds();
-+ sec = ( endTime - startTime ) / 1000.0;
-+ common->Printf( "%i seconds of game, replayed in %5.1f seconds\n", count / 60, sec );
-+}
-+
-+/*
-+===============
-+idSessionLocal::UnloadMap
-+
-+Performs cleanup that needs to happen between maps, or when a
-+game is exited.
-+Exits with mapSpawned = false
-+===============
-+*/
-+void idSessionLocal::UnloadMap() {
-+ StopPlayingRenderDemo();
-+
-+ // end the current map in the game
-+ if ( game ) {
-+ game->MapShutdown();
-+ }
-+
-+ if ( cmdDemoFile ) {
-+ fileSystem->CloseFile( cmdDemoFile );
-+ cmdDemoFile = NULL;
-+ }
-+
-+ if ( writeDemo ) {
-+ StopRecordingRenderDemo();
-+ }
-+
-+ mapSpawned = false;
-+}
-+
-+/*
-+===============
-+idSessionLocal::LoadLoadingGui
-+===============
-+*/
-+void idSessionLocal::LoadLoadingGui( const char *mapName ) {
-+ // load / program a gui to stay up on the screen while loading
-+ idStr stripped = mapName;
-+ stripped.StripFileExtension();
-+ stripped.StripPath();
-+
-+ char guiMap[ MAX_STRING_CHARS ];
-+ strncpy( guiMap, va( "guis/map/%s.gui", stripped.c_str() ), MAX_STRING_CHARS );
-+ // give the gamecode a chance to override
-+ game->GetMapLoadingGUI( guiMap );
-+
-+ if ( uiManager->CheckGui( guiMap ) ) {
-+ guiLoading = uiManager->FindGui( guiMap, true, false, true );
-+ } else {
-+ guiLoading = uiManager->FindGui( "guis/map/loading.gui", true, false, true );
-+ }
-+ guiLoading->SetStateFloat( "map_loading", 0.0f );
-+}
-+
-+/*
-+===============
-+idSessionLocal::GetBytesNeededForMapLoad
-+===============
-+*/
-+int idSessionLocal::GetBytesNeededForMapLoad( const char *mapName ) {
-+ const idDecl *mapDecl = declManager->FindType( DECL_MAPDEF, mapName, false );
-+ const idDeclEntityDef *mapDef = static_cast<const idDeclEntityDef *>( mapDecl );
-+ if ( mapDef ) {
-+ return mapDef->dict.GetInt( va("size%d", Max( 0, com_machineSpec.GetInteger() ) ) );
-+ } else {
-+ if ( com_machineSpec.GetInteger() < 2 ) {
-+ return 200 * 1024 * 1024;
-+ } else {
-+ return 400 * 1024 * 1024;
-+ }
-+ }
-+}
-+
-+/*
-+===============
-+idSessionLocal::SetBytesNeededForMapLoad
-+===============
-+*/
-+void idSessionLocal::SetBytesNeededForMapLoad( const char *mapName, int bytesNeeded ) {
-+ idDecl *mapDecl = const_cast<idDecl *>(declManager->FindType( DECL_MAPDEF, mapName, false ));
-+ idDeclEntityDef *mapDef = static_cast<idDeclEntityDef *>( mapDecl );
-+
-+ if ( com_updateLoadSize.GetBool() && mapDef ) {
-+ // we assume that if com_updateLoadSize is true then the file is writable
-+
-+ mapDef->dict.SetInt( va("size%d", com_machineSpec.GetInteger()), bytesNeeded );
-+
-+ idStr declText = "\nmapDef ";
-+ declText += mapDef->GetName();
-+ declText += " {\n";
-+ for (int i=0; i<mapDef->dict.GetNumKeyVals(); i++) {
-+ const idKeyValue *kv = mapDef->dict.GetKeyVal( i );
-+ if ( kv && (kv->GetKey().Cmp("classname") != 0 ) ) {
-+ declText += "\t\"" + kv->GetKey() + "\"\t\t\"" + kv->GetValue() + "\"\n";
-+ }
-+ }
-+ declText += "}";
-+ mapDef->SetText( declText );
-+ mapDef->ReplaceSourceFileText();
-+ }
-+}
-+
-+/*
-+===============
-+idSessionLocal::ExecuteMapChange
-+
-+Performs the initialization of a game based on mapSpawnData, used for both single
-+player and multiplayer, but not for renderDemos, which don't
-+create a game at all.
-+Exits with mapSpawned = true
-+===============
-+*/
-+void idSessionLocal::ExecuteMapChange( bool noFadeWipe ) {
-+ int i;
-+ bool reloadingSameMap;
-+
-+ // close console and remove any prints from the notify lines
-+ console->Close();
-+
-+ if ( IsMultiplayer() ) {
-+ // make sure the mp GUI isn't up, or when players get back in the
-+ // map, mpGame's menu and the gui will be out of sync.
-+ SetGUI( NULL, NULL );
-+ }
-+
-+ // mute sound
-+ soundSystem->SetMute( true );
-+
-+ // clear all menu sounds
-+ menuSoundWorld->ClearAllSoundEmitters();
-+
-+ // unpause the game sound world
-+ // NOTE: we UnPause again later down. not sure this is needed
-+ if ( sw->IsPaused() ) {
-+ sw->UnPause();
-+ }
-+
-+ if ( !noFadeWipe ) {
-+ // capture the current screen and start a wipe
-+ StartWipe( "wipeMaterial", true );
-+
-+ // immediately complete the wipe to fade out the level transition
-+ // run the wipe to completion
-+ CompleteWipe();
-+ }
-+
-+ // extract the map name from serverinfo
-+ idStr mapString = mapSpawnData.serverInfo.GetString( "si_map" );
-+
-+ idStr fullMapName = "maps/";
-+ fullMapName += mapString;
-+ fullMapName.StripFileExtension();
-+
-+ // shut down the existing game if it is running
-+ UnloadMap();
-+
-+ // don't do the deferred caching if we are reloading the same map
-+ if ( fullMapName == currentMapName ) {
-+ reloadingSameMap = true;
-+ } else {
-+ reloadingSameMap = false;
-+ currentMapName = fullMapName;
-+ }
-+
-+ // note which media we are going to need to load
-+ if ( !reloadingSameMap ) {
-+ declManager->BeginLevelLoad();
-+ renderSystem->BeginLevelLoad();
-+ soundSystem->BeginLevelLoad();
-+ }
-+
-+ uiManager->BeginLevelLoad();
-+ uiManager->Reload( true );
-+
-+ // set the loading gui that we will wipe to
-+ LoadLoadingGui( mapString );
-+
-+ // cause prints to force screen updates as a pacifier,
-+ // and draw the loading gui instead of game draws
-+ insideExecuteMapChange = true;
-+
-+ // if this works out we will probably want all the sizes in a def file although this solution will
-+ // work for new maps etc. after the first load. we can also drop the sizes into the default.cfg
-+ fileSystem->ResetReadCount();
-+ if ( !reloadingSameMap ) {
-+ bytesNeededForMapLoad = GetBytesNeededForMapLoad( mapString.c_str() );
-+ } else {
-+ bytesNeededForMapLoad = 30 * 1024 * 1024;
-+ }
-+
-+ ClearWipe();
-+
-+ // let the loading gui spin for 1 second to animate out
-+ ShowLoadingGui();
-+
-+ // note any warning prints that happen during the load process
-+ common->ClearWarnings( mapString );
-+
-+ // release the mouse cursor
-+ // before we do this potentially long operation
-+ Sys_GrabMouseCursor( false );
-+
-+ // if net play, we get the number of clients during mapSpawnInfo processing
-+ if ( !idAsyncNetwork::IsActive() ) {
-+ numClients = 1;
-+ }
-+
-+ int start = Sys_Milliseconds();
-+
-+ common->Printf( "----- Map Initialization -----\n" );
-+ common->Printf( "Map: %s\n", mapString.c_str() );
-+
-+ // let the renderSystem load all the geometry
-+ if ( !rw->InitFromMap( fullMapName ) ) {
-+ common->Error( "couldn't load %s", fullMapName.c_str() );
-+ }
-+
-+ // for the synchronous networking we needed to roll the angles over from
-+ // level to level, but now we can just clear everything
-+ usercmdGen->InitForNewMap();
-+ memset( &mapSpawnData.mapSpawnUsercmd, 0, sizeof( mapSpawnData.mapSpawnUsercmd ) );
-+
-+ // set the user info
-+ for ( i = 0; i < numClients; i++ ) {
-+ game->SetUserInfo( i, mapSpawnData.userInfo[i], idAsyncNetwork::client.IsActive(), false );
-+ game->SetPersistentPlayerInfo( i, mapSpawnData.persistentPlayerInfo[i] );
-+ }
-+
-+ // load and spawn all other entities ( from a savegame possibly )
-+ if ( loadingSaveGame && savegameFile ) {
-+ if ( game->InitFromSaveGame( fullMapName + ".map", rw, sw, savegameFile ) == false ) {
-+ // If the loadgame failed, restart the map with the player persistent data
-+ loadingSaveGame = false;
-+ fileSystem->CloseFile( savegameFile );
-+ savegameFile = NULL;
-+
-+ game->SetServerInfo( mapSpawnData.serverInfo );
-+ game->InitFromNewMap( fullMapName + ".map", rw, sw, idAsyncNetwork::server.IsActive(), idAsyncNetwork::client.IsActive(), Sys_Milliseconds() );
-+ }
-+ } else {
-+ game->SetServerInfo( mapSpawnData.serverInfo );
-+ game->InitFromNewMap( fullMapName + ".map", rw, sw, idAsyncNetwork::server.IsActive(), idAsyncNetwork::client.IsActive(), Sys_Milliseconds() );
-+ }
-+
-+ if ( !idAsyncNetwork::IsActive() && !loadingSaveGame ) {
-+ // spawn players
-+ for ( i = 0; i < numClients; i++ ) {
-+ game->SpawnPlayer( i );
-+ }
-+ }
-+
-+ // actually purge/load the media
-+ if ( !reloadingSameMap ) {
-+ renderSystem->EndLevelLoad();
-+ soundSystem->EndLevelLoad( mapString.c_str() );
-+ declManager->EndLevelLoad();
-+ SetBytesNeededForMapLoad( mapString.c_str(), fileSystem->GetReadCount() );
-+ }
-+ uiManager->EndLevelLoad();
-+
-+ if ( !idAsyncNetwork::IsActive() && !loadingSaveGame ) {
-+ // run a few frames to allow everything to settle
-+ for ( i = 0; i < 10; i++ ) {
-+ game->RunFrame( mapSpawnData.mapSpawnUsercmd );
-+ }
-+ }
-+
-+ int msec = Sys_Milliseconds() - start;
-+ common->Printf( "%6d msec to load %s\n", msec, mapString.c_str() );
-+
-+ // let the renderSystem generate interactions now that everything is spawned
-+ rw->GenerateAllInteractions();
-+
-+ common->PrintWarnings();
-+
-+ if ( guiLoading && bytesNeededForMapLoad ) {
-+ float pct = guiLoading->State().GetFloat( "map_loading" );
-+ if ( pct < 0.0f ) {
-+ pct = 0.0f;
-+ }
-+ while ( pct < 1.0f ) {
-+ guiLoading->SetStateFloat( "map_loading", pct );
-+ guiLoading->StateChanged( com_frameTime );
-+ Sys_GenerateEvents();
-+ UpdateScreen();
-+ pct += 0.05f;
-+ }
-+ }
-+
-+ // capture the current screen and start a wipe
-+ StartWipe( "wipe2Material" );
-+
-+ usercmdGen->Clear();
-+
-+ // start saving commands for possible writeCmdDemo usage
-+ logIndex = 0;
-+ statIndex = 0;
-+ lastSaveIndex = 0;
-+
-+ // don't bother spinning over all the tics we spent loading
-+ lastGameTic = latchedTicNumber = com_ticNumber;
-+
-+ // remove any prints from the notify lines
-+ console->ClearNotifyLines();
-+
-+ // stop drawing the laoding screen
-+ insideExecuteMapChange = false;
-+
-+ Sys_SetPhysicalWorkMemory( -1, -1 );
-+
-+ // set the game sound world for playback
-+ soundSystem->SetPlayingSoundWorld( sw );
-+
-+ // when loading a save game the sound is paused
-+ if ( sw->IsPaused() ) {
-+ // unpause the game sound world
-+ sw->UnPause();
-+ }
-+
-+ // restart entity sound playback
-+ soundSystem->SetMute( false );
-+
-+ // we are valid for game draws now
-+ mapSpawned = true;
-+ Sys_ClearEvents();
-+}
-+
-+/*
-+===============
-+LoadGame_f
-+===============
-+*/
-+void LoadGame_f( const idCmdArgs &args ) {
-+ console->Close();
-+ if ( args.Argc() < 2 || idStr::Icmp(args.Argv(1), "quick" ) == 0 ) {
-+ idStr saveName = common->GetLanguageDict()->GetString( "#str_07178" );
-+ sessLocal.LoadGame( saveName );
-+ } else {
-+ sessLocal.LoadGame( args.Argv(1) );
-+ }
-+}
-+
-+/*
-+===============
-+SaveGame_f
-+===============
-+*/
-+void SaveGame_f( const idCmdArgs &args ) {
-+ if ( args.Argc() < 2 || idStr::Icmp( args.Argv(1), "quick" ) == 0 ) {
-+ idStr saveName = common->GetLanguageDict()->GetString( "#str_07178" );
-+ if ( sessLocal.SaveGame( saveName ) ) {
-+ common->Printf( "%s\n", saveName.c_str() );
-+ }
-+ } else {
-+ if ( sessLocal.SaveGame( args.Argv(1) ) ) {
-+ common->Printf( "Saved %s\n", args.Argv(1) );
-+ }
-+ }
-+}
-+
-+/*
-+===============
-+TakeViewNotes_f
-+===============
-+*/
-+void TakeViewNotes_f( const idCmdArgs &args ) {
-+ const char *p = ( args.Argc() > 1 ) ? args.Argv( 1 ) : "";
-+ sessLocal.TakeNotes( p );
-+}
-+
-+/*
-+===============
-+TakeViewNotes2_f
-+===============
-+*/
-+void TakeViewNotes2_f( const idCmdArgs &args ) {
-+ const char *p = ( args.Argc() > 1 ) ? args.Argv( 1 ) : "";
-+ sessLocal.TakeNotes( p, true );
-+}
-+
-+/*
-+===============
-+idSessionLocal::TakeNotes
-+===============
-+*/
-+void idSessionLocal::TakeNotes( const char *p, bool extended ) {
-+ if ( !mapSpawned ) {
-+ common->Printf( "No map loaded!\n" );
-+ return;
-+ }
-+
-+ if ( extended ) {
-+ guiTakeNotes = uiManager->FindGui( "guis/takeNotes2.gui", true, false, true );
-+
-+#if 0
-+ const char *people[] = {
-+ "Nobody", "Adam", "Brandon", "David", "PHook", "Jay", "Jake",
-+ "PatJ", "Brett", "Ted", "Darin", "Brian", "Sean"
-+ };
-+#else
-+ const char *people[] = {
-+ "Tim", "Kenneth", "Robert",
-+ "Matt", "Mal", "Jerry", "Steve", "Pat",
-+ "Xian", "Ed", "Fred", "James", "Eric", "Andy", "Seneca", "Patrick", "Kevin",
-+ "MrElusive", "Jim", "Brian", "John", "Adrian", "Nobody"
-+ };
-+#endif
-+ const int numPeople = sizeof( people ) / sizeof( people[0] );
-+
-+ idListGUI * guiList_people = uiManager->AllocListGUI();
-+ guiList_people->Config( guiTakeNotes, "person" );
-+ for ( int i = 0; i < numPeople; i++ ) {
-+ guiList_people->Push( people[i] );
-+ }
-+ uiManager->FreeListGUI( guiList_people );
-+
-+ } else {
-+ guiTakeNotes = uiManager->FindGui( "guis/takeNotes.gui", true, false, true );
-+ }
-+
-+ SetGUI( guiTakeNotes, NULL );
-+ guiActive->SetStateString( "note", "" );
-+ guiActive->SetStateString( "notefile", p );
-+ guiActive->SetStateBool( "extended", extended );
-+ guiActive->Activate( true, com_frameTime );
-+}
-+
-+/*
-+===============
-+Session_Hitch_f
-+===============
-+*/
-+void Session_Hitch_f( const idCmdArgs &args ) {
-+ idSoundWorld *sw = soundSystem->GetPlayingSoundWorld();
-+ if ( sw ) {
-+ soundSystem->SetMute(true);
-+ sw->Pause();
-+ Sys_EnterCriticalSection();
-+ }
-+ if ( args.Argc() == 2 ) {
-+ Sys_Sleep( atoi(args.Argv(1)) );
-+ } else {
-+ Sys_Sleep( 100 );
-+ }
-+ if ( sw ) {
-+ Sys_LeaveCriticalSection();
-+ sw->UnPause();
-+ soundSystem->SetMute(false);
-+ }
-+}
-+
-+/*
-+===============
-+idSessionLocal::ScrubSaveGameFileName
-+
-+Turns a bad file name into a good one or your money back
-+===============
-+*/
-+void idSessionLocal::ScrubSaveGameFileName( idStr &saveFileName ) const {
-+ int i;
-+ idStr inFileName;
-+
-+ inFileName = saveFileName;
-+ inFileName.RemoveColors();
-+ inFileName.StripFileExtension();
-+
-+ saveFileName.Clear();
-+
-+ int len = inFileName.Length();
-+ for ( i = 0; i < len; i++ ) {
-+ if ( strchr( "',.~!@#$%^&*()[]{}<>\\|/=?+;:-\'\"", inFileName[i] ) ) {
-+ // random junk
-+ saveFileName += '_';
-+ } else if ( (const unsigned char)inFileName[i] >= 128 ) {
-+ // high ascii chars
-+ saveFileName += '_';
-+ } else if ( inFileName[i] == ' ' ) {
-+ saveFileName += '_';
-+ } else {
-+ saveFileName += inFileName[i];
-+ }
-+ }
-+}
-+
-+/*
-+===============
-+idSessionLocal::SaveGame
-+===============
-+*/
-+bool idSessionLocal::SaveGame( const char *saveName, bool autosave ) {
-+#ifdef ID_DEDICATED
-+ common->Printf( "Dedicated servers cannot save games.\n" );
-+ return false;
-+#else
-+ int i;
-+ idStr gameFile, previewFile, descriptionFile, mapName;
-+
-+ if ( !mapSpawned ) {
-+ common->Printf( "Not playing a game.\n" );
-+ return false;
-+ }
-+
-+ if ( IsMultiplayer() ) {
-+ common->Printf( "Can't save during net play.\n" );
-+ return false;
-+ }
-+
-+ if ( game->GetPersistentPlayerInfo( 0 ).GetInt( "health" ) <= 0 ) {
-+ MessageBox( MSG_OK, common->GetLanguageDict()->GetString ( "#str_04311" ), common->GetLanguageDict()->GetString ( "#str_04312" ), true );
-+ common->Printf( "You must be alive to save the game\n" );
-+ return false;
-+ }
-+
-+ if ( Sys_GetDriveFreeSpace( cvarSystem->GetCVarString( "fs_savepath" ) ) < 25 ) {
-+ MessageBox( MSG_OK, common->GetLanguageDict()->GetString ( "#str_04313" ), common->GetLanguageDict()->GetString ( "#str_04314" ), true );
-+ common->Printf( "Not enough drive space to save the game\n" );
-+ return false;
-+ }
-+
-+ idSoundWorld *pauseWorld = soundSystem->GetPlayingSoundWorld();
-+ if ( pauseWorld ) {
-+ pauseWorld->Pause();
-+ soundSystem->SetPlayingSoundWorld( NULL );
-+ }
-+
-+ // setup up filenames and paths
-+ gameFile = saveName;
-+ ScrubSaveGameFileName( gameFile );
-+
-+ gameFile = "savegames/" + gameFile;
-+ gameFile.SetFileExtension( ".save" );
-+
-+ previewFile = gameFile;
-+ previewFile.SetFileExtension( ".tga" );
-+
-+ descriptionFile = gameFile;
-+ descriptionFile.SetFileExtension( ".txt" );
-+
-+ // Open savegame file
-+ idFile *fileOut = fileSystem->OpenFileWrite( gameFile );
-+ if ( fileOut == NULL ) {
-+ common->Warning( "Failed to open save file '%s'\n", gameFile.c_str() );
-+ if ( pauseWorld ) {
-+ soundSystem->SetPlayingSoundWorld( pauseWorld );
-+ pauseWorld->UnPause();
-+ }
-+ return false;
-+ }
-+
-+ // Write SaveGame Header:
-+ // Game Name / Version / Map Name / Persistant Player Info
-+
-+ // game
-+ const char *gamename = GAME_NAME;
-+ fileOut->WriteString( gamename );
-+
-+ // version
-+ fileOut->WriteInt( SAVEGAME_VERSION );
-+
-+ // map
-+ mapName = mapSpawnData.serverInfo.GetString( "si_map" );
-+ fileOut->WriteString( mapName );
-+
-+ // persistent player info
-+ for ( i = 0; i < MAX_ASYNC_CLIENTS; i++ ) {
-+ mapSpawnData.persistentPlayerInfo[i] = game->GetPersistentPlayerInfo( i );
-+ mapSpawnData.persistentPlayerInfo[i].WriteToFileHandle( fileOut );
-+ }
-+
-+ // let the game save its state
-+ game->SaveGame( fileOut );
-+
-+ // close the sava game file
-+ fileSystem->CloseFile( fileOut );
-+
-+ // Write screenshot
-+ if ( !autosave ) {
-+ renderSystem->CropRenderSize( 320, 240, false );
-+ game->Draw( 0 );
-+ renderSystem->CaptureRenderToFile( previewFile, true );
-+ renderSystem->UnCrop();
-+ }
-+
-+ // Write description, which is just a text file with
-+ // the unclean save name on line 1, map name on line 2, screenshot on line 3
-+ idFile *fileDesc = fileSystem->OpenFileWrite( descriptionFile );
-+ if ( fileDesc == NULL ) {
-+ common->Warning( "Failed to open description file '%s'\n", descriptionFile.c_str() );
-+ if ( pauseWorld ) {
-+ soundSystem->SetPlayingSoundWorld( pauseWorld );
-+ pauseWorld->UnPause();
-+ }
-+ return false;
-+ }
-+
-+ idStr description = saveName;
-+ description.Replace( "\\", "\\\\" );
-+ description.Replace( "\"", "\\\"" );
-+
-+ const idDeclEntityDef *mapDef = static_cast<const idDeclEntityDef *>(declManager->FindType( DECL_MAPDEF, mapName, false ));
-+ if ( mapDef ) {
-+ mapName = common->GetLanguageDict()->GetString( mapDef->dict.GetString( "name", mapName ) );
-+ }
-+
-+ fileDesc->Printf( "\"%s\"\n", description.c_str() );
-+ fileDesc->Printf( "\"%s\"\n", mapName.c_str());
-+
-+ if ( autosave ) {
-+ idStr sshot = mapSpawnData.serverInfo.GetString( "si_map" );
-+ sshot.StripPath();
-+ sshot.StripFileExtension();
-+ fileDesc->Printf( "\"guis/assets/autosave/%s\"\n", sshot.c_str() );
-+ } else {
-+ fileDesc->Printf( "\"\"\n" );
-+ }
-+
-+ fileSystem->CloseFile( fileDesc );
-+
-+ if ( pauseWorld ) {
-+ soundSystem->SetPlayingSoundWorld( pauseWorld );
-+ pauseWorld->UnPause();
-+ }
-+
-+ syncNextGameFrame = true;
-+
-+
-+ return true;
-+#endif
-+}
-+
-+/*
-+===============
-+idSessionLocal::LoadGame
-+===============
-+*/
-+bool idSessionLocal::LoadGame( const char *saveName ) {
-+#ifdef ID_DEDICATED
-+ common->Printf( "Dedicated servers cannot load games.\n" );
-+ return false;
-+#else
-+ int i;
-+ idStr in, loadFile, saveMap, gamename;
-+
-+ if ( IsMultiplayer() ) {
-+ common->Printf( "Can't load during net play.\n" );
-+ return false;
-+ }
-+
-+ //Hide the dialog box if it is up.
-+ StopBox();
-+
-+ loadFile = saveName;
-+ ScrubSaveGameFileName( loadFile );
-+ loadFile.SetFileExtension( ".save" );
-+
-+ in = "savegames/";
-+ in += loadFile;
-+
-+ // Open savegame file
-+ // only allow loads from the game directory because we don't want a base game to load
-+ idStr game = cvarSystem->GetCVarString( "fs_game" );
-+ savegameFile = fileSystem->OpenFileRead( in, true, game.Length() ? game : NULL );
-+
-+ if ( savegameFile == NULL ) {
-+ common->Warning( "Couldn't open savegame file %s", in.c_str() );
-+ return false;
-+ }
-+
-+ loadingSaveGame = true;
-+
-+ // Read in save game header
-+ // Game Name / Version / Map Name / Persistant Player Info
-+
-+ // game
-+ savegameFile->ReadString( gamename );
-+
-+ // if this isn't a savegame for the correct game, abort loadgame
-+ if ( gamename != GAME_NAME ) {
-+ common->Warning( "Attempted to load an invalid savegame: %s", in.c_str() );
-+
-+ loadingSaveGame = false;
-+ fileSystem->CloseFile( savegameFile );
-+ savegameFile = NULL;
-+ return false;
-+ }
-+
-+ // version
-+ savegameFile->ReadInt( savegameVersion );
-+
-+ // map
-+ savegameFile->ReadString( saveMap );
-+
-+ // persistent player info
-+ for ( i = 0; i < MAX_ASYNC_CLIENTS; i++ ) {
-+ mapSpawnData.persistentPlayerInfo[i].ReadFromFileHandle( savegameFile );
-+ }
-+
-+ // check the version, if it doesn't match, cancel the loadgame,
-+ // but still load the map with the persistant playerInfo from the header
-+ // so that the player doesn't lose too much progress.
-+ if ( savegameVersion != SAVEGAME_VERSION &&
-+ !( savegameVersion == 16 && SAVEGAME_VERSION == 17 ) ) { // handle savegame v16 in v17
-+ common->Warning( "Savegame Version mismatch: aborting loadgame and starting level with persistent data" );
-+ loadingSaveGame = false;
-+ fileSystem->CloseFile( savegameFile );
-+ savegameFile = NULL;
-+ }
-+
-+ common->DPrintf( "loading a v%d savegame\n", savegameVersion );
-+
-+ if ( saveMap.Length() > 0 ) {
-+
-+ // Start loading map
-+ mapSpawnData.serverInfo.Clear();
-+
-+ mapSpawnData.serverInfo = *cvarSystem->MoveCVarsToDict( CVAR_SERVERINFO );
-+ mapSpawnData.serverInfo.Set( "si_gameType", "singleplayer" );
-+
-+ mapSpawnData.serverInfo.Set( "si_map", saveMap );
-+
-+ mapSpawnData.syncedCVars.Clear();
-+ mapSpawnData.syncedCVars = *cvarSystem->MoveCVarsToDict( CVAR_NETWORKSYNC );
-+
-+ mapSpawnData.mapSpawnUsercmd[0] = usercmdGen->TicCmd( latchedTicNumber );
-+ // make sure no buttons are pressed
-+ mapSpawnData.mapSpawnUsercmd[0].buttons = 0;
-+
-+ ExecuteMapChange();
-+
-+ SetGUI( NULL, NULL );
-+ }
-+
-+ if ( loadingSaveGame ) {
-+ fileSystem->CloseFile( savegameFile );
-+ loadingSaveGame = false;
-+ savegameFile = NULL;
-+ }
-+
-+ return true;
-+#endif
-+}
-+
-+/*
-+===============
-+idSessionLocal::ProcessEvent
-+===============
-+*/
-+bool idSessionLocal::ProcessEvent( const sysEvent_t *event ) {
-+ // hitting escape anywhere brings up the menu
-+ if ( !guiActive && event->evType == SE_KEY && event->evValue2 == 1 && event->evValue == K_ESCAPE ) {
-+ console->Close();
-+ if ( game ) {
-+ idUserInterface *gui = NULL;
-+ escReply_t op;
-+ op = game->HandleESC( &gui );
-+ if ( op == ESC_IGNORE ) {
-+ return true;
-+ } else if ( op == ESC_GUI ) {
-+ SetGUI( gui, NULL );
-+ return true;
-+ }
-+ }
-+ StartMenu();
-+ return true;
-+ }
-+
-+ // let the pull-down console take it if desired
-+ if ( console->ProcessEvent( event, false ) ) {
-+ return true;
-+ }
-+
-+ // if we are testing a GUI, send all events to it
-+ if ( guiTest ) {
-+ // hitting escape exits the testgui
-+ if ( event->evType == SE_KEY && event->evValue2 == 1 && event->evValue == K_ESCAPE ) {
-+ guiTest = NULL;
-+ return true;
-+ }
-+
-+ static const char *cmd;
-+ cmd = guiTest->HandleEvent( event, com_frameTime );
-+ if ( cmd && cmd[0] ) {
-+ common->Printf( "testGui event returned: '%s'\n", cmd );
-+ }
-+ return true;
-+ }
-+
-+ // menus / etc
-+ if ( guiActive ) {
-+ MenuEvent( event );
-+ return true;
-+ }
-+
-+ // if we aren't in a game, force the console to take it
-+ if ( !mapSpawned ) {
-+ console->ProcessEvent( event, true );
-+ return true;
-+ }
-+
-+ // in game, exec bindings for all key downs
-+ if ( event->evType == SE_KEY && event->evValue2 == 1 ) {
-+ idKeyInput::ExecKeyBinding( event->evValue );
-+ return true;
-+ }
-+
-+ return false;
-+}
-+
-+/*
-+===============
-+idSessionLocal::DrawWipeModel
-+
-+Draw the fade material over everything that has been drawn
-+===============
-+*/
-+void idSessionLocal::DrawWipeModel() {
-+ int latchedTic = com_ticNumber;
-+
-+ if ( wipeStartTic >= wipeStopTic ) {
-+ return;
-+ }
-+
-+ if ( !wipeHold && latchedTic >= wipeStopTic ) {
-+ return;
-+ }
-+
-+ float fade = ( float )( latchedTic - wipeStartTic ) / ( wipeStopTic - wipeStartTic );
-+ renderSystem->SetColor4( 1, 1, 1, fade );
-+ renderSystem->DrawStretchPic( 0, 0, 640, 480, 0, 0, 1, 1, wipeMaterial );
-+}
-+
-+/*
-+===============
-+idSessionLocal::AdvanceRenderDemo
-+===============
-+*/
-+void idSessionLocal::AdvanceRenderDemo( bool singleFrameOnly ) {
-+ if ( lastDemoTic == -1 ) {
-+ lastDemoTic = latchedTicNumber - 1;
-+ }
-+
-+ int skipFrames = 0;
-+
-+ if ( !aviCaptureMode && !timeDemo && !singleFrameOnly ) {
-+ skipFrames = ( (latchedTicNumber - lastDemoTic) / USERCMD_PER_DEMO_FRAME ) - 1;
-+ // never skip too many frames, just let it go into slightly slow motion
-+ if ( skipFrames > 4 ) {
-+ skipFrames = 4;
-+ }
-+ lastDemoTic = latchedTicNumber - latchedTicNumber % USERCMD_PER_DEMO_FRAME;
-+ } else {
-+ // always advance a single frame with avidemo and timedemo
-+ lastDemoTic = latchedTicNumber;
-+ }
-+
-+ while( skipFrames > -1 ) {
-+ int ds = DS_FINISHED;
-+
-+ readDemo->ReadInt( ds );
-+ if ( ds == DS_FINISHED ) {
-+ if ( numDemoFrames != 1 ) {
-+ // if the demo has a single frame (a demoShot), continuously replay
-+ // the renderView that has already been read
-+ Stop();
-+ StartMenu();
-+ }
-+ break;
-+ }
-+ if ( ds == DS_RENDER ) {
-+ if ( rw->ProcessDemoCommand( readDemo, ¤tDemoRenderView, &demoTimeOffset ) ) {
-+ // a view is ready to render
-+ skipFrames--;
-+ numDemoFrames++;
-+ }
-+ continue;
-+ }
-+ if ( ds == DS_SOUND ) {
-+ sw->ProcessDemoCommand( readDemo );
-+ continue;
-+ }
-+ // appears in v1.2, with savegame format 17
-+ if ( ds == DS_VERSION ) {
-+ readDemo->ReadInt( renderdemoVersion );
-+ common->Printf( "reading a v%d render demo\n", renderdemoVersion );
-+ // set the savegameVersion to current for render demo paths that share the savegame paths
-+ savegameVersion = SAVEGAME_VERSION;
-+ continue;
-+ }
-+ common->Error( "Bad render demo token" );
-+ }
-+
-+ if ( com_showDemo.GetBool() ) {
-+ common->Printf( "frame:%i DemoTic:%i latched:%i skip:%i\n", numDemoFrames, lastDemoTic, latchedTicNumber, skipFrames );
-+ }
-+
-+}
-+
-+/*
-+===============
-+idSessionLocal::DrawCmdGraph
-+
-+Graphs yaw angle for testing smoothness
-+===============
-+*/
-+static const int ANGLE_GRAPH_HEIGHT = 128;
-+static const int ANGLE_GRAPH_STRETCH = 3;
-+void idSessionLocal::DrawCmdGraph() {
-+ if ( !com_showAngles.GetBool() ) {
-+ return;
-+ }
-+ renderSystem->SetColor4( 0.1f, 0.1f, 0.1f, 1.0f );
-+ renderSystem->DrawStretchPic( 0, 480-ANGLE_GRAPH_HEIGHT, MAX_BUFFERED_USERCMD*ANGLE_GRAPH_STRETCH, ANGLE_GRAPH_HEIGHT, 0, 0, 1, 1, whiteMaterial );
-+ renderSystem->SetColor4( 0.9f, 0.9f, 0.9f, 1.0f );
-+ for ( int i = 0 ; i < MAX_BUFFERED_USERCMD-4 ; i++ ) {
-+ usercmd_t cmd = usercmdGen->TicCmd( latchedTicNumber - (MAX_BUFFERED_USERCMD-4) + i );
-+ int h = cmd.angles[1];
-+ h >>= 8;
-+ h &= (ANGLE_GRAPH_HEIGHT-1);
-+ renderSystem->DrawStretchPic( i* ANGLE_GRAPH_STRETCH, 480-h, 1, h, 0, 0, 1, 1, whiteMaterial );
-+ }
-+}
-+
-+/*
-+===============
-+idSessionLocal::PacifierUpdate
-+===============
-+*/
-+void idSessionLocal::PacifierUpdate() {
-+ if ( !insideExecuteMapChange ) {
-+ return;
-+ }
-+
-+ // never do pacifier screen updates while inside the
-+ // drawing code, or we can have various recursive problems
-+ if ( insideUpdateScreen ) {
-+ return;
-+ }
-+
-+ int time = eventLoop->Milliseconds();
-+
-+ if ( time - lastPacifierTime < 100 ) {
-+ return;
-+ }
-+ lastPacifierTime = time;
-+
-+ if ( guiLoading && bytesNeededForMapLoad ) {
-+ float n = fileSystem->GetReadCount();
-+ float pct = ( n / bytesNeededForMapLoad );
-+ // pct = idMath::ClampFloat( 0.0f, 100.0f, pct );
-+ guiLoading->SetStateFloat( "map_loading", pct );
-+ guiLoading->StateChanged( com_frameTime );
-+ }
-+
-+ Sys_GenerateEvents();
-+
-+ UpdateScreen();
-+
-+ idAsyncNetwork::client.PacifierUpdate();
-+ idAsyncNetwork::server.PacifierUpdate();
-+}
-+
-+/*
-+===============
-+idSessionLocal::Draw
-+===============
-+*/
-+void idSessionLocal::Draw() {
-+ bool fullConsole = false;
-+
-+ if ( insideExecuteMapChange ) {
-+ if ( guiLoading ) {
-+ guiLoading->Redraw( com_frameTime );
-+ }
-+ if ( guiActive == guiMsg ) {
-+ guiMsg->Redraw( com_frameTime );
-+ }
-+ } else if ( guiTest ) {
-+ // if testing a gui, clear the screen and draw it
-+ // clear the background, in case the tested gui is transparent
-+ // NOTE that you can't use this for aviGame recording, it will tick at real com_frameTime between screenshots..
-+ renderSystem->SetColor( colorBlack );
-+ renderSystem->DrawStretchPic( 0, 0, 640, 480, 0, 0, 1, 1, declManager->FindMaterial( "_white" ) );
-+ guiTest->Redraw( com_frameTime );
-+ } else if ( guiActive && !guiActive->State().GetBool( "gameDraw" ) ) {
-+
-+ // draw the frozen gui in the background
-+ if ( guiActive == guiMsg && guiMsgRestore ) {
-+ guiMsgRestore->Redraw( com_frameTime );
-+ }
-+
-+ // draw the menus full screen
-+ if ( guiActive == guiTakeNotes && !com_skipGameDraw.GetBool() ) {
-+ game->Draw( GetLocalClientNum() );
-+ }
-+
-+ guiActive->Redraw( com_frameTime );
-+ } else if ( readDemo ) {
-+ rw->RenderScene( ¤tDemoRenderView );
-+ renderSystem->DrawDemoPics();
-+ } else if ( mapSpawned ) {
-+ bool gameDraw = false;
-+ // normal drawing for both single and multi player
-+ if ( !com_skipGameDraw.GetBool() && GetLocalClientNum() >= 0 ) {
-+ // draw the game view
-+ int start = Sys_Milliseconds();
-+ gameDraw = game->Draw( GetLocalClientNum() );
-+ int end = Sys_Milliseconds();
-+ time_gameDraw += ( end - start ); // note time used for com_speeds
-+ }
-+ if ( !gameDraw ) {
-+ renderSystem->SetColor( colorBlack );
-+ renderSystem->DrawStretchPic( 0, 0, 640, 480, 0, 0, 1, 1, declManager->FindMaterial( "_white" ) );
-+ }
-+
-+ // save off the 2D drawing from the game
-+ if ( writeDemo ) {
-+ renderSystem->WriteDemoPics();
-+ }
-+ } else {
-+#if ID_CONSOLE_LOCK
-+ if ( com_allowConsole.GetBool() ) {
-+ console->Draw( true );
-+ } else {
-+ emptyDrawCount++;
-+ if ( emptyDrawCount > 5 ) {
-+ // it's best if you can avoid triggering the watchgod by doing the right thing somewhere else
-+ assert( false );
-+ common->Warning( "idSession: triggering mainmenu watchdog" );
-+ emptyDrawCount = 0;
-+ StartMenu();
-+ }
-+ renderSystem->SetColor4( 0, 0, 0, 1 );
-+ renderSystem->DrawStretchPic( 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 0, 0, 1, 1, declManager->FindMaterial( "_white" ) );
-+ }
-+#else
-+ // draw the console full screen - this should only ever happen in developer builds
-+ console->Draw( true );
-+#endif
-+ fullConsole = true;
-+ }
-+
-+#if ID_CONSOLE_LOCK
-+ if ( !fullConsole && emptyDrawCount ) {
-+ common->DPrintf( "idSession: %d empty frame draws\n", emptyDrawCount );
-+ emptyDrawCount = 0;
-+ }
-+ fullConsole = false;
-+#endif
-+
-+ // draw the wipe material on top of this if it hasn't completed yet
-+ DrawWipeModel();
-+
-+ // draw debug graphs
-+ DrawCmdGraph();
-+
-+ // draw the half console / notify console on top of everything
-+ if ( !fullConsole ) {
-+ console->Draw( false );
-+ }
-+}
-+
-+/*
-+===============
-+idSessionLocal::UpdateScreen
-+===============
-+*/
-+void idSessionLocal::UpdateScreen( bool outOfSequence ) {
-+
-+#ifdef _WIN32
-+
-+ if ( com_editors ) {
-+ if ( !Sys_IsWindowVisible() ) {
-+ return;
-+ }
-+ }
-+#endif
-+
-+ if ( insideUpdateScreen ) {
-+ return;
-+// common->FatalError( "idSessionLocal::UpdateScreen: recursively called" );
-+ }
-+
-+ insideUpdateScreen = true;
-+
-+ // if this is a long-operation update and we are in windowed mode,
-+ // release the mouse capture back to the desktop
-+ if ( outOfSequence ) {
-+ Sys_GrabMouseCursor( false );
-+ }
-+
-+ renderSystem->BeginFrame( renderSystem->GetScreenWidth(), renderSystem->GetScreenHeight() );
-+
-+ // draw everything
-+ Draw();
-+
-+ if ( com_speeds.GetBool() ) {
-+ renderSystem->EndFrame( &time_frontend, &time_backend );
-+ } else {
-+ renderSystem->EndFrame( NULL, NULL );
-+ }
-+
-+ insideUpdateScreen = false;
-+}
-+
-+/*
-+===============
-+idSessionLocal::Frame
-+===============
-+*/
-+void idSessionLocal::Frame() {
-+
-+ if ( com_asyncSound.GetInteger() == 0 ) {
-+ soundSystem->AsyncUpdate( Sys_Milliseconds() );
-+ }
-+
-+ // Editors that completely take over the game
-+ if ( com_editorActive && ( com_editors & ( EDITOR_RADIANT | EDITOR_GUI ) ) ) {
-+ return;
-+ }
-+
-+ // if the console is down, we don't need to hold
-+ // the mouse cursor
-+ if ( console->Active() || com_editorActive ) {
-+ Sys_GrabMouseCursor( false );
-+ } else {
-+ Sys_GrabMouseCursor( true );
-+ }
-+
-+ // save the screenshot and audio from the last draw if needed
-+ if ( aviCaptureMode ) {
-+ idStr name;
-+
-+ name = va("demos/%s/%s_%05i.tga", aviDemoShortName.c_str(), aviDemoShortName.c_str(), aviTicStart );
-+
-+ float ratio = 30.0f / ( 1000.0f / USERCMD_MSEC / com_aviDemoTics.GetInteger() );
-+ aviDemoFrameCount += ratio;
-+ if ( aviTicStart + 1 != ( int )aviDemoFrameCount ) {
-+ // skipped frames so write them out
-+ int c = aviDemoFrameCount - aviTicStart;
-+ while ( c-- ) {
-+ renderSystem->TakeScreenshot( com_aviDemoWidth.GetInteger(), com_aviDemoHeight.GetInteger(), name, com_aviDemoSamples.GetInteger(), NULL );
-+ name = va("demos/%s/%s_%05i.tga", aviDemoShortName.c_str(), aviDemoShortName.c_str(), ++aviTicStart );
-+ }
-+ }
-+ aviTicStart = aviDemoFrameCount;
-+
-+ // remove any printed lines at the top before taking the screenshot
-+ console->ClearNotifyLines();
-+
-+ // this will call Draw, possibly multiple times if com_aviDemoSamples is > 1
-+ renderSystem->TakeScreenshot( com_aviDemoWidth.GetInteger(), com_aviDemoHeight.GetInteger(), name, com_aviDemoSamples.GetInteger(), NULL );
-+ }
-+
-+ // at startup, we may be backwards
-+ if ( latchedTicNumber > com_ticNumber ) {
-+ latchedTicNumber = com_ticNumber;
-+ }
-+
-+ // se how many tics we should have before continuing
-+ int minTic = latchedTicNumber + 1;
-+ if ( com_minTics.GetInteger() > 1 ) {
-+ minTic = lastGameTic + com_minTics.GetInteger();
-+ }
-+
-+ if ( readDemo ) {
-+ if ( !timeDemo && numDemoFrames != 1 ) {
-+ minTic = lastDemoTic + USERCMD_PER_DEMO_FRAME;
-+ } else {
-+ // timedemos and demoshots will run as fast as they can, other demos
-+ // will not run more than 30 hz
-+ minTic = latchedTicNumber;
-+ }
-+ } else if ( writeDemo ) {
-+ minTic = lastGameTic + USERCMD_PER_DEMO_FRAME; // demos are recorded at 30 hz
-+ }
-+
-+ // fixedTic lets us run a forced number of usercmd each frame without timing
-+ if ( com_fixedTic.GetInteger() ) {
-+ minTic = latchedTicNumber;
-+ }
-+
-+ while( 1 ) {
-+ latchedTicNumber = com_ticNumber;
-+ if ( latchedTicNumber >= minTic ) {
-+ break;
-+ }
-+ Sys_WaitForEvent( TRIGGER_EVENT_ONE );
-+ }
-+
-+ if ( authEmitTimeout ) {
-+ // waiting for a game auth
-+ if ( Sys_Milliseconds() > authEmitTimeout ) {
-+ // expired with no reply
-+ // means that if a firewall is blocking the master, we will let through
-+ common->DPrintf( "no reply from auth\n" );
-+ if ( authWaitBox ) {
-+ // close the wait box
-+ StopBox();
-+ authWaitBox = false;
-+ }
-+ if ( cdkey_state == CDKEY_CHECKING ) {
-+ cdkey_state = CDKEY_OK;
-+ }
-+ if ( xpkey_state == CDKEY_CHECKING ) {
-+ xpkey_state = CDKEY_OK;
-+ }
-+ // maintain this empty as it's set by auth denials
-+ authMsg.Empty();
-+ authEmitTimeout = 0;
-+ SetCDKeyGuiVars();
-+ }
-+ }
-+
-+ // send frame and mouse events to active guis
-+ GuiFrameEvents();
-+
-+ // advance demos
-+ if ( readDemo ) {
-+ AdvanceRenderDemo( false );
-+ return;
-+ }
-+
-+ //------------ single player game tics --------------
-+
-+ if ( !mapSpawned || guiActive ) {
-+ if ( !com_asyncInput.GetBool() ) {
-+ // early exit, won't do RunGameTic .. but still need to update mouse position for GUIs
-+ usercmdGen->GetDirectUsercmd();
-+ }
-+ }
-+
-+ if ( !mapSpawned ) {
-+ return;
-+ }
-+
-+ if ( guiActive ) {
-+ lastGameTic = latchedTicNumber;
-+ return;
-+ }
-+
-+ // in message box / GUIFrame, idSessionLocal::Frame is used for GUI interactivity
-+ // but we early exit to avoid running game frames
-+ if ( idAsyncNetwork::IsActive() ) {
-+ return;
-+ }
-+
-+ // check for user info changes
-+ if ( cvarSystem->GetModifiedFlags() & CVAR_USERINFO ) {
-+ mapSpawnData.userInfo[0] = *cvarSystem->MoveCVarsToDict( CVAR_USERINFO );
-+ game->SetUserInfo( 0, mapSpawnData.userInfo[0], false, false );
-+ cvarSystem->ClearModifiedFlags( CVAR_USERINFO );
-+ }
-+
-+ // see how many usercmds we are going to run
-+ int numCmdsToRun = latchedTicNumber - lastGameTic;
-+
-+ // don't let a long onDemand sound load unsync everything
-+ if ( timeHitch ) {
-+ int skip = timeHitch / USERCMD_MSEC;
-+ lastGameTic += skip;
-+ numCmdsToRun -= skip;
-+ timeHitch = 0;
-+ }
-+
-+ // don't get too far behind after a hitch
-+ if ( numCmdsToRun > 10 ) {
-+ lastGameTic = latchedTicNumber - 10;
-+ }
-+
-+ // never use more than USERCMD_PER_DEMO_FRAME,
-+ // which makes it go into slow motion when recording
-+ if ( writeDemo ) {
-+ int fixedTic = USERCMD_PER_DEMO_FRAME;
-+ // we should have waited long enough
-+ if ( numCmdsToRun < fixedTic ) {
-+ common->Error( "idSessionLocal::Frame: numCmdsToRun < fixedTic" );
-+ }
-+ // we may need to dump older commands
-+ lastGameTic = latchedTicNumber - fixedTic;
-+ } else if ( com_fixedTic.GetInteger() > 0 ) {
-+ // this may cause commands run in a previous frame to
-+ // be run again if we are going at above the real time rate
-+ lastGameTic = latchedTicNumber - com_fixedTic.GetInteger();
-+ } else if ( aviCaptureMode ) {
-+ lastGameTic = latchedTicNumber - com_aviDemoTics.GetInteger();
-+ }
-+
-+ // force only one game frame update this frame. the game code requests this after skipping cinematics
-+ // so we come back immediately after the cinematic is done instead of a few frames later which can
-+ // cause sounds played right after the cinematic to not play.
-+ if ( syncNextGameFrame ) {
-+ lastGameTic = latchedTicNumber - 1;
-+ syncNextGameFrame = false;
-+ }
-+
-+ // create client commands, which will be sent directly
-+ // to the game
-+ if ( com_showTics.GetBool() ) {
-+ common->Printf( "%i ", latchedTicNumber - lastGameTic );
-+ }
-+
-+ int gameTicsToRun = latchedTicNumber - lastGameTic;
-+ int i;
-+ for ( i = 0 ; i < gameTicsToRun ; i++ ) {
-+ RunGameTic();
-+ if ( !mapSpawned ) {
-+ // exited game play
-+ break;
-+ }
-+ if ( syncNextGameFrame ) {
-+ // long game frame, so break out and continue executing as if there was no hitch
-+ break;
-+ }
-+ }
-+}
-+
-+/*
-+================
-+idSessionLocal::RunGameTic
-+================
-+*/
-+void idSessionLocal::RunGameTic() {
-+ logCmd_t logCmd;
-+ usercmd_t cmd;
-+
-+ // if we are doing a command demo, read or write from the file
-+ if ( cmdDemoFile ) {
-+ if ( !cmdDemoFile->Read( &logCmd, sizeof( logCmd ) ) ) {
-+ common->Printf( "Command demo completed at logIndex %i\n", logIndex );
-+ fileSystem->CloseFile( cmdDemoFile );
-+ cmdDemoFile = NULL;
-+ if ( aviCaptureMode ) {
-+ EndAVICapture();
-+ Shutdown();
-+ }
-+ // we fall out of the demo to normal commands
-+ // the impulse and chat character toggles may not be correct, and the view
-+ // angle will definitely be wrong
-+ } else {
-+ cmd = logCmd.cmd;
-+ cmd.ByteSwap();
-+ logCmd.consistencyHash = LittleInt( logCmd.consistencyHash );
-+ }
-+ }
-+
-+ // if we didn't get one from the file, get it locally
-+ if ( !cmdDemoFile ) {
-+ // get a locally created command
-+ if ( com_asyncInput.GetBool() ) {
-+ cmd = usercmdGen->TicCmd( lastGameTic );
-+ } else {
-+ cmd = usercmdGen->GetDirectUsercmd();
-+ }
-+ lastGameTic++;
-+ }
-+
-+ // run the game logic every player move
-+ int start = Sys_Milliseconds();
-+ gameReturn_t ret = game->RunFrame( &cmd );
-+
-+ int end = Sys_Milliseconds();
-+ time_gameFrame += end - start; // note time used for com_speeds
-+
-+ // check for constency failure from a recorded command
-+ if ( cmdDemoFile ) {
-+ if ( ret.consistencyHash != logCmd.consistencyHash ) {
-+ common->Printf( "Consistency failure on logIndex %i\n", logIndex );
-+ Stop();
-+ return;
-+ }
-+ }
-+
-+ // save the cmd for cmdDemo archiving
-+ if ( logIndex < MAX_LOGGED_USERCMDS ) {
-+ loggedUsercmds[logIndex].cmd = cmd;
-+ // save the consistencyHash for demo playback verification
-+ loggedUsercmds[logIndex].consistencyHash = ret.consistencyHash;
-+ if (logIndex % 30 == 0 && statIndex < MAX_LOGGED_STATS) {
-+ loggedStats[statIndex].health = ret.health;
-+ loggedStats[statIndex].heartRate = ret.heartRate;
-+ loggedStats[statIndex].stamina = ret.stamina;
-+ loggedStats[statIndex].combat = ret.combat;
-+ statIndex++;
-+ }
-+ logIndex++;
-+ }
-+
-+ syncNextGameFrame = ret.syncNextGameFrame;
-+
-+ if ( ret.sessionCommand[0] ) {
-+ idCmdArgs args;
-+
-+ args.TokenizeString( ret.sessionCommand, false );
-+
-+ if ( !idStr::Icmp( args.Argv(0), "map" ) ) {
-+ // get current player states
-+ for ( int i = 0 ; i < numClients ; i++ ) {
-+ mapSpawnData.persistentPlayerInfo[i] = game->GetPersistentPlayerInfo( i );
-+ }
-+ // clear the devmap key on serverinfo, so player spawns
-+ // won't get the map testing items
-+ mapSpawnData.serverInfo.Delete( "devmap" );
-+
-+ // go to the next map
-+ MoveToNewMap( args.Argv(1) );
-+ } else if ( !idStr::Icmp( args.Argv(0), "devmap" ) ) {
-+ mapSpawnData.serverInfo.Set( "devmap", "1" );
-+ MoveToNewMap( args.Argv(1) );
-+ } else if ( !idStr::Icmp( args.Argv(0), "died" ) ) {
-+ // restart on the same map
-+ UnloadMap();
-+ SetGUI(guiRestartMenu, NULL);
-+ } else if ( !idStr::Icmp( args.Argv(0), "disconnect" ) ) {
-+ cmdSystem->BufferCommandText( CMD_EXEC_INSERT, "stoprecording ; disconnect" );
-+ }
-+ }
-+}
-+
-+/*
-+===============
-+idSessionLocal::Init
-+
-+Called in an orderly fashion at system startup,
-+so commands, cvars, files, etc are all available
-+===============
-+*/
-+void idSessionLocal::Init() {
-+
-+ common->Printf( "----- Initializing Session -----\n" );
-+
-+ cmdSystem->AddCommand( "writePrecache", Sess_WritePrecache_f, CMD_FL_SYSTEM|CMD_FL_CHEAT, "writes precache commands" );
-+
-+#ifndef ID_DEDICATED
-+ cmdSystem->AddCommand( "map", Session_Map_f, CMD_FL_SYSTEM, "loads a map", idCmdSystem::ArgCompletion_MapName );
-+ cmdSystem->AddCommand( "devmap", Session_DevMap_f, CMD_FL_SYSTEM, "loads a map in developer mode", idCmdSystem::ArgCompletion_MapName );
-+ cmdSystem->AddCommand( "testmap", Session_TestMap_f, CMD_FL_SYSTEM, "tests a map", idCmdSystem::ArgCompletion_MapName );
-+
-+ cmdSystem->AddCommand( "writeCmdDemo", Session_WriteCmdDemo_f, CMD_FL_SYSTEM, "writes a command demo" );
-+ cmdSystem->AddCommand( "playCmdDemo", Session_PlayCmdDemo_f, CMD_FL_SYSTEM, "plays back a command demo" );
-+ cmdSystem->AddCommand( "timeCmdDemo", Session_TimeCmdDemo_f, CMD_FL_SYSTEM, "times a command demo" );
-+ cmdSystem->AddCommand( "exitCmdDemo", Session_ExitCmdDemo_f, CMD_FL_SYSTEM, "exits a command demo" );
-+ cmdSystem->AddCommand( "aviCmdDemo", Session_AVICmdDemo_f, CMD_FL_SYSTEM, "writes AVIs for a command demo" );
-+ cmdSystem->AddCommand( "aviGame", Session_AVIGame_f, CMD_FL_SYSTEM, "writes AVIs for the current game" );
-+
-+ cmdSystem->AddCommand( "recordDemo", Session_RecordDemo_f, CMD_FL_SYSTEM, "records a demo" );
-+ cmdSystem->AddCommand( "stopRecording", Session_StopRecordingDemo_f, CMD_FL_SYSTEM, "stops demo recording" );
-+ cmdSystem->AddCommand( "playDemo", Session_PlayDemo_f, CMD_FL_SYSTEM, "plays back a demo", idCmdSystem::ArgCompletion_DemoName );
-+ cmdSystem->AddCommand( "timeDemo", Session_TimeDemo_f, CMD_FL_SYSTEM, "times a demo", idCmdSystem::ArgCompletion_DemoName );
-+ cmdSystem->AddCommand( "timeDemoQuit", Session_TimeDemoQuit_f, CMD_FL_SYSTEM, "times a demo and quits", idCmdSystem::ArgCompletion_DemoName );
-+ cmdSystem->AddCommand( "aviDemo", Session_AVIDemo_f, CMD_FL_SYSTEM, "writes AVIs for a demo", idCmdSystem::ArgCompletion_DemoName );
-+ cmdSystem->AddCommand( "compressDemo", Session_CompressDemo_f, CMD_FL_SYSTEM, "compresses a demo file", idCmdSystem::ArgCompletion_DemoName );
-+#endif
-+
-+ cmdSystem->AddCommand( "disconnect", Session_Disconnect_f, CMD_FL_SYSTEM, "disconnects from a game" );
-+
-+ cmdSystem->AddCommand( "demoShot", Session_DemoShot_f, CMD_FL_SYSTEM, "writes a screenshot for a demo" );
-+ cmdSystem->AddCommand( "testGUI", Session_TestGUI_f, CMD_FL_SYSTEM, "tests a gui" );
-+
-+#ifndef ID_DEDICATED
-+ cmdSystem->AddCommand( "saveGame", SaveGame_f, CMD_FL_SYSTEM|CMD_FL_CHEAT, "saves a game" );
-+ cmdSystem->AddCommand( "loadGame", LoadGame_f, CMD_FL_SYSTEM|CMD_FL_CHEAT, "loads a game", idCmdSystem::ArgCompletion_SaveGame );
-+#endif
-+
-+ cmdSystem->AddCommand( "takeViewNotes", TakeViewNotes_f, CMD_FL_SYSTEM, "take notes about the current map from the current view" );
-+ cmdSystem->AddCommand( "takeViewNotes2", TakeViewNotes2_f, CMD_FL_SYSTEM, "extended take view notes" );
-+
-+ cmdSystem->AddCommand( "rescanSI", Session_RescanSI_f, CMD_FL_SYSTEM, "internal - rescan serverinfo cvars and tell game" );
-+
-+ cmdSystem->AddCommand( "promptKey", Session_PromptKey_f, CMD_FL_SYSTEM, "prompt and sets the CD Key" );
-+
-+ cmdSystem->AddCommand( "hitch", Session_Hitch_f, CMD_FL_SYSTEM|CMD_FL_CHEAT, "hitches the game" );
-+
-+ // the same idRenderWorld will be used for all games
-+ // and demos, insuring that level specific models
-+ // will be freed
-+ rw = renderSystem->AllocRenderWorld();
-+ sw = soundSystem->AllocSoundWorld( rw );
-+
-+ menuSoundWorld = soundSystem->AllocSoundWorld( rw );
-+
-+ // we have a single instance of the main menu
-+ guiMainMenu = uiManager->FindGui( "guis/mainmenu.gui", true, false, true );
-+ guiMainMenu_MapList = uiManager->AllocListGUI();
-+ guiMainMenu_MapList->Config( guiMainMenu, "mapList" );
-+ idAsyncNetwork::client.serverList.GUIConfig( guiMainMenu, "serverList" );
-+ guiRestartMenu = uiManager->FindGui( "guis/restart.gui", true, false, true );
-+ guiGameOver = uiManager->FindGui( "guis/gameover.gui", true, false, true );
-+ guiMsg = uiManager->FindGui( "guis/msg.gui", true, false, true );
-+ guiTakeNotes = uiManager->FindGui( "guis/takeNotes.gui", true, false, true );
-+ guiIntro = uiManager->FindGui( "guis/intro.gui", true, false, true );
-+
-+ whiteMaterial = declManager->FindMaterial( "_white" );
-+
-+ guiInGame = NULL;
-+ guiTest = NULL;
-+
-+ guiActive = NULL;
-+ guiHandle = NULL;
-+
-+ ReadCDKey();
-+}
-+
-+/*
-+===============
-+idSessionLocal::GetLocalClientNum
-+===============
-+*/
-+int idSessionLocal::GetLocalClientNum() {
-+ if ( idAsyncNetwork::client.IsActive() ) {
-+ return idAsyncNetwork::client.GetLocalClientNum();
-+ } else if ( idAsyncNetwork::server.IsActive() ) {
-+ if ( idAsyncNetwork::serverDedicated.GetInteger() == 0 ) {
-+ return 0;
-+ } else if ( idAsyncNetwork::server.IsClientInGame( idAsyncNetwork::serverDrawClient.GetInteger() ) ) {
-+ return idAsyncNetwork::serverDrawClient.GetInteger();
-+ } else {
-+ return -1;
-+ }
-+ } else {
-+ return 0;
-+ }
-+}
-+
-+/*
-+===============
-+idSessionLocal::SetPlayingSoundWorld
-+===============
-+*/
-+void idSessionLocal::SetPlayingSoundWorld() {
-+ if ( guiActive && ( guiActive == guiMainMenu || guiActive == guiIntro || guiActive == guiLoading || ( guiActive == guiMsg && !mapSpawned ) ) ) {
-+ soundSystem->SetPlayingSoundWorld( menuSoundWorld );
-+ } else {
-+ soundSystem->SetPlayingSoundWorld( sw );
-+ }
-+}
-+
-+/*
-+===============
-+idSessionLocal::TimeHitch
-+
-+this is used by the sound system when an OnDemand sound is loaded, so the game action
-+doesn't advance and get things out of sync
-+===============
-+*/
-+void idSessionLocal::TimeHitch( int msec ) {
-+ timeHitch += msec;
-+}
-+
-+/*
-+=================
-+idSessionLocal::ReadCDKey
-+=================
-+*/
-+void idSessionLocal::ReadCDKey( void ) {
-+ idStr filename;
-+ idFile *f;
-+ char buffer[32];
-+
-+ cdkey_state = CDKEY_UNKNOWN;
-+
-+ filename = "../" BASE_GAMEDIR "/" CDKEY_FILE;
-+ f = fileSystem->OpenExplicitFileRead( fileSystem->RelativePathToOSPath( filename, "fs_configpath" ) );
-+
-+ // try the install path, which is where the cd installer and steam put it
-+ if ( !f )
-+ f = fileSystem->OpenExplicitFileRead( fileSystem->RelativePathToOSPath( filename, "fs_basepath" ) );
-+
-+ if ( !f ) {
-+ common->Printf( "Couldn't read %s.\n", filename.c_str() );
-+ cdkey[ 0 ] = '\0';
-+ } else {
-+ memset( buffer, 0, sizeof(buffer) );
-+ f->Read( buffer, CDKEY_BUF_LEN - 1 );
-+ fileSystem->CloseFile( f );
-+ idStr::Copynz( cdkey, buffer, CDKEY_BUF_LEN );
-+ }
-+
-+ xpkey_state = CDKEY_UNKNOWN;
-+
-+ filename = "../" BASE_GAMEDIR "/" XPKEY_FILE;
-+ f = fileSystem->OpenExplicitFileRead( fileSystem->RelativePathToOSPath( filename, "fs_configpath" ) );
-+
-+ // try the install path, which is where the cd installer and steam put it
-+ if ( !f )
-+ f = fileSystem->OpenExplicitFileRead( fileSystem->RelativePathToOSPath( filename, "fs_basepath" ) );
-+
-+ if ( !f ) {
-+ common->Printf( "Couldn't read %s.\n", filename.c_str() );
-+ xpkey[ 0 ] = '\0';
-+ } else {
-+ memset( buffer, 0, sizeof(buffer) );
-+ f->Read( buffer, CDKEY_BUF_LEN - 1 );
-+ fileSystem->CloseFile( f );
-+ idStr::Copynz( xpkey, buffer, CDKEY_BUF_LEN );
-+ }
-+}
-+
-+/*
-+================
-+idSessionLocal::WriteCDKey
-+================
-+*/
-+void idSessionLocal::WriteCDKey( void ) {
-+ idStr filename;
-+ idFile *f;
-+ const char *OSPath;
-+
-+ filename = "../" BASE_GAMEDIR "/" CDKEY_FILE;
-+ // OpenFileWrite advertises creating directories to the path if needed, but that won't work with a '..' in the path
-+ // occasionally on windows, but mostly on Linux and OSX, the fs_configpath/base may not exist in full
-+ OSPath = fileSystem->BuildOSPath( cvarSystem->GetCVarString( "fs_configpath" ), BASE_GAMEDIR, CDKEY_FILE );
-+ fileSystem->CreateOSPath( OSPath );
-+ f = fileSystem->OpenFileWrite( filename, "fs_configpath" );
-+ if ( !f ) {
-+ common->Printf( "Couldn't write %s.\n", filename.c_str() );
-+ return;
-+ }
-+ f->Printf( "%s%s", cdkey, CDKEY_TEXT );
-+ fileSystem->CloseFile( f );
-+
-+ filename = "../" BASE_GAMEDIR "/" XPKEY_FILE;
-+ f = fileSystem->OpenFileWrite( filename, "fs_configpath" );
-+ if ( !f ) {
-+ common->Printf( "Couldn't write %s.\n", filename.c_str() );
-+ return;
-+ }
-+ f->Printf( "%s%s", xpkey, CDKEY_TEXT );
-+ fileSystem->CloseFile( f );
-+}
-+
-+/*
-+===============
-+idSessionLocal::ClearKey
-+===============
-+*/
-+void idSessionLocal::ClearCDKey( bool valid[ 2 ] ) {
-+ if ( !valid[ 0 ] ) {
-+ memset( cdkey, 0, CDKEY_BUF_LEN );
-+ cdkey_state = CDKEY_UNKNOWN;
-+ } else if ( cdkey_state == CDKEY_CHECKING ) {
-+ // if a key was in checking and not explicitely asked for clearing, put it back to ok
-+ cdkey_state = CDKEY_OK;
-+ }
-+ if ( !valid[ 1 ] ) {
-+ memset( xpkey, 0, CDKEY_BUF_LEN );
-+ xpkey_state = CDKEY_UNKNOWN;
-+ } else if ( xpkey_state == CDKEY_CHECKING ) {
-+ xpkey_state = CDKEY_OK;
-+ }
-+ WriteCDKey( );
-+}
-+
-+/*
-+================
-+idSessionLocal::GetCDKey
-+================
-+*/
-+const char *idSessionLocal::GetCDKey( bool xp ) {
-+ if ( !xp ) {
-+ return cdkey;
-+ }
-+ if ( xpkey_state == CDKEY_OK || xpkey_state == CDKEY_CHECKING ) {
-+ return xpkey;
-+ }
-+ return NULL;
-+}
-+
-+// digits to letters table
-+#define CDKEY_DIGITS "TWSBJCGD7PA23RLH"
-+
-+/*
-+===============
-+idSessionLocal::EmitGameAuth
-+we toggled some key state to CDKEY_CHECKING. send a standalone auth packet to validate
-+===============
-+*/
-+void idSessionLocal::EmitGameAuth( void ) {
-+ // make sure the auth reply is empty, we use it to indicate an auth reply
-+ authMsg.Empty();
-+ if ( idAsyncNetwork::client.SendAuthCheck( cdkey_state == CDKEY_CHECKING ? cdkey : NULL, xpkey_state == CDKEY_CHECKING ? xpkey : NULL ) ) {
-+ authEmitTimeout = Sys_Milliseconds() + CDKEY_AUTH_TIMEOUT;
-+ common->DPrintf( "authing with the master..\n" );
-+ } else {
-+ // net is not available
-+ common->DPrintf( "sendAuthCheck failed\n" );
-+ if ( cdkey_state == CDKEY_CHECKING ) {
-+ cdkey_state = CDKEY_OK;
-+ }
-+ if ( xpkey_state == CDKEY_CHECKING ) {
-+ xpkey_state = CDKEY_OK;
-+ }
-+ }
-+}
-+
-+/*
-+================
-+idSessionLocal::CheckKey
-+the function will only modify keys to _OK or _CHECKING if the offline checks are passed
-+if the function returns false, the offline checks failed, and offline_valid holds which keys are bad
-+================
-+*/
-+bool idSessionLocal::CheckKey( const char *key, bool netConnect, bool offline_valid[ 2 ] ) {
-+ char lkey[ 2 ][ CDKEY_BUF_LEN ];
-+ char l_chk[ 2 ][ 3 ];
-+ char s_chk[ 3 ];
-+ int imax,i_key;
-+ unsigned int checksum, chk8;
-+ bool edited_key[ 2 ];
-+
-+ // make sure have a right input string
-+ assert( strlen( key ) == ( CDKEY_BUF_LEN - 1 ) * 2 + 4 + 3 + 4 );
-+
-+ edited_key[ 0 ] = ( key[0] == '1' );
-+ idStr::Copynz( lkey[0], key + 2, CDKEY_BUF_LEN );
-+ idStr::ToUpper( lkey[0] );
-+ idStr::Copynz( l_chk[0], key + CDKEY_BUF_LEN + 2, 3 );
-+ idStr::ToUpper( l_chk[0] );
-+ edited_key[ 1 ] = ( key[ CDKEY_BUF_LEN + 2 + 3 ] == '1' );
-+ idStr::Copynz( lkey[1], key + CDKEY_BUF_LEN + 7, CDKEY_BUF_LEN );
-+ idStr::ToUpper( lkey[1] );
-+ idStr::Copynz( l_chk[1], key + CDKEY_BUF_LEN * 2 + 7, 3 );
-+ idStr::ToUpper( l_chk[1] );
-+
-+ if ( fileSystem->HasD3XP() ) {
-+ imax = 2;
-+ } else {
-+ imax = 1;
-+ }
-+ offline_valid[ 0 ] = offline_valid[ 1 ] = true;
-+ for( i_key = 0; i_key < imax; i_key++ ) {
-+ // check that the characters are from the valid set
-+ int i;
-+ for ( i = 0; i < CDKEY_BUF_LEN - 1; i++ ) {
-+ if ( !strchr( CDKEY_DIGITS, lkey[i_key][i] ) ) {
-+ offline_valid[ i_key ] = false;
-+ continue;
-+ }
-+ }
-+
-+ if ( edited_key[ i_key ] ) {
-+ // verify the checksum for edited keys only
-+ checksum = CRC32_BlockChecksum( lkey[i_key], CDKEY_BUF_LEN - 1 );
-+ chk8 = ( checksum & 0xff ) ^ ( ( ( checksum & 0xff00 ) >> 8 ) ^ ( ( ( checksum & 0xff0000 ) >> 16 ) ^ ( ( checksum & 0xff000000 ) >> 24 ) ) );
-+ idStr::snPrintf( s_chk, 3, "%02X", chk8 );
-+ if ( idStr::Icmp( l_chk[i_key], s_chk ) != 0 ) {
-+ offline_valid[ i_key ] = false;
-+ continue;
-+ }
-+ }
-+ }
-+
-+ if ( !offline_valid[ 0 ] || !offline_valid[1] ) {
-+ return false;
-+ }
-+
-+ // offline checks passed, we'll return true and optionally emit key check requests
-+ // the function should only modify the key states if the offline checks passed successfully
-+
-+ // set the keys, don't send a game auth if we are net connecting
-+ idStr::Copynz( cdkey, lkey[0], CDKEY_BUF_LEN );
-+ netConnect ? cdkey_state = CDKEY_OK : cdkey_state = CDKEY_CHECKING;
-+ if ( fileSystem->HasD3XP() ) {
-+ idStr::Copynz( xpkey, lkey[1], CDKEY_BUF_LEN );
-+ netConnect ? xpkey_state = CDKEY_OK : xpkey_state = CDKEY_CHECKING;
-+ } else {
-+ xpkey_state = CDKEY_NA;
-+ }
-+ if ( !netConnect ) {
-+ EmitGameAuth();
-+ }
-+ SetCDKeyGuiVars();
-+
-+ return true;
-+}
-+
-+/*
-+===============
-+idSessionLocal::CDKeysAreValid
-+checking that the key is present and uses only valid characters
-+if d3xp is installed, check for a valid xpkey as well
-+emit an auth packet to the master if possible and needed
-+===============
-+*/
-+bool idSessionLocal::CDKeysAreValid( bool strict ) {
-+ int i;
-+ bool emitAuth = false;
-+
-+ if ( cdkey_state == CDKEY_UNKNOWN ) {
-+ if ( strlen( cdkey ) != CDKEY_BUF_LEN - 1 ) {
-+ cdkey_state = CDKEY_INVALID;
-+ } else {
-+ for ( i = 0; i < CDKEY_BUF_LEN-1; i++ ) {
-+ if ( !strchr( CDKEY_DIGITS, cdkey[i] ) ) {
-+ cdkey_state = CDKEY_INVALID;
-+ break;
-+ }
-+ }
-+ }
-+ if ( cdkey_state == CDKEY_UNKNOWN ) {
-+ cdkey_state = CDKEY_CHECKING;
-+ emitAuth = true;
-+ }
-+ }
-+ if ( xpkey_state == CDKEY_UNKNOWN ) {
-+ if ( fileSystem->HasD3XP() ) {
-+ if ( strlen( xpkey ) != CDKEY_BUF_LEN -1 ) {
-+ xpkey_state = CDKEY_INVALID;
-+ } else {
-+ for ( i = 0; i < CDKEY_BUF_LEN-1; i++ ) {
-+ if ( !strchr( CDKEY_DIGITS, xpkey[i] ) ) {
-+ xpkey_state = CDKEY_INVALID;
-+ }
-+ }
-+ }
-+ if ( xpkey_state == CDKEY_UNKNOWN ) {
-+ xpkey_state = CDKEY_CHECKING;
-+ emitAuth = true;
-+ }
-+ } else {
-+ xpkey_state = CDKEY_NA;
-+ }
-+ }
-+ if ( emitAuth ) {
-+ EmitGameAuth();
-+ }
-+ // make sure to keep the mainmenu gui up to date in case we made state changes
-+ SetCDKeyGuiVars();
-+ if ( strict ) {
-+ return cdkey_state == CDKEY_OK && ( xpkey_state == CDKEY_OK || xpkey_state == CDKEY_NA );
-+ } else {
-+ return ( cdkey_state == CDKEY_OK || cdkey_state == CDKEY_CHECKING ) && ( xpkey_state == CDKEY_OK || xpkey_state == CDKEY_CHECKING || xpkey_state == CDKEY_NA );
-+ }
-+}
-+
-+/*
-+===============
-+idSessionLocal::WaitingForGameAuth
-+===============
-+*/
-+bool idSessionLocal::WaitingForGameAuth( void ) {
-+ return authEmitTimeout != 0;
-+}
-+
-+/*
-+===============
-+idSessionLocal::CDKeysAuthReply
-+===============
-+*/
-+void idSessionLocal::CDKeysAuthReply( bool valid, const char *auth_msg ) {
-+ //assert( authEmitTimeout > 0 );
-+ if ( authWaitBox ) {
-+ // close the wait box
-+ StopBox();
-+ authWaitBox = false;
-+ }
-+ if ( !valid ) {
-+ common->DPrintf( "auth key is invalid\n" );
-+ authMsg = auth_msg;
-+ if ( cdkey_state == CDKEY_CHECKING ) {
-+ cdkey_state = CDKEY_INVALID;
-+ }
-+ if ( xpkey_state == CDKEY_CHECKING ) {
-+ xpkey_state = CDKEY_INVALID;
-+ }
-+ } else {
-+ common->DPrintf( "client is authed in\n" );
-+ if ( cdkey_state == CDKEY_CHECKING ) {
-+ cdkey_state = CDKEY_OK;
-+ }
-+ if ( xpkey_state == CDKEY_CHECKING ) {
-+ xpkey_state = CDKEY_OK;
-+ }
-+ }
-+ authEmitTimeout = 0;
-+ SetCDKeyGuiVars();
-+}
-+
-+/*
-+===============
-+idSessionLocal::GetCurrentMapName
-+===============
-+*/
-+const char *idSessionLocal::GetCurrentMapName() {
-+ return currentMapName.c_str();
-+}
-+
-+/*
-+===============
-+idSessionLocal::GetSaveGameVersion
-+===============
-+*/
-+int idSessionLocal::GetSaveGameVersion( void ) {
-+ return savegameVersion;
-+}
-+
-+/*
-+===============
-+idSessionLocal::GetAuthMsg
-+===============
-+*/
-+const char *idSessionLocal::GetAuthMsg( void ) {
-+ return authMsg.c_str();
-+}
-diff -Naur dhewm3.old/neo/framework/Session.cpp.rej dhewm3/neo/framework/Session.cpp.rej
---- dhewm3.old/neo/framework/Session.cpp.rej 1970-01-01 01:00:00.000000000 +0100
-+++ dhewm3/neo/framework/Session.cpp.rej 2013-08-22 13:23:33.003219524 +0200
-@@ -0,0 +1,338 @@
-+--- dhewm3.old/neo/framework/Session.cpp 2012-07-28 12:54:25.000000000 +0200
-++++ dhewm3/neo/framework/Session.cpp 2012-08-29 15:07:41.837514785 +0200
-+@@ -2835,335 +2727,6 @@
-+ }
-+
-+ /*
-+-=================
-+-idSessionLocal::ReadCDKey
-+-=================
-+-*/
-+-void idSessionLocal::ReadCDKey( void ) {
-+- idStr filename;
-+- idFile *f;
-+- char buffer[32];
-+-
-+- cdkey_state = CDKEY_UNKNOWN;
-+-
-+- filename = "../" BASE_GAMEDIR "/" CDKEY_FILE;
-+- f = fileSystem->OpenExplicitFileRead( fileSystem->RelativePathToOSPath( filename, "fs_configpath" ) );
-+-
-+- // try the install path, which is where the cd installer and steam put it
-+- if ( !f )
-+- f = fileSystem->OpenExplicitFileRead( fileSystem->RelativePathToOSPath( filename, "fs_basepath" ) );
-+-
-+- if ( !f ) {
-+- common->Printf( "Couldn't read %s.\n", filename.c_str() );
-+- cdkey[ 0 ] = '\0';
-+- } else {
-+- memset( buffer, 0, sizeof(buffer) );
-+- f->Read( buffer, CDKEY_BUF_LEN - 1 );
-+- fileSystem->CloseFile( f );
-+- idStr::Copynz( cdkey, buffer, CDKEY_BUF_LEN );
-+- }
-+-
-+- xpkey_state = CDKEY_UNKNOWN;
-+-
-+- filename = "../" BASE_GAMEDIR "/" XPKEY_FILE;
-+- f = fileSystem->OpenExplicitFileRead( fileSystem->RelativePathToOSPath( filename, "fs_configpath" ) );
-+-
-+- // try the install path, which is where the cd installer and steam put it
-+- if ( !f )
-+- f = fileSystem->OpenExplicitFileRead( fileSystem->RelativePathToOSPath( filename, "fs_basepath" ) );
-+-
-+- if ( !f ) {
-+- common->Printf( "Couldn't read %s.\n", filename.c_str() );
-+- xpkey[ 0 ] = '\0';
-+- } else {
-+- memset( buffer, 0, sizeof(buffer) );
-+- f->Read( buffer, CDKEY_BUF_LEN - 1 );
-+- fileSystem->CloseFile( f );
-+- idStr::Copynz( xpkey, buffer, CDKEY_BUF_LEN );
-+- }
-+-}
-+-
-+-/*
-+-================
-+-idSessionLocal::WriteCDKey
-+-================
-+-*/
-+-void idSessionLocal::WriteCDKey( void ) {
-+- idStr filename;
-+- idFile *f;
-+- const char *OSPath;
-+-
-+- filename = "../" BASE_GAMEDIR "/" CDKEY_FILE;
-+- // OpenFileWrite advertises creating directories to the path if needed, but that won't work with a '..' in the path
-+- // occasionally on windows, but mostly on Linux and OSX, the fs_configpath/base may not exist in full
-+- OSPath = fileSystem->BuildOSPath( cvarSystem->GetCVarString( "fs_configpath" ), BASE_GAMEDIR, CDKEY_FILE );
-+- fileSystem->CreateOSPath( OSPath );
-+- f = fileSystem->OpenFileWrite( filename, "fs_configpath" );
-+- if ( !f ) {
-+- common->Printf( "Couldn't write %s.\n", filename.c_str() );
-+- return;
-+- }
-+- f->Printf( "%s%s", cdkey, CDKEY_TEXT );
-+- fileSystem->CloseFile( f );
-+-
-+- filename = "../" BASE_GAMEDIR "/" XPKEY_FILE;
-+- f = fileSystem->OpenFileWrite( filename, "fs_configpath" );
-+- if ( !f ) {
-+- common->Printf( "Couldn't write %s.\n", filename.c_str() );
-+- return;
-+- }
-+- f->Printf( "%s%s", xpkey, CDKEY_TEXT );
-+- fileSystem->CloseFile( f );
-+-}
-+-
-+-/*
-+-===============
-+-idSessionLocal::ClearKey
-+-===============
-+-*/
-+-void idSessionLocal::ClearCDKey( bool valid[ 2 ] ) {
-+- if ( !valid[ 0 ] ) {
-+- memset( cdkey, 0, CDKEY_BUF_LEN );
-+- cdkey_state = CDKEY_UNKNOWN;
-+- } else if ( cdkey_state == CDKEY_CHECKING ) {
-+- // if a key was in checking and not explicitely asked for clearing, put it back to ok
-+- cdkey_state = CDKEY_OK;
-+- }
-+- if ( !valid[ 1 ] ) {
-+- memset( xpkey, 0, CDKEY_BUF_LEN );
-+- xpkey_state = CDKEY_UNKNOWN;
-+- } else if ( xpkey_state == CDKEY_CHECKING ) {
-+- xpkey_state = CDKEY_OK;
-+- }
-+- WriteCDKey( );
-+-}
-+-
-+-/*
-+-================
-+-idSessionLocal::GetCDKey
-+-================
-+-*/
-+-const char *idSessionLocal::GetCDKey( bool xp ) {
-+- if ( !xp ) {
-+- return cdkey;
-+- }
-+- if ( xpkey_state == CDKEY_OK || xpkey_state == CDKEY_CHECKING ) {
-+- return xpkey;
-+- }
-+- return NULL;
-+-}
-+-
-+-// digits to letters table
-+-#define CDKEY_DIGITS "TWSBJCGD7PA23RLH"
-+-
-+-/*
-+-===============
-+-idSessionLocal::EmitGameAuth
-+-we toggled some key state to CDKEY_CHECKING. send a standalone auth packet to validate
-+-===============
-+-*/
-+-void idSessionLocal::EmitGameAuth( void ) {
-+- // make sure the auth reply is empty, we use it to indicate an auth reply
-+- authMsg.Empty();
-+- if ( idAsyncNetwork::client.SendAuthCheck( cdkey_state == CDKEY_CHECKING ? cdkey : NULL, xpkey_state == CDKEY_CHECKING ? xpkey : NULL ) ) {
-+- authEmitTimeout = Sys_Milliseconds() + CDKEY_AUTH_TIMEOUT;
-+- common->DPrintf( "authing with the master..\n" );
-+- } else {
-+- // net is not available
-+- common->DPrintf( "sendAuthCheck failed\n" );
-+- if ( cdkey_state == CDKEY_CHECKING ) {
-+- cdkey_state = CDKEY_OK;
-+- }
-+- if ( xpkey_state == CDKEY_CHECKING ) {
-+- xpkey_state = CDKEY_OK;
-+- }
-+- }
-+-}
-+-
-+-/*
-+-================
-+-idSessionLocal::CheckKey
-+-the function will only modify keys to _OK or _CHECKING if the offline checks are passed
-+-if the function returns false, the offline checks failed, and offline_valid holds which keys are bad
-+-================
-+-*/
-+-bool idSessionLocal::CheckKey( const char *key, bool netConnect, bool offline_valid[ 2 ] ) {
-+- char lkey[ 2 ][ CDKEY_BUF_LEN ];
-+- char l_chk[ 2 ][ 3 ];
-+- char s_chk[ 3 ];
-+- int imax,i_key;
-+- unsigned int checksum, chk8;
-+- bool edited_key[ 2 ];
-+-
-+- // make sure have a right input string
-+- assert( strlen( key ) == ( CDKEY_BUF_LEN - 1 ) * 2 + 4 + 3 + 4 );
-+-
-+- edited_key[ 0 ] = ( key[0] == '1' );
-+- idStr::Copynz( lkey[0], key + 2, CDKEY_BUF_LEN );
-+- idStr::ToUpper( lkey[0] );
-+- idStr::Copynz( l_chk[0], key + CDKEY_BUF_LEN + 2, 3 );
-+- idStr::ToUpper( l_chk[0] );
-+- edited_key[ 1 ] = ( key[ CDKEY_BUF_LEN + 2 + 3 ] == '1' );
-+- idStr::Copynz( lkey[1], key + CDKEY_BUF_LEN + 7, CDKEY_BUF_LEN );
-+- idStr::ToUpper( lkey[1] );
-+- idStr::Copynz( l_chk[1], key + CDKEY_BUF_LEN * 2 + 7, 3 );
-+- idStr::ToUpper( l_chk[1] );
-+-
-+- if ( fileSystem->HasD3XP() ) {
-+- imax = 2;
-+- } else {
-+- imax = 1;
-+- }
-+- offline_valid[ 0 ] = offline_valid[ 1 ] = true;
-+- for( i_key = 0; i_key < imax; i_key++ ) {
-+- // check that the characters are from the valid set
-+- int i;
-+- for ( i = 0; i < CDKEY_BUF_LEN - 1; i++ ) {
-+- if ( !strchr( CDKEY_DIGITS, lkey[i_key][i] ) ) {
-+- offline_valid[ i_key ] = false;
-+- continue;
-+- }
-+- }
-+-
-+- if ( edited_key[ i_key ] ) {
-+- // verify the checksum for edited keys only
-+- checksum = CRC32_BlockChecksum( lkey[i_key], CDKEY_BUF_LEN - 1 );
-+- chk8 = ( checksum & 0xff ) ^ ( ( ( checksum & 0xff00 ) >> 8 ) ^ ( ( ( checksum & 0xff0000 ) >> 16 ) ^ ( ( checksum & 0xff000000 ) >> 24 ) ) );
-+- idStr::snPrintf( s_chk, 3, "%02X", chk8 );
-+- if ( idStr::Icmp( l_chk[i_key], s_chk ) != 0 ) {
-+- offline_valid[ i_key ] = false;
-+- continue;
-+- }
-+- }
-+- }
-+-
-+- if ( !offline_valid[ 0 ] || !offline_valid[1] ) {
-+- return false;
-+- }
-+-
-+- // offline checks passed, we'll return true and optionally emit key check requests
-+- // the function should only modify the key states if the offline checks passed successfully
-+-
-+- // set the keys, don't send a game auth if we are net connecting
-+- idStr::Copynz( cdkey, lkey[0], CDKEY_BUF_LEN );
-+- netConnect ? cdkey_state = CDKEY_OK : cdkey_state = CDKEY_CHECKING;
-+- if ( fileSystem->HasD3XP() ) {
-+- idStr::Copynz( xpkey, lkey[1], CDKEY_BUF_LEN );
-+- netConnect ? xpkey_state = CDKEY_OK : xpkey_state = CDKEY_CHECKING;
-+- } else {
-+- xpkey_state = CDKEY_NA;
-+- }
-+- if ( !netConnect ) {
-+- EmitGameAuth();
-+- }
-+- SetCDKeyGuiVars();
-+-
-+- return true;
-+-}
-+-
-+-/*
-+-===============
-+-idSessionLocal::CDKeysAreValid
-+-checking that the key is present and uses only valid characters
-+-if d3xp is installed, check for a valid xpkey as well
-+-emit an auth packet to the master if possible and needed
-+-===============
-+-*/
-+-bool idSessionLocal::CDKeysAreValid( bool strict ) {
-+- int i;
-+- bool emitAuth = false;
-+-
-+- if ( cdkey_state == CDKEY_UNKNOWN ) {
-+- if ( strlen( cdkey ) != CDKEY_BUF_LEN - 1 ) {
-+- cdkey_state = CDKEY_INVALID;
-+- } else {
-+- for ( i = 0; i < CDKEY_BUF_LEN-1; i++ ) {
-+- if ( !strchr( CDKEY_DIGITS, cdkey[i] ) ) {
-+- cdkey_state = CDKEY_INVALID;
-+- break;
-+- }
-+- }
-+- }
-+- if ( cdkey_state == CDKEY_UNKNOWN ) {
-+- cdkey_state = CDKEY_CHECKING;
-+- emitAuth = true;
-+- }
-+- }
-+- if ( xpkey_state == CDKEY_UNKNOWN ) {
-+- if ( fileSystem->HasD3XP() ) {
-+- if ( strlen( xpkey ) != CDKEY_BUF_LEN -1 ) {
-+- xpkey_state = CDKEY_INVALID;
-+- } else {
-+- for ( i = 0; i < CDKEY_BUF_LEN-1; i++ ) {
-+- if ( !strchr( CDKEY_DIGITS, xpkey[i] ) ) {
-+- xpkey_state = CDKEY_INVALID;
-+- }
-+- }
-+- }
-+- if ( xpkey_state == CDKEY_UNKNOWN ) {
-+- xpkey_state = CDKEY_CHECKING;
-+- emitAuth = true;
-+- }
-+- } else {
-+- xpkey_state = CDKEY_NA;
-+- }
-+- }
-+- if ( emitAuth ) {
-+- EmitGameAuth();
-+- }
-+- // make sure to keep the mainmenu gui up to date in case we made state changes
-+- SetCDKeyGuiVars();
-+- if ( strict ) {
-+- return cdkey_state == CDKEY_OK && ( xpkey_state == CDKEY_OK || xpkey_state == CDKEY_NA );
-+- } else {
-+- return ( cdkey_state == CDKEY_OK || cdkey_state == CDKEY_CHECKING ) && ( xpkey_state == CDKEY_OK || xpkey_state == CDKEY_CHECKING || xpkey_state == CDKEY_NA );
-+- }
-+-}
-+-
-+-/*
-+-===============
-+-idSessionLocal::WaitingForGameAuth
-+-===============
-+-*/
-+-bool idSessionLocal::WaitingForGameAuth( void ) {
-+- return authEmitTimeout != 0;
-+-}
-+-
-+-/*
-+-===============
-+-idSessionLocal::CDKeysAuthReply
-+-===============
-+-*/
-+-void idSessionLocal::CDKeysAuthReply( bool valid, const char *auth_msg ) {
-+- assert( authEmitTimeout > 0 );
-+- if ( authWaitBox ) {
-+- // close the wait box
-+- StopBox();
-+- authWaitBox = false;
-+- }
-+- if ( !valid ) {
-+- common->DPrintf( "auth key is invalid\n" );
-+- authMsg = auth_msg;
-+- if ( cdkey_state == CDKEY_CHECKING ) {
-+- cdkey_state = CDKEY_INVALID;
-+- }
-+- if ( xpkey_state == CDKEY_CHECKING ) {
-+- xpkey_state = CDKEY_INVALID;
-+- }
-+- } else {
-+- common->DPrintf( "client is authed in\n" );
-+- if ( cdkey_state == CDKEY_CHECKING ) {
-+- cdkey_state = CDKEY_OK;
-+- }
-+- if ( xpkey_state == CDKEY_CHECKING ) {
-+- xpkey_state = CDKEY_OK;
-+- }
-+- }
-+- authEmitTimeout = 0;
-+- SetCDKeyGuiVars();
-+-}
-+-
-+-/*
-+ ===============
-+ idSessionLocal::GetCurrentMapName
-+ ===============
-diff -Naur dhewm3.old/neo/framework/Session.h dhewm3/neo/framework/Session.h
---- dhewm3.old/neo/framework/Session.h 2013-08-15 23:27:20.000000000 +0200
-+++ dhewm3/neo/framework/Session.h 2013-08-22 13:23:33.003219524 +0200
+diff -Naur dhewm3-89f227b365c2086dbe8818d82324f074a8ab4792.old/neo/framework/async/AsyncClient.h dhewm3-89f227b365c2086dbe8818d82324f074a8ab4792/neo/framework/async/AsyncClient.h
+--- dhewm3-89f227b365c2086dbe8818d82324f074a8ab4792.old/neo/framework/async/AsyncClient.h 2016-01-23 10:16:16.945526891 +0100
++++ dhewm3-89f227b365c2086dbe8818d82324f074a8ab4792/neo/framework/async/AsyncClient.h 2016-01-23 10:16:31.530713474 +0100
+@@ -114,9 +114,6 @@
+ void SendReliableGameMessage( const idBitMsg &msg );
+
+ void SendVersionCheck( bool fromMenu = false );
+- // pass NULL for the keys you don't care to auth for
+- // returns false if internet link doesn't appear to be available
+- bool SendAuthCheck( const char *cdkey, const char *xpkey );
+
+ void PacifierUpdate( void );
+
+@@ -201,7 +198,6 @@
+ void ProcessInfoResponseMessage( const netadr_t from, const idBitMsg &msg );
+ void ProcessPrintMessage( const netadr_t from, const idBitMsg &msg );
+ void ProcessServersListMessage( const netadr_t from, const idBitMsg &msg );
+- void ProcessAuthKeyMessage( const netadr_t from, const idBitMsg &msg );
+ void ProcessVersionMessage( const netadr_t from, const idBitMsg &msg );
+ void ConnectionlessMessage( const netadr_t from, const idBitMsg &msg );
+ void ProcessMessage( const netadr_t from, idBitMsg &msg );
+diff -Naur dhewm3-89f227b365c2086dbe8818d82324f074a8ab4792.old/neo/framework/BuildDefines.h dhewm3-89f227b365c2086dbe8818d82324f074a8ab4792/neo/framework/BuildDefines.h
+--- dhewm3-89f227b365c2086dbe8818d82324f074a8ab4792.old/neo/framework/BuildDefines.h 2016-01-23 10:16:16.939526814 +0100
++++ dhewm3-89f227b365c2086dbe8818d82324f074a8ab4792/neo/framework/BuildDefines.h 2016-01-23 10:17:42.613621754 +0100
+@@ -75,24 +75,6 @@
+ #define ID_ALLOW_TOOLS
+ #endif
+
+-#define ID_ENFORCE_KEY 0
+-
+-#ifndef ID_ENFORCE_KEY
+-# if !defined( ID_DEDICATED )
+-# define ID_ENFORCE_KEY 1
+-# else
+-# define ID_ENFORCE_KEY 0
+-# endif
+-#endif
+-
+-#ifndef ID_ENFORCE_KEY_CLIENT
+-# if ID_ENFORCE_KEY
+-# define ID_ENFORCE_KEY_CLIENT 1
+-# else
+-# define ID_ENFORCE_KEY_CLIENT 0
+-# endif
+-#endif
+-
+ // async network
+
+ /*
+diff -Naur dhewm3-89f227b365c2086dbe8818d82324f074a8ab4792.old/neo/framework/Common.cpp dhewm3-89f227b365c2086dbe8818d82324f074a8ab4792/neo/framework/Common.cpp
+--- dhewm3-89f227b365c2086dbe8818d82324f074a8ab4792.old/neo/framework/Common.cpp 2016-01-23 10:16:16.940526827 +0100
++++ dhewm3-89f227b365c2086dbe8818d82324f074a8ab4792/neo/framework/Common.cpp 2016-01-23 10:16:37.409788683 +0100
+@@ -1049,7 +1049,6 @@
+ com_developer.SetBool( false );
+
+ WriteConfigToFile( CONFIG_FILE );
+- session->WriteCDKey( );
+
+ // restore the developer cvar
+ com_developer.SetBool( developer );
+@@ -2832,11 +2831,7 @@
+ InitGame();
+
+ // don't add startup commands if no CD key is present
+-#if ID_ENFORCE_KEY
+- if ( !session->CDKeysAreValid( false ) || !AddStartupCommands() ) {
+-#else
+ if ( !AddStartupCommands() ) {
+-#endif
+ // if the user didn't give any commands, run default action
+ session->StartMenu( true );
+ }
+diff -Naur dhewm3-89f227b365c2086dbe8818d82324f074a8ab4792.old/neo/framework/FileSystem.cpp dhewm3-89f227b365c2086dbe8818d82324f074a8ab4792/neo/framework/FileSystem.cpp
+--- dhewm3-89f227b365c2086dbe8818d82324f074a8ab4792.old/neo/framework/FileSystem.cpp 2016-01-23 10:16:16.943526865 +0100
++++ dhewm3-89f227b365c2086dbe8818d82324f074a8ab4792/neo/framework/FileSystem.cpp 2016-01-23 10:16:37.413788734 +0100
+@@ -2969,12 +2969,6 @@
+ return NULL;
+ }
+
+- // make sure the doomkey file is only readable by game at initialization
+- // any other time the key should only be accessed in memory using the provided functions
+- if( common->IsInitialized() && ( idStr::Icmp( relativePath, CDKEY_FILE ) == 0 || idStr::Icmp( relativePath, XPKEY_FILE ) == 0 ) ) {
+- return NULL;
+- }
+-
+ //
+ // search through the path, one element at a time
+ //
+diff -Naur dhewm3-89f227b365c2086dbe8818d82324f074a8ab4792.old/neo/framework/Licensee.h dhewm3-89f227b365c2086dbe8818d82324f074a8ab4792/neo/framework/Licensee.h
+--- dhewm3-89f227b365c2086dbe8818d82324f074a8ab4792.old/neo/framework/Licensee.h 2016-01-23 10:16:16.944526878 +0100
++++ dhewm3-89f227b365c2086dbe8818d82324f074a8ab4792/neo/framework/Licensee.h 2016-01-23 10:16:37.414788747 +0100
+@@ -92,12 +92,4 @@
+ // Linux info
+ #define LINUX_DEFAULT_PATH "/usr/local/games/doom3"
+
+-// CD Key file info
+-// goes into BASE_GAMEDIR whatever the fs_game is set to
+-// two distinct files for easier win32 installer job
+-#define CDKEY_FILE "doomkey"
+-#define XPKEY_FILE "xpkey"
+-#define CDKEY_TEXT "\n// Do not give this file to ANYONE.\n" \
+- "// id Software or Zenimax will NEVER ask you to send this file to them.\n"
+-
+ #define CONFIG_SPEC "config.spec"
+diff -Naur dhewm3-89f227b365c2086dbe8818d82324f074a8ab4792.old/neo/framework/Session.cpp dhewm3-89f227b365c2086dbe8818d82324f074a8ab4792/neo/framework/Session.cpp
+--- dhewm3-89f227b365c2086dbe8818d82324f074a8ab4792.old/neo/framework/Session.cpp 2016-01-23 10:16:16.944526878 +0100
++++ dhewm3-89f227b365c2086dbe8818d82324f074a8ab4792/neo/framework/Session.cpp 2016-01-23 10:16:37.415788760 +0100
+@@ -210,86 +210,6 @@
+ }
+
+ /*
+-===============
+-idSessionLocal::MaybeWaitOnCDKey
+-===============
+-*/
+-bool idSessionLocal::MaybeWaitOnCDKey( void ) {
+- if ( authEmitTimeout > 0 ) {
+- authWaitBox = true;
+- sessLocal.MessageBox( MSG_WAIT, common->GetLanguageDict()->GetString( "#str_07191" ), NULL, true, NULL, NULL, true );
+- return true;
+- }
+- return false;
+-}
+-
+-/*
+-===================
+-Session_PromptKey_f
+-===================
+-*/
+-static void Session_PromptKey_f( const idCmdArgs &args ) {
+- const char *retkey;
+- bool valid[ 2 ];
+- static bool recursed = false;
+-
+- if ( recursed ) {
+- common->Warning( "promptKey recursed - aborted" );
+- return;
+- }
+- recursed = true;
+-
+- do {
+- // in case we're already waiting for an auth to come back to us ( may happen exceptionally )
+- if ( sessLocal.MaybeWaitOnCDKey() ) {
+- if ( sessLocal.CDKeysAreValid( true ) ) {
+- recursed = false;
+- return;
+- }
+- }
+- // the auth server may have replied and set an error message, otherwise use a default
+- const char *prompt_msg = sessLocal.GetAuthMsg();
+- if ( prompt_msg[ 0 ] == '\0' ) {
+- prompt_msg = common->GetLanguageDict()->GetString( "#str_04308" );
+- }
+- retkey = sessLocal.MessageBox( MSG_CDKEY, prompt_msg, common->GetLanguageDict()->GetString( "#str_04305" ), true, NULL, NULL, true );
+- if ( retkey ) {
+- if ( sessLocal.CheckKey( retkey, false, valid ) ) {
+- // if all went right, then we may have sent an auth request to the master ( unless the prompt is used during a net connect )
+- bool canExit = true;
+- if ( sessLocal.MaybeWaitOnCDKey() ) {
+- // wait on auth reply, and got denied, prompt again
+- if ( !sessLocal.CDKeysAreValid( true ) ) {
+- // server says key is invalid - MaybeWaitOnCDKey was interrupted by a CDKeysAuthReply call, which has set the right error message
+- // the invalid keys have also been cleared in the process
+- sessLocal.MessageBox( MSG_OK, sessLocal.GetAuthMsg(), common->GetLanguageDict()->GetString( "#str_04310" ), true, NULL, NULL, true );
+- canExit = false;
+- }
+- }
+- if ( canExit ) {
+- // make sure that's saved on file
+- sessLocal.WriteCDKey();
+- sessLocal.MessageBox( MSG_OK, common->GetLanguageDict()->GetString( "#str_04307" ), common->GetLanguageDict()->GetString( "#str_04305" ), true, NULL, NULL, true );
+- break;
+- }
+- } else {
+- // offline check sees key invalid
+- // build a message about keys being wrong. do not attempt to change the current key state though
+- // ( the keys may be valid, but user would have clicked on the dialog anyway, that kind of thing )
+- idStr msg;
+- idAsyncNetwork::BuildInvalidKeyMsg( msg, valid );
+- sessLocal.MessageBox( MSG_OK, msg, common->GetLanguageDict()->GetString( "#str_04310" ), true, NULL, NULL, true );
+- }
+- } else if ( args.Argc() == 2 && idStr::Icmp( args.Argv(1), "force" ) == 0 ) {
+- // cancelled in force mode
+- cmdSystem->BufferCommandText( CMD_EXEC_APPEND, "quit\n" );
+- cmdSystem->ExecuteCommandBuffer();
+- }
+- } while ( retkey );
+- recursed = false;
+-}
+-
+-/*
+ ===============================================================================
+
+ SESSION LOCAL
+@@ -1151,23 +1071,6 @@
+ common->Printf( "Dedicated servers cannot start singleplayer games.\n" );
+ return;
+ #else
+-#if ID_ENFORCE_KEY
+- // strict check. don't let a game start without a definitive answer
+- if ( !CDKeysAreValid( true ) ) {
+- bool prompt = true;
+- if ( MaybeWaitOnCDKey() ) {
+- // check again, maybe we just needed more time
+- if ( CDKeysAreValid( true ) ) {
+- // can continue directly
+- prompt = false;
+- }
+- }
+- if ( prompt ) {
+- cmdSystem->BufferCommandText( CMD_EXEC_NOW, "promptKey force" );
+- cmdSystem->ExecuteCommandBuffer();
+- }
+- }
+-#endif
+ if ( idAsyncNetwork::server.IsActive() ) {
+ common->Printf("Server running, use si_map / serverMapRestart\n");
+ return;
+@@ -2601,16 +2504,9 @@
+ StopBox();
+ authWaitBox = false;
+ }
+- if ( cdkey_state == CDKEY_CHECKING ) {
+- cdkey_state = CDKEY_OK;
+- }
+- if ( xpkey_state == CDKEY_CHECKING ) {
+- xpkey_state = CDKEY_OK;
+- }
+ // maintain this empty as it's set by auth denials
+ authMsg.Empty();
+ authEmitTimeout = 0;
+- SetCDKeyGuiVars();
+ }
+ }
+
+@@ -2869,8 +2765,6 @@
+
+ cmdSystem->AddCommand( "rescanSI", Session_RescanSI_f, CMD_FL_SYSTEM, "internal - rescan serverinfo cvars and tell game" );
+
+- cmdSystem->AddCommand( "promptKey", Session_PromptKey_f, CMD_FL_SYSTEM, "prompt and sets the CD Key" );
+-
+ cmdSystem->AddCommand( "hitch", Session_Hitch_f, CMD_FL_SYSTEM|CMD_FL_CHEAT, "hitches the game" );
+
+ // the same idRenderWorld will be used for all games
+@@ -2899,8 +2793,6 @@
+
+ guiActive = NULL;
+ guiHandle = NULL;
+-
+- ReadCDKey();
+ }
+
+ /*
+@@ -2950,335 +2842,6 @@
+ }
+
+ /*
+-=================
+-idSessionLocal::ReadCDKey
+-=================
+-*/
+-void idSessionLocal::ReadCDKey( void ) {
+- idStr filename;
+- idFile *f;
+- char buffer[32];
+-
+- cdkey_state = CDKEY_UNKNOWN;
+-
+- filename = "../" BASE_GAMEDIR "/" CDKEY_FILE;
+- f = fileSystem->OpenExplicitFileRead( fileSystem->RelativePathToOSPath( filename, "fs_configpath" ) );
+-
+- // try the install path, which is where the cd installer and steam put it
+- if ( !f )
+- f = fileSystem->OpenExplicitFileRead( fileSystem->RelativePathToOSPath( filename, "fs_basepath" ) );
+-
+- if ( !f ) {
+- common->Printf( "Couldn't read %s.\n", filename.c_str() );
+- cdkey[ 0 ] = '\0';
+- } else {
+- memset( buffer, 0, sizeof(buffer) );
+- f->Read( buffer, CDKEY_BUF_LEN - 1 );
+- fileSystem->CloseFile( f );
+- idStr::Copynz( cdkey, buffer, CDKEY_BUF_LEN );
+- }
+-
+- xpkey_state = CDKEY_UNKNOWN;
+-
+- filename = "../" BASE_GAMEDIR "/" XPKEY_FILE;
+- f = fileSystem->OpenExplicitFileRead( fileSystem->RelativePathToOSPath( filename, "fs_configpath" ) );
+-
+- // try the install path, which is where the cd installer and steam put it
+- if ( !f )
+- f = fileSystem->OpenExplicitFileRead( fileSystem->RelativePathToOSPath( filename, "fs_basepath" ) );
+-
+- if ( !f ) {
+- common->Printf( "Couldn't read %s.\n", filename.c_str() );
+- xpkey[ 0 ] = '\0';
+- } else {
+- memset( buffer, 0, sizeof(buffer) );
+- f->Read( buffer, CDKEY_BUF_LEN - 1 );
+- fileSystem->CloseFile( f );
+- idStr::Copynz( xpkey, buffer, CDKEY_BUF_LEN );
+- }
+-}
+-
+-/*
+-================
+-idSessionLocal::WriteCDKey
+-================
+-*/
+-void idSessionLocal::WriteCDKey( void ) {
+- idStr filename;
+- idFile *f;
+- const char *OSPath;
+-
+- filename = "../" BASE_GAMEDIR "/" CDKEY_FILE;
+- // OpenFileWrite advertises creating directories to the path if needed, but that won't work with a '..' in the path
+- // occasionally on windows, but mostly on Linux and OSX, the fs_configpath/base may not exist in full
+- OSPath = fileSystem->BuildOSPath( cvarSystem->GetCVarString( "fs_configpath" ), BASE_GAMEDIR, CDKEY_FILE );
+- fileSystem->CreateOSPath( OSPath );
+- f = fileSystem->OpenFileWrite( filename, "fs_configpath" );
+- if ( !f ) {
+- common->Printf( "Couldn't write %s.\n", filename.c_str() );
+- return;
+- }
+- f->Printf( "%s%s", cdkey, CDKEY_TEXT );
+- fileSystem->CloseFile( f );
+-
+- filename = "../" BASE_GAMEDIR "/" XPKEY_FILE;
+- f = fileSystem->OpenFileWrite( filename, "fs_configpath" );
+- if ( !f ) {
+- common->Printf( "Couldn't write %s.\n", filename.c_str() );
+- return;
+- }
+- f->Printf( "%s%s", xpkey, CDKEY_TEXT );
+- fileSystem->CloseFile( f );
+-}
+-
+-/*
+-===============
+-idSessionLocal::ClearKey
+-===============
+-*/
+-void idSessionLocal::ClearCDKey( bool valid[ 2 ] ) {
+- if ( !valid[ 0 ] ) {
+- memset( cdkey, 0, CDKEY_BUF_LEN );
+- cdkey_state = CDKEY_UNKNOWN;
+- } else if ( cdkey_state == CDKEY_CHECKING ) {
+- // if a key was in checking and not explicitely asked for clearing, put it back to ok
+- cdkey_state = CDKEY_OK;
+- }
+- if ( !valid[ 1 ] ) {
+- memset( xpkey, 0, CDKEY_BUF_LEN );
+- xpkey_state = CDKEY_UNKNOWN;
+- } else if ( xpkey_state == CDKEY_CHECKING ) {
+- xpkey_state = CDKEY_OK;
+- }
+- WriteCDKey( );
+-}
+-
+-/*
+-================
+-idSessionLocal::GetCDKey
+-================
+-*/
+-const char *idSessionLocal::GetCDKey( bool xp ) {
+- if ( !xp ) {
+- return cdkey;
+- }
+- if ( xpkey_state == CDKEY_OK || xpkey_state == CDKEY_CHECKING ) {
+- return xpkey;
+- }
+- return NULL;
+-}
+-
+-// digits to letters table
+-#define CDKEY_DIGITS "TWSBJCGD7PA23RLH"
+-
+-/*
+-===============
+-idSessionLocal::EmitGameAuth
+-we toggled some key state to CDKEY_CHECKING. send a standalone auth packet to validate
+-===============
+-*/
+-void idSessionLocal::EmitGameAuth( void ) {
+- // make sure the auth reply is empty, we use it to indicate an auth reply
+- authMsg.Empty();
+- if ( idAsyncNetwork::client.SendAuthCheck( cdkey_state == CDKEY_CHECKING ? cdkey : NULL, xpkey_state == CDKEY_CHECKING ? xpkey : NULL ) ) {
+- authEmitTimeout = Sys_Milliseconds() + CDKEY_AUTH_TIMEOUT;
+- common->DPrintf( "authing with the master..\n" );
+- } else {
+- // net is not available
+- common->DPrintf( "sendAuthCheck failed\n" );
+- if ( cdkey_state == CDKEY_CHECKING ) {
+- cdkey_state = CDKEY_OK;
+- }
+- if ( xpkey_state == CDKEY_CHECKING ) {
+- xpkey_state = CDKEY_OK;
+- }
+- }
+-}
+-
+-/*
+-================
+-idSessionLocal::CheckKey
+-the function will only modify keys to _OK or _CHECKING if the offline checks are passed
+-if the function returns false, the offline checks failed, and offline_valid holds which keys are bad
+-================
+-*/
+-bool idSessionLocal::CheckKey( const char *key, bool netConnect, bool offline_valid[ 2 ] ) {
+- char lkey[ 2 ][ CDKEY_BUF_LEN ];
+- char l_chk[ 2 ][ 3 ];
+- char s_chk[ 3 ];
+- int imax,i_key;
+- unsigned int checksum, chk8;
+- bool edited_key[ 2 ];
+-
+- // make sure have a right input string
+- assert( strlen( key ) == ( CDKEY_BUF_LEN - 1 ) * 2 + 4 + 3 + 4 );
+-
+- edited_key[ 0 ] = ( key[0] == '1' );
+- idStr::Copynz( lkey[0], key + 2, CDKEY_BUF_LEN );
+- idStr::ToUpper( lkey[0] );
+- idStr::Copynz( l_chk[0], key + CDKEY_BUF_LEN + 2, 3 );
+- idStr::ToUpper( l_chk[0] );
+- edited_key[ 1 ] = ( key[ CDKEY_BUF_LEN + 2 + 3 ] == '1' );
+- idStr::Copynz( lkey[1], key + CDKEY_BUF_LEN + 7, CDKEY_BUF_LEN );
+- idStr::ToUpper( lkey[1] );
+- idStr::Copynz( l_chk[1], key + CDKEY_BUF_LEN * 2 + 7, 3 );
+- idStr::ToUpper( l_chk[1] );
+-
+- if ( fileSystem->HasD3XP() ) {
+- imax = 2;
+- } else {
+- imax = 1;
+- }
+- offline_valid[ 0 ] = offline_valid[ 1 ] = true;
+- for( i_key = 0; i_key < imax; i_key++ ) {
+- // check that the characters are from the valid set
+- int i;
+- for ( i = 0; i < CDKEY_BUF_LEN - 1; i++ ) {
+- if ( !strchr( CDKEY_DIGITS, lkey[i_key][i] ) ) {
+- offline_valid[ i_key ] = false;
+- continue;
+- }
+- }
+-
+- if ( edited_key[ i_key ] ) {
+- // verify the checksum for edited keys only
+- checksum = CRC32_BlockChecksum( lkey[i_key], CDKEY_BUF_LEN - 1 );
+- chk8 = ( checksum & 0xff ) ^ ( ( ( checksum & 0xff00 ) >> 8 ) ^ ( ( ( checksum & 0xff0000 ) >> 16 ) ^ ( ( checksum & 0xff000000 ) >> 24 ) ) );
+- idStr::snPrintf( s_chk, 3, "%02X", chk8 );
+- if ( idStr::Icmp( l_chk[i_key], s_chk ) != 0 ) {
+- offline_valid[ i_key ] = false;
+- continue;
+- }
+- }
+- }
+-
+- if ( !offline_valid[ 0 ] || !offline_valid[1] ) {
+- return false;
+- }
+-
+- // offline checks passed, we'll return true and optionally emit key check requests
+- // the function should only modify the key states if the offline checks passed successfully
+-
+- // set the keys, don't send a game auth if we are net connecting
+- idStr::Copynz( cdkey, lkey[0], CDKEY_BUF_LEN );
+- netConnect ? cdkey_state = CDKEY_OK : cdkey_state = CDKEY_CHECKING;
+- if ( fileSystem->HasD3XP() ) {
+- idStr::Copynz( xpkey, lkey[1], CDKEY_BUF_LEN );
+- netConnect ? xpkey_state = CDKEY_OK : xpkey_state = CDKEY_CHECKING;
+- } else {
+- xpkey_state = CDKEY_NA;
+- }
+- if ( !netConnect ) {
+- EmitGameAuth();
+- }
+- SetCDKeyGuiVars();
+-
+- return true;
+-}
+-
+-/*
+-===============
+-idSessionLocal::CDKeysAreValid
+-checking that the key is present and uses only valid characters
+-if d3xp is installed, check for a valid xpkey as well
+-emit an auth packet to the master if possible and needed
+-===============
+-*/
+-bool idSessionLocal::CDKeysAreValid( bool strict ) {
+- int i;
+- bool emitAuth = false;
+-
+- if ( cdkey_state == CDKEY_UNKNOWN ) {
+- if ( strlen( cdkey ) != CDKEY_BUF_LEN - 1 ) {
+- cdkey_state = CDKEY_INVALID;
+- } else {
+- for ( i = 0; i < CDKEY_BUF_LEN-1; i++ ) {
+- if ( !strchr( CDKEY_DIGITS, cdkey[i] ) ) {
+- cdkey_state = CDKEY_INVALID;
+- break;
+- }
+- }
+- }
+- if ( cdkey_state == CDKEY_UNKNOWN ) {
+- cdkey_state = CDKEY_CHECKING;
+- emitAuth = true;
+- }
+- }
+- if ( xpkey_state == CDKEY_UNKNOWN ) {
+- if ( fileSystem->HasD3XP() ) {
+- if ( strlen( xpkey ) != CDKEY_BUF_LEN -1 ) {
+- xpkey_state = CDKEY_INVALID;
+- } else {
+- for ( i = 0; i < CDKEY_BUF_LEN-1; i++ ) {
+- if ( !strchr( CDKEY_DIGITS, xpkey[i] ) ) {
+- xpkey_state = CDKEY_INVALID;
+- }
+- }
+- }
+- if ( xpkey_state == CDKEY_UNKNOWN ) {
+- xpkey_state = CDKEY_CHECKING;
+- emitAuth = true;
+- }
+- } else {
+- xpkey_state = CDKEY_NA;
+- }
+- }
+- if ( emitAuth ) {
+- EmitGameAuth();
+- }
+- // make sure to keep the mainmenu gui up to date in case we made state changes
+- SetCDKeyGuiVars();
+- if ( strict ) {
+- return cdkey_state == CDKEY_OK && ( xpkey_state == CDKEY_OK || xpkey_state == CDKEY_NA );
+- } else {
+- return ( cdkey_state == CDKEY_OK || cdkey_state == CDKEY_CHECKING ) && ( xpkey_state == CDKEY_OK || xpkey_state == CDKEY_CHECKING || xpkey_state == CDKEY_NA );
+- }
+-}
+-
+-/*
+-===============
+-idSessionLocal::WaitingForGameAuth
+-===============
+-*/
+-bool idSessionLocal::WaitingForGameAuth( void ) {
+- return authEmitTimeout != 0;
+-}
+-
+-/*
+-===============
+-idSessionLocal::CDKeysAuthReply
+-===============
+-*/
+-void idSessionLocal::CDKeysAuthReply( bool valid, const char *auth_msg ) {
+- //assert( authEmitTimeout > 0 );
+- if ( authWaitBox ) {
+- // close the wait box
+- StopBox();
+- authWaitBox = false;
+- }
+- if ( !valid ) {
+- common->DPrintf( "auth key is invalid\n" );
+- authMsg = auth_msg;
+- if ( cdkey_state == CDKEY_CHECKING ) {
+- cdkey_state = CDKEY_INVALID;
+- }
+- if ( xpkey_state == CDKEY_CHECKING ) {
+- xpkey_state = CDKEY_INVALID;
+- }
+- } else {
+- common->DPrintf( "client is authed in\n" );
+- if ( cdkey_state == CDKEY_CHECKING ) {
+- cdkey_state = CDKEY_OK;
+- }
+- if ( xpkey_state == CDKEY_CHECKING ) {
+- xpkey_state = CDKEY_OK;
+- }
+- }
+- authEmitTimeout = 0;
+- SetCDKeyGuiVars();
+-}
+-
+-/*
+ ===============
+ idSessionLocal::GetCurrentMapName
+ ===============
+diff -Naur dhewm3-89f227b365c2086dbe8818d82324f074a8ab4792.old/neo/framework/Session.h dhewm3-89f227b365c2086dbe8818d82324f074a8ab4792/neo/framework/Session.h
+--- dhewm3-89f227b365c2086dbe8818d82324f074a8ab4792.old/neo/framework/Session.h 2016-01-23 10:16:16.944526878 +0100
++++ dhewm3-89f227b365c2086dbe8818d82324f074a8ab4792/neo/framework/Session.h 2016-01-23 10:16:39.892820448 +0100
@@ -60,7 +60,6 @@
MSG_OKCANCEL,
MSG_YESNO,
@@ -13815,9 +792,9 @@ diff -Naur dhewm3.old/neo/framework/Session.h dhewm3/neo/framework/Session.h
virtual const char *GetCurrentMapName( void ) = 0;
virtual int GetSaveGameVersion( void ) = 0;
-diff -Naur dhewm3.old/neo/framework/Session_local.h dhewm3/neo/framework/Session_local.h
---- dhewm3.old/neo/framework/Session_local.h 2013-08-15 23:27:20.000000000 +0200
-+++ dhewm3/neo/framework/Session_local.h 2013-08-22 13:23:33.004219515 +0200
+diff -Naur dhewm3-89f227b365c2086dbe8818d82324f074a8ab4792.old/neo/framework/Session_local.h dhewm3-89f227b365c2086dbe8818d82324f074a8ab4792/neo/framework/Session_local.h
+--- dhewm3-89f227b365c2086dbe8818d82324f074a8ab4792.old/neo/framework/Session_local.h 2016-01-23 10:16:16.944526878 +0100
++++ dhewm3-89f227b365c2086dbe8818d82324f074a8ab4792/neo/framework/Session_local.h 2016-01-23 10:16:39.893820460 +0100
@@ -124,16 +124,6 @@
virtual void TimeHitch( int msec );
@@ -13867,9 +844,9 @@ diff -Naur dhewm3.old/neo/framework/Session_local.h dhewm3/neo/framework/Session
int authEmitTimeout;
bool authWaitBox;
-diff -Naur dhewm3.old/neo/framework/Session_menu.cpp dhewm3/neo/framework/Session_menu.cpp
---- dhewm3.old/neo/framework/Session_menu.cpp 2013-08-15 23:27:20.000000000 +0200
-+++ dhewm3/neo/framework/Session_menu.cpp 2013-08-22 13:23:33.004219515 +0200
+diff -Naur dhewm3-89f227b365c2086dbe8818d82324f074a8ab4792.old/neo/framework/Session_menu.cpp dhewm3-89f227b365c2086dbe8818d82324f074a8ab4792/neo/framework/Session_menu.cpp
+--- dhewm3-89f227b365c2086dbe8818d82324f074a8ab4792.old/neo/framework/Session_menu.cpp 2016-01-23 10:16:16.944526878 +0100
++++ dhewm3-89f227b365c2086dbe8818d82324f074a8ab4792/neo/framework/Session_menu.cpp 2016-01-23 10:16:39.893820460 +0100
@@ -313,7 +313,6 @@
guiMainMenu->SetStateString( "inGame", "0" );
}
diff --git a/dhewm3.spec b/dhewm3.spec
index f90e942..6136661 100644
--- a/dhewm3.spec
+++ b/dhewm3.spec
@@ -1,21 +1,20 @@
-%global commit 657ad99bf185feb71199c6af097577d037e59d59
-%global shortcommit %(c=%{commit}; echo ${c:0:7})
+%global commit0 89f227b365c2086dbe8818d82324f074a8ab4792
+%global shortcommit0 %(c=%{commit0}; echo ${c:0:7})
Name: dhewm3
-Version: 1.3.1.1304
-Release: 23%{?shortcommit:.git.%{shortcommit}}%{?dist}
+Version: 1.4.1rc1
+Release: 1.%{?shortcommit0}%{?dist}
Summary: Dhewm's Doom 3 engine
-License: GPLv3+ and BSD
+License: GPLv3+ with exceptions
URL: https://github.com/dhewm/%{name}
-Source0: https://github.com/dhewm/%{name}/archive/%{commit}/%{name}-%{version}-%{s...
+Source0: https://github.com/dhewm/%{name}/archive/%{commit0}.tar.gz#/%{name}-%{sho...
Source1: %{name}-README.txt
# Compatibility with stock Doom 3 has been removed long ago and we don't ship
# Doom 3 / Doom 3 Resurrection of Evil content.
Patch0: %{name}-no-cdkey.patch
-Patch1: %{name}-def-eax-on.patch
-Patch2: %{name}-def-fixedtic.patch
-Patch3: %{name}-carmack.patch
+Patch1: %{name}-def-fixedtic.patch
+Patch2: %{name}-carmack.patch
# Generic provider for Doom 3 engine based games
Provides: doom3-engine = 1.3.1.1304
@@ -28,11 +27,7 @@ BuildRequires: libjpeg-turbo-devel
BuildRequires: libogg-devel
BuildRequires: libvorbis-devel
BuildRequires: openal-soft-devel
-%if 0%{?fedora} >= 19 || 0%{?rhel} >= 7
BuildRequires: SDL2-devel
-%else
-BuildRequires: SDL-devel
-%endif
BuildRequires: speex-devel
BuildRequires: zlib-devel
@@ -43,11 +38,10 @@ original DOOM 3 will be fixed (when identified) without altering the original
game-play.
%prep
-%setup -qn %{name}-%{commit}
+%setup -qn %{name}-%{commit0}
%patch0 -p1
%patch1 -p1
%patch2 -p1
-%patch3 -p1
cp %{SOURCE1} ./README.txt
iconv -f iso8859-1 -t utf-8 COPYING.txt > COPYING.txt.conv && mv -f COPYING.txt.conv COPYING.txt
@@ -58,11 +52,8 @@ iconv -f iso8859-1 -t utf-8 COPYING.txt > COPYING.txt.conv && mv -f COPYING.txt.
-DCMAKE_BUILD_TYPE=Fedora \
-DDEDICATED=ON \
-DZFAIL=1 \
-%if 0%{?fedora} >= 19 || 0%{?rhel} >= 7
- -DSDL2=ON \
-%endif
neo
-make #_smp_mflags}
+make %{?_smp_mflags}
%post
/usr/sbin/alternatives --install %{_bindir}/doom3-engine doom3-engine %{_bindir}/%{name} 10
@@ -73,15 +64,27 @@ if [ "$1" = 0 ]; then
fi
%install
-make install DESTDIR=%{buildroot}
+%make_install
%files
-%doc README.md COPYING.txt README.txt
+%{!?_licensedir:%global license %%doc}
+%license COPYING.txt
+%doc README.md README.txt
%{_bindir}/%{name}
%{_bindir}/%{name}ded
%{_libdir}/%{name}
%changelog
+* Sat Jan 23 2016 Simone Caronni <negativo17(a)gmail.com> - 1.4.1rc1-1.89f227b
+- Update to latest 1.4.1rc1.
+- Drop RHEL 6 support, provided libjpeg is too old and would need to have a
+ bundled jpeg_memory_src() function:
+ https://github.com/dhewm/dhewm3/commit/657ad99bf185feb71199c6af097577d037...
+- Fix Fedora README file (it contained references to RBDoom3BFG...).
+- Update License field as specified in the README.md file.
+- Update Source URL as per packaging guidelines.
+- Add license macro.
+
* Mon Mar 30 2015 Simone Caronni <negativo17(a)gmail.com> - 1.3.1.1304-23.git.657ad99
- Update to latest commits.
diff --git a/sources b/sources
index fe3b5cf..fdef7f5 100644
--- a/sources
+++ b/sources
@@ -1 +1 @@
-8ea722f34a346305144f36006d16e3a5 dhewm3-1.3.1.1304-657ad99.tar.gz
+0a477d45cbd04f97ad9f0cbae8626627 dhewm3-89f227b.tar.gz
7 years, 7 months
[vlc] Update snapshot and rework library split
by Nicolas Chauvet
commit 9202f765b6406667913c8a434e4bbf8316c705ad
Author: Nicolas Chauvet <kwizart(a)gmail.com>
Date: Wed Apr 5 14:00:40 2017 +0200
Update snapshot and rework library split
sources | 2 +-
vlc.spec | 35 ++++++++++++++++++++++++++++-------
2 files changed, 29 insertions(+), 8 deletions(-)
---
diff --git a/sources b/sources
index 01bcf11..006a9ae 100644
--- a/sources
+++ b/sources
@@ -1 +1 @@
-3e19b48d356f2dc8ba2644d82d639587 vlc-3.0.0-20170316-0243-git.tar.xz
+501dbc54f8f78e8882afb58065f93855 vlc-3.0.0-20170405-0244-git.tar.xz
diff --git a/vlc.spec b/vlc.spec
index a79ccb8..4686934 100644
--- a/vlc.spec
+++ b/vlc.spec
@@ -1,4 +1,4 @@
-%global vlc_rc -20170316-0243-git
+%global vlc_rc -20170405-0244-git
%if 0%{?vlc_rc:1}
%global vlc_url http://nightlies.videolan.org/build/source/
%else
@@ -38,7 +38,7 @@
Summary: The cross-platform open-source multimedia framework, player and server
Name: vlc
Version: 3.0.0
-Release: 0.20%{?dist}
+Release: 0.21%{?dist}
License: GPLv2+
Group: Applications/Multimedia
URL: http://www.videolan.org
@@ -417,6 +417,7 @@ fi || :
%{_libdir}/vlc/plugins/video_output/libaa_plugin.so
%{_libdir}/vlc/plugins/video_output/libcaca_plugin.so
%{?_with_wayland:
+%{_libdir}/vlc/plugins/access/libwl_screenshooter_plugin.so
%{_libdir}/vlc/plugins/video_output/libegl_wl_plugin.so
%{_libdir}/vlc/plugins/video_output/libwl_shell_plugin.so
%{_libdir}/vlc/plugins/video_output/libwl_shm_plugin.so
@@ -430,17 +431,27 @@ fi || :
%{_libdir}/vlc/plugins/video_output/libxcb_x11_plugin.so
%{_libdir}/vlc/plugins/video_output/libxcb_window_plugin.so
%{_libdir}/vlc/plugins/video_output/libxcb_xv_plugin.so
+%{_libdir}/vlc/plugins/control/libxcb_hotkeys_plugin.so
+%{_libdir}/vlc/plugins/services_discovery/libxcb_apps_plugin.so
}
%{_libdir}/vlc/plugins/gui/libskins2_plugin.so
%{?_with_projectm:
%{_libdir}/vlc/plugins/visualization/libprojectm_plugin.so
}
+#jack in main
%{_libdir}/vlc/plugins/access/libaccess_jack_plugin.so
-%{_libdir}/vlc/plugins/audio_output/libpulse_plugin.so
%{_libdir}/vlc/plugins/audio_output/libjack_plugin.so
+#pulseaudio in main
+%{_libdir}/vlc/plugins/audio_output/libpulse_plugin.so
+%{_libdir}/vlc/plugins/access/libpulsesrc_plugin.so
+%{_libdir}/vlc/plugins/services_discovery/libpulselist_plugin.so
%{?_with_fluidsynth:
%{_libdir}/vlc/plugins/codec/libfluidsynth_plugin.so
}
+#vdpau in main
+%dir %{_libdir}/vlc/plugins/vdpau
+%{_libdir}/vlc/plugins/vdpau/libvdpau_*_plugin.so
+
%files core -f %{name}.lang
%{_bindir}/vlc
@@ -472,20 +483,23 @@ fi || :
%if 0%{?fedora} < 17
%exclude %{_libdir}/vlc/plugins/control/libglobalhotkeys_plugin.so
%endif
+%exclude %{_libdir}/vlc/plugins/control/libxcb_hotkeys_plugin.so
+%exclude %{_libdir}/vlc/plugins/services_discovery/libxcb_apps_plugin.so
%exclude %{_libdir}/vlc/plugins/video_output/libaa_plugin.so
%exclude %{_libdir}/vlc/plugins/video_output/libcaca_plugin.so
%exclude %{_libdir}/vlc/plugins/video_output/libegl_x11_plugin.so
%exclude %{_libdir}/vlc/plugins/video_output/libgl_plugin.so
%exclude %{_libdir}/vlc/plugins/video_output/libglx_plugin.so
+%exclude %{_libdir}/vlc/plugins/video_output/libxcb_x11_plugin.so
+%exclude %{_libdir}/vlc/plugins/video_output/libxcb_window_plugin.so
+%exclude %{_libdir}/vlc/plugins/video_output/libxcb_xv_plugin.so
+}
%{?_with_wayland:
+%exclude %{_libdir}/vlc/plugins/access/libwl_screenshooter_plugin.so
%exclude %{_libdir}/vlc/plugins/video_output/libegl_wl_plugin.so
%exclude %{_libdir}/vlc/plugins/video_output/libwl_shell_plugin.so
%exclude %{_libdir}/vlc/plugins/video_output/libwl_shm_plugin.so
}
-%exclude %{_libdir}/vlc/plugins/video_output/libxcb_x11_plugin.so
-%exclude %{_libdir}/vlc/plugins/video_output/libxcb_window_plugin.so
-%exclude %{_libdir}/vlc/plugins/video_output/libxcb_xv_plugin.so
-}
%exclude %{_libdir}/vlc/plugins/gui/libskins2_plugin.so
%{?_with_opencv:
%exclude %{_libdir}/vlc/plugins/video_filter/libopencv_example_plugin.so
@@ -496,6 +510,9 @@ fi || :
}
%exclude %{_libdir}/vlc/plugins/audio_output/libjack_plugin.so
%exclude %{_libdir}/vlc/plugins/audio_output/libpulse_plugin.so
+%exclude %{_libdir}/vlc/plugins/access/libpulsesrc_plugin.so
+%exclude %{_libdir}/vlc/plugins/services_discovery/libpulselist_plugin.so
+%exclude %{_libdir}/vlc/plugins/vdpau
%ghost %{_libdir}/vlc/plugins/plugins.dat
%dir %{_libdir}/vlc/
%{_libdir}/vlc/vlc-cache-gen
@@ -528,6 +545,10 @@ fi || :
%changelog
+* Wed Apr 05 2017 Nicolas Chauvet <kwizart(a)gmail.com> - 3.0.0-0.21
+- Update to 20170405 snapshoot
+- Rework main -core library split
+
* Mon Mar 20 2017 RPM Fusion Release Engineering <kwizart(a)rpmfusion.org> - 3.0.0-0.20
- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild
7 years, 7 months
[freetype-freeworld/f25] Add freetype-2.6.5-linear-scaling.patch from Fedora freetype (rh#1437999)
by Kevin Kofler
commit c3df79bf0030a8afcded1ef0a94880b3c5861c45
Author: Kevin Kofler <kevin.kofler(a)chello.at>
Date: Wed Apr 5 02:20:29 2017 +0200
Add freetype-2.6.5-linear-scaling.patch from Fedora freetype (rh#1437999)
* Wed Apr 05 2017 Kevin Kofler <Kevin(a)tigcc.ticalc.org> 2.6.5-3
- Add freetype-2.6.5-linear-scaling.patch from Fedora freetype (rh#1437999)
freetype-2.6.5-linear-scaling.patch | 71 +++++++++++++++++++++++++++++++++++++
freetype-freeworld.spec | 11 +++++-
2 files changed, 81 insertions(+), 1 deletion(-)
---
diff --git a/freetype-2.6.5-linear-scaling.patch b/freetype-2.6.5-linear-scaling.patch
new file mode 100644
index 0000000..57a30a1
--- /dev/null
+++ b/freetype-2.6.5-linear-scaling.patch
@@ -0,0 +1,71 @@
+From bcc74f4dafee25ea89f1d3144646cba7e30f9908 Mon Sep 17 00:00:00 2001
+From: Werner Lemberg <wl(a)gnu.org>
+Date: Thu, 30 Mar 2017 13:14:43 +0200
+Subject: [truetype] Allow linear scaling for unhinted rendering (#50470).
+
+* src/truetype/ttdriver.c (tt_size_request): Revert change from
+2011-07-16; the intended metrics fix seems now to be implemented in
+a different way, making the patch unnecessary. Note that this
+change was usually patched out by all major GNU/Linux distributions
+due to heavy side effects.
+
+* src/truetype/ttgload.c (compute_glyph_metrics, TT_Load_Glyph):
+Refer to the metrics of the `TT_Size' object.
+
+diff --git a/src/truetype/ttdriver.c b/src/truetype/ttdriver.c
+index 4ec78e2f..9bf0f84f 100644
+--- a/src/truetype/ttdriver.c
++++ b/src/truetype/ttdriver.c
+@@ -316,7 +316,6 @@
+ if ( FT_IS_SCALABLE( size->face ) )
+ {
+ error = tt_size_reset( ttsize );
+- ttsize->root.metrics = ttsize->metrics;
+ }
+
+ return error;
+diff --git a/src/truetype/ttgload.c b/src/truetype/ttgload.c
+index 35b62d0e..b225190a 100644
+--- a/src/truetype/ttgload.c
++++ b/src/truetype/ttgload.c
+@@ -2002,7 +2002,7 @@
+
+ y_scale = 0x10000L;
+ if ( ( loader->load_flags & FT_LOAD_NO_SCALE ) == 0 )
+- y_scale = size->root.metrics.y_scale;
++ y_scale = size->metrics.y_scale;
+
+ if ( glyph->format != FT_GLYPH_FORMAT_COMPOSITE )
+ FT_Outline_Get_CBox( &glyph->outline, &bbox );
+@@ -2034,7 +2034,7 @@
+
+
+ widthp = tt_face_get_device_metrics( face,
+- size->root.metrics.x_ppem,
++ size->metrics.x_ppem,
+ glyph_index );
+
+ #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY
+@@ -2623,11 +2623,11 @@
+ if ( !glyph->metrics.horiAdvance && glyph->linearHoriAdvance )
+ glyph->metrics.horiAdvance =
+ FT_MulFix( glyph->linearHoriAdvance,
+- size->root.metrics.x_scale );
++ size->metrics.x_scale );
+ if ( !glyph->metrics.vertAdvance && glyph->linearVertAdvance )
+ glyph->metrics.vertAdvance =
+ FT_MulFix( glyph->linearVertAdvance,
+- size->root.metrics.y_scale );
++ size->metrics.y_scale );
+ }
+
+ return FT_Err_Ok;
+@@ -2717,7 +2717,7 @@
+ /* TrueType glyphs at all sizes using the bytecode interpreter. */
+ /* */
+ if ( !( load_flags & FT_LOAD_NO_SCALE ) &&
+- size->root.metrics.y_ppem < 24 )
++ size->metrics.y_ppem < 24 )
+ glyph->outline.flags |= FT_OUTLINE_HIGH_PRECISION;
+
+ return error;
diff --git a/freetype-freeworld.spec b/freetype-freeworld.spec
index 1bed447..298ac56 100644
--- a/freetype-freeworld.spec
+++ b/freetype-freeworld.spec
@@ -1,7 +1,7 @@
Summary: A free and portable font rendering engine
Name: freetype-freeworld
Version: 2.6.5
-Release: 2%{?dist}
+Release: 3%{?dist}
License: (FTL or GPLv2+) and BSD and MIT and Public Domain and zlib with acknowledgement
URL: http://www.freetype.org
Source: http://download.savannah.gnu.org/releases/freetype/freetype-%{version}.ta...
@@ -11,6 +11,10 @@ Patch21: freetype-2.3.0-enable-spr.patch
# Enable otvalid and gxvalid modules
Patch46: freetype-2.2.1-enable-valid.patch
+# Allow linear scaling for unhinted rendering
+# https://bugzilla.redhat.com/show_bug.cgi?id=1437999
+Patch50: freetype-2.6.5-linear-scaling.patch
+
## Security fixes:
# https://bugzilla.redhat.com/show_bug.cgi?id=1429965
Patch94: freetype-2.6.5-heap-buffer-overflow.patch
@@ -43,6 +47,8 @@ It transparently overrides the system library using ld.so.conf.d.
%patch46 -p1 -b .enable-valid
+%patch50 -p1 -b .linear-scaling
+
%patch94 -p1 -b .heap-buffer-overflow
@@ -88,6 +94,9 @@ echo "%{_libdir}/%{name}" \
%config(noreplace) %{_sysconfdir}/ld.so.conf.d/%{name}-%{_arch}.conf
%changelog
+* Wed Apr 05 2017 Kevin Kofler <Kevin(a)tigcc.ticalc.org> 2.6.5-3
+- Add freetype-2.6.5-linear-scaling.patch from Fedora freetype (rh#1437999)
+
* Sat Mar 11 2017 Kevin Kofler <Kevin(a)tigcc.ticalc.org> 2.6.5-2
- Add CVE-2016-10244 (heap buffer overflow, rh#1429965) fix from Fedora freetype
7 years, 7 months
[freetype-freeworld] Add freetype-2.7.1-linear-scaling.patch from Fedora freetype (rh#1437999)
by Kevin Kofler
commit 4d19ac463f0e5e0ba2ef0ce81e9bdff5076afd33
Author: Kevin Kofler <kevin.kofler(a)chello.at>
Date: Wed Apr 5 02:14:40 2017 +0200
Add freetype-2.7.1-linear-scaling.patch from Fedora freetype (rh#1437999)
* Wed Apr 05 2017 Kevin Kofler <Kevin(a)tigcc.ticalc.org> 2.7.1-3
- Add freetype-2.7.1-linear-scaling.patch from Fedora freetype (rh#1437999)
freetype-2.7.1-linear-scaling.patch | 71 +++++++++++++++++++++++++++++++++++++
freetype-freeworld.spec | 11 +++++-
2 files changed, 81 insertions(+), 1 deletion(-)
---
diff --git a/freetype-2.7.1-linear-scaling.patch b/freetype-2.7.1-linear-scaling.patch
new file mode 100644
index 0000000..b04c0a1
--- /dev/null
+++ b/freetype-2.7.1-linear-scaling.patch
@@ -0,0 +1,71 @@
+From bcc74f4dafee25ea89f1d3144646cba7e30f9908 Mon Sep 17 00:00:00 2001
+From: Werner Lemberg <wl(a)gnu.org>
+Date: Thu, 30 Mar 2017 13:14:43 +0200
+Subject: [truetype] Allow linear scaling for unhinted rendering (#50470).
+
+* src/truetype/ttdriver.c (tt_size_request): Revert change from
+2011-07-16; the intended metrics fix seems now to be implemented in
+a different way, making the patch unnecessary. Note that this
+change was usually patched out by all major GNU/Linux distributions
+due to heavy side effects.
+
+* src/truetype/ttgload.c (compute_glyph_metrics, TT_Load_Glyph):
+Refer to the metrics of the `TT_Size' object.
+
+diff --git a/src/truetype/ttdriver.c b/src/truetype/ttdriver.c
+index 4ec78e2f..9bf0f84f 100644
+--- a/src/truetype/ttdriver.c
++++ b/src/truetype/ttdriver.c
+@@ -355,7 +355,6 @@
+ if ( FT_IS_SCALABLE( size->face ) )
+ {
+ error = tt_size_reset( ttsize );
+- ttsize->root.metrics = ttsize->metrics;
+
+ #ifdef TT_USE_BYTECODE_INTERPRETER
+ /* for the `MPS' bytecode instruction we need the point size */
+diff --git a/src/truetype/ttgload.c b/src/truetype/ttgload.c
+index 35b62d0e..b225190a 100644
+--- a/src/truetype/ttgload.c
++++ b/src/truetype/ttgload.c
+@@ -2035,7 +2035,7 @@
+
+ y_scale = 0x10000L;
+ if ( ( loader->load_flags & FT_LOAD_NO_SCALE ) == 0 )
+- y_scale = size->root.metrics.y_scale;
++ y_scale = size->metrics.y_scale;
+
+ if ( glyph->format != FT_GLYPH_FORMAT_COMPOSITE )
+ FT_Outline_Get_CBox( &glyph->outline, &bbox );
+@@ -2067,7 +2067,7 @@
+
+
+ widthp = tt_face_get_device_metrics( face,
+- size->root.metrics.x_ppem,
++ size->metrics.x_ppem,
+ glyph_index );
+
+ #ifdef TT_SUPPORT_SUBPIXEL_HINTING_INFINALITY
+@@ -2634,11 +2634,11 @@
+ if ( !glyph->metrics.horiAdvance && glyph->linearHoriAdvance )
+ glyph->metrics.horiAdvance =
+ FT_MulFix( glyph->linearHoriAdvance,
+- size->root.metrics.x_scale );
++ size->metrics.x_scale );
+ if ( !glyph->metrics.vertAdvance && glyph->linearVertAdvance )
+ glyph->metrics.vertAdvance =
+ FT_MulFix( glyph->linearVertAdvance,
+- size->root.metrics.y_scale );
++ size->metrics.y_scale );
+ }
+
+ return FT_Err_Ok;
+@@ -2728,7 +2728,7 @@
+ /* TrueType glyphs at all sizes using the bytecode interpreter. */
+ /* */
+ if ( !( load_flags & FT_LOAD_NO_SCALE ) &&
+- size->root.metrics.y_ppem < 24 )
++ size->metrics.y_ppem < 24 )
+ glyph->outline.flags |= FT_OUTLINE_HIGH_PRECISION;
+
+ return error;
diff --git a/freetype-freeworld.spec b/freetype-freeworld.spec
index 68da3dc..7d1e7b2 100644
--- a/freetype-freeworld.spec
+++ b/freetype-freeworld.spec
@@ -1,7 +1,7 @@
Summary: A free and portable font rendering engine
Name: freetype-freeworld
Version: 2.7.1
-Release: 2%{?dist}
+Release: 3%{?dist}
License: (FTL or GPLv2+) and BSD and MIT and Public Domain and zlib with acknowledgement
URL: http://www.freetype.org
Source: http://download.savannah.gnu.org/releases/freetype/freetype-%{version}.ta...
@@ -11,6 +11,10 @@ Patch21: freetype-2.3.0-enable-spr.patch
# Enable otvalid and gxvalid modules
Patch46: freetype-2.2.1-enable-valid.patch
+# Allow linear scaling for unhinted rendering
+# https://bugzilla.redhat.com/show_bug.cgi?id=1437999
+Patch94: freetype-2.7.1-linear-scaling.patch
+
## Security fixes:
# none needed yet
@@ -42,6 +46,8 @@ It transparently overrides the system library using ld.so.conf.d.
%patch46 -p1 -b .enable-valid
+%patch94 -p1 -b .linear-scaling
+
%build
%configure --disable-static \
@@ -85,6 +91,9 @@ echo "%{_libdir}/%{name}" \
%config(noreplace) %{_sysconfdir}/ld.so.conf.d/%{name}-%{_arch}.conf
%changelog
+* Wed Apr 05 2017 Kevin Kofler <Kevin(a)tigcc.ticalc.org> 2.7.1-3
+- Add freetype-2.7.1-linear-scaling.patch from Fedora freetype (rh#1437999)
+
* Sun Mar 19 2017 RPM Fusion Release Engineering <kwizart(a)rpmfusion.org> - 2.7.1-2
- Rebuilt for https://fedoraproject.org/wiki/Fedora_26_Mass_Rebuild
7 years, 7 months
[lightspark] Disable ppc64le and ppc64
by Xavier Bachelot
commit 51e61dabe9c7304f12301e94dbf61f28ac85a9d0
Author: Xavier Bachelot <xavier(a)bachelot.org>
Date: Tue Apr 4 08:24:40 2017 +0200
Disable ppc64le and ppc64
lightspark.spec | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
---
diff --git a/lightspark.spec b/lightspark.spec
index 72e3659..74a0dd2 100644
--- a/lightspark.spec
+++ b/lightspark.spec
@@ -17,7 +17,7 @@
%define pre rc1
%endif
-%define rel 11
+%define rel 12
%define major 0.7.2
@@ -35,6 +35,10 @@ Source0: http://launchpad.net/%{name}/trunk/%{name}-%{version}/+download/
Patch0: lightspark-0.7.2-fix_ffmpeg_include_dir.patch
+# Build fails on ppc64 and ppc64le, temporarily disable them
+# https://github.com/lightspark/lightspark/issues/283
+ExcludeArch: ppc64 ppc64le
+
BuildRequires: cmake
BuildRequires: llvm-devel >= 2.7
BuildRequires: glew-devel >= 1.5.4
@@ -147,6 +151,9 @@ fi
%changelog
+* Tue Apr 04 2017 Xavier Bachelot <xavier(a)bachelot.org> - 0.7.2-12.20170107git
+- Disable ppc64le and ppc64.
+
* Thu Mar 23 2017 Xavier Bachelot <xavier(a)bachelot.org> - 0.7.2-11.20170107git
- New snapshot.
- Specfile cleanup.
7 years, 7 months