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