]> icculus.org git repositories - btb/d2x.git/blob - main/playsave.c
took care of switches triggering single-sided walls (don't try to access the nonexist...
[btb/d2x.git] / main / playsave.c
1 /* $Id: playsave.c,v 1.16 2003-11-26 12:26:33 btb Exp $ */
2 /*
3 THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
4 SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
5 END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
6 ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
7 IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
8 SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
9 FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
10 CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
11 AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
12 COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
13 */
14
15 /*
16  *
17  * Functions to load & save player's settings (*.plr file)
18  *
19  * Old Log:
20  * Revision 1.1  1995/12/05  16:05:47  allender
21  * Initial revision
22  *
23  * Revision 1.10  1995/11/03  12:53:24  allender
24  * shareware changes
25  *
26  * Revision 1.9  1995/10/31  10:19:12  allender
27  * shareware stuff
28  *
29  * Revision 1.8  1995/10/23  14:50:11  allender
30  * set control type for new player *before* calling kc_set_controls
31  *
32  * Revision 1.7  1995/10/21  22:25:31  allender
33  * *** empty log message ***
34  *
35  * Revision 1.6  1995/10/17  15:57:42  allender
36  * removed line setting wrong COnfig_control_type
37  *
38  * Revision 1.5  1995/10/17  13:16:44  allender
39  * new controller support
40  *
41  * Revision 1.4  1995/08/24  16:03:38  allender
42  * call joystick code when player file uses joystick
43  *
44  * Revision 1.3  1995/08/03  15:15:39  allender
45  * got player save file working (more to go for shareware)
46  *
47  * Revision 1.2  1995/08/01  13:57:20  allender
48  * macified the player file stuff -- in a seperate folder
49  *
50  * Revision 1.1  1995/05/16  15:30:00  allender
51  * Initial revision
52  *
53  * Revision 2.3  1995/05/26  16:16:23  john
54  * Split SATURN into define's for requiring cd, using cd, etc.
55  * Also started adding all the Rockwell stuff.
56  *
57  * Revision 2.2  1995/03/24  17:48:21  john
58  * Made player files from saturn excrement the highest level for
59  * normal descent levels.
60  *
61  * Revision 2.1  1995/03/21  14:38:49  john
62  * Ifdef'd out the NETWORK code.
63  *
64  * Revision 2.0  1995/02/27  11:27:59  john
65  * New version 2.0, which has no anonymous unions, builds with
66  * Watcom 10.0, and doesn't require parsing BITMAPS.TBL.
67  *
68  * Revision 1.57  1995/02/13  20:34:55  john
69  * Lintized
70  *
71  * Revision 1.56  1995/02/13  13:23:24  john
72  * Fixed bug with new player joystick selection.
73  *
74  * Revision 1.55  1995/02/13  12:01:19  john
75  * Fixed bug with joystick throttle still asking for
76  * calibration with new pilots.
77  *
78  * Revision 1.54  1995/02/13  10:29:12  john
79  * Fixed bug with creating new player not resetting everything to default.
80  *
81  * Revision 1.53  1995/02/03  10:58:46  john
82  * Added code to save shareware style saved games into new format...
83  * Also, made new player file format not have the saved game array in it.
84  *
85  * Revision 1.52  1995/02/02  21:09:28  matt
86  * Let player start of level 8 if he made it to level 7 in the shareware
87  *
88  * Revision 1.51  1995/02/02  18:50:14  john
89  * Added warning for FCS when new pilot chooses.
90  *
91  * Revision 1.50  1995/02/02  11:21:34  john
92  * Made joystick calibrate when new user selects.
93  *
94  * Revision 1.49  1995/02/01  18:06:38  rob
95  * Put defaults macros into descent.tex
96  *
97  * Revision 1.48  1995/01/25  14:37:53  john
98  * Made joystick only prompt for calibration once...
99  *
100  * Revision 1.47  1995/01/24  19:37:12  matt
101  * Took out incorrect mprintf
102  *
103  * Revision 1.46  1995/01/22  18:57:22  matt
104  * Made player highest level work with missions
105  *
106  * Revision 1.45  1995/01/21  16:36:05  matt
107  * Made starting level system work for now, pending integration with
108  * mission code.
109  *
110  * Revision 1.44  1995/01/20  22:47:32  matt
111  * Mission system implemented, though imcompletely
112  *
113  * Revision 1.43  1995/01/04  14:58:39  rob
114  * Fixed for shareware build.
115  *
116  * Revision 1.42  1995/01/04  11:36:43  rob
117  * Added compatibility with older shareware pilot files.
118  *
119  * Revision 1.41  1995/01/03  11:01:58  rob
120  * fixed a default macro.
121  *
122  * Revision 1.40  1995/01/03  10:44:06  rob
123  * Added default taunt macros.
124  *
125  * Revision 1.39  1994/12/13  10:01:16  allender
126  * pop up message box when unable to correctly save player file
127  *
128  * Revision 1.38  1994/12/12  11:37:14  matt
129  * Fixed auto leveling defaults & saving
130  *
131  * Revision 1.37  1994/12/12  00:26:59  matt
132  * Added support for no-levelling option
133  *
134  * Revision 1.36  1994/12/10  19:09:54  matt
135  * Added assert for valid player number when loading game
136  *
137  * Revision 1.35  1994/12/08  10:53:07  rob
138  * Fixed a bug in highest_level tracking.
139  *
140  * Revision 1.34  1994/12/08  10:01:36  john
141  * Changed the way the player callsign stuff works.
142  *
143  * Revision 1.33  1994/12/07  18:30:38  rob
144  * Load highest level along with player (used to be only if higher)
145  * Capped at LAST_LEVEL in case a person loads a registered player in shareware.
146  *
147  * Revision 1.32  1994/12/03  16:01:12  matt
148  * When player file has bad version, force player to choose another
149  *
150  * Revision 1.31  1994/12/02  19:54:00  yuan
151  * Localization.
152  *
153  * Revision 1.30  1994/12/02  11:01:36  yuan
154  * Localization.
155  *
156  * Revision 1.29  1994/11/29  03:46:28  john
157  * Added joystick sensitivity; Added sound channels to detail menu.  Removed -maxchannels
158  * command line arg.
159  *
160  * Revision 1.28  1994/11/29  01:10:23  john
161  * Took out code that allowed new players to
162  * configure keyboard.
163  *
164  * Revision 1.27  1994/11/25  22:47:10  matt
165  * Made saved game descriptions longer
166  *
167  * Revision 1.26  1994/11/22  12:10:42  rob
168  * Fixed file handle left open if player file versions don't
169  * match.
170  *
171  * Revision 1.25  1994/11/21  19:35:30  john
172  * Replaced calls to joy_init with if (joy_present)
173  *
174  * Revision 1.24  1994/11/21  17:29:34  matt
175  * Cleaned up sequencing & game saving for secret levels
176  *
177  * Revision 1.23  1994/11/21  11:10:01  john
178  * Fixed bug with read-only .plr file making the config file
179  * not update.
180  *
181  * Revision 1.22  1994/11/20  19:03:08  john
182  * Fixed bug with if not having a joystick, default
183  * player input device is cyberman.
184  *
185  * Revision 1.21  1994/11/17  12:24:07  matt
186  * Made an array the right size, to fix error loading games
187  *
188  * Revision 1.20  1994/11/14  17:52:54  allender
189  * add call to WriteConfigFile when player files gets written
190  *
191  * Revision 1.19  1994/11/14  17:19:23  rob
192  * Removed gamma, joystick calibration, and sound settings from player file.
193  * Added default difficulty and multi macros.
194  *
195  * Revision 1.18  1994/11/07  14:01:23  john
196  * Changed the gamma correction sequencing.
197  *
198  * Revision 1.17  1994/11/05  17:22:49  john
199  * Fixed lots of sequencing problems with newdemo stuff.
200  *
201  * Revision 1.16  1994/11/01  16:40:11  john
202  * Added Gamma correction.
203  *
204  * Revision 1.15  1994/10/24  19:56:50  john
205  * Made the new user setup prompt for config options.
206  *
207  * Revision 1.14  1994/10/24  17:44:21  john
208  * Added stereo channel reversing.
209  *
210  * Revision 1.13  1994/10/24  16:05:12  matt
211  * Improved handling of player names that are the names of DOS devices
212  *
213  * Revision 1.12  1994/10/22  00:08:51  matt
214  * Fixed up problems with bonus & game sequencing
215  * Player doesn't get credit for hostages unless he gets them out alive
216  *
217  * Revision 1.11  1994/10/19  19:59:57  john
218  * Added bonus points at the end of level based on skill level.
219  *
220  * Revision 1.10  1994/10/19  15:14:34  john
221  * Took % hits out of player structure, made %kills work properly.
222  *
223  * Revision 1.9  1994/10/19  12:44:26  john
224  * Added hours field to player structure.
225  *
226  * Revision 1.8  1994/10/17  17:24:34  john
227  * Added starting_level to player struct.
228  *
229  * Revision 1.7  1994/10/17  13:07:15  john
230  * Moved the descent.cfg info into the player config file.
231  *
232  * Revision 1.6  1994/10/09  14:54:31  matt
233  * Made player cockpit state & window size save/restore with saved games & automap
234  *
235  * Revision 1.5  1994/10/08  23:08:09  matt
236  * Added error check & handling for game load/save disk io
237  *
238  * Revision 1.4  1994/10/05  17:40:54  rob
239  * Bumped save_file_version to 5 due to change in player.h
240  *
241  * Revision 1.3  1994/10/03  23:00:54  matt
242  * New file version for shorter callsigns
243  *
244  * Revision 1.2  1994/09/28  17:25:05  matt
245  * Added first draft of game save/load system
246  *
247  * Revision 1.1  1994/09/27  14:39:12  matt
248  * Initial revision
249  *
250  */
251
252 #ifdef HAVE_CONFIG_H
253 #include <conf.h>
254 #endif
255
256 #ifdef WINDOWS
257 #include "desw.h"
258 #include <mmsystem.h>
259 #endif
260
261 #include <stdio.h>
262 #include <string.h>
263 #ifndef _MSC_VER
264 #include <unistd.h>
265 #endif
266 #ifndef _WIN32_WCE
267 #include <errno.h>
268 #endif
269
270 #include "error.h"
271
272 #include "pa_enabl.h"
273 #include "strutil.h"
274 #include "game.h"
275 #include "gameseq.h"
276 #include "player.h"
277 #include "playsave.h"
278 #include "joy.h"
279 #include "kconfig.h"
280 #include "digi.h"
281 #include "newmenu.h"
282 #include "joydefs.h"
283 #include "palette.h"
284 #include "multi.h"
285 #include "menu.h"
286 #include "config.h"
287 #include "text.h"
288 #include "mono.h"
289 #include "state.h"
290 #include "gauges.h"
291 #include "screens.h"
292 #include "powerup.h"
293 #include "makesig.h"
294 #include "byteswap.h"
295 #include "escort.h"
296
297 #define SAVE_FILE_ID                    MAKE_SIG('D','P','L','R')
298
299 #ifdef MACINTOSH
300         #include <Files.h>
301         #include <Errors.h>                     // mac doesn't have "normal" error numbers -- must use mac equivs
302         #ifndef ENOENT
303                 #define ENOENT fnfErr
304         #endif
305         #ifdef POLY_ACC
306                 #include "poly_acc.h"
307         #endif
308         #include "isp.h"
309 #elif defined(_WIN32_WCE)
310 # define errno -1
311 # define ENOENT -1
312 # define strerror(x) "Unknown Error"
313 #endif
314
315 int get_lifetime_checksum (int a,int b);
316
317 typedef struct hli {
318         char    shortname[9];
319         ubyte   level_num;
320 } hli;
321
322 short n_highest_levels;
323
324 hli highest_levels[MAX_MISSIONS];
325
326 #define PLAYER_FILE_VERSION     25                      //increment this every time the player file changes
327
328 //version 5  ->  6: added new highest level information
329 //version 6  ->  7: stripped out the old saved_game array.
330 //version 7  ->  8: added reticle flag, & window size
331 //version 8  ->  9: removed player_struct_version
332 //version 9  -> 10: added default display mode
333 //version 10 -> 11: added all toggles in toggle menu
334 //version 11 -> 12: added weapon ordering
335 //version 12 -> 13: added more keys
336 //version 13 -> 14: took out marker key
337 //version 14 -> 15: added guided in big window
338 //version 15 -> 16: added small windows in cockpit
339 //version 16 -> 17: ??
340 //version 17 -> 18: save guidebot name
341 //version 18 -> 19: added automap-highres flag
342 //version 19 -> 20: added kconfig data for windows joysticks
343 //version 20 -> 21: save seperate config types for DOS & Windows
344 //version 21 -> 22: save lifetime netstats 
345 //version 22 -> 23: ??
346 //version 23 -> 24: add name of joystick for windows version.
347 //version 24 -> 25: add d2x keys array
348
349 #define COMPATIBLE_PLAYER_FILE_VERSION          17
350
351
352 int Default_leveling_on=1;
353 extern ubyte SecondaryOrder[],PrimaryOrder[];
354 extern void InitWeaponOrdering();
355
356 #ifdef MACINTOSH
357 extern ubyte default_firebird_settings[];
358 extern ubyte default_mousestick_settings[];
359 #endif
360
361 int new_player_config()
362 {
363         int nitems;
364         int i,j,control_choice;
365         newmenu_item m[8];
366    int mct=CONTROL_MAX_TYPES;
367  
368    #ifndef WINDOWS
369          mct--;
370         #endif
371
372    InitWeaponOrdering ();               //setup default weapon priorities 
373
374 #if defined(MACINTOSH) && defined(USE_ISP)
375         if (!ISpEnabled())
376         {
377 #endif
378 RetrySelection:
379                 #if !defined(MACINTOSH) && !defined(WINDOWS)
380                 for (i=0; i<mct; i++ )  {
381                         m[i].type = NM_TYPE_MENU; m[i].text = CONTROL_TEXT(i);
382                 }
383                 #elif defined(WINDOWS)
384                         m[0].type = NM_TYPE_MENU; m[0].text = CONTROL_TEXT(0);
385                         m[1].type = NM_TYPE_MENU; m[1].text = CONTROL_TEXT(5);
386                         m[2].type = NM_TYPE_MENU; m[2].text = CONTROL_TEXT(7);
387                         i = 3;
388                 #else
389                 for (i = 0; i < 6; i++) {
390                         m[i].type = NM_TYPE_MENU; m[i].text = CONTROL_TEXT(i);
391                 }
392                 m[4].text = "Gravis Firebird/Mousetick II";
393                 m[3].text = "Thrustmaster";
394                 #endif
395                 
396                 nitems = i;
397                 m[0].text = TXT_CONTROL_KEYBOARD;
398         
399                 #ifdef WINDOWS
400                         if (Config_control_type==CONTROL_NONE) control_choice = 0;
401                         else if (Config_control_type == CONTROL_MOUSE) control_choice = 1;
402                         else if (Config_control_type == CONTROL_WINJOYSTICK) control_choice = 2;
403                         else control_choice = 0;
404                 #else
405                         control_choice = Config_control_type;                           // Assume keyboard
406                 #endif
407         
408                 #ifndef APPLE_DEMO
409                         control_choice = newmenu_do1( NULL, TXT_CHOOSE_INPUT, i, m, NULL, control_choice );
410                 #else
411                         control_choice = 0;
412                 #endif
413                 
414                 if ( control_choice < 0 )
415                         return 0;
416
417 #if defined(MACINTOSH) && defined(USE_ISP)
418         }
419         else    // !!!!NOTE ... link to above if (!ISpEnabled()), this is a really crappy function
420         {
421                 control_choice = 0;
422         }
423 #endif
424
425         for (i=0;i<CONTROL_MAX_TYPES; i++ )
426                 for (j=0;j<MAX_CONTROLS; j++ )
427                         kconfig_settings[i][j] = default_kconfig_settings[i][j];
428         //added on 2/4/99 by Victor Rachels for new keys
429         for(i=0; i < MAX_D2X_CONTROLS; i++)
430                 kconfig_d2x_settings[i] = default_kconfig_d2x_settings[i];
431         //end this section addition - VR
432         kc_set_controls();
433
434         Config_control_type = control_choice;
435
436 #ifdef WINDOWS
437         if (control_choice == 1) Config_control_type = CONTROL_MOUSE;
438         else if (control_choice == 2) Config_control_type = CONTROL_WINJOYSTICK;
439
440 //      if (Config_control_type == CONTROL_WINJOYSTICK) 
441 //              joydefs_calibrate();
442 #else
443         #ifndef MACINTOSH
444         if ( Config_control_type==CONTROL_THRUSTMASTER_FCS)     {
445                 i = nm_messagebox( TXT_IMPORTANT_NOTE, 2, "Choose another", TXT_OK, TXT_FCS );
446                 if (i==0) goto RetrySelection;
447         }
448         
449         if ( (Config_control_type>0) &&         (Config_control_type<5))        {
450                 joydefs_calibrate();
451         }
452         #else           // some macintosh only stuff here
453         if ( Config_control_type==CONTROL_THRUSTMASTER_FCS)     {
454                 extern char *tm_warning;
455                 
456                 i = nm_messagebox( TXT_IMPORTANT_NOTE, 2, "Choose another", TXT_OK, tm_warning );
457                 if (i==0) goto RetrySelection;
458         } else  if ( Config_control_type==CONTROL_FLIGHTSTICK_PRO )     {
459                 extern char *ch_warning;
460
461                 i = nm_messagebox( TXT_IMPORTANT_NOTE, 2, "Choose another", TXT_OK, ch_warning );
462                 if (i==0) goto RetrySelection;
463         } else  if ( Config_control_type==CONTROL_GRAVIS_GAMEPAD )      {
464                 extern char *ms_warning;
465
466                 i = nm_messagebox( TXT_IMPORTANT_NOTE, 2, "Choose another", TXT_OK, ms_warning );
467                 if (i==0) goto RetrySelection;
468                 // stupid me -- get real default setting for either mousestick or firebird
469                 joydefs_set_type( Config_control_type );
470                 if (joy_have_firebird())
471                         for (i=0; i<NUM_OTHER_CONTROLS; i++ )
472                                 kconfig_settings[Config_control_type][i] = default_firebird_settings[i];
473                 else
474                         for (i=0; i<NUM_OTHER_CONTROLS; i++ )
475                                 kconfig_settings[Config_control_type][i] = default_mousestick_settings[i];
476                 kc_set_controls();              // reset the joystick control
477         }
478         if ( (Config_control_type>0) && (Config_control_type<5)  ) {
479                 joydefs_set_type( Config_control_type );
480                 joydefs_calibrate();
481         }
482
483         #endif
484 #endif
485         
486         Player_default_difficulty = 1;
487         Auto_leveling_on = Default_leveling_on = 1;
488         n_highest_levels = 1;
489         highest_levels[0].shortname[0] = 0;                     //no name for mission 0
490         highest_levels[0].level_num = 1;                                //was highest level in old struct
491         Config_joystick_sensitivity = 8;
492         Cockpit_3d_view[0]=CV_NONE;
493         Cockpit_3d_view[1]=CV_NONE;
494
495         // Default taunt macros
496         #ifdef NETWORK
497         strcpy(Network_message_macro[0], "Why can't we all just get along?");
498         strcpy(Network_message_macro[1], "Hey, I got a present for ya");
499         strcpy(Network_message_macro[2], "I got a hankerin' for a spankerin'");
500         strcpy(Network_message_macro[3], "This one's headed for Uranus");
501         Netlife_kills=0; Netlife_killed=0;      
502         #endif
503         
504         #ifdef MACINTOSH
505                 #ifdef POLY_ACC
506                         if (PAEnabled)
507                         {
508                                 Scanline_double = 0;            // no pixel doubling for poly_acc
509                         }
510                         else
511                         {
512                                 Scanline_double = 1;            // should be default for new player
513                         }
514                 #else
515                         Scanline_double = 1;                    // should be default for new player
516                 #endif
517         #endif
518
519         return 1;
520 }
521
522 extern int Guided_in_big_window,Automap_always_hires;
523
524 //this length must match the value in escort.c
525 #define GUIDEBOT_NAME_LEN 9
526 extern char guidebot_name[];
527 extern char real_guidebot_name[];
528
529 WIN(extern char win95_current_joyname[]);
530
531 ubyte control_type_dos,control_type_win;
532
533 //read in the player's saved games.  returns errno (0 == no error)
534 int read_player_file()
535 {
536         #ifdef MACINTOSH
537         char filename[FILENAME_LEN+15];
538         #else
539         char filename[FILENAME_LEN];
540         #endif
541         CFILE *file;
542         int errno_ret = EZERO;
543         int id, i;
544         short player_file_version;
545         int rewrite_it=0;
546         int swap = 0;
547
548         Assert(Player_num>=0 && Player_num<MAX_PLAYERS);
549
550 #ifndef MACINTOSH
551         sprintf(filename,"%.8s.plr",Players[Player_num].callsign);
552 #else
553         sprintf(filename, ":Players:%.8s.plr",Players[Player_num].callsign);
554 #endif
555         file = cfopen(filename, "rb");
556
557 #if 0 //ndef MACINTOSH
558         //check filename
559         if (file && isatty(fileno(file))) {
560                 //if the callsign is the name of a tty device, prepend a char
561                 fclose(file);
562                 sprintf(filename,"$%.7s.plr",Players[Player_num].callsign);
563                 file = fopen(filename,"rb");
564         }
565 #endif
566
567         if (!file) {
568                 return errno;
569         }
570
571         id = cfile_read_int(file);
572
573         // SWAPINT added here because old versions of d2x
574         // used the wrong byte order.
575         if (id!=SAVE_FILE_ID && id!=SWAPINT(SAVE_FILE_ID)) {
576                 nm_messagebox(TXT_ERROR, 1, TXT_OK, "Invalid player file");
577                 cfclose(file);
578                 return -1;
579         }
580
581         player_file_version = cfile_read_short(file);
582
583         if (player_file_version > 255) // bigendian file?
584                 swap = 1;
585
586         if (swap)
587                 player_file_version = SWAPSHORT(player_file_version);
588
589         if (player_file_version<COMPATIBLE_PLAYER_FILE_VERSION) {
590                 nm_messagebox(TXT_ERROR, 1, TXT_OK, TXT_ERROR_PLR_VERSION);
591                 cfclose(file);
592                 return -1;
593         }
594
595         Game_window_w = cfile_read_short(file);
596         Game_window_h = cfile_read_short(file);
597
598         if (swap) {
599                 Game_window_w = SWAPSHORT(Game_window_w);
600                 Game_window_h = SWAPSHORT(Game_window_h);
601         }
602
603         Player_default_difficulty = cfile_read_byte(file);
604         Default_leveling_on       = cfile_read_byte(file);
605         Reticle_on                = cfile_read_byte(file);
606         Cockpit_mode              = cfile_read_byte(file);
607         #ifdef POLY_ACC
608          #ifdef PA_3DFX_VOODOO
609                 if (Cockpit_mode<2)
610                 {
611                 Cockpit_mode=2;
612                         Game_window_w  = 640;
613                         Game_window_h   = 480;
614                 }
615          #endif
616         #endif
617  
618         Default_display_mode     = cfile_read_byte(file);
619         Missile_view_enabled     = cfile_read_byte(file);
620         Headlight_active_default = cfile_read_byte(file);
621         Guided_in_big_window     = cfile_read_byte(file);
622
623         if (player_file_version >= 19)
624                 Automap_always_hires = cfile_read_byte(file);
625           
626         Auto_leveling_on = Default_leveling_on;
627
628         //read new highest level info
629
630         n_highest_levels = cfile_read_short(file);
631         if (swap)
632                 n_highest_levels = SWAPSHORT(n_highest_levels);
633         Assert(n_highest_levels <= MAX_MISSIONS);
634
635         if (cfread(highest_levels, sizeof(hli), n_highest_levels, file) != n_highest_levels)
636         {
637                 errno_ret                       = errno;
638                 cfclose(file);
639                 return errno_ret;
640         }
641
642         //read taunt macros
643         {
644 #ifdef NETWORK
645                 int i,len;
646
647                 len                     = MAX_MESSAGE_LEN;
648
649                 for (i                  = 0; i < 4; i++)
650                         if (cfread(Network_message_macro[i], len, 1, file) != 1)
651                                 {errno_ret                      = errno; break;}
652 #else
653                 char dummy[4][MAX_MESSAGE_LEN];
654
655                 cfread(dummy, MAX_MESSAGE_LEN, 4, file);
656 #endif
657         }
658
659         //read kconfig data
660         {
661                 int n_control_types = (player_file_version<20)?7:CONTROL_MAX_TYPES;
662
663                 if (cfread( kconfig_settings, MAX_CONTROLS*n_control_types, 1, file ) != 1)
664                         errno_ret=errno;
665                 else if (cfread((ubyte *)&control_type_dos, sizeof(ubyte), 1, file ) != 1)
666                         errno_ret=errno;
667                 else if (player_file_version >= 21 && cfread((ubyte *)&control_type_win, sizeof(ubyte), 1, file ) != 1)
668                         errno_ret=errno;
669                 else if (cfread(&Config_joystick_sensitivity, sizeof(ubyte), 1, file ) != 1)
670                         errno_ret=errno;
671
672                 #ifdef WINDOWS
673                 Config_control_type = control_type_win;
674                 #else
675                 Config_control_type = control_type_dos;
676                 #endif
677                 
678                 #ifdef MACINTOSH
679                 joydefs_set_type(Config_control_type);
680                 #endif
681
682                 for (i=0;i<11;i++)
683                 {
684                         PrimaryOrder[i]   = cfile_read_byte(file);
685                         SecondaryOrder[i] = cfile_read_byte(file);
686                 }
687
688                 if (player_file_version>=16)
689                 {
690                         Cockpit_3d_view[0] = cfile_read_int(file);
691                         Cockpit_3d_view[1] = cfile_read_int(file);
692                         if (swap) {
693                                 Cockpit_3d_view[0] = SWAPINT(Cockpit_3d_view[0]);
694                                 Cockpit_3d_view[1] = SWAPINT(Cockpit_3d_view[1]);
695                         }
696                 }
697                 
698                   
699                 if (errno_ret==EZERO)   {
700                         kc_set_controls();
701                 }
702
703         }
704
705    if (player_file_version>=22)
706          {
707 #ifdef NETWORK
708                 Netlife_kills = cfile_read_int(file);
709                 Netlife_killed = cfile_read_int(file);
710                 if (swap) {
711                         Netlife_kills = SWAPINT(Netlife_kills);
712                         Netlife_killed = SWAPINT(Netlife_killed);
713                 }
714 #else
715                 cfile_read_int(file);
716                 cfile_read_int(file);
717 #endif
718          }
719 #ifdef NETWORK
720    else
721          {
722                  Netlife_kills=0; Netlife_killed=0;
723          }
724 #endif
725
726         if (player_file_version>=23)
727          {
728           i = cfile_read_int(file);
729           if (swap)
730                   i = SWAPINT(i);
731 #ifdef NETWORK
732           mprintf ((0,"Reading: lifetime checksum is %d\n",i));
733           if (i!=get_lifetime_checksum (Netlife_kills,Netlife_killed))
734                 {
735                  Netlife_kills=0; Netlife_killed=0;
736                  nm_messagebox(NULL, 1, "Shame on me", "Trying to cheat eh?");
737                  rewrite_it=1;
738                 }
739 #endif
740          }
741
742         //read guidebot name
743         if (player_file_version >= 18)
744                 cfile_read_string(guidebot_name, GUIDEBOT_NAME_LEN, file);
745         else
746                 strcpy(guidebot_name,"GUIDE-BOT");
747
748         strcpy(real_guidebot_name,guidebot_name);
749
750         {
751                 char buf[128];
752
753         #ifdef WINDOWS
754                 joy95_get_name(JOYSTICKID1, buf, 127);
755                 if (player_file_version >= 24) 
756                         cfile_read_string(win95_current_joyname, file);
757                 else
758                         strcpy(win95_current_joyname, "Old Player File");
759                 
760                 mprintf((0, "Detected joystick: %s\n", buf));
761                 mprintf((0, "Player's joystick: %s\n", win95_current_joyname));
762
763                 if (strcmp(win95_current_joyname, buf)) {
764                         for (i = 0; i < MAX_CONTROLS; i++)
765                                 kconfig_settings[CONTROL_WINJOYSTICK][i] = 
766                                         default_kconfig_settings[CONTROL_WINJOYSTICK][i];
767                 }        
768         #else
769                 if (player_file_version >= 24) 
770                         cfile_read_string(buf, 127, file);      // Just read it in fpr DPS.
771         #endif
772         }
773
774         if (player_file_version >= 25)
775                 cfread(kconfig_d2x_settings, MAX_D2X_CONTROLS, 1, file);
776         else
777                 for(i=0; i < MAX_D2X_CONTROLS; i++)
778                         kconfig_d2x_settings[i] = default_kconfig_d2x_settings[i];
779
780         if (cfclose(file) && errno_ret == EZERO)
781                 errno_ret                       = errno;
782
783         if (rewrite_it)
784          write_player_file();
785
786         return errno_ret;
787
788 }
789
790
791 //finds entry for this level in table.  if not found, returns ptr to 
792 //empty entry.  If no empty entries, takes over last one 
793 int find_hli_entry()
794 {
795         int i;
796
797         for (i=0;i<n_highest_levels;i++)
798                 if (!stricmp(highest_levels[i].shortname,Mission_list[Current_mission_num].filename))
799                         break;
800
801         if (i==n_highest_levels) {              //not found.  create entry
802
803                 if (i==MAX_MISSIONS)
804                         i--;            //take last entry
805                 else
806                         n_highest_levels++;
807
808                 strcpy(highest_levels[i].shortname,Mission_list[Current_mission_num].filename);
809                 highest_levels[i].level_num                     = 0;
810         }
811
812         return i;
813 }
814
815 //set a new highest level for player for this mission
816 void set_highest_level(int levelnum)
817 {
818         int ret,i;
819
820         if ((ret=read_player_file()) != EZERO)
821                 if (ret != ENOENT)              //if file doesn't exist, that's ok
822                         return;
823
824         i                       = find_hli_entry();
825
826         if (levelnum > highest_levels[i].level_num)
827                 highest_levels[i].level_num                     = levelnum;
828
829         write_player_file();
830 }
831
832 //gets the player's highest level from the file for this mission
833 int get_highest_level(void)
834 {
835         int i;
836         int highest_saturn_level                        = 0;
837         read_player_file();
838 #ifndef SATURN
839         if (strlen(Mission_list[Current_mission_num].filename)==0 )     {
840                 for (i=0;i<n_highest_levels;i++)
841                         if (!stricmp(highest_levels[i].shortname, "DESTSAT"))   //      Destination Saturn.
842                                 highest_saturn_level                    = highest_levels[i].level_num; 
843         }
844 #endif
845    i                    = highest_levels[find_hli_entry()].level_num;
846         if ( highest_saturn_level > i )
847         i                       = highest_saturn_level;
848         return i;
849 }
850
851 extern int Cockpit_mode_save;
852
853 //write out player's saved games.  returns errno (0 == no error)
854 int write_player_file()
855 {
856         #ifdef MACINTOSH
857         char filename[FILENAME_LEN+15];
858         #else
859         char filename[FILENAME_LEN];            // because of ":Players:" path
860         #endif
861         CFILE *file;
862         int errno_ret,i;
863
864 //      #ifdef APPLE_DEMO               // no saving of player files in Apple OEM version
865 //      return 0;
866 //      #endif
867
868         errno_ret                       = WriteConfigFile();
869
870 #ifndef MACINTOSH
871         sprintf(filename,"%s.plr",Players[Player_num].callsign);
872 #else
873         sprintf(filename, ":Players:%.8s.plr",Players[Player_num].callsign);
874 #endif
875         file = cfopen(filename, "wb");
876
877 #if 0 //ndef MACINTOSH
878         //check filename
879         if (file && isatty(fileno(file))) {
880
881                 //if the callsign is the name of a tty device, prepend a char
882
883                 fclose(file);
884                 sprintf(filename,"$%.7s.plr",Players[Player_num].callsign);
885                 file                    = fopen(filename,"wb");
886         }
887 #endif
888
889         if (!file)
890               return errno;
891
892         errno_ret                       = EZERO;
893
894         //Write out player's info
895         cfile_write_int(SAVE_FILE_ID, file);
896         cfile_write_short(PLAYER_FILE_VERSION, file);
897
898         cfile_write_short(Game_window_w, file);
899         cfile_write_short(Game_window_h, file);
900
901         cfile_write_byte(Player_default_difficulty, file);
902         cfile_write_byte(Auto_leveling_on, file);
903         cfile_write_byte(Reticle_on, file);
904         cfile_write_byte((Cockpit_mode_save != -1)?Cockpit_mode_save:Cockpit_mode, file);   //if have saved mode, write it instead of letterbox/rear view
905         cfile_write_byte(Default_display_mode, file);
906         cfile_write_byte(Missile_view_enabled, file);
907         cfile_write_byte(Headlight_active_default, file);
908         cfile_write_byte(Guided_in_big_window, file);
909         cfile_write_byte(Automap_always_hires, file);
910
911         //write higest level info
912         Assert(n_highest_levels <= MAX_MISSIONS);
913         cfile_write_short(n_highest_levels, file);
914         if ((cfwrite(highest_levels, sizeof(hli), n_highest_levels, file) != n_highest_levels))
915         {
916                 errno_ret                       = errno;
917                 cfclose(file);
918                 return errno_ret;
919         }
920
921 #ifdef NETWORK
922         if ((cfwrite(Network_message_macro, MAX_MESSAGE_LEN, 4, file) != 4))
923         {
924                 errno_ret                       = errno;
925                 cfclose(file);
926                 return errno_ret;
927         }
928 #else
929         cfseek(file, MAX_MESSAGE_LEN * 4, SEEK_CUR);
930 #endif
931
932         //write kconfig info
933         {
934
935                 #ifdef WINDOWS
936                 control_type_win = Config_control_type;
937                 #else
938                 control_type_dos = Config_control_type;
939                 #endif
940
941                 if (cfwrite(kconfig_settings, MAX_CONTROLS * CONTROL_MAX_TYPES, 1, file ) != 1)
942                         errno_ret=errno;
943                 else if (cfwrite(&control_type_dos, sizeof(ubyte), 1, file) != 1)
944                         errno_ret=errno;
945                 else if (cfwrite(&control_type_win, sizeof(ubyte), 1, file ) != 1)
946                         errno_ret=errno;
947                 else if (cfwrite(&Config_joystick_sensitivity, sizeof(ubyte), 1, file) != 1)
948                         errno_ret=errno;
949
950                 for (i=0;i<11;i++)
951                 {
952                         cfwrite(&PrimaryOrder[i], sizeof(ubyte), 1, file);
953                         cfwrite(&SecondaryOrder[i], sizeof(ubyte), 1, file);
954                 }
955
956                 cfile_write_int(Cockpit_3d_view[0], file);
957                 cfile_write_int(Cockpit_3d_view[1], file);
958
959 #ifdef NETWORK
960                 cfile_write_int(Netlife_kills, file);
961                 cfile_write_int(Netlife_killed, file);
962                 i=get_lifetime_checksum (Netlife_kills,Netlife_killed);
963                 mprintf ((0,"Writing: Lifetime checksum is %d\n",i));
964 #else
965                 cfile_write_int(0, file);
966                 cfile_write_int(0, file);
967                 i = get_lifetime_checksum(0, 0);
968 #endif
969                 cfile_write_int(i,file);
970         }
971
972         //write guidebot name
973         cfile_write_string(real_guidebot_name, file);
974         {
975                 char buf[128];
976                 #ifdef WINDOWS
977                 joy95_get_name(JOYSTICKID1, buf, 127);
978                 #else
979                 strcpy(buf, "DOS joystick");
980                 #endif
981                 cfile_write_string(buf, file);  // Write out current joystick for player.
982         }
983
984         cfwrite(kconfig_d2x_settings, MAX_D2X_CONTROLS, 1, file);
985
986         if (cfclose(file))
987                 errno_ret                       = errno;
988
989         if (errno_ret != EZERO) {
990                 cfile_delete(filename);         //delete bogus file
991                 nm_messagebox(TXT_ERROR, 1, TXT_OK, "%s\n\n%s",TXT_ERROR_WRITING_PLR, strerror(errno_ret));
992         }
993
994         #ifdef MACINTOSH                // set filetype and creator for playerfile
995         {
996                 FInfo finfo;
997                 Str255 pfilename;
998                 OSErr err;
999
1000                 strcpy(pfilename, filename);
1001                 c2pstr(pfilename);
1002                 err = HGetFInfo(0, 0, pfilename, &finfo);
1003                 finfo.fdType = 'PLYR';
1004                 finfo.fdCreator = 'DCT2';
1005                 err = HSetFInfo(0, 0, pfilename, &finfo);
1006         }
1007         #endif
1008
1009         return errno_ret;
1010
1011 }
1012
1013 //update the player's highest level.  returns errno (0 == no error)
1014 int update_player_file()
1015 {
1016         int ret;
1017
1018         if ((ret=read_player_file()) != EZERO)
1019                 if (ret != ENOENT)              //if file doesn't exist, that's ok
1020                         return ret;
1021
1022         return write_player_file();
1023 }
1024
1025 int get_lifetime_checksum (int a,int b)
1026  {
1027   int num;
1028
1029   // confusing enough to beat amateur disassemblers? Lets hope so
1030
1031   num=(a<<8 ^ b);
1032   num^=(a | b);
1033   num*=num>>2;
1034   return (num);
1035  }
1036   
1037