diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index 5bbc0211c1..adf50bea0e 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -228,6 +229,9 @@ int runApplication(int argc, char* argv[]) if (parseOptions(argc, argv, *engine, cfgMgr)) { + if (!Misc::checkRequiredOSGPluginsArePresent()) + return 1; + engine->go(); } diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 4b3a661253..9868a8fdf9 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -43,6 +43,24 @@ endif (GIT_CHECKOUT) list (APPEND COMPONENT_FILES "${OpenMW_BINARY_DIR}/${VERSION_CPP_FILE}") +# OSG plugin checker +# Helpfully, OSG doesn't export this to its CMake config as it doesn't have one +set(OSG_PLUGIN_PREFIX "") +if (CYGWIN) + SET(OSG_PLUGIN_PREFIX "cygwin_") +elseif(MINGW) + SET(OSG_PLUGIN_PREFIX "mingw_") +endif() +list(TRANSFORM USED_OSG_PLUGINS PREPEND "${OSG_PLUGIN_PREFIX}" OUTPUT_VARIABLE USED_OSG_PLUGIN_FILENAMES) +list(TRANSFORM USED_OSG_PLUGIN_FILENAMES APPEND "${CMAKE_SHARED_MODULE_SUFFIX}") +list(TRANSFORM USED_OSG_PLUGIN_FILENAMES PREPEND "\"" OUTPUT_VARIABLE USED_OSG_PLUGIN_FILENAMES_FORMATTED) +list(TRANSFORM USED_OSG_PLUGIN_FILENAMES_FORMATTED APPEND "\"") +list(JOIN USED_OSG_PLUGIN_FILENAMES_FORMATTED ", " USED_OSG_PLUGIN_FILENAMES_FORMATTED) + +set(OSG_PLUGIN_CHECKER_CPP_FILE "components/misc/osgpluginchecker.cpp") +configure_file("${OpenMW_SOURCE_DIR}/${OSG_PLUGIN_CHECKER_CPP_FILE}.in" "${OpenMW_BINARY_DIR}/${OSG_PLUGIN_CHECKER_CPP_FILE}") +list(APPEND COMPONENT_FILES "${OpenMW_BINARY_DIR}/${OSG_PLUGIN_CHECKER_CPP_FILE}") + # source files add_component_dir (lua @@ -277,8 +295,8 @@ add_component_dir (esm4 add_component_dir (misc barrier budgetmeasurement color compression constants convert coordinateconverter display endianness float16 frameratelimiter - guarded math mathutil messageformatparser notnullptr objectpool osguservalues progressreporter resourcehelpers rng - strongtypedef thread timeconvert timer tuplehelpers tuplemeta utf8stream weakcache windows + guarded math mathutil messageformatparser notnullptr objectpool osgpluginchecker osguservalues progressreporter resourcehelpers + rng strongtypedef thread timeconvert timer tuplehelpers tuplemeta utf8stream weakcache windows ) add_component_dir (misc/strings diff --git a/components/misc/osgpluginchecker.cpp.in b/components/misc/osgpluginchecker.cpp.in new file mode 100644 index 0000000000..b570c8f858 --- /dev/null +++ b/components/misc/osgpluginchecker.cpp.in @@ -0,0 +1,86 @@ +#include "components/misc/osgpluginchecker.hpp" + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace Misc +{ +#if defined(OSG_LIBRARY_STATIC) || defined(__APPLE__) + + bool checkRequiredOSGPluginsArePresent() + { + // assume they were linked in at build time and CMake would have failed if they were missing + // true-ish for MacOS - they're copied into the package and that'd fail if they were missing, + // but if you don't actually make a MacOS package and run a local build, this won't notice. + // the workaround in the real implementation isn't powerful enough to make MacOS work, though. + return true; + } + +#else + + namespace + { + constexpr auto USED_OSG_PLUGIN_FILENAMES = std::to_array({${USED_OSG_PLUGIN_FILENAMES_FORMATTED}}); + } + + bool checkRequiredOSGPluginsArePresent() + { + // work around osgDB::listAllAvailablePlugins() not working on some platforms due to a suspected OSG bug + std::filesystem::path pluginDirectoryName = std::string("osgPlugins-") + std::string(osgGetVersion()); + osgDB::FilePathList& filepath = osgDB::getLibraryFilePathList(); + for (const auto& path : filepath) + { +#ifdef OSG_USE_UTF8_FILENAME + std::filesystem::path osgPath{ stringToU8String(path) }; +#else + std::filesystem::path osgPath{ path }; +#endif + if (!osgPath.has_filename()) + osgPath = osgPath.parent_path(); + + if (osgPath.filename() == pluginDirectoryName) + { + osgPath = osgPath.parent_path(); +#ifdef OSG_USE_UTF8_FILENAME + std::string extraPath = u8StringToString(osgPath.u8string_view()); +#else + std::string extraPath = osgPath.string(); +#endif + filepath.emplace_back(std::move(extraPath)); + } + } + + auto availableOSGPlugins = osgDB::listAllAvailablePlugins(); + bool haveAllPlugins = true; + for (std::string_view plugin : USED_OSG_PLUGIN_FILENAMES) + { + if (std::find_if(availableOSGPlugins.begin(), availableOSGPlugins.end(), + [&](std::string_view availablePlugin) { +#ifdef OSG_USE_UTF8_FILENAME + std::filesystem::path pluginPath{ stringToU8String(availablePlugin) }; +#else + std::filesystem::path pluginPath {availablePlugin}; +#endif + return pluginPath.filename() == plugin; + }) + == availableOSGPlugins.end()) + { + Log(Debug::Error) << "Missing OSG plugin: " << plugin; + haveAllPlugins = false; + } + } + return haveAllPlugins; + } + +#endif +} diff --git a/components/misc/osgpluginchecker.hpp b/components/misc/osgpluginchecker.hpp new file mode 100644 index 0000000000..2f5ea09700 --- /dev/null +++ b/components/misc/osgpluginchecker.hpp @@ -0,0 +1,9 @@ +#ifndef OPENMW_COMPONENTS_MISC_OSGPLUGINCHECKER_HPP +#define OPENMW_COMPONENTS_MISC_OSGPLUGINCHECKER_HPP + +namespace Misc +{ + bool checkRequiredOSGPluginsArePresent(); +} + +#endif