CnC_Remastered_Collection

Command and Conquer: Red Alert
Log | Files | Refs | README | LICENSE

STATS.CPP (31139B)


      1 //
      2 // Copyright 2020 Electronic Arts Inc.
      3 //
      4 // TiberianDawn.DLL and RedAlert.dll and corresponding source code is free 
      5 // software: you can redistribute it and/or modify it under the terms of 
      6 // the GNU General Public License as published by the Free Software Foundation, 
      7 // either version 3 of the License, or (at your option) any later version.
      8 
      9 // TiberianDawn.DLL and RedAlert.dll and corresponding source code is distributed 
     10 // in the hope that it will be useful, but with permitted additional restrictions 
     11 // under Section 7 of the GPL. See the GNU General Public License in LICENSE.TXT 
     12 // distributed with this program. You should have received a copy of the 
     13 // GNU General Public License along with permitted additional restrictions 
     14 // with this program. If not, see https://github.com/electronicarts/CnC_Remastered_Collection
     15 
     16 
     17 /***********************************************************************************************
     18  ***              C O N F I D E N T I A L  ---  W E S T W O O D  S T U D I O S               ***
     19  ***********************************************************************************************
     20  *                                                                                             *
     21  *                 Project Name : Command & Conquer                                            *
     22  *                                                                                             *
     23  *                    File Name : WINSTUB.CPP                                                  *
     24  *                                                                                             *
     25  *                   Programmer : Steve Tall                                                   *
     26  *                                                                                             *
     27  *                   Start Date : 05/29/1996                                                   *
     28  *                                                                                             *
     29  *                  Last Update : May 29th 1996 [ST]                                           *
     30  *                                                                                             *
     31  *---------------------------------------------------------------------------------------------*
     32  * Overview:                                                                                   *
     33  *  Internet game statistics to collect and upload to the server                               *
     34  *                                                                                             *
     35  *                                                                                             *
     36  *---------------------------------------------------------------------------------------------*
     37  *                                                                                             *
     38  * Functions:                                                                                  *
     39  * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
     40 
     41 #ifdef WIN32
     42 
     43 #include	"function.h"
     44 #include "tcpip.h"
     45 #include "packet.h"
     46 #include "ccdde.h"
     47 
     48 #define FIELD_PACKET_TYPE						"TYPE"
     49 #define FIELD_GAME_ID							"IDNO"
     50 #define FIELD_START_CREDITS						"CRED"
     51 #define FIELD_BASES								"BASE"
     52 #define FIELD_TIBERIUM							"TIBR"
     53 #define FIELD_CRATES							"CRAT"
     54 #define FIELD_AI_PLAYERS						"AIPL"
     55 #define FIELD_CAPTURE_THE_FLAG					"FLAG"
     56 #define FIELD_START_UNIT_COUNT					"UNIT"
     57 #define FIELD_TECH_LEVEL						"TECH"
     58 #define FIELD_SCENARIO							"SCEN"
     59 #define FIELD_COMPLETION						"CMPL"
     60 #define FIELD_START_TIME						"TIME"
     61 #define FIELD_GAME_DURATION						"DURA"
     62 #define FIELD_FRAME_RATE						"AFPS"
     63 #define FIELD_SPEED_SETTING						"SPED"
     64 #define FIELD_GAME_VERSION						"VERS"
     65 #define FIELD_GAME_BUILD_DATE					"DATE"
     66 #define FIELD_COVERT_PRESENT					"COVT"
     67 #define FIELD_CPU_TYPE							"PROC"
     68 #define FIELD_MEMORY							"MEMO"
     69 #define FIELD_VIDEO_MEMORY						"VIDM"
     70 #define FIELD_SHADOW_REGROWS					"SHAD"
     71 
     72 #ifdef WOLAPI_INTEGRATION
     73 #define FIELD_HOSTORNOT							"SDFX"
     74 #define FIELD_TOURNAMENT						"TRNY"
     75 #define FIELD_NUM_INITIAL_PLAYERS				"NUMP"
     76 #define FIELD_NUM_REMAINING_PLAYERS				"REMN"
     77 #define FIELD_DISCONNECT_PINGS					"PING"
     78 #define FIELD_COMPUTERTOOKOVER					"QUIT"
     79 //#define FIELD_HARDWARE_GUID					"GUID"
     80 #define FIELD_PLAYER1_IP						"ADR1"
     81 #define FIELD_PLAYER2_IP						"ADR2"
     82 #endif
     83 
     84 //	ajw The following were never used (thank god).
     85 #define FIELD_PLAYER1_HANDLE					"NAM1"
     86 #define FIELD_PLAYER2_HANDLE					"NAM2"
     87 #define FIELD_PLAYER1_TEAM						"SID1"
     88 #define FIELD_PLAYER2_TEAM						"SID2"
     89 #define FIELD_PLAYER1_COLOR						"COL1"
     90 #define FIELD_PLAYER2_COLOR						"COL2"
     91 #define FIELD_PLAYER1_CREDITS					"CRD1"
     92 #define FIELD_PLAYER2_CREDITS					"CRD2"
     93 
     94 #define FIELD_PLAYER1_UNITS_LEFT				"UNL1"
     95 #define FIELD_PLAYER2_UNITS_LEFT				"UNL2"
     96 #define FIELD_PLAYER1_INFANTRY_LEFT				"INL1"
     97 #define FIELD_PLAYER2_INFANTRY_LEFT				"INL2"
     98 #define FIELD_PLAYER1_PLANES_LEFT				"PLL1"
     99 #define FIELD_PLAYER2_PLANES_LEFT				"PLL2"
    100 #define FIELD_PLAYER1_BUILDINGS_LEFT			"BLL1"
    101 #define FIELD_PLAYER2_BUILDINGS_LEFT			"BLL2"
    102 #define FIELD_PLAYER1_VESSELS_LEFT				"VSL1"
    103 #define FIELD_PLAYER2_VESSELS_LEFT				"VSL2"
    104 
    105 #define FIELD_PLAYER1_UNITS_BOUGHT				"UNB1"
    106 #define FIELD_PLAYER2_UNITS_BOUGHT				"UNB2"
    107 #define FIELD_PLAYER1_INFANTRY_BOUGHT			"INB1"
    108 #define FIELD_PLAYER2_INFANTRY_BOUGHT			"INB2"
    109 #define FIELD_PLAYER1_PLANES_BOUGHT				"PLB1"
    110 #define FIELD_PLAYER2_PLANES_BOUGHT				"PLB2"
    111 #define FIELD_PLAYER1_BUILDINGS_BOUGHT			"BLB1"
    112 #define FIELD_PLAYER2_BUILDINGS_BOUGHT			"BLB2"
    113 #define FIELD_PLAYER1_VESSELS_BOUGHT			"VSB1"
    114 #define FIELD_PLAYER2_VESSELS_BOUGHT			"VSB2"
    115 
    116 #define FIELD_PLAYER1_UNITS_KILLED				"UNK1"
    117 #define FIELD_PLAYER2_UNITS_KILLED				"UNK2"
    118 #define FIELD_PLAYER1_INFANTRY_KILLED			"INK1"
    119 #define FIELD_PLAYER2_INFANTRY_KILLED			"INK2"
    120 #define FIELD_PLAYER1_PLANES_KILLED				"PLK1"
    121 #define FIELD_PLAYER2_PLANES_KILLED				"PLK2"
    122 #define FIELD_PLAYER1_BUILDINGS_KILLED			"BLK1"
    123 #define FIELD_PLAYER2_BUILDINGS_KILLED			"BLK2"
    124 #define FIELD_PLAYER1_VESSELS_KILLED			"VSK1"
    125 #define FIELD_PLAYER2_VESSELS_KILLED			"VSK2"
    126 
    127 #define FIELD_PLAYER1_BUILDINGS_CAPTURED		"BLC1"
    128 #define FIELD_PLAYER2_BUILDINGS_CAPTURED		"BLC2"
    129 
    130 #define FIELD_PLAYER1_CRATES_FOUND				"CRA1"
    131 #define FIELD_PLAYER2_CRATES_FOUND				"CRA2"
    132 #define FIELD_PLAYER1_HARVESTED					"HRV1"
    133 #define FIELD_PLAYER2_HARVESTED					"HRV2"
    134 
    135 
    136 #define	PACKET_TYPE_HOST_GAME_INFO		(unsigned char) 50
    137 #define	PACKET_TYPE_GUEST_GAME_INFO		(unsigned char) 51
    138 
    139 //	Note: These enums match those in the game results server code.
    140 enum {
    141 	COMPLETION_CONNECTION_LOST,
    142 	COMPLETION_PLAYER_1_WON,
    143 	COMPLETION_PLAYER_1_WON_BY_RESIGNATION,
    144 	COMPLETION_PLAYER_1_WON_BY_DISCONNECTION,
    145 	COMPLETION_PLAYER_2_WON,
    146 	COMPLETION_PLAYER_2_WON_BY_RESIGNATION,
    147 	COMPLETION_PLAYER_2_WON_BY_DISCONNECTION,
    148 #ifdef FIXIT_VERSION_3		//	Stalemate games.
    149 	COMPLETION_WASH = 64,
    150 #endif
    151 };
    152 
    153 
    154 extern unsigned long 	PlanetWestwoodGameID;
    155 extern HINSTANCE		ProgramInstance;
    156 extern unsigned long 	PlanetWestwoodStartTime;
    157 
    158 extern "C" char	CPUType;
    159 
    160 
    161 bool			GameTimerInUse = false;
    162 TimerClass	GameTimer;
    163 long			GameEndTime;
    164 void			*PacketLater = NULL;
    165 
    166 //#include "WolDebug.h"
    167 
    168 #ifdef WOLAPI_INTEGRATION
    169 #include "WolapiOb.h"
    170 extern WolapiObject* pWolapi;
    171 
    172 extern bool bReconnectDialogCancelled;
    173 #endif
    174 
    175 /***********************************************************************************************
    176  * Send_Statistics_To_Server -- sends internet game statistics to the Westeood server          *
    177  *                                                                                             *
    178  *                                                                                             *
    179  *                                                                                             *
    180  * INPUT:    Nothing                                                                           *
    181  *                                                                                             *
    182  * OUTPUT:   Nothing                                                                           *
    183  *                                                                                             *
    184  * WARNINGS: None                                                                              *
    185  *                                                                                             *
    186  * HISTORY:                                                                                    *
    187  *    5/29/96 12:38PM ST : Created                                                             *
    188  *=============================================================================================*/
    189 
    190 void Send_Statistics_Packet(void)
    191 {
    192 #if (0)//PG
    193 //	debugprint( "Stats: Send_Statistics_Packet() called.\n" );
    194 #ifndef INTERNET_OFF // Denzil 5/4/98
    195 
    196 #ifdef WOLAPI_INTEGRATION
    197 	if( !pWolapi )			//	Should no longer ever happen.
    198 		return;
    199 #endif
    200 
    201 	PacketClass	stats;
    202 	HouseClass	*player;
    203 	static int	packet_size;
    204 	int			index;
    205 	bool			packet_later = false;	// Should the packet be sent later
    206 	void			*packet;
    207 
    208 	static char	field_player_handle[5] 				= { "NAM?" };
    209 	static char	field_player_team[5] 				= { "SID?" };
    210 	static char	field_player_color[5]				= { "COL?" };
    211 	static char field_player_credits[5]				= { "CRD?" };
    212 	static char field_player_units_left[5]			= { "UNL?" };
    213 	static char field_player_infantry_left[5]		= { "INL?" };
    214 	static char field_player_planes_left[5]		= { "PLL?" };
    215 	static char field_player_buildings_left[5]	= { "BLL?" };
    216 	static char field_player_vessels_left[5]		= { "VSL?" };
    217 	static char field_player_units_bought[5] 		= { "UNB?" };
    218 	static char field_player_infantry_bought[5] 	= { "INB?" };
    219 	static char field_player_planes_bought[5]		= { "PLB?" };
    220 	static char field_player_buildings_bought[5]	= { "BLB?" };
    221 	static char field_player_vessels_bought[5]	= { "VSB?" };
    222 	static char field_player_units_killed[5] 		= { "UNK?" };
    223 	static char field_player_infantry_killed[5] 	= { "INK?" };
    224 	static char field_player_planes_killed[5]		= { "PLK?" };
    225 	static char field_player_buildings_killed[5]	= { "BLK?" };
    226 	static char field_player_vessels_killed[5]	= { "VSK?" };
    227 	static char field_player_buildings_captured[5] = { "BLC?" };
    228 	static char field_player_crates_found[5]		= { "CRA?" };
    229 	static char field_player_harvested[5]			= { "HRV?" };
    230 
    231 	static char *houses[] = {
    232 		"SPA",
    233 		"GRE",
    234 		"USS",
    235 		"ENG",
    236 		"ITA",
    237 		"GER",
    238 		"FRA",
    239 		"TKY",
    240 		"GUD",
    241 		"BAD",
    242 		"CIV",
    243 		"JP ",
    244 		"M01",
    245 		"M02",
    246 		"M03",
    247 		"M04",
    248 		"M05",
    249 		"M06",
    250 		"M07",
    251 		"M08"
    252 	};
    253 
    254 	if (!PacketLater){
    255 
    256 		/*
    257 		** Field to identify this as C&C 95 internet game statistics packet
    258 		*/
    259 #ifdef WOLAPI_INTEGRATION
    260 		if( pWolapi->bGameServer )
    261 		{
    262 			stats.Add_Field( FIELD_HOSTORNOT, (unsigned char)0 );		//	Reversed meaning of this for Neal.
    263 		}else{
    264 			stats.Add_Field( FIELD_HOSTORNOT, (unsigned char)1 );
    265 		}
    266 #else
    267 		if (Server){
    268 			stats.Add_Field(FIELD_PACKET_TYPE, PACKET_TYPE_HOST_GAME_INFO);
    269 		}else{
    270 			stats.Add_Field(FIELD_PACKET_TYPE, PACKET_TYPE_GUEST_GAME_INFO);
    271 		}
    272 #endif
    273 
    274 		/*
    275 		** Game ID. A unique game identifier assigned by WChat.
    276 		*/
    277 		stats.Add_Field(FIELD_GAME_ID, PlanetWestwoodGameID);
    278 
    279 #ifdef WOLAPI_INTEGRATION
    280 
    281 		//	Number of players initially in game.
    282 		stats.Add_Field( FIELD_NUM_INITIAL_PLAYERS, (unsigned long)pWolapi->GameInfoCurrent.iPlayerCount );
    283 //debugprint( "Stats: number of initial players is %i\n", pWolapi->GameInfoCurrent.iPlayerCount );
    284 
    285 		//	Number of players remaining in game. Not sure of what use this will be statistically...
    286 		stats.Add_Field( FIELD_NUM_REMAINING_PLAYERS, (unsigned long)Session.Players.Count() );
    287 //debugprint( "Stats: number of remaining players is %i\n", Session.Players.Count() );
    288 
    289 		//	Whether or not this was a tournament game.
    290 		stats.Add_Field( FIELD_TOURNAMENT, (unsigned char)( pWolapi->GameInfoCurrent.bTournament ? 1 : 0 ) );
    291 
    292 //	ajw This is now in WOLAPI...
    293 //		//	A unique value that identifies the machine that the game was played on.
    294 //		HW_PROFILE_INFO hwinfo;
    295 //		::GetCurrentHwProfile( &hwinfo );
    296 //		stats.Add_Field( FIELD_HARDWARE_GUID, hwinfo.szHwProfileGuid );
    297 #endif
    298 
    299 		/*
    300 		** Start credits.
    301 		*/
    302 		stats.Add_Field(FIELD_START_CREDITS, (unsigned long)Session.Options.Credits);
    303 
    304 		/*
    305 		** Bases (On/Off)
    306 		*/
    307 		stats.Add_Field(FIELD_BASES, Session.Options.Bases ? "ON" : "OFF");
    308 
    309 		/*
    310 		** Tiberium (On/Off)
    311 		*/
    312 		stats.Add_Field(FIELD_TIBERIUM, Session.Options.Tiberium ? "ON" : "OFF");
    313 
    314 		/*
    315 		** Crates (On/Off)
    316 		*/
    317 		stats.Add_Field(FIELD_CRATES, Session.Options.Goodies ? "ON" : "OFF");
    318 
    319 		/*
    320 		** AI Players (On/Off)
    321 		*/
    322 		stats.Add_Field(FIELD_AI_PLAYERS, (unsigned long) Session.Options.AIPlayers);
    323 
    324 		/*
    325 		** Shadow regrowth enabled
    326 		*/
    327 		stats.Add_Field(FIELD_SHADOW_REGROWS, Special.IsShadowGrow ? "ON" : "OFF" );
    328 
    329 		/*
    330 		** Capture the flag mode (On/Off)
    331 		*/
    332 		stats.Add_Field(FIELD_CAPTURE_THE_FLAG, Special.IsCaptureTheFlag ? "ON" : "OFF");
    333 
    334 		/*
    335 		** Start unit count
    336 		*/
    337 		stats.Add_Field(FIELD_START_UNIT_COUNT, (unsigned long)Session.Options.UnitCount);
    338 
    339 		/*
    340 		** Tech level.
    341 		*/
    342 		stats.Add_Field(FIELD_TECH_LEVEL, (unsigned long)BuildLevel);
    343 
    344 		/*
    345 		** Scenario
    346 		*/
    347 #if (1)
    348 
    349 		stats.Add_Field(FIELD_SCENARIO, Session.Options.ScenarioDescription);
    350 
    351 #else //(1)
    352 		char fname[128];
    353 		char namebuffer[40];
    354 		char *abuffer = (char *)_ShapeBuffer;
    355 		memset(abuffer, '\0', _ShapeBufferSize);
    356 		sprintf(fname,"%s.INI",Scen.ScenarioName);
    357 		CCFileClass fileo;
    358 		fileo.Set_Name (fname);
    359 		fileo.Read(abuffer, _ShapeBufferSize-1);
    360 		fileo.Close();
    361 		WWGetPrivateProfileString("Basic", "Name", "Nulls-Ville", namebuffer, 40, abuffer);
    362 		stats.Add_Field(FIELD_SCENARIO, namebuffer);
    363 		//stats.Add_Field(FIELD_SCENARIO, MPlayerScenarios[ScenarioIdx]);
    364 #endif	//(1)
    365 
    366 #ifdef WOLAPI_INTEGRATION
    367 		//	Completion status is set for Tournament games only - ajw.
    368 		if( pWolapi->GameInfoCurrent.bTournament )
    369 		{
    370 #endif
    371 
    372 			/*
    373 			** Game completion status.		
    374 			**
    375 			**  Connection lost.
    376 			**  Player 1 won
    377 			**  Player 2 won
    378 			**  Player 1 won by resignation
    379 			**  Player 2 won by resignation
    380 			**  Player 1 aborted
    381 			**  Player 2 aborted
    382 			**
    383 			**	Game was a draw
    384 			*/
    385 			HouseClass *player1 = NULL;
    386 			HouseClass *player2 = NULL;
    387 			for (int h=0 ; h<Session.Players.Count(); h++){
    388 				HouseClass *ptr = HouseClass::As_Pointer ((HousesType)(h + HOUSE_MULTI1));
    389 				if (ptr->IsHuman){
    390 					if (player1){
    391 						player2 = ptr;
    392 						break;
    393 					}else{
    394 						player1 = ptr;
    395 					}
    396 				}
    397 			}
    398 
    399 			int completion = -1;
    400 
    401 			if (player1 && player2){			//	Can this ever fail?		ajw
    402 #ifdef FIXIT_VERSION_3
    403 				//	Send IP addresses of both players.
    404 				NetNumType net;
    405 				NetNodeType node;
    406 				char szIPAddress[ 30 ];
    407 				Session.Players[ 0 ]->Address.Get_Address( net, node );
    408 				sprintf( szIPAddress, "%i.%i.%i.%i", node[0], node[1], node[2], node[3] );
    409 				if( strcmp( szIPAddress, "255.255.255.255" ) == 0 )
    410 				{
    411 
    412 					//	Ok. It's not set. Let's try to get it ourselves...
    413 					char szHostName[ 512 ];
    414 					int iRes = gethostname( szHostName, 512 );
    415 					if( iRes != SOCKET_ERROR )	//	else forget about trying
    416 					{
    417 //						debugprint( "gethostname got me %s\n", szHostName );
    418 						struct hostent* pHostent = gethostbyname( szHostName );
    419 						if( pHostent )	//	else forget about trying
    420 						{
    421 							int i = 0;
    422 							int* piAddress = (int*)pHostent->h_addr_list[ i ];
    423 							while( piAddress )
    424 							{
    425 								//	There is a non-null value for this h_addr_list entry.
    426 								char szAsciiIP[ 30 ];
    427 								strcpy( szAsciiIP, inet_ntoa( *( (struct in_addr*)piAddress ) ) );
    428 								//	We have an address in the right form.
    429 								//	Now, is it an address in a private network? If so we should ignore it.
    430 								unsigned char q1 = ( (char*)piAddress )[ 0 ];	//	First digit.
    431 								unsigned char q2 = ( (char*)piAddress )[ 1 ];	//	Second digit.
    432 //								debugprint( "ip: %s\n", szAsciiIP );
    433 								if( q1 == 10 || ( q1 == 172 && ( q2 >= 16 && q2 <= 31 ) ) || ( q1 == 192 && q2 == 168 ) )
    434 								{
    435 									//	This is a private network address - ignore it and go on to next.
    436 								}
    437 								else
    438 								{
    439 									strcpy( szIPAddress, szAsciiIP );
    440 									break;
    441 								}
    442 								piAddress = (int*)pHostent->h_addr_list[ ++i ];
    443 							}
    444 						}
    445 //						else
    446 //							debugprint( "gethostbyname failed. Error %i\n", WSAGetLastError() );
    447 					}
    448 //					else
    449 //						debugprint( "gethostname failed with %i, error %i\n", iRes, WSAGetLastError() );
    450 				}
    451 				stats.Add_Field( FIELD_PLAYER1_IP, (char*)szIPAddress );
    452 				Session.Players[ 1 ]->Address.Get_Address( net, node );
    453 				sprintf( szIPAddress, "%i.%i.%i.%i", node[0], node[1], node[2], node[3] );
    454 				stats.Add_Field( FIELD_PLAYER2_IP, (char*)szIPAddress );
    455 #endif				
    456 #ifdef FIXIT_VERSION_3		//	Stalemate games.
    457 				if( Scen.bLocalProposesDraw && Scen.bOtherProposesDraw )
    458 				{
    459 					completion = COMPLETION_WASH;
    460 				}
    461 				else
    462 				{
    463 #endif
    464 				if (ConnectionLost){
    465 #ifdef WOLAPI_INTEGRATION
    466 					if( bReconnectDialogCancelled )
    467 					{
    468 						if( Session.Players[ 0 ]->Player.ID == HOUSE_MULTI1 )
    469 							//	I am player1.
    470 							completion = COMPLETION_PLAYER_2_WON_BY_DISCONNECTION;
    471 						else
    472 							completion = COMPLETION_PLAYER_1_WON_BY_DISCONNECTION;
    473 					}
    474 					else
    475 					{
    476 						completion = COMPLETION_CONNECTION_LOST;
    477 						if( pWolapi->bDisconnectPingingCompleted )
    478 						{
    479 							char szPingResult[ 8 ];		//	Format is "x/y a/b", e.g., "3/5 4/5"
    480 							pWolapi->DisconnectPingResultsString( szPingResult );
    481 							stats.Add_Field( FIELD_DISCONNECT_PINGS, (char*)szPingResult );
    482 						}
    483 //						else
    484 //							debugprint( "Stats: bDisconnectPingingCompleted is false! Should be finished!!!!!!!!!!!!!!!\n" );
    485 					}
    486 #else
    487 					completion = COMPLETION_CONNECTION_LOST;
    488 #endif
    489 				}else{
    490 
    491 					if (player1->IsGiverUpper){
    492 						completion = COMPLETION_PLAYER_2_WON_BY_DISCONNECTION;
    493 					}
    494 
    495 					if (player2->IsGiverUpper){
    496 						completion = COMPLETION_PLAYER_1_WON_BY_DISCONNECTION;
    497 					}
    498 
    499 
    500 					if (player2->IsDefeated){
    501 						/*
    502 						** Player 1 won. Find out how.
    503 						*/
    504 						completion = COMPLETION_PLAYER_1_WON;
    505 						if (player2->IsResigner){
    506 							completion = COMPLETION_PLAYER_1_WON_BY_RESIGNATION;
    507 						}else{
    508 							if (player2->IsGiverUpper){
    509 								completion = COMPLETION_PLAYER_1_WON_BY_DISCONNECTION;
    510 							}
    511 						}
    512 
    513 
    514 					}else{
    515 
    516 						if (player1->IsDefeated){
    517 							/*
    518 							** Player 2 won. Find out how.
    519 							*/
    520 							completion = COMPLETION_PLAYER_2_WON;
    521 							if (player1->IsResigner){
    522 								completion = COMPLETION_PLAYER_2_WON_BY_RESIGNATION;
    523 							}else{
    524 								if (player1->IsGiverUpper){
    525 									completion = COMPLETION_PLAYER_2_WON_BY_DISCONNECTION;
    526 								}
    527 							}
    528 						}
    529 					}
    530 				}
    531 #ifdef FIXIT_VERSION_3		//	Stalemate games.
    532 				}
    533 #endif
    534 			}
    535 
    536 			stats.Add_Field (FIELD_COMPLETION, (char) completion);
    537 //debugprint( "Stats: Tournament game completion value: %i\n", completion );
    538 
    539 #ifdef WOLAPI_INTEGRATION
    540 		}
    541 #endif
    542 
    543 
    544 		/*
    545 		** Game start time (GMT or Pacific?)
    546 		**
    547 		** Passed from WChat
    548 		*/
    549 		stats.Add_Field (FIELD_START_TIME, (long) PlanetWestwoodStartTime);
    550 
    551 		/*
    552 		** Game duration (seconds).
    553 		*/
    554 		stats.Add_Field (FIELD_GAME_DURATION, (long) GameEndTime/60);
    555 
    556 		/*
    557 		** Avg. frame rate.
    558 		*/
    559 #ifdef FIXIT_IP_CRASH
    560 		long divisor = GameEndTime / 60;
    561 		if (divisor != 0) {
    562 			stats.Add_Field (FIELD_FRAME_RATE, (long) Frame / (GameEndTime/60) );
    563 		} else {
    564 			stats.Add_Field (FIELD_FRAME_RATE, 0l);
    565 		}
    566 #else
    567 		stats.Add_Field (FIELD_FRAME_RATE, (long) Frame / (GameEndTime/60) );
    568 #endif
    569 
    570 
    571 		/*
    572 		** CPU type
    573 		*/
    574 		stats.Add_Field (FIELD_CPU_TYPE, (char)CPUType);
    575 
    576 		/*
    577 		** Memory
    578 		*/
    579 		MEMORYSTATUS	mem_info;
    580 		mem_info.dwLength=sizeof(mem_info);
    581 		GlobalMemoryStatus(&mem_info);
    582 		stats.Add_Field (FIELD_MEMORY, (long)mem_info.dwTotalPhys);
    583 
    584 		/*
    585 		** Video memory
    586 		*/
    587 		DDCAPS	video_capabilities;
    588 		long		video_memory;
    589 
    590 		if (DirectDrawObject){
    591 			video_capabilities.dwSize = sizeof (video_capabilities);
    592 			if (DD_OK == DirectDrawObject->GetCaps (&video_capabilities , NULL)){
    593 				video_memory = video_capabilities.dwVidMemTotal;
    594 				video_memory += 1024*1024 -1;
    595 				video_memory &= 0xfff00000;
    596 				stats.Add_Field (FIELD_VIDEO_MEMORY, (long) video_memory);
    597 			}
    598 		}
    599 
    600 		/*
    601 		** Game speed setting.
    602 		*/
    603 		stats.Add_Field (FIELD_SPEED_SETTING, (char)Options.GameSpeed);
    604 
    605 		/*
    606 		** Red Alert version/build date
    607 		*/
    608 		char	version[128];
    609 		sprintf (version, "V%s", VerNum.Version_Name() );
    610 		stats.Add_Field (FIELD_GAME_VERSION, (char*)version);
    611 
    612 		char path_to_exe[280];
    613 		FILETIME write_time;		//File time is 64 bits
    614 
    615 		GetModuleFileName (ProgramInstance, path_to_exe, 280);
    616 		RawFileClass file;
    617 		file.Set_Name(path_to_exe);
    618 		file.Open();
    619 		HANDLE handle = file.Get_File_Handle();
    620 
    621 		if (handle != INVALID_HANDLE_VALUE){
    622 			if (GetFileTime (handle, NULL, NULL, &write_time)){
    623 				write_time.dwLowDateTime = htonl (write_time.dwLowDateTime);
    624 				write_time.dwHighDateTime = htonl (write_time.dwHighDateTime);
    625 				stats.Add_Field (FIELD_GAME_BUILD_DATE, (void*)&write_time, sizeof (write_time));
    626 			}
    627 		}
    628 
    629 		/*
    630 		** Covert installed? (Yes/No)
    631 		*/
    632 		//stats.Add_Field(FIELD_COVERT_PRESENT, (char) Expansion_Present());
    633 
    634 		/*
    635 		** Build the player specific statistics
    636 		**
    637 		*/
    638 #ifdef WOLAPI_INTEGRATION
    639 		for (int house = 0 ; house < 8 ; house++){
    640 #else
    641 		for (int house = 0 ; house < 2 ; house++){
    642 #endif
    643 			player = HouseClass::As_Pointer((HousesType) (house + HOUSE_MULTI1));
    644 
    645 #ifdef WOLAPI_INTEGRATION
    646 			if( !player )
    647 				continue;
    648 #endif
    649 
    650 			/*
    651 			** Player handle.
    652 			*/
    653 			field_player_handle[3] = '1' + (char)house;
    654 #ifdef WOLAPI_INTEGRATION
    655 			stats.Add_Field (field_player_handle, (char*) player->InitialName);
    656 //debugprint( "Stats: Player %i name %s\n", house, (char*) player->InitialName );
    657 //debugprint( "Stats: Player %i ending name %s\n", house, (char*) player->IniName );
    658 #else
    659 			stats.Add_Field (field_player_handle, (char*) player->IniName);
    660 #endif
    661 
    662 #ifdef WOLAPI_INTEGRATION
    663 			//	Whether or not this player was taken over by the computer, due to his quitting the game.
    664 			if( strcmp( player->IniName, player->InitialName ) )
    665 				stats.Add_Field( FIELD_COMPUTERTOOKOVER, (unsigned char)1 );
    666 			else
    667 				stats.Add_Field( FIELD_COMPUTERTOOKOVER, (unsigned char)0 );
    668 #endif
    669 			/*
    670 			** Player team. (NOD or GDI)
    671 			*/
    672 			field_player_team[3] = '1' + (char)house;
    673 			stats.Add_Field (field_player_team, houses[player->ActLike]);
    674 
    675 			/*
    676 			** Player color
    677 			*/
    678 			field_player_color[3] = '1' + (char)house;
    679 			stats.Add_Field (field_player_color, (unsigned char) (player->Class->House - HOUSE_MULTI1));
    680 
    681 			/*
    682 			** Player end credits.
    683 			*/
    684 			field_player_credits[3] = '1' + (char)house;
    685 			stats.Add_Field (field_player_credits, player->Credits + player->Tiberium);
    686 
    687 			/*
    688 			** Number of each unit/building type built
    689 			*/
    690 			field_player_infantry_bought[3] = '1' + (char)house;
    691 			field_player_units_bought[3] = '1' + (char)house;
    692 			field_player_planes_bought[3] = '1' + (char)house;
    693 			field_player_buildings_bought[3] = '1' + (char)house;
    694 			field_player_vessels_bought[3] = '1' + (char)house;
    695 
    696 			player->InfantryTotals->To_Network_Format();
    697 			player->UnitTotals->To_Network_Format();
    698 			player->AircraftTotals->To_Network_Format();
    699 			player->BuildingTotals->To_Network_Format();
    700 			player->VesselTotals->To_Network_Format();
    701 
    702 			stats.Add_Field (field_player_infantry_bought, (void*) player->InfantryTotals->Get_All_Totals(), player->InfantryTotals->Get_Unit_Count()*4);
    703 			stats.Add_Field (field_player_units_bought, (void*) player->UnitTotals->Get_All_Totals(), player->UnitTotals->Get_Unit_Count()*4);
    704 			stats.Add_Field (field_player_planes_bought, (void*) player->AircraftTotals->Get_All_Totals(), player->AircraftTotals->Get_Unit_Count()*4);
    705 			stats.Add_Field (field_player_buildings_bought, (void*) player->BuildingTotals->Get_All_Totals(), player->BuildingTotals->Get_Unit_Count()*4);
    706 			stats.Add_Field (field_player_vessels_bought, (void*) player->VesselTotals->Get_All_Totals(), player->VesselTotals->Get_Unit_Count()*4);
    707 
    708 			player->InfantryTotals->To_PC_Format();
    709 			player->UnitTotals->To_PC_Format();
    710 			player->AircraftTotals->To_PC_Format();
    711 			player->BuildingTotals->To_PC_Format();
    712 
    713 			/*
    714 			** Clear out the counts and use the space to count up the current number of units/buildings
    715 			*/
    716 			player->InfantryTotals->Clear_Unit_Total();
    717 			player->AircraftTotals->Clear_Unit_Total();
    718 			player->UnitTotals->Clear_Unit_Total();
    719 			player->BuildingTotals->Clear_Unit_Total();
    720 			player->VesselTotals->Clear_Unit_Total();
    721 
    722 			/*
    723 			** Number of units remaining to player
    724 			*/
    725 			for (index = 0; index < Units.Count(); index++) {
    726 				UnitClass const * unit = Units.Ptr(index);
    727 				if (player == unit->House){
    728 					player->UnitTotals->Increment_Unit_Total (unit->Class->Type);
    729 				}
    730 			}
    731 
    732 			for (index = 0; index < Infantry.Count(); index++) {
    733 			InfantryClass const * infantry = Infantry.Ptr(index);
    734 				if (player == infantry->House && !infantry->Class->IsCivilian){
    735 					player->InfantryTotals->Increment_Unit_Total (infantry->Class->Type);
    736 				}
    737 			}
    738 
    739 			for (index = 0; index < Aircraft.Count(); index++) {
    740 			AircraftClass const * aircraft = Aircraft.Ptr(index);
    741 				if (player == aircraft->House){	// &&	aircraft->Class->Type != AIRCRAFT_CARGO){
    742 					player->AircraftTotals->Increment_Unit_Total (aircraft->Class->Type);
    743 				}
    744 			}
    745 
    746 			for (index = 0; index < Buildings.Count(); index++) {
    747 			BuildingClass const * building = Buildings.Ptr(index);
    748 				if (player == building->House){
    749 					player->BuildingTotals->Increment_Unit_Total (building->Class->Type);
    750 				}
    751 			}
    752 
    753 			for (index = 0; index < Vessels.Count(); index++) {
    754 			VesselClass const * vessel = Vessels.Ptr(index);
    755 				if (player == vessel->House){
    756 					player->VesselTotals->Increment_Unit_Total (vessel->Class->Type);
    757 				}
    758 			}
    759 
    760 
    761 			player->InfantryTotals->To_Network_Format();
    762 			player->UnitTotals->To_Network_Format();
    763 			player->AircraftTotals->To_Network_Format();
    764 			player->BuildingTotals->To_Network_Format();
    765 			player->VesselTotals->To_Network_Format();
    766 
    767 			field_player_infantry_left[3] = '1' + (char)house;
    768 			field_player_units_left[3] = '1' + (char)house;
    769 			field_player_planes_left[3] = '1' + (char)house;
    770 			field_player_buildings_left[3] = '1' + (char)house;
    771 #ifdef FIXIT_IP_STATS
    772 			field_player_vessels_left[3] = '1' + (char)house;
    773 #endif
    774 			stats.Add_Field (field_player_infantry_left, (void*) player->InfantryTotals->Get_All_Totals(), player->InfantryTotals->Get_Unit_Count()*4);
    775 			stats.Add_Field (field_player_units_left, (void*) player->UnitTotals->Get_All_Totals(), player->UnitTotals->Get_Unit_Count()*4);
    776 			stats.Add_Field (field_player_planes_left, (void*) player->AircraftTotals->Get_All_Totals(), player->AircraftTotals->Get_Unit_Count()*4);
    777 			stats.Add_Field (field_player_buildings_left, (void*) player->BuildingTotals->Get_All_Totals(), player->BuildingTotals->Get_Unit_Count()*4);
    778 			stats.Add_Field (field_player_vessels_left, (void*) player->VesselTotals->Get_All_Totals(), player->VesselTotals->Get_Unit_Count()*4);
    779 
    780 
    781 			/*
    782 			** Number of enemy units/buildings of each type destroyed.
    783 			*/
    784 
    785 			player->DestroyedInfantry->To_Network_Format();
    786 			player->DestroyedUnits->To_Network_Format();
    787 			player->DestroyedAircraft->To_Network_Format();
    788 			player->DestroyedBuildings->To_Network_Format();
    789 			player->DestroyedVessels->To_Network_Format();
    790 
    791 			field_player_infantry_killed[3] = '1' + (char)house;
    792 			field_player_units_killed[3] = '1' + (char)house;
    793 			field_player_planes_killed[3] = '1' + (char)house;
    794 			field_player_buildings_killed[3] = '1' + (char)house;
    795 			field_player_vessels_killed[3] = '1' + (char)house;
    796 
    797 			stats.Add_Field (field_player_infantry_killed, (void*) player->DestroyedInfantry->Get_All_Totals(), player->DestroyedInfantry->Get_Unit_Count()*4);
    798 			stats.Add_Field (field_player_units_killed, (void*) player->DestroyedUnits->Get_All_Totals(), player->DestroyedUnits->Get_Unit_Count()*4);
    799 			stats.Add_Field (field_player_planes_killed, (void*) player->DestroyedAircraft->Get_All_Totals(), player->DestroyedAircraft->Get_Unit_Count()*4);
    800 			stats.Add_Field (field_player_buildings_killed, (void*) player->DestroyedBuildings->Get_All_Totals(), player->DestroyedBuildings->Get_Unit_Count()*4);
    801 #ifdef FIXIT_VERSION_3
    802 			stats.Add_Field (field_player_vessels_killed, (void*) player->DestroyedVessels->Get_All_Totals(), player->DestroyedVessels->Get_Unit_Count()*4);
    803 #else
    804 			stats.Add_Field (field_player_vessels_killed, (void*) player->DestroyedVessels->Get_All_Totals(), player->DestroyedBuildings->Get_Unit_Count()*4);
    805 #endif
    806 
    807 
    808 			/*
    809 			** Number and type of enemy buildings captured
    810 			*/
    811 			field_player_buildings_captured[3] = '1' + (char)house;
    812 			player->CapturedBuildings->To_Network_Format();
    813 			stats.Add_Field (field_player_buildings_captured, (void*) player->CapturedBuildings->Get_All_Totals(), player->CapturedBuildings->Get_Unit_Count()*4);
    814 
    815 			/*
    816 			** Number of crates discovered and their contents
    817 			*/
    818 			field_player_crates_found[3] = '1' + (char)house;
    819 			player->TotalCrates->To_Network_Format();
    820 			stats.Add_Field (field_player_crates_found, (void*) player->TotalCrates->Get_All_Totals(), player->TotalCrates->Get_Unit_Count()*4);
    821 
    822 			/*
    823 			** Amount of tiberium turned into credits
    824 			*/
    825 			field_player_harvested[3] = '1' + (char)house;
    826 			stats.Add_Field (field_player_harvested, (unsigned long) player->HarvestedCredits);
    827 
    828 		}
    829 
    830 		/*
    831 		** Create the comms packet to be sent
    832 		*/
    833 		packet = stats.Create_Comms_Packet(packet_size);
    834 
    835 #ifndef WOLAPI_INTEGRATION	//	ajw - 'PacketLater' is no longer ever used.
    836 		/*
    837 		** If a player disconnected then dont send the packet at this time - save it for later
    838 		*/
    839 		if (completion == COMPLETION_PLAYER_1_WON_BY_DISCONNECTION
    840 			|| completion == COMPLETION_PLAYER_2_WON_BY_DISCONNECTION){
    841 			PacketLater = packet;
    842 			return;
    843 		}
    844 #endif
    845 
    846 	}else{		//else for if (!PacketLater)
    847 
    848 		/*
    849 		** Send the packet we calculated earlier when the disconnect occurred
    850 		*/
    851 		packet = PacketLater;
    852 		PacketLater = NULL;
    853 	}
    854 
    855 	/*
    856 	** Send it.....
    857 	*/
    858 	const char* szGameResServer;
    859 	int iPort;
    860 	if( pWolapi->GameInfoCurrent.GameKind == CREATEGAMEINFO::AMGAME )
    861 	{
    862 		szGameResServer = pWolapi->szGameResServerHost2;
    863 		iPort = pWolapi->iGameResServerPort2;
    864 	}
    865 	else
    866 	{
    867 		szGameResServer = pWolapi->szGameResServerHost1;
    868 		iPort = pWolapi->iGameResServerPort1;
    869 	}
    870 
    871 	if( *szGameResServer )
    872 	{
    873 		if( pWolapi->pNetUtil->RequestGameresSend( szGameResServer, iPort, (unsigned char*)packet, packet_size ) != S_OK )
    874 			//debugprint( "RequestGameresSend( %s, %i ) failed!!!\n", szGameResServer, iPort );
    875 			;
    876 	}
    877 
    878 	/*
    879 	** Save it to disk as well so I can see it
    880 	*/
    881 //	RawFileClass anotherfile ("packet.net");
    882 //	anotherfile.Write(packet, packet_size);
    883 //debugprint( "Wrote out packet.net\n" );
    884 
    885 	/*
    886 	** Tidy up
    887 	*/
    888 	delete [] packet;
    889 
    890 	GameStatisticsPacketSent = true;
    891 #endif // INTERNET_OFF
    892 #endif
    893 }
    894 
    895 
    896 
    897 void Register_Game_Start_Time(void)
    898 {
    899 
    900 	GameTimer.Set (0, true);
    901 	GameTimerInUse = true;
    902 }
    903 
    904 extern void Register_Game_End_Time(void)
    905 {
    906 	GameEndTime = GameTimer.Time();
    907 	GameTimerInUse = false;
    908 }
    909 
    910 
    911 #endif	//WIN32