]> icculus.org git repositories - btb/d2x.git/blob - main/old/winmodem.c
This commit was manufactured by cvs2svn to create branch
[btb/d2x.git] / main / old / winmodem.c
1 /*
2 THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
3 SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
4 END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
5 ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
6 IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
7 SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
8 FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
9 CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
10 AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.  
11 COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
12 */
13
14
15 #pragma off (unreferenced)
16 static char rcsid[] = "$Id: winmodem.c,v 1.1.1.1 2001-01-19 03:30:14 bradleyb Exp $";
17 #pragma on (unreferenced)
18
19 #ifdef NETWORK
20
21 #include "desw.h"
22 #include "win\comm.h"
23 #include "win\xtapi.h"
24
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <dos.h>
28 //#include <conio.h>
29 #include <string.h>
30 #include <time.h>
31 #include <io.h>
32
33 #include "game.h"
34 #include "scores.h"
35 #include "modem.h"
36 #include "object.h"
37 #include "player.h"
38 #include "laser.h"
39 #include "error.h"
40 #include "collide.h"
41 #include "endlevel.h"
42 #include "network.h"
43 #include "mono.h"
44 #include "gauges.h"
45 #include "newmenu.h"
46 #include "menu.h"
47 #include "gamesave.h"
48 #include "netmisc.h"
49 #include "cntrlcen.h"
50 #include "multi.h"
51 #include "timer.h"
52 #include "text.h"
53 #include "pcx.h"
54 #include "palette.h"
55 #include "sounds.h"
56 #include "digi.h"
57 #include "multibot.h"
58 #include "args.h"
59
60
61 #define MIN_COMM_GAP 8000
62 #define INIT_STRING_LEN 20
63 #define LEN_PHONE_NUM_OLD 15
64 #define LEN_PHONE_NUM   30
65 #define LEN_PHONE_NAME 12
66 #define NUM_PHONE_NUM 8
67
68 #define COM1    1
69 #define COM2    2
70 #define COM3    3
71 #define COM4    4
72
73 // How many times to repeat 'reliable' messages
74
75 #define EOR_MARK 0xaa
76
77 #define COM_PROCESS_NORMAL 0
78 #define COM_PROCESS_ENDLEVEL 1
79 #define COM_PROCESS_SYNC 2
80 #define COM_PROCESS_MENU 3
81
82 #define SELECTION_STARTGAME             1
83 #define SELECTION_NO_START                      2
84 #define SELECTION_YES_START             3
85 #define SELECTION_STARTGAME_ABORT       4
86 #define SELECTION_CLOSE_LINK            5
87
88
89 //      Code to support modem/null-modem play
90 // Program determined variables for serial play
91
92 typedef struct com_sync_pack {
93         char type;
94         byte proto_version;
95         long sync_time;
96         byte level_num;
97         char difficulty;
98         char game_mode;
99         char callsign[CALLSIGN_LEN+1];
100         short kills[2];
101         ushort seg_checksum;
102         byte sync_id;
103         char mission_name[9];
104         short killed;
105         byte game_flags;
106    
107    short DoMegas:1;
108    short DoSmarts:1;
109    short DoFusions:1;
110    short DoHelix:1;
111         short DoPhoenix:1;
112         short DoAfterburner:1;
113         short DoInvulnerability:1;
114         short DoCloak:1;
115         short DoGauss:1;
116         short DoVulcan:1;
117         short DoPlasma:1;
118         short DoOmega:1;
119         short DoSuperLaser:1;
120         short DoProximity:1;
121         short DoSpread:1;
122         short DoSmartMine:1;
123         short DoFlash:1;
124         short DoGuided:1;
125         short DoEarthShaker:1;
126         short DoMercury:1;
127         short Allow_marker_view:1;
128         short RefusePlayers:1;
129         short AlwaysLighting:1; 
130         short DoAmmoRack:1;
131         short DoConverter:1;
132         short DoHeadlight:1;
133         
134         char    dummy[3]; // Extra space for checksum & sequence number
135 } com_sync_pack;
136
137
138 int default_base[4] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 };
139 int default_irq[4] = { 4, 3, 4, 3 };
140
141
142 COMM_OBJ Comm;
143 BOOL ModemDetect = FALSE;
144
145 int com_device = 0;
146
147 int serial_active;
148 int com_baud_rate = 0;
149 //--unused-- int sync_time = 0;
150 int com_open = 0;
151 int got_sync = 0;
152 int other_got_sync = 0;
153 int carrier_on = 0;
154 long com_type = -1; /* What type of UART is available */
155 static long synccnt;
156 static ubyte rx_seqnum = 0xff;
157 static ubyte tx_seqnum = 0;
158 int OtherPlayer; // Player num for modem opponent
159 int com_process_mode = COM_PROCESS_NORMAL;
160 int master = -1; // Am I the master or is the other guy the master?
161 com_sync_pack my_sync, other_sync;
162 int start_level_num;
163
164 int com_custom_port = -1;
165 int com_custom_irq = -1;
166 int com_custom_base = -1;
167
168 int other_menu_choice = 0;
169
170 int chars_sent = 0;
171
172 // Com buffers
173
174 static char syncbuffer[MAX_MULTI_MESSAGE_LEN+4];
175 static char sendbuf[MAX_MULTI_MESSAGE_LEN+4]; // +4 because of +1 for null and +3 for checksum/sequence
176
177 // Serial setup variables
178
179 int com_port_num = -1;
180 int com_speed = -1;
181 char modem_init_string[INIT_STRING_LEN+1];
182 char phone_num[NUM_PHONE_NUM+1][LEN_PHONE_NUM+1];
183 char phone_name[NUM_PHONE_NUM][LEN_PHONE_NAME+1];
184
185 #define MAX_MODEMS              200
186 #define MODEM_NAME_LEN  22
187
188 char ModemNames[MAX_MODEMS][MODEM_NAME_LEN+1],ModemStrings[MAX_MODEMS][INIT_STRING_LEN+1];  // for init strings
189 char ModemInitString[INIT_STRING_LEN+1];
190
191
192 fix  SerialLastMessage = 0;
193
194
195 /* Function prototypes for functions not exported through modem.h */
196
197 void modem_main_menu(int modem);
198 void modem_dialout(int modem);
199 void modem_hangup();
200 void modem_answer(int modem);
201 void modem_wait_for_connect(int nitems, newmenu_item *menus, int *key, int citem);
202 void modem_wait_for_ring(int nitems, newmenu_item *menus, int *key, int citem);
203
204 int serial_link_menu(int port);
205 void serial_link_start();
206
207 int com_game_menu(void);
208 void com_start_game(void);
209 void com_menu_poll(int nitems, newmenu_item *menus, int *key, int citem);
210 int com_sync(int id);
211 void com_sync_poll(int nitem, newmenu_item *menus, int *key, int citem);
212
213 int GetModemList(void); 
214
215
216 /*
217  * Functions
218 */
219         
220 #if !defined(NDEBUG) && !defined(NMONO)
221 void
222 com_dump_string(char *string)
223 {
224         mprintf((0, "%s\n", string));
225 }
226 #else
227 #define com_dump_string()
228 #endif
229
230
231 int
232 com_enable() 
233 {
234         if (com_open)
235                 return 0;
236
237         rx_seqnum = 0xff;
238         tx_seqnum = 0;
239
240         Comm.baud = com_speed;
241
242         if (!comm_open_connection(&Comm)) {
243                 nm_messagebox(TXT_ERROR, 1, TXT_OK, "Failed to open COM port.");
244                 comm_close_connection(&Comm);
245                 return -1;
246         }        
247         
248         comm_set_dtr(&Comm, 1);
249         
250         if ( FindArg( "-ctsrts" ) || FindArg( "-rtscts" )  )    
251                 comm_set_rtscts(&Comm, 1);      // Now used for null-modem as well
252         else
253                 comm_set_rtscts(&Comm, 0);
254
255         com_open = 1;
256
257         master = -1;
258
259         mprintf((0, "Comm object created.\n"));
260
261         comm_dump_info(&Comm);
262
263         return 0;
264 }
265
266 void
267 com_disable()
268 {
269         // close the com port and free associated structures.
270
271         if (!com_open) 
272                 return;
273
274         comm_close_connection(&Comm);
275
276         com_open = 0;
277
278         master = -1;
279
280         #ifndef NDEBUG
281         mprintf((0, "Comm object released.\n"));
282         #endif
283 }
284
285
286 void
287 com_abort(void)
288 {
289         // this is the safest way to get out of some modem/serial negotiation
290         // and back to the main menu.  Use this whenever this have gone too far
291         // awry to repair.
292
293         com_disable();
294
295         N_players = 0;
296
297         change_playernum_to(0);
298
299         Viewer = ConsoleObject = &Objects[0];
300         Game_mode = GM_GAME_OVER; // Force main menu selection
301 }
302
303 void
304 com_hangup(void)
305 {
306         // Close the serial link
307
308         com_send_choice(SELECTION_CLOSE_LINK);
309         com_abort();
310         modem_hangup();
311 }
312
313 void
314 com_carrier_lost(void)
315 {
316         // Carrier lost, inform and abort
317
318         if (multi_in_menu > 0)
319         {
320                 multi_leave_menu = 1;
321                 return;
322         }
323
324         Function_mode = FMODE_MENU;
325
326         multi_quit_game = 1;
327         nm_messagebox(NULL, 1, TXT_OK, TXT_CARRIER_LOST);
328         com_abort();
329         modem_hangup();
330 }
331
332
333 extern ubyte Cockpit_mode_save; // From object.c
334
335 com_reset_game(void)
336 {
337         int i;
338
339         // Reset various parameters before starting a new game
340         
341         N_players = 2;
342
343         for (i = 0; i < N_players; i++)
344         {
345                 Players[i].connected = 1;
346         }
347
348 //      master = -1;
349
350         multi_new_game(); // Reset kill list, among other things
351         Control_center_destroyed = 0;
352         Endlevel_sequence = 0;
353         
354         // Yes, this really IS as ugly as it gets, kids...
355
356         if (Cockpit_mode == CM_LETTERBOX || Cockpit_mode == CM_REAR_VIEW)
357         {
358                 select_cockpit(Cockpit_mode_save);
359         }
360 }
361
362
363 //      WE SHOULD CHANGE THIS TO BE MORE WINDOW LIKE?
364
365 void
366 com_save_settings(void)
367 {
368         FILE *settings;
369         int i;
370
371
372         if ( (settings = fopen("serial.cfg", "wb")) == NULL)
373                 goto error;
374
375         if (fwrite(&com_speed, sizeof(int), 1, settings) != 1)
376                 goto error;
377         
378         if (fwrite(&com_port_num, sizeof(int), 1, settings) != 1)
379                 goto error;
380
381         if (fwrite(modem_init_string, 1, INIT_STRING_LEN+1, settings) != INIT_STRING_LEN+1)
382                 goto error;
383
384         for (i = 0; i < NUM_PHONE_NUM; i++)
385         {
386                 if (fwrite(phone_num[i], 1, LEN_PHONE_NUM+1, settings) != LEN_PHONE_NUM+1)
387                         goto error;
388                 if (fwrite(phone_name[i], 1, LEN_PHONE_NAME+1, settings) != LEN_PHONE_NAME+1)
389                         goto error;
390         }
391
392         if (fwrite(&com_custom_port, sizeof(int), 1, settings) != 1)
393                 goto error;
394         if (fwrite(&com_custom_irq, sizeof(int), 1, settings) != 1)
395                 goto error;
396         if (fwrite(&com_custom_base, sizeof(int), 1, settings) != 1)
397                 goto error;
398         // 100 % success!
399
400         fclose(settings);
401         return;
402
403 error:
404         nm_messagebox(NULL, 1, TXT_OK, TXT_ERROR_SERIAL_CFG);
405
406         if (settings) {
407                 fclose(settings);
408                 unlink("serial.cfg");
409         }
410         
411         return;
412 }
413
414
415 void
416 com_load_settings(void)
417 {
418         FILE *settings;
419         int i, cfg_size;
420
421
422         if ((settings = fopen("serial.cfg", "rb")) == NULL)
423                 goto defaults;
424
425         cfg_size = filelength(fileno(settings));
426
427         // Read the data from the file
428         
429         if (fread(&com_speed, sizeof(int), 1, settings) != 1)
430                 goto error;
431         if (! ((com_speed == 9600) || (com_speed == 19200) || (com_speed == 38400)) )
432                 goto error;
433
434         mprintf((0, "Here?"));
435
436 /* Not really used for Windows version */
437         if (fread(&com_port_num, sizeof(int), 1, settings) != 1)
438                 goto error;
439         if ( (com_port_num < COM1) || (com_port_num > COM4) )
440                 goto error;
441
442         mprintf((0, "Here?"));
443
444
445 /* Not really used for Windows version */
446         if (fread(modem_init_string, 1, INIT_STRING_LEN+1, settings) != INIT_STRING_LEN+1)
447                 goto error;
448         modem_init_string[INIT_STRING_LEN] = '\0';
449
450         mprintf((0, "Here?"));
451
452
453 /* Used in Windows version */
454         if (cfg_size <= 273 )   {                               // Old 15 char LEN_PHONE_NUM's
455                 mprintf(( 1, "Reading old pre 1.1 phone.cfg\n" ));
456                 for (i = 0; i < NUM_PHONE_NUM; i++)
457                 {
458                         if (fread(phone_num[i], 1, LEN_PHONE_NUM_OLD+1, settings) != LEN_PHONE_NUM_OLD+1)
459                                 goto error;
460                         phone_num[i][LEN_PHONE_NUM_OLD] = '\0';
461                         if (fread(phone_name[i], 1, LEN_PHONE_NAME+1, settings) != LEN_PHONE_NAME+1)
462                                 goto error;
463                         phone_name[i][LEN_PHONE_NAME] = '\0';
464                 }
465         } else {                        // Normal Phone nums
466                 for (i = 0; i < NUM_PHONE_NUM; i++)
467                 {
468                         if (fread(phone_num[i], 1, LEN_PHONE_NUM+1, settings) != LEN_PHONE_NUM+1)
469                                 goto error;
470                         phone_num[i][LEN_PHONE_NUM] = '\0';
471                         if (fread(phone_name[i], 1, LEN_PHONE_NAME+1, settings) != LEN_PHONE_NAME+1)
472                                 goto error;
473                         phone_name[i][LEN_PHONE_NAME] = '\0';
474                 }
475         }
476
477         mprintf((0, "Here?"));
478
479
480 /* Not really used in Windows version */
481         if (fread(&com_custom_port, sizeof(int), 1, settings) != 1) {
482                 mprintf((0, "Reading old file format for serial.cfg.\n"));
483                 goto close;
484         }
485
486         if ( (com_custom_port < -1) || (com_custom_port > COM4) )
487                 goto error;
488
489         if (fread(&com_custom_irq, sizeof(int), 1, settings) != 1)
490                 goto error;
491
492 //!!    if ( (com_custom_port < -1) || (com_custom_port > IRQ15) )
493 //!!            goto error;
494
495         if (fread(&com_custom_base, sizeof(int), 1, settings) != 1)
496                 goto error;
497         if (com_custom_base < -1)
498                 goto error;
499
500
501         mprintf((0, "Here?"));
502         //Everything was A-Ok!
503
504
505 close:
506         fclose(settings);
507
508         return;
509
510 error:
511         nm_messagebox(NULL, 1, TXT_OK, TXT_ERR_SER_SETTINGS);
512                 
513 defaults:
514         // Return some defaults
515         com_speed = CBR_19200;
516
517 /* Here so we can save them back to config file. */
518         strcpy(modem_init_string, "ATZ");
519         com_port_num = COM2;
520         com_custom_port = -1;
521
522         for (i = 0; i < NUM_PHONE_NUM; i++)
523         {
524                 phone_num[i][0] = '\0';
525                 strcpy(phone_name[i], TXT_EMPTY);
526         }
527
528         if (settings)
529                 fclose(settings);
530
531         return;
532 }
533
534 void
535 serial_leave_game(void)
536 {
537         #ifndef NDEBUG
538         mprintf((0, "Called serial_leave_game.\n"));
539         #endif
540 //      com_abort();
541         serial_sync_abort(0); // Just in case the other guy is in sync mode
542         Game_mode |= GM_GAME_OVER;
543         Function_mode = FMODE_MENU;
544 }
545
546
547 void
548 com_send_data(char *ptr, int len, int repeat)
549 {
550         int i;
551
552         // Take the raw packet data specified by ptr and append the sequence
553         // number and checksum, and pass it to com_send_ptr
554
555         if (!com_open)
556                 return;
557
558         if (Game_mode & GM_MODEM)
559         {
560                 i = comm_get_cd(&Comm);
561                 if (i == 0)     {
562                         mprintf((0, "CARRIER LOST!\n"));
563                         com_abort();
564                         modem_hangup();
565                 }
566         }
567         
568         len += 3; // Checksum data is 3 bytes
569
570         *(ubyte *)(ptr+(len-3)) = (tx_seqnum+1)%256;
571         tx_seqnum = (tx_seqnum+1)%256;
572
573         *(ushort *)(ptr+(len-2)) = netmisc_calc_checksum(ptr, len-2);
574
575         com_send_ptr(ptr, len);
576         if (repeat>0)
577                 for (i = 0; i < repeat; i++)
578                         com_send_ptr(ptr, len);
579 }
580
581 com_send_ptr(char *ptr, int len)
582 {
583         register        int count;
584         register char dat;
585
586         for (count = 0, dat=ptr[0]; count < len; dat=ptr[++count])
587         {
588                 comm_write_char(&Comm, dat);
589                 if (dat == EOR_MARK)
590                         comm_write_char(&Comm, EOR_MARK);       // double in-band endmarkers
591         }
592         comm_write_char(&Comm, EOR_MARK);
593         comm_write_char(&Comm, 0);
594         chars_sent += len;
595 }
596
597
598 void
599 com_flush()
600 {
601         // Get rid of all waiting data in the serial buffer
602
603         int i = 0;
604
605         if (!com_open)  
606                 return;
607
608         mprintf((0, "COM FLUSH:"));
609
610         while (comm_read_char_timed(&Comm, 100) >= 0)
611                 i++;
612         mprintf((0, "%d characters.\n", i));
613 }
614
615 int
616 com_getchar()
617 {
618         register int i;
619         static int eor_recv = 0;
620
621         // return values:
622         //  -1 = Nothing in buffer
623         //  -2 = End of record
624
625         if (!com_open)
626                 return(-1);
627
628         i = comm_read_char(&Comm);;
629
630 //      mprintf((0, "%d", i));
631
632         if (i == COMM_BUF_EMPTY)
633                 return (-1);
634
635         if ((i == EOR_MARK) || eor_recv)
636         {
637                 if (!eor_recv)
638                         i = comm_read_char(&Comm);
639
640                 if (i == COMM_BUF_EMPTY)
641                 {
642 //                      Assert(eor_recv == 0);
643                         eor_recv = 1;
644                         return(-1);
645                 }
646                 else if (i == EOR_MARK) 
647                 {
648                         eor_recv = 0;
649                         return(EOR_MARK); // Doubled EOR returns the character
650                 }
651                 else
652                 {
653 #ifndef NDEBUG
654                         if (i != 0) {
655                                 mprintf((0, "EOR followed by unexpected value %d.\n", i));
656                         }
657 #endif
658                         eor_recv = 0;
659                         return(-2);                                                     
660                 }
661         }
662         return(i);
663 }
664
665 #define SERIAL_IDLE_TIMEOUT F1_0*10
666
667
668 void
669 com_do_frame(void)
670 {
671         static fix last_comm_time = 0;
672         static int last_pos_skipped = 0;
673         int rval = 0;
674
675         if (Endlevel_sequence || (com_process_mode==COM_PROCESS_ENDLEVEL)) {    // Only recieve during endlevel
676                 int old_Endlevel_sequence = Endlevel_sequence;
677                 Endlevel_sequence = 1;
678                 com_process_input();
679                 Endlevel_sequence = old_Endlevel_sequence;
680                 return;
681         }
682
683         last_comm_time += FrameTime;
684
685         if ((last_comm_time > MIN_COMM_GAP) || Network_laser_fired)
686         {
687 #ifndef SHAREWARE
688                 if ((Game_mode & GM_MULTI_ROBOTS) && !last_pos_skipped) {
689                         rval = multi_send_robot_frame(0);
690                 }
691                 if (rval && !Network_laser_fired)
692                 {
693                         last_pos_skipped = 1;
694                         goto skippos;
695                 }
696 #endif
697                 last_pos_skipped = 0;
698                 multi_send_position(Players[Player_num].objnum);
699                 multi_send_fire(); // Will return w/o sending if we haven't fired
700
701 skippos:
702 //              mprintf((0, "%d chars sent, %f cps.\n", chars_sent, f2fl(fixdiv((chars_sent*F1_0),last_comm_time)) ));
703                 chars_sent = 0;
704                 last_comm_time = 0;
705         }
706
707         com_process_input();
708
709         if (!Control_center_destroyed && (Function_mode == FMODE_GAME) && (SerialLastMessage+SERIAL_IDLE_TIMEOUT < GameTime))
710         {
711                 SerialLastMessage = 0x7fffffff-SERIAL_IDLE_TIMEOUT; // Give no further warnings until next message arrives!
712                 nm_messagebox(TXT_WARNING, 1, TXT_OK, TXT_CONNECT_LOST, Players[OtherPlayer].callsign);
713         }
714
715         return;
716 }
717
718 int
719 com_check_message(char *checkbuf, int len)
720 {
721         ushort check;
722         int seqnum;
723   
724    //mprintf ((0,"TYPE=%d!\n",checkbuf[0]));
725
726         if (len < 4)
727         {
728                 mprintf((0, "message type %d too short to be a real message!\n", checkbuf[0]));
729                 goto error;
730         }
731
732         if (checkbuf[0] > MULTI_MAX_TYPE)
733         {
734                 mprintf((0, "message type %d out of range.\n", checkbuf[0]));
735                 goto error;
736         }
737
738         if ( (len-3) != message_length[checkbuf[0]])
739         {
740                 mprintf((0, "id:%d message length %d != %d.\n",checkbuf[0], len-3, message_length[checkbuf[0]]));
741                 goto error;
742         }
743
744         check = netmisc_calc_checksum(checkbuf, len-2);
745         if (check != *(ushort *)(checkbuf+(len-2)))
746         {
747                 #ifndef NDEBUG
748                 mprintf((0, "error in message type %d, length %d, checksum %d != %d\n", checkbuf[0], len, check, *(ushort *)(checkbuf+(len-2))));       
749                 #endif
750                 goto error;
751         }
752
753         seqnum = checkbuf[(len-3)];
754
755         if (seqnum == rx_seqnum)
756         {
757 //              mprintf ((0,"returning due to bad checksum! type=%d seq=%d rx=%d\n",checkbuf[0],seqnum,rx_seqnum));
758                 return -1;
759         }
760         
761         if (seqnum != (rx_seqnum+1)%256)
762         {
763                 #ifndef NDEBUG
764                 mprintf((0, "Warning, missed 1 or more messages.\n"));  
765                 #endif
766         }
767         rx_seqnum = seqnum;
768         //mprintf((0, "message type %d len %d OK!\n", checkbuf[0], len));
769         return 0; 
770
771 error:
772         mprintf((1,"Line status: %d.\n", comm_get_line_status(&Comm)));
773         comm_clear_line_status(&Comm);
774         Int3();
775         return -1;
776 }
777         
778
779 void
780 com_process_menu(char *buf, int len)
781 {
782         char text[80];
783
784         len = len;
785
786         mprintf((0, "com_process_menu: type %d.\n", buf[0]));
787
788         switch(buf[0])
789         {
790                 case MULTI_MESSAGE:
791                         sprintf(text, "%s %s\n'%s'", Players[OtherPlayer].callsign, TXT_SAYS, buf+2);
792                         nm_messagebox(NULL, 1, TXT_OK, text);
793                         break;
794                 case MULTI_MENU_CHOICE:
795                         other_menu_choice = buf[1];
796                         mprintf((0, "Other menu choice = %d.\n", other_menu_choice));
797                         break;
798                 case MULTI_BEGIN_SYNC:
799                         // If we get a sync at the menu, send an abort sync, we're not ready yet!
800                         serial_sync_abort(0);
801                         break;
802         }
803 }
804
805 void
806 com_process_input(void)
807 {
808         // Read all complete messages from the serial buffer and process
809         // the contents.  Messages are read into global array snycbuffer.
810
811         static int len = 0;
812         int entry_com_mode = com_process_mode;
813         register        int dat;
814
815         if (!com_open)
816                 return;
817
818 nextmessage:
819         if (Game_mode & GM_MODEM)
820         {
821                 if (!comm_get_cd(&Comm))
822                         com_carrier_lost();
823         }
824
825         if (!com_open) {
826                 if (!multi_in_menu) {
827                         multi_quit_game = 1;
828                 }
829                 else {
830                         multi_leave_menu = 1;
831                 }
832         }
833
834         if (com_process_mode != entry_com_mode)
835         {
836                 mprintf((0, "Exiting com_process_input due to mode switch.\n"));
837                 return;
838         }
839
840         while ( (len <= MAX_MULTI_MESSAGE_LEN) && (dat = com_getchar()) > -1) // Returns -1 when serial pipe empty
841         {
842                 syncbuffer[len++] = dat;
843         }
844
845         if ((dat == -2) || (len > MAX_MULTI_MESSAGE_LEN)) // Returns -2 when end of message reached
846         {
847                 // End of message
848                 SerialLastMessage = GameTime;
849
850                 if (!com_check_message(syncbuffer, len))
851                 {
852                         switch(com_process_mode)
853                         {
854                                 case COM_PROCESS_NORMAL:
855                                 case COM_PROCESS_ENDLEVEL:
856                                         multi_process_data(syncbuffer, len); break;
857                                 case COM_PROCESS_MENU:
858                                         if (!Endlevel_sequence) com_process_menu(syncbuffer, len); break;
859                                 case COM_PROCESS_SYNC:
860                                         if (!Endlevel_sequence) com_process_sync(syncbuffer, len); break;
861                                 default:
862                                         Int3(); // Bad com_process_mode switch set!
863                         }
864                 }
865            else mprintf ((0,"Huh?\n"));
866    
867                 len = 0;
868                 goto nextmessage;
869         }
870         if (dat == -3) // Returns -3 when carrier lost
871         {
872                 com_abort();
873                 len = 0;
874         }
875         return ;
876 }
877
878 int
879 com_connect()
880 {
881
882    Network_status=0;
883         my_sync.type = MULTI_BEGIN_SYNC;
884         my_sync.difficulty = 0;
885         memcpy(my_sync.callsign, Players[Player_num].callsign, CALLSIGN_LEN+1);
886         my_sync.seg_checksum = 0;
887         my_sync.game_mode = Game_mode;
888         my_sync.level_num = 0;
889                                                                  
890         #ifndef NDEBUG
891         mprintf((0, "com_connect()\n"));
892         #endif
893
894         if(com_sync(-1))
895                 return(-1); // Failure in sync
896
897         if (master == -1)
898         {
899                 #ifndef NDEBUG
900                 mprintf((0, "My rand = %d, other rand = %d.\n", my_sync.sync_time, other_sync.sync_time));
901                 #endif
902
903                 // Figure out who is the master
904                 if (my_sync.sync_time > other_sync.sync_time)
905                 {
906                         mprintf((0, "Swtiching player to master.\n"));
907                         master=1;
908                         change_playernum_to(0);
909                 }
910                 else if (my_sync.sync_time < other_sync.sync_time)
911                 {
912                         mprintf((0, "Switching player to slave.\n"));
913                         master = 0;
914                         change_playernum_to(1);
915                 }
916                 else
917                         return(-1);  // Didn't sync properly, try again
918         }
919         
920         // Copy the remote sync data into local variables
921         
922         OtherPlayer = (Player_num+1)%2;
923         mprintf((0, "Other player is #%d.\n", OtherPlayer));
924         memcpy(Players[OtherPlayer].callsign, other_sync.callsign, CALLSIGN_LEN+1);
925
926         return(0);
927 }
928
929
930
931 //      ----------------------------------------------------------------------------
932 //      COM MENU SYSTEM
933 //      ----------------------------------------------------------------------------
934
935 #define ADD_ITEM(t,value,key)  do { m[num_options].type=NM_TYPE_MENU; menu_choice[num_options]=value; m[num_options].text=t; num_options++; } while (0)
936 #define SUBTITLE_LEN 192
937
938 #define MENU_XTAPI_DEVICE                       0
939 #define MENU_COMM_DEVICE                        2
940
941 #define MENU_SERIAL_GAME_START  5
942 #define MENU_SEND_MESSAGE                       6
943 #define MENU_SERIAL_CLOSE                       7
944 #define MENU_MODEM_HANGUP                       8
945
946
947 static TapiDevice *tapiDevices=NULL;
948 static int xtapiCurrentModem = -1;
949 static int numTapiDevs = 0;
950
951 extern void nm_draw_background1(char * filename);
952
953
954 void com_main_menu(void)
955 {
956         newmenu_item m[12];
957         int menu_choice[12];
958         int device_type[6];                     // 0 = serial, 1 = modem
959         char buf[64];
960         int i, num_options=0, choice;
961         int sel;
962         int retval;
963
964         if (com_speed == -1)
965                 com_load_settings();
966
967 //      setjmp(LeaveGame);
968
969         nm_draw_background1(Menu_pcx_name);
970         com_process_mode = COM_PROCESS_MENU;
971
972         if (Game_mode & GM_SERIAL) {    // HACK to get menu system working      
973                 Assert(Comm.port >= 1 && Comm.port <= 4);
974                 mprintf((0, "WINMODEM: Going to serial link menu!\n"));
975                 sel = MENU_COMM_DEVICE + (Comm.port-1);
976                 goto skip_to_com_submenus;
977         }
978         else if (Game_mode & GM_MODEM) {
979                 Assert(xtapiCurrentModem != -1);
980                 mprintf((0, "WINMODEM: Going to modem menu!\n"));
981                 sel = xtapiCurrentModem;
982                 goto skip_to_com_submenus;
983         }
984         
985 //      Get devices for TAPI.
986         retval = xtapi_init(WINAPP_NAME, &numTapiDevs);
987         if (retval == XTAPI_NODEVICES) {
988                 return;
989         }
990         else if (retval == XTAPI_APPCONFLICT) {
991                 return;
992         }
993         else if (retval == XTAPI_INCOMPATIBLE_VERSION) {
994                 return;
995         }
996
997         if (numTapiDevs > 2) numTapiDevs = 2;
998         if (numTapiDevs == 0) tapiDevices = NULL;
999         else {
1000                 tapiDevices = (TapiDevice *)malloc(numTapiDevs*sizeof(TapiDevice));
1001                 if (!tapiDevices) 
1002                         Error("Unable to allocate memory for com_main_menu.");
1003
1004                 retval = xtapi_enumdevices(tapiDevices, numTapiDevs);   
1005                 if (retval != XTAPI_SUCCESS) {
1006                         return;
1007                 }
1008         }
1009         
1010 com_menu_redraw:
1011         num_options = 0;
1012         for (i = 0; i <  numTapiDevs; i++)
1013         {
1014                 if (tapiDevices[i].type == XTAPI_MODEM_DEVICE) {
1015                         sprintf(buf, "Modem (%d baud)", tapiDevices[i].max_baud);
1016                         device_type[i] = 1;
1017                 }
1018                 else {
1019                         sprintf(buf, "Unsupported device (%d baud)", tapiDevices[i].max_baud);
1020                         device_type[i] = 2;
1021                 }
1022
1023                 m[i].type=NM_TYPE_MENU; menu_choice[i]=MENU_XTAPI_DEVICE+i; 
1024                 m[i].text=buf; num_options++;
1025         }
1026
1027         device_type[num_options] = 0;   
1028         m[num_options].type=NM_TYPE_MENU; menu_choice[num_options]=MENU_COMM_DEVICE; 
1029         m[num_options].text="Direct to Com 1"; num_options++;
1030
1031         device_type[num_options] = 0;   
1032         m[num_options].type=NM_TYPE_MENU; menu_choice[num_options]=MENU_COMM_DEVICE+1; 
1033         m[num_options].text="Direct to Com 2"; num_options++;
1034
1035         device_type[num_options] = 0;   
1036         m[num_options].type=NM_TYPE_MENU; menu_choice[num_options]=MENU_COMM_DEVICE+2; 
1037         m[num_options].text="Direct to Com 3"; num_options++;
1038
1039         device_type[num_options] = 0;   
1040         m[num_options].type=NM_TYPE_MENU; menu_choice[num_options]=MENU_COMM_DEVICE+3; 
1041         m[num_options].text="Direct to Com 4"; num_options++;
1042
1043         choice = newmenu_do1(NULL, "Select Serial Device", num_options, m, NULL, 0);
1044         
1045         if (choice == -1) {
1046                 if (tapiDevices) free(tapiDevices);
1047                 xtapi_shutdown();
1048                 return;
1049         }
1050         else if (choice < -1) {
1051
1052                 return;
1053         }
1054
1055         sel = menu_choice[choice];
1056         
1057 skip_to_com_submenus:
1058
1059         switch (sel)
1060         {
1061                 case MENU_XTAPI_DEVICE:
1062                 case MENU_XTAPI_DEVICE+1:
1063                 //      start a xtapi game
1064                         modem_main_menu(sel);
1065                         if (Function_mode == FMODE_GAME) return;
1066                         break;
1067
1068                 case MENU_COMM_DEVICE:
1069                 case MENU_COMM_DEVICE+1:
1070                 case MENU_COMM_DEVICE+2:
1071                 case MENU_COMM_DEVICE+3:
1072                 // start a serial game  
1073                         mprintf((0, "Selected a null-modem game.\n")); 
1074                         if (!serial_link_menu(sel-MENU_COMM_DEVICE+1)) 
1075                                 goto com_menu_redraw;
1076                         mprintf((0, "WINMODEM: starting game...\n"));
1077                         return;
1078         }
1079
1080 //      Freeing XTAPI   
1081         if (tapiDevices) free(tapiDevices);
1082         xtapi_shutdown();
1083         nm_draw_background1(Menu_pcx_name);
1084 }
1085
1086
1087 //       if 1, then we are starting a game
1088 //       if 0, then not.
1089 int com_game_menu(void)
1090 {
1091         newmenu_item m[12];
1092         int menu_choice[12];
1093         int num_options = 0;
1094         int choice=0;
1095         char subtitle[SUBTITLE_LEN];
1096
1097         setjmp(LeaveGame);
1098
1099
1100 com_game_menu_redraw:
1101         num_options = 0;
1102
1103         ADD_ITEM(TXT_START_GAME, MENU_SERIAL_GAME_START, KEY_S);
1104         ADD_ITEM(TXT_SEND_MESSAGEP, MENU_SEND_MESSAGE, KEY_S);
1105         if (Game_mode & GM_MODEM)
1106                 ADD_ITEM(TXT_HANGUP_MODEM, MENU_MODEM_HANGUP, KEY_H);
1107         
1108         if (Game_mode & GM_SERIAL)
1109                 ADD_ITEM(TXT_CLOSE_LINK, MENU_MODEM_HANGUP, KEY_C);
1110
1111         m[num_options].type=NM_TYPE_TEXT; m[num_options].text=""; num_options++;
1112
1113         if (Game_mode & GM_SERIAL)
1114                 sprintf(subtitle, "%s\n\n%s %s\n%s", TXT_SERIAL_GAME, TXT_SERIAL, TXT_LINK_ACTIVE, Players[OtherPlayer].callsign);
1115         else if (Game_mode & GM_MODEM)
1116                 sprintf(subtitle, "%s\n\n%d %s %s\n%s", TXT_SERIAL_GAME, com_baud_rate, TXT_BAUD, TXT_LINK_ACTIVE, Players[OtherPlayer].callsign);      
1117         else {
1118                 mprintf((1, "Game mode isn't set correctly.\n"));
1119                 Int3();
1120         }
1121
1122         multi_leave_menu = 0;
1123
1124         Assert(strlen(subtitle) < SUBTITLE_LEN);
1125
1126         choice = newmenu_do1(NULL, subtitle, num_options, m, com_menu_poll, 0);
1127
1128         if (choice == -1)
1129         {
1130                 m[0].text = TXT_YES; m[1].text = TXT_NO;
1131                 m[0].type = m[1].type = NM_TYPE_MENU;
1132
1133                 choice = newmenu_do1(NULL, TXT_EXIT_WILL_CLOSE, 2, m, com_menu_poll, 0);
1134                 if (choice == 0)
1135                 {
1136                         com_send_choice(SELECTION_CLOSE_LINK);
1137                         com_hangup();
1138                         return 0;
1139                 }
1140                 if ((choice == -2) && (other_menu_choice))
1141                         com_process_other_menu_choice();
1142
1143                 goto com_game_menu_redraw;
1144         }
1145
1146         if (choice == -2)
1147         {
1148                 // Menu poll loop caused a re-draw
1149                 if (other_menu_choice == SELECTION_STARTGAME)   
1150                         com_ready_to_start();
1151                 else if (other_menu_choice == SELECTION_CLOSE_LINK) 
1152                 {
1153                         nm_messagebox(NULL, 1, TXT_OK, TXT_CLOSED_LINK);
1154                         com_hangup();
1155                         mprintf((0, "Leaving modem game (hanging up)...\n"));
1156                 }
1157                         
1158                 if (Function_mode == FMODE_GAME)        {
1159                         mprintf((0, "We think we're still in a modem game!.\n"));
1160                         return 1;       
1161                 }
1162
1163                 if (!com_open)  {
1164                         mprintf((0, "At this point we're taking off..\n"));
1165                         Game_mode = GM_GAME_OVER;
1166                         return 0;
1167                 }
1168
1169                 goto com_game_menu_redraw;
1170         }               
1171
1172         comm_dump_info(&Comm);
1173
1174         if (choice > -1) 
1175         {
1176         //      old_game_mode=Game_mode;
1177                 switch (menu_choice[choice])
1178                 {
1179                         case MENU_SERIAL_GAME_START:
1180                                 com_start_game();
1181                                 if (Function_mode != FMODE_GAME) 
1182                                         goto com_game_menu_redraw;
1183                                 return 1;
1184
1185                         case MENU_SEND_MESSAGE:
1186                                 multi_send_message_dialog();
1187                                 if (Network_message_reciever != -1)
1188                                         multi_send_message();
1189                                 multi_sending_message = 0;
1190                                 goto com_game_menu_redraw;
1191                                 break;
1192
1193                         case MENU_MODEM_HANGUP:
1194                                 com_hangup();
1195                                 return 0;
1196
1197                         default: 
1198                                 mprintf((1, "Bad com_game_menu choice\n"));
1199                                 Int3();
1200                                 return 0;
1201                 }
1202         }
1203         return 0;
1204 }       
1205
1206
1207
1208 /*
1209  * Serial Link/Null Modem Functions
1210 */
1211
1212 //       1 = starting game
1213 //       0 = no game.
1214 int serial_link_menu(int port)
1215 {
1216 /*      This menu will give a listing of possible com_speeds.  The default will
1217         be given, but the user should be given the option to change to a lower 
1218         speed 
1219 */
1220         newmenu_item mm[8];
1221         int com_max_setting, loc, i, mmn, menu_link;    
1222         int com_speed_setting;
1223         int com_speeds[5] = {
1224                 CBR_9600, CBR_19200, CBR_38400, CBR_57600, CBR_115200
1225         };
1226         char *com_speeds_text[5] = {
1227                 "9600", "19200", "38400", "57600", "115200"
1228         };
1229
1230 //      HACK to get menu system working when leaving serial game to
1231 //      get to correct modem.
1232         if (Game_mode & GM_SERIAL) {
1233                 mprintf((0, "WINMODEM:Go directly to com_game menu.\n"));
1234                 goto skip_to_game_menu;
1235         }
1236         else mprintf((0, "WINMODEM:Not going directly to com_game menu.\n"));
1237
1238 /* First check what speed the selected port is set to */
1239         if (com_open) com_disable();
1240
1241         memset(&Comm, 0, sizeof(COMM_OBJ));
1242         Comm.port = port;
1243         Comm.baud = com_speed;
1244
1245         if (!comm_open_connection(&Comm)) {
1246                 nm_messagebox(TXT_ERROR, 1, TXT_OK, "Unable to open selected COM port");
1247                 return 0;
1248         }
1249
1250         switch(Comm.baud)
1251         {
1252                 case CBR_9600: com_max_setting = 1; break;
1253                 case CBR_19200: com_max_setting = 2; break;
1254                 case CBR_38400: com_max_setting = 3; break;
1255                 default:
1256                         mprintf((0, "COM%d baud = %d\n", Comm.port, Comm.baud));
1257                         if (Comm.baud > CBR_38400) com_max_setting = 3;
1258                         else {
1259                                 nm_messagebox(TXT_ERROR, 1, TXT_OK, "Your serial system must be\n capable of at least 9600 baud.");
1260                                 com_max_setting = -1;
1261                         }
1262         }
1263         comm_close_connection(&Comm);
1264
1265         if (com_max_setting == -1) return 0;
1266
1267 /* Create menu */
1268 setup_serial_menu:
1269         loc = 0;
1270         mm[0].type=NM_TYPE_TEXT;        mm[0].text = TXT_BAUD_RATE; loc++;
1271
1272         for (i = 0; i < 3; i++)                                 // Limit to 38.4K
1273         {
1274                 mm[i+1].type=NM_TYPE_RADIO; mm[i+1].value=(i==(com_max_setting-1)); 
1275                 mm[i+1].text=com_speeds_text[i]; mm[i+1].group=1; loc++;
1276         }
1277         mm[loc].type=NM_TYPE_TEXT; mm[loc].text = ""; loc++;
1278
1279         menu_link = loc;
1280         mm[loc].type=NM_TYPE_MENU; mm[loc].text = "Establish Link"; loc++;
1281
1282         mmn = newmenu_do1(NULL, TXT_SERIAL_SETTINGS, loc, mm, NULL, menu_link);
1283         
1284 /* Take care of selection */
1285         if (mmn > -1) {
1286                 for (i = 0; i < 3; i++)
1287                         if (mm[i+1].value) {
1288                                 com_speed_setting = com_speeds[i];
1289                                 break;
1290                         }
1291
1292                 if(menu_link != mmn) goto setup_serial_menu;
1293         }
1294         else return 0;          
1295
1296         com_speed = com_speed_setting;
1297 //      com_enable();
1298
1299         serial_link_start();                    // We should be in GM_SERIAL after this
1300
1301         com_save_settings();
1302
1303 skip_to_game_menu:
1304         if (Game_mode & GM_SERIAL) {
1305                 if (com_game_menu()) return 1;
1306                 else return 0;
1307         }
1308         else return 0;
1309 }
1310
1311
1312 void serial_link_start(void)
1313 {
1314         if (!serial_active)
1315         {
1316                 nm_messagebox(TXT_ERROR, 1, TXT_OK, TXT_NO_SERIAL_OPT);
1317                 return;
1318         }
1319
1320         com_enable(); // Open COM port as configured
1321
1322         if (!com_open)
1323                 return;
1324
1325         N_players = 2;
1326
1327         synccnt = 0;
1328
1329         srand(clock());
1330         my_sync.sync_time = rand();
1331         mprintf((0, "My rand set to %d.\n", my_sync.sync_time));
1332
1333         if (!com_connect()) 
1334         {
1335                 Game_mode |= GM_SERIAL;
1336                 digi_play_sample(SOUND_HUD_MESSAGE, F1_0);
1337         } 
1338         else
1339         {
1340                 nm_messagebox(NULL, 1, TXT_OK, "%s\n%s", TXT_ERROR, TXT_FAILED_TO_NEGOT);
1341                 com_abort();
1342         }
1343 }
1344
1345
1346
1347 /* 
1348  * modem class functions
1349 */
1350
1351 void modem_main_menu(int modem)
1352 {
1353         newmenu_item m[6];
1354         int num_options;
1355         int choice=0;
1356
1357         if (Game_mode & GM_MODEM) {
1358                 mprintf((0, "WINMODEM:Going directly to com_game_menu.\n"));
1359                 com_game_menu();
1360                 return;
1361         }
1362
1363 modem_menu_redraw:
1364         num_options=0;
1365         m[num_options].type=NM_TYPE_MENU; m[num_options].text="Dial"; num_options++;
1366         m[num_options].type=NM_TYPE_MENU; m[num_options].text="Answer"; num_options++;
1367         m[num_options].type=NM_TYPE_TEXT; m[num_options].text=""; num_options++;
1368
1369         choice = newmenu_do1(NULL, "Modem Options", num_options, m, NULL, 0);
1370         if (choice > -1) {
1371                 switch (choice) 
1372                 {
1373                         case 0:
1374                                 modem_dialout(modem);
1375                                 if (Game_mode & GM_MODEM)       com_game_menu();
1376                                 if (Function_mode == FMODE_GAME) return;
1377                                 break;
1378                         
1379                         case 1:
1380                                 modem_answer(modem);
1381                                 if (Game_mode & GM_MODEM)       com_game_menu();
1382                                 if (Function_mode == FMODE_GAME) return;
1383                                 break;
1384
1385                 }
1386         }
1387         else if (choice == -1) return;
1388         
1389         goto modem_menu_redraw;
1390 }
1391
1392
1393 void modem_hangup()
1394 {
1395         if (xtapiCurrentModem > -1) {
1396                 mprintf((0, "WINMODEM: Hanging up..\n"));
1397                 com_disable();
1398                 xtapi_device_hangup();
1399                 xtapi_unlock(&tapiDevices[xtapiCurrentModem]);
1400                 xtapiCurrentModem = -1;
1401                 mprintf((0, "WINMODEM: Complete!\n"));
1402         }
1403 }
1404
1405
1406 void modem_dialout(int modem)
1407 {
1408         newmenu_item m[5];
1409         int choice, retval;
1410
1411 // At this point, xtapi is initialized, so we just need to get a number
1412 //      and dial out.
1413         
1414         if (!serial_active)
1415         {
1416                 nm_messagebox(TXT_ERROR, 1, TXT_OK, TXT_NO_SERIAL_OPT);
1417                 return;
1418         }
1419
1420         retval = xtapi_lock(&tapiDevices[modem]);
1421         if (retval != XTAPI_SUCCESS) {
1422                 mprintf((1, "Failed to lock down an XTAPI device.\n"));
1423                 Int3();
1424         }
1425
1426         if (com_open)
1427                 return;
1428
1429         xtapiCurrentModem = modem;                      // Modem controls com functions
1430
1431 redial:
1432 //      Select a number to dial
1433         if ((choice = modem_dial_menu()) == -1) {
1434                 modem_hangup();
1435                 return;
1436         }
1437
1438         if (strlen(phone_num[choice]) == 0)
1439         {
1440                 nm_messagebox(NULL, 1, TXT_OK, TXT_NO_PHONENUM);
1441                 goto redial;
1442         }
1443
1444 //      dial the number
1445         show_boxed_message(TXT_RESET_MODEM);
1446         retval = xtapi_device_dialout(phone_num[choice]);
1447         if (retval != XTAPI_SUCCESS) {
1448                 clear_boxed_message();
1449                 nm_messagebox(NULL, 1, TXT_OK, "Unable to initiate dialing!");
1450                 mprintf((1, "XTAPI: err %d\n", retval));
1451                 modem_hangup();
1452                 return;
1453         }
1454         clear_boxed_message();
1455
1456         m[0].type=NM_TYPE_TEXT; m[0].text="Initializing\n";
1457         m[1].type=NM_TYPE_TEXT; m[1].text=TXT_ESC_ABORT;
1458
1459 //repeat:
1460         choice = newmenu_do(NULL, TXT_WAITING_FOR_ANS, 2, m, modem_wait_for_connect);
1461         if (choice != -2) {
1462                 modem_hangup();
1463                 return;
1464         }
1465
1466 // We are now connected to the other modem
1467         N_players = 2;
1468
1469    my_sync.sync_time=32000;
1470         // -- commented out by mk, 2/21/96 -- master = 1;   // The person who dialed is the master of the connection
1471         change_playernum_to(0);
1472
1473         if (!com_connect())
1474         {
1475                 Game_mode |= GM_MODEM;
1476                 digi_play_sample(SOUND_HUD_MESSAGE, F1_0);
1477         }
1478         else {
1479                 modem_hangup();
1480         }
1481
1482 }
1483
1484
1485 void modem_answer(int modem)
1486 {
1487         newmenu_item m[5];
1488         int choice, retval;
1489
1490 // At this point, xtapi is initialized, so we just need to get a number
1491 //      and dial out.
1492         
1493         if (!serial_active)
1494         {
1495                 nm_messagebox(TXT_ERROR, 1, TXT_OK, TXT_NO_SERIAL_OPT);
1496                 return;
1497         }
1498
1499         retval = xtapi_lock(&tapiDevices[modem]);
1500         if (retval != XTAPI_SUCCESS) {
1501                 mprintf((1, "Failed to lock down an XTAPI device.\n"));
1502                 Int3();
1503         }
1504
1505         if (com_open)
1506                 return;
1507
1508         xtapiCurrentModem = modem;                      // Modem controls com functions
1509
1510 //      start up modem and wait for connect
1511         show_boxed_message(TXT_RESET_MODEM);
1512         retval = xtapi_device_dialin();
1513         if (retval != XTAPI_SUCCESS) {
1514                 clear_boxed_message();
1515                 nm_messagebox(NULL, 1, TXT_OK, "Unable to initialize modem.");
1516                 mprintf((1, "XTAPI: err %d\n", retval));
1517                 modem_hangup();
1518                 return;
1519         }
1520         clear_boxed_message();
1521         
1522 //      wait for a ring first.
1523 modem_answer_redraw:
1524         m[0].type=NM_TYPE_TEXT; m[0].text=TXT_ESC_ABORT;
1525
1526         choice = newmenu_do(NULL, TXT_WAITING_FOR_CALL, 1, m, modem_wait_for_ring);
1527         if (choice == -1) {
1528                 modem_hangup();
1529                 return;
1530         }
1531         if (choice != -2)       
1532                 goto modem_answer_redraw;
1533
1534 // Now answer the phone and wait for carrier
1535         retval = xtapi_device_answer();
1536
1537         carrier_on = 0;
1538
1539         choice = newmenu_do(NULL, TXT_WAITING_FOR_CARR, 1, m, modem_wait_for_connect);
1540         if (choice != -2) {
1541                 modem_hangup();
1542                 return;
1543         }
1544
1545 // We are now connected to the other modem
1546         // We are now connected to the other modem
1547         N_players = 2;
1548
1549    my_sync.sync_time=0;
1550         //master = 0;
1551         change_playernum_to(1);
1552
1553         if (!com_connect()) 
1554         {
1555                 Game_mode |= GM_MODEM;
1556                 digi_play_sample(SOUND_HUD_MESSAGE, F1_0);
1557         }
1558         else {
1559                 modem_hangup();
1560         }
1561 }
1562
1563
1564 void modem_wait_for_connect(int nitems, newmenu_item *menus, int *key, int citem)
1565 {
1566         uint modem_state;
1567         int retval;
1568
1569         menus = menus;
1570         nitems = nitems;
1571         citem = citem;
1572         
1573         retval = xtapi_device_poll_callstate(&modem_state);
1574         if (retval != XTAPI_SUCCESS) {
1575                 return;
1576         }
1577
1578         switch (modem_state)
1579         {
1580                 case XTAPI_LINE_DIALING:
1581                         menus[0].text = "Dialing\n";
1582                         menus[0].redraw = 1;
1583                         return;
1584
1585                 case XTAPI_LINE_PROCEEDING:
1586                 case XTAPI_LINE_RINGBACK:
1587                         menus[0].text = "Calling\n";
1588                         menus[0].redraw = 1;
1589                         return;
1590
1591                 case XTAPI_LINE_BUSY:
1592                         nm_messagebox(NULL, 1, TXT_OK, "Got a busy signal.");
1593                         *key = -3;
1594                         return;
1595
1596                 case XTAPI_LINE_FEEDBACK:
1597                         nm_messagebox(NULL, 1, TXT_OK, "Unable to connect.");
1598                         *key = -3;
1599                         return;
1600                 
1601                 case XTAPI_LINE_DISCONNECTED:
1602                         nm_messagebox(NULL, 1, TXT_OK, "Disconnected!");
1603                         *key = -3;
1604                         return;
1605
1606                 case XTAPI_LINE_CONNECTED:
1607                 // perform connection scheme: we should create a comm object!
1608                         xtapi_device_create_comm_object(&Comm);
1609                         rx_seqnum = 0xff;
1610                         tx_seqnum = 0;
1611                         com_open = 1;
1612                         mprintf((0, "Connect at %d baud.\n", Comm.baud));
1613                         if (Comm.baud < CBR_9600) {
1614                                 nm_messagebox(NULL, 1, TXT_OK, TXT_BAUD_GREATER_9600);
1615                                 *key = -3; 
1616                                 return;
1617                         }       
1618                         else { 
1619                                 com_baud_rate = Comm.baud;              
1620                                 *key = -2;
1621                                 return;
1622                         }
1623                         break;          
1624
1625                 case XTAPI_LINE_IDLE:
1626                         menus[0].text = "Idle\n";
1627                         menus[0].redraw = 1;
1628                         *key = -3;
1629                         return;
1630                 
1631                 case XTAPI_LINE_DIALTONE:
1632                         menus[0].text = "Dialtone\n";
1633                         menus[0].redraw = 1;
1634                         return;
1635         
1636                 case 0: break;
1637         
1638                 default:
1639                         mprintf((0, "xtapi undefined call state.\n"));
1640         }
1641         
1642         return;
1643 }
1644
1645
1646 void modem_wait_for_ring(int nitems, newmenu_item *menus, int *key, int citem)
1647 {
1648         uint modem_state;
1649         int result;
1650
1651         menus = menus;
1652         nitems = nitems;
1653         citem = citem;
1654
1655         result = xtapi_device_poll_callstate(&modem_state);
1656
1657         if (modem_state == XTAPI_LINE_RINGING) {
1658                 mprintf((0, "Line is ringing. Answering...\n"));
1659                 result = xtapi_device_answer();
1660                 if (result != XTAPI_SUCCESS) return;
1661                 *key = -2;
1662         }       
1663 }
1664
1665
1666 void modem_edit_phonebook(newmenu_item *m)
1667 {
1668         int choice, choice2;
1669         newmenu_item menu[5];
1670         char text[2][25];
1671         int default_choice = 0;
1672
1673         m[NUM_PHONE_NUM].text = TXT_SAVE;
1674
1675         menu[0].text = TXT_NAME; menu[0].type = NM_TYPE_TEXT;
1676         menu[1].type = NM_TYPE_INPUT; menu[1].text = text[0]; menu[1].text_len = LEN_PHONE_NAME;
1677         menu[2].text = TXT_PHONE_NUM; menu[2].type = NM_TYPE_TEXT;
1678         menu[3].type = NM_TYPE_INPUT; menu[3].text = text[1]; menu[3].text_len = LEN_PHONE_NUM;
1679         menu[4].text = TXT_ACCEPT; menu[4].type = NM_TYPE_MENU;
1680
1681 menu:
1682         choice = newmenu_do1(NULL, TXT_SEL_NUMBER_EDIT, NUM_PHONE_NUM+1, m, NULL, default_choice);
1683         if (choice == -1)
1684         {
1685                 com_load_settings();
1686                 return;
1687         }
1688         if (choice == NUM_PHONE_NUM)
1689         {
1690                 // Finished
1691                 com_save_settings();
1692                 return;
1693         }
1694         
1695         default_choice = 1;
1696 edit:
1697         // Edit an entry
1698         strcpy(menu[1].text, phone_name[choice]);
1699         strcpy(menu[3].text, phone_num[choice]);
1700
1701         choice2 = newmenu_do1(NULL, TXT_EDIT_PHONE_ENTRY, 5, menu, NULL, default_choice);
1702         if (choice2 != -1)
1703         {       
1704                 strcpy(phone_name[choice], menu[1].text);
1705                 strcpy(phone_num[choice], menu[3].text);
1706                 sprintf(m[choice].text, "%d. %s \t", choice+1, phone_name[choice]);
1707                 add_phone_number(m[choice].text, phone_num[choice] );
1708         }
1709         if (choice2 != 4)
1710         {
1711                 default_choice += 2; if (default_choice > 4) default_choice = 4;
1712                 goto edit;
1713         }
1714
1715         default_choice = NUM_PHONE_NUM;
1716         goto menu;
1717 }
1718
1719
1720
1721 void add_phone_number( char * src, char * num )
1722 {
1723         char p;
1724         int l;
1725         l = strlen(num);
1726         if ( l<15)      {
1727                 strcat( src, num );
1728                 return;
1729         }
1730         p = num[15];
1731         num[15] = 0;
1732         strcat( src, num );
1733         num[15] = p;
1734         strcat( src, "..." );
1735 }
1736
1737
1738 int modem_dial_menu(void)
1739 {
1740         newmenu_item m[NUM_PHONE_NUM+2];
1741         char menu_text[NUM_PHONE_NUM][80];
1742         int choice = 0;
1743         int i;
1744
1745 menu:
1746         for (i = 0; i < NUM_PHONE_NUM; i++)
1747         {
1748                 m[i].text = menu_text[i];
1749                 sprintf(m[i].text, "%d. %s \t", i+1, phone_name[i]);
1750                 add_phone_number(m[i].text, phone_num[i] );
1751                 m[i].type = NM_TYPE_MENU;
1752         }
1753
1754         strcat(m[i-1].text, "\n");
1755
1756         m[NUM_PHONE_NUM].type = NM_TYPE_MENU; 
1757         m[NUM_PHONE_NUM].text = TXT_MANUAL_ENTRY;
1758         m[NUM_PHONE_NUM+1].text = TXT_EDIT_PHONEBOOK;
1759         m[NUM_PHONE_NUM+1].type = NM_TYPE_MENU;
1760
1761         choice = newmenu_do1(NULL, TXT_SEL_NUMBER_DIAL, NUM_PHONE_NUM+2, m, NULL, 0);
1762         if (choice == -1) 
1763                 return -1; // user abort
1764
1765         if (choice == NUM_PHONE_NUM+1)
1766         {
1767                 // Edit phonebook
1768                 modem_edit_phonebook(m);
1769                 goto menu;
1770         }
1771
1772         if (choice == NUM_PHONE_NUM)
1773         {
1774                 // Manual entry
1775                 newmenu_item m2[1];
1776                 m2[0].type = NM_TYPE_INPUT; m2[0].text = phone_num[NUM_PHONE_NUM]; m2[0].text_len = LEN_PHONE_NUM;
1777                 choice = newmenu_do(NULL, TXT_ENTER_NUMBER_DIAL, 1, m2, NULL);
1778                 if (choice == -1)
1779                         goto menu;
1780                 else
1781                         return NUM_PHONE_NUM;
1782         }
1783
1784         // A phone number was chosen
1785         return(choice);
1786 }
1787
1788
1789
1790 /*
1791  *      COM functions
1792 */
1793
1794 void
1795 com_menu_poll(int nitems, newmenu_item *menus, int *key, int citem)
1796 {
1797         // Watch the serial stream if we are connected and take appropriate actions
1798
1799         int old_game_mode;
1800
1801         menus = menus;
1802         citem = citem;
1803         nitems = nitems;
1804
1805         com_process_mode = COM_PROCESS_MENU;
1806         old_game_mode = Game_mode;
1807         other_menu_choice = 0;  
1808
1809         com_process_input();
1810
1811         if ((old_game_mode != Game_mode) || other_menu_choice || (com_process_mode != COM_PROCESS_MENU))
1812                 *key = -2;
1813         if (multi_leave_menu)
1814                 *key = -2;
1815 }
1816
1817 void
1818 com_send_choice(int choice)
1819 {
1820         sendbuf[0] = (char)MULTI_MENU_CHOICE;
1821         sendbuf[1] = (char)choice;
1822
1823         com_send_data(sendbuf, 2, 1);
1824 }
1825
1826 void
1827 com_ready_to_start(void)
1828 {
1829         newmenu_item m[2];
1830         int choice;
1831
1832         m[0].type = m[1].type = NM_TYPE_MENU;
1833         m[0].text = TXT_YES;
1834         m[1].text = TXT_NO;
1835
1836         choice = newmenu_do1(NULL, TXT_READY_DESCENT, 2, m, com_menu_poll, 0 );
1837         if (choice == 0)
1838         {
1839                 // Yes
1840                 com_send_choice(SELECTION_YES_START);
1841                 other_menu_choice = SELECTION_STARTGAME;
1842                 com_start_game();
1843         }               
1844         else 
1845         {
1846                 com_send_choice(SELECTION_NO_START);
1847         }
1848 }
1849
1850 void
1851 com_process_other_menu_choice(void)
1852 {
1853         if (other_menu_choice == SELECTION_STARTGAME)   
1854                 com_ready_to_start();
1855         else if (other_menu_choice == SELECTION_CLOSE_LINK) 
1856         {
1857                 nm_messagebox(NULL, 1, TXT_OK, TXT_CLOSED_LINK);
1858                 com_hangup();
1859         }
1860 }
1861
1862
1863 // Handshaking to start a serial game, 2 players only
1864
1865 int com_start_game_menu(void)
1866 {
1867         newmenu_item m[15];
1868         char level[5];
1869         int choice = 0;
1870         int opt, diff_opt, mode_opt, options_opt,allow_opt,extraopts_opt;
1871         char level_text[32];
1872
1873 #ifndef SHAREWARE
1874         int new_mission_num, anarchy_only = 0;
1875
1876         new_mission_num = multi_choose_mission(&anarchy_only);
1877
1878         if (new_mission_num < 0)
1879                 return 0;
1880
1881         strcpy(my_sync.mission_name, Mission_list[new_mission_num].filename);
1882 #endif
1883
1884         sprintf(level, "1");
1885
1886         Game_mode &= ~GM_MULTI_COOP;
1887         Game_mode &= ~GM_MULTI_ROBOTS;
1888    Game_mode &= ~GM_CAPTURE;
1889
1890
1891         Netgame.game_flags = 0;
1892         SetAllAllowablesTo(1);
1893
1894         sprintf(level_text, "%s (1-%d)", TXT_LEVEL_, Last_level);
1895 //      if (Last_secret_level < -1)
1896 //              sprintf(level_text+strlen(level_text)-1, ", S1-S%d)", -Last_secret_level);
1897 //      else if (Last_secret_level == -1)
1898 //              sprintf(level_text+strlen(level_text)-1, ", S1)");
1899
1900         Assert(strlen(level_text) < 32);
1901
1902         // Put up menu for user choices controlling gameplay
1903
1904 newmenu:
1905         opt = 0;
1906         m[opt].type = NM_TYPE_TEXT; m[opt].text = level_text; opt++;
1907         m[opt].type = NM_TYPE_INPUT; m[opt].text_len = 4; m[opt].text = level; opt++;
1908         m[opt].type = NM_TYPE_TEXT; m[opt].text = TXT_MODE;
1909         mode_opt = opt; 
1910         m[opt].type = NM_TYPE_RADIO; m[opt].text = TXT_ANARCHY; m[opt].value=!(Game_mode & GM_MULTI_ROBOTS); m[opt].group = 0; opt++;
1911         m[opt].type = NM_TYPE_RADIO; m[opt].text = TXT_ANARCHY_W_ROBOTS; m[opt].value=(!(Game_mode & GM_MULTI_COOP) && (Game_mode & GM_MULTI_ROBOTS)); m[opt].group = 0; opt++;
1912         m[opt].type = NM_TYPE_RADIO; m[opt].text = TXT_COOPERATIVE; m[opt].value=(Game_mode & GM_MULTI_COOP);m[opt].group = 0; opt++;
1913         m[opt].type = NM_TYPE_RADIO; m[opt].text = "Capture the Flag"; m[opt].value=(Game_mode & GM_CAPTURE); m[opt].group = 0; opt++;
1914         diff_opt = opt;
1915         m[opt].type = NM_TYPE_SLIDER; m[opt].text = TXT_DIFFICULTY; m[opt].value = Player_default_difficulty; m[opt].min_value = 0; m[opt].max_value = (NDL-1); opt++;
1916         m[opt].type = NM_TYPE_TEXT; m[opt].text = " "; opt ++;
1917
1918    extraopts_opt=opt;
1919
1920         m[opt].type = NM_TYPE_CHECK; m[opt].text = "Marker camera views"; m[opt].value=Netgame.Allow_marker_view; opt++;
1921    m[opt].type = NM_TYPE_CHECK; m[opt].text = "Indestructable lights"; m[opt].value=Netgame.AlwaysLighting; opt++;
1922            
1923    allow_opt=opt;
1924         m[opt].type = NM_TYPE_MENU; m[opt].text = "Choose objects allowed"; opt++;
1925         
1926
1927 #ifndef SHAREWARE
1928         options_opt = opt;
1929 //      m[opt].type = NM_TYPE_CHECK; m[opt].text = TXT_SHOW_IDS; m[opt].value=0; opt++;
1930         m[opt].type = NM_TYPE_CHECK; m[opt].text = TXT_SHOW_ON_MAP; m[opt].value=0; opt++;
1931 #endif
1932
1933         Assert(opt <= 12);
1934
1935         GetChoice:
1936         
1937         choice = newmenu_do1(NULL, TXT_SERIAL_GAME_SETUP, opt, m, NULL, 1);
1938
1939         
1940         if (choice > -1) 
1941         {
1942            if (choice==allow_opt)
1943                         {
1944                         network_set_power();
1945                                 goto GetChoice;
1946                         }
1947                 Netgame.Allow_marker_view=m[extraopts_opt].value;
1948                 Netgame.AlwaysLighting=m[extraopts_opt+1].value;
1949
1950                 if (m[mode_opt].value)
1951                         Game_mode &= ~(GM_MULTI_COOP | GM_MULTI_ROBOTS);
1952 #ifdef SHAREWARE          
1953                 else {
1954                         nm_messagebox(NULL, 1, TXT_OK, TXT_ONLY_ANARCHY);
1955                         goto newmenu;
1956                 }
1957 #else
1958                 else if (anarchy_only) {
1959                         if (m[mode_opt+3].value)
1960                         Game_mode |= (GM_CAPTURE);
1961                         else 
1962                          {
1963                                 nm_messagebox(NULL, 1, TXT_OK, TXT_ANARCHY_ONLY_MISSION);
1964                                 goto newmenu;
1965                          }
1966                 }
1967                 else if (m[mode_opt+1].value)
1968                 {
1969                         Game_mode &= ~GM_MULTI_COOP;
1970                         Game_mode |= GM_MULTI_ROBOTS;
1971                 }
1972                 else if (m[mode_opt+2].value)
1973                         Game_mode |= (GM_MULTI_COOP | GM_MULTI_ROBOTS);
1974                 else if (m[mode_opt+3].value)
1975                         Game_mode |= (GM_CAPTURE);
1976                 
1977
1978 //              if (m[options_opt].value)
1979 //                      Netgame.game_flags |= NETGAME_FLAG_SHOW_ID;
1980                 if (m[options_opt].value)
1981                         Netgame.game_flags |= NETGAME_FLAG_SHOW_MAP;
1982                 if (!strnicmp(level, "s", 1))
1983                         start_level_num = -atoi(level+1);
1984                 else
1985 #endif
1986                         start_level_num = atoi(level);
1987
1988                 if ((start_level_num < 1) || (start_level_num > Last_level))
1989                 {
1990                         nm_messagebox(TXT_ERROR, 1, TXT_OK, TXT_LEVEL_OUT_RANGE);
1991                         sprintf(level, "1");
1992                         goto newmenu;
1993                 }
1994
1995                 Difficulty_level = m[diff_opt].value;
1996
1997                 return(1); // Go for game!
1998         }
1999         return 0; // No game
2000 }
2001
2002 int
2003 com_ask_to_start()
2004 {       
2005         // Ask the other player if its OK to start now
2006
2007         newmenu_item m[1];
2008         int choice;
2009
2010         com_send_choice(SELECTION_STARTGAME);
2011
2012         m[0].type = NM_TYPE_TEXT; m[0].text = TXT_ESC_ABORT;
2013 menu:
2014         choice = newmenu_do(NULL, TXT_WAIT_FOR_OK, 1, m, com_menu_poll);
2015
2016         if (choice == -1)
2017         {
2018                 com_send_choice(SELECTION_STARTGAME_ABORT);
2019                 return(0);
2020         }
2021         if (choice == -2)
2022         {
2023                 if (other_menu_choice == SELECTION_YES_START)
2024                         return(1);
2025                 else if (other_menu_choice == SELECTION_STARTGAME)
2026                 {
2027                         com_send_choice(SELECTION_YES_START);
2028                         return(1);
2029                 }
2030                 else 
2031                         return(0);
2032         }
2033         goto menu;
2034 }
2035                 
2036
2037 void
2038 com_start_game()
2039 {
2040         // Start a serial game after being linked
2041
2042         mprintf((0, "Entered com_start_game\n"));
2043
2044         com_reset_game();
2045
2046         if (! ( (Game_mode & GM_MODEM) || (Game_mode & GM_SERIAL) ) ) 
2047                 return;
2048         
2049         Assert (master != -1);
2050
2051         if (other_menu_choice != SELECTION_STARTGAME)
2052         {
2053                 if (!com_ask_to_start())
2054                         return;
2055         }
2056
2057         if (master == 1) // Master chooses options
2058         {
2059                 if (com_start_game_menu())
2060                 {
2061                         OtherPlayer = 1;
2062                         change_playernum_to(0);
2063                         my_sync.level_num = start_level_num;
2064                         my_sync.difficulty = Difficulty_level;
2065                         my_sync.game_mode = Game_mode;
2066                         if (Game_mode & GM_CAPTURE)
2067                                 my_sync.game_mode |=GM_UNKNOWN; 
2068                         memcpy(my_sync.callsign, Players[Player_num].callsign, CALLSIGN_LEN+1);
2069                         my_sync.sync_time = control_invul_time;
2070                         my_sync.game_flags = Netgame.game_flags;
2071                         Netgame.control_invul_time = control_invul_time;
2072                         memcpy ((&my_sync.game_flags)+1,(&Netgame.team_vector)+1,4);
2073                         
2074                         com_sync(0);
2075                         if (Game_mode & GM_CAPTURE)
2076                                 my_sync.game_mode &=GM_UNKNOWN;
2077                 }
2078         }
2079         else // Slave
2080         {
2081                 OtherPlayer = 0;
2082                 change_playernum_to(1);
2083                 memcpy(my_sync.callsign, Players[Player_num].callsign, CALLSIGN_LEN+1);
2084         
2085                 my_sync.level_num = 1;
2086                 
2087                 com_sync(0);
2088                 if (com_process_mode == COM_PROCESS_NORMAL)
2089                 {
2090                         Difficulty_level = other_sync.difficulty;
2091                         start_level_num = other_sync.level_num;
2092                         Game_mode = other_sync.game_mode;
2093                         if (Game_mode & GM_UNKNOWN) // Super HACK! Out of bit fields, doubling up
2094                         {
2095                                 Game_mode &=~GM_UNKNOWN;
2096                                 Game_mode |=GM_CAPTURE;
2097                         }
2098
2099                         memcpy ((&Netgame.team_vector)+1,(&other_sync.game_flags)+1,4);
2100                         
2101 #ifndef SHAREWARE
2102                         Netgame.game_flags = other_sync.game_flags;
2103                         Netgame.control_invul_time = other_sync.sync_time;
2104                         if (!load_mission_by_name(other_sync.mission_name))
2105                         {
2106                                 mprintf((0, "Mission not found: %s!\n", other_sync.mission_name));
2107                                 nm_messagebox(NULL, 1, TXT_OK, TXT_MISSION_NOT_FOUND);
2108                                 my_sync.sync_id = start_level_num;
2109                                 serial_sync_abort(0);
2110                                 return;
2111                         }
2112 #endif
2113                 }
2114         }
2115         if (com_process_mode != COM_PROCESS_NORMAL)
2116                 return;
2117
2118         memcpy(Players[OtherPlayer].callsign, other_sync.callsign, CALLSIGN_LEN+1);
2119         Function_mode = FMODE_GAME;
2120         Game_mode &= ~GM_GAME_OVER;
2121         Show_kill_list = 1;
2122         init_player_stats_game();
2123         init_player_stats_level(0);
2124 //      Assert(start_level_num > 0);
2125         Assert((start_level_num >= Last_secret_level) && (start_level_num <= Last_level));
2126         StartNewLevel(start_level_num, 0);
2127 }
2128
2129
2130
2131 //
2132 // Syncronization functions
2133 //
2134
2135 void
2136 serial_sync_abort(int val)
2137 {
2138         // Send "I got Sync but it was no good!" packet
2139
2140         sendbuf[0] = (char)MULTI_END_SYNC;
2141         sendbuf[1] = Player_num;
2142         sendbuf[2] = (char)val; // Indicated failure
2143 //#ifndef SHAREWARE
2144         sendbuf[3] = my_sync.sync_id;
2145         com_send_data(sendbuf, 4, 1);
2146 //#else
2147 //      com_send_data(sendbuf, 3, 1);
2148 //#endif
2149 }
2150         
2151 int
2152 com_level_sync(void)
2153 {
2154         // Send between-level sync stuff
2155
2156         mprintf((0, "entered com_level_sync()\n"));
2157
2158         Function_mode = FMODE_MENU; // Prevent the game loop from running during the menus!
2159
2160         // At this point, the new level is loaded but the extra objects or players have not 
2161         // been removed
2162
2163         my_sync.level_num = Current_level_num;
2164         my_sync.seg_checksum = netmisc_calc_checksum(Segments, (Highest_segment_index+1) * sizeof(segment));
2165         my_sync.kills[0] = kill_matrix[Player_num][0];
2166         my_sync.kills[1] = kill_matrix[Player_num][1];
2167         my_sync.proto_version = MULTI_PROTO_VERSION;
2168 //#ifndef SHAREWARE
2169         my_sync.killed = Players[Player_num].net_killed_total;
2170 //#endif
2171         srand(clock());
2172
2173         if (Game_mode & GM_MULTI_COOP)
2174                 my_sync.difficulty = Player_num;
2175         else
2176                 my_sync.difficulty = rand()%MAX_NUM_NET_PLAYERS; // My starting position
2177
2178         if (com_sync(Current_level_num))
2179         {
2180                 com_process_mode = COM_PROCESS_MENU;
2181                 nm_messagebox(TXT_ERROR, 1, TXT_OK, TXT_NEGOTIATION_FAIL );
2182                 longjmp(LeaveGame, 0);
2183         }
2184
2185         if (my_sync.level_num != other_sync.level_num)
2186         {
2187                 // Fatal error
2188                 nm_messagebox(TXT_ERROR, 1, TXT_OK, "%s %d\n%s %d", TXT_FATAL_ERROR_LEVEL, my_sync.level_num, TXT_OTHER_LEVEL, other_sync.level_num);
2189                 longjmp(LeaveGame, 0);
2190         }
2191
2192         if (my_sync.seg_checksum != other_sync.seg_checksum)
2193         {
2194                 // Checksum failure
2195                 mprintf((1, "My check %d, other check %d.\n", my_sync.seg_checksum, other_sync.seg_checksum));
2196                 nm_messagebox(TXT_ERROR, 1, TXT_OK, "%s %d %s %s%s", TXT_YOUR_LEVEL, my_sync.level_num, TXT_LVL_NO_MATCH, other_sync.callsign, TXT_CHECK_VERSION);
2197                 longjmp(LeaveGame, 0);
2198         }
2199
2200         if (my_sync.proto_version != other_sync.proto_version)
2201         {
2202                 // Version mismatch
2203                 nm_messagebox(TXT_ERROR, 1, TXT_OK, TXT_DESCENT_NO_MATCH);
2204                 longjmp(LeaveGame, 0);
2205         }
2206
2207         mprintf((0, "My pos = %d, other pos = %d.\n", my_sync.difficulty, other_sync.difficulty));
2208
2209         if ((other_sync.difficulty == my_sync.difficulty) && !master)
2210         {
2211                 // If we chose the same position and I am the slave, choose another
2212                 my_sync.difficulty = (my_sync.difficulty+1) % MAX_NUM_NET_PLAYERS;
2213         }
2214
2215         Objects[Players[OtherPlayer].objnum].pos = Player_init[other_sync.difficulty].pos;
2216         Objects[Players[OtherPlayer].objnum].orient = Player_init[other_sync.difficulty].orient;
2217         obj_relink(Players[OtherPlayer].objnum,Player_init[other_sync.difficulty].segnum);
2218         Objects[Players[OtherPlayer].objnum].type = OBJ_PLAYER;
2219
2220         Objects[Players[Player_num].objnum].pos = Player_init[my_sync.difficulty].pos;
2221         Objects[Players[Player_num].objnum].orient = Player_init[my_sync.difficulty].orient;
2222         obj_relink(Players[Player_num].objnum, Player_init[my_sync.difficulty].segnum);
2223         Objects[Players[Player_num].objnum].type = OBJ_PLAYER;
2224
2225         SerialLastMessage = GameTime;
2226
2227         kill_matrix[OtherPlayer][0] = other_sync.kills[0];
2228         kill_matrix[OtherPlayer][1] = other_sync.kills[1];
2229         Players[Player_num].net_kills_total = kill_matrix[Player_num][OtherPlayer] - kill_matrix[Player_num][Player_num];
2230         Players[OtherPlayer].net_kills_total = kill_matrix[OtherPlayer][Player_num] - kill_matrix[OtherPlayer][OtherPlayer];
2231 //      Players[Player_num].net_killed_total = kill_matrix[0][Player_num] + kill_matrix[1][Player_num];
2232 //      Players[OtherPlayer].net_killed_total = kill_matrix[0][OtherPlayer] + kill_matrix[1][OtherPlayer];
2233         Players[OtherPlayer].net_killed_total = other_sync.killed;
2234         Players[OtherPlayer].connected = Players[Player_num].connected = 1;
2235
2236         Assert(N_players == 2);
2237         Assert(Player_num != OtherPlayer);
2238
2239         gr_palette_fade_out(gr_palette, 32, 0);
2240
2241         Function_mode = FMODE_GAME;
2242         com_process_mode = COM_PROCESS_NORMAL;
2243         multi_sort_kill_list();
2244         return(0);
2245 }
2246
2247         
2248 void
2249 com_send_end_sync(void)
2250 {
2251         // Send "I got Sync" packet
2252
2253         sendbuf[0] = (char)MULTI_END_SYNC;
2254         sendbuf[1] = Player_num;
2255         sendbuf[2] = 1; // Indicates success
2256 //#ifndef SHAREWARE
2257         sendbuf[3] = my_sync.sync_id;
2258         com_send_data(sendbuf, 4, 2);
2259 //#else
2260 //      com_send_data(sendbuf, 3, 2);
2261 // #endif
2262 }
2263
2264 void
2265 com_send_begin_sync(void)
2266 {
2267         mprintf((0, "Sending my sync.\n"));
2268         com_send_data((char *)&my_sync, sizeof(com_sync_pack)-3, 1);
2269 }
2270
2271 void
2272 com_process_end_sync(byte *buf)
2273 {
2274         // Process incoming end-sync packet
2275
2276         if (buf[2] != 1) {
2277                 com_process_mode = COM_PROCESS_MENU;
2278                 return;
2279         }
2280
2281 //#ifndef SHAREWARE
2282         if (buf[3] == my_sync.sync_id)
2283 //#endif
2284                 other_got_sync = 1;
2285 }
2286
2287 void
2288 com_process_sync(char *buf, int len)
2289 {
2290    mprintf ((0,"Processing sync!"));
2291
2292         len = len;
2293         switch(buf[0])
2294         {
2295                 case MULTI_END_SYNC:
2296                 {
2297                         com_process_end_sync(buf);
2298                         break;
2299                 }
2300                 case MULTI_BEGIN_SYNC:
2301                 {
2302                         mprintf((0, "Incoming begin_sync message.\n"));
2303                         if (got_sync)
2304                                 break;
2305
2306                         memcpy(&other_sync, buf, sizeof(com_sync_pack)-3);
2307 //#ifndef SHAREWARE
2308                         if (other_sync.sync_id != my_sync.sync_id) 
2309                         {
2310                                 mprintf((0, "Other sync invalid id, %d != %d.\n", other_sync.sync_id, my_sync.sync_id));
2311                         }
2312                         else
2313 //#endif
2314                         {                       
2315                                 mprintf((0, "got other sync size %d.\n", sizeof(com_sync_pack)-3));
2316                                 got_sync = 1;
2317                                 com_send_end_sync();
2318                         }
2319                         break;
2320                 }
2321         } // switch
2322
2323         if (got_sync && other_got_sync)
2324         {
2325                 // OK to start game
2326                 mprintf((1, "Starting game.\n"));
2327                 got_sync = 0;
2328                 other_got_sync = 0;
2329                 com_process_mode = COM_PROCESS_NORMAL;
2330         }
2331 }
2332         
2333 void
2334 com_send_sync(void)
2335 {
2336         // Send sync information, depending on the situation
2337
2338         if (!other_got_sync)
2339         {
2340                 com_send_begin_sync();
2341         }
2342         if (got_sync)
2343         {
2344                 com_send_end_sync();
2345         }
2346 }
2347
2348
2349 void com_sync_poll(int nitems, newmenu_item *menus, int *key, int citem)
2350 {
2351         static fix t1 = 0;
2352
2353         menus = menus;
2354         nitems = nitems;
2355         citem = citem;
2356
2357         if (!com_open)
2358         {
2359                 *key = -3;
2360                 return;
2361         }
2362
2363         if (timer_get_approx_seconds() > t1+F1_0)
2364         {
2365                 com_send_sync();
2366                 t1 = timer_get_approx_seconds();
2367         }
2368
2369         Assert(com_process_mode == COM_PROCESS_SYNC);
2370                 
2371         com_process_input();
2372
2373         if (com_process_mode == COM_PROCESS_NORMAL)
2374         {
2375                 *key = -2;
2376                 com_send_sync();
2377                 mprintf((0, "Sync finished.\n"));
2378                 return;
2379         }
2380         if (com_process_mode == COM_PROCESS_MENU)
2381         {
2382                 *key = -3;
2383                 mprintf((0, "Sync denied by other side.\n"));
2384                 return;
2385         }
2386         return;
2387 }
2388
2389 int
2390 com_sync(int id)
2391 {
2392         // How to handle the end of the level and start of the next level
2393         // returns 0 for success or 1 for failure
2394
2395         int choice;
2396         newmenu_item m[3];
2397         //@@int pcx_error;
2398
2399         mprintf((0, "Entered com_sync\n"));
2400
2401         //@@gr_set_current_canvas(NULL);
2402         //@@pcx_error = pcx_read_bitmap(Menu_pcx_name, &grd_curcanv->cv_bitmap,grd_curcanv->cv_bitmap.bm_type,NULL);
2403         //@@Assert(pcx_error == PCX_ERROR_NONE);
2404
2405         com_process_mode = COM_PROCESS_SYNC;
2406         got_sync = other_got_sync = 0;
2407
2408         com_flush();
2409         com_flush();
2410
2411 //#ifndef SHAREWARE
2412         my_sync.sync_id = id;
2413 //#endif
2414
2415         m[0].type=NM_TYPE_TEXT; m[0].text=TXT_ESC_ABORT;
2416         m[1].type = m[2].type = NM_TYPE_MENU;
2417         m[1].text = TXT_YES; 
2418         m[2].text = TXT_NO;
2419
2420 repeat:
2421         choice = newmenu_do(NULL, TXT_WAIT_OPPONENT, 1, m, com_sync_poll);
2422
2423         if (choice == -1) 
2424         {
2425                 choice = newmenu_do1(NULL, TXT_SURE_ABORT_SYNC, 2, m+1, com_sync_poll, 1);
2426                 if (choice == -1)
2427                         choice = 1;
2428                 if (choice == 0)
2429                         choice = -1;
2430         }
2431
2432         if ((choice == -1) || (choice == -3)) {
2433                 return(-1);
2434         }
2435         else if (choice != -2)
2436                 goto repeat;
2437
2438         return(0);
2439 }
2440
2441 void
2442 com_endlevel(int *secret)
2443 {
2444         // What do we do between levels?
2445
2446         Function_mode = FMODE_MENU;
2447
2448         gr_palette_fade_out(gr_palette, 32, 0);
2449
2450         my_sync.level_num = multi_goto_secret;
2451
2452         if (com_sync(-3))
2453         {
2454                 com_process_mode = COM_PROCESS_MENU;
2455                 nm_messagebox(TXT_ERROR, 1, TXT_OK, TXT_NEGOTIATION_FAIL);
2456                 longjmp(LeaveGame, 0);
2457         }
2458
2459         com_process_mode = COM_PROCESS_ENDLEVEL;
2460
2461         if ((multi_goto_secret == 1) || (other_sync.level_num == 1))
2462                 *secret = 1;
2463         else
2464                 *secret = 0;
2465
2466         multi_goto_secret = 0;
2467
2468         return;
2469 }
2470
2471
2472 #endif
2473
2474 int ReadModemList ()
2475  {
2476    int num=0,i=0,namemode=1;
2477         FILE *MyFile;
2478         char c;
2479
2480         if ((MyFile=fopen ("modem.lst","rb"))==NULL)
2481     return (0);
2482
2483         ModemNames[num][0] = ModemStrings[num][0] = 0;
2484
2485    while (!feof(MyFile))
2486     {
2487       c=fgetc (MyFile);
2488
2489       mprintf ((0,"%c",c));
2490
2491       if (c=='$')
2492        {
2493           fclose (MyFile);
2494           return (num);
2495                  }
2496       else if (c==13)
2497        ;
2498                 else if (c==10) 
2499                  {
2500         ModemStrings[num][i]=0;
2501              namemode=1;
2502                   i=0;
2503                         if (strlen(ModemNames[num]) && strlen(ModemStrings[num]))
2504                      num++;
2505                         if (num >= MAX_MODEMS)
2506                                 break;          //too many!  abort!
2507                         ModemNames[num][0] = ModemStrings[num][0] = 0;
2508             }
2509            else if (c==';')
2510        {
2511              while (fgetc(MyFile)!=10)
2512               ;
2513         namemode=1;
2514                   i=0;
2515                  }
2516            else if (c=='=' && namemode)
2517             {
2518         ModemNames[num][i]=0;
2519              namemode=0;
2520                   i=0;
2521                  }
2522                 else
2523                  {
2524              if (namemode)
2525                    {
2526           if (i<MODEM_NAME_LEN)
2527                                 ModemNames[num][i++]=c;
2528                    }
2529                   else
2530                    {
2531           if (i<INIT_STRING_LEN)
2532                         ModemStrings[num][i++]=c;
2533                         }
2534                  }              
2535          }  // end while
2536
2537         ModemStrings[num][i]=0;         //terminate in case no final newline
2538    fclose (MyFile);
2539    return (num);
2540   }          
2541     
2542    
2543 void com_choose_string ()
2544  { 
2545   int num,i,choice;
2546   newmenu_item m[MAX_MODEMS];
2547
2548   if ((num=ReadModemList())==0)   
2549    {
2550     nm_messagebox ("Error",1,TXT_OK,"No valid modem file found!");
2551     return;
2552    }
2553   
2554   mprintf ((0,"Number of strings=%d\n",num));
2555   
2556
2557   for (i=0;i<num;i++)
2558   {
2559    m[i].type = NM_TYPE_MENU; m[i].text = &ModemNames[i];
2560    mprintf ((0,"Text(%d)=%s\n",i,ModemNames[i]));
2561   }
2562   choice = newmenu_do(NULL, "Choose a modem", num, m, NULL);
2563
2564   if (choice<0)
2565    return;
2566
2567   strcpy (ModemInitString,ModemStrings[choice]);
2568 }