PageRenderTime 50ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/engines/saga/saveload.cpp

https://github.com/bluegr/scummvm
C++ | 393 lines | 275 code | 73 blank | 45 comment | 60 complexity | c7f6affba79941f6f0ccffcf3656fd5f MD5 | raw file
  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 "common/savefile.h"
  23. #include "common/system.h"
  24. #include "graphics/thumbnail.h"
  25. #include "saga/saga.h"
  26. #include "saga/actor.h"
  27. #include "saga/events.h"
  28. #include "saga/interface.h"
  29. #include "saga/isomap.h"
  30. #include "saga/music.h"
  31. #include "saga/render.h"
  32. #include "saga/scene.h"
  33. #include "saga/script.h"
  34. #define CURRENT_SAGA_VER 8
  35. namespace Saga {
  36. static SaveFileData emptySlot = {
  37. "", 0
  38. };
  39. char* SagaEngine::calcSaveFileName(uint slotNumber) {
  40. static char name[MAX_FILE_NAME];
  41. sprintf(name, "%s.s%02u", _targetName.c_str(), slotNumber);
  42. return name;
  43. }
  44. SaveFileData *SagaEngine::getSaveFile(uint idx) {
  45. if (idx >= MAX_SAVES) {
  46. error("getSaveFileName wrong idx");
  47. }
  48. if (isSaveListFull()) {
  49. return &_saveFiles[_saveFilesCount - idx - 1];
  50. } else {
  51. if (!emptySlot.name[0])
  52. Common::strlcpy(emptySlot.name, getTextString(kTextNewSave), SAVE_TITLE_SIZE);
  53. return (idx == 0) ? &emptySlot : &_saveFiles[_saveFilesCount - idx];
  54. }
  55. }
  56. bool SagaEngine::locateSaveFile(char *saveName, uint &titleNumber) {
  57. uint i;
  58. for (i = 0; i < _saveFilesCount; i++) {
  59. if (strcmp(saveName, _saveFiles[i].name) == 0) {
  60. if (isSaveListFull()) {
  61. titleNumber = _saveFilesCount - i - 1;
  62. } else {
  63. titleNumber = _saveFilesCount - i;
  64. }
  65. return true;
  66. }
  67. }
  68. return false;
  69. }
  70. uint SagaEngine::getNewSaveSlotNumber() const {
  71. uint i, j;
  72. bool found;
  73. for (i = 0; i < MAX_SAVES; i++) {
  74. found = false;
  75. for (j = 0; j < _saveFilesCount; j++) {
  76. if (_saveFiles[j].slotNumber == i) {
  77. found = true;
  78. break;
  79. }
  80. }
  81. if (!found) {
  82. return i;
  83. }
  84. }
  85. error("getNewSaveSlotNumber save list is full");
  86. }
  87. static int compareSaveFileData(const void *a, const void *b) {
  88. const SaveFileData *s1 = (const SaveFileData *)a;
  89. const SaveFileData *s2 = (const SaveFileData *)b;
  90. if (s1->slotNumber < s2->slotNumber) {
  91. return -1;
  92. } else if (s1->slotNumber > s2->slotNumber) {
  93. return 1;
  94. } else {
  95. return 0;
  96. }
  97. }
  98. void SagaEngine::fillSaveList() {
  99. int i;
  100. Common::InSaveFile *in;
  101. Common::StringArray filenames;
  102. char slot[3];
  103. int slotNumber;
  104. char *name;
  105. name = calcSaveFileName(MAX_SAVES);
  106. name[strlen(name) - 2] = '*';
  107. name[strlen(name) - 1] = 0;
  108. filenames = _saveFileMan->listSavefiles(name);
  109. for (i = 0; i < MAX_SAVES; i++) {
  110. _saveFiles[i].name[0] = 0;
  111. _saveFiles[i].slotNumber = (uint)-1;
  112. }
  113. _saveFilesCount = 0;
  114. for (Common::StringArray::iterator file = filenames.begin(); file != filenames.end(); ++file){
  115. //Obtain the last 2 digits of the filename, since they correspond to the save slot
  116. slot[0] = file->c_str()[file->size()-2];
  117. slot[1] = file->c_str()[file->size()-1];
  118. slot[2] = 0;
  119. slotNumber = atoi(slot);
  120. if (slotNumber >= 0 && slotNumber < MAX_SAVES) {
  121. name = calcSaveFileName(slotNumber);
  122. if ((in = _saveFileMan->openForLoading(name)) != NULL) {
  123. _saveHeader.type = in->readUint32BE();
  124. _saveHeader.size = in->readUint32LE();
  125. _saveHeader.version = in->readUint32LE();
  126. in->read(_saveHeader.name, sizeof(_saveHeader.name));
  127. if (_saveHeader.type != MKTAG('S','A','G','A')) {
  128. warning("SagaEngine::load wrong save %s format", name);
  129. i++;
  130. continue;
  131. }
  132. strcpy(_saveFiles[_saveFilesCount].name, _saveHeader.name);
  133. _saveFiles[_saveFilesCount].slotNumber = slotNumber;
  134. delete in;
  135. _saveFilesCount++;
  136. }
  137. }
  138. }
  139. qsort(_saveFiles, _saveFilesCount, sizeof(_saveFiles[0]), compareSaveFileData);
  140. }
  141. void SagaEngine::save(const char *fileName, const char *saveName) {
  142. Common::OutSaveFile *out;
  143. char title[TITLESIZE];
  144. if (!(out = _saveFileMan->openForSaving(fileName))) {
  145. return;
  146. }
  147. _saveHeader.type = MKTAG('S','A','G','A');
  148. _saveHeader.size = 0;
  149. _saveHeader.version = CURRENT_SAGA_VER;
  150. // Note that IHNM has a smaller save title size than ITE
  151. // We allocate the ITE save title size here, to preserve
  152. // savegame backwards compatibility
  153. Common::strlcpy(_saveHeader.name, saveName, SAVE_TITLE_SIZE);
  154. out->writeUint32BE(_saveHeader.type);
  155. out->writeUint32LE(_saveHeader.size);
  156. out->writeUint32LE(_saveHeader.version);
  157. out->write(_saveHeader.name, sizeof(_saveHeader.name));
  158. // Original game title
  159. memset(title, 0, TITLESIZE);
  160. Common::strlcpy(title, _gameTitle.c_str(), TITLESIZE);
  161. out->write(title, TITLESIZE);
  162. // Thumbnail
  163. // First draw scene without save dialog
  164. int oldMode = _interface->getMode();
  165. _render->clearFlag(RF_RENDERPAUSE); // Don't show paused game message in saved thumbnail
  166. _interface->setMode(kPanelMain);
  167. _render->drawScene();
  168. Graphics::saveThumbnail(*out);
  169. _interface->setMode(oldMode);
  170. // Date / time
  171. TimeDate curTime;
  172. _system->getTimeAndDate(curTime);
  173. uint32 saveDate = ((curTime.tm_mday & 0xFF) << 24) | (((curTime.tm_mon + 1) & 0xFF) << 16) | ((curTime.tm_year + 1900) & 0xFFFF);
  174. uint16 saveTime = ((curTime.tm_hour & 0xFF) << 8) | ((curTime.tm_min) & 0xFF);
  175. uint32 playTime = g_engine->getTotalPlayTime() / 1000;
  176. out->writeUint32BE(saveDate);
  177. out->writeUint16BE(saveTime);
  178. out->writeUint32BE(playTime);
  179. // Surrounding scene
  180. out->writeSint32LE(_scene->getOutsetSceneNumber());
  181. #ifdef ENABLE_IHNM
  182. if (getGameId() == GID_IHNM) {
  183. out->writeSint32LE(_scene->currentChapterNumber());
  184. out->writeSint32LE(0); // obsolete, was used for the protagonist
  185. out->writeSint32LE(_scene->getCurrentMusicTrack());
  186. out->writeSint32LE(_scene->getCurrentMusicRepeat());
  187. }
  188. #endif
  189. // Inset scene
  190. out->writeSint32LE(_scene->currentSceneNumber());
  191. #ifdef ENABLE_IHNM
  192. if (getGameId() == GID_IHNM) {
  193. out->writeUint32LE(_globalFlags);
  194. for (int i = 0; i < ARRAYSIZE(_ethicsPoints); i++)
  195. out->writeSint16LE(_ethicsPoints[i]);
  196. }
  197. #endif
  198. _interface->saveState(out);
  199. _actor->saveState(out);
  200. out->writeSint16LE(_script->_commonBuffer.size());
  201. out->write(_script->_commonBuffer.getBuffer(), _script->_commonBuffer.size());
  202. // ISO map x, y coordinates for ITE
  203. if (getGameId() == GID_ITE) {
  204. out->writeSint16LE(_isoMap->getMapPosition().x);
  205. out->writeSint16LE(_isoMap->getMapPosition().y);
  206. }
  207. out->finalize();
  208. if (out->err())
  209. warning("Can't write file '%s'. (Disk full?)", fileName);
  210. delete out;
  211. _interface->resetSaveReminder();
  212. }
  213. void SagaEngine::load(const char *fileName) {
  214. Common::InSaveFile *in;
  215. int commonBufferSize;
  216. int sceneNumber, insetSceneNumber;
  217. int mapx, mapy;
  218. char title[TITLESIZE];
  219. if (!(in = _saveFileMan->openForLoading(fileName))) {
  220. return;
  221. }
  222. _saveHeader.type = in->readUint32BE();
  223. _saveHeader.size = in->readUint32LE();
  224. _saveHeader.version = in->readUint32LE();
  225. in->read(_saveHeader.name, sizeof(_saveHeader.name));
  226. // Some older saves were not written in an endian safe fashion.
  227. // We try to detect this here by checking for extremly high version values.
  228. // If found, we retry with the data swapped.
  229. if (_saveHeader.version > 0xFFFFFF) {
  230. warning("This savegame is not endian safe, retrying with the data swapped");
  231. _saveHeader.version = SWAP_BYTES_32(_saveHeader.version);
  232. }
  233. debug(2, "Save version: 0x%X", _saveHeader.version);
  234. if (_saveHeader.version < 4)
  235. warning("This savegame is not endian-safe. There may be problems");
  236. if (_saveHeader.type != MKTAG('S','A','G','A')) {
  237. error("SagaEngine::load wrong save game format");
  238. }
  239. if (_saveHeader.version > 4) {
  240. in->read(title, TITLESIZE);
  241. debug(0, "Save is for: %s", title);
  242. }
  243. if (_saveHeader.version >= 6) {
  244. // We don't need the thumbnail here, so just read it and discard it
  245. Graphics::skipThumbnail(*in);
  246. in->readUint32BE(); // save date
  247. in->readUint16BE(); // save time
  248. if (_saveHeader.version >= 8) {
  249. uint32 playTime = in->readUint32BE();
  250. g_engine->setTotalPlayTime(playTime * 1000);
  251. }
  252. }
  253. // Clear pending events here, and don't process queued music events
  254. _events->clearList(false);
  255. // Surrounding scene
  256. sceneNumber = in->readSint32LE();
  257. #ifdef ENABLE_IHNM
  258. if (getGameId() == GID_IHNM) {
  259. int currentChapter = _scene->currentChapterNumber();
  260. _scene->setChapterNumber(in->readSint32LE());
  261. in->skip(4); // obsolete, was used for setting the protagonist
  262. if (_scene->currentChapterNumber() != currentChapter)
  263. _scene->changeScene(-2, 0, kTransitionFade, _scene->currentChapterNumber());
  264. _scene->setCurrentMusicTrack(in->readSint32LE());
  265. _scene->setCurrentMusicRepeat(in->readSint32LE());
  266. _music->stop();
  267. if (_scene->currentChapterNumber() == 8)
  268. _interface->setMode(kPanelChapterSelection);
  269. if (!isIHNMDemo()) {
  270. _music->play(_music->_songTable[_scene->getCurrentMusicTrack()], _scene->getCurrentMusicRepeat() ? MUSIC_LOOP : MUSIC_NORMAL);
  271. } else {
  272. _music->play(3, MUSIC_LOOP);
  273. }
  274. }
  275. #endif
  276. // Inset scene
  277. insetSceneNumber = in->readSint32LE();
  278. #ifdef ENABLE_IHNM
  279. if (getGameId() == GID_IHNM) {
  280. _globalFlags = in->readUint32LE();
  281. for (int i = 0; i < ARRAYSIZE(_ethicsPoints); i++)
  282. _ethicsPoints[i] = in->readSint16LE();
  283. }
  284. #endif
  285. _interface->loadState(in);
  286. _actor->loadState(in);
  287. commonBufferSize = in->readSint16LE();
  288. _script->_commonBuffer.resize(commonBufferSize);
  289. in->read(_script->_commonBuffer.getBuffer(), commonBufferSize);
  290. if (getGameId() == GID_ITE) {
  291. mapx = in->readSint16LE();
  292. mapy = in->readSint16LE();
  293. _isoMap->setMapPosition(mapx, mapy);
  294. }
  295. // Note: the mapx, mapy ISO map positions were incorrectly saved
  296. // for IHNM too, which has no ISO map scenes, up to save version 6.
  297. // Since they're at the end of the savegame, we just ignore them
  298. delete in;
  299. // Mute volume to prevent outScene music play
  300. int volume = _music->getVolume();
  301. _music->setVolume(0);
  302. _scene->clearSceneQueue();
  303. _scene->changeScene(sceneNumber, ACTOR_NO_ENTRANCE, kTransitionNoFade);
  304. _events->handleEvents(0); //dissolve backgrounds
  305. if (insetSceneNumber != sceneNumber) {
  306. _render->setFlag(RF_DISABLE_ACTORS);
  307. _scene->draw();
  308. _render->drawScene();
  309. _render->clearFlag(RF_DISABLE_ACTORS);
  310. _scene->changeScene(insetSceneNumber, ACTOR_NO_ENTRANCE, kTransitionNoFade);
  311. }
  312. _music->setVolume(volume);
  313. _interface->draw();
  314. // Abort any scene entry protagonist animations and auto-cue speeches.
  315. // Fixes bug #10009.
  316. _actor->abortAllSpeeches();
  317. _actor->_protagonist->_location = _actor->_protagonist->_finalTarget;
  318. _actor->actorEndWalk(ID_PROTAG, true);
  319. }
  320. } // End of namespace Saga