Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | RSS feed

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