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_FS_H 24 #define COMMON_FS_H 25 26 #include "common/array.h" 27 #include "common/archive.h" 28 #include "common/hash-str.h" 29 #include "common/hashmap.h" 30 #include "common/ptr.h" 31 #include "common/str.h" 32 #include "common/ustr.h" 33 34 class AbstractFSNode; 35 36 namespace Common { 37 38 /** 39 * @defgroup common_fs File system 40 * @ingroup common 41 * 42 * @brief API for operations on the file system. 43 * 44 * @{ 45 */ 46 47 class FSNode; 48 class SeekableReadStream; 49 class WriteStream; 50 class SeekableWriteStream; 51 52 /** 53 * List of multiple file system nodes. For example, the contents of a given directory. 54 * This is a subclass instead of just a typedef so that forward declarations 55 * of it can be used in other places. 56 */ 57 class FSList : public Array<FSNode> {}; 58 59 /** 60 * FSNode, short for "File System Node", provides an abstraction for file 61 * paths, allowing for portable file system browsing. This means, for example, 62 * that multiple or single roots have to be supported (compare Unix with a 63 * single root, Windows with multiple roots C:, D:, ...). 64 * 65 * To this end, we abstract away from paths; implementations can be based on 66 * paths (and it is left to them whether '/', '\', or ':' is the path separator) 67 * but it is also possible to use inodes or vrefs (MacOS 9) or anything else. 68 */ 69 class FSNode : public ArchiveMember { 70 private: 71 friend class ::AbstractFSNode; 72 SharedPtr<AbstractFSNode> _realNode; 73 /** 74 * Construct an FSNode from a backend's AbstractFSNode implementation. 75 * 76 * @param realNode Pointer to a heap allocated instance. FSNode will take 77 * ownership of the pointer. 78 */ 79 FSNode(AbstractFSNode *realNode); 80 81 public: 82 /** 83 * Flag to tell listDir() which kind of files to list. 84 */ 85 enum ListMode { 86 kListFilesOnly = 1, 87 kListDirectoriesOnly = 2, 88 kListAll = 3 89 }; 90 91 /** 92 * Create a new pathless FSNode. Since there is no path associated 93 * with this node, path-related operations (i.e. exists(), isDirectory(), 94 * getPath()) will always return false or raise an assertion. 95 */ 96 FSNode(); 97 98 /** 99 * Create a new FSNode referring to the specified path. This is 100 * the counterpart to the path() method. 101 * 102 * If the path is empty or equals ".", then a node representing the "current 103 * directory" will be created. If that is not possible (since e.g. the 104 * operating system does not support the concept), some other directory is 105 * used (usually the root directory). 106 */ 107 explicit FSNode(const Path &path); 108 ~FSNode()109 virtual ~FSNode() {} 110 111 /** 112 * Compare the name of this node to the name of another. Directories 113 * go before normal files. 114 */ 115 bool operator<(const FSNode& node) const; 116 117 /** 118 * Indicate whether the object referred by this node exists in the file system or not. 119 * 120 * @return True if the node exists, false otherwise. 121 */ 122 bool exists() const; 123 124 /** 125 * Create a new node referring to a child node of the current node, which 126 * must be a directory node (otherwise an invalid node is returned). 127 * If a child matching the name exists, a normal node for it is returned. 128 * If no child with the name exists, a node for it is still returned, 129 * but exists() will return 'false' for it. This node can however be used 130 * to create a new file using the createWriteStream() method. 131 * 132 * @todo If createWriteStream() (or a hypothetical future mkdir() method) is used, 133 * this should affect what exists/isDirectory/isReadable/isWritable return 134 * for existing nodes. However, this is not the case for many existing 135 * FSNode implementations. Either fix those, or document that FSNodes 136 * can become 'stale'. 137 * 138 * @param name Name of a child of this directory. 139 * @return The node referring to the child with the given name. 140 */ 141 FSNode getChild(const String &name) const; 142 143 /** 144 * Return a list of all child nodes of this directory node. If called on a node 145 * that does not represent a directory, false is returned. 146 * 147 * @return True if successful, false otherwise (e.g. when the directory does not exist). 148 */ 149 bool getChildren(FSList &fslist, ListMode mode = kListDirectoriesOnly, bool hidden = true) const; 150 151 /** 152 * Return a human-readable string for this node, usable for display (e.g. 153 * in the GUI code). Do *not* rely on it being usable for anything else, 154 * like constructing paths. 155 * 156 * @return The display name. 157 */ 158 virtual U32String getDisplayName() const; 159 160 /** 161 * Return a string representation of the name of the file. This can be 162 * used e.g. by detection code that relies on matching the name of a given 163 * file. However, it is *not* suitable for use with fopen / File::open, nor 164 * should it be archived. 165 * 166 * @return The file name. 167 */ 168 virtual String getName() const; 169 170 /** 171 * Return a string representation of the file that is suitable for 172 * archiving (i.e. writing to the config file). This will usually be a 173 * 'path' (hence the name of the method), but can be anything that meets 174 * the above criteria. What a 'path' is differs greatly from system to 175 * system. 176 * 177 * @note Do not assume that this string contains (back)slashes or any 178 * other kind of 'path separators'. 179 * 180 * @return The 'path' represented by this file system node. 181 */ 182 String getPath() const; 183 184 /** 185 * Get the parent node of this node. If this node has no parent node, 186 * then it returns a duplicate of this node. 187 */ 188 FSNode getParent() const; 189 190 /** 191 * Indicate whether the node refers to a directory or not. 192 * 193 * @todo Currently we assume that a node that is not a directory 194 * automatically is a file (ignoring things like symlinks or pipes). 195 * That might actually be OK... but we could still add an isFile method. 196 * Or even replace isDirectory by a getType() method that can return values like 197 * kDirNodeType, kFileNodeType, kInvalidNodeType. 198 */ 199 bool isDirectory() const; 200 201 /** 202 * Indicate whether the object referred by this node can be read from or not. 203 * 204 * If the node refers to a directory, readability implies being able to read 205 * and list the directory entries. 206 * 207 * If the node refers to a file, readability implies being able to read the 208 * contents of the file. 209 * 210 * @return True if the object can be read, false otherwise. 211 */ 212 bool isReadable() const; 213 214 /** 215 * Indicate whether the object referred by this node can be written to or not. 216 * 217 * If the node refers to a directory, writability implies being able to modify 218 * the directory entry (i.e. rename the directory, remove it, or write files inside of it). 219 * 220 * If the node refers to a file, writability implies being able to write data 221 * to the file. 222 * 223 * @return True if the object can be written to, false otherwise. 224 */ 225 bool isWritable() const; 226 227 /** 228 * Create a SeekableReadStream instance corresponding to the file 229 * referred by this node. This assumes that the node actually refers 230 * to a readable file. If this is not the case, 0 is returned. 231 * 232 * @return Pointer to the stream object, 0 in case of a failure. 233 */ 234 virtual SeekableReadStream *createReadStream() const; 235 236 /** 237 * Create a WriteStream instance corresponding to the file 238 * referred by this node. This assumes that the node actually refers 239 * to a readable file. If this is not the case, 0 is returned. 240 * 241 * @return Pointer to the stream object, 0 in case of a failure. 242 */ 243 SeekableWriteStream *createWriteStream() const; 244 245 /** 246 * Create a directory referred by this node. This assumes that this 247 * node refers to a non-existing directory. If this is not the case, 248 * false is returned. 249 * 250 * @return True if the directory was created, false otherwise. 251 */ 252 bool createDirectory() const; 253 }; 254 255 /** 256 * FSDirectory models a directory tree from the file system and allows users 257 * to access it through the Archive interface. Searching is case-insensitive, 258 * since the intended goal is to support retrieval of game data. 259 * 260 * FSDirectory can represent a single directory, or a tree with specified depth, 261 * depending on the value passed to the 'depth' parameter in the constructors. 262 * In the default mode, file names are cached with their relative path, 263 * with elements separated by slashes, e.g.: 264 * @code 265 * c:\my\data\file.ext 266 * @endcode 267 * would be cached as 'data/file.ext' if FSDirectory was created on 'c:/my' with 268 * depth > 1. If depth was 1, then the 'data' subdirectory would have been 269 * ignored, instead. 270 * Again, only SLASHES are used as separators independently from the 271 * underlying file system. 272 * 273 * Relative paths can be specified when calling matching functions like createReadStreamForMember(), 274 * hasFile(), listMatchingMembers(), and listMembers(). See the function-specific 275 * documentation for more information. 276 * 277 * If the 'flat' argument to the constructor is true, files in subdirectories 278 * are cached without the relative path, so in the following example: 279 * @code 280 * c:\my\data\file.ext 281 * @endcode 282 * would be cached as 'file.ext'. 283 * 284 * When the 'ignoreClashes' argument to the constructor is true, name clashes are 285 * expected by the engine. It means that files that clash should be identical and 286 * getSubDirectory should not be used on clashing directories. This flag is useful 287 * in flat mode when there are directories with the same name at different places in the 288 * tree whose name is not relevant for the engine code. 289 * 290 * Client code can customize the cache by using constructors with the 'prefix' 291 * parameter. In this case, the prefix is prepended to each entry in the cache, 292 * and effectively treated as a 'virtual' parent subdirectory. FSDirectory adds 293 * a trailing slash to the prefix if needed. Following on with the previous example 294 * and using 'your' as a prefix, the cache entry would have been 'your/data/file.ext'. 295 * This is done both in non-flat and flat mode. 296 * 297 */ 298 class FSDirectory : public Archive { 299 FSNode _node; 300 int _depth; 301 bool _flat; 302 bool _ignoreClashes; 303 bool _includeDirectories; 304 305 String _prefix; // string that is prepended to each cache item key 306 void setPrefix(const String &prefix); 307 308 // Caches are case insensitive, clashes are dealt with when creating 309 // Key is stored in lowercase. 310 typedef HashMap<String, FSNode, IgnoreCase_Hash, IgnoreCase_EqualTo> NodeCache; 311 mutable NodeCache _fileCache, _subDirCache; 312 mutable bool _cached; 313 314 // look for a match 315 FSNode *lookupCache(NodeCache &cache, const String &name) const; 316 317 // cache management 318 void cacheDirectoryRecursive(FSNode node, int depth, const Path& prefix) const; 319 320 // fill cache if not already cached 321 void ensureCached() const; 322 323 public: 324 /** 325 * Create a FSDirectory representing a tree with the specified depth. Will result in an 326 * unbound FSDirectory if name is not found in the file system or if the node is not a 327 * valid directory. 328 */ 329 FSDirectory(const Path &name, int depth = 1, bool flat = false, 330 bool ignoreClashes = false, bool includeDirectories = false); 331 /** 332 * @overload 333 */ 334 FSDirectory(const FSNode &node, int depth = 1, bool flat = false, 335 bool ignoreClashes = false, bool includeDirectories = false); 336 337 /** 338 * Create a FSDirectory representing a tree with the specified depth. The parameter 339 * prefix is prepended to the keys in the cache. See @ref FSDirectory. 340 */ 341 FSDirectory(const Path &prefix, const Path &name, int depth = 1, 342 bool flat = false, bool ignoreClashes = false, bool includeDirectories = false); 343 /** 344 * @overload 345 */ 346 FSDirectory(const Path &prefix, const FSNode &node, int depth = 1, 347 bool flat = false, bool ignoreClashes = false, bool includeDirectories = false); 348 349 virtual ~FSDirectory(); 350 351 /** 352 * Return the underlying FSNode of the FSDirectory. 353 */ 354 FSNode getFSNode() const; 355 356 /** 357 * Create a new FSDirectory pointing to a subdirectory of the instance. 358 * @return A new FSDirectory instance. 359 */ 360 FSDirectory *getSubDirectory(const Path &name, int depth = 1, bool flat = false, 361 bool ignoreClashes = false); 362 /** 363 * Create a new FSDirectory pointing to a subdirectory of the instance. See FSDirectory 364 * for an explanation of the prefix parameter. 365 * @return A new FSDirectory instance. 366 */ 367 FSDirectory *getSubDirectory(const Path &prefix, const Path &name, int depth = 1, 368 bool flat = false, bool ignoreClashes = false); 369 370 /** 371 * Check for the existence of a file in the cache. A full match of relative path and file name 372 * is needed for success. 373 */ 374 virtual bool hasFile(const Path &path) const; 375 376 /** 377 * Return a list of matching file names. Pattern can use GLOB wildcards. 378 */ 379 virtual int listMatchingMembers(ArchiveMemberList &list, const Path &pattern) const; 380 381 /** 382 * Return a list of all the files in the cache. 383 */ 384 virtual int listMembers(ArchiveMemberList &list) const; 385 386 /** 387 * Get an ArchiveMember representation of the specified file. A full match of relative 388 * path and file name is needed for success. 389 */ 390 virtual const ArchiveMemberPtr getMember(const Path &path) const; 391 392 /** 393 * Open the specified file. A full match of relative path and file name is needed 394 * for success. 395 */ 396 virtual SeekableReadStream *createReadStreamForMember(const Path &path) const; 397 }; 398 399 /** @} */ 400 401 } // End of namespace Common 402 403 #endif //COMMON_FS_H 404