This commit was manufactured by cvs2svn to create tag 'd2x-0_1_2'.
[btb/d2x.git] / main / playsave.c
1 /*
2 THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
3 SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
4 END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
5 ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
6 IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
7 SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
8 FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
9 CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
10 AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.  
11 COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
12 */
13
14 /*
15  * $Source: /cvs/cvsroot/d2x/main/playsave.c,v $
16  * $Revision: 1.4 $
17  * $Author: bradleyb $
18  * $Date: 2001-11-12 00:59:07 $
19  *
20  * Functions to load & save player games
21  *
22  * $Log: not supported by cvs2svn $
23  * Revision 1.3  2001/11/11 23:39:22  bradleyb
24  * Created header for MAKE_SIG macro
25  *
26  *
27  */
28
29 #ifdef HAVE_CONFIG_H
30 #include <conf.h>
31 #endif
32
33 #ifdef WINDOWS
34 #include "desw.h"
35 #include <mmsystem.h>
36 #endif
37
38 #include <stdio.h>
39 #include <string.h>
40 #include <unistd.h>
41 #include <errno.h>
42
43 #include "error.h"
44
45 #include "pa_enabl.h"
46 #include "strutil.h"
47 #include "game.h"
48 #include "gameseq.h"
49 #include "player.h"
50 #include "playsave.h"
51 #include "joy.h"
52 #include "kconfig.h"
53 #include "digi.h"
54 #include "newmenu.h"
55 #include "joydefs.h"
56 #include "palette.h"
57 #include "multi.h"
58 #include "menu.h"
59 #include "config.h"
60 #include "text.h"
61 #include "mono.h"
62 #include "state.h"
63 #include "gauges.h"
64 #include "screens.h"
65 #include "powerup.h"
66 #include "makesig.h"
67 #include "byteswap.h"
68
69 #define SAVE_FILE_ID                    MAKE_SIG('D','P','L','R')
70
71 #ifdef MACINTOSH
72         #include <Files.h>
73         #include <Errors.h>                     // mac doesn't have "normal" error numbers -- must use mac equivs
74         #ifndef ENOENT
75                 #define ENOENT fnfErr
76         #endif
77         #ifdef POLY_ACC
78                 #include "poly_acc.h"
79         #endif
80         #include "isp.h"
81 #endif
82
83 int get_lifetime_checksum (int a,int b);
84
85 typedef struct hli {
86         char    shortname[9];
87         ubyte   level_num;
88 } hli;
89
90 int n_highest_levels;
91
92 hli highest_levels[MAX_MISSIONS];
93
94 #define PLAYER_FILE_VERSION     24                      //increment this every time the player file changes
95
96 //version 5  ->  6: added new highest level information
97 //version 6  ->  7: stripped out the old saved_game array.
98 //version 7  ->  8: added reticle flag, & window size
99 //version 8  ->  9: removed player_struct_version
100 //version 9  -> 10: added default display mode
101 //version 10 -> 11: added all toggles in toggle menu
102 //version 11 -> 12: added weapon ordering
103 //version 12 -> 13: added more keys
104 //version 13 -> 14: took out marker key
105 //version 14 -> 15: added guided in big window
106 //version 15 -> 16: added small windows in cockpit
107 //version 16 -> 17: ??
108 //version 17 -> 18: save guidebot name
109 //version 18 -> 19: added automap-highres flag
110 //version 19 -> 20: added kconfig data for windows joysticks
111 //version 20 -> 21: save seperate config types for DOS & Windows
112 //version 21 -> 22: save lifetime netstats 
113 //version 22 -> 23: ??
114 //version 23 -> 24: add name of joystick for windows version.
115
116 #define COMPATIBLE_PLAYER_FILE_VERSION          17
117
118
119 int Default_leveling_on=1;
120 extern ubyte SecondaryOrder[],PrimaryOrder[];
121 extern void InitWeaponOrdering();
122
123 #ifdef MACINTOSH
124 extern ubyte default_firebird_settings[];
125 extern ubyte default_mousestick_settings[];
126 #endif
127
128 int new_player_config()
129 {
130         int nitems;
131         int i,j,control_choice;
132         newmenu_item m[8];
133    int mct=CONTROL_MAX_TYPES;
134  
135    #ifndef WINDOWS
136          mct--;
137         #endif
138
139    InitWeaponOrdering ();               //setup default weapon priorities 
140
141 #if defined(MACINTOSH) && defined(USE_ISP)
142         if (!ISpEnabled())
143         {
144 #endif
145 RetrySelection:
146                 #if !defined(MACINTOSH) && !defined(WINDOWS)
147                 for (i=0; i<mct; i++ )  {
148                         m[i].type = NM_TYPE_MENU; m[i].text = CONTROL_TEXT(i);
149                 }
150                 #elif defined(WINDOWS)
151                         m[0].type = NM_TYPE_MENU; m[0].text = CONTROL_TEXT(0);
152                         m[1].type = NM_TYPE_MENU; m[1].text = CONTROL_TEXT(5);
153                         m[2].type = NM_TYPE_MENU; m[2].text = CONTROL_TEXT(7);
154                         i = 3;
155                 #else
156                 for (i = 0; i < 6; i++) {
157                         m[i].type = NM_TYPE_MENU; m[i].text = CONTROL_TEXT(i);
158                 }
159                 m[4].text = "Gravis Firebird/Mousetick II";
160                 m[3].text = "Thrustmaster";
161                 #endif
162                 
163                 nitems = i;
164                 m[0].text = TXT_CONTROL_KEYBOARD;
165         
166                 #ifdef WINDOWS
167                         if (Config_control_type==CONTROL_NONE) control_choice = 0;
168                         else if (Config_control_type == CONTROL_MOUSE) control_choice = 1;
169                         else if (Config_control_type == CONTROL_WINJOYSTICK) control_choice = 2;
170                         else control_choice = 0;
171                 #else
172                         control_choice = Config_control_type;                           // Assume keyboard
173                 #endif
174         
175                 #ifndef APPLE_DEMO
176                         control_choice = newmenu_do1( NULL, TXT_CHOOSE_INPUT, i, m, NULL, control_choice );
177                 #else
178                         control_choice = 0;
179                 #endif
180                 
181                 if ( control_choice < 0 )
182                         return 0;
183
184 #if defined(MACINTOSH) && defined(USE_ISP)
185         }
186         else    // !!!!NOTE ... link to above if (!ISpEnabled()), this is a really crappy function
187         {
188                 control_choice = 0;
189         }
190 #endif
191
192         for (i=0;i<CONTROL_MAX_TYPES; i++ )
193                 for (j=0;j<MAX_CONTROLS; j++ )
194                         kconfig_settings[i][j] = default_kconfig_settings[i][j];
195         kc_set_controls();
196
197         Config_control_type = control_choice;
198
199 #ifdef WINDOWS
200         if (control_choice == 1) Config_control_type = CONTROL_MOUSE;
201         else if (control_choice == 2) Config_control_type = CONTROL_WINJOYSTICK;
202
203 //      if (Config_control_type == CONTROL_WINJOYSTICK) 
204 //              joydefs_calibrate();
205 #else
206         #ifndef MACINTOSH
207         if ( Config_control_type==CONTROL_THRUSTMASTER_FCS)     {
208                 i = nm_messagebox( TXT_IMPORTANT_NOTE, 2, "Choose another", TXT_OK, TXT_FCS );
209                 if (i==0) goto RetrySelection;
210         }
211         
212         if ( (Config_control_type>0) &&         (Config_control_type<5))        {
213                 joydefs_calibrate();
214         }
215         #else           // some macintosh only stuff here
216         if ( Config_control_type==CONTROL_THRUSTMASTER_FCS)     {
217                 extern char *tm_warning;
218                 
219                 i = nm_messagebox( TXT_IMPORTANT_NOTE, 2, "Choose another", TXT_OK, tm_warning );
220                 if (i==0) goto RetrySelection;
221         } else  if ( Config_control_type==CONTROL_FLIGHTSTICK_PRO )     {
222                 extern char *ch_warning;
223
224                 i = nm_messagebox( TXT_IMPORTANT_NOTE, 2, "Choose another", TXT_OK, ch_warning );
225                 if (i==0) goto RetrySelection;
226         } else  if ( Config_control_type==CONTROL_GRAVIS_GAMEPAD )      {
227                 extern char *ms_warning;
228
229                 i = nm_messagebox( TXT_IMPORTANT_NOTE, 2, "Choose another", TXT_OK, ms_warning );
230                 if (i==0) goto RetrySelection;
231                 // stupid me -- get real default setting for either mousestick or firebird
232                 joydefs_set_type( Config_control_type );
233                 if (joy_have_firebird())
234                         for (i=0; i<NUM_OTHER_CONTROLS; i++ )
235                                 kconfig_settings[Config_control_type][i] = default_firebird_settings[i];
236                 else
237                         for (i=0; i<NUM_OTHER_CONTROLS; i++ )
238                                 kconfig_settings[Config_control_type][i] = default_mousestick_settings[i];
239                 kc_set_controls();              // reset the joystick control
240         }
241         if ( (Config_control_type>0) && (Config_control_type<5)  ) {
242                 joydefs_set_type( Config_control_type );
243                 joydefs_calibrate();
244         }
245
246         #endif
247 #endif
248         
249         Player_default_difficulty = 1;
250         Auto_leveling_on = Default_leveling_on = 1;
251         n_highest_levels = 1;
252         highest_levels[0].shortname[0] = 0;                     //no name for mission 0
253         highest_levels[0].level_num = 1;                                //was highest level in old struct
254         Config_joystick_sensitivity = 8;
255         Cockpit_3d_view[0]=CV_NONE;
256         Cockpit_3d_view[1]=CV_NONE;
257
258         // Default taunt macros
259         #ifdef NETWORK
260         strcpy(Network_message_macro[0], "Why can't we all just get along?");
261         strcpy(Network_message_macro[1], "Hey, I got a present for ya");
262         strcpy(Network_message_macro[2], "I got a hankerin' for a spankerin'");
263         strcpy(Network_message_macro[3], "This one's headed for Uranus");
264         Netlife_kills=0; Netlife_killed=0;      
265         #endif
266         
267         #ifdef MACINTOSH
268                 #ifdef POLY_ACC
269                         if (PAEnabled)
270                         {
271                                 Scanline_double = 0;            // no pixel doubling for poly_acc
272                         }
273                         else
274                         {
275                                 Scanline_double = 1;            // should be default for new player
276                         }
277                 #else
278                         Scanline_double = 1;                    // should be default for new player
279                 #endif
280         #endif
281
282         return 1;
283 }
284
285 static int read_int(FILE *file)
286 {
287         int i;
288
289         if (fread( &i, sizeof(i), 1, file) != 1)
290                 Error( "Error reading int in gamesave.c" );
291
292         return i;
293 }
294
295 static short read_short(FILE *file)
296 {
297         short s;
298
299         if (fread( &s, sizeof(s), 1, file) != 1)
300                 Error( "Error reading short in gamesave.c" );
301
302         return s;
303 }
304
305 static byte read_byte(FILE *file)
306 {
307         byte s;
308
309         if (fread( &s, sizeof(s), 1, file) != 1)
310                 Error( "Error reading byte in gamesave.c" );
311
312         return s;
313 }
314
315 static void write_int(int i,FILE *file)
316 {
317         if (fwrite( &i, sizeof(i), 1, file) != 1)
318                 Error( "Error writing int in gamesave.c" );
319
320 }
321
322 static void write_short(short s,FILE *file)
323 {
324         if (fwrite( &s, sizeof(s), 1, file) != 1)
325                 Error( "Error writing short in gamesave.c" );
326
327 }
328
329 static void write_byte(byte i,FILE *file)
330 {
331         if (fwrite( &i, sizeof(i), 1, file) != 1)
332                 Error( "Error writing byte in gamesave.c" );
333
334 }
335
336 extern int Guided_in_big_window,Automap_always_hires;
337
338 //this length must match the value in escort.c
339 #define GUIDEBOT_NAME_LEN 9
340 extern char guidebot_name[];
341 extern char real_guidebot_name[];
342
343 WIN(extern char win95_current_joyname[]);
344
345 void read_string(char *s, FILE *f)
346 {
347         if (feof(f))
348                 *s = 0;
349         else
350                 do
351                         *s = fgetc(f);
352                 while (!feof(f) && *s++!=0);
353 }
354
355 void write_string(char *s, FILE *f)
356 {
357         do
358                 fputc(*s,f);
359         while (*s++!=0);
360 }
361
362 ubyte control_type_dos,control_type_win;
363
364 //read in the player's saved games.  returns errno (0 == no error)
365 int read_player_file()
366 {
367         #ifdef MACINTOSH
368         char filename[FILENAME_LEN+15];
369         #else
370         char filename[FILENAME_LEN];
371         #endif
372         FILE *file;
373         int errno_ret = EZERO;
374         int id,player_file_version,i;
375         int rewrite_it=0;
376
377         Assert(Player_num>=0 && Player_num<MAX_PLAYERS);
378
379 #ifndef MACINTOSH
380         sprintf(filename,"%.8s.plr",Players[Player_num].callsign);
381 #else
382         sprintf(filename, ":Players:%.8s.plr",Players[Player_num].callsign);
383 #endif
384         file = fopen(filename,"rb");
385
386 #ifndef MACINTOSH
387         //check filename
388         if (file && isatty(fileno(file))) {
389                 //if the callsign is the name of a tty device, prepend a char
390                 fclose(file);
391                 sprintf(filename,"$%.7s.plr",Players[Player_num].callsign);
392                 file = fopen(filename,"rb");
393         }
394 #endif
395
396         if (!file) {
397                 return errno;
398         }
399
400         id = read_int(file);
401
402         // SWAPINT added here because old versions of d2x
403         // used the wrong byte order.
404         if (id!=SAVE_FILE_ID && id!=SWAPINT(SAVE_FILE_ID)) {
405                 nm_messagebox(TXT_ERROR, 1, TXT_OK, "Invalid player file");
406                 fclose(file);
407                 return -1;
408         }
409
410         player_file_version = read_short(file);
411
412         if (player_file_version<COMPATIBLE_PLAYER_FILE_VERSION) {
413                 nm_messagebox(TXT_ERROR, 1, TXT_OK, TXT_ERROR_PLR_VERSION);
414                 fclose(file);
415                 return -1;
416         }
417
418         Game_window_w                                   = read_short(file);
419         Game_window_h                                   = read_short(file);
420
421         Player_default_difficulty       = read_byte(file);
422         Default_leveling_on                     = read_byte(file);
423         Reticle_on                                              = read_byte(file);
424         Cockpit_mode                                    = read_byte(file);
425         #ifdef POLY_ACC
426          #ifdef PA_3DFX_VOODOO
427                 if (Cockpit_mode<2)
428                 {
429                 Cockpit_mode=2;
430                         Game_window_w  = 640;
431                         Game_window_h   = 480;
432                 }
433          #endif
434         #endif
435  
436         Default_display_mode                    = read_byte(file);
437         Missile_view_enabled                    = read_byte(file);
438         Headlight_active_default        = read_byte(file);
439         Guided_in_big_window                    = read_byte(file);
440
441         if (player_file_version >= 19)
442                 Automap_always_hires                    = read_byte(file);
443           
444         Auto_leveling_on = Default_leveling_on;
445
446         //read new highest level info
447
448         n_highest_levels = read_short(file);
449         if (fread(highest_levels,sizeof(hli),n_highest_levels,file) != n_highest_levels) {
450                 errno_ret                       = errno;
451                 fclose(file);
452                 return errno_ret;
453         }
454
455         //read taunt macros
456         {
457 #ifdef NETWORK
458                 int i,len;
459
460                 len                     = MAX_MESSAGE_LEN;
461
462                 for (i                  = 0; i < 4; i++)
463                         if (fread(Network_message_macro[i], len, 1, file) != 1)
464                                 {errno_ret                      = errno; break;}
465 #else
466                 char dummy[4][MAX_MESSAGE_LEN];
467                 fread(dummy, MAX_MESSAGE_LEN, 4, file);
468
469 #endif
470         }
471
472         //read kconfig data
473         {
474                 int n_control_types = (player_file_version<20)?7:CONTROL_MAX_TYPES;
475
476                 if (fread( kconfig_settings, MAX_CONTROLS*n_control_types, 1, file )!=1)
477                         errno_ret=errno;
478                 else if (fread((ubyte *)&control_type_dos, sizeof(ubyte), 1, file )!=1)
479                         errno_ret=errno;
480                 else if (player_file_version >= 21 && fread((ubyte *)&control_type_win, sizeof(ubyte), 1, file )!=1)
481                         errno_ret=errno;
482                 else if (fread(&Config_joystick_sensitivity, sizeof(ubyte), 1, file )!=1)
483                         errno_ret=errno;
484
485                 #ifdef WINDOWS
486                 Config_control_type = control_type_win;
487                 #else
488                 Config_control_type = control_type_dos;
489                 #endif
490                 
491                 #ifdef MACINTOSH
492                 joydefs_set_type(Config_control_type);
493                 #endif
494
495                 for (i=0;i<11;i++)
496                  {
497                         PrimaryOrder[i]=read_byte (file);
498                         SecondaryOrder[i]=read_byte(file);
499                  }
500                 
501                 if (player_file_version>=16)
502                  {
503                   Cockpit_3d_view[0]=read_int(file);
504                   Cockpit_3d_view[1]=read_int(file);
505                  }      
506                 
507                   
508                 if (errno_ret==EZERO)   {
509                         kc_set_controls();
510                 }
511
512         }
513
514    if (player_file_version>=22)
515          {
516 #ifdef NETWORK
517                 Netlife_kills=read_int (file);
518                 Netlife_killed=read_int (file);
519 #else
520                 read_int(file); read_int(file);
521 #endif
522          }
523 #ifdef NETWORK
524    else
525          {
526                  Netlife_kills=0; Netlife_killed=0;
527          }
528 #endif
529
530         if (player_file_version>=23)
531          {
532           i=read_int (file);    
533 #ifdef NETWORK
534           mprintf ((0,"Reading: lifetime checksum is %d\n",i));
535           if (i!=get_lifetime_checksum (Netlife_kills,Netlife_killed))
536                 {
537                  Netlife_kills=0; Netlife_killed=0;
538                  nm_messagebox(NULL, 1, "Shame on me", "Trying to cheat eh?");
539                  rewrite_it=1;
540                 }
541 #endif
542          }
543
544         //read guidebot name
545         if (player_file_version >= 18)
546                 read_string(guidebot_name,file);
547         else
548                 strcpy(guidebot_name,"GUIDE-BOT");
549
550         strcpy(real_guidebot_name,guidebot_name);
551
552         {
553                 char buf[128];
554
555         #ifdef WINDOWS
556                 joy95_get_name(JOYSTICKID1, buf, 127);
557                 if (player_file_version >= 24) 
558                         read_string(win95_current_joyname, file);
559                 else
560                         strcpy(win95_current_joyname, "Old Player File");
561                 
562                 mprintf((0, "Detected joystick: %s\n", buf));
563                 mprintf((0, "Player's joystick: %s\n", win95_current_joyname));
564
565                 if (strcmp(win95_current_joyname, buf)) {
566                         for (i = 0; i < MAX_CONTROLS; i++)
567                                 kconfig_settings[CONTROL_WINJOYSTICK][i] = 
568                                         default_kconfig_settings[CONTROL_WINJOYSTICK][i];
569                 }        
570         #else
571                 if (player_file_version >= 24) 
572                         read_string(buf, file);                 // Just read it in fpr DPS.
573         #endif
574         }
575
576         if (fclose(file) && errno_ret==EZERO)
577                 errno_ret                       = errno;
578
579         if (rewrite_it)
580          write_player_file();
581
582         return errno_ret;
583
584 }
585
586
587 //finds entry for this level in table.  if not found, returns ptr to 
588 //empty entry.  If no empty entries, takes over last one 
589 int find_hli_entry()
590 {
591         int i;
592
593         for (i=0;i<n_highest_levels;i++)
594                 if (!stricmp(highest_levels[i].shortname,Mission_list[Current_mission_num].filename))
595                         break;
596
597         if (i==n_highest_levels) {              //not found.  create entry
598
599                 if (i==MAX_MISSIONS)
600                         i--;            //take last entry
601                 else
602                         n_highest_levels++;
603
604                 strcpy(highest_levels[i].shortname,Mission_list[Current_mission_num].filename);
605                 highest_levels[i].level_num                     = 0;
606         }
607
608         return i;
609 }
610
611 //set a new highest level for player for this mission
612 void set_highest_level(int levelnum)
613 {
614         int ret,i;
615
616         if ((ret=read_player_file()) != EZERO)
617                 if (ret != ENOENT)              //if file doesn't exist, that's ok
618                         return;
619
620         i                       = find_hli_entry();
621
622         if (levelnum > highest_levels[i].level_num)
623                 highest_levels[i].level_num                     = levelnum;
624
625         write_player_file();
626 }
627
628 //gets the player's highest level from the file for this mission
629 int get_highest_level(void)
630 {
631         int i;
632         int highest_saturn_level                        = 0;
633         read_player_file();
634 #ifndef SATURN
635         if (strlen(Mission_list[Current_mission_num].filename)==0 )     {
636                 for (i=0;i<n_highest_levels;i++)
637                         if (!stricmp(highest_levels[i].shortname, "DESTSAT"))   //      Destination Saturn.
638                                 highest_saturn_level                    = highest_levels[i].level_num; 
639         }
640 #endif
641    i                    = highest_levels[find_hli_entry()].level_num;
642         if ( highest_saturn_level > i )
643         i                       = highest_saturn_level;
644         return i;
645 }
646
647 extern int Cockpit_mode_save;
648
649 //write out player's saved games.  returns errno (0 == no error)
650 int write_player_file()
651 {
652         #ifdef MACINTOSH
653         char filename[FILENAME_LEN+15];
654         #else
655         char filename[FILENAME_LEN];            // because of ":Players:" path
656         #endif
657         FILE *file;
658         int errno_ret,i;
659
660 //      #ifdef APPLE_DEMO               // no saving of player files in Apple OEM version
661 //      return 0;
662 //      #endif
663
664         errno_ret                       = WriteConfigFile();
665
666 #ifndef MACINTOSH
667         sprintf(filename,"%s.plr",Players[Player_num].callsign);
668 #else
669         sprintf(filename, ":Players:%.8s.plr",Players[Player_num].callsign);
670 #endif
671         file                    = fopen(filename,"wb");
672
673 #ifndef MACINTOSH
674         //check filename
675         if (file && isatty(fileno(file))) {
676
677                 //if the callsign is the name of a tty device, prepend a char
678
679                 fclose(file);
680                 sprintf(filename,"$%.7s.plr",Players[Player_num].callsign);
681                 file                    = fopen(filename,"wb");
682         }
683 #endif
684
685         if (!file)
686               return errno;
687
688         errno_ret                       = EZERO;
689
690         //Write out player's info
691         write_int(SAVE_FILE_ID,file);
692         write_short(PLAYER_FILE_VERSION,file);
693
694         write_short(Game_window_w,file);
695         write_short(Game_window_h,file);
696
697         write_byte(Player_default_difficulty,file);
698         write_byte(Auto_leveling_on,file);
699         write_byte(Reticle_on,file);
700         write_byte((Cockpit_mode_save!=-1)?Cockpit_mode_save:Cockpit_mode,file);        //if have saved mode, write it instead of letterbox/rear view
701         write_byte(Default_display_mode,file);
702         write_byte(Missile_view_enabled,file);
703         write_byte(Headlight_active_default,file);
704         write_byte(Guided_in_big_window,file);
705         write_byte(Automap_always_hires,file);
706
707         //write higest level info
708         write_short(n_highest_levels,file);
709         if ((fwrite(highest_levels, sizeof(hli), n_highest_levels, file) != n_highest_levels)) {
710                 errno_ret                       = errno;
711                 fclose(file);
712                 return errno_ret;
713         }
714
715         #ifdef NETWORK
716         if ((fwrite(Network_message_macro, MAX_MESSAGE_LEN, 4, file) != 4)) {
717                 errno_ret                       = errno;
718                 fclose(file);
719                 return errno_ret;
720         }
721         #else
722         fseek( file, MAX_MESSAGE_LEN * 4, SEEK_CUR );
723         #endif
724
725         //write kconfig info
726         {
727
728                 #ifdef WINDOWS
729                 control_type_win = Config_control_type;
730                 #else
731                 control_type_dos = Config_control_type;
732                 #endif
733
734                 if (fwrite( kconfig_settings, MAX_CONTROLS*CONTROL_MAX_TYPES, 1, file )!=1)
735                         errno_ret=errno;
736                 else if (fwrite( &control_type_dos, sizeof(ubyte), 1, file )!=1)
737                         errno_ret=errno;
738                 else if (fwrite( &control_type_win, sizeof(ubyte), 1, file )!=1)
739                         errno_ret=errno;
740                 else if (fwrite( &Config_joystick_sensitivity, sizeof(ubyte), 1, file )!=1)
741                         errno_ret=errno;
742
743       for (i=0;i<11;i++)
744        {
745         fwrite (&PrimaryOrder[i],sizeof(ubyte),1,file);
746         fwrite (&SecondaryOrder[i],sizeof(ubyte),1,file);
747        }
748                 write_int (Cockpit_3d_view[0],file);
749                 write_int (Cockpit_3d_view[1],file);
750
751 #ifdef NETWORK
752                 write_int (Netlife_kills,file);
753            write_int (Netlife_killed,file);
754                 i=get_lifetime_checksum (Netlife_kills,Netlife_killed);
755                 mprintf ((0,"Writing: Lifetime checksum is %d\n",i));
756 #else
757            write_int(0, file);
758            write_int(0, file);
759            i = get_lifetime_checksum (0, 0);
760 #endif
761            write_int (i,file);
762         }
763
764         //write guidebot name
765         write_string(real_guidebot_name,file);
766
767         {
768                 char buf[128];
769                 #ifdef WINDOWS
770                 joy95_get_name(JOYSTICKID1, buf, 127);
771                 #else
772                 strcpy(buf, "DOS joystick");
773                 #endif
774                 write_string(buf, file);                // Write out current joystick for player.
775         }
776
777         if (fclose(file))
778                 errno_ret                       = errno;
779
780         if (errno_ret != EZERO) {
781                 remove(filename);                       //delete bogus file
782                 nm_messagebox(TXT_ERROR, 1, TXT_OK, "%s\n\n%s",TXT_ERROR_WRITING_PLR, strerror(errno_ret));
783         }
784
785         #ifdef MACINTOSH                // set filetype and creator for playerfile
786         {
787                 FInfo finfo;
788                 Str255 pfilename;
789                 OSErr err;
790
791                 strcpy(pfilename, filename);
792                 c2pstr(pfilename);
793                 err = HGetFInfo(0, 0, pfilename, &finfo);
794                 finfo.fdType = 'PLYR';
795                 finfo.fdCreator = 'DCT2';
796                 err = HSetFInfo(0, 0, pfilename, &finfo);
797         }
798         #endif
799
800         return errno_ret;
801
802 }
803
804 //update the player's highest level.  returns errno (0 == no error)
805 int update_player_file()
806 {
807         int ret;
808
809         if ((ret=read_player_file()) != EZERO)
810                 if (ret != ENOENT)              //if file doesn't exist, that's ok
811                         return ret;
812
813         return write_player_file();
814 }
815
816 int get_lifetime_checksum (int a,int b)
817  {
818   int num;
819
820   // confusing enough to beat amateur disassemblers? Lets hope so
821
822   num=(a<<8 ^ b);
823   num^=(a | b);
824   num*=num>>2;
825   return (num);
826  }
827   
828