1 module dynalib; 2 3 import core.stdc.stdio : printf; 4 import std.stdio : writeln; 5 6 version (Posix) import core.sys.posix.dlfcn; 7 version (Windows) 8 { 9 import core.sys.windows.windows; 10 @nogc nothrow bool function(const char*) setDepDir; 11 } 12 13 private template ptr_of(T) { alias ptr_of = typeof(*T); } 14 15 /*********************************** 16 * Borrowed from BindBC.SFML - Created by mdparker 17 * 18 * Original source: 19 * https://github.com/BindBC/bindbc-sfml/blob/40b08ffa045a139585720a113b619d4357cd89c2/source/bindbc/sfml/config.d#L46 20 */ 21 enum expandEnum(EnumType, string fqnEnumType = EnumType.stringof) = () 22 { 23 string expandEnum = "enum {"; 24 foreach(m; __traits(allMembers, EnumType)) 25 expandEnum ~= m ~ " = " ~ fqnEnumType ~ "." ~ m ~ ","; 26 27 expandEnum ~= "}"; 28 return expandEnum; 29 } (); 30 31 bool loadBindConfig(BindDef* def, string section, string altConfig = "") 32 { 33 import std.file : exists, readText, thisExePath; 34 import std.json : parseJSON; 35 import std.path : dirName, stripExtension; 36 37 if (!def) return false; 38 39 auto cf = thisExePath(); 40 auto cd = dirName(cf); 41 auto file = cd ~ "/" ~ altConfig ~ ".bindconf"; 42 43 if (!exists(file)) 44 { 45 file = stripExtension(cf) ~ ".bindconf"; 46 if (!exists(file)) return false; 47 } 48 49 auto json = parseJSON(readText(file)); 50 if (!(section in json)) return false; 51 52 def.from(json[section]); 53 return true; 54 } 55 56 struct BindDef 57 { 58 import std.json : parseJSON, JSONValue; 59 60 string nameLinux; 61 string nameOSX; 62 string nameWindows; 63 string loadPath; 64 string depPath; 65 66 void from(string input) { from(parseJSON(input)); } 67 68 void from(JSONValue input) 69 { 70 if ("nameLinux" in input) 71 nameLinux = input["nameLinux"].str; 72 else nameLinux = ""; 73 74 if ("nameOSX" in input) 75 nameOSX = input["nameOSX"].str; 76 else nameOSX = ""; 77 78 if ("nameWindows" in input) 79 nameWindows = input["nameWindows"].str; 80 else nameWindows = ""; 81 82 if ("loadPath" in input) 83 loadPath = input["loadPath"].str; 84 else loadPath = ""; 85 86 if ("depPath" in input) 87 depPath = input["depPath"].str; 88 else depPath = ""; 89 } 90 91 string namePlatform() 92 { 93 version (linux) return nameLinux; 94 version (OSX) return nameOSX; 95 version (Windows) return nameWindows; 96 } 97 98 const(char)* fullName() 99 { 100 return cast(const(char*)) (loadPath ~ "/" ~ namePlatform() ~ "\00"); 101 } 102 } 103 104 struct DynaLib 105 { 106 HANDLE handle; 107 string fileName; 108 string directory; 109 string dependencyDir; 110 111 private bool load() 112 { 113 if (handle) 114 { 115 writeln("DynaLib: Invalid request, handle is not yet free!"); 116 return false; 117 } 118 119 version (linux) 120 { 121 if (fileName == "") 122 { 123 writeln("DynaLib: Invalid input, not enough information to load library!"); 124 return false; 125 } 126 } 127 else 128 { 129 if ( 130 fileName == "" || 131 directory == "" || 132 dependencyDir == "" 133 ) { 134 writeln("DynaLib: Invalid input, not enough information to load library!"); 135 return false; 136 } 137 } 138 139 dependencyPath(cast(const char*) dependencyDir); 140 handle = loadLib(cast(const char*)(directory ~ fileName)); 141 dependencyPath(null); 142 143 return handle != null; 144 } 145 146 bool loadFromConfig(string section, string altConfig = "") 147 { 148 auto bind = BindDef(); 149 if (!loadBindConfig(&bind, section, altConfig)) return false; 150 return load(bind); 151 } 152 153 bool load(BindDef def) 154 { 155 version (linux) auto file = def.nameLinux; 156 version (OSX) auto file = def.nameOSX; 157 version (Windows) auto file = def.nameWindows; 158 return load(file, def.loadPath, def.depPath); 159 } 160 161 bool load(string libName, string libDir = "", string depDir = "") 162 { 163 import std.file : exists, thisExePath; 164 import std.path : dirName; 165 166 auto cd = dirName(thisExePath); 167 if (libDir == "") libDir = "./"; 168 if (depDir == "") depDir = "./"; 169 else if (depDir == "!") depDir = libDir; 170 171 if (libDir[0] == '.') libDir = cd ~ libDir[1..$]; 172 if (depDir[0] == '.') depDir = cd ~ depDir[1..$]; 173 174 auto e = cast(int) libDir.length - 1; 175 if (libDir[e] != '/' && libDir[e] != '\\') libDir ~= "/"; 176 177 e = cast(int) depDir.length - 1; 178 if (depDir[e] != '/' && depDir[e] != '\\') depDir ~= "/"; 179 180 version (linux) auto ext = ".so"; 181 version (OSX) auto ext = ".dylib"; 182 version (Windows) auto ext = ".dll"; 183 184 auto file = libDir ~ libName; 185 if (!exists(file)) 186 { 187 file ~= ext; 188 if (!exists(file)) 189 { 190 file = libName; 191 if (!exists(file)) 192 { 193 file ~= ext; 194 if (!exists(file)) 195 { 196 version (linux) 197 { 198 import std.algorithm.searching : endsWith; 199 200 // Pray its on the system paths 201 if (libName.endsWith(ext)) 202 fileName = libName; 203 else fileName = libName ~ ext; 204 205 directory = ""; 206 dependencyDir = ""; 207 208 return load(); 209 } 210 else 211 { 212 writeln("DynaLib: File not found [" ~ libName ~ "]"); 213 return false; 214 } 215 } libName ~= ext; 216 } 217 } libName ~= ext; 218 } 219 220 fileName = libName; 221 directory = libDir; 222 dependencyDir = depDir; 223 224 return load(); 225 } 226 227 bool bind(T)(T* ptr, string symName, bool silent = false) 228 { 229 static if (is(T == P*, P)) 230 return bindSymbol(handle, cast(void**) ptr, 231 cast(const char*) symName, silent); 232 else 233 { 234 T* tmp; 235 auto ret = bindSymbol(handle, cast(void**) &tmp, 236 cast(const char*) symName, silent); 237 *ptr = *tmp; 238 return ret; 239 } 240 } 241 242 bool bindD(T)(T* ptr, string symName, bool silent = false) 243 { 244 import core.demangle : mangle; 245 246 static if (is(T == P*, P)) 247 return bindSymbol(handle, cast(void**) ptr, 248 cast(const char*) mangle!(ptr_of!T)(symName), silent); 249 else 250 { 251 T* tmp; 252 auto ret = bindSymbol(handle, cast(void**) &tmp, 253 cast(const char*) mangle!T(symName), silent); 254 *ptr = *tmp; 255 return ret; 256 } 257 } 258 259 bool unload() 260 { 261 if (unloadLib(handle)) 262 { 263 handle = null; 264 return true; 265 } return false; 266 } 267 268 bool reload() 269 { 270 if (unloadLib(handle)) 271 handle = null; 272 273 return load(); 274 } 275 276 bool isLoaded() 277 { 278 return handle != null; 279 } 280 } 281 282 @nogc nothrow: 283 284 alias HANDLE = void*; 285 286 HANDLE loadLib(const char* libName) 287 { 288 version (Posix) return dlopen(libName, RTLD_NOW); 289 else version (Windows) return LoadLibraryA(libName); 290 else throw new Exception("DynaLib does not support the current operating system."); 291 } 292 293 bool unloadLib(HANDLE lib) 294 { 295 if (!lib) 296 { 297 printf("unloadLib: Library was already null!\n"); 298 return false; 299 } 300 301 version (Posix) dlclose(lib); 302 else version (Windows) FreeLibrary(lib); 303 else throw new Exception("DynaLib does not support the current operating system."); 304 305 return true; 306 } 307 308 bool bindSymbol(T)(HANDLE lib, T* ptr, const char* symbol, bool silent = false) 309 { 310 if (!lib) 311 { 312 printf("bindSymbol: Library is null!\n"); 313 return false; 314 } 315 316 version (Posix) *ptr = dlsym(lib, symbol); 317 else version (Windows) *ptr = GetProcAddress(lib, symbol); 318 else throw new Exception("DynaLib does not support the current operating system."); 319 320 if (*ptr == null) 321 { 322 if (!silent) 323 printf("bindSymbol: Symbol(%s) was not found in the library!\n", symbol); 324 return false; 325 } 326 327 return true; 328 } 329 330 bool dependencyPath(const char* path) 331 { 332 version (Windows) 333 { 334 if(!setDepDir) 335 { 336 auto lib = loadLib("Kernel32.dll"); 337 if (!lib) 338 { 339 printf("dependencyPath: Library is null!\n"); 340 return false; 341 } 342 343 bindSymbol(lib, cast(void**)&setDepDir, "SetDllDirectoryA"); 344 if (!setDepDir) 345 { 346 printf("dependencyPath: Could not re-route dependency search!\n"); 347 return false; 348 } 349 } 350 351 setDepDir(path); 352 } 353 354 return true; 355 }