]> icculus.org git repositories - taylor/freespace2.git/blob - src/menuui/optionsmenumulti.cpp
717e1aa84f312f39359bebc52934f73807789c0d
[taylor/freespace2.git] / src / menuui / optionsmenumulti.cpp
1 /*
2  * Copyright (C) Volition, Inc. 1999.  All rights reserved.
3  *
4  * All source code herein is the property of Volition, Inc. You may not sell 
5  * or otherwise commercially exploit the source or things you created based on
6  * the source.
7  */
8
9 /*
10  * $Logfile: /Freespace2/code/MenuUI/OptionsMenuMulti.cpp $
11  * $Revision$
12  * $Date$
13  * $Author$
14  *
15  * $Log$
16  * Revision 1.7  2005/03/29 02:18:47  taylor
17  * Various 64-bit platform fixes
18  * Fix compiler errors with MAKE_FS1 and fix gr_set_bitmap() too
19  * Make sure that turrets can fire at asteroids for FS1 (needed for a couple missions)
20  * Streaming audio support (big thanks to Pierre Willenbrock!!)
21  * Removed dependance on strings.tbl for FS1 since we don't actually need it now
22  *
23  * Revision 1.6  2003/05/25 02:30:42  taylor
24  * Freespace 1 support
25  *
26  * Revision 1.5  2002/06/09 04:41:22  relnev
27  * added copyright header
28  *
29  * Revision 1.4  2002/06/05 08:05:29  relnev
30  * stub/warning removal.
31  *
32  * reworked the sound code.
33  *
34  * Revision 1.3  2002/06/02 06:02:59  relnev
35  * tcp.cfg namefix
36  *
37  * Revision 1.2  2002/05/07 03:16:46  theoddone33
38  * The Great Newline Fix
39  *
40  * Revision 1.1.1.1  2002/05/03 03:28:09  root
41  * Initial import.
42  *  
43  * 
44  * 21    10/25/99 5:47p Jefff
45  * reassigned some xstr ids
46  * 
47  * 20    8/02/99 2:44p Dave
48  * Disable IPX for demo build.
49  * 
50  * 19    7/15/99 7:15p Jefff
51  * Added various sound FX
52  * 
53  * 18    7/15/99 9:20a Andsager
54  * FS2_DEMO initial checkin
55  * 
56  * 17    6/25/99 11:59a Dave
57  * Multi options screen.
58  * 
59  * 16    6/22/99 7:03p Dave
60  * New detail options screen.
61  * 
62  * 15    4/25/99 3:02p Dave
63  * Build defines for the E3 build.
64  * 
65  * 14    2/25/99 4:19p Dave
66  * Added multiplayer_beta defines. Added cd_check define. Fixed a few
67  * release build warnings. Added more data to the squad war request and
68  * response packets.
69  * 
70  * 13    2/19/99 2:55p Dave
71  * Temporary checking to report the winner of a squad war match.
72  * 
73  * 12    2/17/99 2:10p Dave
74  * First full run of squad war. All freespace and tracker side stuff
75  * works.
76  * 
77  * 11    2/12/99 6:16p Dave
78  * Pre-mission Squad War code is 95% done.
79  * 
80  * 10    2/05/99 7:22p Neilk
81  * Fixed gamma bitmap and converted coordinates for multiple resolutions
82  * 
83  * 9     2/02/99 11:36a Dave
84  * Removed obsolete data reference.
85  * 
86  * 8     12/18/98 1:13a Dave
87  * Rough 1024x768 support for Direct3D. Proper detection and usage through
88  * the launcher.
89  * 
90  * 7     11/20/98 11:16a Dave
91  * Fixed up IPX support a bit. Making sure that switching modes and
92  * loading/saving pilot files maintains proper state.
93  * 
94  * 6     11/19/98 4:19p Dave
95  * Put IPX sockets back in psnet. Consolidated all multiplayer config
96  * files into one.
97  * 
98  * 5     11/05/98 5:55p Dave
99  * Big pass at reducing #includes
100  * 
101  * 4     10/13/98 9:28a Dave
102  * Started neatening up freespace.h. Many variables renamed and
103  * reorganized. Added AlphaColors.[h,cpp]
104  * 
105  * 3     10/09/98 2:57p Dave
106  * Starting splitting up OS stuff.
107  * 
108  * 2     10/07/98 10:53a Dave
109  * Initial checkin.
110  * 
111  * 1     10/07/98 10:49a Dave
112  * 
113  * 27    7/08/98 11:56a Dave
114  * Changed mic problem error message.
115  * 
116  * 26    6/12/98 7:13p Hoffoss
117  * Fixed options screen problem where it wasn't showing tooltips.
118  * 
119  * 25    6/09/98 10:31a Hoffoss
120  * Created index numbers for all xstr() references.  Any new xstr() stuff
121  * added from here on out should be added to the end if the list.  The
122  * current list count can be found in FreeSpace.cpp (search for
123  * XSTR_SIZE).
124  * 
125  * 24    6/01/98 11:43a John
126  * JAS & MK:  Classified all strings for localization.
127  * 
128  * 23    5/21/98 9:45p Dave
129  * Lengthened tracker polling times. Put in initial support for PXO
130  * servers with channel filters. Fixed several small UI bugs.
131  * 
132  * 22    5/20/98 2:24a Dave
133  * Fixed server side voice muting. Tweaked multi debrief/endgame
134  * sequencing a bit. Much friendlier for stats tossing/accepting now.
135  * 
136  * 21    5/19/98 1:35a Dave
137  * Tweaked pxo interface. Added rankings url to pxo.cfg. Make netplayer
138  * local options update dynamically in netgames.
139  * 
140  * 20    5/18/98 12:03p Frank
141  * Make sure network changes made in options screen are loaded into any
142  * current Net_player as well as the Player.
143  * 
144  * 19    5/11/98 11:39p Dave
145  * Stuff.
146  * 
147  * 18    5/10/98 7:05p Dave
148  * Fix endgame sequencing ESC key. Changed how host options warning popups
149  * are done. Fixed pause/message scrollback/options screen problems in mp.
150  * Make sure observer HUD doesn't try to lock weapons.
151  * 
152  * 17    5/08/98 7:08p Dave
153  * Lots of UI tweaking.
154  * 
155  * 16    5/08/98 5:04p Dave
156  * Go to the join game screen when quitting multiplayer. Fixed mission
157  * text chat bugs. Put mission type symbols on the create game list.
158  * Started updating standalone gui controls.
159  * 
160  * 15    5/07/98 12:57a Dave
161  * Fixed incorrect calls to free() from stats code. Put in new artwork for
162  * debrief and host options screens. Another modification to scoring
163  * system for secondary weapons.
164  * 
165  * 14    5/06/98 8:06p Dave
166  * Made standalone reset properly under weird conditions. Tweak
167  * optionsmulti screen. Upped MAX_WEAPONS to 350. Put in new launch
168  * countdown anim. Minro ui fixes/tweaks.
169  * 
170  * 13    5/02/98 5:38p Dave
171  * Put in new tracker API code. Put in ship information on mp team select
172  * screen. Make standalone server name permanent. Fixed standalone server
173  * text messages.
174  * 
175  * 12    4/30/98 5:24p Adam
176  * JAS: Made multi config specify path.
177  * 
178  * 11    4/28/98 5:13p Dave
179  * Remove references to old MT API
180  * 
181  * 10    4/25/98 2:00p Dave
182  * Installed a bunch of multiplayer context help screens. Reworked ingame
183  * join ship select screen. Fix places where network timestamps get hosed.
184  * 
185  * 9     4/22/98 4:09p John
186  * String externalization
187  * 
188  * 8     4/22/98 12:34a Dave
189  * Make sure hud config and control config buttons draw properly in all
190  * tab modes. Make small tab buttons light up correctly in multi options
191  * screen.
192  * 
193  * 7     4/21/98 4:44p Dave
194  * Implement Vasudan ships in multiplayer. Added a debug function to bash
195  * player rank. Fixed a few rtvoice buffer overrun problems. Fixed ui
196  * problem in options screen. 
197  * 
198  * 6     4/20/98 6:04p Dave
199  * Implement multidata cache flushing and xferring mission files to
200  * multidata. Make sure observers can't change hud config. Fix pilot image
201  * viewing in popup. Put in game status field. Tweaked multi options. 
202  * 
203  * 5     4/18/98 12:45p Dave
204  * Aesthetic changes to multi options screen. Put in missing exit button.
205  * oops.
206  * 
207  * 4     4/17/98 6:33p Dave
208  * Finished first run of the screen. About time.
209  * 
210  * 3     4/17/98 5:27p Dave
211  * More work on the multi options screen. Fixed many minor ui todo bugs.
212  * 
213  * 2     4/17/98 12:42a Dave
214  * Worked on voice tab. Need to implement mic testing and player muting
215  * list thingie.
216  * 
217  * 1     4/16/98 11:39p Dave
218  *  
219  * 
220  * $NoKeywords: $
221  */
222
223 #include "ui.h"
224 #include "bmpman.h"
225 #include "cfile.h"
226 #include "key.h"
227 #include "oal.h"
228 #include "font.h"
229 #include "gamesnd.h"
230 #include "freespace.h"
231 #include "player.h"
232 #include "multi.h"
233 #include "multi_voice.h"
234 #include "rtvoice.h"
235 #include "mouse.h"
236 #include "optionsmenu.h"
237 #include "optionsmenumulti.h"
238 #include "popup.h"
239 #include "osregistry.h"
240 #include "alphacolors.h"
241 #include "timer.h"
242 #include "gamesequence.h"  // needed for FS1
243 #include "ptrack.h"
244
245
246 // general data section ------------------------------------------------
247 UI_WINDOW *Om_window = NULL;
248
249 #ifndef FS1_DEMO
250 static const char* Om_background_0_fname[GR_NUM_RESOLUTIONS] = {
251         "OptionsMultiGen",                      // GR_640
252         "2_OptionsMultiGen"                     // GR_1024
253 };
254
255 static const char* Om_background_0_mask_fname[GR_NUM_RESOLUTIONS] = {
256         "OptionsMultiGen-M",                    // GR_640
257         "2_OptionsMultiGen-M"           // GR_1024
258 };
259
260 static const char* Om_background_1_fname[GR_NUM_RESOLUTIONS] = {
261         "OptionsMultiVox",                      // GR_640
262         "2_OptionsMultiVox"                     // GR_1024
263 };
264
265 static const char* Om_background_1_mask_fname[GR_NUM_RESOLUTIONS] = {
266         "OptionsMultiVox-M",                    // GR_640
267         "2_OptionsMultiVox-M"           // GR_1024
268 };
269
270 int Om_background_0 = -1;
271 int Om_mask_0             = -1;
272
273 int Om_background_1 = -1;
274 int Om_mask_1       = -1;
275 #endif  // FS1_DEMO
276
277 // screen modes
278 #define OM_MODE_NONE                                                                    -1              // no mode (unintialized)
279 #define OM_MODE_GENERAL                                                         0               // general tab
280 #define OM_MODE_VOX                                                                     1               // voice tab
281 int Om_mode = OM_MODE_NONE;
282
283 // notification stuff
284 #define OM_NOTIFY_TIME                                                          8000
285 #define OM_NOTIFY_Y                                                                     430
286 #define OM_NOTIFY_Y2                                                                    440
287 int Om_notify_stamp = -1;
288 char Om_notify_string[255];
289
290 // load all background bitmaps
291 void options_multi_load_bmaps();
292
293 // unload all the background bitmaps
294 void options_multi_unload_bmaps();
295
296 // add a notification message
297 void options_multi_add_notify(const char *str);
298
299 // process and blit any notification messages
300 void options_multi_notify_process();
301
302
303 // protocol options section -------------------------------------------
304 #ifdef MAKE_FS1
305 #define OM_PRO_NUM_BUTTONS                                                      12
306
307 #define ABORT_GAME_BUTTON                                                       0
308 #define CONTROL_CONFIG_BUTTON                                           1
309 #define HUD_CONFIG_BUTTON                                                       2
310 #define OM_PRO_SCROLL_IP_UP                                                     3
311 #define OM_PRO_SCROLL_IP_DOWN                                           4
312 #define OM_PRO_ADD_IP                                                           5
313 #define OM_PRO_DELETE_IP                                                        6
314 #define OM_PRO_LOCAL_BROADCAST_YES                                      7
315 #define OM_PRO_LOCAL_BROADCAST_NO                                       8
316 #define OM_PRO_VMT                                                                      9
317 #define OM_PRO_VOX_TAB                                                          10
318 #define OM_PRO_GEN_TAB                                                          11
319 #else
320 #define OM_PRO_NUM_BUTTONS                                                              10
321
322 #define OM_PRO_TCP                                                                              0
323 #define OM_PRO_IPX                                                                              1
324 #define OM_PRO_SCROLL_IP_UP                                                     2
325 #define OM_PRO_SCROLL_IP_DOWN                                                   3
326 #define OM_PRO_ADD_IP                                                                   4
327 #define OM_PRO_DELETE_IP                                                                5
328 #define OM_PRO_LOCAL_BROADCAST                                          6
329 #define OM_PRO_VMT                                                                              7
330 #define OM_PRO_VOX_TAB                                                                  8
331 #define OM_PRO_GEN_TAB                                                                  9
332 #endif
333
334 ui_button_info Om_pro_buttons[GR_NUM_RESOLUTIONS][OM_PRO_NUM_BUTTONS] = {
335         { // GR_640
336 #ifdef MAKE_FS1
337                 ui_button_info("OPb_04",        6,              377,    -1,     -1,     4),             // Exit Game
338                 ui_button_info("OPb_05",        448,    335,    -1,     -1,     5),             // Control Config
339                 ui_button_info("OPb_06",        541,    335,    -1,     -1,     6),             // HUD Config
340
341                 ui_button_info("OPb_87",        252,    129,    -1,     -1,     87),    // Scroll IP Up
342                 ui_button_info("OPb_88",        252,    166,    -1,     -1,     88),    // Scroll IP Down
343                 ui_button_info("OPb_85",        26,             131,    -1,     -1,     85),    // Add IP
344                 ui_button_info("OPb_86",        26,             166,    -1,     -1,     86),    // Remove IP
345                 ui_button_info("OPb_89",        26,             221,    -1,     -1,     89),    // Local Broadcast Yes
346                 ui_button_info("OPb_90",        104,    221,    -1,     -1,     90),    // Local Broadcast No
347                 ui_button_info("OPb_91",        26,             243,    -1,     -1,     91),    // Parallax Online
348                 ui_button_info("OPb_92",        402,    72,     -1,     -1,     92),            // Voice Tab
349                 ui_button_info("OPb_93",        487,    72,     -1,     -1,     93),            // General Tab
350 #else
351                 ui_button_info("OMuB_07",       7,              66,     -1,     -1,     7),
352                 ui_button_info("OMuB_08",       7,              84,     -1,     -1,     8),
353                 ui_button_info("OMuB_09",       1,              124,    -1,     -1,     9),
354                 ui_button_info("OMuB_10",       1,              157,    -1,     -1,     10),
355                 ui_button_info("OMuB_11",       20,     207,    -1,     -1,     11),
356                 ui_button_info("OMuB_12",       64,     207,    -1,     -1,     12),
357                 ui_button_info("OMuB_13",       9,              251,    -1,     -1,     13),
358                 ui_button_info("OMuB_14",       9,              282,    -1,     -1,     14),
359                 ui_button_info("OMuB_15",       610,    53,     -1,     -1,     15),
360                 ui_button_info("OMuB_16",       610,    72,     -1,     -1,     16),
361 #endif
362         },
363         { // GR_1024
364                 ui_button_info("2_OMuB_07",     12,     105,    -1,     -1,     7),
365                 ui_button_info("2_OMuB_08",     12,     134,    -1,     -1,     8),
366                 ui_button_info("2_OMuB_09",     2,              198,    -1,     -1,     9),
367                 ui_button_info("2_OMuB_10",     2,              252,    -1,     -1,     10),
368                 ui_button_info("2_OMuB_11",     32,     332,    -1,     -1,     11),
369                 ui_button_info("2_OMuB_12",     103,    332,    -1,     -1,     12),
370                 ui_button_info("2_OMuB_13",     14,     402,    -1,     -1,     13),
371                 ui_button_info("2_OMuB_14",     14,     452,    -1,     -1,     14),
372                 ui_button_info("2_OMuB_15",     976,    85,     -1,     -1,     15),
373                 ui_button_info("2_OMuB_16",     976,    114,    -1,     -1,     16),
374 #ifdef MAKE_FS1
375                 // filler for extra FS1 buttons
376                 ui_button_info("none",          -1,     -1,     -1,     -1,     -1),
377                 ui_button_info("none",          -1,     -1,     -1,     -1,     -1),
378 #endif
379         }
380 };
381
382 UI_GADGET Om_pro_bogus;
383
384 // test
385 #ifndef MAKE_FS1
386 #define OM_PRO_NUM_TEXT         12
387
388 UI_XSTR Om_pro_text[GR_NUM_RESOLUTIONS][OM_PRO_NUM_TEXT] = {
389         { // GR_640
390                 { "TCP",                                1378,   38,     70,     UI_XSTR_COLOR_GREEN, -1, &Om_pro_buttons[0][OM_PRO_TCP].button },
391                 { "IPX",                                1379,   38,     88,     UI_XSTR_COLOR_GREEN, -1, &Om_pro_buttons[0][OM_PRO_IPX].button },
392                 { "IP Address", 1380,   30,     128,    UI_XSTR_COLOR_GREEN, -1, &Om_pro_bogus },
393                 { "add",                                1381,   22,     235,    UI_XSTR_COLOR_GREEN, -1, &Om_pro_buttons[0][OM_PRO_ADD_IP].button },
394                 { "rem.",                       1382,   68,     235,    UI_XSTR_COLOR_GREEN, -1, &Om_pro_buttons[0][OM_PRO_DELETE_IP].button },
395                 { "Broadcast Locally",  1397,   42,     260,    UI_XSTR_COLOR_GREEN, -1, &Om_pro_buttons[0][OM_PRO_LOCAL_BROADCAST].button },   
396                 { "PXO",                                1383,   42,     291,    UI_XSTR_COLOR_GREEN, -1, &Om_pro_buttons[0][OM_PRO_VMT].button },
397                 { "Login",                      1384,   14,     309,    UI_XSTR_COLOR_GREEN, -1, &Om_pro_bogus },
398                 { "Password",           1385,   14,     336,    UI_XSTR_COLOR_GREEN, -1, &Om_pro_bogus },
399                 { "Squadron",           1386,   14,     363,    UI_XSTR_COLOR_GREEN, -1, &Om_pro_bogus },
400                 { "Voice",                      1528,   557,    60,     UI_XSTR_COLOR_GREEN, -1, &Om_pro_buttons[0][OM_PRO_VOX_TAB].button },
401                 { "General",            1388,   542,    77,     UI_XSTR_COLOR_GREEN, -1, &Om_pro_buttons[0][OM_PRO_GEN_TAB].button },   
402         },
403         { // GR_1024
404                 { "TCP",                                1378,   61,     113,    UI_XSTR_COLOR_GREEN, -1, &Om_pro_buttons[1][OM_PRO_TCP].button },
405                 { "IPX",                                1379,   61,     141,    UI_XSTR_COLOR_GREEN, -1, &Om_pro_buttons[1][OM_PRO_IPX].button },
406                 { "IP Address", 1380,   47,     206,    UI_XSTR_COLOR_GREEN, -1, &Om_pro_bogus },
407                 { "add",                                1381,   36,     375,    UI_XSTR_COLOR_GREEN, -1, &Om_pro_buttons[1][OM_PRO_ADD_IP].button },
408                 { "rem.",                       1382,   109,    375,    UI_XSTR_COLOR_GREEN, -1, &Om_pro_buttons[1][OM_PRO_DELETE_IP].button },
409                 { "Broadcast Locally",  1397,   68,     417,    UI_XSTR_COLOR_GREEN, -1, &Om_pro_buttons[1][OM_PRO_LOCAL_BROADCAST].button },   
410                 { "PXO",                                1383,   68,     467,    UI_XSTR_COLOR_GREEN, -1, &Om_pro_buttons[1][OM_PRO_VMT].button },
411                 { "Login",                      1384,   23,     495,    UI_XSTR_COLOR_GREEN, -1, &Om_pro_bogus },
412                 { "Password",           1385,   23,     538,    UI_XSTR_COLOR_GREEN, -1, &Om_pro_bogus },
413                 { "Squadron",           1386,   23,     582,    UI_XSTR_COLOR_GREEN, -1, &Om_pro_bogus },
414                 { "Voice",                      1528,   921,    96,     UI_XSTR_COLOR_GREEN, -1, &Om_pro_buttons[1][OM_PRO_VOX_TAB].button },
415                 { "General",            1388,   902,    123,    UI_XSTR_COLOR_GREEN, -1, &Om_pro_buttons[1][OM_PRO_GEN_TAB].button },   
416         }
417 };
418 #endif
419
420 // defines for the tracker input boxes
421 int Om_tracker_login_coords[GR_NUM_RESOLUTIONS][4] = {
422         {
423 #ifdef MAKE_FS1
424                 62, 284, 175, -1
425 #else
426                 19, 322, 226, -1                // GR_640
427 #endif
428         },
429         {
430                 31, 518, 361, -1                // GR_1024
431         }
432 };
433 int Om_tracker_passwd_coords[GR_NUM_RESOLUTIONS][4] = {
434         {
435 #ifdef MAKE_FS1
436                 62, 328, 175, -1
437 #else
438                 19, 350, 226, -1                // GR_640
439 #endif
440         },
441         {
442                 31, 562, 361, -1                // GR_1024
443         }
444 };
445 int Om_tracker_squad_name_coords[GR_NUM_RESOLUTIONS][4] = {
446         {
447                 19, 378, 226, -1                // GR_640
448         },
449         {
450                 31, 607, 361, -1                // GR_1024
451         }
452 };
453
454 // protocol section tracker login input box
455 UI_INPUTBOX Om_tracker_login;
456
457 // protocol section tracker passwd input box
458 UI_INPUTBOX Om_tracker_passwd;
459
460 // pxo squad name/password
461 UI_INPUTBOX Om_tracker_squad_name;
462
463 #define TRACKER_FOCUS_NONE                      0
464 #define TRACKER_FOCUS_LOGIN             1
465 #define TRACKER_FOCUS_PASSWORD  2
466 #define TRACKER_FOCUS_SQUADRON  3
467 static int Om_tracker_focus = 0;
468
469 // ip address list vars
470 #define IP_STRING_LEN                                                           255
471 #define MAX_IP_ADDRS                                                                    100
472
473 #define IP_EMPTY_STRING                                                         ""
474
475 int Ip_list_coords[GR_NUM_RESOLUTIONS][4] = {
476         {
477 #ifdef MAKE_FS1
478                 106, 147, 141, 48
479 #else
480                 29, 137, 227, 67                // GR_640
481 #endif
482         },
483         {
484                 46, 220, 364, 106               // GR_1024
485         }
486 };
487
488
489 int Ip_list_max_display[GR_NUM_RESOLUTIONS] = {
490         5,
491         5
492 };
493
494 static int Ip_input_coords[GR_NUM_RESOLUTIONS][4] = {
495         {
496 #ifdef MAKE_FS1
497                 107, 138, 140, -1
498 #else
499                 109, 128, 140, -1               // GR_640
500 #endif
501         },
502         {
503                 132, 206, 261, -1               // GR_640
504         }
505 };
506
507 int Om_input_mode = 0;
508 int Om_ip_start;                                                                                                // index of the first element to be displayed in the list box
509 int Om_ip_selected;                                        // the selected default IP address
510 int Om_ip_disp_count;                              // how many items are currently being displayed
511 int Om_num_ips;                                                                                         // # of ip addresses we have
512 char Om_ip_addrs[MAX_IP_ADDRS][IP_STRING_LEN];          // the ip addresses themselves
513 UI_BUTTON Om_ip_button;                                                                         // button for detecting clicks on the ip address list
514 UI_INPUTBOX Om_ip_input;                                                                        // input box for adding new ip addresses
515
516 // setting vars
517 int Om_local_broadcast;                                                                         // whether the player has local broadcast selected or not
518 int Om_tracker_flag;                                                                                    // if the guy has the tracker selected
519 int Om_protocol;                                                                                                // protocol in use
520
521 // load all the controls for the protocol section
522 void options_multi_load_protocol_controls();
523
524 // disable/hide all the controls for the protocol section
525 void options_multi_disable_protocol_controls();
526
527 // enable/unhide all the controls for the protocol section
528 void options_multi_enable_protocol_controls();
529
530 // intialize the protocol section vars
531 void options_multi_init_protocol_vars();
532
533 // do frame for the protocol section
534 void options_multi_protocol_do(int key);
535
536 // if the accept button was hit
537 void options_multi_protocol_accept();
538
539 // check for button presses
540 void options_multi_protocol_check_buttons();
541
542 // if a button was pressed
543 void options_multi_protocol_button_pressed(int n);
544
545 // load the ip address file
546 void options_multi_protocol_load_ip_file();
547
548 // save the ip address file
549 void options_multi_protocol_save_ip_file();
550
551 // draw the list of ip addresses
552 void options_multi_protocol_display_ips();
553
554 // scroll the list of ip addresses down
555 void options_multi_protocol_scroll_ip_down();
556
557 // scroll the list of ip addresses up
558 void options_multi_protocol_scroll_ip_up();
559
560 // check the ip list to see if the user has selected a new item
561 void options_multi_protocol_check_ip_list();
562
563 // delete the currently selected ip if any
564 void options_multi_protocol_delete_ip();
565
566 // attempt to add the currently entered ip address
567 void options_multi_protocol_add_current_ip();
568
569
570 // general options tab section -------------------------------------------
571 #define OM_GEN_NUM_BUTTONS                                                              10
572
573 #define OM_GEN_OBJ_LOW                                                                  0
574 #define OM_GEN_OBJ_MED                                                                  1
575 #define OM_GEN_OBJ_HIGH                                                                 2
576 #define OM_GEN_OBJ_LAN                                                                  3
577 #define OM_GEN_PIX_YES                                                                  4
578 #define OM_GEN_PIX_NO                                                                   5
579 #define OM_GEN_XFER_MULTIDATA_YES                                       6
580 #define OM_GEN_XFER_MULTIDATA_NO                                                7
581 #define OM_GEN_FLUSH_NO                                                                 8
582 #define OM_GEN_FLUSH_YES                                                                9
583
584 ui_button_info Om_gen_buttons[GR_NUM_RESOLUTIONS][OM_GEN_NUM_BUTTONS] = {
585         { // GR_640
586 #ifdef MAKE_FS1
587                 ui_button_info("OPb_94",        410,    121,    -1,     -1,     94),
588                 ui_button_info("OPb_95",        410,    143,    -1,     -1,     95),
589                 ui_button_info("OPb_96",        410,    165,    -1,     -1,     96),
590                 ui_button_info("none",          -1,             -1,             -1,     -1,     -1),    // no LAN setting in FS1
591                 ui_button_info("OPb_97",        410,    199,    -1,     -1,     97),
592                 ui_button_info("OPb_98",        496,    199,    -1,     -1,     98),
593                 ui_button_info("OPb_99",        410,    233,    -1,     -1,     99),
594                 ui_button_info("OPb_100",       410,    256,    -1,     -1,     100),
595                 ui_button_info("OPb_101",       410,    290,    -1,     -1,     101),
596                 ui_button_info("OPb_102",       410,    313,    -1,     -1,     102),
597 #else
598                 ui_button_info("OGB_17",        598,    117,    -1,     -1,     17),
599                 ui_button_info("OGB_18",        598,    139,    -1,     -1,     18),
600                 ui_button_info("OGB_19",        598,    161,    -1,     -1,     19),
601                 ui_button_info("OGB_20",        598,    183,    -1,     -1,     20),
602                 ui_button_info("OGB_21",        549,    229,    -1,     -1,     21),
603                 ui_button_info("OGB_22",        598,    229,    -1,     -1,     22),
604                 ui_button_info("OGB_23",        598,    286,    -1,     -1,     23),
605                 ui_button_info("OGB_24",        598,    307,    -1,     -1,     24),
606                 ui_button_info("OGB_25",        598,    347,    -1,     -1,     25),
607                 ui_button_info("OGB_26",        598,    368,    -1,     -1,     26),
608 #endif
609         },
610         { // GR_1024
611                 ui_button_info("2_OGB_17",      957,    188,    -1,     -1,     17),
612                 ui_button_info("2_OGB_18",      957,    223,    -1,     -1,     18),
613                 ui_button_info("2_OGB_19",      957,    258,    -1,     -1,     19),
614                 ui_button_info("2_OGB_20",      957,    293,    -1,     -1,     20),
615                 ui_button_info("2_OGB_21",      879,    366,    -1,     -1,     21),
616                 ui_button_info("2_OGB_22",      957,    366,    -1,     -1,     22),
617                 ui_button_info("2_OGB_23",      957,    457,    -1,     -1,     23),
618                 ui_button_info("2_OGB_24",      957,    491,    -1,     -1,     24),
619                 ui_button_info("2_OGB_25",      957,    555,    -1,     -1,     25),
620                 ui_button_info("2_OGB_26",      957,    589,    -1,     -1,     26),
621         }
622 };
623
624 UI_GADGET Om_gen_bogus;
625
626 // text
627 #ifndef MAKE_FS1
628 #define OM_GEN_NUM_TEXT                                 14
629
630 UI_XSTR Om_gen_text[GR_NUM_RESOLUTIONS][OM_GEN_NUM_TEXT] = {
631         { // GR_640
632                 { "Object Update",      1391,           511,    104,    UI_XSTR_COLOR_GREEN,    -1,     &Om_gen_bogus },                
633                 { "Low",                                        1160,           558,    127,    UI_XSTR_COLOR_GREEN,    -1,     &Om_gen_buttons[0][OM_GEN_OBJ_LOW].button },
634                 { "Medium",                             1161,           538,    149,    UI_XSTR_COLOR_GREEN,    -1,     &Om_gen_buttons[0][OM_GEN_OBJ_MED].button },            
635                 { "High",                               1162,           556,    171,    UI_XSTR_COLOR_GREEN,    -1,     &Om_gen_buttons[0][OM_GEN_OBJ_HIGH].button },           
636                 { "Lan",                                        1392,           561,    193,    UI_XSTR_COLOR_GREEN,    -1,     &Om_gen_buttons[0][OM_GEN_OBJ_LAN].button },            
637                 { "Pilot / Squad Images",       1393,           463,    214,    UI_XSTR_COLOR_GREEN,    -1,     &Om_gen_bogus },                
638                 { "Yes",                                        1394,           555,    257,    UI_XSTR_COLOR_GREEN,    -1,     &Om_gen_buttons[0][OM_GEN_PIX_YES].button },            
639                 { "No",                                 1395,           604,    257,    UI_XSTR_COLOR_GREEN,    -1,     &Om_gen_buttons[0][OM_GEN_PIX_NO].button },             
640                 { "Transfer Missions",          1396,           478,    271,    UI_XSTR_COLOR_GREEN,    -1,     &Om_gen_bogus },                
641                 { "/multidata",         1397,           519,    292,    UI_XSTR_COLOR_GREEN,    -1,     &Om_gen_buttons[0][OM_GEN_XFER_MULTIDATA_YES].button },         
642                 { "/missions",                  1398,           527,    314,    UI_XSTR_COLOR_GREEN,    -1,     &Om_gen_buttons[0][OM_GEN_XFER_MULTIDATA_NO].button },          
643                 { "Flush Cache",                1399,           529,    334,    UI_XSTR_COLOR_GREEN,    -1,     &Om_gen_bogus },                
644                 { "Never",                              1400,           548,    355,    UI_XSTR_COLOR_GREEN,    -1,     &Om_gen_buttons[0][OM_GEN_FLUSH_NO].button },           
645                 { "Before Game",                1401,           502,    377,    UI_XSTR_COLOR_GREEN,    -1,     &Om_gen_buttons[0][OM_GEN_FLUSH_YES].button },          
646         },
647         { // GR_1024
648                 { "Object Update",      1391,           818,    166,    UI_XSTR_COLOR_GREEN,    -1,     &Om_gen_bogus },                
649                 { "Low",                                        1160,           913,    204,    UI_XSTR_COLOR_GREEN,    -1,     &Om_gen_buttons[1][OM_GEN_OBJ_LOW].button },
650                 { "Medium",                             1161,           892,    239,    UI_XSTR_COLOR_GREEN,    -1,     &Om_gen_buttons[1][OM_GEN_OBJ_MED].button },            
651                 { "High",                               1162,           909,    274,    UI_XSTR_COLOR_GREEN,    -1,     &Om_gen_buttons[1][OM_GEN_OBJ_HIGH].button },           
652                 { "Lan",                                        1392,           916,    310,    UI_XSTR_COLOR_GREEN,    -1,     &Om_gen_buttons[1][OM_GEN_OBJ_LAN].button },            
653                 { "Pilot / Squad Images",       1393,           821,    345,    UI_XSTR_COLOR_GREEN,    -1,     &Om_gen_bogus },                
654                 { "Yes",                                        1394,           887,    411,    UI_XSTR_COLOR_GREEN,    -1,     &Om_gen_buttons[1][OM_GEN_PIX_YES].button },            
655                 { "No",                                 1395,           966,    411,    UI_XSTR_COLOR_GREEN,    -1,     &Om_gen_buttons[1][OM_GEN_PIX_NO].button },             
656                 { "Transfer Missions",          1396,           844,    435,    UI_XSTR_COLOR_GREEN,    -1,     &Om_gen_bogus },                
657                 { "/multidata",         1397,           858,    468,    UI_XSTR_COLOR_GREEN,    -1,     &Om_gen_buttons[1][OM_GEN_XFER_MULTIDATA_YES].button },         
658                 { "/missions",                  1398,           870,    503,    UI_XSTR_COLOR_GREEN,    -1,     &Om_gen_buttons[1][OM_GEN_XFER_MULTIDATA_NO].button },          
659                 { "Flush Cache",                1399,           886,    533,    UI_XSTR_COLOR_GREEN,    -1,     &Om_gen_bogus },                
660                 { "Never",                              1400,           897,    568,    UI_XSTR_COLOR_GREEN,    -1,     &Om_gen_buttons[1][OM_GEN_FLUSH_NO].button },           
661                 { "Before Game",                1401,           849,    603,    UI_XSTR_COLOR_GREEN,    -1,     &Om_gen_buttons[1][OM_GEN_FLUSH_YES].button },          
662         }
663 };
664 #endif
665
666 // setting vars
667 int Om_gen_obj_update;                                                          // object update level
668 int Om_gen_pix;                                                                         // accept pilot pix or not
669 int Om_gen_xfer_multidata;                                                      // xfer missions to multidata or not
670 int Om_gen_flush_cache;                                                         // flush multidata directory before every game
671
672 // load all the general tab controls
673 void options_multi_load_gen_controls();
674
675 // disable/hide all the general tab controls
676 void options_multi_disable_gen_controls();
677
678 // enable/unhide all the general tab controls
679 void options_multi_enable_gen_controls();
680
681 // initialize the general tab vars
682 void options_multi_init_gen_vars();
683
684 // accept function for the general tab
685 void options_multi_gen_accept();
686
687 // do frame for the general tab
688 void options_multi_gen_do();
689
690 // check for button presses
691 void options_multi_gen_check_buttons();
692
693 // a button was pressed
694 void options_multi_gen_button_pressed(int n);
695
696
697 // voice options tab section -------------------------------------------
698 #define OM_VOX_NUM_BUTTONS                                                              6
699
700 #define OM_VOX_VOICE_TEST                                                               0
701 #define OM_VOX_VOICE_YES                                                                1
702 #define OM_VOX_VOICE_NO                                                                 2
703 #define OM_VOX_PLIST_UP                                                                 3
704 #define OM_VOX_PLIST_DOWN                                                               4
705 #define OM_VOX_VOICE_MUTE                                                               5
706
707 UI_GADGET Om_vox_bogus;
708
709 ui_button_info Om_vox_buttons[GR_NUM_RESOLUTIONS][OM_VOX_NUM_BUTTONS] = {
710         { // GR_640
711 #ifdef MAKE_FS1
712                 ui_button_info("OPb_103",       413,    110,    -1,     -1,     103),
713                 ui_button_info("OPb_105",       413,    186,    -1,     -1,     105),
714                 ui_button_info("OPb_106",       491,    186,    -1,     -1,     106),
715                 ui_button_info("OPb_108",       413,    253,    -1,     -1,     108),
716                 ui_button_info("OPb_109",       413,    291,    -1,     -1,     109),
717                 ui_button_info("OPb_107",       413,    229,    -1,     -1,     107),
718 #else
719                 ui_button_info("OVB_17",        562,    118,    -1,     -1,     17),
720                 ui_button_info("OVB_19",        551,    208,    -1,     -1,     19),
721                 ui_button_info("OVB_20",        599,    208,    -1,     -1,     20),
722                 ui_button_info("OVB_21",        614,    256,    -1,     -1,     21),
723                 ui_button_info("OVB_22",        614,    290,    -1,     -1,     22),
724                 ui_button_info("OVB_23",        599,    354,    -1,     -1,     23),
725 #endif
726         },
727         { // GR_640
728                 ui_button_info("2_OVB_17",      900,    189,    -1,     -1,     17),
729                 ui_button_info("2_OVB_19",      882,    333,    -1,     -1,     19),
730                 ui_button_info("2_OVB_20",      959,    333,    -1,     -1,     20),
731                 ui_button_info("2_OVB_21",      983,    410,    -1,     -1,     21),
732                 ui_button_info("2_OVB_22",      983,    464,    -1,     -1,     22),
733                 ui_button_info("2_OVB_23",      959,    566,    -1,     -1,     23),
734         }
735 };
736
737 // text
738 #ifndef MAKE_FS1
739 #define OM_VOX_NUM_TEXT                                 6
740
741 UI_XSTR Om_vox_text[GR_NUM_RESOLUTIONS][OM_VOX_NUM_TEXT] = {
742         { // GR_640
743                 { "Mic test",                           1389,           567,    104,    UI_XSTR_COLOR_GREEN,    -1, &Om_vox_buttons[0][OM_VOX_VOICE_TEST].button },
744                 { "Voice Quality",              1531,           439,    149,    UI_XSTR_COLOR_GREEN,    -1, &Om_vox_bogus },
745                 { "Voice Transmission", 1530,           439,    193,    UI_XSTR_COLOR_GREEN,    -1, &Om_vox_bogus },
746                 { "On",                                         1285,           556,    233,    UI_XSTR_COLOR_GREEN,    -1, &Om_vox_buttons[0][OM_VOX_VOICE_YES].button },
747                 { "Off",                                                1286,           604,    233,    UI_XSTR_COLOR_GREEN,    -1, &Om_vox_buttons[0][OM_VOX_VOICE_NO].button },
748                 { "Mute",                                       1390,           594,    381,    UI_XSTR_COLOR_GREEN,    -1, &Om_vox_buttons[0][OM_VOX_VOICE_MUTE].button },
749         },
750         { // GR_1024
751                 { "mic test",                           1389,           908,    166,    UI_XSTR_COLOR_GREEN,    -1, &Om_vox_buttons[1][OM_VOX_VOICE_TEST].button },
752                 { "Voice Quality",              1531,           703,    239,    UI_XSTR_COLOR_GREEN,    -1, &Om_vox_bogus },
753                 { "Voice Transmission", 1530,           783,    310,    UI_XSTR_COLOR_GREEN,    -1, &Om_vox_bogus },
754                 { "On",                                         1285,           890,    373,    UI_XSTR_COLOR_GREEN,    -1, &Om_vox_buttons[1][OM_VOX_VOICE_YES].button },
755                 { "Off",                                                1286,           967,    373,    UI_XSTR_COLOR_GREEN,    -1, &Om_vox_buttons[1][OM_VOX_VOICE_NO].button },
756                 { "Mute",                                       1390,           950,    609,    UI_XSTR_COLOR_GREEN,    -1, &Om_vox_buttons[1][OM_VOX_VOICE_MUTE].button },
757         }
758 };
759 #endif
760
761 #define NUM_OM_VOX_SLIDERS                              1
762 #define OM_VOX_QOS_SLIDER                               0
763
764 op_sliders Om_vox_sliders[GR_NUM_RESOLUTIONS][NUM_OM_VOX_SLIDERS] = {
765         { // GR_640                             
766 #ifdef MAKE_FS1
767                 op_sliders("OPb_104",   413,    151,    -1,     -1,     104,    20,     10,     NULL, -1,       -1,     -1,     NULL,   -1,     -1,     -1),
768 #else
769                 op_sliders("OVB_18",    429,    162,    -1,     -1,     18,     20,     10, NULL, -1, -1, -1, NULL, -1, -1, -1),        // voice QOS
770 #endif
771         },      
772         { // GR_1024                            
773                 op_sliders("2_OVB_18",  686,    259,    -1,     -1,     18,     32,     10, NULL, -1, -1, -1, NULL, -1, -1, -1),        // voice QOS
774         }
775 };
776
777 // player list area
778 int Om_vox_plist_coords[GR_NUM_RESOLUTIONS][4] = {
779         {       // GR_640
780 #ifdef MAKE_FS1
781                 445, 260, 142, 61
782 #else
783                 377, 270, 232, 79
784 #endif
785         },
786         {       // GR_1024
787                 604, 432, 371, 127
788         }
789 };
790 int Om_vox_plist_max_display[GR_NUM_RESOLUTIONS] = {
791         6,
792         6
793 };
794
795 int Om_vox_plist_start;
796 UI_BUTTON Om_vox_plist_button;
797
798 // voice test buffer
799 #define OM_VOX_BUF_SIZE                         (1<<12)
800 #define OM_VOX_COMP_SIZE                        ((1<<15) + (1<<14))
801
802 #define OM_VOX_WAVE_Y                           240
803 #define OM_VOX_WAVE_WIDTH                       300
804
805 #define OM_VOX_DROP_ICON_X                      100
806 #define OM_VOX_DROP_ICON_Y                      100
807
808 #define OM_VOX_RECORD_INT                       175
809
810 unsigned char Om_vox_voice_buffer[OM_VOX_BUF_SIZE];
811 int Om_vox_voice_buffer_size = -1;
812
813 unsigned char Om_vox_comp_buffer[OM_VOX_COMP_SIZE];
814 int Om_vox_voice_comp_size = -1;
815
816 int Om_vox_playback_handle;
817
818 // status of any test voice recording
819 #define OM_VOX_TEST_NONE                                        -1
820 #define OM_VOX_TEST_RECORDING                           0
821 #define OM_VOX_TEST_PLAYBACK                            1
822 int Om_vox_test_status = OM_VOX_TEST_NONE;
823
824 // setting vars
825 int Om_vox_accept_voice;
826
827 // simple list of players we are looking through
828 net_player *Om_vox_players[MAX_PLAYERS];
829
830 // selected player
831 net_player *Om_vox_player_select;
832
833 // vox QoS setting
834 static int Om_vox_qos_pos;
835
836 // mute or don't mute for each player
837 int Om_vox_player_flags[MAX_PLAYERS];
838
839 // the # of players
840 int Om_vox_num_players;
841
842 // load all the voice tab controls
843 void options_multi_load_vox_controls();
844
845 // disable/hide all the voice tab controls
846 void options_multi_disable_vox_controls();
847
848 // enable/unhide all the voice tab controls
849 void options_multi_enable_vox_controls();
850
851 // initialize the voice tab vars
852 void options_multi_init_vox_vars();
853
854 // accept function for the voice tab
855 void options_multi_vox_accept();
856
857 // do frame for the voice tab
858 void options_multi_vox_do();
859
860 // check for button presses
861 void options_multi_vox_check_buttons();
862
863 // a button was pressed
864 void options_multi_vox_button_pressed(int n);
865
866 // process/display the player list
867 void options_multi_vox_process_player_list();
868
869 // scroll the player list down
870 void options_multi_vox_plist_scroll_down();
871
872 // scroll the player list up
873 void options_multi_vox_plist_scroll_up();
874
875 // get the index into the player list of the passed netplayer
876 int options_multi_vox_plist_get(net_player *pl);
877
878
879 // general data section ------------------------------------------------
880
881 // load all background bitmaps
882 void options_multi_load_bmaps()
883 {
884 #ifndef FS1_DEMO
885         // load both background bitmaps
886         Om_background_0 = bm_load(Om_background_0_fname[gr_screen.res]);
887         if(Om_background_0 == -1){
888                 nprintf(("Network","Error loading options background %s\n",Om_background_0_fname[gr_screen.res]));
889         }
890
891         Om_background_1 = bm_load(Om_background_1_fname[gr_screen.res]);
892         if(Om_background_1 == -1){
893                 nprintf(("Network","Error loading options background %s\n",Om_background_1_fname[gr_screen.res]));
894         }
895
896         // load in both mask bitmaps
897         Om_mask_0 = bm_load(Om_background_0_mask_fname[gr_screen.res]);
898         if(Om_mask_0 == -1){
899                 nprintf(("Network","Error loading options background mask %s\n",Om_background_0_mask_fname[gr_screen.res]));
900         }
901
902         Om_mask_1 = bm_load(Om_background_1_mask_fname[gr_screen.res]);
903         if(Om_mask_1 == -1){
904                 nprintf(("Network","Error loading options background mask %s\n",Om_background_1_mask_fname[gr_screen.res]));
905         }
906 #endif
907 }
908
909 // unload all the background bitmaps
910 void options_multi_unload_bmaps()
911 {
912 #ifndef FS1_DEMO
913         // unload all background bitmaps
914         if(Om_background_0 != -1){
915                 bm_release(Om_background_0);
916                 Om_background_0 = -1;
917         } 
918         if(Om_background_1 != -1){
919                 bm_release(Om_background_1);
920                 Om_background_1 = -1;
921         }
922
923         // unload all mask bitmaps
924         if(Om_mask_0 != -1){
925                 bm_release(Om_mask_0);
926                 Om_mask_0 = -1;
927         }
928         if(Om_mask_1 != -1){
929                 bm_release(Om_mask_1);
930                 Om_mask_1 = -1;
931         }
932 #endif
933 }
934
935 // add a notification message
936 void options_multi_add_notify(const char *str)
937 {
938         // copy the string
939         memset(Om_notify_string,0,255);
940         if(str != NULL){                
941                 SDL_strlcpy(Om_notify_string, str, SDL_arraysize(Om_notify_string));
942         }               
943
944         // set the timestamp
945         Om_notify_stamp = timestamp(OM_NOTIFY_TIME);
946 }
947
948 // process and blit any notification messages
949 void options_multi_notify_process()
950 {
951         int w;
952         char *p_str[3];
953         int n_chars[3];
954         char line[255];
955         int line_count;
956         int y_start;
957         int idx;
958         int len;
959         
960         // if there is no timestamp, do nothing
961         if(Om_notify_stamp == -1){
962                 return;
963         }
964
965         // otherwise, if it has elapsed, unset it
966         if(timestamp_elapsed(Om_notify_stamp)){
967                 Om_notify_stamp = -1;
968                 return;
969         }
970
971         // otherwise display the string
972         line_count = split_str(Om_notify_string, 600, n_chars, p_str, 3);       
973         y_start = OM_NOTIFY_Y;
974         gr_set_color_fast(&Color_bright);
975         for(idx=0;idx<line_count;idx++){
976                 len = SDL_min(n_chars[idx] + 1, (int)SDL_arraysize(line));
977                 SDL_strlcpy(line, p_str[idx], len);
978
979                 gr_get_string_size(&w,NULL,line);
980                 gr_string((600 - w)/2,y_start,line);
981
982                 y_start += 10;
983         }       
984 }
985
986
987 // protocol section --------------------------------------------------------
988
989 // load all the controls for the protocol section
990 void options_multi_load_protocol_controls()
991 {
992         int idx;
993         
994         SDL_assert(Om_window != NULL);
995
996         // instantiate all the buttons
997         for(idx=0; idx<OM_PRO_NUM_BUTTONS; idx++){
998                 // create the object
999                 Om_pro_buttons[gr_screen.res][idx].button.create(Om_window, "", Om_pro_buttons[gr_screen.res][idx].x, Om_pro_buttons[gr_screen.res][idx].y, 1, 1, 0, 1);
1000
1001                 // set the sound to play when highlighted
1002                 Om_pro_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
1003
1004                 // set the ani for the button
1005                 Om_pro_buttons[gr_screen.res][idx].button.set_bmaps(Om_pro_buttons[gr_screen.res][idx].filename);
1006
1007                 // set the hotspot
1008                 Om_pro_buttons[gr_screen.res][idx].button.link_hotspot(Om_pro_buttons[gr_screen.res][idx].hotspot);
1009         }
1010
1011 #ifndef MAKE_FS1
1012         // text
1013         for(idx=0; idx<OM_PRO_NUM_TEXT; idx++){
1014                 Om_window->add_XSTR(&Om_pro_text[gr_screen.res][idx]);
1015         }
1016 #endif
1017
1018         // create the tracker input boxes       
1019         Om_tracker_login.create(Om_window, Om_tracker_login_coords[gr_screen.res][0], Om_tracker_login_coords[gr_screen.res][1], Om_tracker_login_coords[gr_screen.res][2], LOGIN_LEN - 1, Multi_tracker_login, UI_INPUTBOX_FLAG_INVIS | UI_INPUTBOX_FLAG_ESC_CLR | UI_INPUTBOX_FLAG_KEYTHRU | UI_INPUTBOX_FLAG_NO_BACK);
1020         Om_tracker_passwd.create(Om_window, Om_tracker_passwd_coords[gr_screen.res][0], Om_tracker_passwd_coords[gr_screen.res][1], Om_tracker_passwd_coords[gr_screen.res][2], PASSWORD_LEN - 1, Multi_tracker_passwd, UI_INPUTBOX_FLAG_INVIS | UI_INPUTBOX_FLAG_ESC_CLR | UI_INPUTBOX_FLAG_PASSWD | UI_INPUTBOX_FLAG_KEYTHRU | UI_INPUTBOX_FLAG_NO_BACK);
1021         Om_tracker_squad_name.create(Om_window, Om_tracker_squad_name_coords[gr_screen.res][0], Om_tracker_squad_name_coords[gr_screen.res][1], Om_tracker_squad_name_coords[gr_screen.res][2], LOGIN_LEN - 1, Multi_tracker_squad_name, UI_INPUTBOX_FLAG_INVIS | UI_INPUTBOX_FLAG_ESC_CLR | UI_INPUTBOX_FLAG_KEYTHRU | UI_INPUTBOX_FLAG_NO_BACK);
1022
1023         // create the invisible button for checking for clicks on the ip address list
1024         Om_ip_button.create(Om_window, "", Ip_list_coords[gr_screen.res][0], Ip_list_coords[gr_screen.res][1], Ip_list_coords[gr_screen.res][2], Ip_list_coords[gr_screen.res][3], 0, 1);
1025         Om_ip_button.hide();
1026
1027         // create the new ip address input box  
1028         Om_ip_input.create(Om_window, Ip_input_coords[gr_screen.res][0], Ip_input_coords[gr_screen.res][1], Ip_input_coords[gr_screen.res][2], IP_STRING_LEN, IP_EMPTY_STRING, UI_INPUTBOX_FLAG_INVIS | UI_INPUTBOX_FLAG_ESC_CLR | UI_INPUTBOX_FLAG_KEYTHRU);
1029         Om_ip_input.hide();
1030         Om_ip_input.disable();
1031
1032 #ifndef MAKE_FS1
1033         // disable IPX button
1034         Om_pro_buttons[gr_screen.res][OM_PRO_IPX].button.disable();
1035         Om_pro_buttons[gr_screen.res][OM_PRO_IPX].button.hide();
1036 #endif
1037
1038         // bogus control
1039         Om_pro_bogus.base_create(Om_window, UI_KIND_ICON, 0, 0, 0, 0);
1040 }
1041
1042 // disable/hide all the controls for the protocol section
1043 void options_multi_disable_protocol_controls()
1044 {
1045         int idx;
1046         
1047         // hide and disable all the protocol buttons
1048         for(idx=0;idx<OM_PRO_NUM_BUTTONS;idx++){
1049                 // hide the button
1050                 Om_pro_buttons[gr_screen.res][idx].button.hide();
1051
1052                 // disable the button
1053                 Om_pro_buttons[gr_screen.res][idx].button.disable();
1054         }
1055
1056         // hide and disable the tracker input boxes
1057         Om_tracker_login.hide();
1058         Om_tracker_login.disable();
1059         Om_tracker_passwd.hide();
1060         Om_tracker_passwd.disable();
1061         Om_tracker_squad_name.hide();
1062         Om_tracker_squad_name.disable();
1063
1064         // disable the click detection button
1065         Om_ip_button.disable();
1066
1067         // disable and hide the ip address inputbox
1068         Om_ip_input.hide();
1069         Om_ip_input.disable();
1070
1071         // undo input mode if necessary
1072         Om_input_mode = 0;
1073
1074         // bogus control
1075         Om_pro_bogus.hide();
1076         Om_pro_bogus.disable();
1077 }
1078
1079 // enable/unhide all the controls for the protocol section
1080 void options_multi_enable_protocol_controls()
1081 {
1082         int idx;
1083         
1084         // unhide and enable all the protocol buttons
1085         for(idx=0;idx<OM_PRO_NUM_BUTTONS;idx++){
1086                 // enable the button
1087                 Om_pro_buttons[gr_screen.res][idx].button.enable();
1088                 
1089                 // unhide the button
1090                 Om_pro_buttons[gr_screen.res][idx].button.unhide();             
1091         }
1092
1093         // unhide and enable the tracker input boxes
1094         if(Om_tracker_flag){
1095                 Om_tracker_login.enable();      
1096                 Om_tracker_passwd.enable();
1097                 Om_tracker_squad_name.enable();
1098         }
1099         Om_tracker_login.unhide();
1100         Om_tracker_passwd.unhide();
1101         Om_tracker_squad_name.unhide();
1102
1103         // enable the click detection button
1104         Om_ip_button.enable();  
1105
1106         // bogus control
1107         Om_pro_bogus.enable();
1108         Om_pro_bogus.unhide();  
1109 }
1110
1111 // intialize the protocol section vars
1112 void options_multi_init_protocol_vars()
1113 {
1114         // current protocol
1115         Om_protocol = Multi_options_g.protocol;
1116
1117         // whether or not the user has the local broadcast button selected
1118         Om_local_broadcast = (Player->m_local_options.flags & MLO_FLAG_LOCAL_BROADCAST) ? 1 : 0;
1119
1120         // whether or not we're playing on the tracker
1121         Om_tracker_flag = Multi_options_g.pxo ? 1 : 0;
1122
1123         // load the ip address list     
1124         Om_ip_disp_count = 0;
1125         options_multi_protocol_load_ip_file();
1126         Om_ip_selected = Om_num_ips - 1;        
1127         Om_ip_start = Om_num_ips - 1;                   
1128 }
1129
1130 // do frame for the protocol section
1131 void options_multi_protocol_do(int key)
1132 {
1133         // check for button presses
1134         options_multi_protocol_check_buttons();
1135         
1136         // force draw the correct "local broadcast" button      
1137         if(Om_local_broadcast){
1138 #ifdef MAKE_FS1
1139                 Om_pro_buttons[gr_screen.res][OM_PRO_LOCAL_BROADCAST_YES].button.draw_forced(2);
1140         } else {
1141                 Om_pro_buttons[gr_screen.res][OM_PRO_LOCAL_BROADCAST_NO].button.draw_forced(2);
1142 #else
1143                 Om_pro_buttons[gr_screen.res][OM_PRO_LOCAL_BROADCAST].button.draw_forced(2);
1144 #endif
1145         }
1146
1147         // draw the "vmt" button if it is selected
1148         if(Om_tracker_flag){
1149                 Om_pro_buttons[gr_screen.res][OM_PRO_VMT].button.draw_forced(2);
1150         }
1151
1152         // see if he hit any interesting key presses
1153         switch(key){
1154         case SDLK_RETURN:
1155                 // add a new ip string if we're in "input" mode
1156                 if(Om_input_mode){                      
1157                         options_multi_protocol_add_current_ip();
1158                         
1159                         // clear the text control and input mode
1160                         Om_ip_input.set_text("");
1161                         Om_ip_input.clear_focus();
1162                         Om_ip_input.disable();
1163                         Om_input_mode = 0;
1164                 }
1165
1166                 // if the tracker login inputbox has focus, lose it
1167                 if(Om_tracker_login.has_focus()){
1168                         Om_tracker_login.clear_focus();
1169                         gamesnd_play_iface(SND_COMMIT_PRESSED);
1170                 }
1171                 // if the tracker password inputbox has focus, lose it
1172                 if(Om_tracker_passwd.has_focus()){
1173                         Om_tracker_passwd.clear_focus();
1174                         gamesnd_play_iface(SND_COMMIT_PRESSED);
1175                 }
1176                 // if the tracker squad name inputbox has focus, lose it
1177                 if(Om_tracker_squad_name.has_focus()){
1178                         Om_tracker_squad_name.clear_focus();
1179                         gamesnd_play_iface(SND_COMMIT_PRESSED);
1180                 }
1181                 break;
1182
1183         case SDLK_ESCAPE:
1184                 // if we're in input mode, cancel out
1185                 if(Om_input_mode){
1186                         // clear the text control and input mode
1187                         Om_ip_input.set_text("");
1188                         Om_ip_input.clear_focus();
1189                         Om_ip_input.disable();
1190                         Om_input_mode = 0;
1191                 }
1192                 // otherwise quit the options screen altogether
1193                 else {
1194                         options_cancel_exit();
1195                 }
1196                 break;
1197
1198         case SDLK_TAB:
1199                 // tab through the tracker input controls
1200                 if(Om_tracker_login.has_focus()){
1201                         Om_tracker_passwd.set_focus();
1202                 } else if(Om_tracker_passwd.has_focus()){
1203                         Om_tracker_squad_name.set_focus();
1204                 } else if(Om_tracker_squad_name.has_focus()){
1205                         Om_tracker_login.set_focus();
1206                 }
1207                 break;
1208         }
1209
1210         // force draw the proper protocol
1211 #ifndef MAKE_FS1        // not in FS1 menu
1212         Om_pro_buttons[gr_screen.res][OM_PRO_TCP].button.draw_forced(2);
1213 #endif
1214
1215         // force draw the proper tab button
1216         switch (Om_mode) {
1217         case OM_MODE_GENERAL:
1218                 Om_pro_buttons[gr_screen.res][OM_PRO_GEN_TAB].button.draw_forced(2);
1219                 break;
1220
1221         case OM_MODE_VOX:
1222                 Om_pro_buttons[gr_screen.res][OM_PRO_VOX_TAB].button.draw_forced(2);
1223                 break;
1224         }
1225
1226         // check to see if the user has clicked on the ip list and selected a new item
1227         options_multi_protocol_check_ip_list();
1228
1229         // draw the list of ip addresses
1230         options_multi_protocol_display_ips();   
1231
1232         // hack to play sound when input boxes gain focus
1233         if (Om_tracker_login.has_focus()) {
1234                 if (Om_tracker_focus != TRACKER_FOCUS_LOGIN) {
1235                         Om_tracker_focus = TRACKER_FOCUS_LOGIN;
1236                         gamesnd_play_iface(SND_USER_SELECT);
1237                 }
1238         } else if (Om_tracker_passwd.has_focus()) {
1239                 if (Om_tracker_focus != TRACKER_FOCUS_PASSWORD) {
1240                         Om_tracker_focus = TRACKER_FOCUS_PASSWORD;
1241                         gamesnd_play_iface(SND_USER_SELECT);
1242                 }
1243         } else if (Om_tracker_squad_name.has_focus()) {
1244                 if (Om_tracker_focus != TRACKER_FOCUS_SQUADRON) {
1245                         Om_tracker_focus = TRACKER_FOCUS_SQUADRON;
1246                         gamesnd_play_iface(SND_USER_SELECT);
1247                 }
1248         } else {
1249                 Om_tracker_focus = TRACKER_FOCUS_NONE;
1250         }
1251 }
1252
1253 // if the accept button was hit
1254 void options_multi_protocol_accept()
1255 {
1256         // if the user has selected local broadcast write it into his options struct
1257         Player->m_local_options.flags &= ~(MLO_FLAG_LOCAL_BROADCAST);
1258         if(Om_local_broadcast){
1259                 Player->m_local_options.flags |= MLO_FLAG_LOCAL_BROADCAST;
1260         }       
1261
1262         // active protocol
1263         Multi_options_g.protocol = Om_protocol;
1264
1265         // VMT status
1266         Multi_options_g.pxo = Om_tracker_flag;
1267
1268         // copy the VMT login and password data
1269         Om_tracker_login.get_text(Multi_tracker_login);
1270         Om_tracker_passwd.get_text(Multi_tracker_passwd);
1271         Om_tracker_squad_name.get_text(Multi_tracker_squad_name);
1272
1273         // write out the tracker login and passwd values to the registry
1274         os_config_write_string( "PXO", "Login", Multi_tracker_login );
1275         os_config_write_string( "PXO", "Password", Multi_tracker_passwd );
1276
1277         // write out the PXO squad name and passwd values to the registry
1278         os_config_write_string( "PXO", "SquadName", Multi_tracker_squad_name );
1279         
1280         // save the ip address list
1281         options_multi_protocol_save_ip_file();
1282 }
1283
1284 // check for button presses
1285 void options_multi_protocol_check_buttons()
1286 {
1287         int idx;
1288
1289         // go through each button
1290         for(idx=0;idx<OM_PRO_NUM_BUTTONS;idx++){
1291                 if(Om_pro_buttons[gr_screen.res][idx].button.pressed()){
1292                         options_multi_protocol_button_pressed(idx);
1293                         break;
1294                 }
1295         }
1296 }
1297
1298 // if a button was pressed
1299 void options_multi_protocol_button_pressed(int n)
1300 {
1301 #ifdef MAKE_FS1
1302         int choice;
1303 #endif
1304
1305         switch(n){
1306         // add an ip address
1307         case OM_PRO_ADD_IP:
1308                 // don't process if we're in input mode
1309                 if(Om_input_mode){
1310                         break;
1311                 }
1312
1313                 // setup the input mode
1314                 Om_input_mode = 1;
1315                 Om_ip_input.enable();
1316                 Om_ip_input.unhide();
1317                 Om_ip_input.set_text(IP_EMPTY_STRING);
1318                 Om_ip_input.set_focus();
1319                 gamesnd_play_iface(SND_USER_SELECT);
1320                 break;
1321
1322         // delete the currently selected ip
1323         case OM_PRO_DELETE_IP:
1324                 // don't process if we're in input mode
1325                 if(Om_input_mode){
1326                         break;
1327                 }
1328
1329                 options_multi_protocol_delete_ip();
1330                 gamesnd_play_iface(SND_USER_SELECT);
1331                 break;
1332
1333         // the "local" broadcast button - toggle
1334 #ifdef MAKE_FS1
1335         // Yes and No buttons in FS1
1336         case OM_PRO_LOCAL_BROADCAST_YES:
1337                 if(Om_input_mode){
1338                         break;
1339                 }
1340
1341                 Om_local_broadcast = 1;
1342
1343                 gamesnd_play_iface(SND_USER_SELECT);
1344                 break;
1345
1346         case OM_PRO_LOCAL_BROADCAST_NO:
1347                 if(Om_input_mode){
1348                         break;
1349                 }
1350
1351                 Om_local_broadcast = 0;
1352
1353                 gamesnd_play_iface(SND_USER_SELECT);
1354                 break;
1355 #else
1356         case OM_PRO_LOCAL_BROADCAST:
1357                 // don't process if we're in input mode
1358                 if(Om_input_mode){
1359                         break;
1360                 }
1361
1362                 if(!Om_local_broadcast){                        
1363                         Om_local_broadcast = 1;
1364                 } else {
1365                         Om_local_broadcast = 0;
1366                 }
1367
1368                 gamesnd_play_iface(SND_USER_SELECT);
1369                 break;
1370 #endif
1371
1372         // scroll ips down
1373         case OM_PRO_SCROLL_IP_DOWN:             
1374                 // don't process if we're in input mode
1375                 if(Om_input_mode){
1376                         break;
1377                 }
1378
1379                 options_multi_protocol_scroll_ip_down();
1380                 break;
1381
1382         // scroll ips up
1383         case OM_PRO_SCROLL_IP_UP:               
1384                 // don't process if we're in input mode
1385                 if(Om_input_mode){
1386                         break;
1387                 }
1388
1389                 options_multi_protocol_scroll_ip_up();
1390                 break;
1391
1392         // the vmt button
1393         case OM_PRO_VMT:
1394                 // for the multiplayer beta, always force tracker mode
1395 #ifdef MULTIPLAYER_BETA_BUILD // do we want this for FS2_DEMO
1396                 Om_tracker_flag = 1;
1397 #else
1398                 // don't process if we're in input mode
1399                 if(Om_input_mode){
1400                         break;
1401                 }
1402
1403                 // toggle the stupid thing
1404                 Om_tracker_flag = !Om_tracker_flag;
1405
1406                 // if the thing is toggled on - enable the inputbox controls, else diable them
1407                 if(Om_tracker_flag){
1408                         Om_tracker_login.enable();
1409                         Om_tracker_passwd.enable();
1410                         Om_tracker_squad_name.enable();
1411                 } else {
1412                         Om_tracker_login.disable();
1413                         Om_tracker_passwd.disable();
1414                         Om_tracker_squad_name.disable();
1415                 }
1416
1417                 // play a sound
1418                 gamesnd_play_iface(SND_USER_SELECT);
1419 #endif
1420                 break;
1421
1422         // general tab button 
1423 #ifndef FS1_DEMO
1424         case OM_PRO_GEN_TAB:    
1425                 if(Om_mode != OM_MODE_GENERAL){
1426                         // set the general tab
1427                         Om_mode = OM_MODE_GENERAL;              
1428
1429                         // disable the voice controls
1430                         options_multi_disable_vox_controls();
1431
1432                         // enable the general controls
1433                         options_multi_enable_gen_controls();
1434
1435                         // set the general screen mask  
1436                         SDL_assert(Om_mask_0 >= 0);
1437                         Om_window->set_mask_bmap(Om_mask_0, Om_background_0_mask_fname[gr_screen.res]);
1438                 }
1439
1440                 // play a sound
1441                 gamesnd_play_iface(SND_USER_SELECT);
1442
1443                 break;
1444
1445         // voice tab button
1446         case OM_PRO_VOX_TAB:
1447                 if(Om_mode != OM_MODE_VOX){
1448                         // set the voice tab
1449                         Om_mode = OM_MODE_VOX;
1450
1451                         // disable the general controls
1452                         options_multi_disable_gen_controls();
1453
1454                         // enable the voice controls
1455                         options_multi_enable_vox_controls();
1456
1457                         // set the voice screen mask    
1458                         SDL_assert(Om_mask_1 >= 0);
1459                         Om_window->set_mask_bmap(Om_mask_1, Om_background_1_mask_fname[gr_screen.res]);
1460                 }
1461                 // play a sound
1462                 gamesnd_play_iface(SND_USER_SELECT);
1463
1464                 break;
1465 #endif  // FS1_DEMO
1466
1467         // tcp mode
1468 #ifndef MAKE_FS1        // not in FS1 option menu
1469         case OM_PRO_TCP:
1470                 Om_protocol = NET_TCP;
1471                 gamesnd_play_iface(SND_USER_SELECT);
1472                 break;
1473
1474         // ipx mode
1475         case OM_PRO_IPX:
1476                 gamesnd_play_iface(SND_GENERAL_FAIL);
1477                 break;
1478 #endif
1479
1480 #ifdef MAKE_FS1
1481         case ABORT_GAME_BUTTON:
1482                 gamesnd_play_iface(SND_USER_SELECT);
1483                 choice = popup( PF_NO_NETWORKING | PF_BODY_BIG, 2, POPUP_NO, POPUP_YES, XSTR("Exit Game?", 374));
1484                 if ( choice == 1 )
1485                         gameseq_post_event(GS_EVENT_QUIT_GAME);
1486                 break;
1487 #endif
1488         }
1489 }
1490
1491 // load the ip address file
1492 void options_multi_protocol_load_ip_file()
1493 {
1494         char line[IP_STRING_LEN];
1495         CFILE *file = NULL;
1496
1497         // reset the ip address count
1498         Om_num_ips = 0;
1499
1500         // attempt to open the ip list file
1501         file = cfopen(IP_CONFIG_FNAME,"rt",CFILE_NORMAL,CF_TYPE_DATA);  
1502         if(file == NULL){
1503                 nprintf(("Network","Error loading tcp.cfg file!\n"));
1504                 return;
1505         }
1506
1507         // read in all the strings in the file
1508         while(!cfeof(file)){
1509                 line[0] = '\0';
1510                 cfgets(line,IP_STRING_LEN,file);
1511
1512                 // strip off any newline character
1513                 if(line[strlen(line) - 1] == '\n'){
1514                         line[strlen(line) - 1] = '\0';
1515                 }
1516
1517                 // 0 length lines don't get processed
1518                 if((line[0] == '\0') || (line[0] == '\n') )
1519                         continue;
1520
1521                 if ( !psnet_is_valid_ip_string(line) ) {
1522                         nprintf(("Network","Invalid ip string (%s)\n",line));
1523                 } else {
1524                         if(Om_num_ips < MAX_IP_ADDRS-1){
1525                                 SDL_strlcpy(Om_ip_addrs[Om_num_ips++], line, IP_STRING_LEN);
1526                         }
1527                 }
1528         }
1529
1530         cfclose(file);
1531 }
1532
1533 // save the ip address file
1534 void options_multi_protocol_save_ip_file()
1535 {
1536         int idx;
1537         CFILE *file = NULL;
1538
1539         // attempt to open the ip list file for writing
1540         file = cfopen(IP_CONFIG_FNAME,"wt",CFILE_NORMAL,CF_TYPE_DATA );
1541         if(file == NULL){
1542                 nprintf(("Network","Error loading tcp.cfg file\n"));
1543                 return;
1544         }
1545
1546         // write out all the string we have
1547         for(idx=0;idx<Om_num_ips;idx++){
1548                 // make _absolutely_ sure its a valid address
1549                 // MWA -- commented out next line because name resolution might fail when
1550                 // it was added.  We'll only grab games that we can actually get to.
1551                 //SDL_assert(psnet_is_valid_ip_string(Multi_ip_addrs[idx]));
1552
1553                 cfputs(Om_ip_addrs[idx],file);
1554                                 
1555            // make sure to tack on a newline if necessary
1556                 if(Om_ip_addrs[idx][strlen(&Om_ip_addrs[idx][0]) - 1] != '\n'){
1557                         cfputs(NOX("\n"),file);
1558                 }
1559         }
1560
1561         cfclose(file);
1562 }
1563
1564 // draw the list of ip addresses
1565 void options_multi_protocol_display_ips()
1566 {
1567         int idx;
1568         int y_start = Ip_list_coords[gr_screen.res][1];
1569                 
1570         // get the # of items we should be displaying based upon the # of addresses and the starting display point
1571         if(Om_ip_start >= Ip_list_max_display[gr_screen.res]){
1572                 Om_ip_disp_count = Ip_list_max_display[gr_screen.res];
1573         } else {
1574                 Om_ip_disp_count = Om_ip_start + 1;
1575         }
1576         
1577         // display the addresses
1578         for(idx=Om_ip_start; idx >= Om_ip_start - Om_ip_disp_count + 1 ; idx--){        
1579                 if(idx == Om_ip_selected){
1580                         gr_set_color_fast(&Color_bright);
1581                 } else {
1582                         gr_set_color_fast(&Color_white);
1583                 }
1584
1585                 gr_printf(Ip_list_coords[gr_screen.res][0], y_start, Om_ip_addrs[idx]);
1586                 y_start += 10;
1587         }
1588 }
1589
1590 // scroll the list of ip addresses down
1591 void options_multi_protocol_scroll_ip_down()
1592 {
1593         if(Om_ip_start >= Ip_list_max_display[gr_screen.res]){
1594                 gamesnd_play_iface(SND_SCROLL);
1595                 Om_ip_start--;
1596         } else {
1597                 gamesnd_play_iface(SND_GENERAL_FAIL);
1598         }       
1599 }
1600
1601 // scroll the list of ip addresses up
1602 void options_multi_protocol_scroll_ip_up()
1603 {
1604         if(Om_ip_start < Om_num_ips-1){
1605                 gamesnd_play_iface(SND_SCROLL);
1606                 Om_ip_start++;
1607         } else {
1608                 gamesnd_play_iface(SND_GENERAL_FAIL);
1609         }
1610 }
1611
1612 // check the ip list to see if the user has selected a new item
1613 void options_multi_protocol_check_ip_list()
1614 {
1615         int click_y;    
1616         int item;
1617         
1618         if(Om_ip_button.pressed()){
1619                 // determine which item he clicked on   
1620                 Om_ip_button.get_mouse_pos(NULL, &click_y);
1621                 item = click_y / 10;
1622                         
1623                 // determine if there is an item in this location, and select it if so
1624                 if(item < Om_ip_disp_count){
1625                         Om_ip_selected = Om_ip_start - item;
1626                 }
1627         }
1628 }
1629
1630 // delete the currently selected ip if any
1631 void options_multi_protocol_delete_ip()
1632 {
1633         int idx;
1634         
1635         // attempt to delete the currently highlighted item
1636         if(Om_ip_selected != -1){               
1637
1638                 // move down all the other items                                
1639                 for(idx=Om_ip_selected; idx < Om_num_ips; idx++){
1640                         SDL_strlcpy(Om_ip_addrs[idx], Om_ip_addrs[idx+1], IP_STRING_LEN);
1641                 }
1642
1643                 // make sure to decrement the starting index
1644                 Om_ip_start--;                  
1645                         
1646                 // check to make sure that the selected item is valid                   
1647                 Om_num_ips--;                   
1648                 if(Om_num_ips <= 0){
1649                         Om_ip_selected = -1;
1650                 } else {
1651                         if(Om_ip_selected > 0){
1652                                 Om_ip_selected--;       
1653                         } 
1654                 }                                               
1655         }
1656 }
1657
1658 // return 10, if successflu
1659 char Ip_str[IP_STRING_LEN+1];
1660 int Ip_validated_already = 0;
1661 int options_multi_verify_ip()
1662 {
1663         int result;
1664
1665         if(!Ip_validated_already){
1666                 // see if its a valid ip address
1667                 result = psnet_is_valid_ip_string(Ip_str);
1668         
1669                 // if the result is a valid ip string, return immediately
1670                 if(result){
1671                         return 10;
1672                 }
1673
1674                 // otherwise, change the popup text to indicate that it is invalid and wait for the user to click ok
1675                 popup_change_text(XSTR( "Ip string is invalid!", 386));
1676         }       
1677
1678         Ip_validated_already = 1;
1679
1680         // always wait for the user to hit the "cancel" button
1681         return 0;
1682 }
1683
1684 // attempt to add the currently entered ip address
1685 void options_multi_protocol_add_current_ip()
1686 {               
1687         // get the entered string
1688         Om_ip_input.get_text(Ip_str);
1689
1690         // this popup wil do several things. 
1691         // 1.) It will display a popup so the user isn't left scratching his head
1692         // 2.) If the address
1693         Ip_validated_already = 0;
1694         if(popup_till_condition(options_multi_verify_ip, XSTR( "Cancel", 387), XSTR( "Verifying ip address", 388)) == 10){
1695                 if(Om_num_ips < MAX_IP_ADDRS){
1696                         SDL_strlcpy(Om_ip_addrs[Om_num_ips], Ip_str, IP_STRING_LEN);
1697                         Om_ip_start = Om_num_ips;
1698                         Om_num_ips++;
1699                         
1700                         // if this is the first item on the list, select it
1701                         if(Om_num_ips == 1){
1702                                 Om_ip_selected = 0;
1703                         }
1704                 } else {
1705                         options_multi_add_notify(XSTR( "Max # of IP addresses reached!", 389));
1706                 }
1707         }                       
1708 }
1709
1710 // general options tab section -------------------------------------------
1711
1712 // load all the general tab controls
1713 void options_multi_load_gen_controls()
1714 {
1715         int idx;
1716         
1717         SDL_assert(Om_window != NULL);
1718
1719         // instantiate all the buttons
1720         for(idx=0; idx<OM_GEN_NUM_BUTTONS; idx++){                              
1721                 // create the object
1722                 Om_gen_buttons[gr_screen.res][idx].button.create(Om_window, "", Om_gen_buttons[gr_screen.res][idx].x, Om_gen_buttons[gr_screen.res][idx].y, 1, 1, 0, 1);
1723
1724                 // set the sound to play when highlighted
1725                 Om_gen_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
1726
1727                 // set the ani for the button
1728                 Om_gen_buttons[gr_screen.res][idx].button.set_bmaps(Om_gen_buttons[gr_screen.res][idx].filename);
1729
1730                 // set the hotspot
1731                 Om_gen_buttons[gr_screen.res][idx].button.link_hotspot(Om_gen_buttons[gr_screen.res][idx].hotspot);
1732         }
1733
1734 #ifndef MAKE_FS1
1735         // text
1736         for(idx=0; idx<OM_GEN_NUM_TEXT; idx++){
1737                 Om_window->add_XSTR(&Om_gen_text[gr_screen.res][idx]);
1738         }
1739 #endif
1740
1741         // bogus control
1742         Om_gen_bogus.base_create(Om_window, UI_KIND_ICON, 0, 0, 0, 0);
1743 }
1744
1745 // disable/hide all the general tab controls
1746 void options_multi_disable_gen_controls()
1747 {
1748         int idx;
1749
1750         // go through all the controls
1751         for(idx=0;idx<OM_GEN_NUM_BUTTONS;idx++){
1752                 Om_gen_buttons[gr_screen.res][idx].button.hide();
1753                 Om_gen_buttons[gr_screen.res][idx].button.disable();
1754         }
1755
1756         // bogus control
1757         Om_gen_bogus.hide();
1758         Om_gen_bogus.disable();
1759 }
1760
1761 // enable/unhide all the general tab controls
1762 void options_multi_enable_gen_controls()
1763 {
1764         int idx;
1765
1766         // go through all the controls
1767         for(idx=0;idx<OM_GEN_NUM_BUTTONS;idx++){                
1768                 Om_gen_buttons[gr_screen.res][idx].button.enable();
1769                 Om_gen_buttons[gr_screen.res][idx].button.unhide();
1770         }
1771
1772         // bogus control
1773         Om_gen_bogus.enable();
1774         Om_gen_bogus.unhide();
1775 }
1776
1777 // initialize the general tab vars
1778 void options_multi_init_gen_vars()
1779 {
1780         // initialize the object update
1781         Om_gen_obj_update = Player->m_local_options.obj_update_level;
1782
1783         // initialize the accept pix var        
1784         if(Player->m_local_options.flags & MLO_FLAG_ACCEPT_PIX){
1785                 Om_gen_pix = 1;
1786         } else {
1787                 Om_gen_pix = 0;
1788         }
1789
1790         // initialize the xfer_multidata var
1791         if(Player->m_local_options.flags & MLO_FLAG_XFER_MULTIDATA){
1792                 Om_gen_xfer_multidata = 1;
1793         } else {
1794                 Om_gen_xfer_multidata = 0;
1795         }
1796
1797         // initialize the flush cache var
1798         if(Player->m_local_options.flags & MLO_FLAG_FLUSH_CACHE){
1799                 Om_gen_flush_cache = 1;
1800         } else {
1801                 Om_gen_flush_cache = 0;
1802         }
1803 }
1804
1805 // accept function for the general tab
1806 void options_multi_gen_accept()
1807 {
1808         // apply the object update level
1809         Player->m_local_options.obj_update_level = Om_gen_obj_update;
1810
1811         // apply the accept pix var
1812         Player->m_local_options.flags &= ~(MLO_FLAG_ACCEPT_PIX);
1813         if(Om_gen_pix){
1814                 Player->m_local_options.flags |= MLO_FLAG_ACCEPT_PIX;
1815         }
1816
1817         // apply the xfer multidata var
1818         Player->m_local_options.flags &= ~(MLO_FLAG_XFER_MULTIDATA);
1819         if(Om_gen_xfer_multidata){
1820                 Player->m_local_options.flags |= MLO_FLAG_XFER_MULTIDATA;
1821         } 
1822
1823         // apply the flush cache var
1824         Player->m_local_options.flags &= ~(MLO_FLAG_FLUSH_CACHE);
1825         if(Om_gen_flush_cache){
1826                 Player->m_local_options.flags |= MLO_FLAG_FLUSH_CACHE;
1827         }
1828 }
1829
1830 // do frame for the general tab
1831 void options_multi_gen_do()
1832 {
1833         // check for button presses
1834         options_multi_gen_check_buttons();
1835
1836         // draw the proper object update button
1837         switch(Om_gen_obj_update){
1838         case OBJ_UPDATE_LOW:
1839                 Om_gen_buttons[gr_screen.res][OM_GEN_OBJ_LOW].button.draw_forced(2);
1840                 break;
1841         case OBJ_UPDATE_MEDIUM:
1842                 Om_gen_buttons[gr_screen.res][OM_GEN_OBJ_MED].button.draw_forced(2);
1843                 break;
1844         case OBJ_UPDATE_HIGH:
1845                 Om_gen_buttons[gr_screen.res][OM_GEN_OBJ_HIGH].button.draw_forced(2);
1846                 break;
1847         case OBJ_UPDATE_LAN:
1848                 Om_gen_buttons[gr_screen.res][OM_GEN_OBJ_LAN].button.draw_forced(2);
1849                 break;
1850         default :
1851                 Int3();
1852         }
1853
1854         // draw the proper pix button
1855         if(Om_gen_pix){
1856                 Om_gen_buttons[gr_screen.res][OM_GEN_PIX_YES].button.draw_forced(2);
1857         } else {
1858                 Om_gen_buttons[gr_screen.res][OM_GEN_PIX_NO].button.draw_forced(2);
1859         }
1860
1861         // draw the proper xfer multidata button
1862         if(Om_gen_xfer_multidata){
1863                 Om_gen_buttons[gr_screen.res][OM_GEN_XFER_MULTIDATA_YES].button.draw_forced(2);
1864         } else {
1865                 Om_gen_buttons[gr_screen.res][OM_GEN_XFER_MULTIDATA_NO].button.draw_forced(2);
1866         }
1867
1868         // draw the proper flush cache button
1869         if(Om_gen_flush_cache){
1870                 Om_gen_buttons[gr_screen.res][OM_GEN_FLUSH_YES].button.draw_forced(2);
1871         } else {
1872                 Om_gen_buttons[gr_screen.res][OM_GEN_FLUSH_NO].button.draw_forced(2);
1873         }
1874 }
1875
1876 // check for button presses
1877 void options_multi_gen_check_buttons()
1878 {
1879         int idx;
1880
1881         // go through all the buttons
1882         for(idx=0;idx<OM_GEN_NUM_BUTTONS;idx++){
1883                 if(Om_gen_buttons[gr_screen.res][idx].button.pressed()){
1884                         options_multi_gen_button_pressed(idx);
1885                         break;
1886                 }
1887         }
1888 }
1889
1890 // a button was pressed
1891 void options_multi_gen_button_pressed(int n)
1892 {
1893         switch(n){
1894         // low object update level
1895         case OM_GEN_OBJ_LOW:
1896                 if(Om_gen_obj_update != OBJ_UPDATE_LOW){
1897                         gamesnd_play_iface(SND_USER_SELECT);
1898                         Om_gen_obj_update = OBJ_UPDATE_LOW;
1899                 } else {
1900                         gamesnd_play_iface(SND_GENERAL_FAIL);
1901                 }
1902                 break;
1903         
1904         // medium object update level
1905         case OM_GEN_OBJ_MED:
1906                 if(Om_gen_obj_update != OBJ_UPDATE_MEDIUM){
1907                         gamesnd_play_iface(SND_USER_SELECT);
1908                         Om_gen_obj_update = OBJ_UPDATE_MEDIUM;
1909                 } else {
1910                         gamesnd_play_iface(SND_GENERAL_FAIL);
1911                 }
1912                 break;
1913         
1914         // high object update level
1915         case OM_GEN_OBJ_HIGH:
1916                 if(Om_gen_obj_update != OBJ_UPDATE_HIGH){
1917                         gamesnd_play_iface(SND_USER_SELECT);
1918                         Om_gen_obj_update = OBJ_UPDATE_HIGH;
1919                 } else {
1920                         gamesnd_play_iface(SND_GENERAL_FAIL);
1921                 }
1922                 break;
1923
1924         // lan object update level
1925         case OM_GEN_OBJ_LAN:
1926                 if(Om_gen_obj_update != OBJ_UPDATE_LAN){
1927                         gamesnd_play_iface(SND_USER_SELECT);
1928                         Om_gen_obj_update = OBJ_UPDATE_LAN;
1929                 } else {
1930                         gamesnd_play_iface(SND_GENERAL_FAIL);
1931                 }
1932                 break;
1933         
1934         // accept pix
1935         case OM_GEN_PIX_YES:
1936                 if(!Om_gen_pix){
1937                         gamesnd_play_iface(SND_USER_SELECT);
1938                         Om_gen_pix = 1;
1939                 } else {
1940                         gamesnd_play_iface(SND_GENERAL_FAIL);
1941                 }
1942                 break;
1943         
1944         // don't accept pix
1945         case OM_GEN_PIX_NO:
1946                 if(Om_gen_pix){
1947                         gamesnd_play_iface(SND_USER_SELECT);
1948                         Om_gen_pix = 0;
1949                 } else {
1950                         gamesnd_play_iface(SND_GENERAL_FAIL);
1951                 }
1952                 break;
1953         
1954         // put missions in the multidate directory
1955         case OM_GEN_XFER_MULTIDATA_YES:
1956                 if(!Om_gen_xfer_multidata){
1957                         gamesnd_play_iface(SND_USER_SELECT);
1958                         Om_gen_xfer_multidata = 1;
1959                 } else {
1960                         gamesnd_play_iface(SND_GENERAL_FAIL);
1961                 }
1962                 break;
1963         
1964         // dont put missions in the multidata directory
1965         case OM_GEN_XFER_MULTIDATA_NO:
1966                 if(Om_gen_xfer_multidata){
1967                         gamesnd_play_iface(SND_USER_SELECT);
1968                         Om_gen_xfer_multidata = 0;
1969                 } else {
1970                         gamesnd_play_iface(SND_GENERAL_FAIL);
1971                 }
1972                 break;
1973         
1974         // flush the cache before each mission
1975         case OM_GEN_FLUSH_YES:
1976                 if(!Om_gen_flush_cache){
1977                         gamesnd_play_iface(SND_USER_SELECT);
1978                         Om_gen_flush_cache = 1;
1979                 } else {
1980                         gamesnd_play_iface(SND_GENERAL_FAIL);
1981                 }
1982                 break;
1983         
1984         // don't flush the cache before each mission
1985         case OM_GEN_FLUSH_NO:
1986                 if(Om_gen_flush_cache){
1987                         gamesnd_play_iface(SND_USER_SELECT);
1988                         Om_gen_flush_cache = 0;
1989                 } else {
1990                         gamesnd_play_iface(SND_GENERAL_FAIL);
1991                 }
1992                 break;  
1993         }
1994 }
1995
1996 // voice options tab section -------------------------------------------
1997
1998 // load all the voice tab controls
1999 void options_multi_load_vox_controls()
2000 {
2001         int idx;
2002         
2003         SDL_assert(Om_window != NULL);
2004
2005         // instantiate all the buttons
2006         for(idx=0; idx<OM_VOX_NUM_BUTTONS; idx++){
2007                 // create the object
2008                 Om_vox_buttons[gr_screen.res][idx].button.create(Om_window, "", Om_vox_buttons[gr_screen.res][idx].x, Om_vox_buttons[gr_screen.res][idx].y, 1, 1, 0, 1);
2009
2010                 // set the sound to play when highlighted
2011                 Om_vox_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
2012
2013                 // set the ani for the button
2014                 Om_vox_buttons[gr_screen.res][idx].button.set_bmaps(Om_vox_buttons[gr_screen.res][idx].filename);
2015
2016                 // set the hotspot
2017                 Om_vox_buttons[gr_screen.res][idx].button.link_hotspot(Om_vox_buttons[gr_screen.res][idx].hotspot);
2018         }
2019
2020 #ifndef MAKE_FS1
2021         // text
2022         for(idx=0; idx<OM_VOX_NUM_TEXT; idx++){
2023                 Om_window->add_XSTR(&Om_vox_text[gr_screen.res][idx]);
2024         }
2025 #endif
2026         
2027         // sliders
2028         for ( idx = 0; idx < NUM_OM_VOX_SLIDERS; idx++ ) {
2029                  Om_vox_sliders[gr_screen.res][idx].slider.create(Om_window, Om_vox_sliders[gr_screen.res][idx].x, Om_vox_sliders[gr_screen.res][idx].y,
2030                                                                                                                                                 Om_vox_sliders[gr_screen.res][idx].dots, Om_vox_sliders[gr_screen.res][idx].filename,
2031                                                                                                                                                 Om_vox_sliders[gr_screen.res][idx].hotspot, Om_vox_sliders[gr_screen.res][idx].right_filename, Om_vox_sliders[gr_screen.res][idx].right_mask, Om_vox_sliders[gr_screen.res][idx].right_x, Om_vox_sliders[gr_screen.res][idx].right_y,
2032                                                                                                                                                 Om_vox_sliders[gr_screen.res][idx].left_filename, Om_vox_sliders[gr_screen.res][idx].left_mask, Om_vox_sliders[gr_screen.res][idx].left_x, Om_vox_sliders[gr_screen.res][idx].left_y,
2033                                                                                                                                                 Om_vox_sliders[gr_screen.res][idx].dot_w);
2034         }       
2035
2036         // default position from settings (slider is 0-9, hence the -1)
2037         Om_vox_qos_pos = Player->m_server_options.voice_qos - 1;
2038         Om_vox_sliders[gr_screen.res][OM_VOX_QOS_SLIDER].slider.pos = Om_vox_qos_pos;
2039
2040         // create the player list select button
2041         Om_vox_plist_button.create(Om_window, "", Om_vox_plist_coords[gr_screen.res][0], Om_vox_plist_coords[gr_screen.res][1], Om_vox_plist_coords[gr_screen.res][2], Om_vox_plist_coords[gr_screen.res][3], 0, 1);
2042         Om_vox_plist_button.hide();
2043
2044         // build a list of net players
2045         Om_vox_num_players = 0;
2046         for(idx=0;idx<MAX_PLAYERS;idx++){
2047                 Om_vox_players[idx] = NULL;
2048
2049                 // if i'm not connected, do nothing
2050                 if((Net_player == NULL) || !(Net_player->flags & NETINFO_FLAG_CONNECTED)){
2051                         continue;
2052                 }
2053
2054                 // add all players I know about
2055                 if(MULTI_CONNECTED(Net_players[idx]) && !MULTI_STANDALONE(Net_players[idx]) && (Net_player != &Net_players[idx])){
2056                         // set the netplayer pointer
2057                         Om_vox_players[Om_vox_num_players] = &Net_players[idx];
2058
2059                         // set his mute flag
2060                         Om_vox_player_flags[Om_vox_num_players] = (Multi_voice_local_prefs & (1<<idx)) ? 1 : 0;
2061
2062                         // increment the count
2063                         Om_vox_num_players++;
2064                 }
2065         }       
2066         
2067         // bogus control
2068         Om_vox_bogus.base_create(Om_window, UI_KIND_ICON, 0, 0, 0, 0);
2069 }
2070
2071 // disable/hide all the voice tab controls
2072 void options_multi_disable_vox_controls()
2073 {
2074         int idx;
2075
2076         // go through all the controls
2077         for(idx=0; idx<OM_VOX_NUM_BUTTONS; idx++){
2078                 Om_vox_buttons[gr_screen.res][idx].button.hide();
2079                 Om_vox_buttons[gr_screen.res][idx].button.disable();
2080         }
2081
2082         // hide the qos control
2083         Om_vox_sliders[gr_screen.res][OM_VOX_QOS_SLIDER].slider.hide();
2084         Om_vox_sliders[gr_screen.res][OM_VOX_QOS_SLIDER].slider.disable();      
2085
2086         // unset the sound buffer size so we don't display any waveforms
2087         Om_vox_voice_buffer_size = -1;
2088         Om_vox_voice_comp_size = -1;
2089         Om_vox_playback_handle = -1;
2090         Om_vox_test_status = OM_VOX_TEST_NONE;  
2091
2092         // disable the player list select button
2093         Om_vox_plist_button.disable();
2094
2095         // bogus controls
2096         Om_vox_bogus.hide();
2097         Om_vox_bogus.disable();
2098 }
2099
2100 // enable/unhide all the voice tab controls
2101 void options_multi_enable_vox_controls()
2102 {
2103         int idx;
2104
2105         // go through all the controls
2106         for(idx=0; idx<OM_VOX_NUM_BUTTONS; idx++){              
2107                 Om_vox_buttons[gr_screen.res][idx].button.enable();
2108                 Om_vox_buttons[gr_screen.res][idx].button.unhide();
2109         }
2110
2111         // unhide the qos control       
2112         Om_vox_sliders[gr_screen.res][OM_VOX_QOS_SLIDER].slider.enable();       
2113         Om_vox_sliders[gr_screen.res][OM_VOX_QOS_SLIDER].slider.unhide();       
2114
2115         // unset the sound buffer size so we don't display any waveforms
2116         Om_vox_voice_buffer_size = -1;
2117         Om_vox_voice_comp_size = -1;
2118         Om_vox_playback_handle = -1;
2119         Om_vox_test_status = OM_VOX_TEST_NONE;  
2120
2121         // select the first player on the list
2122         Om_vox_player_select = Om_vox_players[0];
2123         Om_vox_plist_start = 0;
2124
2125         // enable the player list select button
2126         Om_vox_plist_button.enable();
2127
2128         // bogus controls
2129         Om_vox_bogus.enable();
2130         Om_vox_bogus.unhide();
2131 }
2132
2133 // initialize the voice tab vars
2134 void options_multi_init_vox_vars()
2135 {
2136         // intialize the accept voice var
2137         if(Player->m_local_options.flags & MLO_FLAG_NO_VOICE){
2138                 Om_vox_accept_voice = 0;
2139         } else {
2140                 Om_vox_accept_voice = 1;
2141         }
2142 }
2143
2144 // accept function for the voice tab
2145 void options_multi_vox_accept()
2146 {
2147         int idx;
2148         int voice_pref_flags;
2149         
2150         // set the accept voice flag
2151         Player->m_local_options.flags &= ~(MLO_FLAG_NO_VOICE);
2152         if(!Om_vox_accept_voice){
2153                 Player->m_local_options.flags |= MLO_FLAG_NO_VOICE;
2154         }
2155
2156         // VOX QoS
2157         Players->m_server_options.voice_qos = (ubyte)(Om_vox_qos_pos + 1);
2158
2159         // build the voice preferences stuff
2160         voice_pref_flags = 0xffffffff;
2161         for(idx=0;idx<Om_vox_num_players;idx++){
2162                 // if this guy is muted
2163                 if(!Om_vox_player_flags[idx]){
2164                         voice_pref_flags &= ~(1 << NET_PLAYER_INDEX(Om_vox_players[idx]));
2165                 }
2166         }
2167         multi_voice_set_prefs(voice_pref_flags);
2168 }
2169
2170 // do frame for the voice tab
2171 void options_multi_vox_do()
2172 {
2173         int handle;
2174         
2175         // check for button presses
2176         options_multi_vox_check_buttons();
2177
2178         // maybe do something with the QoS slider
2179         if (Om_vox_qos_pos != Om_vox_sliders[gr_screen.res][OM_VOX_QOS_SLIDER].slider.pos) {
2180                 Om_vox_qos_pos = Om_vox_sliders[gr_screen.res][OM_VOX_QOS_SLIDER].slider.pos;
2181                 gamesnd_play_iface(SND_USER_SELECT);
2182         }
2183
2184         // draw the proper accept voice button
2185         if(Om_vox_accept_voice){
2186                 Om_vox_buttons[gr_screen.res][OM_VOX_VOICE_YES].button.draw_forced(2);
2187         } else {
2188                 Om_vox_buttons[gr_screen.res][OM_VOX_VOICE_NO].button.draw_forced(2);
2189         }
2190
2191         // if the currently selected player is muted
2192         if((Om_vox_player_select != NULL) && !Om_vox_player_flags[options_multi_vox_plist_get(Om_vox_player_select)]){
2193                 Om_vox_buttons[gr_screen.res][OM_VOX_VOICE_MUTE].button.draw_forced(2);
2194         }
2195
2196         // process and display the player list
2197         options_multi_vox_process_player_list();
2198
2199         // if we're currently doing a voice test recording, process some stuff
2200         switch(Om_vox_test_status){
2201         case OM_VOX_TEST_RECORDING:     
2202                 multi_voice_test_process();
2203
2204                 // force draw the mic test button
2205                 Om_vox_buttons[gr_screen.res][OM_VOX_VOICE_TEST].button.draw_forced(2);
2206
2207                 // if we are no longer recording, switch to playback if possible
2208                 if(!multi_voice_test_recording()){
2209                         Om_vox_test_status = OM_VOX_TEST_PLAYBACK;
2210                         
2211                         if(Om_vox_voice_comp_size != -1){
2212                                 // stop any playing back sounds
2213                                 rtvoice_stop_playback_all();
2214
2215                                 // attempt to get a playback handle
2216                                 handle = multi_voice_test_get_playback_buffer();
2217                                 if(handle != -1){
2218                                         Om_vox_playback_handle = rtvoice_play_uncompressed(handle, Om_vox_comp_buffer, Om_vox_voice_comp_size);
2219
2220                                         // mark us as playing back
2221                                         Om_vox_test_status = OM_VOX_TEST_PLAYBACK;
2222                                 }
2223                                 // on error, notify the user something is wrong
2224                                 else {
2225                                         options_multi_add_notify(XSTR( "Error trying to playback recorded voice! Check your hardware", 390));
2226
2227                                         // mark us as doing nothing
2228                                         Om_vox_test_status = OM_VOX_TEST_NONE;
2229                                 }
2230                         } else {
2231                                 // mark us as doing nothing
2232                                 Om_vox_test_status = OM_VOX_TEST_NONE;
2233                         }                       
2234                 }
2235                 break;
2236         
2237         case OM_VOX_TEST_PLAYBACK:                      
2238                 // if we were playing a sound back, but now the sound is done
2239                 if (Om_vox_playback_handle != -1) {
2240                         int channel = oal_get_channel(Om_vox_playback_handle);
2241
2242                         // channel will be -1 if sound has already stopped playing
2243                         if ( (channel == -1) || (oal_get_play_position(channel) >= Om_vox_voice_comp_size) ) {
2244                                 // flush all playing sounds safely
2245                                 rtvoice_stop_playback_all();
2246
2247                                 // null the sound handle
2248                                 Om_vox_playback_handle = -1;
2249
2250                                 // set this so we know not to display any more waveforms
2251                                 Om_vox_voice_buffer_size = -1;
2252                                 Om_vox_voice_comp_size = -1;
2253
2254                                 // free the status up
2255                                 Om_vox_test_status = OM_VOX_TEST_NONE;
2256                         }
2257                 }
2258                 break;
2259         }
2260 }
2261
2262 // check for button presses
2263 void options_multi_vox_check_buttons()
2264 {
2265         int idx;
2266
2267         // go through all the buttons
2268         for(idx=0; idx<OM_VOX_NUM_BUTTONS; idx++){
2269                 if(Om_vox_buttons[gr_screen.res][idx].button.pressed()){
2270                         options_multi_vox_button_pressed(idx);
2271                         break;
2272                 }
2273         }
2274 }
2275
2276 // a button was pressed
2277 void options_multi_vox_button_pressed(int n)
2278 {
2279         switch(n){
2280         // accept voice button
2281         case OM_VOX_VOICE_YES:
2282                 if(!Om_vox_accept_voice){
2283                         gamesnd_play_iface(SND_USER_SELECT);
2284                         Om_vox_accept_voice = 1;
2285                 } else {
2286                         gamesnd_play_iface(SND_GENERAL_FAIL);
2287                 }
2288                 break;
2289         
2290         // don't accept voice button
2291         case OM_VOX_VOICE_NO:
2292                 if(Om_vox_accept_voice){
2293                         gamesnd_play_iface(SND_USER_SELECT);
2294                         Om_vox_accept_voice = 0;
2295                 } else {
2296                         gamesnd_play_iface(SND_GENERAL_FAIL);
2297                 }
2298                 break;
2299
2300         // mute/unmute button
2301         case OM_VOX_VOICE_MUTE:
2302                 if(Om_vox_player_select != NULL){
2303                         Om_vox_player_flags[options_multi_vox_plist_get(Om_vox_player_select)] = !Om_vox_player_flags[options_multi_vox_plist_get(Om_vox_player_select)];
2304                         gamesnd_play_iface(SND_USER_SELECT);
2305                 } else {
2306                         gamesnd_play_iface(SND_GENERAL_FAIL);
2307                 }
2308                 break;
2309
2310         // scroll the player list up
2311         case OM_VOX_PLIST_UP:
2312                 options_multi_vox_plist_scroll_up();
2313                 break;
2314
2315         // scroll the player list down
2316         case OM_VOX_PLIST_DOWN:
2317                 options_multi_vox_plist_scroll_down();
2318                 break;          
2319
2320         // mic test button
2321         case OM_VOX_VOICE_TEST:
2322                 // if in a multiplayer game, don't allow testing
2323                 if((Net_player != NULL) && (Net_player->flags & NETINFO_FLAG_CONNECTED)){
2324                         options_multi_add_notify(XSTR( "Cannot test mic while in a multiplayer game!", 391));
2325                         break;
2326                 }
2327
2328                 // if this machine is not capable of playing
2329                 if(!Multi_voice_can_record){
2330                         options_multi_add_notify(XSTR( "DirectSoundCapture could not be initialized. To initialize DirectSoundCapture your sound card must be full duplex and your sound card drivers must support DirectSoundCapture", 392));
2331                 } else {
2332                         // if we're not already doing a record test
2333                         if(Om_vox_test_status == OM_VOX_TEST_NONE){
2334                                 // set the quality of sound
2335                                 rtvoice_set_qos(Om_vox_qos_pos + 1);
2336
2337                                 // clear the comp buffer
2338                                 memset(Om_vox_comp_buffer,128,OM_VOX_COMP_SIZE);
2339
2340                                 Om_vox_test_status = OM_VOX_TEST_RECORDING;
2341                                 multi_voice_test_record_start();
2342                         }
2343                 }
2344                 break;
2345         }
2346 }
2347
2348 // screen shader
2349 extern shader Grey_shader;
2350
2351 // process and blit any voice waveform if necessary
2352 void options_multi_vox_process_waveform()
2353 {
2354         int c_width = OM_VOX_WAVE_WIDTH;
2355         int avg_len;
2356         int buf_offset;
2357         int idx,a_idx,running_avg;      
2358         
2359         // if we're not in recording or playback mode
2360         if(Om_vox_test_status == OM_VOX_TEST_NONE){
2361                 return;
2362         }
2363
2364         // grey the screen
2365         gr_set_shader(&Grey_shader);
2366         gr_shade(0,0,gr_screen.clip_width, gr_screen.clip_height);
2367
2368         switch(Om_vox_test_status){
2369         case OM_VOX_TEST_RECORDING:
2370                 // if we have no sound buffer size, do nothing
2371                 if(Om_vox_voice_buffer_size <= 0){
2372                         return;
2373                 }
2374
2375                 // if we are not recording, do nothing
2376                 if(Om_vox_test_status != OM_VOX_TEST_RECORDING){
2377                         return;
2378                 }
2379
2380                 // get the # of samples we'll average for one line
2381                 avg_len = Om_vox_voice_buffer_size / c_width;
2382
2383                 // blit the waveform
2384                 gr_set_color_fast(&Color_green);
2385                 buf_offset = 0;
2386                 for(idx=0; idx < c_width; idx++){
2387                         // reset the running average
2388                         running_avg = 0;
2389                         for(a_idx = 0; a_idx < avg_len; a_idx++){
2390                                 running_avg += (int)Om_vox_voice_buffer[buf_offset] - 128;                      
2391
2392                                 // increment the buffer offset
2393                                 buf_offset++;
2394                         }
2395
2396                         running_avg /= avg_len;
2397                         gr_line((gr_screen.max_w - OM_VOX_WAVE_WIDTH)/2 + idx, OM_VOX_WAVE_Y, (gr_screen.max_w - OM_VOX_WAVE_WIDTH)/2 + idx, OM_VOX_WAVE_Y + running_avg);
2398                 }               
2399
2400                 // if this packet would have been dropped, notify the user
2401                 if(multi_voice_test_packet_tossed()){
2402                         gr_set_color_fast(&Color_bright);
2403                         gr_string(OM_VOX_DROP_ICON_X,OM_VOX_DROP_ICON_Y, XSTR( "Packet Overflow", 393));
2404                 }               
2405                 break;
2406
2407         case OM_VOX_TEST_PLAYBACK:
2408                 // get the offset into the playing direct sound buffer
2409                 buf_offset = oal_get_play_position(oal_get_channel(Om_vox_playback_handle));
2410
2411                 // get the # of samples we'll average for one line
2412                 avg_len = (int)((float)OM_VOX_RECORD_INT * ((1024.0f * 11.0f) / 1000.0f)) / c_width;                            
2413
2414                 // blit the waveform
2415                 gr_set_color_fast(&Color_red);          
2416                 for(idx=0; idx < c_width; idx++){
2417                         // reset the running average
2418                         running_avg = 0;
2419                         for(a_idx = 0; a_idx < avg_len; a_idx++){
2420                                 if(buf_offset < (OM_VOX_COMP_SIZE - 2)){
2421                                         running_avg += (int)Om_vox_comp_buffer[buf_offset] - 128;                       
2422
2423                                         // increment the buffer offset                          
2424                                         buf_offset++;
2425                                 }
2426                         }
2427
2428                         running_avg /= avg_len;                 
2429                         gr_line((gr_screen.max_w - OM_VOX_WAVE_WIDTH)/2 + idx, OM_VOX_WAVE_Y, (gr_screen.max_w - OM_VOX_WAVE_WIDTH)/2 + idx, OM_VOX_WAVE_Y + running_avg);
2430                 }                               
2431                 break;
2432         }
2433 }
2434
2435 // process/display the player list
2436 void options_multi_vox_process_player_list()
2437 {
2438         int idx;
2439         int y_start,p_count;
2440         int selected_index,click_y;
2441         char str[CALLSIGN_LEN+2];
2442
2443         // check for mouse clicks
2444         if(Om_vox_plist_button.pressed()){
2445                 Om_vox_plist_button.get_mouse_pos(NULL,&click_y);
2446                 selected_index = (click_y / 10) + Om_vox_plist_start;
2447
2448                 // if he clicked on a valid player, select him
2449                 if(Om_vox_players[selected_index] != NULL){
2450                         Om_vox_player_select = Om_vox_players[selected_index];
2451
2452                         nprintf(("Network","Selecting player %s\n",Om_vox_player_select->player->callsign));
2453                 }
2454         }
2455
2456         // draw the list of players
2457         p_count = 0;
2458         y_start = Om_vox_plist_coords[gr_screen.res][1];
2459         for(idx = Om_vox_plist_start; idx < Om_vox_num_players; idx++){
2460                 if(Om_vox_players[idx] != NULL){
2461                         // if he's the selected player, highlight him
2462                         if(Om_vox_players[idx] == Om_vox_player_select){
2463                                 gr_set_color_fast(&Color_bright);
2464                         } else {
2465                                 gr_set_color_fast(&Color_normal);
2466                         }
2467
2468                         // force fit his callsign
2469                         SDL_strlcpy(str, Om_vox_players[idx]->player->callsign, SDL_arraysize(str));
2470                         gr_force_fit_string(str, CALLSIGN_LEN+1, Om_vox_plist_coords[gr_screen.res][2]);
2471
2472                         // blit the callsign
2473                         gr_string(Om_vox_plist_coords[gr_screen.res][0], y_start, str);
2474
2475                         // increment the y index
2476                         y_start += 10;
2477
2478                         // increment the player count
2479                         p_count++;
2480                 }
2481
2482                 // if we've reached max display, break out
2483                 if(p_count >= Om_vox_plist_max_display[gr_screen.res]){
2484                         break;
2485                 }
2486         }
2487 }       
2488
2489 // get the index into the player list of the passed netplayer
2490 int options_multi_vox_plist_get(net_player *pl)
2491 {
2492         int idx;
2493
2494         for(idx=0;idx<Om_vox_num_players;idx++){
2495                 if(pl == Om_vox_players[idx]){
2496                         return idx;
2497                 }
2498         }
2499
2500         // should neve get here. hmmm.
2501         Int3();
2502         return -1;
2503 }
2504
2505 // scroll the player list down
2506 void options_multi_vox_plist_scroll_down()
2507 {
2508         if(Om_vox_num_players < Om_vox_plist_max_display[gr_screen.res]){
2509                 gamesnd_play_iface(SND_GENERAL_FAIL);
2510                 return;
2511         }
2512
2513         if((Om_vox_num_players - Om_vox_plist_start) >= Om_vox_plist_max_display[gr_screen.res]){
2514                 Om_vox_plist_start++;
2515                 gamesnd_play_iface(SND_USER_SELECT);
2516         } else {
2517                 gamesnd_play_iface(SND_GENERAL_FAIL);
2518         }
2519 }
2520
2521 // scroll the player list up
2522 void options_multi_vox_plist_scroll_up()
2523 {
2524         if(Om_vox_plist_start > 0){
2525                 Om_vox_plist_start--;
2526                 gamesnd_play_iface(SND_USER_SELECT);
2527         } else {
2528                 gamesnd_play_iface(SND_GENERAL_FAIL);
2529         }
2530 }
2531
2532
2533 // extern functions -----------------------------------------------------
2534
2535 // called when the options screen is initialized, pass in the UI window
2536 void options_multi_init(UI_WINDOW *options_window)
2537 {
2538         // assign the options window
2539         Om_window = options_window;
2540
2541         // load the background bitmaps
2542         options_multi_load_bmaps();     
2543
2544         // load the controls for the protocol area
2545         options_multi_load_protocol_controls();
2546
2547         // load the controls for the general tab
2548         options_multi_load_gen_controls();
2549
2550         // load the controls for the voice tab
2551         options_multi_load_vox_controls();
2552
2553         // disable all the protocol controls
2554         options_multi_disable_protocol_controls();
2555
2556         // disable all the general tab controls
2557         options_multi_disable_gen_controls();
2558
2559         // disable all the voice tab controls
2560         options_multi_disable_vox_controls();
2561
2562         // intialize the protocol section vars
2563         options_multi_init_protocol_vars();
2564
2565         // initialize the general tab vars
2566         options_multi_init_gen_vars();
2567
2568         // initialize the voice tab vars
2569         options_multi_init_vox_vars();
2570
2571         // intialize the multiplayer voice recording system
2572         if( !((Net_player != NULL) && (Net_player->flags & NETINFO_FLAG_CONNECTED)) ){
2573                 multi_voice_init();
2574         }
2575
2576         // set the default screen mode
2577         Om_mode = OM_MODE_NONE;
2578 }
2579
2580 // do frame for the multi options screen
2581 void options_multi_do(int key)
2582 {               
2583         // do frame for the protocol section
2584         options_multi_protocol_do(key);
2585
2586         // process and blit any notification messages
2587         options_multi_notify_process();
2588
2589         // process the proper tab control
2590         switch(Om_mode){
2591         case OM_MODE_GENERAL:
2592                 options_multi_gen_do();
2593                 break;
2594         case OM_MODE_VOX:
2595                 options_multi_vox_do();
2596                 break;
2597         default :
2598                 Int3();         
2599         }       
2600 }
2601
2602 // called when the entire options screen is closed
2603 void options_multi_close()
2604 {
2605         // null out the window handle
2606         Om_window = NULL;
2607
2608         // unload all background bitmaps
2609         options_multi_unload_bmaps();
2610
2611         // stop any playing voice
2612         rtvoice_stop_playback_all();
2613
2614         // unset the screen mode
2615         Om_mode = OM_MODE_NONE;
2616 }
2617
2618 // called if the accept button on the main options screen was hit
2619 void options_multi_accept()
2620 {       
2621         // accept function for the protocol section
2622         options_multi_protocol_accept();
2623
2624         // accept function for the general tab
2625         options_multi_gen_accept();
2626
2627         // accept function for the voice tab
2628         options_multi_vox_accept();
2629
2630         // if Net_player is not null, copy these new settings to him
2631         if(Net_player != NULL){
2632                 multi_options_local_load(&Net_player->p_info.options, NULL);            
2633         }
2634         multi_options_local_load(&Player->m_local_options, NULL);
2635
2636         // if we're connected to a game server, update our options on the server now
2637         if((Net_player != NULL) && !(Net_player->flags & NETINFO_FLAG_AM_MASTER) && MULTI_CONNECTED(Net_players[MY_NET_PLAYER_NUM]) ){
2638                 multi_options_update_local();
2639         }
2640 }
2641
2642 // called when the multiplayer tab is hit - initializes/switches all necessary data.
2643 // NOTE : this is different from the initialization function, which is called only when the options menu is started
2644 void options_multi_select()
2645 {
2646 #ifndef FS1_DEMO
2647         // set the windows mask bitmap
2648         SDL_assert(Om_mask_0 >= 0);
2649         Om_window->set_mask_bmap(Om_mask_0, Om_background_0_mask_fname[gr_screen.res]);
2650
2651         // set the default screen mode
2652         Om_mode = OM_MODE_GENERAL;
2653
2654         // clear any notification messages
2655         Om_notify_stamp = -1;   
2656
2657         // enable all the protocol controls
2658         options_multi_enable_protocol_controls();       
2659
2660         // enable the general tab controls
2661         options_multi_enable_gen_controls();
2662 #endif
2663 }
2664
2665 // return the bitmap handle of the current background bitmap, or -1 if the multiplayer tab is not active
2666 int options_multi_background_bitmap()
2667 {
2668 #ifndef FS1_DEMO
2669         // return the background bitmap mode based upon the current mode
2670         switch(Om_mode){
2671         case OM_MODE_GENERAL:
2672                 return Om_background_0;
2673
2674         case OM_MODE_VOX:
2675                 return Om_background_1;
2676         }
2677 #endif
2678
2679         // unknown mode of some kind
2680         return -1;
2681 }
2682
2683 // called when the multiplayer tab has been switched from
2684 void options_multi_unselect()
2685 {
2686         // unset the mode
2687         Om_mode = OM_MODE_NONE;
2688
2689         // disable all the protocol controls
2690         options_multi_disable_protocol_controls();
2691
2692         // disable all the general tab controls
2693         options_multi_disable_gen_controls();
2694
2695         // disable all the vox tab controls
2696         options_multi_disable_vox_controls();
2697
2698         // stop any test voice recording
2699         multi_voice_test_record_stop();
2700 }
2701
2702 // set voice sound buffer for display 
2703 void options_multi_set_voice_data(unsigned char *sound_buf,int buf_size,unsigned char *comp_buf, int comp_size, int uncomp_size, double gain)
2704 {
2705         // copy the buffer to the vox tab data
2706         if(buf_size > OM_VOX_BUF_SIZE){
2707                 memcpy(Om_vox_voice_buffer,sound_buf,OM_VOX_BUF_SIZE);
2708                 Om_vox_voice_buffer_size = OM_VOX_BUF_SIZE;
2709         } else {
2710                 memcpy(Om_vox_voice_buffer,sound_buf,buf_size);
2711                 Om_vox_voice_buffer_size = buf_size;
2712         }
2713
2714         // copy and uncompress the compressed buffer
2715         if(Om_vox_voice_comp_size == -1){
2716                 Om_vox_voice_comp_size = 0;
2717         }
2718         // if we can fit it, decompress this data
2719         if((Om_vox_voice_comp_size + uncomp_size) < OM_VOX_COMP_SIZE){
2720                 // uncompress the data
2721                 rtvoice_uncompress(comp_buf, comp_size, gain, Om_vox_comp_buffer + Om_vox_voice_comp_size, uncomp_size);
2722
2723                 Om_vox_voice_comp_size += uncomp_size;
2724         }
2725 }
2726
2727 // return whether we want to eat a tabbed keypress
2728 int options_multi_eat_tab()
2729 {
2730         // do we want to eat the tab key or not
2731         if(Om_tracker_passwd.has_focus() || Om_tracker_login.has_focus() || Om_tracker_squad_name.has_focus()){
2732                 return 1;
2733         }
2734
2735         return 0;
2736 }
2737