1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 #ifndef COMMON_ARCHIVE_H
24 #define COMMON_ARCHIVE_H
25 
26 #include "common/str.h"
27 #include "common/list.h"
28 #include "common/path.h"
29 #include "common/ptr.h"
30 #include "common/singleton.h"
31 
32 namespace Common {
33 
34 /**
35  * @defgroup common_arch Archive
36  * @ingroup common
37  *
38  * @brief  The Archive module allows for managing the members of arbitrary containers in a uniform
39  * fashion.
40  * It also supports looking up by names and file names, opening a file, and returning a usable input stream.
41  * @{
42  */
43 
44 class FSNode;
45 class SeekableReadStream;
46 
47 
48 /**
49  * The ArchiveMember class is an abstract interface to represent elements inside
50  * implementations of an archive.
51  *
52  * Archive subclasses must provide their own implementation of ArchiveMember,
53  * and use it when serving calls to @ref Archive::listMembers and @ref Archive::listMatchingMembers.
54  * Alternatively, you can use the @ref GenericArchiveMember.
55  */
56 class ArchiveMember {
57 public:
~ArchiveMember()58 	virtual ~ArchiveMember() { }
59 	virtual SeekableReadStream *createReadStream() const = 0; /*!< Create a read stream. */
60 	virtual String getName() const = 0; /*!< Get the name of the archive member. */
getDisplayName()61 	virtual U32String getDisplayName() const { return getName(); } /*!< Get the display name of the archive member. */
62 };
63 
64 typedef SharedPtr<ArchiveMember> ArchiveMemberPtr; /*!< Shared pointer to an archive member. */
65 typedef List<ArchiveMemberPtr> ArchiveMemberList; /*!< List of archive members. */
66 
67 /**
68  * Compare two archive member operators @p a and @p b and return which of them is higher.
69  */
70 struct ArchiveMemberListComparator {
operatorArchiveMemberListComparator71 	bool operator()(const ArchiveMemberPtr &a, const ArchiveMemberPtr &b) {
72 		return a->getName() < b->getName();
73 	}
74 };
75 
76 class Archive;
77 
78 /**
79  * Simple ArchiveMember implementation which allows
80  * creation of ArchiveMember compatible objects via
81  * a simple Archive and name pair.
82  *
83  * Note that GenericArchiveMember objects will not
84  * be working anymore after the 'parent' object
85  * is destroyed.
86  */
87 class GenericArchiveMember : public ArchiveMember {
88 	const Archive *_parent;
89 	const String _name;
90 public:
91 	GenericArchiveMember(const String &name, const Archive *parent); /*!< Create a generic archive member that belongs to the @p parent archive. */
92 	String getName() const; /*!< Get the name of a generic archive member. */
93 	SeekableReadStream *createReadStream() const; /*!< Create a read stream. */
94 };
95 
96 
97 /**
98  * The Archive class allows for managing the members of arbitrary containers in a uniform
99  * fashion, allowing lookup by (file) names.
100  * It also supports opening a file and returning a usable input stream.
101  */
102 class Archive {
103 public:
~Archive()104 	virtual ~Archive() { }
105 
106 	/**
107 	 * Check if a member with the given @p name is present in the Archive.
108 	 * Patterns are not allowed, as this is meant to be a quick File::exists()
109 	 * replacement.
110 	 */
111 	virtual bool hasFile(const Path &path) const = 0;
112 
113 	/**
114 	 * Add all members of the Archive matching the specified pattern to the list.
115 	 * Must only append to list, and not remove elements from it.
116 	 *
117 	 * @return The number of members added to list.
118 	 */
119 	virtual int listMatchingMembers(ArchiveMemberList &list, const Path &pattern) const;
120 
121 	/**
122 	 * Add all members of the Archive to the list.
123 	 * Must only append to list, and not remove elements from it.
124 	 *
125 	 * @return The number of names added to list.
126 	 */
127 	virtual int listMembers(ArchiveMemberList &list) const = 0;
128 
129 	/**
130 	 * Return an ArchiveMember representation of the given file.
131 	 */
132 	virtual const ArchiveMemberPtr getMember(const Path &path) const = 0;
133 
134 	/**
135 	 * Create a stream bound to a member with the specified name in the
136 	 * archive. If no member with this name exists, 0 is returned.
137 	 *
138 	 * @return The newly created input stream.
139 	 */
140 	virtual SeekableReadStream *createReadStreamForMember(const Path &path) const = 0;
141 };
142 
143 
144 /**
145  * The SearchSet class enables access to a group of Archives through the Archive interface.
146  *
147  * Its intended usage is a situation in which there are no name clashes among names in the
148  * contained Archives, hence the simplistic policy of always looking for the first
149  * match. SearchSet does guarantee that searches are performed in DESCENDING
150  * priority order. In case of conflicting priorities, insertion order prevails.
151  */
152 class SearchSet : public Archive {
153 	struct Node {
154 		int		_priority;
155 		String	_name;
156 		Archive	*_arc;
157 		bool	_autoFree;
NodeNode158 		Node(int priority, const String &name, Archive *arc, bool autoFree)
159 			: _priority(priority), _name(name), _arc(arc), _autoFree(autoFree) {
160 		}
161 	};
162 	typedef List<Node> ArchiveNodeList;
163 	ArchiveNodeList _list;
164 
165 	ArchiveNodeList::iterator find(const String &name);
166 	ArchiveNodeList::const_iterator find(const String &name) const;
167 
168 	void insert(const Node& node); //!< Add an archive while keeping the list sorted by descending priority.
169 
170 	bool _ignoreClashes;
171 
172 public:
SearchSet()173 	SearchSet() : _ignoreClashes(false) { }
~SearchSet()174 	virtual ~SearchSet() { clear(); }
175 
176 	/**
177 	 * Add a new archive to the searchable set.
178 	 */
179 	void add(const String& name, Archive *arch, int priority = 0, bool autoFree = true);
180 
181 	/**
182 	 * Create and add an FSDirectory by name.
183 	 */
184 	void addDirectory(const String &name, const String &directory, int priority = 0, int depth = 1, bool flat = false);
185 
186 	/**
187 	 * Create and add an FSDirectory by FSNode.
188 	 */
189 	void addDirectory(const String &name, const FSNode &directory, int priority = 0, int depth = 1, bool flat = false);
190 
191 	/**
192 	 * Create and add a subdirectory by name (caseless).
193 	 *
194 	 * It is also possible to add subdirectories of subdirectories (of any depth) with this function.
195 	 * The path seperator for this case is SLASH for all systems.
196 	 *
197 	 * Example:
198 	 *
199 	 *   "game/itedata"
200 	 *
201 	 * In this example, the code first tries to search for all directories matching
202 	 * "game" (case insensitive) in the path "directory" first and search through all
203 	 * of the matches for "itedata" (case insensitive too).
204 	 *
205 	 * Note that it will add all matches found!
206 	 *
207 	 * Even though this method is currently implemented via addSubDirectoriesMatching, it is not safe
208 	 * to assume that this method is using anything other than a simple case insensitive compare.
209 	 * Thus, do not use any tokens like '*' or '?' in the "caselessName" parameter of this function.
210 	 */
211 	void addSubDirectoryMatching(const FSNode &directory, const String &caselessName, int priority = 0, int depth = 1, bool flat = false) {
212 		addSubDirectoriesMatching(directory, caselessName, true, priority, depth, flat);
213 	}
214 
215 	/**
216 	 * Create and add subdirectories by pattern.
217 	 *
218 	 * It is also possible to add subdirectories of subdirectories (of any depth) with this function.
219 	 * The path seperator for this case is SLASH for all systems.
220 	 *
221 	 * Example:
222 	 *
223 	 *   "game/itedata"
224 	 *
225 	 * In this example, the code first tries to search for all directories matching
226 	 * "game" in the path "directory" first and search through all of the matches for
227 	 * "itedata". If "ingoreCase" is set to true, the code does a case insensitive
228 	 * match, otherwise it is doing a case sensitive match.
229 	 *
230 	 * This method also works with tokens. For a list of available tokens,
231 	 * see @ref Common::matchString.
232 	 */
233 	void addSubDirectoriesMatching(const FSNode &directory, String origPattern, bool ignoreCase, int priority = 0, int depth = 1, bool flat = false);
234 
235 	/**
236 	 * Remove an archive from the searchable set.
237 	 */
238 	void remove(const String& name);
239 
240 	/**
241 	 * Check if a given archive name is already present.
242 	 */
243 	bool hasArchive(const String &name) const;
244 
245 	/**
246 	 * Empty the searchable set.
247 	 */
248 	virtual void clear();
249 
250 	/**
251 	 * Change the order of searches.
252 	 */
253 	void setPriority(const String& name, int priority);
254 
255 	virtual bool hasFile(const Path &path) const;
256 	virtual int listMatchingMembers(ArchiveMemberList &list, const Path &pattern) const;
257 	virtual int listMembers(ArchiveMemberList &list) const;
258 
259 	virtual const ArchiveMemberPtr getMember(const Path &path) const;
260 
261 	/**
262 	 * Implement createReadStreamForMember from the Archive base class. The current policy is
263 	 * opening the first file encountered that matches the name.
264 	 */
265 	virtual SeekableReadStream *createReadStreamForMember(const Path &path) const;
266 
267 	/**
268 	 * Ignore clashes when adding directories. For more details, see the corresponding parameter
269 	 * in @ref FSDirectory documentation.
270 	 */
setIgnoreClashes(bool ignoreClashes)271 	void setIgnoreClashes(bool ignoreClashes) { _ignoreClashes = ignoreClashes; }
272 };
273 
274 
275 class SearchManager : public Singleton<SearchManager>, public SearchSet {
276 public:
277 
278 	/**
279 	 * Reset the Search Manager to the default list of search paths (system
280 	 * specific dirs + current dir).
281 	 */
282 	virtual void clear();
283 
284 private:
285 	friend class Singleton<SingletonBaseType>;
286 	SearchManager();
287 };
288 
289 /** Shortcut for accessing the Search Manager. */
290 #define SearchMan		Common::SearchManager::instance()
291 
292 /** @} */
293 
294 } // namespace Common
295 
296 #endif
297