1 /** 2 * TODO 3 */ 4 module dash.utility.resources; 5 import dash.utility.output; 6 7 import std.file, std.path, std.stdio, std.array, std.algorithm, std.datetime; 8 9 /** 10 * Paths to the different resource files. 11 */ 12 enum Resources : string 13 { 14 Home = "..", 15 Materials = Home ~ "/Materials", 16 Meshes = Home ~ "/Meshes", 17 Textures = Home ~ "/Textures", 18 Audio = Home ~ "/Audio", 19 Scripts = Home ~ "/Scripts", 20 Prefabs = Home ~ "/Prefabs", 21 Objects = Home ~ "/Objects", 22 Shaders = Home ~ "/Shaders", 23 UI = Home ~ "/UI", 24 ConfigDir = Home ~ "/Config", 25 ConfigFile = ConfigDir ~ "/Config", 26 InputBindings = ConfigDir ~ "/Input", 27 } 28 29 /** 30 * Get all files in a given directory. 31 */ 32 Resource[] scanDirectory( string path, string pattern = "" ) 33 { 34 // Get absolute path to folder 35 string safePath = path.absolutePath().buildNormalizedPath(); 36 37 if( !safePath.exists() ) 38 { 39 tracef( "%s does not exist.", path ); 40 return []; 41 } 42 43 // Start array 44 return ( pattern.length 45 ? safePath.dirEntries( pattern, SpanMode.breadth ).array 46 : safePath.dirEntries( SpanMode.breadth ).array ) 47 .filter!( entry => entry.isFile ) 48 .map!( entry => Resource( entry ) ) 49 .array(); 50 } 51 52 enum internalResource = Resource( true ); 53 54 /** 55 * Represents a resource on the file system. 56 */ 57 struct Resource 58 { 59 public: 60 @disable this(); 61 62 /** 63 * Creates a Resource from the given filepath. 64 * 65 * Params: 66 * filePath = The path of the file created. 67 */ 68 this( string filePath ) 69 { 70 assert( filePath.isFile(), "Invalid file name." ); 71 _fullPath = filePath.absolutePath().buildNormalizedPath(); 72 } 73 74 /** 75 * Shuts down the File if it was instantiated. 76 */ 77 ~this() 78 { 79 if( file && file.isOpen ) 80 file.close(); 81 } 82 83 /// The full path to the file. 84 @property string fullPath() { return _fullPath; } 85 /// The relative path from the executable to the file. 86 @property string relativePath() { return _fullPath.relativePath(); } 87 /// The name of the file with its extension. 88 @property string fileName() { return _fullPath.baseName(); } 89 /// The name of the file without its extension. 90 @property string baseFileName() { return fileName().stripExtension(); } 91 /// The path to the directory containing the file. 92 @property string directory() { return _fullPath.dirName(); } 93 /// The extensino of the file. 94 @property string extension() { return _fullPath.extension(); } 95 /// Checks if the file still exists. 96 bool exists() @property { return isInternal || fullPath.isFile(); } 97 /// Converts to a std.stdio.File 98 File* getFile( string mode = "r" ) 99 { 100 if( isInternal ) 101 return null; 102 103 if( !file ) 104 file = new File( _fullPath, mode ); 105 106 return file; 107 } 108 109 /** 110 * Read the contents of the file. 111 * 112 * Returns: The contents of a file as a ubyte[]. 113 */ 114 ubyte[] read() 115 { 116 if( isInternal ) 117 return []; 118 119 markRead(); 120 return cast(ubyte[])_fullPath.read(); 121 } 122 123 /** 124 * Read the contents of the file. 125 * 126 * Returns: The contents of a file as a string. 127 */ 128 string readText() 129 { 130 if( isInternal ) 131 return ""; 132 133 markRead(); 134 return _fullPath.readText(); 135 } 136 137 /** 138 * Checks if the file has been modified since it was last loaded. 139 * 140 * Returns: Whether the last modified time is more recent than the time it was last read. 141 */ 142 bool needsRefresh() 143 { 144 if( isInternal ) 145 return false; 146 147 return fullPath.timeLastModified > timeRead; 148 } 149 150 private: 151 string _fullPath; 152 bool isInternal; 153 std.stdio.File* file; 154 SysTime timeRead; 155 156 this( bool internal ) 157 { 158 isInternal = internal; 159 _fullPath = "__internal"; 160 } 161 162 void markRead() 163 { 164 if( !isInternal ) 165 timeRead = fullPath.timeLastModified(); 166 } 167 }