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