Files
commandergenius/project/jni/application/opentyrian/src/config.c

1025 lines
26 KiB
C

/*
* OpenTyrian: A modern cross-platform port of Tyrian
* Copyright (C) 2007-2009 The OpenTyrian Development Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "config.h"
#include "episodes.h"
#include "file.h"
#include "joystick.h"
#include "loudness.h"
#include "mtrand.h"
#include "nortsong.h"
#include "opentyr.h"
#include "player.h"
#include "varz.h"
#include "vga256d.h"
#include "video.h"
#include "video_scale.h"
#include <unistd.h>
#include <sys/stat.h>
/* Configuration Load/Save handler */
const JE_byte cryptKey[10] = /* [1..10] */
{
15, 50, 89, 240, 147, 34, 86, 9, 32, 208
};
const JE_KeySettingType defaultKeySettings =
{
SDLK_UP, SDLK_DOWN, SDLK_LEFT, SDLK_RIGHT, SDLK_SPACE, SDLK_RETURN, SDLK_LCTRL, SDLK_LALT
/* 72, 80, 75, 77, 57, 28, 29, 56*/
};
const char defaultHighScoreNames[34][23] = /* [1..34] of string [22] */
{/*1P*/
/*TYR*/ "The Prime Chair", /*13*/
"Transon Lohk",
"Javi Onukala",
"Mantori",
"Nortaneous",
"Dougan",
"Reid",
"General Zinglon",
"Late Gyges Phildren",
"Vykromod",
"Beppo",
"Borogar",
"ShipMaster Carlos",
/*OTHER*/ "Jill", /*5*/
"Darcy",
"Jake Stone",
"Malvineous Havershim",
"Marta Louise Velasquez",
/*JAZZ*/ "Jazz Jackrabbit", /*3*/
"Eva Earlong",
"Devan Shell",
/*OMF*/ "Crystal Devroe", /*11*/
"Steffan Tommas",
"Milano Angston",
"Christian",
"Shirro",
"Jean-Paul",
"Ibrahim Hothe",
"Angel",
"Cossette Akira",
"Raven",
"Hans Kreissack",
/*DARE*/ "Tyler", /*2*/
"Rennis the Rat Guard"
};
const char defaultTeamNames[22][25] = /* [1..22] of string [24] */
{
"Jackrabbits",
"Team Tyrian",
"The Elam Brothers",
"Dare to Dream Team",
"Pinball Freaks",
"Extreme Pinball Freaks",
"Team Vykromod",
"Epic All-Stars",
"Hans Keissack's WARriors",
"Team Overkill",
"Pied Pipers",
"Gencore Growlers",
"Microsol Masters",
"Beta Warriors",
"Team Loco",
"The Shellians",
"Jungle Jills",
"Murderous Malvineous",
"The Traffic Department",
"Clan Mikal",
"Clan Patrok",
"Carlos' Crawlers"
};
const JE_EditorItemAvailType initialItemAvail =
{
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 */
0,0,0,0,0,0,0,0,0,0,1, /* Fill */
1,0,0,0,0,1,0,0,0,1,1,0,1,0,0,0,0,0, /* Sidekicks 51-68 */
0,0,0,0,0,0,0,0,0,0,0, /* Fill */
1,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* Special Weapons 81-93 */
0,0,0,0,0 /* Fill */
};
/* Last 2 bytes = Word
*
* Max Value = 1680
* X div 60 = Armor (1-28)
* X div 168 = Shield (1-12)
* X div 280 = Engine (1-06)
*/
JE_boolean smoothies[9] = /* [1..9] */
{ 0, 0, 0, 0, 0, 0, 0, 0, 0 };
JE_byte starShowVGASpecialCode;
/* CubeData */
JE_word lastCubeMax, cubeMax;
JE_word cubeList[4]; /* [1..4] */
/* High-Score Stuff */
JE_boolean gameHasRepeated; // can only get highscore on first play-through
/* Difficulty */
JE_shortint difficultyLevel, oldDifficultyLevel,
initialDifficulty; // can only get highscore on initial episode
/* Player Stuff */
uint power, lastPower, powerAdd;
JE_byte shieldWait, shieldT;
JE_byte shotRepeat[11], shotMultiPos[11];
JE_boolean portConfigChange, portConfigDone;
/* Level Data */
char lastLevelName[11], levelName[11]; /* string [10] */
JE_byte mainLevel, nextLevel, saveLevel; /*Current Level #*/
/* Keyboard Junk */
JE_KeySettingType keySettings;
/* Configuration */
JE_shortint levelFilter, levelFilterNew, levelBrightness, levelBrightnessChg;
JE_boolean filtrationAvail, filterActive, filterFade, filterFadeStart;
JE_boolean gameJustLoaded;
JE_boolean galagaMode;
JE_boolean extraGame;
JE_boolean twoPlayerMode, twoPlayerLinked, onePlayerAction, superTyrian;
JE_boolean trentWin = false;
JE_byte superArcadeMode;
JE_byte superArcadePowerUp;
JE_real linkGunDirec;
JE_byte inputDevice[2] = { 1, 2 }; // 0:any 1:keyboard 2:mouse 3+:joystick
JE_byte secretHint;
JE_byte background3over;
JE_byte background2over;
JE_byte gammaCorrection;
JE_boolean superPause = false;
JE_boolean explosionTransparent,
youAreCheating,
displayScore,
background2, smoothScroll, wild, superWild, starActive,
topEnemyOver,
skyEnemyOverAll,
background2notTransparent;
JE_byte soundEffects; // dummy value for config
JE_byte versionNum; /* SW 1.0 and SW/Reg 1.1 = 0 or 1
* EA 1.2 = 2 */
JE_byte fastPlay;
JE_boolean pentiumMode;
/* Savegame files */
JE_byte gameSpeed;
JE_byte processorType; /* 1=386 2=486 3=Pentium Hyper */
JE_SaveFilesType saveFiles; /*array[1..saveLevelnum] of savefiletype;*/
JE_SaveGameTemp saveTemp;
JE_word editorLevel; /*Initial value 800*/
TouchscreenControlMode_t touchscreenControlMode = TOUCHSCREEN_CONTROL_FINGER;
cJSON *load_json( const char *filename )
{
FILE *f = dir_fopen_warn(get_user_directory(), filename, "rb");
if (f == NULL)
return NULL;
size_t buffer_len = ftell_eof(f);
char *buffer = (char *)malloc(buffer_len + 1);
fread(buffer, 1, buffer_len, f);
buffer[buffer_len] = '\0';
fclose(f);
cJSON *root = cJSON_Parse(buffer);
free(buffer);
return root;
}
void save_json( cJSON *root, const char *filename )
{
FILE *f = dir_fopen_warn(get_user_directory(), filename, "w+");
if (f == NULL)
return;
char *buffer = cJSON_Print(root);
if (buffer != NULL)
{
fputs(buffer, f);
free(buffer);
}
fclose(f);
}
bool load_opentyrian_config( void )
{
// defaults
fullscreen_enabled = false;
set_scaler_by_name("Scale2x");
cJSON *root = load_json("opentyrian.conf");
if (root == NULL)
return false;
cJSON *section = cJSON_GetObjectItem(root, "video");
if (section != NULL)
{
cJSON *setting;
if ((setting = cJSON_GetObjectItem(section, "fullscreen")))
fullscreen_enabled = (setting->type == cJSON_True);
if ((setting = cJSON_GetObjectItem(section, "scaler")))
set_scaler_by_name(setting->valuestring);
if ((setting = cJSON_GetObjectItem(section, "touchscreenmode")))
touchscreenControlMode = setting->valueint;
}
cJSON_Delete(root);
return true;
}
bool save_opentyrian_config( void )
{
cJSON *root = load_json("opentyrian.conf");
if (root == NULL)
root = cJSON_CreateObject();
cJSON *section;
section = cJSON_CreateOrGetObjectItem(root, "video");
cJSON_ForceType(section, cJSON_Object);
{
cJSON *setting;
setting = cJSON_CreateOrGetObjectItem(section, "fullscreen");
cJSON_SetBoolean(setting, fullscreen_enabled);
setting = cJSON_CreateOrGetObjectItem(section, "scaler");
cJSON_SetString(setting, scalers[scaler].name);
setting = cJSON_CreateOrGetObjectItem(section, "touchscreenmode");
cJSON_SetNumber(setting, touchscreenControlMode);
}
save_json(root, "opentyrian.conf");
cJSON_Delete(root);
return true;
}
static void playeritems_to_pitems( JE_PItemsType pItems, PlayerItems *items, JE_byte initial_episode_num )
{
pItems[0] = items->weapon[FRONT_WEAPON].id;
pItems[1] = items->weapon[REAR_WEAPON].id;
pItems[2] = items->super_arcade_mode;
pItems[3] = items->sidekick[LEFT_SIDEKICK];
pItems[4] = items->sidekick[RIGHT_SIDEKICK];
pItems[5] = items->generator;
pItems[6] = items->sidekick_level;
pItems[7] = items->sidekick_series;
pItems[8] = initial_episode_num;
pItems[9] = items->shield;
pItems[10] = items->special;
pItems[11] = items->ship;
}
static void pitems_to_playeritems( PlayerItems *items, JE_PItemsType pItems, JE_byte *initial_episode_num )
{
items->weapon[FRONT_WEAPON].id = pItems[0];
items->weapon[REAR_WEAPON].id = pItems[1];
items->super_arcade_mode = pItems[2];
items->sidekick[LEFT_SIDEKICK] = pItems[3];
items->sidekick[RIGHT_SIDEKICK] = pItems[4];
items->generator = pItems[5];
items->sidekick_level = pItems[6];
items->sidekick_series = pItems[7];
if (initial_episode_num != NULL)
*initial_episode_num = pItems[8];
items->shield = pItems[9];
items->special = pItems[10];
items->ship = pItems[11];
}
void JE_saveGame( JE_byte slot, const char *name )
{
saveFiles[slot-1].initialDifficulty = initialDifficulty;
saveFiles[slot-1].gameHasRepeated = gameHasRepeated;
saveFiles[slot-1].level = saveLevel;
if (superTyrian)
player[0].items.super_arcade_mode = SA_SUPERTYRIAN;
else if (superArcadeMode == SA_NONE && onePlayerAction)
player[0].items.super_arcade_mode = SA_ARCADE;
else
player[0].items.super_arcade_mode = superArcadeMode;
playeritems_to_pitems(saveFiles[slot-1].items, &player[0].items, initial_episode_num);
if (twoPlayerMode)
playeritems_to_pitems(saveFiles[slot-1].lastItems, &player[1].items, 0);
else
playeritems_to_pitems(saveFiles[slot-1].lastItems, &player[0].last_items, 0);
saveFiles[slot-1].score = player[0].cash;
saveFiles[slot-1].score2 = player[1].cash;
memcpy(&saveFiles[slot-1].levelName, &lastLevelName, sizeof(lastLevelName));
saveFiles[slot-1].cubes = lastCubeMax;
if (strcmp(lastLevelName, "Completed") == 0)
{
temp = episodeNum - 1;
if (temp < 1)
{
temp = EPISODE_AVAILABLE; /* JE: {Episodemax is 4 for completion purposes} */
}
saveFiles[slot-1].episode = temp;
}
else
{
saveFiles[slot-1].episode = episodeNum;
}
saveFiles[slot-1].difficulty = difficultyLevel;
saveFiles[slot-1].secretHint = secretHint;
saveFiles[slot-1].input1 = inputDevice[0];
saveFiles[slot-1].input2 = inputDevice[1];
strcpy(saveFiles[slot-1].name, name);
for (uint port = 0; port < 2; ++port)
{
// if two-player, use first player's front and second player's rear weapon
saveFiles[slot-1].power[port] = player[twoPlayerMode ? port : 0].items.weapon[port].power;
}
JE_saveConfiguration();
}
void JE_loadGame( JE_byte slot )
{
superTyrian = false;
onePlayerAction = false;
twoPlayerMode = false;
extraGame = false;
galagaMode = false;
initialDifficulty = saveFiles[slot-1].initialDifficulty;
gameHasRepeated = saveFiles[slot-1].gameHasRepeated;
twoPlayerMode = (slot-1) > 10;
difficultyLevel = saveFiles[slot-1].difficulty;
pitems_to_playeritems(&player[0].items, saveFiles[slot-1].items, &initial_episode_num);
superArcadeMode = player[0].items.super_arcade_mode;
if (superArcadeMode == SA_SUPERTYRIAN)
superTyrian = true;
if (superArcadeMode != SA_NONE)
onePlayerAction = true;
if (superArcadeMode > SA_NORTSHIPZ)
superArcadeMode = SA_NONE;
if (twoPlayerMode)
{
onePlayerAction = false;
pitems_to_playeritems(&player[1].items, saveFiles[slot-1].lastItems, NULL);
}
else
{
pitems_to_playeritems(&player[0].last_items, saveFiles[slot-1].lastItems, NULL);
}
/* Compatibility with old version */
if (player[1].items.sidekick_level < 101)
{
player[1].items.sidekick_level = 101;
player[1].items.sidekick_series = player[1].items.sidekick[LEFT_SIDEKICK];
}
player[0].cash = saveFiles[slot-1].score;
player[1].cash = saveFiles[slot-1].score2;
mainLevel = saveFiles[slot-1].level;
cubeMax = saveFiles[slot-1].cubes;
lastCubeMax = cubeMax;
secretHint = saveFiles[slot-1].secretHint;
inputDevice[0] = saveFiles[slot-1].input1;
inputDevice[1] = saveFiles[slot-1].input2;
for (uint port = 0; port < 2; ++port)
{
// if two-player, use first player's front and second player's rear weapon
player[twoPlayerMode ? port : 0].items.weapon[port].power = saveFiles[slot-1].power[port];
}
int episode = saveFiles[slot-1].episode;
memcpy(&levelName, &saveFiles[slot-1].levelName, sizeof(levelName));
if (strcmp(levelName, "Completed") == 0)
{
if (episode == EPISODE_AVAILABLE)
{
episode = 1;
} else if (episode < EPISODE_AVAILABLE) {
episode++;
}
/* Increment episode. Episode EPISODE_AVAILABLE goes to 1. */
}
JE_initEpisode(episode);
saveLevel = mainLevel;
memcpy(&lastLevelName, &levelName, sizeof(levelName));
}
void JE_initProcessorType( void )
{
/* SYN: Originally this proc looked at your hardware specs and chose appropriate options. We don't care, so I'll just set
decent defaults here. */
wild = false;
superWild = false;
smoothScroll = true;
explosionTransparent = true;
filtrationAvail = false;
background2 = true;
displayScore = true;
switch (processorType)
{
case 1: /* 386 */
background2 = false;
displayScore = false;
explosionTransparent = false;
break;
case 2: /* 486 - Default */
break;
case 3: /* High Detail */
smoothScroll = false;
break;
case 4: /* Pentium */
wild = true;
filtrationAvail = true;
break;
case 5: /* Nonstandard VGA */
smoothScroll = false;
break;
case 6: /* SuperWild */
wild = true;
superWild = true;
filtrationAvail = true;
break;
}
switch (gameSpeed)
{
case 1: /* Slug Mode */
fastPlay = 3;
break;
case 2: /* Slower */
fastPlay = 4;
break;
case 3: /* Slow */
fastPlay = 5;
break;
case 4: /* Normal */
fastPlay = 0;
break;
case 5: /* Pentium Hyper */
fastPlay = 1;
break;
}
}
void JE_setNewGameSpeed( void )
{
pentiumMode = false;
switch (fastPlay)
{
case 0:
speed = 0x4300;
smoothScroll = true;
frameCountMax = 2;
break;
case 1:
speed = 0x3000;
smoothScroll = true;
frameCountMax = 2;
break;
case 2:
speed = 0x2000;
smoothScroll = false;
frameCountMax = 2;
break;
case 3:
speed = 0x5300;
smoothScroll = true;
frameCountMax = 4;
break;
case 4:
speed = 0x4300;
smoothScroll = true;
frameCountMax = 3;
break;
case 5:
speed = 0x4300;
smoothScroll = true;
frameCountMax = 2;
pentiumMode = true;
break;
}
frameCount = frameCountMax;
JE_resetTimerInt();
JE_setTimerInt();
}
void JE_encryptSaveTemp( void )
{
JE_SaveGameTemp s3;
JE_word x;
JE_byte y;
memcpy(&s3, &saveTemp, sizeof(s3));
y = 0;
for (x = 0; x < SAVE_FILE_SIZE; x++)
{
y += s3[x];
}
saveTemp[SAVE_FILE_SIZE] = y;
y = 0;
for (x = 0; x < SAVE_FILE_SIZE; x++)
{
y -= s3[x];
}
saveTemp[SAVE_FILE_SIZE+1] = y;
y = 1;
for (x = 0; x < SAVE_FILE_SIZE; x++)
{
y = (y * s3[x]) + 1;
}
saveTemp[SAVE_FILE_SIZE+2] = y;
y = 0;
for (x = 0; x < SAVE_FILE_SIZE; x++)
{
y = y ^ s3[x];
}
saveTemp[SAVE_FILE_SIZE+3] = y;
for (x = 0; x < SAVE_FILE_SIZE; x++)
{
saveTemp[x] = saveTemp[x] ^ cryptKey[(x+1) % 10];
if (x > 0)
{
saveTemp[x] = saveTemp[x] ^ saveTemp[x - 1];
}
}
}
void JE_decryptSaveTemp( void )
{
JE_boolean correct = true;
JE_SaveGameTemp s2;
int x;
JE_byte y;
/* Decrypt save game file */
for (x = (SAVE_FILE_SIZE - 1); x >= 0; x--)
{
s2[x] = (JE_byte)saveTemp[x] ^ (JE_byte)(cryptKey[(x+1) % 10]);
if (x > 0)
{
s2[x] ^= (JE_byte)saveTemp[x - 1];
}
}
/* for (x = 0; x < SAVE_FILE_SIZE; x++) printf("%c", s2[x]); */
/* Check save file for correctitude */
y = 0;
for (x = 0; x < SAVE_FILE_SIZE; x++)
{
y += s2[x];
}
if (saveTemp[SAVE_FILE_SIZE] != y)
{
correct = false;
printf("Failed additive checksum: %d vs %d\n", saveTemp[SAVE_FILE_SIZE], y);
}
y = 0;
for (x = 0; x < SAVE_FILE_SIZE; x++)
{
y -= s2[x];
}
if (saveTemp[SAVE_FILE_SIZE+1] != y)
{
correct = false;
printf("Failed subtractive checksum: %d vs %d\n", saveTemp[SAVE_FILE_SIZE+1], y);
}
y = 1;
for (x = 0; x < SAVE_FILE_SIZE; x++)
{
y = (y * s2[x]) + 1;
}
if (saveTemp[SAVE_FILE_SIZE+2] != y)
{
correct = false;
printf("Failed multiplicative checksum: %d vs %d\n", saveTemp[SAVE_FILE_SIZE+2], y);
}
y = 0;
for (x = 0; x < SAVE_FILE_SIZE; x++)
{
y = y ^ s2[x];
}
if (saveTemp[SAVE_FILE_SIZE+3] != y)
{
correct = false;
printf("Failed XOR'd checksum: %d vs %d\n", saveTemp[SAVE_FILE_SIZE+3], y);
}
/* Barf and die if save file doesn't validate */
if (!correct)
{
fprintf(stderr, "Error reading save file!\n");
exit(255);
}
/* Keep decrypted version plz */
memcpy(&saveTemp, &s2, sizeof(s2));
}
#ifndef TARGET_MACOSX
const char *get_user_directory( void )
{
static char user_dir[500] = "";
if (strlen(user_dir) == 0)
{
#ifdef TARGET_UNIX
if (getenv("HOME"))
snprintf(user_dir, sizeof(user_dir), "%s/.opentyrian", getenv("HOME"));
#else
strcpy(user_dir, ".");
#endif // TARGET_UNIX
}
return user_dir;
}
#endif // TARGET_MACOSX
// for compatibility
Uint8 joyButtonAssign[4] = {1, 4, 5, 5};
Uint8 inputDevice_ = 0, jConfigure = 0, midiPort = 1;
void JE_loadConfiguration( void )
{
FILE *fi;
int z;
JE_byte *p;
int y;
fi = dir_fopen_warn(get_user_directory(), "tyrian.cfg", "rb");
if (fi && ftell_eof(fi) == 20 + sizeof(keySettings))
{
/* SYN: I've hardcoded the sizes here because the .CFG file format is fixed
anyways, so it's not like they'll change. */
background2 = 0;
efread(&background2, 1, 1, fi);
efread(&gameSpeed, 1, 1, fi);
efread(&inputDevice_, 1, 1, fi);
efread(&jConfigure, 1, 1, fi);
efread(&versionNum, 1, 1, fi);
efread(&processorType, 1, 1, fi);
efread(&midiPort, 1, 1, fi);
efread(&soundEffects, 1, 1, fi);
efread(&gammaCorrection, 1, 1, fi);
efread(&difficultyLevel, 1, 1, fi);
efread(joyButtonAssign, 1, 4, fi);
efread(&tyrMusicVolume, 2, 1, fi);
efread(&fxVolume, 2, 1, fi);
efread(inputDevice, 1, 2, fi);
efread(keySettings, sizeof(*keySettings), COUNTOF(keySettings), fi);
fclose(fi);
}
else
{
printf("\nInvalid or missing TYRIAN.CFG! Continuing using defaults.\n\n");
soundEffects = 1;
memcpy(&keySettings, &defaultKeySettings, sizeof(keySettings));
background2 = true;
tyrMusicVolume = fxVolume = 128;
gammaCorrection = 0;
processorType = 3;
gameSpeed = 4;
}
load_opentyrian_config();
if (tyrMusicVolume > 255)
tyrMusicVolume = 255;
if (fxVolume > 255)
fxVolume = 255;
JE_calcFXVol();
set_volume(tyrMusicVolume, fxVolume);
fi = dir_fopen_warn(get_user_directory(), "tyrian.sav", "rb");
if (fi)
{
fseek(fi, 0, SEEK_SET);
efread(saveTemp, 1, sizeof(saveTemp), fi);
JE_decryptSaveTemp();
/* SYN: The original mostly blasted the save file into raw memory. However, our lives are not so
easy, because the C struct is necessarily a different size. So instead we have to loop
through each record and load fields manually. *emo tear* :'( */
p = saveTemp;
for (z = 0; z < SAVE_FILES_NUM; z++)
{
memcpy(&saveFiles[z].encode, p, sizeof(JE_word)); p += 2;
saveFiles[z].encode = SDL_SwapLE16(saveFiles[z].encode);
memcpy(&saveFiles[z].level, p, sizeof(JE_word)); p += 2;
saveFiles[z].level = SDL_SwapLE16(saveFiles[z].level);
memcpy(&saveFiles[z].items, p, sizeof(JE_PItemsType)); p += sizeof(JE_PItemsType);
memcpy(&saveFiles[z].score, p, sizeof(JE_longint)); p += 4;
saveFiles[z].score = SDL_SwapLE32(saveFiles[z].score);
memcpy(&saveFiles[z].score2, p, sizeof(JE_longint)); p += 4;
saveFiles[z].score2 = SDL_SwapLE32(saveFiles[z].score2);
/* SYN: Pascal strings are prefixed by a byte holding the length! */
memset(&saveFiles[z].levelName, 0, sizeof(saveFiles[z].levelName));
memcpy(&saveFiles[z].levelName, &p[1], *p);
p += 10;
/* This was a BYTE array, not a STRING, in the original. Go fig. */
memcpy(&saveFiles[z].name, p, 14);
p += 14;
memcpy(&saveFiles[z].cubes, p, sizeof(JE_byte)); p++;
memcpy(&saveFiles[z].power, p, sizeof(JE_byte) * 2); p += 2;
memcpy(&saveFiles[z].episode, p, sizeof(JE_byte)); p++;
memcpy(&saveFiles[z].lastItems, p, sizeof(JE_PItemsType)); p += sizeof(JE_PItemsType);
memcpy(&saveFiles[z].difficulty, p, sizeof(JE_byte)); p++;
memcpy(&saveFiles[z].secretHint, p, sizeof(JE_byte)); p++;
memcpy(&saveFiles[z].input1, p, sizeof(JE_byte)); p++;
memcpy(&saveFiles[z].input2, p, sizeof(JE_byte)); p++;
/* booleans were 1 byte in pascal -- working around it */
Uint8 temp;
memcpy(&temp, p, 1); p++;
saveFiles[z].gameHasRepeated = temp != 0;
memcpy(&saveFiles[z].initialDifficulty, p, sizeof(JE_byte)); p++;
memcpy(&saveFiles[z].highScore1, p, sizeof(JE_longint)); p += 4;
saveFiles[z].highScore1 = SDL_SwapLE32(saveFiles[z].highScore1);
memcpy(&saveFiles[z].highScore2, p, sizeof(JE_longint)); p += 4;
saveFiles[z].highScore2 = SDL_SwapLE32(saveFiles[z].highScore2);
memset(&saveFiles[z].highScoreName, 0, sizeof(saveFiles[z].highScoreName));
memcpy(&saveFiles[z].highScoreName, &p[1], *p);
p += 30;
memcpy(&saveFiles[z].highScoreDiff, p, sizeof(JE_byte)); p++;
}
/* SYN: This is truncating to bytes. I have no idea what this is doing or why. */
/* TODO: Figure out what this is about and make sure it isn't broked. */
editorLevel = (saveTemp[SIZEOF_SAVEGAMETEMP - 5] << 8) | saveTemp[SIZEOF_SAVEGAMETEMP - 6];
fclose(fi);
} else {
/* We didn't have a save file! Let's make up random stuff! */
editorLevel = 800;
for (z = 0; z < 100; z++)
{
saveTemp[SAVE_FILES_SIZE + z] = initialItemAvail[z];
}
for (z = 0; z < SAVE_FILES_NUM; z++)
{
saveFiles[z].level = 0;
for (y = 0; y < 14; y++)
{
saveFiles[z].name[y] = ' ';
}
saveFiles[z].name[14] = 0;
saveFiles[z].highScore1 = ((mt_rand() % 20) + 1) * 1000;
if (z % 6 > 2)
{
saveFiles[z].highScore2 = ((mt_rand() % 20) + 1) * 1000;
strcpy(saveFiles[z].highScoreName, defaultTeamNames[mt_rand() % 22]);
} else {
strcpy(saveFiles[z].highScoreName, defaultHighScoreNames[mt_rand() % 34]);
}
}
}
JE_initProcessorType();
}
void JE_saveConfiguration( void )
{
#ifdef TARGET_UNIX
if (getenv("HOME"))
{
char dir[1000];
snprintf(dir, sizeof(dir), "%s/.opentyrian", getenv("HOME"));
mkdir(dir, 0755);
}
#endif /* HOME */
FILE *f;
JE_byte *p;
int z;
p = saveTemp;
for (z = 0; z < SAVE_FILES_NUM; z++)
{
JE_SaveFileType tempSaveFile;
memcpy(&tempSaveFile, &saveFiles[z], sizeof(tempSaveFile));
tempSaveFile.encode = SDL_SwapLE16(tempSaveFile.encode);
memcpy(p, &tempSaveFile.encode, sizeof(JE_word)); p += 2;
tempSaveFile.level = SDL_SwapLE16(tempSaveFile.level);
memcpy(p, &tempSaveFile.level, sizeof(JE_word)); p += 2;
memcpy(p, &tempSaveFile.items, sizeof(JE_PItemsType)); p += sizeof(JE_PItemsType);
tempSaveFile.score = SDL_SwapLE32(tempSaveFile.score);
memcpy(p, &tempSaveFile.score, sizeof(JE_longint)); p += 4;
tempSaveFile.score2 = SDL_SwapLE32(tempSaveFile.score2);
memcpy(p, &tempSaveFile.score2, sizeof(JE_longint)); p += 4;
/* SYN: Pascal strings are prefixed by a byte holding the length! */
memset(p, 0, sizeof(tempSaveFile.levelName));
*p = strlen(tempSaveFile.levelName);
memcpy(&p[1], &tempSaveFile.levelName, *p);
p += 10;
/* This was a BYTE array, not a STRING, in the original. Go fig. */
memcpy(p, &tempSaveFile.name, 14);
p += 14;
memcpy(p, &tempSaveFile.cubes, sizeof(JE_byte)); p++;
memcpy(p, &tempSaveFile.power, sizeof(JE_byte) * 2); p += 2;
memcpy(p, &tempSaveFile.episode, sizeof(JE_byte)); p++;
memcpy(p, &tempSaveFile.lastItems, sizeof(JE_PItemsType)); p += sizeof(JE_PItemsType);
memcpy(p, &tempSaveFile.difficulty, sizeof(JE_byte)); p++;
memcpy(p, &tempSaveFile.secretHint, sizeof(JE_byte)); p++;
memcpy(p, &tempSaveFile.input1, sizeof(JE_byte)); p++;
memcpy(p, &tempSaveFile.input2, sizeof(JE_byte)); p++;
/* booleans were 1 byte in pascal -- working around it */
Uint8 temp = tempSaveFile.gameHasRepeated != false;
memcpy(p, &temp, 1); p++;
memcpy(p, &tempSaveFile.initialDifficulty, sizeof(JE_byte)); p++;
tempSaveFile.highScore1 = SDL_SwapLE32(tempSaveFile.highScore1);
memcpy(p, &tempSaveFile.highScore1, sizeof(JE_longint)); p += 4;
tempSaveFile.highScore2 = SDL_SwapLE32(tempSaveFile.highScore2);
memcpy(p, &tempSaveFile.highScore2, sizeof(JE_longint)); p += 4;
memset(p, 0, sizeof(tempSaveFile.highScoreName));
*p = strlen(tempSaveFile.highScoreName);
memcpy(&p[1], &tempSaveFile.highScoreName, *p);
p += 30;
memcpy(p, &tempSaveFile.highScoreDiff, sizeof(JE_byte)); p++;
}
saveTemp[SIZEOF_SAVEGAMETEMP - 6] = editorLevel >> 8;
saveTemp[SIZEOF_SAVEGAMETEMP - 5] = editorLevel;
JE_encryptSaveTemp();
f = dir_fopen_warn(get_user_directory(), "tyrian.sav", "wb");
if (f)
{
efwrite(saveTemp, 1, sizeof(saveTemp), f);
fclose(f);
#if (_BSD_SOURCE || _XOPEN_SOURCE >= 500)
sync();
#endif
}
JE_decryptSaveTemp();
f = dir_fopen_warn(get_user_directory(), "tyrian.cfg", "wb");
if (f)
{
efwrite(&background2, 1, 1, f);
efwrite(&gameSpeed, 1, 1, f);
efwrite(&inputDevice_, 1, 1, f);
efwrite(&jConfigure, 1, 1, f);
efwrite(&versionNum, 1, 1, f);
efwrite(&processorType, 1, 1, f);
efwrite(&midiPort, 1, 1, f);
efwrite(&soundEffects, 1, 1, f);
efwrite(&gammaCorrection, 1, 1, f);
efwrite(&difficultyLevel, 1, 1, f);
efwrite(joyButtonAssign, 1, 4, f);
efwrite(&tyrMusicVolume, 2, 1, f);
efwrite(&fxVolume, 2, 1, f);
efwrite(inputDevice, 1, 2, f);
efwrite(keySettings, sizeof(*keySettings), COUNTOF(keySettings), f);
fclose(f);
}
save_opentyrian_config();
#if (_BSD_SOURCE || _XOPEN_SOURCE >= 500)
sync();
#endif
}