1 /*
2 * OpenTyrian: A modern cross-platform port of Tyrian
3 * Copyright (C) 2007-2009 The OpenTyrian Development Team
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 */
19 #include "config.h"
20 #include "episodes.h"
21 #include "file.h"
22 #include "joystick.h"
23 #include "loudness.h"
24 #include "mtrand.h"
25 #include "nortsong.h"
26 #include "opentyr.h"
27 #include "player.h"
28 #include "varz.h"
29 #include "vga256d.h"
30 #include "video.h"
31 #include "video_scale.h"
32
33 #include <unistd.h>
34 #include <sys/stat.h>
35
36
37 /* Configuration Load/Save handler */
38
39 const JE_byte cryptKey[10] = /* [1..10] */
40 {
41 15, 50, 89, 240, 147, 34, 86, 9, 32, 208
42 };
43
44 const JE_KeySettingType defaultKeySettings =
45 {
46 SDLK_UP, SDLK_DOWN, SDLK_LEFT, SDLK_RIGHT, SDLK_SPACE, SDLK_RETURN, SDLK_LCTRL, SDLK_LALT
47 /* 72, 80, 75, 77, 57, 28, 29, 56*/
48 };
49
50 const char defaultHighScoreNames[34][23] = /* [1..34] of string [22] */
51 {/*1P*/
52 /*TYR*/ "The Prime Chair", /*13*/
53 "Transon Lohk",
54 "Javi Onukala",
55 "Mantori",
56 "Nortaneous",
57 "Dougan",
58 "Reid",
59 "General Zinglon",
60 "Late Gyges Phildren",
61 "Vykromod",
62 "Beppo",
63 "Borogar",
64 "ShipMaster Carlos",
65
66 /*OTHER*/ "Jill", /*5*/
67 "Darcy",
68 "Jake Stone",
69 "Malvineous Havershim",
70 "Marta Louise Velasquez",
71
72 /*JAZZ*/ "Jazz Jackrabbit", /*3*/
73 "Eva Earlong",
74 "Devan Shell",
75
76 /*OMF*/ "Crystal Devroe", /*11*/
77 "Steffan Tommas",
78 "Milano Angston",
79 "Christian",
80 "Shirro",
81 "Jean-Paul",
82 "Ibrahim Hothe",
83 "Angel",
84 "Cossette Akira",
85 "Raven",
86 "Hans Kreissack",
87
88 /*DARE*/ "Tyler", /*2*/
89 "Rennis the Rat Guard"
90 };
91
92 const char defaultTeamNames[22][25] = /* [1..22] of string [24] */
93 {
94 "Jackrabbits",
95 "Team Tyrian",
96 "The Elam Brothers",
97 "Dare to Dream Team",
98 "Pinball Freaks",
99 "Extreme Pinball Freaks",
100 "Team Vykromod",
101 "Epic All-Stars",
102 "Hans Keissack's WARriors",
103 "Team Overkill",
104 "Pied Pipers",
105 "Gencore Growlers",
106 "Microsol Masters",
107 "Beta Warriors",
108 "Team Loco",
109 "The Shellians",
110 "Jungle Jills",
111 "Murderous Malvineous",
112 "The Traffic Department",
113 "Clan Mikal",
114 "Clan Patrok",
115 "Carlos' Crawlers"
116 };
117
118
119 const JE_EditorItemAvailType initialItemAvail =
120 {
121 1,1,1,0,0,1,1,0,1,1,1,1,1,0,1,0,1,1,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0, /* Front/Rear Weapons 1-38 */
122 0,0,0,0,0,0,0,0,0,0,1, /* Fill */
123 1,0,0,0,0,1,0,0,0,1,1,0,1,0,0,0,0,0, /* Sidekicks 51-68 */
124 0,0,0,0,0,0,0,0,0,0,0, /* Fill */
125 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* Special Weapons 81-93 */
126 0,0,0,0,0 /* Fill */
127 };
128
129 /* Last 2 bytes = Word
130 *
131 * Max Value = 1680
132 * X div 60 = Armor (1-28)
133 * X div 168 = Shield (1-12)
134 * X div 280 = Engine (1-06)
135 */
136
137
138 JE_boolean smoothies[9] = /* [1..9] */
139 { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
140
141 JE_byte starShowVGASpecialCode;
142
143 /* CubeData */
144 JE_word lastCubeMax, cubeMax;
145 JE_word cubeList[4]; /* [1..4] */
146
147 /* High-Score Stuff */
148 JE_boolean gameHasRepeated; // can only get highscore on first play-through
149
150 /* Difficulty */
151 JE_shortint difficultyLevel, oldDifficultyLevel,
152 initialDifficulty; // can only get highscore on initial episode
153
154 /* Player Stuff */
155 uint power, lastPower, powerAdd;
156 JE_byte shieldWait, shieldT;
157
158 JE_byte shotRepeat[11], shotMultiPos[11];
159 JE_boolean portConfigChange, portConfigDone;
160
161 /* Level Data */
162 char lastLevelName[11], levelName[11]; /* string [10] */
163 JE_byte mainLevel, nextLevel, saveLevel; /*Current Level #*/
164
165 /* Keyboard Junk */
166 JE_KeySettingType keySettings;
167
168 /* Configuration */
169 JE_shortint levelFilter, levelFilterNew, levelBrightness, levelBrightnessChg;
170 JE_boolean filtrationAvail, filterActive, filterFade, filterFadeStart;
171
172 JE_boolean gameJustLoaded;
173
174 JE_boolean galagaMode;
175
176 JE_boolean extraGame;
177
178 JE_boolean twoPlayerMode, twoPlayerLinked, onePlayerAction, superTyrian;
179 JE_boolean trentWin = false;
180 JE_byte superArcadeMode;
181
182 JE_byte superArcadePowerUp;
183
184 JE_real linkGunDirec;
185 JE_byte inputDevice[2] = { 1, 2 }; // 0:any 1:keyboard 2:mouse 3+:joystick
186
187 JE_byte secretHint;
188 JE_byte background3over;
189 JE_byte background2over;
190 JE_byte gammaCorrection;
191 JE_boolean superPause = false;
192 JE_boolean explosionTransparent,
193 youAreCheating,
194 displayScore,
195 background2, smoothScroll, wild, superWild, starActive,
196 topEnemyOver,
197 skyEnemyOverAll,
198 background2notTransparent;
199
200 JE_byte soundEffects; // dummy value for config
201 JE_byte versionNum; /* SW 1.0 and SW/Reg 1.1 = 0 or 1
202 * EA 1.2 = 2 */
203
204 JE_byte fastPlay;
205 JE_boolean pentiumMode;
206
207 /* Savegame files */
208 JE_byte gameSpeed;
209 JE_byte processorType; /* 1=386 2=486 3=Pentium Hyper */
210
211 JE_SaveFilesType saveFiles; /*array[1..saveLevelnum] of savefiletype;*/
212 JE_SaveGameTemp saveTemp;
213
214 JE_word editorLevel; /*Initial value 800*/
215
216
load_json(const char * filename)217 cJSON *load_json( const char *filename )
218 {
219 FILE *f = dir_fopen_warn(get_user_directory(), filename, "rb");
220 if (f == NULL)
221 return NULL;
222
223 size_t buffer_len = ftell_eof(f);
224 char *buffer = malloc(buffer_len + 1);
225
226 fread(buffer, 1, buffer_len, f);
227 buffer[buffer_len] = '\0';
228
229 fclose(f);
230
231 cJSON *root = cJSON_Parse(buffer);
232
233 free(buffer);
234
235 return root;
236 }
237
save_json(cJSON * root,const char * filename)238 void save_json( cJSON *root, const char *filename )
239 {
240 FILE *f = dir_fopen_warn(get_user_directory(), filename, "w+");
241 if (f == NULL)
242 return;
243
244 char *buffer = cJSON_Print(root);
245
246 if (buffer != NULL)
247 {
248 fputs(buffer, f);
249 free(buffer);
250 }
251
252 fclose(f);
253 }
254
load_opentyrian_config(void)255 bool load_opentyrian_config( void )
256 {
257 // defaults
258 fullscreen_enabled = false;
259 set_scaler_by_name("Scale2x");
260
261 cJSON *root = load_json("opentyrian.conf");
262 if (root == NULL)
263 return false;
264
265 cJSON *section = cJSON_GetObjectItem(root, "video");
266 if (section != NULL)
267 {
268 cJSON *setting;
269
270 if ((setting = cJSON_GetObjectItem(section, "fullscreen")))
271 fullscreen_enabled = (setting->type == cJSON_True);
272
273 if ((setting = cJSON_GetObjectItem(section, "scaler")))
274 set_scaler_by_name(setting->valuestring);
275 }
276
277 cJSON_Delete(root);
278
279 return true;
280 }
281
save_opentyrian_config(void)282 bool save_opentyrian_config( void )
283 {
284 cJSON *root = load_json("opentyrian.conf");
285 if (root == NULL)
286 root = cJSON_CreateObject();
287
288 cJSON *section;
289
290 section = cJSON_CreateOrGetObjectItem(root, "video");
291 cJSON_ForceType(section, cJSON_Object);
292
293 {
294 cJSON *setting;
295
296 setting = cJSON_CreateOrGetObjectItem(section, "fullscreen");
297 cJSON_SetBoolean(setting, fullscreen_enabled);
298
299 setting = cJSON_CreateOrGetObjectItem(section, "scaler");
300 cJSON_SetString(setting, scalers[scaler].name);
301 }
302
303 save_json(root, "opentyrian.conf");
304
305 cJSON_Delete(root);
306
307 return true;
308 }
309
playeritems_to_pitems(JE_PItemsType pItems,PlayerItems * items,JE_byte initial_episode_num)310 static void playeritems_to_pitems( JE_PItemsType pItems, PlayerItems *items, JE_byte initial_episode_num )
311 {
312 pItems[0] = items->weapon[FRONT_WEAPON].id;
313 pItems[1] = items->weapon[REAR_WEAPON].id;
314 pItems[2] = items->super_arcade_mode;
315 pItems[3] = items->sidekick[LEFT_SIDEKICK];
316 pItems[4] = items->sidekick[RIGHT_SIDEKICK];
317 pItems[5] = items->generator;
318 pItems[6] = items->sidekick_level;
319 pItems[7] = items->sidekick_series;
320 pItems[8] = initial_episode_num;
321 pItems[9] = items->shield;
322 pItems[10] = items->special;
323 pItems[11] = items->ship;
324 }
325
pitems_to_playeritems(PlayerItems * items,JE_PItemsType pItems,JE_byte * initial_episode_num)326 static void pitems_to_playeritems( PlayerItems *items, JE_PItemsType pItems, JE_byte *initial_episode_num )
327 {
328 items->weapon[FRONT_WEAPON].id = pItems[0];
329 items->weapon[REAR_WEAPON].id = pItems[1];
330 items->super_arcade_mode = pItems[2];
331 items->sidekick[LEFT_SIDEKICK] = pItems[3];
332 items->sidekick[RIGHT_SIDEKICK] = pItems[4];
333 items->generator = pItems[5];
334 items->sidekick_level = pItems[6];
335 items->sidekick_series = pItems[7];
336 if (initial_episode_num != NULL)
337 *initial_episode_num = pItems[8];
338 items->shield = pItems[9];
339 items->special = pItems[10];
340 items->ship = pItems[11];
341 }
342
JE_saveGame(JE_byte slot,const char * name)343 void JE_saveGame( JE_byte slot, const char *name )
344 {
345 saveFiles[slot-1].initialDifficulty = initialDifficulty;
346 saveFiles[slot-1].gameHasRepeated = gameHasRepeated;
347 saveFiles[slot-1].level = saveLevel;
348
349 if (superTyrian)
350 player[0].items.super_arcade_mode = SA_SUPERTYRIAN;
351 else if (superArcadeMode == SA_NONE && onePlayerAction)
352 player[0].items.super_arcade_mode = SA_ARCADE;
353 else
354 player[0].items.super_arcade_mode = superArcadeMode;
355
356 playeritems_to_pitems(saveFiles[slot-1].items, &player[0].items, initial_episode_num);
357
358 if (twoPlayerMode)
359 playeritems_to_pitems(saveFiles[slot-1].lastItems, &player[1].items, 0);
360 else
361 playeritems_to_pitems(saveFiles[slot-1].lastItems, &player[0].last_items, 0);
362
363 saveFiles[slot-1].score = player[0].cash;
364 saveFiles[slot-1].score2 = player[1].cash;
365
366 memcpy(&saveFiles[slot-1].levelName, &lastLevelName, sizeof(lastLevelName));
367 saveFiles[slot-1].cubes = lastCubeMax;
368
369 if (strcmp(lastLevelName, "Completed") == 0)
370 {
371 temp = episodeNum - 1;
372 if (temp < 1)
373 {
374 temp = EPISODE_AVAILABLE; /* JE: {Episodemax is 4 for completion purposes} */
375 }
376 saveFiles[slot-1].episode = temp;
377 }
378 else
379 {
380 saveFiles[slot-1].episode = episodeNum;
381 }
382
383 saveFiles[slot-1].difficulty = difficultyLevel;
384 saveFiles[slot-1].secretHint = secretHint;
385 saveFiles[slot-1].input1 = inputDevice[0];
386 saveFiles[slot-1].input2 = inputDevice[1];
387
388 strcpy(saveFiles[slot-1].name, name);
389
390 for (uint port = 0; port < 2; ++port)
391 {
392 // if two-player, use first player's front and second player's rear weapon
393 saveFiles[slot-1].power[port] = player[twoPlayerMode ? port : 0].items.weapon[port].power;
394 }
395
396 JE_saveConfiguration();
397 }
398
JE_loadGame(JE_byte slot)399 void JE_loadGame( JE_byte slot )
400 {
401 superTyrian = false;
402 onePlayerAction = false;
403 twoPlayerMode = false;
404 extraGame = false;
405 galagaMode = false;
406
407 initialDifficulty = saveFiles[slot-1].initialDifficulty;
408 gameHasRepeated = saveFiles[slot-1].gameHasRepeated;
409 twoPlayerMode = (slot-1) > 10;
410 difficultyLevel = saveFiles[slot-1].difficulty;
411
412 pitems_to_playeritems(&player[0].items, saveFiles[slot-1].items, &initial_episode_num);
413
414 superArcadeMode = player[0].items.super_arcade_mode;
415
416 if (superArcadeMode == SA_SUPERTYRIAN)
417 superTyrian = true;
418 if (superArcadeMode != SA_NONE)
419 onePlayerAction = true;
420 if (superArcadeMode > SA_NORTSHIPZ)
421 superArcadeMode = SA_NONE;
422
423 if (twoPlayerMode)
424 {
425 onePlayerAction = false;
426
427 pitems_to_playeritems(&player[1].items, saveFiles[slot-1].lastItems, NULL);
428 }
429 else
430 {
431 pitems_to_playeritems(&player[0].last_items, saveFiles[slot-1].lastItems, NULL);
432 }
433
434 /* Compatibility with old version */
435 if (player[1].items.sidekick_level < 101)
436 {
437 player[1].items.sidekick_level = 101;
438 player[1].items.sidekick_series = player[1].items.sidekick[LEFT_SIDEKICK];
439 }
440
441 player[0].cash = saveFiles[slot-1].score;
442 player[1].cash = saveFiles[slot-1].score2;
443
444 mainLevel = saveFiles[slot-1].level;
445 cubeMax = saveFiles[slot-1].cubes;
446 lastCubeMax = cubeMax;
447
448 secretHint = saveFiles[slot-1].secretHint;
449 inputDevice[0] = saveFiles[slot-1].input1;
450 inputDevice[1] = saveFiles[slot-1].input2;
451
452 for (uint port = 0; port < 2; ++port)
453 {
454 // if two-player, use first player's front and second player's rear weapon
455 player[twoPlayerMode ? port : 0].items.weapon[port].power = saveFiles[slot-1].power[port];
456 }
457
458 int episode = saveFiles[slot-1].episode;
459
460 memcpy(&levelName, &saveFiles[slot-1].levelName, sizeof(levelName));
461
462 if (strcmp(levelName, "Completed") == 0)
463 {
464 if (episode == EPISODE_AVAILABLE)
465 {
466 episode = 1;
467 } else if (episode < EPISODE_AVAILABLE) {
468 episode++;
469 }
470 /* Increment episode. Episode EPISODE_AVAILABLE goes to 1. */
471 }
472
473 JE_initEpisode(episode);
474 saveLevel = mainLevel;
475 memcpy(&lastLevelName, &levelName, sizeof(levelName));
476 }
477
JE_initProcessorType(void)478 void JE_initProcessorType( void )
479 {
480 /* SYN: Originally this proc looked at your hardware specs and chose appropriate options. We don't care, so I'll just set
481 decent defaults here. */
482
483 wild = false;
484 superWild = false;
485 smoothScroll = true;
486 explosionTransparent = true;
487 filtrationAvail = false;
488 background2 = true;
489 displayScore = true;
490
491 switch (processorType)
492 {
493 case 1: /* 386 */
494 background2 = false;
495 displayScore = false;
496 explosionTransparent = false;
497 break;
498 case 2: /* 486 - Default */
499 break;
500 case 3: /* High Detail */
501 smoothScroll = false;
502 break;
503 case 4: /* Pentium */
504 wild = true;
505 filtrationAvail = true;
506 break;
507 case 5: /* Nonstandard VGA */
508 smoothScroll = false;
509 break;
510 case 6: /* SuperWild */
511 wild = true;
512 superWild = true;
513 filtrationAvail = true;
514 break;
515 }
516
517 switch (gameSpeed)
518 {
519 case 1: /* Slug Mode */
520 fastPlay = 3;
521 break;
522 case 2: /* Slower */
523 fastPlay = 4;
524 break;
525 case 3: /* Slow */
526 fastPlay = 5;
527 break;
528 case 4: /* Normal */
529 fastPlay = 0;
530 break;
531 case 5: /* Pentium Hyper */
532 fastPlay = 1;
533 break;
534 }
535
536 }
537
JE_setNewGameSpeed(void)538 void JE_setNewGameSpeed( void )
539 {
540 pentiumMode = false;
541
542 switch (fastPlay)
543 {
544 case 0:
545 speed = 0x4300;
546 smoothScroll = true;
547 frameCountMax = 2;
548 break;
549 case 1:
550 speed = 0x3000;
551 smoothScroll = true;
552 frameCountMax = 2;
553 break;
554 case 2:
555 speed = 0x2000;
556 smoothScroll = false;
557 frameCountMax = 2;
558 break;
559 case 3:
560 speed = 0x5300;
561 smoothScroll = true;
562 frameCountMax = 4;
563 break;
564 case 4:
565 speed = 0x4300;
566 smoothScroll = true;
567 frameCountMax = 3;
568 break;
569 case 5:
570 speed = 0x4300;
571 smoothScroll = true;
572 frameCountMax = 2;
573 pentiumMode = true;
574 break;
575 }
576
577 frameCount = frameCountMax;
578 JE_resetTimerInt();
579 JE_setTimerInt();
580 }
581
JE_encryptSaveTemp(void)582 void JE_encryptSaveTemp( void )
583 {
584 JE_SaveGameTemp s3;
585 JE_word x;
586 JE_byte y;
587
588 memcpy(&s3, &saveTemp, sizeof(s3));
589
590 y = 0;
591 for (x = 0; x < SAVE_FILE_SIZE; x++)
592 {
593 y += s3[x];
594 }
595 saveTemp[SAVE_FILE_SIZE] = y;
596
597 y = 0;
598 for (x = 0; x < SAVE_FILE_SIZE; x++)
599 {
600 y -= s3[x];
601 }
602 saveTemp[SAVE_FILE_SIZE+1] = y;
603
604 y = 1;
605 for (x = 0; x < SAVE_FILE_SIZE; x++)
606 {
607 y = (y * s3[x]) + 1;
608 }
609 saveTemp[SAVE_FILE_SIZE+2] = y;
610
611 y = 0;
612 for (x = 0; x < SAVE_FILE_SIZE; x++)
613 {
614 y = y ^ s3[x];
615 }
616 saveTemp[SAVE_FILE_SIZE+3] = y;
617
618 for (x = 0; x < SAVE_FILE_SIZE; x++)
619 {
620 saveTemp[x] = saveTemp[x] ^ cryptKey[(x+1) % 10];
621 if (x > 0)
622 {
623 saveTemp[x] = saveTemp[x] ^ saveTemp[x - 1];
624 }
625 }
626 }
627
JE_decryptSaveTemp(void)628 void JE_decryptSaveTemp( void )
629 {
630 JE_boolean correct = true;
631 JE_SaveGameTemp s2;
632 int x;
633 JE_byte y;
634
635 /* Decrypt save game file */
636 for (x = (SAVE_FILE_SIZE - 1); x >= 0; x--)
637 {
638 s2[x] = (JE_byte)saveTemp[x] ^ (JE_byte)(cryptKey[(x+1) % 10]);
639 if (x > 0)
640 {
641 s2[x] ^= (JE_byte)saveTemp[x - 1];
642 }
643
644 }
645
646 /* for (x = 0; x < SAVE_FILE_SIZE; x++) printf("%c", s2[x]); */
647
648 /* Check save file for correctitude */
649 y = 0;
650 for (x = 0; x < SAVE_FILE_SIZE; x++)
651 {
652 y += s2[x];
653 }
654 if (saveTemp[SAVE_FILE_SIZE] != y)
655 {
656 correct = false;
657 printf("Failed additive checksum: %d vs %d\n", saveTemp[SAVE_FILE_SIZE], y);
658 }
659
660 y = 0;
661 for (x = 0; x < SAVE_FILE_SIZE; x++)
662 {
663 y -= s2[x];
664 }
665 if (saveTemp[SAVE_FILE_SIZE+1] != y)
666 {
667 correct = false;
668 printf("Failed subtractive checksum: %d vs %d\n", saveTemp[SAVE_FILE_SIZE+1], y);
669 }
670
671 y = 1;
672 for (x = 0; x < SAVE_FILE_SIZE; x++)
673 {
674 y = (y * s2[x]) + 1;
675 }
676 if (saveTemp[SAVE_FILE_SIZE+2] != y)
677 {
678 correct = false;
679 printf("Failed multiplicative checksum: %d vs %d\n", saveTemp[SAVE_FILE_SIZE+2], y);
680 }
681
682 y = 0;
683 for (x = 0; x < SAVE_FILE_SIZE; x++)
684 {
685 y = y ^ s2[x];
686 }
687 if (saveTemp[SAVE_FILE_SIZE+3] != y)
688 {
689 correct = false;
690 printf("Failed XOR'd checksum: %d vs %d\n", saveTemp[SAVE_FILE_SIZE+3], y);
691 }
692
693 /* Barf and die if save file doesn't validate */
694 if (!correct)
695 {
696 fprintf(stderr, "Error reading save file!\n");
697 exit(255);
698 }
699
700 /* Keep decrypted version plz */
701 memcpy(&saveTemp, &s2, sizeof(s2));
702 }
703
704 #ifndef TARGET_MACOSX
get_user_directory(void)705 const char *get_user_directory( void )
706 {
707 static char user_dir[500] = "";
708
709 if (strlen(user_dir) == 0)
710 {
711 #ifdef TARGET_UNIX
712 if (getenv("HOME"))
713 snprintf(user_dir, sizeof(user_dir), "%s/.opentyrian", getenv("HOME"));
714 #else
715 strcpy(user_dir, ".");
716 #endif // TARGET_UNIX
717 }
718
719 return user_dir;
720 }
721 #endif // TARGET_MACOSX
722
723 // for compatibility
724 Uint8 joyButtonAssign[4] = {1, 4, 5, 5};
725 Uint8 inputDevice_ = 0, jConfigure = 0, midiPort = 1;
726
JE_loadConfiguration(void)727 void JE_loadConfiguration( void )
728 {
729 FILE *fi;
730 int z;
731 JE_byte *p;
732 int y;
733
734 fi = dir_fopen_warn(get_user_directory(), "tyrian.cfg", "rb");
735 if (fi && ftell_eof(fi) == 20 + sizeof(keySettings))
736 {
737 /* SYN: I've hardcoded the sizes here because the .CFG file format is fixed
738 anyways, so it's not like they'll change. */
739 background2 = 0;
740 efread(&background2, 1, 1, fi);
741 efread(&gameSpeed, 1, 1, fi);
742
743 efread(&inputDevice_, 1, 1, fi);
744 efread(&jConfigure, 1, 1, fi);
745
746 efread(&versionNum, 1, 1, fi);
747
748 efread(&processorType, 1, 1, fi);
749 efread(&midiPort, 1, 1, fi);
750 efread(&soundEffects, 1, 1, fi);
751 efread(&gammaCorrection, 1, 1, fi);
752 efread(&difficultyLevel, 1, 1, fi);
753
754 efread(joyButtonAssign, 1, 4, fi);
755
756 efread(&tyrMusicVolume, 2, 1, fi);
757 efread(&fxVolume, 2, 1, fi);
758
759 efread(inputDevice, 1, 2, fi);
760
761 efread(keySettings, sizeof(*keySettings), COUNTOF(keySettings), fi);
762
763 fclose(fi);
764 }
765 else
766 {
767 printf("\nInvalid or missing TYRIAN.CFG! Continuing using defaults.\n\n");
768
769 soundEffects = 1;
770 memcpy(&keySettings, &defaultKeySettings, sizeof(keySettings));
771 background2 = true;
772 tyrMusicVolume = fxVolume = 128;
773 gammaCorrection = 0;
774 processorType = 3;
775 gameSpeed = 4;
776 }
777
778 load_opentyrian_config();
779
780 if (tyrMusicVolume > 255)
781 tyrMusicVolume = 255;
782 if (fxVolume > 255)
783 fxVolume = 255;
784
785 JE_calcFXVol();
786
787 set_volume(tyrMusicVolume, fxVolume);
788
789 fi = dir_fopen_warn(get_user_directory(), "tyrian.sav", "rb");
790 if (fi)
791 {
792
793 fseek(fi, 0, SEEK_SET);
794 efread(saveTemp, 1, sizeof(saveTemp), fi);
795 JE_decryptSaveTemp();
796
797 /* SYN: The original mostly blasted the save file into raw memory. However, our lives are not so
798 easy, because the C struct is necessarily a different size. So instead we have to loop
799 through each record and load fields manually. *emo tear* :'( */
800
801 p = saveTemp;
802 for (z = 0; z < SAVE_FILES_NUM; z++)
803 {
804 memcpy(&saveFiles[z].encode, p, sizeof(JE_word)); p += 2;
805 saveFiles[z].encode = SDL_SwapLE16(saveFiles[z].encode);
806
807 memcpy(&saveFiles[z].level, p, sizeof(JE_word)); p += 2;
808 saveFiles[z].level = SDL_SwapLE16(saveFiles[z].level);
809
810 memcpy(&saveFiles[z].items, p, sizeof(JE_PItemsType)); p += sizeof(JE_PItemsType);
811
812 memcpy(&saveFiles[z].score, p, sizeof(JE_longint)); p += 4;
813 saveFiles[z].score = SDL_SwapLE32(saveFiles[z].score);
814
815 memcpy(&saveFiles[z].score2, p, sizeof(JE_longint)); p += 4;
816 saveFiles[z].score2 = SDL_SwapLE32(saveFiles[z].score2);
817
818 /* SYN: Pascal strings are prefixed by a byte holding the length! */
819 memset(&saveFiles[z].levelName, 0, sizeof(saveFiles[z].levelName));
820 memcpy(&saveFiles[z].levelName, &p[1], *p);
821 p += 10;
822
823 /* This was a BYTE array, not a STRING, in the original. Go fig. */
824 memcpy(&saveFiles[z].name, p, 14);
825 p += 14;
826
827 memcpy(&saveFiles[z].cubes, p, sizeof(JE_byte)); p++;
828 memcpy(&saveFiles[z].power, p, sizeof(JE_byte) * 2); p += 2;
829 memcpy(&saveFiles[z].episode, p, sizeof(JE_byte)); p++;
830 memcpy(&saveFiles[z].lastItems, p, sizeof(JE_PItemsType)); p += sizeof(JE_PItemsType);
831 memcpy(&saveFiles[z].difficulty, p, sizeof(JE_byte)); p++;
832 memcpy(&saveFiles[z].secretHint, p, sizeof(JE_byte)); p++;
833 memcpy(&saveFiles[z].input1, p, sizeof(JE_byte)); p++;
834 memcpy(&saveFiles[z].input2, p, sizeof(JE_byte)); p++;
835
836 /* booleans were 1 byte in pascal -- working around it */
837 Uint8 temp;
838 memcpy(&temp, p, 1); p++;
839 saveFiles[z].gameHasRepeated = temp != 0;
840
841 memcpy(&saveFiles[z].initialDifficulty, p, sizeof(JE_byte)); p++;
842
843 memcpy(&saveFiles[z].highScore1, p, sizeof(JE_longint)); p += 4;
844 saveFiles[z].highScore1 = SDL_SwapLE32(saveFiles[z].highScore1);
845
846 memcpy(&saveFiles[z].highScore2, p, sizeof(JE_longint)); p += 4;
847 saveFiles[z].highScore2 = SDL_SwapLE32(saveFiles[z].highScore2);
848
849 memset(&saveFiles[z].highScoreName, 0, sizeof(saveFiles[z].highScoreName));
850 memcpy(&saveFiles[z].highScoreName, &p[1], *p);
851 p += 30;
852
853 memcpy(&saveFiles[z].highScoreDiff, p, sizeof(JE_byte)); p++;
854 }
855
856 /* SYN: This is truncating to bytes. I have no idea what this is doing or why. */
857 /* TODO: Figure out what this is about and make sure it isn't broked. */
858 editorLevel = (saveTemp[SIZEOF_SAVEGAMETEMP - 5] << 8) | saveTemp[SIZEOF_SAVEGAMETEMP - 6];
859
860 fclose(fi);
861 } else {
862 /* We didn't have a save file! Let's make up random stuff! */
863 editorLevel = 800;
864
865 for (z = 0; z < 100; z++)
866 {
867 saveTemp[SAVE_FILES_SIZE + z] = initialItemAvail[z];
868 }
869
870 for (z = 0; z < SAVE_FILES_NUM; z++)
871 {
872 saveFiles[z].level = 0;
873
874 for (y = 0; y < 14; y++)
875 {
876 saveFiles[z].name[y] = ' ';
877 }
878 saveFiles[z].name[14] = 0;
879
880 saveFiles[z].highScore1 = ((mt_rand() % 20) + 1) * 1000;
881
882 if (z % 6 > 2)
883 {
884 saveFiles[z].highScore2 = ((mt_rand() % 20) + 1) * 1000;
885 strcpy(saveFiles[z].highScoreName, defaultTeamNames[mt_rand() % 22]);
886 } else {
887 strcpy(saveFiles[z].highScoreName, defaultHighScoreNames[mt_rand() % 34]);
888 }
889 }
890 }
891
892 JE_initProcessorType();
893 }
894
JE_saveConfiguration(void)895 void JE_saveConfiguration( void )
896 {
897 #ifdef TARGET_UNIX
898 if (getenv("HOME"))
899 {
900 char dir[1000];
901 snprintf(dir, sizeof(dir), "%s/.opentyrian", getenv("HOME"));
902 mkdir(dir, 0755);
903 }
904 #endif /* HOME */
905
906 FILE *f;
907 JE_byte *p;
908 int z;
909
910 p = saveTemp;
911 for (z = 0; z < SAVE_FILES_NUM; z++)
912 {
913 JE_SaveFileType tempSaveFile;
914 memcpy(&tempSaveFile, &saveFiles[z], sizeof(tempSaveFile));
915
916 tempSaveFile.encode = SDL_SwapLE16(tempSaveFile.encode);
917 memcpy(p, &tempSaveFile.encode, sizeof(JE_word)); p += 2;
918
919 tempSaveFile.level = SDL_SwapLE16(tempSaveFile.level);
920 memcpy(p, &tempSaveFile.level, sizeof(JE_word)); p += 2;
921
922 memcpy(p, &tempSaveFile.items, sizeof(JE_PItemsType)); p += sizeof(JE_PItemsType);
923
924 tempSaveFile.score = SDL_SwapLE32(tempSaveFile.score);
925 memcpy(p, &tempSaveFile.score, sizeof(JE_longint)); p += 4;
926
927 tempSaveFile.score2 = SDL_SwapLE32(tempSaveFile.score2);
928 memcpy(p, &tempSaveFile.score2, sizeof(JE_longint)); p += 4;
929
930 /* SYN: Pascal strings are prefixed by a byte holding the length! */
931 memset(p, 0, sizeof(tempSaveFile.levelName));
932 *p = strlen(tempSaveFile.levelName);
933 memcpy(&p[1], &tempSaveFile.levelName, *p);
934 p += 10;
935
936 /* This was a BYTE array, not a STRING, in the original. Go fig. */
937 memcpy(p, &tempSaveFile.name, 14);
938 p += 14;
939
940 memcpy(p, &tempSaveFile.cubes, sizeof(JE_byte)); p++;
941 memcpy(p, &tempSaveFile.power, sizeof(JE_byte) * 2); p += 2;
942 memcpy(p, &tempSaveFile.episode, sizeof(JE_byte)); p++;
943 memcpy(p, &tempSaveFile.lastItems, sizeof(JE_PItemsType)); p += sizeof(JE_PItemsType);
944 memcpy(p, &tempSaveFile.difficulty, sizeof(JE_byte)); p++;
945 memcpy(p, &tempSaveFile.secretHint, sizeof(JE_byte)); p++;
946 memcpy(p, &tempSaveFile.input1, sizeof(JE_byte)); p++;
947 memcpy(p, &tempSaveFile.input2, sizeof(JE_byte)); p++;
948
949 /* booleans were 1 byte in pascal -- working around it */
950 Uint8 temp = tempSaveFile.gameHasRepeated != false;
951 memcpy(p, &temp, 1); p++;
952
953 memcpy(p, &tempSaveFile.initialDifficulty, sizeof(JE_byte)); p++;
954
955 tempSaveFile.highScore1 = SDL_SwapLE32(tempSaveFile.highScore1);
956 memcpy(p, &tempSaveFile.highScore1, sizeof(JE_longint)); p += 4;
957
958 tempSaveFile.highScore2 = SDL_SwapLE32(tempSaveFile.highScore2);
959 memcpy(p, &tempSaveFile.highScore2, sizeof(JE_longint)); p += 4;
960
961 memset(p, 0, sizeof(tempSaveFile.highScoreName));
962 *p = strlen(tempSaveFile.highScoreName);
963 memcpy(&p[1], &tempSaveFile.highScoreName, *p);
964 p += 30;
965
966 memcpy(p, &tempSaveFile.highScoreDiff, sizeof(JE_byte)); p++;
967 }
968
969 saveTemp[SIZEOF_SAVEGAMETEMP - 6] = editorLevel >> 8;
970 saveTemp[SIZEOF_SAVEGAMETEMP - 5] = editorLevel;
971
972 JE_encryptSaveTemp();
973
974 f = dir_fopen_warn(get_user_directory(), "tyrian.sav", "wb");
975 if (f)
976 {
977 efwrite(saveTemp, 1, sizeof(saveTemp), f);
978 fclose(f);
979 #if (_BSD_SOURCE || _XOPEN_SOURCE >= 500)
980 sync();
981 #endif
982 }
983 JE_decryptSaveTemp();
984
985 f = dir_fopen_warn(get_user_directory(), "tyrian.cfg", "wb");
986 if (f)
987 {
988 efwrite(&background2, 1, 1, f);
989 efwrite(&gameSpeed, 1, 1, f);
990
991 efwrite(&inputDevice_, 1, 1, f);
992 efwrite(&jConfigure, 1, 1, f);
993
994 efwrite(&versionNum, 1, 1, f);
995 efwrite(&processorType, 1, 1, f);
996 efwrite(&midiPort, 1, 1, f);
997 efwrite(&soundEffects, 1, 1, f);
998 efwrite(&gammaCorrection, 1, 1, f);
999 efwrite(&difficultyLevel, 1, 1, f);
1000 efwrite(joyButtonAssign, 1, 4, f);
1001
1002 efwrite(&tyrMusicVolume, 2, 1, f);
1003 efwrite(&fxVolume, 2, 1, f);
1004
1005 efwrite(inputDevice, 1, 2, f);
1006
1007 efwrite(keySettings, sizeof(*keySettings), COUNTOF(keySettings), f);
1008
1009 fclose(f);
1010 }
1011
1012 save_opentyrian_config();
1013
1014 #if (_BSD_SOURCE || _XOPEN_SOURCE >= 500)
1015 sync();
1016 #endif
1017 }
1018
1019