2016-02-26 23:32:06 +01:00
|
|
|
/* 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.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2016-02-29 16:50:24 +01:00
|
|
|
#include "common/system.h"
|
|
|
|
#include "common/savefile.h"
|
2016-03-03 13:43:26 +01:00
|
|
|
#include "common/translation.h"
|
2018-02-01 22:20:52 +01:00
|
|
|
#include "common/file.h"
|
|
|
|
#include "common/md5.h"
|
|
|
|
#include "common/debug.h"
|
2016-02-29 16:50:24 +01:00
|
|
|
|
2016-03-02 21:56:06 +01:00
|
|
|
#include "graphics/thumbnail.h"
|
|
|
|
|
2016-02-26 23:32:06 +01:00
|
|
|
#include "engines/advancedDetector.h"
|
2016-02-27 10:46:37 +01:00
|
|
|
|
2016-03-06 21:59:55 +01:00
|
|
|
#include "adl/detection.h"
|
2018-02-01 22:20:52 +01:00
|
|
|
#include "adl/disk.h"
|
2016-02-26 23:32:06 +01:00
|
|
|
|
|
|
|
namespace Adl {
|
|
|
|
|
2016-08-08 23:00:36 +02:00
|
|
|
// Mystery House was designed for monochrome display, so we default to
|
|
|
|
// monochrome mode there. All the other games default to color mode.
|
|
|
|
#define GAMEOPTION_COLOR_DEFAULT_OFF GUIO_GAMEOPTIONS1
|
|
|
|
#define GAMEOPTION_SCANLINES GUIO_GAMEOPTIONS2
|
|
|
|
#define GAMEOPTION_COLOR_DEFAULT_ON GUIO_GAMEOPTIONS3
|
2016-03-03 13:43:26 +01:00
|
|
|
|
|
|
|
static const ADExtraGuiOptionsMap optionsList[] = {
|
|
|
|
{
|
2016-08-08 23:00:36 +02:00
|
|
|
GAMEOPTION_COLOR_DEFAULT_OFF,
|
2016-03-03 13:43:26 +01:00
|
|
|
{
|
|
|
|
_s("Color mode"),
|
|
|
|
_s("Use color graphics"),
|
|
|
|
"color",
|
|
|
|
false
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2016-03-10 12:09:59 +01:00
|
|
|
{
|
2016-08-08 23:00:36 +02:00
|
|
|
GAMEOPTION_COLOR_DEFAULT_ON,
|
2016-03-10 12:09:59 +01:00
|
|
|
{
|
|
|
|
_s("Color mode"),
|
|
|
|
_s("Use color graphics"),
|
|
|
|
"color",
|
|
|
|
true
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
2016-03-03 13:43:26 +01:00
|
|
|
{
|
|
|
|
GAMEOPTION_SCANLINES,
|
|
|
|
{
|
|
|
|
_s("Scanlines"),
|
|
|
|
_s("Show scanlines"),
|
|
|
|
"scanlines",
|
|
|
|
false
|
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
AD_EXTRA_GUI_OPTIONS_TERMINATOR
|
|
|
|
};
|
|
|
|
|
2016-02-26 23:32:06 +01:00
|
|
|
static const PlainGameDescriptor adlGames[] = {
|
2016-04-03 22:19:29 +02:00
|
|
|
{ "hires0", "Hi-Res Adventure #0: Mission Asteroid" },
|
2016-03-10 10:46:06 +01:00
|
|
|
{ "hires1", "Hi-Res Adventure #1: Mystery House" },
|
|
|
|
{ "hires2", "Hi-Res Adventure #2: Wizard and the Princess" },
|
2017-07-25 13:53:44 +02:00
|
|
|
{ "hires3", "Hi-Res Adventure #3: Cranston Manor" },
|
2016-08-26 20:49:41 +02:00
|
|
|
{ "hires4", "Hi-Res Adventure #4: Ulysses and the Golden Fleece" },
|
2016-11-13 12:02:18 +01:00
|
|
|
{ "hires5", "Hi-Res Adventure #5: Time Zone" },
|
2016-04-08 12:35:23 +02:00
|
|
|
{ "hires6", "Hi-Res Adventure #6: The Dark Crystal" },
|
2016-03-10 10:46:06 +01:00
|
|
|
{ 0, 0 }
|
2016-02-26 23:32:06 +01:00
|
|
|
};
|
|
|
|
|
2018-02-03 11:42:03 +01:00
|
|
|
struct AdlGameDescription {
|
|
|
|
ADGameDescription desc;
|
|
|
|
GameType gameType;
|
|
|
|
GameVersion version;
|
|
|
|
};
|
|
|
|
|
2018-02-01 22:20:52 +01:00
|
|
|
static const AdlGameDescription gameFileDescriptions[] = {
|
2018-02-04 16:11:59 +01:00
|
|
|
{ // Hi-Res Adventure #1: Mystery House - Apple II - Contains Simi Valley address
|
|
|
|
{
|
|
|
|
"hires1", 0,
|
|
|
|
{
|
|
|
|
{ "ADVENTURE", 0, "22d9e63a11d69fa033ba1738715ad09a", 29952 },
|
|
|
|
{ "AUTO LOAD OBJ", 0, "a2ab7be25842e1fa9f1343b0894a8b6f", 4095 },
|
|
|
|
AD_LISTEND
|
|
|
|
},
|
|
|
|
Common::EN_ANY,
|
|
|
|
Common::kPlatformApple2,
|
|
|
|
ADGF_NO_FLAGS,
|
|
|
|
GUIO2(GAMEOPTION_COLOR_DEFAULT_OFF, GAMEOPTION_SCANLINES)
|
|
|
|
},
|
|
|
|
GAME_TYPE_HIRES1,
|
|
|
|
GAME_VER_HR1_SIMI
|
|
|
|
},
|
|
|
|
{ // Hi-Res Adventure #1: Mystery House - Apple II - Contains Coarsegold address
|
2018-02-03 11:42:03 +01:00
|
|
|
{
|
|
|
|
"hires1", 0,
|
|
|
|
{
|
|
|
|
{ "ADVENTURE", 0, "22d9e63a11d69fa033ba1738715ad09a", 29952 },
|
|
|
|
{ "AUTO LOAD OBJ", 0, "f6a6ac60c04c6ba6dff68b92cc279ba2", 12291 },
|
|
|
|
AD_LISTEND
|
|
|
|
},
|
|
|
|
Common::EN_ANY,
|
|
|
|
Common::kPlatformApple2,
|
|
|
|
ADGF_NO_FLAGS,
|
|
|
|
GUIO2(GAMEOPTION_COLOR_DEFAULT_OFF, GAMEOPTION_SCANLINES)
|
|
|
|
},
|
|
|
|
GAME_TYPE_HIRES1,
|
|
|
|
GAME_VER_HR1_COARSE
|
|
|
|
},
|
|
|
|
{ // Hi-Res Adventure #1: Mystery House - Apple II - Roberta Williams Anthology
|
2016-02-26 23:32:06 +01:00
|
|
|
{
|
|
|
|
"hires1", 0,
|
|
|
|
{
|
2016-03-10 10:46:06 +01:00
|
|
|
{ "ADVENTURE", 0, "22d9e63a11d69fa033ba1738715ad09a", 29952 },
|
|
|
|
{ "AUTO LOAD OBJ", 0, "23bfccfe9fcff9b22cf6c41bde9078ac", 12291 },
|
|
|
|
{ "MYSTERY.HELLO", 0, "2289b7fea300b506e902a4c597968369", 836 },
|
2016-02-26 23:32:06 +01:00
|
|
|
AD_LISTEND
|
|
|
|
},
|
|
|
|
Common::EN_ANY,
|
2016-08-08 23:07:28 +02:00
|
|
|
Common::kPlatformApple2,
|
2016-10-07 11:43:54 +02:00
|
|
|
ADGF_NO_FLAGS,
|
2016-08-08 23:00:36 +02:00
|
|
|
GUIO2(GAMEOPTION_COLOR_DEFAULT_OFF, GAMEOPTION_SCANLINES)
|
2016-03-24 22:50:42 +01:00
|
|
|
},
|
2018-02-03 11:42:03 +01:00
|
|
|
GAME_TYPE_HIRES1,
|
|
|
|
GAME_VER_HR1_PD
|
2016-03-24 22:50:42 +01:00
|
|
|
},
|
2018-02-03 11:42:03 +01:00
|
|
|
{ AD_TABLE_END_MARKER, GAME_TYPE_NONE, GAME_VER_NONE }
|
2018-02-01 22:20:52 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
static const AdlGameDescription gameDiskDescriptions[] = {
|
2018-02-04 16:11:59 +01:00
|
|
|
{ // Hi-Res Adventure #1: Mystery House - Apple II - Contains Coarsegold address
|
2016-03-24 22:50:42 +01:00
|
|
|
{
|
|
|
|
"hires1", 0,
|
|
|
|
{
|
2018-02-04 16:11:59 +01:00
|
|
|
{ "mysthous", 0, "8df0b3b3e609a2e40237e2419c1cb767", 116480 },
|
2016-03-24 22:50:42 +01:00
|
|
|
AD_LISTEND
|
|
|
|
},
|
|
|
|
Common::EN_ANY,
|
2016-08-08 23:07:28 +02:00
|
|
|
Common::kPlatformApple2,
|
2016-10-07 11:43:54 +02:00
|
|
|
ADGF_NO_FLAGS,
|
2016-08-08 23:00:36 +02:00
|
|
|
GUIO2(GAMEOPTION_COLOR_DEFAULT_OFF, GAMEOPTION_SCANLINES)
|
2016-02-26 23:32:06 +01:00
|
|
|
},
|
2018-02-03 11:42:03 +01:00
|
|
|
GAME_TYPE_HIRES1,
|
2018-02-04 16:11:59 +01:00
|
|
|
GAME_VER_HR1_COARSE
|
2018-02-03 11:42:03 +01:00
|
|
|
},
|
2018-02-04 16:11:59 +01:00
|
|
|
{ // Hi-Res Adventure #1: Mystery House - Apple II - Roberta Williams Anthology
|
2018-02-03 11:42:03 +01:00
|
|
|
{
|
|
|
|
"hires1", 0,
|
|
|
|
{
|
2018-02-04 16:11:59 +01:00
|
|
|
{ "mysthous", 0, "54d20eb1ef0084ac3c2d16c31c5b7eb7", 143360 },
|
2018-02-03 11:42:03 +01:00
|
|
|
AD_LISTEND
|
|
|
|
},
|
|
|
|
Common::EN_ANY,
|
|
|
|
Common::kPlatformApple2,
|
|
|
|
ADGF_NO_FLAGS,
|
|
|
|
GUIO2(GAMEOPTION_COLOR_DEFAULT_OFF, GAMEOPTION_SCANLINES)
|
|
|
|
},
|
|
|
|
GAME_TYPE_HIRES1,
|
2018-02-04 16:11:59 +01:00
|
|
|
GAME_VER_HR1_PD
|
2016-02-26 23:32:06 +01:00
|
|
|
},
|
2016-04-03 22:19:29 +02:00
|
|
|
{ // Hi-Res Adventure #2: Wizard and the Princess - Apple II - Roberta Williams Anthology
|
2016-03-10 10:46:06 +01:00
|
|
|
{
|
|
|
|
"hires2", 0,
|
|
|
|
{
|
2018-02-01 22:20:52 +01:00
|
|
|
{ "wizard", 0, "72b114bf8f94fafe5672daac2a70c765", 143360 },
|
2016-03-10 10:46:06 +01:00
|
|
|
AD_LISTEND
|
|
|
|
},
|
|
|
|
Common::EN_ANY,
|
2016-08-08 23:07:28 +02:00
|
|
|
Common::kPlatformApple2,
|
2016-10-07 11:43:54 +02:00
|
|
|
ADGF_NO_FLAGS,
|
2016-08-08 23:00:36 +02:00
|
|
|
GUIO2(GAMEOPTION_COLOR_DEFAULT_ON, GAMEOPTION_SCANLINES)
|
2016-03-10 10:46:06 +01:00
|
|
|
},
|
2018-02-03 11:42:03 +01:00
|
|
|
GAME_TYPE_HIRES2,
|
|
|
|
GAME_VER_NONE
|
2016-03-10 10:46:06 +01:00
|
|
|
},
|
2016-04-03 22:19:29 +02:00
|
|
|
{ // Hi-Res Adventure #0: Mission Asteroid - Apple II - Roberta Williams Anthology
|
|
|
|
{
|
|
|
|
"hires0", 0,
|
|
|
|
{
|
2018-02-01 22:20:52 +01:00
|
|
|
{ "mission", 0, "6bc53f51a3c8ee65c020af55fb8bd875", 116480 },
|
2016-04-03 22:19:29 +02:00
|
|
|
AD_LISTEND
|
|
|
|
},
|
|
|
|
Common::EN_ANY,
|
2016-08-08 23:07:28 +02:00
|
|
|
Common::kPlatformApple2,
|
2016-10-07 11:43:54 +02:00
|
|
|
ADGF_NO_FLAGS,
|
2016-08-08 23:00:36 +02:00
|
|
|
GUIO2(GAMEOPTION_COLOR_DEFAULT_ON, GAMEOPTION_SCANLINES)
|
2016-04-03 22:19:29 +02:00
|
|
|
},
|
2018-02-03 11:42:03 +01:00
|
|
|
GAME_TYPE_HIRES0,
|
|
|
|
GAME_VER_NONE
|
2016-04-03 22:19:29 +02:00
|
|
|
},
|
2017-07-25 13:53:44 +02:00
|
|
|
{ // Hi-Res Adventure #3: Cranston Manor - Apple II
|
|
|
|
{
|
|
|
|
"hires3", 0,
|
|
|
|
{
|
2018-02-01 22:20:52 +01:00
|
|
|
{ "cranston", 0, "e4d35440791a36e55299c7be1ccd2b04", 116480 },
|
2017-07-25 13:53:44 +02:00
|
|
|
AD_LISTEND
|
|
|
|
},
|
|
|
|
Common::EN_ANY,
|
|
|
|
Common::kPlatformApple2,
|
2017-11-11 23:41:39 +01:00
|
|
|
ADGF_NO_FLAGS,
|
2017-07-25 13:53:44 +02:00
|
|
|
GUIO2(GAMEOPTION_COLOR_DEFAULT_ON, GAMEOPTION_SCANLINES)
|
|
|
|
},
|
2018-02-03 11:42:03 +01:00
|
|
|
GAME_TYPE_HIRES3,
|
|
|
|
GAME_VER_NONE
|
2017-07-25 13:53:44 +02:00
|
|
|
},
|
2017-02-21 11:34:25 +01:00
|
|
|
{ // Hi-Res Adventure #4: Ulysses and the Golden Fleece - Apple II - Load 'N' Go
|
|
|
|
{
|
|
|
|
"hires4", 0,
|
|
|
|
{
|
2018-02-01 22:20:52 +01:00
|
|
|
{ "ulyssesa", 0, "1eaeb2f1a773ce2d1cb9f16b2ef09049", 143360 },
|
|
|
|
{ "ulyssesb", 1, "9fa8552255ae651b252844168b8b6617", 143360 },
|
2017-02-21 11:34:25 +01:00
|
|
|
AD_LISTEND
|
|
|
|
},
|
|
|
|
Common::EN_ANY,
|
|
|
|
Common::kPlatformApple2,
|
2017-11-11 23:41:39 +01:00
|
|
|
ADGF_NO_FLAGS,
|
2017-02-21 11:34:25 +01:00
|
|
|
GUIO2(GAMEOPTION_COLOR_DEFAULT_ON, GAMEOPTION_SCANLINES)
|
|
|
|
},
|
2018-02-03 11:42:03 +01:00
|
|
|
GAME_TYPE_HIRES4,
|
|
|
|
GAME_VER_NONE
|
2017-02-21 11:34:25 +01:00
|
|
|
},
|
2016-08-26 20:49:41 +02:00
|
|
|
{ // Hi-Res Adventure #4: Ulysses and the Golden Fleece - Atari 8-bit - Re-release
|
|
|
|
{
|
|
|
|
"hires4", 0,
|
|
|
|
{
|
2018-02-01 22:20:52 +01:00
|
|
|
{ "ulys1b", 0, "bb6aab9a35b41d160b6eefa088165f56", 92160 },
|
|
|
|
{ "ulys1a", 0, "c227eeee34d0bacd62b2d6231c409204", 92160 },
|
2016-09-15 21:27:02 +02:00
|
|
|
// Load 'N' Go Software release XAG-0646 appears to be missing the second disk
|
2018-02-01 22:20:52 +01:00
|
|
|
{ "ulys2c", 0, "8c6a76d1767e4ffa2f0118c9c56c0e90", 92160 },
|
2016-08-26 20:49:41 +02:00
|
|
|
AD_LISTEND
|
|
|
|
},
|
|
|
|
Common::EN_ANY,
|
2016-09-15 21:27:02 +02:00
|
|
|
Common::kPlatformAtari8Bit,
|
2016-08-26 20:49:41 +02:00
|
|
|
ADGF_UNSTABLE,
|
|
|
|
GUIO2(GAMEOPTION_COLOR_DEFAULT_ON, GAMEOPTION_SCANLINES)
|
|
|
|
},
|
2018-02-03 11:42:03 +01:00
|
|
|
GAME_TYPE_HIRES4,
|
|
|
|
GAME_VER_NONE
|
2016-08-26 20:49:41 +02:00
|
|
|
},
|
2016-11-13 12:02:18 +01:00
|
|
|
{ // Hi-Res Adventure #5: Time Zone - Apple II - Version 1.1 - Roberta Williams Anthology
|
|
|
|
{
|
|
|
|
"hires5", 0,
|
|
|
|
{
|
2018-02-01 22:20:52 +01:00
|
|
|
{ "tzone1a", 2, "731844b1d19c2801e3a5bc61d109af54", 143360 },
|
|
|
|
{ "tzone1b", 3, "4eaf8d790e3f93097cca9ddbe863df50", 143360 },
|
|
|
|
{ "tzone2c", 4, "e3aa4f56e727339b1ec00978ce9d435b", 143360 },
|
|
|
|
{ "tzone2d", 5, "77b8219a380410015c986fa192d4c3bf", 143360 },
|
|
|
|
{ "tzone3e", 6, "f7acc03edd8d8aecb90711cd5f9e5593", 143360 },
|
|
|
|
{ "tzone3f", 7, "ed74c056976ecea2eab07448c8a72eb8", 143360 },
|
|
|
|
{ "tzone4g", 8, "de7bda8a641169fc2dedd8a7b0b7e7de", 143360 },
|
|
|
|
{ "tzone4h", 9, "21cf76d97505ff09fff5d5e4711bc47c", 143360 },
|
|
|
|
{ "tzone5i", 10, "d665df374e594cd0978b73c3490e5de2", 143360 },
|
|
|
|
{ "tzone5j", 11, "5095be23d13201d0897b9169c4e473df", 143360 },
|
|
|
|
{ "tzone6k", 12, "bef044503f21af5f0a4088e99aa778b1", 143360 },
|
|
|
|
{ "tzone6l", 13, "84801b7c2ab6c09e62a2a0809b94d16a", 143360 },
|
2016-11-13 12:02:18 +01:00
|
|
|
AD_LISTEND
|
|
|
|
},
|
|
|
|
Common::EN_ANY,
|
|
|
|
Common::kPlatformApple2,
|
2017-11-11 10:39:16 +01:00
|
|
|
ADGF_NO_FLAGS,
|
2016-11-13 12:02:18 +01:00
|
|
|
GUIO2(GAMEOPTION_COLOR_DEFAULT_ON, GAMEOPTION_SCANLINES)
|
|
|
|
},
|
2018-02-03 11:42:03 +01:00
|
|
|
GAME_TYPE_HIRES5,
|
|
|
|
GAME_VER_NONE
|
2016-11-13 12:02:18 +01:00
|
|
|
},
|
2016-04-08 12:35:23 +02:00
|
|
|
{ // Hi-Res Adventure #6: The Dark Crystal - Apple II - Roberta Williams Anthology
|
|
|
|
{
|
|
|
|
"hires6", 0,
|
|
|
|
{
|
2018-02-01 22:20:52 +01:00
|
|
|
{ "dark1a", 0, "9a5968a8f378c84454d88f4cd4e143a9", 143360 },
|
|
|
|
{ "dark1b", 3, "1271ff9c3e1bdb4942301dd37dd0ef87", 143360 },
|
|
|
|
{ "dark2a", 4, "090e77563add7b4c9ab25f444d727316", 143360 },
|
|
|
|
{ "dark2b", 5, "f2db96af0955324900b800505af4d91f", 143360 },
|
2016-04-08 12:35:23 +02:00
|
|
|
AD_LISTEND
|
|
|
|
},
|
|
|
|
Common::EN_ANY,
|
2016-08-08 23:07:28 +02:00
|
|
|
Common::kPlatformApple2,
|
2017-11-11 10:39:16 +01:00
|
|
|
ADGF_NO_FLAGS,
|
2016-08-08 23:00:36 +02:00
|
|
|
GUIO2(GAMEOPTION_COLOR_DEFAULT_ON, GAMEOPTION_SCANLINES)
|
2016-04-08 12:35:23 +02:00
|
|
|
},
|
2018-02-03 11:42:03 +01:00
|
|
|
GAME_TYPE_HIRES6,
|
|
|
|
GAME_VER_NONE
|
2016-04-08 12:35:23 +02:00
|
|
|
},
|
2018-02-03 11:42:03 +01:00
|
|
|
{ AD_TABLE_END_MARKER, GAME_TYPE_NONE, GAME_VER_NONE }
|
2016-02-26 23:32:06 +01:00
|
|
|
};
|
|
|
|
|
2018-02-01 22:20:52 +01:00
|
|
|
struct DiskImageExt {
|
|
|
|
Common::Platform platform;
|
|
|
|
const char *extension;
|
|
|
|
};
|
|
|
|
|
2018-02-02 22:53:00 +01:00
|
|
|
const DiskImageExt diskImageExts[] = {
|
2018-02-01 22:20:52 +01:00
|
|
|
{ Common::kPlatformApple2, ".nib" },
|
|
|
|
{ Common::kPlatformApple2, ".dsk" },
|
|
|
|
{ Common::kPlatformApple2, ".d13" },
|
2018-02-02 23:38:52 +01:00
|
|
|
{ Common::kPlatformAtari8Bit, ".xfd" }
|
2018-02-01 22:20:52 +01:00
|
|
|
};
|
|
|
|
|
2016-02-26 23:32:06 +01:00
|
|
|
class AdlMetaEngine : public AdvancedMetaEngine {
|
|
|
|
public:
|
2018-02-01 22:20:52 +01:00
|
|
|
AdlMetaEngine() : AdvancedMetaEngine(gameFileDescriptions, sizeof(AdlGameDescription), adlGames, optionsList) { }
|
2016-02-26 23:32:06 +01:00
|
|
|
|
|
|
|
const char *getName() const {
|
2016-02-27 10:46:37 +01:00
|
|
|
return "ADL";
|
2016-02-26 23:32:06 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
const char *getOriginalCopyright() const {
|
|
|
|
return "Copyright (C) Sierra On-Line";
|
|
|
|
}
|
|
|
|
|
2016-02-29 16:50:24 +01:00
|
|
|
bool hasFeature(MetaEngineFeature f) const;
|
2016-03-02 21:56:06 +01:00
|
|
|
SaveStateDescriptor querySaveMetaInfos(const char *target, int slot) const;
|
|
|
|
int getMaximumSaveSlot() const { return 'O' - 'A'; }
|
2016-02-29 16:50:24 +01:00
|
|
|
SaveStateList listSaves(const char *target) const;
|
2016-03-02 21:56:06 +01:00
|
|
|
void removeSaveState(const char *target, int slot) const;
|
2018-02-01 22:20:52 +01:00
|
|
|
virtual ADGameDescList detectGame(const Common::FSNode &parent, const FileMap &allFiles, Common::Language language, Common::Platform platform, const Common::String &extra) const;
|
|
|
|
|
|
|
|
bool addFileProps(const FileMap &allFiles, Common::String fname, ADFilePropertiesMap &filePropsMap) const;
|
2016-02-29 16:50:24 +01:00
|
|
|
|
2016-02-26 23:32:06 +01:00
|
|
|
bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *gd) const;
|
|
|
|
};
|
|
|
|
|
2016-02-29 16:50:24 +01:00
|
|
|
bool AdlMetaEngine::hasFeature(MetaEngineFeature f) const {
|
|
|
|
switch(f) {
|
|
|
|
case kSupportsListSaves:
|
|
|
|
case kSupportsLoadingDuringStartup:
|
2016-03-02 21:56:06 +01:00
|
|
|
case kSupportsDeleteSave:
|
|
|
|
case kSavesSupportMetaInfo:
|
|
|
|
case kSavesSupportThumbnail:
|
|
|
|
case kSavesSupportCreationDate:
|
|
|
|
case kSavesSupportPlayTime:
|
2016-07-25 14:32:04 +06:00
|
|
|
case kSimpleSavesNames:
|
2016-02-29 16:50:24 +01:00
|
|
|
return true;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-02 21:56:06 +01:00
|
|
|
SaveStateDescriptor AdlMetaEngine::querySaveMetaInfos(const char *target, int slot) const {
|
|
|
|
Common::String fileName = Common::String::format("%s.s%02d", target, slot);
|
|
|
|
Common::InSaveFile *inFile = g_system->getSavefileManager()->openForLoading(fileName);
|
|
|
|
|
|
|
|
if (!inFile)
|
|
|
|
return SaveStateDescriptor();
|
|
|
|
|
|
|
|
if (inFile->readUint32BE() != MKTAG('A', 'D', 'L', ':')) {
|
|
|
|
delete inFile;
|
|
|
|
return SaveStateDescriptor();
|
|
|
|
}
|
|
|
|
|
|
|
|
byte saveVersion = inFile->readByte();
|
|
|
|
if (saveVersion != SAVEGAME_VERSION) {
|
|
|
|
delete inFile;
|
|
|
|
return SaveStateDescriptor();
|
|
|
|
}
|
|
|
|
|
|
|
|
char name[SAVEGAME_NAME_LEN] = { };
|
|
|
|
inFile->read(name, sizeof(name) - 1);
|
|
|
|
inFile->readByte();
|
|
|
|
|
|
|
|
if (inFile->eos() || inFile->err()) {
|
|
|
|
delete inFile;
|
|
|
|
return SaveStateDescriptor();
|
|
|
|
}
|
|
|
|
|
|
|
|
SaveStateDescriptor sd(slot, name);
|
|
|
|
|
|
|
|
int year = inFile->readUint16BE();
|
|
|
|
int month = inFile->readByte();
|
|
|
|
int day = inFile->readByte();
|
|
|
|
sd.setSaveDate(year + 1900, month + 1, day);
|
|
|
|
|
|
|
|
int hour = inFile->readByte();
|
|
|
|
int minutes = inFile->readByte();
|
|
|
|
sd.setSaveTime(hour, minutes);
|
|
|
|
|
|
|
|
uint32 playTime = inFile->readUint32BE();
|
|
|
|
sd.setPlayTime(playTime);
|
|
|
|
|
|
|
|
if (inFile->eos() || inFile->err()) {
|
|
|
|
delete inFile;
|
|
|
|
return SaveStateDescriptor();
|
|
|
|
}
|
|
|
|
|
ALL: Load savegame thumbnail only when necessary
This commit introduces the following changes:
1. Graphics::loadThumbnail()
Now returns a boolean and takes a new argument skipThumbnail which
defaults to false. In case of true, loadThumbnail() reads past the
thumbnail data in the input stream instead of actually loading the
thumbnail. This simplifies savegame handling where, up until now,
many engines always read the whole savegame metadata (including
the thumbnail) and then threw away the thumbnail when not needed
(which is in almost all cases, the most common exception being
MetaEngine::querySaveMetaInfos() which is responsible for loading
savegame metadata for displaying it in the GUI launcher.
2. readSavegameHeader()
Engines which already implement such a method (name varies) now take
a new argument skipThumbnail (default: true) which is passed
through to loadThumbnail(). This means that the default case for
readSavegameHeader() is now _not_ loading the thumbnail from a
savegame and just reading past it. In those cases, e.g.
querySaveMetaInfos(), where we actually are interested in loading
the thumbnail readSavegameHeader() needs to explicitely be called
with skipThumbnail == false.
Engines whose readSavegameHeader() (name varies) already takes an
argument loadThumbnail have been adapted to have a similar
prototype and semantics.
I.e. readSaveHeader(in, loadThumbnail, header) now is
readSaveHeader(in, header, skipThumbnail).
3. Error handling
Engines which previously did not check the return value of
readSavegameHeader() (name varies) now do so ensuring that possibly
broken savegames (be it a broken thumbnail or something else) don't
make it into the GUI launcher list in the first place.
2018-04-06 00:06:38 +02:00
|
|
|
Graphics::Surface *thumbnail;
|
|
|
|
if (!Graphics::loadThumbnail(*inFile, thumbnail)) {
|
|
|
|
delete inFile;
|
|
|
|
return SaveStateDescriptor();
|
|
|
|
}
|
2016-03-02 21:56:06 +01:00
|
|
|
sd.setThumbnail(thumbnail);
|
|
|
|
|
|
|
|
delete inFile;
|
|
|
|
return sd;
|
|
|
|
}
|
|
|
|
|
2016-02-29 16:50:24 +01:00
|
|
|
SaveStateList AdlMetaEngine::listSaves(const char *target) const {
|
|
|
|
Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
|
|
|
|
Common::StringArray files = saveFileMan->listSavefiles(Common::String(target) + ".s##");
|
|
|
|
|
|
|
|
SaveStateList saveList;
|
2016-03-02 21:56:06 +01:00
|
|
|
|
2016-02-29 16:50:24 +01:00
|
|
|
for (uint i = 0; i < files.size(); ++i) {
|
|
|
|
const Common::String &fileName = files[i];
|
|
|
|
Common::InSaveFile *inFile = saveFileMan->openForLoading(fileName);
|
|
|
|
if (!inFile) {
|
2016-03-06 21:59:55 +01:00
|
|
|
warning("Cannot open save file '%s'", fileName.c_str());
|
2016-02-29 16:50:24 +01:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (inFile->readUint32BE() != MKTAG('A', 'D', 'L', ':')) {
|
|
|
|
warning("No header found in '%s'", fileName.c_str());
|
|
|
|
delete inFile;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
byte saveVersion = inFile->readByte();
|
|
|
|
if (saveVersion != SAVEGAME_VERSION) {
|
2016-03-06 21:59:55 +01:00
|
|
|
warning("Unsupported save game version %i found in '%s'", saveVersion, fileName.c_str());
|
2016-02-29 16:50:24 +01:00
|
|
|
delete inFile;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
char name[SAVEGAME_NAME_LEN] = { };
|
|
|
|
inFile->read(name, sizeof(name) - 1);
|
|
|
|
delete inFile;
|
|
|
|
|
|
|
|
int slotNum = atoi(fileName.c_str() + fileName.size() - 2);
|
|
|
|
SaveStateDescriptor sd(slotNum, name);
|
|
|
|
saveList.push_back(sd);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Sort saves based on slot number.
|
|
|
|
Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
|
|
|
|
return saveList;
|
|
|
|
}
|
|
|
|
|
2016-03-02 21:56:06 +01:00
|
|
|
void AdlMetaEngine::removeSaveState(const char *target, int slot) const {
|
|
|
|
Common::String fileName = Common::String::format("%s.s%02d", target, slot);
|
|
|
|
g_system->getSavefileManager()->removeSavefile(fileName);
|
|
|
|
}
|
|
|
|
|
2018-02-03 11:42:03 +01:00
|
|
|
Common::String getDiskImageName(const AdlGameDescription &adlDesc, byte volume) {
|
|
|
|
const ADGameDescription &desc = adlDesc.desc;
|
2018-02-01 22:20:52 +01:00
|
|
|
for (uint i = 0; desc.filesDescriptions[i].fileName; ++i) {
|
|
|
|
const ADGameFileDescription &fDesc = desc.filesDescriptions[i];
|
|
|
|
|
|
|
|
if (fDesc.fileType == volume) {
|
|
|
|
for (uint e = 0; e < ARRAYSIZE(diskImageExts); ++e) {
|
|
|
|
if (diskImageExts[e].platform == desc.platform) {
|
|
|
|
Common::String testFileName(fDesc.fileName);
|
|
|
|
testFileName += diskImageExts[e].extension;
|
|
|
|
if (Common::File::exists(testFileName))
|
|
|
|
return testFileName;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
error("Failed to find disk image '%s'", fDesc.fileName);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
error("Disk volume %d not found", volume);
|
|
|
|
}
|
|
|
|
|
2018-02-03 11:42:03 +01:00
|
|
|
GameType getGameType(const AdlGameDescription &adlDesc) {
|
|
|
|
return adlDesc.gameType;
|
|
|
|
}
|
|
|
|
|
|
|
|
GameVersion getGameVersion(const AdlGameDescription &adlDesc) {
|
|
|
|
return adlDesc.version;
|
|
|
|
}
|
|
|
|
|
|
|
|
Common::Platform getPlatform(const AdlGameDescription &adlDesc) {
|
|
|
|
return adlDesc.desc.platform;
|
|
|
|
}
|
|
|
|
|
2018-02-01 22:20:52 +01:00
|
|
|
bool AdlMetaEngine::addFileProps(const FileMap &allFiles, Common::String fname, ADFilePropertiesMap &filePropsMap) const {
|
|
|
|
if (filePropsMap.contains(fname))
|
|
|
|
return true;
|
|
|
|
|
|
|
|
if (!allFiles.contains(fname))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
ADFileProperties fileProps;
|
|
|
|
fileProps.size = computeMD5(allFiles[fname], fileProps.md5, 16384);
|
|
|
|
|
|
|
|
if (fileProps.size != -1) {
|
|
|
|
debug(3, "> '%s': '%s'", fname.c_str(), fileProps.md5.c_str());
|
|
|
|
filePropsMap[fname] = fileProps;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Based on AdvancedMetaEngine::detectGame
|
|
|
|
ADGameDescList AdlMetaEngine::detectGame(const Common::FSNode &parent, const FileMap &allFiles, Common::Language language, Common::Platform platform, const Common::String &extra) const {
|
|
|
|
// We run the file-based detector first and then add to the returned list
|
|
|
|
ADGameDescList matched = AdvancedMetaEngine::detectGame(parent, allFiles, language, platform, extra);
|
|
|
|
|
|
|
|
debug(3, "Starting disk image detection in dir '%s'", parent.getPath().c_str());
|
|
|
|
|
|
|
|
ADFilePropertiesMap filesProps;
|
|
|
|
ADGameIdList matchedGameIds;
|
|
|
|
bool gotAnyMatchesWithAllFiles = false;
|
|
|
|
|
|
|
|
for (uint g = 0; gameDiskDescriptions[g].desc.gameId != 0; ++g) {
|
|
|
|
const ADGameDescription &desc = gameDiskDescriptions[g].desc;
|
|
|
|
|
|
|
|
// Skip games that don't meet the language/platform/extra criteria
|
|
|
|
if (language != Common::UNK_LANG && desc.language != Common::UNK_LANG) {
|
|
|
|
if (desc.language != language && !(language == Common::EN_ANY && (desc.flags & ADGF_ADDENGLISH)))
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (platform != Common::kPlatformUnknown && desc.platform != Common::kPlatformUnknown && desc.platform != platform)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if ((_flags & kADFlagUseExtraAsHint) && !extra.empty() && desc.extra != extra)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
bool fileMissing = false;
|
|
|
|
bool allFilesPresent = true;
|
|
|
|
bool hashOrSizeMismatch = false;
|
|
|
|
|
|
|
|
for (uint f = 0; desc.filesDescriptions[f].fileName; ++f) {
|
|
|
|
const ADGameFileDescription &fDesc = desc.filesDescriptions[f];
|
|
|
|
Common::String fileName;
|
|
|
|
bool foundDiskImage = false;
|
|
|
|
|
|
|
|
for (uint e = 0; e < ARRAYSIZE(diskImageExts); ++e) {
|
|
|
|
if (diskImageExts[e].platform == desc.platform) {
|
|
|
|
Common::String testFileName(fDesc.fileName);
|
|
|
|
testFileName += diskImageExts[e].extension;
|
|
|
|
|
|
|
|
if (addFileProps(allFiles, testFileName, filesProps)) {
|
|
|
|
if (foundDiskImage) {
|
|
|
|
warning("Ignoring '%s' (already found '%s')", testFileName.c_str(), fileName.c_str());
|
|
|
|
filesProps.erase(testFileName);
|
|
|
|
} else {
|
|
|
|
foundDiskImage = true;
|
|
|
|
fileName = testFileName;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!foundDiskImage) {
|
|
|
|
fileMissing = true;
|
|
|
|
allFilesPresent = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (hashOrSizeMismatch)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (fDesc.md5 && fDesc.md5 != filesProps[fileName].md5) {
|
|
|
|
debug(3, "MD5 Mismatch. Skipping (%s) (%s)", fDesc.md5, filesProps[fileName].md5.c_str());
|
|
|
|
fileMissing = true;
|
|
|
|
hashOrSizeMismatch = true;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (fDesc.fileSize != -1 && fDesc.fileSize != filesProps[fileName].size) {
|
|
|
|
debug(3, "Size Mismatch. Skipping");
|
|
|
|
fileMissing = true;
|
|
|
|
hashOrSizeMismatch = true;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
debug(3, "Matched file: %s", fileName.c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!fileMissing) {
|
|
|
|
debug(2, "Found game: %s (%s/%s) (%d)", desc.gameId, getPlatformDescription(desc.platform), getLanguageDescription(desc.language), g);
|
|
|
|
matched.push_back(&desc);
|
|
|
|
} else {
|
|
|
|
if (allFilesPresent) {
|
|
|
|
gotAnyMatchesWithAllFiles = true;
|
|
|
|
if (!matchedGameIds.size() || strcmp(matchedGameIds.back(), desc.gameId) != 0)
|
|
|
|
matchedGameIds.push_back(desc.gameId);
|
|
|
|
}
|
|
|
|
|
|
|
|
debug(5, "Skipping game: %s (%s/%s) (%d)", desc.gameId, getPlatformDescription(desc.platform), getLanguageDescription(desc.language), g);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-02 15:37:55 +01:00
|
|
|
// TODO: This could be improved to handle matched and unknown games together in a single directory
|
2018-02-01 22:20:52 +01:00
|
|
|
if (matched.empty()) {
|
|
|
|
if (!filesProps.empty() && gotAnyMatchesWithAllFiles) {
|
|
|
|
reportUnknown(parent, filesProps, matchedGameIds);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return matched;
|
|
|
|
}
|
|
|
|
|
2016-03-06 21:59:55 +01:00
|
|
|
Engine *HiRes1Engine_create(OSystem *syst, const AdlGameDescription *gd);
|
2016-03-10 10:46:06 +01:00
|
|
|
Engine *HiRes2Engine_create(OSystem *syst, const AdlGameDescription *gd);
|
2016-08-23 00:34:56 +02:00
|
|
|
Engine *HiRes0Engine_create(OSystem *syst, const AdlGameDescription *gd);
|
2017-07-25 13:53:44 +02:00
|
|
|
Engine *HiRes3Engine_create(OSystem *syst, const AdlGameDescription *gd);
|
2016-08-26 20:49:41 +02:00
|
|
|
Engine *HiRes4Engine_create(OSystem *syst, const AdlGameDescription *gd);
|
2016-11-13 12:02:18 +01:00
|
|
|
Engine *HiRes5Engine_create(OSystem *syst, const AdlGameDescription *gd);
|
2016-04-08 12:35:23 +02:00
|
|
|
Engine *HiRes6Engine_create(OSystem *syst, const AdlGameDescription *gd);
|
2016-03-06 21:59:55 +01:00
|
|
|
|
2016-02-26 23:32:06 +01:00
|
|
|
bool AdlMetaEngine::createInstance(OSystem *syst, Engine **engine, const ADGameDescription *gd) const {
|
2016-03-06 21:59:55 +01:00
|
|
|
if (!gd)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
const AdlGameDescription *adlGd = (const AdlGameDescription *)gd;
|
|
|
|
|
|
|
|
switch (adlGd->gameType) {
|
|
|
|
case GAME_TYPE_HIRES1:
|
|
|
|
*engine = HiRes1Engine_create(syst, adlGd);
|
|
|
|
break;
|
2016-03-10 10:46:06 +01:00
|
|
|
case GAME_TYPE_HIRES2:
|
|
|
|
*engine = HiRes2Engine_create(syst, adlGd);
|
|
|
|
break;
|
2016-08-23 00:34:56 +02:00
|
|
|
case GAME_TYPE_HIRES0:
|
|
|
|
*engine = HiRes0Engine_create(syst, adlGd);
|
|
|
|
break;
|
2017-07-25 13:53:44 +02:00
|
|
|
case GAME_TYPE_HIRES3:
|
|
|
|
*engine = HiRes3Engine_create(syst, adlGd);
|
|
|
|
break;
|
2016-08-26 20:49:41 +02:00
|
|
|
case GAME_TYPE_HIRES4:
|
|
|
|
*engine = HiRes4Engine_create(syst, adlGd);
|
|
|
|
break;
|
2016-11-13 12:02:18 +01:00
|
|
|
case GAME_TYPE_HIRES5:
|
|
|
|
*engine = HiRes5Engine_create(syst, adlGd);
|
|
|
|
break;
|
2016-04-08 12:35:23 +02:00
|
|
|
case GAME_TYPE_HIRES6:
|
|
|
|
*engine = HiRes6Engine_create(syst, adlGd);
|
|
|
|
break;
|
2016-03-06 21:59:55 +01:00
|
|
|
default:
|
|
|
|
error("Unknown GameType");
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
2016-02-26 23:32:06 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
} // End of namespace Adl
|
|
|
|
|
|
|
|
#if PLUGIN_ENABLED_DYNAMIC(ADL)
|
|
|
|
REGISTER_PLUGIN_DYNAMIC(ADL, PLUGIN_TYPE_ENGINE, Adl::AdlMetaEngine);
|
|
|
|
#else
|
|
|
|
REGISTER_PLUGIN_STATIC(ADL, PLUGIN_TYPE_ENGINE, Adl::AdlMetaEngine);
|
|
|
|
#endif
|