File: detection.cpp

package info (click to toggle)
scummvm 2.9.0%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 450,268 kB
  • sloc: cpp: 4,297,604; asm: 28,322; python: 12,901; sh: 11,219; java: 8,477; xml: 7,843; perl: 2,633; ansic: 2,465; yacc: 1,670; javascript: 1,020; makefile: 933; lex: 578; awk: 275; objc: 82; sed: 11; php: 1
file content (319 lines) | stat: -rw-r--r-- 14,945 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
/* 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 3 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, see <http://www.gnu.org/licenses/>.
 *
 */

#include "engines/advancedDetector.h"
#include "base/plugins.h"
#include "common/file.h"
#include "common/hashmap.h"
#include "common/ptr.h"

#include "gui/ThemeEval.h"
#include "gui/widget.h"
#include "gui/widgets/popup.h"

#include "sci/detection.h"
#include "sci/detection_internal.h"
#include "sci/dialogs.h"
#include "sci/graphics/helpers_detection_enums.h"
#include "sci/sci.h"

static const DebugChannelDef debugFlagList[] = {
	{Sci::kDebugLevelError, "Error", "Script error debugging"},
	{Sci::kDebugLevelNodes, "Lists", "Lists and nodes debugging"},
	{Sci::kDebugLevelGraphics, "Graphics", "Graphics debugging"},
	{Sci::kDebugLevelStrings, "Strings", "Strings debugging"},
	{Sci::kDebugLevelMemory, "Memory", "Memory debugging"},
	{Sci::kDebugLevelFuncCheck, "Func", "Function parameter debugging"},
	{Sci::kDebugLevelBresen, "Bresenham", "Bresenham algorithms debugging"},
	{Sci::kDebugLevelSound, "Sound", "Sound debugging"},
	{Sci::kDebugLevelBaseSetter, "Base", "Base Setter debugging"},
	{Sci::kDebugLevelParser, "Parser", "Parser debugging"},
	{Sci::kDebugLevelSaid, "Said", "Said specs debugging"},
	{Sci::kDebugLevelFile, "File", "File I/O debugging"},
	{Sci::kDebugLevelTime, "Time", "Time debugging"},
	{Sci::kDebugLevelRoom, "Room", "Room number debugging"},
	{Sci::kDebugLevelAvoidPath, "Pathfinding", "Pathfinding debugging"},
	{Sci::kDebugLevelDclInflate, "DCL", "DCL inflate debugging"},
	{Sci::kDebugLevelVM, "VM", "VM debugging"},
	{Sci::kDebugLevelScripts, "Scripts", "Notifies when scripts are unloaded"},
	{Sci::kDebugLevelPatcher, "Patcher", "Notifies when scripts or resources are patched"},
	{Sci::kDebugLevelWorkarounds, "Workarounds", "Notifies when workarounds are triggered"},
	{Sci::kDebugLevelVideo, "Video", "Video (SEQ, VMD, RBT) debugging"},
	{Sci::kDebugLevelGame, "Game", "Debug calls from game scripts"},
	{Sci::kDebugLevelGC, "GC", "Garbage Collector debugging"},
	{Sci::kDebugLevelResMan, "ResMan", "Resource manager debugging"},
	{Sci::kDebugLevelOnStartup, "OnStartup", "Enter debugger at start of game"},
	{Sci::kDebugLevelDebugMode, "DebugMode", "Enable game debug mode at start of game"},
	DEBUG_CHANNEL_END
};

namespace Sci {

// Titles of the games
static const PlainGameDescriptor s_sciGameTitles[] = {
	{"sci",             "Sierra SCI Game"},
	{"sci-fanmade",     "Fanmade SCI Game"},
	// === SCI0 games =========================================================
	{"astrochicken",    "Astro Chicken"},
	{"christmas1988",   "Christmas Card 1988"},
	{"iceman",          "Codename: Iceman"},
	{"camelot",         "Conquests of Camelot: King Arthur, Quest for the Grail"},
	{"funseeker",       "Fun Seeker's Guide"},
	{"hoyle1",          "Hoyle Official Book of Games: Volume 1"},
	{"hoyle2",          "Hoyle Official Book of Games: Volume 2"},
	{"kq4sci",          "King's Quest IV: The Perils of Rosella"},	// Note: There was also an AGI version of this
	{"laurabow",        "Laura Bow I: The Colonel's Bequest"},
	{"lsl2",            "Leisure Suit Larry 2: Goes Looking for Love (in Several Wrong Places)"},
	{"lsl3",            "Leisure Suit Larry 3: Passionate Patti in Pursuit of the Pulsating Pectorals"},
	{"mothergoose",     "Mixed-Up Mother Goose"},
	{"pq2",             "Police Quest II: The Vengeance"},
	{"qfg1",            "Hero's Quest: So You Want to Be a Hero"},	// Note: There was also a SCI11 VGA remake of this (further down) called Quest for Glory I: So You Want to Be a Hero
	{"sq3",             "Space Quest III: The Pirates of Pestulon"},
	// === SCI01 games ========================================================
	{"qfg2",            "Quest for Glory II: Trial by Fire"},
	{"kq1sci",          "King's Quest I: Quest for the Crown"},	// Note: There was also an AGI version of this called King's Quest: Quest for the Crown
	// === SCI1 games =========================================================
	{"castlebrain",     "Castle of Dr. Brain"},
	{"christmas1990",   "Christmas Card 1990: The Seasoned Professional"},
	{"cnick-lsl",       "Crazy Nick's Software Picks: Leisure Suit Larry's Casino"},
	{"cnick-kq",        "Crazy Nick's Software Picks: King Graham's Board Game Challenge"},
	{"cnick-laurabow",  "Crazy Nick's Software Picks: Parlor Games with Laura Bow"},
	{"cnick-longbow",   "Crazy Nick's Software Picks: Robin Hood's Game of Skill and Chance"},
	{"cnick-sq",        "Crazy Nick's Software Picks: Roger Wilco's Spaced Out Game Pack"},
	{"ecoquest",        "EcoQuest: The Search for Cetus"},	// floppy is SCI1, CD SCI1.1
	{"fairytales",      "Mixed-up Fairy Tales"},
	{"hoyle3",          "Hoyle Official Book of Games: Volume 3"},
	{"jones",           "Jones in the Fast Lane"},
	{"kq5",             "King's Quest V: Absence Makes the Heart Go Yonder"},
	{"longbow",         "Conquests of the Longbow: The Adventures of Robin Hood"},
	{"lsl1sci",         "Leisure Suit Larry in the Land of the Lounge Lizards"},	// Note: There was also an AGI version of this
	{"lsl5",            "Leisure Suit Larry 5: Passionate Patti Does a Little Undercover Work"},
	{"mothergoose256",  "Mixed-Up Mother Goose"},
	{"msastrochicken",  "Ms. Astro Chicken"},
	{"pq1sci",          "Police Quest: In Pursuit of the Death Angel"},	// Note: There was also an AGI version of this
	{"pq3",             "Police Quest III: The Kindred"},
	{"sq1sci",          "Space Quest I: Roger Wilco in the Sarien Encounter"},	// Note: There was also an AGI version of this called Space Quest: Chapter I - The Sarien Encounter
	{"sq4",             "Space Quest IV: Roger Wilco and the Time Rippers"},	// floppy is SCI1, CD SCI1.1
	// === SCI1.1 games =======================================================
	{"christmas1992",   "Christmas Card 1992"},
	{"ecoquest2",       "EcoQuest II: Lost Secret of the Rainforest"},
	{"freddypharkas",   "Freddy Pharkas: Frontier Pharmacist"},
	{"hoyle4",          "Hoyle Classic Card Games"},
	{"inndemo",         "ImagiNation Network (INN) Demo"},
	{"kq6",             "King's Quest VI: Heir Today, Gone Tomorrow"},
	{"laurabow2",       "Laura Bow II: The Dagger of Amon Ra"},
	{"qfg1vga",         "Quest for Glory I: So You Want to Be a Hero"},	// Note: There was also a SCI0 version of this (further up) called Hero's Quest: So You Want to Be a Hero
	{"qfg3",            "Quest for Glory III: Wages of War"},
	{"sq5",             "Space Quest V: The Next Mutation"},
	{"islandbrain",     "The Island of Dr. Brain"},
	{"lsl6",            "Leisure Suit Larry 6: Shape Up or Slip Out!"},
	{"pepper",          "Pepper's Adventures in Time"},
	{"slater",          "Slater & Charlie Go Camping"},
	{"gk1demo",         "Gabriel Knight: Sins of the Fathers"},
	{"qfg4demo",        "Quest for Glory IV: Shadows of Darkness"},
	{"pq4demo",         "Police Quest IV: Open Season"},
	// === SCI2 games =========================================================
	{"gk1",             "Gabriel Knight: Sins of the Fathers"},
	{"pq4",             "Police Quest IV: Open Season"}, // floppy is SCI2, CD SCI2.1
	{"qfg4",            "Quest for Glory IV: Shadows of Darkness"},	// floppy is SCI2, CD SCI2.1
	// === SCI2.1 games ========================================================
	{"hoyle5",          "Hoyle Classic Games"},
	{"hoyle5bridge",    "Hoyle Bridge"},
	{"hoyle5children",  "Hoyle Children's Collection"},
	{"hoyle5school",    "Hoyle School House Math"},
	{"hoyle5solitaire", "Hoyle Solitaire"},
	{"chest",           "Inside the Chest"},	// aka Behind the Developer's Shield
	{"gk2",             "The Beast Within: A Gabriel Knight Mystery"},
	{"kq7",             "King's Quest VII: The Princeless Bride"},
	{"kquestions",      "King's Questions"},
	{"lsl6hires",       "Leisure Suit Larry 6: Shape Up or Slip Out!"},
	{"mothergoosehires","Mixed-Up Mother Goose Deluxe"},
	{"phantasmagoria",  "Phantasmagoria"},
	{"pqswat",          "Police Quest: SWAT"},
	{"realm",           "The Realm"},
	{"shivers",         "Shivers"},
	{"sq6",             "Space Quest 6: The Spinal Frontier"},
	{"torin",           "Torin's Passage"},
	// === SCI3 games =========================================================
	{"lsl7",            "Leisure Suit Larry 7: Love for Sail!"},
	{"lighthouse",      "Lighthouse: The Dark Being"},
	{"phantasmagoria2", "Phantasmagoria 2: A Puzzle of Flesh"},
	{"shivers2",        "Shivers II: Harvest of Souls"},	// Not SCI
	{"rama",            "RAMA"},
	{nullptr, nullptr}
};

} // End of namespace Sci

#include "sci/detection_tables.h"

namespace Sci {

static const char *const directoryGlobs[] = {
	"avi",
	"english",
	"french",
	"german",
	"italian",
	"msg",
	"spanish",
	"patches",
	nullptr
};

class SciMetaEngineDetection : public AdvancedMetaEngineDetection<ADGameDescription> {
public:
	SciMetaEngineDetection() : AdvancedMetaEngineDetection(Sci::SciGameDescriptions, s_sciGameTitles) {
		_maxScanDepth = 3;
		_directoryGlobs = directoryGlobs;
		// Use SCI fallback detection results instead of the partial matches found by
		// advanced detector. SCI fallback detection is excellent because games have
		// predictable file names and contain a unique game string.
		// Advanced detector's partial matches aren't very useful in SCI because of
		// those similar file names; most games are partial matches of each other.
		_flags = kADFlagMatchFullPaths | kADFlagPreferFallbackDetection;
	}

	const DebugChannelDef *getDebugChannels() const override {
		return debugFlagList;
	}

	const char *getName() const override {
		return "sci";
	}

	const char *getEngineName() const override {
		return "SCI ["
#ifdef ENABLE_SCI32
			"all games"
#else
			"SCI0, SCI01, SCI10, SCI11"
#endif
			"]";
	}

	const char *getOriginalCopyright() const override {
		return "Sierra's Creative Interpreter (C) Sierra Online";
	}

	DetectedGames detectGames(const Common::FSList &fslist, uint32 skipADFlags, bool skipIncomplete) override;

	ADDetectedGame fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist, ADDetectedGameExtraInfo **extra) const override;

private:
	void addFileToDetectedGame(const Common::Path &name, const FileMap &allFiles, MD5Properties md5Prop, ADDetectedGame &game) const;
};

DetectedGames SciMetaEngineDetection::detectGames(const Common::FSList &fslist, uint32 skipADFlags, bool skipIncomplete) {
	DetectedGames games = AdvancedMetaEngineDetection::detectGames(fslist, skipADFlags, skipIncomplete);

	for (DetectedGame &game : games) {
		const GameIdStrToEnum *g = gameIdStrToEnum;
		for (; g->gameidStr; ++g) {
			if (game.gameId.equals(g->gameidStr))
				break;
		}
		game.setGUIOptions(customizeGuiOptions(fslist.begin()->getParent().getPath(), parseGameGUIOptions(game.getGUIOptions()), game.platform, g->gameidStr, g->version));
		game.appendGUIOptions(getGameGUIOptionsDescriptionLanguage(game.language));
	}

	return games;
}

ADDetectedGame SciMetaEngineDetection::fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist, ADDetectedGameExtraInfo **extra) const {
	/**
	 * Fallback detection for Sci heavily depends on engine resources, so it's not possible
	 * to use them without the engine present in a clean way.
	 */

	if (ConfMan.hasKey("always_run_fallback_detection_extern")) {
		if (ConfMan.getBool("always_run_fallback_detection_extern") == false) {
			warning("SCI: Fallback detection is disabled");
			return ADDetectedGame();
		}
	}

	const Plugin *enginePlugin = PluginMan.findEnginePlugin(getName());
	if (!enginePlugin) {
		static bool warn = true;
		if (warn) {
			warning("Engine plugin for SCI not present. Fallback detection is disabled");
			warn = false;
		}
		return ADDetectedGame();
	}

	ADDetectedGame game = enginePlugin->get<AdvancedMetaEngineBase>().fallbackDetectExtern(_md5Bytes, allFiles, fslist);
	if (!game.desc) {
		return game;
	}

	// detect all the matched files here in SciMetaEngineDetection, instead of
	// external fallback detection, so that we can use AdvancedMetaEngineDetection
	// methods instead of duplicating code. fallback detection has identified the
	// game, platform, and language. now we want a full list of all resource map
	// and volume files in the directory. this code attempts to add all possible
	// files, even if there are gaps, because we want to be able to identify
	// incomplete directories when users submit unknown-game reports.
	MD5Properties md5Prop = kMD5Head;
	if (allFiles.contains("resource.map")) {
		// add the map and volumes
		addFileToDetectedGame("resource.map", allFiles, md5Prop, game);
		for (int i = 0; i <= 11; i++) {
			Common::String volume = Common::String::format("resource.%03d", i);
			addFileToDetectedGame(Common::Path(volume, '/'), allFiles, md5Prop, game);
		}

		// add message and audio volumes.
		// sometimes we need these to differentiate between localized versions.
		addFileToDetectedGame("resource.aud", allFiles, md5Prop, game);
		addFileToDetectedGame("resource.msg", allFiles, md5Prop, game);
	} else if (allFiles.contains("resmap.000") || allFiles.contains("resmap.001")) {
		// add maps and volumes
		for (int i = 0; i <= 7; i++) {
			Common::String map = Common::String::format("resmap.%03d", i);
			Common::String volume = Common::String::format("ressci.%03d", i);
			addFileToDetectedGame(Common::Path(map), allFiles, md5Prop, game);
			addFileToDetectedGame(Common::Path(volume), allFiles, md5Prop, game);
		}
	} else if (allFiles.contains("Data1")) {
		// add Mac volumes
		md5Prop = (MD5Properties)(md5Prop | kMD5MacResOrDataFork);
		for (int i = 1; i <= 13; i++) {
			Common::String volume = Common::String::format("Data%d", i);
			addFileToDetectedGame(Common::Path(volume), allFiles, md5Prop, game);
		}
	}

	return game;
}

void SciMetaEngineDetection::addFileToDetectedGame(const Common::Path &name, const FileMap &allFiles, MD5Properties md5Prop, ADDetectedGame &game) const {
	FileProperties fileProperties;
	if (getFileProperties(allFiles, md5Prop, name, fileProperties)) {
		game.hasUnknownFiles = true;
		game.matchedFiles[name] = fileProperties;
	}
}

} // End of namespace Sci

REGISTER_PLUGIN_STATIC(SCI_DETECTION, PLUGIN_TYPE_ENGINE_DETECTION, Sci::SciMetaEngineDetection);