{"action":"created","comment":{"url":"https://api.github.com/repos/scummvm/scummvm/pulls/comments/30571658","id":30571658,"diff_hunk":"@@ -0,0 +1,522 @@\n+/* ScummVM - Graphic Adventure Engine\n+ *\n+ * ScummVM is the legal property of its developers, whose names\n+ * are too numerous to list here. Please refer to the COPYRIGHT\n+ * file distributed with this source distribution.\n+ *\n+ * This program is free software; you can redistribute it and/or\n+ * modify it under the terms of the GNU General Public License\n+ * as published by the Free Software Foundation; either version 2\n+ * of the License, or (at your option) any later version.\n+ *\n+ * This program is distributed in the hope that it will be useful,\n+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\n+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n+ * GNU General Public License for more details.\n+ *\n+ * You should have received a copy of the GNU General Public License\n+ * along with this program; if not, write to the Free Software\n+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n+ *\n+ */\n+\n+#include \"sherlock/saveload.h\"\n+#include \"sherlock/surface.h\"\n+#include \"sherlock/sherlock.h\"\n+#include \"common/system.h\"\n+#include \"graphics/scaler.h\"\n+#include \"graphics/thumbnail.h\"\n+\n+namespace Sherlock {\n+\n+const int ENV_POINTS[6][3] = {\n+\t{ 41, 80, 61 },\t\t// Exit\n+\t{ 81, 120, 101 },\t// Load\n+\t{ 121, 160, 141 },\t// Save\n+\t{ 161, 200, 181 },\t// Up\n+\t{ 201, 240, 221 },\t// Down\n+\t{ 241, 280, 261 }\t// Quit\n+};\n+\n+/*----------------------------------------------------------------*/\n+\n+SaveManager::SaveManager(SherlockEngine *vm, const Common::String &target) :\n+\t\t_vm(vm), _target(target) {\n+\t_saveThumb = nullptr;\n+\t_envMode = SAVEMODE_NONE;\n+\t_justLoaded = false;\n+\t_savegameIndex = 0;\n+}\n+\n+SaveManager::~SaveManager() {\n+\tif (_saveThumb) {\n+\t\t_saveThumb->free();\n+\t\tdelete _saveThumb;\n+\t}\n+}\n+\n+/**\n+ * Shows the in-game dialog interface for loading and saving games\n+ */\n+void SaveManager::drawInterface() {\n+\tScreen &screen = *_vm->_screen;\n+\tUserInterface &ui = *_vm->_ui;\n+\n+\t// Create a list of savegame slots\n+\tcreateSavegameList();\n+\n+\tscreen._backBuffer1.fillRect(Common::Rect(0, CONTROLS_Y, SHERLOCK_SCREEN_WIDTH, CONTROLS_Y + 10), BORDER_COLOR);\n+\tscreen._backBuffer1.fillRect(Common::Rect(0, CONTROLS_Y + 10, 2, SHERLOCK_SCREEN_HEIGHT), BORDER_COLOR);\n+\tscreen._backBuffer1.fillRect(Common::Rect(318, CONTROLS_Y + 10, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT), BORDER_COLOR);\n+\tscreen._backBuffer1.fillRect(Common::Rect(0, 199, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT), BORDER_COLOR);\n+\tscreen._backBuffer1.fillRect(Common::Rect(2, CONTROLS_Y + 10, SHERLOCK_SCREEN_WIDTH - 2, SHERLOCK_SCREEN_HEIGHT - 2), INV_BACKGROUND);\n+\n+\tscreen.makeButton(Common::Rect(ENV_POINTS[0][0], CONTROLS_Y, ENV_POINTS[0][1], CONTROLS_Y + 10),\n+\t\tENV_POINTS[0][2] - screen.stringWidth(\"Exit\") / 2, \"Exit\");\n+\tscreen.makeButton(Common::Rect(ENV_POINTS[1][0], CONTROLS_Y, ENV_POINTS[1][1], CONTROLS_Y + 10),\n+\t\tENV_POINTS[1][2] - screen.stringWidth(\"Load\") / 2, \"Load\");\n+\tscreen.makeButton(Common::Rect(ENV_POINTS[2][0], CONTROLS_Y, ENV_POINTS[2][1], CONTROLS_Y + 10),\n+\t\tENV_POINTS[2][2] - screen.stringWidth(\"Save\") / 2, \"Save\");\n+\tscreen.makeButton(Common::Rect(ENV_POINTS[3][0], CONTROLS_Y, ENV_POINTS[3][1], CONTROLS_Y + 10),\n+\t\tENV_POINTS[3][2] - screen.stringWidth(\"Up\") / 2, \"Up\");\n+\tscreen.makeButton(Common::Rect(ENV_POINTS[4][0], CONTROLS_Y, ENV_POINTS[4][1], CONTROLS_Y + 10),\n+\t\tENV_POINTS[4][2] - screen.stringWidth(\"Down\") / 2, \"Down\");\n+\tscreen.makeButton(Common::Rect(ENV_POINTS[5][0], CONTROLS_Y, ENV_POINTS[5][1], CONTROLS_Y + 10),\n+\t\tENV_POINTS[5][2] - screen.stringWidth(\"Quit\") / 2, \"Quit\");\n+\n+\tif (!_savegameIndex)\n+\t\tscreen.buttonPrint(Common::Point(ENV_POINTS[3][2], CONTROLS_Y), COMMAND_NULL, 0, \"Up\");\n+\n+\tif (_savegameIndex == MAX_SAVEGAME_SLOTS - 5)\n+\t\tscreen.buttonPrint(Common::Point(ENV_POINTS[4][2], CONTROLS_Y), COMMAND_NULL, 0, \"Down\");\n+\n+\tfor (int idx = _savegameIndex; idx < _savegameIndex + 5; ++idx)\n+\t{\n+\t\tscreen.gPrint(Common::Point(6, CONTROLS_Y + 11 + (idx - _savegameIndex) * 10),\n+\t\t\tINV_FOREGROUND, \"%d.\", idx + 1);\n+\t\tscreen.gPrint(Common::Point(24, CONTROLS_Y + 11 + (idx - _savegameIndex) * 10),\n+\t\t\tINV_FOREGROUND, \"%s\", _savegames[idx].c_str());\n+\t}\n+\n+\tif (!ui._slideWindows) {\n+\t\tscreen.slamRect(Common::Rect(0, CONTROLS_Y, SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT));\n+\t} else {\n+\t\tui.summonWindow();\n+\t}\n+\n+\t_envMode = SAVEMODE_NONE;\n+}\n+\n+/**\n+ * Build up a savegame list, with empty slots given an explicit Empty message\n+ */\n+void SaveManager::createSavegameList() {\n+\tScreen &screen = *_vm->_screen;\n+\n+\t_savegames.clear();\n+\tfor (int idx = 0; idx < MAX_SAVEGAME_SLOTS; ++idx)\n+\t\t_savegames.push_back(\"-EMPTY-\");\n+\n+\tSaveStateList saveList = getSavegameList(_target);\n+\tfor (uint idx = 0; idx < saveList.size(); ++idx) {\n+\t\tint slot = saveList[idx].getSaveSlot() - 1;\n+\t\tif (slot >= 0 && slot < MAX_SAVEGAME_SLOTS)\n+\t\t\t_savegames[slot] = saveList[idx].getDescription();\n+\t}\n+\n+\t// Ensure the names will fit on the screen\n+\tfor (uint idx = 0; idx < _savegames.size(); ++idx) {\n+\t\tint width = screen.stringWidth(_savegames[idx]) + 24;\n+\t\tif (width > 308) {\n+\t\t\t// It won't fit in, so remove characters until it does\n+\t\t\tdo {\n+\t\t\t\twidth -= screen.charWidth(_savegames[idx].lastChar());\n+\t\t\t\t_savegames[idx].deleteLastChar();\n+\t\t\t} while (width > 300);\n+\t\t}\n+\t}\n+}\n+\n+/**\n+ * Load a list of savegames\n+ */\n+SaveStateList SaveManager::getSavegameList(const Common::String &target) {\n+\tCommon::SaveFileManager *saveFileMan = g_system->getSavefileManager();\n+\tCommon::StringArray filenames;\n+\tCommon::String saveDesc;\n+\tCommon::String pattern = Common::String::format(\"%s.0??\", target.c_str());\n+\tSherlockSavegameHeader header;\n+\n+\tfilenames = saveFileMan->listSavefiles(pattern);\n+\tsort(filenames.begin(), filenames.end());   // Sort to get the files in numerical order\n+\n+\tSaveStateList saveList;\n+\tfor (Common::StringArray::const_iterator file = filenames.begin(); file != filenames.end(); ++file) {\n+\t\tconst char *ext = strrchr(file->c_str(), '.');\n+\t\tint slot = ext ? atoi(ext + 1) : -1;\n+\n+\t\tif (slot >= 0 && slot < MAX_SAVEGAME_SLOTS) {\n+\t\t\tCommon::InSaveFile *in = g_system->getSavefileManager()->openForLoading(*file);\n+\n+\t\t\tif (in) {\n+\t\t\t\treadSavegameHeader(in, header);\n+\t\t\t\tsaveList.push_back(SaveStateDescriptor(slot, header._saveName));\n+\n+\t\t\t\theader._thumbnail->free();\n+\t\t\t\tdelete header._thumbnail;\n+\t\t\t\tdelete in;\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\treturn saveList;\n+}\n+\n+const char *const SAVEGAME_STR = \"SHLK\";\n+#define SAVEGAME_STR_SIZE 4\n+\n+/**\n+ * Read in the header information for a savegame\n+ */\n+bool SaveManager::readSavegameHeader(Common::InSaveFile *in, SherlockSavegameHeader &header) {\n+\tchar saveIdentBuffer[SAVEGAME_STR_SIZE + 1];\n+\theader._thumbnail = nullptr;\n+\n+\t// Validate the header Id\n+\tin->read(saveIdentBuffer, SAVEGAME_STR_SIZE + 1);\n+\tif (strncmp(saveIdentBuffer, SAVEGAME_STR, SAVEGAME_STR_SIZE))\n+\t\treturn false;\n+\n+\theader._version = in->readByte();\n+\tif (header._version > SHERLOCK_SAVEGAME_VERSION)\n+\t\treturn false;\n+\n+\t// Read in the string\n+\theader._saveName.clear();\n+\tchar ch;\n+\twhile ((ch = (char)in->readByte()) != '\\0') header._saveName += ch;\n+\n+\t// Get the thumbnail\n+\theader._thumbnail = Graphics::loadThumbnail(*in);\n+\tif (!header._thumbnail)\n+\t\treturn false;\n+\n+\t// Read in save date/time\n+\theader._year = in->readSint16LE();\n+\theader._month = in->readSint16LE();\n+\theader._day = in->readSint16LE();\n+\theader._hour = in->readSint16LE();\n+\theader._minute = in->readSint16LE();\n+\theader._totalFrames = in->readUint32LE();\n+\n+\treturn true;\n+}\n+\n+/**\n+ * Write out the header information for a savegame\n+ */\n+void SaveManager::writeSavegameHeader(Common::OutSaveFile *out, SherlockSavegameHeader &header) {\n+\t// Write out a savegame header\n+\tout->write(SAVEGAME_STR, SAVEGAME_STR_SIZE + 1);\n+\n+\tout->writeByte(SHERLOCK_SAVEGAME_VERSION);\n+\n+\t// Write savegame name\n+\tout->write(header._saveName.c_str(), header._saveName.size());\n+\tout->writeByte('\\0');\n+\n+\t// Handle the thumbnail. If there's already one set by the game, create one\n+\tif (!_saveThumb)\n+\t\tcreateThumbnail();\n+\tGraphics::saveThumbnail(*out, *_saveThumb);\n+\n+\t_saveThumb->free();\n+\tdelete _saveThumb;\n+\t_saveThumb = nullptr;\n+\n+\t// Write out the save date/time\n+\tTimeDate td;\n+\tg_system->getTimeAndDate(td);\n+\tout->writeSint16LE(td.tm_year + 1900);\n+\tout->writeSint16LE(td.tm_mon + 1);\n+\tout->writeSint16LE(td.tm_mday);\n+\tout->writeSint16LE(td.tm_hour);\n+\tout->writeSint16LE(td.tm_min);\n+\tout->writeUint32LE(_vm->_events->getFrameCounter());\n+}\n+\n+/**\n+ * Creates a thumbnail for the current on-screen contents\n+ */\n+void SaveManager::createThumbnail() {\n+\tif (_saveThumb) {\n+\t\t_saveThumb->free();\n+\t\tdelete _saveThumb;\n+\t}\n+\n+\tuint8 thumbPalette[PALETTE_SIZE];\n+\t_vm->_screen->getPalette(thumbPalette);\n+\t_saveThumb = new Graphics::Surface();\n+\t::createThumbnail(_saveThumb, (const byte *)_vm->_screen->getPixels(), SHERLOCK_SCREEN_WIDTH, SHERLOCK_SCREEN_HEIGHT, thumbPalette);\n+}\n+\n+/**\n+ * Return the index of the button the mouse is over, if any\n+ */\n+int SaveManager::getHighlightedButton() const {\n+\tCommon::Point pt = _vm->_events->mousePos();\n+\n+\tfor (int idx = 0; idx < 6; ++idx) {\n+\t\tif (pt.x > ENV_POINTS[idx][0] && pt.x < ENV_POINTS[idx][1] && pt.y > CONTROLS_Y\n+\t\t\t\t&& pt.y < (CONTROLS_Y + 10))\n+\t\t\treturn idx;\n+\t}\n+\n+\treturn -1;\n+}\n+\n+/**\n+ * Handle highlighting buttons\n+ */\n+void SaveManager::highlightButtons(int btnIndex) {\n+\tScreen &screen = *_vm->_screen;\n+\tbyte color = (btnIndex == 0) ? COMMAND_HIGHLIGHTED : COMMAND_FOREGROUND;\n+\n+\tscreen.buttonPrint(Common::Point(ENV_POINTS[0][2], CONTROLS_Y), color, 1, \"Exit\");\n+\n+\tif ((btnIndex == 1) || ((_envMode == 1) && (btnIndex != 2)))\n+\t\tscreen.buttonPrint(Common::Point(ENV_POINTS[1][2], CONTROLS_Y), COMMAND_HIGHLIGHTED, true, \"Load\");\n+\telse\n+\t\tscreen.buttonPrint(Common::Point(ENV_POINTS[1][2], CONTROLS_Y), COMMAND_FOREGROUND, true, \"Load\");\n+\n+\tif ((btnIndex == 2) || ((_envMode == 2) && (btnIndex != 1)))\n+\t\tscreen.buttonPrint(Common::Point(ENV_POINTS[2][2], CONTROLS_Y), COMMAND_HIGHLIGHTED, true, \"Save\");\n+\telse\n+\t\tscreen.buttonPrint(Common::Point(ENV_POINTS[2][2], CONTROLS_Y), COMMAND_FOREGROUND, true, \"Save\");\n+\n+\tif (btnIndex == 3 && _savegameIndex)\n+\t\tscreen.buttonPrint(Common::Point(ENV_POINTS[3][2], CONTROLS_Y), COMMAND_HIGHLIGHTED, true, \"Up\");\n+\telse\n+\t\tif (_savegameIndex)\n+\t\t\tscreen.buttonPrint(Common::Point(ENV_POINTS[3][2], CONTROLS_Y), COMMAND_FOREGROUND, true, \"Up\");\n+\n+\tif ((btnIndex == 4) && (_savegameIndex < MAX_SAVEGAME_SLOTS - 5))\n+\t\tscreen.buttonPrint(Common::Point(ENV_POINTS[4][2], CONTROLS_Y), COMMAND_HIGHLIGHTED, true, \"Down\");\n+\telse if (_savegameIndex < (MAX_SAVEGAME_SLOTS - 5))\n+\t\tscreen.buttonPrint(Common::Point(ENV_POINTS[4][2], CONTROLS_Y), COMMAND_FOREGROUND, true, \"Down\");\n+\n+\tcolor = (btnIndex == 5) ? COMMAND_HIGHLIGHTED : COMMAND_FOREGROUND;\n+\tscreen.buttonPrint(Common::Point(ENV_POINTS[5][2], CONTROLS_Y), color, 1, \"Quit\");\n+}\n+\n+/**\n+ * Load the game in the specified slot\n+ */\n+void SaveManager::loadGame(int slot) {\n+\tCommon::InSaveFile *saveFile = g_system->getSavefileManager()->openForLoading(\n+\t\tgenerateSaveName(slot));\n+\tif (!saveFile)\n+\t\treturn;\n+\n+\t// Load the savaegame header\n+\tSherlockSavegameHeader header;\n+\tif (!readSavegameHeader(saveFile, header))\n+\t\terror(\"Invalid savegame\");\n+\n+\tif (header._thumbnail) {\n+\t\theader._thumbnail->free();\n+\t\tdelete header._thumbnail;\n+\t}\n+\n+\t// Synchronize the savegame data\n+\tCommon::Serializer s(saveFile, nullptr);\n+\tsynchronize(s);\n+\n+\tdelete saveFile;\n+}\n+\n+/**\n+ * Save the game in the specified slot with the given name\n+ */\n+void SaveManager::saveGame(int slot, const Common::String &name) {\n+\tCommon::OutSaveFile *out = g_system->getSavefileManager()->openForSaving(\n+\t\tgenerateSaveName(slot));\n+\n+\tSherlockSavegameHeader header;\n+\theader._saveName = name;\n+\twriteSavegameHeader(out, header);\n+\n+\t// Synchronize the savegame data\n+\tCommon::Serializer s(nullptr, out);\n+\tsynchronize(s);\n+\n+\tout->finalize();\n+\tdelete out;\n+}\n+\n+/**\n+ * Support method that generates a savegame name\n+ * @param slot\t\tSlot number\n+ */\n+Common::String SaveManager::generateSaveName(int slot) {\n+\treturn Common::String::format(\"%s.%03d\", _target.c_str(), slot);\n+}\n+\n+/**\n+ * Synchronize the data for a savegame\n+ */\n+void SaveManager::synchronize(Common::Serializer &s) {\n+\tInventory &inv = *_vm->_inventory;\n+\tJournal &journal = *_vm->_journal;\n+\tMap &map = *_vm->_map;\n+\tPeople &people = *_vm->_people;\n+\tScene &scene = *_vm->_scene;\n+\tScreen &screen = *_vm->_screen;\n+\tTalk &talk = *_vm->_talk;\n+\n+\tint oldFont = screen.fontNumber();\n+\n+\tinv.synchronize(s);\n+\tjournal.synchronize(s);\n+\tpeople.synchronize(s);\n+\tmap.synchronize(s);\n+\tscene.synchronize(s);\n+\tscreen.synchronize(s);\n+\ttalk.synchronize(s);\n+\t_vm->synchronize(s);\n+\n+\tif (screen.fontNumber() != oldFont)\n+\t\tjournal.resetPosition();\n+\n+\t_justLoaded = true;\n+}\n+\n+/**\n+ * Make sure that the selected savegame is on-screen\n+ */\n+bool SaveManager::checkGameOnScreen(int slot) {\n+\tScreen &screen = *_vm->_screen;\n+\n+\t// Check if it's already on-screen\n+\tif (slot != -1 && (slot < _savegameIndex || slot >= (_savegameIndex + 5))) {\n+\t\t_savegameIndex = slot;\n+\n+\t\tscreen._backBuffer1.fillRect(Common::Rect(3, CONTROLS_Y + 11, SHERLOCK_SCREEN_WIDTH - 2,\n+\t\t\tSHERLOCK_SCREEN_HEIGHT - 1), INV_BACKGROUND);\n+\n+\t\tfor (int idx = _savegameIndex; idx < (_savegameIndex + 5); ++idx) {\n+\t\t\tscreen.gPrint(Common::Point(6, CONTROLS_Y + 11 + (idx - _savegameIndex) * 10),\n+\t\t\t\tINV_FOREGROUND, \"%d.\", idx + 1);\n+\t\t\tscreen.gPrint(Common::Point(24, CONTROLS_Y + 11 + (idx - _savegameIndex) * 10),\n+\t\t\t\tINV_FOREGROUND, \"%s\", _savegames[idx].c_str());\n+\t\t}\n+\n+\t\tscreen.slamRect(Common::Rect(3, CONTROLS_Y + 11, 318, SHERLOCK_SCREEN_HEIGHT));\n+\n+\t\tbyte color = !_savegameIndex ? COMMAND_NULL : COMMAND_FOREGROUND;\n+\t\tscreen.buttonPrint(Common::Point(ENV_POINTS[3][2], CONTROLS_Y), color, 1, \"Up\");\n+\n+\t\tcolor = (_savegameIndex == (MAX_SAVEGAME_SLOTS - 5)) ? COMMAND_NULL : COMMAND_FOREGROUND;\n+\t\tscreen.buttonPrint(Common::Point(ENV_POINTS[4][2], CONTROLS_Y), color, 1, \"Down\");\n+\n+\t\treturn true;\n+\t}\n+\n+\treturn false;\n+}\n+\n+bool SaveManager::getFilename(int slot) {\n+\tEvents &events = *_vm->_events;\n+\tScene &scene = *_vm->_scene;\n+\tScreen &screen = *_vm->_screen;\n+\tTalk &talk = *_vm->_talk;\n+\tint xp, yp;\n+\tbool flag = false;\n+\n+\tscreen.buttonPrint(Common::Point(ENV_POINTS[0][2], CONTROLS_Y), COMMAND_NULL, true, \"Exit\");\n+\tscreen.buttonPrint(Common::Point(ENV_POINTS[1][2], CONTROLS_Y), COMMAND_NULL, true, \"Load\");\n+\tscreen.buttonPrint(Common::Point(ENV_POINTS[2][2], CONTROLS_Y), COMMAND_NULL, true, \"Save\");\n+\tscreen.buttonPrint(Common::Point(ENV_POINTS[3][2], CONTROLS_Y), COMMAND_NULL, true, \"Up\");\n+\tscreen.buttonPrint(Common::Point(ENV_POINTS[4][2], CONTROLS_Y), COMMAND_NULL, true, \"Down\");\n+\tscreen.buttonPrint(Common::Point(ENV_POINTS[5][2], CONTROLS_Y), COMMAND_NULL, true, \"Quit\");\n+\n+\tCommon::String saveName = _savegames[slot];\n+\tif (saveName.equalsIgnoreCase(\"-EMPTY-\")) {","path":"engines/sherlock/saveload.cpp","position":444,"original_position":444,"commit_id":"0faf1c0b8f5c52cde1addae3e14469fc5fa9b9a2","original_commit_id":"0faf1c0b8f5c52cde1addae3e14469fc5fa9b9a2","user":{"login":"eriktorbjorn","id":601765,"avatar_url":"https://avatars.githubusercontent.com/u/601765?v=3","gravatar_id":"","url":"https://api.github.com/users/eriktorbjorn","html_url":"https://github.com/eriktorbjorn","followers_url":"https://api.github.com/users/eriktorbjorn/followers","following_url":"https://api.github.com/users/eriktorbjorn/following{/other_user}","gists_url":"https://api.github.com/users/eriktorbjorn/gists{/gist_id}","starred_url":"https://api.github.com/users/eriktorbjorn/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/eriktorbjorn/subscriptions","organizations_url":"https://api.github.com/users/eriktorbjorn/orgs","repos_url":"https://api.github.com/users/eriktorbjorn/repos","events_url":"https://api.github.com/users/eriktorbjorn/events{/privacy}","received_events_url":"https://api.github.com/users/eriktorbjorn/received_events","type":"User","site_admin":false},"body":"It's unlikely that anyone would actually do it, but you can create a savegame with the name \"-EMPTY-\", so I guess it would be safer to check if there's a save file for the slot. (Even though the current behavior does match the original engine.)","created_at":"2015-05-19T06:16:23Z","updated_at":"2015-05-19T06:16:23Z","html_url":"https://github.com/scummvm/scummvm/pull/595#discussion_r30571658","pull_request_url":"https://api.github.com/repos/scummvm/scummvm/pulls/595","_links":{"self":{"href":"https://api.github.com/repos/scummvm/scummvm/pulls/comments/30571658"},"html":{"href":"https://github.com/scummvm/scummvm/pull/595#discussion_r30571658"},"pull_request":{"href":"https://api.github.com/repos/scummvm/scummvm/pulls/595"}}},"pull_request":{"url":"https://api.github.com/repos/scummvm/scummvm/pulls/595","id":34967668,"html_url":"https://github.com/scummvm/scummvm/pull/595","diff_url":"https://github.com/scummvm/scummvm/pull/595.diff","patch_url":"https://github.com/scummvm/scummvm/pull/595.patch","issue_url":"https://api.github.com/repos/scummvm/scummvm/issues/595","number":595,"state":"open","locked":false,"title":"SHERLOCK: Support for The Lost Files of Sherlock Holmes - The Case of the Serrated Scalpel","user":{"login":"dreammaster","id":552828,"avatar_url":"https://avatars.githubusercontent.com/u/552828?v=3","gravatar_id":"","url":"https://api.github.com/users/dreammaster","html_url":"https://github.com/dreammaster","followers_url":"https://api.github.com/users/dreammaster/followers","following_url":"https://api.github.com/users/dreammaster/following{/other_user}","gists_url":"https://api.github.com/users/dreammaster/gists{/gist_id}","starred_url":"https://api.github.com/users/dreammaster/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dreammaster/subscriptions","organizations_url":"https://api.github.com/users/dreammaster/orgs","repos_url":"https://api.github.com/users/dreammaster/repos","events_url":"https://api.github.com/users/dreammaster/events{/privacy}","received_events_url":"https://api.github.com/users/dreammaster/received_events","type":"User","site_admin":false},"body":"This is a pull request for the game  The Lost Files of Sherlock Holmes - The Case of the Serrated Scalpel. It was the first of two games using a custom-built engine developed by Mythos Software, and published by EA. The second game of which being the The Case of the Rose Tattoo. The game is completable, although I'm still actively testing the game. There are only a few things left to do:\r\n\r\n* Implement sound support. There's some in-progress work on this that hasn't yet been committed in.\r\n* The game has an infamous (at least here in the group) initial logo display that uses a separate executable just for that which is, of all things, using the TsAGE engine. :). This still needs to be disassembled at some point; but since it's just a logo display, and not even a cutscene, I don't think this should be considered an obstacle to merging or even announcing official support for the game. Rather, a nice to have for the future.\r\n\r\nOther than that, the code base is pretty solid. Apart from minor bugs that I'm still fixing as I identify them, the engine is fairly solid. Everything's broken up into clean classes, and I've kept the code I consider to be specific to Serrated Scalpel separate in it's own descendant class of the main \"SherlockEngine\" class. That way, it should be easier to add in support for the Rose Tattoo as well later on, without major refactoring","created_at":"2015-05-07T23:13:22Z","updated_at":"2015-05-19T06:16:23Z","closed_at":null,"merged_at":null,"merge_commit_sha":"134edc5d33f1731acf7e67df859b2b93a03e0e60","assignee":null,"milestone":null,"commits_url":"https://api.github.com/repos/scummvm/scummvm/pulls/595/commits","review_comments_url":"https://api.github.com/repos/scummvm/scummvm/pulls/595/comments","review_comment_url":"https://api.github.com/repos/scummvm/scummvm/pulls/comments{/number}","comments_url":"https://api.github.com/repos/scummvm/scummvm/issues/595/comments","statuses_url":"https://api.github.com/repos/scummvm/scummvm/statuses/0faf1c0b8f5c52cde1addae3e14469fc5fa9b9a2","head":{"label":"dreammaster:sherlock","ref":"sherlock","sha":"0faf1c0b8f5c52cde1addae3e14469fc5fa9b9a2","user":{"login":"dreammaster","id":552828,"avatar_url":"https://avatars.githubusercontent.com/u/552828?v=3","gravatar_id":"","url":"https://api.github.com/users/dreammaster","html_url":"https://github.com/dreammaster","followers_url":"https://api.github.com/users/dreammaster/followers","following_url":"https://api.github.com/users/dreammaster/following{/other_user}","gists_url":"https://api.github.com/users/dreammaster/gists{/gist_id}","starred_url":"https://api.github.com/users/dreammaster/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dreammaster/subscriptions","organizations_url":"https://api.github.com/users/dreammaster/orgs","repos_url":"https://api.github.com/users/dreammaster/repos","events_url":"https://api.github.com/users/dreammaster/events{/privacy}","received_events_url":"https://api.github.com/users/dreammaster/received_events","type":"User","site_admin":false},"repo":{"id":1364360,"name":"scummvm","full_name":"dreammaster/scummvm","owner":{"login":"dreammaster","id":552828,"avatar_url":"https://avatars.githubusercontent.com/u/552828?v=3","gravatar_id":"","url":"https://api.github.com/users/dreammaster","html_url":"https://github.com/dreammaster","followers_url":"https://api.github.com/users/dreammaster/followers","following_url":"https://api.github.com/users/dreammaster/following{/other_user}","gists_url":"https://api.github.com/users/dreammaster/gists{/gist_id}","starred_url":"https://api.github.com/users/dreammaster/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/dreammaster/subscriptions","organizations_url":"https://api.github.com/users/dreammaster/orgs","repos_url":"https://api.github.com/users/dreammaster/repos","events_url":"https://api.github.com/users/dreammaster/events{/privacy}","received_events_url":"https://api.github.com/users/dreammaster/received_events","type":"User","site_admin":false},"private":false,"html_url":"https://github.com/dreammaster/scummvm","description":"ScummVM main repository","fork":true,"url":"https://api.github.com/repos/dreammaster/scummvm","forks_url":"https://api.github.com/repos/dreammaster/scummvm/forks","keys_url":"https://api.github.com/repos/dreammaster/scummvm/keys{/key_id}","collaborators_url":"https://api.github.com/repos/dreammaster/scummvm/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/dreammaster/scummvm/teams","hooks_url":"https://api.github.com/repos/dreammaster/scummvm/hooks","issue_events_url":"https://api.github.com/repos/dreammaster/scummvm/issues/events{/number}","events_url":"https://api.github.com/repos/dreammaster/scummvm/events","assignees_url":"https://api.github.com/repos/dreammaster/scummvm/assignees{/user}","branches_url":"https://api.github.com/repos/dreammaster/scummvm/branches{/branch}","tags_url":"https://api.github.com/repos/dreammaster/scummvm/tags","blobs_url":"https://api.github.com/repos/dreammaster/scummvm/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/dreammaster/scummvm/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/dreammaster/scummvm/git/refs{/sha}","trees_url":"https://api.github.com/repos/dreammaster/scummvm/git/trees{/sha}","statuses_url":"https://api.github.com/repos/dreammaster/scummvm/statuses/{sha}","languages_url":"https://api.github.com/repos/dreammaster/scummvm/languages","stargazers_url":"https://api.github.com/repos/dreammaster/scummvm/stargazers","contributors_url":"https://api.github.com/repos/dreammaster/scummvm/contributors","subscribers_url":"https://api.github.com/repos/dreammaster/scummvm/subscribers","subscription_url":"https://api.github.com/repos/dreammaster/scummvm/subscription","commits_url":"https://api.github.com/repos/dreammaster/scummvm/commits{/sha}","git_commits_url":"https://api.github.com/repos/dreammaster/scummvm/git/commits{/sha}","comments_url":"https://api.github.com/repos/dreammaster/scummvm/comments{/number}","issue_comment_url":"https://api.github.com/repos/dreammaster/scummvm/issues/comments{/number}","contents_url":"https://api.github.com/repos/dreammaster/scummvm/contents/{+path}","compare_url":"https://api.github.com/repos/dreammaster/scummvm/compare/{base}...{head}","merges_url":"https://api.github.com/repos/dreammaster/scummvm/merges","archive_url":"https://api.github.com/repos/dreammaster/scummvm/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/dreammaster/scummvm/downloads","issues_url":"https://api.github.com/repos/dreammaster/scummvm/issues{/number}","pulls_url":"https://api.github.com/repos/dreammaster/scummvm/pulls{/number}","milestones_url":"https://api.github.com/repos/dreammaster/scummvm/milestones{/number}","notifications_url":"https://api.github.com/repos/dreammaster/scummvm/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/dreammaster/scummvm/labels{/name}","releases_url":"https://api.github.com/repos/dreammaster/scummvm/releases{/id}","created_at":"2011-02-14T07:44:28Z","updated_at":"2015-02-20T01:58:11Z","pushed_at":"2015-05-19T04:29:03Z","git_url":"git://github.com/dreammaster/scummvm.git","ssh_url":"git@github.com:dreammaster/scummvm.git","clone_url":"https://github.com/dreammaster/scummvm.git","svn_url":"https://github.com/dreammaster/scummvm","homepage":"http://www.scummvm.org/","size":173209,"stargazers_count":3,"watchers_count":3,"language":"C++","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":1,"mirror_url":null,"open_issues_count":0,"forks":1,"open_issues":0,"watchers":3,"default_branch":"master"}},"base":{"label":"scummvm:master","ref":"master","sha":"2e8e85310d245f273bb7c89c01d8eb5f0491c639","user":{"login":"scummvm","id":365181,"avatar_url":"https://avatars.githubusercontent.com/u/365181?v=3","gravatar_id":"","url":"https://api.github.com/users/scummvm","html_url":"https://github.com/scummvm","followers_url":"https://api.github.com/users/scummvm/followers","following_url":"https://api.github.com/users/scummvm/following{/other_user}","gists_url":"https://api.github.com/users/scummvm/gists{/gist_id}","starred_url":"https://api.github.com/users/scummvm/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/scummvm/subscriptions","organizations_url":"https://api.github.com/users/scummvm/orgs","repos_url":"https://api.github.com/users/scummvm/repos","events_url":"https://api.github.com/users/scummvm/events{/privacy}","received_events_url":"https://api.github.com/users/scummvm/received_events","type":"Organization","site_admin":false},"repo":{"id":1358940,"name":"scummvm","full_name":"scummvm/scummvm","owner":{"login":"scummvm","id":365181,"avatar_url":"https://avatars.githubusercontent.com/u/365181?v=3","gravatar_id":"","url":"https://api.github.com/users/scummvm","html_url":"https://github.com/scummvm","followers_url":"https://api.github.com/users/scummvm/followers","following_url":"https://api.github.com/users/scummvm/following{/other_user}","gists_url":"https://api.github.com/users/scummvm/gists{/gist_id}","starred_url":"https://api.github.com/users/scummvm/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/scummvm/subscriptions","organizations_url":"https://api.github.com/users/scummvm/orgs","repos_url":"https://api.github.com/users/scummvm/repos","events_url":"https://api.github.com/users/scummvm/events{/privacy}","received_events_url":"https://api.github.com/users/scummvm/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/scummvm/scummvm","description":"ScummVM main repository","fork":false,"url":"https://api.github.com/repos/scummvm/scummvm","forks_url":"https://api.github.com/repos/scummvm/scummvm/forks","keys_url":"https://api.github.com/repos/scummvm/scummvm/keys{/key_id}","collaborators_url":"https://api.github.com/repos/scummvm/scummvm/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/scummvm/scummvm/teams","hooks_url":"https://api.github.com/repos/scummvm/scummvm/hooks","issue_events_url":"https://api.github.com/repos/scummvm/scummvm/issues/events{/number}","events_url":"https://api.github.com/repos/scummvm/scummvm/events","assignees_url":"https://api.github.com/repos/scummvm/scummvm/assignees{/user}","branches_url":"https://api.github.com/repos/scummvm/scummvm/branches{/branch}","tags_url":"https://api.github.com/repos/scummvm/scummvm/tags","blobs_url":"https://api.github.com/repos/scummvm/scummvm/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/scummvm/scummvm/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/scummvm/scummvm/git/refs{/sha}","trees_url":"https://api.github.com/repos/scummvm/scummvm/git/trees{/sha}","statuses_url":"https://api.github.com/repos/scummvm/scummvm/statuses/{sha}","languages_url":"https://api.github.com/repos/scummvm/scummvm/languages","stargazers_url":"https://api.github.com/repos/scummvm/scummvm/stargazers","contributors_url":"https://api.github.com/repos/scummvm/scummvm/contributors","subscribers_url":"https://api.github.com/repos/scummvm/scummvm/subscribers","subscription_url":"https://api.github.com/repos/scummvm/scummvm/subscription","commits_url":"https://api.github.com/repos/scummvm/scummvm/commits{/sha}","git_commits_url":"https://api.github.com/repos/scummvm/scummvm/git/commits{/sha}","comments_url":"https://api.github.com/repos/scummvm/scummvm/comments{/number}","issue_comment_url":"https://api.github.com/repos/scummvm/scummvm/issues/comments{/number}","contents_url":"https://api.github.com/repos/scummvm/scummvm/contents/{+path}","compare_url":"https://api.github.com/repos/scummvm/scummvm/compare/{base}...{head}","merges_url":"https://api.github.com/repos/scummvm/scummvm/merges","archive_url":"https://api.github.com/repos/scummvm/scummvm/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/scummvm/scummvm/downloads","issues_url":"https://api.github.com/repos/scummvm/scummvm/issues{/number}","pulls_url":"https://api.github.com/repos/scummvm/scummvm/pulls{/number}","milestones_url":"https://api.github.com/repos/scummvm/scummvm/milestones{/number}","notifications_url":"https://api.github.com/repos/scummvm/scummvm/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/scummvm/scummvm/labels{/name}","releases_url":"https://api.github.com/repos/scummvm/scummvm/releases{/id}","created_at":"2011-02-12T15:50:57Z","updated_at":"2015-05-17T12:17:00Z","pushed_at":"2015-05-19T04:29:04Z","git_url":"git://github.com/scummvm/scummvm.git","ssh_url":"git@github.com:scummvm/scummvm.git","clone_url":"https://github.com/scummvm/scummvm.git","svn_url":"https://github.com/scummvm/scummvm","homepage":"http://www.scummvm.org/","size":230477,"stargazers_count":504,"watchers_count":504,"language":"C++","has_issues":false,"has_downloads":true,"has_wiki":false,"has_pages":false,"forks_count":258,"mirror_url":null,"open_issues_count":26,"forks":258,"open_issues":26,"watchers":504,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/scummvm/scummvm/pulls/595"},"html":{"href":"https://github.com/scummvm/scummvm/pull/595"},"issue":{"href":"https://api.github.com/repos/scummvm/scummvm/issues/595"},"comments":{"href":"https://api.github.com/repos/scummvm/scummvm/issues/595/comments"},"review_comments":{"href":"https://api.github.com/repos/scummvm/scummvm/pulls/595/comments"},"review_comment":{"href":"https://api.github.com/repos/scummvm/scummvm/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/scummvm/scummvm/pulls/595/commits"},"statuses":{"href":"https://api.github.com/repos/scummvm/scummvm/statuses/0faf1c0b8f5c52cde1addae3e14469fc5fa9b9a2"}}}}