CnC_Remastered_Collection

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

QUEUE.CPP (178697B)


      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 /* $Header:   F:\projects\c&c0\vcs\code\queue.cpv   2.24   11 Oct 1995 13:47:40   JOE_BOSTIC  $ */
     17 /***************************************************************************
     18  *                                                                         *
     19  *                 Project Name : Command & Conquer                        *
     20  *                                                                         *
     21  *                    File Name : QUEUE.CPP                                *
     22  *                                                                         *
     23  *                   Programmer : Bill R. Randolph                         *
     24  *                                                                         *
     25  *                   Start Date : 11/28/95                                 *
     26  *                                                                         *
     27  *                  Last Update : November 28, 1995 [BRR]                  *
     28  *                                                                         *
     29  *-------------------------------------------------------------------------*
     30  * Functions for Queueing Events:                                          *
     31  *   Queue_Mission -- Queue a mega mission event.                          *
     32  *   Queue_Options -- Queue the options event.                             *
     33  *   Queue_Exit -- Add the exit game event to the queue.                   *
     34  *                                                                         *
     35  * Functions for processing Queued Events:											*
     36  *   Queue_AI -- Process all queued events.                                *
     37  *   Queue_AI_Normal -- Process all queued events.                         *
     38  *   Queue_AI_Multiplayer -- Process all queued events.                    *
     39  *                                                                         *
     40  * Main Multiplayer Queue Logic:															*
     41  *   Wait_For_Players -- Waits for other systems to come on-line           *
     42  *   Generate_Timing_Event -- computes & queues a RESPONSE_TIME event      *
     43  *   Process_Send_Period -- timing for sending packets every 'n' frames    *
     44  *   Send_Packets -- sends out events from the OutList                     *
     45  *   Send_FrameSync -- Sends a FRAMESYNC packet                            *
     46  *   Process_Receive_Packet -- processes an incoming packet                *
     47  *   Process_Serial_Packet -- Handles an incoming serial packet            *
     48  *   Can_Advance -- determines if it's OK to advance to the next frame     *
     49  *   Process_Reconnect_Dialog -- processes the reconnection dialog         *
     50  *   Handle_Timeout -- attempts to reconnect; if fails, bails.             *
     51  *   Stop_Game -- stops the game															*
     52  *                                                                         *
     53  * Packet Compression / Decompression:													*
     54  *   Build_Send_Packet -- Builds a big packet from a bunch of little ones.	*
     55  *   Add_Uncompressed_Events -- adds uncompressed events to a packet       *
     56  *   Add_Compressed_Events -- adds compressed events to a packet        	*
     57  *   Breakup_Receive_Packet -- Splits a big packet into little ones.			*
     58  *   Extract_Uncompressed_Events -- extracts events from a packet				*
     59  *   Extract_Compressed_Events -- extracts events from a packet            *
     60  *                                                                         *
     61  * DoList Management:																		*
     62  *   Execute_DoList -- Executes commands from the DoList                   *
     63  *   Clean_DoList -- Cleans out old events from the DoList                 *
     64  *   Queue_Record -- Records the DoList to disk                            *
     65  *   Queue_Playback -- plays back queue entries from a record file         *
     66  *                                                                         *
     67  * Debugging:																					*
     68  *   Compute_Game_CRC -- Computes a CRC value of the entire game.				*
     69  *   Add_CRC -- Adds a value to a CRC                                      *
     70  *   Print_CRCs -- Prints a data file for finding Sync Bugs						*
     71  *   Init_Queue_Mono -- inits mono display                                 *
     72  *   Update_Queue_Mono -- updates mono display                             *
     73  *   Print_Framesync_Values -- displays frame-sync variables               *
     74  * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
     75 #include	"function.h"
     76 #include 	"tcpip.h"
     77 
     78 /********************************** Defines *********************************/
     79 #define SHOW_MONO	1
     80 
     81 int tmp_flag = 0;
     82 
     83 /********************************** Globals *********************************/
     84 //---------------------------------------------------------------------------
     85 //	GameCRC is the current computed CRC value for this frame.
     86 //	CRC[] is a record of our last 32 game CRC's.
     87 // ColorNames is for debug output in Print_CRCs
     88 //---------------------------------------------------------------------------
     89 #ifndef DEMO
     90 static unsigned long GameCRC;
     91 static unsigned long CRC[32] =
     92 	{0,0,0,0,0,0,0,0,0,0,
     93 	 0,0,0,0,0,0,0,0,0,0,
     94 	 0,0,0,0,0,0,0,0,0,0,
     95 	 0,0};
     96 
     97 static char *ColorNames[6] = {
     98 	"Yellow",
     99 	"Red",
    100 	"BlueGreen",
    101 	"Orange",
    102 	"Green",
    103 	"Blue",
    104 };
    105 #endif	//DEMO
    106 
    107 //...........................................................................
    108 // Mono debugging variables:
    109 // NetMonoMode: 0 = show connection output, 1 = flowcount output
    110 // NewMonoMode: set by anything that toggles NetMonoMode; re-inits screen
    111 // IsMono: used for taking control of Mono screen away from the engine
    112 //...........................................................................
    113 #ifndef DEMO
    114 int NetMonoMode = 1;
    115 int NewMonoMode = 1;
    116 static int IsMono = 0;
    117 #endif	//DEMO
    118 
    119 //---------------------------------------------------------------------------
    120 // Several routines return various codes; here's an enum for all of them.
    121 //---------------------------------------------------------------------------
    122 typedef enum RetcodeEnum {
    123 	RC_NORMAL,						// no news is good news
    124 	RC_PLAYER_READY,				// a new player has been heard from
    125 	RC_SCENARIO_MISMATCH,		// scenario mismatch
    126 	RC_DOLIST_FULL,				// DoList is full
    127 	RC_SERIAL_PROCESSED,			// modem: SERIAL packet was processed
    128 	RC_PLAYER_LEFT,				// modem: other player left the game
    129 	RC_HUNG_UP,						// modem has hung up
    130 	RC_NOT_RESPONDING,			// other player not responding (timeout/hung up)
    131 	RC_CANCEL,						// user cancelled
    132 } RetcodeType;
    133 
    134 
    135 /********************************* Prototypes *******************************/
    136 //...........................................................................
    137 // Main multiplayer queue logic
    138 //...........................................................................
    139 static void Queue_AI_Normal(void);
    140 #ifndef DEMO
    141 static void Queue_AI_Multiplayer(void);
    142 static RetcodeType Wait_For_Players(int first_time, ConnManClass *net,
    143 	int resend_delta, int dialog_time, int timeout, char *multi_packet_buf,
    144 	int my_sent, long *their_frame,  unsigned short *their_sent,
    145 	unsigned short *their_recv);
    146 static void Generate_Timing_Event(ConnManClass *net, int my_sent);
    147 static void Generate_Real_Timing_Event(ConnManClass *net, int my_sent);
    148 static void Generate_Process_Time_Event(ConnManClass *net);
    149 static int Process_Send_Period(ConnManClass *net);
    150 static int Send_Packets(ConnManClass *net, char *multi_packet_buf,
    151 	int multi_packet_max, int max_ahead, int my_sent);
    152 static void Send_FrameSync(ConnManClass *net, int cmd_count);
    153 static RetcodeType Process_Receive_Packet(ConnManClass *net,
    154 	char *multi_packet_buf, int id, int packetlen, long *their_frame,
    155 	unsigned short *their_sent, unsigned short *their_recv);
    156 static RetcodeType Process_Serial_Packet(char *multi_packet_buf,
    157 	int first_time);
    158 static int Can_Advance(ConnManClass *net, int max_ahead, long *their_frame,
    159 	unsigned short *their_sent, unsigned short *their_recv);
    160 static int Process_Reconnect_Dialog(CountDownTimerClass *timeout_timer,
    161 	long *their_frame, int num_conn, int reconn, int fresh);
    162 static int Handle_Timeout(ConnManClass *net, long *their_frame,
    163 	unsigned short *their_sent, unsigned short *their_recv);
    164 static void Stop_Game(void);
    165 #endif	//DEMO
    166 
    167 //...........................................................................
    168 // Packet compression/decompression:
    169 //...........................................................................
    170 #ifndef DEMO
    171 static int Build_Send_Packet(void *buf, int bufsize, int frame_delay,
    172 	int num_cmds, int cap);
    173 static int Breakup_Receive_Packet(void *buf, int bufsize );
    174 #endif	//DEMO
    175 int Add_Uncompressed_Events(void *buf, int bufsize, int frame_delay, int size,
    176 	int cap);
    177 int Add_Compressed_Events(void *buf, int bufsize, int frame_delay, int size,
    178 	int cap);
    179 int Extract_Uncompressed_Events(void *buf, int bufsize);
    180 int Extract_Compressed_Events(void *buf, int bufsize);
    181 
    182 //...........................................................................
    183 // DoList management:
    184 //...........................................................................
    185 static int Execute_DoList(int max_houses, HousesType base_house,
    186 	ConnManClass *net, TCountDownTimerClass *skip_crc,
    187 	long *their_frame, unsigned short *their_sent, unsigned short *their_recv);
    188 static void Clean_DoList(ConnManClass *net);
    189 #ifndef DEMO
    190 static void Queue_Record(void);
    191 static void Queue_Playback(void);
    192 #endif	//DEMO
    193 
    194 //...........................................................................
    195 // Debugging:
    196 //...........................................................................
    197 #ifndef DEMO
    198 static void Compute_Game_CRC(void);
    199 static void Init_Queue_Mono(ConnManClass *net);
    200 static void Update_Queue_Mono(ConnManClass *net, int flow_index);
    201 static void Print_Framesync_Values(long curframe, unsigned long max_ahead,
    202 	int num_connections, unsigned short *their_recv,
    203 	unsigned short *their_sent, unsigned short my_sent);
    204 #endif	//DEMO
    205 void Add_CRC(unsigned long *crc, unsigned long val);
    206 void Print_CRCs(EventClass *);
    207 
    208 extern void Keyboard_Process(KeyNumType &input);
    209 void Dump_Packet_Too_Late_Stuff(EventClass *event);
    210 
    211 extern void Register_Game_End_Time(void);
    212 extern void Send_Statistics_Packet(void);
    213 
    214 /***************************************************************************
    215  * Queue_Mission -- Queue a mega mission event.                            *
    216  *                                                                         *
    217  * This routine is called when the player causes a change to a game unit. 	*
    218  * The event that initiates the change is queued to as a result of a call 	*
    219  * to this routine.                 													*
    220  *                                                                         *
    221  * INPUT:                                                                  *
    222  *		whom		Whom this mission request applies to (a friendly unit).     *
    223  *    mission	The mission to assign to this object.                       *
    224  *    target	The target of this mission (if any).                        *
    225  *    dest		The movement destination for this mission (if any).         *
    226  *                                                                         *
    227  * OUTPUT:                                                                 *
    228  *		Was the mission request queued successfully?                         *
    229  *                                                                         *
    230  * WARNINGS:                                                               *
    231  *		none.                                                                *
    232  *                                                                         *
    233  * HISTORY:                                                                *
    234  *   09/21/1995 JLB : Created.                                             *
    235  *=========================================================================*/
    236 bool Queue_Mission(TARGET whom, MissionType mission, TARGET target,
    237 	TARGET destination)
    238 {
    239 	if (! OutList.Add(EventClass(whom, mission, target, destination))) {
    240 		return(false);
    241 	}
    242 	else {
    243 		return(true);
    244 	}
    245 
    246 }	/* end of Queue_Mission */
    247 
    248 
    249 /***************************************************************************
    250  * Queue_Options -- Queue the options event.                               *
    251  *                                                                         *
    252  * INPUT:                                                                  *
    253  *		none.																						*
    254  *                                                                         *
    255  * OUTPUT:                                                                 *
    256  *		Was the options screen event queued successfully?                    *
    257  *                                                                         *
    258  * WARNINGS:                                                               *
    259  *		none.																						*
    260  *                                                                         *
    261  * HISTORY:                                                                *
    262  *   09/21/1995 JLB : Created.                                             *
    263  *=========================================================================*/
    264 bool Queue_Options(void)
    265 {
    266 	if (! OutList.Add(EventClass(EventClass::OPTIONS))) {
    267 		return(false);
    268 	}
    269 	else {
    270 		return(true);
    271 	}
    272 
    273 }		/* end of Queue_Options */
    274 
    275 
    276 /***************************************************************************
    277  * Queue_Exit -- Add the exit game event to the queue.                     *
    278  *                                                                         *
    279  * INPUT:                                                                  *
    280  *		none.																						*
    281  *                                                                         *
    282  * OUTPUT:                                                                 *
    283  *		Was the exit event queued successfully?                              *
    284  *                                                                         *
    285  * WARNINGS:                                                               *
    286  *		none.																						*
    287  *                                                                         *
    288  * HISTORY:                                                                *
    289  *   09/21/1995 JLB : Created.                                             *
    290  *=========================================================================*/
    291 bool Queue_Exit(void)
    292 {
    293 	if (! OutList.Add(EventClass(EventClass::EXIT))) {
    294 		return(false);
    295 	}
    296 	else {
    297 		return(true);
    298 	}
    299 
    300 }		/* end of Queue_Exit */
    301 
    302 
    303 /***************************************************************************
    304  * Queue_AI -- Process all queued events.                                  *
    305  *                                                                         *
    306  * INPUT:                                                                  *
    307  *		none.																						*
    308  *                                                                         *
    309  * OUTPUT:                                                                 *
    310  *		none.																						*
    311  *                                                                         *
    312  * WARNINGS:                                                               *
    313  *		none.																						*
    314  *                                                                         *
    315  * HISTORY:                                                                *
    316  *   09/21/1995 JLB : Created.                                             *
    317  *=========================================================================*/
    318 void Queue_AI(void)
    319 {
    320 #ifdef DEMO
    321 	Queue_AI_Normal();
    322 #else	//DEMO
    323 
    324 	if (PlaybackGame) {
    325 		Queue_Playback();
    326 	}
    327 
    328 	else {
    329 
    330 		switch (GameToPlay) {
    331 
    332 			case GAME_NORMAL:
    333 				Queue_AI_Normal();
    334 				break;
    335 
    336 			case GAME_MODEM:
    337 			case GAME_NULL_MODEM:
    338 			case GAME_IPX:
    339 			case GAME_INTERNET:
    340 				Queue_AI_Multiplayer();
    341 				break;
    342 		}
    343 	}
    344 #endif	//DEMO
    345 
    346 }	/* end of Queue_AI */
    347 
    348 
    349 /***************************************************************************
    350  * Queue_AI_Normal -- Process all queued events.                           *
    351  *                                                                         *
    352  * This is the "normal" version of the queue management routine.  It does 	*
    353  * the following:		  																		*
    354  * - Transfers items in the OutList to the DoList									*
    355  * - Executes any commands in the DoList that are supposed to be done on 	*
    356  *   this frame #			  																	*
    357  * - Cleans out the DoList																	*
    358  *                                                                         *
    359  * INPUT:                                                                  *
    360  *		none.																						*
    361  *                                                                         *
    362  * OUTPUT:                                                                 *
    363  *		none.																						*
    364  *                                                                         *
    365  * WARNINGS:                                                               *
    366  *		none.																						*
    367  *                                                                         *
    368  * HISTORY:                                                                *
    369  *   09/21/1995 JLB : Created.                                             *
    370  *=========================================================================*/
    371 static void Queue_AI_Normal(void)
    372 {
    373 	//------------------------------------------------------------------------
    374 	//	Move events from the OutList (events generated by this player) into the
    375 	//	DoList (the list of events to execute).
    376 	//------------------------------------------------------------------------
    377 	while (OutList.Count) {
    378 		OutList.First().IsExecuted = false;
    379 		if (!DoList.Add(OutList.First())) {
    380 			;
    381 		}
    382 		OutList.Next();
    383 	}
    384 
    385 	//------------------------------------------------------------------------
    386 	// Save the DoList to disk, if we're in "Record" mode
    387 	//------------------------------------------------------------------------
    388 #ifndef DEMO
    389 	if (RecordGame) {
    390 		Queue_Record();
    391 	}
    392 #endif	//DEMO
    393 
    394 	//------------------------------------------------------------------------
    395 	// Execute the DoList
    396 	//------------------------------------------------------------------------
    397 	if (!Execute_DoList(1,PlayerPtr->Class->House, NULL, NULL, NULL,
    398 		NULL, NULL)) {
    399 		GameActive = 0;
    400 		return;
    401 	}
    402 	//------------------------------------------------------------------------
    403 	//	Clean out the DoList
    404 	//------------------------------------------------------------------------
    405 	Clean_DoList(NULL);
    406 
    407 }	/* end of Queue_AI_Normal */
    408 
    409 #ifndef DEMO
    410 
    411 /***************************************************************************
    412  * Queue_AI_Multiplayer -- Process all queued events.                      *
    413  *                                                                         *
    414  * This is the network version of the queue management routine.  It does 	*
    415  * the following:																				*
    416  * - If this is the 1st frame, waits for other systems to signal ready		*
    417  * - Generates a timing event, to allow the connection time to be dynamic	*
    418  * - Handles timing related to sending packets every 'n' frames				*
    419  * - Sends outgoing events																	*
    420  * - Frame-syncs to the other systems (see below)									*
    421  * - Executes & cleans out the DoList													*
    422  *                                                                         *
    423  * The Frame-Sync'ing logic is the heart & soul of network play.  It works	*
    424  * by ensuring that any system won't out-run the other system by more than	*
    425  * 'MaxAhead' frames; this in turn ensures that a packet's 						*
    426  * execution frame # won't have been passed by the time that packet is 		*
    427  * received by all systems.																*
    428  *                                                                         *
    429  * To achieve this, the system must keep track of all other system's 		*
    430  * current frame #'s; these are stored in an array called 'their_frame[]'. *
    431  * However, because current frame #'s are sent in FRAMEINFO packets, which *
    432  * don't require an ACK, and command packets are sent in packets requiring *
    433  * an ACK, it's possible for a command packet to get lost, and the next 	*
    434  * frame's FRAMEINFO packet to not get lost; the other system may then 		*
    435  * advance past the frame # the command is to execute on!  So, to prevent 	*
    436  * this, all FRAMEINFO packets include a CommandCount field.  This value 	*
    437  * tells the other system how many events it should have received by this 	*
    438  * time.  This system can therefore keep track of how many commands it's	*
    439  * actually received, and compare it to the CommandCount field, to see if 	*
    440  * it's missed an event packet.  The # of events we've received from each 	*
    441  * system is stored in 'their_recv[]', and the # events they say they've 	*
    442  * sent is stored in 'their_sent[]'.													*
    443  *                                                                         *
    444  * Thus, two conditions must be met in order to advance to the next frame:	*
    445  * - Our current frame # must be <= their_frame + MaxAhead						*
    446  * - their_recv[i] must be >= their_sent[i]											*
    447  *                                                                         *
    448  * 'their_frame[] is updated by Process_Receive_Packet()							*
    449  * 'their_recv[] is updated by Process_Receive_Packet()							*
    450  * 'their_sent[] is updated by Process_Receive_Packet()							*
    451  * 'my_sent' is updated by this routine.												*
    452  *                                                                         *
    453  * The only routines allowed to pop up dialogs are:								*
    454  * 	Wait_For_Players() (only pops up the reconnect dialog)					*
    455  * 	Execute_DoList() (tells if out of sync, or packet recv'd too late)	*
    456  *                                                                         *
    457  * Sign-off's are detected by:															*
    458  * - Timing out while waiting for a packet											*
    459  * - Detecting that the other player is now at the score screen or 			*
    460  *   connection dialog (serial)															*
    461  * - If we see an EventClass::EXIT event on the private channel				*
    462  *                                                                         *
    463  * The current communications protocol, COMM_PROTOCOL_MULTI_E_COMP, has 	*
    464  * the following properites:																*
    465  * - It compresses packets, so that the minimum number of bytes are 			*
    466  *   transmitted.  Packets are compressed by extracting all info common to *
    467  *   the events into the packet header, and then sending only the bytes 	*
    468  *   relevant to each type of event.  For instance, if 100 infantry guys 	*
    469  *   are told to move to the same location, the command itself & the 		*
    470  *   location will be included in the 1st movement command only; after 		*
    471  *   that, there will be a rep count then 99 infantry TARGET numbers, 		*
    472  *   identifying all the infantry told to move.										*
    473  * - The protocol also only sends packets out every 'n' frames.  This cuts *
    474  *   the data rate dramatically.  It means that 'MaxAhead' must be 			*
    475  *   divisible by 'n'; also, the minimum value for 'MaxAhead' is 				*
    476  *   'n * 2', to give both sides some "breathing" room in case a FRAMEINFO	*
    477  *   packet gets missed.																	*
    478  *                                                                         *
    479  * Note:  For synchronization-waiting loops (like waiting to hear from all *
    480  * other players, waiting to advance to the next frame, etc), use 			*
    481  * Net.Num_Connections() rather than NumPlayers; this reflects the 			*
    482  * actual # of connections, and can be "faked" into playing even when 		*
    483  * there aren't any other players actually there.  A typical example of 	*
    484  * this is playing back a recorded game.  For command-execution loops, use *
    485  * NumPlayers.  This ensures all commands get executed, even if 				*
    486  * there isn't a human generating those commands.									*
    487  *                                                                         *
    488  * The modem works a little differently from the network in this respect:	*
    489  * - The connection has to stay "alive" even if the other player exits to 	*
    490  *   the join dialog.  This prevents each system from timing out & hanging *
    491  *   the modem up.  Thus, packets are sent back & forth & just thrown away,*
    492  *   but each system knows the other is still there.  Messages may be sent	*
    493  *   between systems, though. 															*
    494  * - Destroy_Null_Connection doesn't hang up the modem, so 						*
    495  *   Num_Connections() still reports a value of 1 even though the other 	*
    496  *   player has left.																		*
    497  * - Any waits on Num_Connections() must also check for 							*
    498  *   NumPlayers > 1, to keep from waiting forever if the other 				*
    499  *   guy has left																				*
    500  * - Packets sent to a player who's left require no ACK							*
    501  *                                                                         *
    502  * INPUT:                                                                  *
    503  *		none.																						*
    504  *                                                                         *
    505  * OUTPUT:                                                                 *
    506  *		none.																						*
    507  *                                                                         *
    508  * WARNINGS:                                                               *
    509  *		none.																						*
    510  *                                                                         *
    511  * HISTORY:                                                                *
    512  *   11/21/1995 BRR : Created.                                             *
    513  *=========================================================================*/
    514 static void Queue_AI_Multiplayer(void)
    515 {
    516 	//........................................................................
    517 	// Enums:
    518 	//........................................................................
    519 	enum {
    520 		MIXFILE_RESEND_DELTA = 120,	// ticks b/w resends
    521 		MIXFILE_TIMEOUT = 3600,			// timeout waiting for mixfiles
    522 		FRAMESYNC_DLG_TIME = (3*60),	// time until displaying reconnect dialog
    523 		FRAMESYNC_TIMEOUT = (25*60),	// timeout waiting for frame sync packet
    524 	};
    525 
    526 	int timeout_factor = (GameToPlay == GAME_INTERNET) ? 6 : 1;
    527 
    528 	//........................................................................
    529 	// Variables for sending, receiving & parsing packets:
    530 	//........................................................................
    531 	ConnManClass *net;					// ptr to access all multiplayer functions
    532 	EventClass packet;					// for sending single frame-sync's
    533 	char *multi_packet_buf;				// buffer for sending/receiving
    534 	int multi_packet_max;				// max length of multi_packet_buf
    535 
    536 	//........................................................................
    537 	// Frame-sync'ing variables.  Values in these arrays are stored in the
    538 	// order in which the connections are created.
    539 	// (ie net->Connection_Index(id))
    540 	//........................................................................
    541 	static long
    542 		their_frame[MAX_PLAYERS - 1];	// other players' frame #'s
    543 	static unsigned short
    544 		their_sent[MAX_PLAYERS - 1];	// # cmds other player claims to have sent
    545 	static unsigned short
    546 		their_recv[MAX_PLAYERS - 1];	// # cmds actually received from others
    547 	static unsigned short
    548 		my_sent;								// # cmds I've sent out
    549 
    550 	//........................................................................
    551 	// Other misc variables
    552 	//........................................................................
    553 	int i;
    554 	RetcodeType rc;
    555 	int reconnect_dlg = 0;				// 1 = the reconnect dialog is displayed
    556 
    557 	//------------------------------------------------------------------------
    558 	//	Initialize the packet buffer pointer & its max size
    559 	//------------------------------------------------------------------------
    560 	if (GameToPlay == GAME_MODEM
    561 		|| GameToPlay == GAME_NULL_MODEM){
    562 //PG_TO_FIX
    563 #if (0)
    564 		multi_packet_buf = NullModem.BuildBuf;
    565 		multi_packet_max = NullModem.MaxLen - sizeof (CommHeaderType);
    566 		net = &NullModem;
    567 #endif
    568 	}
    569 	else if (GameToPlay == GAME_IPX || GameToPlay == GAME_INTERNET) {
    570 		multi_packet_buf = MetaPacket;
    571 		multi_packet_max = MetaSize;
    572 		net = &Ipx;
    573 	}
    574 
    575 	//------------------------------------------------------------------------
    576 	//	Debug stuff
    577 	//------------------------------------------------------------------------
    578 	Init_Queue_Mono(net);
    579 	Update_Queue_Mono (net, 0);
    580 
    581 	//------------------------------------------------------------------------
    582 	//	If we've just started a game, or loaded a multiplayer game, we must
    583 	// wait for all other systems to signal ready.
    584 	//------------------------------------------------------------------------
    585 	if (Frame==0) {
    586 		//.....................................................................
    587 		//	Initialize static locals
    588 		//.....................................................................
    589 		for (i = 0; i < MAX_PLAYERS - 1; i++) {
    590 			their_frame[i] = -1;
    591 			their_sent[i] = 0;
    592 			their_recv[i] = 0;
    593 			TheirProcessTime[i] = -1;
    594 		}
    595 		my_sent = 0;
    596 		for (i = 0; i < 32; i++) {
    597 			CRC[i] = 0;
    598 		}
    599 
    600 		//.....................................................................
    601 		// Send our initial FRAMESYNC packet
    602 		//.....................................................................
    603 		Send_FrameSync(net, my_sent);
    604 
    605 		//.....................................................................
    606 		// Wait for the other guys
    607 		//.....................................................................
    608 		rc = Wait_For_Players (1, net, MIXFILE_RESEND_DELTA, FRAMESYNC_DLG_TIME*timeout_factor,
    609 			MIXFILE_TIMEOUT, multi_packet_buf, my_sent, their_frame,
    610 			their_sent, their_recv);
    611 
    612 		if (rc != RC_NORMAL) {
    613 			if (rc == RC_NOT_RESPONDING) {
    614 				CCMessageBox().Process (TXT_SYSTEM_NOT_RESPONDING);
    615 			}
    616 			else if (rc == RC_SCENARIO_MISMATCH) {
    617 				CCMessageBox().Process (TXT_SCENARIOS_DO_NOT_MATCH);
    618 			}
    619 			else if (rc == RC_DOLIST_FULL) {
    620 				CCMessageBox().Process(TXT_QUEUE_FULL);
    621 			}
    622 			Stop_Game();
    623 			return;
    624 		}
    625 
    626 		//.....................................................................
    627 		//	Re-initialize frame numbers (in case somebody signed off while I was
    628 		//	waiting for MIX files to load; we would have fallen through, but
    629 		//	their frame # would still be -1).
    630 		//.....................................................................
    631 		for (i = 0; i < MAX_PLAYERS - 1; i++)
    632 			their_frame[i] = 0;
    633 
    634 		//.....................................................................
    635 		//	Reset the network response time computation, now that we're both
    636 		// sending data again  (loading MIX files will have introduced
    637 		// deceptively large values).
    638 		//.....................................................................
    639 		net->Reset_Response_Time();
    640 
    641 		//.....................................................................
    642 		// Initialize the frame timers
    643 		//.....................................................................
    644 		if (CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) {
    645 			Process_Send_Period(net);
    646 		}
    647 
    648 	} 	// end of Frame 0 wait
    649 
    650 	//------------------------------------------------------------------------
    651 	// Adjust connection timing parameters every 128 frames.
    652 	//------------------------------------------------------------------------
    653 	else if ( (Frame & 0x007f) == 0) {
    654 		//
    655 		// If we're using the new spiffy protocol, do proper timing handling.
    656 		// If we're the net "master", compute our desired frame rate & new
    657 		// 'MaxAhead' value.
    658 		//
    659 		if (CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) {
    660 
    661 			//
    662 			// All systems will transmit their required process time.
    663 			//
    664 			Generate_Process_Time_Event(net);
    665 
    666 			//
    667 			// The game "host" will transmit timing adjustment events.
    668 			//
    669 			if (MPlayerLocalID == MPlayerID[0]) {
    670 				Generate_Real_Timing_Event(net, my_sent);
    671 			}
    672 		} else {
    673 			//
    674 			// For the older protocols, do the old broken timing handling.
    675 			//
    676 			Generate_Timing_Event(net, my_sent);
    677 		}
    678 	}
    679 
    680 	//------------------------------------------------------------------------
    681 	//	Compute the Game's CRC
    682 	//------------------------------------------------------------------------
    683 	Compute_Game_CRC();
    684 	CRC[Frame & 0x001f] = GameCRC;
    685 	//unsigned long save_crc = GameCRC;
    686 	//Print_CRCs((EventClass *)NULL);
    687 	//GameCRC = save_crc;
    688 
    689 	//------------------------------------------------------------------------
    690 	// Only process every 'FrameSendRate' frames
    691 	//------------------------------------------------------------------------
    692 	if (CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) {
    693 		if (!Process_Send_Period(net)) {
    694 			if (IsMono) {
    695 				MonoClass::Disable();
    696 			}
    697 			return;
    698 		}
    699 	}
    700 
    701 	//------------------------------------------------------------------------
    702 	// Send our data packet(s); update my command-sent counter
    703 	//------------------------------------------------------------------------
    704 	my_sent += Send_Packets(net, multi_packet_buf, multi_packet_max,
    705 		MPlayerMaxAhead, my_sent);
    706 
    707 	//------------------------------------------------------------------------
    708 	//	If this is our first time through, we're done.
    709 	//------------------------------------------------------------------------
    710 	if (Frame==0) {
    711 		if (IsMono) {
    712 			MonoClass::Disable();
    713 		}
    714 		return;
    715 	}
    716 
    717 	//------------------------------------------------------------------------
    718 	//	Frame-sync'ing: wait until it's OK to advance to the next frame.
    719 	//------------------------------------------------------------------------
    720 	rc = Wait_For_Players (0, net,
    721 		(MPlayerMaxAhead << 3),
    722 		MAX ( net->Response_Time() * 3, (unsigned long)FRAMESYNC_DLG_TIME*timeout_factor ),
    723 		FRAMESYNC_TIMEOUT * (timeout_factor*2),
    724 		multi_packet_buf, my_sent, their_frame,
    725 		their_sent, their_recv);
    726 
    727 	if (rc != RC_NORMAL) {
    728 		if (rc == RC_NOT_RESPONDING) {
    729 			CCMessageBox().Process (TXT_SYSTEM_NOT_RESPONDING);
    730 		}
    731 		else if (rc == RC_SCENARIO_MISMATCH) {
    732 			CCMessageBox().Process (TXT_SCENARIOS_DO_NOT_MATCH);
    733 		}
    734 		else if (rc == RC_DOLIST_FULL) {
    735 			CCMessageBox().Process(TXT_QUEUE_FULL);
    736 		}
    737 		Stop_Game();
    738 		return;
    739 	}
    740 
    741 	//------------------------------------------------------------------------
    742 	//	Save the DoList to disk, if we're in "Record" mode
    743 	//------------------------------------------------------------------------
    744 	if (RecordGame) {
    745 		Queue_Record();
    746 	}
    747 
    748 	//------------------------------------------------------------------------
    749 	// Execute the DoList; if an error occurs, bail out.
    750 	//------------------------------------------------------------------------
    751 	if (!Execute_DoList(MPlayerMax, HOUSE_MULTI1, net, NULL,
    752 		their_frame, their_sent, their_recv)) {
    753 		Stop_Game();
    754 		return;
    755 	}
    756 
    757 	//------------------------------------------------------------------------
    758 	//	Clean out the DoList
    759 	//------------------------------------------------------------------------
    760 	Clean_DoList(net);
    761 
    762 	if (IsMono) {
    763 		MonoClass::Disable();
    764 	}
    765 
    766 }	// end of Queue_AI_Multiplayer
    767 
    768 
    769 /***************************************************************************
    770  * Wait_For_Players -- Waits for other systems to come on-line             *
    771  *                                                                         *
    772  * This routine performs the most critical logic in multiplayer; that of	*
    773  * synchronizing my frame number with those of the other systems.				*
    774  *                                                                         *
    775  * INPUT:                                                                  *
    776  *		first_time				1 = 1st time this routine is called					*
    777  *		net						ptr to connection manager								*
    778  *		resend_delta			time (ticks) between FRAMESYNC resends				*
    779  *		dialog_time				time (ticks) until pop up a reconnect dialog		*
    780  *		timeout					time (ticks) until we give up the ghost			*
    781  *		multi_packet_buf		buffer to store packets in								*
    782  *		my_sent					# commands I've sent so far							*
    783  *		their_frame				array of their frame #'s								*
    784  *		their_sent				array of their CommandCount values					*
    785  *		their_recv				array of # cmds I've received from them			*
    786  *                                                                         *
    787  * OUTPUT:                                                                 *
    788  *		RC_NORMAL				OK to advance to the next frame						*
    789  *		RC_CANCEL				user hit 'Cancel' at the timeout countdown dlg	*
    790  *		RC_NOT_RESPONDING		other player(s) not responding						*
    791  *		RC_SCENARIO_MISMATCH	scenario's don't match (first_time only)			*
    792  *		RC_DOLIST_FULL			DoList was full											*
    793  *                                                                         *
    794  * WARNINGS:                                                               *
    795  *		none.																						*
    796  *                                                                         *
    797  * HISTORY:                                                                *
    798  *   11/21/1995 BRR : Created.                                             *
    799  *=========================================================================*/
    800 static RetcodeType Wait_For_Players(int first_time, ConnManClass *net,
    801 	int resend_delta, int dialog_time, int timeout, char *multi_packet_buf,
    802 	int my_sent, long *their_frame, unsigned short *their_sent,
    803 	unsigned short *their_recv)
    804 {
    805 	//........................................................................
    806 	// Variables for sending, receiving & parsing packets:
    807 	//........................................................................
    808 	EventClass *event;					// event ptr for parsing incoming packets
    809 	int packetlen;							// size of meta-packet sent, & received
    810 	int id;									// id of other player
    811 	int messages_this_loop;				// to limit # messages processed each loop
    812 
    813 	//........................................................................
    814 	// Variables used only if 'first_time':
    815 	//........................................................................
    816 	int num_ready;							// # players signalling ready
    817 
    818 	//........................................................................
    819 	// Timing variables
    820 	//........................................................................
    821 	CountDownTimerClass retry_timer;		// time between FRAMESYNC packet resends
    822 	CountDownTimerClass dialog_timer;	// time to pop up a dialog
    823 	CountDownTimerClass timeout_timer;	// general-purpose timeout
    824 
    825 	//........................................................................
    826 	// Dialog variables
    827 	//........................................................................
    828 	int reconnect_dlg = 0;				// 1 = the reconnect dialog is displayed
    829 
    830 	//........................................................................
    831 	// Other misc variables
    832 	//........................................................................
    833 	KeyNumType input;						// for user input
    834 	int x,y;									// for map input
    835 	RetcodeType rc;
    836 
    837 	//------------------------------------------------------------------------
    838 	// Wait to hear from all other players
    839 	//------------------------------------------------------------------------
    840 	num_ready = 0;
    841 	retry_timer.Set (resend_delta, true);	// time to retry
    842 	dialog_timer.Set (dialog_time, true);	// time to show dlg
    843 	timeout_timer.Set (timeout, true);		// time to bail out
    844 
    845 	while (1) {
    846 
    847 		Update_Queue_Mono (net, 2);
    848 
    849 		//---------------------------------------------------------------------
    850 		//	Resend a frame-sync packet if longer than one propogation delay goes
    851 		// by; this prevents a "deadlock".  If he's waiting for me to advance,
    852 		// but has missed my last few FRAMEINFO packets, I may be waiting for
    853 		// him to advance.  Resending a FRAMESYNC ensures he knows what frame
    854 		// number I'm on.
    855 		//---------------------------------------------------------------------
    856 		if (!retry_timer.Time()) {
    857 			retry_timer.Set (resend_delta, true);		// time to retry
    858 			Update_Queue_Mono (net, 3);
    859 			Send_FrameSync(net, my_sent);
    860 		}
    861 
    862 		//---------------------------------------------------------------------
    863 		//	Service the connections
    864 		//---------------------------------------------------------------------
    865 		net->Service();
    866 
    867 		//---------------------------------------------------------------------
    868 		//	Pop up a reconnect dialog if enough time goes by
    869 		//---------------------------------------------------------------------
    870 		if (!dialog_timer.Time() && SpecialDialog==SDLG_NONE) {
    871 			if (Process_Reconnect_Dialog(&timeout_timer, their_frame,
    872 				net->Num_Connections(), (first_time==0), (reconnect_dlg==0))) {
    873 				return (RC_CANCEL);
    874 			}
    875 			reconnect_dlg = 1;
    876 		}
    877 
    878 		//---------------------------------------------------------------------
    879 		//	Exit if too much time goes by (the other system has crashed or
    880 		// bailed)
    881 		//---------------------------------------------------------------------
    882 		if (!timeout_timer.Time()) {
    883 			//..................................................................
    884 			// For the first-time run, just give up; something's wrong.
    885 			//..................................................................
    886 			if (first_time) {
    887 				return (RC_NOT_RESPONDING);
    888 			}
    889 			//..................................................................
    890 			// Otherwise, we're in the middle of a game; so, the modem &
    891 			// network must deal with a timeout differently.
    892 			//..................................................................
    893 			else {
    894 				Update_Queue_Mono (net, 4);
    895 
    896 				if (Handle_Timeout(net, their_frame, their_sent, their_recv)) {
    897 					Map.Flag_To_Redraw(true);	// erase modem reconnect dialog
    898 					Map.Render();
    899 					retry_timer.Set (resend_delta, true);
    900 					dialog_timer.Set (dialog_time, true);
    901 					timeout_timer.Set (timeout, true);
    902 				}
    903 				else {
    904 					return (RC_NOT_RESPONDING);
    905 				}
    906 			}
    907 		}
    908 
    909 		//---------------------------------------------------------------------
    910 		//	Check for an incoming message.  We must still process commands
    911 		// even if 'first_time' is set, in case the other system got my 1st
    912 		// FRAMESYNC, but I didn't get his; he'll be at the next frame, and
    913 		// may be sending commands.
    914 		// We have to limit the number of incoming messages we handle; it's
    915 		// possible to go into an infinite loop processing modem messages.
    916 		//---------------------------------------------------------------------
    917 		messages_this_loop = 0;
    918 		while ( (messages_this_loop++ < 5) &&
    919 			net->Get_Private_Message (multi_packet_buf, &packetlen, &id) ) {
    920 			Update_Queue_Mono (net, 5);
    921 
    922 			/*..................................................................
    923 			Get an event ptr to the incoming message
    924 			..................................................................*/
    925 			event = (EventClass *)multi_packet_buf;
    926 
    927 			//------------------------------------------------------------------
    928 			// Special processing for a modem game: process SERIAL packets
    929 			//------------------------------------------------------------------
    930 			if (GameToPlay == GAME_MODEM
    931 				|| GameToPlay == GAME_NULL_MODEM){
    932 				//|| GameToPlay == GAME_INTERNET) {
    933 				rc = Process_Serial_Packet(multi_packet_buf, first_time);
    934 				//...............................................................
    935 				// SERIAL packet received & processed
    936 				//...............................................................
    937 				if (rc == RC_SERIAL_PROCESSED) {
    938 					net->Service();
    939 					retry_timer.Set (resend_delta, true);
    940 					dialog_timer.Set (dialog_time, true);
    941 					timeout_timer.Set (timeout, true);
    942 					continue;
    943 				}
    944 				//...............................................................
    945 				// other player has left the game
    946 				//...............................................................
    947 				else if (rc == RC_PLAYER_LEFT) {
    948 					if (first_time) {
    949 						num_ready++;
    950 					}
    951 					break;
    952 				}
    953 				//...............................................................
    954 				// Connection was lost
    955 				//...............................................................
    956 				else if (rc == RC_HUNG_UP) {
    957 					return (RC_NOT_RESPONDING);
    958 				}
    959 				//...............................................................
    960 				// If it was any other type of serial packet, break
    961 				//...............................................................
    962 				else if (rc != RC_NORMAL) {
    963 					break;
    964 				}
    965 			}
    966 
    967 			//------------------------------------------------------------------
    968 			//	Process the incoming packet
    969 			//------------------------------------------------------------------
    970 			rc = Process_Receive_Packet(net, multi_packet_buf, id, packetlen,
    971 				their_frame, their_sent, their_recv);
    972 			//..................................................................
    973 			// New player heard from
    974 			//..................................................................
    975 			if (rc == RC_PLAYER_READY) {
    976 				num_ready++;
    977 			}
    978 			//..................................................................
    979 			// Scenario's don't match
    980 			//..................................................................
    981 			else if (rc == RC_SCENARIO_MISMATCH) {
    982 				return (RC_SCENARIO_MISMATCH);
    983 			}
    984 			//..................................................................
    985 			// DoList was full
    986 			//..................................................................
    987 			else if (rc == RC_DOLIST_FULL) {
    988 				return (RC_DOLIST_FULL);
    989 			}
    990 
    991 			//..................................................................
    992 			//	Service the connection, to clean out the receive queues
    993 			//..................................................................
    994 			net->Service();
    995 		}
    996 
    997 		//---------------------------------------------------------------------
    998 		// Debug output
    999 		//---------------------------------------------------------------------
   1000 		Print_Framesync_Values(Frame, MPlayerMaxAhead, net->Num_Connections(),
   1001 			their_recv, their_sent, my_sent);
   1002 
   1003 		//---------------------------------------------------------------------
   1004 		//	Attempt to advance to the next frame.
   1005 		//---------------------------------------------------------------------
   1006 		//.....................................................................
   1007 		// For the first-time run, just check to see if we've heard from
   1008 		// everyone.
   1009 		//.....................................................................
   1010 		if (first_time) {
   1011 			if (num_ready >= net->Num_Connections()) 	break;
   1012 		}
   1013 		//.....................................................................
   1014 		// For in-game processing, we have to check their_sent, their_recv,
   1015 		// their_frame, etc.
   1016 		//.....................................................................
   1017 		else {
   1018 			if (Can_Advance(net, MPlayerMaxAhead, their_frame, their_sent,
   1019 				their_recv)) {
   1020 				break;
   1021 			}
   1022 		}
   1023 
   1024 		//---------------------------------------------------------------------
   1025 		//	Service game stuff.  Servicing the map's input, and rendering the
   1026 		//	map, allows the map to scroll even though we're hung up waiting for
   1027 		//	packets.  Don't do this if 'first_time' is set, since users could be
   1028 		// waiting a very long time for all systems to load the scenario, and
   1029 		// it gets frustrating being able to scroll around without doing
   1030 		// anything.
   1031 		//---------------------------------------------------------------------
   1032 		Call_Back();
   1033 		if (!first_time && SpecialDialog == SDLG_NONE && reconnect_dlg==0) {
   1034 			WWMouse->Erase_Mouse(&HidPage, TRUE);
   1035 			Map.Input(input, x, y);
   1036 			if (input)
   1037 				Keyboard_Process(input);
   1038 			Map.Render();
   1039 		}
   1040 
   1041 	}	/* end of while */
   1042 
   1043 	//------------------------------------------------------------------------
   1044 	//	If the reconnect dialog was shown, force the map to redraw.
   1045 	//------------------------------------------------------------------------
   1046 	if (reconnect_dlg) {
   1047 		Map.Flag_To_Redraw(true);
   1048 		Map.Render();
   1049 	}
   1050 
   1051 	return (RC_NORMAL);
   1052 
   1053 }	// end of Wait_For_Players
   1054 
   1055 
   1056 /***************************************************************************
   1057  * Generate_Timing_Event -- computes & queues a RESPONSE_TIME event        *
   1058  *                                                                         *
   1059  * This routine adjusts the connection timing on the local system; it also	*
   1060  * optionally generates a RESPONSE_TIME event, to tell all systems to		*
   1061  * dynamically adjust the current MaxAhead value.  This allows both the		*
   1062  * MaxAhead & the connection retry logic to have dynamic timing, to adjust	*
   1063  * to varying line conditions.															*
   1064  *                                                                         *
   1065  * INPUT:                                                                  *
   1066  *		net			ptr to connection manager											*
   1067  *		my_sent		# commands I've sent out so far									*
   1068  *                                                                         *
   1069  * OUTPUT:                                                                 *
   1070  *		none.																						*
   1071  *                                                                         *
   1072  * WARNINGS:                                                               *
   1073  *		none.																						*
   1074  *                                                                         *
   1075  * HISTORY:                                                                *
   1076  *   11/21/1995 BRR : Created.                                             *
   1077  *=========================================================================*/
   1078 static void Generate_Timing_Event(ConnManClass *net, int my_sent)
   1079 {
   1080 	unsigned long resp_time;			// connection response time, in ticks
   1081 	EventClass ev;
   1082 
   1083 	//------------------------------------------------------------------------
   1084 	// Measure the current connection response time.  This time will be in
   1085 	// 60ths of a second, and represents full round-trip time of a packet.
   1086 	// To convert to one-way packet time, divide by 2; to convert to game
   1087 	// frames, divide again by 4, assuming a game rate of 15 fps.
   1088 	//------------------------------------------------------------------------
   1089 	resp_time = net->Response_Time();
   1090 
   1091 	//------------------------------------------------------------------------
   1092 	//	Adjust my connection retry timing; only do this if I've sent out more
   1093 	//	than 5 commands, so I know I have a measure of the response time.
   1094 	//------------------------------------------------------------------------
   1095 	if (my_sent > 5) {
   1096 
   1097 		net->Set_Timing (resp_time + 10, -1, (resp_time * 4)+15);
   1098 
   1099 		//.....................................................................
   1100 		// If I'm the network "master", I'm also responsible for updating the
   1101 		// MaxAhead value on all systems, so do that here too.
   1102 		//.....................................................................
   1103 		if (MPlayerLocalID == MPlayerID[0]) {
   1104 			ev.Type = EventClass::RESPONSE_TIME;
   1105 			//..................................................................
   1106 			// For multi-frame compressed events, the MaxAhead must be an even
   1107 			// multiple of the FrameSendRate.
   1108 			//..................................................................
   1109 			if (CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) {
   1110 				ev.Data.FrameInfo.Delay = MAX( ((((resp_time / 8) +
   1111 					(FrameSendRate - 1)) / FrameSendRate) *
   1112 					FrameSendRate), (FrameSendRate * 2) );
   1113 char flip[128];
   1114 sprintf (flip, "C&C95 - Generating timing packet - MaxAhead = %d frames\n", ev.Data.FrameInfo.Delay);
   1115 CCDebugString (flip);
   1116 
   1117 			}
   1118 			//..................................................................
   1119 			// For sending packets every frame, just use the 1-way connection
   1120 			// response time.
   1121 			//..................................................................
   1122 			else {
   1123 				if (GameToPlay == GAME_MODEM
   1124 					|| GameToPlay == GAME_NULL_MODEM){
   1125 					//|| GameToPlay == GAME_INTERNET) {
   1126 					ev.Data.FrameInfo.Delay = MAX( (resp_time / 8),
   1127 						 (unsigned long)MODEM_MIN_MAX_AHEAD );
   1128 				}
   1129 				else if (GameToPlay == GAME_IPX || GameToPlay == GAME_INTERNET) {
   1130 					ev.Data.FrameInfo.Delay = MAX( (resp_time / 8),
   1131 						 (unsigned long)NETWORK_MIN_MAX_AHEAD );
   1132 				}
   1133 			}
   1134 			OutList.Add(ev);
   1135 		}
   1136 	}
   1137 
   1138 }	// end of Generate_Timing_Event
   1139 
   1140 
   1141 /***************************************************************************
   1142  * Generate_Real_Timing_Event -- Generates a TIMING event                  *
   1143  *                                                                         *
   1144  * INPUT:                                                                  *
   1145  *		net			ptr to connection manager											*
   1146  *		my_sent		# commands I've sent out so far									*
   1147  *                                                                         *
   1148  * OUTPUT:                                                                 *
   1149  *		none.																						*
   1150  *                                                                         *
   1151  * WARNINGS:                                                               *
   1152  *		none.																						*
   1153  *                                                                         *
   1154  * HISTORY:                                                                *
   1155  *   07/02/1996 BRR : Created.                                             *
   1156  *=========================================================================*/
   1157 static void Generate_Real_Timing_Event(ConnManClass *net, int my_sent)
   1158 {
   1159 	unsigned long resp_time;			// connection response time, in ticks
   1160 	EventClass ev;
   1161 	int highest_ticks;
   1162 	int i;
   1163 	int specified_frame_rate;
   1164 	int maxahead;
   1165 
   1166 	//
   1167 	// If we haven't sent out at least 5 guaranteed-delivery packets, don't
   1168 	// bother trying to measure our connection response time; just return.
   1169 	//
   1170 	if (my_sent < 5) {
   1171 		return;
   1172 	}
   1173 
   1174 	//
   1175 	// Find the highest processing time we have stored
   1176 	//
   1177 	highest_ticks = 0;
   1178 	for (i = 0; i < MPlayerCount; i++) {
   1179 		//
   1180 		// If we haven't heard from all systems yet, bail out.
   1181 		//
   1182 		if (TheirProcessTime[i] == -1) {
   1183 			return;
   1184 		}
   1185 		if (TheirProcessTime[i] > highest_ticks) {
   1186 			highest_ticks = TheirProcessTime[i];
   1187 		}
   1188 	}
   1189 
   1190 	//
   1191 	// Compute our "desired" frame rate as the lower of:
   1192 	// - What the user has dialed into the options screen
   1193 	// - What we're really able to run at
   1194 	//
   1195 	if (highest_ticks == 0) {
   1196 		DesiredFrameRate = 60;
   1197 	} else {
   1198 		DesiredFrameRate = 60 / highest_ticks;
   1199 	}
   1200 
   1201 	if (Options.GameSpeed == 0) {
   1202 		specified_frame_rate = 60;
   1203 	} else {
   1204 		specified_frame_rate = 60 / Options.GameSpeed;
   1205 	}
   1206 
   1207 	DesiredFrameRate = MIN (DesiredFrameRate, specified_frame_rate);
   1208 
   1209 	//
   1210 	// Measure the current connection response time.  This time will be in
   1211 	// 60ths of a second, and represents full round-trip time of a packet.
   1212 	// To convert to one-way packet time, divide by 2; to convert to game
   1213 	// frames, ....uh....
   1214 	//
   1215 	resp_time = net->Response_Time();
   1216 
   1217 	//
   1218 	// Compute our new 'MaxAhead' value, based upon the response time of our
   1219 	// connection and our desired frame rate.
   1220 	// 'MaxAhead' in frames is:
   1221 	//
   1222 	// (resp_time / 2 ticks) * (1 sec/60 ticks) * (n Frames / sec)
   1223 	//
   1224 	// resp_time is divided by 2 because, as reported, it represents a round-
   1225 	// trip, and we only want to use a one-way trip.
   1226 	//
   1227 	maxahead = (resp_time * DesiredFrameRate) / (2 * 60);
   1228 
   1229 	//
   1230 	// Now, we have to round 'maxahead' so it's an even multiple of our
   1231 	// send rate.  It also must be at least thrice the FrameSendRate.
   1232 	// (Isn't "thrice" a cool word?)
   1233 	//
   1234 	maxahead = ((maxahead + FrameSendRate - 1) / FrameSendRate) * FrameSendRate;
   1235 	maxahead = MAX (maxahead, (int)FrameSendRate * 3);
   1236 
   1237 	ev.Type = EventClass::TIMING;
   1238 	ev.Data.Timing.DesiredFrameRate = DesiredFrameRate;
   1239 	ev.Data.Timing.MaxAhead = maxahead;
   1240 
   1241 	OutList.Add(ev);
   1242 
   1243 	//
   1244 	//	Adjust my connection retry timing.  These values set the retry timeout
   1245 	// to just over one round-trip time, the 'maxretries' to -1, and the
   1246 	// connection timeout to allow for about 4 retries.
   1247 	//
   1248 	net->Set_Timing (resp_time + 10, -1, (resp_time * 4)+15);
   1249 
   1250 }
   1251 
   1252 
   1253 /***************************************************************************
   1254  * Generate_Process_Time_Event -- Generates a PROCESS_TIME event           *
   1255  *                                                                         *
   1256  * INPUT:                                                                  *
   1257  *		net			ptr to connection manager											*
   1258  *                                                                         *
   1259  * OUTPUT:                                                                 *
   1260  *		none.																						*
   1261  *                                                                         *
   1262  * WARNINGS:                                                               *
   1263  *		none.																						*
   1264  *                                                                         *
   1265  * HISTORY:                                                                *
   1266  *   07/02/1996 BRR : Created.                                             *
   1267  *=========================================================================*/
   1268 static void Generate_Process_Time_Event(ConnManClass *net)
   1269 {
   1270 	EventClass ev;
   1271 	int avgticks;
   1272 	unsigned long resp_time;			// connection response time, in ticks
   1273 
   1274 	//
   1275 	// Measure the current connection response time.  This time will be in
   1276 	// 60ths of a second, and represents full round-trip time of a packet.
   1277 	// To convert to one-way packet time, divide by 2; to convert to game
   1278 	// frames, ....uh....
   1279 	//
   1280 	resp_time = net->Response_Time();
   1281 
   1282 	//
   1283 	//	Adjust my connection retry timing.  These values set the retry timeout
   1284 	// to just over one round-trip time, the 'maxretries' to -1, and the
   1285 	// connection timeout to allow for about 4 retries.
   1286 	//
   1287 	net->Set_Timing (resp_time + 10, -1, (resp_time * 4)+15);
   1288 
   1289 	if (IsMono) {
   1290 		MonoClass::Enable();
   1291 		Mono_Set_Cursor(0,23);
   1292 		Mono_Printf("Processing Ticks:%03d Frames:%03d\n", ProcessTicks,ProcessFrames);
   1293 		MonoClass::Disable();
   1294 	}
   1295 
   1296 	avgticks = ProcessTicks / ProcessFrames;
   1297 
   1298 	ev.Type = EventClass::PROCESS_TIME;
   1299 	ev.Data.ProcessTime.AverageTicks = avgticks;
   1300 char flip[128];
   1301 sprintf (flip, "C&C95 - Sending PROCESS_TIME packet of %04x ticks\n", ev.Data.ProcessTime.AverageTicks);
   1302 CCDebugString (flip);
   1303 
   1304 	OutList.Add(ev);
   1305 
   1306 	ProcessTicks = 0;
   1307 	ProcessFrames = 0;
   1308 }
   1309 
   1310 
   1311 /***************************************************************************
   1312  * Process_Send_Period -- timing for sending packets every 'n' frames      *
   1313  *                                                                         *
   1314  * This function is for a CommProtocol of COMM_PROTOCOL_MULTI_E_COMP only.	*
   1315  * It determines if it's time to send a packet or not.							*
   1316  *																									*
   1317  * INPUT:                                                                  *
   1318  *		net		ptr to connection manager												*
   1319  *                                                                         *
   1320  * OUTPUT:                                                                 *
   1321  *		1 = it's time to send a packet; 0 = don't send a packet this frame.	*
   1322  *                                                                         *
   1323  * WARNINGS:                                                               *
   1324  *                                                                         *
   1325  * HISTORY:                                                                *
   1326  *   11/21/1995 BRR : Created.                                             *
   1327  *=========================================================================*/
   1328 static int Process_Send_Period(ConnManClass *net)
   1329 {
   1330 	//------------------------------------------------------------------------
   1331 	// If the current frame # is not an even multiple of 'FrameSendRate', then
   1332 	// it's not time to send a packet; just return.
   1333 	//------------------------------------------------------------------------
   1334 	if (Frame != (((Frame + (FrameSendRate - 1)) /
   1335 		FrameSendRate) * FrameSendRate) ) {
   1336 
   1337 		net->Service();
   1338 
   1339 		if (IsMono) {
   1340 			MonoClass::Disable();
   1341 		}
   1342 
   1343 		return (0);
   1344 	}
   1345 
   1346 	return (1);
   1347 
   1348 }	// end of Process_Send_Period
   1349 
   1350 
   1351 /***************************************************************************
   1352  * Send_Packets -- sends out events from the OutList                       *
   1353  *                                                                         *
   1354  * This routine computes how many events can be sent this frame, and then	*
   1355  * builds the "meta-packet" & sends it.												*
   1356  *                                                                         *
   1357  * The 'cap' value is the max # of events we can send.  Ideally, it should	*
   1358  * be based upon the bandwidth of our connection.  Currently, it's just		*
   1359  * hardcoded to prevent the modem from having to resend "too much" data,	*
   1360  * which is about 200 bytes per frame.													*
   1361  *                                                                         *
   1362  * INPUT:                                                                  *
   1363  *		net						ptr to connection manager								*
   1364  *		multi_packet_buf		buffer to store packets in								*
   1365  *		multi_packet_max		max size of multi_packet_buf							*
   1366  *		max_ahead				current game MaxAhead value							*
   1367  *		my_sent					# commands I've sent this game						*
   1368  *                                                                         *
   1369  * OUTPUT:                                                                 *
   1370  *		# events sent, NOT including the FRAMEINFO event							*
   1371  *                                                                         *
   1372  * WARNINGS:                                                               *
   1373  *                                                                         *
   1374  * HISTORY:                                                                *
   1375  *   11/21/1995 BRR : Created.                                             *
   1376  *=========================================================================*/
   1377 static int Send_Packets(ConnManClass *net, char *multi_packet_buf,
   1378 	int multi_packet_max, int max_ahead, int my_sent)
   1379 {
   1380 	int cap;				// max # events to send, NOT including FRAMEINFO event
   1381 	int do_once;		// true: only go through packet loop once
   1382 	int ack_req;		// 0 = no ack required on outgoing packet
   1383 	int packetlen;		// size of meta-packet sent
   1384 
   1385 	//------------------------------------------------------------------------
   1386 	//	Determine how many events it's OK to send this frame.
   1387 	//------------------------------------------------------------------------
   1388 	//........................................................................
   1389 	// If we have 4 or more packets queue'd for sending, don't add any more
   1390 	// this frame.
   1391 	//........................................................................
   1392 	if (net->Private_Num_Send() >= 4) {
   1393 		cap = 0;
   1394 		do_once = 1;
   1395 	}
   1396 	//........................................................................
   1397 	// If there are 2 or more packets queued, the entire packet we send must
   1398 	// fit within a single ComQueue buffer, so limit # events to 5.
   1399 	// (The Modem connection manager has a max buffer size of 200 bytes, which
   1400 	// is large enough for 6 uncompressed events, which leaves room for 5
   1401 	// events plus a FRAMEINFO.)
   1402 	//........................................................................
   1403 	else if (net->Private_Num_Send() >= 2) {
   1404 		cap = 5;
   1405 		do_once = 1;
   1406 
   1407 	}
   1408 	//........................................................................
   1409 	// Otherwise, just send all events in the OutList
   1410 	//........................................................................
   1411 	else {
   1412 		cap = OutList.Count;
   1413 		do_once = 0;
   1414 	}
   1415 	//........................................................................
   1416 	// Make sure we aren't sending more events than are in the OutList
   1417 	//........................................................................
   1418 	if (cap > OutList.Count) {
   1419 		cap = OutList.Count;
   1420 	}
   1421 
   1422 	//........................................................................
   1423 	// Make sure we don't send so many events that our DoList fills up
   1424 	//........................................................................
   1425 	if (cap > (MAX_EVENTS * 8) - DoList.Count) {
   1426 		cap = (MAX_EVENTS * 8) - DoList.Count;
   1427 	}
   1428 
   1429 	/*
   1430 	** No cap for internet game
   1431 	**
   1432 	** Or for serial games for that matter ST - 5/31/96 4:00PM
   1433 	*/
   1434 	if (GameToPlay == GAME_INTERNET || GameToPlay == GAME_MODEM || GameToPlay == GAME_NULL_MODEM){
   1435 		cap = OutList.Count;
   1436 		do_once = 0;
   1437 	}
   1438 
   1439 	//------------------------------------------------------------------------
   1440 	//	Build our meta-packet & transmit it.
   1441 	//------------------------------------------------------------------------
   1442 	while (1) {
   1443 
   1444 		Update_Queue_Mono (net, 1);
   1445 
   1446 		//.....................................................................
   1447 		//	If there are no commands this frame, we'll just be sending a FRAMEINFO
   1448 		//	packet; no ack is required.  For the modem's sake, check
   1449 		// MPlayerCount; no ACK is needed if we're just sending to someone
   1450 		// who's left the game.
   1451 		//.....................................................................
   1452 		if (cap == 0 || OutList.Count == 0 || MPlayerCount == 1) {
   1453 			ack_req = 0;
   1454 		}
   1455 		else {
   1456 			ack_req = 1;
   1457 		}
   1458 
   1459 		//.....................................................................
   1460 		//	Build & send out our message
   1461 		//.....................................................................
   1462 		packetlen = Build_Send_Packet (multi_packet_buf, multi_packet_max,
   1463 			max_ahead, my_sent, cap);
   1464 		net->Send_Private_Message (multi_packet_buf, packetlen, ack_req);
   1465 
   1466 		//.....................................................................
   1467 		//	Call Service() to actually send the packet
   1468 		//.....................................................................
   1469 		net->Service();
   1470 
   1471 		//.....................................................................
   1472 		//	Stop if there's no more data to send, or if our send queue is
   1473 		// filling up.
   1474 		//.....................................................................
   1475 		if (OutList.Count == 0 || do_once) {
   1476 			break;
   1477 		}
   1478 	}
   1479 
   1480 	return (cap);
   1481 
   1482 }	// end of Send_Packets
   1483 
   1484 
   1485 /***************************************************************************
   1486  * Send_FrameSync -- Sends a FRAMESYNC packet                              *
   1487  *                                                                         *
   1488  * This routine is used to periodically remind the other systems that 		*
   1489  * we're still here, and to tell them what frame # we're on, in case			*
   1490  * they've missed my FRAMEINFO packets.												*
   1491  *                                                                         *
   1492  * INPUT:                                                                  *
   1493  *		net				ptr to connection manager										*
   1494  *		cmd_count		# commands I've sent so far									*
   1495  *                                                                         *
   1496  * OUTPUT:                                                                 *
   1497  *		none.																						*
   1498  *                                                                         *
   1499  * WARNINGS:                                                               *
   1500  *		none.																						*
   1501  *                                                                         *
   1502  * HISTORY:                                                                *
   1503  *   11/21/1995 BRR : Created.                                             *
   1504  *=========================================================================*/
   1505 static void Send_FrameSync(ConnManClass *net, int cmd_count)
   1506 {
   1507 	EventClass packet;
   1508 
   1509 	//------------------------------------------------------------------------
   1510 	//	Build a frame-sync event to send.  FRAMESYNC packets contain a
   1511 	// scenario-based CRC rather than a game-state-based CRC, to let the
   1512 	// games compare scenario CRC's on startup.
   1513 	//------------------------------------------------------------------------
   1514 	memset (&packet, 0, sizeof(EventClass));
   1515 	packet.Type = EventClass::FRAMESYNC;
   1516 	if (CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) {
   1517 		packet.Frame = ((Frame + MPlayerMaxAhead + (FrameSendRate - 1)) /
   1518 			 FrameSendRate) * FrameSendRate;
   1519 	}
   1520 	else {
   1521 		packet.Frame = Frame + MPlayerMaxAhead;
   1522 	}
   1523 	packet.ID = Houses.ID(PlayerPtr);
   1524 	packet.MPlayerID = MPlayerLocalID;
   1525 	packet.Data.FrameInfo.CRC = ScenarioCRC;
   1526 	packet.Data.FrameInfo.CommandCount = cmd_count;
   1527 	packet.Data.FrameInfo.Delay = MPlayerMaxAhead;
   1528 
   1529 	//------------------------------------------------------------------------
   1530 	// Send the event.  For modem, this just sends to the other player;
   1531 	// for network, it sends to everyone we're connected to.
   1532 	//------------------------------------------------------------------------
   1533 	net->Send_Private_Message (&packet, (offsetof(EventClass, Data) +
   1534 		size_of(EventClass, Data.FrameInfo)), 0 );
   1535 
   1536 	return;
   1537 
   1538 }	// end of Send_FrameSync
   1539 
   1540 
   1541 /***************************************************************************
   1542  * Process_Receive_Packet -- processes an incoming packet                  *
   1543  *                                                                         *
   1544  * This routine receives a packet from another system, adds it to our		*
   1545  * execution queue (the DoList), and updates my arrays of their frame #,	*
   1546  * their commands-sent, and their commands-received.								*
   1547  *                                                                         *
   1548  * INPUT:                                                                  *
   1549  *		net					ptr to connection manager									*
   1550  *		multi_packet_buf	buffer containing packet(s) to parse					*
   1551  *		id						id of sender													*
   1552  *		their_frame			array containing frame #'s of other players			*
   1553  *		their_sent			array containing command count of other players		*
   1554  *		their_recv			array containing # recv'd cmds from other players	*
   1555  *                                                                         *
   1556  * OUTPUT:                                                                 *
   1557  *		RC_NORMAL:					nothing unusual happened, although 				*
   1558  * 									their_sent or their_recv may have been 		*
   1559  *										altered													*
   1560  *		RC_PLAYER_READY:			player has been heard from for the 1st time; *
   1561  * 									this presumes that his original 					*
   1562  * 									'their_frame[]' value was -1 when this 		*
   1563  * 									routine was called									*
   1564  *		RC_SCENARIO_MISMATCH:	FRAMEINFO scenario CRC doesn't match; 			*
   1565  * 									normally only applies after loading a new 	*
   1566  * 									scenario or save-game								*
   1567  *		RC_DOLIST_FULL:			fatal error; unable to add events to DoList	*
   1568  *                                                                         *
   1569  * WARNINGS:                                                               *
   1570  *		none.																						*
   1571  *                                                                         *
   1572  * HISTORY:                                                                *
   1573  *   11/21/1995 BRR : Created.                                             *
   1574  *=========================================================================*/
   1575 static RetcodeType Process_Receive_Packet(ConnManClass *net,
   1576 	char *multi_packet_buf, int id, int packetlen, long *their_frame,
   1577 	unsigned short *their_sent, unsigned short *their_recv)
   1578 {
   1579 	EventClass *event;
   1580 	int index;
   1581 	RetcodeType retcode = RC_NORMAL;
   1582 	int i;
   1583 
   1584 	//------------------------------------------------------------------------
   1585 	//	Get an event ptr to the incoming message
   1586 	//------------------------------------------------------------------------
   1587 	event = (EventClass *)multi_packet_buf;
   1588 
   1589 	//------------------------------------------------------------------------
   1590 	//	Get the index of the sender
   1591 	//------------------------------------------------------------------------
   1592 	index = net->Connection_Index(id);
   1593 
   1594 	//------------------------------------------------------------------------
   1595 	//	Compute the other player's frame # (at the time this packet was sent)
   1596 	//------------------------------------------------------------------------
   1597 	if (their_frame[index] <
   1598 		(int)(event->Frame - event->Data.FrameInfo.Delay)) {
   1599 
   1600 		//.....................................................................
   1601 		// If the original frame # for this player is -1, it means we've heard
   1602 		// from this player for the 1st time; return the appropriate value.
   1603 		//.....................................................................
   1604 		if (their_frame[index]==-1) {
   1605 			retcode = RC_PLAYER_READY;
   1606 		}
   1607 
   1608 		their_frame[index] = event->Frame - event->Data.FrameInfo.Delay;
   1609 	}
   1610 
   1611 	//------------------------------------------------------------------------
   1612 	//	Extract the other player's CommandCount.  This count will include
   1613 	//	the commands in this packet, if there are any.
   1614 	//------------------------------------------------------------------------
   1615 	if (event->Data.FrameInfo.CommandCount > their_sent[index]) {
   1616 		their_sent[index] = event->Data.FrameInfo.CommandCount;
   1617 	}
   1618 
   1619 	//------------------------------------------------------------------------
   1620 	//	If this packet was not a FRAMESYNC packet:
   1621 	//	- Add the events in it to our DoList
   1622 	//	- Increment our commands-received counter by the number of non-
   1623 	//	  FRAMEINFO packets received
   1624 	//------------------------------------------------------------------------
   1625 	if (event->Type != EventClass::FRAMESYNC) {
   1626 		//.....................................................................
   1627 		// Break up the packet into its component events.  A returned packet
   1628 		// count of -1 indicates a fatal queue-full error.
   1629 		//.....................................................................
   1630 		i = Breakup_Receive_Packet( multi_packet_buf, packetlen);
   1631 		if (i==-1) {
   1632 			return (RC_DOLIST_FULL);
   1633 		}
   1634 		//.....................................................................
   1635 		// Compute the actual # commands in the packet by subtracting off the
   1636 		// FRAMEINFO event
   1637 		//.....................................................................
   1638 		if ( (event->Type==EventClass::FRAMEINFO) && (i > 0)) {
   1639 			i--;
   1640 		}
   1641 
   1642 		their_recv[index] += i;
   1643 	}
   1644 
   1645 	//------------------------------------------------------------------------
   1646 	//	If the event was a FRAMESYNC packet, there will be no commands to add,
   1647 	//	but we must check the ScenarioCRC value.
   1648 	//------------------------------------------------------------------------
   1649 	else if (event->Data.FrameInfo.CRC != ScenarioCRC) {
   1650 		return (RC_SCENARIO_MISMATCH);
   1651 	}
   1652 
   1653 	return (retcode);
   1654 
   1655 }	// end of Process_Receive_Packet
   1656 
   1657 
   1658 /***************************************************************************
   1659  * Process_Serial_Packet -- Handles an incoming serial packet              *
   1660  *                                                                         *
   1661  * This routine is needed because the modem classes don't support a 			*
   1662  * "global channel" like the network classes do, but that functionality is	*
   1663  * still needed for modem communications.  Specifically, the modem dialogs	*
   1664  * transmit their own special packets back & forth, and messages are sent	*
   1665  * using a special packet type.  Thus, we have to call this routine when	*
   1666  * we receive a modem packet, to allow it to process messages & dialog		*
   1667  * packets.																						*
   1668  *                                                                         *
   1669  * INPUT:                                                                  *
   1670  *		multi_packet_buf		packet buffer to process								*
   1671  *		first_time				1 = this is the 1st game frame						*
   1672  *                                                                         *
   1673  * OUTPUT:                                                                 *
   1674  *		RC_NORMAL:				this wasn't a SERIAL-type packet						*
   1675  *		RC_SERIAL_PROCESSED:	this was a SERIAL-type packet, and was 			*
   1676  * 								processed; the other player is still connected,	*
   1677  * 								even if he's not in the game.							*
   1678  *		RC_PLAYER_LEFT:		other player has left the game						*
   1679  *		RC_HUNG_UP:				we're getting our own packets back; thus, the 	*
   1680  * 								modem is mirroring our packets, which means the *
   1681  * 								modem hung up!												*
   1682  *                                                                         *
   1683  * WARNINGS:                                                               *
   1684  *		none.																						*
   1685  *                                                                         *
   1686  * HISTORY:                                                                *
   1687  *   11/21/1995 BRR : Created.                                             *
   1688  *=========================================================================*/
   1689 static RetcodeType Process_Serial_Packet(char *multi_packet_buf,
   1690 	int first_time)
   1691 {
   1692 	multi_packet_buf;
   1693 	first_time;
   1694 	return (RC_NORMAL);
   1695 #if (0)	// ST - 1/2/2019 5:28PM
   1696 	SerialPacketType 	*serial_packet;		// for parsing serial packets
   1697 	int 					player_gone;
   1698 	EventClass 			*event;
   1699 	char 					txt[MAX_MESSAGE_LENGTH+80];
   1700 	unsigned short		magic_number;
   1701 	unsigned short		crc;
   1702 
   1703 	//------------------------------------------------------------------------
   1704 	//	Determine if this packet means that the other player has left the game
   1705 	//------------------------------------------------------------------------
   1706 	serial_packet = (SerialPacketType *)multi_packet_buf;
   1707 	player_gone = 0;
   1708 	//........................................................................
   1709 	// On Frame 0, only a SIGN_OFF means the other player left; the other
   1710 	// packet types may be left over from a previous session.
   1711 	//........................................................................
   1712 	if (first_time) {
   1713 		if (serial_packet->Command == SERIAL_SIGN_OFF) {
   1714 			player_gone = 1;
   1715 		}
   1716 	}
   1717 	//........................................................................
   1718 	// On subsequent frames, any of SIGN_OFF, TIMING, or SCORE_SCREEN means
   1719 	// the other player is gone.
   1720 	//........................................................................
   1721 	else {
   1722 		if (serial_packet->Command == SERIAL_SIGN_OFF ||
   1723 			serial_packet->Command == SERIAL_TIMING ||
   1724 			serial_packet->Command == SERIAL_SCORE_SCREEN ) {
   1725 			player_gone = 1;
   1726 		}
   1727 	}
   1728 	if (player_gone) {
   1729 		Destroy_Null_Connection(serial_packet->Color, 0);
   1730 		return (RC_PLAYER_LEFT);
   1731 	}
   1732 
   1733 	//------------------------------------------------------------------------
   1734 	// Process an incoming message
   1735 	//------------------------------------------------------------------------
   1736 	if (serial_packet->Command == SERIAL_MESSAGE) {
   1737 		sprintf(txt, Text_String(TXT_FROM), serial_packet->Name,
   1738 			serial_packet->Message);
   1739 
   1740 		magic_number = *((unsigned short*)(serial_packet->Message + COMPAT_MESSAGE_LENGTH-4));
   1741 		crc = *((unsigned short*)(serial_packet->Message + COMPAT_MESSAGE_LENGTH-2));
   1742 
   1743 		Messages.Add_Message (txt,
   1744 			MPlayerTColors[MPlayerID_To_ColorIndex(serial_packet->ID)],
   1745 			TPF_6PT_GRAD | TPF_USE_GRAD_PAL | TPF_FULLSHADOW, 600, magic_number, crc);
   1746 
   1747 		//.....................................................................
   1748 		//	Save this message in our last-message buffer
   1749 		//.....................................................................
   1750 		if (strlen (serial_packet->Message)) {
   1751 			strcpy (LastMessage, serial_packet->Message);
   1752 		}
   1753 
   1754 		//.....................................................................
   1755 		//	Tell the map to do a partial update (just to force the
   1756 		// messages to redraw).
   1757 		//.....................................................................
   1758 		Map.Flag_To_Redraw(false);
   1759 		return (RC_SERIAL_PROCESSED);
   1760 	}
   1761 
   1762 	//------------------------------------------------------------------------
   1763 	// Any other SERIAL-type packet means the other player is still there;
   1764 	// throw them away, but let the caller know the connection is OK.
   1765 	//------------------------------------------------------------------------
   1766 	if ( (serial_packet->Command >= SERIAL_CONNECT &&
   1767 		serial_packet->Command < SERIAL_LAST_COMMAND) ||
   1768 		MPlayerCount == 1) {
   1769 		return (RC_SERIAL_PROCESSED);
   1770 	}
   1771 
   1772 	//........................................................................
   1773 	//	are we getting our own packets back??
   1774 	//........................................................................
   1775 	event = (EventClass *)multi_packet_buf;
   1776 	if (event->ID == Houses.ID(PlayerPtr)) {
   1777 		return (RC_HUNG_UP);
   1778 	}
   1779 
   1780 	return (RC_NORMAL);
   1781 #endif
   1782 }	// end of Process_Serial_Packet
   1783 
   1784 
   1785 /***************************************************************************
   1786  * Can_Advance -- determines if it's OK to advance to the next frame       *
   1787  *                                                                         *
   1788  * This routine uses the current values stored in their_frame[], 				*
   1789  * their_send[], and their_recv[] to see if it's OK to advance to the next	*
   1790  * game frame.  We must not advance if:												*
   1791  * - If our frame # would be too far ahead of the slowest player (the 		*
   1792  *   lowest their_frame[] value).  "Too far" means 								*
   1793  *   (Frame > their_frame + MaxAhead).													*
   1794  * - our current command count doesn't match the sent command count of one	*
   1795  *   other player (meaning that we've missed a command packet from that 	*
   1796  *   player, and thus the frame # we're receiving from him may be due to a	*
   1797  *   FRAMEINFO packet sent later than the command, so we shouldn't use 		*
   1798  *   this frame # to see if we should advance; we should wait until we 		*
   1799  *   have all the commands before we advance.										*
   1800  *                                                                         *
   1801  * Of course, this routine assumes the values in their_frame[] etc are		*
   1802  * kept current by the caller.															*
   1803  *                                                                         *
   1804  * INPUT:                                                                  *
   1805  *		net				ptr to connection manager										*
   1806  *		max_ahead		max frames ahead													*
   1807  *		their_frame		array of their frame #'s										*
   1808  *		their_sent		array of their sent command count							*
   1809  *		their_recv		array of their # received commands							*
   1810  *                                                                         *
   1811  * OUTPUT:                                                                 *
   1812  *		1 = OK to advance; 0 = not OK														*
   1813  *                                                                         *
   1814  * WARNINGS:                                                               *
   1815  *		none.																						*
   1816  *                                                                         *
   1817  * HISTORY:                                                                *
   1818  *   11/21/1995 BRR : Created.                                             *
   1819  *=========================================================================*/
   1820 static int Can_Advance(ConnManClass *net, int max_ahead, long *their_frame,
   1821 	unsigned short *their_sent, unsigned short *their_recv)
   1822 {
   1823 	long their_oldest_frame;			// other players' oldest frame #
   1824 	int count_ok;							// true = my cmd count matches theirs
   1825 	int i;
   1826 
   1827 	//------------------------------------------------------------------------
   1828 	// Special case for modem: if the other player has left, go ahead and
   1829 	// advance to the next frame; don't wait on him.
   1830 	//------------------------------------------------------------------------
   1831 	if (MPlayerCount == 1) {
   1832 		return (1);
   1833 	}
   1834 	//------------------------------------------------------------------------
   1835 	//	Find the oldest frame # in 'their_frame'
   1836 	//------------------------------------------------------------------------
   1837 	their_oldest_frame = Frame + 1000;
   1838 	for (i = 0; i < net->Num_Connections(); i++) {
   1839 		if (their_frame[i] < their_oldest_frame)
   1840 			their_oldest_frame = their_frame[i];
   1841 	}
   1842 
   1843 	//------------------------------------------------------------------------
   1844 	//	I can advance to the next frame IF:
   1845 	//	1) I'm less than a one-way propogation delay ahead of the other
   1846 	//    players' frame numbers, AND
   1847 	//	2) their_recv[i] >= their_sent[i] (ie I've received all the commands
   1848 	//	   the other players have sent so far).
   1849 	//------------------------------------------------------------------------
   1850 	count_ok = 1;
   1851 	for (i = 0; i < net->Num_Connections(); i++) {
   1852 		if (their_recv[i] < their_sent[i]) {
   1853 			count_ok = 0;
   1854 			break;
   1855 		}
   1856 	}
   1857 	if (count_ok && (Frame < (their_oldest_frame + max_ahead))) {
   1858 		return (1);
   1859 	}
   1860 
   1861 	return (0);
   1862 
   1863 }	// end of Can_Advance
   1864 
   1865 
   1866 /***************************************************************************
   1867  * Process_Reconnect_Dialog -- processes the reconnection dialog           *
   1868  *                                                                         *
   1869  * This routine [re]draws the reconnection dialog; if 'reconn' is set,		*
   1870  * it tells the user who we're trying to reconnect to; otherwise, is just	*
   1871  * says something generic like "Waiting for connections".						*
   1872  *                                                                         *
   1873  * INPUT:                                                                  *
   1874  *		timeout_timer	ptr to count down timer, showing time remaining			*
   1875  *		their_frame		array of other players' frame #'s							*
   1876  *		num_conn			# connections in 'their_frame'								*
   1877  *		reconn			1 = reconnect, 0 = waiting for first-time connection	*
   1878  *		fresh				1 = draw from scratch, 0 = only update time counter	*
   1879  *                                                                         *
   1880  * OUTPUT:                                                                 *
   1881  *		1 = user wants to cancel, 0 = not												*
   1882  *                                                                         *
   1883  * WARNINGS:                                                               *
   1884  *                                                                         *
   1885  * HISTORY:                                                                *
   1886  *   11/21/1995 BRR : Created.                                             *
   1887  *=========================================================================*/
   1888 static int Process_Reconnect_Dialog(CountDownTimerClass *timeout_timer,
   1889 	long *their_frame, int num_conn, int reconn, int fresh)
   1890 {
   1891 	static int displayed_time = 0;	// time value currently displayed
   1892 	int new_time;
   1893 	int oldest_index;						// index of person requiring a reconnect
   1894 	int i,j;
   1895 
   1896 	//------------------------------------------------------------------------
   1897 	// Convert the timer to seconds
   1898 	//------------------------------------------------------------------------
   1899 	new_time = timeout_timer->Time() / 60;
   1900 
   1901 	//--------------------------------------------------------------------------------
   1902 	// If we have just received input focus again after running in the background then
   1903 	// we need to redraw the whole dialog.
   1904 	//--------------------------------------------------------------------------------
   1905 	if (AllSurfaces.SurfacesRestored){
   1906 		AllSurfaces.SurfacesRestored=FALSE;
   1907 		fresh = true;
   1908 	}
   1909 
   1910 	//------------------------------------------------------------------------
   1911 	// If the timer has changed, or 'fresh' is set, redraw the dialog
   1912 	//------------------------------------------------------------------------
   1913 	if (fresh || (new_time != displayed_time)) {
   1914 		//.....................................................................
   1915 		// Find the index of the person we're trying to reconnect to
   1916 		//.....................................................................
   1917 		if (reconn) {
   1918 			j = 0x7fffffff;
   1919 			oldest_index = 0;
   1920 			for (i = 0; i < num_conn; i++) {
   1921 				if (their_frame[i] < j) {
   1922 					j = their_frame[i];
   1923 					oldest_index = i;
   1924 				}
   1925 			}
   1926 		}
   1927 		Net_Reconnect_Dialog(reconn, fresh, oldest_index, new_time);
   1928 	}
   1929 	displayed_time = new_time;
   1930 
   1931 	//........................................................................
   1932 	//	If user hits ESC, bail out
   1933 	//........................................................................
   1934 	if (Check_Key()) {
   1935 		if (Get_Key_Num()==KN_ESC) {
   1936 			return (1);
   1937 		}
   1938 	}
   1939 
   1940 	return (0);
   1941 
   1942 }	// end of Process_Reconnect_Dialog
   1943 
   1944 
   1945 /***************************************************************************
   1946  * Handle_Timeout -- handles a timeout in the wait-for-players loop			*
   1947  *                                                                         *
   1948  * This routine "gracefully" handles a timeout in the frame-sync loop.		*
   1949  * The timeout must be handled differently by a modem game or network 		*
   1950  * game.																							*
   1951  *                                                                         *
   1952  * The modem game must detect if the other player is still connected			*
   1953  * physically, even if he's not playing the game any more; if so, this		*
   1954  * routine returns an OK status.  If the other player isn't even 				*
   1955  * physically connected, an error is returned.										*
   1956  *                                                                         *
   1957  * The network game must find the connection that's causing the timeout,	*
   1958  * and destroy it.  The game continues, even if there are no more human		*
   1959  * players left.																				*
   1960  *                                                                         *
   1961  * INPUT:                                                                  *
   1962  *		net					ptr to connection manager									*
   1963  *		their_frame			array containing frame #'s of other players			*
   1964  *		their_sent			array containing command count of other players		*
   1965  *		their_recv			array containing # recv'd cmds from other players	*
   1966  *                                                                         *
   1967  * OUTPUT:                                                                 *
   1968  *		1 = it's OK; reset timeout timers & keep processing						*
   1969  *		0 = game over, man																	*
   1970  *                                                                         *
   1971  * WARNINGS:                                                               *
   1972  *                                                                         *
   1973  * HISTORY:                                                                *
   1974  *   11/21/1995 BRR : Created.                                             *
   1975  *=========================================================================*/
   1976 static int Handle_Timeout(ConnManClass *net, long *their_frame,
   1977 	unsigned short *their_sent, unsigned short *their_recv)
   1978 {
   1979 	int oldest_index;						// index of person requiring a reconnect
   1980 	int i,j;
   1981 	int id;
   1982 
   1983 	//------------------------------------------------------------------------
   1984 	// For modem, attempt to reconnect; if that fails, save the game & bail.
   1985 	//------------------------------------------------------------------------
   1986 	if (GameToPlay == GAME_MODEM || GameToPlay == GAME_NULL_MODEM) {
   1987 #if (0)			//ST - 1/2/2019 5:28PM
   1988 		if ( net->Num_Connections() ) {
   1989 			if (!Reconnect_Modem()) {
   1990 				return (0);
   1991 			}
   1992 			else {
   1993 				return (1);
   1994 			}
   1995 		}
   1996 #endif
   1997 	}
   1998 
   1999 	//------------------------------------------------------------------------
   2000 	//	For network, destroy the oldest connection
   2001 	//------------------------------------------------------------------------
   2002 	else if (GameToPlay == GAME_IPX || GameToPlay == GAME_INTERNET) {
   2003 		j = 0x7fffffff;
   2004 		oldest_index = 0;
   2005 		for (i = 0; i < net->Num_Connections(); i++) {
   2006 			if (their_frame[i] < j) {
   2007 				j = their_frame[i];
   2008 				oldest_index = i;
   2009 			}
   2010 		}
   2011 
   2012 		id = net->Connection_ID(oldest_index);
   2013 
   2014 		/*
   2015 		** Send the game statistics packet now if the game is effectivly over
   2016 		*/
   2017 		if (MPlayerCount == 2 &&
   2018 				GameToPlay == GAME_INTERNET &&
   2019 				!GameStatisticsPacketSent){
   2020 			Register_Game_End_Time();
   2021 			ConnectionLost = true;
   2022 			Send_Statistics_Packet();
   2023 		}
   2024 
   2025 		if (id != ConnManClass::CONNECTION_NONE) {
   2026 			for (i = oldest_index; i < net->Num_Connections() - 1; i++) {
   2027 				their_frame[i] = their_frame[i+1];
   2028 				their_sent[i] = their_sent[i+1];
   2029 				their_recv[i] = their_recv[i+1];
   2030 			}
   2031 			CCDebugString ("C&C95 = Destroying connection due to time out\n");
   2032 			Destroy_Connection(id,1);
   2033 		}
   2034 	}
   2035 
   2036 	return (1);
   2037 
   2038 }	// end of Handle_Timeout
   2039 
   2040 
   2041 /***************************************************************************
   2042  * Stop_Game -- stops the game															*
   2043  *                                                                         *
   2044  * This routine clears any global flags that need it, in preparation for	*
   2045  * halting the game prematurely.															*
   2046  *                                                                         *
   2047  * INPUT:                                                                  *
   2048  *		none.																						*
   2049  *                                                                         *
   2050  * OUTPUT:                                                                 *
   2051  *		none.																						*
   2052  *                                                                         *
   2053  * WARNINGS:                                                               *
   2054  *		none.																						*
   2055  *                                                                         *
   2056  * HISTORY:                                                                *
   2057  *   11/22/1995 BRR : Created.                                             *
   2058  *=========================================================================*/
   2059 static void Stop_Game(void)
   2060 {
   2061 	CCDebugString ("C&C95 - In Stop_Game.\n");
   2062 	GameActive = 0;
   2063 	if (IsMono) {
   2064 		MonoClass::Disable();
   2065 	}
   2066 
   2067 	if (GameToPlay == GAME_INTERNET){
   2068 		ConnectionLost = true;
   2069 		CCDebugString ("C&C95 - About to send statistics packet.\n");
   2070 		Register_Game_End_Time();
   2071 		Send_Statistics_Packet();
   2072 		CCDebugString ("C&C95 - Returned from sending stats packet.\n");
   2073 	}
   2074 
   2075 	return;
   2076 
   2077 }	// end of Stop_Game
   2078 
   2079 
   2080 /***************************************************************************
   2081  * Build_Send_Packet -- Builds a big packet from a bunch of little ones.	*
   2082  *                                                                         *
   2083  * This routine takes events from the OutList, and puts them into a 			*
   2084  * "meta-packet", which is transmitted to all systems we're connected to.	*
   2085  * Also, these events are added to our own DoList.									*
   2086  *                                                                         *
   2087  * Every Meta-Packet we send uses a FRAMEINFO packet as a header; this 		*
   2088  * tells the other systems what frame we're on, as well as serving as a 	*
   2089  * standard packet header.																	*
   2090  *                                                                         *
   2091  * INPUT:                                                                  *
   2092  *		buf				buffer to store packet in										*
   2093  *		bufsize			max size of buffer												*
   2094  *		frame_delay		desired frame delay to attach to all outgoing packets	*
   2095  *		num_cmds			value to use for the CommandCount field					*
   2096  *		cap				max # events to send												*
   2097  *                                                                         *
   2098  * OUTPUT:                                                                 *
   2099  *		new size of packet																	*
   2100  *                                                                         *
   2101  * WARNINGS:                                                               *
   2102  * 'num_cmds' should be the total of of commands, including all those sent *
   2103  * this frame!																					*
   2104  *                                                                         *
   2105  * HISTORY:                                                                *
   2106  *   11/21/1995 BRR : Created.                                             *
   2107  *=========================================================================*/
   2108 static int Build_Send_Packet(void *buf, int bufsize, int frame_delay,
   2109 	int num_cmds, int cap)
   2110 {
   2111 	int size = 0;
   2112 	EventClass *finfo;
   2113 
   2114 	//------------------------------------------------------------------------
   2115 	// All events start with a FRAMEINFO event; fill this part in.
   2116 	//------------------------------------------------------------------------
   2117 	//........................................................................
   2118 	// Set the event type
   2119 	//........................................................................
   2120 	finfo = (EventClass *)buf;
   2121 	finfo->Type = EventClass::FRAMEINFO;
   2122 	//........................................................................
   2123 	// Set the frame to execute this event on; this is protocol-specific
   2124 	//........................................................................
   2125 	if (CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) {
   2126 		finfo->Frame = ((Frame + frame_delay + (FrameSendRate - 1)) /
   2127 			 FrameSendRate) * FrameSendRate;
   2128 	}
   2129 	else {
   2130 		finfo->Frame = Frame + frame_delay;
   2131 	}
   2132 	//........................................................................
   2133 	// Fill in the rest of the event
   2134 	//........................................................................
   2135 	finfo->ID = Houses.ID(PlayerPtr);
   2136 	finfo->MPlayerID = MPlayerLocalID;
   2137 	finfo->Data.FrameInfo.CRC = GameCRC;
   2138 	finfo->Data.FrameInfo.CommandCount = num_cmds;
   2139 	finfo->Data.FrameInfo.Delay = frame_delay;
   2140 
   2141 	//------------------------------------------------------------------------
   2142 	// Initialize the # of bytes processed; this is protocol-specific
   2143 	//------------------------------------------------------------------------
   2144 	if (CommProtocol==COMM_PROTOCOL_SINGLE_NO_COMP) {
   2145 		size += sizeof(EventClass);
   2146 	}
   2147 	else {
   2148 		size += (offsetof(EventClass, Data) +
   2149 			size_of(EventClass, Data.FrameInfo));
   2150 	}
   2151 
   2152 	//------------------------------------------------------------------------
   2153 	//	Transfer all events from the OutList into the DoList, building our
   2154 	//	packet while we go.
   2155 	//------------------------------------------------------------------------
   2156 	switch (CommProtocol) {
   2157 		//.....................................................................
   2158 		// COMM_PROTOCOL_SINGLE_NO_COMP:
   2159 		// We'll send at least a FRAMEINFO every single frame, no compression
   2160 		//.....................................................................
   2161 		case (COMM_PROTOCOL_SINGLE_NO_COMP):
   2162 			size = Add_Uncompressed_Events(buf, bufsize, frame_delay, size, cap);
   2163 			break;
   2164 
   2165 		//.....................................................................
   2166 		// COMM_PROTOCOL_SINGLE_E_COMP:
   2167 		//   Compress a group of packets into our send buffer; send out
   2168 		//   compressed packets every frame.
   2169 		// COMM_PROTOCOL_MULTI_E_COMP:
   2170 		//   Compress a group of packets into our send buffer; send out
   2171 		//   compressed packets every 'n' frames.
   2172 		//.....................................................................
   2173 		case (COMM_PROTOCOL_SINGLE_E_COMP):
   2174 		case (COMM_PROTOCOL_MULTI_E_COMP):
   2175 			size = Add_Compressed_Events(buf, bufsize, frame_delay, size, cap);
   2176 			break;
   2177 
   2178 		//.....................................................................
   2179 		// Default: We have no idea what to do, so do nothing.
   2180 		//.....................................................................
   2181 		default:
   2182 			size = 0;
   2183 			break;
   2184 	}
   2185 
   2186 	return( size );
   2187 
   2188 }	/* end of Build_Send_Packet */
   2189 
   2190 
   2191 /***************************************************************************
   2192  * Add_Uncompressed_Events -- adds uncompressed events to a packet         *
   2193  *                                                                         *
   2194  * INPUT:                                                                  *
   2195  *		buf				buffer to store packet in										*
   2196  *		bufsize			max size of buffer												*
   2197  *		frame_delay		desired frame delay to attach to all outgoing packets	*
   2198  *		size				current packet size												*
   2199  *		cap				max # events to process											*
   2200  *                                                                         *
   2201  * OUTPUT:                                                                 *
   2202  *		new size value																			*
   2203  *                                                                         *
   2204  * WARNINGS:                                                               *
   2205  *		This routine MUST check to be sure it doesn't overflow the buffer.	*
   2206  *                                                                         *
   2207  * HISTORY:                                                                *
   2208  *   11/21/1995 DRD : Created.                                             *
   2209  *=========================================================================*/
   2210 static int Add_Uncompressed_Events(void *buf, int bufsize, int frame_delay,
   2211 	int size, int cap)
   2212 {
   2213 	int num = 0;			// # of events processed
   2214 	int ev_size;			// size of event we're adding
   2215 
   2216 	//------------------------------------------------------------------------
   2217 	// Loop until there are no more events, or we've processed our max # of
   2218 	// events, or the buffer is full.
   2219 	//------------------------------------------------------------------------
   2220 	while (OutList.Count && (num < cap)) {
   2221 		ev_size = sizeof(EventClass);
   2222 		//.....................................................................
   2223 		// Will the next event exceed the size of the buffer?  If so, break.
   2224 		//.....................................................................
   2225 		if ( (size + ev_size) > bufsize ) {
   2226 			return (size);
   2227 		}
   2228 
   2229 		//.....................................................................
   2230 		// Set the event's frame delay
   2231 		//.....................................................................
   2232 		OutList.First().Frame = Frame + frame_delay;
   2233 
   2234 		//.....................................................................
   2235 		// Set the event's ID
   2236 		//.....................................................................
   2237 		OutList.First().ID = Houses.ID(PlayerPtr);
   2238 		OutList.First().MPlayerID = MPlayerLocalID;
   2239 
   2240 		//.....................................................................
   2241 		// Transfer the event in OutList to DoList, un-queue the OutList
   2242 		// event.  If the DoList is full, stop transferring immediately.
   2243 		//.....................................................................
   2244 		OutList.First().IsExecuted = 0;
   2245 		if (!DoList.Add(OutList.First())) {
   2246 			return (size);
   2247 		}
   2248 
   2249 		//.....................................................................
   2250 		// Add event to the send packet
   2251 		//.....................................................................
   2252 		memcpy ( ((char *)buf) + size, &OutList.First(), sizeof(EventClass) );
   2253 		size += sizeof(EventClass);
   2254 
   2255 		//.....................................................................
   2256 		// Increment our event counter; delete the last event from the queue
   2257 		//.....................................................................
   2258 		num++;
   2259 		OutList.Next();
   2260 	}
   2261 
   2262 	return (size);
   2263 
   2264 }	// end of Add_Uncompressed_Events
   2265 
   2266 
   2267 /***************************************************************************
   2268  * Add_Compressed_Events -- adds an compressed events to a packet          *
   2269  *                                                                         *
   2270  * INPUT:                                                                  *
   2271  *		buf				buffer to store packet in										*
   2272  *		bufsize			max size of buffer												*
   2273  *		frame_delay		desired frame delay to attach to all outgoing packets	*
   2274  *		size				reference to current packet size								*
   2275  *		cap				max # events to process											*
   2276  *                                                                         *
   2277  * OUTPUT:                                                                 *
   2278  *		new size value																			*
   2279  *                                                                         *
   2280  * WARNINGS:                                                               *
   2281  *		This routine MUST check to be sure it doesn't overflow the buffer.	*
   2282  *                                                                         *
   2283  * HISTORY:                                                                *
   2284  *   11/21/1995 DRD : Created.                                             *
   2285  *=========================================================================*/
   2286 static int Add_Compressed_Events(void *buf, int bufsize, int frame_delay,
   2287 	int size, int cap)
   2288 {
   2289 	int num = 0;							// # of events processed
   2290 	EventClass::EventType eventtype;	// type of event being compressed
   2291 	EventClass prevevent;				// last event processed
   2292 	int datasize;							// size of element plucked from event union
   2293 	int storedsize;						// actual # bytes stored from event
   2294 	unsigned char *unitsptr = NULL;	// ptr to buffer pos to store mega. rep count
   2295 	unsigned char numunits = 0;		// megamission rep count value
   2296 	bool missiondup = false;			// flag: is this event a megamission repeat?
   2297 
   2298 	//------------------------------------------------------------------------
   2299 	// clear previous event
   2300 	//------------------------------------------------------------------------
   2301 	memset (&prevevent, 0, sizeof(EventClass));
   2302 
   2303 	//------------------------------------------------------------------------
   2304 	// Loop until there are no more events, we've processed our max # of
   2305 	// events, or the buffer is full.
   2306 	//------------------------------------------------------------------------
   2307 	while (OutList.Count && (num < cap)) {
   2308 		eventtype = OutList.First().Type;
   2309 		datasize = EventClass::EventLength[ eventtype ];
   2310 		//.....................................................................
   2311 		// For a variable-sized event, pull the size from the event; otherwise,
   2312 		// the size will be the data element size plus the event type value.
   2313 		//.....................................................................
   2314 		storedsize = datasize + sizeof (EventClass::EventType);
   2315 
   2316 		//.....................................................................
   2317 		// MegaMission compression:  MegaMissions are stored as:
   2318 		//   EventType
   2319 		//   Rep Count
   2320 		//   MegaMission structure (event # 1 only)
   2321 		//   Whom #2
   2322 		//   Whom #3
   2323 		//   Whom #4
   2324 		//   ...
   2325 		//   Whom #n
   2326 		//.....................................................................
   2327 		if (prevevent.Type == EventClass::MEGAMISSION) {
   2328 			//..................................................................
   2329 			// If previous & current events are both MegaMissions:
   2330 			//..................................................................
   2331 			if (eventtype == EventClass::MEGAMISSION) {
   2332 				//...............................................................
   2333 				// If the Mission, Target, & Destination are the same, compress
   2334 				// the events into one:
   2335 				// - Change datasize to the size of the 'Whom' field only
   2336 				// - set total # bytes to store to the size of the 'Whom' only
   2337 				// - increment the MegaMission rep count
   2338 				// - set the MegaMission rep flag
   2339 				//...............................................................
   2340 				if (OutList.First().Data.MegaMission.Mission ==
   2341 					prevevent.Data.MegaMission.Mission &&
   2342 					OutList.First().Data.MegaMission.Target ==
   2343 						prevevent.Data.MegaMission.Target &&
   2344 					OutList.First().Data.MegaMission.Destination ==
   2345 						prevevent.Data.MegaMission.Destination) {
   2346 
   2347 					datasize = sizeof(prevevent.Data.MegaMission.Whom);
   2348 					storedsize = datasize;
   2349 					numunits++;
   2350 					missiondup = true;
   2351 				}
   2352 				//...............................................................
   2353 				// Data doesn't match; start a new run of MegaMissions:
   2354 				// - Store previous MegaMission rep count
   2355 				// - Init 'unitsptr' to buffer pos after next EventType
   2356 				// - set total # bytes to store to 'datasize' + sizeof(EventType) +
   2357 				//   sizeof (numunits)
   2358 				// - init the MegaMission rep count to 1
   2359 				// - clear the MegaMission rep flag
   2360 				//...............................................................
   2361 				else {
   2362 					*unitsptr = numunits;
   2363 					unitsptr = ((unsigned char *)buf) + size +
   2364 						sizeof(EventClass::EventType);
   2365 					storedsize += sizeof(numunits);
   2366 					numunits = 1;
   2367 					missiondup = false;
   2368 				}
   2369 			}
   2370 			//..................................................................
   2371 			// Previous event was a MegaMission, but this one isn't: end the
   2372 			// run of MegaMissions:
   2373 			// - Store previous MegaMission rep count
   2374 			// - Clear variables
   2375 			//..................................................................
   2376 			else {
   2377 				*unitsptr = numunits;		// save # events in our run
   2378 				unitsptr = NULL;				// init other values
   2379 				numunits = 0;
   2380 				missiondup = false;
   2381 			}
   2382 		}
   2383 
   2384 		//.....................................................................
   2385 		// The previous event is not a MEGAMISSION but the current event is:
   2386 		// Set up a new run of MegaMissions:
   2387 		// - Init 'unitsptr' to buffer pos after next EventType
   2388 		// - set total # bytes to store to 'datasize' + sizeof(EventType) +
   2389 		//   sizeof (numunits)
   2390 		// - init the MegaMission rep count to 1
   2391 		// - clear the MegaMission rep flag
   2392 		//.....................................................................
   2393 		else if (eventtype == EventClass::MEGAMISSION) {
   2394 			unitsptr = ((unsigned char *)buf) + size +
   2395 				sizeof(EventClass::EventType);
   2396 			storedsize += sizeof(numunits);
   2397 			numunits = 1;
   2398 			missiondup = false;
   2399 		}
   2400 
   2401 		//.....................................................................
   2402 		// Will the next event exceed the size of the buffer?  If so,
   2403 		// stop compressing.
   2404 		//.....................................................................
   2405 		if ( (size + storedsize) > bufsize )
   2406 			break;
   2407 
   2408 		//.....................................................................
   2409 		// Set the event's frame delay (this is protocol-dependent)
   2410 		//.....................................................................
   2411 		if (CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) {
   2412 			OutList.First().Frame = ((Frame + frame_delay +
   2413 				(FrameSendRate - 1)) / FrameSendRate) *
   2414 				FrameSendRate;
   2415 		}
   2416 		else {
   2417 			OutList.First().Frame = Frame + frame_delay;
   2418 		}
   2419 
   2420 		//.....................................................................
   2421 		// Set the event's ID
   2422 		//.....................................................................
   2423 		OutList.First().ID = Houses.ID(PlayerPtr);
   2424 		OutList.First().MPlayerID = MPlayerLocalID;
   2425 
   2426 		//.....................................................................
   2427 		// Transfer the event in OutList to DoList, un-queue the OutList event.
   2428 		// If the DoList is full, stop transferring immediately.
   2429 		//.....................................................................
   2430 		OutList.First().IsExecuted = 0;
   2431 		if ( !DoList.Add( OutList.First() ) ) {
   2432 			break;
   2433 		}
   2434 
   2435 		//---------------------------------------------------------------------
   2436 		// Compress the event into the send packet buffer
   2437 		//---------------------------------------------------------------------
   2438 		switch ( eventtype ) {
   2439 			//..................................................................
   2440 			// RESPONSE_TIME: just use the Delay field of the FrameInfo union
   2441 			//..................................................................
   2442 			case (EventClass::RESPONSE_TIME):
   2443 
   2444 				*(EventClass::EventType *)( ((char *)buf) + size) = eventtype;
   2445 
   2446 				memcpy ( ((char *)buf) + size + sizeof(EventClass::EventType),
   2447 					&OutList.First().Data.FrameInfo.Delay, datasize );
   2448 
   2449 				size += (datasize + sizeof(EventClass::EventType));
   2450 				break;
   2451 
   2452 			//..................................................................
   2453 			// MEGAMISSION:
   2454 			//..................................................................
   2455 			case (EventClass::MEGAMISSION):
   2456 				//...............................................................
   2457 				// Repeated mission in a run:
   2458 				//   - Update the rep count (in case we break out)
   2459 				//   - Copy the Whom field only
   2460 				//...............................................................
   2461 				if (missiondup) {
   2462 					*unitsptr = numunits;
   2463 
   2464 					memcpy ( ((char *)buf) + size,
   2465 						&OutList.First().Data.MegaMission.Whom, datasize );
   2466 
   2467 					size += datasize;
   2468 				}
   2469 				//...............................................................
   2470 				// 1st mission in a run:
   2471 				//   - Init the rep count (in case we break out)
   2472 				//   - Set the EventType
   2473 				//   - Copy the MegaMission structure, leaving room for 'numunits'
   2474 				//...............................................................
   2475 				else {
   2476 					*unitsptr = numunits;
   2477 
   2478 					*(EventClass::EventType *)( ((char *)buf) + size) = eventtype;
   2479 
   2480 					memcpy ( ((char *)buf) + size +
   2481 						sizeof(EventClass::EventType) + sizeof(numunits),
   2482 						&OutList.First().Data.MegaMission, datasize );
   2483 
   2484 					size += (datasize + sizeof(EventClass::EventType) + sizeof(numunits));
   2485 				}
   2486 				break;
   2487 
   2488 			//..................................................................
   2489 			// Default case: Just copy over the data field from the union
   2490 			//..................................................................
   2491 			default:
   2492 				*(EventClass::EventType *)( ((char *)buf) + size) = eventtype;
   2493 
   2494 				memcpy ( ((char *)buf) + size + sizeof(EventClass::EventType),
   2495 					&OutList.First().Data, datasize );
   2496 
   2497 				size += (datasize + sizeof(EventClass::EventType));
   2498 
   2499 				break;
   2500 		}
   2501 
   2502 		//---------------------------------------------------------------------
   2503 		// update # events processed
   2504 		//---------------------------------------------------------------------
   2505 		num++;
   2506 
   2507 		//---------------------------------------------------------------------
   2508 		// Update 'prevevent'
   2509 		//---------------------------------------------------------------------
   2510 		memcpy ( &prevevent, &OutList.First(), sizeof(EventClass) );
   2511 
   2512 		//---------------------------------------------------------------------
   2513 		// Go to the next event to process
   2514 		//---------------------------------------------------------------------
   2515 		OutList.Next();
   2516 	}
   2517 
   2518 	return (size);
   2519 
   2520 }	// end of Add_Compressed_Events
   2521 
   2522 
   2523 /***************************************************************************
   2524  * Breakup_Receive_Packet -- Splits a big packet into little ones.			*
   2525  *                                                                         *
   2526  * INPUT:                                                                  *
   2527  *		buf			buffer to break up													*
   2528  *		bufsize		length of buffer														*
   2529  *                                                                         *
   2530  * OUTPUT:                                                                 *
   2531  *		# events added to queue, -1 if fatal error (queue is full)				*
   2532  *    (return value includes any FRAMEINFO packets encountered; 				*
   2533  *		FRAMESYNC's are ignored)		  													*
   2534  *                                                                         *
   2535  * WARNINGS:                                                               *
   2536  *		none.																						*
   2537  *                                                                         *
   2538  * HISTORY:                                                                *
   2539  *   11/21/1995 BRR : Created.                                             *
   2540  *=========================================================================*/
   2541 static int Breakup_Receive_Packet(void *buf, int bufsize )
   2542 {
   2543 	int count = 0;
   2544 
   2545 	/*
   2546 	** is there enough leftover for another record
   2547 	*/
   2548 	switch (CommProtocol) {
   2549 		case (COMM_PROTOCOL_SINGLE_NO_COMP):
   2550 			count = Extract_Uncompressed_Events(buf, bufsize);
   2551 			break;
   2552 
   2553 		default:
   2554 			count = Extract_Compressed_Events(buf, bufsize);
   2555 			break;
   2556 	}
   2557 
   2558 	return (count);
   2559 
   2560 }	/* end of Breakup_Receive_Packet */
   2561 
   2562 
   2563 /***************************************************************************
   2564  * Extract_Uncompressed_Events -- extracts events from a packet            *
   2565  *                                                                         *
   2566  * INPUT:                                                                  *
   2567  *		buf			buffer containing events to extract								*
   2568  *		bufsize		length of 'buf'														*
   2569  *                                                                         *
   2570  * OUTPUT:                                                                 *
   2571  *		# events extracted																	*
   2572  *                                                                         *
   2573  * WARNINGS:                                                               *
   2574  *		none.																						*
   2575  *                                                                         *
   2576  * HISTORY:                                                                *
   2577  *   11/21/1995 DRD : Created.                                             *
   2578  *=========================================================================*/
   2579 static int Extract_Uncompressed_Events(void *buf, int bufsize)
   2580 {
   2581 	int count = 0;
   2582 	int pos = 0;
   2583 	int leftover = bufsize;
   2584 	EventClass *event;
   2585 
   2586 	//------------------------------------------------------------------------
   2587 	// Loop until there are no more events in the packet
   2588 	//------------------------------------------------------------------------
   2589 	while (leftover >= sizeof(EventClass) ) {
   2590 		event = (EventClass *)(((char *)buf) + pos);
   2591 
   2592 		//.....................................................................
   2593 		// add event to the DoList, only if it's not a FRAMESYNC
   2594 		// (but FRAMEINFO's do get added.)
   2595 		//.....................................................................
   2596 		if (event->Type != EventClass::FRAMESYNC) {
   2597 			event->IsExecuted = 0;
   2598 
   2599 			if (!DoList.Add( *event )) {
   2600 				return (-1);
   2601 			}
   2602 
   2603 			//..................................................................
   2604 			// Keep count of how many events we add to the queue
   2605 			//..................................................................
   2606 			count++;
   2607 		}
   2608 
   2609 		//.....................................................................
   2610 		// Point to the next position in the buffer; decrement our 'leftover'
   2611 		//.....................................................................
   2612 		pos += sizeof(EventClass);
   2613 		leftover -= sizeof(EventClass);
   2614 	}
   2615 
   2616 	return (count);
   2617 
   2618 }	// end of Extract_Uncompressed_Events
   2619 
   2620 
   2621 /***************************************************************************
   2622  * Extract_Compressed_Events -- extracts events from a packet   				*
   2623  *                                                                         *
   2624  * INPUT:                                                                  *
   2625  *		buf			buffer containing events to extract								*
   2626  *		bufsize		length of 'buf'														*
   2627  *                                                                         *
   2628  * OUTPUT:                                                                 *
   2629  *		# events extracted																	*
   2630  *                                                                         *
   2631  * WARNINGS:                                                               *
   2632  *		none.																						*
   2633  *                                                                         *
   2634  * HISTORY:                                                                *
   2635  *   11/21/1995 DRD : Created.                                             *
   2636  *=========================================================================*/
   2637 static int Extract_Compressed_Events(void *buf, int bufsize)
   2638 {
   2639 	int pos = 0;						// current buffer parsing position
   2640 	int leftover = bufsize;			// # bytes left to process
   2641 	EventClass *event;				// event ptr for parsing buffer
   2642 	int count = 0;						// # events processed
   2643 	int datasize = 0;					// size of data to copy
   2644 	EventClass eventdata;			// stores Frame, ID, etc
   2645 	unsigned char numunits = 0;	// # units stored in compressed MegaMissions
   2646 //int lasteventtype=0;
   2647 	//------------------------------------------------------------------------
   2648 	// Clear work event structure
   2649 	//------------------------------------------------------------------------
   2650 	memset (&eventdata, 0, sizeof(EventClass));
   2651 
   2652 	//------------------------------------------------------------------------
   2653 	// Assume the first event is a FRAMEINFO event
   2654 	// Init 'datasize' to the amount of data to copy, minus the EventType value
   2655 	// For the 1st packet only, this will include all info before the Data
   2656 	// union, plus the size of the FrameInfo structure, minus the EventType size.
   2657 	//------------------------------------------------------------------------
   2658 	datasize = (offsetof(EventClass, Data) +
   2659 		size_of(EventClass, Data.FrameInfo)) - sizeof(EventClass::EventType);
   2660 	event = (EventClass *)(((char *)buf) + pos);
   2661 
   2662 	while ((unsigned)leftover >= (datasize + sizeof(EventClass::EventType)) ) {
   2663 		//.....................................................................
   2664 		// add event to the DoList, only if it's not a FRAMESYNC
   2665 		// (but FRAMEINFO's do get added.)
   2666 		//.....................................................................
   2667 		if (event->Type != EventClass::FRAMESYNC) {
   2668 			//..................................................................
   2669 			// initialize the common data from the FRAMEINFO event
   2670 			// keeping IsExecuted 0
   2671 			//..................................................................
   2672 			if (event->Type == EventClass::FRAMEINFO) {
   2673 				eventdata.Frame = event->Frame;
   2674 				eventdata.ID = event->ID;
   2675 				eventdata.MPlayerID = event->MPlayerID;
   2676 
   2677 				//...............................................................
   2678 				// Adjust position past the common data
   2679 				//...............................................................
   2680 				pos += (offsetof(EventClass, Data) -
   2681 						 sizeof(EventClass::EventType));
   2682 				leftover -= (offsetof(EventClass, Data) -
   2683 								sizeof(EventClass::EventType));
   2684 			}
   2685 			//..................................................................
   2686 			// if MEGAMISSION event get the number of units (events to generate)
   2687 			//..................................................................
   2688 			else if (event->Type == EventClass::MEGAMISSION) {
   2689 				numunits = *(((unsigned char *)buf) + pos + sizeof(eventdata.Type));
   2690 				pos += sizeof(numunits);
   2691 				leftover -= sizeof(numunits);
   2692 			}
   2693 
   2694 			//..................................................................
   2695 			// clear the union data portion of the event
   2696 			//..................................................................
   2697 			memset (&eventdata.Data, 0, sizeof(eventdata.Data));
   2698 			eventdata.Type = event->Type;
   2699 			datasize = EventClass::EventLength[ eventdata.Type ];
   2700 
   2701 			switch (eventdata.Type) {
   2702 				case (EventClass::RESPONSE_TIME):
   2703 					memcpy ( &eventdata.Data.FrameInfo.Delay,
   2704 						((char *)buf) + pos + sizeof(EventClass::EventType),
   2705 						datasize );
   2706 					break;
   2707 
   2708 				case (EventClass::MEGAMISSION):
   2709 					memcpy ( &eventdata.Data.MegaMission,
   2710 						((char *)buf) + pos + sizeof(EventClass::EventType),
   2711 						datasize );
   2712 
   2713 					if (numunits > 1) {
   2714 						pos += (datasize + sizeof(EventClass::EventType));
   2715 						leftover -= (datasize + sizeof(EventClass::EventType));
   2716 						datasize = sizeof(eventdata.Data.MegaMission.Whom);
   2717 
   2718 						while (numunits) {
   2719 							if ( !DoList.Add( eventdata ) ) {
   2720 								return (-1);
   2721 							}
   2722 
   2723 							//......................................................
   2724 							// Keep count of how many events we add to the queue
   2725 							//......................................................
   2726 							count++;
   2727 							numunits--;
   2728 							memcpy ( &eventdata.Data.MegaMission.Whom,
   2729 								((char *)buf) + pos, datasize );
   2730 
   2731 							//......................................................
   2732 							// if one unit left fall thru to normal code
   2733 							//......................................................
   2734 							if (numunits == 1) {
   2735 								datasize -= sizeof(EventClass::EventType);
   2736 								break;
   2737 							}
   2738 							else {
   2739 								pos += datasize;
   2740 								leftover -= datasize;
   2741 							}
   2742 						}
   2743 					}
   2744 					break;
   2745 
   2746 				default:
   2747 					memcpy ( &eventdata.Data,
   2748 						((char *)buf) + pos + sizeof(EventClass::EventType),
   2749 						datasize );
   2750 					break;
   2751 			}
   2752 
   2753 char flip[128];
   2754 sprintf (flip, "C&C95 - Adding event type %d to queue\n", eventdata.Type);
   2755 CCDebugString (flip);
   2756 
   2757 //if (lasteventtype == 11){
   2758 //	break;
   2759 //}
   2760 
   2761 //lasteventtype = (int) eventdata.Type;
   2762 
   2763 
   2764 			if ( !DoList.Add( eventdata ) ) {
   2765 				return (-1);
   2766 			}
   2767 
   2768 			//..................................................................
   2769 			// Keep count of how many events we add to the queue
   2770 			//..................................................................
   2771 			count++;
   2772 
   2773 			pos += (datasize + sizeof(EventClass::EventType));
   2774 			leftover -= (datasize + sizeof(EventClass::EventType));
   2775 
   2776 			if (leftover) {
   2777 				event = (EventClass *)(((char *)buf) + pos);
   2778 				datasize = EventClass::EventLength[ event->Type ];
   2779 				if (event->Type == EventClass::MEGAMISSION) {
   2780 					datasize += sizeof(numunits);
   2781 				}
   2782 			}
   2783 		}
   2784 		//.....................................................................
   2785 		// FRAMESYNC event: This >should< be the only event in the buffer,
   2786 		// and it will be uncompressed.
   2787 		//.....................................................................
   2788 		else {
   2789 			pos += (datasize + sizeof(EventClass::EventType));
   2790 			leftover -= (datasize + sizeof(EventClass::EventType));
   2791 			event = (EventClass *)(((char *)buf) + pos);
   2792 
   2793 			//..................................................................
   2794 			// size of FRAMESYNC event - EventType size
   2795 			//..................................................................
   2796 			datasize = (offsetof(EventClass, Data) +
   2797 							size_of(EventClass, Data.FrameInfo)) -
   2798 							sizeof(EventClass::EventType);
   2799 		}
   2800 	}
   2801 
   2802 	return (count);
   2803 
   2804 }	// end of Extract_Compressed_Events
   2805 
   2806 #endif	//DEMO
   2807 
   2808 /***************************************************************************
   2809  * Execute_DoList -- Executes commands from the DoList                     *
   2810  *                                                                         *
   2811  * This routine executes any events in the DoList that need to be executed	*
   2812  * on the current game frame.  The events must be executed in a special		*
   2813  * order, so that all systems execute all events in exactly the same			*
   2814  * order.																						*
   2815  *                                                                         *
   2816  * This routine also handles checking the Game CRC sent by other systems	*
   2817  * against my own, to be sure we're still in sync.									*
   2818  *                                                                         *
   2819  * INPUT:                                                                  *
   2820  *		max_houses	# houses to execute commands for									*
   2821  *		base_house	HousesType to start with											*
   2822  *		net			ptr to connection manager; NULL if none						*
   2823  *		skip_crc		a frame-based countdown timer; if it's non-zero, the		*
   2824  *						CRC check will be skipped.  Ignored if NULL.					*
   2825  *		their_frame	array of their frame #'s											*
   2826  *		their_sent	array of # commands they've sent									*
   2827  *		their_recv	array of # commands I've received from them					*
   2828  *                                                                         *
   2829  * (their_xxx are ignored if 'net' is NULL.)											*
   2830  *                                                                         *
   2831  * OUTPUT:                                                                 *
   2832  *		1 = OK, 0 = some error occurred (CRC error, packet rcv'd too late.)	*
   2833  *                                                                         *
   2834  * WARNINGS:                                                               *
   2835  *                                                                         *
   2836  * HISTORY:                                                                *
   2837  *   11/21/1995 BRR : Created.                                             *
   2838  *=========================================================================*/
   2839 static int Execute_DoList(int , HousesType ,
   2840 	ConnManClass *net, TCountDownTimerClass *,
   2841 	long *their_frame, unsigned short *their_sent, unsigned short *their_recv)
   2842 {
   2843 	int i,j,k,wibble;
   2844 	int index;
   2845 
   2846 	//------------------------------------------------------------------------
   2847 	// For a single-player game, just execute all events in the queue.
   2848 	//------------------------------------------------------------------------
   2849 	if (GameToPlay == GAME_NORMAL) {
   2850 		for (i = 0; i < DoList.Count; i++) {
   2851 			if ((unsigned)Frame >= DoList[i].Frame && !DoList[i].IsExecuted) {
   2852 				DoList[i].Execute();				// execute it
   2853 				DoList[i].IsExecuted = true;	// mark as having been executed
   2854 			}
   2855 		}
   2856 		return (1);
   2857 	}
   2858 
   2859 //#if(TIMING_FIX)
   2860 	//
   2861 	// If MPlayerMaxAhead is recomputed such that it increases, the systems
   2862 	// may try to free-run to the new MaxAhead value.  If so, they may miss
   2863 	// an event that was generated after the TIMING event was created, but
   2864 	// before it executed; this event will be scheduled with the older,
   2865 	// shorter MaxAhead value.  If a system doesn't receive this event, it
   2866 	// may execute past the frame it's scheduled to execute on, creating
   2867 	// a Packet-Recieved-Too-Late error.  To prevent this, find any events
   2868 	// that are scheduled to execute during this "period of vulnerability",
   2869 	// and re-schedule for the end of that period.
   2870 	//
   2871 	if (CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) {
   2872 		for (j = 0; j < DoList.Count; j++) {
   2873 			if (DoList[j].Type != EventClass::FRAMEINFO &&
   2874 				DoList[j].Frame > (unsigned)NewMaxAheadFrame1 &&
   2875 				DoList[j].Frame < (unsigned)NewMaxAheadFrame2) {
   2876 				DoList[j].Frame = (unsigned)NewMaxAheadFrame2;
   2877 			}
   2878 		}
   2879 	}
   2880 //#endif
   2881 
   2882 	//------------------------------------------------------------------------
   2883 	//	Execute the DoList.  Events must be executed in the same order on all
   2884 	//	systems; so, execute them in the order of the MPlayerID array.  This
   2885 	//	array is stored in the same order on all systems.
   2886 	//------------------------------------------------------------------------
   2887 	for (i = 0; i < MPlayerCount; i++) {
   2888 
   2889 		HousesType house;
   2890 		HouseClass *housep;
   2891 
   2892 		house = MPlayerHouses [i];
   2893 		housep= HouseClass::As_Pointer (house);
   2894 
   2895 		//.....................................................................
   2896 		// If for some reason this house doesn't exist, skip it.
   2897 		// Also, if this house has exited the game, skip it.  (The user can
   2898 		// generate events after he exits, because the exit event is scheduled
   2899 		// at least FrameSendRate*3 frames ahead.  If one system gets these
   2900 		// packets & another system doesn't, they'll go out of sync because
   2901 		// they aren't checking the CommandCount for that house, since that
   2902 		// house isn't connected any more.)
   2903 		//.....................................................................
   2904 		if (!housep){
   2905 			continue;
   2906 		}
   2907 
   2908 		if (!housep->IsHuman){
   2909 			continue;
   2910 		}
   2911 
   2912 		//.....................................................................
   2913 		//	Loop through all events
   2914 		//.....................................................................
   2915 		for (j = 0; j < DoList.Count; j++) {
   2916 
   2917 #ifndef DEMO
   2918 			if (net)
   2919 				Update_Queue_Mono (net, 6);
   2920 #endif	//DEMO
   2921 
   2922 			//..................................................................
   2923 			//	If this event was from the currently-executing player ID, and it's
   2924 			//	time to execute it, execute it.
   2925 			//..................................................................
   2926 			if (DoList[j].MPlayerID == MPlayerID[i] && (unsigned)Frame >= DoList[j].Frame &&
   2927 				!DoList[j].IsExecuted) {
   2928 
   2929 				//...............................................................
   2930 				//	Error if it's too late to execute this packet!
   2931 				//...............................................................
   2932 				if ((unsigned)Frame > DoList[j].Frame && DoList[j].Type !=
   2933 					EventClass::FRAMEINFO) {
   2934 #ifndef DEMO
   2935 					Dump_Packet_Too_Late_Stuff(&DoList[j]);
   2936 #endif	//DEMO
   2937 					CCMessageBox().Process (TXT_PACKET_TOO_LATE);
   2938 					return (0);
   2939 				}
   2940 
   2941 				//...............................................................
   2942 				//	Only execute EXIT & OPTIONS commands if they're from myself.
   2943 				//...............................................................
   2944 				if (DoList[j].Type==EventClass::EXIT ||
   2945 						DoList[j].Type==EventClass::OPTIONS) {
   2946 
   2947 
   2948 					if (DoList[j].Type==EventClass::EXIT){
   2949 CCDebugString ("C&C95 -  Received EXIT packet\n");
   2950 
   2951 						/*
   2952 						** Flag that this house lost because it quit. ST - 6/5/96 0:29AM
   2953 						*/
   2954 						for (wibble = 0; wibble < MPlayerCount; wibble++) {
   2955 							if (MPlayerID[wibble] == DoList[j].MPlayerID) {
   2956 								house = MPlayerHouses[wibble];
   2957 								housep = HouseClass::As_Pointer (house);
   2958 								housep->IGaveUp = true;
   2959 								break;
   2960 							}
   2961 						}
   2962 
   2963 						/*
   2964 						** Send the game statistics packet now since the game is effectivly over
   2965 						*/
   2966 						if (MPlayerCount == 2 &&
   2967 								GameToPlay == GAME_INTERNET &&
   2968 								!GameStatisticsPacketSent){
   2969 							Register_Game_End_Time();
   2970 							Send_Statistics_Packet();
   2971 						}
   2972 					}
   2973 
   2974 
   2975 
   2976 					if (DoList[j].ID == Houses.ID(PlayerPtr)) {
   2977 						DoList[j].Execute();
   2978 					}
   2979 
   2980 					//............................................................
   2981 					//	If this EXIT event isn't from myself, destroy the connection
   2982 					//	for that player.  The HousesType for this event is the
   2983 					// connection ID.
   2984 					//............................................................
   2985 					else if (DoList[j].Type==EventClass::EXIT) {
   2986 						if (GameToPlay == GAME_MODEM
   2987 							|| GameToPlay == GAME_NULL_MODEM){
   2988 							//|| GameToPlay == GAME_INTERNET) {
   2989 							
   2990 							//ST - 1/2/2019 5:29PM
   2991 							//Destroy_Null_Connection( DoList[j].MPlayerID, 0 );
   2992 						}
   2993 
   2994 						else if ((GameToPlay == GAME_IPX || GameToPlay == GAME_INTERNET) && net) {
   2995 							index = net->Connection_Index (DoList[j].MPlayerID);
   2996 							if (index != -1) {
   2997 								for (k = index; k < net->Num_Connections() - 1; k++) {
   2998 									their_frame[k] = their_frame[k+1];
   2999 									their_sent[k] = their_sent[k+1];
   3000 									their_recv[k] = their_recv[k+1];
   3001 								}
   3002 								CCDebugString ("C&C95 = Destroying connection due to exit event\n");
   3003 #ifndef DEMO
   3004 								Destroy_Connection(DoList[j].MPlayerID,0);
   3005 #endif	//DEMO
   3006 							}
   3007 						}
   3008 					}
   3009 				}
   3010 
   3011 				//...............................................................
   3012 				//	For a FRAMEINFO event, check the CRC value.
   3013 				// This could be an old FRAMEINFO packet that was floating around
   3014 				// for awhile and just arrived; if so, its Frame value will be
   3015 				// old.  Ignore these packets.  (This created bogus sync bugs on
   3016 				// the Internet, when packets that were 35 frames old arrived.)
   3017 				//...............................................................
   3018 #ifndef DEMO
   3019 				else if (DoList[j].Type == EventClass::FRAMEINFO) {
   3020 					if (DoList[j].Frame == Frame &&
   3021 						DoList[j].Data.FrameInfo.Delay < 32) {
   3022 						index = ((DoList[j].Frame - DoList[j].Data.FrameInfo.Delay) &
   3023 							0x001f);
   3024 						if (CRC[index] != DoList[j].Data.FrameInfo.CRC) {
   3025 							Print_CRCs(&DoList[j]);
   3026 							if (CCMessageBox().Process (TXT_OUT_OF_SYNC,
   3027 								TXT_CONTINUE, TXT_STOP) == 0) {
   3028 								if (GameToPlay == GAME_MODEM ||
   3029 									GameToPlay == GAME_NULL_MODEM){
   3030 #if (0)//ST - 1/2/2019 5:29PM										
   3031 									Destroy_Null_Connection( DoList[j].MPlayerID, -1 );
   3032 									Shutdown_Modem();
   3033 #endif
   3034 									GameToPlay = GAME_NORMAL;
   3035 								}
   3036 								else if ((GameToPlay == GAME_IPX || GameToPlay == GAME_INTERNET) && net) {
   3037 									CCDebugString ("C&C95 = Destroying connections due to bad frame info packet\n");
   3038 									while (net->Num_Connections()) {
   3039 										Destroy_Connection (net->Connection_ID(0), -1);
   3040 									}
   3041 								}
   3042 								Map.Flag_To_Redraw(true);
   3043 							}
   3044 							else {
   3045 								return (0);
   3046 							}
   3047 							return (1);
   3048 						}
   3049 					}
   3050 				}
   3051 #endif	//DEMO
   3052 				//...............................................................
   3053 				//	Execute other commands
   3054 				//...............................................................
   3055 				else {
   3056 					DoList[j].Execute();
   3057 				}
   3058 
   3059 				//...............................................................
   3060 				//	Mark this event as executed.
   3061 				//...............................................................
   3062 				DoList[j].IsExecuted = 1;
   3063 			}
   3064 		}
   3065 	}
   3066 
   3067 	return (1);
   3068 
   3069 }	// end of Execute_DoList
   3070 
   3071 
   3072 /***************************************************************************
   3073  * Clean_DoList -- Cleans out old events from the DoList                   *
   3074  *                                                                         *
   3075  * Currently, an event can only be removed from the DoList if it's at the	*
   3076  * head of the list; and event can't be removed from the middle.  So,		*
   3077  * this routine loops as long as the next event in the DoList has been		*
   3078  * executed, it's removed.																	*
   3079  *                                                                         *
   3080  * INPUT:                                                                  *
   3081  *		net		ptr to connection manager; ignored if NULL						*
   3082  *                                                                         *
   3083  * OUTPUT:                                                                 *
   3084  *		none.																						*
   3085  *                                                                         *
   3086  * WARNINGS:                                                               *
   3087  *		none.																						*
   3088  *                                                                         *
   3089  * HISTORY:                                                                *
   3090  *   11/21/1995 BRR : Created.                                             *
   3091  *=========================================================================*/
   3092 static void Clean_DoList(ConnManClass *net)
   3093 {
   3094 	while (DoList.Count) {
   3095 
   3096 #ifndef DEMO
   3097 		if (net)
   3098 			Update_Queue_Mono (net, 7);
   3099 #else
   3100 		net = net;
   3101 #endif	//DEMO
   3102 
   3103 		//.....................................................................
   3104 		//	Discard events that have been executed, OR it's too late to execute.
   3105 		//	(This happens if another player exits the game; he'll leave FRAMEINFO
   3106 		//	events lying around in my queue.  They won't have been "executed",
   3107 		//	because his IPX connection was destroyed.)
   3108 		//.....................................................................
   3109 		if ( (DoList.First().IsExecuted) || ((unsigned)Frame > DoList.First().Frame) ) {
   3110 			DoList.Next();
   3111 		}
   3112 		else {
   3113 			break;
   3114 		}
   3115 	}
   3116 
   3117 }	// end of Clean_DoList
   3118 
   3119 #ifndef DEMO
   3120 
   3121 /***************************************************************************
   3122  * Queue_Record -- Records the DoList to disk                              *
   3123  *                                                                         *
   3124  * This routine just saves any events in the DoList to disk; we can later	*
   3125  * "play back" the recording just be pulling events from disk rather than	*
   3126  * from the network!																			*
   3127  *                                                                         *
   3128  * INPUT:                                                                  *
   3129  *		none.																						*
   3130  *                                                                         *
   3131  * OUTPUT:                                                                 *
   3132  *		none.																						*
   3133  *                                                                         *
   3134  * WARNINGS:                                                               *
   3135  *		none.																						*
   3136  *                                                                         *
   3137  * HISTORY:                                                                *
   3138  *   08/14/1995 BRR : Created.                                             *
   3139  *=========================================================================*/
   3140 static void Queue_Record(void)
   3141 {
   3142 	int i,j;
   3143 
   3144 	//------------------------------------------------------------------------
   3145 	//	Compute # of events to save this frame
   3146 	//------------------------------------------------------------------------
   3147 	j = 0;
   3148 	for (i = 0; i < DoList.Count; i++) {
   3149 		if (Frame == DoList[i].Frame && !DoList[i].IsExecuted) {
   3150 			j++;
   3151 		}
   3152 	}
   3153 
   3154 	//------------------------------------------------------------------------
   3155 	//	Save the # of events, then all events.
   3156 	//------------------------------------------------------------------------
   3157 	RecordFile.Write (&j,sizeof(j));
   3158 	for (i = 0; i < DoList.Count; i++) {
   3159 		if (Frame == DoList[i].Frame && !DoList[i].IsExecuted) {
   3160 			RecordFile.Write (&DoList[i],sizeof (EventClass));
   3161 			j--;
   3162 		}
   3163 	}
   3164 
   3165 }	/* end of Queue_Record */
   3166 
   3167 
   3168 /***************************************************************************
   3169  * Queue_Playback -- plays back queue entries from a record file           *
   3170  *                                                                         *
   3171  * This routine reads events from disk, putting them into the DoList;		*
   3172  * it then executes the DoList just like the network version does.  The		*
   3173  * result is that the game "plays back" like a recording.						*
   3174  *																									*
   3175  * This routine detects mouse motion and stops playback, so it can work		*
   3176  * like an "attract" mode, showing a demo of the game itself.					*
   3177  *                                                                         *
   3178  * INPUT:                                                                  *
   3179  *		none.																						*
   3180  *                                                                         *
   3181  * OUTPUT:                                                                 *
   3182  *		none.																						*
   3183  *                                                                         *
   3184  * WARNINGS:                                                               *
   3185  *		none.																						*
   3186  *                                                                         *
   3187  * HISTORY:                                                                *
   3188  *   05/15/1995 BRR : Created.                                             *
   3189  *=========================================================================*/
   3190 static void Queue_Playback(void)
   3191 {
   3192 	int numevents;
   3193 	EventClass event;
   3194 	int i;
   3195 	int ok;
   3196   	static int mx,my;
   3197 	int max_houses;
   3198 	HousesType base_house;
   3199 	int key;
   3200 	int testframe;
   3201 
   3202 	//------------------------------------------------------------------------
   3203 	//	If the user hits ESC, stop the playback
   3204 	//------------------------------------------------------------------------
   3205 	if (Check_Key_Num()) {
   3206 		key = Get_Key();
   3207 		//
   3208 		// If the user hit ESC, end the recording.  If this is an Attract-mode
   3209 		// recording, end it no matter what the user does (any key or mouse).
   3210 		//
   3211 		if (key == KA_ESC || AllowAttract) {
   3212 			GameActive = 0;
   3213 			return;
   3214 		}
   3215 	}
   3216 
   3217 	//------------------------------------------------------------------------
   3218 	// If we're in "Attact" mode, and the user moves the mouse, stop the
   3219 	// playback.
   3220 	//------------------------------------------------------------------------
   3221 	if (AllowAttract && Frame > 0 &&
   3222 		(mx != Get_Mouse_X() || my != Get_Mouse_Y())) {
   3223 		GameActive = 0;
   3224 		return;
   3225 	}
   3226 	mx = Get_Mouse_X();
   3227 	my = Get_Mouse_Y();
   3228 
   3229 	//------------------------------------------------------------------------
   3230 	//	Compute the Game's CRC
   3231 	//------------------------------------------------------------------------
   3232 	Compute_Game_CRC();
   3233 	CRC[Frame & 0x001f] = GameCRC;
   3234 
   3235 	//------------------------------------------------------------------------
   3236 	//	Don't read anything the first time through (since the Queue_AI_Network
   3237 	//	routine didn't write anything the first time through); do this after the
   3238 	//	CRC is computed, since we'll still need a CRC for Frame 0.
   3239 	//------------------------------------------------------------------------
   3240 	if (Frame==0 && GameToPlay!=GAME_NORMAL) {
   3241 		return;
   3242 	}
   3243 
   3244 	//------------------------------------------------------------------------
   3245 	// Only process every 'FrameSendRate' frames
   3246 	//------------------------------------------------------------------------
   3247 	testframe = ((Frame + (FrameSendRate - 1)) / FrameSendRate) * FrameSendRate;
   3248 
   3249 	if (GameToPlay != GAME_NORMAL &&
   3250 		CommProtocol == COMM_PROTOCOL_MULTI_E_COMP) {
   3251 		if (Frame != testframe) {
   3252 			return;
   3253 		}
   3254 	}
   3255 
   3256 	//------------------------------------------------------------------------
   3257 	//	Read the DoList from disk
   3258 	//------------------------------------------------------------------------
   3259 	ok = 1;
   3260 	if (RecordFile.Read (&numevents, sizeof(numevents)) ==
   3261 		sizeof(numevents)) {
   3262 		for (i = 0; i < numevents; i++) {
   3263 			if (RecordFile.Read (&event, sizeof(EventClass)) ==
   3264 				sizeof(EventClass)) {
   3265 				event.IsExecuted = 0;
   3266 				DoList.Add (event);
   3267 			}
   3268 			else {
   3269 				ok = 0;
   3270 				break;
   3271 			}
   3272 		}
   3273 	}
   3274 	else {
   3275 		ok = 0;
   3276 	}
   3277 
   3278 	if (!ok) {
   3279 		GameActive = 0;
   3280 		return;
   3281 	}
   3282 
   3283 
   3284 	//------------------------------------------------------------------------
   3285 	// Execute the DoList; if an error occurs, bail out.
   3286 	//------------------------------------------------------------------------
   3287 	if (GameToPlay == GAME_NORMAL) {
   3288 		max_houses = 1;
   3289 		base_house = PlayerPtr->Class->House;
   3290 	}
   3291 	else {
   3292 		max_houses = MPlayerMax;
   3293 		base_house = HOUSE_MULTI1;
   3294 	}
   3295 	if (!Execute_DoList(max_houses, base_house, NULL, NULL, NULL, NULL, NULL)) {
   3296 		GameActive = 0;
   3297 		return;
   3298 	}
   3299 
   3300 	//------------------------------------------------------------------------
   3301 	//	Clean out the DoList
   3302 	//------------------------------------------------------------------------
   3303 	Clean_DoList(NULL);
   3304 
   3305 }	/* end of Queue_Playback */
   3306 
   3307 
   3308 /***************************************************************************
   3309  * Compute_Game_CRC -- Computes a CRC value of the entire game.				*
   3310  *                                                                         *
   3311  * INPUT:                                                                  *
   3312  *		none.																						*
   3313  *                                                                         *
   3314  * OUTPUT:                                                                 *
   3315  *		none.																						*
   3316  *                                                                         *
   3317  * WARNINGS:                                                               *
   3318  *		none.																						*
   3319  *                                                                         *
   3320  * HISTORY:                                                                *
   3321  *   05/09/1995 BRR : Created.                                             *
   3322  *=========================================================================*/
   3323 static void Compute_Game_CRC(void)
   3324 {
   3325 	int i;
   3326 	InfantryClass *infp;
   3327 	UnitClass *unitp;
   3328 	BuildingClass *bldgp;
   3329 	//ObjectClass *objp;
   3330 
   3331 	GameCRC = 0;
   3332 
   3333 	//------------------------------------------------------------------------
   3334 	//	Infantry
   3335 	//------------------------------------------------------------------------
   3336 	for (i = 0; i < Infantry.Count(); i++) {
   3337 		infp = (InfantryClass *)Infantry.Active_Ptr(i);
   3338 		Add_CRC (&GameCRC, (int)infp->Coord + (int)infp->PrimaryFacing);
   3339 	}
   3340 
   3341 	//------------------------------------------------------------------------
   3342 	//	Units
   3343 	//------------------------------------------------------------------------
   3344 	for (i = 0; i < Units.Count(); i++) {
   3345 		unitp = (UnitClass *)Units.Active_Ptr(i);
   3346 		Add_CRC (&GameCRC, (int)unitp->Coord + (int)unitp->PrimaryFacing +
   3347 			(int)unitp->SecondaryFacing);
   3348 	}
   3349 
   3350 	//------------------------------------------------------------------------
   3351 	//	Buildings
   3352 	//------------------------------------------------------------------------
   3353 	for (i = 0; i < Buildings.Count(); i++) {
   3354 		bldgp = (BuildingClass *)Buildings.Active_Ptr(i);
   3355 		Add_CRC (&GameCRC, (int)bldgp->Coord + (int)bldgp->PrimaryFacing);
   3356 	}
   3357 
   3358 #if(0)
   3359 	//------------------------------------------------------------------------
   3360 	//	Map Layers
   3361 	//------------------------------------------------------------------------
   3362 	for (i = 0; i < LAYER_COUNT; i++) {
   3363 		for (j = 0; j < Map.Layer[i].Count(); j++) {
   3364 			objp = Map.Layer[i][j];
   3365 			Add_CRC (&GameCRC, (int)objp->Coord + (int)objp->What_Am_I());
   3366 		}
   3367 	}
   3368 
   3369 	//------------------------------------------------------------------------
   3370 	//	Logic Layers
   3371 	//------------------------------------------------------------------------
   3372 	for (i = 0; i < Logic.Count(); i++) {
   3373 		objp = Logic[i];
   3374 		Add_CRC (&GameCRC, (int)objp->Coord + (int)objp->What_Am_I());
   3375 	}
   3376 #endif
   3377 
   3378 	//------------------------------------------------------------------------
   3379 	//	A random #
   3380 	//------------------------------------------------------------------------
   3381 	Add_CRC(&GameCRC, rand());
   3382 
   3383 }	/* end of Compute_Game_CRC */
   3384 
   3385 
   3386 /***************************************************************************
   3387  * Add_CRC -- Adds a value to a CRC                                        *
   3388  *                                                                         *
   3389  * INPUT:                                                                  *
   3390  *		crc		ptr to crc																	*
   3391  *		val		value to add																*
   3392  *                                                                         *
   3393  * OUTPUT:                                                                 *
   3394  *		none																						*
   3395  *                                                                         *
   3396  * WARNINGS:                                                               *
   3397  *		none																						*
   3398  *                                                                         *
   3399  * HISTORY:                                                                *
   3400  *   05/09/1995 BRR : Created.                                             *
   3401  *=========================================================================*/
   3402 void Add_CRC(unsigned long *crc, unsigned long val)
   3403 {
   3404 	int hibit;
   3405 
   3406 	if ( (*crc) & 0x80000000) {
   3407 		hibit = 1;
   3408 	}
   3409 	else {
   3410 		hibit = 0;
   3411 	}
   3412 
   3413 	(*crc) <<= 1;
   3414 	(*crc) += val;
   3415 	(*crc) += hibit;
   3416 
   3417 }	/* end of Add_CRC */
   3418 
   3419 
   3420 /***************************************************************************
   3421  * Print_CRCs -- Prints a data file for finding Sync Bugs						*
   3422  *                                                                         *
   3423  * INPUT:                                                                  *
   3424  *		none.																						*
   3425  *                                                                         *
   3426  * OUTPUT:                                                                 *
   3427  *		none																						*
   3428  *                                                                         *
   3429  * WARNINGS:                                                               *
   3430  *		none																						*
   3431  *                                                                         *
   3432  * HISTORY:                                                                *
   3433  *   05/09/1995 BRR : Created.                                             *
   3434  *=========================================================================*/
   3435 void Print_CRCs(EventClass *ev)
   3436 {
   3437 	ev=ev;
   3438 
   3439 	int i;	//,j;
   3440 	InfantryClass *infp;
   3441 	UnitClass *unitp;
   3442 	BuildingClass *bldgp;
   3443 	//ObjectClass *objp;
   3444 	FILE *fp;
   3445 	int rnd;
   3446 	HouseClass *housep;
   3447 	//HousesType house;
   3448 	int color;
   3449 
   3450 	Mono_Clear_Screen();
   3451 	Mono_Set_Cursor (0,0);
   3452 
   3453 	char filename[80];
   3454 	sprintf (filename, "CRC%02d.TXT", Frame & 0x1f);
   3455 
   3456 	fp = fopen(filename, "wt");	//"OUT.TXT","wt");
   3457 	if (fp==NULL) {
   3458 		return;
   3459 	}
   3460 
   3461 	for (i = 0; i < 32; i++) {
   3462 		fprintf(fp,"CRC[%d]=%x\n",i,CRC[i]);
   3463 	}
   3464 
   3465 
   3466 	housep = HouseClass::As_Pointer (HOUSE_MULTI1);
   3467 	if (housep) {
   3468 		color = housep->RemapColor;
   3469 		fprintf(fp,"Multi1: IsHuman:%d  Color:%s\n",housep->IsHuman,
   3470 			ColorNames[color]);
   3471 	}
   3472 
   3473 	housep = HouseClass::As_Pointer (HOUSE_MULTI2);
   3474 	if (housep) {
   3475 		color = housep->RemapColor;
   3476 		fprintf(fp,"Multi2: IsHuman:%d  Color:%s\n",housep->IsHuman,
   3477 			ColorNames[color]);
   3478 	}
   3479 
   3480 	housep = HouseClass::As_Pointer (HOUSE_MULTI3);
   3481 	if (housep) {
   3482 		color = housep->RemapColor;
   3483 		fprintf(fp,"Multi3: IsHuman:%d  Color:%s\n",housep->IsHuman,
   3484 			ColorNames[color]);
   3485 	}
   3486 
   3487 	housep = HouseClass::As_Pointer (HOUSE_MULTI4);
   3488 	if (housep) {
   3489 		color = housep->RemapColor;
   3490 		fprintf(fp,"Multi4: IsHuman:%d  Color:%s\n",housep->IsHuman,
   3491 			ColorNames[color]);
   3492 	}
   3493 
   3494 	housep = HouseClass::As_Pointer (HOUSE_MULTI5);
   3495 	if (housep) {
   3496 		color = housep->RemapColor;
   3497 		fprintf(fp,"Multi5: IsHuman:%d  Color:%s\n",housep->IsHuman,
   3498 			ColorNames[color]);
   3499 	}
   3500 
   3501 	housep = HouseClass::As_Pointer (HOUSE_MULTI6);
   3502 	if (housep) {
   3503 		color = housep->RemapColor;
   3504 		fprintf(fp,"Multi6: IsHuman:%d  Color:%s\n",housep->IsHuman,
   3505 			ColorNames[color]);
   3506 	}
   3507 
   3508 
   3509 	//------------------------------------------------------------------------
   3510 	//	Multi1 Infantry
   3511 	//------------------------------------------------------------------------
   3512 	if (HouseClass::As_Pointer(HOUSE_MULTI1)) {
   3513 		GameCRC = 0;
   3514 		fprintf(fp,"-------------------- MULTI1 INFANTRY -------------------\n");
   3515 		for (i = 0; i < Infantry.Count(); i++) {
   3516 			infp = (InfantryClass *)Infantry.Active_Ptr(i);
   3517 			if (infp->Owner()==HOUSE_MULTI1) {
   3518 				Add_CRC (&GameCRC, (int)infp->Coord + (int)infp->PrimaryFacing);
   3519 				fprintf(fp,"COORD:%x   Facing:%d   Mission:%d   Type:%d\n",
   3520 					infp->Coord,(int)infp->PrimaryFacing,infp->Get_Mission(),
   3521 					infp->Class->Type);
   3522 			}
   3523 		}
   3524 		Mono_Printf("Multi1 Infantry:%d\n",GameCRC);
   3525 	}
   3526 
   3527 	//------------------------------------------------------------------------
   3528 	//	Multi2 Infantry
   3529 	//------------------------------------------------------------------------
   3530 	if (HouseClass::As_Pointer(HOUSE_MULTI2)) {
   3531 		GameCRC = 0;
   3532 		fprintf(fp,"-------------------- MULTI2 INFANTRY -------------------\n");
   3533 		for (i = 0; i < Infantry.Count(); i++) {
   3534 			infp = (InfantryClass *)Infantry.Active_Ptr(i);
   3535 			if (infp->Owner()==HOUSE_MULTI2) {
   3536 				Add_CRC (&GameCRC, (int)infp->Coord + (int)infp->PrimaryFacing);
   3537 				fprintf(fp,"COORD:%x   Facing:%d   Mission:%d   Type:%d\n",
   3538 					infp->Coord,(int)infp->PrimaryFacing,infp->Get_Mission(),
   3539 					infp->Class->Type);
   3540 			}
   3541 		}
   3542 		Mono_Printf("Multi2 Infantry:%d\n",GameCRC);
   3543 	}
   3544 
   3545 	//------------------------------------------------------------------------
   3546 	//	Multi3 Infantry
   3547 	//------------------------------------------------------------------------
   3548 	if (HouseClass::As_Pointer(HOUSE_MULTI3)) {
   3549 		GameCRC = 0;
   3550 		fprintf(fp,"-------------------- MULTI3 INFANTRY -------------------\n");
   3551 		for (i = 0; i < Infantry.Count(); i++) {
   3552 			infp = (InfantryClass *)Infantry.Active_Ptr(i);
   3553 			if (infp->Owner()==HOUSE_MULTI3) {
   3554 				Add_CRC (&GameCRC, (int)infp->Coord + (int)infp->PrimaryFacing);
   3555 				fprintf(fp,"COORD:%x   Facing:%d   Mission:%d   Type:%d\n",
   3556 					infp->Coord,(int)infp->PrimaryFacing,infp->Get_Mission(),
   3557 					infp->Class->Type);
   3558 			}
   3559 		}
   3560 		Mono_Printf("Multi3 Infantry:%d\n",GameCRC);
   3561 	}
   3562 
   3563 	//------------------------------------------------------------------------
   3564 	//	Multi4 Infantry
   3565 	//------------------------------------------------------------------------
   3566 	if (HouseClass::As_Pointer(HOUSE_MULTI4)) {
   3567 		GameCRC = 0;
   3568 		fprintf(fp,"-------------------- MULTI4 INFANTRY -------------------\n");
   3569 		for (i = 0; i < Infantry.Count(); i++) {
   3570 			infp = (InfantryClass *)Infantry.Active_Ptr(i);
   3571 			if (infp->Owner()==HOUSE_MULTI4) {
   3572 				Add_CRC (&GameCRC, (int)infp->Coord + (int)infp->PrimaryFacing);
   3573 				fprintf(fp,"COORD:%x   Facing:%d   Mission:%d   Type:%d\n",
   3574 					infp->Coord,(int)infp->PrimaryFacing,infp->Get_Mission(),
   3575 					infp->Class->Type);
   3576 			}
   3577 		}
   3578 		Mono_Printf("Multi4 Infantry:%d\n",GameCRC);
   3579 	}
   3580 
   3581 	//------------------------------------------------------------------------
   3582 	//	Multi5 Infantry
   3583 	//------------------------------------------------------------------------
   3584 	if (HouseClass::As_Pointer(HOUSE_MULTI5)) {
   3585 		GameCRC = 0;
   3586 		fprintf(fp,"-------------------- MULTI5 INFANTRY -------------------\n");
   3587 		for (i = 0; i < Infantry.Count(); i++) {
   3588 			infp = (InfantryClass *)Infantry.Active_Ptr(i);
   3589 			if (infp->Owner()==HOUSE_MULTI5) {
   3590 				Add_CRC (&GameCRC, (int)infp->Coord + (int)infp->PrimaryFacing);
   3591 				fprintf(fp,"COORD:%x   Facing:%d   Mission:%d   Type:%d\n",
   3592 					infp->Coord,(int)infp->PrimaryFacing,infp->Get_Mission(),
   3593 					infp->Class->Type);
   3594 			}
   3595 		}
   3596 		Mono_Printf("Multi5 Infantry:%d\n",GameCRC);
   3597 	}
   3598 
   3599 	//------------------------------------------------------------------------
   3600 	//	Multi6 Infantry
   3601 	//------------------------------------------------------------------------
   3602 	if (HouseClass::As_Pointer(HOUSE_MULTI6)) {
   3603 		GameCRC = 0;
   3604 		fprintf(fp,"-------------------- MULTI6 INFANTRY -------------------\n");
   3605 		for (i = 0; i < Infantry.Count(); i++) {
   3606 			infp = (InfantryClass *)Infantry.Active_Ptr(i);
   3607 			if (infp->Owner()==HOUSE_MULTI6) {
   3608 				Add_CRC (&GameCRC, (int)infp->Coord + (int)infp->PrimaryFacing);
   3609 				fprintf(fp,"COORD:%x   Facing:%d   Mission:%d   Type:%d\n",
   3610 					infp->Coord,(int)infp->PrimaryFacing,infp->Get_Mission(),
   3611 					infp->Class->Type);
   3612 			}
   3613 		}
   3614 		Mono_Printf("Multi6 Infantry:%d\n",GameCRC);
   3615 	}
   3616 
   3617 	//------------------------------------------------------------------------
   3618 	//	Multi1 Units
   3619 	//------------------------------------------------------------------------
   3620 	if (HouseClass::As_Pointer(HOUSE_MULTI1)) {
   3621 		GameCRC = 0;
   3622 		fprintf(fp,"-------------------- MULTI1 UNITS -------------------\n");
   3623 		for (i = 0; i < Units.Count(); i++) {
   3624 			unitp = (UnitClass *)Units.Active_Ptr(i);
   3625 			if (unitp->Owner()==HOUSE_MULTI1) {
   3626 				Add_CRC (&GameCRC, (int)unitp->Coord + (int)unitp->PrimaryFacing);
   3627 				fprintf(fp,
   3628 					"COORD:%x   Facing:%d   Facing2:%d   Mission:%d   Type:%d\n",
   3629 					unitp->Coord,(int)unitp->PrimaryFacing,
   3630 					(int)unitp->SecondaryFacing,unitp->Get_Mission(),
   3631 					unitp->Class->Type);
   3632 			}
   3633 		}
   3634 		Mono_Printf("Multi1 Units:%d\n",GameCRC);
   3635 	}
   3636 
   3637 	//------------------------------------------------------------------------
   3638 	//	Multi2 Units
   3639 	//------------------------------------------------------------------------
   3640 	if (HouseClass::As_Pointer(HOUSE_MULTI2)) {
   3641 		GameCRC = 0;
   3642 		fprintf(fp,"-------------------- MULTI2 UNITS -------------------\n");
   3643 		for (i = 0; i < Units.Count(); i++) {
   3644 			unitp = (UnitClass *)Units.Active_Ptr(i);
   3645 			if (unitp->Owner()==HOUSE_MULTI2) {
   3646 				Add_CRC (&GameCRC, (int)unitp->Coord + (int)unitp->PrimaryFacing);
   3647 				fprintf(fp,
   3648 					"COORD:%x   Facing:%d   Facing2:%d   Mission:%d   Type:%d\n",
   3649 					unitp->Coord,(int)unitp->PrimaryFacing,
   3650 					(int)unitp->SecondaryFacing,unitp->Get_Mission(),
   3651 					unitp->Class->Type);
   3652 			}
   3653 		}
   3654 		Mono_Printf("Multi2 Units:%d\n",GameCRC);
   3655 	}
   3656 
   3657 	//------------------------------------------------------------------------
   3658 	//	Multi3 Units
   3659 	//------------------------------------------------------------------------
   3660 	if (HouseClass::As_Pointer(HOUSE_MULTI3)) {
   3661 		GameCRC = 0;
   3662 		fprintf(fp,"-------------------- MULTI3 UNITS -------------------\n");
   3663 		for (i = 0; i < Units.Count(); i++) {
   3664 			unitp = (UnitClass *)Units.Active_Ptr(i);
   3665 			if (unitp->Owner()==HOUSE_MULTI3) {
   3666 				Add_CRC (&GameCRC, (int)unitp->Coord + (int)unitp->PrimaryFacing);
   3667 				fprintf(fp,
   3668 					"COORD:%x   Facing:%d   Facing2:%d   Mission:%d   Type:%d\n",
   3669 					unitp->Coord,(int)unitp->PrimaryFacing,
   3670 					(int)unitp->SecondaryFacing,unitp->Get_Mission(),
   3671 					unitp->Class->Type);
   3672 			}
   3673 		}
   3674 		Mono_Printf("Multi3 Units:%d\n",GameCRC);
   3675 	}
   3676 
   3677 	//------------------------------------------------------------------------
   3678 	//	Multi4 Units
   3679 	//------------------------------------------------------------------------
   3680 	if (HouseClass::As_Pointer(HOUSE_MULTI4)) {
   3681 		GameCRC = 0;
   3682 		fprintf(fp,"-------------------- MULTI4 UNITS -------------------\n");
   3683 		for (i = 0; i < Units.Count(); i++) {
   3684 			unitp = (UnitClass *)Units.Active_Ptr(i);
   3685 			if (unitp->Owner()==HOUSE_MULTI4) {
   3686 				Add_CRC (&GameCRC, (int)unitp->Coord + (int)unitp->PrimaryFacing);
   3687 				fprintf(fp,
   3688 					"COORD:%x   Facing:%d   Facing2:%d   Mission:%d   Type:%d\n",
   3689 					unitp->Coord,(int)unitp->PrimaryFacing,
   3690 					(int)unitp->SecondaryFacing,unitp->Get_Mission(),
   3691 					unitp->Class->Type);
   3692 			}
   3693 		}
   3694 		Mono_Printf("Multi4 Units:%d\n",GameCRC);
   3695 	}
   3696 
   3697 	//------------------------------------------------------------------------
   3698 	//	Multi5 Units
   3699 	//------------------------------------------------------------------------
   3700 	if (HouseClass::As_Pointer(HOUSE_MULTI5)) {
   3701 		GameCRC = 0;
   3702 		fprintf(fp,"-------------------- MULTI5 UNITS -------------------\n");
   3703 		for (i = 0; i < Units.Count(); i++) {
   3704 			unitp = (UnitClass *)Units.Active_Ptr(i);
   3705 			if (unitp->Owner()==HOUSE_MULTI5) {
   3706 				Add_CRC (&GameCRC, (int)unitp->Coord + (int)unitp->PrimaryFacing);
   3707 				fprintf(fp,
   3708 					"COORD:%x   Facing:%d   Facing2:%d   Mission:%d   Type:%d\n",
   3709 					unitp->Coord,(int)unitp->PrimaryFacing,
   3710 					(int)unitp->SecondaryFacing,unitp->Get_Mission(),
   3711 					unitp->Class->Type);
   3712 			}
   3713 		}
   3714 		Mono_Printf("Multi5 Units:%d\n",GameCRC);
   3715 	}
   3716 
   3717 	//------------------------------------------------------------------------
   3718 	//	Multi6 Units
   3719 	//------------------------------------------------------------------------
   3720 	if (HouseClass::As_Pointer(HOUSE_MULTI6)) {
   3721 		GameCRC = 0;
   3722 		fprintf(fp,"-------------------- MULTI6 UNITS -------------------\n");
   3723 		for (i = 0; i < Units.Count(); i++) {
   3724 			unitp = (UnitClass *)Units.Active_Ptr(i);
   3725 			if (unitp->Owner()==HOUSE_MULTI6) {
   3726 				Add_CRC (&GameCRC, (int)unitp->Coord + (int)unitp->PrimaryFacing);
   3727 				fprintf(fp,
   3728 					"COORD:%x   Facing:%d   Facing2:%d   Mission:%d   Type:%d\n",
   3729 					unitp->Coord,(int)unitp->PrimaryFacing,
   3730 					(int)unitp->SecondaryFacing,unitp->Get_Mission(),
   3731 					unitp->Class->Type);
   3732 			}
   3733 		}
   3734 		Mono_Printf("Multi6 Units:%d\n",GameCRC);
   3735 	}
   3736 
   3737 	//------------------------------------------------------------------------
   3738 	//	Multi1 Buildings
   3739 	//------------------------------------------------------------------------
   3740 	if (HouseClass::As_Pointer(HOUSE_MULTI1)) {
   3741 		GameCRC = 0;
   3742 		fprintf(fp,"-------------------- MULTI1 BUILDINGS -------------------\n");
   3743 		for (i = 0; i < Buildings.Count(); i++) {
   3744 			bldgp = (BuildingClass *)Buildings.Active_Ptr(i);
   3745 			if (bldgp->Owner()==HOUSE_MULTI1) {
   3746 				Add_CRC (&GameCRC, (int)bldgp->Coord + (int)bldgp->PrimaryFacing);
   3747 				fprintf(fp,"COORD:%x   Facing:%d   Mission:%d   Type:%d\n",
   3748 					bldgp->Coord,(int)bldgp->PrimaryFacing,bldgp->Get_Mission(),
   3749 					bldgp->Class->Type);
   3750 			}
   3751 		}
   3752 		Mono_Printf("Multi1 Buildings:%d\n",GameCRC);
   3753 	}
   3754 
   3755 	//------------------------------------------------------------------------
   3756 	//	Multi2 Buildings
   3757 	//------------------------------------------------------------------------
   3758 	if (HouseClass::As_Pointer(HOUSE_MULTI2)) {
   3759 		GameCRC = 0;
   3760 		fprintf(fp,"-------------------- MULTI2 BUILDINGS -------------------\n");
   3761 		for (i = 0; i < Buildings.Count(); i++) {
   3762 			bldgp = (BuildingClass *)Buildings.Active_Ptr(i);
   3763 			if (bldgp->Owner()==HOUSE_MULTI2) {
   3764 				Add_CRC (&GameCRC, (int)bldgp->Coord + (int)bldgp->PrimaryFacing);
   3765 				fprintf(fp,"COORD:%x   Facing:%d   Mission:%d   Type:%d\n",
   3766 					bldgp->Coord,(int)bldgp->PrimaryFacing,bldgp->Get_Mission(),
   3767 					bldgp->Class->Type);
   3768 			}
   3769 		}
   3770 		Mono_Printf("Multi2 Buildings:%d\n",GameCRC);
   3771 	}
   3772 
   3773 	//------------------------------------------------------------------------
   3774 	//	Multi3 Buildings
   3775 	//------------------------------------------------------------------------
   3776 	if (HouseClass::As_Pointer(HOUSE_MULTI3)) {
   3777 		GameCRC = 0;
   3778 		fprintf(fp,"-------------------- MULTI3 BUILDINGS -------------------\n");
   3779 		for (i = 0; i < Buildings.Count(); i++) {
   3780 			bldgp = (BuildingClass *)Buildings.Active_Ptr(i);
   3781 			if (bldgp->Owner()==HOUSE_MULTI3) {
   3782 				Add_CRC (&GameCRC, (int)bldgp->Coord + (int)bldgp->PrimaryFacing);
   3783 				fprintf(fp,"COORD:%x   Facing:%d   Mission:%d   Type:%d\n",
   3784 					bldgp->Coord,(int)bldgp->PrimaryFacing,bldgp->Get_Mission(),
   3785 					bldgp->Class->Type);
   3786 			}
   3787 		}
   3788 		Mono_Printf("Multi3 Buildings:%d\n",GameCRC);
   3789 	}
   3790 
   3791 	//------------------------------------------------------------------------
   3792 	//	Multi4 Buildings
   3793 	//------------------------------------------------------------------------
   3794 	if (HouseClass::As_Pointer(HOUSE_MULTI4)) {
   3795 		GameCRC = 0;
   3796 		fprintf(fp,"-------------------- MULTI4 BUILDINGS -------------------\n");
   3797 		for (i = 0; i < Buildings.Count(); i++) {
   3798 			bldgp = (BuildingClass *)Buildings.Active_Ptr(i);
   3799 			if (bldgp->Owner()==HOUSE_MULTI4) {
   3800 				Add_CRC (&GameCRC, (int)bldgp->Coord + (int)bldgp->PrimaryFacing);
   3801 				fprintf(fp,"COORD:%x   Facing:%d   Mission:%d   Type:%d\n",
   3802 					bldgp->Coord,(int)bldgp->PrimaryFacing,bldgp->Get_Mission(),
   3803 					bldgp->Class->Type);
   3804 			}
   3805 		}
   3806 		Mono_Printf("Multi4 Buildings:%d\n",GameCRC);
   3807 	}
   3808 
   3809 	//------------------------------------------------------------------------
   3810 	//	Multi5 Buildings
   3811 	//------------------------------------------------------------------------
   3812 	if (HouseClass::As_Pointer(HOUSE_MULTI5)) {
   3813 		GameCRC = 0;
   3814 		fprintf(fp,"-------------------- MULTI5 BUILDINGS -------------------\n");
   3815 		for (i = 0; i < Buildings.Count(); i++) {
   3816 			bldgp = (BuildingClass *)Buildings.Active_Ptr(i);
   3817 			if (bldgp->Owner()==HOUSE_MULTI5) {
   3818 				Add_CRC (&GameCRC, (int)bldgp->Coord + (int)bldgp->PrimaryFacing);
   3819 				fprintf(fp,"COORD:%x   Facing:%d   Mission:%d   Type:%d\n",
   3820 					bldgp->Coord,(int)bldgp->PrimaryFacing,bldgp->Get_Mission(),
   3821 					bldgp->Class->Type);
   3822 			}
   3823 		}
   3824 		Mono_Printf("Multi5 Buildings:%d\n",GameCRC);
   3825 	}
   3826 
   3827 	//------------------------------------------------------------------------
   3828 	//	Multi6 Buildings
   3829 	//------------------------------------------------------------------------
   3830 	if (HouseClass::As_Pointer(HOUSE_MULTI6)) {
   3831 		GameCRC = 0;
   3832 		fprintf(fp,"-------------------- MULTI6 BUILDINGS -------------------\n");
   3833 		for (i = 0; i < Buildings.Count(); i++) {
   3834 			bldgp = (BuildingClass *)Buildings.Active_Ptr(i);
   3835 			if (bldgp->Owner()==HOUSE_MULTI6) {
   3836 				Add_CRC (&GameCRC, (int)bldgp->Coord + (int)bldgp->PrimaryFacing);
   3837 				fprintf(fp,"COORD:%x   Facing:%d   Mission:%d   Type:%d\n",
   3838 					bldgp->Coord,(int)bldgp->PrimaryFacing,bldgp->Get_Mission(),
   3839 					bldgp->Class->Type);
   3840 			}
   3841 		}
   3842 		Mono_Printf("Multi6 Buildings:%d\n",GameCRC);
   3843 	}
   3844 #if (0)
   3845 	//------------------------------------------------------------------------
   3846 	//	Map Layers
   3847 	//------------------------------------------------------------------------
   3848 	GameCRC = 0;
   3849 	for (i = 0; i < LAYER_COUNT; i++) {
   3850 		fprintf(fp,">>>> MAP LAYER %d <<<<\n",i);
   3851 		for (j = 0; j < Map.Layer[i].Count(); j++) {
   3852 			objp = Map.Layer[i][j];
   3853 			Add_CRC (&GameCRC, (int)objp->Coord + (int)objp->What_Am_I());
   3854 			fprintf(fp,"Object %d: %x ",j,objp->Coord);
   3855 
   3856 			if (objp->What_Am_I() == RTTI_AIRCRAFT)
   3857 				fprintf(fp,"Aircraft  (Type:%d) ",
   3858 					(AircraftType)(*((AircraftClass *)objp)));
   3859 			else if (objp->What_Am_I() == RTTI_ANIM)
   3860 				fprintf(fp,"Anim      (Type:%d) ",
   3861 					(AnimType)(*((AnimClass *)objp)));
   3862 			else if (objp->What_Am_I() == RTTI_BUILDING)
   3863 				fprintf(fp,"Building  (Type:%d) ",
   3864 					(StructType)(*((BuildingClass *)objp)));
   3865 			else if (objp->What_Am_I() == RTTI_BULLET)
   3866 				fprintf(fp,"Bullet    (Type:%d) ",
   3867 					(BulletType)(*((BulletClass *)objp)));
   3868 			else if (objp->What_Am_I() == RTTI_INFANTRY)
   3869 				fprintf(fp,"Infantry  (Type:%d) ",
   3870 					(InfantryType)(*((InfantryClass *)objp)));
   3871 			else if (objp->What_Am_I() == RTTI_OVERLAY)
   3872 				fprintf(fp,"Overlay   (Type:%d) ",
   3873 					(OverlayType)(*((OverlayClass *)objp)));
   3874 			else if (objp->What_Am_I() == RTTI_SMUDGE)
   3875 				fprintf(fp,"Smudge    (Type:%d) ",
   3876 					(SmudgeType)(*((SmudgeClass *)objp)));
   3877 			else if (objp->What_Am_I() == RTTI_TEMPLATE)
   3878 				fprintf(fp,"Template  (Type:%d) ",
   3879 					(TemplateType)(*((TemplateClass *)objp)));
   3880 			else if (objp->What_Am_I() == RTTI_TERRAIN)
   3881 				fprintf(fp,"Terrain   (Type:%d) ",
   3882 					(TerrainType)(*((TerrainClass *)objp)));
   3883 			else if (objp->What_Am_I() == RTTI_UNIT)
   3884 				fprintf(fp,"Unit      (Type:%d) ",
   3885 					(UnitType)(*((UnitClass *)objp)));
   3886 
   3887 			house = objp->Owner();
   3888 			if (house!=HOUSE_NONE) {
   3889 				housep = HouseClass::As_Pointer (house);
   3890 				fprintf(fp,"Owner: %s\n",housep->Class->IniName);
   3891 			}
   3892 			else {
   3893 				fprintf(fp,"Owner: NONE\n");
   3894 			}
   3895 		}
   3896 	}
   3897 	Mono_Printf("Map Layers:%d\n",GameCRC);
   3898 
   3899 	//------------------------------------------------------------------------
   3900 	//	Logic Layers
   3901 	//------------------------------------------------------------------------
   3902 	GameCRC = 0;
   3903 	fprintf(fp,">>>> LOGIC LAYER <<<<\n");
   3904 	for (i = 0; i < Logic.Count(); i++) {
   3905 		objp = Logic[i];
   3906 		Add_CRC (&GameCRC, (int)objp->Coord + (int)objp->What_Am_I());
   3907 		fprintf(fp,"Object %d: %x ",i,objp->Coord);
   3908 
   3909 		if (objp->What_Am_I() == RTTI_AIRCRAFT)
   3910 			fprintf(fp,"Aircraft  (Type:%d) ",
   3911 				(AircraftType)(*((AircraftClass *)objp)));
   3912 		else if (objp->What_Am_I() == RTTI_ANIM)
   3913 			fprintf(fp,"Anim      (Type:%d) ",
   3914 				(AnimType)(*((AnimClass *)objp)));
   3915 		else if (objp->What_Am_I() == RTTI_BUILDING)
   3916 			fprintf(fp,"Building  (Type:%d) ",
   3917 				(StructType)(*((BuildingClass *)objp)));
   3918 		else if (objp->What_Am_I() == RTTI_BULLET)
   3919 			fprintf(fp,"Bullet    (Type:%d) ",
   3920 				(BulletType)(*((BulletClass *)objp)));
   3921 		else if (objp->What_Am_I() == RTTI_INFANTRY)
   3922 			fprintf(fp,"Infantry  (Type:%d) ",
   3923 				(InfantryType)(*((InfantryClass *)objp)));
   3924 		else if (objp->What_Am_I() == RTTI_OVERLAY)
   3925 			fprintf(fp,"Overlay   (Type:%d) ",
   3926 				(OverlayType)(*((OverlayClass *)objp)));
   3927 		else if (objp->What_Am_I() == RTTI_SMUDGE)
   3928 			fprintf(fp,"Smudge    (Type:%d) ",
   3929 				(SmudgeType)(*((SmudgeClass *)objp)));
   3930 		else if (objp->What_Am_I() == RTTI_TEMPLATE)
   3931 			fprintf(fp,"Template  (Type:%d) ",
   3932 				(TemplateType)(*((TemplateClass *)objp)));
   3933 		else if (objp->What_Am_I() == RTTI_TERRAIN)
   3934 			fprintf(fp,"Terrain   (Type:%d) ",
   3935 				(TerrainType)(*((TerrainClass *)objp)));
   3936 		else if (objp->What_Am_I() == RTTI_UNIT)
   3937 			fprintf(fp,"Unit      (Type:%d) ",
   3938 				(UnitType)(*((UnitClass *)objp)));
   3939 
   3940 		house = objp->Owner();
   3941 		if (house!=HOUSE_NONE) {
   3942 			housep = HouseClass::As_Pointer (house);
   3943 			fprintf(fp,"Owner: %s\n",housep->Class->IniName);
   3944 		}
   3945 		else {
   3946 			fprintf(fp,"Owner: NONE\n");
   3947 		}
   3948 	}
   3949 	Mono_Printf("Logic:%d\n",GameCRC);
   3950 #endif
   3951 
   3952 	//------------------------------------------------------------------------
   3953 	//	Random # generator, frame #
   3954 	//------------------------------------------------------------------------
   3955 	rnd = rand();
   3956 
   3957 	Mono_Printf("Random Number:%d\n",rnd);
   3958 	fprintf(fp,"\nRandom Number:%d\n",rnd);
   3959 
   3960 	Mono_Printf("My Frame:%d\n",Frame);
   3961 	fprintf(fp,"My Frame:%d\n",Frame);
   3962 #if (0)
   3963 	fprintf(fp,"-------------- Offending event: ----------------\n");
   3964 	fprintf(fp,"Type:         %d\n",ev->Type);
   3965 	fprintf(fp,"Frame:        %d\n",ev->Frame);
   3966 	fprintf(fp,"MPlayerID:    %04x\n",ev->MPlayerID);
   3967 	if (ev->Type == EventClass::FRAMEINFO) {
   3968 		fprintf(fp,"CRC:          %x\n",ev->Data.FrameInfo.CRC);
   3969 		fprintf(fp,"CommandCount: %x\n",ev->Data.FrameInfo.CommandCount);
   3970 		fprintf(fp,"Delay:        %x\n",ev->Data.FrameInfo.Delay);
   3971 	}
   3972 #endif
   3973 	fclose(fp);
   3974 
   3975 }	/* end of Print_CRCs */
   3976 
   3977 
   3978 /***************************************************************************
   3979  * Init_Queue_Mono -- inits mono display                                   *
   3980  *                                                                         *
   3981  * This routine steals control of the mono screen away from the rest of		*
   3982  * the engine, by setting the global IsMono; if IsMono is set, the other	*
   3983  * routines in this module turn off the Mono display when they're done		*
   3984  * with it, so the rest of the engine won't over-write what we're writing.	*
   3985  *                                                                         *
   3986  * INPUT:                                                                  *
   3987  *		net		ptr to connection manager												*
   3988  *                                                                         *
   3989  * OUTPUT:                                                                 *
   3990  *		none.																						*
   3991  *                                                                         *
   3992  * WARNINGS:                                                               *
   3993  *		none.																						*
   3994  *                                                                         *
   3995  * HISTORY:                                                                *
   3996  *   11/21/1995 BRR : Created.                                             *
   3997  *=========================================================================*/
   3998 static void Init_Queue_Mono(ConnManClass *net)
   3999 {
   4000 #if(SHOW_MONO)
   4001 	//------------------------------------------------------------------------
   4002 	// Set 'IsMono' so we can steal the mono screen from the engine
   4003 	//------------------------------------------------------------------------
   4004 	if (Frame==0 && MonoClass::Is_Enabled()) {
   4005 		IsMono = true;
   4006 	}
   4007 
   4008 	//------------------------------------------------------------------------
   4009 	// Enable mono output for our stuff; we must Disable it before we return
   4010 	// control to the engine.
   4011 	//------------------------------------------------------------------------
   4012 	if (IsMono)
   4013 		MonoClass::Enable();
   4014 
   4015 	if (net->Num_Connections() > 0) {
   4016 		//.....................................................................
   4017 		//	Network mono debugging screen
   4018 		//.....................................................................
   4019 		if (NetMonoMode==0) {
   4020 			if (Frame==0 || NewMonoMode) {
   4021 				net->Configure_Debug (0, sizeof (CommHeaderType),
   4022 					sizeof(EventClass::EventType), EventClass::EventNames, 0);
   4023 				net->Mono_Debug_Print (0,1);
   4024 				NewMonoMode = 0;
   4025 			}
   4026 			else {
   4027 				net->Mono_Debug_Print (0,0);
   4028 			}
   4029 		}
   4030 		//.....................................................................
   4031 		//	Flow control debugging output
   4032 		//.....................................................................
   4033 		else {
   4034 			if (NewMonoMode) {
   4035 				Mono_Clear_Screen();
   4036 				Mono_Printf("                         Queue AI:\n");	// flowcount[0]
   4037 				Mono_Printf("                Build Packet Loop:\n");	// flowcount[1]
   4038 				Mono_Printf("                       Frame Sync:\n");	// flowcount[2]
   4039 				Mono_Printf("                Frame Sync Resend:\n");	// flowcount[3]
   4040 				Mono_Printf("               Frame Sync Timeout:\n");	// flowcount[4]
   4041 				Mono_Printf("           Frame Sync New Message:\n");	// flowcount[5]
   4042 				Mono_Printf("                 DoList Execution:\n");	// flowcount[6]
   4043 				Mono_Printf("                  DoList Cleaning:\n");	// flowcount[7]
   4044 				Mono_Printf("\n");
   4045 				Mono_Printf("                            Frame:\n");
   4046 				Mono_Printf("                  MPlayerMaxAhead:\n");
   4047 				Mono_Printf("                       their_recv:\n");
   4048 				Mono_Printf("                       their_sent:\n");
   4049 				Mono_Printf("                          my_sent:\n");
   4050 				Mono_Printf("                 DesiredFrameRate:\n");
   4051 				NewMonoMode = 0;
   4052 			}
   4053 		}
   4054 	}
   4055 #else
   4056 	net = net;
   4057 #endif
   4058 }	// end of Init_Queue_Mono
   4059 
   4060 
   4061 /***************************************************************************
   4062  * Update_Queue_Mono -- updates mono display                               *
   4063  *                                                                         *
   4064  * INPUT:                                                                  *
   4065  *		net				ptr to connection manager										*
   4066  *		flow_index		index # for flow-count updates								*
   4067  *							-1: display															*
   4068  *                                                                         *
   4069  * OUTPUT:                                                                 *
   4070  *		none.																						*
   4071  *                                                                         *
   4072  * WARNINGS:                                                               *
   4073  *		none.																						*
   4074  *                                                                         *
   4075  * HISTORY:                                                                *
   4076  *   11/21/1995 BRR : Created.                                             *
   4077  *=========================================================================*/
   4078 static void Update_Queue_Mono(ConnManClass *net, int flow_index)
   4079 {
   4080 #if(SHOW_MONO)
   4081 	static int flowcount[20] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
   4082 
   4083 	//------------------------------------------------------------------------
   4084 	// If 'NetMonoMode' is 1, display flowcount info
   4085 	//------------------------------------------------------------------------
   4086 	if (NetMonoMode==1) {
   4087 		if (flow_index >= 0 && flow_index < 20) {
   4088 			Mono_Set_Cursor(35,flow_index);
   4089 			flowcount[flow_index]++;
   4090 			Mono_Printf("%d",flowcount[flow_index]);
   4091 		}
   4092 	}
   4093 	//------------------------------------------------------------------------
   4094 	// Otherwise, display the connection debug screen
   4095 	//------------------------------------------------------------------------
   4096 	else {
   4097 		net->Mono_Debug_Print (0,0);
   4098 	}
   4099 
   4100 #else
   4101 	flow_index = flow_index;
   4102 	net = net;
   4103 #endif
   4104 
   4105 }	// end of Update_Queue_Mono
   4106 
   4107 
   4108 /***************************************************************************
   4109  * Print_Framesync_Values -- displays frame-sync variables                 *
   4110  *                                                                         *
   4111  * INPUT:                                                                  *
   4112  *		curframe					current game Frame #										*
   4113  *		max_ahead				max-ahead value											*
   4114  *		num_connections		# connections												*
   4115  *		their_recv				# commands I've received from my connections		*
   4116  *		their_sent				# commands each connection claims to have sent	*
   4117  *		my_sent					# commands I've sent										*
   4118  *                                                                         *
   4119  * OUTPUT:                                                                 *
   4120  *		none.																						*
   4121  *                                                                         *
   4122  * WARNINGS:                                                               *
   4123  *		none.																						*
   4124  *                                                                         *
   4125  * HISTORY:                                                                *
   4126  *   11/21/1995 BRR : Created.                                             *
   4127  *=========================================================================*/
   4128 static void Print_Framesync_Values(long curframe, unsigned long max_ahead,
   4129 	int num_connections, unsigned short *their_recv,
   4130 	unsigned short *their_sent, unsigned short my_sent)
   4131 {
   4132 #if(SHOW_MONO)
   4133 	int i;
   4134 
   4135 	if (NetMonoMode==1) {
   4136 		Mono_Set_Cursor(35,9);
   4137 		Mono_Printf("%d",curframe);
   4138 
   4139 		Mono_Set_Cursor(35,10);
   4140 		Mono_Printf("%d",max_ahead);
   4141 
   4142 		for (i = 0; i < num_connections; i++) {
   4143 			Mono_Set_Cursor(35 + i*5,11);
   4144 			Mono_Printf("%4d",(int)their_recv[i]);
   4145 		}
   4146 
   4147 		for (i = 0; i < num_connections; i++) {
   4148 			Mono_Set_Cursor(35 + i*5,12);
   4149 			Mono_Printf("%4d",(int)their_sent[i]);
   4150 		}
   4151 
   4152 		Mono_Set_Cursor(35,13);
   4153 		Mono_Printf("%4d",(int)my_sent);
   4154 
   4155 		Mono_Set_Cursor(35,14);
   4156 		Mono_Printf("%2d",(int)DesiredFrameRate);
   4157 	}
   4158 #else
   4159 	curframe = curframe;
   4160 	max_ahead = max_ahead;
   4161 	num_connections = num_connections;
   4162 	their_recv = their_recv;
   4163 	their_sent = their_sent;
   4164 	my_sent = my_sent;
   4165 #endif
   4166 }	// end of Print_Framesync_Values
   4167 
   4168 
   4169 /***************************************************************************
   4170  * Dump_Packet_Too_Late_Stuff -- Dumps a debug file to disk                *
   4171  *                                                                         *
   4172  * INPUT:                                                                  *
   4173  *		event		ptr to event to print													*
   4174  *                                                                         *
   4175  * OUTPUT:                                                                 *
   4176  *		none.																						*
   4177  *                                                                         *
   4178  * WARNINGS:                                                               *
   4179  *		none.																						*
   4180  *                                                                         *
   4181  * HISTORY:                                                                *
   4182  *   06/28/1996 BRR : Created.                                             *
   4183  *=========================================================================*/
   4184 void Dump_Packet_Too_Late_Stuff(EventClass *event)
   4185 {
   4186 	FILE *fp;
   4187 	int i;
   4188 
   4189 	fp = fopen("toolate.txt", "wt");
   4190 	if (!fp) {
   4191 		return;
   4192 	}
   4193 	fprintf(fp,"--------- Event data: -------------------\n");
   4194 	fprintf(fp,"Type:       %s\n",EventClass::EventNames[event->Type]);
   4195 	fprintf(fp,"Frame:      %d\n",event->Frame);
   4196 	fprintf(fp,"ID:         %d\n",event->ID);
   4197 	fprintf(fp,"MPlayerID:  %04x\n",event->MPlayerID);
   4198 
   4199 	for (i = 0; i < MPlayerCount; i++) {
   4200 		if (event->MPlayerID == MPlayerID[i]) {
   4201 			fprintf(fp,"Player's Name: %s",MPlayerNames[i]);
   4202 		}
   4203 	}
   4204 
   4205 	fprintf(fp,"----------- My data: ------------------\n");
   4206 	fprintf(fp,"Frame:%d\n",Frame);
   4207 	fprintf(fp,"MaxAhead:%d\n",MPlayerMaxAhead);
   4208 
   4209 	fclose(fp);
   4210 }
   4211 
   4212 #endif	//DEMO
   4213 
   4214 /*************************** end of queue.cpp ******************************/