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