//----------------------------------------------------------------------------- // Copyright (c) 2012 GarageGames, LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to // deal in the Software without restriction, including without limitation the // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or // sell copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. //----------------------------------------------------------------------------- #ifndef _VOLUME_H_ #define _VOLUME_H_ #ifndef _TSIGNAL_H_ #include "core/util/tSignal.h" #endif #ifndef _TVECTOR_H_ #include "core/util/tVector.h" #endif #ifndef _REFBASE_H_ #include "core/util/refBase.h" #endif #ifndef _TDICTIONARY_H_ #include "core/util/tDictionary.h" #endif #ifndef _TORQUE_LIST_ #include "core/util/tList.h" #endif #ifndef _PATH_H_ #include "core/util/path.h" #endif #ifndef _TIMECLASS_H_ #include "core/util/timeClass.h" #endif namespace Torque { namespace FS { ///@defgroup VolumeSystem Volume System /// Volume access. //----------------------------------------------------------------------------- /// Base class for all FileIO objects. /// @ingroup VolumeSystem class FileBase : public StrongRefBase { public: virtual ~FileBase() {} }; //----------------------------------------------------------------------------- /// Base class for objects in a FileSystem. /// This class provides the functionality required by all file system /// files, which is basically name and attributes. /// @ingroup VolumeSystem class FileNode : public FileBase { public: enum NodeStatus { Open, ///< In an open state Closed, ///< In a closed state EndOfFile, ///< End of file reached UnknownError, ///< An undetermined error has occurred FileSystemFull, ///< File system full NoSuchFile, ///< File or path does not exist AccessDenied, ///< File access denied IllegalCall, ///< An unsupported operation was used. SharingViolation, ///< File being used by another process NoDisk, ///< No disk or dvd in drive DriveOpen, ///< Disk or DVD drive open WrongDisk, ///< Disk or DVD has been swapped }; enum Mode { File = 1 << 0, ///< Normal file Directory = 1 << 1, ///< Directory System = 1 << 2, ///< OS specific system file Hidden = 1 << 3, ///< Hidden file or directory ReadOnly = 1 << 4, ///< Read only Compressed = 1 << 5, ///< Part of a compressed archive? Encrypted = 1 << 6, ///< Part of an encrypted archive? Library = 1 << 7, ///< Dynamic Library Executable = 1 << 8, ///< Executable file }; struct Attributes { U32 flags; ///< File/Directory modes String name; ///< File/Directory name Time mtime; ///< Last modified time Time atime; ///< Last access time Time ctime; ///< Creation Time U64 size; }; public: FileNode(); // Properties virtual Path getName() const = 0; virtual NodeStatus getStatus() const = 0; virtual bool getAttributes(Attributes*) = 0; // Convenience routines - may be overridden for optimal access virtual Time getModifiedTime(); ///< @note This will return Time() on failure virtual Time getCreatedTime(); ///< @note This will return Time() on failure virtual U64 getSize(); ///< @note This will return 0 on failure virtual U32 getChecksum(); ///< @note This will return 0 on failure protected: virtual U32 calculateChecksum() = 0; ///< return 0 on failure private: U32 mChecksum; Torque::Time mLastChecksum; }; typedef WeakRefPtr FileNodePtr; typedef StrongRefPtr FileNodeRef; //----------------------------------------------------------------------------- /// File object in a FileSystem. /// File object in a FileSystem. When a file is initially obtained from a /// FileSystem it is in a closed state. /// @ingroup VolumeSystem class File : public FileNode { public: enum AccessMode { Read = 0, ///< Open for read only. Write = 1, ///< Open for write only. ReadWrite = 2, ///< Open for read-write. WriteAppend = 3 ///< Write-only, starting at end of file. }; enum SeekMode { Begin, ///< Relative to the start of the file Current, ///< Relative to the current position End, ///< Relative to the end of the file }; File(); virtual ~File(); // Properties virtual U32 getPosition() = 0; virtual U32 setPosition(U32 pos, SeekMode mode) = 0; // Functions virtual bool open(AccessMode mode) = 0; virtual bool close() = 0; virtual U32 read(void* dst, U32 size) = 0; virtual U32 write(const void* src, U32 size) = 0; }; typedef WeakRefPtr FilePtr; typedef StrongRefPtr FileRef; //----------------------------------------------------------------------------- /// Directory in a FileSystem. /// Directory object in a FileSystem. When a directory is initially obtained from a /// FileSystem it is in a closed state. /// @ingroup VolumeSystem class Directory : public FileNode { public: Directory(); virtual ~Directory(); // Functions virtual bool open() = 0; virtual bool close() = 0; virtual bool read(Attributes*) = 0; bool dump(Vector& out); bool dumpFiles(Vector& out); bool dumpDirectories(Vector& out); }; typedef WeakRefPtr DirectoryPtr; typedef StrongRefPtr DirectoryRef; //----------------------------------------------------------------------------- class FileSystem; class FileSystemChangeNotifier { public: typedef Delegate ChangeDelegate; typedef Signal ChangeSignal; public: FileSystemChangeNotifier( FileSystem *fs ) : mFS( fs ), mNotifying( false ) { } virtual ~FileSystemChangeNotifier() {} /// Adds a file change notification. /// @see FS::AddChangeNotification virtual bool addNotification( const Path &path, ChangeDelegate callback ); /// Removes an existing file change notification. /// @see FS::RemoveChangeNotification virtual bool removeNotification( const Path &path, ChangeDelegate callback ); void startNotifier(); void stopNotifier(); /// Returns true if the notifier is enabled and file /// change notifications will be sent. bool isNotifying() const { return mNotifying; } protected: struct FileInfo { /// The full path to the file. Path filePath; /// The last known modification time. Time savedLastModTime; /// The notifications and reference count. ChangeSignal signal; }; typedef List FileInfoList; typedef Map DirMap; ///< map a directory to a list of files and their mod times void process(); virtual void internalProcessOnce() = 0; /// This is called so the inherited class can do its own bookkeeping on addNotification() /// @note We pass the directory here, not the file virtual bool internalAddNotification( const Path &dir ) = 0; /// This is called so the inherited class can do its own bookkeeping on removeNotification() /// @note We pass the directory here, not the file virtual bool internalRemoveNotification( const Path &dir ) = 0; /// Called by the inherited class to let us know a directory has changed /// so we can find the file which changed and notify on it void internalNotifyDirChanged( const Path &dir ); /// Makes sure paths going in and out of the notifier will have the same format String cleanPath(const Path& dir); FileSystem *mFS; DirMap mDirMap; bool mNotifying; }; //----------------------------------------------------------------------------- /// Collection of FileNode objects. /// File systems represent collections of FileNode objects. Functions are /// provided for manipulating FileNode objects but the internal organization /// and representation is opaque. /// Path names must be fully specified relative to the file system root and /// names cannot contain relative path information. /// @ingroup VolumeSystem class FileSystem : public FileBase { public: FileSystem(); virtual ~FileSystem(); virtual String getTypeStr() const = 0; ///< Used for describing the file system type virtual FileNodeRef resolve(const Path& path) = 0; virtual FileNodeRef resolveLoose(const Path& path) { return resolve(path); } virtual FileNodeRef create(const Path& path,FileNode::Mode) = 0; virtual bool remove(const Path& path) = 0; virtual bool rename(const Path& a,const Path& b) = 0; virtual Path mapTo(const Path& path) = 0; virtual Path mapFrom(const Path& path) = 0; /// Returns the file change notifier. /// @see FS::AddChangeNotification /// @see FS::RemoveChangeNotification FileSystemChangeNotifier *getChangeNotifier() { return mChangeNotifier; } bool isReadOnly() { return mReadOnly; } protected: FileSystemChangeNotifier *mChangeNotifier; bool mReadOnly; }; typedef WeakRefPtr FileSystemPtr; typedef StrongRefPtr FileSystemRef; //----------------------------------------------------------------------------- ///@name File System Access /// A collection of file systems. /// @ingroup VolumeSystem class MountSystem { public: virtual ~MountSystem() {} FileRef createFile(const Path& path); bool copyFile(const Path& source, const Path& destination, bool noOverwrite); bool dumpDirectories(const Path& path, Vector& directories, S32 depth, bool noBasePath); DirectoryRef createDirectory(const Path& path, FileSystemRef fs = NULL); virtual bool createPath(const Path& path); FileRef openFile(const Path& path,File::AccessMode mode); DirectoryRef openDirectory(const Path& path); bool remove(const Path& path); bool rename(const Path& from,const Path& to); virtual bool mount(String root, FileSystemRef fs); virtual bool mount(String root, const Path &path); virtual FileSystemRef unmount(String root); virtual bool unmount(FileSystemRef fs); bool setCwd(const Path& file); const Path &getCwd() const; FileSystemRef getFileSystem(const Path& path); bool getFileAttributes(const Path& path,FileNode::Attributes* attr); FileNodeRef getFileNode(const Path& path); bool mapFSPath( const String &inRoot, const Path &inPath, Path &outPath ); virtual S32 findByPattern( const Path &inBasePath, const String &inFilePattern, bool inRecursive, Vector &outList, bool includeDirs=false, bool multiMatch = true ); bool isFile(const Path &path); bool isDirectory(const Path &path, FileSystemRef fsRef = NULL); bool isReadOnly(const Path &path); S32 getNumMounts() const { return mMountList.size(); } String getMountRoot( S32 index ) const { return mMountList[index].root; } String getMountPath( S32 index ) const { return mMountList[index].fileSystem->mapTo(mMountList[index].path); } String getMountType( S32 index ) const { return mMountList[index].fileSystem->getTypeStr(); } // File system notifications void startFileChangeNotifications(); void stopFileChangeNotifications(); private: bool _dumpDirectories(DirectoryRef directory, Vector& directories, S32 depth, bool noBasePath, S32 currentDepth, const Path& basePath); protected: virtual void _log(const String& msg); protected: struct MountFS { String root; // Root for file system String path; // File system path FileSystemRef fileSystem; }; virtual FileSystemRef _removeMountFromList(String root); virtual FileSystemRef _getFileSystemFromList(const Path& path) const ; void _setFindByPatternOverrideFS(FileSystemRef fs) { mFindByPatternOverrideFS = fs; } Path _normalize(const Path& path); Vector mMountList; Path mCWD; FileSystemRef mFindByPatternOverrideFS; }; ///@name File System Access /// Functions for mounting file systems and dealing with files and directories. /// The kernel provides FileSystem mounting, the concept of a current working /// directory as well as relative paths. ///@{ /// Mount file system ///@ingroup VolumeSystem bool Mount(String root, FileSystemRef fs); /// Mount file system redirect ///@ingroup VolumeSystem bool Mount(String root, const Path &path); /// Remove mounted file system. /// The file system object associated with the given root is unmounted. /// Open files associated with this file system are unaffected. ///@return The unmounted file system. ///@ingroup VolumeSystem FileSystemRef Unmount(String root); /// Remove mounted file system. /// Open files associated with this file system are unaffected. ///@return true if the filesystem was successfully unmounted, false otherwise (most likely, the FS was not mounted) bool Unmount(FileSystemRef fs); /// Find the the file system which owns the given file. ///@ingroup VolumeSystem FileSystemRef GetFileSystem(const Path &file); /// Find the file system node for the given file. ///@return Null if the file doesn't exist ///@ingroup VolumeSystem FileNodeRef GetFileNode(const Path &path); /// Adds a file change notification callback. ///@ingroup VolumeSystem template inline bool AddChangeNotification( const Path &path, T obj, U func ) { FileSystemRef fs = GetFileSystem( path ); if ( !fs || !fs->getChangeNotifier() ) return false; FileSystemChangeNotifier::ChangeDelegate dlg( obj, func ); return fs->getChangeNotifier()->addNotification( path, dlg ); } /// Removes an existing file change notification callback. ///@ingroup VolumeSystem template inline bool RemoveChangeNotification( const Path &path, T obj, U func ) { FileSystemRef fs = GetFileSystem( path ); if ( !fs || !fs->getChangeNotifier() ) return false; FileSystemChangeNotifier::ChangeDelegate dlg( obj, func ); return fs->getChangeNotifier()->removeNotification( path, dlg ); } /// Map a real file system path to a virtual one based on a root. /// This is useful when we get a real path back from an OS file dialog for example. /// e.g. If we have a root "gumby" which points at "C:/foo/bar", /// MapFSPath("gumby", "C:/foo/bar/blat/picture.png", path ); /// will map "C:/foo/bar/blat/picture.png" to "gumby:/blat/picture.png" ///@param inRoot The root to check ///@param inPath The real file system path ///@param outPath The resulting volume system path ///@return Success or failure bool MapFSPath( const String &inRoot, const Path &inPath, Path &outPath ); /// Returns a true file system path without virtual mounts. /// ///@param inPath The path to convert. ///@param outPath The resulting real file system path. /// bool GetFSPath( const Path &inPath, Path &outPath ); /// Find files matching a pattern starting in a given dir. ///@param inBasePath path to start in ///@param inFilePattern the file pattern [it uses the FindMatch class] ///@param inRecursive do we search recursively? ///@param outList the list of files as Strings [Paths are more expensive to compute, so these may be converted on demand] ///@param multiMatch match against multiple file patterns given in inFilePattern? ///@return number of files which matched ///@ingroup VolumeSystem S32 FindByPattern( const Path &inBasePath, const String &inFilePattern, bool inRecursive, Vector &outList, bool multiMatch = false ); /// Set current working directory. ///@ingroup VolumeSystem bool SetCwd(const Path &file); /// Get the current working directory. ///@ingroup VolumeSystem const Path& GetCwd(); /// Remove (or delete) a file from the file system. ///@ingroup VolumeSystem bool Remove(const Path &file); /// Rename a file or directory. ///@ingroup VolumeSystem bool Rename(const Path &from, const Path &to); /// Get the file attributes. /// @return success ///@ingroup VolumeSystem bool GetFileAttributes(const Path &path, FileNode::Attributes *attr); /// Compare modified times of p1 & p2 /// @return -1 if p1 < p2, 0 if p1 == p2, 1 if p1 > p2 S32 CompareModifiedTimes(const Path& p1, const Path& p2); /// Open a file. /// If the file exists a file object will be returned even if the /// open operation fails. ///@return Null if the file does not exist ///@ingroup VolumeSystem FileRef OpenFile(const Path &file, File::AccessMode mode); /// Read in an entire file /// @note Caller is responsible for freeing memory ///@param inPath the file ///@param outData the pointer to return the data ///@param outSize the size of the data returned ///@param inNullTerminate add an extra '\0' byte to the return buffer ///@return successful read? If not, outData will be NULL and outSize will be 0 bool ReadFile(const Path &inPath, void *&outData, U32 &outSize, bool inNullTerminate = false ); /// Open a directory. /// If the directory exists a directory object will be returned even if the /// open operation fails. ///@return Null if the file does not exist ///@ingroup VolumeSystem DirectoryRef OpenDirectory(const Path &file); /// Create a file. /// The file object is returned in a closed state. ///@ingroup VolumeSystem FileRef CreateFile(const Path &file); /// Copy a file from one location to another. bool CopyFile(const Path& source, const Path& destination, bool noOverride); /// Retrieve list of directories in the specified directory. bool DumpDirectories(const Path& path, Vector& directories, S32 depth, bool noBasePath); /// Create a directory. /// The directory object is returned in a closed state. ///@ingroup VolumeSystem DirectoryRef CreateDirectory(const Path &file); /// Create all the directories in the path if they don't already exist ///@ingroup VolumeSystem bool CreatePath(const Path &path); bool IsReadOnly(const Path &path); bool IsDirectory(const Path &path); bool IsFile(const Path &path); bool IsScriptFile(const char* pFilePath); bool VerifyWriteAccess(const Path &path); /// This returns a unique file path from the components /// by appending numbers to the end of the file name if /// a file with the same name already exists. /// /// @param path The root and directory for the file. /// @param fileName The file name without extension. /// @param ext The dot-less extension. String MakeUniquePath( const char *path, const char *fileName, const char *ext ); void StartFileChangeNotifications(); void StopFileChangeNotifications(); S32 GetNumMounts(); String GetMountRoot( S32 index ); String GetMountPath( S32 index ); String GetMountType( S32 index ); ///@} } // Namespace FS } // Namespace Torque #endif