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