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