/* * Descent 3 * Copyright (C) 2024 Parallax Software * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . --- HISTORICAL COMMENTS FOLLOW --- * $Logfile: /DescentIII/Main/module/module.cpp $ * $Revision: 20 $ * $Date: 10/21/99 2:43p $ * $Author: Kevin $ * * Source for Dynamic Loadable Module functions * * $Log: /DescentIII/Main/module/module.cpp $ * * 20 10/21/99 2:43p Kevin * Mac Merge * * 19 8/24/99 4:33p Jeff * fixed bug with finding alternate file names in Linux * * 18 8/22/99 7:12p Jeff * when open a module on a Linux system, if the original open fails, then * check for alternate files with different case. * * 17 8/16/99 11:48a Nate * (JEFF) Use calls to dd_ instead of ddio_ (this library cannot be * dependant on any other libs) * * 16 7/28/99 5:22p Kevin * Mac Merge fixes * * 15 7/28/99 2:17p Kevin * Macintosh Stuff! * * 14 5/13/99 5:07p Ardussi * changes for compiling on the Mac * * 13 4/19/99 4:01a Jeff * fixed splitpath * * 12 4/18/99 3:14p Jeff * fixed splitpath for linux * * 11 4/16/99 1:06a Jeff * * 10 1/19/99 1:08p Jason * added dynamically loadable dlls * * 9 1/14/99 10:36a Jeff * removed ddio_ dependency (requires a #ifdef around functions taken out * of ddio_) * * 8 1/13/99 6:50a Jeff * made linux friendly (#include case-sensitivity) * * 7 1/11/99 1:01p Jeff * code will convert an .so->.dll for win32 and .dll->.so for Linux * * 6 1/11/99 12:53p Jeff * added a function that given a module name it will make sure it has an * extension. Made Osiris friendly with modules with no extension * * 5 1/11/99 12:29p Jeff * tack on an automatic extension to the module name if one isn't given * (system dependent) * * 4 1/10/99 6:47a Jeff * Changes made to get linux version to compile * * 3 7/06/98 10:45a Jeff * Made Linux friendly * * 2 6/05/98 2:15p Jeff * Initial creation * * 1 6/05/98 2:14p Jeff * * $NoKeywords: $ */ #include #include #include #include #if defined(POSIX) #include #endif #include "cfile.h" #include "crossplat.h" #include "log.h" #include "module.h" int ModLastError = MODERR_NOERROR; std::filesystem::path mod_GetRealModuleName(const std::filesystem::path &mod_filename) { std::filesystem::path filename = mod_filename; std::string ext = mod_filename.extension().u8string(); if (ext.empty()) { filename.replace_extension(MODULE_EXT); return filename; } std::transform(ext.begin(), ext.end(), ext.begin(), ::tolower); if (ext == MODULE_EXT) { return filename; } const std::vector replace_exts = { ".dll", ".dylib", ".msl", ".so", }; for (const auto &replace_ext : replace_exts) { if (ext == replace_ext) { filename.replace_extension(MODULE_EXT); return filename; } } return filename; } // Loads a dynamic module into memory for use. // Returns true on success, false otherwise // modfilename is the name of the module (without an extension such as DLL, or so) bool mod_LoadModule(module *handle, const std::filesystem::path &imodfilename, int flags) { if (imodfilename.empty()) { ModLastError = MODERR_OTHER; return false; } if (!handle) { ModLastError = MODERR_INVALIDHANDLE; return false; } handle->handle = nullptr; std::filesystem::path modfilename = mod_GetRealModuleName(imodfilename); #if defined(WIN32) handle->handle = LoadLibrary(modfilename.u8string().c_str()); if (!handle->handle) { // There was an error loading the module DWORD err = GetLastError(); switch (err) { case ERROR_DLL_INIT_FAILED: ModLastError = MODERR_MODINITFAIL; break; case ERROR_DLL_NOT_FOUND: ModLastError = MODERR_MODNOTFOUND; break; case ERROR_INVALID_DLL: ModLastError = MODERR_INVALIDMOD; break; default: ModLastError = MODERR_OTHER; break; } return false; } #elif defined(POSIX) int f = 0; if (flags & MODF_LAZY) f |= RTLD_LAZY; if (flags & MODF_NOW) f |= RTLD_NOW; if (flags & MODF_GLOBAL) f |= RTLD_GLOBAL; handle->handle = dlopen(modfilename.u8string().c_str(), f); if (!handle->handle) { // ok we couldn't find the given name...try other ways std::filesystem::path parent_path = modfilename.parent_path().filename(); std::filesystem::path new_filename = cf_LocatePath(parent_path / modfilename.filename()); if (new_filename.empty()) { LOG_ERROR.printf("Module Load Err: %s", dlerror()); ModLastError = MODERR_MODNOTFOUND; return false; } // ok we have a different filename LOG_DEBUG.printf("MOD: Attempting to open %s instead of %s", new_filename.u8string().c_str(), modfilename.u8string().c_str()); modfilename = parent_path / new_filename; handle->handle = dlopen(modfilename.u8string().c_str(), f); if (!handle->handle) { LOG_ERROR.printf("Module Load Err: %s", dlerror()); ModLastError = MODERR_MODNOTFOUND; return false; } } #endif // Success return true; } // Frees a previously loaded module from memory, it can no longer be used // Returns true on success, false otherwise bool mod_FreeModule(module *handle) { bool ret = true; if (!handle) { ModLastError = MODERR_INVALIDHANDLE; return false; } if (!handle->handle) { ModLastError = MODERR_OTHER; return false; } #if defined(WIN32) ret = (FreeLibrary(handle->handle) != 0); #elif defined(POSIX) dlclose(handle->handle); // dlclose() returns an int, but no docs say what values!!! #endif handle->handle = nullptr; return ret; } // Returns a pointer to a function within a loaded module. If it returns NULL there was an error. Check // mod_GetLastError to see if there was an error symstr is the name of the function you want to get the symbol for (Do // NOT give any pre/suffix to this name) parmbytes is the size (in bytes) of the parameter list the function should have MODPROCADDRESS mod_GetSymbol(module *handle, const char *symstr, uint8_t parmbytes) { MODPROCADDRESS sym; if (!handle) { ModLastError = MODERR_INVALIDHANDLE; return nullptr; } if (!symstr) { ModLastError = MODERR_OTHER; return nullptr; } if (!handle->handle) { ModLastError = MODERR_NOMOD; return nullptr; } #if defined(WIN32) char buffer[256]; // We need to first form the correct symbol name (for Windows) if (parmbytes == 255) sprintf(buffer, "%s", symstr); else sprintf(buffer, "_%s@%d", symstr, parmbytes); DWORD err; sym = GetProcAddress(handle->handle, buffer); if (!sym) { err = GetLastError(); // Try again using no byte ordinals if (parmbytes != 255) { sprintf(buffer, "%s", symstr); sym = GetProcAddress(handle->handle, buffer); if (!sym) err = GetLastError(); } } if (!sym) { // there was an error switch (err) { case ERROR_DLL_INIT_FAILED: ModLastError = MODERR_MODINITFAIL; break; case ERROR_DLL_NOT_FOUND: ModLastError = MODERR_MODNOTFOUND; break; case ERROR_INVALID_DLL: ModLastError = MODERR_INVALIDMOD; break; default: ModLastError = MODERR_OTHER; break; } return NULL; } #elif defined(POSIX) sym = dlsym(handle->handle, symstr); if (!sym) { ModLastError = MODERR_OTHER; return nullptr; } #endif return sym; } // Returns an error code to what the last error was. When this function is called the last error is cleared, so by // calling this function it not only returns the last error, but it removes it, so if you were to call this function // again, it would return no error int mod_GetLastError() { // Clear out the errors #if defined(WIN32) SetLastError(0); #elif defined(POSIX) dlerror(); #endif int ret = ModLastError; ModLastError = MODERR_NOERROR; return ret; }