/*
========================
idLobby::PickNewHostInternal
========================
*/
void idLobby::PickNewHostInternal( bool forceMe, bool inviteOldHost )
{

	if( migrationInfo.state == MIGRATE_PICKING_HOST )
	{
		return;		// Already picking new host
	}
	
	idLib::Printf( "PickNewHost: Started picking new host %s.\n", GetLobbyName() );
	
	if( IsHost() )
	{
		idLib::Printf( "PickNewHost: Already host of session %s\n", GetLobbyName() );
		return;
	}
	
	// Find the user with the lowest ping
	int bestUserIndex			= -1;
	int bestPingMs				= 0;
	lobbyUserID_t bestUserId;
	
	for( int i = 0; i < GetNumLobbyUsers(); i++ )
	{
		lobbyUser_t* user = GetLobbyUser( i );
		
		if( !verify( user != NULL ) )
		{
			continue;
		}
		
		if( user->IsDisconnected() )
		{
			continue;
		}
		
		if( user->peerIndex == -1 )
		{
			continue;		// Don't try and pick old host
		}
		
		if( bestUserIndex == -1 || IsBetterHost( user->pingMs, user->lobbyUserID, bestPingMs, bestUserId ) )
		{
			bestUserIndex	= i;
			bestPingMs		= user->pingMs;
			bestUserId		= user->lobbyUserID;
		}
		
		if( user->peerIndex == net_migration_forcePeerAsHost.GetInteger() )
		{
			bestUserIndex	= i;
			bestPingMs		= user->pingMs;
			bestUserId		= user->lobbyUserID;
			break;
		}
	}
	
	// Remember when we first started picking a new host
	migrationInfo.state						= MIGRATE_PICKING_HOST;
	migrationInfo.migrationStartTime		= Sys_Milliseconds();
	
	migrationInfo.persistUntilGameEndsData.wasMigratedGame = sessionCB->GetState() == idSession::INGAME;
	
	if( bestUserIndex == -1 )  	// This can happen if we call PickNewHost on an lobby that was Shutdown
	{
		NET_VERBOSE_PRINT( "MIGRATION: PickNewHost was called on an lobby that was Shutdown\n" );
		BecomeHost();
		return;
	}
	
	NET_VERBOSE_PRINT( "MIGRATION: Chose user index %d (%s) for new host\n", bestUserIndex, GetLobbyUser( bestUserIndex )->gamertag );
	
	bool bestWasLocal = IsSessionUserIndexLocal( bestUserIndex );		// Check before shutting down the lobby
	migrateMsgFlags = parms.matchFlags;						// Save off match parms
	
	// Build invite list
	BuildMigrationInviteList( inviteOldHost );
	
	// If the best user is on this machine, then we become the host now, otherwise, wait for a new host to contact us
	if( forceMe || bestWasLocal )
	{
		BecomeHost();
	}
}
/*
========================
idSnapShot::ReadDeltaForJob
========================
*/
bool idSnapShot::ReadDeltaForJob( const char* deltaMem, int deltaSize, int visIndex, idSnapShot* templateStates )
{

	bool report = net_verboseSnapshotReport.GetBool();
	net_verboseSnapshotReport.SetBool( false );
	
	lzwCompressionData_t		lzwData;
	idZeroRunLengthCompressor	rleCompressor;
	idLZWCompressor				lzwCompressor( &lzwData );
	int bytesRead = 0; // how many uncompressed bytes we read in. Used to figure out compression ratio
	
	lzwCompressor.Start( ( uint8* )deltaMem, deltaSize );
	
	// Skip past sequence and baseSequence
	int sequence		= 0;
	int baseSequence	= 0;
	
	lzwCompressor.ReadAgnostic( sequence );
	lzwCompressor.ReadAgnostic( baseSequence );
	lzwCompressor.ReadAgnostic( time );
	bytesRead += sizeof( int ) * 3;
	
	int objectNum = 0;
	uint16 delta = 0;
	
	
	while( lzwCompressor.ReadAgnostic( delta, true ) == sizeof( delta ) )
	{
		bytesRead += sizeof( delta );
		
		objectNum += delta;
		if( objectNum >= 0xFFFF )
		{
			// full delta
			if( net_verboseSnapshotCompression.GetBool() )
			{
				float compRatio = static_cast<float>( deltaSize ) / static_cast<float>( bytesRead );
				idLib::Printf( "Snapshot (%d/%d). ReadSize: %d DeltaSize: %d Ratio: %.3f\n", sequence, baseSequence, bytesRead, deltaSize, compRatio );
			}
			return true;
		}
		
		objectState_t& state = FindOrCreateObjectByID( objectNum );
		
		objectSize_t newsize = 0;
		lzwCompressor.ReadAgnostic( newsize );
		bytesRead += sizeof( newsize );
		
		if( newsize == SIZE_STALE )
		{
			NET_VERBOSESNAPSHOT_PRINT( "read delta: object %d goes stale\n", objectNum );
			// sanity
			bool oldVisible = ( state.visMask & ( 1 << visIndex ) ) != 0;
			if( !oldVisible )
			{
				NET_VERBOSESNAPSHOT_PRINT( "ERROR: unexpected already stale\n" );
			}
			state.visMask &= ~( 1 << visIndex );
			state.stale = true;
			// We need to make sure we haven't freed stale objects.
			assert( state.buffer.Size() > 0 );
			// no more data
			continue;
		}
		else if( newsize == SIZE_NOT_STALE )
		{
			NET_VERBOSESNAPSHOT_PRINT( "read delta: object %d no longer stale\n", objectNum );
			// sanity
			bool oldVisible = ( state.visMask & ( 1 << visIndex ) ) != 0;
			if( oldVisible )
			{
				NET_VERBOSESNAPSHOT_PRINT( "ERROR: unexpected not stale\n" );
			}
			state.visMask |= ( 1 << visIndex );
			state.stale = false;
			// the latest state is packed in, get the new size and continue reading the new state
			lzwCompressor.ReadAgnostic( newsize );
			bytesRead += sizeof( newsize );
		}
		
		objectState_t* 	objTemplateState = templateStates->FindObjectByID( objectNum );
		
		if( newsize == 0 )
		{
			// object deleted: reset state now so next one to use it doesn't have old data
			state.deleted = false;
			state.stale = false;
			state.changedCount = 0;
			state.expectedSequence = 0;
			state.visMask = 0;
			state.buffer._Release();
			state.createdFromTemplate = false;
			
			if( objTemplateState != NULL && objTemplateState->buffer.Size() && objTemplateState->expectedSequence < baseSequence )
			{
				idLib::PrintfIf( net_ssTemplateDebug.GetBool(), "Clearing old template state[%d] [%d<%d]\n", objectNum, objTemplateState->expectedSequence, baseSequence );
				objTemplateState->deleted = false;
				objTemplateState->stale = false;
				objTemplateState->changedCount = 0;
				objTemplateState->expectedSequence = 0;
				objTemplateState->visMask = 0;
				objTemplateState->buffer._Release();
			}
			
		}
		else
		{
		
			// new state?
			bool debug = false;
			if( state.buffer.Size() == 0 )
			{
				state.createdFromTemplate = true;
				// Brand new state
				if( objTemplateState != NULL && objTemplateState->buffer.Size() > 0 && sequence >= objTemplateState->expectedSequence )
				{
					idLib::PrintfIf( net_ssTemplateDebug.GetBool(), "\nAdding basestate for new object %d (for SS %d/%d. obj base created in ss %d) deltaSize: %d\n", objectNum, sequence, baseSequence, objTemplateState->expectedSequence, deltaSize );
					state.buffer = objTemplateState->buffer;
					
					if( net_ssTemplateDebug.GetBool() )
					{
						state.Print( "SPAWN STATE" );
						debug = true;
						PrintAlign( "DELTA STATE" );
					}
				}
				else if( net_ssTemplateDebug.GetBool() )
				{
					idLib::Printf( "\nNew snapobject[%d] in snapshot %d/%d but no basestate found locally so creating new\n", objectNum, sequence, baseSequence );
				}
			}
			else
			{
				state.createdFromTemplate = false;
			}
			
			// the buffer shrank or stayed the same
			objectBuffer_t newbuffer( newsize );
			rleCompressor.Start( NULL, &lzwCompressor, newsize );
			objectSize_t compareSize = Min( state.buffer.Size(), newsize );
			for( objectSize_t i = 0; i < compareSize; i++ )
			{
				byte b = rleCompressor.ReadByte();
				newbuffer[i] = state.buffer[i] + b;
				
				if( debug && InDebugRange( i ) )
				{
					idLib::Printf( "%02X", b );
				}
			}
			// Catch leftover
			if( newsize > compareSize )
			{
				rleCompressor.ReadBytes( newbuffer.Ptr() + compareSize, newsize - compareSize );
				
				if( debug )
				{
					for( objectSize_t i = compareSize; i < newsize; i++ )
					{
						if( InDebugRange( i ) )
						{
							idLib::Printf( "%02X", newbuffer[i] );
						}
					}
				}
				
			}
			state.buffer = newbuffer;
			state.changedCount = sequence;
			bytesRead += sizeof( byte ) * newsize;
			if( debug )
			{
				idLib::Printf( "\n" );
				state.Print( "NEW STATE" );
			}
			
			if( report )
			{
				idLib::Printf( "    Obj %d Compressed: Size %d \n", objectNum, rleCompressor.CompressedSize() );
			}
		}
#ifdef SNAPSHOT_CHECKSUMS
		extern uint32 SnapObjChecksum( const uint8 * data, int length );
		if( state.buffer.Size() > 0 )
		{
			uint32 checksum = 0;
			lzwCompressor.ReadAgnostic( checksum );
			bytesRead += sizeof( checksum );
			if( !verify( checksum == SnapObjChecksum( state.buffer.Ptr(), state.buffer.Size() ) ) )
			{
				idLib::Error( " Invalid snapshot checksum" );
			}
		}
#endif
	}
	// partial delta
	return false;
}
/*
========================
idMenuScreen_Shell_SystemOptions::idMenuDataSource_SystemSettings::AdjustField
========================
*/
void idMenuScreen_Shell_SystemOptions::idMenuDataSource_SystemSettings::AdjustField( const int fieldIndex, const int adjustAmount )
{
	switch( fieldIndex )
	{
		case SYSTEM_FIELD_FRAMERATE:
		{
			static const int numValues = 2;
			static const int values[numValues] = { 60, 120 };
			com_engineHz.SetInteger( AdjustOption( com_engineHz.GetInteger(), values, numValues, adjustAmount ) );
			break;
		}
		case SYSTEM_FIELD_VSYNC:
		{
			static const int numValues = 3;
			static const int values[numValues] = { 0, 1, 2 };
			r_swapInterval.SetInteger( AdjustOption( r_swapInterval.GetInteger(), values, numValues, adjustAmount ) );
			break;
		}
		case SYSTEM_FIELD_ANTIALIASING:
		{
			// RB: disabled 16x MSAA
			static const int numValues = 4;
			static const int values[numValues] = { 0, 2, 4, 8 };
			// RB end
			r_multiSamples.SetInteger( AdjustOption( r_multiSamples.GetInteger(), values, numValues, adjustAmount ) );
			break;
		}
		case SYSTEM_FIELD_MOTIONBLUR:
		{
			static const int numValues = 5;
			static const int values[numValues] = { 0, 2, 3, 4, 5 };
			r_motionBlur.SetInteger( AdjustOption( r_motionBlur.GetInteger(), values, numValues, adjustAmount ) );
			break;
		}
		// RB begin
		case SYSTEM_FIELD_SHADOWMAPPING:
		{
			static const int numValues = 2;
			static const int values[numValues] = { 0, 1 };
			r_useShadowMapping.SetInteger( AdjustOption( r_useShadowMapping.GetInteger(), values, numValues, adjustAmount ) );
			break;
		}
		/*case SYSTEM_FIELD_LODBIAS:
		{
			const float percent = LinearAdjust( r_lodBias.GetFloat(), -1.0f, 1.0f, 0.0f, 100.0f );
			const float adjusted = percent + ( float )adjustAmount * 5.0f;
			const float clamped = idMath::ClampFloat( 0.0f, 100.0f, adjusted );
			r_lodBias.SetFloat( LinearAdjust( clamped, 0.0f, 100.0f, -1.0f, 1.0f ) );
			break;
		}*/
		// RB end
		case SYSTEM_FIELD_BRIGHTNESS:
		{
			const float percent = LinearAdjust( r_lightScale.GetFloat(), 2.0f, 4.0f, 0.0f, 100.0f );
			const float adjusted = percent + ( float )adjustAmount;
			const float clamped = idMath::ClampFloat( 0.0f, 100.0f, adjusted );
			r_lightScale.SetFloat( LinearAdjust( clamped, 0.0f, 100.0f, 2.0f, 4.0f ) );
			break;
		}
		case SYSTEM_FIELD_VOLUME:
		{
			const float percent = 100.0f * Square( 1.0f - ( s_volume_dB.GetFloat() / DB_SILENCE ) );
			const float adjusted = percent + ( float )adjustAmount;
			const float clamped = idMath::ClampFloat( 0.0f, 100.0f, adjusted );
			s_volume_dB.SetFloat( DB_SILENCE - ( idMath::Sqrt( clamped / 100.0f ) * DB_SILENCE ) );
			break;
		}
	}
	cvarSystem->ClearModifiedFlags( CVAR_ARCHIVE );
}
namespace BFG
{

idCVar in_nograb( "in_nograb", "0", CVAR_SYSTEM | CVAR_NOCHEAT, "prevents input grabbing" );

// RB: FIXME this shit. We need the OpenGL alpha channel for advanced rendering effects
idCVar r_waylandcompat( "r_waylandcompat", "0", CVAR_SYSTEM | CVAR_NOCHEAT | CVAR_ARCHIVE, "wayland compatible framebuffer" );

// RB: only relevant if using SDL 2.0
#if defined(__APPLE__)
// only core profile is supported on OS X
idCVar r_useOpenGL32( "r_useOpenGL32", "2", CVAR_INTEGER, "0 = OpenGL 3.x, 1 = OpenGL 3.2 compatibility profile, 2 = OpenGL 3.2 core profile", 0, 2 );
#elif defined(__linux__)
// Linux open source drivers suck
idCVar r_useOpenGL32( "r_useOpenGL32", "0", CVAR_INTEGER, "0 = OpenGL 3.x, 1 = OpenGL 3.2 compatibility profile, 2 = OpenGL 3.2 core profile", 0, 2 );
#else
idCVar r_useOpenGL32( "r_useOpenGL32", "1", CVAR_INTEGER, "0 = OpenGL 3.x, 1 = OpenGL 3.2 compatibility profile, 2 = OpenGL 3.2 core profile", 0, 2 );
#endif
// RB end

#if SDL_VERSION_ATLEAST(2, 0, 0)
static SDL_Window* window = NULL;
static SDL_GLContext context = NULL;
#else
static SDL_Surface* window = NULL;
#define SDL_WINDOW_OPENGL SDL_OPENGL
#define SDL_WINDOW_FULLSCREEN SDL_FULLSCREEN
#define SDL_WINDOW_RESIZABLE SDL_RESIZABLE
#endif

/*
===================
GLimp_PreInit

 R_GetModeListForDisplay is called before GLimp_Init(), but SDL needs SDL_Init() first.
 So do that in GLimp_PreInit()
 Calling that function more than once doesn't make a difference
===================
*/
void GLimp_PreInit() // DG: added this function for SDL compatibility
{
	if( !SDL_WasInit( SDL_INIT_VIDEO ) )
	{
		if( SDL_Init( SDL_INIT_VIDEO ) )
			common->Error( "Error while initializing SDL: %s", SDL_GetError() );
	}
}


/*
===================
GLimp_Init
===================
*/
bool GLimp_Init( glimpParms_t parms )
{
	common->Printf( "Initializing OpenGL subsystem\n" );
	
	GLimp_PreInit(); // DG: make sure SDL is initialized
	
	// DG: make window resizable
	Uint32 flags = SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE;
	// DG end
	
	if( parms.fullScreen )
		flags |= SDL_WINDOW_FULLSCREEN;
		
	int colorbits = 24;
	int depthbits = 24;
	int stencilbits = 8;
	
	for( int i = 0; i < 16; i++ )
	{
		// 0 - default
		// 1 - minus colorbits
		// 2 - minus depthbits
		// 3 - minus stencil
		if( ( i % 4 ) == 0 && i )
		{
			// one pass, reduce
			switch( i / 4 )
			{
				case 2 :
					if( colorbits == 24 )
						colorbits = 16;
					break;
				case 1 :
					if( depthbits == 24 )
						depthbits = 16;
					else if( depthbits == 16 )
						depthbits = 8;
				case 3 :
					if( stencilbits == 24 )
						stencilbits = 16;
					else if( stencilbits == 16 )
						stencilbits = 8;
			}
		}
		
		int tcolorbits = colorbits;
		int tdepthbits = depthbits;
		int tstencilbits = stencilbits;
		
		if( ( i % 4 ) == 3 )
		{
			// reduce colorbits
			if( tcolorbits == 24 )
				tcolorbits = 16;
		}
		
		if( ( i % 4 ) == 2 )
		{
			// reduce depthbits
			if( tdepthbits == 24 )
				tdepthbits = 16;
			else if( tdepthbits == 16 )
				tdepthbits = 8;
		}
		
		if( ( i % 4 ) == 1 )
		{
			// reduce stencilbits
			if( tstencilbits == 24 )
				tstencilbits = 16;
			else if( tstencilbits == 16 )
				tstencilbits = 8;
			else
				tstencilbits = 0;
		}
		
		int channelcolorbits = 4;
		if( tcolorbits == 24 )
			channelcolorbits = 8;
			
		SDL_GL_SetAttribute( SDL_GL_RED_SIZE, channelcolorbits );
		SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, channelcolorbits );
		SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, channelcolorbits );
		SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
		SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, tdepthbits );
		SDL_GL_SetAttribute( SDL_GL_STENCIL_SIZE, tstencilbits );
		
		if( r_waylandcompat.GetBool() )
			SDL_GL_SetAttribute( SDL_GL_ALPHA_SIZE, 0 );
		else
			SDL_GL_SetAttribute( SDL_GL_ALPHA_SIZE, channelcolorbits );
			
		SDL_GL_SetAttribute( SDL_GL_STEREO, parms.stereo ? 1 : 0 );
		
		SDL_GL_SetAttribute( SDL_GL_MULTISAMPLEBUFFERS, parms.multiSamples ? 1 : 0 );
		SDL_GL_SetAttribute( SDL_GL_MULTISAMPLESAMPLES, parms.multiSamples );
		
#if SDL_VERSION_ATLEAST(2, 0, 0)
		
		// RB begin
		if( r_useOpenGL32.GetInteger() > 0 )
		{
			glConfig.driverType = GLDRV_OPENGL32_COMPATIBILITY_PROFILE;
			
			SDL_GL_SetAttribute( SDL_GL_CONTEXT_MAJOR_VERSION, 3 );
			SDL_GL_SetAttribute( SDL_GL_CONTEXT_MINOR_VERSION, 2 );
		}
		if( r_debugContext.GetBool() )
		{
			SDL_GL_SetAttribute( SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG );
		}
		
		if( r_useOpenGL32.GetInteger() > 1 )
		{
			glConfig.driverType = GLDRV_OPENGL32_CORE_PROFILE;
			
			SDL_GL_SetAttribute( SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE );
		}
		// RB end
		
		// DG: set display num for fullscreen
		int windowPos = SDL_WINDOWPOS_UNDEFINED;
		if( parms.fullScreen > 0 )
		{
			if( parms.fullScreen > SDL_GetNumVideoDisplays() )
			{
				common->Warning( "Couldn't set display to num %i because we only have %i displays",
								 parms.fullScreen, SDL_GetNumVideoDisplays() );
			}
			else
			{
				// -1 because SDL starts counting displays at 0, while parms.fullScreen starts at 1
				windowPos = SDL_WINDOWPOS_UNDEFINED_DISPLAY( ( parms.fullScreen - 1 ) );
			}
		}
		// TODO: if parms.fullScreen == -1 there should be a borderless window spanning multiple displays
		/*
		 * NOTE that this implicitly handles parms.fullScreen == -2 (from r_fullscreen -2) meaning
		 * "do fullscreen, but I don't care on what monitor", at least on my box it's the monitor with
		 * the mouse cursor.
		 */
		
		
		window = SDL_CreateWindow( GAME_NAME,
								   windowPos,
								   windowPos,
								   parms.width, parms.height, flags );
		// DG end
		
		context = SDL_GL_CreateContext( window );
		
		if( !window )
		{
			common->DPrintf( "Couldn't set GL mode %d/%d/%d: %s",
							 channelcolorbits, tdepthbits, tstencilbits, SDL_GetError() );
			continue;
		}
		
		if( SDL_GL_SetSwapInterval( r_swapInterval.GetInteger() ) < 0 )
			common->Warning( "SDL_GL_SWAP_CONTROL not supported" );
			
		// RB begin
		SDL_GetWindowSize( window, &glConfig.nativeScreenWidth, &glConfig.nativeScreenHeight );
		// RB end
		
		glConfig.isFullscreen = ( SDL_GetWindowFlags( window ) & SDL_WINDOW_FULLSCREEN ) == SDL_WINDOW_FULLSCREEN;
#else
		glConfig.driverType = GLDRV_OPENGL3X;
		
		SDL_WM_SetCaption( GAME_NAME, GAME_NAME );
		
		if( SDL_GL_SetAttribute( SDL_GL_SWAP_CONTROL, r_swapInterval.GetInteger() ) < 0 )
			common->Warning( "SDL_GL_SWAP_CONTROL not supported" );
		
		window = SDL_SetVideoMode( parms.width, parms.height, colorbits, flags );
		if( !window )
		{
			common->DPrintf( "Couldn't set GL mode %d/%d/%d: %s",
							 channelcolorbits, tdepthbits, tstencilbits, SDL_GetError() );
			continue;
		}
		
		glConfig.nativeScreenWidth = window->w;
		glConfig.nativeScreenHeight = window->h;
		
		glConfig.isFullscreen = ( window->flags & SDL_FULLSCREEN ) == SDL_FULLSCREEN;
#endif
		
		common->Printf( "Using %d color bits, %d depth, %d stencil display\n",
						channelcolorbits, tdepthbits, tstencilbits );
						
		glConfig.colorBits = tcolorbits;
		glConfig.depthBits = tdepthbits;
		glConfig.stencilBits = tstencilbits;
		
		// RB begin
		glConfig.displayFrequency = 60;
		glConfig.isStereoPixelFormat = parms.stereo;
		glConfig.multisamples = parms.multiSamples;
		
		glConfig.pixelAspect = 1.0f;	// FIXME: some monitor modes may be distorted
		// should side-by-side stereo modes be consider aspect 0.5?
		
		// RB end
		
		break;
	}
	
	if( !window )
	{
		common->Printf( "No usable GL mode found: %s", SDL_GetError() );
		return false;
	}
	
#ifdef __APPLE__
	glewExperimental = GL_TRUE;
#endif
	
	GLenum glewResult = glewInit();
	if( GLEW_OK != glewResult )
	{
		// glewInit failed, something is seriously wrong
		common->Printf( "^3GLimp_Init() - GLEW could not load OpenGL subsystem: %s", glewGetErrorString( glewResult ) );
	}
	else
	{
		common->Printf( "Using GLEW %s\n", glewGetString( GLEW_VERSION ) );
	}
	
	// DG: disable cursor, we have two cursors in menu (because mouse isn't grabbed in menu)
	SDL_ShowCursor( SDL_DISABLE );
	// DG end
	
	return true;
}
/*
===================
 Helper functions for GLimp_SetScreenParms()
===================
*/

#if SDL_VERSION_ATLEAST(2, 0, 0)
// SDL1 doesn't support multiple displays, so the source is much shorter and doesn't need separate functions
// makes sure the window will be full-screened on the right display and returns the SDL display index
static int ScreenParmsHandleDisplayIndex( glimpParms_t parms )
{
	int displayIdx;
	if( parms.fullScreen > 0 )
	{
		displayIdx = parms.fullScreen - 1; // first display for SDL is 0, in parms it's 1
	}
	else // -2 == use current display
	{
		displayIdx = SDL_GetWindowDisplayIndex( window );
		if( displayIdx < 0 ) // for some reason the display for the window couldn't be detected
			displayIdx = 0;
	}
	
	if( parms.fullScreen > SDL_GetNumVideoDisplays() )
	{
		common->Warning( "Can't set fullscreen mode to display number %i, because SDL2 only knows about %i displays!",
						 parms.fullScreen, SDL_GetNumVideoDisplays() );
		return -1;
	}
	
	if( parms.fullScreen != glConfig.isFullscreen )
	{
		// we have to switch to another display
		if( glConfig.isFullscreen )
		{
			// if we're already in fullscreen mode but want to switch to another monitor
			// we have to go to windowed mode first to move the window.. SDL-oddity.
			SDL_SetWindowFullscreen( window, SDL_FALSE );
		}
		// select display ; SDL_WINDOWPOS_UNDEFINED_DISPLAY() doesn't work.
		int x = SDL_WINDOWPOS_CENTERED_DISPLAY( displayIdx );
		// move window to the center of selected display
		SDL_SetWindowPosition( window, x, x );
	}
	return displayIdx;
}

static bool SetScreenParmsFullscreen( glimpParms_t parms )
{
	SDL_DisplayMode m = {0};
	int displayIdx = ScreenParmsHandleDisplayIndex( parms );
	if( displayIdx < 0 )
		return false;
		
	// get current mode of display the window should be full-screened on
	SDL_GetCurrentDisplayMode( displayIdx, &m );
	
	// change settings in that display mode according to parms
	// FIXME: check if refreshrate, width and height are supported?
	// m.refresh_rate = parms.displayHz;
	m.w = parms.width;
	m.h = parms.height;
	
	// set that displaymode
	if( SDL_SetWindowDisplayMode( window, &m ) < 0 )
	{
		common->Warning( "Couldn't set window mode for fullscreen, reason: %s", SDL_GetError() );
		return false;
	}
	
	// if we're currently not in fullscreen mode, we need to switch to fullscreen
	if( !( SDL_GetWindowFlags( window ) & SDL_WINDOW_FULLSCREEN ) )
	{
		if( SDL_SetWindowFullscreen( window, SDL_TRUE ) < 0 )
		{
			common->Warning( "Couldn't switch to fullscreen mode, reason: %s!", SDL_GetError() );
			return false;
		}
	}
	return true;
}

static bool SetScreenParmsWindowed( glimpParms_t parms )
{
	SDL_SetWindowSize( window, parms.width, parms.height );
	SDL_SetWindowPosition( window, parms.x, parms.y );
	
	// if we're currently in fullscreen mode, we need to disable that
	if( SDL_GetWindowFlags( window ) & SDL_WINDOW_FULLSCREEN )
	{
		if( SDL_SetWindowFullscreen( window, SDL_FALSE ) < 0 )
		{
			common->Warning( "Couldn't switch to windowed mode, reason: %s!", SDL_GetError() );
			return false;
		}
	}
	return true;
}
#endif // SDL_VERSION_ATLEAST(2, 0, 0)

/*
===================
GLimp_SetScreenParms
===================
*/
bool GLimp_SetScreenParms( glimpParms_t parms )
{
#if SDL_VERSION_ATLEAST(2, 0, 0)
	if( parms.fullScreen > 0 || parms.fullScreen == -2 )
	{
		if( !SetScreenParmsFullscreen( parms ) )
			return false;
	}
	else if( parms.fullScreen == 0 ) // windowed mode
	{
		if( !SetScreenParmsWindowed( parms ) )
			return false;
	}
	else
	{
		common->Warning( "GLimp_SetScreenParms: fullScreen -1 (borderless window for multiple displays) currently unsupported!" );
		return false;
	}
#else // SDL 1.2 - so much shorter, but doesn't handle multiple displays
	SDL_Surface* s = SDL_GetVideoSurface();
	if( s == NULL )
	{
		common->Warning( "GLimp_SetScreenParms: Couldn't get video information, reason: %s", SDL_GetError() );
		return false;
	}
	
	
	int bitsperpixel = 24;
	if( s->format )
		bitsperpixel = s->format->BitsPerPixel;
	
	Uint32 flags = s->flags;
	
	if( parms.fullScreen )
		flags |= SDL_FULLSCREEN;
	else
		flags &= ~SDL_FULLSCREEN;
	
	s = SDL_SetVideoMode( parms.width, parms.height, bitsperpixel, flags );
	if( s == NULL )
	{
		common->Warning( "GLimp_SetScreenParms: Couldn't set video information, reason: %s", SDL_GetError() );
		return false;
	}
#endif // SDL_VERSION_ATLEAST(2, 0, 0)
	
	// Note: the following stuff would also work with SDL1.2
	SDL_GL_SetAttribute( SDL_GL_STEREO, parms.stereo ? 1 : 0 );
	
	SDL_GL_SetAttribute( SDL_GL_MULTISAMPLEBUFFERS, parms.multiSamples ? 1 : 0 );
	SDL_GL_SetAttribute( SDL_GL_MULTISAMPLESAMPLES, parms.multiSamples );
	
	glConfig.isFullscreen = parms.fullScreen;
	glConfig.isStereoPixelFormat = parms.stereo;
	glConfig.nativeScreenWidth = parms.width;
	glConfig.nativeScreenHeight = parms.height;
	glConfig.displayFrequency = parms.displayHz;
	glConfig.multisamples = parms.multiSamples;
	
	return true;
}

/*
===================
GLimp_Shutdown
===================
*/
void GLimp_Shutdown()
{
	common->Printf( "Shutting down OpenGL subsystem\n" );
	
#if SDL_VERSION_ATLEAST(2, 0, 0)
	if( context )
	{
		SDL_GL_DeleteContext( context );
		context = NULL;
	}
	
	if( window )
	{
		SDL_DestroyWindow( window );
		window = NULL;
	}
#endif
}

/*
===================
GLimp_SwapBuffers
===================
*/
void GLimp_SwapBuffers()
{
#if SDL_VERSION_ATLEAST(2, 0, 0)
	SDL_GL_SwapWindow( window );
#else
	SDL_GL_SwapBuffers();
#endif
}

/*
=================
GLimp_SetGamma
=================
*/
void GLimp_SetGamma( unsigned short red[256], unsigned short green[256], unsigned short blue[256] )
{
	if( !window )
	{
		common->Warning( "GLimp_SetGamma called without window" );
		return;
	}
	
#if SDL_VERSION_ATLEAST(2, 0, 0)
	if( SDL_SetWindowGammaRamp( window, red, green, blue ) )
#else
	if( SDL_SetGammaRamp( red, green, blue ) )
#endif
		common->Warning( "Couldn't set gamma ramp: %s", SDL_GetError() );
}

/*
===================
GLimp_ExtensionPointer
===================
*/
/*
GLExtension_t GLimp_ExtensionPointer(const char *name) {
	assert(SDL_WasInit(SDL_INIT_VIDEO));

	return (GLExtension_t)SDL_GL_GetProcAddress(name);
}
*/

/*
===============
Sys_GrabMouseCursor
===============
*/
void Sys_GrabMouseCursor( bool grab )
{
	static bool grabbed = false;
	
	if( in_nograb.GetBool() )
		grab = false;
		
	if( grabbed != grab )
	{
		grabbed = grab;
		
#if SDL_VERSION_ATLEAST(2, 0, 0)
		SDL_SetRelativeMouseMode( grab ? SDL_TRUE : SDL_FALSE );
		SDL_SetWindowGrab( window, grab ? SDL_TRUE : SDL_FALSE );
#else
		SDL_WM_GrabInput( grab ? SDL_GRAB_ON : SDL_GRAB_OFF );
#endif
	}
}

/*
====================
DumpAllDisplayDevices
====================
*/
void DumpAllDisplayDevices()
{
	common->DPrintf( "TODO: DumpAllDisplayDevices\n" );
}



class idSort_VidMode : public idSort_Quick< vidMode_t, idSort_VidMode >
{
public:
	int Compare( const vidMode_t& a, const vidMode_t& b ) const
	{
		int wd = a.width - b.width;
		int hd = a.height - b.height;
		int fd = a.displayHz - b.displayHz;
		return ( hd != 0 ) ? hd : ( wd != 0 ) ? wd : fd;
	}
};

// RB: resolutions supported by XreaL
static void FillStaticVidModes( idList<vidMode_t>& modeList )
{
	modeList.AddUnique( vidMode_t( 320,   240, 60 ) );
	modeList.AddUnique( vidMode_t( 400,   300, 60 ) );
	modeList.AddUnique( vidMode_t( 512,   384, 60 ) );
	modeList.AddUnique( vidMode_t( 640,   480, 60 ) );
	modeList.AddUnique( vidMode_t( 800,   600, 60 ) );
	modeList.AddUnique( vidMode_t( 960,   720, 60 ) );
	modeList.AddUnique( vidMode_t( 1024,  768, 60 ) );
	modeList.AddUnique( vidMode_t( 1152,  864, 60 ) );
	modeList.AddUnique( vidMode_t( 1280,  720, 60 ) );
	modeList.AddUnique( vidMode_t( 1280,  768, 60 ) );
	modeList.AddUnique( vidMode_t( 1280,  800, 60 ) );
	modeList.AddUnique( vidMode_t( 1280, 1024, 60 ) );
	modeList.AddUnique( vidMode_t( 1360,  768, 60 ) );
	modeList.AddUnique( vidMode_t( 1440,  900, 60 ) );
	modeList.AddUnique( vidMode_t( 1680, 1050, 60 ) );
	modeList.AddUnique( vidMode_t( 1600, 1200, 60 ) );
	modeList.AddUnique( vidMode_t( 1920, 1080, 60 ) );
	modeList.AddUnique( vidMode_t( 1920, 1200, 60 ) );
	modeList.AddUnique( vidMode_t( 2048, 1536, 60 ) );
	modeList.AddUnique( vidMode_t( 2560, 1600, 60 ) );
	
	modeList.SortWithTemplate( idSort_VidMode() );
}

/*
====================
R_GetModeListForDisplay
====================
*/
bool R_GetModeListForDisplay( const int requestedDisplayNum, idList<vidMode_t>& modeList )
{
	assert( requestedDisplayNum >= 0 );
	
	modeList.Clear();
#if SDL_VERSION_ATLEAST(2, 0, 0)
	// DG: SDL2 implementation
	if( requestedDisplayNum >= SDL_GetNumVideoDisplays() )
	{
		// requested invalid displaynum
		return false;
	}
	
	int numModes = SDL_GetNumDisplayModes( requestedDisplayNum );
	if( numModes > 0 )
	{
		for( int i = 0; i < numModes; i++ )
		{
			SDL_DisplayMode m;
			int ret = SDL_GetDisplayMode( requestedDisplayNum, i, &m );
			if( ret != 0 )
			{
				common->Warning( "Can't get video mode no %i, because of %s\n", i, SDL_GetError() );
				continue;
			}
			
			vidMode_t mode;
			mode.width = m.w;
			mode.height = m.h;
			mode.displayHz = m.refresh_rate ? m.refresh_rate : 60; // default to 60 if unknown (0)
			modeList.AddUnique( mode );
		}
		
		if( modeList.Num() < 1 )
		{
			common->Warning( "Couldn't get a single video mode for display %i, using default ones..!\n", requestedDisplayNum );
			FillStaticVidModes( modeList );
		}
		
		// sort with lowest resolution first
		modeList.SortWithTemplate( idSort_VidMode() );
	}
	else
	{
		common->Warning( "Can't get Video Info, using default modes...\n" );
		if( numModes < 0 )
		{
			common->Warning( "Reason was: %s\n", SDL_GetError() );
		}
		FillStaticVidModes( modeList );
	}
	
	return true;
	// DG end
	
#else // SDL 1
	
	// DG: SDL1 only knows of one display - some functions rely on
	// R_GetModeListForDisplay() returning false for invalid displaynum to iterate all displays
	if( requestedDisplayNum >= 1 )
	{
		return false;
	}
	// DG end
	
	const SDL_VideoInfo* videoInfo = SDL_GetVideoInfo();
	if( videoInfo == NULL )
	{
		// DG: yes, this can actually fail, e.g. if SDL_Init( SDL_INIT_VIDEO ) wasn't called
		common->Warning( "Can't get Video Info, using default modes...\n" );
		FillStaticVidModes( modeList );
		return true;
	}
	
	SDL_Rect** modes = SDL_ListModes( videoInfo->vfmt, SDL_OPENGL | SDL_FULLSCREEN );
	
	if( !modes )
	{
		common->Warning( "Can't get list of available modes, using default ones...\n" );
		FillStaticVidModes( modeList );
		return true;
	}
	
	if( modes == ( SDL_Rect** ) - 1 )
	{
		common->Printf( "Display supports any resolution\n" );
		FillStaticVidModes( modeList );
		return true;
	}
	
	int numModes;
	for( numModes = 0; modes[numModes]; numModes++ );
	
	if( numModes > 1 )
	{
		for( int i = 0; i < numModes; i++ )
		{
			vidMode_t mode;
			mode.width =  modes[i]->w;
			mode.height =  modes[i]->h;
			mode.displayHz = 60; // FIXME;
			modeList.AddUnique( mode );
		}
	
		// sort with lowest resolution first
		modeList.SortWithTemplate( idSort_VidMode() );
	
		return true;
	}
	
	return false;
#endif
}

} // namespace BFG
/*
=================
idRenderModelManagerLocal::GetModel
=================
*/
idRenderModel* idRenderModelManagerLocal::GetModel( const char* _modelName, bool createIfNotFound )
{

	if( !_modelName || !_modelName[0] )
	{
		return NULL;
	}
	
	idStrStatic< MAX_OSPATH > canonical = _modelName;
	canonical.ToLower();
	
	idStrStatic< MAX_OSPATH > extension;
	canonical.ExtractFileExtension( extension );
	
	// see if it is already present
	int key = hash.GenerateKey( canonical, false );
	for( int i = hash.First( key ); i != -1; i = hash.Next( i ) )
	{
		idRenderModel* model = models[i];
		
		if( canonical.Icmp( model->Name() ) == 0 )
		{
			if( !model->IsLoaded() )
			{
				// reload it if it was purged
				idStr generatedFileName = "generated/rendermodels/";
				generatedFileName.AppendPath( canonical );
				generatedFileName.SetFileExtension( va( "b%s", extension.c_str() ) );
				if( model->SupportsBinaryModel() && r_binaryLoadRenderModels.GetBool() )
				{
					idFileLocal file( fileSystem->OpenFileReadMemory( generatedFileName ) );
					model->PurgeModel();
					if( !model->LoadBinaryModel( file, 0 ) )
					{
						model->LoadModel();
					}
				}
				else
				{
					model->LoadModel();
				}
			}
			else if( insideLevelLoad && !model->IsLevelLoadReferenced() )
			{
				// we are reusing a model already in memory, but
				// touch all the materials to make sure they stay
				// in memory as well
				model->TouchData();
			}
			model->SetLevelLoadReferenced( true );
			return model;
		}
	}
	
	// see if we can load it
	
	// determine which subclass of idRenderModel to initialize
	
	idRenderModel* model = NULL;
	
	if( ( extension.Icmp( "ase" ) == 0 ) || ( extension.Icmp( "lwo" ) == 0 ) || ( extension.Icmp( "flt" ) == 0 ) || ( extension.Icmp( "ma" ) == 0 ) )
	{
		model = new( TAG_MODEL ) idRenderModelStatic;
	}
	else if( extension.Icmp( MD5_MESH_EXT ) == 0 )
	{
		model = new( TAG_MODEL ) idRenderModelMD5;
	}
	else if( extension.Icmp( "md3" ) == 0 )
	{
		model = new( TAG_MODEL ) idRenderModelMD3;
	}
	else if( extension.Icmp( "prt" ) == 0 )
	{
		model = new( TAG_MODEL ) idRenderModelPrt;
	}
	else if( extension.Icmp( "liquid" ) == 0 )
	{
		model = new( TAG_MODEL ) idRenderModelLiquid;
	}
	
	idStrStatic< MAX_OSPATH > generatedFileName;
	
	if( model != NULL )
	{
	
		generatedFileName = "generated/rendermodels/";
		generatedFileName.AppendPath( canonical );
		generatedFileName.SetFileExtension( va( "b%s", extension.c_str() ) );
		
		// Get the timestamp on the original file, if it's newer than what is stored in binary model, regenerate it
		ID_TIME_T sourceTimeStamp = fileSystem->GetTimestamp( canonical );
		
		idFileLocal file( fileSystem->OpenFileReadMemory( generatedFileName ) );
		
		if( !model->SupportsBinaryModel() || !r_binaryLoadRenderModels.GetBool() )
		{
			model->InitFromFile( canonical );
		}
		else
		{
			if( !model->LoadBinaryModel( file, sourceTimeStamp ) )
			{
				model->InitFromFile( canonical );
				
				idFileLocal outputFile( fileSystem->OpenFileWrite( generatedFileName, "fs_basepath" ) );
				idLib::Printf( "Writing %s\n", generatedFileName.c_str() );
				model->WriteBinaryModel( outputFile );
			} /* else {
				idLib::Printf( "loaded binary model %s from file %s\n", model->Name(), generatedFileName.c_str() );
			} */
		}
	}
	
	// Not one of the known formats
	if( model == NULL )
	{
	
		if( extension.Length() )
		{
			common->Warning( "unknown model type '%s'", canonical.c_str() );
		}
		
		if( !createIfNotFound )
		{
			return NULL;
		}
		
		idRenderModelStatic*	smodel = new( TAG_MODEL ) idRenderModelStatic;
		smodel->InitEmpty( canonical );
		smodel->MakeDefaultModel();
		
		model = smodel;
	}
	
	if( cvarSystem->GetCVarBool( "fs_buildresources" ) )
	{
		fileSystem->AddModelPreload( canonical );
	}
	model->SetLevelLoadReferenced( true );
	
	if( !createIfNotFound && model->IsDefaultModel() )
	{
		delete model;
		model = NULL;
		
		return NULL;
	}
	
	if( cvarSystem->GetCVarBool( "fs_buildgame" ) )
	{
		fileSystem->AddModelPreload( model->Name() );
	}
	
	AddModel( model );
	
	return model;
}
/*
========================
ConvertCG2GLSL
========================
*/
idStr ConvertCG2GLSL( const idStr & in, const char * name, bool isVertexProgram, idStr & uniforms ) {
	idStr program;
	program.ReAllocate( in.Length() * 2, false );

	idList< inOutVariable_t, TAG_RENDERPROG > varsIn;
	idList< inOutVariable_t, TAG_RENDERPROG > varsOut;
	idList< idStr > uniformList;

	idLexer src( LEXFL_NOFATALERRORS );
	src.LoadMemory( in.c_str(), in.Length(), name );

	bool inMain = false;
	const char * uniformArrayName = isVertexProgram ? VERTEX_UNIFORM_ARRAY_NAME : FRAGMENT_UNIFORM_ARRAY_NAME;
	char newline[128] = { "\n" };

	idToken token;
	while ( src.ReadToken( &token ) ) {

		// check for uniforms
		while ( token == "uniform" && src.CheckTokenString( "float4" ) ) {
			src.ReadToken( &token );
			uniformList.Append( token );

			// strip ': register()' from uniforms
			if ( src.CheckTokenString( ":" ) ) {
				if ( src.CheckTokenString( "register" ) ) {
					src.SkipUntilString( ";" );
				}
			}

			src.ReadToken( & token );
		}

		// convert the in/out structs
		if ( token == "struct" ) {
			if ( src.CheckTokenString( "VS_IN" ) ) {
				ParseInOutStruct( src, AT_VS_IN, varsIn );
				program += "\n\n";
				for ( int i = 0; i < varsIn.Num(); i++ ) {
					if ( varsIn[i].declareInOut ) {
						program += "in " + varsIn[i].type + " " + varsIn[i].nameGLSL + ";\n";
					}
				}
				continue;
			} else if ( src.CheckTokenString( "VS_OUT" ) ) {
				ParseInOutStruct( src, AT_VS_OUT, varsOut );
				program += "\n";
				for ( int i = 0; i < varsOut.Num(); i++ ) {
					if ( varsOut[i].declareInOut ) {
						program += "out " + varsOut[i].type + " " + varsOut[i].nameGLSL + ";\n";
					}
				}
				continue;
			} else if ( src.CheckTokenString( "PS_IN" ) ) {
				ParseInOutStruct( src, AT_PS_IN, varsIn );
				program += "\n\n";
				for ( int i = 0; i < varsIn.Num(); i++ ) {
					if ( varsIn[i].declareInOut ) {
						program += "in " + varsIn[i].type + " " + varsIn[i].nameGLSL + ";\n";
					}
				}
				inOutVariable_t var;
				var.type = "vec4";
				var.nameCg = "position";
				var.nameGLSL = "gl_FragCoord";
				varsIn.Append( var );
				continue;
			} else if ( src.CheckTokenString( "PS_OUT" ) ) {
				ParseInOutStruct( src, AT_PS_OUT, varsOut );
				program += "\n";
				for ( int i = 0; i < varsOut.Num(); i++ ) {
					if ( varsOut[i].declareInOut ) {
						program += "out " + varsOut[i].type + " " + varsOut[i].nameGLSL + ";\n";
					}
				}
				continue;
			}
		}

		// strip 'static'
		if ( token == "static" ) {
			program += ( token.linesCrossed > 0 ) ? newline : ( token.WhiteSpaceBeforeToken() > 0 ? " " : "" );
			src.SkipWhiteSpace( true ); // remove white space between 'static' and the next token
			continue;
		}

		// strip ': register()' from uniforms
		if ( token == ":" ) {
			if ( src.CheckTokenString( "register" ) ) {
				src.SkipUntilString( ";" );
				program += ";";
				continue;
			}
		}

		// strip in/program parameters from main
		if ( token == "void" && src.CheckTokenString( "main" ) ) {
			src.ExpectTokenString( "(" );
			while( src.ReadToken( &token ) ) {
				if ( token == ")" ) {
					break;
				}
			}

			program += "\nvoid main()";
			inMain = true;
			continue;
		}

		// strip 'const' from local variables in main()
		if ( token == "const" && inMain ) {
			program += ( token.linesCrossed > 0 ) ? newline : ( token.WhiteSpaceBeforeToken() > 0 ? " " : "" );
			src.SkipWhiteSpace( true ); // remove white space between 'const' and the next token
			continue;
		}

		// maintain indentation
		if ( token == "{" ) {
			program += ( token.linesCrossed > 0 ) ? newline : ( token.WhiteSpaceBeforeToken() > 0 ? " " : "" );
			program += "{";

			int len = Min( idStr::Length( newline ) + 1, (int)sizeof( newline ) - 1 );
			newline[len - 1] = '\t';
			newline[len - 0] = '\0';
			continue;
		}
		if ( token == "}" ) {
			int len = Max( idStr::Length( newline ) - 1, 0 );
			newline[len] = '\0';

			program += ( token.linesCrossed > 0 ) ? newline : ( token.WhiteSpaceBeforeToken() > 0 ? " " : "" );
			program += "}";
			continue;
		}

		// check for a type conversion
		bool foundType = false;
		for ( int i = 0; typeConversion[i].typeCG != NULL; i++ ) {
			if ( token.Cmp( typeConversion[i].typeCG ) == 0 ) {
				program += ( token.linesCrossed > 0 ) ? newline : ( token.WhiteSpaceBeforeToken() > 0 ? " " : "" );
				program += typeConversion[i].typeGLSL;
				foundType = true;
				break;
			}
		}
		if ( foundType ) {
			continue;
		}

		if ( r_useUniformArrays.GetBool() ) {
			// check for uniforms that need to be converted to the array
			bool isUniform = false;
			for ( int i = 0; i < uniformList.Num(); i++ ) {
				if ( token == uniformList[i] ) {
					program += ( token.linesCrossed > 0 ) ? newline : ( token.WhiteSpaceBeforeToken() > 0 ? " " : "" );
					program += va( "%s[%d /* %s */]", uniformArrayName, i, uniformList[i].c_str() );
					isUniform = true;
					break;
				}
			}
			if ( isUniform ) {
				continue;
			}
		}

		// check for input/output parameters
		if ( src.CheckTokenString( "." ) ) {

			if ( token == "vertex" || token == "fragment" ) {
				idToken member;
				src.ReadToken( &member );

				bool foundInOut = false;
				for ( int i = 0; i < varsIn.Num(); i++ ) {
					if ( member.Cmp( varsIn[i].nameCg ) == 0 ) {
						program += ( token.linesCrossed > 0 ) ? newline : ( token.WhiteSpaceBeforeToken() > 0 ? " " : "" );
						program += varsIn[i].nameGLSL;
						foundInOut = true;
						break;
					}
				}
				if ( !foundInOut ) {
					src.Error( "invalid input parameter %s.%s", token.c_str(), member.c_str() );
					program += ( token.linesCrossed > 0 ) ? newline : ( token.WhiteSpaceBeforeToken() > 0 ? " " : "" );
					program += token;
					program += ".";
					program += member;
				}
				continue;
			}

			if ( token == "result" ) {
				idToken member;
				src.ReadToken( &member );

				bool foundInOut = false;
				for ( int i = 0; i < varsOut.Num(); i++ ) {
					if ( member.Cmp( varsOut[i].nameCg ) == 0 ) {
						program += ( token.linesCrossed > 0 ) ? newline : ( token.WhiteSpaceBeforeToken() > 0 ? " " : "" );
						program += varsOut[i].nameGLSL;
						foundInOut = true;
						break;
					}
				}
				if ( !foundInOut ) {
					src.Error( "invalid output parameter %s.%s", token.c_str(), member.c_str() );
					program += ( token.linesCrossed > 0 ) ? newline : ( token.WhiteSpaceBeforeToken() > 0 ? " " : "" );
					program += token;
					program += ".";
					program += member;
				}
				continue;
			}

			program += ( token.linesCrossed > 0 ) ? newline : ( token.WhiteSpaceBeforeToken() > 0 ? " " : "" );
			program += token;
			program += ".";
			continue;
		}

		// check for a function conversion
		bool foundFunction = false;
		for ( int i = 0; builtinConversion[i].nameCG != NULL; i++ ) {
			if ( token.Cmp( builtinConversion[i].nameCG ) == 0 ) {
				program += ( token.linesCrossed > 0 ) ? newline : ( token.WhiteSpaceBeforeToken() > 0 ? " " : "" );
				program += builtinConversion[i].nameGLSL;
				foundFunction = true;
				break;
			}
		}
		if ( foundFunction ) {
			continue;
		}

		program += ( token.linesCrossed > 0 ) ? newline : ( token.WhiteSpaceBeforeToken() > 0 ? " " : "" );
		program += token;
	}

	idStr out;

	if ( isVertexProgram ) {
		out.ReAllocate( idStr::Length( vertexInsert ) + in.Length() * 2, false );
		out += vertexInsert;
	} else {
		out.ReAllocate( idStr::Length( fragmentInsert ) + in.Length() * 2, false );
		out += fragmentInsert;
	}

	if ( uniformList.Num() > 0 ) {
		if ( r_useUniformArrays.GetBool() ) {
			out += va( "\nuniform vec4 %s[%d];\n", uniformArrayName, uniformList.Num() );
		} else {
			out += "\n";
			for ( int i = 0; i < uniformList.Num(); i++ ) {
				out += "uniform vec4 ";
				out += uniformList[i];
				out += ";\n";
			}
		}
	}

	out += program;

	for ( int i = 0; i < uniformList.Num(); i++ ) {
		uniforms += uniformList[i];
		uniforms += "\n";
	}
	uniforms += "\n";

	return out;
}
/*
========================
idSoundSample_XAudio2::Load
========================
*/
void idSoundSample_XAudio2::LoadResource()
{
	FreeData();
	
	if( idStr::Icmpn( GetName(), "_default", 8 ) == 0 )
	{
		MakeDefault();
		return;
	}
	
	if( s_noSound.GetBool() )
	{
		MakeDefault();
		return;
	}
	
	loaded = false;
	
	for( int i = 0; i < 2; i++ )
	{
		idStrStatic< MAX_OSPATH > sampleName = GetName();
		if( ( i == 0 ) && !sampleName.Replace( "/vo/", va( "/vo/%s/", sys_lang.GetString() ) ) )
		{
			i++;
		}
		idStrStatic< MAX_OSPATH > generatedName = "generated/";
		generatedName.Append( sampleName );
		
		{
			if( s_useCompression.GetBool() )
			{
				sampleName.Append( ".msadpcm" );
			}
			else
			{
				sampleName.Append( ".wav" );
			}
			generatedName.Append( ".idwav" );
		}
		loaded = LoadGeneratedSample( generatedName ) || LoadWav( sampleName );
		
		if( !loaded && s_useCompression.GetBool() )
		{
			sampleName.SetFileExtension( "wav" );
			loaded = LoadWav( sampleName );
		}
		
		if( loaded )
		{
			if( cvarSystem->GetCVarBool( "fs_buildresources" ) )
			{
				fileSystem->AddSamplePreload( GetName() );
				WriteAllSamples( GetName() );
				
				if( sampleName.Find( "/vo/" ) >= 0 )
				{
					for( int i = 0; i < Sys_NumLangs(); i++ )
					{
						const char* lang = Sys_Lang( i );
						if( idStr::Icmp( lang, ID_LANG_ENGLISH ) == 0 )
						{
							continue;
						}
						idStrStatic< MAX_OSPATH > locName = GetName();
						locName.Replace( "/vo/", va( "/vo/%s/", Sys_Lang( i ) ) );
						WriteAllSamples( locName );
					}
				}
			}
			return;
		}
	}
	
	if( !loaded )
	{
		// make it default if everything else fails
		MakeDefault();
	}
	return;
}
/*
========================
idSoundHardware_XAudio2::Init
========================
*/
void idSoundHardware_XAudio2::Init() {

	cmdSystem->AddCommand( "listDevices", listDevices_f, 0, "Lists the connected sound devices", NULL );

	DWORD xAudioCreateFlags = 0;

// RB: not available on Windows 8 SDK
#if !defined(USE_WINRT) && defined(_DEBUG)
	xAudioCreateFlags |= XAUDIO2_DEBUG_ENGINE;
#endif
// RB end

	XAUDIO2_PROCESSOR xAudioProcessor = XAUDIO2_DEFAULT_PROCESSOR;

// RB: not available on Windows 8 SDK
	if ( FAILED( XAudio2Create( &pXAudio2, xAudioCreateFlags, xAudioProcessor ) ) ){
#if !defined(USE_WINRT) && defined(_DEBUG)
		if ( xAudioCreateFlags & XAUDIO2_DEBUG_ENGINE ) {
			// in case the debug engine isn't installed
			xAudioCreateFlags &= ~XAUDIO2_DEBUG_ENGINE;
			if ( FAILED( XAudio2Create( &pXAudio2, xAudioCreateFlags, xAudioProcessor ) ) ) {		
				idLib::FatalError( "Failed to create XAudio2 engine.  Try installing the latest DirectX." );
				return;
			}
		} else 
#endif
// RB end
		{
			idLib::FatalError( "Failed to create XAudio2 engine.  Try installing the latest DirectX." );
			return;
		}
	}
#ifdef _DEBUG
	XAUDIO2_DEBUG_CONFIGURATION debugConfiguration = { 0 };
	debugConfiguration.TraceMask = XAUDIO2_LOG_WARNINGS;
	debugConfiguration.BreakMask = XAUDIO2_LOG_ERRORS;
	pXAudio2->SetDebugConfiguration( &debugConfiguration );
#endif

	// Register the sound engine callback
	pXAudio2->RegisterForCallbacks( &soundEngineCallback );
	soundEngineCallback.hardware = this;
    DWORD outputSampleRate = 44100; // Max( (DWORD)XAUDIO2FX_REVERB_MIN_FRAMERATE, Min( (DWORD)XAUDIO2FX_REVERB_MAX_FRAMERATE, deviceDetails.OutputFormat.Format.nSamplesPerSec ) );

	idCmdArgs args;
	listDevices_f( args );

    // RB: not available on Windows 8 SDK
#if defined(USE_WINRT) //(_WIN32_WINNT >= 0x0602 /*_WIN32_WINNT_WIN8*/)
    AudioDevice defaultDevice;
    std::vector<AudioDevice> vAudioDevices = EnumerateAudioDevices(&defaultDevice);

    if (!vAudioDevices.empty()) {

        AudioDevice selectedDevice;

        int preferredDevice = s_device.GetInteger();
        bool validPreference = (preferredDevice >= 0 && preferredDevice < (int)vAudioDevices.size());
        // Do we select a device automatically?
        if (validPreference)
        {
            // Use the user's selected device
            selectedDevice = vAudioDevices[preferredDevice];
        }
        else if (!defaultDevice.id.empty())
        {
            // Fall back to the default device if there is one
            selectedDevice = defaultDevice;
        }
        else
        {
            // Fall back to first device
            selectedDevice = vAudioDevices[0];
        }
        
        if (SUCCEEDED(pXAudio2->CreateMasteringVoice(&pMasterVoice,
                                                     XAUDIO2_DEFAULT_CHANNELS,
                                                     outputSampleRate,
                                                     0,
                                                     selectedDevice.id.c_str(),
                                                     NULL,
                                                     AudioCategory_GameEffects)))
        {
            XAUDIO2_VOICE_DETAILS deviceDetails;
            pMasterVoice->GetVoiceDetails(&deviceDetails);

            pMasterVoice->SetVolume(DBtoLinear(s_volume_dB.GetFloat()));

            outputChannels = deviceDetails.InputChannels;
            DWORD win8_channelMask;
            pMasterVoice->GetChannelMask(&win8_channelMask);

            channelMask = (unsigned int)win8_channelMask;
            idLib::Printf( "Using device %S\n", selectedDevice.name );
        }
        else {
            idLib::Warning("Failed to create master voice");
            pXAudio2->Release();
            pXAudio2 = NULL;
            return;
        }
     }

#else	
    UINT32 deviceCount = 0;
	if( pXAudio2->GetDeviceCount( &deviceCount ) != S_OK || deviceCount == 0 )
	{
		idLib::Warning( "No audio devices found" );
		pXAudio2->Release();
		pXAudio2 = NULL;
		return;
	}
	
	int preferredDevice = s_device.GetInteger();
	if( preferredDevice < 0 || preferredDevice >= ( int )deviceCount )
	{
		int preferredChannels = 0;
		for( unsigned int i = 0; i < deviceCount; i++ )
		{
			XAUDIO2_DEVICE_DETAILS deviceDetails;
			if( pXAudio2->GetDeviceDetails( i, &deviceDetails ) != S_OK )
			{
				continue;
			}
	
			if( deviceDetails.Role & DefaultGameDevice )
			{
				// if we find a device the user marked as their preferred 'game' device, then always use that
				preferredDevice = i;
				preferredChannels = deviceDetails.OutputFormat.Format.nChannels;
				break;
			}
	
			if( deviceDetails.OutputFormat.Format.nChannels > preferredChannels )
			{
				preferredDevice = i;
				preferredChannels = deviceDetails.OutputFormat.Format.nChannels;
			}
		}
	}
	
	idLib::Printf( "Using device %d\n", preferredDevice );
	
	XAUDIO2_DEVICE_DETAILS deviceDetails;
	if( pXAudio2->GetDeviceDetails( preferredDevice, &deviceDetails ) != S_OK )
	{
		// One way this could happen is if a device is removed between the loop and this line of code
		// Highly unlikely but possible
		idLib::Warning( "Failed to get device details" );
		pXAudio2->Release();
		pXAudio2 = NULL;
		return;
	}
	

	if( FAILED( pXAudio2->CreateMasteringVoice( &pMasterVoice, XAUDIO2_DEFAULT_CHANNELS, outputSampleRate, 0, preferredDevice, NULL ) ) )
	{
		idLib::Warning( "Failed to create master voice" );
		pXAudio2->Release();
		pXAudio2 = NULL;
		return;
	}
	pMasterVoice->SetVolume( DBtoLinear( s_volume_dB.GetFloat() ) );
	
	outputChannels = deviceDetails.OutputFormat.Format.nChannels;
	channelMask = deviceDetails.OutputFormat.dwChannelMask;

#endif // #if (_WIN32_WINNT < 0x0602 /*_WIN32_WINNT_WIN8*/)

	idSoundVoice::InitSurround( outputChannels, channelMask );

	// ---------------------
	// Initialize the Doom classic sound system.
	// ---------------------
	I_InitSoundHardware( outputChannels, channelMask );

	// ---------------------
	// Create VU Meter Effect
	// ---------------------
	IUnknown * vuMeter = NULL;
	XAudio2CreateVolumeMeter( &vuMeter, 0 );

	XAUDIO2_EFFECT_DESCRIPTOR descriptor;
	descriptor.InitialState = true;
	descriptor.OutputChannels = outputChannels;
	descriptor.pEffect = vuMeter;

	XAUDIO2_EFFECT_CHAIN chain;
	chain.EffectCount = 1;
	chain.pEffectDescriptors = &descriptor;

	pMasterVoice->SetEffectChain( &chain );

	vuMeter->Release();

	// ---------------------
	// Create VU Meter Graph
	// ---------------------

	vuMeterRMS = console->CreateGraph( outputChannels );
	vuMeterPeak = console->CreateGraph( outputChannels );
	vuMeterRMS->Enable( false );
	vuMeterPeak->Enable( false );

	memset( vuMeterPeakTimes, 0, sizeof( vuMeterPeakTimes ) );

	vuMeterPeak->SetFillMode( idDebugGraph::GRAPH_LINE );
	vuMeterPeak->SetBackgroundColor( idVec4( 0.0f, 0.0f, 0.0f, 0.0f ) );

	vuMeterRMS->AddGridLine( 0.500f, idVec4( 0.5f, 0.5f, 0.5f, 1.0f ) );
	vuMeterRMS->AddGridLine( 0.250f, idVec4( 0.5f, 0.5f, 0.5f, 1.0f ) );
	vuMeterRMS->AddGridLine( 0.125f, idVec4( 0.5f, 0.5f, 0.5f, 1.0f ) );

	const char * channelNames[] = { "L", "R", "C", "S", "Lb", "Rb", "Lf", "Rf", "Cb", "Ls", "Rs" };
	for ( int i = 0, ci = 0; ci < sizeof( channelNames ) / sizeof( channelNames[0] ); ci++ ) {
		if ( ( channelMask & BIT( ci ) ) == 0 ) {
			continue;
		}
		vuMeterRMS->SetLabel( i, channelNames[ ci ] );
		i++;
	}

	// ---------------------
	// Create submix buffer
	// ---------------------
	if ( FAILED( pXAudio2->CreateSubmixVoice( &pSubmixVoice, 1, outputSampleRate, 0, 0, NULL, NULL ) ) ) {
		idLib::FatalError( "Failed to create submix voice" );
	}

	// XAudio doesn't really impose a maximum number of voices
	voices.SetNum( voices.Max() );
	freeVoices.SetNum( voices.Max() );
	zombieVoices.SetNum( 0 );
	for ( int i = 0; i < voices.Num(); i++ ) {
		freeVoices[i] = &voices[i];
	}
// RB end
}
Exemple #9
0
/*
=================
idUsercmdGenLocal::MouseMove
=================
*/
void idUsercmdGenLocal::MouseMove( void ) {
	float		mx, my, strafeMx, strafeMy;
	static int	history[8][2];
	static int	historyCounter;
	int			i;

	history[historyCounter&7][0] = mouseDx;
	history[historyCounter&7][1] = mouseDy;

	// allow mouse movement to be smoothed together
	int smooth = m_smooth.GetInteger();
	if ( smooth < 1 ) {
		smooth = 1;
	}
	if ( smooth > 8 ) {
		smooth = 8;
	}
	mx = 0;
	my = 0;
	for ( i = 0 ; i < smooth ; i++ ) {
		mx += history[ ( historyCounter - i + 8 ) & 7 ][0];
		my += history[ ( historyCounter - i + 8 ) & 7 ][1];
	}
	mx /= smooth;
	my /= smooth;

	// use a larger smoothing for strafing
	smooth = m_strafeSmooth.GetInteger();
	if ( smooth < 1 ) {
		smooth = 1;
	}
	if ( smooth > 8 ) {
		smooth = 8;
	}
	strafeMx = 0;
	strafeMy = 0;
	for ( i = 0 ; i < smooth ; i++ ) {
		strafeMx += history[ ( historyCounter - i + 8 ) & 7 ][0];
		strafeMy += history[ ( historyCounter - i + 8 ) & 7 ][1];
	}
	strafeMx /= smooth;
	strafeMy /= smooth;

	historyCounter++;

	if ( idMath::Fabs( mx ) > 1000 || idMath::Fabs( my ) > 1000 ) {
		Sys_DebugPrintf( "idUsercmdGenLocal::MouseMove: Ignoring ridiculous mouse delta.\n" );
		mx = my = 0;
	}

	mx *= sensitivity.GetFloat();
	my *= sensitivity.GetFloat();

	if ( m_showMouseRate.GetBool() ) {
		Sys_DebugPrintf( "[%3i %3i  = %5.1f %5.1f = %5.1f %5.1f] ", mouseDx, mouseDy, mx, my, strafeMx, strafeMy );
	}

	mouseDx = 0;
	mouseDy = 0;

	if ( !strafeMx && !strafeMy ) {
		return;
	}

	if ( ButtonState( UB_STRAFE ) || !( cmd.buttons & BUTTON_MLOOK ) ) {
		// add mouse X/Y movement to cmd
		strafeMx *= m_strafeScale.GetFloat();
		strafeMy *= m_strafeScale.GetFloat();
		// clamp as a vector, instead of separate floats
		float len = sqrt( strafeMx * strafeMx + strafeMy * strafeMy );
		if ( len > 127 ) {
			strafeMx = strafeMx * 127 / len;
			strafeMy = strafeMy * 127 / len;
		}
	}

	if ( !ButtonState( UB_STRAFE ) ) {
		viewangles[YAW] -= m_yaw.GetFloat() * mx;
	} else {
		cmd.rightmove = idMath::ClampChar( (int)(cmd.rightmove + strafeMx) );
	}

	if ( !ButtonState( UB_STRAFE ) && ( cmd.buttons & BUTTON_MLOOK ) ) {
		viewangles[PITCH] += m_pitch.GetFloat() * my;
	} else {
		cmd.forwardmove = idMath::ClampChar( (int)(cmd.forwardmove - strafeMy) );
	}
}
Exemple #10
0
/*
========================
idAimAssist::FindAimAssistTarget
========================
*/
idEntity* idAimAssist::FindAimAssistTarget( idVec3& targetPos ) {
	if ( player == NULL ) {
		return NULL;
	}

	//TO DO: Make this faster
	//TO DO: Defer Traces
	idEntity *	optimalTarget = NULL;
	float		currentBestScore = -idMath::INFINITY;
	targetPos = vec3_zero;

	idVec3 cameraPos;
	idMat3 cameraAxis;
	player->GetViewPos( cameraPos, cameraAxis );

	float maxDistanceSquared = Square( aa_targetMaxDistance.GetFloat() );

	idVec3 dirToTarget;
	float  distanceToTargetSquared;
	idVec3 primaryTargetPos;
	idVec3 secondaryTargetPos;
	
	for ( idEntity * entity = gameLocal.aimAssistEntities.Next(); entity != NULL; entity = entity->aimAssistNode.Next() ) {
		if ( !entity->IsActive() ) {
			continue;
		}

		if ( entity->IsHidden() ) {
			continue;
		}

		if ( entity->IsType( idActor::Type ) ) {
			idActor * actor = static_cast<idActor *>( entity );
			if ( actor->team == player->team ) {
				// In DM, LMS, and Tourney, all players are on the same team
				if ( gameLocal.gameType == GAME_CTF || gameLocal.gameType == GAME_TDM || gameLocal.gameType == GAME_SP ) {
					continue;
				}
			}
		}

		if ( entity->IsType( idAI::Type ) ) {
			idAI * aiEntity = static_cast<idAI *>( entity );
			if ( aiEntity->ReactionTo( player ) == ATTACK_IGNORE ) {
				continue;
			}
		}

		// check whether we have a valid target position for this entity - skip it if we don't
		if ( !ComputeTargetPos( entity, primaryTargetPos, secondaryTargetPos ) ) {
			continue;
		}

		// is it close enough to us
		dirToTarget = primaryTargetPos-cameraPos;
		distanceToTargetSquared = dirToTarget.LengthSqr();
		if ( distanceToTargetSquared > maxDistanceSquared ) {
			continue;
		}

		// Compute a score in the range of 0..1 for how much are looking towards the target.
		idVec3 forward = cameraAxis[ 0 ];
		forward.Normalize();
		dirToTarget.Normalize();
		float ViewDirDotTargetDir = idMath::ClampFloat( -1.0f, 1.0f, forward * dirToTarget ); // compute the dot and clamp to account for floating point error

		// throw out anything thats outside of weapon's global FOV.
		if ( ViewDirDotTargetDir < 0.0f ) {
			continue;
		}

		// to be consistent we always use the primaryTargetPos to compute the score for this entity
		float computedScore = ComputeEntityAimAssistScore( primaryTargetPos, cameraPos, cameraAxis );

		// check if the current score beats our current best score and we have line of sight to it.
		if ( computedScore > currentBestScore ) {

			// determine if the current target is in our line of sight
			trace_t tr;
			gameLocal.clip.TracePoint( tr, cameraPos, primaryTargetPos, MASK_MONSTERSOLID, player );
		
			// did our trace fail?
			if ( ( ( tr.fraction < 1.0f ) && ( tr.c.entityNum != entity->entityNumber ) ) || ( tr.fraction >= 1.0f ) ) {

				// if the collision test failed for the primary position -- check the secondary position
				trace_t tr2;
				gameLocal.clip.TracePoint( tr2, cameraPos, secondaryTargetPos, MASK_MONSTERSOLID, player );

				if ( ( ( tr2.fraction < 1.0f ) && ( tr2.c.entityNum != entity->entityNumber ) ) || ( tr2.fraction >= 1.0f ) ) {
					// if the secondary position is also not visible then give up
					continue;
				}

				// we can see the secondary target position so we should consider this entity but use
				// the secondary position as the target position
				targetPos = secondaryTargetPos;
			}  else {
				targetPos = primaryTargetPos;
			}

			// if we got here then this is our new best score
			optimalTarget = entity;
			currentBestScore = computedScore;
		}
	}

	return optimalTarget;
}
Exemple #11
0
/*
================
idServerScan::IsFiltered
================
*/
bool idServerScan::IsFiltered( const networkServer_t server ) {
	int i;
	const idKeyValue *keyval;

	// OS support filter
#if 0
	// filter out pure servers that won't provide checksumed game code for client OS
	keyval = server.serverInfo.FindKey( "si_pure" );
	if ( keyval && !idStr::Cmp( keyval->GetValue(), "1" ) ) {
		if ( ( server.OSMask & ( 1 << BUILD_OS_ID ) ) == 0 ) {
			return true;
		}
	}
#else
	if ( ( server.OSMask & ( 1 << BUILD_OS_ID ) ) == 0 ) {
		return true;
	}
#endif
	// password filter
	keyval = server.serverInfo.FindKey( "si_usePass" );
	if ( keyval && gui_filter_password.GetInteger() == 1 ) {
		// show passworded only
		if ( keyval->GetValue()[ 0 ] == '0' ) {
			return true;
		}
	} else if ( keyval && gui_filter_password.GetInteger() == 2 ) {
		// show no password only
		if ( keyval->GetValue()[ 0 ] != '0' ) {
			return true;
		}
	}
	// players filter
	keyval = server.serverInfo.FindKey( "si_maxPlayers" );
	if ( keyval ) {
		if ( gui_filter_players.GetInteger() == 1 && server.clients == atoi( keyval->GetValue() ) ) {
			return true;
		} else if ( gui_filter_players.GetInteger() == 2 && ( !server.clients || server.clients == atoi( keyval->GetValue() ) ) ) {
			return true;
		}
	}
	// gametype filter
	keyval = server.serverInfo.FindKey( "si_gameType" );
	if ( keyval && gui_filter_gameType.GetInteger() ) {
		i = 0;
		while ( l_gameTypes[ i ] ) {
			if ( !keyval->GetValue().Icmp( l_gameTypes[ i ] ) ) {
				break;
			}
			i++;
		}
		if ( l_gameTypes[ i ] && i != gui_filter_gameType.GetInteger() -1 ) {
			return true;
		}
	}
	// idle server filter
	keyval = server.serverInfo.FindKey( "si_idleServer" );
	if ( keyval && !gui_filter_idle.GetInteger() ) {
		if ( !keyval->GetValue().Icmp( "1" ) ) {
			return true;
		}
	}

	// autofilter D3XP games if the user does not has the XP installed
	if(!fileSystem->HasD3XP() && !idStr::Icmp(server.serverInfo.GetString( "fs_game" ), "d3xp")) {
		return true;
	}

	// filter based on the game doom or XP
	if(gui_filter_game.GetInteger() == 1) { //Only Doom
		if(idStr::Icmp(server.serverInfo.GetString("fs_game"), "")) {
			return true;
		}
	} else if(gui_filter_game.GetInteger() == 2) { //Only D3XP
		if(idStr::Icmp(server.serverInfo.GetString("fs_game"), "d3xp")) {
			return true;
		}
	}

	return false;
}
Exemple #12
0
/*
===============
idSoundShader::ParseShader
===============
*/
bool idSoundShader::ParseShader( idLexer &src ) {
	idToken		token;

	parms.minDistance = 1;
	parms.maxDistance = 10;
	parms.volume = 1;
	parms.shakes = 0;
	parms.soundShaderFlags = 0;
	parms.soundClass = 0;

	speakerMask = 0;
	altSound = NULL;

	entries.Clear();

	while ( 1 ) {
		if ( !src.ExpectAnyToken( &token ) ) {
			return false;
		}
		// end of definition
		else if ( token == "}" ) {
			break;
		}
		// minimum number of sounds
		else if ( !token.Icmp( "minSamples" ) ) {
			src.ParseInt();
		}
		// description
		else if ( !token.Icmp( "description" ) ) {
			src.ReadTokenOnLine( &token );
		}
		// mindistance
		else if ( !token.Icmp( "mindistance" ) ) {
			parms.minDistance = src.ParseFloat();
		}
		// maxdistance
		else if ( !token.Icmp( "maxdistance" ) ) {
			parms.maxDistance = src.ParseFloat();
		}
		// shakes screen
		else if ( !token.Icmp( "shakes" ) ) {
			src.ExpectAnyToken( &token );
			if ( token.type == TT_NUMBER ) {
				parms.shakes = token.GetFloatValue();
			} else {
				src.UnreadToken( &token );
				parms.shakes = 1.0f;
			}
		}
		// reverb
		else if ( !token.Icmp( "reverb" ) ) {
			src.ParseFloat();
			if ( !src.ExpectTokenString( "," ) ) {
				src.FreeSource();
				return false;
			}
			src.ParseFloat();
			// no longer supported
		}
		// volume
		else if ( !token.Icmp( "volume" ) ) {
			parms.volume = src.ParseFloat();
		}
		// leadinVolume is used to allow light breaking leadin sounds to be much louder than the broken loop
		else if ( !token.Icmp( "leadinVolume" ) ) {
			leadinVolume = src.ParseFloat();
			leadin = true;
		}
		// speaker mask
		else if ( !token.Icmp( "mask_center" ) ) {
			speakerMask |= 1<<SPEAKER_CENTER;
		}
		// speaker mask
		else if ( !token.Icmp( "mask_left" ) ) {
			speakerMask |= 1<<SPEAKER_LEFT;
		}
		// speaker mask
		else if ( !token.Icmp( "mask_right" ) ) {
			speakerMask |= 1<<SPEAKER_RIGHT;
		}
		// speaker mask
		else if ( !token.Icmp( "mask_backright" ) ) {
			speakerMask |= 1<<SPEAKER_BACKRIGHT;
		}
		// speaker mask
		else if ( !token.Icmp( "mask_backleft" ) ) {
			speakerMask |= 1<<SPEAKER_BACKLEFT;
		}
		// speaker mask
		else if ( !token.Icmp( "mask_lfe" ) ) {
			speakerMask |= 1<<SPEAKER_LFE;
		}
		// soundClass
		else if ( !token.Icmp( "soundClass" ) ) {
			parms.soundClass = src.ParseInt();
			if ( parms.soundClass < 0 || parms.soundClass >= SOUND_MAX_CLASSES ) {
				src.Warning( "SoundClass out of range" );
				return false;
			}
		}
		// altSound
		else if ( !token.Icmp( "altSound" ) ) {
			if ( !src.ExpectAnyToken( &token ) ) {
				return false;
			}
			altSound = declManager->FindSound( token.c_str() );
		}
		// ordered
		else if ( !token.Icmp( "ordered" ) ) {
			// no longer supported
		}
		// no_dups
		else if ( !token.Icmp( "no_dups" ) ) {
			parms.soundShaderFlags |= SSF_NO_DUPS;
		}
		// no_flicker
		else if ( !token.Icmp( "no_flicker" ) ) {
			parms.soundShaderFlags |= SSF_NO_FLICKER;
		}
		// plain
		else if ( !token.Icmp( "plain" ) ) {	
			// no longer supported
		}
		// looping
		else if ( !token.Icmp( "looping" ) ) {
			parms.soundShaderFlags |= SSF_LOOPING;
		}
		// no occlusion
		else if ( !token.Icmp( "no_occlusion" ) ) {
			parms.soundShaderFlags |= SSF_NO_OCCLUSION;
		}
		// private
		else if ( !token.Icmp( "private" ) ) {
			parms.soundShaderFlags |= SSF_PRIVATE_SOUND;
		}
		// antiPrivate
		else if ( !token.Icmp( "antiPrivate" ) ) {
			parms.soundShaderFlags |= SSF_ANTI_PRIVATE_SOUND;
		}
		// once
		else if ( !token.Icmp( "playonce" ) ) {
			parms.soundShaderFlags |= SSF_PLAY_ONCE;
		}
		// global
		else if ( !token.Icmp( "global" ) ) {
			parms.soundShaderFlags |= SSF_GLOBAL;
		}
		// unclamped
		else if ( !token.Icmp( "unclamped" ) ) {
			parms.soundShaderFlags |= SSF_UNCLAMPED;
		}
		// omnidirectional
		else if ( !token.Icmp( "omnidirectional" ) ) {
			parms.soundShaderFlags |= SSF_OMNIDIRECTIONAL;
		}
		else if ( !token.Icmp( "onDemand" ) ) {
			// no longer loading sounds on demand
		}
		// the wave files
		else if ( !token.Icmp( "leadin" ) ) {
			leadin = true;
		} else if ( token.Find( ".wav", false ) != -1 || token.Find( ".ogg", false ) != -1 ) {
			if ( token.IcmpPrefixPath( "sound/vo/" ) == 0 || token.IcmpPrefixPath( "sound/guis/" ) == 0 ) {
				parms.soundShaderFlags |= SSF_VO;
			}
			if ( token.IcmpPrefixPath( "sound/musical/" ) == 0 ) {
				parms.soundShaderFlags |= SSF_MUSIC;
			}
			// add to the wav list
			if ( s_maxSamples.GetInteger() == 0 || ( s_maxSamples.GetInteger() > 0 && entries.Num() < s_maxSamples.GetInteger() ) ) {
				entries.Append( soundSystemLocal.LoadSample( token.c_str() ) );
			}
		} else {
			src.Warning( "unknown token '%s'", token.c_str() );
			return false;
		}
	}

	return true;
}
/*
==========
idAudioHardwareOSX::Initialize
==========
*/
bool idAudioHardwareOSX::Initialize( )
{

    UInt32			size;
    OSStatus		status;
    int				i, deviceCount;
    AudioDeviceID	*deviceList;
    char			buf[ 1024 ];

    status = AudioHardwareGetPropertyInfo( kAudioHardwarePropertyDevices, &size, NULL );
    if ( status != kAudioHardwareNoError )
    {
        common->Warning( "AudioHardwareGetPropertyInfo kAudioHardwarePropertyDevices failed. status: %s", ExtractStatus( status ) );
        InitFailed();
        return false;
    }

    deviceCount = size / sizeof( AudioDeviceID );
    if ( !deviceCount )
    {
        common->Printf( "No sound device found\n" );
        InitFailed();
        return false;
    }

    deviceList = (AudioDeviceID*)malloc( size );
    status = AudioHardwareGetProperty( kAudioHardwarePropertyDevices, &size, deviceList );
    if ( status != kAudioHardwareNoError )
    {
        common->Warning( "AudioHardwareGetProperty kAudioHardwarePropertyDevices failed. status: %s", ExtractStatus( status ) );
        free( deviceList );
        InitFailed();
        return false;
    }

    common->Printf( "%d sound device(s)\n", deviceCount );
    for( i = 0; i < deviceCount; i++ )
    {
        size = 1024;
        status = AudioDeviceGetProperty( deviceList[ i ], 0, false, kAudioDevicePropertyDeviceName, &size, buf );
        if ( status != kAudioHardwareNoError )
        {
            common->Warning( "AudioDeviceGetProperty kAudioDevicePropertyDeviceName %d failed. status: %s", i, ExtractStatus( status ) );
            free( deviceList );
            InitFailed();
            return false;
        }
        common->Printf( "  %d: ID %d, %s - ", i, deviceList[ i ], buf );
        size = 1024;
        status = AudioDeviceGetProperty( deviceList[ i ], 0, false, kAudioDevicePropertyDeviceManufacturer, &size, buf );
        if ( status != kAudioHardwareNoError )
        {
            common->Warning( "AudioDeviceGetProperty kAudioDevicePropertyDeviceManufacturer %d failed. status: %s", i, ExtractStatus( status ) );
            free( deviceList );
            InitFailed();
            return false;
        }
        common->Printf( "%s\n", buf );
    }

    if ( s_device.GetInteger() != -1 && s_device.GetInteger() < deviceCount )
    {
        selectedDevice = deviceList[ s_device.GetInteger() ];
        common->Printf( "s_device: device ID %d\n", selectedDevice );
    }
    else
    {
        size = sizeof( selectedDevice );
        status = AudioHardwareGetProperty( kAudioHardwarePropertyDefaultOutputDevice, &size, &selectedDevice );
        if ( status != kAudioHardwareNoError )
        {
            common->Warning( "AudioHardwareGetProperty kAudioHardwarePropertyDefaultOutputDevice failed. status: %s", ExtractStatus( status ) );

            free( deviceList );
            InitFailed();
            return false;
        }
        common->Printf( "select default device, ID %d\n", selectedDevice );
    }

    free( deviceList );
    deviceList = NULL;

    /*
    // setup a listener to watch for changes to properties
    status = AudioDeviceAddPropertyListener( selectedDevice, 0, false, kAudioDeviceProcessorOverload, DeviceListener, this );
    if ( status != kAudioHardwareNoError ) {
    	common->Warning( "AudioDeviceAddPropertyListener kAudioDeviceProcessorOverload failed. status: %s", ExtractStatus( status ) );
    	InitFailed();
    	return;
    }
    */

    Float64 sampleRate;
    size = sizeof( sampleRate );
    status = AudioDeviceGetProperty( selectedDevice, 0, false, kAudioDevicePropertyNominalSampleRate, &size, &sampleRate );
    if ( status != kAudioHardwareNoError )
    {
        common->Warning( "AudioDeviceGetProperty %d kAudioDevicePropertyNominalSampleRate failed. status: %s", selectedDevice, ExtractStatus( status ) );
        InitFailed();
        return false;
    }
    common->Printf( "current nominal rate: %g\n", sampleRate );

    if ( sampleRate != PRIMARYFREQ )
    {

        GetAvailableNominalSampleRates();

        sampleRate = PRIMARYFREQ;
        common->Printf( "setting rate to: %g\n", sampleRate );
        status = AudioDeviceSetProperty( selectedDevice, NULL, 0, false, kAudioDevicePropertyNominalSampleRate, size, &sampleRate );
        if ( status != kAudioHardwareNoError )
        {
            common->Warning( "AudioDeviceSetProperty %d kAudioDevicePropertyNominalSampleRate %g failed. status: %s", selectedDevice, sampleRate, ExtractStatus( status ) );
            InitFailed();
            return false;
        }
    }

    UInt32 frameSize;
    size = sizeof( UInt32 );
    status = AudioDeviceGetProperty( selectedDevice, 0, false, kAudioDevicePropertyBufferFrameSize, &size, &frameSize );
    if ( status != kAudioHardwareNoError )
    {
        common->Warning( "AudioDeviceGetProperty %d kAudioDevicePropertyBufferFrameSize failed.status: %s", selectedDevice, ExtractStatus( status ) );
        InitFailed();
        return false;
    }
    common->Printf( "current frame size: %d\n", frameSize );

    // get the allowed frame size range
    AudioValueRange frameSizeRange;
    size = sizeof( AudioValueRange );
    status = AudioDeviceGetProperty( selectedDevice, 0, false, kAudioDevicePropertyBufferFrameSizeRange, &size, &frameSizeRange );
    if ( status != kAudioHardwareNoError )
    {
        common->Warning( "AudioDeviceGetProperty %d kAudioDevicePropertyBufferFrameSizeRange failed. status: %s", selectedDevice, ExtractStatus( status ) );
        InitFailed();
        return false;
    }
    common->Printf( "frame size allowed range: %g %g\n", frameSizeRange.mMinimum, frameSizeRange.mMaximum );

    if ( frameSizeRange.mMaximum < MIXBUFFER_SAMPLES )
    {
        common->Warning( "can't obtain the required frame size of %d bits", MIXBUFFER_SAMPLES );
        InitFailed();
        return false;
    }

    if ( frameSize != (unsigned int)MIXBUFFER_SAMPLES )
    {
        frameSize = MIXBUFFER_SAMPLES;
        common->Printf( "setting frame size to: %d\n", frameSize );
        size = sizeof( frameSize );
        status = AudioDeviceSetProperty( selectedDevice, NULL, 0, false, kAudioDevicePropertyBufferFrameSize, size, &frameSize );
        if ( status != kAudioHardwareNoError )
        {
            common->Warning( "AudioDeviceSetProperty %d kAudioDevicePropertyBufferFrameSize failed. status: %s", selectedDevice, ExtractStatus( status ) );
            InitFailed();
            return false;
        }
    }

    if ( idSoundSystemLocal::s_numberOfSpeakers.GetInteger() != 2 )
    {
        common->Warning( "only stereo sound currently supported" );
        idSoundSystemLocal::s_numberOfSpeakers.SetInteger( 2 );
    }
    UInt32 channels[ 2 ];
    size = 2 * sizeof( UInt32 );
    status = AudioDeviceGetProperty( selectedDevice, 0, false, 	kAudioDevicePropertyPreferredChannelsForStereo, &size, &channels );
    if ( status != kAudioHardwareNoError )
    {
        common->Warning( "AudioDeviceGetProperty %d kAudioDevicePropertyPreferredChannelsForStereo failed. status: %s", selectedDevice, ExtractStatus( status ) );
        InitFailed();
        return false;
    }
    common->Printf( "using stereo channel IDs %d %d\n", channels[ 0 ], channels[ 1 ] );

    status = AudioDeviceAddIOProc( selectedDevice, DeviceIOProc, NULL );
    if ( status != kAudioHardwareNoError )
    {
        common->Warning( "AudioDeviceAddIOProc failed. status: %s", ExtractStatus( status ) );
        InitFailed();
        return false;
    }
    activeIOProc = true;

    status = AudioDeviceStart( selectedDevice, DeviceIOProc );
    if ( status != kAudioHardwareNoError )
    {
        common->Warning( "AudioDeviceStart failed. status: %s", ExtractStatus( status ) );
        InitFailed();
        return false;
    }

    /*
    // allocate the mix buffer
    // it has the space for ROOM_SLICES_IN_BUFFER DeviceIOProc loops
    mixBufferSize =  dwSpeakers * dwSampleSize * dwPrimaryBitRate * ROOM_SLICES_IN_BUFFER / 8;
    mixBuffer = malloc( mixBufferSize );
    memset( mixBuffer, 0, mixBufferSize );
    */

    return true;
}
/*
========================
idLobby::SendMigrationGameData
========================
*/
void idLobby::SendMigrationGameData()
{
	if( net_migration_disable.GetBool() )
	{
		return;
	}
	
	if( sessionCB->GetState() != idSession::INGAME )
	{
		return;
	}
	
	if( !migrationInfo.persistUntilGameEndsData.hasGameData )
	{
		// Haven't been given any migration game data yet
		return;
	}
	
	const int now = Sys_Milliseconds();
	if( nextSendMigrationGameTime > now )
	{
		return;
	}
	
	byte	packetData[ idPacketProcessor::MAX_MSG_SIZE ];
	idBitMsg msg( packetData, sizeof( packetData ) );
	
	// Write global data
	msg.WriteData( &migrationInfo.persistUntilGameEndsData.gameData, sizeof( migrationInfo.persistUntilGameEndsData.gameData ) );
	msg.WriteByte( GetNumLobbyUsers() );
	
	// Write user data
	for( int userIndex = 0; userIndex < GetNumLobbyUsers(); ++userIndex )
	{
		lobbyUser_t* u = GetLobbyUser( userIndex );
		if( u->IsDisconnected() || u->migrationGameData < 0 )
		{
			continue;
		}
		
		u->lobbyUserID.WriteToMsg( msg );
		msg.WriteData( migrationInfo.persistUntilGameEndsData.gameDataUser[ u->migrationGameData ], sizeof( migrationInfo.persistUntilGameEndsData.gameDataUser[ u->migrationGameData ] ) );
	}
	
	// Send to 1 peer
	for( int i = 0; i < peers.Num(); i++ )
	{
		int peerToSend = ( nextSendMigrationGamePeer + i ) % peers.Num();
		
		if( peers[ peerToSend ].IsConnected() && peers[ peerToSend ].loaded )
		{
			if( peers[ peerToSend ].packetProc->NumQueuedReliables() > idPacketProcessor::MAX_RELIABLE_QUEUE / 2 )
			{
				// This is kind of a hack for development so we don't DC clients by sending them too many reliable migration messages
				// when they aren't responding. Doesn't seem like a horrible thing to have in a shipping product but is not necessary.
				NET_VERBOSE_PRINT( "NET: Skipping reliable game migration data msg because client reliable queue is > half full\n" );
				
			}
			else
			{
				if( net_migration_debug.GetBool() )
				{
					idLib::Printf( "NET: Sending migration game data to peer %d. size: %d\n", peerToSend, msg.GetSize() );
				}
				QueueReliableMessage( peerToSend, RELIABLE_MIGRATION_GAME_DATA, msg.GetReadData(), msg.GetSize() );
			}
			break;
		}
	}
	
	// Increment next send time / next send peer
	nextSendMigrationGamePeer++;
	if( nextSendMigrationGamePeer >= peers.Num() )
	{
		nextSendMigrationGamePeer = 0;
	}
	
	nextSendMigrationGameTime = now + MIGRATION_GAME_DATA_INTERVAL_MS;
}
/*
================================================================================================
idRenderProgManager::LoadGLSLProgram
================================================================================================
*/
void idRenderProgManager::LoadGLSLProgram( const int programIndex, const int vertexShaderIndex, const int fragmentShaderIndex ) {
	glslProgram_t & prog = glslPrograms[programIndex];

	if ( prog.progId != INVALID_PROGID ) {
		return; // Already loaded
	}

	GLuint vertexProgID = ( vertexShaderIndex != -1 ) ? vertexShaders[ vertexShaderIndex ].progId : INVALID_PROGID;
	GLuint fragmentProgID = ( fragmentShaderIndex != -1 ) ? fragmentShaders[ fragmentShaderIndex ].progId : INVALID_PROGID;

	const GLuint program = qglCreateProgram();
	if ( program ) {

		if ( vertexProgID != INVALID_PROGID ) {
			qglAttachShader( program, vertexProgID );
		}

		if ( fragmentProgID != INVALID_PROGID ) {
			qglAttachShader( program, fragmentProgID );
		}

		// bind vertex attribute locations
		for ( int i = 0; attribsPC[i].glsl != NULL; i++ ) {
			if ( ( attribsPC[i].flags & AT_VS_IN ) != 0 ) {
				qglBindAttribLocation( program, attribsPC[i].bind, attribsPC[i].glsl );
			}
		}

		qglLinkProgram( program );

		int infologLength = 0;
		qglGetProgramiv( program, GL_INFO_LOG_LENGTH, &infologLength );
		if ( infologLength > 1 ) {
			char * infoLog = (char *)malloc( infologLength );
			int charsWritten = 0;
			qglGetProgramInfoLog( program, infologLength, &charsWritten, infoLog );

			// catch the strings the ATI and Intel drivers output on success
			if ( strstr( infoLog, "Vertex shader(s) linked, fragment shader(s) linked." ) != NULL || strstr( infoLog, "No errors." ) != NULL ) {
				//idLib::Printf( "render prog %s from %s linked\n", GetName(), GetFileName() );
			} else {
				idLib::Printf( "While linking GLSL program %d with vertexShader %s and fragmentShader %s\n", 
					programIndex, 
					( vertexShaderIndex >= 0 ) ? vertexShaders[vertexShaderIndex].name.c_str() : "<Invalid>", 
					( fragmentShaderIndex >= 0 ) ? fragmentShaders[ fragmentShaderIndex ].name.c_str() : "<Invalid>" );
				idLib::Printf( "%s\n", infoLog );
			}

			free( infoLog );
		}
	}

	int linked = GL_FALSE;
	qglGetProgramiv( program, GL_LINK_STATUS, &linked );
	if ( linked == GL_FALSE ) {
		qglDeleteProgram( program );
		idLib::Error( "While linking GLSL program %d with vertexShader %s and fragmentShader %s\n", 
			programIndex, 
			( vertexShaderIndex >= 0 ) ? vertexShaders[vertexShaderIndex].name.c_str() : "<Invalid>", 
			( fragmentShaderIndex >= 0 ) ? fragmentShaders[ fragmentShaderIndex ].name.c_str() : "<Invalid>" );
		return;
	}

	if ( r_useUniformArrays.GetBool() ) {
		prog.vertexUniformArray = qglGetUniformLocation( program, VERTEX_UNIFORM_ARRAY_NAME );
		prog.fragmentUniformArray = qglGetUniformLocation( program, FRAGMENT_UNIFORM_ARRAY_NAME );

		assert( prog.vertexUniformArray != -1 || vertexShaderIndex < 0 || vertexShaders[vertexShaderIndex].uniforms.Num() == 0 );
		assert( prog.fragmentUniformArray != -1 || fragmentShaderIndex < 0 || fragmentShaders[fragmentShaderIndex].uniforms.Num() == 0 );
	} else {
		// store the uniform locations after we have linked the GLSL program
		prog.uniformLocations.Clear();
		for ( int i = 0; i < RENDERPARM_TOTAL; i++ ) {
			const char * parmName = GetGLSLParmName( i );
			GLint loc = qglGetUniformLocation( program, parmName );
			if ( loc != -1 ) {
				glslUniformLocation_t uniformLocation;
				uniformLocation.parmIndex = i;
				uniformLocation.uniformIndex = loc;
				prog.uniformLocations.Append( uniformLocation );
			}
		}

		// store the USER uniform locations
		for ( int i = 0; i < MAX_GLSL_USER_PARMS; i++ ) {
			const char * parmName = GetGLSLParmName( RENDERPARM_USER + i );
			GLint loc = qglGetUniformLocation( program, parmName );
			if ( loc != -1 ) {
				glslUniformLocation_t uniformLocation;
				uniformLocation.parmIndex = RENDERPARM_USER + i;
				uniformLocation.uniformIndex = loc;
				prog.uniformLocations.Append( uniformLocation );
			}
		}

		// sort the uniforms based on index
		prog.uniformLocations.SortWithTemplate( idSort_QuickUniforms() );
	}

	// get the uniform buffer binding for skinning joint matrices
	GLint blockIndex = qglGetUniformBlockIndex( program, "matrices_ubo" );
	if ( blockIndex != -1 ) {
		qglUniformBlockBinding( program, blockIndex, 0 );
	}

	// set the texture unit locations once for the render program. We only need to do this once since we only link the program once
	qglUseProgram( program );
	for ( int i = 0; i < MAX_PROG_TEXTURE_PARMS; ++i ) {
		GLint loc = qglGetUniformLocation( program, va( "samp%d", i ) );
		if ( loc != -1 ) {
			qglUniform1i( loc, i );
		}
	}

	idStr programName = vertexShaders[ vertexShaderIndex ].name;
	programName.StripFileExtension();
	prog.name = programName;
	prog.progId = program;
	prog.fragmentShaderIndex = fragmentShaderIndex;
	prog.vertexShaderIndex = vertexShaderIndex;
}
/*
========================
idSoundVoice_OpenAL::Start
========================
*/
void idSoundVoice_OpenAL::Start( int offsetMS, int ssFlags )
{
	if( s_debugHardware.GetBool() )
	{
		idLib::Printf( "%dms: %i starting %s @ %dms\n", Sys_Milliseconds(), openalSource, leadinSample ? leadinSample->GetName() : "<null>", offsetMS );
	}
	
	if( !leadinSample )
	{
		return;
	}
	
	if( !alIsSource( openalSource ) )
	{
		return;
	}
	
	if( leadinSample->IsDefault() )
	{
		idLib::Warning( "Starting defaulted sound sample %s", leadinSample->GetName() );
	}
	
	bool flicker = ( ssFlags & SSF_NO_FLICKER ) == 0;
	
	if( flicker != hasVUMeter )
	{
		hasVUMeter = flicker;
		
		/*
		if( flicker )
		{
			IUnknown* vuMeter = NULL;
		
			if( XAudio2CreateVolumeMeter( &vuMeter, 0 ) == S_OK )
			{
		
				XAUDIO2_EFFECT_DESCRIPTOR descriptor;
				descriptor.InitialState = true;
				descriptor.OutputChannels = leadinSample->NumChannels();
				descriptor.pEffect = vuMeter;
		
				XAUDIO2_EFFECT_CHAIN chain;
				chain.EffectCount = 1;
				chain.pEffectDescriptors = &descriptor;
		
				pSourceVoice->SetEffectChain( &chain );
		
				vuMeter->Release();
			}
		}
		else
		{
			pSourceVoice->SetEffectChain( NULL );
		}
		*/
	}
	
	assert( offsetMS >= 0 );
	int offsetSamples = MsecToSamples( offsetMS, leadinSample->SampleRate() );
	if( loopingSample == NULL && offsetSamples >= leadinSample->playLength )
	{
		return;
	}
	
	RestartAt( offsetSamples );
	Update();
	UnPause();
}
/*
========================
StripDeadCode
========================
*/
idStr StripDeadCode( const idStr & in, const char * name ) {
	if ( r_skipStripDeadCode.GetBool() ) {
		return in;
	}

	//idLexer src( LEXFL_NOFATALERRORS );
	idParser src( LEXFL_NOFATALERRORS );
	src.LoadMemory( in.c_str(), in.Length(), name );
	src.AddDefine("PC");

	idList< idCGBlock, TAG_RENDERPROG > blocks;

	blocks.SetNum( 100 );

	idToken token;
	while ( !src.EndOfFile() ) {
		idCGBlock & block = blocks.Alloc();
		// read prefix
		while ( src.ReadToken( &token ) ) {
			bool found = false;
			for ( int i = 0; i < numPrefixes; i++ ) {
				if ( token == prefixes[i] ) {
					found = true;
					break;
				}
			}
			if ( !found ) {
				for ( int i = 0; i < numTypes; i++ ) {
					if ( token == types[i] ) {
						found = true;
						break;
					}
					int typeLen = idStr::Length( types[i] );
					if ( token.Cmpn( types[i], typeLen ) == 0 ) {
						for ( int j = 0; j < numTypePosts; j++ ) {
							if ( idStr::Cmp( token.c_str() + typeLen, typePosts[j] ) == 0 ) {
								found = true;
								break;
							}
						}
						if ( found ) {
							break;
						}
					}
				}
			}
			if ( found ) {
				if ( block.prefix.Length() > 0 && token.WhiteSpaceBeforeToken() ) {
					block.prefix += ' ';
				}
				block.prefix += token;
			} else {
				src.UnreadToken( &token );
				break;
			}
		}
		if ( !src.ReadToken( &token ) ) {
			blocks.SetNum( blocks.Num() - 1 );
			break;
		}
		block.name = token;

		if ( src.PeekTokenString( "=" ) || src.PeekTokenString( ":" ) || src.PeekTokenString( "[" ) ) {
			src.ReadToken( &token );
			block.postfix = token;
			while ( src.ReadToken( &token ) ) {
				if ( token == ";" ) {
					block.postfix += ';';
					break;
				} else {
					if ( token.WhiteSpaceBeforeToken() ){
						block.postfix += ' ';
					}
					block.postfix += token;
				}
			}
		} else if ( src.PeekTokenString( "(" ) ) {
			idStr parms, body;
			src.ParseBracedSection( parms, -1, true, '(', ')' );
			if ( src.CheckTokenString( ";" ) ) {
				block.postfix = parms + ";";
			} else {
				src.ParseBracedSection( body, -1, true, '{', '}' );
				block.postfix = parms + " " + body;
			}
		} else if ( src.PeekTokenString( "{" ) ) {
			src.ParseBracedSection( block.postfix, -1, true, '{', '}' );
			if ( src.CheckTokenString( ";" ) ) {
				block.postfix += ';';
			}
		} else if ( src.CheckTokenString( ";" ) ) {
			block.postfix = idStr( ';' );
		} else {
			src.Warning( "Could not strip dead code -- unknown token %s\n", token.c_str() );
			return in;
		}
	}

	idList<int, TAG_RENDERPROG> stack;
	for ( int i = 0; i < blocks.Num(); i++ ) {
		blocks[i].used = ( ( blocks[i].name == "main" )
			|| blocks[i].name.Right( 4 ) == "_ubo"
			);

		if ( blocks[i].name == "include" ) {
			blocks[i].used = true;
			blocks[i].name = ""; // clear out the include tag
		}

		if ( blocks[i].used ) {
			stack.Append( i );
		}
	}

	while ( stack.Num() > 0 ) {
		int i = stack[stack.Num() - 1];
		stack.SetNum( stack.Num() - 1 );

		idLexer src( LEXFL_NOFATALERRORS );
		src.LoadMemory( blocks[i].postfix.c_str(), blocks[i].postfix.Length(), name );
		while ( src.ReadToken( &token ) ) {
			for ( int j = 0; j < blocks.Num(); j++ ) {
				if ( !blocks[j].used ) {
					if ( token == blocks[j].name ) {
						blocks[j].used = true;
						stack.Append( j );
					}
				}
			}
		}
	}

	idStr out;

	for ( int i = 0; i < blocks.Num(); i++ ) {
		if ( blocks[i].used ) {
			out += blocks[i].prefix;
			out += ' ';
			out += blocks[i].name;
			out += ' ';
			out += blocks[i].postfix;
			out += '\n';
		}
	}

	return out;
}
/*
========================
idSoundVoice_OpenAL::Create
========================
*/
void idSoundVoice_OpenAL::Create( const idSoundSample* leadinSample_, const idSoundSample* loopingSample_ )
{
	if( IsPlaying() )
	{
		// This should never hit
		Stop();
		return;
	}
	
	triggered = true;
	
	leadinSample = ( idSoundSample_OpenAL* )leadinSample_;
	loopingSample = ( idSoundSample_OpenAL* )loopingSample_;
	
	if( alIsSource( openalSource ) && CompatibleFormat( leadinSample ) )
	{
		sampleRate = leadinSample->format.basic.samplesPerSec;
	}
	else
	{
		DestroyInternal();
		formatTag = leadinSample->format.basic.formatTag;
		numChannels = leadinSample->format.basic.numChannels;
		sampleRate = leadinSample->format.basic.samplesPerSec;
		
		//soundSystemLocal.hardware.pXAudio2->CreateSourceVoice( &pSourceVoice, ( const WAVEFORMATEX* )&leadinSample->format, XAUDIO2_VOICE_USEFILTER, 4.0f, &streamContext );
		
		CheckALErrors();
		
		alGenSources( 1, &openalSource );
		if( CheckALErrors() != AL_NO_ERROR )
			//if( pSourceVoice == NULL )
		{
			// If this hits, then we are most likely passing an invalid sample format, which should have been caught by the loader (and the sample defaulted)
			return;
		}
		
		alSourcef( openalSource, AL_ROLLOFF_FACTOR, 0.0f );
		
		//if( ( loopingSample == NULL && leadinSample->openalBuffer != 0 ) || ( loopingSample != NULL && soundShader->entries[0]->hardwareBuffer ) )
		if( leadinSample->openalBuffer != 0 )
		{
			alSourcei( openalSource, AL_BUFFER, 0 );
			
			// handle uncompressed (non streaming) single shot and looping sounds
			/*
			if( triggered )
			{
				alSourcei( openalSource, AL_BUFFER, looping ? chan->soundShader->entries[0]->openalBuffer : leadinSample->openalBuffer );
			}
			*/
		}
		else
		{
			//if( triggered )
			
			// handle streaming sounds (decode on the fly) both single shot AND looping
			
			alSourcei( openalSource, AL_BUFFER, 0 );
			alDeleteBuffers( 3, &lastopenalStreamingBuffer[0] );
			lastopenalStreamingBuffer[0] = openalStreamingBuffer[0];
			lastopenalStreamingBuffer[1] = openalStreamingBuffer[1];
			lastopenalStreamingBuffer[2] = openalStreamingBuffer[2];
			
			alGenBuffers( 3, &openalStreamingBuffer[0] );
			/*
			if( soundSystemLocal.alEAXSetBufferMode )
			{
				soundSystemLocal.alEAXSetBufferMode( 3, &chan->openalStreamingBuffer[0], alGetEnumValue( ID_ALCHAR "AL_STORAGE_ACCESSIBLE" ) );
			}
			*/
			openalStreamingBuffer[0];
			openalStreamingBuffer[1];
			openalStreamingBuffer[2];
		}
		
		if( s_debugHardware.GetBool() )
		{
			if( loopingSample == NULL || loopingSample == leadinSample )
			{
				idLib::Printf( "%dms: %i created for %s\n", Sys_Milliseconds(), openalSource, leadinSample ? leadinSample->GetName() : "<null>" );
			}
			else
			{
				idLib::Printf( "%dms: %i created for %s and %s\n", Sys_Milliseconds(), openalSource, leadinSample ? leadinSample->GetName() : "<null>", loopingSample ? loopingSample->GetName() : "<null>" );
			}
		}
	}
	
	sourceVoiceRate = sampleRate;
	//pSourceVoice->SetSourceSampleRate( sampleRate );
	//pSourceVoice->SetVolume( 0.0f );
	
	alSourcei( openalSource, AL_SOURCE_RELATIVE, AL_TRUE );
	alSource3f( openalSource, AL_POSITION, 0.0f, 0.0f, 0.0f );
	
	// RB: FIXME 0.0f ?
	alSourcef( openalSource, AL_GAIN, 1.0f );
	
	//OnBufferStart( leadinSample, 0 );
}
/*
================================================================================================
idRenderProgManager::LoadGLSLShader
================================================================================================
*/
GLuint idRenderProgManager::LoadGLSLShader( GLenum target, const char * name, idList<int> & uniforms ) {

	idStr inFile;
	idStr outFileHLSL;
	idStr outFileGLSL;
	idStr outFileUniforms;
	inFile.Format( "renderprogs\\%s", name );
	inFile.StripFileExtension();
	outFileHLSL.Format( "renderprogs\\glsl\\%s", name );
	outFileHLSL.StripFileExtension();
	outFileGLSL.Format( "renderprogs\\glsl\\%s", name );
	outFileGLSL.StripFileExtension();
	outFileUniforms.Format( "renderprogs\\glsl\\%s", name );
	outFileUniforms.StripFileExtension();
	if ( target == GL_FRAGMENT_SHADER ) {
		inFile += ".pixel";
		outFileHLSL += "_fragment.hlsl";
		outFileGLSL += "_fragment.glsl";
		outFileUniforms += "_fragment.uniforms";
	} else {
		inFile += ".vertex";
		outFileHLSL += "_vertex.hlsl";
		outFileGLSL += "_vertex.glsl";
		outFileUniforms += "_vertex.uniforms";
	}

	// first check whether we already have a valid GLSL file and compare it to the hlsl timestamp;
	ID_TIME_T hlslTimeStamp;
	int hlslFileLength = fileSystem->ReadFile( inFile.c_str(), NULL, &hlslTimeStamp );

	ID_TIME_T glslTimeStamp;
	int glslFileLength = fileSystem->ReadFile( outFileGLSL.c_str(), NULL, &glslTimeStamp );

	// if the glsl file doesn't exist or we have a newer HLSL file we need to recreate the glsl file.
	idStr programGLSL;
	idStr programUniforms;
	if ( ( glslFileLength <= 0 ) || ( hlslTimeStamp > glslTimeStamp ) ) {
		if ( hlslFileLength <= 0 ) {
			// hlsl file doesn't even exist bail out
			return false;
		}

		void * hlslFileBuffer = NULL;
		int len = fileSystem->ReadFile( inFile.c_str(), &hlslFileBuffer );
		if ( len <= 0 ) {
			return false;
		}
		idStr hlslCode( ( const char* ) hlslFileBuffer );
		idStr programHLSL = StripDeadCode( hlslCode, inFile );
		programGLSL = ConvertCG2GLSL( programHLSL, inFile, target == GL_VERTEX_SHADER, programUniforms );

		fileSystem->WriteFile( outFileHLSL, programHLSL.c_str(), programHLSL.Length(), "fs_basepath" );
		fileSystem->WriteFile( outFileGLSL, programGLSL.c_str(), programGLSL.Length(), "fs_basepath" );
		if ( r_useUniformArrays.GetBool() ) {
			fileSystem->WriteFile( outFileUniforms, programUniforms.c_str(), programUniforms.Length(), "fs_basepath" );
		}
	} else {
		// read in the glsl file
		void * fileBufferGLSL = NULL;
		int lengthGLSL = fileSystem->ReadFile( outFileGLSL.c_str(), &fileBufferGLSL );
		if ( lengthGLSL <= 0 ) {
			idLib::Error( "GLSL file %s could not be loaded and may be corrupt", outFileGLSL.c_str() );
		}
		programGLSL = ( const char * ) fileBufferGLSL;
		Mem_Free( fileBufferGLSL );

		if ( r_useUniformArrays.GetBool() ) {
			// read in the uniform file
			void * fileBufferUniforms = NULL;
			int lengthUniforms = fileSystem->ReadFile( outFileUniforms.c_str(), &fileBufferUniforms );
			if ( lengthUniforms <= 0 ) {
				idLib::Error( "uniform file %s could not be loaded and may be corrupt", outFileUniforms.c_str() );
			}
			programUniforms = ( const char* ) fileBufferUniforms;
			Mem_Free( fileBufferUniforms );
		}
	}

	// find the uniforms locations in either the vertex or fragment uniform array
	if ( r_useUniformArrays.GetBool() ) {
		uniforms.Clear();

		idLexer src( programUniforms, programUniforms.Length(), "uniforms" );
		idToken token;
		while ( src.ReadToken( &token ) ) {
			int index = -1;
			for ( int i = 0; i < RENDERPARM_TOTAL && index == -1; i++ ) {
				const char * parmName = GetGLSLParmName( i );
				if ( token == parmName ) {
					index = i;
				}
			}
			for ( int i = 0; i < MAX_GLSL_USER_PARMS && index == -1; i++ ) {
				const char * parmName = GetGLSLParmName( RENDERPARM_USER + i );
				if ( token == parmName ) {
					index = RENDERPARM_USER + i;
				}
			}
			if ( index == -1 ) {
				idLib::Error( "couldn't find uniform %s for %s", token.c_str(), outFileGLSL.c_str() );
			}
			uniforms.Append( index );
		}
	}

	// create and compile the shader
	const GLuint shader = qglCreateShader( target );
	if ( shader ) {
		const char * source[1] = { programGLSL.c_str() };

		qglShaderSource( shader, 1, source, NULL );
		qglCompileShader( shader );

		int infologLength = 0;
		qglGetShaderiv( shader, GL_INFO_LOG_LENGTH, &infologLength );
		if ( infologLength > 1 ) {
			idTempArray<char> infoLog( infologLength );
			int charsWritten = 0;
			qglGetShaderInfoLog( shader, infologLength, &charsWritten, infoLog.Ptr() );

			// catch the strings the ATI and Intel drivers output on success
			if ( strstr( infoLog.Ptr(), "successfully compiled to run on hardware" ) != NULL || 
					strstr( infoLog.Ptr(), "No errors." ) != NULL ) {
				//idLib::Printf( "%s program %s from %s compiled to run on hardware\n", typeName, GetName(), GetFileName() );
			} else if ( r_displayGLSLCompilerMessages.GetBool() ) {
				idLib::Printf( "While compiling %s program %s\n", ( target == GL_FRAGMENT_SHADER ) ? "fragment" : "vertex" , inFile.c_str() );

				const char separator = '\n';
				idList<idStr> lines;
				lines.Clear();
				idStr source( programGLSL );
				lines.Append( source );
				for ( int index = 0, ofs = lines[index].Find( separator ); ofs != -1; index++, ofs = lines[index].Find( separator ) ) {
					lines.Append( lines[index].c_str() + ofs + 1 );
					lines[index].CapLength( ofs );
				}

				idLib::Printf( "-----------------\n" );
				for ( int i = 0; i < lines.Num(); i++ ) {
					idLib::Printf( "%3d: %s\n", i+1, lines[i].c_str() );
				}
				idLib::Printf( "-----------------\n" );

				idLib::Printf( "%s\n", infoLog.Ptr() );
			}
		}

		GLint compiled = GL_FALSE;
		qglGetShaderiv( shader, GL_COMPILE_STATUS, &compiled );
		if ( compiled == GL_FALSE ) {
			qglDeleteShader( shader );
			return INVALID_PROGID;
		}
	}

	return shader;
}
/*
========================
idAchievementManager::EventCompletesAchievement
========================
*/
void idAchievementManager::EventCompletesAchievement( const achievement_t eventId )
{
	if( g_demoMode.GetBool() )
	{
		return;
	}
	
	idLocalUser* localUser = GetLocalUser();
	if( localUser == NULL || localUser->GetProfile() == NULL )
	{
	
		// Send a Reliable Message to the User that needs to unlock this.
		if( owner != NULL )
		{
			int playerId = owner->entityNumber;
			const int bufferSize = sizeof( playerId ) + sizeof( eventId );
			byte buffer[ bufferSize ];
			idBitMsg msg;
			msg.InitWrite( buffer, bufferSize );
			
			msg.WriteByte( playerId );
			msg.WriteByte( eventId );
			
			msg.WriteByteAlign();
			idLib::Printf( "Host Sending Achievement\n" );
			session->GetActingGameStateLobbyBase().SendReliableToLobbyUser( gameLocal.lobbyUserIDs[ owner->entityNumber ], GAME_RELIABLE_MESSAGE_ACHIEVEMENT_UNLOCK, msg );
		}
		
		return; // Remote user or build game
	}
	
	// Check to see if we've already given the achievement.
	// If so, don't do again because we don't want to autosave every time a trigger is hit
	if( localUser->GetProfile()->GetAchievement( eventId ) )
	{
		return;
	}
	
#ifdef ID_RETAIL
	if( common->GetConsoleUsed() )
	{
		if( !cheatingDialogShown )
		{
			common->Dialog().AddDialog( GDM_ACHIEVEMENTS_DISABLED_DUE_TO_CHEATING, DIALOG_ACCEPT, NULL, NULL, true );
			cheatingDialogShown = true;
		}
		return;
	}
#endif
	
	counts[eventId]++;
	
	if( counts[eventId] >= achievementInfo[eventId].required )
	{
		session->GetAchievementSystem().AchievementUnlock( localUser, eventId );
	}
	else
	{
		if( achievementInfo[eventId].lifetime )
		{
			localUser->SetStatInt( eventId, counts[eventId] );
		}
	}
}
/*
===================
GLimp_Init
===================
*/
bool GLimp_Init( glimpParms_t parms )
{
	common->Printf( "Initializing OpenGL subsystem\n" );
	
	GLimp_PreInit(); // DG: make sure SDL is initialized
	
	// DG: make window resizable
	Uint32 flags = SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE;
	// DG end
	
	if( parms.fullScreen )
		flags |= SDL_WINDOW_FULLSCREEN;
		
	int colorbits = 24;
	int depthbits = 24;
	int stencilbits = 8;
	
	for( int i = 0; i < 16; i++ )
	{
		// 0 - default
		// 1 - minus colorbits
		// 2 - minus depthbits
		// 3 - minus stencil
		if( ( i % 4 ) == 0 && i )
		{
			// one pass, reduce
			switch( i / 4 )
			{
				case 2 :
					if( colorbits == 24 )
						colorbits = 16;
					break;
				case 1 :
					if( depthbits == 24 )
						depthbits = 16;
					else if( depthbits == 16 )
						depthbits = 8;
				case 3 :
					if( stencilbits == 24 )
						stencilbits = 16;
					else if( stencilbits == 16 )
						stencilbits = 8;
			}
		}
		
		int tcolorbits = colorbits;
		int tdepthbits = depthbits;
		int tstencilbits = stencilbits;
		
		if( ( i % 4 ) == 3 )
		{
			// reduce colorbits
			if( tcolorbits == 24 )
				tcolorbits = 16;
		}
		
		if( ( i % 4 ) == 2 )
		{
			// reduce depthbits
			if( tdepthbits == 24 )
				tdepthbits = 16;
			else if( tdepthbits == 16 )
				tdepthbits = 8;
		}
		
		if( ( i % 4 ) == 1 )
		{
			// reduce stencilbits
			if( tstencilbits == 24 )
				tstencilbits = 16;
			else if( tstencilbits == 16 )
				tstencilbits = 8;
			else
				tstencilbits = 0;
		}
		
		int channelcolorbits = 4;
		if( tcolorbits == 24 )
			channelcolorbits = 8;
			
		SDL_GL_SetAttribute( SDL_GL_RED_SIZE, channelcolorbits );
		SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, channelcolorbits );
		SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, channelcolorbits );
		SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
		SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, tdepthbits );
		SDL_GL_SetAttribute( SDL_GL_STENCIL_SIZE, tstencilbits );
		
		if( r_waylandcompat.GetBool() )
			SDL_GL_SetAttribute( SDL_GL_ALPHA_SIZE, 0 );
		else
			SDL_GL_SetAttribute( SDL_GL_ALPHA_SIZE, channelcolorbits );
			
		SDL_GL_SetAttribute( SDL_GL_STEREO, parms.stereo ? 1 : 0 );
		
		SDL_GL_SetAttribute( SDL_GL_MULTISAMPLEBUFFERS, parms.multiSamples ? 1 : 0 );
		SDL_GL_SetAttribute( SDL_GL_MULTISAMPLESAMPLES, parms.multiSamples );
		
#if SDL_VERSION_ATLEAST(2, 0, 0)
		
		// RB begin
		if( r_useOpenGL32.GetInteger() > 0 )
		{
			glConfig.driverType = GLDRV_OPENGL32_COMPATIBILITY_PROFILE;
			
			SDL_GL_SetAttribute( SDL_GL_CONTEXT_MAJOR_VERSION, 3 );
			SDL_GL_SetAttribute( SDL_GL_CONTEXT_MINOR_VERSION, 2 );
		}
		if( r_debugContext.GetBool() )
		{
			SDL_GL_SetAttribute( SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG );
		}
		
		if( r_useOpenGL32.GetInteger() > 1 )
		{
			glConfig.driverType = GLDRV_OPENGL32_CORE_PROFILE;
			
			SDL_GL_SetAttribute( SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE );
		}
		// RB end
		
		// DG: set display num for fullscreen
		int windowPos = SDL_WINDOWPOS_UNDEFINED;
		if( parms.fullScreen > 0 )
		{
			if( parms.fullScreen > SDL_GetNumVideoDisplays() )
			{
				common->Warning( "Couldn't set display to num %i because we only have %i displays",
								 parms.fullScreen, SDL_GetNumVideoDisplays() );
			}
			else
			{
				// -1 because SDL starts counting displays at 0, while parms.fullScreen starts at 1
				windowPos = SDL_WINDOWPOS_UNDEFINED_DISPLAY( ( parms.fullScreen - 1 ) );
			}
		}
		// TODO: if parms.fullScreen == -1 there should be a borderless window spanning multiple displays
		/*
		 * NOTE that this implicitly handles parms.fullScreen == -2 (from r_fullscreen -2) meaning
		 * "do fullscreen, but I don't care on what monitor", at least on my box it's the monitor with
		 * the mouse cursor.
		 */
		
		
		window = SDL_CreateWindow( GAME_NAME,
								   windowPos,
								   windowPos,
								   parms.width, parms.height, flags );
		// DG end
		
		context = SDL_GL_CreateContext( window );
		
		if( !window )
		{
			common->DPrintf( "Couldn't set GL mode %d/%d/%d: %s",
							 channelcolorbits, tdepthbits, tstencilbits, SDL_GetError() );
			continue;
		}
		
		if( SDL_GL_SetSwapInterval( r_swapInterval.GetInteger() ) < 0 )
			common->Warning( "SDL_GL_SWAP_CONTROL not supported" );
			
		// RB begin
		SDL_GetWindowSize( window, &glConfig.nativeScreenWidth, &glConfig.nativeScreenHeight );
		// RB end
		
		glConfig.isFullscreen = ( SDL_GetWindowFlags( window ) & SDL_WINDOW_FULLSCREEN ) == SDL_WINDOW_FULLSCREEN;
#else
		glConfig.driverType = GLDRV_OPENGL3X;
		
		SDL_WM_SetCaption( GAME_NAME, GAME_NAME );
		
		if( SDL_GL_SetAttribute( SDL_GL_SWAP_CONTROL, r_swapInterval.GetInteger() ) < 0 )
			common->Warning( "SDL_GL_SWAP_CONTROL not supported" );
		
		window = SDL_SetVideoMode( parms.width, parms.height, colorbits, flags );
		if( !window )
		{
			common->DPrintf( "Couldn't set GL mode %d/%d/%d: %s",
							 channelcolorbits, tdepthbits, tstencilbits, SDL_GetError() );
			continue;
		}
		
		glConfig.nativeScreenWidth = window->w;
		glConfig.nativeScreenHeight = window->h;
		
		glConfig.isFullscreen = ( window->flags & SDL_FULLSCREEN ) == SDL_FULLSCREEN;
#endif
		
		common->Printf( "Using %d color bits, %d depth, %d stencil display\n",
						channelcolorbits, tdepthbits, tstencilbits );
						
		glConfig.colorBits = tcolorbits;
		glConfig.depthBits = tdepthbits;
		glConfig.stencilBits = tstencilbits;
		
		// RB begin
		glConfig.displayFrequency = 60;
		glConfig.isStereoPixelFormat = parms.stereo;
		glConfig.multisamples = parms.multiSamples;
		
		glConfig.pixelAspect = 1.0f;	// FIXME: some monitor modes may be distorted
		// should side-by-side stereo modes be consider aspect 0.5?
		
		// RB end
		
		break;
	}
	
	if( !window )
	{
		common->Printf( "No usable GL mode found: %s", SDL_GetError() );
		return false;
	}
	
#ifdef __APPLE__
	glewExperimental = GL_TRUE;
#endif
	
	GLenum glewResult = glewInit();
	if( GLEW_OK != glewResult )
	{
		// glewInit failed, something is seriously wrong
		common->Printf( "^3GLimp_Init() - GLEW could not load OpenGL subsystem: %s", glewGetErrorString( glewResult ) );
	}
	else
	{
		common->Printf( "Using GLEW %s\n", glewGetString( GLEW_VERSION ) );
	}
	
	// DG: disable cursor, we have two cursors in menu (because mouse isn't grabbed in menu)
	SDL_ShowCursor( SDL_DISABLE );
	// DG end
	
	return true;
}
/*
========================
idResolutionScale::InitForMap
========================
*/
void idResolutionScale::InitForMap( const char* mapName )
{
	dropMilliseconds = rs_dropMilliseconds.GetFloat();
	raiseMilliseconds = rs_raiseMilliseconds.GetFloat();
}
/*
=============
R_CheckCvars

See if some cvars that we watch have changed
=============
*/
static void R_CheckCvars()
{

	// gamma stuff
	if( r_gamma.IsModified() || r_brightness.IsModified() )
	{
		r_gamma.ClearModified();
		r_brightness.ClearModified();
		R_SetColorMappings();
	}
	
	// filtering
	if( r_maxAnisotropicFiltering.IsModified() || r_useTrilinearFiltering.IsModified() || r_lodBias.IsModified() )
	{
		idLib::Printf( "Updating texture filter parameters.\n" );
		r_maxAnisotropicFiltering.ClearModified();
		r_useTrilinearFiltering.ClearModified();
		r_lodBias.ClearModified();
		for( int i = 0 ; i < globalImages->images.Num() ; i++ )
		{
			if( globalImages->images[i] )
			{
				globalImages->images[i]->Bind();
				globalImages->images[i]->SetTexParameters();
			}
		}
	}
	
	extern idCVar r_useSeamlessCubeMap;
	if( r_useSeamlessCubeMap.IsModified() )
	{
		r_useSeamlessCubeMap.ClearModified();
		if( glConfig.seamlessCubeMapAvailable )
		{
			if( r_useSeamlessCubeMap.GetBool() )
			{
				qglEnable( GL_TEXTURE_CUBE_MAP_SEAMLESS );
			}
			else
			{
				qglDisable( GL_TEXTURE_CUBE_MAP_SEAMLESS );
			}
		}
	}
	
	extern idCVar r_useSRGB;
	if( r_useSRGB.IsModified() )
	{
		r_useSRGB.ClearModified();
		if( glConfig.sRGBFramebufferAvailable )
		{
			if( r_useSRGB.GetBool() )
			{
				qglEnable( GL_FRAMEBUFFER_SRGB );
			}
			else
			{
				qglDisable( GL_FRAMEBUFFER_SRGB );
			}
		}
	}
	
	
	if( r_multiSamples.IsModified() )
	{
		if( r_multiSamples.GetInteger() > 0 )
		{
			qglEnable( GL_MULTISAMPLE_ARB );
		}
		else
		{
			qglDisable( GL_MULTISAMPLE_ARB );
		}
	}
	
	// check for changes to logging state
	GLimp_EnableLogging( r_logFile.GetInteger() != 0 );
}
/*
=============================
idGameBearShootWindow::UpdateGame
=============================
*/
void idGameBearShootWindow::UpdateGame() {
	int i;

	if ( onNewGame ) {
		ResetGameState();
		if ( goal ) {
			goal->position.x = 550;
			goal->position.y = 164;
			goal->velocity.Zero();
		}
		if ( helicopter ) {
			helicopter->position.x = 550;
			helicopter->position.y = 100;
			helicopter->velocity.Zero();
		}
		if ( bear ) {
			bear->SetVisible( false );
		}

		bearTurretAngle.SetFloat( 0.f );
		bearTurretForce.SetFloat( 200.f );

		gamerunning = true;
	}
	if ( onContinue ) {
		gameOver = false;
		timeRemaining = 60.f;

		onContinue = false;
	}

	if(gamerunning == true) {
		int current_time = gui->GetTime();
		idRandom rnd( current_time );

		// Check for button presses
		UpdateButtons();

		if ( bear ) {
			UpdateBear();
		}
		if ( helicopter && goal ) {
			UpdateHelicopter();
		}

		// Update Wind
		if ( windUpdateTime < current_time ) {
			float	scale;
			int		width;

			windForce = rnd.CRandomFloat() * ( MAX_WINDFORCE * 0.75f );
			if (windForce > 0) {
				windForce += ( MAX_WINDFORCE * 0.25f );
				wind->rotation = 0;
			} else {
				windForce -= ( MAX_WINDFORCE * 0.25f );
				wind->rotation = 180;
			}

			scale = 1.f - (( MAX_WINDFORCE - idMath::Fabs(windForce) ) / MAX_WINDFORCE);
			width = 100*scale;

			if ( windForce < 0 ) {
				wind->position.x = 500 - width + 1;
			} else {
				wind->position.x = 500;
			}
			wind->SetSize( width, 40 );

			windUpdateTime = current_time + 7000 + rnd.RandomInt(5000);
		}

		// Update turret rotation angle
		if ( turret ) {
			turretAngle = bearTurretAngle.GetFloat();
			turret->rotation = turretAngle;
		}

		for( i = 0; i < entities.Num(); i++ ) {
			entities[i]->Update( timeSlice );
		}

		// Update countdown timer
		timeRemaining -= timeSlice;
		timeRemaining = idMath::ClampFloat( 0.f, 99999.f, timeRemaining );
		gui->SetStateString( "time_remaining", va("%2.1f", timeRemaining ) );

		if ( timeRemaining <= 0.f && !gameOver ) {
			gameOver = true;
			updateScore = true;
		}

		if ( updateScore ) {
			UpdateScore();
			updateScore = false;
		}
	}
}
/*
=================
idRenderModelManagerLocal::Preload
=================
*/
void idRenderModelManagerLocal::Preload( const idPreloadManifest& manifest )
{
	if( preload_MapModels.GetBool() )
	{
		// preload this levels images
		int	start = Sys_Milliseconds();
		int numLoaded = 0;
		idList< preloadSort_t > preloadSort;
		preloadSort.Resize( manifest.NumResources() );
		for( int i = 0; i < manifest.NumResources(); i++ )
		{
			const preloadEntry_s& p = manifest.GetPreloadByIndex( i );
			idResourceCacheEntry rc;
			idStrStatic< MAX_OSPATH > filename;
			if( p.resType == PRELOAD_MODEL )
			{
				filename = "generated/rendermodels/";
				filename += p.resourceName;
				idStrStatic< 16 > ext;
				filename.ExtractFileExtension( ext );
				filename.SetFileExtension( va( "b%s", ext.c_str() ) );
			}
			if( p.resType == PRELOAD_PARTICLE )
			{
				filename = "generated/particles/";
				filename += p.resourceName;
				filename += ".bprt";
			}
			if( !filename.IsEmpty() )
			{
				if( fileSystem->GetResourceCacheEntry( filename, rc ) )
				{
					preloadSort_t ps = {};
					ps.idx = i;
					ps.ofs = rc.offset;
					preloadSort.Append( ps );
				}
			}
		}
		
		preloadSort.SortWithTemplate( idSort_Preload() );
		
		for( int i = 0; i < preloadSort.Num(); i++ )
		{
			const preloadSort_t& ps = preloadSort[ i ];
			const preloadEntry_s& p = manifest.GetPreloadByIndex( ps.idx );
			if( p.resType == PRELOAD_MODEL )
			{
				idRenderModel* model = FindModel( p.resourceName );
				if( model != NULL )
				{
					model->SetLevelLoadReferenced( true );
				}
			}
			else if( p.resType == PRELOAD_PARTICLE )
			{
				declManager->FindType( DECL_PARTICLE, p.resourceName );
			}
			numLoaded++;
		}
		
		int	end = Sys_Milliseconds();
		common->Printf( "%05d models preloaded ( or were already loaded ) in %5.1f seconds\n", numLoaded, ( end - start ) * 0.001 );
		common->Printf( "----------------------------------------\n" );
	}
}
/*
========================
idSoundHardware_OpenAL::Update
========================
*/
void idSoundHardware_OpenAL::Update()
{
	if( openalDevice == NULL )
	{
		int nowTime = Sys_Milliseconds();
		if( lastResetTime + 1000 < nowTime )
		{
			lastResetTime = nowTime;
			Init();
		}
		return;
	}
	
	if( soundSystem->IsMuted() )
	{
		alListenerf( AL_GAIN, 0.0f );
	}
	else
	{
		alListenerf( AL_GAIN, DBtoLinear( s_volume_dB.GetFloat() ) );
	}
	
	// IXAudio2SourceVoice::Stop() has been called for every sound on the
	// zombie list, but it is documented as asyncronous, so we have to wait
	// until it actually reports that it is no longer playing.
	for( int i = 0; i < zombieVoices.Num(); i++ )
	{
		zombieVoices[i]->FlushSourceBuffers();
		if( !zombieVoices[i]->IsPlaying() )
		{
			freeVoices.Append( zombieVoices[i] );
			zombieVoices.RemoveIndexFast( i );
			i--;
		}
		else
		{
			static int playingZombies;
			playingZombies++;
		}
	}
	
	/*
	if( s_showPerfData.GetBool() )
	{
		XAUDIO2_PERFORMANCE_DATA perfData;
		pXAudio2->GetPerformanceData( &perfData );
		idLib::Printf( "Voices: %d/%d CPU: %.2f%% Mem: %dkb\n", perfData.ActiveSourceVoiceCount, perfData.TotalSourceVoiceCount, perfData.AudioCyclesSinceLastQuery / ( float )perfData.TotalCyclesSinceLastQuery, perfData.MemoryUsageInBytes / 1024 );
	}
	*/
	
	/*
	if( vuMeterRMS == NULL )
	{
		// Init probably hasn't been called yet
		return;
	}
	
	vuMeterRMS->Enable( s_showLevelMeter.GetBool() );
	vuMeterPeak->Enable( s_showLevelMeter.GetBool() );
	
	if( !s_showLevelMeter.GetBool() )
	{
		pMasterVoice->DisableEffect( 0 );
		return;
	}
	else
	{
		pMasterVoice->EnableEffect( 0 );
	}
	
	float peakLevels[ 8 ];
	float rmsLevels[ 8 ];
	
	XAUDIO2FX_VOLUMEMETER_LEVELS levels;
	levels.ChannelCount = outputChannels;
	levels.pPeakLevels = peakLevels;
	levels.pRMSLevels = rmsLevels;
	
	if( levels.ChannelCount > 8 )
	{
		levels.ChannelCount = 8;
	}
	
	pMasterVoice->GetEffectParameters( 0, &levels, sizeof( levels ) );
	
	int currentTime = Sys_Milliseconds();
	for( int i = 0; i < outputChannels; i++ )
	{
		if( vuMeterPeakTimes[i] < currentTime )
		{
			vuMeterPeak->SetValue( i, vuMeterPeak->GetValue( i ) * 0.9f, colorRed );
		}
	}
	
	float width = 20.0f;
	float height = 200.0f;
	float left = 100.0f;
	float top = 100.0f;
	
	sscanf( s_meterPosition.GetString(), "%f %f %f %f", &left, &top, &width, &height );
	
	vuMeterRMS->SetPosition( left, top, width * levels.ChannelCount, height );
	vuMeterPeak->SetPosition( left, top, width * levels.ChannelCount, height );
	
	for( uint32 i = 0; i < levels.ChannelCount; i++ )
	{
		vuMeterRMS->SetValue( i, rmsLevels[ i ], idVec4( 0.5f, 1.0f, 0.0f, 1.00f ) );
		if( peakLevels[ i ] >= vuMeterPeak->GetValue( i ) )
		{
			vuMeterPeak->SetValue( i, peakLevels[ i ], colorRed );
			vuMeterPeakTimes[i] = currentTime + s_meterTopTime.GetInteger();
		}
	}
	*/
}
/*
========================
InDebugRange
Helper function for net_ssTemplateDebug debugging
========================
*/
bool InDebugRange( int i )
{
	return ( i >= net_ssTemplateDebug_start.GetInteger() && i < net_ssTemplateDebug_start.GetInteger() + net_ssTemplateDebug_len.GetInteger() );
}
namespace BFG
{

class idCmdArgs;

idCVar s_showLevelMeter( "s_showLevelMeter", "0", CVAR_BOOL | CVAR_ARCHIVE, "Show VU meter" );
idCVar s_meterTopTime( "s_meterTopTime", "1000", CVAR_INTEGER | CVAR_ARCHIVE, "How long (in milliseconds) peaks are displayed on the VU meter" );
idCVar s_meterPosition( "s_meterPosition", "100 100 20 200", CVAR_ARCHIVE, "VU meter location (x y w h)" );
idCVar s_device( "s_device", "-1", CVAR_INTEGER | CVAR_ARCHIVE, "Which audio device to use (listDevices to list, -1 for default)" );
//idCVar s_showPerfData( "s_showPerfData", "0", CVAR_BOOL, "Show XAudio2 Performance data" );
extern idCVar s_volume_dB;


/*
========================
idSoundHardware_OpenAL::idSoundHardware_OpenAL
========================
*/
idSoundHardware_OpenAL::idSoundHardware_OpenAL()
{
	openalDevice = NULL;
	openalContext = NULL;
	
	//vuMeterRMS = NULL;PrintDeviceList
	//vuMeterPeak = NULL;
	
	//outputChannels = 0;
	//channelMask = 0;
	
	voices.SetNum( 0 );
	zombieVoices.SetNum( 0 );
	freeVoices.SetNum( 0 );
	
	OpenALDeviceList.Clear();

	lastResetTime = 0;
}

void idSoundHardware_OpenAL::OpenBestDevice() {
	const ALCchar *deviceNameList = NULL;

	idLib::Printf( "idSoundHardware_OpenAL::OpenBestDevice: rebuilding devices list\n" );

	//first clean the list
	OpenALDeviceList.Clear();

	//let's build it again
	if( alcIsExtensionPresent( NULL, "ALC_ENUMERATE_ALL_EXT" ) != AL_FALSE ) {
		deviceNameList = alcGetString( NULL, ALC_ALL_DEVICES_SPECIFIER ); //all the devices
	} else {
		deviceNameList = alcGetString( NULL, ALC_DEVICE_SPECIFIER ); // just one device
	}

	if( deviceNameList && *deviceNameList != '\0' ) {
		do {
			OpenALDeviceList.Append( deviceNameList );
			deviceNameList += strlen( deviceNameList ) + 1;
		} while( *deviceNameList != '\0' );
	}

	if ( OpenALDeviceList.Num() == 0 ) {
		idLib::Printf( "idSoundHardware_OpenAL::OpenBestDevice: there are still no devices in the device list!!!\n" );
		openalDevice = NULL;
		return;
	}

	//let's proceed to open the correct device

	int selectedInt = s_device.GetInteger();

	if ( ( s_device.GetInteger() > OpenALDeviceList.Num() - 1 ) || ( selectedInt == -1 ) || ( OpenALDeviceList.Num() == 1 ) ) {
		idLib::Printf( "idSoundHardware_OpenAL::OpenBestDevice: selected default device\n" );
		openalDevice = alcOpenDevice( NULL ); //the default one
	} else {
		idLib::Printf( "idSoundHardware_OpenAL::OpenBestDevice: selected the %ith device\n", selectedInt );
		openalDevice = alcOpenDevice( OpenALDeviceList[ selectedInt ] );
	}
}

void idSoundHardware_OpenAL::PrintDeviceList( const char* list )
{
	int index = 0;

	if( !list || *list == '\0' )
	{
		idLib::Printf( "	!!! none !!!\n" );
	}
	else
	{
		do
		{
			idLib::Printf( "    %i: %s\n", index, list );
			list += strlen( list ) + 1;
			index++;
		}
		while( *list != '\0' );
	}
}

void idSoundHardware_OpenAL::PrintALCInfo( ALCdevice* device )
{
	ALCint major, minor;
	
	if( device )
	{
		const ALCchar* devname = NULL;
		idLib::Printf( "\n" );
		if( alcIsExtensionPresent( device, "ALC_ENUMERATE_ALL_EXT" ) != AL_FALSE )
		{
			devname = alcGetString( device, ALC_ALL_DEVICES_SPECIFIER );
		}
		
		if( CheckALCErrors( device ) != ALC_NO_ERROR || !devname )
		{
			devname = alcGetString( device, ALC_DEVICE_SPECIFIER );
		}
		
		idLib::Printf( "** Info for device \"%s\" **\n", devname );
	}
	
	if( CheckALCErrors( device ) == ALC_NO_ERROR ) {
		alcGetIntegerv( device, ALC_MAJOR_VERSION, 1, &major );
		alcGetIntegerv( device, ALC_MINOR_VERSION, 1, &minor );
		idLib::Printf( "ALC version: %d.%d\n", major, minor );
	}
		
	if( device )
	{
		idLib::Printf( "OpenAL extensions:\n%s\n", alGetString( AL_EXTENSIONS ) );
		
		//idLib::Printf("ALC extensions:");
		//printList(alcGetString(device, ALC_EXTENSIONS), ' ');
		CheckALCErrors( device );
	}
}

void idSoundHardware_OpenAL::PrintALInfo()
{
	idLib::Printf( "OpenAL vendor string: %s\n", alGetString( AL_VENDOR ) );
	idLib::Printf( "OpenAL renderer string: %s\n", alGetString( AL_RENDERER ) );
	idLib::Printf( "OpenAL version string: %s\n", alGetString( AL_VERSION ) );
	idLib::Printf( "OpenAL extensions: %s", alGetString( AL_EXTENSIONS ) );
	//PrintList(alGetString(AL_EXTENSIONS), ' ');
	CheckALErrors();
}

//void idSoundHardware_OpenAL::listDevices_f( const idCmdArgs& args )
void listDevices_f( const idCmdArgs& args )

{
	idLib::Printf( "Available playback devices:\n\n" );
	if( alcIsExtensionPresent( NULL, "ALC_ENUMERATE_ALL_EXT" ) != AL_FALSE )
	{
		idSoundHardware_OpenAL::PrintDeviceList( alcGetString( NULL, ALC_ALL_DEVICES_SPECIFIER ) );
	}
	else
	{
		idSoundHardware_OpenAL::PrintDeviceList( alcGetString( NULL, ALC_DEVICE_SPECIFIER ) );
	}
	
	idLib::Printf( "\n" );

	//idLib::Printf("Available capture devices:\n");
	//printDeviceList(alcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER));
	
	if( alcIsExtensionPresent( NULL, "ALC_ENUMERATE_ALL_EXT" ) != AL_FALSE )
	{
		idLib::Printf( "Default playback device: %s\n", alcGetString( NULL, ALC_DEFAULT_ALL_DEVICES_SPECIFIER ) );
	}
	else
	{
		idLib::Printf( "Default playback device: %s\n",  alcGetString( NULL, ALC_DEFAULT_DEVICE_SPECIFIER ) );
	}
	
	if( s_device.GetInteger() == -1) {
		idLib::Printf( "Selected playback device is default's.\n" );
	} else {
		idLib::Printf( "Selected playback device is device: %i\n", s_device.GetInteger() ); //FIXME this could bring wrong info if s_device points to a fake number
	}

	//idLib::Printf("Default capture device: %s\n", alcGetString(NULL, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER));
	
	idSoundHardware_OpenAL::PrintALCInfo( NULL );
	
	idSoundHardware_OpenAL::PrintALCInfo( ( ALCdevice* )soundSystem->GetOpenALDevice() );

}

/*
========================
idSoundHardware_OpenAL::Init
========================
*/
void idSoundHardware_OpenAL::Init()
{
	cmdSystem->AddCommand( "listDevices", listDevices_f, 0, "Lists the connected sound devices", NULL );
	
	common->Printf( "Setup OpenAL device and context... \n" );

	idSoundHardware_OpenAL::OpenBestDevice();

	if( openalDevice == NULL )
	{
		common->FatalError( "idSoundHardware_OpenAL::Init: alcOpenDevice() failed\n" );
		return;
	}
	
	openalContext = alcCreateContext( openalDevice, NULL );
	if( alcMakeContextCurrent( openalContext ) == 0 )
	{
		common->FatalError( "idSoundHardware_OpenAL::Init: alcMakeContextCurrent( %p) failed\n", openalContext );
		return;
	}
	
	common->Printf( "Done.\n" );
	
	common->Printf( "OpenAL vendor: %s\n", alGetString( AL_VENDOR ) );
	common->Printf( "OpenAL renderer: %s\n", alGetString( AL_RENDERER ) );
	common->Printf( "OpenAL version: %s\n", alGetString( AL_VERSION ) );
	common->Printf( "OpenAL extensions: %s\n", alGetString( AL_EXTENSIONS ) );
	
	//pMasterVoice->SetVolume( DBtoLinear( s_volume_dB.GetFloat() ) );
	
	//outputChannels = deviceDetails.OutputFormat.Format.nChannels;
	//channelMask = deviceDetails.OutputFormat.dwChannelMask;
	
	//idSoundVoice::InitSurround( outputChannels, channelMask );
	
	// ---------------------
	// Create VU Meter Effect
	// ---------------------
	/*
	IUnknown* vuMeter = NULL;
	XAudio2CreateVolumeMeter( &vuMeter, 0 );
	
	XAUDIO2_EFFECT_DESCRIPTOR descriptor;
	descriptor.InitialState = true;
	descriptor.OutputChannels = outputChannels;
	descriptor.pEffect = vuMeter;
	
	XAUDIO2_EFFECT_CHAIN chain;
	chain.EffectCount = 1;
	chain.pEffectDescriptors = &descriptor;
	
	pMasterVoice->SetEffectChain( &chain );
	
	vuMeter->Release();
	*/
	
	// ---------------------
	// Create VU Meter Graph
	// ---------------------
	
	/*
	vuMeterRMS = console->CreateGraph( outputChannels );
	vuMeterPeak = console->CreateGraph( outputChannels );
	vuMeterRMS->Enable( false );
	vuMeterPeak->Enable( false );
	
	memset( vuMeterPeakTimes, 0, sizeof( vuMeterPeakTimes ) );
	
	vuMeterPeak->SetFillMode( idDebugGraph::GRAPH_LINE );
	vuMeterPeak->SetBackgroundColor( idVec4( 0.0f, 0.0f, 0.0f, 0.0f ) );
	
	vuMeterRMS->AddGridLine( 0.500f, idVec4( 0.5f, 0.5f, 0.5f, 1.0f ) );
	vuMeterRMS->AddGridLine( 0.250f, idVec4( 0.5f, 0.5f, 0.5f, 1.0f ) );
	vuMeterRMS->AddGridLine( 0.125f, idVec4( 0.5f, 0.5f, 0.5f, 1.0f ) );
	
	const char* channelNames[] = { "L", "R", "C", "S", "Lb", "Rb", "Lf", "Rf", "Cb", "Ls", "Rs" };
	for( int i = 0, ci = 0; ci < sizeof( channelNames ) / sizeof( channelNames[0] ); ci++ )
	{
		if( ( channelMask & BIT( ci ) ) == 0 )
		{
			continue;
		}
		vuMeterRMS->SetLabel( i, channelNames[ ci ] );
		i++;
	}
	*/
	
	// OpenAL doesn't really impose a maximum number of sources
	voices.SetNum( voices.Max() );
	freeVoices.SetNum( voices.Max() );
	zombieVoices.SetNum( 0 );
	for( int i = 0; i < voices.Num(); i++ )
	{
		freeVoices[i] = &voices[i];
	}
}

/*
========================
idSoundHardware_OpenAL::Shutdown
========================
*/
void idSoundHardware_OpenAL::Shutdown()
{
	zombieVoices.Clear();
	freeVoices.Clear();
	for( int i = 0; i < voices.Num(); i++ )
	{
		voices[ i ].DestroyInternal();
	}
	voices.Clear();
	
	alcMakeContextCurrent( NULL );
	
	alcDestroyContext( openalContext );
	openalContext = NULL;

	alcCloseDevice( openalDevice );
	openalDevice = NULL;

	OpenALDeviceList.Clear();

	/*
	if( vuMeterRMS != NULL )
	{
		console->DestroyGraph( vuMeterRMS );
		vuMeterRMS = NULL;
	}
	if( vuMeterPeak != NULL )
	{
		console->DestroyGraph( vuMeterPeak );
		vuMeterPeak = NULL;
	}
	*/
}

/*
========================
idSoundHardware_OpenAL::AllocateVoice
========================
*/
idSoundVoice* idSoundHardware_OpenAL::AllocateVoice( const idSoundSample* leadinSample, const idSoundSample* loopingSample )
{
	if( leadinSample == NULL )
	{
		return NULL;
	}
	if( loopingSample != NULL )
	{
		if( ( leadinSample->format.basic.formatTag != loopingSample->format.basic.formatTag ) || ( leadinSample->format.basic.numChannels != loopingSample->format.basic.numChannels ) )
		{
			idLib::Warning( "Leadin/looping format mismatch: %s & %s", leadinSample->GetName(), loopingSample->GetName() );
			loopingSample = NULL;
		}
	}
	
	// Try to find a free voice that matches the format
	// But fallback to the last free voice if none match the format
	idSoundVoice* voice = NULL;
	for( int i = 0; i < freeVoices.Num(); i++ )
	{
		if( freeVoices[i]->IsPlaying() )
		{
			continue;
		}
		voice = ( idSoundVoice* )freeVoices[i];
		if( voice->CompatibleFormat( ( idSoundSample_OpenAL* )leadinSample ) )
		{
			break;
		}
	}
	if( voice != NULL )
	{
		voice->Create( leadinSample, loopingSample );
		freeVoices.Remove( voice );
		return voice;
	}
	
	return NULL;
}

/*
========================
idSoundHardware_OpenAL::FreeVoice
========================
*/
void idSoundHardware_OpenAL::FreeVoice( idSoundVoice* voice )
{
	voice->Stop();
	
	// Stop() is asyncronous, so we won't flush bufferes until the
	// voice on the zombie channel actually returns !IsPlaying()
	zombieVoices.Append( voice );
}

/*
========================
idSoundHardware_OpenAL::Update
========================
*/
void idSoundHardware_OpenAL::Update()
{
	if( openalDevice == NULL )
	{
		int nowTime = Sys_Milliseconds();
		if( lastResetTime + 1000 < nowTime )
		{
			lastResetTime = nowTime;
			Init();
		}
		return;
	}
	
	if( soundSystem->IsMuted() )
	{
		alListenerf( AL_GAIN, 0.0f );
	}
	else
	{
		alListenerf( AL_GAIN, DBtoLinear( s_volume_dB.GetFloat() ) );
	}
	
	// IXAudio2SourceVoice::Stop() has been called for every sound on the
	// zombie list, but it is documented as asyncronous, so we have to wait
	// until it actually reports that it is no longer playing.
	for( int i = 0; i < zombieVoices.Num(); i++ )
	{
		zombieVoices[i]->FlushSourceBuffers();
		if( !zombieVoices[i]->IsPlaying() )
		{
			freeVoices.Append( zombieVoices[i] );
			zombieVoices.RemoveIndexFast( i );
			i--;
		}
		else
		{
			static int playingZombies;
			playingZombies++;
		}
	}
	
	/*
	if( s_showPerfData.GetBool() )
	{
		XAUDIO2_PERFORMANCE_DATA perfData;
		pXAudio2->GetPerformanceData( &perfData );
		idLib::Printf( "Voices: %d/%d CPU: %.2f%% Mem: %dkb\n", perfData.ActiveSourceVoiceCount, perfData.TotalSourceVoiceCount, perfData.AudioCyclesSinceLastQuery / ( float )perfData.TotalCyclesSinceLastQuery, perfData.MemoryUsageInBytes / 1024 );
	}
	*/
	
	/*
	if( vuMeterRMS == NULL )
	{
		// Init probably hasn't been called yet
		return;
	}
	
	vuMeterRMS->Enable( s_showLevelMeter.GetBool() );
	vuMeterPeak->Enable( s_showLevelMeter.GetBool() );
	
	if( !s_showLevelMeter.GetBool() )
	{
		pMasterVoice->DisableEffect( 0 );
		return;
	}
	else
	{
		pMasterVoice->EnableEffect( 0 );
	}
	
	float peakLevels[ 8 ];
	float rmsLevels[ 8 ];
	
	XAUDIO2FX_VOLUMEMETER_LEVELS levels;
	levels.ChannelCount = outputChannels;
	levels.pPeakLevels = peakLevels;
	levels.pRMSLevels = rmsLevels;
	
	if( levels.ChannelCount > 8 )
	{
		levels.ChannelCount = 8;
	}
	
	pMasterVoice->GetEffectParameters( 0, &levels, sizeof( levels ) );
	
	int currentTime = Sys_Milliseconds();
	for( int i = 0; i < outputChannels; i++ )
	{
		if( vuMeterPeakTimes[i] < currentTime )
		{
			vuMeterPeak->SetValue( i, vuMeterPeak->GetValue( i ) * 0.9f, colorRed );
		}
	}
	
	float width = 20.0f;
	float height = 200.0f;
	float left = 100.0f;
	float top = 100.0f;
	
	sscanf( s_meterPosition.GetString(), "%f %f %f %f", &left, &top, &width, &height );
	
	vuMeterRMS->SetPosition( left, top, width * levels.ChannelCount, height );
	vuMeterPeak->SetPosition( left, top, width * levels.ChannelCount, height );
	
	for( uint32 i = 0; i < levels.ChannelCount; i++ )
	{
		vuMeterRMS->SetValue( i, rmsLevels[ i ], idVec4( 0.5f, 1.0f, 0.0f, 1.00f ) );
		if( peakLevels[ i ] >= vuMeterPeak->GetValue( i ) )
		{
			vuMeterPeak->SetValue( i, peakLevels[ i ], colorRed );
			vuMeterPeakTimes[i] = currentTime + s_meterTopTime.GetInteger();
		}
	}
	*/
}

} // namespace BFG
/*
========================
idMenuScreen_Shell_SystemOptions::idMenuDataSource_SystemSettings::GetField
========================
*/
idSWFScriptVar idMenuScreen_Shell_SystemOptions::idMenuDataSource_SystemSettings::GetField( const int fieldIndex ) const
{
	switch( fieldIndex )
	{
		case SYSTEM_FIELD_FULLSCREEN:
		{
			const int fullscreen = r_fullscreen.GetInteger();
			const int vidmode = r_vidMode.GetInteger();
			if( fullscreen == 0 )
			{
				return "#str_swf_disabled";
			}
			if( fullscreen < 0 || vidmode < 0 || vidmode >= modeList.Num() )
			{
				return "???";
			}
			if( modeList[vidmode].displayHz == 60 )
			{
				return va( "%4i x %4i", modeList[vidmode].width, modeList[vidmode].height );
			}
			else
			{
				return va( "%4i x %4i @ %dhz", modeList[vidmode].width, modeList[vidmode].height, modeList[vidmode].displayHz );
			}
		}
		case SYSTEM_FIELD_FRAMERATE:
			return va( "%d FPS", com_engineHz.GetInteger() );
		case SYSTEM_FIELD_VSYNC:
			if( r_swapInterval.GetInteger() == 1 )
			{
				return "#str_swf_smart";
			}
			else if( r_swapInterval.GetInteger() == 2 )
			{
				return "#str_swf_enabled";
			}
			else
			{
				return "#str_swf_disabled";
			}
		case SYSTEM_FIELD_ANTIALIASING:
			if( r_multiSamples.GetInteger() == 0 )
			{
				return "#str_swf_disabled";
			}
			return va( "%dx", r_multiSamples.GetInteger() );
		case SYSTEM_FIELD_MOTIONBLUR:
			if( r_motionBlur.GetInteger() == 0 )
			{
				return "#str_swf_disabled";
			}
			return va( "%dx", idMath::IPow( 2, r_motionBlur.GetInteger() ) );
		// RB begin
		case SYSTEM_FIELD_SHADOWMAPPING:
			if( r_useShadowMapping.GetInteger() == 1 )
			{
				return "#str_swf_enabled";
			}
			else
			{
				return "#str_swf_disabled";
			}
		//case SYSTEM_FIELD_LODBIAS:
		//	return LinearAdjust( r_lodBias.GetFloat(), -1.0f, 1.0f, 0.0f, 100.0f );
		// RB end
		case SYSTEM_FIELD_BRIGHTNESS:
			return LinearAdjust( r_lightScale.GetFloat(), 2.0f, 4.0f, 0.0f, 100.0f );
		case SYSTEM_FIELD_VOLUME:
		{
			return 100.0f * Square( 1.0f - ( s_volume_dB.GetFloat() / DB_SILENCE ) );
		}
	}
	return false;
}
Exemple #30
0
rvGamePlayback::rvGamePlayback( void )
{
    idStr			newName;
    const idVec3 trace_mins( -1.0f, -1.0f, -1.0f );
    const idVec3 trace_maxs( 1.0f, 1.0f, 1.0f );
    const idBounds trace_bounds( trace_mins, trace_maxs );
    idTraceModel	traceModel( trace_bounds );

    mStartTime = gameLocal.time;
    mOldFlags = 0;
    mClipModel = new idClipModel( traceModel );

    if( !g_currentPlayback.GetInteger() )
    {
        newName = declManager->GetNewName( DECL_PLAYBACK, "playbacks/untitled" );
        mPlayback = ( rvDeclPlayback * )declManager->CreateNewDecl( DECL_PLAYBACK, newName, newName + ".playback" );
        mPlayback->ReplaceSourceFileText();
        mPlayback->Invalidate();

        g_currentPlayback.SetInteger( mPlayback->Index() );
    }
    else
    {
        mPlayback = ( rvDeclPlayback * )declManager->PlaybackByIndex( g_currentPlayback.GetInteger() );
    }

    declManager->StartPlaybackRecord( mPlayback );
    common->Printf( "Starting playback record to %s type %d\n", mPlayback->GetName(), g_recordPlayback.GetInteger() );
}