text: /*
  GameSpy PT SDK 
  Dan "Mr. Pants" Schoenblum
  dan@gamespy.com
  
  Copyright 2000 GameSpy Industries, Inc
  
  */
  
  #ifdef XRAY_DISABLE_GAMESPY_WARNINGS
  #pragma warning(disable: 4244) //lines: 606
  #endif //#ifdef XRAY_DISABLE_GAMESPY_WARNINGS
  
  
  #include <stdio.h>
  #include "pt.h"
  #include "../ghttp/ghttp.h"
  #include "../common/gsStringUtil.h"
  #include "../common/gsAvailable.h"
  
  /************
  ** DEFINES **
  ************/
  #define PTA_DEFAULT_VERCHECK_URL    "http://motd." GSI_DOMAIN_NAME "/motd/vercheck.asp"
  #define PTA_DEFAULT_MOTD_URL        "http://motd." GSI_DOMAIN_NAME "/motd/motd.asp"
  #define PTA_DEFAULT_FILEPLANET_URL  "http://www.fileplanet.com/dlfileraw.asp"
  #define MAX_MIRRORS         32
  #define PTA_MAX_STRING_SIZE 2048
  
  char gPTAVercheckURL[PTA_MAX_STRING_SIZE];
  char gPTAMOTDURL[PTA_MAX_STRING_SIZE];
  char gPTAFilePlanetURL[PTA_MAX_STRING_SIZE];
  
  /**********
  ** TYPES **
  **********/
  typedef struct ptaPatchData
  {
  	ptPatchCallback callback;
  	void * param;
  } ptaPatchData;
  
  typedef struct ptaFilePlanetInfoData
  {
  	int fileID;
  	ptFilePlanetInfoCallback callback;
  	void * param;
  } ptaFilePlanetInfoData;
  
  /************
  ** GLOBALS **
  ************/
  //static char URL[PTA_MAX_STRING_SIZE];
  
  /**************
  ** FUNCTIONS **
  **************/
  static const char * ptaGetKeyValue
  (
  	const char * buffer,
  	const char * key
  )
  {
  	static char value[PTA_MAX_STRING_SIZE];
  	const char * str;
  	int len;
  
  	str = strstr(buffer, key);
  	if(!str)
  		return NULL;
  	str += strlen(key);
  	len = (int)strcspn(str, "\\");
  	len = min(len, (int)sizeof(value) - 1);
  	memcpy(value, str, (unsigned int)len);
  	value[len] = '\0';
  
  	return value;
  }
  
  static char Line[PTA_MAX_STRING_SIZE];
  static int ptaFillLine
  (
  	const char * buffer
  )
  {
  	char * str = Line;
  	int i;
  
  	// Skip white space.
  	////////////////////
  	for(i = 0 ; isspace(*buffer) ; i++)
  		buffer++;
  
  	// Check for EOF.
  	/////////////////
  	if(*buffer == '\0')
  		return EOF;
  
  	// Copy off the line.
  	/////////////////////
  	for( ; *buffer && ((*buffer != 0x0A) && (*buffer != 0x0D)) ; i++)
  	{
  		if(i == sizeof(Line) - 1)
  		{
  			// This line is too big for our buffer.
  			///////////////////////////////////////
  			assert(0);
  			return EOF;
  		}
  		*str++ = *buffer++;
  	}
  	*str = '\0';
  
  	return i;
  }
  
  static void ptaCallPatchCallback
  (
  	ptaPatchData * data,
  	PTBool available,
  	PTBool mandatory,
  	const char * versionName,
  	int fileID,
  	const char * downloadURL
  )
  {
  	if(data->callback)
  	{
  #ifndef GSI_UNICODE
  		data->callback(available, mandatory, versionName, fileID, downloadURL, data->param);
  #else
  		unsigned short versionName_W[255];
  		unsigned short downloadURL_W[PTA_MAX_STRING_SIZE*2];
  		UTF8ToUCS2String(versionName, versionName_W);
  		AsciiToUCS2String(downloadURL, downloadURL_W);
  		data->callback(available, mandatory, versionName_W, fileID, downloadURL_W, data->param);
  #endif
  	}
  
  	gsifree(data);
  }
  
  // already returns ghttptrue
  static GHTTPBool ptaPatchFailed
  (
  	ptaPatchData * data
  )
  {
  	ptaCallPatchCallback(data, PTFalse, PTFalse, "", 0, "");
  
  	return GHTTPTrue;
  }
  
  static void ptaCallFilePlanetInfoCallback
  (
  	ptaFilePlanetInfoData * data,
  	PTBool found,
  	const char * description,
  	const char * size,
  	int numMirrors,
  	char ** mirrorNames,
  	char ** mirrorURLs
  )
  {
  	int i;
  
  	if(data->callback)
  	{
  		if (!found)
  			data->callback(data->fileID, PTFalse, NULL, NULL, 0, NULL, NULL, data->param);
  		else
  		{
  #ifndef GSI_UNICODE
  			data->callback(data->fileID, found, description, size, numMirrors, (const char**)mirrorNames, (const char**)mirrorURLs, data->param);
  #else
  			unsigned short description_W[255];
  			unsigned short size_W[1024];
  			unsigned short** mirrorNames_W;
  			unsigned short** mirrorURLs_W;
  			int i;
  
  			UTF8ToUCS2String(description, description_W);
  			AsciiToUCS2String(size, size_W);
  			mirrorNames_W = UTF8ToUCS2StringArrayAlloc((const UTF8String *)mirrorNames, numMirrors);
  			mirrorURLs_W = UTF8ToUCS2StringArrayAlloc((const UTF8String *)mirrorURLs, numMirrors);
  			data->callback(data->fileID, found, description_W, size_W, numMirrors, (const unsigned short**)mirrorNames_W, (const unsigned short**)mirrorURLs_W, data->param);
  
  			for (i=0; i < numMirrors; i++)
  			{
  				gsifree(mirrorNames[i]);
  				gsifree(mirrorURLs[i]);
  			}
  			gsifree(mirrorNames);
  			gsifree(mirrorURLs);
  #endif
  		}
  	}
  
  	for(i = 0 ; i < numMirrors ; i++)
  	{
  		gsifree(mirrorNames[i]);
  		gsifree(mirrorURLs[i]);
  	}
  
  	gsifree(data);
  }
  
  // already returns ghttptrue
  static GHTTPBool ptaFilePlanetInfoFailed
  (
  	ptaFilePlanetInfoData * data
  )
  {
  	ptaCallFilePlanetInfoCallback(data, PTFalse, NULL, NULL, 0, NULL, NULL);
  
  	return GHTTPTrue;
  }
  
  static GHTTPBool ptaPatchCompletedCallback
  (
  	GHTTPRequest request,
  	GHTTPResult result,
  	char * buffer,
  	GHTTPByteCount bufferLen,
  	void * param
  )
  {
  	ptaPatchData * data = (ptaPatchData *)param;
  	const char * value;
  	PTBool mandatory;
  	int fileID;
  	char versionName[PTA_MAX_STRING_SIZE];
  	char downloadURL[PTA_MAX_STRING_SIZE];
  
  	GSI_UNUSED(bufferLen);
  	GSI_UNUSED(request);
  
  	// Check for success.
  	/////////////////////
  	if(result != GHTTPSuccess)
  		return ptaPatchFailed(data);
  
  	// Check for a patch.
  	/////////////////////
  	value = ptaGetKeyValue(buffer, "\\newver\\");
  	if(!value || !atoi(value))
  		return ptaPatchFailed(data);
  
  	// Check the mandatory flag.
  	////////////////////////////
  	value = ptaGetKeyValue(buffer, "\\lockout\\");
  	mandatory = (value && atoi(value));
  
  	// Get the file id.
  	///////////////////
  	value = ptaGetKeyValue(buffer, "\\fpfileid\\");
  	if(value)
  		fileID = atoi(value);
  	else
  		fileID = 0;
  
  	// Get the name.
  	////////////////
  	value = ptaGetKeyValue(buffer, "\\newvername\\");
  	if(value)
  	{
  		strncpy(versionName, value, sizeof(versionName));
  		versionName[sizeof(versionName) - 1] = '\0';
  	}
  	else
  		versionName[0] = '\0';
  
  	// Get the URL.
  	///////////////
  	value = ptaGetKeyValue(buffer, "\\dlurl\\");
  	if(value)
  	{
  		strncpy(downloadURL, value, sizeof(downloadURL));
  		downloadURL[sizeof(downloadURL) - 1] = '\0';
  	}
  	else
  		downloadURL[0] = '\0';
  
  	// Call the callback.
  	/////////////////////
  	ptaCallPatchCallback(data, PTTrue, mandatory, versionName, fileID, downloadURL);
  
  	return GHTTPTrue;
  }
  
  PTBool ptCheckForPatchA
  (
  	int productID,
  	const char * versionUniqueID,
  	int distributionID,
  	ptPatchCallback callback,
  	PTBool blocking,
  	void * param
  )
  {
  	int charsWritten;
  	char aURL[PTA_MAX_STRING_SIZE];
  	ptaPatchData * data;
  
  	// check if the backend is available
  	if(__GSIACResult != GSIACAvailable)
  		return PTFalse;
  
  	// Check the arguments.
  	///////////////////////
  	assert(versionUniqueID);
  	if(!versionUniqueID)
  		return PTFalse;
  	assert(callback);
  	if(!callback)
  		return PTFalse;
  
  	// override hostname?
  	if (gPTAVercheckURL[0] == '\0')
  		sprintf(gPTAVercheckURL, PTA_DEFAULT_VERCHECK_URL);
  
  	// Store some data.
  	///////////////////
  	data = (ptaPatchData *)gsimalloc(sizeof(ptaPatchData));
  	if(!data)
  		return PTFalse;
  	memset(data, 0, sizeof(ptaPatchData));
  	data->callback = callback;
  	data->param = param;
  
  	// Build the URL.
  	/////////////////
  	charsWritten = 
  		snprintf(aURL, PTA_MAX_STRING_SIZE,
  		"%s?productid=%d&versionuniqueid=%s&distid=%d&gamename=%s",
  		gPTAVercheckURL, productID, versionUniqueID, distributionID,
  		__GSIACGamename);
  	
  	assert(charsWritten >= 0);
  	if (charsWritten < 0)
  		return PTFalse;
  
  	// Send the request.
  	////////////////////
  	if((ghttpGetFileA(aURL, (GHTTPBool)blocking, ptaPatchCompletedCallback, data) == (unsigned char)(-1)) && !blocking)
  		return PTFalse;
  
  	return PTTrue;
  }
  #ifdef GSI_UNICODE
  PTBool ptCheckForPatchW
  (
  	int productID,
  	const unsigned short* versionUniqueID,
  	int distributionID,
  	ptPatchCallback callback,
  	PTBool blocking,
  	void * param
  )
  {
  	char versionUniqueID_A[255];
  	UCS2ToUTF8String(versionUniqueID, versionUniqueID_A);
  	return ptCheckForPatchA(productID, versionUniqueID_A, distributionID, callback, blocking, param);
  }
  #endif
  
  PTBool ptTrackUsageA
  (
  	int userID,
  	int productID,
  	const char * versionUniqueID,
  	int distributionID,
  	PTBool blocking
  )
  {
  	int charsWritten;
  	char aURL[PTA_MAX_STRING_SIZE];
  	
  	// check if the backend is available
  	if(__GSIACResult != GSIACAvailable)
  		return PTFalse;
  
  	// Check the arguments.
  	///////////////////////
  	assert(versionUniqueID);
  	if(!versionUniqueID)
  		return PTFalse;
  
  	// override hostname?
  	if (gPTAMOTDURL[0] == '\0')
  		sprintf(gPTAMOTDURL, PTA_DEFAULT_MOTD_URL);
  
  	// Build the URL.
  	/////////////////
  	charsWritten = snprintf(aURL, PTA_MAX_STRING_SIZE, 
  		"%s?userid=%d&productid=%d&versionuniqueid=%s&distid=%d&uniqueid=%s&gamename=%s",
  		gPTAMOTDURL, userID, productID, versionUniqueID,	distributionID,	GOAGetUniqueID(),
  		__GSIACGamename);
  	assert(charsWritten >= 0);
  	if (charsWritten < 0)
  		return PTFalse;
  	// Send the info.
  	/////////////////
  	if(ghttpGetFileA(aURL, (GHTTPBool)blocking, NULL, NULL) == -1)
  		return PTFalse;
  
  	return PTTrue;
  }
  #ifdef GSI_UNICODE
  PTBool ptTrackUsageW
  (
  	int userID,
  	int productID,
  	const unsigned short* versionUniqueID,
  	int distributionID,
  	PTBool blocking
  )
  {
  	char versionUniqueID_A[255];
  	UCS2ToUTF8String(versionUniqueID, versionUniqueID_A);
  	return ptTrackUsageA(userID, productID, versionUniqueID_A, distributionID, blocking);
  }
  #endif
  
  PTBool ptCreateCheckPatchTrackUsageReqA
  (
  	int userID,
  	int productID,
  	const char * versionUniqueID,
  	int distributionID,
  	ptPatchCallback callback,
  	PTBool blocking,
  	int *request,
  	void * param
  )
  {
  	int charsWritten;
  	char aURL[PTA_MAX_STRING_SIZE];
  	ptaPatchData * data;
  	
  	// check if the backend is available
  	if(__GSIACResult != GSIACAvailable)
  		return PTFalse;
  
  	// Check the arguments.
  	///////////////////////
  	assert(versionUniqueID);
  	if(!versionUniqueID)
  		return PTFalse;
  	assert(callback);
  	if(!callback)
  		return PTFalse;
  
  	// Store some data.
  	///////////////////
  	data = (ptaPatchData *)gsimalloc(sizeof(ptaPatchData));
  	if(!data)
  		return PTFalse;
  	memset(data, 0, sizeof(ptaPatchData));
  	data->callback = callback;
  	data->param = param;
  
  	// override hostname?
  	if (gPTAVercheckURL[0] == '\0')
  		sprintf(gPTAVercheckURL, PTA_DEFAULT_VERCHECK_URL);
  
  	// Build the URL.
  	/////////////////
  	charsWritten = snprintf(aURL, PTA_MAX_STRING_SIZE,  
  		"%s?userid=%d&productid=%d&versionuniqueid=%s&distid=%d&uniqueid=%s&gamename=%s",
  		gPTAVercheckURL, userID, productID, versionUniqueID, distributionID,	GOAGetUniqueID(),
  		__GSIACGamename);
  
  	assert(charsWritten >= 0);
  	if (charsWritten < 0)
  		return PTFalse;
  
  	// Send the request.
  	////////////////////
  	*request = ghttpGetFileA(aURL, (GHTTPBool)blocking, ptaPatchCompletedCallback, data);
  	if (*request == -1)
  		return PTFalse;
  	return PTTrue;
  }
  
  #ifdef GSI_UNICODE
  PTBool ptCreateCheckPatchTrackUsageReqW
  (
  	int userID,
  	int productID,
  	const unsigned short * versionUniqueID,
  	int distributionID,
  	ptPatchCallback callback,
  	PTBool blocking,
  	int *request,
  	void * param
   )
  {
  	char versionUniqueID_A[255];
  	UCS2ToUTF8String(versionUniqueID, versionUniqueID_A);
  	return ptCreateCheckPatchTrackUsageReqA(userID, productID, versionUniqueID_A, distributionID, callback, blocking, request, param);
  }
  #endif
  
  PTBool ptCheckForPatchAndTrackUsageA
  (
  	int userID,
  	int productID,
  	const char * versionUniqueID,
  	int distributionID,
  	ptPatchCallback callback,
  	PTBool blocking,
  	void * param
  )
  {
  	GHTTPRequest aRequest;
  	
  	// create the request and send it.
  	///////////////////////////////////
  	return ptCreateCheckPatchTrackUsageReqA(userID, productID, versionUniqueID, distributionID, callback, blocking, &aRequest, param);
  	
  }
  
  #ifdef GSI_UNICODE
  PTBool ptCheckForPatchAndTrackUsageW
  (
  	int userID,
  	int productID,
  	const unsigned short * versionUniqueID,
  	int distributionID,
  	ptPatchCallback callback,
  	PTBool blocking,
  	void * param
  )
  {
  	char versionUniqueID_A[255];
  	UCS2ToUTF8String(versionUniqueID, versionUniqueID_A);
  	return ptCheckForPatchAndTrackUsageA(userID, productID, versionUniqueID_A, distributionID, callback, blocking, param);
  }
  #endif
  
  static GHTTPBool ptaFilePlanetCompletedCallback
  (
  	GHTTPRequest request,
  	GHTTPResult result,
  	char * buffer,
  	GHTTPByteCount bufferLen,
  	void * param
  )
  {
  	ptaFilePlanetInfoData * data = (ptaFilePlanetInfoData *)param;
  	int len;
  	char description[256];
  	char size[64];
  	char * mirrorNames[MAX_MIRRORS];
  	char * mirrorURLs[MAX_MIRRORS];
  	int i;
  	char * str;
  
  	// check if the backend is available
  	if(__GSIACResult != GSIACAvailable)
  		return GHTTPFalse;
  
  	GSI_UNUSED(request);
  	GSI_UNUSED(bufferLen);
  
  	// Check for success.
  	/////////////////////
  	if(result != GHTTPSuccess)
  		return ptaFilePlanetInfoFailed(data);
  
  	// Get the description.
  	///////////////////////
  	len = ptaFillLine(buffer);
  	if(len == EOF)
  		return ptaFilePlanetInfoFailed(data);
  	buffer += len;
  	strncpy(description, Line, sizeof(description));
  	description[sizeof(description) - 1] = '\0';
  
  	// Get the size.
  	////////////////
  	len = ptaFillLine(buffer);
  	if(len == EOF)
  		return ptaFilePlanetInfoFailed(data);
  	buffer += len;
  	strncpy(size, Line, sizeof(size));
  	size[sizeof(size) - 1] = '\0';
  
  	// Get the mirrors.
  	///////////////////
  	for(i = 0 ; (i < MAX_MIRRORS) && ((len = ptaFillLine(buffer)) != EOF) ; )
  	{
  		// Adjust the buffer.
  		/////////////////////
  		buffer += len;
  
  		// Find the tab.
  		////////////////
  		str = strchr(Line, '\t');
  		if(!str)
  			continue;
  
  		// Copy off the name.
  		/////////////////////
  		len = (str - Line);
  		mirrorNames[i] = (char *)gsimalloc((unsigned int)len + 1);
  		if(!mirrorNames[i])
  			break;
  		memcpy(mirrorNames[i], Line, (unsigned int)len);
  		mirrorNames[i][len] = '\0';
  
  		// Copy off the URL.
  		////////////////////
  		str++;
  		len = (int)strlen(str);
  		mirrorURLs[i] = (char *)gsimalloc((unsigned int)len + 1);
  		if(!mirrorURLs[i])
  		{
  			gsifree(mirrorNames[i]);
  			break;
  		}
  		strcpy(mirrorURLs[i], str);
  
  		// One more mirror.
  		///////////////////
  		i++;
  	}
  
  	// Call the callback.
  	/////////////////////
  	ptaCallFilePlanetInfoCallback(data, PTTrue, description, size, i, mirrorNames, mirrorURLs);
  
  	return GHTTPTrue;
  }
  
  // 9/7/2004 (xgd) ptLookupFilePlanetInfo() deprecated; per case 2724.
  //
  PTBool ptLookupFilePlanetInfo
  (
  	int fileID,
  	ptFilePlanetInfoCallback callback,
  	PTBool blocking,
  	void * param
  )
  {
  	char aURL[PTA_MAX_STRING_SIZE];
  	ptaFilePlanetInfoData * data;
  
  	// Check the arguments.
  	///////////////////////
  	assert(callback);
  	if(!callback)
  		return PTFalse;
  
  	// override hostname?
  	if (gPTAFilePlanetURL[0] == '\0')
  		sprintf(gPTAFilePlanetURL, PTA_DEFAULT_FILEPLANET_URL);
  
  	// Store some data.
  	///////////////////
  	data = (ptaFilePlanetInfoData *)gsimalloc(sizeof(ptaFilePlanetInfoData));
  	if(!data)
  		return PTFalse;
  	memset(data, 0, sizeof(ptaFilePlanetInfoData));
  	data->callback = callback;
  	data->param = param;
  	data->fileID = fileID;
  
  	// Build the URL.
  	/////////////////
  	// 11/24/2004 - Added By Saad Nader
  	// Now using string size as limit for printing
  	// also null terminate string automatically
  	///////////////////////////////////////////////
  	snprintf(aURL, PTA_MAX_STRING_SIZE, 
  		"%s?file=%d&gamename=%s", gPTAFilePlanetURL, fileID, __GSIACGamename);
  	
  	
  	// Send the request.
  	////////////////////
  	if((ghttpGetFileA(aURL, (GHTTPBool)blocking, ptaFilePlanetCompletedCallback, data) == -1) && !blocking)
  		return PTFalse;
  
  	return PTTrue;
  }
  