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