]> icculus.org git repositories - taylor/freespace2.git/blob - src/playerman/managepilot.cpp
Freespace 1 support
[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.5  2003/05/25 02:30:43  taylor
20  * Freespace 1 support
21  *
22  * Revision 1.4  2002/06/09 04:41:25  relnev
23  * added copyright header
24  *
25  * Revision 1.3  2002/06/09 03:16:05  relnev
26  * added _splitpath.
27  *
28  * removed unneeded asm, old sdl 2d setup.
29  *
30  * fixed crash caused by opengl_get_region.
31  *
32  * Revision 1.2  2002/05/07 03:16:50  theoddone33
33  * The Great Newline Fix
34  *
35  * Revision 1.1.1.1  2002/05/03 03:28:11  root
36  * Initial import.
37  *
38  * 
39  * 41    9/13/99 4:52p Dave
40  * RESPAWN FIX
41  * 
42  * 40    9/01/99 10:09a Dave
43  * Pirate bob.
44  * 
45  * 39    8/26/99 8:49p Jefff
46  * old player file compatibility with new medal stuff
47  * 
48  * 38    8/22/99 5:53p Dave
49  * Scoring fixes. Added self destruct key. Put callsigns in the logfile
50  * instead of ship designations for multiplayer players.
51  * 
52  * 37    8/16/99 4:06p Dave
53  * Big honking checkin.
54  * 
55  * 36    8/11/99 11:36a Jefff
56  * added compatibility w/ fs2 demo plr version
57  * 
58  * 35    8/10/99 3:46p Jefff
59  * changes for Intelligence section of new tech room
60  * 
61  * 34    8/04/99 11:38p Andsager
62  * make new pilot detail level match registry info.
63  * 
64  * 33    8/02/99 9:55p Dave
65  * Hardcode a nice hud color config for the demo.
66  * 
67  * 32    8/02/99 9:13p Dave
68  * Added popup tips.
69  * 
70  * 31    8/01/99 12:39p Dave
71  * Added HUD contrast control key (for nebula).
72  * 
73  * 30    7/30/99 10:31p Dave
74  * Added comm menu to the configurable hud files.
75  * 
76  * 29    7/29/99 10:47p Dave
77  * Standardized D3D fogging using vertex fog. Shook out Savage 4 bugs.
78  * 
79  * 28    7/29/99 12:05a Dave
80  * Nebula speed optimizations.
81  * 
82  * 27    7/24/99 1:54p Dave
83  * Hud text flash gauge. Reworked dead popup to use 4 buttons in red-alert
84  * missions.
85  * 
86  * 26    6/22/99 7:03p Dave
87  * New detail options screen.
88  * 
89  * 25    6/16/99 4:06p Dave
90  * New pilot info popup. Added new draw-bitmap-as-poly function.
91  * 
92  * 24    6/11/99 11:13a Dave
93  * last minute changes before press tour build.
94  * 
95  * 23    6/08/99 1:14a Dave
96  * Multi colored hud test.
97  * 
98  * 22    5/03/99 8:33p Dave
99  * New version of multi host options screen.
100  * 
101  * 21    3/24/99 4:05p Dave
102  * Put in support for assigning the player to a specific squadron with a
103  * specific logo. Preliminary work for doing pos/orient checksumming in
104  * multiplayer to reduce bandwidth.
105  * 
106  * 20    1/30/99 1:29a Dave
107  * Fixed nebula thumbnail problem. Full support for 1024x768 choose pilot
108  * screen.  Fixed beam weapon death messages.
109  * 
110  * 19    1/29/99 2:08a Dave
111  * Fixed beam weapon collisions with players. Reduced size of scoring
112  * struct for multiplayer. Disabled PXO.
113  * 
114  * 18    1/21/99 2:06p Dave
115  * Final checkin for multiplayer testing.
116  * 
117  * 17    1/15/99 2:49p Dave
118  * Fixed creation of pilots.
119  * 
120  * 16    1/14/99 6:06p Dave
121  * 100% full squad logo support for single player and multiplayer.
122  * 
123  * 15    1/12/99 3:15a Dave
124  * Barracks screen support for selecting squad logos. We need real artwork
125  * :)
126  * 
127  * 14    1/06/99 2:24p Dave
128  * Stubs and release build fixes.
129  * 
130  * 13    12/14/98 12:13p Dave
131  * Spiffed up xfer system a bit. Put in support for squad logo file xfer.
132  * Need to test now.
133  * 
134  * 12    11/20/98 11:16a Dave
135  * Fixed up IPX support a bit. Making sure that switching modes and
136  * loading/saving pilot files maintains proper state.
137  * 
138  * 11    11/19/98 4:51p Dave
139  * Ignore multiplayer protocol settings in the pilot file for now.
140  * 
141  * 10    11/19/98 4:19p Dave
142  * Put IPX sockets back in psnet. Consolidated all multiplayer config
143  * files into one.
144  * 
145  * 9     10/13/98 2:47p Andsager
146  * Remove reference to Tech_shivan_species_avail
147  * 
148  * 8     10/13/98 9:29a Dave
149  * Started neatening up freespace.h. Many variables renamed and
150  * reorganized. Added AlphaColors.[h,cpp]
151  * 
152  * 7     10/12/98 9:30a Andsager
153  * Clean up barracks.cpp.  Remove unneeded ".h" files from ManagePilot
154  * 
155  * 6     10/09/98 5:17p Andsager
156  * move barracks screen into barracks.cpp
157  * 
158  * 5     10/09/98 2:57p Dave
159  * Starting splitting up OS stuff.
160  * 
161  * 4     10/08/98 9:19a Andsager
162  * Clean up pilot player read and write, starting with new version.
163  * 
164  * 3     10/07/98 6:27p Dave
165  * Globalized mission and campaign file extensions. Removed Silent Threat
166  * special code. Moved \cache \players and \multidata into the \data
167  * directory.
168  * 
169  * 2     10/07/98 10:53a Dave
170  * Initial checkin.
171  * 
172  * 1     10/07/98 10:50a Dave
173  * 
174  * 279   9/21/98 10:02p Dave
175  * Last minute changes to techroom weapon/ship/species stuff.
176  * 
177  * 278   9/08/98 12:10p Andsager
178  * Fixed a bug with saving ship and weapon info techroom flags.
179  * 
180  * 277   9/01/98 4:25p Dave
181  * Put in total (I think) backwards compatibility between mission disk
182  * freespace and non mission disk freespace, including pilot files and
183  * campaign savefiles.
184  * 
185  * 276   6/09/98 10:31a Hoffoss
186  * Created index numbers for all xstr() references.  Any new xstr() stuff
187  * added from here on out should be added to the end if the list.  The
188  * current list count can be found in FreeSpace.cpp (search for
189  * XSTR_SIZE).
190  * 
191  * 275   6/05/98 9:49a Lawrance
192  * OEM changes
193  * 
194  * 274   6/01/98 11:43a John
195  * JAS & MK:  Classified all strings for localization.
196  * 
197  * 273   5/26/98 11:53a Allender
198  * fix multiplayer problems and sexpression crash
199  * 
200  * 272   5/24/98 2:46p Lawrance
201  * Fix bug where skill level would be reset to default when switching
202  * between pilots
203  * 
204  * 271   5/23/98 4:02p Allender
205  * version change
206  * 
207  * 270   5/23/98 2:41p Mike
208  * Make Easy the default skill level and prevent old pilot's skill level
209  * from carrying into new pilot.
210  *
211 */
212
213 #include <errno.h>
214 #include "managepilot.h"
215 #include "2d.h"
216 #include "freespace.h"
217 #include "hudsquadmsg.h"
218 #include "sound.h"
219 #include "multi.h"
220 #include "eventmusic.h"
221 #include "audiostr.h"
222 #include "osregistry.h"
223 #include "font.h"
224 #include "playermenu.h"
225 #include "missionshipchoice.h"
226 #include "hudconfig.h"
227 #include "popup.h"
228 #include "redalert.h"
229 #include "techmenu.h"
230 #include "joy.h"
231 #include "mouse.h"
232 #include "cutscenes.h"
233 #include "bmpman.h"
234
235 // update this when altering data that is read/written to .PLR file
236 #ifndef MAKE_FS1
237 #define CURRENT_PLAYER_FILE_VERSION                             140
238 #else
239 // 141: add more FS1 detail settings
240 // 142: HUD config settings (brightness)
241 #define CURRENT_PLAYER_FILE_VERSION                             142
242 #define PREVIOUS_PLAYER_FILE_VERSION                    140
243 #endif
244 #define FS2_DEMO_PLAYER_FILE_VERSION                            135
245 #ifndef MAKE_FS1
246 #define LOWEST_COMPATIBLE_PLAYER_FILE_VERSION   CURRENT_PLAYER_FILE_VERSION                     // demo plr files should work in final
247 #else
248 #define LOWEST_COMPATIBLE_PLAYER_FILE_VERSION   PREVIOUS_PLAYER_FILE_VERSION
249 #endif
250
251 // keep track of pilot file changes here 
252 // version 2    : Added squad logo filename
253 // version 3    : Changed size of scoring struct. use ushort instead of ints for storing alltime kills by ship type
254 // version 4/5 : Added squadron name field
255 // version 6    : changed max length on a multiplayer options field
256 // version 130  : changed size of hud config struct
257 // version 133 : misc changes. new hud gauge
258 // version 134 : added HUD contrast toggle key
259 // version 135 : added tips flag  (THIS IS THE DEMO VERSION - RETAIN COMPATIBILITY FROM HERE ON OUT)
260 // version 136 : added intelligence flags to tech room visibility data
261 // version 137 : 2 new HUD gauges. 
262 // version 138  : new multiplayer config
263 // version 139 : # medals increased - added compatibility with old plr file versions
264 // version 140 : ships table reordered. clear out old pilot files
265 // search for PLAYER INIT for new pilot initialization stuff. I _think_ its in the right spot for now
266 #define PLR_FILE_ID     'FPSF'  // unique signiture to identify a .PLR file (FreeSpace Player File)  // FPSF appears as FSPF in file.
267
268 // Current content of a .PLR file
269 //
270
271 // Global variables
272 int Player_sel_mode;
273
274 // pilot pic image list stuff ( call pilot_load_pic_list() to make these valid )
275 char Pilot_images_arr[MAX_PILOT_IMAGES][MAX_FILENAME_LEN];
276 char *Pilot_image_names[MAX_PILOT_IMAGES];
277 int Num_pilot_images = 0;
278
279 // squad logo list stuff (call pilot_load_squad_pic_list() to make these valid )
280 char Pilot_squad_images_arr[MAX_PILOT_IMAGES][MAX_FILENAME_LEN];
281 char *Pilot_squad_image_names[MAX_PILOT_IMAGES];
282 int Num_pilot_squad_images = 0;
283
284 static uint Player_file_version;
285
286 // forward declarations
287 void read_stats_block(CFILE *file, int Player_file_version, scoring_struct *stats);
288 void write_stats_block(CFILE *file, scoring_struct *stats);
289 void read_multiplayer_options(player *p,CFILE *file);
290 void write_multiplayer_options(player *p,CFILE *file);
291
292 // internal function to delete a player file.  Called after a pilot is obsoleted, and when a pilot is deleted
293 // used in barracks and player_select
294 void delete_pilot_file( char *pilot_name, int single )
295 {
296         char filename[MAX_FILENAME_LEN];
297         char basename[MAX_FILENAME_LEN];
298
299         // get the player file.
300
301         _splitpath(pilot_name, NULL, NULL, basename, NULL);
302
303         strcpy( filename, basename );
304         strcat( filename, NOX(".plr") );
305         if (Player_sel_mode == PLAYER_SELECT_MODE_SINGLE){
306                 cf_delete(filename, CF_TYPE_SINGLE_PLAYERS);
307         } else {
308                 cf_delete(filename, CF_TYPE_MULTI_PLAYERS);
309         }
310
311         // we must try and delete the campaign save files for a pilot as well.
312         mission_campaign_delete_all_savefiles( basename, !single );
313 }
314
315 // check if a pilot file is valid or not (i.e. is usable, not out of date, etc)
316 // used in barracks and player_select
317 int verify_pilot_file(char *filename, int single, int *rank)
318 {
319         CFILE   *file;
320         uint id, file_version;
321         int type;
322
323         filename = cf_add_ext(filename, NOX(".plr"));
324         
325         if (single){
326                 file = cfopen(filename, "rb", CFILE_NORMAL, CF_TYPE_SINGLE_PLAYERS);
327         } else {
328                 file = cfopen(filename, "rb", CFILE_NORMAL, CF_TYPE_MULTI_PLAYERS);
329         }
330
331         if (!file){
332                 return -1;
333         }
334
335         id = cfread_uint(file);
336         if (id != PLR_FILE_ID) {
337                 nprintf(("Warning", "Player file has invalid signature\n"));
338                 cfclose(file);
339                 delete_pilot_file( filename, single );
340                 return -1;
341         }
342
343         // check for compatibility here
344         file_version = cfread_uint(file);
345 /*      if (file_version < INITIAL_RELEASE_FILE_VERSION) { */
346 //      if (file_version != CURRENT_PLAYER_FILE_VERSION) {
347         if (file_version < LOWEST_COMPATIBLE_PLAYER_FILE_VERSION) {
348                 nprintf(("Warning", "WARNING => Player file is outdated and not compatible...\n"));
349                 cfclose(file);
350                 delete_pilot_file( filename, single );
351                 return -1;
352         }
353
354         type = !cfread_ubyte(file);
355         if (rank){
356                 *rank = 0;      
357         }
358                 
359         if (rank){
360                 *rank = cfread_int(file);
361         } else {
362                 cfread_int(file);
363         }
364
365         cfclose(file);
366         if (type != single){
367                 return -1;
368         }
369
370         return 0;
371 }
372
373 void pilot_write_techroom_data(CFILE *file)
374 {
375         int idx;                
376         ubyte out;
377
378         // write the ship and weapon count
379         cfwrite_int(Num_ship_types, file);
380         cfwrite_int(Num_weapon_types, file);
381         cfwrite_int(Intel_info_size, file);
382
383         // write all ship flags out
384         for (idx=0; idx<Num_ship_types; idx++) {
385                 out = (Ship_info[idx].flags & SIF_IN_TECH_DATABASE) ? (ubyte)1 : (ubyte)0;              
386                 cfwrite_ubyte(out, file);                               
387         }
388
389         // write all weapon types out
390         for (idx=0; idx<Num_weapon_types; idx++) {
391                 out = (Weapon_info[idx].wi_flags & WIF_IN_TECH_DATABASE) ? (ubyte)1 : (ubyte)0;
392                 cfwrite_ubyte(out, file);
393         }       
394
395         // write all intel entry flags out
396         for (idx=0; idx<Intel_info_size; idx++) {
397                 cfwrite_ubyte((ubyte)Intel_info[idx].in_tech_db, file);
398         }
399 }
400
401 void pilot_read_techroom_data(CFILE *file)
402 {
403         int idx;
404         int ship_count, weapon_count, intel_count;
405         ubyte in;
406
407         // read in ship and weapon counts
408         ship_count = cfread_int(file);
409         weapon_count = cfread_int(file);
410         Assert(ship_count <= MAX_SHIP_TYPES);
411         Assert(weapon_count <= MAX_WEAPON_TYPES);
412
413         // maintain compatibility w/ demo version
414         if (Player_file_version < 136) {
415                 // skip over all this data, because the lack of tech room in the demo
416                 // left this all hosed in the demo .plr files
417                 // this will all get initialized as if this fella was a new pilot
418                 for (idx=0; idx<ship_count+weapon_count; idx++) {
419                         in = cfread_ubyte(file);
420                 }
421
422         } else {
423
424                 intel_count = cfread_int(file);
425                 Assert(intel_count <= MAX_INTEL_ENTRIES);
426
427                 // read all ships in
428                 for (idx=0; idx<ship_count; idx++) {
429                         in = cfread_ubyte(file);
430                         if (in) {
431                                 Ship_info[idx].flags |= SIF_IN_TECH_DATABASE | SIF_IN_TECH_DATABASE_M;
432                         } else {
433                                 Ship_info[idx].flags &= ~SIF_IN_TECH_DATABASE;
434                         }
435                 }
436
437                 // read all weapons in
438                 for (idx=0; idx<weapon_count; idx++) {
439                         in = cfread_ubyte(file);
440                         if (in) {
441                                 Weapon_info[idx].wi_flags |= WIF_IN_TECH_DATABASE;
442                         } else {
443                                 Weapon_info[idx].wi_flags &= ~WIF_IN_TECH_DATABASE;
444                         }
445                 }
446
447                 // read all intel entries in
448                 for (idx=0; idx<intel_count; idx++) {
449                         in = cfread_ubyte(file);
450                         if (in) {
451                                 Intel_info[idx].in_tech_db = 1;
452                         } else {
453                                 Intel_info[idx].in_tech_db = 0;
454                         }
455                 }
456         }
457 }
458
459 // write out the player ship selection
460 void pilot_write_loadout(CFILE *file)
461 {
462         int i, j;
463         wss_unit *slot; 
464
465         cfwrite_string_len(Player_loadout.filename, file);
466         cfwrite_string_len(Player_loadout.last_modified, file);
467
468         // write ship and weapon counts
469         cfwrite_int(Num_ship_types, file);
470         cfwrite_int(Num_weapon_types, file);
471
472         // write ship pool
473         for ( i = 0; i < Num_ship_types; i++ ) {
474                 cfwrite_int(Player_loadout.ship_pool[i], file);
475         }
476
477         // write weapons pool
478         for ( i = 0; i < Num_weapon_types; i++ ) {
479                 cfwrite_int(Player_loadout.weapon_pool[i], file);
480         }
481
482         // write ship loadouts
483         for ( i = 0; i < MAX_WSS_SLOTS; i++ ) {
484                 slot = &Player_loadout.unit_data[i];
485                 cfwrite_int(slot->ship_class, file);
486                 for ( j = 0; j < MAX_WL_WEAPONS; j++ ) {
487                         cfwrite_int(slot->wep[j], file);
488                         cfwrite_int(slot->wep_count[j], file);
489                 }
490         }
491 }
492
493 // read in the ship selection for the pilot
494 void pilot_read_loadout(CFILE *file)
495 {
496         int i, j;
497         wss_unit *slot;
498         int ship_count, weapon_count;
499
500         memset(Player_loadout.filename, 0, MAX_FILENAME_LEN);
501         cfread_string_len(Player_loadout.filename, MAX_FILENAME_LEN, file);
502
503         memset(Player_loadout.last_modified, 0, DATE_TIME_LENGTH);      
504         cfread_string_len(Player_loadout.last_modified, DATE_TIME_LENGTH, file);        
505
506         // read in ship and weapon counts
507         ship_count = cfread_int(file);
508         weapon_count = cfread_int(file);
509         Assert(ship_count <= MAX_SHIP_TYPES);
510         Assert(weapon_count <= MAX_WEAPON_TYPES);
511
512         // read in ship pool
513         for ( i = 0; i < ship_count; i++ ) {
514                 Player_loadout.ship_pool[i] = cfread_int(file);
515         }
516
517         // read in weapons pool
518         for ( i = 0; i < weapon_count; i++ ) {
519                 Player_loadout.weapon_pool[i] = cfread_int(file);
520         }
521
522         // read in loadout info
523         for ( i = 0; i < MAX_WSS_SLOTS; i++ ) {
524                 slot = &Player_loadout.unit_data[i];
525                 slot->ship_class = cfread_int(file);
526                 for ( j = 0; j < MAX_WL_WEAPONS; j++ ) {
527                         slot->wep[j] = cfread_int(file);
528                         slot->wep_count[j] = cfread_int(file);
529                 }
530         }
531 }
532
533 // read_pilot_file()
534 //
535 // returns 0 - file read in correctly
536 //        -1 - .PLR file doesn't exist or file not compatible
537 //        >0 - errno from fopen error
538 // if single == 1, look for players in the single players directory, otherwise look in the 
539 // multiplayers directory
540 int read_pilot_file(char *callsign, int single, player *p)
541 {
542         ubyte num_ctrls;
543         ubyte is_multi = 0;
544         char filename[MAX_FILENAME_LEN], ship_name[NAME_LENGTH];
545         CFILE   *file;
546         uint id;
547         int idx;
548         int i, key_value;
549
550         if (!p) {
551                 Assert((Player_num >= 0) && (Player_num < MAX_PLAYERS));
552                 p = &Players[Player_num];
553         }
554
555         //sprintf(filename, "%-.8s.plr",Players[Player_num].callsign);
556         Assert(strlen(callsign) < MAX_FILENAME_LEN - 4);  // ensure we won't overrun the buffer
557         strcpy( filename, callsign );
558         strcat( filename, NOX(".plr") );
559
560         // if we're a standalone server in multiplayer, just fill in some bogus values since we don't have a pilot file
561         if ((Game_mode & GM_MULTIPLAYER) && (Game_mode & GM_STANDALONE_SERVER)) {
562                 memset(Player, 0, sizeof(player));
563                 strcpy(Player->callsign, NOX("Standalone"));
564                 strcpy(Player->short_callsign, NOX("Standalone"));
565                 return 0;
566         }
567         
568         // see comments at the beginning of function
569         if (single) {
570                 file = cfopen(filename, "rb", CFILE_NORMAL, CF_TYPE_SINGLE_PLAYERS);
571         } else {
572                 file = cfopen(filename, "rb", CFILE_NORMAL, CF_TYPE_MULTI_PLAYERS);
573         }
574
575         if (!file) {
576                 return errno;
577         }
578
579         id = cfread_uint(file);
580         if (id != PLR_FILE_ID) {
581                 Warning(LOCATION, "Player file has invalid signature");
582                 cfclose(file);
583                 return -1;
584         }
585
586         // check for compatibility here
587         Player_file_version = cfread_uint(file);
588         cf_set_version( file, Player_file_version );
589
590         if (Player_file_version < LOWEST_COMPATIBLE_PLAYER_FILE_VERSION) {
591                 nprintf(("Warning", "WARNING => Player file is outdated and not compatible...\n"));
592                 cfclose(file);
593                 delete_pilot_file( filename, single );
594                 return -1;
595         }
596
597         // read in whether he's a multiplayer or not
598         is_multi = cfread_ubyte(file);
599         if (is_multi){
600                 p->flags |= PLAYER_FLAGS_IS_MULTI;
601         } else {
602                 p->flags &= ~PLAYER_FLAGS_IS_MULTI;     // this takes care of unsetting any leftover bits from a (possibly) previously selected pilot
603         }
604
605         // read in rank.
606         cfread_int(file);  
607
608         // get player location
609         p->on_bastion = cfread_ubyte(file);
610
611         // tips?
612         p->tips = cfread_int(file);
613
614         // write out the image file name
615         cfread_string_len(p->image_filename, MAX_FILENAME_LEN - 1, file);
616
617         // write out the image file name
618         p->insignia_texture = -1;
619         cfread_string_len(p->squad_name, NAME_LENGTH, file);
620         cfread_string_len(p->squad_filename, MAX_FILENAME_LEN - 1, file);
621         player_set_squad_bitmap(p, p->squad_filename);
622
623         // deal with campaign stuff.  The way we store the information in the file is to first store the
624         // name of the current campaign that the player is playing.  Next we store the info regarding the campaigns
625         // that the player is currently playing
626         memset(p->current_campaign, 0, MAX_FILENAME_LEN);
627         cfread_string_len(p->current_campaign, MAX_FILENAME_LEN, file);
628
629         // read in the ship name for last ship flown by the player
630         memset(ship_name, 0, NAME_LENGTH);
631         cfread_string_len(ship_name, NAME_LENGTH, file);
632         p->last_ship_flown_si_index = ship_info_lookup(ship_name);
633         if ( p->last_ship_flown_si_index < 0 ) {
634                 nprintf(("Warning","WARNING => Ship class %s not located in Ship_info[] in player file\n",ship_name));
635                 p->last_ship_flown_si_index = ship_info_lookup(default_player_ship);
636         }
637
638         // set all the entries in the control config arrays to -1 (undefined)
639         control_config_clear();
640
641         // ---------------------------------------------
642         // read in the keyboard/joystick mapping
643         // ---------------------------------------------
644         num_ctrls = cfread_ubyte(file);
645         for (i=0; i<num_ctrls; i++) {
646                 key_value = cfread_short(file);
647                 // NOTE: next two lines are only here for transitioning from 255 to -1 as undefined key items
648                 if (key_value == 255)
649                         key_value = -1;
650
651                 Control_config[i].key_id = (short) key_value;
652
653                 key_value = cfread_short(file);
654                 // NOTE: next two lines are only here for transitioning from 255 to -1 as undefined key items
655                 if (key_value == 255)
656                         key_value = -1;
657
658                 Control_config[i].joy_id = (short) key_value;
659         }
660         
661         HUD_config.show_flags = cfread_int(file);       
662         HUD_config.show_flags2 = cfread_int(file);
663
664         HUD_config.popup_flags = cfread_int(file);
665         HUD_config.popup_flags2 = cfread_int(file);
666
667         HUD_config.num_msg_window_lines = cfread_ubyte(file);                   
668         HUD_config.rp_flags = cfread_int(file);
669         HUD_config.rp_dist =    cfread_int(file);
670 #ifdef MAKE_FS1
671         if(Player_file_version >= 142){
672                 HUD_config.main_color = cfread_int(file);
673                 HUD_color_alpha = cfread_int(file);
674         }
675         if ( HUD_color_alpha < HUD_COLOR_ALPHA_USER_MIN ) {
676                 HUD_color_alpha = HUD_COLOR_ALPHA_DEFAULT;
677         }
678         hud_config_record_color(HUD_config.main_color);
679 #endif
680
681         // added 2 gauges with version 137
682         if(Player_file_version < 137){
683                 for(idx=0; idx<NUM_HUD_GAUGES-2; idx++){
684                         cfread(&HUD_config.clr[idx], sizeof(color), 1, file);
685                 }
686
687                 // set the 2 new gauges to be radar color
688                 memcpy(&HUD_config.clr[NUM_HUD_GAUGES-2], &HUD_config.clr[HUD_RADAR], sizeof(color));
689                 memcpy(&HUD_config.clr[NUM_HUD_GAUGES-1], &HUD_config.clr[HUD_RADAR], sizeof(color));
690         } else {
691                 for(idx=0; idx<NUM_HUD_GAUGES; idx++){
692                         cfread(&HUD_config.clr[idx], sizeof(color), 1, file);
693                 }
694         }
695
696         // read in the cutscenes which have been viewed
697         Cutscenes_viewable = cfread_int(file);
698
699         Master_sound_volume = cfread_float(file);
700         Master_event_music_volume = cfread_float(file);
701         Master_voice_volume = cfread_float(file);
702
703         audiostream_set_volume_all(Master_voice_volume, ASF_VOICE);
704         audiostream_set_volume_all(Master_event_music_volume, ASF_EVENTMUSIC);
705
706         if ( Master_event_music_volume > 0.0f ) {
707                 Event_music_enabled = 1;
708         } else {
709                 Event_music_enabled = 0;
710         }
711
712 #ifdef MAKE_FS1
713         // add in extra detail settings
714         if(Player_file_version < 141){
715                 cfread( &Detail, sizeof(detail_levels) - sizeof(Detail.engine_glows), 1, file );
716         } else {
717                 cfread( &Detail, sizeof(detail_levels), 1, file );
718         }
719 #else
720         cfread( &Detail, sizeof(detail_levels), 1, file );
721 #endif
722
723         // restore list of most recently played missions
724         Num_recent_missions = cfread_int( file );
725         Assert(Num_recent_missions <= MAX_RECENT_MISSIONS);
726         for ( i = 0; i < Num_recent_missions; i++ ) {
727                 char *p;
728
729                 cfread_string_len( Recent_missions[i], MAX_FILENAME_LEN, file);
730                 // Remove the extension
731                 p = strchr(Recent_missions[i], '.');
732                 if (p)
733                         *p = 0;
734         }
735         
736         // use this block of stats from now on
737         read_stats_block(file, Player_file_version, &p->stats); 
738
739    Game_skill_level = cfread_int(file);
740
741         for (i=0; i<NUM_JOY_AXIS_ACTIONS; i++) {
742                 Axis_map_to[i] = cfread_int(file);
743                 Invert_axis[i] = cfread_int(file);
744         }
745
746         // restore some player flags
747         Player[Player_num].save_flags = cfread_int(file);
748
749         // restore the most recent ship selection       
750         pilot_read_loadout(file);       
751
752         // read in multiplayer options
753         read_multiplayer_options(p,file);
754
755         p->readyroom_listing_mode = cfread_int(file);
756         Briefing_voice_enabled = cfread_int(file);
757
758         // restore the default netgame protocol mode
759         int protocol_temp = cfread_int(file);
760         switch(protocol_temp){
761         // plain TCP
762         case NET_VMT:   
763         case NET_TCP:
764                 Multi_options_g.protocol = NET_TCP;
765                 break;
766         // IPX
767         case NET_IPX:           
768                 Multi_options_g.protocol = NET_IPX;
769                 break;                  
770         }       
771
772         // restore wingman status used by red alert missions
773         red_alert_read_wingman_status(file, Player_file_version);
774
775         // read techroom data
776         pilot_read_techroom_data(file);
777
778         // restore auto-advance pref
779         Player->auto_advance = cfread_int(file);
780
781         Use_mouse_to_fly = cfread_int(file);
782         Mouse_sensitivity = cfread_int(file);
783         Joy_sensitivity = cfread_int(file);
784         Dead_zone_size = cfread_int(file);
785
786         if (cfclose(file))
787                 return errno;
788
789         // restore the callsign into the Player structure
790         strcpy(p->callsign, callsign);
791
792         // restore the truncated callsign into Player structure
793         pilot_set_short_callsign(p, SHORT_CALLSIGN_PIXEL_W);
794         
795         // when we store the LastPlayer key, we have to mark it as being single or multiplayer, so we know where to look for him
796         // (since we could have a single and a multiplayer pilot with the same callsign)
797         // 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
798         char cat[35];
799
800         strcpy(cat, p->callsign);
801         if (is_multi)
802                 strcat(cat, NOX("M"));
803         else
804                 strcat(cat, NOX("S"));
805
806         os_config_write_string( NULL, "LastPlayer", cat );
807 /*
808         // if he's not a multiplayer pilot, then load in the campaign file at this point!
809         if (!is_multi) {
810                 if (mission_campaign_load_by_name(campaign_fname)) {
811                         strcpy(campaign_fname, BUILTIN_CAMPAIGN);
812                         if (mission_campaign_load_by_name(campaign_fname))
813                                 Assert(0);
814                 }
815         }
816         //Campaign.current_mission = mission_num;*/
817
818         hud_squadmsg_save_keys();                       // when new pilot read in, must save info for squadmate messaging
819
820         return 0;
821 }
822
823 void read_stats_block(CFILE *file, int Player_file_version, scoring_struct *stats)
824 {
825         int i, total;
826    
827         init_scoring_element(stats);
828         stats->score = cfread_int(file);
829         stats->rank = cfread_int(file);
830         stats->assists = cfread_int(file);
831
832         if (Player_file_version < 139) {
833                 // support for FS2_DEMO pilots that still have FS1 medal info in the .plr files
834                 for (i=0; i < NUM_MEDALS_FS1; i++) {
835                         total = cfread_int(file);                       // dummy read
836                 }
837         } else {
838                 // read the usual way
839                 for (i=0; i < NUM_MEDALS; i++) {
840                         stats->medals[i] = cfread_int(file);
841                 }
842         }
843
844         total = cfread_int(file);
845         if (total > MAX_SHIP_TYPES){
846                 Warning(LOCATION, "Some ship kill information will be lost due to MAX_SHIP_TYPES decrease");
847         }
848
849         for (i=0; i<total && i<MAX_SHIP_TYPES; i++){
850                 stats->kills[i] = cfread_ushort(file);
851         }
852
853         stats->kill_count = cfread_int(file);
854         stats->kill_count_ok = cfread_int(file);
855         
856         stats->p_shots_fired = cfread_uint(file);
857         stats->s_shots_fired = cfread_uint(file);
858         stats->p_shots_hit = cfread_uint(file);
859         stats->s_shots_hit = cfread_uint(file);
860         
861    stats->p_bonehead_hits = cfread_uint(file);
862         stats->s_bonehead_hits = cfread_uint(file);
863         stats->bonehead_kills = cfread_uint(file);
864 }
865
866 // Will write the pilot file in the most current format
867 //
868 // if single == 1, save into the single players directory, else save into the multiplayers directory
869 int write_pilot_file_core(player *p)
870 {
871         char filename[MAX_FILENAME_LEN + 1];
872    int i, si_index, idx;
873         ubyte is_multi;
874         CFILE *file;
875
876         // never save a pilot file for the standalone server in multiplayer
877         if ((Game_mode & GM_MULTIPLAYER) && (Game_mode & GM_STANDALONE_SERVER)) {
878                 return 0;
879         }
880
881         if (!p) {
882                 Assert((Player_num >= 0) && (Player_num < MAX_PLAYERS));
883                 p = &Players[Player_num];
884         }
885
886         i = strlen(p->callsign);
887         if (i == 0)
888                 return 0;       //      This means there is no player, probably meaning he was deleted and game exited from same screen.
889
890         Assert((i > 0) && (i <= MAX_FILENAME_LEN - 4));  // ensure we won't overrun the buffer
891         strcpy( filename, p->callsign);
892         strcat( filename, NOX(".plr") );
893
894         // determine if this pilot is a multiplayer pilot or not
895         if (p->flags & PLAYER_FLAGS_IS_MULTI){
896                 is_multi = 1;
897         } else {
898                 is_multi = 0;
899         }
900
901         // see above
902         if ( !is_multi ){
903                 file = cfopen(filename, "wb", CFILE_NORMAL, CF_TYPE_SINGLE_PLAYERS);
904         } else {
905                 file = cfopen(filename, "wb", CFILE_NORMAL, CF_TYPE_MULTI_PLAYERS);
906         }
907
908         if (!file){
909                 return errno;
910         }
911
912         // Write out player's info
913         cfwrite_uint(PLR_FILE_ID, file);
914         cfwrite_uint(CURRENT_PLAYER_FILE_VERSION, file);
915
916         cfwrite_ubyte(is_multi, file);
917         cfwrite_int(p->stats.rank, file);
918         cfwrite_ubyte((ubyte) p->on_bastion, file);
919
920         cfwrite_int(p->tips, file);
921
922         // write out the image file name
923         cfwrite_string_len(p->image_filename, file);
924
925         // write out the image file name
926         cfwrite_string_len(p->squad_name, file);
927         cfwrite_string_len(p->squad_filename, file);
928
929         // write out the name of the player's active campaign.
930         cfwrite_string_len(p->current_campaign, file);  
931
932         // write the ship name for last ship flown by the player
933         si_index = p->last_ship_flown_si_index;
934         if((si_index < 0) || (si_index >= Num_ship_types)){
935                 si_index = 0;
936         }
937
938         cfwrite_string_len(Ship_info[si_index].name, file);
939
940         // ---------------------------------------------
941         // write the keyboard/joystick configuration
942         // ---------------------------------------------
943         cfwrite_ubyte( CCFG_MAX, file );
944         for (i=0; i<CCFG_MAX; i++) {
945                 cfwrite_short( Control_config[i].key_id, file );
946                 cfwrite_short( Control_config[i].joy_id, file );
947         }               
948
949         // if this hud is an observer, the player ended the last mission as an observer, so we should
950         // restore his "real" ship HUD.
951         HUD_CONFIG_TYPE hc_temp;
952         hc_temp.show_flags = 0;
953         int stored_observer = 0;
954         if ( HUD_config.is_observer ){
955                 // if we're in mission, copy the HUD we're currently using
956                 if(Game_mode & GM_IN_MISSION){                  
957                         memcpy(&hc_temp,&HUD_config,sizeof(HUD_CONFIG_TYPE));
958                         stored_observer = 1;
959                 }
960
961                 hud_config_restore();                           
962         }
963
964         // write the hud configuration
965         cfwrite_int(HUD_config.show_flags, file);       
966         cfwrite_int(HUD_config.show_flags2, file);      
967         cfwrite_int(HUD_config.popup_flags, file);      
968         cfwrite_int(HUD_config.popup_flags2, file);     
969         cfwrite_ubyte( (ubyte) HUD_config.num_msg_window_lines, file );
970         cfwrite_int( HUD_config.rp_flags, file );
971         cfwrite_int( HUD_config.rp_dist, file );
972 #ifdef MAKE_FS1
973         cfwrite_int( HUD_config.main_color, file );
974         cfwrite_int( HUD_color_alpha, file );
975 #endif
976         for(idx=0; idx<NUM_HUD_GAUGES; idx++){
977                 cfwrite(&HUD_config.clr[idx], sizeof(color), 1, file);
978         }
979
980         // restore the HUD we backed up
981         if( (Game_mode & GM_IN_MISSION) && stored_observer ){           
982                 memcpy(&HUD_config,&hc_temp,sizeof(HUD_CONFIG_TYPE));
983         }
984
985         // write the cutscenes which have been viewed
986         cfwrite_int(Cutscenes_viewable, file);
987
988         // store the digital sound fx volume, and music volume
989         cfwrite_float(Master_sound_volume, file);
990         cfwrite_float(Master_event_music_volume, file);
991         cfwrite_float(Master_voice_volume, file);
992
993
994         cfwrite( &Detail, sizeof(detail_levels), 1, file );
995
996         // store list of most recently played missions
997         cfwrite_int(Num_recent_missions, file);
998         for (i=0; i<Num_recent_missions; i++) {
999                 cfwrite_string_len(Recent_missions[i], file);
1000         }
1001
1002         // write the player stats
1003         write_stats_block(file, &p->stats);     
1004    cfwrite_int(Game_skill_level, file);
1005
1006         for (i=0; i<NUM_JOY_AXIS_ACTIONS; i++) {
1007                 cfwrite_int(Axis_map_to[i], file);
1008                 cfwrite_int(Invert_axis[i], file);
1009         }
1010
1011         // store some player flags
1012    cfwrite_int(Player->save_flags, file);
1013
1014         // store ship selection for most recent mission
1015         pilot_write_loadout(file);
1016
1017         // read in multiplayer options  
1018         write_multiplayer_options(p, file);
1019
1020         cfwrite_int(p->readyroom_listing_mode, file);
1021    cfwrite_int(Briefing_voice_enabled, file);
1022
1023         // store the default netgame protocol mode for this pilot
1024         if (Multi_options_g.protocol == NET_TCP) {              
1025                 cfwrite_int(NET_TCP, file);             
1026         } else {
1027                 cfwrite_int(NET_IPX, file);
1028         }       
1029
1030         red_alert_write_wingman_status(file);
1031         pilot_write_techroom_data(file);
1032
1033         // store auto-advance pref
1034    cfwrite_int(Player->auto_advance, file);
1035
1036         cfwrite_int(Use_mouse_to_fly, file);
1037         cfwrite_int(Mouse_sensitivity, file);
1038         cfwrite_int(Joy_sensitivity, file);
1039         cfwrite_int(Dead_zone_size, file);
1040
1041         if (!cfclose(file))
1042                 return 0;
1043
1044         return errno;
1045 }
1046
1047 int write_pilot_file(player *the_player)
1048 {
1049         int pilot_write_rval;
1050         do {
1051                 // write_pilot_file_core returns 0 if ok, non-zero for error
1052                 pilot_write_rval = write_pilot_file_core(the_player);
1053
1054                 // check with user if write not successful
1055                 if (pilot_write_rval) {
1056                         int popup_rval = popup(PF_TITLE_RED | PF_TITLE_BIG, 3, XSTR( "&Retry", 41), XSTR( "&Ignore", 42), XSTR( "&Quit Game", 43),
1057                                 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) );
1058
1059                         // quit game popup return value (2)
1060                         if (popup_rval == 2) {
1061                                 exit(1);
1062                         }
1063
1064                         // _ignore_ popup return value (1) - don't save the file
1065                         if (popup_rval) {
1066                                 return pilot_write_rval;
1067                         }
1068
1069                         // retry popup return value (0) - try again 
1070                 }
1071
1072         } while (pilot_write_rval);
1073
1074         // write successful
1075         return 0;
1076 }
1077
1078 void write_stats_block(CFILE *file,scoring_struct *stats)
1079 {
1080         int i;
1081         int total;
1082
1083         cfwrite_int(stats->score, file);
1084         cfwrite_int(stats->rank, file);
1085         cfwrite_int(stats->assists, file);
1086         for (i=0; i<NUM_MEDALS; i++){
1087                 cfwrite_int(stats->medals[i], file);
1088         }
1089
1090         total = MAX_SHIP_TYPES;
1091         while (total && !stats->kills[total - 1]){  // find last used element
1092                 total--;
1093         }
1094
1095         cfwrite_int(total, file);
1096         for (i=0; i<total; i++){
1097                 cfwrite_ushort(stats->kills[i], file);
1098         }
1099
1100         cfwrite_int(stats->kill_count,file);
1101         cfwrite_int(stats->kill_count_ok,file);
1102
1103    cfwrite_uint(stats->p_shots_fired,file);
1104         cfwrite_uint(stats->s_shots_fired,file);
1105         cfwrite_uint(stats->p_shots_hit,file);
1106         cfwrite_uint(stats->s_shots_hit,file);
1107         cfwrite_uint(stats->p_bonehead_hits,file);
1108         cfwrite_uint(stats->s_bonehead_hits,file);
1109         cfwrite_uint(stats->bonehead_kills,file);
1110 }
1111
1112 // write multiplayer information
1113 void write_multiplayer_options(player *p,CFILE *file)
1114 {       
1115         // write the netgame options
1116         cfwrite_ubyte(p->m_server_options.squad_set,file);
1117         cfwrite_ubyte(p->m_server_options.endgame_set,file);    
1118         cfwrite_int(p->m_server_options.flags,file);
1119         cfwrite_uint(p->m_server_options.respawn,file);
1120         cfwrite_ubyte(p->m_server_options.max_observers,file);
1121         cfwrite_ubyte(p->m_server_options.skill_level,file);
1122         cfwrite_ubyte(p->m_server_options.voice_qos,file);
1123         cfwrite_int(p->m_server_options.voice_token_wait,file);
1124         cfwrite_int(p->m_server_options.voice_record_time,file);
1125         cfwrite(&p->m_server_options.mission_time_limit,sizeof(fix),1,file);
1126         cfwrite_int(p->m_server_options.kill_limit,file);
1127
1128         // write the local options
1129         cfwrite_int(p->m_local_options.flags,file);
1130         cfwrite_int(p->m_local_options.obj_update_level,file);
1131 }
1132
1133 // read multiplayer options
1134 void read_multiplayer_options(player *p,CFILE *file)
1135 {
1136         // read the netgame options
1137         p->m_server_options.squad_set = cfread_ubyte(file);
1138         p->m_server_options.endgame_set = cfread_ubyte(file);   
1139         p->m_server_options.flags = cfread_int(file);
1140         p->m_server_options.respawn = cfread_uint(file);
1141         p->m_server_options.max_observers = cfread_ubyte(file);
1142         p->m_server_options.skill_level = cfread_ubyte(file);
1143         p->m_server_options.voice_qos = cfread_ubyte(file);
1144         p->m_server_options.voice_token_wait = cfread_int(file);
1145         p->m_server_options.voice_record_time = cfread_int(file);
1146         cfread(&p->m_server_options.mission_time_limit,sizeof(fix),1,file);
1147         p->m_server_options.kill_limit = cfread_int(file);
1148
1149         // read the local options
1150         p->m_local_options.flags = cfread_int(file);
1151         p->m_local_options.obj_update_level = cfread_int(file);
1152 }
1153
1154 // run thorough an open file (at the beginning) and see if this pilot is a multiplayer pilot
1155 int is_pilot_multi(CFILE *fp)
1156 {
1157         uint id,file_version;
1158         ubyte is_multi;
1159         
1160         id = cfread_uint(fp);
1161         if (id != PLR_FILE_ID) {
1162                 Warning(LOCATION, "Player file has invalid signature");
1163                 cfclose(fp);
1164                 return -1;
1165         }
1166
1167         // check for compatibility here
1168         file_version = cfread_uint(fp);
1169         if (file_version < LOWEST_COMPATIBLE_PLAYER_FILE_VERSION) {
1170                 nprintf(("Warning", "WARNING => Player file is outdated and not compatible...\n"));
1171                 cfclose(fp);
1172                 return -1;
1173         }
1174
1175         // read in whether he's a multiplayer or not
1176         is_multi = cfread_ubyte(fp);
1177         if (is_multi)
1178                 return 1;
1179
1180         return 0;
1181 }
1182
1183 int is_pilot_multi(player *p)
1184 {
1185         return (p->flags & PLAYER_FLAGS_IS_MULTI) ? 1 : 0;
1186 }
1187
1188 // this works on barracks and player_select interface screens
1189 void init_new_pilot(player *p, int reset)
1190 {
1191         int cur_speed;
1192
1193         if (reset) {
1194                 hud_set_default_hud_config(p);          // use a default hud config
1195
1196                 // in the demo, load up the hardcoded hcf file
1197 #ifdef FS2_DEMO
1198                 hud_config_color_load("hud_1.hcf");
1199 #else
1200                 hud_config_color_load("hud_3.hcf");
1201 #endif
1202
1203                 control_config_reset_defaults();                // get a default keyboard config
1204                 player_set_pilot_defaults(p);                   // set up any player struct defaults
1205
1206                 cur_speed = os_config_read_uint(NULL, NOX("ComputerSpeed"), 2 );
1207                 if ( cur_speed < 0 )    {
1208                         cur_speed = 0;
1209                 } else if ( cur_speed >= NUM_DEFAULT_DETAIL_LEVELS )    {
1210                         cur_speed = NUM_DEFAULT_DETAIL_LEVELS-1;
1211                 }       
1212                 // always set to high
1213                 // DKA: 8/4/99 USE speed from registry
1214                 // cur_speed = NUM_DEFAULT_DETAIL_LEVELS-2;
1215
1216 #if NUM_DEFAULT_DETAIL_LEVELS != 4
1217 #error Code in ManagePilot assumes NUM_DEFAULT_DETAIL_LEVELS = 4
1218 #endif
1219
1220                 detail_level_set(cur_speed);
1221
1222                 Game_skill_level = game_get_default_skill_level();
1223
1224                 mprintf(( "Setting detail level to %d because of new pilot\n", cur_speed ));
1225                 Use_mouse_to_fly = 0;
1226                 Mouse_sensitivity = 4;
1227                 Joy_sensitivity = 9;
1228                 Dead_zone_size = 10;
1229         }
1230
1231         // unassigned squadron
1232         strcpy(p->squad_name, XSTR("Unassigned", 1255));
1233         strcpy(p->squad_filename, "");
1234
1235         // set him to be a single player pilot by default (the actual creation routines will change this if necessary)
1236         p->flags &= ~PLAYER_FLAGS_IS_MULTI;
1237
1238         // effectively sets the length return by strlen() to 0  
1239         Campaign.filename[0] = 0;
1240         p->on_bastion = 0;
1241
1242         // pick a random pilot image for this guy
1243         if (reset){
1244                 pilot_set_random_pic(p);
1245                 p->insignia_texture = -1;
1246                 pilot_set_random_squad_pic(p);
1247         }
1248
1249         init_scoring_element(&p->stats);        
1250         
1251         p->stats.score = 0;
1252         p->stats.rank = RANK_ENSIGN;    
1253
1254         p->tips = 1;
1255
1256         Multi_options_g.protocol = NET_TCP;     
1257
1258         // initialize default multiplayer options
1259         multi_options_set_netgame_defaults(&p->m_server_options);
1260         multi_options_set_local_defaults(&p->m_local_options);
1261
1262         Player_loadout.filename[0] = 0;
1263
1264         // reset the cutscenes which can be viewed
1265         if ( reset ){
1266                 Cutscenes_viewable = INTRO_CUTSCENE_FLAG;
1267         }
1268 }
1269
1270 void pilot_set_short_callsign(player *p, int max_width)
1271 {
1272         strcpy(p->short_callsign, p->callsign);
1273         gr_set_font(FONT1);
1274         gr_force_fit_string(p->short_callsign, CALLSIGN_LEN - 1, max_width);
1275         gr_get_string_size( &(p->short_callsign_width), NULL, p->short_callsign );
1276 }
1277
1278 // pick a random image for the passed player
1279 void pilot_set_random_pic(player *p)
1280 {
1281         // if there are no available pilot pics, set the image filename to null
1282         if (Num_pilot_images <= 0) {
1283                 strcpy(p->image_filename, "");
1284         } else {
1285                 // pick a random name from the list
1286                 int random_index = rand() % Num_pilot_images;
1287                 Assert((random_index >= 0) && (random_index < Num_pilot_images));
1288                 strcpy(p->image_filename, Pilot_images_arr[random_index]);
1289         }       
1290 }
1291
1292 // pick a random image for the passed player
1293 void pilot_set_random_squad_pic(player *p)
1294 {       
1295         // if there are no available pilot pics, set the image filename to null
1296         if (Num_pilot_squad_images <= 0) {
1297                 player_set_squad_bitmap(p, "");
1298                 // strcpy(p->squad_filename, "");               
1299         } else {
1300                 // pick a random name from the list
1301                 int random_index = rand() % Num_pilot_squad_images;             
1302                 Assert((random_index >= 0) && (random_index < Num_pilot_squad_images));
1303                 player_set_squad_bitmap(p, Pilot_squad_images_arr[random_index]); 
1304                 // strcpy(p->squad_filename, Pilot_squad_images_arr[random_index]);
1305         }       
1306 }
1307
1308 // format a pilot's callsign into a "personal" form - ie, adding a 's or just an ' as appropriate
1309 void pilot_format_callsign_personal(char *in_callsign,char *out_callsign)
1310 {
1311         // don't do anything if we've got invalid strings
1312         if((in_callsign == NULL) || (out_callsign == NULL)){
1313                 return;
1314         }
1315
1316         // copy the original string
1317         strcpy(out_callsign,in_callsign);
1318
1319         // tack on the appropriate postfix
1320         if(in_callsign[strlen(in_callsign) - 1] == 's'){                
1321                 strcat(out_callsign,XSTR( "\'", 45));
1322         } else {
1323                 strcat(out_callsign,XSTR( "\'s", 46));
1324         }
1325 }
1326
1327 // throw up a popup asking the user to verify the overwrite of an existing pilot name
1328 // 1 == ok to overwrite, 0 == not ok
1329 int pilot_verify_overwrite()
1330 {
1331         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));
1332 }
1333
1334 extern int Skip_packfile_search;  // located in CFileList.cpp
1335
1336
1337 // load up the list of pilot image filenames (do this at game startup as well as barracks startup)
1338 void pilot_load_pic_list()
1339 {
1340         Num_pilot_images = 0;
1341         
1342         // load pilot images from the player images directory
1343         Num_pilot_images = cf_get_file_list_preallocated(MAX_PILOT_IMAGES, Pilot_images_arr, Pilot_image_names, CF_TYPE_PLAYER_IMAGES_MAIN, NOX("*.pcx"));
1344
1345         // sort all filenames
1346         cf_sort_filenames(Num_pilot_images, Pilot_image_names, CF_SORT_NAME);
1347 }
1348
1349 // load up the list of pilot squad filenames
1350 void pilot_load_squad_pic_list()
1351 {
1352         Num_pilot_squad_images = 0;
1353         
1354         // load pilot images from the player images directory
1355         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"));
1356
1357         // sort all filenames
1358         cf_sort_filenames(Num_pilot_squad_images, Pilot_squad_image_names, CF_SORT_NAME);
1359 }
1360
1361 // will attempt to load an insignia bitmap and set it as active for the player
1362 void player_set_squad_bitmap(player *p, char *fname)
1363 {
1364         // sanity check
1365         if(p == NULL){
1366                 return;
1367         }
1368
1369         // if he has another bitmap already - unload it
1370         if(p->insignia_texture >= 0){
1371                 bm_unload(p->insignia_texture);
1372         }
1373
1374         p->insignia_texture = -1;
1375
1376         // try and set the new one
1377         strncpy(p->squad_filename, fname, MAX_FILENAME_LEN);
1378         if(strlen(p->squad_filename) > 0){
1379                 p->insignia_texture = bm_load_duplicate(fname);
1380                 
1381                 // lock is as a transparent texture
1382                 if(p->insignia_texture != -1){
1383                         bm_lock(p->insignia_texture, 16, BMP_TEX_XPARENT);
1384                         bm_unlock(p->insignia_texture);
1385                 }
1386         }
1387
1388         /*
1389         flen = strlen(filename);
1390         elen = strlen(ext);
1391         Assert(flen < MAX_PATH_LEN);
1392         strcpy(path, filename);
1393         if ((flen < 4) || stricmp(path + flen - elen, ext)) {
1394                 Assert(flen + elen < MAX_PATH_LEN);
1395                 strcat(path, ext);
1396         }
1397         */
1398 }
1399
1400 // set squadron
1401 void player_set_squad(player *p, char *squad_name)
1402 {
1403         // sanity check
1404         if(p == NULL){
1405                 return;
1406         }
1407
1408         strncpy(p->squad_name, squad_name, NAME_LENGTH+1);
1409 }
1410
1411 DCF(pilot,"Changes pilot stats. (Like reset campaign)" )
1412 {
1413         if (Dc_command) {
1414                 dc_get_arg(ARG_STRING);
1415                 if (!strcmp(Dc_arg, NOX("reset"))) {
1416                         if (strlen(Campaign.filename)) {
1417                                 mission_campaign_savefile_delete(Campaign.filename);
1418                                 mission_campaign_load(Campaign.filename);
1419                         }
1420
1421                 } else {
1422                         Dc_help = 1;  // print usage, not stats
1423                 }
1424         }
1425
1426         if (Dc_help) {
1427                 dc_printf( "Usage: pilot keyword\nWhere keyword can be in the following forms:\n" );
1428                 dc_printf( "pilot reset                 Resets campaign stats.\n" );
1429                 Dc_status = 0;  // don't print status if help is printed.  Too messy.
1430         }
1431
1432         if (Dc_status) {
1433                 // no stats
1434         }
1435 }
1436