aboutsummaryrefslogtreecommitdiff
path: root/engines/advancedDetector.h
blob: 088dfeae41514c0127eeca2deed902d10a895e08 (plain)
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
320
321
322
323
/* 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.
 *
 */

#ifndef ENGINES_ADVANCED_DETECTOR_H
#define ENGINES_ADVANCED_DETECTOR_H

#include "engines/metaengine.h"
#include "engines/engine.h"

#include "common/hash-str.h"

#include "common/gui_options.h" // FIXME: Temporary hack?

namespace Common {
class Error;
class FSList;
}

/**
 * A record describing a file to be matched for detecting a specific game
 * variant. A list of such records is used inside every ADGameDescription to
 * enable detection.
 */
struct ADGameFileDescription {
	const char *fileName; ///< Name of described file.
	uint16 fileType;      ///< Optional. Not used during detection, only by engines.
	const char *md5;      ///< MD5 of (the beginning of) the described file. Optional. Set to NULL to ignore.
	int32 fileSize;       ///< Size of the described file. Set to -1 to ignore.
};

/**
 * A shortcut to produce an empty ADGameFileDescription record. Used to mark
 * the end of a list of these.
 */
#define AD_LISTEND {NULL, 0, NULL, 0}

/**
 * A shortcut to produce a list of ADGameFileDescription records with only one
 * record that contains just a filename with an MD5, and no file size.
 */
#define AD_ENTRY1(f, x) {{ f, 0, x, -1}, AD_LISTEND}

/**
 * A shortcut to produce a list of ADGameFileDescription records with only one
 * record that contains just a filename with an MD5, plus a file size.
 */
#define AD_ENTRY1s(f, x, s) {{ f, 0, x, s}, AD_LISTEND}

enum ADGameFlags {
	ADGF_NO_FLAGS        =  0,
	ADGF_AUTOGENTARGET   = (1 << 20), ///< automatically generate gameid from extra
	ADGF_UNSTABLE        = (1 << 21), ///< flag to designate not yet officially-supported games that are not fit for public testing
	ADGF_TESTING         = (1 << 22), ///< flag to designate not yet officially-supported games that are fit for public testing
	ADGF_PIRATED         = (1 << 23), ///< flag to designate well known pirated versions with cracks
	ADGF_ADDENGLISH      = (1 << 24), ///< always add English as language option
	ADGF_MACRESFORK      = (1 << 25), ///< the md5 for this entry will be calculated from the resource fork
	ADGF_USEEXTRAASTITLE = (1 << 26), ///< Extra field value will be used as main game title, not gameid
	ADGF_DROPLANGUAGE    = (1 << 27), ///< don't add language to gameid
	ADGF_DROPPLATFORM    = (1 << 28), ///< don't add platform to gameid
	ADGF_CD              = (1 << 29), ///< add "-cd" to gameid
	ADGF_DEMO            = (1 << 30)  ///< add "-demo" to gameid
};

struct ADGameDescription {
	const char *gameId;
	const char *extra;
	ADGameFileDescription filesDescriptions[14];
	Common::Language language;
	Common::Platform platform;

	/**
	 * A bitmask of extra flags. The top 16 bits are reserved for generic flags
	 * defined in the ADGameFlags. This leaves 16 bits to be used by client
	 * code.
	 */
	uint32 flags;

	const char *guiOptions;
};

/**
 * A game installation matching an AD game description
 */
struct ADDetectedGame {
	bool hasUnknownFiles;
	FilePropertiesMap matchedFiles;
	const ADGameDescription *desc;

	ADDetectedGame() : desc(nullptr), hasUnknownFiles(false) {}
	explicit ADDetectedGame(const ADGameDescription *d) : desc(d), hasUnknownFiles(false) {}
};

/** A list of games detected by the AD */
typedef Common::Array<ADDetectedGame> ADDetectedGames;

/**
 * End marker for a table of ADGameDescription structs. Use this to
 * terminate a list to be passed to the AdvancedDetector API.
 */
#define AD_TABLE_END_MARKER	\
	{ NULL, NULL, { { NULL, 0, NULL, 0 } }, Common::UNK_LANG, Common::kPlatformUnknown, ADGF_NO_FLAGS, GUIO0() }

struct ADFileBasedFallback {
	/**
	 * Pointer to an ADGameDescription or subclass thereof which will get
	 * returned if there's a detection match.
	 */
	const ADGameDescription *desc;

	/**
	 * A zero-terminated list of filenames used for matching. All files in
	 * the list must be present to get a detection match.
	 */
	const char *filenames[10];
};


enum ADFlags {
	/**
	 * Store value of extra field in config file, and use it as a hint
	 * on subsequent runs. Could be used when there is no way to autodetect
	 * game (when more than one game sits in same directory), and user picks
	 * up a variant manually.
	 * In addition, this is useful if two variants of a game sharing the same
	 * gameid are contained in a single directory.
	 */
	kADFlagUseExtraAsHint = (1 << 0)
};


/**
 * Map entry for mapping GUIO_GAMEOPTIONS* to their ExtraGuiOption
 * description.
 */
struct ADExtraGuiOptionsMap {
	/**
	 * GUIO_GAMEOPTION* string.
	 */
	const char *guioFlag;

	/**
	 * The associated option.
	 */
	ExtraGuiOption option;
};

#define AD_EXTRA_GUI_OPTIONS_TERMINATOR { 0, { 0, 0, 0, 0 } }

/**
 * A MetaEngine implementation based around the advanced detector code.
 */
class AdvancedMetaEngine : public MetaEngine {
protected:
	/**
	 * Pointer to an array of objects which are either ADGameDescription
	 * or superset structures (i.e. start with an ADGameDescription member.
	 * The list is terminated by an entry with a gameid equal to 0
	 * (see AD_TABLE_END_MARKER).
	 */
	const byte *_gameDescriptors;

	/**
	 * The size of a single entry of the above descs array. Always
	 * must be >= sizeof(ADGameDescription).
	 */
	const uint _descItemSize;

	/**
	 * A list of all gameids (and their corresponding descriptions) supported
	 * by this engine.
	 */
	const PlainGameDescriptor *_gameIds;

	/**
	 * A map containing all the extra game GUI options the engine supports.
	 */
	const ADExtraGuiOptionsMap * const _extraGuiOptions;

	/**
	 * The number of bytes to compute MD5 sum for. The AdvancedDetector
	 * is primarily based on computing and matching MD5 checksums of files.
	 * Since doing that for large files can be slow, it can be restricted
	 * to a subset of all files.
	 * Typically this will be set to something between 5 and 50 kilobytes,
	 * but arbitrary non-zero values are possible. The default is 5000.
	 */
	uint _md5Bytes;

	/**
	 * A bitmask of flags which can be used to configure the behavior
	 * of the AdvancedDetector. Refer to ADFlags for a list of flags
	 * that can be ORed together and passed here.
	 */
	uint32 _flags;

	/**
	 * A list of game GUI options which will be added to each
	 * entry in addition to per-game options. Refer to GameGUIOption
	 * enum for the list.
	 */
	Common::String _guiOptions;

	/**
	 * Maximum depth of directories to look up.
	 * If set to 0, the depth is 1 level
	 */
	uint32 _maxScanDepth;

	/**
	 * Case-insensitive list of directory globs which could be used for
	 * going deeper into the directory structure.
	 * @see String::matchString() method for format description.
	 *
	 * @note Last item must be 0
	 */
	const char * const *_directoryGlobs;

	/**
	 * If true, filenames will be matched against the entire path, relative to
	 * the root detection directory (e.g. "foo/bar.000" for a file at
	 * "<root>/foo/bar.000"). Otherwise, filenames only match the basename
	 * (e.g. "bar.000" for the same file).
	 *
	 * @note _maxScanDepth and _directoryGlobs must still be configured to allow
	 * the detector to find files inside subdirectories.
	 */
	bool _matchFullPaths;

public:
	AdvancedMetaEngine(const void *descs, uint descItemSize, const PlainGameDescriptor *gameIds, const ADExtraGuiOptionsMap *extraGuiOptions = 0);

	/**
	 * Returns list of targets supported by the engine.
	 * Distinguishes engines with single ID
	 */
	PlainGameList getSupportedGames() const override;

	PlainGameDescriptor findGame(const char *gameId) const override;

	DetectedGames detectGames(const Common::FSList &fslist) const override;

	virtual Common::Error createInstance(OSystem *syst, Engine **engine) const;

	virtual const ExtraGuiOptions getExtraGuiOptions(const Common::String &target) const;

protected:
	// To be implemented by subclasses
	virtual bool createInstance(OSystem *syst, Engine **engine, const ADGameDescription *desc) const = 0;

	typedef Common::HashMap<Common::String, Common::FSNode, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> FileMap;

	/**
	 * An (optional) generic fallback detect function which is invoked
	 * if the regular MD5 based detection failed to detect anything.
	 */
	virtual ADDetectedGame fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist) const {
		return ADDetectedGame();
	}

private:
	void initSubSystems(const ADGameDescription *gameDesc) const;

protected:
	/**
	 * Detect games in specified directory.
	 * Parameters language and platform are used to pass on values
	 * specified by the user. This is used to restrict search scope.
	 *
	 * @param allFiles	list of all present files, as computed by composeFileHashMap
	 * @param language	restrict results to specified language
	 * @param platform	restrict results to specified platform
	 * @param extra		restrict results to specified extra string (only if kADFlagUseExtraAsHint is set)
	 * @return	list of ADGameDescription pointers corresponding to matched games
	 */
	virtual ADDetectedGames detectGame(const Common::FSNode &parent, const FileMap &allFiles, Common::Language language, Common::Platform platform, const Common::String &extra) const;

	/**
	 * Iterates over all ADFileBasedFallback records inside fileBasedFallback.
	 * This then returns the record (or rather, the ADGameDescription
	 * contained in it) for which all files described by it are present, and
	 * among those the one with the maximal number of matching files.
	 * In case of a tie, the entry coming first in the list is chosen.
	 *
	 * @param allFiles	a map describing all present files
	 * @param fslist	a list of nodes for all present files
	 * @param fileBasedFallback	a list of ADFileBasedFallback records, zero-terminated
	 * @param filesProps	if not 0, return a map of properties for all detected files here
	 */
	ADDetectedGame detectGameFilebased(const FileMap &allFiles, const Common::FSList &fslist, const ADFileBasedFallback *fileBasedFallback) const;

	/**
	 * Compose a hashmap of all files in fslist.
	 * Includes nifty stuff like removing trailing dots and ignoring case.
	 */
	void composeFileHashMap(FileMap &allFiles, const Common::FSList &fslist, int depth, const Common::String &parentName = Common::String()) const;

	/** Get the properties (size and MD5) of this file. */
	bool getFileProperties(const Common::FSNode &parent, const FileMap &allFiles, const ADGameDescription &game, const Common::String fname, FileProperties &fileProps) const;

	/** Convert an AD game description into the shared game description format */
	DetectedGame toDetectedGame(const ADDetectedGame &adGame) const;
};

#endif