aboutsummaryrefslogtreecommitdiff
path: root/engines/titanic/core/project_item.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/titanic/core/project_item.cpp')
-rw-r--r--engines/titanic/core/project_item.cpp549
1 files changed, 549 insertions, 0 deletions
diff --git a/engines/titanic/core/project_item.cpp b/engines/titanic/core/project_item.cpp
new file mode 100644
index 0000000000..76293233b0
--- /dev/null
+++ b/engines/titanic/core/project_item.cpp
@@ -0,0 +1,549 @@
+/* ScummVM - Graphic Adventure Engine
+ *
+ * ScummVM is the legal property of its developers, whose names
+ * are too numerous to list here. Please refer to the COPYRIGHT
+ * file distributed with this source distribution.
+ *
+ * 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 "common/file.h"
+#include "common/savefile.h"
+#include "graphics/scaler.h"
+#include "graphics/thumbnail.h"
+#include "titanic/game_manager.h"
+#include "titanic/titanic.h"
+#include "titanic/core/dont_save_file_item.h"
+#include "titanic/core/node_item.h"
+#include "titanic/core/project_item.h"
+#include "titanic/core/view_item.h"
+#include "titanic/pet_control/pet_control.h"
+
+namespace Titanic {
+
+#define CURRENT_SAVEGAME_VERSION 1
+#define MAX_SAVEGAME_SLOTS 99
+#define MINIMUM_SAVEGAME_VERSION 1
+
+static const char *const SAVEGAME_STR = "TNIC";
+#define SAVEGAME_STR_SIZE 4
+
+EMPTY_MESSAGE_MAP(CProjectItem, CFileItem);
+
+void CFileListItem::save(SimpleFile *file, int indent) {
+ file->writeNumberLine(0, indent);
+ file->writeQuotedLine(_name, indent);
+
+ ListItem::save(file, indent);
+}
+
+void CFileListItem::load(SimpleFile *file) {
+ file->readNumber();
+ _name = file->readString();
+
+ ListItem::load(file);
+}
+
+/*------------------------------------------------------------------------*/
+
+CProjectItem::CProjectItem() : _nextRoomNumber(0), _nextMessageNumber(0),
+ _nextObjectNumber(0), _gameManager(nullptr) {
+}
+
+void CProjectItem::save(SimpleFile *file, int indent) {
+ file->writeNumberLine(6, indent);
+ file->writeQuotedLine("Next Avail. Object Number", indent);
+ file->writeNumberLine(_nextObjectNumber, indent);
+ file->writeQuotedLine("Next Avail. Message Number", indent);
+ file->writeNumberLine(_nextMessageNumber, indent);
+
+ file->writeQuotedLine("", indent);
+ _files.save(file, indent);
+
+ file->writeQuotedLine("Next Avail. Room Number", indent);
+ file->writeNumberLine(_nextRoomNumber, indent);
+
+ CTreeItem::save(file, indent);
+}
+
+void CProjectItem::buildFilesList() {
+ _files.destroyContents();
+
+ CTreeItem *treeItem = getFirstChild();
+ while (treeItem) {
+ if (treeItem->isFileItem()) {
+ CString name = static_cast<CFileItem *>(treeItem)->getFilename();
+ _files.add()->_name = name;
+ }
+
+ treeItem = getNextSibling();
+ }
+}
+
+void CProjectItem::load(SimpleFile *file) {
+ int val = file->readNumber();
+ _files.destroyContents();
+ int count;
+
+ switch (val) {
+ case 1:
+ file->readBuffer();
+ _nextRoomNumber = file->readNumber();
+ // Deliberate fall-through
+
+ case 0:
+ // Load the list of files
+ count = file->readNumber();
+ for (int idx = 0; idx < count; ++idx) {
+ CString name = file->readString();
+ _files.add()->_name = name;
+ }
+ break;
+
+ case 6:
+ file->readBuffer();
+ _nextObjectNumber = file->readNumber();
+ // Deliberate fall-through
+
+ case 5:
+ file->readBuffer();
+ _nextMessageNumber = file->readNumber();
+ // Deliberate fall-through
+
+ case 4:
+ file->readBuffer();
+ // Deliberate fall-through
+
+ case 2:
+ case 3:
+ _files.load(file);
+ file->readBuffer();
+ _nextRoomNumber = file->readNumber();
+ break;
+
+ default:
+ break;
+ }
+
+ CTreeItem::load(file);
+}
+
+CGameManager *CProjectItem::getGameManager() const {
+ return _gameManager;
+}
+
+void CProjectItem::setGameManager(CGameManager *gameManager) {
+ if (!_gameManager)
+ _gameManager = gameManager;
+}
+
+void CProjectItem::resetGameManager() {
+ _gameManager = nullptr;
+}
+
+void CProjectItem::loadGame(int slotId) {
+ CompressedFile file;
+
+ // Clear any existing project contents and call preload code
+ clear();
+ preLoad();
+
+ // Open either an existing savegame slot or the new game template
+ if (slotId >= 0) {
+ Common::InSaveFile *saveFile = g_system->getSavefileManager()->openForLoading(
+ g_vm->generateSaveName(slotId));
+ file.open(saveFile);
+ } else {
+ Common::File *newFile = new Common::File();
+ if (!newFile->open("newgame.st"))
+ error("Could not open newgame.st");
+ file.open(newFile);
+ }
+
+ // Load the savegame header in
+ TitanicSavegameHeader header;
+ readSavegameHeader(&file, header);
+ delete header._thumbnail;
+
+ // Load the contents in
+ CProjectItem *newProject = loadData(&file);
+ file.IsClassStart();
+ getGameManager()->load(&file);
+
+ file.close();
+
+ // Clear existing project
+ clear();
+
+ // Detach each item under the loaded project, and re-attach them
+ // to the existing project instance (this)
+ CTreeItem *item;
+ while ((item = newProject->getFirstChild()) != nullptr) {
+ item->detach();
+ item->addUnder(this);
+ }
+
+ // Loaded project instance is no longer needed
+ newProject->destroyAll();
+
+ // Post-load processing
+ postLoad();
+}
+
+void CProjectItem::saveGame(int slotId, const CString &desc) {
+ CompressedFile file;
+ Common::OutSaveFile *saveFile = g_system->getSavefileManager()->openForSaving(
+ g_vm->generateSaveName(slotId), false);
+ file.open(saveFile);
+
+ // Signal the game is being saved
+ preSave();
+
+ // Write out the savegame header
+ TitanicSavegameHeader header;
+ header._saveName = desc;
+ writeSavegameHeader(&file, header);
+
+ // Save the contents out
+ saveData(&file, this);
+
+ // Save the game manager data
+ _gameManager->save(&file);
+
+ // Close the file and signal that the saving has finished
+ file.close();
+ postSave();
+}
+
+void CProjectItem::clear() {
+ CTreeItem *item;
+ while ((item = getFirstChild()) != nullptr)
+ item->destroyAll();
+}
+
+CProjectItem *CProjectItem::loadData(SimpleFile *file) {
+ if (!file->IsClassStart())
+ return nullptr;
+
+ CProjectItem *root = nullptr;
+ CTreeItem *parent = nullptr;
+ CTreeItem *item = nullptr;
+
+ do {
+ CString entryString = file->readString();
+
+ if (entryString == "ALONG") {
+ // Move along, nothing needed
+ } else if (entryString == "UP") {
+ // Move up
+ if (parent == nullptr ||
+ (parent = parent->getParent()) == nullptr)
+ break;
+ } else if (entryString == "DOWN") {
+ // Move down
+ if (parent == nullptr)
+ parent = item;
+ else
+ parent = parent->getLastChild();
+ } else {
+ // Create new class instance
+ item = dynamic_cast<CTreeItem *>(CSaveableObject::createInstance(entryString));
+ assert(item);
+
+ if (root) {
+ // Already created root project
+ item->addUnder(parent);
+ } else {
+ root = dynamic_cast<CProjectItem *>(item);
+ assert(root);
+ root->_filename = _filename;
+ }
+
+ // Load the data for the item
+ item->load(file);
+ }
+
+ file->IsClassStart();
+ } while (file->IsClassStart());
+
+ return root;
+}
+
+void CProjectItem::saveData(SimpleFile *file, CTreeItem *item) const {
+ while (item) {
+ item->saveHeader(file, 0);
+ item->save(file, 1);
+ item->saveFooter(file, 0);
+
+ CTreeItem *child = item->getFirstChild();
+ if (child) {
+ file->write("\n{\n", 3);
+ file->writeQuotedString("DOWN");
+ file->write("\n}\n", 3);
+ saveData(file, child);
+ file->write("\n{\n", 3);
+ file->writeQuotedString("UP");
+ } else {
+ file->write("\n{\n", 3);
+ file->writeQuotedString("ALONG");
+ }
+
+ file->write("\n}\n", 3);
+ item = item->getNextSibling();
+ }
+}
+
+void CProjectItem::preLoad() {
+ if (_gameManager)
+ _gameManager->preLoad();
+}
+
+void CProjectItem::postLoad() {
+ CGameManager *gameManager = getGameManager();
+ if (gameManager)
+ gameManager->postLoad(this);
+
+ CPetControl *petControl = getPetControl();
+ if (petControl)
+ petControl->postLoad();
+}
+
+void CProjectItem::preSave() {
+ if (_gameManager)
+ _gameManager->preSave(this);
+}
+
+void CProjectItem::postSave() {
+ if (_gameManager)
+ _gameManager->postSave();
+}
+
+CPetControl *CProjectItem::getPetControl() const {
+ CDontSaveFileItem *fileItem = getDontSaveFileItem();
+ CTreeItem *treeItem;
+
+ if (!fileItem || (treeItem = fileItem->getLastChild()) == nullptr)
+ return nullptr;
+
+ while (treeItem) {
+ CPetControl *petControl = dynamic_cast<CPetControl *>(treeItem);
+ if (petControl)
+ return petControl;
+
+ treeItem = treeItem->getPriorSibling();
+ }
+
+ return nullptr;
+}
+
+CRoomItem *CProjectItem::findFirstRoom() const {
+ return dynamic_cast<CRoomItem *>(findChildInstance(CRoomItem::_type));
+}
+
+CTreeItem *CProjectItem::findChildInstance(ClassDef *classDef) const {
+ CTreeItem *treeItem = getFirstChild();
+ if (treeItem == nullptr)
+ return nullptr;
+
+ do {
+ CTreeItem *childItem = treeItem->getFirstChild();
+ if (childItem) {
+ do {
+ if (childItem->isInstanceOf(classDef))
+ return childItem;
+ } while ((childItem = childItem->getNextSibling()) != nullptr);
+ }
+ } while ((treeItem = treeItem->getNextSibling()) != nullptr);
+
+ return nullptr;
+}
+
+CRoomItem *CProjectItem::findNextRoom(CRoomItem *priorRoom) const {
+ return dynamic_cast<CRoomItem *>(findSiblingInstanceOf(CRoomItem::_type, priorRoom));
+}
+
+CTreeItem *CProjectItem::findSiblingInstanceOf(ClassDef *classDef, CTreeItem *startItem) const {
+ CTreeItem *treeItem = startItem->getParent()->getNextSibling();
+ if (treeItem == nullptr)
+ return nullptr;
+
+ return findChildInstance(classDef);
+}
+
+CDontSaveFileItem *CProjectItem::getDontSaveFileItem() const {
+ for (CTreeItem *treeItem = getFirstChild(); treeItem; treeItem = treeItem->getNextSibling()) {
+ if (treeItem->isInstanceOf(CDontSaveFileItem::_type))
+ return dynamic_cast<CDontSaveFileItem *>(treeItem);
+ }
+
+ return nullptr;
+}
+
+CRoomItem *CProjectItem::findHiddenRoom() {
+ return dynamic_cast<CRoomItem *>(findByName("HiddenRoom"));
+}
+
+CViewItem *CProjectItem::findView(int roomNumber, int nodeNumber, int viewNumber) {
+ CTreeItem *treeItem = getFirstChild();
+ CRoomItem *roomItem = nullptr;
+
+ // Scan for the specified room
+ if (treeItem) {
+ do {
+ CTreeItem *childItem = treeItem->getFirstChild();
+ CRoomItem *rItem = dynamic_cast<CRoomItem *>(childItem);
+ if (rItem && rItem->_roomNumber == roomNumber) {
+ roomItem = rItem;
+ break;
+ }
+ } while ((treeItem = treeItem->getNextSibling()) != nullptr);
+ }
+ if (!roomItem)
+ return nullptr;
+
+ // Scan for the specified node within the room
+ CNodeItem *nodeItem = nullptr;
+
+ CNodeItem *nItem = dynamic_cast<CNodeItem *>(
+ roomItem->findChildInstanceOf(CNodeItem::_type));
+ for (; nItem && !nodeItem; nItem = dynamic_cast<CNodeItem *>(
+ findNextInstanceOf(CNodeItem::_type, nItem))) {
+ if (nItem->_nodeNumber == nodeNumber)
+ nodeItem = nItem;
+ }
+ if (!nodeItem)
+ return nullptr;
+
+ // Scan for the specified view within the node
+ CViewItem *viewItem = dynamic_cast<CViewItem *>(
+ nodeItem->findChildInstanceOf(CViewItem::_type));
+ for (; viewItem; viewItem = dynamic_cast<CViewItem *>(
+ findNextInstanceOf(CViewItem::_type, viewItem))) {
+ if (viewItem->_viewNumber == viewNumber)
+ return viewItem;
+ }
+
+ return nullptr;
+}
+
+SaveStateList CProjectItem::getSavegameList(const Common::String &target) {
+ Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
+ Common::StringArray filenames;
+ Common::String saveDesc;
+ Common::String pattern = Common::String::format("%s.0??", target.c_str());
+ TitanicSavegameHeader header;
+
+ filenames = saveFileMan->listSavefiles(pattern);
+ sort(filenames.begin(), filenames.end()); // Sort to get the files in numerical order
+
+ SaveStateList saveList;
+ for (Common::StringArray::const_iterator file = filenames.begin(); file != filenames.end(); ++file) {
+ const char *ext = strrchr(file->c_str(), '.');
+ int slot = ext ? atoi(ext + 1) : -1;
+
+ if (slot >= 0 && slot < MAX_SAVEGAME_SLOTS) {
+ Common::InSaveFile *in = g_system->getSavefileManager()->openForLoading(*file);
+
+ if (in) {
+ SimpleFile f;
+ f.open(in);
+ if (!readSavegameHeader(&f, header))
+ continue;
+
+ saveList.push_back(SaveStateDescriptor(slot, header._saveName));
+
+ header._thumbnail->free();
+ delete header._thumbnail;
+ delete in;
+ }
+ }
+ }
+
+ return saveList;
+}
+
+bool CProjectItem::readSavegameHeader(SimpleFile *file, TitanicSavegameHeader &header) {
+ char saveIdentBuffer[SAVEGAME_STR_SIZE + 1];
+ header._thumbnail = nullptr;
+
+ // Validate the header Id
+ file->unsafeRead(saveIdentBuffer, SAVEGAME_STR_SIZE + 1);
+ if (strncmp(saveIdentBuffer, SAVEGAME_STR, SAVEGAME_STR_SIZE)) {
+ file->seek(-SAVEGAME_STR_SIZE, SEEK_CUR);
+ header._saveName = "Unnamed";
+ return true;
+ }
+
+ header._version = file->readByte();
+ if (header._version < MINIMUM_SAVEGAME_VERSION || header._version > CURRENT_SAVEGAME_VERSION)
+ return false;
+
+ // Read in the string
+ header._saveName.clear();
+ char ch;
+ while ((ch = (char)file->readByte()) != '\0') header._saveName += ch;
+
+ // Get the thumbnail
+ header._thumbnail = Graphics::loadThumbnail(*file);
+ if (!header._thumbnail)
+ return false;
+
+ // Read in save date/time
+ header._year = file->readUint16LE();
+ header._month = file->readUint16LE();
+ header._day = file->readUint16LE();
+ header._hour = file->readUint16LE();
+ header._minute = file->readUint16LE();
+ header._totalFrames = file->readUint32LE();
+
+ return true;
+}
+
+void CProjectItem::writeSavegameHeader(SimpleFile *file, TitanicSavegameHeader &header) {
+ // Write out a savegame header
+ file->write(SAVEGAME_STR, SAVEGAME_STR_SIZE + 1);
+
+ file->writeByte(CURRENT_SAVEGAME_VERSION);
+
+ // Write savegame name
+ file->write(header._saveName.c_str(), header._saveName.size());
+ file->writeByte('\0');
+
+ // Create a thumbnail of the screen and save it out
+ Graphics::Surface *thumb = createThumbnail();
+ Graphics::saveThumbnail(*file, *thumb);
+ thumb->free();
+ delete thumb;
+
+ // Write out the save date/time
+ TimeDate td;
+ g_system->getTimeAndDate(td);
+ file->writeUint16LE(td.tm_year + 1900);
+ file->writeUint16LE(td.tm_mon + 1);
+ file->writeUint16LE(td.tm_mday);
+ file->writeUint16LE(td.tm_hour);
+ file->writeUint16LE(td.tm_min);
+ file->writeUint32LE(g_vm->_events->getFrameCounter());
+}
+
+Graphics::Surface *CProjectItem::createThumbnail() {
+ Graphics::Surface *thumb = new Graphics::Surface();
+
+ ::createThumbnailFromScreen(thumb);
+ return thumb;
+}
+
+} // End of namespace Titanic