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