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