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 version(DL_ERROR) 116 writeln("DynaLib: Invalid request, handle is not yet free!"); 117 return false; 118 } 119 120 version (linux) 121 { 122 if (fileName == "") 123 { 124 version(DL_ERROR) 125 writeln("DynaLib: Invalid input, not enough information to load library!"); 126 return false; 127 } 128 } 129 else 130 { 131 if ( 132 fileName == "" || 133 directory == "" || 134 dependencyDir == "" 135 ) { 136 version(DL_ERROR) 137 writeln("DynaLib: Invalid input, not enough information to load library!"); 138 return false; 139 } 140 } 141 142 dependencyPath(cast(const char*) dependencyDir); 143 handle = loadLib(cast(const char*)(directory ~ fileName)); 144 dependencyPath(null); 145 146 return handle != null; 147 } 148 149 bool loadFromConfig(string section, string altConfig = "") 150 { 151 auto bind = BindDef(); 152 if (!loadBindConfig(&bind, section, altConfig)) return false; 153 return load(bind); 154 } 155 156 bool load(BindDef def) 157 { 158 version (linux) auto file = def.nameLinux; 159 version (OSX) auto file = def.nameOSX; 160 version (Windows) auto file = def.nameWindows; 161 return load(file, def.loadPath, def.depPath); 162 } 163 164 bool load(string libName, string libDir = "", string depDir = "") 165 { 166 import std.file : exists, thisExePath; 167 import std.path : dirName; 168 169 auto cd = dirName(thisExePath); 170 if (libDir == "") libDir = "./"; 171 if (depDir == "") depDir = "./"; 172 else if (depDir == "!") depDir = libDir; 173 174 if (libDir[0] == '.') libDir = cd ~ libDir[1..$]; 175 if (depDir[0] == '.') depDir = cd ~ depDir[1..$]; 176 177 auto e = cast(int) libDir.length - 1; 178 if (libDir[e] != '/' && libDir[e] != '\\') libDir ~= "/"; 179 180 e = cast(int) depDir.length - 1; 181 if (depDir[e] != '/' && depDir[e] != '\\') depDir ~= "/"; 182 183 version (linux) auto ext = ".so"; 184 version (OSX) auto ext = ".dylib"; 185 version (Windows) auto ext = ".dll"; 186 187 auto file = libDir ~ libName; 188 if (!exists(file)) 189 { 190 file ~= ext; 191 if (!exists(file)) 192 { 193 file = libName; 194 if (!exists(file)) 195 { 196 file ~= ext; 197 if (!exists(file)) 198 { 199 version (linux) 200 { 201 import std.algorithm.searching : endsWith; 202 203 // Pray its on the system paths 204 if (libName.endsWith(ext)) 205 fileName = libName; 206 else fileName = libName ~ ext; 207 208 directory = ""; 209 dependencyDir = ""; 210 211 return load(); 212 } 213 else 214 { 215 version(DL_ERROR) 216 writeln("DynaLib: File not found [" ~ libName ~ "]"); 217 return false; 218 } 219 } libName ~= ext; 220 } 221 } libName ~= ext; 222 } 223 224 fileName = libName; 225 directory = libDir; 226 dependencyDir = depDir; 227 228 return load(); 229 } 230 231 bool bind(T)(T* ptr, string symName) 232 { 233 static if (is(T == P*, P)) 234 return bindSymbol(handle, cast(void**) ptr, 235 cast(const char*) symName); 236 else 237 { 238 T* tmp; 239 auto ret = bindSymbol(handle, cast(void**) &tmp, 240 cast(const char*) symName); 241 *ptr = *tmp; 242 return ret; 243 } 244 } 245 246 bool bindD(T)(T* ptr, string symName) 247 { 248 import core.demangle : mangle; 249 250 static if (is(T == P*, P)) 251 return bindSymbol(handle, cast(void**) ptr, 252 cast(const char*) mangle!(ptr_of!T)(symName)); 253 else 254 { 255 T* tmp; 256 auto ret = bindSymbol(handle, cast(void**) &tmp, 257 cast(const char*) mangle!T(symName)); 258 *ptr = *tmp; 259 return ret; 260 } 261 } 262 263 bool unload() 264 { 265 if (unloadLib(handle)) 266 { 267 handle = null; 268 return true; 269 } return false; 270 } 271 272 bool reload() 273 { 274 if (unloadLib(handle)) 275 handle = null; 276 277 return load(); 278 } 279 280 bool isLoaded() 281 { 282 return handle != null; 283 } 284 } 285 286 @nogc nothrow: 287 288 alias HANDLE = void*; 289 290 HANDLE loadLib(const char* libName) 291 { 292 version (Posix) return dlopen(libName, RTLD_NOW); 293 else version (Windows) return LoadLibraryA(libName); 294 else 295 { 296 version(DL_ERROR) 297 printf("DynaLib does not support the current operating system.\n"); 298 return null; 299 } 300 } 301 302 bool unloadLib(HANDLE lib) 303 { 304 if (!lib) 305 { 306 version(DL_WARNING) 307 printf("unloadLib: Library was already null!\n"); 308 return false; 309 } 310 311 version (Posix) dlclose(lib); 312 else version (Windows) FreeLibrary(lib); 313 else 314 { 315 version(DL_ERROR) 316 printf("DynaLib does not support the current operating system.\n"); 317 return false; 318 } 319 320 return true; 321 } 322 323 bool bindSymbol(T)(HANDLE lib, T* ptr, const char* symbol) 324 { 325 if (!lib) 326 { 327 version(DL_ERROR) 328 printf("bindSymbol: Library is null!\n"); 329 return false; 330 } 331 332 version (Posix) *ptr = dlsym(lib, symbol); 333 else version (Windows) *ptr = GetProcAddress(lib, symbol); 334 else 335 { 336 version(DL_ERROR) 337 printf("DynaLib does not support the current operating system.\n"); 338 return false; 339 } 340 341 if (*ptr == null) 342 { 343 version(DL_WARNING) 344 printf("bindSymbol: Symbol(%s) was not found in the library!\n", symbol); 345 return false; 346 } 347 348 return true; 349 } 350 351 bool dependencyPath(const char* path) 352 { 353 version (Windows) 354 { 355 if(!setDepDir) 356 { 357 auto lib = loadLib("Kernel32.dll"); 358 if (!lib) 359 { 360 version(DL_ERROR) 361 printf("dependencyPath: Library 'Kernel32.dll' is null!\n"); 362 return false; 363 } 364 365 if (!bindSymbol(lib, cast(void**)&setDepDir, "SetDllDirectoryA")) 366 { 367 version(DL_ERROR) 368 printf("dependencyPath: 'SetDllDirectoryA' bind failed.\n"); 369 return false; 370 } 371 if (!setDepDir) 372 { 373 version(DL_ERROR) 374 printf("dependencyPath: Could not re-route dependency search!\n"); 375 return false; 376 } 377 } 378 379 setDepDir(path); 380 } 381 382 return true; 383 }