]> icculus.org git repositories - taylor/freespace2.git/blob - src/playerman/managepilot.cpp
enable PXO by default
[taylor/freespace2.git] / src / playerman / managepilot.cpp
1 /*
2  * Copyright (C) Volition, Inc. 1999.  All rights reserved.
3  *
4  * All source code herein is the property of Volition, Inc. You may not sell 
5  * or otherwise commercially exploit the source or things you created based on
6  * the source.
7  */
8
9 /*
10  * $Logfile: /Freespace2/code/Playerman/ManagePilot.cpp $
11  * $Revision$
12  * $Date$
13  * $Author$
14  *
15  * ManagePilot.cpp has code to load and save pilot files, and to select and 
16  * manage the pilot
17  *
18  * $Log$
19  * Revision 1.6  2004/07/04 11:41:24  taylor
20  * warning fixes, cross platform pilot compat with OSX-Linux-Windows
21  *
22  * Revision 1.5  2003/05/25 02:30:43  taylor
23  * Freespace 1 support
24  *
25  * Revision 1.4  2002/06/09 04:41:25  relnev
26  * added copyright header
27  *
28  * Revision 1.3  2002/06/09 03:16:05  relnev
29  * added _splitpath.
30  *
31  * removed unneeded asm, old sdl 2d setup.
32  *
33  * fixed crash caused by opengl_get_region.
34  *
35  * Revision 1.2  2002/05/07 03:16:50  theoddone33
36  * The Great Newline Fix
37  *
38  * Revision 1.1.1.1  2002/05/03 03:28:11  root
39  * Initial import.
40  *
41  * 
42  * 41    9/13/99 4:52p Dave
43  * RESPAWN FIX
44  * 
45  * 40    9/01/99 10:09a Dave
46  * Pirate bob.
47  * 
48  * 39    8/26/99 8:49p Jefff
49  * old player file compatibility with new medal stuff
50  * 
51  * 38    8/22/99 5:53p Dave
52  * Scoring fixes. Added self destruct key. Put callsigns in the logfile
53  * instead of ship designations for multiplayer players.
54  * 
55  * 37    8/16/99 4:06p Dave
56  * Big honking checkin.
57  * 
58  * 36    8/11/99 11:36a Jefff
59  * added compatibility w/ fs2 demo plr version
60  * 
61  * 35    8/10/99 3:46p Jefff
62  * changes for Intelligence section of new tech room
63  * 
64  * 34    8/04/99 11:38p Andsager
65  * make new pilot detail level match registry info.
66  * 
67  * 33    8/02/99 9:55p Dave
68  * Hardcode a nice hud color config for the demo.
69  * 
70  * 32    8/02/99 9:13p Dave
71  * Added popup tips.
72  * 
73  * 31    8/01/99 12:39p Dave
74  * Added HUD contrast control key (for nebula).
75  * 
76  * 30    7/30/99 10:31p Dave
77  * Added comm menu to the configurable hud files.
78  * 
79  * 29    7/29/99 10:47p Dave
80  * Standardized D3D fogging using vertex fog. Shook out Savage 4 bugs.
81  * 
82  * 28    7/29/99 12:05a Dave
83  * Nebula speed optimizations.
84  * 
85  * 27    7/24/99 1:54p Dave
86  * Hud text flash gauge. Reworked dead popup to use 4 buttons in red-alert
87  * missions.
88  * 
89  * 26    6/22/99 7:03p Dave
90  * New detail options screen.
91  * 
92  * 25    6/16/99 4:06p Dave
93  * New pilot info popup. Added new draw-bitmap-as-poly function.
94  * 
95  * 24    6/11/99 11:13a Dave
96  * last minute changes before press tour build.
97  * 
98  * 23    6/08/99 1:14a Dave
99  * Multi colored hud test.
100  * 
101  * 22    5/03/99 8:33p Dave
102  * New version of multi host options screen.
103  * 
104  * 21    3/24/99 4:05p Dave
105  * Put in support for assigning the player to a specific squadron with a
106  * specific logo. Preliminary work for doing pos/orient checksumming in
107  * multiplayer to reduce bandwidth.
108  * 
109  * 20    1/30/99 1:29a Dave
110  * Fixed nebula thumbnail problem. Full support for 1024x768 choose pilot
111  * screen.  Fixed beam weapon death messages.
112  * 
113  * 19    1/29/99 2:08a Dave
114  * Fixed beam weapon collisions with players. Reduced size of scoring
115  * struct for multiplayer. Disabled PXO.
116  * 
117  * 18    1/21/99 2:06p Dave
118  * Final checkin for multiplayer testing.
119  * 
120  * 17    1/15/99 2:49p Dave
121  * Fixed creation of pilots.
122  * 
123  * 16    1/14/99 6:06p Dave
124  * 100% full squad logo support for single player and multiplayer.
125  * 
126  * 15    1/12/99 3:15a Dave
127  * Barracks screen support for selecting squad logos. We need real artwork
128  * :)
129  * 
130  * 14    1/06/99 2:24p Dave
131  * Stubs and release build fixes.
132  * 
133  * 13    12/14/98 12:13p Dave
134  * Spiffed up xfer system a bit. Put in support for squad logo file xfer.
135  * Need to test now.
136  * 
137  * 12    11/20/98 11:16a Dave
138  * Fixed up IPX support a bit. Making sure that switching modes and
139  * loading/saving pilot files maintains proper state.
140  * 
141  * 11    11/19/98 4:51p Dave
142  * Ignore multiplayer protocol settings in the pilot file for now.
143  * 
144  * 10    11/19/98 4:19p Dave
145  * Put IPX sockets back in psnet. Consolidated all multiplayer config
146  * files into one.
147  * 
148  * 9     10/13/98 2:47p Andsager
149  * Remove reference to Tech_shivan_species_avail
150  * 
151  * 8     10/13/98 9:29a Dave
152  * Started neatening up freespace.h. Many variables renamed and
153  * reorganized. Added AlphaColors.[h,cpp]
154  * 
155  * 7     10/12/98 9:30a Andsager
156  * Clean up barracks.cpp.  Remove unneeded ".h" files from ManagePilot
157  * 
158  * 6     10/09/98 5:17p Andsager
159  * move barracks screen into barracks.cpp
160  * 
161  * 5     10/09/98 2:57p Dave
162  * Starting splitting up OS stuff.
163  * 
164  * 4     10/08/98 9:19a Andsager
165  * Clean up pilot player read and write, starting with new version.
166  * 
167  * 3     10/07/98 6:27p Dave
168  * Globalized mission and campaign file extensions. Removed Silent Threat
169  * special code. Moved \cache \players and \multidata into the \data
170  * directory.
171  * 
172  * 2     10/07/98 10:53a Dave
173  * Initial checkin.
174  * 
175  * 1     10/07/98 10:50a Dave
176  * 
177  * 279   9/21/98 10:02p Dave
178  * Last minute changes to techroom weapon/ship/species stuff.
179  * 
180  * 278   9/08/98 12:10p Andsager
181  * Fixed a bug with saving ship and weapon info techroom flags.
182  * 
183  * 277   9/01/98 4:25p Dave
184  * Put in total (I think) backwards compatibility between mission disk
185  * freespace and non mission disk freespace, including pilot files and
186  * campaign savefiles.
187  * 
188  * 276   6/09/98 10:31a Hoffoss
189  * Created index numbers for all xstr() references.  Any new xstr() stuff
190  * added from here on out should be added to the end if the list.  The
191  * current list count can be found in FreeSpace.cpp (search for
192  * XSTR_SIZE).
193  * 
194  * 275   6/05/98 9:49a Lawrance
195  * OEM changes
196  * 
197  * 274   6/01/98 11:43a John
198  * JAS & MK:  Classified all strings for localization.
199  * 
200  * 273   5/26/98 11:53a Allender
201  * fix multiplayer problems and sexpression crash
202  * 
203  * 272   5/24/98 2:46p Lawrance
204  * Fix bug where skill level would be reset to default when switching
205  * between pilots
206  * 
207  * 271   5/23/98 4:02p Allender
208  * version change
209  * 
210  * 270   5/23/98 2:41p Mike
211  * Make Easy the default skill level and prevent old pilot's skill level
212  * from carrying into new pilot.
213  *
214 */
215
216 #include <errno.h>
217 #include "managepilot.h"
218 #include "2d.h"
219 #include "freespace.h"
220 #include "hudsquadmsg.h"
221 #include "sound.h"
222 #include "multi.h"
223 #include "eventmusic.h"
224 #include "audiostr.h"
225 #include "osregistry.h"
226 #include "font.h"
227 #include "playermenu.h"
228 #include "missionshipchoice.h"
229 #include "hudconfig.h"
230 #include "popup.h"
231 #include "redalert.h"
232 #include "techmenu.h"
233 #include "joy.h"
234 #include "mouse.h"
235 #include "cutscenes.h"
236 #include "bmpman.h"
237 #include "key.h"
238
239 // update this when altering data that is read/written to .PLR file
240 #ifndef MAKE_FS1
241 #define CURRENT_PLAYER_FILE_VERSION                             140
242 #else
243 //  99: original retail release (version 1.00)
244 // 100: retail mission pack update (version 1.04)
245 #define CURRENT_PLAYER_FILE_VERSION                             100
246 #define PREVIOUS_PLAYER_FILE_VERSION                    99
247 #endif
248 #define FS2_DEMO_PLAYER_FILE_VERSION                            135
249 #ifndef MAKE_FS1
250 #define LOWEST_COMPATIBLE_PLAYER_FILE_VERSION   CURRENT_PLAYER_FILE_VERSION                     // demo plr files should work in final
251 #else
252 #define LOWEST_COMPATIBLE_PLAYER_FILE_VERSION   PREVIOUS_PLAYER_FILE_VERSION
253 #endif
254
255 // keep track of pilot file changes here 
256 // version 2    : Added squad logo filename
257 // version 3    : Changed size of scoring struct. use ushort instead of ints for storing alltime kills by ship type
258 // version 4/5 : Added squadron name field
259 // version 6    : changed max length on a multiplayer options field
260 // version 130  : changed size of hud config struct
261 // version 133 : misc changes. new hud gauge
262 // version 134 : added HUD contrast toggle key
263 // version 135 : added tips flag  (THIS IS THE DEMO VERSION - RETAIN COMPATIBILITY FROM HERE ON OUT)
264 // version 136 : added intelligence flags to tech room visibility data
265 // version 137 : 2 new HUD gauges. 
266 // version 138  : new multiplayer config
267 // version 139 : # medals increased - added compatibility with old plr file versions
268 // version 140 : ships table reordered. clear out old pilot files
269 // search for PLAYER INIT for new pilot initialization stuff. I _think_ its in the right spot for now
270 #define PLR_FILE_ID     0x46505346      // FPSF, unique signiture to identify a .PLR file (FreeSpace Player File)  // FPSF appears as FSPF in file.
271
272 #ifdef MAKE_FS1
273 #define PLR_MAX_SHIP_TYPES_OLD                  75
274 #define PLR_MAX_WEAPON_TYPES_OLD                44
275
276 extern void hud_config_set_color(int color);
277 #endif
278
279 // Current content of a .PLR file
280 //
281
282 // Global variables
283 int Player_sel_mode;
284
285 // pilot pic image list stuff ( call pilot_load_pic_list() to make these valid )
286 char Pilot_images_arr[MAX_PILOT_IMAGES][MAX_FILENAME_LEN];
287 char *Pilot_image_names[MAX_PILOT_IMAGES];
288 int Num_pilot_images = 0;
289
290 // squad logo list stuff (call pilot_load_squad_pic_list() to make these valid )
291 char Pilot_squad_images_arr[MAX_PILOT_IMAGES][MAX_FILENAME_LEN];
292 char *Pilot_squad_image_names[MAX_PILOT_IMAGES];
293 int Num_pilot_squad_images = 0;
294
295 static uint Player_file_version;
296
297 // forward declarations
298 void read_detail_settings(CFILE *file, int Player_file_version);
299 void write_detail_settings(CFILE *file);
300 void read_stats_block(CFILE *file, int Player_file_version, scoring_struct *stats);
301 void write_stats_block(CFILE *file, scoring_struct *stats);
302 void read_multiplayer_options(player *p,CFILE *file);
303 void write_multiplayer_options(player *p,CFILE *file);
304
305 static SDL_Keycode keycode_translate_from(short keycode);
306 static short keycode_translate_to(SDL_Keycode keycode);
307
308 // internal function to delete a player file.  Called after a pilot is obsoleted, and when a pilot is deleted
309 // used in barracks and player_select
310 void delete_pilot_file( const char *pilot_name, int single )
311 {
312         char filename[MAX_FILENAME_LEN];
313         char basename[MAX_FILENAME_LEN];
314
315         // get the player file.
316
317         base_filename(pilot_name, basename, SDL_arraysize(basename));
318
319         SDL_strlcpy( filename, basename, SDL_arraysize(filename) );
320         SDL_strlcat(filename, NOX(".plr"), SDL_arraysize(filename) );
321         if (Player_sel_mode == PLAYER_SELECT_MODE_SINGLE){
322                 cf_delete(filename, CF_TYPE_SINGLE_PLAYERS);
323         } else {
324                 cf_delete(filename, CF_TYPE_MULTI_PLAYERS);
325         }
326
327         // we must try and delete the campaign save files for a pilot as well.
328         mission_campaign_delete_all_savefiles( basename, !single );
329 }
330
331 // check if a pilot file is valid or not (i.e. is usable, not out of date, etc)
332 // used in barracks and player_select
333 int verify_pilot_file(const char *filename, int single, int *rank)
334 {
335         CFILE   *file;
336         uint id, file_version;
337         int type;
338
339         filename = cf_add_ext(filename, NOX(".plr"));
340         
341         if (single){
342                 file = cfopen(filename, "rb", CFILE_NORMAL, CF_TYPE_SINGLE_PLAYERS);
343         } else {
344                 file = cfopen(filename, "rb", CFILE_NORMAL, CF_TYPE_MULTI_PLAYERS);
345         }
346
347         if (!file){
348                 return -1;
349         }
350
351         id = cfread_uint(file);
352         if (id != PLR_FILE_ID) {
353                 nprintf(("Warning", "Player file has invalid signature\n"));
354                 cfclose(file);
355                 delete_pilot_file( filename, single );
356                 return -1;
357         }
358
359         // check for compatibility here
360         file_version = cfread_uint(file);
361 /*      if (file_version < INITIAL_RELEASE_FILE_VERSION) { */
362 //      if (file_version != CURRENT_PLAYER_FILE_VERSION) {
363         if (file_version < LOWEST_COMPATIBLE_PLAYER_FILE_VERSION) {
364                 nprintf(("Warning", "WARNING => Player file is outdated and not compatible...\n"));
365                 cfclose(file);
366                 delete_pilot_file( filename, single );
367                 return -1;
368         }
369
370         type = !cfread_ubyte(file);
371         if (rank){
372                 *rank = 0;      
373         }
374                 
375         if (rank){
376                 *rank = cfread_int(file);
377         } else {
378                 cfread_int(file);
379         }
380
381         cfclose(file);
382         if (type != single){
383                 return -1;
384         }
385
386         return 0;
387 }
388
389 void pilot_write_techroom_data(CFILE *file)
390 {
391         int idx;                
392         ubyte out;
393
394         // write the ship and weapon count
395         cfwrite_int(Num_ship_types, file);
396         cfwrite_int(Num_weapon_types, file);
397 #ifndef MAKE_FS1
398         cfwrite_int(Intel_info_size, file);
399 #endif
400
401         // write all ship flags out
402         for (idx=0; idx<Num_ship_types; idx++) {
403                 out = (Ship_info[idx].flags & SIF_IN_TECH_DATABASE) ? (ubyte)1 : (ubyte)0;              
404                 cfwrite_ubyte(out, file);                               
405         }
406
407         // write all weapon types out
408         for (idx=0; idx<Num_weapon_types; idx++) {
409                 out = (Weapon_info[idx].wi_flags & WIF_IN_TECH_DATABASE) ? (ubyte)1 : (ubyte)0;
410                 cfwrite_ubyte(out, file);
411         }       
412
413 #ifndef MAKE_FS1
414         // write all intel entry flags out
415         for (idx=0; idx<Intel_info_size; idx++) {
416                 cfwrite_ubyte((ubyte)Intel_info[idx].in_tech_db, file);
417         }
418 #else
419         // whether shivans are visible or not
420         int shivans = Intel_info[2].in_tech_db ? 1 : 0;
421         cfwrite_int(shivans, file);
422 #endif
423 }
424
425
426 #ifdef MAKE_FS1
427 void pilot_read_techroom_data(CFILE *file)
428 {
429         int idx;
430         int ship_count, weapon_count, shivans;
431         ubyte in;
432
433         if (Player_file_version < 100) {
434                 int vflags = 0;
435
436                 // first set of ships visible
437                 vflags = cfread_int(file);
438
439                 for (idx = 0; idx < 32; idx++) {
440                         if ( (vflags & (1<<idx)) && (idx < Num_ship_types) ) {
441                                 Ship_info[idx].flags |= SIF_IN_TECH_DATABASE | SIF_IN_TECH_DATABASE_M;
442                         } else if (idx < Num_ship_types) {
443                                 Ship_info[idx].flags &= ~SIF_IN_TECH_DATABASE;
444                         }
445                 }
446
447                 // second set of ships visible
448                 vflags = cfread_int(file);
449
450                 for (idx = 0; idx < 32; idx++) {
451                         if ( (vflags & (1<<idx)) && ((idx+32) < Num_ship_types) ) {
452                                 Ship_info[idx+32].flags |= SIF_IN_TECH_DATABASE | SIF_IN_TECH_DATABASE_M;
453                         } else if ((idx+32) < Num_ship_types) {
454                                 Ship_info[idx+32].flags &= ~SIF_IN_TECH_DATABASE;
455                         }
456                 }
457
458                 // last set of ships visible
459                 vflags = cfread_int(file);
460
461                 for (idx = 0; idx < 32; idx++) {
462                         if ( (vflags & (1<<idx)) && ((idx+64) < Num_ship_types) ) {
463                                 Ship_info[idx+64].flags |= SIF_IN_TECH_DATABASE | SIF_IN_TECH_DATABASE_M;
464                         } else if ((idx+64) < Num_ship_types) {
465                                 Ship_info[idx+64].flags &= ~SIF_IN_TECH_DATABASE;
466                         }
467                 }
468
469                 // first set of weapons visible
470                 vflags = cfread_int(file);
471
472                 for (idx = 0; idx < 32; idx++) {
473                         if ( (vflags & (1<<idx)) && (idx < Num_weapon_types) ) {
474                                 Weapon_info[idx].wi_flags |= WIF_IN_TECH_DATABASE;
475                         } else if (idx < Num_ship_types) {
476                                 Weapon_info[idx].wi_flags &= ~WIF_IN_TECH_DATABASE;
477                         }
478                 }
479
480                 // last set of weapons visible
481                 vflags = cfread_int(file);
482
483                 for (idx = 0; idx < 32; idx++) {
484                         if ( (vflags & (1<<idx)) && ((idx+32) < Num_weapon_types) ) {
485                                 Weapon_info[idx+32].wi_flags |= WIF_IN_TECH_DATABASE;
486                         } else if ((idx+32) < Num_ship_types) {
487                                 Weapon_info[idx+32].wi_flags &= ~WIF_IN_TECH_DATABASE;
488                         }
489                 }
490
491                 // shivans visible?
492                 shivans = cfread_int(file);
493
494                 if (shivans) {
495                         Intel_info[2].in_tech_db = 1;
496                 } else {
497                         Intel_info[2].in_tech_db = 0;
498                 }
499         } else {
500                 // read in ship and weapon counts
501                 ship_count = cfread_int(file);
502                 weapon_count = cfread_int(file);
503                 SDL_assert(ship_count <= MAX_SHIP_TYPES);
504                 SDL_assert(weapon_count <= MAX_WEAPON_TYPES);
505
506                 // read all ships in
507                 for (idx=0; idx<ship_count; idx++) {
508                         in = cfread_ubyte(file);
509                         if (in) {
510                                 Ship_info[idx].flags |= SIF_IN_TECH_DATABASE | SIF_IN_TECH_DATABASE_M;
511                         } else {
512                                 Ship_info[idx].flags &= ~SIF_IN_TECH_DATABASE;
513                         }
514                 }
515
516                 // read all weapons in
517                 for (idx=0; idx<weapon_count; idx++) {
518                         in = cfread_ubyte(file);
519                         if (in) {
520                                 Weapon_info[idx].wi_flags |= WIF_IN_TECH_DATABASE;
521                         } else {
522                                 Weapon_info[idx].wi_flags &= ~WIF_IN_TECH_DATABASE;
523                         }
524                 }
525
526                 // shivans visible?
527                 shivans = cfread_int(file);
528
529                 if (shivans) {
530                         Intel_info[2].in_tech_db = 1;
531                 } else {
532                         Intel_info[2].in_tech_db = 0;
533                 }
534         }
535 }
536 #else
537 void pilot_read_techroom_data(CFILE *file)
538 {
539         int idx;
540         int ship_count, weapon_count, intel_count;
541         ubyte in;
542
543         // read in ship and weapon counts
544         ship_count = cfread_int(file);
545         weapon_count = cfread_int(file);
546         SDL_assert(ship_count <= MAX_SHIP_TYPES);
547         SDL_assert(weapon_count <= MAX_WEAPON_TYPES);
548
549         // maintain compatibility w/ demo version
550         if (Player_file_version < 136) {
551                 // skip over all this data, because the lack of tech room in the demo
552                 // left this all hosed in the demo .plr files
553                 // this will all get initialized as if this fella was a new pilot
554                 for (idx=0; idx<ship_count+weapon_count; idx++) {
555                         cfread_ubyte(file);
556                 }
557
558         } else {
559
560                 intel_count = cfread_int(file);
561                 SDL_assert(intel_count <= MAX_INTEL_ENTRIES);
562
563                 // read all ships in
564                 for (idx=0; idx<ship_count; idx++) {
565                         in = cfread_ubyte(file);
566                         if (in) {
567                                 Ship_info[idx].flags |= SIF_IN_TECH_DATABASE | SIF_IN_TECH_DATABASE_M;
568                         } else {
569                                 Ship_info[idx].flags &= ~SIF_IN_TECH_DATABASE;
570                         }
571                 }
572
573                 // read all weapons in
574                 for (idx=0; idx<weapon_count; idx++) {
575                         in = cfread_ubyte(file);
576                         if (in) {
577                                 Weapon_info[idx].wi_flags |= WIF_IN_TECH_DATABASE;
578                         } else {
579                                 Weapon_info[idx].wi_flags &= ~WIF_IN_TECH_DATABASE;
580                         }
581                 }
582
583                 // read all intel entries in
584                 for (idx=0; idx<intel_count; idx++) {
585                         in = cfread_ubyte(file);
586                         if (in) {
587                                 Intel_info[idx].in_tech_db = 1;
588                         } else {
589                                 Intel_info[idx].in_tech_db = 0;
590                         }
591                 }
592         }
593 }
594 #endif
595
596 // write out the player ship selection
597 void pilot_write_loadout(CFILE *file)
598 {
599         int i, j;
600         wss_unit *slot; 
601
602         cfwrite_string_len(Player_loadout.filename, file);
603         cfwrite_string_len(Player_loadout.last_modified, file);
604
605         // write ship and weapon counts
606         cfwrite_int(Num_ship_types, file);
607         cfwrite_int(Num_weapon_types, file);
608
609         // write ship pool
610         for ( i = 0; i < Num_ship_types; i++ ) {
611                 cfwrite_int(Player_loadout.ship_pool[i], file);
612         }
613
614         // write weapons pool
615         for ( i = 0; i < Num_weapon_types; i++ ) {
616                 cfwrite_int(Player_loadout.weapon_pool[i], file);
617         }
618
619         // write ship loadouts
620         for ( i = 0; i < MAX_WSS_SLOTS; i++ ) {
621                 slot = &Player_loadout.unit_data[i];
622                 cfwrite_int(slot->ship_class, file);
623                 for ( j = 0; j < MAX_WL_WEAPONS; j++ ) {
624                         cfwrite_int(slot->wep[j], file);
625                         cfwrite_int(slot->wep_count[j], file);
626                 }
627         }
628 }
629
630 // read in the ship selection for the pilot
631 void pilot_read_loadout(CFILE *file)
632 {
633         int i, j;
634         wss_unit *slot;
635         int ship_count, weapon_count;
636
637         memset(Player_loadout.filename, 0, MAX_FILENAME_LEN);
638         cfread_string_len(Player_loadout.filename, MAX_FILENAME_LEN, file);
639
640         memset(Player_loadout.last_modified, 0, DATE_TIME_LENGTH);      
641         cfread_string_len(Player_loadout.last_modified, DATE_TIME_LENGTH, file);        
642
643         // read in ship and weapon counts
644 #ifndef MAKE_FS1
645         ship_count = cfread_int(file);
646         weapon_count = cfread_int(file);
647 #else
648         if (Player_file_version < 100) {
649                 ship_count = PLR_MAX_SHIP_TYPES_OLD;
650                 weapon_count = PLR_MAX_WEAPON_TYPES_OLD;
651         } else {
652                 ship_count = cfread_int(file);
653                 weapon_count = cfread_int(file);
654         }
655 #endif
656
657         SDL_assert(ship_count <= MAX_SHIP_TYPES);
658         SDL_assert(weapon_count <= MAX_WEAPON_TYPES);
659
660         // read in ship pool
661         for ( i = 0; i < ship_count; i++ ) {
662                 Player_loadout.ship_pool[i] = cfread_int(file);
663         }
664
665         // read in weapons pool
666         for ( i = 0; i < weapon_count; i++ ) {
667                 Player_loadout.weapon_pool[i] = cfread_int(file);
668         }
669
670         // read in loadout info
671         for ( i = 0; i < MAX_WSS_SLOTS; i++ ) {
672                 slot = &Player_loadout.unit_data[i];
673                 slot->ship_class = cfread_int(file);
674                 for ( j = 0; j < MAX_WL_WEAPONS; j++ ) {
675                         slot->wep[j] = cfread_int(file);
676                         slot->wep_count[j] = cfread_int(file);
677                 }
678         }
679 }
680
681 // read_pilot_file()
682 //
683 // returns 0 - file read in correctly
684 //        -1 - .PLR file doesn't exist or file not compatible
685 //        >0 - errno from fopen error
686 // if single == 1, look for players in the single players directory, otherwise look in the 
687 // multiplayers directory
688 int read_pilot_file(const char *callsign, int single, player *p)
689 {
690         ubyte num_ctrls;
691         ubyte is_multi = 0;
692         char filename[MAX_FILENAME_LEN], ship_name[NAME_LENGTH];
693         CFILE   *file;
694         uint id;
695         int i, key_value;
696
697         if (!p) {
698                 SDL_assert((Player_num >= 0) && (Player_num < MAX_PLAYERS));
699                 p = &Players[Player_num];
700         }
701
702         //sprintf(filename, "%-.8s.plr",Players[Player_num].callsign);
703         SDL_assert(strlen(callsign) < MAX_FILENAME_LEN - 4);  // ensure we won't overrun the buffer
704         SDL_strlcpy( filename, callsign, SDL_arraysize(filename) );
705         SDL_strlcat( filename, NOX(".plr"), SDL_arraysize(filename) );
706
707         // if we're a standalone server in multiplayer, just fill in some bogus values since we don't have a pilot file
708         if ((Game_mode & GM_MULTIPLAYER) && (Game_mode & GM_STANDALONE_SERVER)) {
709                 memset(Player, 0, sizeof(player));
710                 SDL_strlcpy(Player->callsign, NOX("Standalone"), SDL_arraysize(Player->callsign));
711                 SDL_strlcpy(Player->short_callsign, NOX("Standalone"), SDL_arraysize(Player->short_callsign));
712                 return 0;
713         }
714         
715         // see comments at the beginning of function
716         if (single) {
717                 file = cfopen(filename, "rb", CFILE_NORMAL, CF_TYPE_SINGLE_PLAYERS);
718         } else {
719                 file = cfopen(filename, "rb", CFILE_NORMAL, CF_TYPE_MULTI_PLAYERS);
720         }
721
722         if (!file) {
723                 return errno;
724         }
725
726         id = cfread_uint(file);
727         if (id != PLR_FILE_ID) {
728                 Warning(LOCATION, "Player file has invalid signature");
729                 cfclose(file);
730                 return -1;
731         }
732
733         // check for compatibility here
734         Player_file_version = cfread_uint(file);
735         cf_set_version( file, Player_file_version );
736
737         if (Player_file_version < LOWEST_COMPATIBLE_PLAYER_FILE_VERSION) {
738                 nprintf(("Warning", "WARNING => Player file is outdated and not compatible...\n"));
739                 cfclose(file);
740                 delete_pilot_file( filename, single );
741                 return -1;
742         }
743
744         // read in whether he's a multiplayer or not
745         is_multi = cfread_ubyte(file);
746         if (is_multi){
747                 p->flags |= PLAYER_FLAGS_IS_MULTI;
748         } else {
749                 p->flags &= ~PLAYER_FLAGS_IS_MULTI;     // this takes care of unsetting any leftover bits from a (possibly) previously selected pilot
750         }
751
752         // read in rank.
753         cfread_int(file);  
754
755         // get player location
756         p->on_bastion = cfread_ubyte(file);
757
758 #ifndef MAKE_FS1
759         // tips?
760         p->tips = cfread_int(file);
761 #endif
762
763         // write out the image file name
764         cfread_string_len(p->image_filename, MAX_FILENAME_LEN - 1, file);
765
766         // write out the image file name
767         p->insignia_texture = -1;
768 #ifndef MAKE_FS1
769         cfread_string_len(p->squad_name, NAME_LENGTH, file);
770         cfread_string_len(p->squad_filename, MAX_FILENAME_LEN - 1, file);
771         player_set_squad_bitmap(p, p->squad_filename);
772 #endif
773
774         // deal with campaign stuff.  The way we store the information in the file is to first store the
775         // name of the current campaign that the player is playing.  Next we store the info regarding the campaigns
776         // that the player is currently playing
777         memset(p->current_campaign, 0, MAX_FILENAME_LEN);
778         cfread_string_len(p->current_campaign, MAX_FILENAME_LEN, file);
779
780         // read in the ship name for last ship flown by the player
781         memset(ship_name, 0, NAME_LENGTH);
782         cfread_string_len(ship_name, NAME_LENGTH, file);
783         p->last_ship_flown_si_index = ship_info_lookup(ship_name);
784         if ( p->last_ship_flown_si_index < 0 ) {
785                 nprintf(("Warning","WARNING => Ship class %s not located in Ship_info[] in player file\n",ship_name));
786                 p->last_ship_flown_si_index = ship_info_lookup(default_player_ship);
787         }
788
789         // set all the entries in the control config arrays to -1 (undefined)
790         control_config_clear();
791
792         // ---------------------------------------------
793         // read in the keyboard/joystick mapping
794         // ---------------------------------------------
795         num_ctrls = cfread_ubyte(file);
796         for (i=0; i<num_ctrls; i++) {
797                 key_value = cfread_short(file);
798                 // NOTE: next two lines are only here for transitioning from 255 to -1 as undefined key items
799                 if (key_value == 255)
800                         key_value = -1;
801
802                 Control_config[i].key_id = keycode_translate_from((short)key_value);
803
804                 key_value = cfread_short(file);
805                 // NOTE: next two lines are only here for transitioning from 255 to -1 as undefined key items
806                 if (key_value == 255)
807                         key_value = -1;
808
809                 Control_config[i].joy_id = (short) key_value;
810         }
811         
812         HUD_config.show_flags = cfread_int(file);       
813         HUD_config.show_flags2 = cfread_int(file);
814
815         HUD_config.popup_flags = cfread_int(file);
816         HUD_config.popup_flags2 = cfread_int(file);
817
818         HUD_config.num_msg_window_lines = cfread_ubyte(file);                   
819         HUD_config.rp_flags = cfread_int(file);
820         HUD_config.rp_dist =    cfread_int(file);
821
822 #ifdef MAKE_FS1
823         HUD_config.main_color = cfread_int(file);
824         HUD_color_alpha = cfread_int(file);
825
826         if ( HUD_color_alpha < HUD_COLOR_ALPHA_USER_MIN ) {
827                 HUD_color_alpha = HUD_COLOR_ALPHA_DEFAULT;
828         }
829
830         hud_config_set_color(HUD_config.main_color);
831 #else
832         // added 2 gauges with version 137
833         if(Player_file_version < 137){
834                 for(i=0; i<NUM_HUD_GAUGES-2; i++){
835                         cfread(&HUD_config.clr[i], sizeof(color), 1, file);
836                 }
837
838                 // set the 2 new gauges to be radar color
839                 memcpy(&HUD_config.clr[NUM_HUD_GAUGES-2], &HUD_config.clr[HUD_RADAR], sizeof(color));
840                 memcpy(&HUD_config.clr[NUM_HUD_GAUGES-1], &HUD_config.clr[HUD_RADAR], sizeof(color));
841         } else {
842                 for(i=0; i<NUM_HUD_GAUGES; i++){
843                         cfread(&HUD_config.clr[i], sizeof(color), 1, file);
844                 }
845         }
846 #endif
847
848         // read in the cutscenes which have been viewed
849         Cutscenes_viewable = cfread_int(file);
850
851         Master_sound_volume = cfread_float(file);
852         Master_event_music_volume = cfread_float(file);
853         Master_voice_volume = cfread_float(file);
854
855         audiostream_set_volume_all(Master_voice_volume, ASF_VOICE);
856         audiostream_set_volume_all(Master_event_music_volume, ASF_EVENTMUSIC);
857
858         if ( Master_event_music_volume > 0.0f ) {
859                 Event_music_enabled = 1;
860         } else {
861                 Event_music_enabled = 0;
862         }
863
864         read_detail_settings(file, Player_file_version);
865
866         // restore list of most recently played missions
867         Num_recent_missions = cfread_int( file );
868         SDL_assert(Num_recent_missions <= MAX_RECENT_MISSIONS);
869         for ( i = 0; i < Num_recent_missions; i++ ) {
870                 char *c;
871
872                 cfread_string_len( Recent_missions[i], MAX_FILENAME_LEN, file);
873                 // Remove the extension
874                 c = SDL_strchr(Recent_missions[i], '.');
875                 if (c)
876                         *c = 0;
877         }
878         
879         // use this block of stats from now on
880         read_stats_block(file, Player_file_version, &p->stats); 
881
882    Game_skill_level = cfread_int(file);
883
884         for (i=0; i<NUM_JOY_AXIS_ACTIONS; i++) {
885                 Axis_map_to[i] = cfread_int(file);
886                 Invert_axis[i] = cfread_int(file);
887         }
888
889         // restore some player flags
890         Player[Player_num].save_flags = cfread_int(file);
891
892         // restore the most recent ship selection       
893         pilot_read_loadout(file);       
894
895         // read in multiplayer options
896         read_multiplayer_options(p,file);
897
898         p->readyroom_listing_mode = cfread_int(file);
899         Briefing_voice_enabled = cfread_int(file);
900
901         // restore the default netgame protocol mode
902         int protocol_temp = cfread_int(file);
903         switch(protocol_temp){
904                 // TCP & PXO
905                 case NET_VMT:
906                         Multi_options_g.pxo = 1;
907                         Multi_options_g.protocol = NET_TCP;
908                         break;
909
910                 // plain TCP
911                 case NET_TCP:
912                         Multi_options_g.pxo = 0;
913                         Multi_options_g.protocol = NET_TCP;
914                         break;
915
916                 // in case of IPX, which is deprecated
917                 default:
918                         Multi_options_g.pxo = 0;
919                         Multi_options_g.protocol = NET_TCP;
920                         break;
921         }
922
923         // restore wingman status used by red alert missions
924         red_alert_read_wingman_status(file, Player_file_version);
925
926         // read techroom data
927         pilot_read_techroom_data(file);
928
929         // restore auto-advance pref
930         Player->auto_advance = cfread_int(file);
931
932         Use_mouse_to_fly = cfread_int(file);
933         Mouse_sensitivity = cfread_int(file);
934         Joy_sensitivity = cfread_int(file);
935         Dead_zone_size = cfread_int(file);
936
937         if (cfclose(file))
938                 return errno;
939
940         // restore the callsign into the Player structure
941         SDL_strlcpy(p->callsign, callsign, SDL_arraysize(p->callsign));
942
943         // restore the truncated callsign into Player structure
944         pilot_set_short_callsign(p, SHORT_CALLSIGN_PIXEL_W);
945         
946         // when we store the LastPlayer key, we have to mark it as being single or multiplayer, so we know where to look for him
947         // (since we could have a single and a multiplayer pilot with the same callsign)
948         // we'll distinguish them by putting an M and the end of the multiplayer callsign and a P at the end of a single player
949         char cat[35];
950
951         SDL_strlcpy(cat, p->callsign, SDL_arraysize(cat));
952         if (is_multi)
953                 SDL_strlcat(cat, NOX("M"), SDL_arraysize(cat));
954         else
955                 SDL_strlcat(cat, NOX("S"), SDL_arraysize(cat));
956
957         os_config_write_string( NULL, "LastPlayer", cat );
958 /*
959         // if he's not a multiplayer pilot, then load in the campaign file at this point!
960         if (!is_multi) {
961                 if (mission_campaign_load_by_name(campaign_fname)) {
962                         strcpy(campaign_fname, BUILTIN_CAMPAIGN);
963                         if (mission_campaign_load_by_name(campaign_fname))
964                                 SDL_assert(0);
965                 }
966         }
967         //Campaign.current_mission = mission_num;*/
968
969         hud_squadmsg_save_keys();                       // when new pilot read in, must save info for squadmate messaging
970
971         return 0;
972 }
973
974 void read_stats_block(CFILE *file, int file_version, scoring_struct *stats)
975 {
976         int i, total;
977    
978         init_scoring_element(stats);
979         stats->score = cfread_int(file);
980         stats->rank = cfread_int(file);
981         stats->assists = cfread_int(file);
982
983 #ifndef MAKE_FS1
984         if (file_version < 139) {
985                 // support for FS2_DEMO pilots that still have FS1 medal info in the .plr files
986                 for (i=0; i < NUM_MEDALS_FS1; i++) {
987                         cfread_int(file);                       // dummy read
988                 }
989         } else {
990                 // read the usual way
991                 for (i=0; i < NUM_MEDALS; i++) {
992                         stats->medals[i] = cfread_int(file);
993                 }
994         }
995 #else
996         for (i = 0; i < NUM_MEDALS; i++) {
997                 stats->medals[i] = cfread_int(file);
998         }
999 #endif
1000
1001         total = cfread_int(file);
1002         if (total > MAX_SHIP_TYPES){
1003                 Warning(LOCATION, "Some ship kill information will be lost due to MAX_SHIP_TYPES decrease");
1004         }
1005
1006         for (i=0; i<total && i<MAX_SHIP_TYPES; i++){
1007 #ifndef MAKE_FS1
1008                 stats->kills[i] = cfread_ushort(file);
1009 #else
1010                 stats->kills[i] = cfread_int(file);
1011 #endif
1012         }
1013
1014         stats->kill_count = cfread_int(file);
1015         stats->kill_count_ok = cfread_int(file);
1016         
1017         stats->p_shots_fired = cfread_uint(file);
1018         stats->s_shots_fired = cfread_uint(file);
1019         stats->p_shots_hit = cfread_uint(file);
1020         stats->s_shots_hit = cfread_uint(file);
1021         
1022    stats->p_bonehead_hits = cfread_uint(file);
1023         stats->s_bonehead_hits = cfread_uint(file);
1024         stats->bonehead_kills = cfread_uint(file);
1025 }
1026
1027 // grab the various detail settings
1028 void read_detail_settings(CFILE *file, int pfile_version)
1029 {
1030         detail_level_set(NUM_DEFAULT_DETAIL_LEVELS-1);
1031
1032
1033         Detail.setting = cfread_int(file);
1034
1035         Detail.nebula_detail = cfread_int(file);
1036         Detail.detail_distance = cfread_int(file);
1037 #ifdef MAKE_FS1
1038         Detail.weapon_detail = cfread_int(file);
1039 #endif
1040         Detail.hardware_textures = cfread_int(file);
1041         Detail.num_small_debris = cfread_int(file);
1042         Detail.num_particles = cfread_int(file);
1043         Detail.num_stars = cfread_int(file);
1044         Detail.shield_effects = cfread_int(file);
1045         Detail.lighting = cfread_int(file);
1046 #ifdef MAKE_FS1
1047         Detail.unknown_slider = cfread_int(file);
1048 #endif
1049
1050         // Booleans
1051         Detail.targetview_model = cfread_int(file);
1052         Detail.planets_suns = cfread_int(file);
1053 #ifdef MAKE_FS1
1054         Detail.unknown_boolean1 = cfread_int(file);
1055         Detail.unknown_boolean2 = cfread_int(file);
1056         Detail.engine_glows = cfread_int(file);
1057         Detail.alpha_effects = cfread_int(file);
1058 #else
1059         Detail.weapon_extras = cfread_int(file);
1060 #endif
1061 }
1062
1063 // Will write the pilot file in the most current format
1064 //
1065 // if single == 1, save into the single players directory, else save into the multiplayers directory
1066 int write_pilot_file_core(player *p)
1067 {
1068         char filename[MAX_FILENAME_LEN + 1];
1069    int i, si_index;
1070         ubyte is_multi;
1071         CFILE *file;
1072
1073         // never save a pilot file for the standalone server in multiplayer
1074         if ((Game_mode & GM_MULTIPLAYER) && (Game_mode & GM_STANDALONE_SERVER)) {
1075                 return 0;
1076         }
1077
1078         if (!p) {
1079                 SDL_assert((Player_num >= 0) && (Player_num < MAX_PLAYERS));
1080                 p = &Players[Player_num];
1081         }
1082
1083         i = strlen(p->callsign);
1084         if (i == 0)
1085                 return 0;       //      This means there is no player, probably meaning he was deleted and game exited from same screen.
1086
1087         SDL_assert((i > 0) && (i <= MAX_FILENAME_LEN - 4));  // ensure we won't overrun the buffer
1088         SDL_strlcpy( filename, p->callsign, SDL_arraysize(filename));
1089         SDL_strlcat( filename, NOX(".plr"), SDL_arraysize(filename) );
1090
1091         // determine if this pilot is a multiplayer pilot or not
1092         if (p->flags & PLAYER_FLAGS_IS_MULTI){
1093                 is_multi = 1;
1094         } else {
1095                 is_multi = 0;
1096         }
1097
1098         // see above
1099         if ( !is_multi ){
1100                 file = cfopen(filename, "wb", CFILE_NORMAL, CF_TYPE_SINGLE_PLAYERS);
1101         } else {
1102                 file = cfopen(filename, "wb", CFILE_NORMAL, CF_TYPE_MULTI_PLAYERS);
1103         }
1104
1105         if (!file){
1106                 return errno;
1107         }
1108
1109         // Write out player's info
1110         cfwrite_uint(PLR_FILE_ID, file);
1111         cfwrite_uint(CURRENT_PLAYER_FILE_VERSION, file);
1112
1113         cfwrite_ubyte(is_multi, file);
1114         cfwrite_int(p->stats.rank, file);
1115         cfwrite_ubyte((ubyte) p->on_bastion, file);
1116
1117 #ifndef MAKE_FS1
1118         cfwrite_int(p->tips, file);
1119 #endif
1120
1121         // write out the image file name
1122         cfwrite_string_len(p->image_filename, file);
1123
1124 #ifndef MAKE_FS1
1125         // write out the image file name
1126         cfwrite_string_len(p->squad_name, file);
1127         cfwrite_string_len(p->squad_filename, file);
1128 #endif
1129
1130         // write out the name of the player's active campaign.
1131         cfwrite_string_len(p->current_campaign, file);  
1132
1133         // write the ship name for last ship flown by the player
1134         si_index = p->last_ship_flown_si_index;
1135         if((si_index < 0) || (si_index >= Num_ship_types)){
1136                 si_index = 0;
1137         }
1138
1139         cfwrite_string_len(Ship_info[si_index].name, file);
1140
1141         // ---------------------------------------------
1142         // write the keyboard/joystick configuration
1143         // ---------------------------------------------
1144         short key_id = 0;
1145         cfwrite_ubyte( CCFG_MAX, file );
1146         for (i=0; i<CCFG_MAX; i++) {
1147                 key_id = keycode_translate_to(Control_config[i].key_id);
1148                 cfwrite_short( key_id, file );
1149                 cfwrite_short( Control_config[i].joy_id, file );
1150         }               
1151
1152         // if this hud is an observer, the player ended the last mission as an observer, so we should
1153         // restore his "real" ship HUD.
1154         HUD_CONFIG_TYPE hc_temp;
1155         hc_temp.show_flags = 0;
1156         int stored_observer = 0;
1157         if ( HUD_config.is_observer ){
1158                 // if we're in mission, copy the HUD we're currently using
1159                 if(Game_mode & GM_IN_MISSION){                  
1160                         memcpy(&hc_temp,&HUD_config,sizeof(HUD_CONFIG_TYPE));
1161                         stored_observer = 1;
1162                 }
1163
1164                 hud_config_restore();                           
1165         }
1166
1167         // write the hud configuration
1168         cfwrite_int(HUD_config.show_flags, file);       
1169         cfwrite_int(HUD_config.show_flags2, file);      
1170         cfwrite_int(HUD_config.popup_flags, file);      
1171         cfwrite_int(HUD_config.popup_flags2, file);     
1172         cfwrite_ubyte( (ubyte) HUD_config.num_msg_window_lines, file );
1173         cfwrite_int( HUD_config.rp_flags, file );
1174         cfwrite_int( HUD_config.rp_dist, file );
1175
1176 #ifdef MAKE_FS1
1177         cfwrite_int( HUD_config.main_color, file );
1178         cfwrite_int( HUD_color_alpha, file );
1179 #else
1180         for(i=0; i<NUM_HUD_GAUGES; i++){
1181                 cfwrite(&HUD_config.clr[i], sizeof(color), 1, file);
1182         }
1183 #endif
1184
1185         // restore the HUD we backed up
1186         if( (Game_mode & GM_IN_MISSION) && stored_observer ){           
1187                 memcpy(&HUD_config,&hc_temp,sizeof(HUD_CONFIG_TYPE));
1188         }
1189
1190         // write the cutscenes which have been viewed
1191         cfwrite_int(Cutscenes_viewable, file);
1192
1193         // store the digital sound fx volume, and music volume
1194         cfwrite_float(Master_sound_volume, file);
1195         cfwrite_float(Master_event_music_volume, file);
1196         cfwrite_float(Master_voice_volume, file);
1197
1198
1199         //cfwrite( &Detail, sizeof(detail_levels), 1, file );
1200         write_detail_settings(file);
1201
1202         // store list of most recently played missions
1203         cfwrite_int(Num_recent_missions, file);
1204         for (i=0; i<Num_recent_missions; i++) {
1205                 cfwrite_string_len(Recent_missions[i], file);
1206         }
1207
1208         // write the player stats
1209         write_stats_block(file, &p->stats);     
1210    cfwrite_int(Game_skill_level, file);
1211
1212         for (i=0; i<NUM_JOY_AXIS_ACTIONS; i++) {
1213                 cfwrite_int(Axis_map_to[i], file);
1214                 cfwrite_int(Invert_axis[i], file);
1215         }
1216
1217         // store some player flags
1218    cfwrite_int(Player->save_flags, file);
1219
1220         // store ship selection for most recent mission
1221         pilot_write_loadout(file);
1222
1223         // read in multiplayer options  
1224         write_multiplayer_options(p, file);
1225
1226         cfwrite_int(p->readyroom_listing_mode, file);
1227    cfwrite_int(Briefing_voice_enabled, file);
1228
1229         // store the default netgame protocol mode for this pilot
1230         SDL_assert(Multi_options_g.protocol == NET_TCP);
1231         if (Multi_options_g.pxo == 1) {
1232                 cfwrite_int(NET_VMT, file);
1233         } else {
1234                 cfwrite_int(NET_TCP, file);
1235         }
1236
1237         red_alert_write_wingman_status(file);
1238         pilot_write_techroom_data(file);
1239
1240         // store auto-advance pref
1241    cfwrite_int(Player->auto_advance, file);
1242
1243         cfwrite_int(Use_mouse_to_fly, file);
1244         cfwrite_int(Mouse_sensitivity, file);
1245         cfwrite_int(Joy_sensitivity, file);
1246         cfwrite_int(Dead_zone_size, file);
1247
1248         if (!cfclose(file))
1249                 return 0;
1250
1251         return errno;
1252 }
1253
1254 int write_pilot_file(player *the_player)
1255 {
1256         int pilot_write_rval;
1257         do {
1258                 // write_pilot_file_core returns 0 if ok, non-zero for error
1259                 pilot_write_rval = write_pilot_file_core(the_player);
1260
1261                 // check with user if write not successful
1262                 if (pilot_write_rval) {
1263                         int popup_rval = popup(PF_TITLE_RED | PF_TITLE_BIG, 3, XSTR( "&Retry", 41), XSTR( "&Ignore", 42), XSTR( "&Quit Game", 43),
1264                                 XSTR( "Warning\nFailed to save pilot file.  You may be out of disk space.  If so, you should press Alt-Tab, free up some disk space, then come back and choose retry.\n", 44) );
1265
1266                         // quit game popup return value (2)
1267                         if (popup_rval == 2) {
1268                                 exit(1);
1269                         }
1270
1271                         // _ignore_ popup return value (1) - don't save the file
1272                         if (popup_rval) {
1273                                 return pilot_write_rval;
1274                         }
1275
1276                         // retry popup return value (0) - try again 
1277                 }
1278
1279         } while (pilot_write_rval);
1280
1281         // write successful
1282         return 0;
1283 }
1284
1285 void write_stats_block(CFILE *file,scoring_struct *stats)
1286 {
1287         int i;
1288         int total;
1289
1290         cfwrite_int(stats->score, file);
1291         cfwrite_int(stats->rank, file);
1292         cfwrite_int(stats->assists, file);
1293         for (i=0; i<NUM_MEDALS; i++){
1294                 cfwrite_int(stats->medals[i], file);
1295         }
1296
1297         total = MAX_SHIP_TYPES;
1298         while (total && !stats->kills[total - 1]){  // find last used element
1299                 total--;
1300         }
1301
1302         cfwrite_int(total, file);
1303         for (i=0; i<total; i++){
1304 #ifndef MAKE_FS1
1305                 cfwrite_ushort(stats->kills[i], file);
1306 #else
1307                 cfwrite_int(stats->kills[i], file);
1308 #endif
1309         }
1310
1311         cfwrite_int(stats->kill_count,file);
1312         cfwrite_int(stats->kill_count_ok,file);
1313
1314    cfwrite_uint(stats->p_shots_fired,file);
1315         cfwrite_uint(stats->s_shots_fired,file);
1316         cfwrite_uint(stats->p_shots_hit,file);
1317         cfwrite_uint(stats->s_shots_hit,file);
1318         cfwrite_uint(stats->p_bonehead_hits,file);
1319         cfwrite_uint(stats->s_bonehead_hits,file);
1320         cfwrite_uint(stats->bonehead_kills,file);
1321 }
1322
1323 // write the various detail settings
1324 void write_detail_settings(CFILE *file)
1325 {
1326         cfwrite_int(Detail.setting, file);
1327
1328         cfwrite_int(Detail.nebula_detail, file);
1329         cfwrite_int(Detail.detail_distance, file);
1330 #ifdef MAKE_FS1
1331         cfwrite_int(Detail.weapon_detail, file);
1332 #endif
1333         cfwrite_int(Detail.hardware_textures, file);
1334         cfwrite_int(Detail.num_small_debris, file);
1335         cfwrite_int(Detail.num_particles, file);
1336         cfwrite_int(Detail.num_stars, file);
1337         cfwrite_int(Detail.shield_effects, file);
1338         cfwrite_int(Detail.lighting, file);
1339 #ifdef MAKE_FS1
1340         cfwrite_int(Detail.unknown_slider, file);
1341 #endif
1342
1343         cfwrite_int(Detail.targetview_model, file);
1344         cfwrite_int(Detail.planets_suns, file);
1345 #ifdef MAKE_FS1
1346         cfwrite_int(Detail.unknown_boolean1, file);
1347         cfwrite_int(Detail.unknown_boolean2, file);
1348         cfwrite_int(Detail.engine_glows, file);
1349         cfwrite_int(Detail.alpha_effects, file);
1350 #else
1351         cfwrite_int(Detail.weapon_extras, file);
1352 #endif
1353 }
1354
1355 // write multiplayer information
1356 void write_multiplayer_options(player *p,CFILE *file)
1357 {       
1358         // write the netgame options
1359         cfwrite_ubyte(p->m_server_options.squad_set,file);
1360         cfwrite_ubyte(p->m_server_options.endgame_set,file);    
1361         cfwrite_int(p->m_server_options.flags,file);
1362         cfwrite_uint(p->m_server_options.respawn,file);
1363         cfwrite_ubyte(p->m_server_options.max_observers,file);
1364         cfwrite_ubyte(p->m_server_options.skill_level,file);
1365         cfwrite_ubyte(p->m_server_options.voice_qos,file);
1366         cfwrite_int(p->m_server_options.voice_token_wait,file);
1367         cfwrite_int(p->m_server_options.voice_record_time,file);
1368         cfwrite(&p->m_server_options.mission_time_limit,sizeof(fix),1,file);
1369         cfwrite_int(p->m_server_options.kill_limit,file);
1370
1371         // write the local options
1372         cfwrite_int(p->m_local_options.flags,file);
1373         cfwrite_int(p->m_local_options.obj_update_level,file);
1374 }
1375
1376 // read multiplayer options
1377 void read_multiplayer_options(player *p,CFILE *file)
1378 {
1379         // read the netgame options
1380         p->m_server_options.squad_set = cfread_ubyte(file);
1381         p->m_server_options.endgame_set = cfread_ubyte(file);   
1382         p->m_server_options.flags = cfread_int(file);
1383         p->m_server_options.respawn = cfread_uint(file);
1384         p->m_server_options.max_observers = cfread_ubyte(file);
1385         p->m_server_options.skill_level = cfread_ubyte(file);
1386         p->m_server_options.voice_qos = cfread_ubyte(file);
1387         p->m_server_options.voice_token_wait = cfread_int(file);
1388         p->m_server_options.voice_record_time = cfread_int(file);
1389         cfread(&p->m_server_options.mission_time_limit,sizeof(fix),1,file);
1390         p->m_server_options.kill_limit = cfread_int(file);
1391
1392         // read the local options
1393         p->m_local_options.flags = cfread_int(file);
1394         p->m_local_options.obj_update_level = cfread_int(file);
1395 }
1396
1397 // run thorough an open file (at the beginning) and see if this pilot is a multiplayer pilot
1398 int is_pilot_multi(CFILE *fp)
1399 {
1400         uint id,file_version;
1401         ubyte is_multi;
1402         
1403         id = cfread_uint(fp);
1404         if (id != PLR_FILE_ID) {
1405                 Warning(LOCATION, "Player file has invalid signature");
1406                 cfclose(fp);
1407                 return -1;
1408         }
1409
1410         // check for compatibility here
1411         file_version = cfread_uint(fp);
1412         if (file_version < LOWEST_COMPATIBLE_PLAYER_FILE_VERSION) {
1413                 nprintf(("Warning", "WARNING => Player file is outdated and not compatible...\n"));
1414                 cfclose(fp);
1415                 return -1;
1416         }
1417
1418         // read in whether he's a multiplayer or not
1419         is_multi = cfread_ubyte(fp);
1420         if (is_multi)
1421                 return 1;
1422
1423         return 0;
1424 }
1425
1426 int is_pilot_multi(player *p)
1427 {
1428         return (p->flags & PLAYER_FLAGS_IS_MULTI) ? 1 : 0;
1429 }
1430
1431 // this works on barracks and player_select interface screens
1432 void init_new_pilot(player *p, int reset)
1433 {
1434         int cur_speed;
1435
1436         if (reset) {
1437                 hud_set_default_hud_config(p);          // use a default hud config
1438
1439 #ifndef MAKE_FS1
1440                 // in the demo, load up the hardcoded hcf file
1441 #ifdef FS2_DEMO
1442                 hud_config_color_load("hud_1.hcf");
1443 #else
1444                 hud_config_color_load("hud_3.hcf");
1445 #endif
1446 #else
1447                 hud_config_set_color(HUD_COLOR_GREEN);
1448 #endif
1449
1450                 control_config_reset_defaults();                // get a default keyboard config
1451                 player_set_pilot_defaults(p);                   // set up any player struct defaults
1452
1453                 cur_speed = os_config_read_uint(NULL, NOX("ComputerSpeed"), 2 );
1454                 if ( cur_speed < 0 )    {
1455                         cur_speed = 0;
1456                 } else if ( cur_speed >= NUM_DEFAULT_DETAIL_LEVELS )    {
1457                         cur_speed = NUM_DEFAULT_DETAIL_LEVELS-1;
1458                 }       
1459                 // always set to high
1460                 // DKA: 8/4/99 USE speed from registry
1461                 // cur_speed = NUM_DEFAULT_DETAIL_LEVELS-2;
1462
1463 #if NUM_DEFAULT_DETAIL_LEVELS != 4
1464 #error Code in ManagePilot assumes NUM_DEFAULT_DETAIL_LEVELS = 4
1465 #endif
1466
1467                 detail_level_set(cur_speed);
1468
1469                 Game_skill_level = game_get_default_skill_level();
1470
1471                 mprintf(( "Setting detail level to %d because of new pilot\n", cur_speed ));
1472                 Use_mouse_to_fly = 0;
1473                 Mouse_sensitivity = 4;
1474                 Joy_sensitivity = 9;
1475                 Dead_zone_size = 10;
1476         }
1477
1478         // unassigned squadron
1479         SDL_strlcpy(p->squad_name, XSTR("Unassigned", 1255), SDL_arraysize(p->squad_name));
1480         SDL_strlcpy(p->squad_filename, "", SDL_arraysize(p->squad_filename));
1481
1482         // set him to be a single player pilot by default (the actual creation routines will change this if necessary)
1483         p->flags &= ~PLAYER_FLAGS_IS_MULTI;
1484
1485         // effectively sets the length return by strlen() to 0  
1486         Campaign.filename[0] = 0;
1487         p->on_bastion = 0;
1488
1489         // pick a random pilot image for this guy
1490         if (reset){
1491                 pilot_set_random_pic(p);
1492                 p->insignia_texture = -1;
1493                 pilot_set_random_squad_pic(p);
1494         }
1495
1496         init_scoring_element(&p->stats);        
1497         
1498         p->stats.score = 0;
1499         p->stats.rank = RANK_ENSIGN;    
1500
1501 #ifndef MAKE_FS1
1502         p->tips = 1;
1503 #else
1504         p->tips = 0;
1505 #endif
1506
1507         Multi_options_g.protocol = NET_TCP;     
1508
1509         // initialize default multiplayer options
1510         multi_options_set_netgame_defaults(&p->m_server_options);
1511         multi_options_set_local_defaults(&p->m_local_options);
1512
1513         Player_loadout.filename[0] = 0;
1514
1515         // reset the cutscenes which can be viewed
1516         if ( reset ){
1517                 Cutscenes_viewable = INTRO_CUTSCENE_FLAG;
1518         }
1519 }
1520
1521 void pilot_set_short_callsign(player *p, int max_width)
1522 {
1523         SDL_strlcpy(p->short_callsign, p->callsign, SDL_arraysize(p->short_callsign));
1524         gr_set_font(FONT1);
1525         gr_force_fit_string(p->short_callsign, CALLSIGN_LEN - 1, max_width);
1526         gr_get_string_size( &(p->short_callsign_width), NULL, p->short_callsign );
1527 }
1528
1529 // pick a random image for the passed player
1530 void pilot_set_random_pic(player *p)
1531 {
1532         // if there are no available pilot pics, set the image filename to null
1533         if (Num_pilot_images <= 0) {
1534                 SDL_strlcpy(p->image_filename, "", SDL_arraysize(p->image_filename));
1535         } else {
1536                 // pick a random name from the list
1537                 int random_index = rand() % Num_pilot_images;
1538                 SDL_assert((random_index >= 0) && (random_index < Num_pilot_images));
1539                 SDL_strlcpy(p->image_filename, Pilot_images_arr[random_index], SDL_arraysize(p->image_filename));
1540         }       
1541 }
1542
1543 // pick a random image for the passed player
1544 void pilot_set_random_squad_pic(player *p)
1545 {       
1546         // if there are no available pilot pics, set the image filename to null
1547         if (Num_pilot_squad_images <= 0) {
1548                 player_set_squad_bitmap(p, "");
1549                 // strcpy(p->squad_filename, "");               
1550         } else {
1551                 // pick a random name from the list
1552                 int random_index = rand() % Num_pilot_squad_images;             
1553                 SDL_assert((random_index >= 0) && (random_index < Num_pilot_squad_images));
1554                 player_set_squad_bitmap(p, Pilot_squad_images_arr[random_index]); 
1555                 // strcpy(p->squad_filename, Pilot_squad_images_arr[random_index]);
1556         }       
1557 }
1558
1559 // format a pilot's callsign into a "personal" form - ie, adding a 's or just an ' as appropriate
1560 void pilot_format_callsign_personal(const char *in_callsign, char *out_callsign, const int out_size)
1561 {
1562         // don't do anything if we've got invalid strings
1563         if((in_callsign == NULL) || (out_callsign == NULL)){
1564                 return;
1565         }
1566
1567         // copy the original string
1568         SDL_strlcpy(out_callsign, in_callsign, out_size);
1569
1570         // tack on the appropriate postfix
1571         if(in_callsign[strlen(in_callsign) - 1] == 's'){                
1572                 SDL_strlcat(out_callsign,XSTR( "\'", 45), out_size);
1573         } else {
1574                 SDL_strlcat(out_callsign,XSTR( "\'s", 46), out_size);
1575         }
1576 }
1577
1578 // throw up a popup asking the user to verify the overwrite of an existing pilot name
1579 // 1 == ok to overwrite, 0 == not ok
1580 int pilot_verify_overwrite()
1581 {
1582         return popup(PF_USE_AFFIRMATIVE_ICON | PF_USE_NEGATIVE_ICON | PF_TITLE_RED | PF_TITLE_BIG, 2, XSTR( "&No", 47), XSTR( "&Yes", 48), XSTR( "Warning\nA duplicate pilot exists\nOverwrite?", 49));
1583 }
1584
1585 extern int Skip_packfile_search;  // located in CFileList.cpp
1586
1587
1588 // load up the list of pilot image filenames (do this at game startup as well as barracks startup)
1589 void pilot_load_pic_list()
1590 {
1591         Num_pilot_images = 0;
1592         
1593         // load pilot images from the player images directory
1594         Num_pilot_images = cf_get_file_list_preallocated(MAX_PILOT_IMAGES, Pilot_images_arr, Pilot_image_names, CF_TYPE_PLAYER_IMAGES_MAIN, NOX("*.pcx"));
1595
1596         // sort all filenames
1597         cf_sort_filenames(Num_pilot_images, Pilot_image_names, CF_SORT_NAME);
1598 }
1599
1600 // load up the list of pilot squad filenames
1601 void pilot_load_squad_pic_list()
1602 {
1603         Num_pilot_squad_images = 0;
1604         
1605         // load pilot images from the player images directory
1606         Num_pilot_squad_images = cf_get_file_list_preallocated(MAX_PILOT_IMAGES, Pilot_squad_images_arr, Pilot_squad_image_names, CF_TYPE_SQUAD_IMAGES_MAIN, NOX("*.pcx"));
1607
1608         // sort all filenames
1609         cf_sort_filenames(Num_pilot_squad_images, Pilot_squad_image_names, CF_SORT_NAME);
1610 }
1611
1612 // will attempt to load an insignia bitmap and set it as active for the player
1613 void player_set_squad_bitmap(player *p, const char *fname)
1614 {
1615         // sanity check
1616         if(p == NULL){
1617                 return;
1618         }
1619
1620         // if he has another bitmap already - unload it
1621         if(p->insignia_texture >= 0){
1622                 bm_unload(p->insignia_texture);
1623         }
1624
1625         p->insignia_texture = -1;
1626
1627         // try and set the new one
1628         if (fname != p->squad_filename) {
1629                 SDL_strlcpy(p->squad_filename, fname, SDL_arraysize(p->squad_filename));
1630         }
1631
1632         if(strlen(p->squad_filename) > 0){
1633                 p->insignia_texture = bm_load_duplicate(fname);
1634                 
1635                 // lock is as a transparent texture
1636                 if(p->insignia_texture != -1){
1637                         bm_lock(p->insignia_texture, 16, BMP_TEX_XPARENT);
1638                         bm_unlock(p->insignia_texture);
1639                 }
1640         }
1641
1642         /*
1643         flen = strlen(filename);
1644         elen = strlen(ext);
1645         SDL_assert(flen < MAX_PATH_LEN);
1646         strcpy(path, filename);
1647         if ((flen < 4) || SDL_strcasecmp(path + flen - elen, ext)) {
1648                 SDL_assert(flen + elen < MAX_PATH_LEN);
1649                 strcat(path, ext);
1650         }
1651         */
1652 }
1653
1654 // set squadron
1655 void player_set_squad(player *p, const char *squad_name)
1656 {
1657         // sanity check
1658         if(p == NULL){
1659                 return;
1660         }
1661
1662         SDL_strlcpy(p->squad_name, squad_name, SDL_arraysize(p->squad_name));
1663 }
1664
1665 DCF(pilot,"Changes pilot stats. (Like reset campaign)" )
1666 {
1667         if (Dc_command) {
1668                 dc_get_arg(ARG_STRING);
1669                 if (!strcmp(Dc_arg, NOX("reset"))) {
1670                         if (strlen(Campaign.filename)) {
1671                                 mission_campaign_savefile_delete(Campaign.filename);
1672                                 mission_campaign_load(Campaign.filename);
1673                         }
1674
1675                 } else {
1676                         Dc_help = 1;  // print usage, not stats
1677                 }
1678         }
1679
1680         if (Dc_help) {
1681                 dc_printf( "Usage: pilot keyword\nWhere keyword can be in the following forms:\n" );
1682                 dc_printf( "pilot reset                 Resets campaign stats.\n" );
1683                 Dc_status = 0;  // don't print status if help is printed.  Too messy.
1684         }
1685
1686         if (Dc_status) {
1687                 // no stats
1688         }
1689 }
1690
1691 struct fs_keycode_t {
1692         short fs_code;
1693         SDL_Keycode sdl_code;
1694 };
1695
1696 static const fs_keycode_t keycode_lookup[] = {
1697         { /* KEY_0 */           0x0B,   SDLK_0                  },
1698         { /* KEY_1 */           0x02,   SDLK_1                  },
1699         { /* KEY_2 */           0x03,   SDLK_2                  },
1700         { /* KEY_3 */           0x04,   SDLK_3                  },
1701         { /* KEY_4 */           0x05,   SDLK_4                  },
1702         { /* KEY_5 */           0x06,   SDLK_5                  },
1703         { /* KEY_6 */           0x07,   SDLK_6                  },
1704         { /* KEY_7 */           0x08,   SDLK_7                  },
1705         { /* KEY_8 */           0x09,   SDLK_8                  },
1706         { /* KEY_9 */           0x0A,   SDLK_9                  },
1707         { /* KEY_A */           0x1E,   SDLK_a                  },
1708         { /* KEY_B */           0x30,   SDLK_b                  },
1709         { /* KEY_C */           0x2E,   SDLK_c                  },
1710         { /* KEY_D */           0x20,   SDLK_d                  },
1711         { /* KEY_E */           0x12,   SDLK_e                  },
1712         { /* KEY_F */           0x21,   SDLK_f                  },
1713         { /* KEY_G */           0x22,   SDLK_g                  },
1714         { /* KEY_H */           0x23,   SDLK_h                  },
1715         { /* KEY_I */           0x17,   SDLK_i                  },
1716         { /* KEY_J */           0x24,   SDLK_j                  },
1717         { /* KEY_K */           0x25,   SDLK_k                  },
1718         { /* KEY_L */           0x26,   SDLK_l                  },
1719         { /* KEY_M */           0x32,   SDLK_m                  },
1720         { /* KEY_N */           0x31,   SDLK_n                  },
1721         { /* KEY_O */           0x18,   SDLK_o                  },
1722         { /* KEY_P */           0x19,   SDLK_p                  },
1723         { /* KEY_Q */           0x10,   SDLK_q                  },
1724         { /* KEY_R */           0x13,   SDLK_r                  },
1725         { /* KEY_S */           0x1F,   SDLK_s                  },
1726         { /* KEY_T */           0x14,   SDLK_t                  },
1727         { /* KEY_U */           0x16,   SDLK_u                  },
1728         { /* KEY_V */           0x2F,   SDLK_v                  },
1729         { /* KEY_W */           0x11,   SDLK_w                  },
1730         { /* KEY_X */           0x2D,   SDLK_x                  },
1731         { /* KEY_Y */           0x15,   SDLK_y                  },
1732         { /* KEY_Z */           0x2C,   SDLK_z                  },
1733         { /* KEY_MINUS */       0x0C,   SDLK_MINUS              },
1734         { /* KEY_EQUAL */       0x0D,   SDLK_EQUALS             },
1735         { /* KEY_DIVIDE */      0x35,   SDLK_SLASH              },
1736         { /* KEY_SLASH */       0x2B,   SDLK_BACKSLASH  },
1737         { /* KEY_SLASH_UK */    0x56,   SDLK_BACKSLASH  },
1738         { /* KEY_COMMA */       0x33,   SDLK_COMMA              },
1739         { /* KEY_PERIOD */      0x34,   SDLK_PERIOD             },
1740         { /* KEY_SEMICOL */     0x27,   SDLK_SEMICOLON  },
1741         { /* KEY_LBRACKET */    0x1A,   SDLK_LEFTBRACKET        },
1742         { /* KEY_RBRACKET */    0x1B,   SDLK_RIGHTBRACKET       },
1743         { /* KEY_RAPOSTRO */    0x28,   SDLK_QUOTE              },
1744         { /* KEY_LAPOSTRO */    0x29,   SDLK_BACKQUOTE  },
1745         { /* KEY_ESC */         0x01,   SDLK_ESCAPE             },
1746         { /* KEY_ENTER */       0x1C,   SDLK_RETURN             },
1747         { /* KEY_BACKSP */      0x0E,   SDLK_BACKSPACE  },
1748         { /* KEY_TAB */         0x0F,   SDLK_TAB                },
1749         { /* KEY_SPACEBAR */    0x39,   SDLK_SPACE              },
1750         { /* KEY_NUMLOCK */     0x61,   SDLK_NUMLOCKCLEAR       },
1751         { /* KEY_SCROLLOCK */   0x46,   SDLK_SCROLLLOCK },
1752         { /* KEY_CAPSLOCK */    0x3A,   SDLK_CAPSLOCK   },
1753         { /* KEY_LSHIFT */      0x2A,   SDLK_LSHIFT             },
1754         { /* KEY_RSHIFT */      0x36,   SDLK_RSHIFT             },
1755         { /* KEY_LALT */        0x38,   SDLK_LALT               },
1756         { /* KEY_RALT */        0xB8,   SDLK_RALT               },
1757         { /* KEY_LCTRL */       0x1D,   SDLK_LCTRL              },
1758         { /* KEY_RCTRL */       0x9D,   SDLK_RCTRL              },
1759         { /* KEY_F1 */          0x3B,   SDLK_F1                 },
1760         { /* KEY_F2 */          0x3C,   SDLK_F2                 },
1761         { /* KEY_F3 */          0x3D,   SDLK_F3                 },
1762         { /* KEY_F4 */          0x3E,   SDLK_F4                 },
1763         { /* KEY_F5 */          0x3F,   SDLK_F5                 },
1764         { /* KEY_F6 */          0x40,   SDLK_F6                 },
1765         { /* KEY_F7 */          0x41,   SDLK_F7                 },
1766         { /* KEY_F8 */          0x42,   SDLK_F8                 },
1767         { /* KEY_F9 */          0x43,   SDLK_F9                 },
1768         { /* KEY_F10 */         0x44,   SDLK_F10                },
1769         { /* KEY_F11 */         0x57,   SDLK_F11                },
1770         { /* KEY_F12 */         0x58,   SDLK_F12                },
1771         { /* KEY_PAD0 */        0x52,   SDLK_KP_0               },
1772         { /* KEY_PAD1 */        0x4F,   SDLK_KP_1               },
1773         { /* KEY_PAD2 */        0x50,   SDLK_KP_2               },
1774         { /* KEY_PAD3 */        0x51,   SDLK_KP_3               },
1775         { /* KEY_PAD4 */        0x4B,   SDLK_KP_4               },
1776         { /* KEY_PAD5 */        0x4C,   SDLK_KP_5               },
1777         { /* KEY_PAD6 */        0x4D,   SDLK_KP_6               },
1778         { /* KEY_PAD7 */        0x47,   SDLK_KP_7               },
1779         { /* KEY_PAD8 */        0x48,   SDLK_KP_8               },
1780         { /* KEY_PAD9 */        0x49,   SDLK_KP_9               },
1781         { /* KEY_PADMINUS */    0x4A,   SDLK_KP_MINUS   },
1782         { /* KEY_PADPLUS */     0x4E,   SDLK_KP_PLUS    },
1783         { /* KEY_PADPERIOD */   0x53,   SDLK_KP_PERIOD  },
1784         { /* KEY_PADDIVIDE */   0xB5,   SDLK_KP_DIVIDE  },
1785         { /* KEY_PADMULTIPLY */ 0x37,   SDLK_KP_MULTIPLY        },
1786         { /* KEY_PADENTER */    0x9C,   SDLK_KP_ENTER   },
1787         { /* KEY_INSERT */      0xD2,   SDLK_INSERT             },
1788         { /* KEY_HOME */        0xC7,   SDLK_HOME               },
1789         { /* KEY_PAGEUP */      0xC9,   SDLK_PAGEUP             },
1790         { /* KEY_DELETE */      0xd3,   SDLK_DELETE             },
1791         { /* KEY_END */         0xCF,   SDLK_END                },
1792         { /* KEY_PAGEDOWN */    0xD1,   SDLK_PAGEDOWN   },
1793         { /* KEY_UP */          0xC8,   SDLK_UP                 },
1794         { /* KEY_DOWN */        0xD0,   SDLK_DOWN               },
1795         { /* KEY_LEFT */        0xCB,   SDLK_LEFT               },
1796         { /* KEY_RIGHT */       0xCD,   SDLK_RIGHT              },
1797         { /* KEY_PRINT_SCRN */  0xB7,   SDLK_PRINTSCREEN        },
1798         { /* KEY_PAUSE */       0x45,   SDLK_PAUSE              },
1799         { /* KEY_BREAK */       0xc6,   SDLK_PAUSE              }
1800 };
1801
1802 static SDL_Keycode keycode_translate_from(short keycode)
1803 {
1804         const int tbl_size = sizeof(keycode_lookup) / sizeof(fs_keycode_t);
1805
1806         if (keycode < 0) {
1807                 return -1;
1808         }
1809
1810         int mods = keycode & 0xf900;
1811         keycode &= KEY_MASK;
1812
1813         for (int i = 0; i < tbl_size; i++) {
1814                 if (keycode_lookup[i].fs_code == keycode) {
1815                         return (SDL_Keycode)(keycode_lookup[i].sdl_code | mods);
1816                 }
1817         }
1818
1819         return -1;
1820 }
1821
1822 static short keycode_translate_to(SDL_Keycode keycode)
1823 {
1824         const int tbl_size = sizeof(keycode_lookup) / sizeof(fs_keycode_t);
1825
1826         if (keycode < 0) {
1827                 return -1;
1828         }
1829
1830         int mods = keycode & 0xf900;
1831         keycode &= KEY_MASK;
1832
1833         for (int i = 0; i < tbl_size; i++) {
1834                 if (keycode_lookup[i].sdl_code == keycode) {
1835                         return (short)(keycode_lookup[i].fs_code | mods);
1836                 }
1837         }
1838
1839         return -1;
1840 }