PageRenderTime 80ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 0ms

/engines/sky/detection.cpp

http://github.com/scummvm/scummvm
C++ | 416 lines | 308 code | 70 blank | 38 comment | 52 complexity | 684ca1dea9bc2e30a81858965908c074 MD5 | raw file
Possible License(s): GPL-3.0, LGPL-2.1, GPL-2.0
  1. /* ScummVM - Graphic Adventure Engine
  2. *
  3. * ScummVM is the legal property of its developers, whose names
  4. * are too numerous to list here. Please refer to the COPYRIGHT
  5. * file distributed with this source distribution.
  6. *
  7. * This program is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU General Public License
  9. * as published by the Free Software Foundation; either version 2
  10. * of the License, or (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU General Public License
  18. * along with this program; if not, write to the Free Software
  19. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  20. *
  21. */
  22. #include "sky/control.h"
  23. #include "sky/sky.h"
  24. #include "base/plugins.h"
  25. #include "backends/keymapper/action.h"
  26. #include "backends/keymapper/keymap.h"
  27. #include "backends/keymapper/standard-actions.h"
  28. #include "common/config-manager.h"
  29. #include "engines/advancedDetector.h"
  30. #include "engines/metaengine.h"
  31. #include "common/system.h"
  32. #include "common/file.h"
  33. #include "common/fs.h"
  34. #include "common/savefile.h"
  35. #include "common/textconsole.h"
  36. #include "common/translation.h"
  37. #include "engines/metaengine.h"
  38. static const PlainGameDescriptor skySetting =
  39. {"sky", "Beneath a Steel Sky" };
  40. static const ExtraGuiOption skyExtraGuiOption = {
  41. _s("Floppy intro"),
  42. _s("Use the floppy version's intro (CD version only)"),
  43. "alt_intro",
  44. false
  45. };
  46. struct SkyVersion {
  47. int dinnerTableEntries;
  48. int dataDiskSize;
  49. const char *extraDesc;
  50. int version;
  51. const char *guioptions;
  52. };
  53. // TODO: Would be nice if Disk::determineGameVersion() used this table, too.
  54. static const SkyVersion skyVersions[] = {
  55. { 232, -1, "floppy demo", 272, GUIO1(GUIO_NOSPEECH) }, // German
  56. { 243, -1, "pc gamer demo", 109, GUIO1(GUIO_NOSPEECH) },
  57. { 247, -1, "floppy demo", 267, GUIO1(GUIO_NOSPEECH) }, // English
  58. { 1404, -1, "floppy", 288, GUIO1(GUIO_NOSPEECH) },
  59. { 1413, -1, "floppy", 303, GUIO1(GUIO_NOSPEECH) },
  60. { 1445, 8830435, "floppy", 348, GUIO1(GUIO_NOSPEECH) },
  61. { 1445, -1, "floppy", 331, GUIO1(GUIO_NOSPEECH) },
  62. { 1711, -1, "cd demo", 365, GUIO0() },
  63. { 5099, -1, "cd", 368, GUIO0() },
  64. { 5097, -1, "cd", 372, GUIO0() },
  65. { 0, 0, 0, 0, 0 }
  66. };
  67. class SkyMetaEngine : public MetaEngine {
  68. public:
  69. const char *getName() const override;
  70. const char *getOriginalCopyright() const override;
  71. const char *getEngineId() const override {
  72. return "sky";
  73. }
  74. bool hasFeature(MetaEngineFeature f) const override;
  75. PlainGameList getSupportedGames() const override;
  76. const ExtraGuiOptions getExtraGuiOptions(const Common::String &target) const override;
  77. PlainGameDescriptor findGame(const char *gameid) const override;
  78. DetectedGames detectGames(const Common::FSList &fslist) const override;
  79. Common::KeymapArray initKeymaps(const char *target) const override;
  80. Common::Error createInstance(OSystem *syst, Engine **engine) const override;
  81. SaveStateList listSaves(const char *target) const override;
  82. int getMaximumSaveSlot() const override;
  83. void removeSaveState(const char *target, int slot) const override;
  84. };
  85. const char *SkyMetaEngine::getName() const {
  86. return "Beneath a Steel Sky";
  87. }
  88. const char *SkyMetaEngine::getOriginalCopyright() const {
  89. return "Beneath a Steel Sky (C) Revolution";
  90. }
  91. bool SkyMetaEngine::hasFeature(MetaEngineFeature f) const {
  92. return
  93. (f == kSupportsListSaves) ||
  94. (f == kSupportsLoadingDuringStartup) ||
  95. (f == kSupportsDeleteSave);
  96. }
  97. bool Sky::SkyEngine::hasFeature(EngineFeature f) const {
  98. return
  99. (f == kSupportsRTL) ||
  100. (f == kSupportsLoadingDuringRuntime) ||
  101. (f == kSupportsSavingDuringRuntime);
  102. }
  103. PlainGameList SkyMetaEngine::getSupportedGames() const {
  104. PlainGameList games;
  105. games.push_back(skySetting);
  106. return games;
  107. }
  108. const ExtraGuiOptions SkyMetaEngine::getExtraGuiOptions(const Common::String &target) const {
  109. Common::String guiOptions;
  110. ExtraGuiOptions options;
  111. if (target.empty()) {
  112. options.push_back(skyExtraGuiOption);
  113. return options;
  114. }
  115. if (ConfMan.hasKey("guioptions", target)) {
  116. guiOptions = ConfMan.get("guioptions", target);
  117. guiOptions = parseGameGUIOptions(guiOptions);
  118. }
  119. if (!guiOptions.contains(GUIO_NOSPEECH))
  120. options.push_back(skyExtraGuiOption);
  121. return options;
  122. }
  123. PlainGameDescriptor SkyMetaEngine::findGame(const char *gameid) const {
  124. if (0 == scumm_stricmp(gameid, skySetting.gameId))
  125. return skySetting;
  126. return PlainGameDescriptor::empty();
  127. }
  128. DetectedGames SkyMetaEngine::detectGames(const Common::FSList &fslist) const {
  129. DetectedGames detectedGames;
  130. bool hasSkyDsk = false;
  131. bool hasSkyDnr = false;
  132. int dinnerTableEntries = -1;
  133. int dataDiskSize = -1;
  134. // Iterate over all files in the given directory
  135. for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) {
  136. if (!file->isDirectory()) {
  137. if (0 == scumm_stricmp("sky.dsk", file->getName().c_str())) {
  138. Common::File dataDisk;
  139. if (dataDisk.open(*file)) {
  140. hasSkyDsk = true;
  141. dataDiskSize = dataDisk.size();
  142. }
  143. }
  144. if (0 == scumm_stricmp("sky.dnr", file->getName().c_str())) {
  145. Common::File dinner;
  146. if (dinner.open(*file)) {
  147. hasSkyDnr = true;
  148. dinnerTableEntries = dinner.readUint32LE();
  149. }
  150. }
  151. }
  152. }
  153. if (hasSkyDsk && hasSkyDnr) {
  154. // Match found, add to list of candidates, then abort inner loop.
  155. // The game detector uses US English by default. We want British
  156. // English to match the recorded voices better.
  157. const SkyVersion *sv = skyVersions;
  158. while (sv->dinnerTableEntries) {
  159. if (dinnerTableEntries == sv->dinnerTableEntries &&
  160. (sv->dataDiskSize == dataDiskSize || sv->dataDiskSize == -1)) {
  161. break;
  162. }
  163. ++sv;
  164. }
  165. if (sv->dinnerTableEntries) {
  166. Common::String extra = Common::String::format("v0.0%d %s", sv->version, sv->extraDesc);
  167. DetectedGame game = DetectedGame(getEngineId(), skySetting.gameId, skySetting.description, Common::UNK_LANG, Common::kPlatformUnknown, extra);
  168. game.setGUIOptions(sv->guioptions);
  169. detectedGames.push_back(game);
  170. } else {
  171. detectedGames.push_back(DetectedGame(getEngineId(), skySetting.gameId, skySetting.description));
  172. }
  173. }
  174. return detectedGames;
  175. }
  176. Common::KeymapArray SkyMetaEngine::initKeymaps(const char *target) const {
  177. using namespace Common;
  178. using namespace Sky;
  179. Keymap *mainKeymap = new Keymap(Keymap::kKeymapTypeGame, "sky-main", "Beneath a Steel Sky");
  180. Action *act;
  181. act = new Action("LCLK", _("Walk / Look / Talk"));
  182. act->setLeftClickEvent();
  183. act->addDefaultInputMapping("MOUSE_LEFT");
  184. act->addDefaultInputMapping("JOY_A");
  185. mainKeymap->addAction(act);
  186. act = new Action("RCLK", _("Use"));
  187. act->setRightClickEvent();
  188. act->addDefaultInputMapping("MOUSE_RIGHT");
  189. act->addDefaultInputMapping("JOY_B");
  190. mainKeymap->addAction(act);
  191. act = new Action("CONFIRM", _("Confirm"));
  192. act->setCustomEngineActionEvent(kSkyActionConfirm);
  193. act->addDefaultInputMapping("RETURN");
  194. act->addDefaultInputMapping("KP_ENTER");
  195. mainKeymap->addAction(act);
  196. act = new Action(kStandardActionSkip, _("Skip / Close"));
  197. act->setCustomEngineActionEvent(kSkyActionSkip);
  198. act->addDefaultInputMapping("ESCAPE");
  199. act->addDefaultInputMapping("JOY_Y");
  200. mainKeymap->addAction(act);
  201. Keymap *shortcutsKeymap = new Keymap(Keymap::kKeymapTypeGame, SkyEngine::shortcutsKeymapId, "Beneath a Steel Sky - Shortcuts");
  202. act = new Action(kStandardActionOpenMainMenu, _("Open control panel"));
  203. act->setCustomEngineActionEvent(kSkyActionOpenControlPanel);
  204. act->addDefaultInputMapping("F5");
  205. act->addDefaultInputMapping("JOY_X");
  206. shortcutsKeymap->addAction(act);
  207. act = new Action("SKPL", _("Skip line"));
  208. act->setCustomEngineActionEvent(kSkyActionSkipLine);
  209. act->addDefaultInputMapping("PERIOD");
  210. shortcutsKeymap->addAction(act);
  211. act = new Action(kStandardActionPause, _("Pause"));
  212. act->setCustomEngineActionEvent(kSkyActionPause);
  213. act->addDefaultInputMapping("p");
  214. shortcutsKeymap->addAction(act);
  215. act = new Action("FAST", _("Toggle fast mode"));
  216. act->setCustomEngineActionEvent(kSkyActionToggleFastMode);
  217. act->addDefaultInputMapping("C+f");
  218. shortcutsKeymap->addAction(act);
  219. act = new Action("RFAST", _("Toggle really fast mode"));
  220. act->setCustomEngineActionEvent(kSkyActionToggleReallyFastMode);
  221. act->addDefaultInputMapping("C+g");
  222. shortcutsKeymap->addAction(act);
  223. KeymapArray keymaps(2);
  224. keymaps[0] = mainKeymap;
  225. keymaps[1] = shortcutsKeymap;
  226. return keymaps;
  227. }
  228. Common::Error SkyMetaEngine::createInstance(OSystem *syst, Engine **engine) const {
  229. assert(engine);
  230. *engine = new Sky::SkyEngine(syst);
  231. return Common::kNoError;
  232. }
  233. SaveStateList SkyMetaEngine::listSaves(const char *target) const {
  234. Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
  235. SaveStateList saveList;
  236. // Load the descriptions
  237. Common::StringArray savenames;
  238. savenames.resize(MAX_SAVE_GAMES+1);
  239. Common::InSaveFile *inf;
  240. inf = saveFileMan->openForLoading("SKY-VM.SAV");
  241. if (inf != NULL) {
  242. char *tmpBuf = new char[MAX_SAVE_GAMES * MAX_TEXT_LEN];
  243. char *tmpPtr = tmpBuf;
  244. inf->read(tmpBuf, MAX_SAVE_GAMES * MAX_TEXT_LEN);
  245. for (int i = 0; i < MAX_SAVE_GAMES; ++i) {
  246. savenames[i] = tmpPtr;
  247. tmpPtr += savenames[i].size() + 1;
  248. }
  249. delete inf;
  250. delete[] tmpBuf;
  251. }
  252. // Find all saves
  253. Common::StringArray filenames;
  254. filenames = saveFileMan->listSavefiles("SKY-VM.###");
  255. // Prepare the list of savestates by looping over all matching savefiles
  256. for (Common::StringArray::const_iterator file = filenames.begin(); file != filenames.end(); ++file) {
  257. // Extract the extension
  258. Common::String ext = file->c_str() + file->size() - 3;
  259. ext.toUppercase();
  260. int slotNum = atoi(ext.c_str());
  261. Common::InSaveFile *in = saveFileMan->openForLoading(*file);
  262. if (in) {
  263. saveList.push_back(SaveStateDescriptor(slotNum,
  264. (slotNum == 0) ? _("Autosave") : savenames[slotNum - 1]));
  265. delete in;
  266. }
  267. }
  268. // Sort saves based on slot number.
  269. Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
  270. return saveList;
  271. }
  272. int SkyMetaEngine::getMaximumSaveSlot() const { return MAX_SAVE_GAMES; }
  273. void SkyMetaEngine::removeSaveState(const char *target, int slot) const {
  274. if (slot == 0) // do not delete the auto save
  275. return;
  276. Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
  277. char fName[20];
  278. sprintf(fName,"SKY-VM.%03d", slot);
  279. saveFileMan->removeSavefile(fName);
  280. // Load current save game descriptions
  281. Common::StringArray savenames;
  282. savenames.resize(MAX_SAVE_GAMES+1);
  283. Common::InSaveFile *inf;
  284. inf = saveFileMan->openForLoading("SKY-VM.SAV");
  285. if (inf != NULL) {
  286. char *tmpBuf = new char[MAX_SAVE_GAMES * MAX_TEXT_LEN];
  287. char *tmpPtr = tmpBuf;
  288. inf->read(tmpBuf, MAX_SAVE_GAMES * MAX_TEXT_LEN);
  289. for (int i = 0; i < MAX_SAVE_GAMES; ++i) {
  290. savenames[i] = tmpPtr;
  291. tmpPtr += savenames[i].size() + 1;
  292. }
  293. delete inf;
  294. delete[] tmpBuf;
  295. }
  296. // Update the save game description at the given slot
  297. savenames[slot] = "";
  298. // Save the updated descriptions
  299. Common::OutSaveFile *outf;
  300. outf = saveFileMan->openForSaving("SKY-VM.SAV");
  301. bool ioFailed = true;
  302. if (outf) {
  303. for (uint16 cnt = 0; cnt < MAX_SAVE_GAMES; cnt++) {
  304. outf->write(savenames[cnt].c_str(), savenames[cnt].size() + 1);
  305. }
  306. outf->finalize();
  307. if (!outf->err())
  308. ioFailed = false;
  309. delete outf;
  310. }
  311. if (ioFailed)
  312. warning("Unable to store Savegame names to file SKY-VM.SAV. (%s)", saveFileMan->popErrorDesc().c_str());
  313. }
  314. #if PLUGIN_ENABLED_DYNAMIC(SKY)
  315. REGISTER_PLUGIN_DYNAMIC(SKY, PLUGIN_TYPE_ENGINE, SkyMetaEngine);
  316. #else
  317. REGISTER_PLUGIN_STATIC(SKY, PLUGIN_TYPE_ENGINE, SkyMetaEngine);
  318. #endif
  319. namespace Sky {
  320. Common::Error SkyEngine::loadGameState(int slot) {
  321. uint16 result = _skyControl->quickXRestore(slot - 1);
  322. return (result == GAME_RESTORED) ? Common::kNoError : Common::kUnknownError;
  323. }
  324. Common::Error SkyEngine::saveGameState(int slot, const Common::String &desc, bool isAutosave) {
  325. // Set the save slot and save the game
  326. _skyControl->_selectedGame = isAutosave ? 0 : slot - 1;
  327. if (_skyControl->saveGameToFile(false, nullptr, isAutosave) != GAME_SAVED)
  328. return Common::kWritePermissionDenied;
  329. // Load current save game descriptions
  330. Common::StringArray saveGameTexts;
  331. saveGameTexts.resize(MAX_SAVE_GAMES+1);
  332. _skyControl->loadDescriptions(saveGameTexts);
  333. // Update the save game description at the given slot
  334. if (!isAutosave)
  335. saveGameTexts[slot - 1] = desc;
  336. // Save the updated descriptions
  337. _skyControl->saveDescriptions(saveGameTexts);
  338. return Common::kNoError;
  339. }
  340. bool SkyEngine::canLoadGameStateCurrently() {
  341. return _systemVars.pastIntro && _skyControl->loadSaveAllowed();
  342. }
  343. bool SkyEngine::canSaveGameStateCurrently() {
  344. return _systemVars.pastIntro && _skyControl->loadSaveAllowed();
  345. }
  346. } // End of namespace Sky