]> icculus.org git repositories - taylor/freespace2.git/blob - src/network/multi_pxo.cpp
fix up strings
[taylor/freespace2.git] / src / network / multi_pxo.cpp
1 /*
2  * Copyright (C) Volition, Inc. 2005.  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 the 
6  * source.
7  *
8 */
9
10 /*
11  * $Logfile: /Freespace2/code/Network/multi_pxo.cpp $
12  * $Revision: 42 $
13  * $Date: 10/25/99 5:52p $
14  * $Author: Jefff $
15  *
16  * $Log: /Freespace2/code/Network/multi_pxo.cpp $
17  * 
18  * 42    10/25/99 5:52p Jefff
19  * fixed some non-localized text
20  * 
21  * 41    10/14/99 2:51p Jefff
22  * localization fixes
23  * 
24  * 40    10/13/99 3:29p Jefff
25  * fixed unnumbered XSTRs
26  * 
27  * 39    9/13/99 11:30a Dave
28  * Added checkboxes and functionality for disabling PXO banners as well as
29  * disabling d3d zbuffer biasing.
30  * 
31  * 38    8/30/99 5:01p Dave
32  * Made d3d do less state changing in the nebula. Use new chat server for
33  * PXO.
34  * 
35  * 37    8/20/99 2:09p Dave
36  * PXO banner cycling.
37  * 
38  * 36    8/17/99 3:31p Jefff
39  * fixed too-big chat area in 640
40  * 
41  * 35    8/16/99 9:51a Jefff
42  * webcursor over html links
43  * 
44  * 34    8/11/99 12:19p Jefff
45  * no longer force pxo chatbox to snap to bottom when new text is added
46  * 
47  * 33    8/10/99 5:53p Jefff
48  * fixed 640 chatbox width
49  * 
50  * 32    8/06/99 1:49p Jefff
51  * added hi-res pxo logo
52  * 
53  * 31    8/06/99 12:29a Dave
54  * Multiple bug fixes.
55  * 
56  * 30    8/05/99 4:17p Dave
57  * Tweaks to client interpolation.
58  * 
59  * 29    8/05/99 4:11p Jefff
60  * 
61  * 28    8/04/99 10:59a Dave
62  * Removed "squad" text.
63  * 
64  * 27    8/03/99 5:55p Jefff
65  * column headings in channel window, some general scrubbing
66  * 
67  * 26    7/26/99 11:14a Andsager
68  * Disable medals in demo multiplayer
69  * 
70  * 25    7/19/99 2:13p Dave
71  * Added some new strings for Heiko.
72  * 
73  * 24    6/29/99 7:39p Dave
74  * Lots of small bug fixes.
75  * 
76  * 23    6/18/99 5:16p Dave
77  * Added real beam weapon lighting. Fixed beam weapon sounds. Added MOTD
78  * dialog to PXO screen.
79  * 
80  * 22    6/16/99 4:55p Dave
81  * New pxo pilot info popup.
82  * 
83  * 21    6/07/99 9:51p Dave
84  * Consolidated all multiplayer ports into one.
85  * 
86  * 20    6/01/99 3:52p Dave
87  * View footage screen. Fixed xstrings to not display the & symbol. Popup,
88  * dead popup, pxo find player popup, pxo private room popup.
89  * 
90  * 19    5/21/99 6:45p Dave
91  * Sped up ui loading a bit. Sped up localization disk access stuff. Multi
92  * start game screen, multi password, and multi pxo-help screen.
93  * 
94  * 18    5/19/99 4:07p Dave
95  * Moved versioning code into a nice isolated common place. Fixed up
96  * updating code on the pxo screen. Fixed several stub problems.
97  * 
98  * 17    5/17/99 9:25a Dave
99  * Updated PXO screen. Still needs popups.
100  * 
101  * 16    4/30/99 12:18p Dave
102  * Several minor bug fixes.
103  * 
104  * 15    4/24/99 8:03p Dave
105  * Finalized banner code on PXO screen.
106  * 
107  * 14    4/21/99 11:29p Dave
108  * Improved the banner stuff. Will display bitmaps, launcher URLs and
109  * highlight the banner bitmap.
110  * 
111  * 13    4/20/99 6:39p Dave
112  * Almost done with artillery targeting. Added support for downloading
113  * images on the PXO screen.
114  * 
115  * 12    2/17/99 2:11p Dave
116  * First full run of squad war. All freespace and tracker side stuff
117  * works.
118  * 
119  * 11    2/08/99 5:07p Dave
120  * FS2 chat server support. FS2 specific validated missions.
121  * 
122  * 10    2/04/99 6:29p Dave
123  * First full working rev of FS2 PXO support.  Fixed Glide lighting
124  * problems.
125  * 
126  * 9     2/03/99 6:06p Dave
127  * Groundwork for FS2 PXO usertracker support.  Gametracker support next.
128  * 
129  * 8     1/30/99 5:08p Dave
130  * More new hi-res stuff.Support for nice D3D textures.
131  * 
132  * 7     12/18/98 1:13a Dave
133  * Rough 1024x768 support for Direct3D. Proper detection and usage through
134  * the launcher.
135  * 
136  * 6     11/30/98 1:07p Dave
137  * 16 bit conversion, first run.
138  * 
139  * 5     11/19/98 4:19p Dave
140  * Put IPX sockets back in psnet. Consolidated all multiplayer config
141  * files into one.
142  * 
143  * 4     11/05/98 5:55p Dave
144  * Big pass at reducing #includes
145  * 
146  * 3     10/13/98 9:29a Dave
147  * Started neatening up freespace.h. Many variables renamed and
148  * reorganized. Added AlphaColors.[h,cpp]
149  * 
150  * 2     10/07/98 10:53a Dave
151  * Initial checkin.
152  * 
153  * 1     10/07/98 10:50a Dave
154  * 
155  * 43    9/18/98 2:22a Dave
156  * Fixed freespace-side PXO api to correctly handle length 10 id strings.
157  * Fixed team select screen to handle alpha/beta/gamma ships which are not
158  * marked as OF_PLAYER_SHIP
159  * 
160  * 42    9/17/98 3:08p Dave
161  * PXO to non-pxo game warning popup. Player icon stuff in create and join
162  * game screens. Upped server count refresh time in PXO to 35 secs (from
163  * 20).
164  * 
165  * 41    9/09/98 5:53p Dave
166  * Put in new tracker packets in API. Change cfile to be able to checksum
167  * portions of a file.
168  * 
169  * 40    7/13/98 10:30a Lawrance
170  * add index numbers for newly localized strings
171  * 
172  * 39    7/13/98 10:12a Dave
173  * Remove improperly localized strings.
174  * 
175  * 38    7/10/98 2:03p Dave
176  * Additional PXO changes.
177  * 
178  * 37    7/09/98 6:01p Dave
179  * Firsts full version of PXO updater. Put in stub for displaying
180  * connection status.
181  * 
182  * 36    7/09/98 4:51p Dave
183  * First revision of PXO autoupdate check system.
184  * 
185  * 35    7/08/98 8:15p Dave
186  * Fixed bandwidth capping for new system, and fixed pxo join button bug.
187  * 
188  * 34    7/08/98 5:28p Dave
189  * Display channel name when returning to previous PXO channel from
190  * another screen.
191  * 
192  * 33    7/08/98 4:54p Dave
193  * Join last used channel when returning from the games list.
194  * 
195  * 32    6/17/98 10:56a Dave
196  * Put in debug code for detecting potential tracker stats update
197  * problems.
198  * 
199  * 31    6/16/98 10:39a Allender
200  * channel changes
201  * 
202  * 30    6/13/98 6:01p Hoffoss
203  * Externalized all new (or forgot to be added) strings to all the code.
204  * 
205  * 29    6/13/98 4:42p Hoffoss
206  * Fixed code to work properly with the XSTR program.
207  * 
208  * 28    6/13/98 3:19p Hoffoss
209  * NOX()ed out a bunch of strings that shouldn't be translated.
210  * 
211  * 27    6/13/98 1:45p Sandeep
212  * 
213  * 26    6/05/98 3:08p Dave
214  * Fixed broken version checking. Fixed pxo chat scroll-down bug.
215  * 
216  * 25    6/04/98 3:52p Dave
217  * Fixed pxo chat bug. Used physics compression code to compress observer
218  * updates. Put in rate limiting for observer updates.
219  * 
220  * 24    5/27/98 1:15p Dave
221  * Change pxo login failure popup text. Put in message display of player
222  * who is currently doing voice.
223  * 
224  * 23    5/26/98 11:25a Dave
225  * Check for NULL when scroll chat area up.
226  * 
227  * 22    5/24/98 8:15p Dave
228  * Tweaked pxo some more. 
229  * 
230  * 21    5/24/98 3:45a Dave
231  * Minor object update fixes. Justify channel information on PXO. Add a
232  * bunch of configuration stuff for the standalone.
233  * 
234  * 20    5/24/98 12:14a Dave
235  * Yet more tweaking.
236  * 
237  * 19    5/23/98 3:31p Dave
238  * Tweaked pxo code. Fixed observer HUD stuff.
239  * 
240  * 18    5/23/98 3:02a Dave
241  * Pxo tweaks.
242  * 
243  * 17    5/21/98 9:45p Dave
244  * Lengthened tracker polling times. Put in initial support for PXO
245  * servers with channel filters. Fixed several small UI bugs.
246  * 
247  * 16    5/21/98 1:52a Dave
248  * Remove obsolete command line functions. Reduce shield explosion packets
249  * drastically. Tweak PXO screen even more. Fix file xfer system so that
250  * we can guarantee file uniqueness.
251  * 
252  * 15    5/20/98 12:12p Dave
253  * Channel name fixes.
254  * 
255  * 14    5/20/98 2:24a Dave
256  * Fixed server side voice muting. Tweaked multi debrief/endgame
257  * sequencing a bit. Much friendlier for stats tossing/accepting now.
258  * 
259  * 13    5/19/98 8:35p Dave
260  * Revamp PXO channel listing system. Send campaign goals/events to
261  * clients for evaluation. Made lock button pressable on all screens. 
262  * 
263  * 12    5/19/98 1:35a Dave
264  * Tweaked pxo interface. Added rankings url to pxo.cfg. Make netplayer
265  * local options update dynamically in netgames.
266  * 
267  * 11    5/18/98 9:15p Dave
268  * Put in network config file support.
269  * 
270  * 10    5/17/98 1:43a Dave
271  * Eradicated chatbox problems. Remove speed match for observers. Put in
272  * help screens for PXO. Fix messaging and end mission privelges. Fixed
273  * team select screen bugs. Misc UI fixes.
274  * 
275  * 9     5/15/98 9:52p Dave
276  * Added new stats for freespace. Put in artwork for viewing stats on PXO.
277  * 
278  * 8     5/15/98 1:41p Dave
279  * Update api so everyone can test.
280  * 
281  * 7     5/15/98 12:09a Dave
282  * New tracker api code. New game tracker code. Finished up first run of
283  * the PXO screen. Fixed a few game server list exceptions.
284  * 
285  * 6     5/14/98 2:41p Johnson
286  * Fixed a nick switching bug.
287  * 
288  * 5     5/14/98 12:40a Dave
289  * Still more additions to the PXO screen. Updated tracker code.
290  * 
291  * 4     5/13/98 6:54p Dave
292  * More sophistication to PXO interface. Changed respawn checking so
293  * there's no window for desynchronization between the server and the
294  * clients.
295  * 
296  * 3     5/12/98 11:59p Dave
297  * Put in some more functionality for Parallax Online.
298  * 
299  * 2     5/12/98 2:46a Dave
300  * Rudimentary communication between Parallax Online and freespace. Can
301  * get and store channel lists.
302  * 
303  * 1     5/11/98 11:47p Dave
304  *  
305  * 
306  * $NoKeywords: $
307  */
308
309
310 #ifdef PLAT_UNIX
311 #include <netinet/in.h>
312 #endif
313
314 #include "multi_pxo.h"
315 #include "animplay.h"
316 #include "ui.h"
317 #include "key.h"
318 #include "bmpman.h"
319 #include "palman.h"
320 #include "gamesnd.h"
321 #include "gamesequence.h"
322 #include "cfile.h"
323 #include "chat_api.h"
324 #include "popup.h"
325 #include "freespace.h"
326 #include "font.h"
327 #include "multi.h"
328 #include "multiui.h"
329 #include "multi_fstracker.h"
330 #include "ptrack.h"
331 #include "gtrack.h"
332 #include "medals.h"
333 #include "multi_update.h"
334 #include "alphacolors.h"
335 #include "timer.h"
336 #include "inetgetfile.h"
337 #include "cfilesystem.h"
338 #include "osregistry.h"
339
340 // ----------------------------------------------------------------------------------------------------
341 // PXO DEFINES/VARS
342 //
343
344 // button definitions
345 #define MULTI_PXO_NUM_BUTTONS                           15
346 #define MULTI_PXO_PLIST_UP                                      0
347 #define MULTI_PXO_PLIST_DOWN                            1
348 #define MULTI_PXO_RANKINGS                                      2
349 #define MULTI_PXO_PINFO                                         3
350 #define MULTI_PXO_FIND                                          4
351 #define MULTI_PXO_MOTD                                          5
352 #define MULTI_PXO_JOIN                                          6
353 #define MULTI_PXO_JOIN_PRIV                             7
354 #define MULTI_PXO_CHAN_UP                                       8
355 #define MULTI_PXO_CHAN_DOWN                             9
356 #define MULTI_PXO_TEXT_UP                                       10
357 #define MULTI_PXO_TEXT_DOWN                             11
358 #define MULTI_PXO_EXIT                                          12
359 #define MULTI_PXO_HELP                                          13
360 #define MULTI_PXO_GAMES                                         14
361
362
363 ui_button_info Multi_pxo_buttons[GR_NUM_RESOLUTIONS][MULTI_PXO_NUM_BUTTONS] = {
364         { // GR_640
365                 ui_button_info( "PXB_00",               1,              104,    -1,     -1,     0 ),                                    // scroll player list up
366                 ui_button_info( "PXB_01",               1,              334,    -1,     -1,     1 ),                                    // scroll player list down
367                 ui_button_info( "PXB_02",               18,     385,    -1,     -1,     2 ),                                    // rankings webpage
368                 ui_button_info( "PXB_03",               71,     385,    -1,     -1,     3 ),                                    // pilot info
369                 ui_button_info( "PXB_04",               115,    385,    -1,     -1,     4 ),                                    // find player
370                 ui_button_info( "PXB_05",               1,              443,    -1,     -1,     5 ),                                    // motd
371                 ui_button_info( "PXB_06",               330,    96,     -1,     -1,     6 ),                                    // join channel
372                 ui_button_info( "PXB_07",               330,    131,    -1,     -1,     7 ),                                    // join private channel
373                 ui_button_info( "PXB_08",               618,    92,     -1,     -1,     8 ),                                    // scroll channels up
374                 ui_button_info( "PXB_09",               618,    128,    -1,     -1,     9 ),                                    // scroll channels down
375                 ui_button_info( "PXB_10",               615,    171,    -1,     -1,     10 ),                                   // scroll text up
376                 ui_button_info( "PXB_11",               615,    355,    -1,     -1,     11 ),                                   // scroll text down
377                 ui_button_info( "PXB_12",               482,    435,    -1,     -1,     12 ),                                   // exit
378                 ui_button_info( "PXB_13",               533,    432,    -1,     -1,     13 ),                                   // help         
379                 ui_button_info( "PXB_14",               573,    432,    -1,     -1,     14 ),                                   // games list
380         },
381         { // GR_1024
382                 ui_button_info( "2_PXB_00",             2,              166,    -1,     -1,     0 ),                                    // scroll player list up
383                 ui_button_info( "2_PXB_01",             2,              534,    -1,     -1,     1 ),                                    // scroll player list down
384                 ui_button_info( "2_PXB_02",             29,     616,    -1,     -1,     2 ),                                    // rankings webpage
385                 ui_button_info( "2_PXB_03",             114,    616,    -1,     -1,     3 ),                                    // pilot info
386                 ui_button_info( "2_PXB_04",             184,    616,    -1,     -1,     4 ),                                    // find player
387                 ui_button_info( "2_PXB_05",             2,              709,    -1,     -1,     5 ),                                    // motd
388                 ui_button_info( "2_PXB_06",             528,    119,    -1,     -1,     6 ),                                    // join channel
389                 ui_button_info( "2_PXB_07",             528,    175,    -1,     -1,     7 ),                                    // join private channel
390                 ui_button_info( "2_PXB_08",             989,    112,    -1,     -1,     8 ),                                    // scroll channels up
391                 ui_button_info( "2_PXB_09",             989,    170,    -1,     -1,     9 ),                                    // scroll channels down
392                 ui_button_info( "2_PXB_10",             984,    240,    -1,     -1,     10 ),                                   // scroll text up
393                 ui_button_info( "2_PXB_11",             984,    568,    -1,     -1,     11 ),                                   // scroll text down
394                 ui_button_info( "2_PXB_12",             771,    696,    -1,     -1,     12 ),                                   // exit
395                 ui_button_info( "2_PXB_13",             853,    691,    -1,     -1,     13 ),                                   // help         
396                 ui_button_info( "2_PXB_14",             917,    691,    -1,     -1,     14 ),                                   // games list
397         },
398 };
399
400 // define MULTI_PXO_NUM_TEXT                    18
401 #define MULTI_PXO_NUM_TEXT                      16
402 UI_XSTR Multi_pxo_text[GR_NUM_RESOLUTIONS][MULTI_PXO_NUM_TEXT] = {
403         { // GR_640             
404                 {"Web",                                                         1313, 20,       415,    UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[0][MULTI_PXO_RANKINGS].button},
405                 {"Ranking",                                                     1314, 6,                426,    UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[0][MULTI_PXO_RANKINGS].button},
406                 {"Pilot",                                                       1310,   68,     415,    UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[0][MULTI_PXO_PINFO].button},
407                 {"Info",                                                                1311,   72,     426,    UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[0][MULTI_PXO_PINFO].button},
408                 {"Find",                                                                1315,   119,    415,    UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[0][MULTI_PXO_FIND].button},
409                 {"Motd",                                                                1316,   36,     456,    UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[0][MULTI_PXO_MOTD].button}, 
410                 {"Join",                                                                1505,   291,    100,    UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[0][MULTI_PXO_JOIN].button},
411                 {"Channel",                                                     1317,   266,    112,    UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[0][MULTI_PXO_JOIN].button},  
412                 {"Join",                                                                1506,   291,    134,    UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[0][MULTI_PXO_JOIN_PRIV].button},
413                 {"Private",                                                     1318,   273,    146,    UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[0][MULTI_PXO_JOIN_PRIV].button},
414                 {"Exit",                                                                1416,   493,    424,    UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[0][MULTI_PXO_EXIT].button},
415                 {"Help",                                                                928,    535,    416,    UI_XSTR_COLOR_GREEN,    -1, &Multi_pxo_buttons[0][MULTI_PXO_HELP].button},
416                 {"Games",                                                       1319,   579,    416,    UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[0][MULTI_PXO_GAMES].button},
417                 {"Players",                                                     1269,   29,     102,    UI_XSTR_COLOR_GREEN,    -1, NULL},
418                 // {"Squad",                                                    1320,   110,    101,    UI_XSTR_COLOR_GREEN, -1, NULL},
419                 // {"Channels",                                         1321,   369,    76,     UI_XSTR_COLOR_GREEN, -1, NULL},
420                 {"Players",                                                     1269,   507,    90,     UI_XSTR_COLOR_GREEN,    -1, NULL},              
421                 {"Games",                                                       1319,   568,    90,     UI_XSTR_COLOR_GREEN, -1, NULL}
422         },
423         { // GR_1024
424                 {"Web",                                                         1313, 32,       664,    UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[1][MULTI_PXO_RANKINGS].button},
425                 {"Ranking",                                                     1314, 9,                674,    UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[1][MULTI_PXO_RANKINGS].button},
426                 {"Pilot",                                                       1310,   109,    664,    UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[1][MULTI_PXO_PINFO].button},
427                 {"Info",                                                                1311,   115,    674,    UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[1][MULTI_PXO_PINFO].button},
428                 {"Find",                                                                1315,   190,    664,    UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[1][MULTI_PXO_FIND].button},
429                 {"Motd",                                                                1316,   58,     729,    UI_XSTR_COLOR_GREEN, -1, &Multi_pxo_buttons[1][MULTI_PXO_MOTD].button}, 
430                 {"Join",                                                                1505,   488,    129,    UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[1][MULTI_PXO_JOIN].button},
431                 {"Channel",                                                     1317,   461,    139,    UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[1][MULTI_PXO_JOIN].button},  
432                 {"Join",                                                                1506,   487,    184,    UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[1][MULTI_PXO_JOIN_PRIV].button},
433                 {"Private",                                                     1318,   467,    194,    UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[1][MULTI_PXO_JOIN_PRIV].button},
434                 {"Exit",                                                                1416,   789,    678,    UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[1][MULTI_PXO_EXIT].button},
435                 {"Help",                                                                928,    857,    667,    UI_XSTR_COLOR_GREEN,    -1, &Multi_pxo_buttons[1][MULTI_PXO_HELP].button},
436                 {"Games",                                                       1319,   917,    667,    UI_XSTR_COLOR_PINK, -1, &Multi_pxo_buttons[1][MULTI_PXO_GAMES].button},
437                 {"Players",                                                     1269,   47,     163,    UI_XSTR_COLOR_GREEN,    -1, NULL},
438                 // {"Squad",                                                    1320,   176,    163,    UI_XSTR_COLOR_GREEN, -1, NULL},
439                 // {"Channels",                                         1321,   591,    86,     UI_XSTR_COLOR_GREEN, -1, NULL},
440                 {"Players",                                                     1269,   852,    109,    UI_XSTR_COLOR_GREEN,    -1, NULL},              
441                 {"Games",                                                       1319,   926,    109,    UI_XSTR_COLOR_GREEN, -1, NULL}
442         }
443 };
444
445 char Multi_pxo_bitmap_fname[GR_NUM_RESOLUTIONS][MAX_FILENAME_LEN] = {
446         "PXOChat",
447         "2_PXOChat"
448 };
449 char Multi_pxo_mask_fname[GR_NUM_RESOLUTIONS][MAX_FILENAME_LEN] = {
450         "PXOChat-M",
451         "2_PXOChat-M"
452 };
453
454 UI_WINDOW Multi_pxo_window;
455 int Multi_pxo_bitmap = -1;
456 int Multi_pxo_palette = -1;
457
458
459 // pxo animation
460 #define MULTI_PXO_ANIM_FNAME                            "pxologo"
461 #define MULTI_PXO_ANIM_X                                        0
462 #define MULTI_PXO_ANIM_Y                                        4
463 anim *Multi_pxo_anim = NULL;
464 anim_instance *Multi_pxo_anim_instance = NULL;
465
466 // rankings last clicked time
467 #define MULTI_PXO_RANK_TIME                             (5.0f)  
468 float Multi_pxo_ranking_last = -1.0f;
469
470 // chat api vars
471 int Multi_pxo_must_connect = 0;                                 // if we still need to connect
472 int Multi_pxo_connected = 0;                                            // if we are connected
473 int Multi_pxo_must_validate = 0;                                        // if we need to validate on the tracker
474 int Multi_pxo_must_autojoin = 1;                                        // still need to autojoin a channel
475 int Multi_pxo_must_verify_version = 1;                  // only do it once per instance of freespace
476
477 // mode
478 #define MULTI_PXO_MODE_NORMAL                   0                       // normal mode
479 #define MULTI_PXO_MODE_PRIVATE          1                       // private channel popup
480 #define MULTI_PXO_MODE_FIND                     2                       // find player popup
481 int Multi_pxo_mode = MULTI_PXO_MODE_NORMAL;
482
483 // our nick for this session
484 int x = NAME_LENGTH;
485 char Multi_pxo_nick[NAME_LENGTH+1];
486
487 // check for button presses
488 void multi_pxo_check_buttons();
489
490 // handle a button press
491 void multi_pxo_button_pressed(int n);
492
493 // condition function for popup_do_with_condition for connected to Parallax Online
494 // return 10 : on successful connect
495 int multi_pxo_connect_do();
496
497 // attempt to connect to Parallax Online, return success or fail
498 int multi_pxo_connect();
499
500 // run the networking functions for the PXO API
501 void multi_pxo_api_process();
502
503 // process a "nick" change event
504 void multi_pxo_process_nick_change(char *data);
505
506 // run normally (no popups)
507 void multi_pxo_do_normal();
508
509 // blit everything on the "normal" screen
510 void multi_pxo_blit_all();
511
512 // process common stuff
513 void multi_pxo_process_common();
514
515 // get selected player information
516 void multi_pxo_get_data(char *name);
517
518 // handle being kicked
519 void multi_pxo_handle_kick();
520
521 // handle being disconnected
522 void multi_pxo_handle_disconnect();
523
524 // return string2, which is the first substring of string 1 without a space
525 // it is safe to pass the same pointer for both parameters
526 void multi_pxo_strip_space(char *string1,char *string2, const int str2_len);
527
528 // fire up the given URL
529 void multi_pxo_url(char *url);
530
531 // load/set the palette
532 void multi_pxo_load_palette();
533
534 // unload the palette
535 void multi_pxo_unload_palette();
536
537 // if we're currently on a private channel
538 int multi_pxo_on_private_channel();
539
540 // convert string 1 into string 2, substituting underscores for spaces
541 void multi_pxo_underscore_nick(char *string1, char *string2, const int str2_len);
542
543 // if the command is a potential "nick" command
544 int multi_pxo_is_nick_command(char *msg);
545
546
547 // status bar stuff -----------------------------------------------
548 int Multi_pxo_status_coords[GR_NUM_RESOLUTIONS][4] = {
549         { // GR_640
550                 95, 467, 354, 12
551         },
552         { // GR_1024
553                 152, 750, 570, 12
554         },
555 };
556
557 // the status text itself
558 char Multi_pxo_status_text[255];
559
560 // set the status text
561 void multi_pxo_set_status_text(const char *txt);
562
563 // blit the status text
564 void multi_pxo_blit_status_text();
565
566
567 // channel related stuff -------------------------------------------
568 #define MAX_CHANNEL_NAME_LEN                    32
569 #define MAX_CHANNEL_DESCRIPT_LEN                120
570
571 // some convenient macros
572 #define SWITCHING_CHANNELS()                    (Multi_pxo_channel_switch.num_users != -1)
573 #define ON_CHANNEL()                                            (Multi_pxo_channel_current.num_users != -1)
574
575 typedef struct pxo_channel {
576         pxo_channel *next,*prev;                                                        // next and previous items in the list
577         char name[MAX_CHANNEL_NAME_LEN+1];                              // name 
578         char desc[MAX_CHANNEL_DESCRIPT_LEN+1];                  // description
579         short num_users;                                                                                // # users, or -1 if not in use                 
580         short num_servers;                                                                      // the # of servers registered on this channel
581 } pxo_channel;
582
583 // last channel we were on before going to the game list screen
584 char Multi_pxo_channel_last[MAX_CHANNEL_NAME_LEN+1] = "";
585 int Multi_pxo_use_last_channel = 0;
586
587 // all channels which are prefixed with this are "lobby" channels
588 #define MULTI_PXO_AUTOJOIN_PREFIX                                       "#lobby"        
589
590 // join this channel to get put in an appropriate lobby channel
591 #define MULTI_PXO_AUTOJOIN_CHANNEL                                      "#autoselect"
592
593 int Multi_pxo_chan_coords[GR_NUM_RESOLUTIONS][4] = {
594         { // GR_640
595                 369, 101, 241, 60
596         },
597         { // GR_1024
598                 593, 124, 386, 100
599         },
600 };
601
602 // this is the offset from the RIGHT side of the channel box
603 #define CHAN_PLAYERS_COLUMN             0
604 #define CHAN_GAMES_COLUMN                       1
605 static int Multi_pxo_chan_column_offsets[GR_NUM_RESOLUTIONS][2] = {
606         { 81, 26 },
607         { 103, 35 }
608 };
609
610 #define CHANNEL_REFRESH_TIME                    (75.0f)
611 float Multi_pxo_channel_last_refresh = -1.0f;
612
613 #define CHANNEL_SERVER_REFRESH_TIME     (35.0f)
614 float Multi_pxo_channel_server_refresh = -1.0f;
615
616 int Multi_pxo_max_chan_display[GR_NUM_RESOLUTIONS] = {
617         6,              // GR_640
618         10              // GR_1024
619 };
620
621 UI_BUTTON Multi_pxo_channel_button;
622
623 // head of the list of available (displayed) channels
624 pxo_channel *Multi_pxo_channels = NULL;
625 int Multi_pxo_channel_count = 0;
626
627 // item we're going to start displaying at
628 pxo_channel *Multi_pxo_channel_start = NULL;
629 int Multi_pxo_channel_start_index = -1;
630
631 // items we've currently got selected
632 pxo_channel *Multi_pxo_channel_select = NULL;
633
634 // channel we're currently connected to, num_users == -1, if we're not connected
635 pxo_channel Multi_pxo_channel_current;
636
637 // channel we're currently trying to change to, num_users == -1, if we're not trying to change channels
638 pxo_channel Multi_pxo_channel_switch;
639
640 // get a list of channels on the server (clear any old list as well)
641 void multi_pxo_get_channels();
642
643 // clear the old channel list
644 void multi_pxo_clear_channels();
645
646 // parse the input string and make a list of new channels
647 void multi_pxo_make_channels(char *chan_str);
648
649 // create a new channel with the given name and place it on the channel list, return a pointer or NULL on fail
650 pxo_channel *multi_pxo_add_channel(char *name, pxo_channel **list);
651
652 // lookup a channel with the specified name
653 pxo_channel *multi_pxo_find_channel(char *name, pxo_channel *list);
654
655 // process the channel list (select, etc)
656 void multi_pxo_process_channels();
657
658 // display the channel list
659 void multi_pxo_blit_channels();
660
661 // scroll channel list up
662 void multi_pxo_scroll_channels_up();
663
664 // scroll channel list down
665 void multi_pxo_scroll_channels_down();
666
667 // attempt to join a channel
668 void multi_pxo_join_channel(pxo_channel *chan);
669
670 // handle any processing details if we're currently trying to join a channel
671 void multi_pxo_handle_channel_change();
672
673 // autojoin an appropriate channel
674 void multi_pxo_autojoin();
675
676 // does the string match the "autojoin" prefic
677 int multi_pxo_is_autojoin(char *name);
678
679 // send a request to refresh our channel server counts
680 void multi_pxo_channel_refresh_servers();
681
682 // refresh current channel server count
683 void multi_pxo_channel_refresh_current();
684
685
686 // player related stuff -------------------------------------------
687 #define MAX_PLAYER_NAME_LEN             32
688
689 typedef struct player_list {
690         player_list *next,*prev;
691         char name[MAX_PLAYER_NAME_LEN+1];
692 } player_list;
693
694 // channel list region
695 int Multi_pxo_player_coords[GR_NUM_RESOLUTIONS][4] = {
696         { // GR_640
697                 27, 121, 141, 261
698         },
699         { // GR_1024
700                 43, 194, 154, 417
701         },
702 };
703
704 int Multi_pxo_max_player_display[GR_NUM_RESOLUTIONS] = {
705         25,     // GR_640
706         41              // GR_1024
707 };
708 UI_BUTTON Multi_pxo_player_button;
709
710 // UI_SLIDER2 Multi_pxo_player_slider;
711
712 // slider coords
713 int Multi_pxo_player_slider_coords[GR_NUM_RESOLUTIONS][4] = {
714         { // GR_640
715                 1, 139, 21, 192
716         },
717         { // GR_1024
718                 2, 219, 33, 314
719         }
720 };
721 const char *Multi_pxo_player_slider_name[GR_NUM_RESOLUTIONS] = {
722         "slider",                               // GR_640
723         "2_slider"                      // GR_1024
724 };
725
726 // head of the list of players in this channel
727 player_list *Multi_pxo_players = NULL;
728 int Multi_pxo_player_count = 0;
729
730 // item we're going to start displaying at
731 player_list *Multi_pxo_player_start = NULL;
732 // int Multi_pxo_player_start_index = -1;
733
734 // items we've currently got selected
735 player_list *Multi_pxo_player_select = NULL;
736
737 // clear the old player list
738 void multi_pxo_clear_players();
739
740 // create a new player with the given name and place it on the player list, return a pointer or NULL on fail
741 player_list *multi_pxo_add_player(char *name);
742
743 // remove a player with the given name
744 void multi_pxo_del_player(char *name);
745
746 // try and find a player with the given name, return a pointer to his entry (or NULL)
747 player_list *multi_pxo_find_player(char *name);
748
749 // process the player list (select, etc)
750 void multi_pxo_process_players();
751
752 // display the player list
753 void multi_pxo_blit_players();
754
755 // scroll player list up
756 void multi_pxo_scroll_players_up();
757
758 // scroll player list down
759 void multi_pxo_scroll_players_down();
760
761 // get the absolute index of the displayed items which our currently selected one is
762 int multi_pxo_get_select_index();
763
764 DCF(players, "")
765 {
766         char name[512] = "";
767
768         // add a bunch of bogus players
769         dc_get_arg(ARG_INT);
770         for(int idx=0; idx<Dc_arg_int; idx++){
771                 SDL_snprintf(name, SDL_arraysize(name), "player %d", idx);
772                 multi_pxo_add_player(name);
773         }
774 }
775
776 // chat text stuff -----------------------------------------
777 #define MAX_CHAT_LINES                                  60
778 #define MAX_CHAT_LINE_LEN                               256
779
780 int Multi_pxo_chat_title_y[GR_NUM_RESOLUTIONS] = {
781         181,    // GR_640
782         253     // GR_1024
783 };
784
785 int Multi_pxo_chat_coords[GR_NUM_RESOLUTIONS][4] = {
786         { // GR_640
787                 196, 197, 412, 185
788         },
789         { // GR_1024
790                 314, 271, 665, 330
791         }
792 };
793
794 int Multi_pxo_input_coords[GR_NUM_RESOLUTIONS][4] = {
795         { // GR_640
796                 196, 386, 407, 24
797         },
798         { // GR_1024
799                 314, 617, 660, 38
800         }       
801 };
802
803 int Multi_pxo_max_chat_display[GR_NUM_RESOLUTIONS] = {
804         17,     // GR_640
805         32              // GR_1024
806 };
807
808 // all messages from the server are prefixed with this
809 #define MULTI_PXO_SERVER_PREFIX         "*** "
810
811 // the "has left" message from the server
812 #define MULTI_PXO_HAS_LEFT                              "has left"
813
814 // chat flags
815 #define CHAT_MODE_NORMAL                                0                       // normal chat from someone
816 #define CHAT_MODE_SERVER                                1                       // is from the server, display appropriately
817 #define CHAT_MODE_CARRY                                 2                       // is a carryover from a previous line
818 #define CHAT_MODE_PRIVATE                               3                       // is a private message
819 #define CHAT_MODE_CHANNEL_SWITCH                4                       // "switching channels" message - draw in red
820 #define CHAT_MODE_MOTD                                  5                       // message of the day from the chat server
821
822 typedef struct chat_line {
823         chat_line *next,*prev;
824         char text[MAX_CHAT_LINE_LEN+1];
825         int mode;
826 } chat_line;
827
828 // the chat linked list itself
829 chat_line *Multi_pxo_chat = NULL;
830
831 // the current add line
832 chat_line *Multi_pxo_chat_add = NULL;
833
834 // the current line to start displaying from
835 chat_line *Multi_pxo_chat_start = NULL;
836 int Multi_pxo_chat_start_index = -1;
837
838 // input box for text
839 UI_INPUTBOX Multi_pxo_chat_input;
840
841 // slider for chat
842 UI_SLIDER2 Multi_pxo_chat_slider;
843
844 int Multi_pxo_chat_slider_coords[GR_NUM_RESOLUTIONS][4] = {
845         { // GR_640
846                 620, 206, 21, 147
847         },
848         { // GR_1024
849                 990, 295, 34, 269
850         }
851 };
852
853 const char *Multi_pxo_chat_slider_name[GR_NUM_RESOLUTIONS] = {
854         "slider",
855         "2_slider"
856 };
857
858 // how many chat lines we have
859 int Multi_pxo_chat_count = 0;
860
861 // extra delay time when switching channels
862 #define MULTI_PXO_SWITCH_DELAY_TIME                     2000
863 int Multi_pxo_switch_delay = -1;
864
865 // initialize and create the chat text linked list
866 void multi_pxo_chat_init();
867
868 // free up all chat list stuff
869 void multi_pxo_chat_free();
870
871 // clear all lines of chat text in the chat area
872 void multi_pxo_chat_clear();
873
874 // blit the chat text
875 void multi_pxo_chat_blit();
876
877 // add a line of text
878 void multi_pxo_chat_add_line(char *txt,int mode);
879
880 // process an incoming line of text
881 void multi_pxo_chat_process_incoming(const char *txt,int mode = CHAT_MODE_NORMAL);
882
883 // scroll to the very bottom of the chat area
884 void multi_pxo_goto_bottom();
885
886 // check whether we can scroll down or not
887 int multi_pxo_can_scroll_down();
888
889 static int Can_scroll_down = 0;
890
891 // scroll the text up
892 void multi_pxo_scroll_chat_up();
893
894 // scroll the text down
895 void multi_pxo_scroll_chat_down();
896
897 // process chat controls
898 void multi_pxo_chat_process();
899
900 // if the text is a private message, return a pointer to the beginning of the message, otherwise return NULL
901 const char *multi_pxo_chat_is_private(const char *txt);
902
903 // if the text came from the server
904 int multi_pxo_is_server_text(const char *txt);
905
906 // if the text is message of the day text
907 int multi_pxo_is_motd_text(const char *txt);
908
909 // if the text is the end of motd text
910 int multi_pxo_is_end_of_motd_text(const char *txt);
911
912 // if the text is a "has left message" from the server
913 int multi_pxo_chat_is_left_message(const char *txt);
914
915 // recalculate the chat start index, and adjust the slider properly
916 void multi_pxo_chat_adjust_start();
917
918
919 // motd stuff ---------------------------------------------------------
920 #define MAX_PXO_MOTD_LEN                        1024
921 #define PXO_MOTD_BLINK_TIME             500
922 char Pxo_motd[1024] = "";
923 int Pxo_motd_end = 0;
924 int Pxo_motd_read = 0;
925 int Pxo_motd_blink_stamp = -1;
926 int Pxo_motd_blink_on = 0;
927 int Pxo_motd_blinked_already = 0;
928
929 // initialize motd when going into this screen
930 void multi_pxo_motd_init();
931
932 // set the motd text
933 void multi_pxo_motd_add_text(const char *text);
934
935 // set end of motd
936 void multi_pxo_set_end_of_motd();
937
938 // display the motd dialog
939 void multi_pxo_motd_dialog();
940
941 // call to maybe blink the motd button
942 void multi_pxo_motd_maybe_blit();
943
944
945 // common dialog stuff ------------------------------------------------
946 const char *Multi_pxo_com_fname[GR_NUM_RESOLUTIONS] = {
947         "PXOPop",
948         "2_PXOPop"
949 };
950 const char *Multi_pxo_com_mask_fname[GR_NUM_RESOLUTIONS] = {
951         "PXOPop-m",
952         "2_PXOPop-m"
953 };
954
955 // popup coords
956 int Multi_pxo_com_coords[GR_NUM_RESOLUTIONS][2] = {
957         { // GR_640
958                 38, 129
959         },
960         { // GR_1024
961                 61, 207
962         }
963 };
964
965 // input box coords
966 int Multi_pxo_com_input_coords[GR_NUM_RESOLUTIONS][4] = {
967         { // GR_640
968                 53, 233, 448, 25
969         },
970         { // GR_1024
971                 85, 372, 716, 40
972         }
973 };
974
975 #define MULTI_PXO_COM_NUM_BUTTONS               2
976 #define MULTI_PXO_COM_CANCEL                            0
977 #define MULTI_PXO_COM_OK                                        1
978
979 ui_button_info Multi_pxo_com_buttons[GR_NUM_RESOLUTIONS][MULTI_PXO_COM_NUM_BUTTONS] = {
980         {       // GR_640
981                 ui_button_info("PXP_00",                573,    192,    -1,     -1,     0),
982                 ui_button_info("PXP_01",                573,    226,    -1,     -1,     1)
983         },
984         {       // GR_1024
985                 ui_button_info("2_PXP_00",              917,    308,    -1,     -1,     0),
986                 ui_button_info("2_PXP_01",              917,    361,    -1,     -1,     1)
987         }
988 };
989
990 #define MULTI_PXO_COM_NUM_TEXT                  2
991 UI_XSTR Multi_pxo_com_text[GR_NUM_RESOLUTIONS][MULTI_PXO_COM_NUM_TEXT] = {
992         { // GR_640
993                 { "&Cancel",                    645,    510,    204,    UI_XSTR_COLOR_PINK,     -1,     &Multi_pxo_com_buttons[0][MULTI_PXO_COM_CANCEL].button },
994                 { "&Ok",                                        669,    548,    233,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_pxo_com_buttons[0][MULTI_PXO_COM_OK].button }
995         },
996         { // GR_1024
997                 { "&Cancel",                    645,    847,    327,    UI_XSTR_COLOR_PINK,     -1,     &Multi_pxo_com_buttons[1][MULTI_PXO_COM_CANCEL].button },
998                 { "&Ok",                                        669,    877,    372,    UI_XSTR_COLOR_GREEN,    -1,     &Multi_pxo_com_buttons[1][MULTI_PXO_COM_OK].button }
999         }
1000 };
1001
1002 int Multi_pxo_com_bitmap = -1;
1003 UI_WINDOW Multi_pxo_com_window;
1004 UI_INPUTBOX Multi_pxo_com_input;
1005
1006 // text on the "top" half of the dialog display area
1007 char Multi_pxo_com_top_text[255];
1008
1009 // text on the "middle" portion of the dialog display area
1010 char Multi_pxo_com_middle_text[255];
1011
1012 // text on the "bottom" half of the dialog display area
1013 char Multi_pxo_com_bottom_text[255];
1014
1015 int Multi_pxo_com_top_text_coords[GR_NUM_RESOLUTIONS][2] = {
1016         { // GR_640
1017                 58, 152
1018         },
1019         { // GR_1024
1020                 91, 227
1021         }
1022 };
1023 int Multi_pxo_com_middle_text_y[GR_NUM_RESOLUTIONS] = {
1024         172,            // GR_640
1025         280             // GR_1024
1026 };
1027 int Multi_pxo_com_bottom_text_y[GR_NUM_RESOLUTIONS] = {
1028         192,            // GR_640
1029         326             // GR_1024
1030 };
1031
1032 // initialize the common dialog with the passed max input length
1033 void multi_pxo_com_init();
1034
1035 // close down the common dialog
1036 void multi_pxo_com_close();
1037
1038 // blit all text lines, top, middle, bottoms
1039 void multi_pxo_com_blit_text();
1040
1041 // set the top text, shortening as necessary
1042 void multi_pxo_com_set_top_text(const char *txt);
1043
1044 // set the middle text, shortening as necessary
1045 void multi_pxo_com_set_middle_text(const char *txt);
1046
1047 // set the bottom text, shortening as necessary
1048 void multi_pxo_com_set_bottom_text(const char *txt);
1049
1050
1051 // private channel join stuff -----------------------------------------
1052 #define MULTI_PXO_PRIV_MAX_TEXT_LEN             30
1053
1054 // max private channel name length
1055 char Multi_pxo_priv_chan[MULTI_PXO_PRIV_MAX_TEXT_LEN+100];
1056
1057 // return code, set to something other than -1 if we're supposed to return
1058 int Multi_pxo_priv_return_code = -1;
1059
1060 // initialize the popup
1061 void multi_pxo_priv_init();
1062
1063 // close down the popup
1064 void multi_pxo_priv_close();
1065
1066 // run the popup, 0 if still running, -1 if cancel, 1 if ok 
1067 int multi_pxo_priv_popup();
1068
1069 // process button presses
1070 void multi_pxo_priv_process_buttons();
1071
1072 // handle a button press
1073 void multi_pxo_priv_button_pressed(int n);
1074
1075 // process the inputbox
1076 void multi_pxo_priv_process_input();
1077
1078
1079 // find player stuff -----------------------------------------
1080
1081 char Multi_pxo_find_channel[MAX_CHANNEL_NAME_LEN+1];
1082
1083 // return code, set to something other than -1 if we're supposed to return
1084 int Multi_pxo_find_return_code = -1;
1085
1086 // initialize the popup
1087 void multi_pxo_find_init();
1088
1089 // close down the popup
1090 void multi_pxo_find_close();
1091
1092 // run the popup, 0 if still running, -1 if cancel, 1 if ok 
1093 int multi_pxo_find_popup();
1094
1095 // process button presses
1096 void multi_pxo_find_process_buttons();
1097
1098 // handle a button press
1099 void multi_pxo_find_button_pressed(int n);
1100
1101 // process the inputbox
1102 void multi_pxo_find_process_input();
1103
1104 // process search mode if applicable
1105 void multi_pxo_find_search_process();
1106
1107
1108 // player info stuff -----------------------------------------
1109 const char *Multi_pxo_pinfo_fname[GR_NUM_RESOLUTIONS] = {
1110         "PilotInfo2",
1111         "2_PilotInfo2"
1112 };
1113 const char *Multi_pxo_pinfo_mask_fname[GR_NUM_RESOLUTIONS] = {
1114         "PilotInfo2-M",
1115         "2_PilotInfo2-M"
1116 };
1117
1118 // medals
1119 #define MULTI_PXO_PINFO_NUM_BUTTONS             2
1120 #define MULTI_PXO_PINFO_MEDALS                  0
1121 #define MULTI_PXO_PINFO_OK                                      1
1122
1123 ui_button_info Multi_pxo_pinfo_buttons[GR_NUM_RESOLUTIONS][MULTI_PXO_PINFO_NUM_BUTTONS] = {
1124         { // GR_640
1125                 ui_button_info("PI2_00",        328,    446,    319,    433,    0),
1126                 ui_button_info("PI2_01",        376,    446,    382,    433,    1),
1127         },
1128         { // GR_1024
1129                 ui_button_info("2_PI2_00",      525,    714,    510,    695,    0),
1130                 ui_button_info("2_PI2_01",      601,    714,    611,    695,    1),
1131         }
1132 };
1133
1134 // text
1135 #define MULTI_PXO_PINFO_NUM_TEXT                        2
1136 UI_XSTR Multi_pxo_pinfo_text[GR_NUM_RESOLUTIONS][MULTI_PXO_PINFO_NUM_TEXT] = {
1137         { // GR_640
1138                 { "Medals",             1037,           319,    433,    UI_XSTR_COLOR_GREEN,    -1, &Multi_pxo_pinfo_buttons[0][MULTI_PXO_PINFO_MEDALS].button },
1139                 { "Ok",                 345,            382,    433,    UI_XSTR_COLOR_PINK,     -1, &Multi_pxo_pinfo_buttons[0][MULTI_PXO_PINFO_OK].button },
1140         },
1141         { // GR_1024
1142                 { "Medals",             1037,           510,    695,    UI_XSTR_COLOR_GREEN,    -1, &Multi_pxo_pinfo_buttons[1][MULTI_PXO_PINFO_MEDALS].button },
1143                 { "Ok",                 345,            611,    695,    UI_XSTR_COLOR_PINK,     -1, &Multi_pxo_pinfo_buttons[1][MULTI_PXO_PINFO_OK].button },
1144         }
1145 };
1146
1147 int Multi_pxo_pinfo_bitmap = -1;
1148 UI_WINDOW Multi_pxo_pinfo_window;
1149
1150 vmt_freespace2_struct Multi_pxo_pinfo;
1151 player Multi_pxo_pinfo_player;
1152
1153 int Multi_pxo_retrieve_mode = -1;
1154
1155 char Multi_pxo_retrieve_name[MAX_PLAYER_NAME_LEN+1];
1156 char Multi_pxo_retrieve_id[128];
1157
1158 // stats label stuff
1159 #define MULTI_PXO_PINFO_NUM_LABELS                      18
1160
1161 int Multi_pxo_pinfo_coords[GR_NUM_RESOLUTIONS][4] = {
1162         { // GR_640
1163                 37, 142, 377, 289
1164         },
1165         { // GR_640
1166                 54, 227, 602, 462
1167         },
1168 };
1169 int Multi_pxo_pinfo_val_x[GR_NUM_RESOLUTIONS] = {
1170         230,    // GR_640
1171         310     // GR_1024
1172 };
1173
1174 char *Multi_pxo_pinfo_stats_labels[MULTI_PXO_PINFO_NUM_LABELS];
1175
1176 /* = {
1177         "Name",
1178         "Rank",
1179         "Kills",
1180         "Assists",
1181         "Friendly kills",
1182         "Missions flown",
1183         "Flight time",
1184         "Last flown",
1185         "Primary shots fired",
1186         "Primary shots hit",
1187         "Primary hit %",
1188         "Secondary shots fired",
1189         "Secondary shots hit",
1190         "Secondary hit %",
1191         "Primary friendly hits",
1192         "Primary friendly hit %",
1193         "Secondary friendly hits",
1194         "Secondary friendly hit %"      
1195 };
1196 */
1197 //XSTR:ON
1198
1199 char Multi_pxo_pinfo_vals[MULTI_PXO_PINFO_NUM_LABELS][50];
1200
1201 int Multi_pxo_pinfo_stats_spacing[MULTI_PXO_PINFO_NUM_LABELS] = {
1202         10,20,10,10,20,10,10,20,10,10,20,10,10,20,10,20,10,0
1203 };
1204
1205 // popup conditional functions, returns 10 on successful get of stats
1206 int multi_pxo_pinfo_cond();
1207
1208 // return 1 if Multi_pxo_pinfo was successfully filled in, 0 otherwise
1209 int multi_pxo_pinfo_get(char *name);
1210
1211 // fire up the stats view popup
1212 void multi_pxo_pinfo_show();
1213
1214 // build the stats labels values
1215 void multi_pxo_pinfo_build_vals();
1216
1217 // initialize the popup
1218 void multi_pxo_pinfo_init();
1219
1220 // do frame
1221 int multi_pxo_pinfo_do();
1222
1223 // close
1224 void multi_pxo_pinfo_close();
1225
1226 // blit all the stats on this screen
1227 void multi_pxo_pinfo_blit();
1228
1229 // run the medals screen
1230 void multi_pxo_run_medals();
1231
1232 // notify stuff stuff -----------------------------------------
1233 #define MULTI_PXO_NOTIFY_TIME                           4000
1234 #define MULTI_PXO_NOTIFY_Y                                      435
1235
1236 char Multi_pxo_notify_text[255];
1237 int Multi_pxo_notify_stamp = -1;
1238
1239 // add a notification string
1240 void multi_pxo_notify_add(const char *txt);
1241
1242 // blit and process the notification string
1243 void multi_pxo_notify_blit();
1244
1245
1246 // help screen stuff -----------------------------------------
1247 //XSTR:OFF
1248 const char *Multi_pxo_help_fname[GR_NUM_RESOLUTIONS] = {
1249         "PXHelp",
1250         "2_PXHelp"
1251 };
1252 const char *Multi_pxo_help_mask_fname[GR_NUM_RESOLUTIONS] = {
1253         "PXOHelp-M",
1254         "2_PXOHelp-M"
1255 };
1256
1257 #define MULTI_PXO_HELP_NUM_BUTTONS                      3
1258 #define MULTI_PXO_HELP_PREV                                     0
1259 #define MULTI_PXO_HELP_NEXT                                     1
1260 #define MULTI_PXO_HELP_CONTINUE                         2
1261
1262 ui_button_info Multi_pxo_help_buttons[GR_NUM_RESOLUTIONS][MULTI_PXO_HELP_NUM_BUTTONS] = {
1263         { // GR_640
1264                 ui_button_info("PXH_00",        15,     389,    -1,     -1,     0),
1265                 ui_button_info("PXH_01",        60,     389,    -1,     -1,     1),
1266                 ui_button_info("PXH_02",        574,    431,    571,    413,    2),
1267         },
1268         { // GR_1024
1269                 ui_button_info("2_PXH_00",      24,     622,    -1,     -1,     0),
1270                 ui_button_info("2_PXH_01",      96,     622,    -1,     -1,     1),
1271                 ui_button_info("2_PXH_02",      919,    689,    928,    663,    2),
1272         }
1273 };
1274
1275 #define MULTI_PXO_HELP_NUM_TEXT                         1
1276 UI_XSTR Multi_pxo_help_text[GR_NUM_RESOLUTIONS][MULTI_PXO_HELP_NUM_TEXT] = {
1277         {       // GR_640
1278                 {"Continue",            1069,           571,    413,    UI_XSTR_COLOR_PINK, -1, &Multi_pxo_help_buttons[0][MULTI_PXO_HELP_CONTINUE].button },
1279         },
1280         {       // GR_1024
1281                 {"Continue",            1069,           928,    663,    UI_XSTR_COLOR_PINK, -1, &Multi_pxo_help_buttons[1][MULTI_PXO_HELP_CONTINUE].button },
1282         },
1283 };
1284
1285 // help text
1286 #define MULTI_PXO_HELP_FILE                     "pxohelp.txt"
1287 #define MULTI_PXO_MAX_LINES_PP          57
1288 #define MULTI_PXO_MAX_PAGES                     3
1289
1290 int Multi_pxo_help_coords[GR_NUM_RESOLUTIONS][2] = {
1291         { // GR_640
1292                 40, 40
1293         },
1294         { // GR_1024
1295                 60, 40
1296         }
1297 };
1298
1299 int Multi_pxo_chars_per_line[GR_NUM_RESOLUTIONS] = {
1300         130,            // GR_640
1301         130             // GR_1024
1302 };
1303
1304 int Multi_pxo_lines_pp[GR_NUM_RESOLUTIONS] = {
1305         32,             // GR_640
1306         57                      // GR_1024
1307 };
1308
1309 // help text pages
1310 typedef struct help_page {
1311         char *text[MULTI_PXO_MAX_LINES_PP];
1312         int num_lines;
1313 } help_page;
1314
1315 help_page Multi_pxo_help_pages[MULTI_PXO_MAX_PAGES];
1316 // int Multi_pxo_help_loaded = 0;
1317
1318 int Multi_pxo_help_num_pages = 0;
1319
1320 int Multi_pxo_help_bitmap = -1;
1321 UI_WINDOW Multi_pxo_help_window;
1322
1323 // current page we're on
1324 int Multi_pxo_help_cur = 0;
1325
1326 // load the help file up
1327 void multi_pxo_help_load();
1328
1329 // blit the current page
1330 void multi_pxo_help_blit_page();
1331
1332 // process button presses
1333 void multi_pxo_help_process_buttons();
1334
1335 // button pressed
1336 void multi_pxo_help_button_pressed(int n);
1337
1338
1339 // http banner stuff ---------------------------------------------
1340 InetGetFile *Multi_pxo_ban_get = NULL;
1341
1342 // banners file
1343 #define PXO_BANNERS_CONFIG_FILE                 "pxobanners.cfg"
1344
1345 // coords to display banners at
1346 int Pxo_ban_coords[GR_NUM_RESOLUTIONS][4] = {
1347         { // GR_640
1348                 149, 3, 475, 75
1349         },
1350         { // GR_1024
1351                 524, 3, 475, 75
1352         }
1353 };
1354
1355 // http modes
1356 #define PXO_BAN_MODE_LIST_STARTUP               0               // start downloading list
1357 #define PXO_BAN_MODE_LIST                                       1               // downloading list
1358 #define PXO_BAN_MODE_IMAGES_STARTUP             2               // start downloading images
1359 #define PXO_BAN_MODE_IMAGES                             3               // downloading images
1360 #define PXO_BAN_MODE_IMAGES_DONE                        4               // done downloading everything - now maybe load an image
1361 #define PXO_BAN_MODE_IDLE                                       5               // done with everything - doing nothing
1362 #define PXO_BAN_MODE_CHOOSE_RANDOM              6               // choose a bitmap we've already downloaded at random
1363
1364 // interface button for detecting clicks
1365 UI_BUTTON Multi_pxo_ban_button;
1366
1367 // banners
1368 typedef struct pxo_banner {     
1369         char    ban_file[MAX_FILENAME_LEN+1];                                           // base filename of the banner
1370         char    ban_file_url[MULTI_OPTIONS_STRING_LEN+1];               // full url of the file to get (convenient)
1371         char    ban_url[MULTI_OPTIONS_STRING_LEN+1];                    // url to go to when clicked
1372         int     ban_bitmap;                                                                                             // banner bitmap        
1373 } pxo_banner;
1374
1375 // active pxo banner
1376 pxo_banner Multi_pxo_banner;
1377
1378 // mode
1379 int Multi_pxo_ban_mode = PXO_BAN_MODE_LIST_STARTUP;
1380
1381 // init
1382 void multi_pxo_ban_init();
1383
1384 // process http download details
1385 void multi_pxo_ban_process();
1386
1387 // close
1388 void multi_pxo_ban_close();
1389
1390 // parse the banners file and maybe fill in Multi_pxo_dl_file[]
1391 void multi_pxo_ban_parse_banner_file(int choose_existing);
1392
1393 // any bitmap or info or whatever
1394 void multi_pxo_ban_draw();
1395
1396 // called when the URL button is clicked
1397 void multi_pxo_ban_clicked();
1398
1399
1400 // ----------------------------------------------------------------------------------------------------
1401 // PXO FUNCTIONS
1402 //
1403
1404 // initialize the PXO screen
1405 void multi_pxo_init(int use_last_channel)
1406 {
1407         int idx;        
1408
1409         // load the background bitmap
1410         Multi_pxo_bitmap = bm_load(Multi_pxo_bitmap_fname[gr_screen.res]);
1411         if(Multi_pxo_bitmap < 0){
1412                 // we failed to load the bitmap - this is very bad
1413                 Int3();
1414         }
1415
1416         // load up the private channel bitmap
1417         Multi_pxo_com_bitmap = bm_load(Multi_pxo_com_fname[gr_screen.res]);
1418         SDL_assert(Multi_pxo_com_bitmap != -1);
1419
1420         // create the interface window
1421         Multi_pxo_window.create(0, 0, gr_screen.max_w, gr_screen.max_h, 0);
1422         Multi_pxo_window.set_mask_bmap(Multi_pxo_mask_fname[gr_screen.res]);
1423
1424         // multiplayer screen common palettes
1425         multi_pxo_load_palette();       
1426
1427         // create the interface buttons
1428         for(idx=0;idx<MULTI_PXO_NUM_BUTTONS;idx++){
1429                 // create the object
1430                 Multi_pxo_buttons[gr_screen.res][idx].button.create(&Multi_pxo_window, "", Multi_pxo_buttons[gr_screen.res][idx].x, Multi_pxo_buttons[gr_screen.res][idx].y, 1, 1, 0, 1);
1431
1432                 // set the sound to play when highlighted
1433                 Multi_pxo_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
1434
1435                 // set the ani for the button
1436                 Multi_pxo_buttons[gr_screen.res][idx].button.set_bmaps(Multi_pxo_buttons[gr_screen.res][idx].filename);
1437
1438                 // set the hotspot
1439                 Multi_pxo_buttons[gr_screen.res][idx].button.link_hotspot(Multi_pxo_buttons[gr_screen.res][idx].hotspot);
1440         }               
1441
1442         // add all xstrs
1443         for(idx=0; idx<MULTI_PXO_NUM_TEXT; idx++){
1444                 Multi_pxo_window.add_XSTR(&Multi_pxo_text[gr_screen.res][idx]);
1445         }
1446
1447         if(use_last_channel && strlen(Multi_pxo_channel_last)){
1448                 Multi_pxo_use_last_channel = 1;
1449         } else {
1450                 SDL_zero(Multi_pxo_channel_last);
1451                 Multi_pxo_use_last_channel = 0;
1452         }
1453
1454         // make all scrolling buttons repeatable
1455         Multi_pxo_buttons[gr_screen.res][MULTI_PXO_TEXT_UP].button.repeatable(1);
1456         Multi_pxo_buttons[gr_screen.res][MULTI_PXO_TEXT_DOWN].button.repeatable(1);
1457         Multi_pxo_buttons[gr_screen.res][MULTI_PXO_CHAN_UP].button.repeatable(1);
1458         Multi_pxo_buttons[gr_screen.res][MULTI_PXO_CHAN_DOWN].button.repeatable(1);
1459         Multi_pxo_buttons[gr_screen.res][MULTI_PXO_PLIST_UP].button.repeatable(1);
1460         Multi_pxo_buttons[gr_screen.res][MULTI_PXO_PLIST_DOWN].button.repeatable(1);
1461
1462         // set the mouseover cursor if it loaded ok
1463         if (Web_cursor_bitmap > 0) {
1464                 Multi_pxo_buttons[gr_screen.res][MULTI_PXO_RANKINGS].button.set_custom_cursor_bmap(Web_cursor_bitmap);
1465         }
1466
1467         // create the channel list select button and hide it
1468         Multi_pxo_channel_button.create(&Multi_pxo_window, "", Multi_pxo_chan_coords[gr_screen.res][0], Multi_pxo_chan_coords[gr_screen.res][1], Multi_pxo_chan_coords[gr_screen.res][2], Multi_pxo_chan_coords[gr_screen.res][3], 0, 1);
1469         Multi_pxo_channel_button.hide();
1470
1471         // create the player list select button and hide it
1472         Multi_pxo_player_button.create(&Multi_pxo_window, "", Multi_pxo_player_coords[gr_screen.res][0], Multi_pxo_player_coords[gr_screen.res][1], Multi_pxo_player_coords[gr_screen.res][2], Multi_pxo_player_coords[gr_screen.res][3], 0, 1);
1473         Multi_pxo_player_button.hide();
1474
1475         // create the chat input box
1476         Multi_pxo_chat_input.create(&Multi_pxo_window, Multi_pxo_input_coords[gr_screen.res][0], Multi_pxo_input_coords[gr_screen.res][1], Multi_pxo_input_coords[gr_screen.res][2], MAX_CHAT_LINE_LEN + 1, "", UI_INPUTBOX_FLAG_INVIS | UI_INPUTBOX_FLAG_ESC_CLR | UI_INPUTBOX_FLAG_KEYTHRU | UI_INPUTBOX_FLAG_EAT_USED);
1477         Multi_pxo_chat_input.set_focus();
1478
1479         // create the banner button and hide it
1480         Multi_pxo_ban_button.create(&Multi_pxo_window, "", Pxo_ban_coords[gr_screen.res][0], Pxo_ban_coords[gr_screen.res][1], Pxo_ban_coords[gr_screen.res][2], Pxo_ban_coords[gr_screen.res][3], 0, 1);
1481         Multi_pxo_ban_button.hide();
1482
1483         // create the player list slider
1484         // Multi_pxo_player_slider.create(&Multi_pxo_window, Multi_pxo_player_slider_coords[gr_screen.res][0], Multi_pxo_player_slider_coords[gr_screen.res][1], Multi_pxo_player_slider_coords[gr_screen.res][2], Multi_pxo_player_slider_coords[gr_screen.res][3], 0, Multi_pxo_player_slider_name[gr_screen.res], multi_pxo_scroll_players_up, multi_pxo_scroll_players_down, NULL);
1485
1486         // create the chat slider
1487         Multi_pxo_chat_slider.create(&Multi_pxo_window, Multi_pxo_chat_slider_coords[gr_screen.res][0], Multi_pxo_chat_slider_coords[gr_screen.res][1], Multi_pxo_chat_slider_coords[gr_screen.res][2], Multi_pxo_chat_slider_coords[gr_screen.res][3], 0, Multi_pxo_chat_slider_name[gr_screen.res], multi_pxo_scroll_chat_up, multi_pxo_scroll_chat_down, NULL);
1488
1489         // set our connection status so that we do the right stuff next frame
1490         Multi_pxo_must_validate = 1;
1491         Multi_pxo_must_connect = 0;
1492         Multi_pxo_connected = 0;        
1493
1494         // channel we're currently connected to
1495         memset(&Multi_pxo_channel_current,0,sizeof(pxo_channel));
1496         Multi_pxo_channel_current.num_users = -1;
1497         
1498         // channel we're currently trying to change to, or NULL if nont 
1499         memset(&Multi_pxo_channel_switch,0,sizeof(pxo_channel));        
1500         Multi_pxo_channel_switch.num_users = -1;        
1501
1502         // last time clicked the url button (so we don't have repeats)
1503         Multi_pxo_ranking_last = -1.0f;
1504
1505         // channel switching extra time delay stamp
1506         Multi_pxo_switch_delay = -1;
1507
1508         // our nick for this session            
1509         multi_pxo_underscore_nick(Player->callsign, Multi_pxo_nick, SDL_arraysize(Multi_pxo_nick));
1510
1511         // clear the channel list
1512         multi_pxo_clear_channels();     
1513
1514         // clear the player list
1515         multi_pxo_clear_players();
1516
1517         // initialize the chat system
1518         multi_pxo_chat_init();
1519
1520         // initialize http
1521         multi_pxo_ban_init();
1522
1523         // load the animation up
1524         if (gr_screen.res == GR_1024) {
1525                 char anim_filename[32] = "2_";
1526                 SDL_strlcat(anim_filename, MULTI_PXO_ANIM_FNAME, SDL_arraysize(anim_filename));
1527                 Multi_pxo_anim = anim_load(anim_filename);
1528
1529                 // if hi-res is not there, fallback to low
1530                 if (Multi_pxo_anim == NULL) {
1531                         Multi_pxo_anim = anim_load(MULTI_PXO_ANIM_FNAME);
1532                 }
1533         } else {
1534                 Multi_pxo_anim = anim_load(MULTI_PXO_ANIM_FNAME);
1535         }
1536
1537         // clear the status text
1538         multi_pxo_set_status_text("");
1539
1540         // last refresh time
1541         Multi_pxo_channel_last_refresh = -1.0f;
1542
1543         // server count last refresh time
1544         Multi_pxo_channel_server_refresh = -1.0f;
1545
1546         // set our mode
1547         Multi_pxo_mode = MULTI_PXO_MODE_NORMAL;
1548
1549         // init motd
1550         multi_pxo_motd_init();
1551
1552         // make sure we autojoin
1553         Multi_pxo_must_autojoin = 1;
1554
1555         // clear all tracker channel related strings
1556         SDL_zero(Multi_fs_tracker_channel);
1557         SDL_zero(Multi_fs_tracker_filter);
1558 }
1559
1560 // do frame for the PXO screen
1561 void multi_pxo_do()
1562 {
1563         pxo_channel priv_chan;
1564         
1565         // run api stuff        
1566         if(Multi_pxo_connected) {
1567                 multi_pxo_api_process();
1568         }
1569
1570         // process common stuff
1571         multi_pxo_process_common();
1572         
1573         switch(Multi_pxo_mode){
1574         // private channel join mode
1575         case MULTI_PXO_MODE_PRIVATE:
1576                 switch(multi_pxo_priv_popup()){
1577                 // still running
1578                 case 0:
1579                         break;
1580
1581                 // user hit "cancel"
1582                 case -1:
1583                         // return to normal mode
1584                         Multi_pxo_mode = MULTI_PXO_MODE_NORMAL;
1585                         break;
1586
1587                 // user hit "ok"
1588                 case 1 :
1589                         // setup some information
1590                         memset(&priv_chan,0,sizeof(pxo_channel));
1591                         priv_chan.num_users = 0;
1592                         SDL_strlcpy(priv_chan.name, Multi_pxo_priv_chan, SDL_arraysize(priv_chan.name));
1593                         
1594                         // see if we know about this channel already
1595                         multi_pxo_join_channel(&priv_chan);
1596
1597                         // return to normal mode
1598                         Multi_pxo_mode = MULTI_PXO_MODE_NORMAL;
1599                         break;
1600                 }
1601                 break;
1602
1603         // find player mode
1604         case MULTI_PXO_MODE_FIND:
1605                 switch(multi_pxo_find_popup()){
1606                 // still running
1607                 case 0:
1608                         break;
1609
1610                 // user hit "cancel"
1611                 case -1:
1612                         // return to normal mode
1613                         Multi_pxo_mode = MULTI_PXO_MODE_NORMAL;
1614                         break;
1615
1616                 // user hit "ok"
1617                 case 1 :                        
1618                         // return to normal mode
1619                         Multi_pxo_mode = MULTI_PXO_MODE_NORMAL;
1620
1621                         // if there is a valid channel name try and join it
1622                         if(strlen(Multi_pxo_find_channel) && !SWITCHING_CHANNELS()){
1623                                 pxo_channel join;
1624
1625                                 // setup the info
1626                                 memset(&join,0,sizeof(pxo_channel));
1627                                 join.num_users = 0;
1628                                 SDL_strlcpy(join.name, Multi_pxo_find_channel, SDL_arraysize(join.name));
1629
1630                                 // try and join
1631                                 multi_pxo_join_channel(&join);
1632                         }
1633                         break;
1634                 }
1635                 break;
1636         // normal mode
1637         case MULTI_PXO_MODE_NORMAL:     
1638                 multi_pxo_do_normal();
1639                 break;
1640         }
1641 }
1642 //XSTR:ON
1643 // close the PXO screen
1644 void multi_pxo_close()
1645 {
1646         // unload any bitmaps
1647         bm_unload(Multi_pxo_bitmap);
1648         bm_unload(Multi_pxo_com_bitmap);
1649                 
1650         // record the last channel we were on, if any
1651         SDL_zero(Multi_fs_tracker_channel);
1652         SDL_zero(Multi_fs_tracker_filter);
1653
1654         if( ON_CHANNEL() && strlen(Multi_pxo_channel_current.name) ){
1655                 // channel name
1656                 SDL_strlcpy(Multi_fs_tracker_channel, Multi_pxo_channel_current.name, SDL_arraysize(Multi_fs_tracker_channel));
1657                 
1658                 // filter name
1659                 SDL_strlcpy(Multi_fs_tracker_filter, Multi_pxo_channel_current.name, SDL_arraysize(Multi_fs_tracker_filter));
1660         } 
1661
1662         // disconnect from the server
1663         DisconnectFromChatServer();
1664         Multi_pxo_connected = 0;
1665
1666         // unload the animation 
1667         anim_release_all_instances(GS_STATE_PXO);
1668         Multi_pxo_anim_instance = NULL;
1669         if(Multi_pxo_anim != NULL){
1670                 anim_free(Multi_pxo_anim);
1671                 Multi_pxo_anim = NULL;
1672         }
1673
1674         // unload the palette for this screen
1675         multi_pxo_unload_palette();
1676         
1677         // destroy the UI_WINDOW
1678         Multi_pxo_window.destroy();
1679
1680         // clear the channel list
1681         multi_pxo_clear_channels();
1682
1683         // close the chat system
1684         multi_pxo_chat_free();
1685
1686         // close http stuff
1687         multi_pxo_ban_close();
1688 }
1689
1690 // run normally (no popups)
1691 void multi_pxo_do_normal()
1692 {               
1693         int validate_code;
1694         int k = Multi_pxo_window.process();
1695         
1696         // if the animation isn't playing, start it up
1697         if((Multi_pxo_anim_instance == NULL) && (Multi_pxo_anim != NULL)){
1698                 anim_play_struct aps;
1699
1700                 // fire up the animation
1701                 anim_play_init(&aps, Multi_pxo_anim, MULTI_PXO_ANIM_X, MULTI_PXO_ANIM_Y);
1702                 aps.screen_id = GS_STATE_PXO;
1703                 aps.framerate_independent = 1;                          
1704                 aps.looped = 1;
1705                 Multi_pxo_anim_instance = anim_play(&aps);                              
1706         }
1707
1708         // process any keypresses
1709         switch(k){
1710         case SDLK_ESCAPE:
1711                 gamesnd_play_iface(SND_USER_SELECT);
1712                 gameseq_post_event(GS_EVENT_MAIN_MENU);
1713                 break;  
1714         }               
1715
1716         // check for button presses
1717         multi_pxo_check_buttons();      
1718
1719         // if we're not in a chatroom, disable and hide the chat input box
1720         if(!ON_CHANNEL()){
1721                 Multi_pxo_chat_input.hide();
1722                 Multi_pxo_chat_input.disable();
1723         } else {
1724                 Multi_pxo_chat_input.enable();
1725                 Multi_pxo_chat_input.unhide();
1726         }       
1727
1728         // blit everything
1729         multi_pxo_blit_all();           
1730
1731         // flip the page
1732         gr_flip();
1733
1734         // verify version # now (only once per Freespace instance)      
1735         if(Multi_pxo_must_verify_version){
1736                 switch(multi_update_gobaby()){
1737                 // everything is cool. Move along
1738                 case MULTI_UPDATE_CONTINUE:
1739                         Multi_pxo_must_verify_version = 0;
1740                         break;
1741
1742                 // go back to the main menu
1743                 case MULTI_UPDATE_MAIN_MENU:
1744                         gameseq_post_event(GS_EVENT_MAIN_MENU);
1745                         
1746                         // unset these so we don't do anything else PXO related
1747                         Multi_pxo_must_validate = 0;
1748                         Multi_pxo_must_connect = 0;
1749                         break;
1750
1751                 // freespace will be shutting down shortly
1752                 case MULTI_UPDATE_SHUTTING_DOWN:
1753                         return;
1754                 }               
1755         }       
1756
1757         // if we need to get tracker info for ourselves, do so
1758         if(Multi_pxo_must_validate){
1759                 // initialize the master tracker API for Freespace
1760                 multi_fs_tracker_init();
1761
1762                 // validate the current player with the master tracker (will create the pilot on the MT if necessary)
1763                 validate_code = multi_fs_tracker_validate(0);
1764                 if(validate_code != 1){
1765                         // show an error popup if it failed (not cancelled by the user)
1766                         if (validate_code == 0) {
1767                                 switch (popup(PF_USE_AFFIRMATIVE_ICON | PF_WEB_CURSOR_1 | PF_WEB_CURSOR_2, 3, POPUP_CANCEL,XSTR("&Create Acct",936), XSTR("&Verify Acct",937), XSTR("PXO Login not accepted.  You may visit the Parallax Online website to create or verify your login.  Or you may click Cancel to play without using the Parallax Online service.  (You may switch back to Parallax Online from the Options Menu under the Multi tab.)",938))) {
1768                                         case 0:
1769                                                 nprintf(("Network","PXO CANCEL\n"));
1770
1771                                                 // flip his "pxo" bit temporarily and push him to the join game screen
1772                                                 Multi_options_g.pxo = 0;
1773                                                 // Net_game_tcp_mode = NET_TCP;
1774                                                 gameseq_post_event(GS_EVENT_MULTI_JOIN_GAME);
1775                                                 break;
1776
1777                                         case 1:
1778                                                 nprintf(("Network","PXO CREATE\n"));
1779                                                 // fire up the given URL
1780                                                 multi_pxo_url(Multi_options_g.pxo_create_url);
1781                                                 break;
1782
1783                                         case 2:
1784                                                 nprintf(("Network","PXO VERIFY\n"));
1785                                                 // fire up the given URL
1786                                                 multi_pxo_url(Multi_options_g.pxo_verify_url);
1787                                                 break;
1788                                 }
1789                         }
1790
1791                         // go back to the main hall
1792                         gameseq_post_event(GS_EVENT_MAIN_MENU);
1793
1794                         Multi_pxo_must_connect = 0;
1795                         Multi_pxo_must_validate = 0;
1796                 }
1797                 // now we have to conenct to PXO
1798                 else {                  
1799                         Multi_pxo_must_connect = 1;
1800                         Multi_pxo_must_validate = 0;
1801                 }
1802         }
1803
1804         // if we need to connect, do so now
1805         if(Multi_pxo_must_connect){             
1806                 // for now, just try once
1807                 Multi_pxo_connected = multi_pxo_connect();
1808
1809                 // if we successfully connected, send a request for a list of channels on the server
1810                 if(Multi_pxo_connected){
1811                         multi_pxo_get_channels();
1812
1813                         // set our status
1814                         multi_pxo_set_status_text(XSTR("Retrieving Public Channels",939));
1815                 } else {
1816                         // set our status
1817                         multi_pxo_set_status_text(XSTR("Failed to connect to Parallax Online",940));
1818                 }
1819
1820                 // no longer need to connect
1821                 Multi_pxo_must_connect = 0;
1822         }
1823 }
1824
1825 // blit everything on the "normal" screen
1826 void multi_pxo_blit_all()
1827 {
1828         // draw the background, etc
1829         gr_reset_clip();        
1830         // GR_MAYBE_CLEAR_RES(Multi_pxo_bitmap);
1831         int bmap = Multi_pxo_bitmap;
1832         do  { 
1833                 int bmw = -1; 
1834                 int bmh = -1; 
1835                 if(bmap != -1){ 
1836                         bm_get_info( bmap, &bmw, &bmh); 
1837                         if((bmw != gr_screen.max_w) || (bmh != gr_screen.max_h)){
1838                                 gr_clear();
1839                         } 
1840                 } else {
1841                         gr_clear();
1842                 } 
1843         } while(0);
1844         if(Multi_pxo_bitmap != -1){
1845                 gr_set_bitmap(Multi_pxo_bitmap);
1846                 gr_bitmap(0,0);
1847         }
1848         Multi_pxo_window.draw();
1849
1850         // display the channel list
1851         multi_pxo_blit_channels();
1852
1853         // display the player list
1854         multi_pxo_blit_players();
1855
1856         // blit the chat text
1857         multi_pxo_chat_blit();
1858
1859         // blit the status text
1860         multi_pxo_blit_status_text();           
1861
1862         // blit and process the notification string
1863         multi_pxo_notify_blit();
1864
1865         // any bitmap or info or whatever
1866         multi_pxo_ban_draw();
1867
1868         // draw any motd stuff
1869         multi_pxo_motd_maybe_blit();
1870
1871         // if we have a valid animation handle, play it
1872         if(Multi_pxo_anim_instance != NULL){
1873                 anim_render_all(GS_STATE_PXO,flFrametime);
1874         }
1875 }
1876
1877 // process common stuff
1878 void multi_pxo_process_common()
1879 {
1880         // process the channel list (select, etc)
1881         multi_pxo_process_channels();
1882
1883         // process the player list (select, etc)
1884         multi_pxo_process_players();
1885
1886         // process chat controls
1887         multi_pxo_chat_process();
1888         
1889         // process http download details
1890         multi_pxo_ban_process();
1891 }
1892
1893 // get selected player information
1894 void multi_pxo_get_data(char *name)
1895 {
1896 }
1897
1898 // handle being kicked
1899 void multi_pxo_handle_kick()
1900 {
1901         // remove ourselves from the room       
1902         memset(&Multi_pxo_channel_current,0,sizeof(pxo_channel));
1903         Multi_pxo_channel_current.num_users = -1;
1904
1905         // clear text
1906         multi_pxo_chat_clear();
1907
1908         // clear the old player list
1909         multi_pxo_clear_players();
1910
1911         // add a notification string
1912         multi_pxo_notify_add(XSTR("You have been kicked",941));
1913 }
1914
1915 // handle being disconnected
1916 void multi_pxo_handle_disconnect()
1917 {
1918         popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,XSTR("You have been disconnected from the server",942));
1919         gameseq_post_event(GS_EVENT_MAIN_MENU);
1920 }
1921
1922 // return string2, which is the first substring of string 1 without a space
1923 // it is safe to pass the same pointer for both parameters
1924 void multi_pxo_strip_space(char *string1, char *string2, const int str2_len)
1925 {
1926         char midway[255];
1927         char *tok;
1928
1929         // copy the original
1930         SDL_strlcpy(midway, string1, SDL_arraysize(midway));
1931         tok = strtok(midway," ");
1932         if(tok != NULL){
1933                 SDL_strlcpy(string2, tok, str2_len);
1934         } else {
1935                 SDL_strlcpy(string2, "", str2_len);
1936         }
1937 }
1938
1939 // fire up the given URL
1940 void multi_pxo_url(char *url)
1941 {
1942 #ifdef PLAT_UNIX
1943         STUB_FUNCTION;
1944 #else
1945         // execute the shell command
1946         int r = (int) ShellExecute(NULL, NOX("open"), url, NULL, NULL, SW_SHOW);
1947         if (r < 32) {           
1948                 switch (r) {
1949                         case 0: 
1950                         case ERROR_BAD_FORMAT: 
1951                         case SE_ERR_ACCESSDENIED: 
1952                         case SE_ERR_ASSOCINCOMPLETE: 
1953                         case SE_ERR_DDEBUSY:
1954                         case SE_ERR_DDEFAIL:
1955                         case SE_ERR_DDETIMEOUT:
1956                         case SE_ERR_DLLNOTFOUND:
1957                         case SE_ERR_OOM:
1958                         case SE_ERR_SHARE:                      
1959                         case SE_ERR_NOASSOC:
1960                         case ERROR_FILE_NOT_FOUND:
1961                         case ERROR_PATH_NOT_FOUND:
1962                                 popup(PF_USE_AFFIRMATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG,1,POPUP_OK,XSTR("Warning\nCould not locate/launch default Internet Browser",943));
1963                                 break;
1964                 }                                       
1965         }
1966 #endif
1967 }
1968
1969 // load/set the palette
1970 void multi_pxo_load_palette()
1971 {
1972         // use the palette
1973 #ifndef HARDWARE_ONLY
1974         palette_use_bm_palette(Multi_pxo_palette);
1975 #endif
1976 }
1977
1978 // unload the palette
1979 void multi_pxo_unload_palette()
1980 {
1981         // unload the palette if it exists
1982         if(Multi_pxo_palette != -1){
1983                 bm_release(Multi_pxo_palette);
1984                 Multi_pxo_palette = -1;
1985         }
1986 }
1987
1988 // if we're currently on a private channel
1989 int multi_pxo_on_private_channel()
1990 {
1991         // if we're connected to a channel with the "+" symbol on front
1992         if(ON_CHANNEL() && (Multi_pxo_channel_current.name[0] == '+')){
1993                 return 1;
1994         }
1995
1996         // otherwise return falos
1997         return 0;
1998 }
1999
2000 // convert string 1 into string 2, substituting underscores for spaces
2001 void multi_pxo_underscore_nick(char *string1, char *string2, const int str2_len)
2002 {
2003         char nick_temp[512];
2004         char *tok;
2005         
2006         // don't do anything if we have bogus string
2007         if((string1 == NULL) || (string2 == NULL)){
2008                 return;
2009         }
2010
2011         // copy the nickname
2012         SDL_strlcpy(nick_temp, string1, SDL_arraysize(nick_temp));
2013
2014         // get the first token
2015         tok = strtok(nick_temp, " ");
2016         if(tok != NULL){
2017                 SDL_strlcpy(string2, tok, str2_len);
2018
2019                 // get the next token
2020                 tok = strtok(NULL," ");
2021                 while(tok != NULL){                             
2022                         if(tok != NULL){
2023                                 SDL_strlcat(string2, "_", str2_len);
2024                                 SDL_strlcat(string2, tok, str2_len);
2025                         }
2026
2027                         tok = strtok(NULL," ");
2028                 }
2029         } else {
2030                 SDL_strlcpy(string2, string1, str2_len);
2031         }
2032 }
2033
2034 // if the command is a potential "nick" command
2035 int multi_pxo_is_nick_command(char *msg)
2036 {
2037         char *tok;
2038         char tmp[512];
2039
2040         // get the first token in the message
2041         SDL_strlcpy(tmp, msg, SDL_arraysize(tmp));
2042         tok = strtok(tmp," ");
2043         if(tok == NULL){
2044                 // can't be a nick message
2045                 return 0;
2046         }
2047
2048         return !SDL_strcasecmp(tok,NOX("/nick"));
2049 }
2050
2051 // check for button presses
2052 void multi_pxo_check_buttons()
2053 {
2054         int idx;
2055
2056         // go through all buttons
2057         for(idx=0;idx<MULTI_PXO_NUM_BUTTONS;idx++){
2058                 if(Multi_pxo_buttons[gr_screen.res][idx].button.pressed()){
2059                         multi_pxo_button_pressed(idx);
2060                         break;
2061                 }
2062         }
2063 }
2064
2065 // handle a button press
2066 void multi_pxo_button_pressed(int n)
2067 {
2068         switch(n){
2069         case MULTI_PXO_EXIT:
2070                 gamesnd_play_iface(SND_USER_SELECT);
2071                 gameseq_post_event(GS_EVENT_MAIN_MENU);
2072                 break;
2073
2074         case MULTI_PXO_CHAN_UP:
2075                 multi_pxo_scroll_channels_up();
2076                 break;
2077
2078         case MULTI_PXO_CHAN_DOWN:
2079                 multi_pxo_scroll_channels_down();
2080                 break;
2081
2082         case MULTI_PXO_TEXT_UP:
2083                 multi_pxo_scroll_chat_up();
2084                 break;
2085
2086         case MULTI_PXO_TEXT_DOWN:
2087                 multi_pxo_scroll_chat_down();
2088                 break;
2089
2090         case MULTI_PXO_PLIST_UP:
2091                 multi_pxo_scroll_players_up();
2092                 multi_pxo_chat_adjust_start();
2093                 break;
2094
2095         case MULTI_PXO_PLIST_DOWN:
2096                 multi_pxo_scroll_players_down();                
2097                 multi_pxo_chat_adjust_start();          
2098                 break;
2099
2100         case MULTI_PXO_JOIN:
2101                 // if there are no channels to join, let the user know
2102                 if((Multi_pxo_channel_count == 0) || (Multi_pxo_channels == NULL)){
2103                         gamesnd_play_iface(SND_GENERAL_FAIL);
2104                         multi_pxo_notify_add(XSTR("No channels!",944));
2105                         break;
2106                 }
2107
2108                 // if we're not already trying to join, allow this
2109                 if(!SWITCHING_CHANNELS() && (Multi_pxo_channel_select != NULL)){
2110                         gamesnd_play_iface(SND_USER_SELECT);
2111                         multi_pxo_join_channel(Multi_pxo_channel_select);
2112                 } else {
2113                         multi_pxo_notify_add(XSTR("Already trying to join a channel!",945));
2114                         gamesnd_play_iface(SND_GENERAL_FAIL);
2115                 }
2116                 break;
2117
2118         case MULTI_PXO_GAMES:
2119                 // move to the join game screen as normally (temporary!)
2120                 gameseq_post_event( GS_EVENT_MULTI_JOIN_GAME );
2121                 break;
2122
2123         case MULTI_PXO_JOIN_PRIV:
2124                 // if we're not already trying to join, allow this
2125                 if(!SWITCHING_CHANNELS()){
2126                         gamesnd_play_iface(SND_USER_SELECT);
2127
2128                         // fire up the private join popup
2129                         multi_pxo_priv_popup();
2130                 } else {
2131                         multi_pxo_notify_add(XSTR("Already trying to join a channel!",945));
2132                         gamesnd_play_iface(SND_GENERAL_FAIL);
2133                 }               
2134                 break;
2135
2136         case MULTI_PXO_FIND:
2137                 gamesnd_play_iface(SND_USER_SELECT);
2138
2139                 // fire up the find join popup
2140                 multi_pxo_find_popup();
2141                 break;
2142
2143         case MULTI_PXO_HELP:
2144                 gamesnd_play_iface(SND_USER_SELECT);
2145                 gameseq_post_event(GS_EVENT_PXO_HELP);
2146                 break;
2147
2148         case MULTI_PXO_PINFO:
2149                 char stats[255];
2150
2151                 // if we have a guy selected, try and get his info
2152                 if(Multi_pxo_player_select != NULL){
2153                         // if we successfully got info for this guy
2154                         if(multi_pxo_pinfo_get(Multi_pxo_player_select->name)){                         
2155                                 // show the stats
2156                                 multi_pxo_pinfo_show();                         
2157                         }
2158                         // if we didn't get stats for this guy.
2159                         else {
2160                                 SDL_snprintf(stats, SDL_arraysize(stats), XSTR("Could not get stats for %s\n(May not be a registered pilot)", 946), Multi_pxo_player_select->name);
2161                                 popup(PF_USE_AFFIRMATIVE_ICON,1,POPUP_OK,stats);
2162                         }
2163                 } else {
2164                         gamesnd_play_iface(SND_GENERAL_FAIL);
2165                 }
2166                 break;
2167
2168         case MULTI_PXO_RANKINGS:                
2169                 // make sure he doesn't click it too many times
2170                 if((Multi_pxo_ranking_last < 0.0f) || ((f2fl(timer_get_fixed_seconds()) - Multi_pxo_ranking_last) > MULTI_PXO_RANK_TIME) ){
2171                         gamesnd_play_iface(SND_USER_SELECT);
2172                         
2173                         // fire up the url
2174                         multi_pxo_url(Multi_options_g.pxo_rank_url);
2175
2176                         // mark the time down
2177                         Multi_pxo_ranking_last = f2fl(timer_get_fixed_seconds());
2178                 } else {
2179                         gamesnd_play_iface(SND_GENERAL_FAIL);
2180                 }
2181                 break;
2182
2183         case MULTI_PXO_MOTD:
2184                 // maybe fire up the pxo motd dialog
2185                 multi_pxo_motd_dialog();
2186                 break;
2187         }
2188 }
2189
2190 // condition function for popup_do_with_condition for connected to Parallax Online
2191 int mpxo_failed = 0;
2192 int multi_pxo_connect_do()
2193 {
2194         int ret_code;           
2195         char id_string[255] = "";
2196         char ip_string[255] = "";       
2197
2198         // if we already tried and failed, sit around until the user presses cancel
2199         if(!mpxo_failed){       
2200                 // try and connect to the server        
2201                 SDL_assert(Player);
2202
2203                 // build the tracker id string
2204                 SDL_snprintf(id_string, SDL_arraysize(id_string), "%s %s", Multi_tracker_id_string, Player->callsign);
2205                 
2206                 // build the ip string
2207                 SDL_snprintf(ip_string, SDL_arraysize(ip_string), "%s:%d", Multi_options_g.pxo_ip, PXO_CHAT_PORT);
2208
2209                 // connect to the server
2210                 ret_code = ConnectToChatServer(ip_string, Multi_pxo_nick, id_string);           
2211                 
2212                 // give some time to the pxo api.
2213                 multi_pxo_api_process();        
2214
2215                 switch(ret_code){
2216                 // already connected, return success
2217                 case -2:
2218                         return 10;
2219
2220                 // failed to connect, return fail
2221                 case -1 :
2222                         mpxo_failed = 1;
2223                         popup_change_text(XSTR("Failed to connect to Parallax Online!", 947));
2224                         return 0;
2225
2226                 // connected, return success
2227                 case 1 :
2228                         return 10;
2229
2230                 // still connecting
2231                 case 0 :                        
2232                         return 0;
2233                 }
2234         }
2235
2236         return 0;
2237 }
2238
2239 // popup loop which does an autojoin of a public channel.  Returns when the autojoin process is complete
2240 int multi_pxo_autojoin_do()
2241 {
2242         pxo_channel last_channel;
2243
2244         // if we need to autojoin, do so now
2245         if(Multi_pxo_must_autojoin){
2246                 Multi_pxo_must_autojoin = 0;
2247
2248                 // if we're supposed to be using a (valid) "last" channel, do so
2249                 if(Multi_pxo_use_last_channel && strlen(Multi_pxo_channel_last)){
2250                         // setup the data
2251                         memset(&last_channel, 0, sizeof(pxo_channel));
2252                         last_channel.num_users = 0;
2253                         SDL_strlcpy(last_channel.name, Multi_pxo_channel_last, SDL_arraysize(last_channel.name));
2254
2255                         // join the channel
2256                         multi_pxo_join_channel(&last_channel);
2257
2258                         nprintf(("Network","PXO : using last channel\n"));
2259                 } else {
2260                         multi_pxo_autojoin();
2261
2262                         nprintf(("Network","PXO : using autojoin channel\n"));
2263                 }
2264                 multi_pxo_get_channels();
2265         }
2266
2267         // give some time to the pxo api.
2268         multi_pxo_api_process();        
2269         multi_pxo_process_common();
2270
2271         // next value is not -1 when actually switching channels, so keep processing by returning 0.
2272         if ( SWITCHING_CHANNELS() ){
2273                 return 0;
2274         }
2275
2276         // couldn't switch channel for some reason.  bail out with -1
2277         if ( !ON_CHANNEL() ){
2278                 return -1;
2279         }
2280
2281         // return success
2282         return 1;
2283 }
2284
2285 // attempt to connect to Parallax Online, return success or fail
2286 int multi_pxo_connect()
2287 {
2288         char join_str[256];     
2289         char join_fail_str[256];
2290         
2291         // intiialize chat api
2292         ChatInit();
2293
2294         // set us to "must autojoin"
2295         Multi_pxo_must_autojoin = 1;
2296
2297         // run the connect dialog/popup
2298         mpxo_failed = 0;
2299         if(popup_till_condition(multi_pxo_connect_do, XSTR("&Cancel", 779), XSTR("Logging into Parallax Online",949)) == 10){
2300                 int rval;
2301
2302                 // if we're going to use the "last" channel
2303                 if(Multi_pxo_use_last_channel && strlen(Multi_pxo_channel_last)){                       
2304                         SDL_strlcpy(join_str, XSTR("Joining last channel (",982), SDL_arraysize(join_str));
2305                         SDL_strlcat(join_str, Multi_pxo_channel_last + 1, SDL_arraysize(join_str));
2306                         SDL_strlcat(join_str, ")", SDL_arraysize(join_str));
2307
2308                         SDL_strlcpy(join_fail_str, XSTR("Unable to join last channel", 983), SDL_arraysize(join_fail_str));
2309                 } else {
2310                         SDL_strlcpy(join_str, XSTR("Autojoining public channel", 984), SDL_arraysize(join_str));
2311                         SDL_strlcpy(join_fail_str, XSTR("Unable to autojoin public channel", 985), SDL_arraysize(join_fail_str));
2312                 }
2313
2314                 // once connected, we should do an autojoin before allowing the guy to continue.
2315                 rval = popup_till_condition( multi_pxo_autojoin_do, XSTR("&Cancel", 779), join_str );
2316                 if ( rval == 1 ) {
2317                         return 1;
2318                 }
2319
2320                 popup( PF_USE_AFFIRMATIVE_ICON, 1, XSTR("OK", 1492), join_fail_str);
2321         }
2322
2323         // otherwise disconnect just to be safe
2324         DisconnectFromChatServer();
2325         gameseq_post_event(GS_EVENT_MAIN_MENU);
2326
2327         // did not successfully connect
2328         return 0;
2329 }
2330
2331 // run the networking functions for the PXO API
2332 void multi_pxo_api_process()
2333 {
2334         char *p;
2335         char msg_str[512];
2336         Chat_command *cmd;      
2337         pxo_channel *lookup;
2338
2339         // give some time to psnet
2340         PSNET_TOP_LAYER_PROCESS();
2341
2342         // give some time to the game tracker API
2343         IdleGameTracker();
2344
2345         // give some time to the user tracker API
2346         PollPTrackNet();
2347         
2348         // get any incoming text 
2349         do
2350         {
2351                 p = GetChatText();
2352                 if(p)
2353                 {                                               
2354                         // process the chat line
2355                         multi_pxo_chat_process_incoming(p);
2356                 }
2357         } while(p);
2358         
2359         // get any incoming channel list stuff
2360         p = GetChannelList();
2361         if(p)
2362         {
2363                 // nprintf(("Network","%s\n",p));
2364                 multi_pxo_make_channels(p);
2365         }       
2366         
2367         // process any chat commands
2368         cmd = GetChatCommand();
2369         while(cmd)
2370         {               
2371                 switch(cmd->command)
2372                 {                       
2373                 case CC_USER_JOINING:                   
2374                         // add a user, if he doesn't already exist
2375                         if(multi_pxo_find_player(cmd->data) == NULL){
2376                                 multi_pxo_add_player(cmd->data);
2377                         }
2378
2379                         // increase the player count
2380                         if(ON_CHANNEL()){
2381                                 lookup = multi_pxo_find_channel(Multi_pxo_channel_current.name,Multi_pxo_channels);
2382                                 if(lookup != NULL){
2383                                         lookup->num_users++;
2384                                 }
2385                         }
2386                         break;
2387                 
2388                 case CC_USER_LEAVING:                   
2389                         // delete a user
2390                         multi_pxo_del_player(cmd->data);
2391
2392                         // add a text message
2393                         SDL_snprintf(msg_str, SDL_arraysize(msg_str), XSTR("*** %s has left", 950), cmd->data);
2394                         multi_pxo_chat_process_incoming(msg_str);
2395
2396                         // decrease the player count
2397                         if(ON_CHANNEL()){
2398                                 lookup = multi_pxo_find_channel(Multi_pxo_channel_current.name,Multi_pxo_channels);
2399                                 if(lookup != NULL){
2400                                         lookup->num_users--;
2401                                 }
2402                         }
2403                         break;
2404                 
2405                 case CC_DISCONNECTED:
2406                         multi_pxo_handle_disconnect();
2407                         break;
2408                 
2409                 case CC_KICKED:
2410                         multi_pxo_handle_kick();
2411                         break;
2412
2413                 case CC_NICKCHANGED:
2414                         // process a nick change
2415                         multi_pxo_process_nick_change(cmd->data);                       
2416                         break;
2417
2418                 case CC_YOURCHANNEL:
2419                         // copy the current channel info, and unset the switching status
2420                         memset(&Multi_pxo_channel_current,0,sizeof(pxo_channel));
2421                         Multi_pxo_channel_switch.num_users = -1;                        
2422
2423                         SetNewChatChannel(NULL);
2424
2425                         SDL_strlcpy(Multi_pxo_channel_current.name, cmd->data, SDL_arraysize(Multi_pxo_channel_current.name));
2426
2427                         // if we don't already have this guy on the list, add him
2428                         pxo_channel *lookup;
2429                         lookup = multi_pxo_find_channel(Multi_pxo_channel_current.name,Multi_pxo_channels);
2430                         if(lookup == NULL){
2431                                 // create a new channel with the given name and place it on the channel list, return a pointer or NULL on fail
2432                                 lookup = multi_pxo_add_channel(Multi_pxo_channel_current.name,&Multi_pxo_channels);
2433                         }
2434
2435                         // set the user count to be 0
2436                         if(lookup != NULL){
2437                                 lookup->num_users = 0;
2438                         }
2439
2440                         // set our "last" channel to be this one
2441                         SDL_strlcpy(Multi_pxo_channel_last, Multi_pxo_channel_current.name, SDL_arraysize(Multi_pxo_channel_last));
2442
2443                         // refresh current channel server count
2444                         multi_pxo_channel_refresh_current();
2445
2446                         // clear the chat area
2447                         // multi_pxo_chat_clear();              
2448                         break;
2449                 
2450                 default:
2451                         Int3();
2452                 }
2453
2454                 cmd = GetChatCommand();
2455         }       
2456
2457         // handle any processing details if we're currently trying to join a channel
2458         multi_pxo_handle_channel_change();
2459 }
2460
2461 // process a "nick" change event
2462 void multi_pxo_process_nick_change(char *data)
2463 {
2464         char *from, *to;
2465         player_list *lookup;    
2466         
2467         // get the new string
2468         from = strtok(data," ");
2469         to = strtok(NULL,"");
2470         if((from != NULL) && (to != NULL)){
2471                 lookup = multi_pxo_find_player(from);
2472                 if(lookup != NULL){
2473                         SDL_strlcpy(lookup->name, to, SDL_arraysize(lookup->name));
2474
2475                         // if this is also my nick, change it
2476                         if(!SDL_strcasecmp(Multi_pxo_nick,from)){
2477                                 SDL_strlcpy(Multi_pxo_nick, to, SDL_arraysize(Multi_pxo_nick));
2478                         }
2479                 }               
2480         }       
2481 }
2482
2483 // autojoin an appropriate channel
2484 void multi_pxo_autojoin()
2485 {
2486         pxo_channel sw;
2487
2488         memset(&sw,0,sizeof(pxo_channel));
2489         sw.num_users = 0;
2490         SDL_strlcpy(sw.name, MULTI_PXO_AUTOJOIN_CHANNEL, SDL_arraysize(sw.name));
2491
2492         // if we found a valid room, attempt to join it 
2493         multi_pxo_join_channel(&sw);            
2494 }
2495
2496 // does the string match the "autojoin" prefic
2497 int multi_pxo_is_autojoin(char *name)
2498 {
2499         // check to see if the name is long enough
2500         if(strlen(name) < strlen(MULTI_PXO_AUTOJOIN_PREFIX)){
2501                 return 0;
2502         }
2503
2504         // check to see if the first n chars match
2505         return !SDL_strncasecmp(name,MULTI_PXO_AUTOJOIN_PREFIX,strlen(MULTI_PXO_AUTOJOIN_PREFIX));
2506 }
2507
2508 // called from the game tracker API - server count update for a channel
2509 void multi_pxo_channel_count_update(char *name,int count)
2510 {
2511         pxo_channel *lookup;
2512         
2513         // lookup the channel name on the normal list   
2514         lookup = NULL;
2515         lookup = multi_pxo_find_channel(name,Multi_pxo_channels);
2516         if(lookup != NULL){
2517                 lookup->num_servers = (ushort)count;
2518
2519                 nprintf(("Network","PXO : updated channel %s server count to %d\n",name,count));
2520         }       
2521 }
2522
2523 // status bar stuff -----------------------------------------------
2524
2525 // set the status text
2526 void multi_pxo_set_status_text(const char *txt)
2527 {
2528         // copy in the text
2529         SDL_strlcpy(Multi_pxo_status_text, txt, SDL_arraysize(Multi_pxo_status_text));
2530
2531         // make sure it fits properly
2532         gr_force_fit_string(Multi_pxo_status_text, 254, Multi_pxo_status_coords[gr_screen.res][2]);
2533 }
2534
2535 // blit the status text
2536 void multi_pxo_blit_status_text()
2537 {
2538         int w;
2539
2540         // center and draw the text
2541         if(strlen(Multi_pxo_status_text)) {
2542                 gr_set_color_fast(&Color_bright);
2543                 gr_get_string_size(&w, NULL, Multi_pxo_status_text);
2544                 gr_string(Multi_pxo_status_coords[gr_screen.res][0] + ((Multi_pxo_status_coords[gr_screen.res][2] - w)/2), Multi_pxo_status_coords[gr_screen.res][1], Multi_pxo_status_text);
2545         }
2546 }
2547
2548
2549 // channel related stuff -------------------------------------------
2550
2551 // get a list of channels on the server
2552 void multi_pxo_get_channels()
2553 {               
2554         SendChatString(NOX("/list"));
2555 }
2556
2557 // clear the old channel list
2558 void multi_pxo_clear_channels()
2559 {
2560         pxo_channel *moveup,*backup;
2561         
2562         // only clear a non-null list
2563         if(Multi_pxo_channels != NULL){         
2564                 // otherwise
2565                 moveup = Multi_pxo_channels;
2566                 backup = NULL;
2567                 if(moveup != NULL){
2568                         do {                    
2569                                 backup = moveup;
2570                                 moveup = moveup->next;                  
2571                 
2572                                 // free the struct itself
2573                                 free(backup);
2574                                 backup = NULL;
2575                         } while(moveup != Multi_pxo_channels);
2576                         Multi_pxo_channels = NULL;
2577                 }       
2578
2579                 // head of the list of available channels
2580                 Multi_pxo_channels = NULL;
2581                 Multi_pxo_channel_count = 0;
2582
2583                 // item we're going to start displaying at
2584                 Multi_pxo_channel_start = NULL;
2585                 Multi_pxo_channel_start_index = -1;
2586
2587                 // items we've currently got selected
2588                 Multi_pxo_channel_select = NULL;
2589         }       
2590 }
2591
2592 // parse the input string and make a list of new channels
2593 void multi_pxo_make_channels(char *chan_str)
2594 {       
2595         char *name_tok,*user_tok,*desc_tok;
2596         pxo_channel *res;
2597         pxo_channel *lookup;
2598         int num_users;
2599         
2600         nprintf(("Network","Making some channels!\n"));
2601
2602         // clear the channel list
2603         // multi_pxo_clear_channels();  
2604
2605         // set the last get time
2606         Multi_pxo_channel_last_refresh = f2fl(timer_get_fixed_seconds());
2607
2608         name_tok = strtok(chan_str," ");
2609         if(name_tok == NULL){
2610                 return;
2611         } 
2612         name_tok += 1;
2613         do {
2614                 // parse the user count token           
2615                 user_tok = strtok(NULL," ");
2616
2617                 // parse the channel description token
2618                 desc_tok = strtok(NULL,"$");
2619
2620                 // something invalid in the data, return here.....
2621                 if((name_tok == NULL) || (user_tok == NULL) || (desc_tok == NULL)){
2622                         return;
2623                 }
2624
2625                 // get the # of users
2626                 num_users = 0;          
2627                 num_users = (ubyte)atoi(user_tok);              
2628
2629                 // if the # of users is > 0, or its not an autojoin, place it on the display list
2630                 if((num_users > 0) || !multi_pxo_is_autojoin(name_tok)){
2631                         // see if it exists already, and if so, just update the user count
2632                         lookup = multi_pxo_find_channel(name_tok,Multi_pxo_channels);
2633                         
2634                         if(lookup != NULL){
2635                                 lookup->num_users = (short)num_users;
2636                         }
2637                         // add the channel
2638                         else {
2639                                 res = multi_pxo_add_channel(name_tok,&Multi_pxo_channels);
2640                                 if(res != NULL){
2641                                         //Multi_pxo_channel_count++;
2642                                         res->num_users = (short)num_users;
2643                                         SDL_strlcpy(res->desc, desc_tok, SDL_arraysize(res->desc));
2644                                 }               
2645                         }
2646                 }                               
2647
2648                 // get the next name token
2649                 name_tok = strtok(NULL," ");
2650         } while(name_tok != NULL);
2651                 
2652         // if we need to autojoin, do so now
2653         //if(Multi_pxo_must_autojoin){
2654         //      Multi_pxo_must_autojoin = 0;
2655         //      
2656         //      multi_pxo_autojoin();
2657         //}
2658
2659         // refresh channels
2660         multi_pxo_set_status_text(XSTR("Connected to Parallax Online",951));    
2661
2662         // if we haven't refreshed server counts yet, do it now
2663         if(Multi_pxo_channel_server_refresh < 0.0f){
2664                 multi_pxo_channel_refresh_servers();
2665         }
2666
2667         // if we don't already have this guy on the list, add him
2668         if(ON_CHANNEL()){
2669                 pxo_channel *lookup;
2670                 lookup = multi_pxo_find_channel(Multi_pxo_channel_current.name,Multi_pxo_channels);
2671                 if(lookup == NULL){
2672                         // create a new channel with the given name and place it on the channel list, return a pointer or NULL on fail
2673                         multi_pxo_add_channel(Multi_pxo_channel_current.name,&Multi_pxo_channels);
2674                 }
2675         }
2676 }
2677
2678 // create a new channel with the given name and place it on the channel list, return a pointer or NULL on fail
2679 pxo_channel *multi_pxo_add_channel(char *name,pxo_channel **list)
2680 {
2681         pxo_channel *new_channel;
2682
2683         // try and allocate a new pxo_channel struct
2684         new_channel = (pxo_channel *)malloc(sizeof(pxo_channel));
2685         if ( new_channel == NULL ) {
2686                 nprintf(("Network", "Cannot allocate space for new pxo_channel structure\n"));
2687                 return NULL;
2688         }       
2689         memset(new_channel,0,sizeof(pxo_channel));
2690         // try and allocate a string for the channel name
2691         SDL_strlcpy(new_channel->name, name, SDL_arraysize(new_channel->name));
2692
2693         // insert it on the list
2694         if ( *list != NULL ) {
2695                 new_channel->next = (*list)->next;
2696                 new_channel->next->prev = new_channel;
2697                 (*list)->next = new_channel;
2698                 new_channel->prev = *list;
2699         } else {
2700                 *list = new_channel;
2701                 (*list)->next = (*list)->prev = *list;
2702         }
2703                 
2704         Multi_pxo_channel_count++;
2705         return new_channel;
2706 }
2707
2708 // lookup a channel with the specified name
2709 pxo_channel *multi_pxo_find_channel(char *name,pxo_channel *list)
2710 {
2711         pxo_channel *moveup;
2712
2713         // look the sucker up
2714         moveup = list;
2715         if(moveup == NULL){
2716                 return NULL;
2717         } 
2718         do {
2719                 if(!SDL_strcasecmp(name,moveup->name)){
2720                         return moveup;
2721                 }
2722
2723                 moveup = moveup->next;
2724         } while((moveup != list) && (moveup != NULL));
2725
2726         return NULL;
2727 }
2728
2729 // process the channel list (select, etc)
2730 void multi_pxo_process_channels()
2731 {
2732         int item_index,my;
2733         int idx;
2734         
2735         // if we don't have a start item, but the list is non-null
2736         if((Multi_pxo_channel_start == NULL) && (Multi_pxo_channels != NULL)){
2737                 Multi_pxo_channel_start = Multi_pxo_channels;
2738                 Multi_pxo_channel_start_index = 0;
2739         } 
2740
2741         // if we don't have a selected item, but the list is non-null
2742         if((Multi_pxo_channel_select == NULL) && (Multi_pxo_channels != NULL)){
2743                 Multi_pxo_channel_select = Multi_pxo_channels;
2744
2745                 // set the text
2746                 multi_pxo_set_status_text(Multi_pxo_channel_select->desc);
2747         }
2748
2749         // if the "switch" delay timestamp is set, see if it has expired
2750         if((Multi_pxo_switch_delay != -1) && timestamp_elapsed(Multi_pxo_switch_delay)){
2751                 Multi_pxo_switch_delay = -1;
2752         }
2753
2754         // see if we have a mouse click on the channel region
2755         if(Multi_pxo_channel_button.pressed()){
2756                 Multi_pxo_channel_button.get_mouse_pos(NULL,&my);
2757
2758                 // index from the top
2759                 item_index = my / 10;
2760
2761                 // select the item if possible
2762                 if((item_index + Multi_pxo_channel_start_index) < Multi_pxo_channel_count){
2763                         Multi_pxo_channel_select = Multi_pxo_channel_start;
2764                         for(idx=0;idx<item_index;idx++){
2765                                 Multi_pxo_channel_select = Multi_pxo_channel_select->next;
2766                         }
2767
2768                         // set the text
2769                         multi_pxo_set_status_text(Multi_pxo_channel_select->desc);
2770                 }
2771         }
2772
2773         // last refresh time
2774         if((Multi_pxo_channel_last_refresh > 0.0f) && ((f2fl(timer_get_fixed_seconds()) - Multi_pxo_channel_last_refresh) > CHANNEL_REFRESH_TIME) ){
2775                 // refresh channels
2776                 multi_pxo_set_status_text(XSTR("Refreshing Public Channel List",952));                          
2777
2778                 // get a list of channels on the server (clear any old list as well)
2779                 multi_pxo_get_channels();
2780
2781                 // refresh
2782                 Multi_pxo_channel_last_refresh = -1.0f;
2783
2784                 nprintf(("Network","Refreshing channels\n"));
2785         }
2786
2787         // if we haven't updated our server channel counts in a while, do so again
2788         // last refresh time
2789         if((Multi_pxo_channel_server_refresh > 0.0f) && ((f2fl(timer_get_fixed_seconds()) - Multi_pxo_channel_server_refresh) > CHANNEL_SERVER_REFRESH_TIME) ){
2790                 // refresh server counts
2791                 // multi_pxo_set_status_text("Refreshing Public Channel Server Counts");
2792
2793                 // do it _NOW_ I"M RIGHT HERE KILL ME WHAT ARE YOU WAITING FOR DO IT KILL ME DO IT NOW!
2794                 multi_pxo_channel_refresh_servers();            
2795         }       
2796 }
2797
2798 // send a request to refresh our channel server counts
2799 void multi_pxo_channel_refresh_servers()
2800 {
2801         pxo_channel *lookup;
2802         filter_game_list_struct filter;
2803         
2804         // traverse the list of existing channels we know about and query the game tracker about them
2805         lookup = Multi_pxo_channels;
2806         if(lookup == NULL){
2807                 return;
2808         }
2809         do {
2810                 if(strlen(lookup->name)){
2811                         // copy in the info
2812                         memset(&filter,0,sizeof(filter_game_list_struct));
2813                         SDL_strlcpy(filter.channel, lookup->name, SDL_arraysize(filter.channel));
2814                         
2815                         // send the request
2816                         RequestGameCountWithFilter(&filter);
2817                 }
2818
2819                 // next item
2820                 lookup = lookup->next;
2821         } while((lookup != NULL) && (lookup != Multi_pxo_channels));
2822
2823         // record the time
2824         Multi_pxo_channel_server_refresh = f2fl(timer_get_fixed_seconds());
2825 }
2826
2827 // refresh current channel server count
2828 void multi_pxo_channel_refresh_current()
2829 {
2830         // send a request for a server count on this channel
2831         if(strlen(Multi_pxo_channel_current.name)){
2832                 // fill in the data
2833                 filter_game_list_struct filter;
2834                 memset(&filter,0,sizeof(filter_game_list_struct));
2835                 SDL_strlcpy(filter.channel, Multi_pxo_channel_current.name, SDL_arraysize(filter.channel));
2836
2837                 // send the request
2838                 RequestGameCountWithFilter(&filter);
2839         }               
2840 }
2841
2842 // display the channel list
2843 void multi_pxo_blit_channels()
2844 {
2845         pxo_channel *moveup;
2846         char chan_name[255];
2847         char chan_users[15];
2848         char chan_servers[15];
2849         int user_w,server_w;
2850         int disp_count,y_start;
2851
2852         // blit as many channels as we can
2853         disp_count = 0;
2854         y_start = Multi_pxo_chan_coords[gr_screen.res][1];
2855         moveup = Multi_pxo_channel_start;
2856         if(moveup == NULL){
2857                 return;
2858         }
2859         do {            
2860                 // if this is the currently selected item, highlight it
2861                 if(moveup == Multi_pxo_channel_select){
2862                         gr_set_color_fast(&Color_bright);
2863                 }
2864                 // otherwise draw it normally
2865                 else {
2866                         gr_set_color_fast(&Color_normal);
2867                 }
2868
2869                 // get the # of users on the channel
2870                 SDL_snprintf(chan_users, SDL_arraysize(chan_users), "%d", moveup->num_users);
2871
2872                 // get the width of the user count string
2873                 gr_get_string_size(&user_w, NULL, chan_users);
2874
2875                 // get the # of servers on the channel
2876                 SDL_snprintf(chan_servers, SDL_arraysize(chan_servers), "%d", moveup->num_servers);
2877
2878                 // get the width of the user count string
2879                 gr_get_string_size(&server_w, NULL, chan_servers);
2880
2881                 // make sure the name fits
2882                 SDL_assert(moveup->name);
2883                 SDL_strlcpy(chan_name, moveup->name, SDL_arraysize(chan_name));
2884                 gr_force_fit_string(chan_name, 254, Multi_pxo_chan_coords[gr_screen.res][2] - Multi_pxo_chan_column_offsets[gr_screen.res][CHAN_PLAYERS_COLUMN]);
2885
2886                 // blit the strings
2887                 gr_string(Multi_pxo_chan_coords[gr_screen.res][0], y_start, chan_name + 1);
2888                 gr_string(Multi_pxo_chan_coords[gr_screen.res][0] + Multi_pxo_chan_coords[gr_screen.res][2] - Multi_pxo_chan_column_offsets[gr_screen.res][CHAN_PLAYERS_COLUMN], y_start, chan_users);
2889                 gr_set_color_fast(&Color_bright);
2890                 gr_string(Multi_pxo_chan_coords[gr_screen.res][0] + Multi_pxo_chan_coords[gr_screen.res][2] - Multi_pxo_chan_column_offsets[gr_screen.res][CHAN_GAMES_COLUMN], y_start, chan_servers);
2891
2892                 // increment the displayed count
2893                 disp_count++;
2894                 y_start += 10;          
2895
2896                 // next item
2897                 moveup = moveup->next;
2898         } while((moveup != Multi_pxo_channels) && (disp_count < Multi_pxo_max_chan_display[gr_screen.res]));
2899 }
2900
2901 // scroll channel list up
2902 void multi_pxo_scroll_channels_up()
2903 {               
2904         // if we're already at the head of the list, do nothing
2905         if((Multi_pxo_channel_start == NULL) || (Multi_pxo_channel_start == Multi_pxo_channels)){
2906                 gamesnd_play_iface(SND_GENERAL_FAIL);           
2907                 return;
2908         }
2909         
2910         // otherwise move up one
2911         Multi_pxo_channel_start = Multi_pxo_channel_start->prev;
2912         Multi_pxo_channel_start_index--;
2913         gamesnd_play_iface(SND_USER_SELECT);
2914 }
2915
2916 // scroll channel list down
2917 void multi_pxo_scroll_channels_down()
2918 {
2919         // if we're already at the tail of the list, do nothing
2920         if((Multi_pxo_channel_start == NULL) || (Multi_pxo_channel_start->next == Multi_pxo_channels)){
2921                 gamesnd_play_iface(SND_GENERAL_FAIL);
2922                 return;
2923         }
2924
2925         // if we can't scroll further without going past the end of the viewable list, don't
2926         if((Multi_pxo_channel_start_index + Multi_pxo_max_chan_display[gr_screen.res]) >= Multi_pxo_channel_count){
2927                 gamesnd_play_iface(SND_GENERAL_FAIL);
2928                 return;
2929         }
2930
2931         // otherwise move down one
2932         Multi_pxo_channel_start = Multi_pxo_channel_start->next;
2933         Multi_pxo_channel_start_index++;
2934         gamesnd_play_iface(SND_USER_SELECT);
2935 }
2936
2937 // attempt to join a channel
2938 void multi_pxo_join_channel(pxo_channel *chan)
2939 {       
2940         char switch_msg[256];
2941         
2942         // if we're already on this channel, do nothing
2943         if(ON_CHANNEL() && !SDL_strcasecmp(chan->name,Multi_pxo_channel_current.name)){
2944                 return;
2945         }
2946
2947         // if we're already trying to join a channel, do nothing
2948         if(SWITCHING_CHANNELS()){
2949                 return;
2950         }
2951
2952         // try and join the channel     
2953         switch(SetNewChatChannel(chan->name)){
2954         case -1 :
2955                 Int3();
2956                 break;
2957                 
2958         case 0 :
2959                 // decrement the count of our current channel
2960                 pxo_channel *lookup;
2961                 lookup = multi_pxo_find_channel(Multi_pxo_channel_current.name,Multi_pxo_channels);
2962                 if(lookup != NULL){
2963                         lookup->num_users--;
2964                 }
2965
2966                 // set our current channel as none
2967                 memset(&Multi_pxo_channel_current,0,sizeof(pxo_channel));
2968                 Multi_pxo_channel_current.num_users = -1;
2969
2970                 multi_pxo_set_status_text(XSTR("Switching channels",953));
2971
2972                 // copy the channel
2973                 memcpy(&Multi_pxo_channel_switch,chan,sizeof(pxo_channel));
2974
2975                 // clear the player list
2976                 multi_pxo_clear_players();
2977
2978                 // display a line of text indicating that we're switching channels
2979                 if(strlen(Multi_pxo_channel_switch.name) > 1){
2980                         SDL_snprintf(switch_msg, SDL_arraysize(switch_msg), "[Switching to channel %s]", Multi_pxo_channel_switch.name + 1);
2981                 } else {
2982                         SDL_snprintf(switch_msg, SDL_arraysize(switch_msg), "[Switching to channel %s]", Multi_pxo_channel_switch.name);
2983                 }
2984                 multi_pxo_chat_process_incoming(switch_msg, CHAT_MODE_CHANNEL_SWITCH);
2985                 break;
2986
2987         case 1 :
2988                 Int3();         
2989         }       
2990 }
2991
2992 // handle any processing details if we're currently trying to join a channel
2993 void multi_pxo_handle_channel_change()
2994 {                       
2995         // if we're not switching channels, do nothing
2996         if(!SWITCHING_CHANNELS()){
2997                 return;
2998         }
2999
3000         // if we are, check the status
3001         switch(SetNewChatChannel(NULL)){
3002         // failed to switch
3003         case -1 :
3004                 // unset our switching struct
3005                 memset(&Multi_pxo_channel_switch,0,sizeof(pxo_channel));
3006                 Multi_pxo_channel_switch.num_users = -1;
3007
3008                 // notify of error
3009                 multi_pxo_set_status_text(XSTR("No channel (error while switching)",954));
3010                 break;
3011
3012         // still switching
3013         case 0:
3014                 break;
3015
3016         // successfully changed
3017         case 1:
3018                 // copy the current channel info, and unset the switching status
3019                 memcpy(&Multi_pxo_channel_current,&Multi_pxo_channel_switch,sizeof(pxo_channel));
3020                 Multi_pxo_channel_switch.num_users = -1;
3021
3022                 // set our "last" channel
3023                 SDL_strlcpy(Multi_pxo_channel_last, Multi_pxo_channel_current.name, SDL_arraysize(Multi_pxo_channel_last));
3024
3025                 // notify the user              
3026                 multi_pxo_set_status_text(XSTR("Connected to Parallax Online",951));
3027
3028                 // if we don't already have this guy on the list, add him
3029                 pxo_channel *lookup;
3030                 lookup = multi_pxo_find_channel(Multi_pxo_channel_current.name,Multi_pxo_channels);
3031                 if(lookup == NULL){
3032                         // create a new channel with the given name and place it on the channel list, return a pointer or NULL on fail
3033                         lookup = multi_pxo_add_channel(Multi_pxo_channel_current.name,&Multi_pxo_channels);
3034                 }
3035
3036                 // set the user count to be 1 (just me)
3037                 if(lookup != NULL){
3038                         lookup->num_users = 1;
3039                 }
3040
3041                 // clear the chat area
3042                 // multi_pxo_chat_clear();              
3043
3044                 // set the "switch" delay timestamp
3045                 Multi_pxo_switch_delay = timestamp(MULTI_PXO_SWITCH_DELAY_TIME);
3046
3047                 // refresh current channel server count
3048                 multi_pxo_channel_refresh_current();            
3049                 break;
3050         }
3051 }
3052
3053
3054 // player related stuff -------------------------------------------
3055
3056 // clear the old player list
3057 void multi_pxo_clear_players()
3058 {
3059         player_list *moveup,*backup;
3060         
3061         // if the list is null, don't free it up
3062         if(Multi_pxo_players != NULL){          
3063                 // otherwise
3064                 moveup = Multi_pxo_players;
3065                 backup = NULL;
3066                 if(moveup != NULL){
3067                         do {                    
3068                                 backup = moveup;
3069                                 moveup = moveup->next;                  
3070                 
3071                                 // free the struct itself
3072                                 free(backup);
3073                                 backup = NULL;
3074                         } while(moveup != Multi_pxo_players);
3075                         Multi_pxo_players = NULL;
3076                 }       
3077         }
3078
3079         Multi_pxo_player_start = NULL;  
3080         // Multi_pxo_player_start_index = -1;
3081         Multi_pxo_player_select = NULL;
3082
3083         // reset the slider
3084         // Multi_pxo_player_slider.set_numberItems(0);
3085
3086         // add a bunch of bogus players
3087         /*
3088         char str[50];
3089         for(int idx=0;idx<30;idx++){
3090                 sprintf(str,"player%d",idx);
3091                 multi_pxo_add_player(str);
3092         }
3093         */
3094 }
3095
3096 // create a new player with the given name and place it on the player list, return a pointer or NULL on fail
3097 player_list *multi_pxo_add_player(char *name)
3098 {
3099         player_list *new_player;
3100
3101         // try and allocate a new player_list struct
3102         new_player = (player_list *)malloc(sizeof(player_list));
3103         if ( new_player == NULL ) {
3104                 nprintf(("Network", "Cannot allocate space for new player_list structure\n"));
3105                 return NULL;
3106         }       
3107         // try and allocate a string for the channel name
3108         SDL_strlcpy(new_player->name, name, SDL_arraysize(new_player->name));
3109
3110         // insert it on the list
3111         if ( Multi_pxo_players != NULL ) {
3112                 new_player->next = Multi_pxo_players->next;
3113                 new_player->next->prev = new_player;
3114                 Multi_pxo_players->next = new_player;
3115                 new_player->prev = Multi_pxo_players;           
3116         } else {
3117                 Multi_pxo_players = new_player;
3118                 Multi_pxo_players->next = Multi_pxo_players->prev = Multi_pxo_players;
3119         }
3120
3121         // increment the start position
3122         if(Multi_pxo_player_start != NULL){
3123                 // Multi_pxo_player_start_index++;
3124         }
3125
3126         // new player
3127         Multi_pxo_player_count++;
3128
3129         // update the count on the slider
3130         // Multi_pxo_player_slider.set_numberItems(Multi_pxo_player_count);
3131                 
3132         return new_player;
3133 }
3134
3135 // remove a player with the given name
3136 void multi_pxo_del_player(char *name)
3137 {
3138         player_list *lookup;
3139
3140         // try and find this guy
3141         lookup = Multi_pxo_players;
3142         if(lookup == NULL){
3143                 return;
3144         }
3145         do {
3146                 // if we found a match, delete it
3147                 if(!SDL_strcasecmp(name,lookup->name)){
3148                         // if this is the only item on the list, free stuff up
3149                         if(lookup->next == lookup){
3150                                 SDL_assert(lookup == Multi_pxo_players);
3151                                 free(lookup);
3152                                 Multi_pxo_players = NULL;
3153                                 multi_pxo_clear_players();
3154                         }
3155                         // otherwise, just delete it 
3156                         else {
3157                                 lookup->next->prev = lookup->prev;
3158                                 lookup->prev->next = lookup->next;                              
3159                                 
3160                                 // if this was our selected item, unselect it
3161                                 if((lookup == Multi_pxo_player_select) && (Multi_pxo_player_select != NULL)){
3162                                         Multi_pxo_player_select = Multi_pxo_player_select->next;
3163                                 }
3164
3165                                 // if this was our point to start viewing from, select another
3166                                 if(lookup == Multi_pxo_player_start){
3167                                         // if this is the head of the list, move up one
3168                                         if(Multi_pxo_players == lookup){
3169                                                 Multi_pxo_player_start = Multi_pxo_player_start->next;
3170                                                 // Multi_pxo_player_start_index = 0;
3171                                         }
3172                                         // otherwise move back
3173                                         else {
3174                                                 Multi_pxo_player_start = Multi_pxo_player_start->prev;
3175                                                 // Multi_pxo_player_start_index++;
3176                                         }
3177                                 }
3178
3179                                 // if this is the head of the list, move it up
3180                                 if(lookup == Multi_pxo_players){
3181                                         Multi_pxo_players = Multi_pxo_players->next;                                    
3182                                 }
3183
3184                                 // free the item up
3185                                 lookup->next = NULL;
3186                                 lookup->prev = NULL;
3187                                 free(lookup);
3188                         }       
3189
3190                         // new player
3191                         Multi_pxo_player_count--;
3192                         SDL_assert(Multi_pxo_player_count >= 0);
3193
3194                         // update the count on the slider
3195                         // Multi_pxo_player_slider.set_numberItems(Multi_pxo_player_count);
3196                         // Multi_pxo_player_slider.force_currentItem(Multi_pxo_player_start_index);
3197                                 
3198                         // we're done now
3199                         return;
3200                 }
3201
3202                 // next item
3203                 lookup = lookup->next;
3204         } while((lookup != NULL) && (lookup != Multi_pxo_players));
3205 }
3206
3207 // try and find a player with the given name, return a pointer to his entry (or NULL)
3208 player_list *multi_pxo_find_player(char *name)
3209 {
3210         player_list *lookup;
3211
3212         // look through all players
3213         lookup = Multi_pxo_players;
3214         if(lookup == NULL){
3215                 return NULL;
3216         } 
3217         do {    
3218                 if(!SDL_strcasecmp(name,lookup->name)){
3219                         return lookup;
3220                 }
3221
3222                 lookup = lookup->next;
3223         } while((lookup != NULL) && (lookup != Multi_pxo_players));
3224
3225         // return NULL
3226         return NULL;
3227 }
3228
3229 // process the player list (select, etc)
3230 void multi_pxo_process_players()
3231 {
3232         int item_index,my;
3233         player_list *lookup;
3234         
3235         // if we don't have a start item, but the list is non-null
3236         if((Multi_pxo_player_start == NULL) && (Multi_pxo_players != NULL)){
3237                 Multi_pxo_player_start = Multi_pxo_players;             
3238                 // Multi_pxo_player_start_index = 0;
3239
3240                 // update the slider
3241                 // Multi_pxo_player_slider.set_currentItem(Multi_pxo_player_start_index);
3242         } 
3243
3244         // if we don't have a selected item, but the list is non-null
3245         if((Multi_pxo_player_select == NULL) && (Multi_pxo_players != NULL)){
3246                 Multi_pxo_player_select = Multi_pxo_players;
3247         }
3248
3249         // see if we have a mouse click on the channel region
3250         if(Multi_pxo_player_button.pressed()){
3251                 Multi_pxo_player_button.get_mouse_pos(NULL,&my);
3252
3253                 // index from the top
3254                 item_index = my / 10;
3255
3256                 // select the item if possible
3257                 lookup = Multi_pxo_player_start;
3258                 if(lookup == NULL){
3259                         return;
3260                 }
3261                 // select item 0
3262                 if(item_index == 0){
3263                         Multi_pxo_player_select = Multi_pxo_player_start;
3264                         return;
3265                 }
3266                 do {
3267                         // move to the next item
3268                         lookup = lookup->next;
3269                         item_index--;
3270
3271                         // if this item is our guy
3272                         if((item_index == 0) && (lookup != Multi_pxo_players)){
3273                                 Multi_pxo_player_select = lookup;
3274                                 return;
3275                         }
3276                 } while((lookup != Multi_pxo_players) && (item_index > 0));             
3277         }
3278 }
3279
3280 // display the player list
3281 void multi_pxo_blit_players()
3282 {
3283         player_list *moveup;
3284         char player_name[255];
3285         int disp_count,y_start;
3286
3287         // blit as many channels as we can
3288         disp_count = 0;
3289         y_start = Multi_pxo_player_coords[gr_screen.res][1];
3290         moveup = Multi_pxo_player_start;
3291         if(moveup == NULL){
3292                 return;
3293         }
3294         do {
3295                 // if this is the currently selected item, highlight it
3296                 if(moveup == Multi_pxo_player_select){
3297                         gr_set_color_fast(&Color_bright);
3298                 }
3299                 // otherwise draw it normally
3300                 else {
3301                         gr_set_color_fast(&Color_normal);
3302                 }
3303
3304                 // make sure the string fits            
3305                 SDL_strlcpy(player_name, moveup->name, SDL_arraysize(player_name));
3306                 gr_force_fit_string(player_name, 254, Multi_pxo_player_coords[gr_screen.res][2]);
3307
3308                 // blit the string
3309                 gr_string(Multi_pxo_player_coords[gr_screen.res][0], y_start, player_name);
3310
3311                 // increment the displayed count
3312                 disp_count++;
3313                 y_start += 10;
3314
3315                 // next item
3316                 moveup = moveup->next;
3317         } while((moveup != Multi_pxo_players) && (disp_count < Multi_pxo_max_player_display[gr_screen.res]));
3318 }
3319
3320 // scroll player list up
3321 void multi_pxo_scroll_players_up()
3322 {
3323         // if we're already at the head of the list, do nothing
3324         if((Multi_pxo_player_start == NULL) || (Multi_pxo_player_start == Multi_pxo_players)){
3325                 gamesnd_play_iface(SND_GENERAL_FAIL);           
3326                 return;
3327         }
3328         
3329         // otherwise move up one
3330         Multi_pxo_player_start = Multi_pxo_player_start->prev;  
3331         // Multi_pxo_player_start_index--;
3332         // SDL_assert(Multi_pxo_player_start_index >= 0);
3333
3334         gamesnd_play_iface(SND_USER_SELECT);
3335 }
3336
3337 // scroll player list down
3338 void multi_pxo_scroll_players_down()
3339 {
3340         player_list *lookup;
3341         int count = 0;
3342         
3343         // see if its okay to scroll down
3344         lookup = Multi_pxo_player_start;
3345         if(lookup == NULL ){
3346                 gamesnd_play_iface(SND_GENERAL_FAIL);
3347                 return;
3348         }
3349         count = 0;
3350         while(lookup->next != Multi_pxo_players){
3351                 lookup = lookup->next;
3352                 count++;
3353         }
3354         
3355         // if we can move down
3356         if(count >= Multi_pxo_max_player_display[gr_screen.res]){
3357                 Multi_pxo_player_start = Multi_pxo_player_start->next;
3358
3359                 // Multi_pxo_player_start_index++;
3360
3361                 gamesnd_play_iface(SND_USER_SELECT);
3362         } else {
3363                 gamesnd_play_iface(SND_GENERAL_FAIL);
3364         }       
3365 }
3366
3367
3368 // chat text stuff -----------------------------------------
3369
3370 // initialize and create the chat text linked list
3371 void multi_pxo_chat_init()
3372 {
3373         int idx;
3374         chat_line *new_line;
3375
3376         // no chat lines
3377         Multi_pxo_chat = NULL;
3378         Multi_pxo_chat_add = NULL;
3379         Multi_pxo_chat_start = NULL;
3380         Multi_pxo_chat_start_index = -1;
3381
3382         // create the lines in a non-circular doubly linked list
3383         for(idx=0;idx<MAX_CHAT_LINES;idx++){
3384                 new_line = (chat_line*)malloc(sizeof(chat_line));       
3385                 
3386                 // clear the line out
3387                 SDL_assert(new_line != NULL);
3388                 if(new_line == NULL){
3389                         return;
3390                 }
3391                 memset(new_line,0,sizeof(chat_line));
3392                 new_line->prev = NULL;
3393                 new_line->next = NULL;          
3394
3395                 // insert it into the (empty) list
3396                 if(Multi_pxo_chat == NULL){
3397                         Multi_pxo_chat = new_line;
3398                 }
3399                 // insert it onto the (non-empty) list
3400                 else {
3401                         Multi_pxo_chat->prev = new_line;
3402                         new_line->next = Multi_pxo_chat;
3403                         Multi_pxo_chat = new_line;
3404                 }
3405         }
3406
3407         // start adding chat lines at the beginning of the list
3408         Multi_pxo_chat_add = Multi_pxo_chat;
3409 }
3410
3411 // free up all chat list stuff
3412 void multi_pxo_chat_free()
3413 {
3414         chat_line *moveup, *backup;     
3415
3416         // free all items up
3417         moveup = Multi_pxo_chat;
3418         while(moveup != NULL){
3419                 backup = moveup;                
3420                 moveup = moveup->next;
3421
3422                 free(backup);
3423         }
3424
3425         // no chat lines
3426         Multi_pxo_chat = NULL;
3427         Multi_pxo_chat_add = NULL;
3428         Multi_pxo_chat_start = NULL;
3429         Multi_pxo_chat_start_index = -1;
3430         Multi_pxo_chat_count = 0;
3431         Multi_pxo_chat_slider.set_numberItems(0);       
3432 }
3433
3434 // clear all lines of chat text in the chat area
3435 void multi_pxo_chat_clear()
3436 {
3437         chat_line *moveup;
3438
3439         // clear the text in all the lines
3440         moveup = Multi_pxo_chat;
3441         while(moveup != NULL){
3442                 SDL_zero(moveup->text);
3443                 moveup = moveup->next;
3444         }
3445
3446         // how many chat lines we have
3447         Multi_pxo_chat_count = 0;
3448
3449         // start adding chat lines at the beginning of the list
3450         Multi_pxo_chat_add = Multi_pxo_chat;
3451 }
3452
3453 // add a line of text
3454 void multi_pxo_chat_add_line(char *txt, int mode)
3455 {
3456         chat_line *temp;
3457         
3458         // copy in the text
3459         SDL_assert(Multi_pxo_chat_add != NULL);
3460         SDL_strlcpy(Multi_pxo_chat_add->text, txt, SDL_arraysize(Multi_pxo_chat_add->text));
3461         Multi_pxo_chat_add->mode = mode;
3462
3463         // if we're at the end of the list, move the front item down
3464         if(Multi_pxo_chat_add->next == NULL) {
3465                 // store the new "head" of the list
3466                 temp = Multi_pxo_chat->next;
3467
3468                 // move the current head to the end of the list
3469                 Multi_pxo_chat_add->next = Multi_pxo_chat;
3470                 temp->prev = NULL;              
3471                 Multi_pxo_chat->prev = Multi_pxo_chat_add;
3472                 Multi_pxo_chat->next = NULL;
3473
3474                 // reset the head of the list
3475                 Multi_pxo_chat = temp;
3476
3477                 // set the new add line
3478                 Multi_pxo_chat_add = Multi_pxo_chat_add->next;
3479                 SDL_zero(Multi_pxo_chat_add->text);
3480                 Multi_pxo_chat_add->mode = CHAT_MODE_NORMAL;
3481         } 
3482         // if we're not at the end of the list, just move up by one
3483         else {
3484                 // set the new add line
3485                 Multi_pxo_chat_add = Multi_pxo_chat_add->next;
3486         }
3487
3488         // if we've reached max chat lines, don't increment
3489         if(Multi_pxo_chat_count < MAX_CHAT_LINES) {
3490                 Multi_pxo_chat_count++;
3491         }
3492
3493         // set the count
3494         Multi_pxo_chat_slider.set_numberItems(Multi_pxo_chat_count > Multi_pxo_max_chat_display[gr_screen.res] ? Multi_pxo_chat_count - Multi_pxo_max_chat_display[gr_screen.res] : 0, 0);              // the 0 means don't reset
3495
3496         // force the position, in case we arent at the bottom of the list
3497
3498
3499         // move to the bottom of the chat area
3500         /*
3501         if(from_self){
3502                 multi_pxo_goto_bottom();
3503         }       
3504         else {          
3505                 // if we have more than the # of lines displayable
3506                 if(Multi_pxo_chat_count >= MULTI_PXO_MAX_CHAT_DISPLAY){
3507                 }
3508                 multi_pxo_goto_bottom();
3509         }
3510         */
3511         multi_pxo_goto_bottom();
3512 }
3513
3514 // process an incoming line of text
3515 void multi_pxo_chat_process_incoming(const char *txt,int mode)
3516 {
3517         char msg_total[512],line[512];
3518         int     n_lines,idx;
3519         int     n_chars[20];
3520         char    *p_str[20];                     //  the initial line (unindented)       
3521         const char *priv_ptr = NULL;
3522
3523         // filter out "has left" channel messages, when switching channels
3524         if((SWITCHING_CHANNELS() || ((Multi_pxo_switch_delay != -1) && !timestamp_elapsed(Multi_pxo_switch_delay))) && 
3525                 multi_pxo_chat_is_left_message(txt)){
3526                 return;
3527         }
3528                 
3529         // if the text is a private message, return a pointer to the beginning of the message, otherwise return NULL
3530         priv_ptr = multi_pxo_chat_is_private(txt);
3531         if(priv_ptr != NULL){           
3532                 SDL_strlcpy(msg_total, priv_ptr, SDL_arraysize(msg_total));
3533         } else {
3534                 SDL_strlcpy(msg_total, txt, SDL_arraysize(msg_total));
3535         }       
3536
3537         // determine what mode to display this text in
3538
3539         // if this is private chat
3540         if(priv_ptr != NULL){
3541                 mode = CHAT_MODE_PRIVATE;
3542         }
3543         // all other chat
3544         else {
3545                 // if this is a server message
3546                 if(multi_pxo_is_server_text(txt)){
3547                         mode = CHAT_MODE_SERVER;
3548                 }
3549                 // if this is a MOTD
3550                 else if(multi_pxo_is_motd_text(txt)){
3551                         // mode = CHAT_MODE_MOTD;
3552                         // stuff the motd
3553                         multi_pxo_motd_add_text(txt);
3554                         return;
3555                 } 
3556                 // if this is the end of motd text
3557                 else if(multi_pxo_is_end_of_motd_text(txt)){
3558                         multi_pxo_set_end_of_motd();
3559                         return;
3560                 }
3561         }
3562
3563         // split the text up into as many lines as necessary
3564         n_lines = split_str(msg_total, Multi_pxo_chat_coords[gr_screen.res][2] - 5, n_chars, p_str, 3);
3565         SDL_assert((n_lines != -1) && (n_lines <= 20));
3566         if((n_lines < 0) || (n_lines > 20)) {
3567                 return;
3568         }
3569
3570         // if the string fits on one line
3571         if(n_lines == 1) {
3572                 multi_pxo_chat_add_line(msg_total,mode);
3573
3574                 // don't pad with extra spaces if its from the server
3575                 /*
3576                 if(mode != CHAT_MODE_SERVER){
3577                         multi_pxo_chat_add_line("",CHAT_MODE_NORMAL);
3578                 }
3579                 */
3580         }
3581         // if the string was split into multiple lines
3582         else {
3583                 // add the first line           
3584                 memcpy(line,p_str[0],n_chars[0]);
3585                 line[n_chars[0]] = '\0';
3586                 multi_pxo_chat_add_line(line,mode);
3587
3588                 // copy the rest of the lines
3589                 for(idx=1; idx<n_lines; idx++){
3590                         memcpy(line,p_str[idx],n_chars[idx]);
3591                         line[n_chars[idx]] = '\0';                      
3592                         
3593                         // unless the current mode is server or "switching channels", make all these CHAT_MODE_CARRY
3594                         if((mode != CHAT_MODE_SERVER) && (mode != CHAT_MODE_CHANNEL_SWITCH)){
3595                                 mode = CHAT_MODE_CARRY;
3596                         }                       
3597                         multi_pxo_chat_add_line(line, mode);
3598                 }
3599         }       
3600 }
3601
3602 // blit the chat text
3603 void multi_pxo_chat_blit()
3604 {
3605         int y_start;
3606         int disp_count,token_width;
3607         char piece[100];
3608         char title[255];
3609         char *tok;
3610         chat_line *moveup;
3611
3612         // blit the title line
3613         if(ON_CHANNEL()){
3614                 if(strlen(Multi_pxo_channel_current.name) > 1){
3615                         SDL_snprintf(title, SDL_arraysize(title), XSTR("%s on %s", 955), Multi_pxo_nick, Multi_pxo_channel_current.name+1);  // [[ <who> on <channel> ]]
3616                 } else {
3617                         SDL_snprintf(title, SDL_arraysize(title), XSTR("%s on %s", 955), Multi_pxo_nick, Multi_pxo_channel_current.name);         // [[ <who> on <channel> ]]
3618                 }
3619         } else {
3620                 SDL_strlcpy(title, XSTR("Parallax Online - No Channel", 956), SDL_arraysize(title));
3621         }       
3622         gr_force_fit_string(title, 254, Multi_pxo_chat_coords[gr_screen.res][2] - 10);
3623         gr_get_string_size(&token_width,NULL,title);
3624         gr_set_color_fast(&Color_normal);
3625         gr_string(Multi_pxo_chat_coords[gr_screen.res][0] + ((Multi_pxo_chat_coords[gr_screen.res][2] - token_width)/2), Multi_pxo_chat_title_y[gr_screen.res], title); 
3626
3627         // blit all active lines of text
3628         moveup = Multi_pxo_chat_start;  
3629         disp_count = 0;
3630         y_start = Multi_pxo_chat_coords[gr_screen.res][1];
3631         while((moveup != NULL) && (moveup != Multi_pxo_chat_add) && (disp_count < (Multi_pxo_max_chat_display[gr_screen.res]))){
3632                 switch(moveup->mode){
3633                 // if this is text from the server, display it all "bright"
3634                 case CHAT_MODE_SERVER:                          
3635                         gr_set_color_fast(&Color_bright);
3636                         gr_string(Multi_pxo_chat_coords[gr_screen.res][0], y_start, moveup->text);
3637                         break;
3638
3639                 // if this is motd, display it all "bright"
3640                 case CHAT_MODE_MOTD:
3641                         gr_set_color_fast(&Color_bright_white);
3642                         gr_string(Multi_pxo_chat_coords[gr_screen.res][0], y_start, moveup->text);
3643                         break;
3644
3645                 // normal mode, just highlight the server
3646                 case CHAT_MODE_PRIVATE:         
3647                 case CHAT_MODE_NORMAL:                                  
3648                         SDL_strlcpy(piece, moveup->text, SDL_arraysize(piece));
3649                         tok = strtok(piece," ");
3650                         if(tok != NULL){
3651                                 // get the width of just the first "piece"
3652                                 gr_get_string_size(&token_width, NULL, tok);
3653                                 
3654                                 // draw it brightly
3655                                 gr_set_color_fast(&Color_bright);
3656                                 gr_string(Multi_pxo_chat_coords[gr_screen.res][0], y_start, tok);
3657
3658                                 // draw the rest of the string normally
3659                                 tok = strtok(NULL,"");
3660                                 if(tok != NULL){
3661                                         gr_set_color_fast(&Color_normal);
3662                                         gr_string(Multi_pxo_chat_coords[gr_screen.res][0] + token_width + 6, y_start, tok);
3663                                 }
3664                         }
3665                         break;
3666                 
3667                 // carry mode, display with no highlight
3668                 case CHAT_MODE_CARRY:
3669                         gr_set_color_fast(&Color_normal);
3670                         gr_string(Multi_pxo_chat_coords[gr_screen.res][0], y_start, moveup->text);
3671                         break;
3672
3673                 // "switching channels mode", display it bright
3674                 case CHAT_MODE_CHANNEL_SWITCH:
3675                         gr_set_color_fast(&Color_bright);
3676                         gr_string(Multi_pxo_chat_coords[gr_screen.res][0], y_start, moveup->text);
3677                         break;
3678                 }
3679                 
3680                 // next chat line
3681                 moveup = moveup->next;
3682                 disp_count++;
3683                 y_start += 10;
3684         }
3685
3686         if ((moveup != Multi_pxo_chat_add) && (moveup != NULL)) {
3687                 Can_scroll_down = 1;
3688         } else {
3689                 Can_scroll_down = 0;
3690         }
3691 }
3692
3693 // scroll to the very bottom of the chat area
3694 void multi_pxo_goto_bottom()
3695 {
3696         chat_line *backup;
3697         int idx;
3698
3699         if (Multi_pxo_chat == NULL) {
3700                 return;
3701         }
3702         
3703         // if we have less than the displayable amount of lines, do nothing
3704         if(Multi_pxo_chat_count <= Multi_pxo_max_chat_display[gr_screen.res]){
3705                 Multi_pxo_chat_start = Multi_pxo_chat;                                          
3706                 
3707                 // nothing to do for the slider
3708                 Multi_pxo_chat_slider.set_numberItems(0);
3709                 return;
3710         }
3711
3712         if (!Can_scroll_down)
3713         {
3714                 // otherwise move back the right # of items
3715                 backup = Multi_pxo_chat_add;    
3716                 for(idx=0; idx<Multi_pxo_max_chat_display[gr_screen.res]; idx++){
3717                         SDL_assert(backup->prev != NULL);
3718                         backup = backup->prev;          
3719                 }
3720
3721                 Multi_pxo_chat_start = backup;
3722
3723                 // fixup the start index
3724                 multi_pxo_chat_adjust_start();  
3725         }
3726 }
3727
3728 // scroll the text up
3729 void multi_pxo_scroll_chat_up()
3730 {
3731         // if we're already at the top of the list, don't do anything   
3732         if ((Multi_pxo_chat_start == NULL) || (Multi_pxo_chat_start == Multi_pxo_chat)) {
3733                 gamesnd_play_iface(SND_GENERAL_FAIL);
3734                 return;
3735         }
3736
3737         // otherwise move up one
3738         Multi_pxo_chat_start = Multi_pxo_chat_start->prev;      
3739
3740         multi_pxo_chat_adjust_start();  
3741         
3742         gamesnd_play_iface(SND_USER_SELECT);
3743 }
3744
3745 // returns 1 if we can scroll down, 0 otherwise
3746 int multi_pxo_can_scroll_down()
3747 {
3748         chat_line *lookup;
3749         int count = 0;
3750         
3751         // see if its okay to scroll down
3752         lookup = Multi_pxo_chat_start;
3753         if (lookup == NULL) {
3754         //      gamesnd_play_iface(SND_GENERAL_FAIL);
3755                 return 0;
3756         }
3757         count = 0;
3758         while (lookup != Multi_pxo_chat_add) {
3759                 lookup = lookup->next;
3760                 count++;
3761         }
3762         
3763         // check if we can move down, return accordingly
3764         if (count > Multi_pxo_max_chat_display[gr_screen.res]) {
3765                 return 1;
3766         } else {
3767                 return 0;
3768         }
3769 }
3770
3771 // scroll the text down
3772 void multi_pxo_scroll_chat_down()
3773 {
3774         // if we can move down
3775         if (multi_pxo_can_scroll_down()) {
3776                 Multi_pxo_chat_start = Multi_pxo_chat_start->next;              
3777                 multi_pxo_chat_adjust_start();  
3778                 gamesnd_play_iface(SND_USER_SELECT);
3779         } else {
3780                 gamesnd_play_iface(SND_GENERAL_FAIL);
3781         }
3782 }
3783
3784 // process chat controls
3785 void multi_pxo_chat_process()
3786 {
3787         char *remainder = NULL;
3788         const char *result = NULL;
3789         char msg[512];
3790         int msg_pixel_width;
3791
3792         // if the chat line is getting too long, fire off the message, putting the last
3793         // word on the next input line.
3794         SDL_zero(msg);
3795         Multi_pxo_chat_input.get_text(msg);
3796
3797         // determine if the width of the string in pixels is > than the inputbox width -- if so,
3798         // then send the message
3799         gr_get_string_size(&msg_pixel_width, NULL, msg);
3800         // if ( msg_pixel_width >= (Chatbox_inputbox_w - Player->short_callsign_width) ) {
3801         if ( msg_pixel_width >= (Multi_pxo_input_coords[gr_screen.res][2])) {
3802                 remainder = strrchr(msg, ' ');
3803                 if ( remainder ) {
3804                         *remainder = '\0';
3805                         remainder++;
3806                 }       
3807                 
3808                 // if we're connected to a channel, send the chat to the server
3809                 if(ON_CHANNEL()){
3810                         result = SendChatString(msg,1);
3811                         if(result != NULL){
3812                                 multi_pxo_chat_process_incoming(result);
3813                         }
3814
3815                         // display any remainder of text on the next line
3816                         Multi_pxo_chat_input.set_text( remainder ? remainder : "" );
3817                 } else {
3818                         Multi_pxo_chat_input.set_text("");
3819                 }
3820         } else if((Multi_pxo_chat_input.pressed() && (strlen(msg) > 0)) || (strlen(msg) >= MAX_CHAT_LINE_LEN)) { 
3821                 // tack on the null terminator in the boundary case
3822                 int x = strlen(msg);
3823                 if(x >= MAX_CHAT_LINE_LEN){
3824                         msg[MAX_CHAT_LINE_LEN-1] = '\0';
3825                 }               
3826
3827                 // ignore "/nick" commands
3828                 if(multi_pxo_is_nick_command(msg)){
3829                         Multi_pxo_chat_input.set_text("");
3830                         return;
3831                 }
3832                 
3833                 // send the chat to the server                  
3834                 // if we're connected to a channel, send the chat to the server
3835                 if(ON_CHANNEL()){               
3836                         result = SendChatString(msg,1);
3837                         if(result != NULL){
3838                                 multi_pxo_chat_process_incoming(result);
3839                         }
3840
3841                         // display any remainder of text on the next line
3842                         Multi_pxo_chat_input.set_text( remainder ? remainder : "" );
3843                 } else {
3844                         Multi_pxo_chat_input.set_text("");
3845                 }
3846         }       
3847 }
3848
3849 // if the text is a private message, return a pointer to the beginning of the message, otherwise return NULL
3850 //XSTR:OFF
3851
3852 // NOTE : DO NOT LOCALIZE THESE STRINGS!!!! THEY ARE CONSTANTS WHICH ARE CHECKED AGAINST 
3853 // PXO CHAT SERVER DATA. THEY CANNOT CHANGE!!!
3854 #define PMSG_FROM                       "private message from "
3855 #define PMSG_TO                 "private message to "
3856 const char *multi_pxo_chat_is_private(const char *txt)
3857 {
3858         // quick check
3859         if( strlen(txt) > strlen( PMSG_FROM ) ){        
3860                 // otherwise do a comparison
3861                 if(!SDL_strncasecmp( txt, PMSG_FROM, strlen(PMSG_FROM) )){
3862                         return &txt[strlen( PMSG_FROM )];
3863                 } 
3864         }
3865
3866         // quick check
3867         if(strlen(txt) > strlen( PMSG_TO )){    
3868                 // otherwise do a comparison
3869                 if(!SDL_strncasecmp(txt,PMSG_TO,strlen(PMSG_TO))){
3870                         return &txt[strlen(PMSG_TO)];
3871                 } 
3872         }
3873         
3874         return NULL;
3875 }
3876 //XSTR:ON
3877
3878 // if the text came from the server
3879 int multi_pxo_is_server_text(const char *txt)
3880 {               
3881         // if the message is prefaced by a ***
3882         if((strlen(txt) >= strlen(MULTI_PXO_SERVER_PREFIX)) && !strncmp(txt, MULTI_PXO_SERVER_PREFIX, strlen(MULTI_PXO_SERVER_PREFIX))){
3883                 return 1;
3884         }
3885
3886         return 0;
3887 }
3888
3889 // if the text is message of the day text
3890 int multi_pxo_is_motd_text(const char *txt)
3891 {
3892         // if we're not on a channel, and this is not a channel switching message assume its coming from a server
3893         if((strlen(txt) >= strlen(PXO_CHAT_MOTD_PREFIX)) && !strncmp(txt, PXO_CHAT_MOTD_PREFIX, strlen(PXO_CHAT_MOTD_PREFIX))){
3894                 return 1;
3895         }       
3896         
3897         return 0;
3898 }
3899
3900 // if the text is the end of motd text
3901 int multi_pxo_is_end_of_motd_text(const char *txt)
3902 {
3903         // if we're not on a channel, and this is not a channel switching message assume its coming from a server
3904         if((strlen(txt) >= strlen(PXO_CHAT_END_OF_MOTD_PREFIX)) && !strncmp(txt, PXO_CHAT_END_OF_MOTD_PREFIX, strlen(PXO_CHAT_END_OF_MOTD_PREFIX))){
3905                 return 1;
3906         }       
3907         
3908         return 0;
3909 }
3910
3911 // if the text is a "has left message" from the server
3912 int multi_pxo_chat_is_left_message(const char *txt)
3913 {
3914         char last_portion[100];
3915         
3916         // if the text is not server text
3917         if(!multi_pxo_is_server_text(txt)){
3918                 return 0;
3919         }
3920
3921         // check to see if the last portion is the correct wording
3922         SDL_zero(last_portion);
3923         if((strlen(txt) > strlen(MULTI_PXO_HAS_LEFT)) && !strcmp(&txt[strlen(txt) - strlen(MULTI_PXO_HAS_LEFT)], MULTI_PXO_HAS_LEFT)){
3924                 return 1;
3925         }
3926
3927         // check the end of the line
3928         return 0;
3929 }
3930
3931 // recalculate the chat start index, and adjust the slider properly
3932 void multi_pxo_chat_adjust_start()
3933 {
3934         chat_line *moveup;
3935
3936         // if we have no chat
3937         if (Multi_pxo_chat == NULL) {
3938                 Multi_pxo_chat_start_index = -1;                
3939                 return;
3940         }
3941
3942         // traverse
3943         Multi_pxo_chat_start_index = 0;
3944         moveup = Multi_pxo_chat;
3945         while((moveup != Multi_pxo_chat_start) && (moveup != NULL)){
3946                 Multi_pxo_chat_start_index++;
3947                 moveup = moveup->next;
3948         }
3949
3950         // set the slider index
3951         Multi_pxo_chat_slider.force_currentItem(Multi_pxo_chat_start_index);
3952 }
3953
3954 // motd stuff ---------------------------------------------------------
3955
3956 // initialize motd when going into this screen
3957 void multi_pxo_motd_init()
3958 {
3959         // zero the motd string
3960         SDL_strlcpy(Pxo_motd, "", SDL_arraysize(Pxo_motd));
3961
3962         // haven't gotten it yet
3963         Pxo_motd_end = 0;
3964
3965         // haven't read it yet either
3966         Pxo_motd_read = 0;
3967 }
3968
3969 // set the motd text
3970 void multi_pxo_motd_add_text(const char *text)
3971 {
3972         int cur_len = strlen(Pxo_motd);
3973         int new_len;
3974
3975         // sanity
3976         if(text == NULL){
3977                 return;
3978         }
3979
3980         // make sure its motd text
3981         SDL_assert(multi_pxo_is_motd_text(text));
3982         if(!multi_pxo_is_motd_text(text)){
3983                 return;
3984         }
3985         
3986         // if its a 0 line motd
3987         if(strlen(text) <= strlen(PXO_CHAT_MOTD_PREFIX)){
3988                 return;
3989         }
3990
3991         // add text to the motd
3992         new_len = strlen(text + strlen(PXO_CHAT_MOTD_PREFIX)) - 1;
3993         if((cur_len + new_len + 1) < MAX_PXO_MOTD_LEN){
3994                 SDL_strlcat(Pxo_motd, text + strlen(PXO_CHAT_MOTD_PREFIX) + 1, SDL_arraysize(Pxo_motd));
3995                 SDL_strlcat(Pxo_motd, "\n", SDL_arraysize(Pxo_motd));
3996                 mprintf(("MOTD ADD : %s\n", Pxo_motd));
3997         }
3998 }
3999
4000 // set end of motd
4001 void multi_pxo_set_end_of_motd()
4002 {
4003         int blink = 1;
4004
4005         Pxo_motd_end = 1;
4006         mprintf(("MOTD ALL : %s\n", Pxo_motd));
4007         
4008         Pxo_motd_read = 0;
4009
4010         // do we have an old MOTD file laying around? If so, read it in and see if its the same
4011         unsigned int old_chksum;
4012         unsigned long new_chksum;
4013
4014         // checksum the current motd            
4015         new_chksum = cf_add_chksum_long(0, Pxo_motd, strlen(Pxo_motd));         
4016
4017         // checksum the old motd if its lying around
4018         CFILE *in = cfopen("oldmotd.txt", "rb");
4019         if(in != NULL){
4020                 // read the old checksum
4021                 cfread(&old_chksum, sizeof(old_chksum), 1, in);
4022                 cfclose(in);
4023                 
4024                 // same checksum? no blink
4025                 if(new_chksum == old_chksum){
4026                         blink = 0;
4027                 }
4028         }       
4029         
4030         // write out the motd for next time
4031         if(strlen(Pxo_motd)){
4032                 CFILE *out = cfopen("oldmotd.txt", "wb", CFILE_NORMAL, CF_TYPE_DATA);
4033                 if(out != NULL){
4034                         // write all the text
4035                         cfwrite(&new_chksum, sizeof(new_chksum), 1, out);
4036                         
4037                         // close the outfile
4038                         cfclose(out);
4039                 }
4040         }
4041         
4042         // set the blink stamp
4043         Pxo_motd_blink_stamp = -1;
4044         if(blink){              
4045                 Pxo_motd_blink_on = 0;
4046                 if(!Pxo_motd_blinked_already){
4047                         Pxo_motd_blink_stamp = timestamp(PXO_MOTD_BLINK_TIME);
4048                         Pxo_motd_blink_on = 1;
4049                 }
4050         }
4051
4052         Pxo_motd_blinked_already = 1;
4053 }
4054
4055 // display the motd dialog
4056 void multi_pxo_motd_dialog()
4057 {
4058         // mark the motd as read
4059         Pxo_motd_read = 1;
4060
4061         // simple popup, with a slider
4062         popup(PF_USE_AFFIRMATIVE_ICON, 1, POPUP_OK, Pxo_motd);
4063 }
4064
4065 // call to maybe blink the motd button
4066 void multi_pxo_motd_maybe_blit()
4067 {
4068         // if we got the end of the motd, and he hasn't read it yet
4069         if(Pxo_motd_end && !Pxo_motd_read && (Pxo_motd_blink_stamp != -1)){
4070                 // if the timestamp elapsed, flip the blink flag
4071                 if(timestamp_elapsed(Pxo_motd_blink_stamp)){
4072                         Pxo_motd_blink_on = !Pxo_motd_blink_on;
4073                         Pxo_motd_blink_stamp = timestamp(PXO_MOTD_BLINK_TIME);
4074                 }
4075
4076                 // draw properly
4077                 if(Pxo_motd_blink_on){
4078                         Multi_pxo_buttons[gr_screen.res][MULTI_PXO_MOTD].button.draw_forced(2);
4079                 }
4080         }       
4081 }
4082
4083
4084 // common dialog stuff ------------------------------------------------
4085
4086 int Multi_pxo_searching = 0; 
4087
4088 // initialize the common dialog with the passed max input length
4089 void multi_pxo_com_init(int input_len)
4090 {
4091         int idx;
4092         
4093         // create the interface window
4094         Multi_pxo_com_window.create(0, 0, gr_screen.max_w,gr_screen.max_h, 0);
4095         Multi_pxo_com_window.set_mask_bmap(Multi_pxo_com_mask_fname[gr_screen.res]);    
4096
4097         // create the interface buttons
4098         for(idx=0; idx<MULTI_PXO_COM_NUM_BUTTONS; idx++){
4099                 // create the object
4100                 Multi_pxo_com_buttons[gr_screen.res][idx].button.create(&Multi_pxo_com_window, "", Multi_pxo_com_buttons[gr_screen.res][idx].x, Multi_pxo_com_buttons[gr_screen.res][idx].y, 1, 1, 0, 1);
4101
4102                 // set the sound to play when highlighted
4103                 Multi_pxo_com_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
4104
4105                 // set the ani for the button
4106                 Multi_pxo_com_buttons[gr_screen.res][idx].button.set_bmaps(Multi_pxo_com_buttons[gr_screen.res][idx].filename);
4107
4108                 // set the hotspot
4109                 Multi_pxo_com_buttons[gr_screen.res][idx].button.link_hotspot(Multi_pxo_com_buttons[gr_screen.res][idx].hotspot);
4110         }                       
4111
4112         // add xstrs
4113         for(idx=0; idx<MULTI_PXO_COM_NUM_TEXT; idx++){
4114                 Multi_pxo_com_window.add_XSTR(&Multi_pxo_com_text[gr_screen.res][idx]);
4115         }
4116
4117         // create the input box
4118         Multi_pxo_com_input.create(&Multi_pxo_com_window, Multi_pxo_com_input_coords[gr_screen.res][0], Multi_pxo_com_input_coords[gr_screen.res][1], Multi_pxo_com_input_coords[gr_screen.res][2], input_len, "", UI_INPUTBOX_FLAG_INVIS | UI_INPUTBOX_FLAG_ESC_CLR | UI_INPUTBOX_FLAG_KEYTHRU | UI_INPUTBOX_FLAG_EAT_USED);   
4119         Multi_pxo_com_input.set_focus();
4120
4121         // clear all text lines
4122         SDL_zero(Multi_pxo_com_bottom_text);
4123         SDL_zero(Multi_pxo_com_middle_text);
4124         SDL_zero(Multi_pxo_com_top_text);
4125 }
4126
4127 // close down the common dialog
4128 void multi_pxo_com_close()
4129 {
4130         // destroy the UI_WINDOW
4131         Multi_pxo_com_window.destroy();
4132 }
4133
4134 // blit all text lines, top, middle, bottoms
4135 void multi_pxo_com_blit_text()
4136 {
4137         // blit top, middle and bottom text if possible
4138         if(strlen(Multi_pxo_com_top_text) > 0){
4139                 gr_set_color_fast(&Color_bright);
4140                 gr_string(Multi_pxo_com_top_text_coords[gr_screen.res][0], Multi_pxo_com_top_text_coords[gr_screen.res][1], Multi_pxo_com_top_text);
4141         }
4142         if(strlen(Multi_pxo_com_middle_text) > 0){
4143                 gr_set_color_fast(&Color_bright);
4144                 gr_string(Multi_pxo_com_top_text_coords[gr_screen.res][0], Multi_pxo_com_middle_text_y[gr_screen.res], Multi_pxo_com_middle_text);
4145         }
4146         if(strlen(Multi_pxo_com_bottom_text) > 0){
4147                 gr_set_color_fast(&Color_bright);
4148                 gr_string(Multi_pxo_com_top_text_coords[gr_screen.res][0], Multi_pxo_com_bottom_text_y[gr_screen.res], Multi_pxo_com_bottom_text);
4149         }
4150 }
4151
4152 // set the top text, shortening as necessary
4153 void multi_pxo_com_set_top_text(const char *txt)
4154 {       
4155         if((txt != NULL) && strlen(txt)){
4156                 SDL_strlcpy(Multi_pxo_com_top_text, txt, SDL_arraysize(Multi_pxo_com_top_text));
4157                 gr_force_fit_string(Multi_pxo_com_top_text, 254, Multi_pxo_com_input_coords[gr_screen.res][2]);
4158         }       
4159 }
4160
4161 // set the middle text, shortening as necessary
4162 void multi_pxo_com_set_middle_text(const char *txt)
4163 {
4164         if((txt != NULL) && strlen(txt)){
4165                 SDL_strlcpy(Multi_pxo_com_middle_text, txt, SDL_arraysize(Multi_pxo_com_middle_text));
4166                 gr_force_fit_string(Multi_pxo_com_middle_text, 254, Multi_pxo_com_input_coords[gr_screen.res][2]);
4167         }       
4168 }
4169
4170 // set the bottom text, shortening as necessary
4171 void multi_pxo_com_set_bottom_text(const char *txt)
4172 {
4173         if((txt != NULL) && strlen(txt)){
4174                 SDL_strlcpy(Multi_pxo_com_bottom_text, txt, SDL_arraysize(Multi_pxo_com_bottom_text));
4175                 gr_force_fit_string(Multi_pxo_com_bottom_text, 254, Multi_pxo_com_input_coords[gr_screen.res][2]);
4176         }       
4177 }
4178
4179
4180 // private channel join stuff -----------------------------------------
4181
4182 // initialize the popup
4183 void multi_pxo_priv_init()
4184 {
4185         SDL_assert(Multi_pxo_mode != MULTI_PXO_MODE_PRIVATE);
4186
4187         // initialize the common dialog with the passed max input length
4188         multi_pxo_com_init(MULTI_PXO_PRIV_MAX_TEXT_LEN);
4189         
4190         // initialize the return code
4191         Multi_pxo_priv_return_code = -1;        
4192
4193         // mark us as running
4194         Multi_pxo_mode = MULTI_PXO_MODE_PRIVATE;
4195
4196         // set some text
4197         multi_pxo_com_set_middle_text(XSTR("Type the name of the channel to join/create",961)); 
4198 }
4199
4200 // close down the popup
4201 void multi_pxo_priv_close()
4202 {       
4203         // close down the common dialog
4204         multi_pxo_com_close();
4205
4206         // mark us as not running any more
4207         Multi_pxo_mode = MULTI_PXO_MODE_NORMAL;
4208 }
4209
4210 // run the popup, 0 if still running, -1 if cancel, 1 if ok 
4211 int multi_pxo_priv_popup()
4212 {
4213         int k;
4214         
4215         // if we're not already running, initialize stuff
4216         if(Multi_pxo_mode != MULTI_PXO_MODE_PRIVATE){
4217                 // intialize
4218                 multi_pxo_priv_init();
4219
4220                 // return "still running"
4221                 return 0;
4222         }
4223
4224         k = Multi_pxo_com_window.process();
4225
4226         // process keypresses
4227         switch(k){
4228         // like hitting the cancel button
4229         case SDLK_ESCAPE:
4230                 Multi_pxo_priv_return_code = 0;
4231                 break;
4232         }
4233
4234         // process button presses
4235         multi_pxo_priv_process_buttons();
4236
4237         // process the inputbox
4238         multi_pxo_priv_process_input();
4239
4240         // blit the background
4241         multi_pxo_blit_all();   
4242
4243         // blit my stuff                
4244         gr_reset_clip();        
4245         gr_set_bitmap(Multi_pxo_com_bitmap);
4246         gr_bitmap(Multi_pxo_com_coords[gr_screen.res][0], Multi_pxo_com_coords[gr_screen.res][1]);
4247         Multi_pxo_com_window.draw();    
4248
4249         // blit all text lines, top, middle, bottoms
4250         multi_pxo_com_blit_text();
4251
4252         gr_flip();
4253
4254         // check the return code
4255         switch(Multi_pxo_priv_return_code){
4256         // still in progress
4257         case -1 :
4258                 return 0;
4259
4260         // user hit cancel
4261         case 0 :
4262                 multi_pxo_priv_close();
4263                 return -1;
4264
4265         // user hit ok
4266         case 1 :                
4267                 multi_pxo_priv_close();
4268                 return 1;
4269         }       
4270
4271         return 0;
4272 }
4273
4274 // process button presses
4275 void multi_pxo_priv_process_buttons()
4276 {
4277         int idx;
4278
4279         // check all buttons
4280         for(idx=0;idx<MULTI_PXO_COM_NUM_BUTTONS;idx++){
4281                 if(Multi_pxo_com_buttons[gr_screen.res][idx].button.pressed()){
4282                         multi_pxo_priv_button_pressed(idx);
4283                         return;
4284                 }
4285         }
4286 }
4287
4288 // handle a button press
4289 void multi_pxo_priv_button_pressed(int n)
4290 {
4291         char priv_chan_name[128];
4292
4293         switch(n){      
4294         case MULTI_PXO_COM_CANCEL:
4295                 Multi_pxo_priv_return_code = 0;
4296                 break;
4297         
4298         case MULTI_PXO_COM_OK:
4299                 Multi_pxo_com_input.get_text(priv_chan_name);
4300                 multi_pxo_strip_space(priv_chan_name, priv_chan_name, SDL_arraysize(priv_chan_name));
4301
4302                 // if its a 0 length string, interpret as a cancel
4303                 if(strlen(priv_chan_name) <= 0){
4304                         Multi_pxo_priv_return_code = 0;
4305                         return;
4306                 }
4307
4308                 Multi_pxo_priv_return_code = 1;
4309                 break;
4310         }       
4311 }
4312
4313 // process the inputbox
4314 void multi_pxo_priv_process_input()
4315 {
4316         char priv_chan_name[128];
4317         
4318         // see if the user has pressed enter
4319         if(Multi_pxo_com_input.pressed()){
4320                 Multi_pxo_com_input.get_text(priv_chan_name);
4321                 multi_pxo_strip_space(priv_chan_name, priv_chan_name, SDL_arraysize(priv_chan_name));
4322                 
4323                 // if its a 0 length string, interpret as a cancel
4324                 if(strlen(priv_chan_name) <= 0){
4325                         Multi_pxo_priv_return_code = 0;
4326                         return;
4327                 }
4328
4329                 // otherwise interpret as "accept"
4330                 Multi_pxo_priv_return_code = 1;
4331
4332                 // add in the "+" which indicates a private room
4333                 SDL_strlcpy(Multi_pxo_priv_chan, "+", SDL_arraysize(Multi_pxo_priv_chan));
4334                 SDL_strlcat(Multi_pxo_priv_chan, priv_chan_name, SDL_arraysize(Multi_pxo_priv_chan));
4335         }
4336 }
4337
4338 // find player stuff -----------------------------------------
4339
4340 char name_lookup[255];
4341
4342 // initialize the popup
4343 void multi_pxo_find_init()
4344 {
4345         SDL_assert(Multi_pxo_mode != MULTI_PXO_MODE_FIND);
4346
4347         // initialize the common dialog with the passed max input length
4348         multi_pxo_com_init(MAX_PLAYER_NAME_LEN);        
4349
4350         // return code, set to something other than -1 if we're supposed to return
4351         Multi_pxo_find_return_code = -1;
4352
4353         // mark us as running
4354         Multi_pxo_mode = MULTI_PXO_MODE_FIND;   
4355
4356         // not searching yet
4357         Multi_pxo_searching = 0; 
4358
4359         // set the top text
4360         multi_pxo_com_set_top_text(XSTR("Enter user to be found",962)); 
4361
4362         // 0 length
4363         SDL_strlcpy(Multi_pxo_find_channel, "", SDL_arraysize(Multi_pxo_find_channel));
4364
4365         // 0 length
4366         SDL_strlcpy(name_lookup, "", SDL_arraysize(name_lookup));
4367 }
4368
4369 // close down the popup
4370 void multi_pxo_find_close()
4371 {
4372         // close down the common dialog
4373         multi_pxo_com_close();
4374
4375         // mark us as not running any more
4376         Multi_pxo_mode = MULTI_PXO_MODE_NORMAL;
4377 }
4378
4379 // run the popup, 0 if still running, -1 if cancel, 1 if ok 
4380 int multi_pxo_find_popup()
4381 {
4382         int k;
4383         
4384         // if we're not already running, initialize stuff
4385         if(Multi_pxo_mode != MULTI_PXO_MODE_FIND){
4386                 // intialize
4387                 multi_pxo_find_init();
4388
4389                 // return "still running"
4390                 return 0;
4391         }
4392
4393         k = Multi_pxo_com_window.process();
4394
4395         // process keypresses
4396         switch(k){
4397         // like hitting the cancel button
4398         case SDLK_ESCAPE:
4399                 Multi_pxo_find_return_code = 0;
4400                 break;
4401         }
4402
4403         // process button presses
4404         multi_pxo_find_process_buttons();
4405
4406         // process the inputbox
4407         multi_pxo_find_process_input();
4408
4409         // process search mode if applicable
4410         multi_pxo_find_search_process();
4411
4412         // blit the background
4413         multi_pxo_blit_all();   
4414
4415         // blit my stuff                
4416         gr_reset_clip();        
4417         gr_set_bitmap(Multi_pxo_com_bitmap);
4418         gr_bitmap(Multi_pxo_com_coords[gr_screen.res][0], Multi_pxo_com_coords[gr_screen.res][1]);
4419         Multi_pxo_com_window.draw();    
4420
4421         // blit any text lines
4422         multi_pxo_com_blit_text();
4423         
4424         gr_flip();
4425
4426         // check the return code
4427         switch(Multi_pxo_find_return_code){
4428         // still in progress
4429         case -1 :
4430                 return 0;
4431
4432         // user hit cancel
4433         case 0 :
4434                 // close the popup down
4435                 multi_pxo_find_close();
4436                 return -1;
4437
4438         // user hit ok
4439         case 1 :                
4440                 // close the popup down
4441                 multi_pxo_find_close();
4442
4443                 // if we have a channel, join it now if possible
4444                 if(strlen(Multi_pxo_find_channel) > 0){
4445                         pxo_channel *lookup;
4446                         lookup = multi_pxo_find_channel(Multi_pxo_find_channel,Multi_pxo_channels);
4447                         
4448                         // if we couldn't find it, don't join
4449                         if(lookup != NULL){                             
4450                                 multi_pxo_join_channel(lookup);
4451                         }
4452                 }
4453                 return 1;
4454         }       
4455
4456         return 0;
4457 }
4458
4459 // process button presses
4460 void multi_pxo_find_process_buttons()
4461 {
4462         int idx;
4463
4464         // check all buttons
4465         for(idx=0;idx<MULTI_PXO_COM_NUM_BUTTONS;idx++){
4466                 if(Multi_pxo_com_buttons[gr_screen.res][idx].button.pressed()){
4467                         multi_pxo_find_button_pressed(idx);
4468                         return;
4469                 }
4470         }
4471 }
4472
4473 // handle a button press
4474 void multi_pxo_find_button_pressed(int n)
4475 {
4476         switch(n){      
4477         case MULTI_PXO_COM_CANCEL:
4478                 Multi_pxo_find_return_code = 0;
4479                 break;
4480         
4481         case MULTI_PXO_COM_OK:
4482                 Multi_pxo_find_return_code = 1;
4483                 break;
4484         }       
4485 }
4486
4487 // process the inputbox
4488 void multi_pxo_find_process_input()
4489 {               
4490         // see if the user has pressed enter
4491         if(Multi_pxo_com_input.pressed()){
4492                 // if we're not already in search mode
4493                 if(!Multi_pxo_searching){
4494                         // clear all text
4495                         SDL_zero(Multi_pxo_com_middle_text);
4496                         SDL_zero(Multi_pxo_com_bottom_text);
4497
4498                         Multi_pxo_com_input.get_text(name_lookup);
4499                         multi_pxo_strip_space(name_lookup, name_lookup, SDL_arraysize(name_lookup));
4500
4501                         // never search with a zero length string
4502                         if(strlen(name_lookup) > 0){
4503                                 char search_text[512];
4504
4505                                 // put us in search mode
4506                                 Multi_pxo_searching = 1;
4507
4508                                 // look for the guy
4509                                 GetChannelByUser(name_lookup);                  
4510
4511                                 // set the top text
4512                                 SDL_snprintf(search_text, SDL_arraysize(search_text), XSTR("Searching for %s", 963), name_lookup);
4513                                 multi_pxo_com_set_top_text(search_text);
4514                         }
4515                         // clear everything
4516                         else {
4517                                 SDL_zero(Multi_pxo_com_top_text);
4518                         }
4519                 }
4520         }
4521 }
4522
4523 // process search mode if applicable
4524 void multi_pxo_find_search_process()
4525 {
4526         char *channel;
4527         
4528         // if we're not searching for anything, return
4529         if(!Multi_pxo_searching){
4530                 return;
4531         }
4532
4533         // otherwise check to see if we've found him
4534         channel = GetChannelByUser(NULL);
4535         
4536         // if we've got a result, let the user know
4537         if(channel){
4538                 // if he couldn't be found
4539                 if(channel == (char *)-1){
4540                         multi_pxo_com_set_middle_text(XSTR("User not found",964));                                                                      
4541                         SDL_strlcpy(Multi_pxo_find_channel, "", SDL_arraysize(Multi_pxo_find_channel));
4542                 } else {        
4543                         if(channel[0] == '*'){
4544                                 multi_pxo_com_set_middle_text(XSTR("Player is logged in but is not on a channel",965));                         
4545                                 SDL_strlcpy(Multi_pxo_find_channel, "", SDL_arraysize(Multi_pxo_find_channel));
4546                         } else {
4547                                 char p_text[512];
4548
4549                                 // if this guy is on a public channel, display which one
4550                                 if(channel[0] == '#'){                  
4551                                         SDL_snprintf(p_text, SDL_arraysize(p_text), XSTR("Found %s on :", 966), name_lookup);
4552
4553                                         // display the results                                                          
4554                                         multi_pxo_com_set_middle_text(p_text);                                                          
4555                                         multi_pxo_com_set_bottom_text(channel+1);
4556
4557                                         // mark down the channel name so we know where to find him
4558                                         SDL_strlcpy(Multi_pxo_find_channel, channel, SDL_arraysize(Multi_pxo_find_channel));
4559                                         // strip out trailing whitespace
4560                                         if(Multi_pxo_find_channel[strlen(Multi_pxo_find_channel) - 1] == ' '){
4561                                                 Multi_pxo_find_channel[strlen(Multi_pxo_find_channel) - 1] = '\0';
4562                                         }                               
4563                                 }
4564                                 // if this is a private channel
4565                                 else if(channel[0] == '+'){
4566                                         SDL_snprintf(p_text, SDL_arraysize(p_text), XSTR("Found %s on a private channel", 967), name_lookup);
4567                                         multi_pxo_com_set_middle_text(p_text);
4568
4569                                         SDL_strlcpy(Multi_pxo_find_channel, "", SDL_arraysize(Multi_pxo_find_channel));
4570                                 }                                                               
4571                         }
4572                 }
4573
4574                 // unset search mode
4575                 Multi_pxo_searching = 0;
4576
4577                 // clear the inputbox
4578                 Multi_pxo_com_input.set_text("");
4579         }
4580 }
4581
4582
4583 // player info stuff -----------------------------------------
4584
4585 // popup conditional functions, returns 10 on successful get of stats
4586 int multi_pxo_pinfo_cond()
4587 {
4588         char *ret_string;
4589         char temp_string[255];
4590         char *tok;
4591
4592         // process common stuff
4593         multi_pxo_process_common();
4594
4595         // run the networking functions for the PXO API
4596         multi_pxo_api_process();
4597
4598         // process depending on what mode we're in
4599         switch(Multi_pxo_retrieve_mode){        
4600         // getting his player tracker id
4601         case 0:         
4602                 // if the thing is non-null, do something               
4603                 ret_string = GetTrackerIdByUser(Multi_pxo_retrieve_name);
4604                 if(ret_string != NULL){
4605                         // user not-online/not found
4606                         if(ret_string == (char *)-1){
4607                                 return 1;
4608                         } 
4609
4610                         // user not a tracker pilot
4611                         if(!SDL_strcasecmp(ret_string,"-1")){
4612                                 return 1;
4613                         }
4614
4615                         // otherwise parse into his id and callsign
4616                         SDL_strlcpy(temp_string, ret_string, SDL_arraysize(temp_string));
4617                         tok = strtok(temp_string," ");
4618                         
4619                         // get tracker id
4620                         if(tok != NULL){
4621                                 SDL_strlcpy(Multi_pxo_retrieve_id, tok, SDL_arraysize(Multi_pxo_retrieve_id));
4622
4623                                 // get the callsign
4624                                 tok = strtok(NULL,"");
4625                                 if(tok != NULL){
4626                                         SDL_strlcpy(Multi_pxo_retrieve_name, tok, SDL_arraysize(Multi_pxo_retrieve_name));
4627                                 }
4628                                 // failure
4629                                 else {
4630                                         return 1;
4631                                 }
4632                         }
4633                         // failure of some kind or another
4634                         else {
4635                                 return 1;
4636                         }                       
4637
4638                         Multi_pxo_retrieve_mode = 1;
4639                         return 0;                       
4640                 }
4641                 break;
4642
4643         // initial call to get his stats
4644         case 1:         
4645                 // change the popup text
4646                 popup_change_text(XSTR("Getting player stats",968));
4647
4648                 // fill in the data
4649                 memset(&Multi_pxo_pinfo, 0, sizeof(vmt_freespace2_struct));
4650                 SDL_strlcpy(Multi_pxo_pinfo.pilot_name, Multi_pxo_retrieve_name, SDL_arraysize(Multi_pxo_pinfo.pilot_name));
4651                 SDL_strlcpy(Multi_pxo_pinfo.tracker_id, Multi_pxo_retrieve_id, SDL_arraysize(Multi_pxo_pinfo.tracker_id));
4652
4653                 // make the initial call to the API
4654                 GetFSPilotData((vmt_freespace2_struct*)0xffffffff,NULL,NULL,0);
4655                 if(GetFSPilotData(&Multi_pxo_pinfo,Multi_pxo_retrieve_name,Multi_pxo_retrieve_id,0) != 0){
4656                         return 2;
4657                 }
4658                 // if the call went through, set the mode to 2
4659                 else {
4660                         Multi_pxo_retrieve_mode = 2;
4661                 }
4662                 break;
4663         
4664         // busy retrieving his stats
4665         case 2:
4666                 switch(GetFSPilotData(NULL,NULL,NULL,0)){
4667                 // timeout, fail, cancel
4668                 case -1:
4669                 case 3:
4670                 case 2:
4671                         return 2;                       
4672
4673                 // got the data
4674                 case 1:
4675                         return 10;
4676
4677                 // still busy
4678                 case 0:
4679                         break;
4680                 }
4681
4682                 break;          
4683         }
4684
4685         // return not done yet
4686         return 0;
4687 }
4688
4689 // return 1 if Multi_pxo_pinfo was successfully filled in, 0 otherwise
4690 int multi_pxo_pinfo_get(char *name)
4691 {
4692         // run the popup        
4693         Multi_pxo_retrieve_mode = 0;
4694         SDL_strlcpy(Multi_pxo_retrieve_name, name, SDL_arraysize(Multi_pxo_retrieve_name));
4695         switch(popup_till_condition(multi_pxo_pinfo_cond,XSTR("&Cancel", 779),XSTR("Retrieving player tracker id",969))){
4696         // success
4697         case 10 :
4698                 return 1;               
4699
4700         // failed to get his tracker id
4701         case 1 :
4702                 return 0;
4703
4704         // failed to get his stats
4705         case 2 :
4706                 return 0;       
4707         }
4708                         
4709         // we didn't get the stats
4710         return 0;
4711 }
4712
4713 // fire up the stats view popup
4714 void multi_pxo_pinfo_show()
4715 {
4716         // initialize the popup
4717         multi_pxo_pinfo_init();
4718         
4719         // run the popup
4720         do {
4721                 game_set_frametime(GS_STATE_PXO);
4722         } while(!multi_pxo_pinfo_do());
4723
4724         // close down the popup
4725         multi_pxo_pinfo_close();
4726 }
4727
4728 // build the stats labels values
4729 void multi_pxo_pinfo_build_vals()
4730 {
4731         vmt_freespace2_struct *fs = &Multi_pxo_pinfo;   
4732
4733         SDL_zero(Multi_pxo_pinfo_vals);
4734
4735         // pilot name
4736         SDL_strlcpy(Multi_pxo_pinfo_vals[0], fs->pilot_name, SDL_arraysize(Multi_pxo_pinfo_vals[0]));
4737         gr_force_fit_string(Multi_pxo_pinfo_vals[0], 49, Multi_pxo_pinfo_coords[gr_screen.res][2] - (Multi_pxo_pinfo_val_x[gr_screen.res] - Multi_pxo_pinfo_coords[gr_screen.res][0]));
4738
4739         // rank
4740         multi_sg_rank_build_name(Ranks[fs->rank].name, Multi_pxo_pinfo_vals[1], SDL_arraysize(Multi_pxo_pinfo_vals[1]));
4741         gr_force_fit_string(Multi_pxo_pinfo_vals[1], 49, Multi_pxo_pinfo_coords[gr_screen.res][2] - (Multi_pxo_pinfo_val_x[gr_screen.res] - Multi_pxo_pinfo_coords[gr_screen.res][0]));
4742
4743         // kills
4744         SDL_snprintf(Multi_pxo_pinfo_vals[2], SDL_arraysize(Multi_pxo_pinfo_vals[2]), "%d", fs->kill_count);
4745
4746         // assists
4747         SDL_snprintf(Multi_pxo_pinfo_vals[3], SDL_arraysize(Multi_pxo_pinfo_vals[3]), "%d", fs->assists);
4748
4749         // friendly kills
4750         SDL_snprintf(Multi_pxo_pinfo_vals[4], SDL_arraysize(Multi_pxo_pinfo_vals[4]), "%d", fs->kill_count - fs->kill_count_ok);
4751
4752         // missions flown
4753         SDL_snprintf(Multi_pxo_pinfo_vals[5], SDL_arraysize(Multi_pxo_pinfo_vals[5]), "%d", (int)fs->missions_flown);
4754
4755         // flight time  
4756         game_format_time(fl2f((float)fs->flight_time), Multi_pxo_pinfo_vals[6], SDL_arraysize(Multi_pxo_pinfo_vals[6]));
4757
4758         // last flown
4759         if(fs->last_flown == 0){                
4760                 SDL_strlcpy(Multi_pxo_pinfo_vals[7], XSTR("No missions flown", 970), SDL_arraysize(Multi_pxo_pinfo_vals[7]));
4761         } else {
4762                 tm *tmr = gmtime((time_t*)&fs->last_flown);
4763                 if(tmr != NULL){
4764                         strftime(Multi_pxo_pinfo_vals[7],30,"%m/%d/%y %H:%M",tmr);      
4765                 } else {
4766                         SDL_strlcpy(Multi_pxo_pinfo_vals[7], "", SDL_arraysize(Multi_pxo_pinfo_vals[7]));
4767                 }
4768         }               
4769
4770         // primary shots fired
4771         SDL_snprintf(Multi_pxo_pinfo_vals[8], SDL_arraysize(Multi_pxo_pinfo_vals[8]), "%d", (int)fs->p_shots_fired);
4772
4773         // primary shots hit
4774         SDL_snprintf(Multi_pxo_pinfo_vals[9], SDL_arraysize(Multi_pxo_pinfo_vals[9]), "%d", (int)fs->p_shots_hit);
4775
4776         // primary hit pct
4777         if(fs->p_shots_fired > 0){              
4778                 SDL_snprintf(Multi_pxo_pinfo_vals[10], SDL_arraysize(Multi_pxo_pinfo_vals[10]), "%d%%", (int)((float)fs->p_shots_hit / (float)fs->p_shots_fired * 100.0f));
4779         } else {                
4780                 SDL_strlcpy(Multi_pxo_pinfo_vals[10], "0%", SDL_arraysize(Multi_pxo_pinfo_vals[10]));
4781         }
4782
4783         // secondary shots fired
4784         SDL_snprintf(Multi_pxo_pinfo_vals[11], SDL_arraysize(Multi_pxo_pinfo_vals[11]), "%d", (int)fs->s_shots_fired);
4785
4786         // secondary shots hit
4787         SDL_snprintf(Multi_pxo_pinfo_vals[12], SDL_arraysize(Multi_pxo_pinfo_vals[12]), "%d", (int)fs->s_shots_hit);
4788
4789         // secondary hit pct
4790         if(fs->s_shots_fired > 0){              
4791                 SDL_snprintf(Multi_pxo_pinfo_vals[13], SDL_arraysize(Multi_pxo_pinfo_vals[13]), "%d%%", (int)((float)fs->s_shots_hit / (float)fs->s_shots_fired * 100.0f));
4792         } else {                
4793                 SDL_strlcpy(Multi_pxo_pinfo_vals[13], "0%", SDL_arraysize(Multi_pxo_pinfo_vals[13]));
4794         }
4795
4796         // primary friendly hits
4797         SDL_snprintf(Multi_pxo_pinfo_vals[14], SDL_arraysize(Multi_pxo_pinfo_vals[14]), "%d", fs->p_bonehead_hits);
4798
4799         // primary friendly hit %
4800         if(fs->p_shots_hit > 0){                
4801                 SDL_snprintf(Multi_pxo_pinfo_vals[15], SDL_arraysize(Multi_pxo_pinfo_vals[15]), "%d%%", (int)((float)100.0f*((float)fs->p_bonehead_hits/(float)fs->p_shots_fired)));
4802         } else {                
4803                 SDL_strlcpy(Multi_pxo_pinfo_vals[15], "0%", SDL_arraysize(Multi_pxo_pinfo_vals[15]));
4804         }
4805
4806         // secondary friendly hits
4807         SDL_snprintf(Multi_pxo_pinfo_vals[16], SDL_arraysize(Multi_pxo_pinfo_vals[16]), "%d", fs->s_bonehead_hits);
4808
4809         // secondary friendly hit %
4810         if(fs->s_shots_hit > 0){
4811                 SDL_snprintf(Multi_pxo_pinfo_vals[17], SDL_arraysize(Multi_pxo_pinfo_vals[17]), "%d%%", (int)((float)100.0f*((float)fs->s_bonehead_hits/(float)fs->s_shots_fired)));
4812         } else {                
4813                 SDL_strlcpy(Multi_pxo_pinfo_vals[17], "0%", SDL_arraysize(Multi_pxo_pinfo_vals[17]));
4814         }
4815 }
4816
4817 // initialize the popup
4818 void multi_pxo_pinfo_init()
4819 {
4820         int idx;
4821         
4822         // create the interface window
4823         Multi_pxo_pinfo_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
4824         Multi_pxo_pinfo_window.set_mask_bmap(Multi_pxo_pinfo_mask_fname[gr_screen.res]);        
4825         
4826         Multi_pxo_pinfo_bitmap = bm_load(Multi_pxo_pinfo_fname[gr_screen.res]);
4827         SDL_assert(Multi_pxo_pinfo_bitmap != -1);
4828
4829         // create the interface buttons
4830         for(idx=0; idx<MULTI_PXO_PINFO_NUM_BUTTONS; idx++){
4831                 // create the object
4832                 Multi_pxo_pinfo_buttons[gr_screen.res][idx].button.create(&Multi_pxo_pinfo_window, "", Multi_pxo_pinfo_buttons[gr_screen.res][idx].x, Multi_pxo_pinfo_buttons[gr_screen.res][idx].y, 1, 1, 0, 1);
4833
4834                 // set the sound to play when highlighted
4835                 Multi_pxo_pinfo_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
4836
4837                 // set the ani for the button
4838                 Multi_pxo_pinfo_buttons[gr_screen.res][idx].button.set_bmaps(Multi_pxo_pinfo_buttons[gr_screen.res][idx].filename);
4839
4840                 // set the hotspot
4841                 Multi_pxo_pinfo_buttons[gr_screen.res][idx].button.link_hotspot(Multi_pxo_pinfo_buttons[gr_screen.res][idx].hotspot);
4842         }                               
4843
4844         // add xstrs
4845         for(idx=0; idx<MULTI_PXO_PINFO_NUM_TEXT; idx++){
4846                 Multi_pxo_pinfo_window.add_XSTR(&Multi_pxo_pinfo_text[gr_screen.res][idx]);
4847         }
4848
4849         // set up the stats labels
4850         Multi_pxo_pinfo_stats_labels[0] = strdup(XSTR("Name", 1532));
4851         Multi_pxo_pinfo_stats_labels[1] = strdup(XSTR("Rank", 1533));
4852         Multi_pxo_pinfo_stats_labels[2] = strdup(XSTR("Kills", 1534));
4853         Multi_pxo_pinfo_stats_labels[3] = strdup(XSTR("Assists", 1535));
4854         Multi_pxo_pinfo_stats_labels[4] = strdup(XSTR("Friendly kills", 1536));
4855         Multi_pxo_pinfo_stats_labels[5] = strdup(XSTR("Missions flown", 1537));
4856         Multi_pxo_pinfo_stats_labels[6] = strdup(XSTR("Flight time", 1538));
4857         Multi_pxo_pinfo_stats_labels[7] = strdup(XSTR("Last flown", 1539));
4858         Multi_pxo_pinfo_stats_labels[8] = strdup(XSTR("Primary shots fired", 1540));
4859         Multi_pxo_pinfo_stats_labels[9] = strdup(XSTR("Primary shots hit", 1541));
4860         Multi_pxo_pinfo_stats_labels[10] = strdup(XSTR("Primary hit %", 1542));
4861         Multi_pxo_pinfo_stats_labels[11] = strdup(XSTR("Secondary shots fired", 1543));
4862         Multi_pxo_pinfo_stats_labels[12] = strdup(XSTR("Secondary shots hit", 1544));
4863         Multi_pxo_pinfo_stats_labels[13] = strdup(XSTR("Secondary hit %", 1545));
4864         Multi_pxo_pinfo_stats_labels[14] = strdup(XSTR("Primary friendly hits", 1546));
4865         Multi_pxo_pinfo_stats_labels[15] = strdup(XSTR("Primary friendly hit %", 1547));
4866         Multi_pxo_pinfo_stats_labels[16] = strdup(XSTR("Secondary friendly hits", 1548));
4867         Multi_pxo_pinfo_stats_labels[17] = strdup(XSTR("Secondary friendly hit %", 1549));
4868
4869         // build the stats labels values
4870         multi_pxo_pinfo_build_vals();
4871 }
4872
4873 // do frame
4874 int multi_pxo_pinfo_do()
4875 {
4876         int k = Multi_pxo_pinfo_window.process();
4877
4878         // process common stuff
4879         multi_pxo_process_common();
4880
4881         // run the networking functions for the PXO API
4882         multi_pxo_api_process();
4883
4884         // check to see if he pressed escp
4885         if(k == SDLK_ESCAPE){
4886                 return 1;
4887         }
4888
4889         // if he pressed the ok button
4890         if(Multi_pxo_pinfo_buttons[gr_screen.res][MULTI_PXO_PINFO_OK].button.pressed()){
4891                 return 1;
4892         }
4893
4894         // if he pressed the medals buttons, run the medals screen
4895         if(Multi_pxo_pinfo_buttons[gr_screen.res][MULTI_PXO_PINFO_MEDALS].button.pressed()){
4896 #ifdef FS2_DEMO
4897         game_feature_not_in_demo_popup();
4898 #else
4899                 multi_pxo_run_medals();
4900 #endif
4901         }
4902         
4903         // draw stuff
4904
4905         // blit everything on the "normal" screen
4906         multi_pxo_blit_all();
4907
4908         // blit our own stuff
4909         gr_reset_clip();        
4910         gr_set_bitmap(Multi_pxo_pinfo_bitmap);
4911         gr_bitmap(0, 0);
4912         Multi_pxo_pinfo_window.draw();  
4913
4914         // blit the stats themselves
4915         multi_pxo_pinfo_blit();
4916
4917         // flip the page
4918         gr_flip();
4919
4920         // not done yet
4921         return 0;
4922 }
4923
4924 // close
4925 void multi_pxo_pinfo_close()
4926 {
4927         int i;
4928
4929         // destroy the UI_WINDOW
4930         Multi_pxo_pinfo_window.destroy();
4931
4932         // unload the bitmap
4933         if(Multi_pxo_pinfo_bitmap != -1){
4934                 bm_unload(Multi_pxo_pinfo_bitmap);
4935         }
4936
4937         // free the stats labels strings
4938         for (i=0; i<MULTI_PXO_PINFO_NUM_LABELS; i++) {
4939                 free(Multi_pxo_pinfo_stats_labels[i]);
4940         }
4941 }
4942
4943 // blit all the stats on this screen
4944 void multi_pxo_pinfo_blit()
4945 {
4946         int idx;
4947         int y_start;
4948         
4949         // blit all the labels  
4950         y_start = Multi_pxo_pinfo_coords[gr_screen.res][1];
4951         for(idx=0; idx<MULTI_PXO_PINFO_NUM_LABELS; idx++){
4952                 // blit the label
4953                 gr_set_color_fast(&Color_bright);
4954                 gr_string(Multi_pxo_pinfo_coords[gr_screen.res][0], y_start, Multi_pxo_pinfo_stats_labels[idx]);
4955
4956                 // blit the label's value
4957                 gr_set_color_fast(&Color_normal);
4958                 gr_string(Multi_pxo_pinfo_val_x[gr_screen.res], y_start, Multi_pxo_pinfo_vals[idx]);
4959
4960                 // spacing
4961                 y_start += Multi_pxo_pinfo_stats_spacing[idx];
4962         }
4963 }
4964
4965 // run the medals screen
4966 void multi_pxo_run_medals()
4967 {       
4968         int ret_code;
4969         
4970         // process common stuff
4971         multi_pxo_process_common();
4972
4973         // run the networking functions for the PXO API
4974         multi_pxo_api_process();
4975
4976         // initialize the freespace data and the player struct  
4977         multi_stats_tracker_to_fs(&Multi_pxo_pinfo, &Multi_pxo_pinfo_player.stats);
4978         SDL_strlcpy(Multi_pxo_pinfo_player.callsign, Multi_pxo_pinfo.pilot_name, SDL_arraysize(Multi_pxo_pinfo_player.callsign));
4979         
4980         // initialize the medals screen
4981         medal_main_init(&Multi_pxo_pinfo_player, MM_POPUP);
4982
4983         // run the medals screen until it says that it should be closed
4984         do {
4985                 // set frametime and run common functions
4986                 game_set_frametime(-1);
4987                 game_do_state_common(gameseq_get_state());
4988
4989                 // run the medals screen
4990                 ret_code = medal_main_do();             
4991         } while(ret_code);
4992
4993         // close the medals screen down
4994         medal_main_close();
4995         
4996         // reset the palette
4997         multi_pxo_load_palette();
4998 }
4999
5000
5001 // notify stuff stuff -----------------------------------------
5002
5003 // add a notification string
5004 void multi_pxo_notify_add(const char *txt)
5005 {
5006         // copy the text
5007         SDL_strlcpy(Multi_pxo_notify_text, txt, SDL_arraysize(Multi_pxo_notify_text));
5008
5009         // set the timestamp
5010         Multi_pxo_notify_stamp = timestamp(MULTI_PXO_NOTIFY_TIME);
5011 }
5012
5013 // blit and process the notification string
5014 void multi_pxo_notify_blit()
5015 {
5016         int w;
5017
5018         // if the timestamp is -1, do nothing
5019         if(Multi_pxo_notify_stamp == -1){
5020                 return;
5021         }
5022
5023         // if it has expired, do nothing
5024         if(timestamp_elapsed(Multi_pxo_notify_stamp)){
5025                 Multi_pxo_notify_stamp = -1;
5026         }
5027
5028         // otherwise blit the text
5029         gr_set_color_fast(&Color_bright);
5030         gr_get_string_size(&w,NULL,Multi_pxo_notify_text);
5031         gr_string((gr_screen.max_w - w)/2,MULTI_PXO_NOTIFY_Y,Multi_pxo_notify_text);
5032 }
5033
5034
5035 // initialize the PXO help screen
5036 void multi_pxo_help_init()
5037 {
5038         int idx;
5039         
5040         // load the background bitmap
5041         Multi_pxo_help_bitmap = bm_load(Multi_pxo_help_fname[gr_screen.res]);
5042         if(Multi_pxo_help_bitmap < 0){
5043                 // we failed to load the bitmap - this is very bad
5044                 Int3();
5045         }
5046                 // create the interface window
5047         Multi_pxo_help_window.create(0,0,gr_screen.max_w,gr_screen.max_h,0);
5048         Multi_pxo_help_window.set_mask_bmap(Multi_pxo_help_mask_fname[gr_screen.res]);
5049
5050                 // create the interface buttons
5051         for(idx=0; idx<MULTI_PXO_HELP_NUM_BUTTONS; idx++){
5052                 // create the object
5053                 Multi_pxo_help_buttons[gr_screen.res][idx].button.create(&Multi_pxo_help_window, "", Multi_pxo_help_buttons[gr_screen.res][idx].x, Multi_pxo_help_buttons[gr_screen.res][idx].y, 1, 1, 0, 1);
5054
5055                 // set the sound to play when highlighted
5056                 Multi_pxo_help_buttons[gr_screen.res][idx].button.set_highlight_action(common_play_highlight_sound);
5057
5058                 // set the ani for the button
5059                 Multi_pxo_help_buttons[gr_screen.res][idx].button.set_bmaps(Multi_pxo_help_buttons[gr_screen.res][idx].filename);
5060
5061                 // set the hotspot
5062                 Multi_pxo_help_buttons[gr_screen.res][idx].button.link_hotspot(Multi_pxo_help_buttons[gr_screen.res][idx].hotspot);
5063         }       
5064         
5065         // add xstrs
5066         for(idx=0; idx<MULTI_PXO_HELP_NUM_TEXT; idx++){
5067                 Multi_pxo_help_window.add_XSTR(&Multi_pxo_help_text[gr_screen.res][idx]);
5068         }
5069
5070         // if we haven't already loaded in the text, do so
5071         // if(!Multi_pxo_help_loaded){
5072                 multi_pxo_help_load();
5073         // }
5074
5075         // set the current page to 0
5076         Multi_pxo_help_cur = 0;
5077 }
5078
5079 // do frame for PXO help
5080 void multi_pxo_help_do()
5081 {
5082         // run api stuff        
5083         if(Multi_pxo_connected){
5084                 multi_pxo_api_process();
5085         }
5086
5087         // process common stuff
5088         multi_pxo_process_common();
5089
5090         int k = Multi_pxo_help_window.process();
5091
5092         // process any keypresses
5093         switch(k){
5094         case SDLK_ESCAPE:
5095                 gamesnd_play_iface(SND_USER_SELECT);
5096                 gameseq_post_event(GS_EVENT_PXO);
5097                 break;
5098         }               
5099
5100         // process button presses
5101         multi_pxo_help_process_buttons();
5102
5103         // draw the background, etc
5104         gr_reset_clip();
5105         GR_MAYBE_CLEAR_RES(Multi_pxo_help_bitmap);
5106         if(Multi_pxo_help_bitmap != -1){
5107                 gr_set_bitmap(Multi_pxo_help_bitmap);
5108                 gr_bitmap(0,0);
5109         }
5110         Multi_pxo_help_window.draw();
5111
5112         // blit the current page
5113         multi_pxo_help_blit_page();
5114
5115         // page flip
5116         gr_flip();
5117 }
5118
5119 // close the pxo screen
5120 void multi_pxo_help_close()
5121 {
5122         int idx, idx2;
5123
5124         // unload any bitmaps
5125         bm_unload(Multi_pxo_help_bitmap);               
5126         
5127         // destroy the UI_WINDOW
5128         Multi_pxo_help_window.destroy();
5129
5130         // free all pages
5131         for(idx=0; idx<Multi_pxo_help_num_pages; idx++){
5132                 for(idx2=0; idx2<Multi_pxo_help_pages[idx].num_lines; idx2++){
5133                         // maybe free
5134                         if(Multi_pxo_help_pages[idx].text[idx2] != NULL){
5135                                 free(Multi_pxo_help_pages[idx].text[idx2]);
5136                                 Multi_pxo_help_pages[idx].text[idx2] = NULL;
5137                         }
5138                 }
5139         }
5140 }
5141
5142 // load the help file up
5143 void multi_pxo_help_load()
5144 {
5145         CFILE *in;      
5146         help_page *cp;  
5147         
5148         // if its already loaded, do nothing
5149         // if(Multi_pxo_help_loaded){
5150                 // return;
5151         // }
5152
5153         // read in the text file
5154         in = NULL;
5155         in = cfopen(MULTI_PXO_HELP_FILE,"rt",CFILE_NORMAL,CF_TYPE_DATA);                        
5156         SDL_assert(in != NULL);
5157         if(in == NULL){
5158                 return;
5159         }
5160
5161         Multi_pxo_help_num_pages = 0;
5162
5163         // blast all the help pages clear
5164         memset(Multi_pxo_help_pages, 0, sizeof(help_page) * MULTI_PXO_MAX_PAGES);       
5165         Multi_pxo_help_num_pages = 0;
5166         cp = &Multi_pxo_help_pages[0];
5167
5168         while(!cfeof(in)){
5169                 // malloc the line
5170                 cp->text[cp->num_lines] = (char*)malloc(Multi_pxo_chars_per_line[gr_screen.res]);
5171                 if(cp->text[cp->num_lines] == NULL){
5172                         break;
5173                 }
5174                 
5175                 // read in the next line                
5176                 cfgets(cp->text[cp->num_lines++], Multi_pxo_chars_per_line[gr_screen.res], in);
5177
5178                 // skip to the next page if necessary
5179                 if(cp->num_lines == Multi_pxo_lines_pp[gr_screen.res]){                 
5180                         Multi_pxo_help_num_pages++;
5181                         SDL_assert(Multi_pxo_help_num_pages < MULTI_PXO_MAX_PAGES);
5182                         if(Multi_pxo_help_num_pages >= MULTI_PXO_MAX_PAGES){
5183                                 Multi_pxo_help_num_pages--;
5184                                 break;
5185                         }
5186                         cp = &Multi_pxo_help_pages[Multi_pxo_help_num_pages];
5187                 }
5188         }
5189
5190         // close the file
5191         cfclose(in);
5192
5193         // mark the help as having been loaded
5194         // Multi_pxo_help_loaded = 1;
5195 }
5196
5197 // blit the current page
5198 void multi_pxo_help_blit_page()
5199 {
5200         int idx;
5201         int start_pos;
5202         int y_start;
5203         help_page *cp = &Multi_pxo_help_pages[Multi_pxo_help_cur];
5204         
5205         // blit each line
5206         y_start = Multi_pxo_help_coords[gr_screen.res][1];
5207         for(idx=0;idx<cp->num_lines;idx++){
5208                 // if the first symbol is "@", highlight the line
5209                 if(cp->text[idx][0] == '@'){
5210                         gr_set_color_fast(&Color_bright);
5211                         start_pos = 1;
5212                 } else {
5213                         gr_set_color_fast(&Color_normal);
5214                         start_pos = 0;
5215                 }
5216
5217                 // blit the line
5218                 gr_string(Multi_pxo_help_coords[gr_screen.res][0], y_start, cp->text[idx] + start_pos);
5219
5220                 // increment the y location
5221                 y_start += 10;
5222         }
5223 }
5224
5225 // process button presses
5226 void multi_pxo_help_process_buttons()
5227 {
5228         int idx;
5229
5230         // process all buttons
5231         for(idx=0;idx<MULTI_PXO_HELP_NUM_BUTTONS;idx++){
5232                 if(Multi_pxo_help_buttons[gr_screen.res][idx].button.pressed()){
5233                         multi_pxo_help_button_pressed(idx);
5234                         return;
5235                 }
5236         }
5237 }
5238
5239 // button pressed
5240 void multi_pxo_help_button_pressed(int n)
5241 {       
5242         switch(n){
5243         case MULTI_PXO_HELP_PREV:
5244                 // if we're already at page 0, do nothing
5245                 if(Multi_pxo_help_cur == 0){
5246                         gamesnd_play_iface(SND_GENERAL_FAIL);                   
5247                 } else {
5248                         Multi_pxo_help_cur--;
5249                         gamesnd_play_iface(SND_USER_SELECT);
5250                 }
5251                 break;
5252
5253         case MULTI_PXO_HELP_NEXT:
5254                 // if we're already at max pages, do nothing
5255                 if(Multi_pxo_help_cur == Multi_pxo_help_num_pages){
5256                         gamesnd_play_iface(SND_GENERAL_FAIL);
5257                 } else {
5258                         Multi_pxo_help_cur++;
5259                         gamesnd_play_iface(SND_USER_SELECT);
5260                 }
5261                 break;
5262
5263         case MULTI_PXO_HELP_CONTINUE:
5264                 gamesnd_play_iface(SND_USER_SELECT);
5265                 gameseq_post_event(GS_EVENT_PXO);
5266                 break;
5267         }
5268 }
5269
5270 // http banner stuff ---------------------------------------------
5271
5272 // init
5273 void multi_pxo_ban_init()
5274 {
5275         // zero the active banner bitmap
5276         Multi_pxo_banner.ban_bitmap = -1;       
5277
5278         // are we doing banners at all?
5279         if(os_config_read_uint(NULL, "PXOBanners", 1)){
5280                 // if we're already in idle mode, we're done downloading for this instance of freespace. pick a random image we already have
5281                 if(Multi_pxo_ban_mode == PXO_BAN_MODE_IDLE){
5282                         Multi_pxo_ban_mode = PXO_BAN_MODE_CHOOSE_RANDOM;                
5283                         return;
5284                 }
5285                 
5286                 // set ourselves to startup mode        
5287                 Multi_pxo_ban_mode = PXO_BAN_MODE_LIST_STARTUP;
5288                 Multi_pxo_ban_get = NULL;
5289         } else {
5290                 // set ourselves to idle mode
5291                 Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE;
5292                 Multi_pxo_ban_get = NULL;
5293         }
5294
5295         // zero the active banner bitmap
5296         SDL_zero(Multi_pxo_banner);
5297         Multi_pxo_banner.ban_bitmap = -1;
5298 }
5299
5300 // process http download details
5301 void multi_pxo_ban_process()
5302 {
5303         char url_string[512] = "";
5304         char local_file[MAX_PATH_LEN] = "";
5305
5306         // process stuff
5307         switch(Multi_pxo_ban_mode){
5308         // start downloading list
5309         case PXO_BAN_MODE_LIST_STARTUP:         
5310                 // remote file
5311                 SDL_snprintf(url_string, SDL_arraysize(url_string), "%s/%s", Multi_options_g.pxo_banner_url, PXO_BANNERS_CONFIG_FILE);
5312
5313                 // local file
5314                 cf_create_default_path_string(local_file, CF_TYPE_MULTI_CACHE, PXO_BANNERS_CONFIG_FILE);
5315                 
5316                 // try creating the file get object
5317                 Multi_pxo_ban_get = new InetGetFile(url_string, local_file);
5318
5319                 // bad
5320                 if(Multi_pxo_ban_get == NULL){                  
5321                         Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE;
5322                 }
5323
5324                 // go to the downloading list mode
5325                 Multi_pxo_ban_mode = PXO_BAN_MODE_LIST;
5326                 break;
5327
5328         // downloading list
5329         case PXO_BAN_MODE_LIST:
5330                 // error
5331                 if(Multi_pxo_ban_get->IsFileError()){                   
5332                         delete Multi_pxo_ban_get;
5333                         Multi_pxo_ban_get = NULL;
5334                         Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE;
5335                         break;
5336                 } 
5337
5338                 // connecting, receiving
5339                 if(Multi_pxo_ban_get->IsConnecting() || Multi_pxo_ban_get->IsReceiving()){
5340                         break;
5341                 }
5342
5343                 // done!
5344                 if(Multi_pxo_ban_get->IsFileReceived()){
5345                         delete Multi_pxo_ban_get;
5346                         Multi_pxo_ban_get = NULL;
5347                         Multi_pxo_ban_mode = PXO_BAN_MODE_IMAGES_STARTUP;
5348                 }
5349                 break;
5350
5351         // start downloading files
5352         case PXO_BAN_MODE_IMAGES_STARTUP:
5353                 // first thing - parse the banners file and pick a file
5354                 multi_pxo_ban_parse_banner_file(0);
5355
5356                 // if we have no active file, we're done
5357                 if((strlen(Multi_pxo_banner.ban_file) <= 0) || (strlen(Multi_pxo_banner.ban_file_url) <= 0)){
5358                         Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE;
5359                         break;
5360                 }
5361
5362                 // if the file already exists, we're done
5363                 if(cf_exist(Multi_pxo_banner.ban_file, CF_TYPE_MULTI_CACHE)){
5364                         Multi_pxo_ban_mode = PXO_BAN_MODE_IMAGES_DONE;
5365                         break;
5366                 }
5367
5368                 // otherwise try and download it                                
5369                 cf_create_default_path_string(local_file, CF_TYPE_MULTI_CACHE, Multi_pxo_banner.ban_file);
5370                 // try creating the file get object
5371                 Multi_pxo_ban_get = new InetGetFile(Multi_pxo_banner.ban_file_url, local_file);
5372
5373                 // bad
5374                 if(Multi_pxo_ban_get == NULL){                  
5375                         Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE;
5376                 }
5377
5378                 // go to the downloading images mode
5379                 Multi_pxo_ban_mode = PXO_BAN_MODE_IMAGES;
5380                 break;
5381
5382         // downloading files
5383         case PXO_BAN_MODE_IMAGES:
5384                 // error
5385                 if(Multi_pxo_ban_get->IsFileError()){                   
5386                         delete Multi_pxo_ban_get;
5387                         Multi_pxo_ban_get = NULL;
5388                         Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE;
5389                         break;
5390                 } 
5391
5392                 // connecting, receiving
5393                 if(Multi_pxo_ban_get->IsConnecting() || Multi_pxo_ban_get->IsReceiving()){
5394                         break;
5395                 }
5396
5397                 // done!
5398                 if(Multi_pxo_ban_get->IsFileReceived()){
5399                         delete Multi_pxo_ban_get;
5400                         Multi_pxo_ban_get = NULL;
5401                         Multi_pxo_ban_mode = PXO_BAN_MODE_IMAGES_DONE;
5402                 }
5403                 break;
5404
5405         // done downloading - maybe load an image
5406         case PXO_BAN_MODE_IMAGES_DONE:
5407                 // make sure we have a valid filename
5408                 // SDL_assert(strlen(Multi_pxo_banner.ban_file) > 0);
5409                 if(strlen(Multi_pxo_banner.ban_file) > 0){
5410                         Multi_pxo_banner.ban_bitmap = bm_load(Multi_pxo_banner.ban_file);
5411                 }
5412
5413                 // now we're idle
5414                 Multi_pxo_ban_mode = PXO_BAN_MODE_IDLE;
5415                 break;
5416
5417         // idle (done with EVERYTHING)
5418         case PXO_BAN_MODE_IDLE:
5419                 // if the banner button was clicked
5420                 if(Multi_pxo_ban_button.pressed()){
5421                         multi_pxo_ban_clicked();                        
5422                 }
5423                 break;
5424
5425         case PXO_BAN_MODE_CHOOSE_RANDOM:
5426                 // first thing - parse the banners file and pick a file
5427                 multi_pxo_ban_parse_banner_file(1);
5428
5429                 Multi_pxo_ban_mode = PXO_BAN_MODE_IMAGES_DONE;
5430                 break;
5431         }
5432 }
5433
5434 // close
5435 void multi_pxo_ban_close()
5436 {
5437         // if we have a currently active transfer
5438         if(Multi_pxo_ban_get != NULL){
5439                 Multi_pxo_ban_get->AbortGet();
5440                 delete Multi_pxo_ban_get;
5441                 Multi_pxo_ban_get = NULL;
5442         }
5443
5444         // if we have a loaded bitmap, unload it
5445         if(Multi_pxo_banner.ban_bitmap != -1){
5446                 bm_unload(Multi_pxo_banner.ban_bitmap);
5447                 Multi_pxo_banner.ban_bitmap = -1;
5448         }
5449 }
5450
5451 // parse the banners file and maybe fill in Multi_pxo_dl_file
5452 void multi_pxo_ban_parse_banner_file(int choose_existing)
5453 {
5454         char file_url[MAX_PATH_LEN] = "";
5455         char banners[10][MAX_PATH_LEN];
5456         char urls[10][MAX_PATH_LEN];
5457         int exists[10];
5458         int exist_count;
5459         int num_banners, idx;
5460         CFILE *in = cfopen(PXO_BANNERS_CONFIG_FILE, "rt", CFILE_NORMAL, CF_TYPE_MULTI_CACHE);
5461
5462         SDL_zero(Multi_pxo_banner);
5463         Multi_pxo_banner.ban_bitmap = -1;
5464
5465         // bad
5466         if(in == NULL){
5467                 return;
5468         }
5469
5470         // clear all strings
5471         SDL_zero(banners);
5472         SDL_zero(urls);
5473
5474         // get the global banner url
5475         if(cfgets(file_url, SDL_arraysize(file_url), in) == NULL){
5476                 cfclose(in);
5477                 cf_delete(PXO_BANNERS_CONFIG_FILE, CF_TYPE_MULTI_CACHE);
5478                 return;
5479         }
5480         drop_leading_white_space(file_url);
5481         drop_trailing_white_space(file_url);
5482
5483         // otherwise read in            
5484         num_banners = 0;
5485         while(num_banners < 10){
5486                 // try and get the pcx
5487                 if(cfgets(banners[num_banners], SDL_arraysize(banners[0]), in) == NULL){
5488                         break;
5489                 }
5490                 // try and get the url
5491                 if(cfgets(urls[num_banners], SDL_arraysize(urls[0]), in) == NULL){
5492                         break;
5493                 }
5494
5495                 // strip off trailing and leading whitespace
5496                 drop_leading_white_space(banners[num_banners]);
5497                 drop_trailing_white_space(banners[num_banners]);
5498                 drop_leading_white_space(urls[num_banners]);
5499                 drop_trailing_white_space(urls[num_banners]);
5500
5501                 // got one
5502                 num_banners++;          
5503         }
5504
5505         // close the file
5506         cfclose(in);
5507
5508         // no banners
5509         if(num_banners <= 0){           
5510                 return;
5511         }
5512
5513         // if we're only selecting files which already exist (previously downloaded)
5514         if(choose_existing){
5515                 // non exist
5516                 for(idx=0; idx<10; idx++){
5517                         exists[idx] = 0;
5518                 }
5519
5520                 // build a list of existing files
5521                 exist_count = 0;
5522                 for(idx=0; idx<num_banners; idx++){
5523                         if(cf_exist(banners[idx], CF_TYPE_MULTI_CACHE)){
5524                                 exists[idx] = 1;
5525                                 exist_count++;
5526                         }
5527                 }
5528
5529                 // bogus
5530                 if(exist_count <= 0){
5531                         return;
5532                 }
5533
5534                 // select one
5535                 int select = (int)frand_range(0.0f, (float)exist_count);
5536                 if(select >= exist_count){
5537                         select = exist_count - 1;
5538                 }
5539                 if(select < 0){
5540                         select = 0;
5541                 }
5542                 for(idx=0; idx<exist_count; idx++){
5543                         if(select == 0){
5544                                 break;
5545                         }
5546                         if(exists[idx]){
5547                                 select--;
5548                         }
5549                 }
5550
5551                 // valid?
5552                 if(idx < exist_count){
5553                         // base filename
5554                         SDL_strlcpy(Multi_pxo_banner.ban_file, banners[idx], SDL_arraysize(Multi_pxo_banner.ban_file));
5555
5556                         // get the full file url
5557                         SDL_strlcpy(Multi_pxo_banner.ban_file_url, file_url, SDL_arraysize(Multi_pxo_banner.ban_file_url));
5558                         SDL_strlcat(Multi_pxo_banner.ban_file_url, banners[idx], SDL_arraysize(Multi_pxo_banner.ban_file_url));
5559
5560                         // url of where to go to when clicked
5561                         SDL_strlcpy(Multi_pxo_banner.ban_url, urls[idx], SDL_arraysize(Multi_pxo_banner.ban_url));
5562                 }
5563         }
5564         // randomly pick a file for download
5565         else {                  
5566                 idx = (int)frand_range(0.0f, (float)num_banners);
5567                 
5568                 if(idx >= num_banners){
5569                         idx = num_banners - 1;
5570                 } 
5571                 if(idx < 0){
5572                         idx = 0;
5573                 }
5574
5575                 // base filename
5576                 SDL_strlcpy(Multi_pxo_banner.ban_file, banners[idx], SDL_arraysize(Multi_pxo_banner.ban_file));
5577
5578                 // get the full file url
5579                 SDL_strlcpy(Multi_pxo_banner.ban_file_url, file_url, SDL_arraysize(Multi_pxo_banner.ban_file_url));
5580                 SDL_strlcat(Multi_pxo_banner.ban_file_url, banners[idx], SDL_arraysize(Multi_pxo_banner.ban_file_url));
5581
5582                 // url of where to go to when clicked
5583                 SDL_strlcpy(Multi_pxo_banner.ban_url, urls[idx], SDL_arraysize(Multi_pxo_banner.ban_url));
5584         }
5585
5586         // delete the banner config file
5587         // cf_delete(PXO_BANNERS_CONFIG_FILE, CF_TYPE_MULTI_CACHE);
5588 }
5589
5590 // any bitmap or info or whatever
5591 void multi_pxo_ban_draw()
5592 {       
5593         // if we have a valid bitmap
5594         if(Multi_pxo_banner.ban_bitmap >= 0){
5595                 // if the mouse is over the banner button, highlight with a rectangle
5596                 if(Multi_pxo_ban_button.is_mouse_on()){
5597                         gr_set_color_fast(&Color_bright_blue);
5598                         gr_rect(Pxo_ban_coords[gr_screen.res][0] - 1, Pxo_ban_coords[gr_screen.res][1] - 1, Pxo_ban_coords[gr_screen.res][2] + 2, Pxo_ban_coords[gr_screen.res][3] + 2);
5599                 }
5600
5601                 // draw the bitmap itself
5602                 gr_set_bitmap(Multi_pxo_banner.ban_bitmap);
5603                 gr_bitmap(Pxo_ban_coords[gr_screen.res][0], Pxo_ban_coords[gr_screen.res][1]);
5604         }
5605 }
5606
5607 // called when the URL button is clicked
5608 void multi_pxo_ban_clicked()
5609 {
5610         // if we have a valid bitmap and URL, launch the URL
5611         if((Multi_pxo_banner.ban_bitmap >= 0) && (strlen(Multi_pxo_banner.ban_url) > 0)){
5612                 multi_pxo_url(Multi_pxo_banner.ban_url);
5613         }
5614 }
5615