]> icculus.org git repositories - btb/d2x.git/blob - main/config.c
separate control type into separate cvars for joy and mouse
[btb/d2x.git] / main / config.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  *
16  * contains routine(s) to read in the configuration file which contains
17  * game configuration stuff like detail level, sound card, etc
18  *
19  */
20
21 #ifdef HAVE_CONFIG_H
22 #include <conf.h>
23 #endif
24
25 #ifndef MACINTOSH                       // I'm going to totally seperate these routines -- yeeech!!!!
26                                                         // see end of file for macintosh equivs
27
28 #ifdef WINDOWS
29 #define WIN32_LEAN_AND_MEAN
30 #include <windows.h>
31 #include "winapp.h"
32 #else
33 #endif
34
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <string.h>
38 #include <ctype.h>
39
40 #include <physfs.h>
41
42 #include "inferno.h"
43 #include "menu.h"
44 #include "movie.h"
45 #include "digi.h"
46 #include "kconfig.h"
47 #include "palette.h"
48 #include "joy.h"
49 #include "songs.h"
50 #include "args.h"
51 #include "player.h"
52 #include "mission.h"
53 #include "mono.h"
54 #include "key.h"
55 #include "physfsx.h"
56 #include "multi.h"
57
58
59 #ifdef __MSDOS__
60 cvar_t Config_digi_type         = { "DigiDeviceID8", "0", 1 };
61 cvar_t digi_driver_board_16     = { "DigiDeviceID16", "0", 1 };
62 //cvar_t digi_driver_port         = { "DigiPort", "0", 1 };
63 //cvar_t digi_driver_irq          = { "DigiIrq", "0", 1 };
64 cvar_t Config_digi_dma          = { "DigiDma8", "0", 1 };
65 cvar_t digi_driver_dma_16       = { "DigiDma16", "0", 1 };
66 cvar_t Config_midi_type         = { "MidiDeviceID", "0", 1 };
67 //cvar_t digi_midi_port           = { "MidiPort", "0", 1 };
68 #endif
69 cvar_t Config_digi_volume       = { "DigiVolume", "8", 1 };
70 cvar_t Config_midi_volume       = { "MidiVolume", "8", 1 };
71 cvar_t Config_redbook_volume    = { "RedbookVolume", "8", 1 };
72 cvar_t Config_detail_level      = { "DetailLevel", "4", 1 };
73 cvar_t Config_gamma_level       = { "GammaLevel", "0", 1 };
74 cvar_t Config_control_joystick  = { "Joystick", "0", 1 };
75 cvar_t Config_control_mouse     = { "Mouse", "0", 1 };
76 cvar_t Config_channels_reversed = { "StereoReverse", "0", 1 };
77 cvar_t Config_joystick_sensitivity = { "JoystickSensitivity", "8", 1 };
78 cvar_t Config_joystick_min      = { "JoystickMin", "0,0,0,0", 1 };
79 cvar_t Config_joystick_max      = { "JoystickMax", "0,0,0,0", 1 };
80 cvar_t Config_joystick_cen      = { "JoystickCen", "0,0,0,0", 1 };
81 cvar_t config_last_player       = { "LastPlayer", "", 1 };
82 cvar_t config_last_mission      = { "LastMission", "", 1 };
83 cvar_t Config_vr_type           = { "VR_type", "0", 1 };
84 cvar_t Config_vr_resolution     = { "VR_resolution", "0", 1 };
85 cvar_t Config_vr_tracking       = { "VR_tracking", "0", 1 };
86 cvar_t Config_primary_order     = { "PrimaryOrder", "", 1 };
87 cvar_t Config_secondary_order   = { "SecondaryOrder", "", 1 };
88 cvar_t Config_lifetime_kills    = { "LifetimeKills", "0", 1 };
89 cvar_t Config_lifetime_killed   = { "LifetimeKilled", "0", 1 };
90 cvar_t Config_lifetime_checksum = { "LifetimeChecksum", "0", 1 };
91 cvar_t Config_display_mode      = { "vid_mode", "0", 1 };
92
93
94 #define _CRYSTAL_LAKE_8_ST              0xe201
95 #define _CRYSTAL_LAKE_16_ST     0xe202
96 #define _AWE32_8_ST                             0xe208
97 #define _AWE32_16_ST                            0xe209
98
99
100 extern sbyte Object_complexity, Object_detail, Wall_detail, Wall_render_depth, Debris_amount, SoundChannels;
101
102 void set_custom_detail_vars(void);
103
104 uint32_t legacy_display_mode[] = { SM(320,200), SM(640,480), SM(320,400), SM(640,400), SM(800,600), SM(1024,768), SM(1280,1024) };
105
106
107 #define CL_MC0 0xF8F
108 #define CL_MC1 0xF8D
109 /*
110 void CrystalLakeWriteMCP( ushort mc_addr, ubyte mc_data )
111 {
112         _disable();
113         outp( CL_MC0, 0xE2 );                           // Write password
114         outp( mc_addr, mc_data );               // Write data
115         _enable();
116 }
117
118 ubyte CrystalLakeReadMCP( ushort mc_addr )
119 {
120         ubyte value;
121         _disable();
122         outp( CL_MC0, 0xE2 );           // Write password
123         value = inp( mc_addr );         // Read data
124         _enable();
125         return value;
126 }
127
128 void CrystalLakeSetSB()
129 {
130         ubyte tmp;
131         tmp = CrystalLakeReadMCP( CL_MC1 );
132         tmp &= 0x7F;
133         CrystalLakeWriteMCP( CL_MC1, tmp );
134 }
135
136 void CrystalLakeSetWSS()
137 {
138         ubyte tmp;
139         tmp = CrystalLakeReadMCP( CL_MC1 );
140         tmp |= 0x80;
141         CrystalLakeWriteMCP( CL_MC1, tmp );
142 }
143 */
144 //MovieHires might be changed by -nohighres, so save a "real" copy of it
145 int SaveMovieHires;
146 int save_redbook_enabled;
147
148 #ifdef WINDOWS
149 void CheckMovieAttributes()
150 {
151                 HKEY hKey;
152                 DWORD len, type, val;
153                 long lres;
154  
155                 lres = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Parallax\\Descent II\\1.1\\INSTALL",
156                                                         0, KEY_READ, &hKey);
157                 if (lres == ERROR_SUCCESS) {
158                         len = sizeof(val);
159                         lres = RegQueryValueEx(hKey, "HIRES", NULL, &type, &val, &len);
160                         if (lres == ERROR_SUCCESS) {
161                                 cvar_setint( &MovieHires, val );
162                                 logentry("HIRES=%d\n", val);
163                         }
164                         RegCloseKey(hKey);
165                 }
166 }
167 #endif
168
169
170 static int config_initialized;
171
172 static void config_init(void)
173 {
174         /* make sure our cvars are registered */
175         cvar_registervariable(&Config_digi_volume);
176         cvar_registervariable(&Config_midi_volume);
177         cvar_registervariable(&Redbook_enabled);
178         cvar_registervariable(&Config_redbook_volume);
179         cvar_registervariable(&Config_channels_reversed);
180         cvar_registervariable(&Config_gamma_level);
181         cvar_registervariable(&Config_detail_level);
182         cvar_registervariable(&Config_control_joystick);
183         cvar_registervariable(&Config_control_mouse);
184         cvar_registervariable(&Config_joystick_sensitivity);
185         cvar_registervariable(&Config_joystick_min);
186         cvar_registervariable(&Config_joystick_cen);
187         cvar_registervariable(&Config_joystick_max);
188         cvar_registervariable(&config_last_player);
189         cvar_registervariable(&config_last_mission);
190         cvar_registervariable(&Config_vr_type);
191         cvar_registervariable(&Config_vr_resolution);
192         cvar_registervariable(&Config_vr_tracking);
193         cvar_registervariable(&MovieHires);
194         cvar_registervariable(&real_guidebot_name);
195         cvar_registervariable(&Config_primary_order);
196         cvar_registervariable(&Config_secondary_order);
197         cvar_registervariable(&Cockpit_3d_view[0]);
198         cvar_registervariable(&Cockpit_3d_view[1]);
199         cvar_registervariable(&Config_lifetime_kills);
200         cvar_registervariable(&Config_lifetime_killed);
201         cvar_registervariable(&Config_lifetime_checksum);
202 #ifdef NETWORK
203         cvar_registervariable(&Network_message_macro[0]);
204         cvar_registervariable(&Network_message_macro[1]);
205         cvar_registervariable(&Network_message_macro[2]);
206         cvar_registervariable(&Network_message_macro[3]);
207 #endif
208         cvar_registervariable(&Game_window_w);
209         cvar_registervariable(&Game_window_h);
210         cvar_registervariable(&Player_default_difficulty);
211         cvar_registervariable(&Auto_leveling_on);
212         cvar_registervariable(&Reticle_on);
213         cvar_registervariable(&Cockpit_mode);
214         cvar_registervariable(&Config_display_mode);
215         cvar_registervariable(&Missile_view_enabled);
216         cvar_registervariable(&Headlight_active_default);
217         cvar_registervariable(&Guided_in_big_window);
218         cvar_registervariable(&Automap_always_hires);
219
220         config_initialized = 1;
221 }
222
223
224 static int get_lifetime_checksum (int a,int b)
225 {
226         int num;
227
228         // confusing enough to beat amateur disassemblers? Lets hope so
229
230         num=(a<<8 ^ b);
231         num^=(a | b);
232         num*=num>>2;
233         return (num);
234 }
235
236
237 void LoadConfigDefaults(void)
238 {
239         cmd_append("bind UP     +lookdown;      bind PAD8   +lookdown");
240         cmd_append("bind DOWN   +lookup;        bind PAD2   +lookup");
241         cmd_append("bind LEFT   +left;          bind PAD4   +left");
242         cmd_append("bind RIGHT  +right;         bind PAD6   +right");
243
244         cmd_append("bind LALT   +strafe");
245         cmd_append("bind PAD1   +moveleft");
246         cmd_append("bind PAD3   +moveright");
247         cmd_append("bind PAD-   +moveup");
248         cmd_append("bind PAD+   +movedown");
249
250         cmd_append("bind Q      +bankleft;      bind PAD7   +bankleft");
251         cmd_append("bind E      +bankright;     bind PAD9   +bankright");
252
253         cmd_append("bind ,      +cycle");
254         cmd_append("bind .      +cycle2");
255
256         cmd_append("bind LCTRL  +attack;        bind RCTRL  +attack");
257         cmd_append("bind SPC    +attack2");
258         cmd_append("bind F      +flare");
259         cmd_append("bind B      +bomb");
260
261         cmd_append("bind R      +rearview");
262         cmd_append("bind TAB    +automap");
263
264         cmd_append("bind A      +forward");
265         cmd_append("bind Z      +back");
266         cmd_append("bind S      +afterburner");
267
268         cmd_append("bind H      +headlight");
269         cmd_append("bind T      +nrgshield");
270
271         cmd_append("bind J1B1   +attack");
272         cmd_append("bind J1B2   +attack2");
273
274         cmd_append("bind MB1    +attack");
275         cmd_append("bind MB2    +attack2");
276
277         cmd_append("bind 1 weapon 1");
278         cmd_append("bind 2 weapon 2");
279         cmd_append("bind 3 weapon 3");
280         cmd_append("bind 4 weapon 4");
281         cmd_append("bind 5 weapon 5");
282         cmd_append("bind 6 weapon 6");
283         cmd_append("bind 7 weapon 7");
284         cmd_append("bind 8 weapon 8");
285         cmd_append("bind 9 weapon 9");
286         cmd_append("bind 0 weapon 0");
287 }
288
289
290 int ReadConfigFile()
291 {
292         int joy_axis_min[7];
293         int joy_axis_center[7];
294         int joy_axis_max[7];
295         int i;
296
297         if (!config_initialized)
298                 config_init();
299
300         cvar_set_cvar( &config_last_player, "" );
301
302         joy_axis_min[0] = joy_axis_min[1] = joy_axis_min[2] = joy_axis_min[3] = 0;
303         joy_axis_max[0] = joy_axis_max[1] = joy_axis_max[2] = joy_axis_max[3] = 0;
304         joy_axis_center[0] = joy_axis_center[1] = joy_axis_center[2] = joy_axis_center[3] = 0;
305
306         joy_set_cal_vals(joy_axis_min, joy_axis_center, joy_axis_max);
307
308 #if 0
309         cvar_setint(&digi_driver_board, 0);
310         cvar_setint(&digi_driver_port, 0);
311         cvar_setint(&digi_driver_irq, 0);
312         cvar_setint(&digi_driver_dma, 0);
313         cvar_setint(&digi_midi_type, 0);
314         cvar_setint(&digi_midi_port, 0);
315 #endif
316
317         cvar_setint( &Config_digi_volume, 8 );
318         cvar_setint( &Config_midi_volume, 8 );
319         cvar_setint( &Config_redbook_volume, 8 );
320         cvar_setint( &Config_control_joystick, 0 );
321         cvar_setint( &Config_control_mouse, 0 );
322         cvar_setint( &Config_channels_reversed, 0);
323         cvar_setint( &Config_joystick_sensitivity, 8 );
324
325         //set these here in case no cfg file
326         SaveMovieHires = MovieHires.intval;
327         save_redbook_enabled = Redbook_enabled.intval;
328
329         InitWeaponOrdering(); // setup default weapon priorities
330         cvar_set_cvarf(&Config_primary_order, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d",
331                 PrimaryOrder[0], PrimaryOrder[1], PrimaryOrder[2], PrimaryOrder[3],
332                 PrimaryOrder[4], PrimaryOrder[5], PrimaryOrder[6], PrimaryOrder[7],
333                 PrimaryOrder[8], PrimaryOrder[9], PrimaryOrder[10]);
334         cvar_set_cvarf(&Config_secondary_order, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d",
335                 SecondaryOrder[0], SecondaryOrder[1], SecondaryOrder[2], SecondaryOrder[3],
336                 SecondaryOrder[4], SecondaryOrder[5], SecondaryOrder[6], SecondaryOrder[7],
337                 SecondaryOrder[8], SecondaryOrder[9], SecondaryOrder[10]);
338
339         cvar_setint( &Cockpit_3d_view[0], CV_NONE );
340         cvar_setint( &Cockpit_3d_view[1], CV_NONE );
341
342         cvar_setint( &Config_lifetime_kills, 0 );
343         cvar_setint( &Config_lifetime_killed, 0 );
344         cvar_setint( &Config_lifetime_checksum, 0 );
345
346         // Default taunt macros
347 #ifdef NETWORK
348         cvar_set_cvar(&Network_message_macro[0], "Why can't we all just get along?");
349         cvar_set_cvar(&Network_message_macro[1], "Hey, I got a present for ya");
350         cvar_set_cvar(&Network_message_macro[2], "I got a hankerin' for a spankerin'");
351         cvar_set_cvar(&Network_message_macro[3], "This one's headed for Uranus");
352 #endif
353
354         cvar_setint(&Player_default_difficulty, 1);
355         cvar_setint(&Auto_leveling_on, 1);
356
357         if (cfexist("descent.cfg"))
358                 cmd_append("exec descent.cfg");
359         else
360                 LoadConfigDefaults();
361
362         cmd_queue_process();
363
364         /* TODO: allow cvars to define a callback that will carry out these initialization things on change. */
365
366         gr_palette_set_gamma( Config_gamma_level.intval );
367
368         Detail_level = strtol(Config_detail_level.string, NULL, 10);
369         if (Detail_level == NUM_DETAIL_LEVELS - 1) {
370                 int count,dummy,oc,od,wd,wrd,da,sc;
371
372                 count = sscanf (Config_detail_level.string, "%d,%d,%d,%d,%d,%d,%d\n",&dummy,&oc,&od,&wd,&wrd,&da,&sc);
373
374                 if (count == 7) {
375                         Object_complexity = oc;
376                         Object_detail = od;
377                         Wall_detail = wd;
378                         Wall_render_depth = wrd;
379                         Debris_amount = da;
380                         SoundChannels = sc;
381                         set_custom_detail_vars();
382                 }
383         }
384
385         sscanf( Config_joystick_min.string, "%d,%d,%d,%d", &joy_axis_min[0], &joy_axis_min[1], &joy_axis_min[2], &joy_axis_min[3] );
386         sscanf( Config_joystick_max.string, "%d,%d,%d,%d", &joy_axis_max[0], &joy_axis_max[1], &joy_axis_max[2], &joy_axis_max[3] );
387         sscanf( Config_joystick_cen.string, "%d,%d,%d,%d", &joy_axis_center[0], &joy_axis_center[1], &joy_axis_center[2], &joy_axis_center[3] );
388
389         joy_set_cal_vals(joy_axis_min, joy_axis_center, joy_axis_max);
390
391         i = FindArg( "-volume" );
392         
393         if ( i > 0 )    {
394                 i = atoi( Args[i+1] );
395                 if ( i < 0 ) i = 0;
396                 if ( i > 100 ) i = 100;
397                 cvar_setint( &Config_digi_volume, (i * 8) / 100 );
398                 cvar_setint( &Config_midi_volume, (i * 8) / 100 );
399                 cvar_setint( &Config_redbook_volume, (i * 8) / 100 );
400         }
401
402         if ( Config_digi_volume.intval > 8 ) cvar_setint( &Config_digi_volume, 8 );
403         if ( Config_midi_volume.intval > 8 ) cvar_setint( &Config_midi_volume, 8 );
404         if ( Config_redbook_volume.intval > 8 ) cvar_setint( &Config_redbook_volume, 8 );
405
406         digi_set_volume( (Config_digi_volume.intval * 32768) / 8, (Config_midi_volume.intval * 128) / 8 );
407
408         kc_set_controls();
409
410         strncpy(guidebot_name, real_guidebot_name.string, GUIDEBOT_NAME_LEN);
411         guidebot_name[GUIDEBOT_NAME_LEN] = 0;
412
413         sscanf(Config_primary_order.string, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d",
414                 (int *)&PrimaryOrder[0], (int *)&PrimaryOrder[1], (int *)&PrimaryOrder[2], (int *)&PrimaryOrder[3],
415                 (int *)&PrimaryOrder[4], (int *)&PrimaryOrder[5], (int *)&PrimaryOrder[6], (int *)&PrimaryOrder[7],
416                 (int *)&PrimaryOrder[8], (int *)&PrimaryOrder[9], (int *)&PrimaryOrder[10]);
417         sscanf(Config_secondary_order.string, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d",
418                 (int *)&SecondaryOrder[0], (int *)&SecondaryOrder[1], (int *)&SecondaryOrder[2], (int *)&SecondaryOrder[3],
419                 (int *)&SecondaryOrder[4], (int *)&SecondaryOrder[5], (int *)&SecondaryOrder[6], (int *)&SecondaryOrder[7],
420                 (int *)&SecondaryOrder[8], (int *)&SecondaryOrder[9], (int *)&SecondaryOrder[10]);
421
422 #ifdef NETWORK
423         Netlife_kills = Config_lifetime_kills.intval;
424         Netlife_killed = Config_lifetime_killed.intval;
425
426         con_printf(CON_DEBUG, "Reading: lifetime checksum is %d\n", Config_lifetime_checksum.intval);
427         if (Config_lifetime_checksum.intval != get_lifetime_checksum(Netlife_kills, Netlife_killed)) {
428                 Netlife_kills = Netlife_killed = 0;
429                 Warning("Shame on me\nTrying to cheat eh?");
430         }
431 #endif
432
433         Default_display_mode = legacy_display_mode[Config_display_mode.intval];
434
435 #if 0
436         printf( "DigiDeviceID: 0x%x\n", digi_driver_board );
437         printf( "DigiPort: 0x%x\n", digi_driver_port.intval );
438         printf( "DigiIrq: 0x%x\n",  digi_driver_irq.intval );
439         printf( "DigiDma: 0x%x\n",      digi_driver_dma.intval );
440         printf( "MidiDeviceID: 0x%x\n", digi_midi_type.intval );
441         printf( "MidiPort: 0x%x\n", digi_midi_port.intval );
442         key_getch();
443
444         cvar_setint( &Config_midi_type, digi_midi_type );
445         cvar_setint( &Config_digi_type, digi_driver_board );
446         cvar_setint( &Config_digi_dma, digi_driver_dma );
447 #endif
448
449 #if 0
450         if (digi_driver_board_16.intval > 0 && !FindArg("-no16bit") && digi_driver_board_16.intval != _GUS_16_ST) {
451                 digi_driver_board = digi_driver_board_16.intval;
452                 digi_driver_dma = digi_driver_dma_16.intval;
453         }
454
455         // HACK!!!
456         //Hack to make some cards look like others, such as
457         //the Crytal Lake look like Microsoft Sound System
458         if ( digi_driver_board == _CRYSTAL_LAKE_8_ST )  {
459                 ubyte tmp;
460                 tmp = CrystalLakeReadMCP( CL_MC1 );
461                 if ( !(tmp & 0x80) )
462                         atexit( CrystalLakeSetSB );             // Restore to SB when done.
463                 CrystalLakeSetWSS();
464                 digi_driver_board = _MICROSOFT_8_ST;
465         } else if ( digi_driver_board == _CRYSTAL_LAKE_16_ST )  {
466                 ubyte tmp;
467                 tmp = CrystalLakeReadMCP( CL_MC1 );
468                 if ( !(tmp & 0x80) )
469                         atexit( CrystalLakeSetSB );             // Restore to SB when done.
470                 CrystalLakeSetWSS();
471                 digi_driver_board = _MICROSOFT_16_ST;
472         } else if ( digi_driver_board == _AWE32_8_ST )  {
473                 digi_driver_board = _SB16_8_ST;
474         } else if ( digi_driver_board == _AWE32_16_ST ) {
475                 digi_driver_board = _SB16_16_ST;
476         } else
477                 digi_driver_board               = digi_driver_board;
478 #else
479
480         if (cfexist("descentw.cfg")) {
481                 cmd_append("exec descentw.cfg");
482                 cmd_queue_process();
483
484                 sscanf( Config_joystick_min.string, "%d,%d,%d,%d,%d,%d,%d", &joy_axis_min[0], &joy_axis_min[1], &joy_axis_min[2], &joy_axis_min[3], &joy_axis_min[4], &joy_axis_min[5], &joy_axis_min[6] );
485                 sscanf( Config_joystick_max.string, "%d,%d,%d,%d,%d,%d,%d", &joy_axis_max[0], &joy_axis_max[1], &joy_axis_max[2], &joy_axis_max[3], &joy_axis_max[4], &joy_axis_max[5], &joy_axis_max[6] );
486                 sscanf( Config_joystick_cen.string, "%d,%d,%d,%d,%d,%d,%d", &joy_axis_center[0], &joy_axis_center[1], &joy_axis_center[2], &joy_axis_center[3], &joy_axis_center[4], &joy_axis_center[5], &joy_axis_center[6] );
487         }
488 #endif
489
490         return 0;
491 }
492
493 int WriteConfigFile()
494 {
495         PHYSFS_file *outfile;
496         int joy_axis_min[7];
497         int joy_axis_center[7];
498         int joy_axis_max[7];
499         int i;
500         
501         joy_get_cal_vals(joy_axis_min, joy_axis_center, joy_axis_max);
502
503         if (FindArg("-noredbook"))
504                 cvar_setint( &Redbook_enabled, save_redbook_enabled );
505
506         cvar_setint( &Config_gamma_level, gr_palette_get_gamma() );
507
508         if (Detail_level == NUM_DETAIL_LEVELS-1)
509                 cvar_set_cvarf( &Config_detail_level, "%d,%d,%d,%d,%d,%d,%d", Detail_level,
510                                            Object_complexity,Object_detail,Wall_detail,Wall_render_depth,Debris_amount,SoundChannels );
511         else
512                 cvar_setint( &Config_detail_level, Detail_level );
513
514         cvar_set_cvarf( &Config_joystick_min, "%d,%d,%d,%d", joy_axis_min[0], joy_axis_min[1], joy_axis_min[2], joy_axis_min[3] );
515         cvar_set_cvarf( &Config_joystick_cen, "%d,%d,%d,%d", joy_axis_center[0], joy_axis_center[1], joy_axis_center[2], joy_axis_center[3] );
516         cvar_set_cvarf( &Config_joystick_max, "%d,%d,%d,%d", joy_axis_max[0], joy_axis_max[1], joy_axis_max[2], joy_axis_max[3] );
517
518         cvar_set_cvar( &config_last_player, Players[Player_num].callsign );
519
520         cvar_setint( &MovieHires, SaveMovieHires );
521
522         cvar_set_cvarf(&Config_primary_order, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d",
523                 PrimaryOrder[0], PrimaryOrder[1], PrimaryOrder[2], PrimaryOrder[3],
524                 PrimaryOrder[4], PrimaryOrder[5], PrimaryOrder[6], PrimaryOrder[7],
525                 PrimaryOrder[8], PrimaryOrder[9], PrimaryOrder[10]);
526         cvar_set_cvarf(&Config_secondary_order, "%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d",
527                 SecondaryOrder[0], SecondaryOrder[1], SecondaryOrder[2], SecondaryOrder[3],
528                 SecondaryOrder[4], SecondaryOrder[5], SecondaryOrder[6], SecondaryOrder[7],
529                 SecondaryOrder[8], SecondaryOrder[9], SecondaryOrder[10]);
530
531 #ifdef NETWORK
532         cvar_setint(&Config_lifetime_kills, Netlife_kills);
533         cvar_setint(&Config_lifetime_killed, Netlife_killed);
534         cvar_setint(&Config_lifetime_checksum, get_lifetime_checksum(Netlife_kills, Netlife_killed));
535         con_printf(CON_DEBUG, "Writing: Lifetime checksum is %d\n", Config_lifetime_checksum.intval);
536 #endif
537
538         cvar_setint(&Cockpit_mode, (Cockpit_mode_save != -1)?Cockpit_mode_save:Cockpit_mode.intval); //if have saved mode, write it instead of letterbox/rear view
539
540         for (i = 0; i < (sizeof(legacy_display_mode) / sizeof(uint32_t)); i++) {
541                 if (legacy_display_mode[i] == Current_display_mode)
542                         cvar_setint(&Config_display_mode, i);
543         }
544
545         outfile = PHYSFSX_openWriteBuffered("descent.cfg");
546         if (outfile == NULL)
547                 return 1;
548         cvar_write(outfile);
549         key_write_bindings(outfile);
550         PHYSFS_close(outfile);
551
552         if (FindArg("-nohires") || FindArg("-nohighres") || FindArg("-lowresmovies"))
553                 cvar_setint( &MovieHires, 0 );
554
555 #ifdef WINDOWS
556         CheckMovieAttributes();
557 #endif
558
559         return 0;
560 }               
561
562 #else           // !defined(MACINTOSH)
563
564 #include <stdio.h>
565 #include <stdlib.h>
566 #include <string.h>
567 #include <ctype.h>
568
569 #include <Memory.h>
570 #include <Folders.h>
571 #include <GestaltEqu.h>
572 #include <Errors.h>
573 #include <Processes.h>
574 #include <Resources.h>
575 #include <LowMem.h>
576
577 #include "error.h"
578 #include "pstypes.h"
579 #include "game.h"
580 #include "digi.h"
581 #include "kconfig.h"
582 #include "palette.h"
583 #include "joy.h"
584 #include "args.h"
585 #include "player.h"
586 #include "mission.h"
587 #include "prefs.h"                      // prefs file for configuration stuff -- from DeSalvo
588
589 #define MAX_CTB_LEN     512
590
591 typedef struct preferences {
592         ubyte   digi_volume;
593         ubyte   midi_volume;
594         ubyte   stereo_reverse;
595         ubyte   detail_level;
596         ubyte   oc;                                     // object complexity
597         ubyte   od;                                     // object detail
598         ubyte   wd;                                     // wall detail
599         ubyte   wrd;                            // wall render depth
600         ubyte   da;                                     // debris amount
601         ubyte   sc;                                     // sound channels
602         ubyte   gamma_level;
603         ubyte   pixel_double;
604         int             joy_axis_min[4];
605         int             joy_axis_max[4];
606         int             joy_axis_center[4];
607         char    lastplayer[CALLSIGN_LEN+1];
608         char    lastmission[MISSION_NAME_LEN+1];
609         char    ctb_config[MAX_CTB_LEN];
610         int             ctb_tool;
611         ubyte   master_volume;
612         ubyte   display_dialog;
613         ubyte   change_resolution;
614         ubyte   nosound;
615         ubyte   nomidi;
616         ubyte   sound_11k;
617         ubyte   no_movies;
618         ubyte   game_monitor;
619         ubyte   redbook_volume;
620         ubyte   enable_rave;
621         ubyte   enable_input_sprockets;
622 } Preferences;
623
624 char config_last_player[CALLSIGN_LEN+1] = "";
625 char config_last_mission[MISSION_NAME_LEN+1] = "";
626 char config_last_ctb_cfg[MAX_CTB_LEN] = "";
627 int config_last_ctb_tool;
628 ubyte Config_master_volume = 4;
629 ubyte Config_digi_volume = 8;
630 ubyte Config_midi_volume = 8;
631 ubyte Config_redbook_volume = 8;
632 ubyte Config_control_type = 0;
633 ubyte Config_channels_reversed = 0;
634 ubyte Config_joystick_sensitivity = 8;
635
636 int Config_vr_type = 0;
637 int Config_vr_resolution = 0;
638 int Config_vr_tracking = 0;
639
640 extern sbyte Object_complexity, Object_detail, Wall_detail, Wall_render_depth, Debris_amount, SoundChannels;
641 extern void digi_set_master_volume( int volume );
642
643 void set_custom_detail_vars(void);
644
645 static ubyte have_prefs = 0;
646
647 //¥     ------------------------------  Private Definitions
648 //¥     ------------------------------  Private Types
649
650 typedef struct
651 {
652         Str31   fileName;
653         OSType  creator;
654         OSType  fileType;
655         OSType  resType;
656         short   resID;
657 } PrefsInfo, *PrefsInfoPtr, **PrefsInfoHandle;
658
659 //¥     ------------------------------  Private Variables
660
661 static PrefsInfo                prefsInfo;
662 static Boolean          prefsInited = 0;
663
664 //¥     ------------------------------  Private Functions
665
666 static void Pstrcpy(StringPtr dst, StringPtr src);
667 static void Pstrcat(StringPtr dst, StringPtr src);
668 static Boolean FindPrefsFile(short *prefVRefNum, long *prefDirID);
669
670 //¥     --------------------    Pstrcpy
671
672 static void
673 Pstrcpy(StringPtr dst, StringPtr src)
674 {
675         BlockMove(src, dst, (*src) + 1);
676 }
677
678 //¥     --------------------    Pstrcat
679
680 static void
681 Pstrcat(StringPtr dst, StringPtr src)
682 {
683         BlockMove(src + 1, dst + (*dst) + 1, *src);
684         *dst += *src;
685 }
686
687 //¥     --------------------    FindPrefsFile
688
689 static Boolean
690 FindPrefsFile(short *prefVRefNum, long *prefDirID)
691 {
692 OSErr           theErr;
693 long                    response;
694 CInfoPBRec      infoPB;
695
696         if (! prefsInited)
697                 return (0);
698                 
699         theErr = Gestalt(gestaltFindFolderAttr, &response);
700         if (theErr == noErr && ((response >> gestaltFindFolderPresent) & 1))
701         {
702                 //¥     Find (or make) it the easy way...
703                 theErr = FindFolder(kOnSystemDisk, kPreferencesFolderType, kCreateFolder, prefVRefNum, prefDirID);
704         }
705         else
706         {
707         SysEnvRec       theSysEnv;
708         StringPtr               prefFolderName = "\pPreferences";
709
710                 //¥     yeachh -- we have to do it all by hand!
711                 theErr = SysEnvirons(1, &theSysEnv);
712                 if (theErr != noErr)
713                         return (0);
714                         
715                 *prefVRefNum = theSysEnv.sysVRefNum;
716                 
717                 //¥     Check whether Preferences folder already exists
718                 infoPB.hFileInfo.ioCompletion   = 0;
719                 infoPB.hFileInfo.ioNamePtr      = prefFolderName;
720                 infoPB.hFileInfo.ioVRefNum      = *prefVRefNum;
721                 infoPB.hFileInfo.ioFDirIndex    = 0;
722                 infoPB.hFileInfo.ioDirID                = 0;
723
724                 theErr = PBGetCatInfo(&infoPB, 0);
725                 if (theErr == noErr)
726                 {
727                         *prefDirID = infoPB.hFileInfo.ioDirID;
728                 }
729                 else if (theErr == fnfErr)              //¥     Preferences doesn't already exist
730                 {
731                 HParamBlockRec  dirPB;
732                 
733                         //¥     Create "Preferences" folder
734                         dirPB.fileParam.ioCompletion    = 0;
735                         dirPB.fileParam.ioVRefNum       = *prefVRefNum;
736                         dirPB.fileParam.ioNamePtr       = prefFolderName;
737                         dirPB.fileParam.ioDirID         = 0;
738
739                         theErr = PBDirCreate(&dirPB, 0);
740                         if (theErr == noErr)
741                                 *prefDirID = dirPB.fileParam.ioDirID;
742                 }
743         }
744         
745         //¥     If we make it here OK, create Preferences file if necessary
746         if (theErr == noErr)
747         {
748                 infoPB.hFileInfo.ioCompletion   = 0;
749                 infoPB.hFileInfo.ioNamePtr      = prefsInfo.fileName;
750                 infoPB.hFileInfo.ioVRefNum      = *prefVRefNum;
751                 infoPB.hFileInfo.ioFDirIndex    = 0;
752                 infoPB.hFileInfo.ioDirID                = *prefDirID;
753
754                 theErr = PBGetCatInfo(&infoPB, 0);
755                 if (theErr == fnfErr)
756                 {
757                         theErr = HCreate(*prefVRefNum, *prefDirID, prefsInfo.fileName, prefsInfo.creator, prefsInfo.fileType);
758                         if (theErr == noErr)
759                         {
760                                 HCreateResFile(*prefVRefNum, *prefDirID, prefsInfo.fileName);
761                                 theErr = ResError();
762                         }
763                 }
764         }
765         
766         return (theErr == noErr);
767 }
768
769 //¥     --------------------    InitPrefsFile
770
771 #define UNKNOWN_TYPE 0x3f3f3f3f
772
773 void
774 InitPrefsFile(OSType creator)
775 {
776         OSErr err;
777 PrefsInfoHandle         piHdl;
778         
779         if ((piHdl = (PrefsInfoHandle) GetResource('PRFI', 0)) == nil)
780         {
781         ProcessSerialNumber     thePSN;
782         ProcessInfoRec                  thePIR;
783         FSSpec                          appSpec;
784         StringPtr                       app_string;
785
786 #if 0   
787                 GetCurrentProcess(&thePSN);
788                 thePIR.processName = nil;
789                 thePIR.processAppSpec = &appSpec;
790                 
791                 //¥     Set default to 'ÇApplicationÈ Prefs', PREF 0
792                 err = GetProcessInformation(&thePSN, &thePIR);
793                 if (err)
794                         Int3();
795 #endif
796                 app_string = LMGetCurApName();
797 //              Pstrcpy(prefsInfo.fileName, appSpec.name);
798                 Pstrcpy(prefsInfo.fileName, app_string);
799                 Pstrcat(prefsInfo.fileName, "\p Preferences");
800                 
801                 //¥     Set creator to calling application's signature (should be able to
802                 //¥     Determine this automatically, but unable to for some reason)
803                 prefsInfo.creator = creator;
804                 prefsInfo.fileType = 'pref';
805                 prefsInfo.resType = 'pref';
806                 prefsInfo.resID = 0;
807         }
808         else
809         {
810                 //¥     Get Preferences file setup from PRFI 0
811                 BlockMove(*piHdl, &prefsInfo, sizeof (prefsInfo));
812                 ReleaseResource((Handle) piHdl);
813                 
814                 if (prefsInfo.creator == UNKNOWN_TYPE)
815                         prefsInfo.creator = creator;
816         }
817         
818         prefsInited = 1;
819 }
820
821 //¥     --------------------    LoadPrefsFile
822
823 OSErr
824 LoadPrefsFile(Handle prefsHdl)
825 {
826 short   prefVRefNum, prefRefNum;
827 long            prefDirID;
828 OSErr   theErr = noErr;
829 Handle  origHdl;
830 Size            prefSize, origSize;
831
832         if (prefsHdl == nil)
833                 return (nilHandleErr);
834
835         prefSize = GetHandleSize(prefsHdl);
836                 
837         if (! FindPrefsFile(&prefVRefNum, &prefDirID))
838                 return (fnfErr);
839
840         prefRefNum = HOpenResFile(prefVRefNum, prefDirID, prefsInfo.fileName, fsRdWrPerm);
841         if (prefRefNum == -1)
842                 return (ResError());
843         
844         //¥     Not finding the resource is not an error -- caller will use default data
845         if ((origHdl = Get1Resource(prefsInfo.resType, prefsInfo.resID)) != nil)
846         {
847                 origSize = GetHandleSize(origHdl);
848                 if (origSize > prefSize)                        //¥     Extend handle for extra stored data
849                         SetHandleSize(prefsHdl, origSize);
850
851                 BlockMove(*origHdl, *prefsHdl, origSize);
852                 ReleaseResource(origHdl);
853         }
854         
855         CloseResFile(prefRefNum);
856
857         if (theErr == noErr)
858                 theErr = ResError();
859         
860         return (theErr);
861 }
862
863 //¥     --------------------    SavePrefsFile
864
865 OSErr
866 SavePrefsFile(Handle prefHdl)
867 {
868 short   prefVRefNum, prefRefNum;
869 long            prefDirID;
870 Handle  origHdl = nil;
871 Size            origSize, prefSize;
872 OSErr   theErr = noErr;
873         
874         if (! FindPrefsFile(&prefVRefNum, &prefDirID))
875                 return (fnfErr);
876         
877         if (prefHdl == nil)
878                 return (nilHandleErr);
879
880         prefSize = GetHandleSize(prefHdl);
881
882         prefRefNum = HOpenResFile(prefVRefNum, prefDirID, prefsInfo.fileName, fsRdWrPerm);
883         if (prefRefNum == -1)
884                 return (ResError());
885                 
886         if ((origHdl = Get1Resource(prefsInfo.resType, prefsInfo.resID)) != nil)
887         {
888                 //¥     Overwrite existing preferences
889                 origSize = GetHandleSize(origHdl);
890                 if (prefSize > origSize)
891                         SetHandleSize(origHdl, prefSize);
892                         
893                 BlockMove(*prefHdl, *origHdl, prefSize);
894                 ChangedResource(origHdl);
895                 WriteResource(origHdl);
896                 ReleaseResource(origHdl);
897         }
898         else
899         {
900                 //¥     Store specified preferences for the first time
901                 AddResource(prefHdl, prefsInfo.resType, prefsInfo.resID, "\p");
902                 WriteResource(prefHdl);
903                 DetachResource(prefHdl);
904         }
905         
906         CloseResFile(prefRefNum);
907
908         if (theErr == noErr)
909                 theErr = ResError();
910         
911         return (theErr);
912 }
913
914 //¥     -------------------------------------------------------------------------------------------
915
916 /*
917
918         This module provides the ability to save and load a preferences file in the
919         Preferences folder in the System Folder.  An optional resource, PRFI 0,
920         is used to provide specifications for the preferences file (creator, etc.).
921
922         Three functions are provided:
923
924                 void InitPrefsFile(OSType creator)
925
926         This function will initialize the preferences file, that is, it will create
927         it in the appropriate place if it doesn't currently exist.  It should be
928         called with the creator code for the application.  Note that the creator
929         code specified in PRFI 0 (if any) will be used only if the creator code
930         passed to this function is '????'.  Without the PRFI 0 resource, the default
931         specifications are:
932
933         File Name: "{Application} Prefs" (i.e., the name of the app plus " Prefs"
934         Creator: the creator passed to InitPrefsFile
935         Type: 'PREF'
936         Pref Resource Type: 'PREF'
937         Pref Resource ID: 0
938
939         The PRFI 0 resource allows you to specify overrides for each of the above
940         values.  This is useful for development, since the application name might
941         go through changes, but the preferences file name is held constant.
942
943                 OSErr LoadPrefsFile(Handle prefsHndl)
944
945         This function will attempt to copy the data stored in the preferences
946         file to the given handle (which must be pre-allocated).  If the handle is too
947         small, then it will be enlarged.  If it is too large, it will not be resized.
948         The data in the preferences file (normally in PREF 0) will then be copied
949         into the handle.  If the preferences file did not exist, the original data
950         in the handle will not change.
951
952                 OSErr SavePrefsFile(Handle prefsHndl)
953
954         This function will attempt to save the given handle to the preferences
955         file.  Its contents will completely replace the previous data (normally
956         the PREF 0 resource).
957
958         In typical use, you will use InitPrefsFile once, then allocate a handle large
959         enough to contain your preferences data, and initialize it with default values.
960         Throughout the course of your program, the handle will undergo modification as
961         user preferences change.  You can use SavePrefsFile anytime to update the
962         preferences file, or wait until program exit to do so.
963
964 */
965
966 int ReadConfigFile()
967 {
968         int i;
969         OSErr err;
970         Handle prefs_handle;
971         Preferences *prefs;
972         char *p;
973         
974         if (!have_prefs) {                      // not initialized....get a handle to the preferences file
975                 InitPrefsFile('DCT2');
976                 have_prefs = 1;
977         }
978         
979         prefs_handle = NewHandleClear(sizeof(Preferences));             // new prefs handle
980         if (prefs_handle == NULL)
981                 return;
982                 
983         prefs = (Preferences *)(*prefs_handle);
984         err = LoadPrefsFile(prefs_handle);
985         if (err) {
986                 DisposeHandle(prefs_handle);
987                 return -1;
988         }
989
990         p = (char *)prefs;
991         for (i = 0; i < sizeof(Preferences); i++) {
992                 if (*p != 0)
993                         break;
994                 p++;
995         }
996         if ( i == sizeof(Preferences) )
997                 return -1;
998         
999         Config_digi_volume = prefs->digi_volume;
1000         Config_midi_volume = prefs->midi_volume;
1001         Config_master_volume = prefs->master_volume;
1002         Config_redbook_volume = prefs->redbook_volume;
1003         Config_channels_reversed = prefs->stereo_reverse;
1004         gr_palette_set_gamma( (int)(prefs->gamma_level) );
1005
1006         Scanline_double = (int)prefs->pixel_double;
1007         if ( PAEnabled )
1008                 Scanline_double = 0;            // can't double with hardware acceleration
1009                 
1010         Detail_level = prefs->detail_level;
1011         if (Detail_level == NUM_DETAIL_LEVELS-1) {
1012                 Object_complexity = prefs->oc;
1013                 Object_detail = prefs->od;
1014                 Wall_detail = prefs->wd;
1015                 Wall_render_depth = prefs->wrd;
1016                 Debris_amount = prefs->da;
1017                 SoundChannels = prefs->sc;
1018                 set_custom_detail_vars();
1019         }
1020
1021         strncpy( config_last_player, prefs->lastplayer, CALLSIGN_LEN );
1022         p = strchr(config_last_player, '\n' );
1023         if (p) *p = 0;
1024         
1025         strncpy(config_last_mission, prefs->lastmission, MISSION_NAME_LEN);
1026         p = strchr(config_last_mission, '\n' );
1027         if (p) *p = 0;
1028
1029         strcpy(config_last_ctb_cfg, prefs->ctb_config);
1030         
1031         if ( Config_digi_volume > 8 ) Config_digi_volume = 8;
1032
1033         if ( Config_midi_volume > 8 ) Config_midi_volume = 8;
1034
1035         joy_set_cal_vals( prefs->joy_axis_min, prefs->joy_axis_center, prefs->joy_axis_max);
1036         digi_set_volume( (Config_digi_volume*256)/8, (Config_midi_volume*256)/8 );
1037         digi_set_master_volume(Config_master_volume);
1038         
1039         gConfigInfo.mDoNotDisplayOptions = prefs->display_dialog;
1040         gConfigInfo.mUse11kSounds = prefs->sound_11k;
1041         gConfigInfo.mDisableSound = prefs->nosound;
1042         gConfigInfo.mDisableMIDIMusic = prefs->nomidi;
1043         gConfigInfo.mChangeResolution = prefs->change_resolution;
1044         gConfigInfo.mDoNotPlayMovies = prefs->no_movies;
1045         gConfigInfo.mGameMonitor = prefs->game_monitor;
1046         gConfigInfo.mAcceleration = prefs->enable_rave;
1047         gConfigInfo.mInputSprockets = prefs->enable_input_sprockets;
1048         
1049         DisposeHandle(prefs_handle);
1050         return 0;
1051 }
1052
1053 int WriteConfigFile()
1054 {
1055         OSErr err;
1056         Handle prefs_handle;
1057         Preferences *prefs;
1058         
1059         prefs_handle = NewHandleClear(sizeof(Preferences));             // new prefs handle
1060         if (prefs_handle == NULL)
1061                 return;
1062                 
1063         prefs = (Preferences *)(*prefs_handle);
1064         
1065         joy_get_cal_vals(prefs->joy_axis_min, prefs->joy_axis_center, prefs->joy_axis_max);
1066         prefs->digi_volume = Config_digi_volume;
1067         prefs->midi_volume = Config_midi_volume;
1068         prefs->stereo_reverse = Config_channels_reversed;
1069         prefs->detail_level = Detail_level;
1070         if (Detail_level == NUM_DETAIL_LEVELS-1) {
1071                 prefs->oc = Object_complexity;
1072                 prefs->od = Object_detail;
1073                 prefs->wd = Wall_detail;
1074                 prefs->wrd = Wall_render_depth;
1075                 prefs->da = Debris_amount;
1076                 prefs->sc = SoundChannels;
1077         }
1078         prefs->gamma_level = (ubyte)gr_palette_get_gamma();
1079
1080         if ( !PAEnabled )
1081                 prefs->pixel_double = (ubyte)Scanline_double;           // hmm..don't write this out if doing hardware accel.
1082                 
1083         strncpy( prefs->lastplayer, Players[Player_num].callsign, CALLSIGN_LEN );
1084         strncpy( prefs->lastmission, config_last_mission, MISSION_NAME_LEN );
1085         strcpy( prefs->ctb_config, config_last_ctb_cfg);
1086         prefs->ctb_tool = config_last_ctb_tool;
1087         prefs->master_volume = Config_master_volume;
1088         prefs->display_dialog = gConfigInfo.mDoNotDisplayOptions;
1089         prefs->change_resolution = gConfigInfo.mChangeResolution;
1090         prefs->nosound = gConfigInfo.mDisableSound;
1091         prefs->nomidi = gConfigInfo.mDisableMIDIMusic;
1092         prefs->sound_11k = gConfigInfo.mUse11kSounds;
1093         prefs->no_movies = gConfigInfo.mDoNotPlayMovies;
1094         prefs->game_monitor = gConfigInfo.mGameMonitor;
1095         prefs->redbook_volume = Config_redbook_volume;
1096         prefs->enable_rave = gConfigInfo.mAcceleration;
1097         prefs->enable_input_sprockets = gConfigInfo.mInputSprockets;
1098
1099         err = SavePrefsFile(prefs_handle);
1100         DisposeHandle(prefs_handle);
1101         return (int)err;
1102 }
1103
1104 #endif
1105