void AdjustSuperDebugArgs( CUtlVector<char*> &args )
{
	// Get the directory this exe was run out of.
	char filename[512];
	if ( GetModuleFileName( GetModuleHandle( NULL ), filename, sizeof( filename ) ) == 0 )
		return;
	
	char *pLastSlash = filename;
	char *pCurPos = filename;
	while ( *pCurPos )
	{
		if ( *pCurPos == '/' || *pCurPos == '\\' )
			pLastSlash = pCurPos;
		++pCurPos;
	}
	*pLastSlash = 0;
	
	// In superdebug mode, run it out of c:/hl2/bin.
	const char *pBase = args[0];
	const char *pBaseCur = pBase;
	while ( *pBaseCur )
	{
		if ( *pBaseCur == '/' || *pBaseCur == '\\' || *pBaseCur == ':' )
		{
			pBase = pBaseCur+1;
			pBaseCur = pBase;
		}
		++pBaseCur;
	}
	
	int maxLen = 64 + strlen( pBase ) + 1;
	char *pNewFilename = new char[maxLen];
	_snprintf( pNewFilename, maxLen, "%s\\%s", filename, pBase );
	delete args[0];
	args[0] = pNewFilename;


	// Now insert -allowdebug.
	const char *pAllowDebug = "-allowdebug";
	char *pToInsert = new char[ strlen( pAllowDebug ) + 1 ];
	strcpy( pToInsert, pAllowDebug );
	args.InsertAfter( 0, pToInsert );

}
void HandlePacket_LOOKING_FOR_WORKERS( bf_read &buf, const CIPAddr &ipFrom )
{
	// If we're downloading files for a job request, don't process any more "looking for workers" packets.
	if ( g_Waiting_hProcess )
		return;
	
	// This will be a nonzero-length string if patching.
	char versionString[512];
	buf.ReadString( versionString, sizeof( versionString ) );

	int iPort = buf.ReadShort();
	int iPriority = buf.ReadShort();

	// Make sure we don't run the same job more than once.
	if ( !CheckJobID( buf, g_CurJobID ) )
		return;

	CUtlVector<char*> newArgv;
	GetArgsFromBuffer( buf, newArgv, &g_Waiting_bShowAppWindow );

	bool bForcePatch = false;
	if ( buf.GetNumBytesLeft() >= 1 )
		bForcePatch = (buf.ReadByte() != 0);

	int iDownloaderPort = iPort;
	if  ( buf.GetNumBytesLeft() >= 2 )
		iDownloaderPort = buf.ReadShort();

	// Add these arguments after the executable filename to tell the program
	// that it's an MPI worker and who to connect to. 
	char strDownloaderIP[128], strMainIP[128];
	V_snprintf( strDownloaderIP, sizeof( strDownloaderIP ), "%d.%d.%d.%d:%d", ipFrom.ip[0], ipFrom.ip[1], ipFrom.ip[2], ipFrom.ip[3], iDownloaderPort );
	V_snprintf( strMainIP, sizeof( strMainIP ), "%d.%d.%d.%d:%d", ipFrom.ip[0], ipFrom.ip[1], ipFrom.ip[2], ipFrom.ip[3], iPort );

	// (-mpi is already on the command line of whoever ran the app).
	// AppendArg( commandLine, sizeof( commandLine ), "-mpi" );
	newArgv.InsertAfter( 0, CopyString( "-mpi_worker" ) );
	newArgv.InsertAfter( 1, CopyString( strDownloaderIP ) );


	// If the version string is set, then this is a patch.
	bool bPatching = false;
	if ( versionString[0] != 0 )
	{
		bPatching = true;
		
		// Check that we haven't applied this patch version yet. This case usually happens right after we've applied a patch
		// and we're restarting. The vmpi_transfer master is still pinging us telling us to patch, but we don't want to
		// reapply this patch.
		if ( atof( versionString ) <= atof( g_VersionString ) && !bForcePatch )
		{
			newArgv.PurgeAndDeleteElements();
			return;
		}
		
		// Ok, it's a new version. Get rid of whatever was running before.
		KillRunningProcess( "Starting a patch..", true );
	}
								 
	// If there's already a job running, only interrupt it if this new one has a higher priority.
	if ( WaitForProcessToExit() )
	{
		if ( iPriority > g_CurJobPriority )
		{
			KillRunningProcess( "Interrupted by a higher priority process", true );
		}
		else
		{
			// This means we're already running a job with equal to or greater priority than
			// the one that has been requested. We're going to ignore this request.
			newArgv.PurgeAndDeleteElements();
			return;
		}
	}

	// Responses go here.
	g_CurRespondAddr = ipFrom;
	
	// Also look for -mpi_ShowAppWindow in the args to the service.
	if ( !g_Waiting_bShowAppWindow && FindArg( __argc, __argv, "-mpi_ShowAppWindow" ) )
		g_Waiting_bShowAppWindow = true;

	// Copy all the files from the master and put them in our cache dir to run with.
	char cacheDir[MAX_PATH];
	if ( StartDownloadingAppFiles( newArgv, cacheDir, sizeof( cacheDir ), g_Waiting_bShowAppWindow, &g_Waiting_hProcess, bPatching ) )
	{
		// After it's downloaded, we want it to switch to the main connection port.
		if ( newArgv.Count() >= 3 && V_stricmp( newArgv[2], strDownloaderIP ) == 0 )
		{
			delete newArgv[2];
			newArgv[2] = CopyString( strMainIP );
		}
		
		g_Waiting_StartTime = Plat_FloatTime();
		g_Waiting_Argv.PurgeAndDeleteElements();
		g_Waiting_Argv = newArgv;
		g_Waiting_Priority = iPriority;
		g_Waiting_bPatching = bPatching;
		newArgv.Purge();
	}
	else
	{
		newArgv.PurgeAndDeleteElements();
	}

	// Remember that we tried to run this job so we don't try to run it again.
	AddJobMemory( g_CurJobID );
	
	SendStateToServicesBrowsers();
}
// -------------------------------------------------------------------------------- //
// Purpose: Launches vmpi_transfer.exe to download the required
// files from the master so we can launch.
//
// If successful, it sets hProcess to the process handle of the downloader.
// When that process terminates, we look for [cache dir]\ReadyToGo.txt and if it's
// there, then we start the job.
// -------------------------------------------------------------------------------- //
bool StartDownloadingAppFiles( 
	CUtlVector<char*> &newArgv, 
	char *cacheDir, 
	int cacheDirLen, 
	bool bShowAppWindow, 
	HANDLE *hProcess, 
	bool bPatching )
{
	*hProcess = NULL;
	
	V_strncpy( cacheDir, g_FileCachePath, cacheDirLen );
	
	// For now, cache dir is always the same. It's [current directory]\cache.
	if ( _access( cacheDir, 0 ) != 0 )
	{
		if ( !CreateDirectory( cacheDir, NULL ) && GetLastError() != ERROR_ALREADY_EXISTS )
		{
			Warning( "Unable to create cache directory: %s.\n", cacheDir );
			return false;
		}
	}

	// Clear all the files in the directory.
	char searchStr[MAX_PATH];
	V_ComposeFileName( cacheDir, "*.*", searchStr, sizeof( searchStr ) );
	_finddata_t findData;
	intptr_t ret = _findfirst( searchStr, &findData );
	if ( ret != -1 )
	{
		do
		{
			if ( findData.name[0] == '.' )
				continue;
			
			char fullFilename[MAX_PATH];
			V_ComposeFileName( cacheDir, findData.name, fullFilename, sizeof( fullFilename ) );
			if ( _unlink( fullFilename ) != 0 )
			{
				Warning( "_unlink( %s ) failed.\n", fullFilename );
				return false;
			}
		} while ( _findnext( ret, &findData ) == 0 );
		
		_findclose( ret );
	}

	// Change the EXE name to an absolute path to exactly where it is in the cache directory.
	int maxExeNameLen = 1024;
	char *pExeName = new char[maxExeNameLen];
	if ( bPatching )
	{
		V_ComposeFileName( cacheDir, "vmpi_service_install.exe", pExeName, maxExeNameLen );
		
		// Add args for the installer.
		newArgv.InsertAfter( 0, CopyString( "-DontTouchUI" ) );

		// When patching, we can't start the UI and the installer can't because we're running in the local system account
		// and the UI is running on the account of whoever logged in. So what we do is send a message to the UI telling it
		// to run <cacheDir>\WaitAndRestart and restart itself in N seconds.
		newArgv.InsertAfter( 0, CopyString( "-Install_Quiet" ) );
	}
	else
	{
		V_ComposeFileName( cacheDir, newArgv[0], pExeName, maxExeNameLen );
	}
		
	delete newArgv[0];
	newArgv[0] = pExeName;

	char fullExeFilename[MAX_PATH];
	V_ComposeFileName( g_BaseAppPath, "vmpi_transfer.exe", fullExeFilename, sizeof( fullExeFilename ) );
	
	CUtlVector<char*> downloaderArgs;
	downloaderArgs.AddToTail( fullExeFilename );
#if defined( _DEBUG )
	downloaderArgs.AddToTail( "-allowdebug" );
#endif
	downloaderArgs.AddToTail( "-CachePath" );		// Tell it where to download the files to.
	downloaderArgs.AddToTail( cacheDir );
	
	// Pass all the -mpi_worker, -mpi_file, -mpi_filebase args into the downloader app.
	for ( int i=1; i < (int)newArgv.Count()-1; i++ )
	{
		if ( V_stricmp( newArgv[i], "-mpi_filebase" ) == 0 || V_stricmp( newArgv[i], "-mpi_file" ) == 0 )
		{
			downloaderArgs.AddToTail( newArgv[i] );
			downloaderArgs.AddToTail( newArgv[i+1] );
			newArgv.Remove( i );
			newArgv.Remove( i );
			--i;
		}
		else if ( V_stricmp( newArgv[i], "-mpi_worker" ) == 0 )
		{
			// We need this arg so it knows what IP to connect to, but we want to leave it in the final launch args too.
			downloaderArgs.AddToTail( newArgv[i] );
			downloaderArgs.AddToTail( newArgv[i+1] );
			++i;
		}
	}	
	
	// Transfer each file.
	PROCESS_INFORMATION pi;
	if ( !RunProcessFromArgs( downloaderArgs, bShowAppWindow, false, g_BaseAppPath, &pi ) )
		return false;
	
	*hProcess = pi.hProcess;
	return true;
}