From 35cf7703b1595c407d4fe2ffbc4580dfad8690f4 Mon Sep 17 00:00:00 2001 From: Ryan Houdek Date: Sat, 20 Aug 2022 10:02:13 -0700 Subject: [PATCH] Thunks: Support direct thunk config in configuration files Previously in order to enable thunks, we needed an independent description file of which thunks to be enabled. This is nice for quickly testing out new games by setting `FEX_THUNKCONFIG` environment variable. For users that just want to enable thunks this is an unwieldy indirection that doesn't make much sense at a glance. Previously this meant you needed two files as an example: ``` ryanh@ubuntu-linux-20-04-desktop:~/.fex-emu$ cat thunks.json { "ThunksDB": { "GL": 1, "Vulkan": 1 } } ryanh@ubuntu-linux-20-04-desktop:~/.fex-emu$ cat AppConfig/EnderLiliesSteam-Linux-Shipping.json { "Config": { "ThunkConfig":"~\/.fex-emu\/thunks.json" } } ``` Instead of this unwieldy redirection just support `ThunksDB` json directly in the AppConfig. ``` ryanh@ubuntu-linux-20-04-desktop:~/.fex-emu$ cat AppConfig/EnderLiliesSteam-Linux-Shipping.json { "Config": { <...> }, "ThunksDB": { "GL": 1, "Vulkan": 1 } } ``` As can be seen this makes this significantly easier for new users getting in to thunks. Depending on which path to enable thunks the user is more comfortable with, they can still enable them using the `ThunkConfig` option or embedding directly in the application configuration. Additionally this removes the older non-ThunksDB path to loading thunks. All users of it have moved on to using ThunksDB. --- Source/Tests/LinuxSyscalls/FileManagement.cpp | 177 ++++++++---------- Source/Tests/LinuxSyscalls/FileManagement.h | 1 + 2 files changed, 83 insertions(+), 95 deletions(-) diff --git a/Source/Tests/LinuxSyscalls/FileManagement.cpp b/Source/Tests/LinuxSyscalls/FileManagement.cpp index 4beb7c03b..126965349 100644 --- a/Source/Tests/LinuxSyscalls/FileManagement.cpp +++ b/Source/Tests/LinuxSyscalls/FileManagement.cpp @@ -7,6 +7,7 @@ $end_info$ #include "Common/FDUtils.h" +#include "FEXCore/Config/Config.h" #include "Tests/LinuxSyscalls/FileManagement.h" #include "Tests/LinuxSyscalls/EmulatedFiles/EmulatedFiles.h" #include "Tests/LinuxSyscalls/Syscalls.h" @@ -222,16 +223,81 @@ void FileManager::LoadThunkDatabase(bool Global) { FileManager::FileManager(FEXCore::Context::Context *ctx) : EmuFD {ctx} { + bool LoadedThunkDatabase{}; auto ThunkConfigFile = ThunkConfig(); + auto ThunkGuestPath = std::filesystem::path(ThunkGuestLibs()); - if (ThunkConfigFile.size()) { + auto LoadThunksDB = [this, ThunkGuestPath](bool *LoadedThunkDatabase, json_t const* ThunksDB) { + // If a thunks DB property exists then we pull in data from the thunks database + // Load the initial thunks database + if (LoadedThunkDatabase) { + LoadThunkDatabase(true); + LoadThunkDatabase(false); + *LoadedThunkDatabase = true; + } - auto ThunkGuestPath = std::filesystem::path(ThunkGuestLibs()); + // Now load this property + for (json_t const* Item = json_getChild(ThunksDB); Item != nullptr; Item = json_getSibling(Item)) { + const char *LibraryName = json_getName(Item); + int64_t LibraryEnabled = json_getInteger(Item); + if (LibraryEnabled != 0) { + // If the library is enabled then find it in the DB + // Enable the overlay and all the dependencies in one go + auto DBObject = ThunkDB.find(LibraryName); + if (DBObject != ThunkDB.end() && + DBObject->second.Enabled == false) { + auto ThunkPath = ThunkGuestPath / DBObject->second.LibraryName; + if (std::filesystem::exists(ThunkPath)) { + for (auto Overlay : DBObject->second.Overlays) { + // Direct full path in guest RootFS to our overlay file + ThunkOverlays.emplace(Overlay, ThunkPath); + } + } + DBObject->second.Enabled = true; + // Now walk the dependencies and set them up as well + // Make sure to enable each one as we go to remove circular dependencies + std::function &Depends)> InsertDependencies + = [this, &ThunkGuestPath, &InsertDependencies](std::unordered_set &Depends) -> void { + for (auto &Depend : Depends) { + auto DBDepend = ThunkDB.find(Depend); + if (DBDepend != ThunkDB.end() && + DBDepend->second.Enabled == false) { + + auto ThunkPath = ThunkGuestPath / DBDepend->second.LibraryName; + if (std::filesystem::exists(ThunkPath)) { + for (auto Overlay : DBDepend->second.Overlays) { + // Direct full path in guest RootFS to our overlay file + ThunkOverlays.emplace(Overlay, ThunkPath); + } + } + + // Enabled, now walk this dependencies + DBDepend->second.Enabled = true; + InsertDependencies(DBDepend->second.Depends); + } + } + }; + InsertDependencies(DBObject->second.Depends); + } + } + } + }; + + // We try to load ThunksDB from {FEX global config, FEX user config, AppConfig Global, AppConfig Local, Defined ThunksConfig option} + // This doesn't support the classic thunks interface. + + std::vector ConfigPaths { + FEXCore::Config::GetConfigFileLocation(true), + FEXCore::Config::GetConfigFileLocation(false), + FEXCore::Config::GetApplicationConfig(AppConfigName(), true), + FEXCore::Config::GetApplicationConfig(AppConfigName(), false), + ThunkConfigFile, + }; + + for (const auto &Path : ConfigPaths) { std::vector FileData; - if (LoadFile(FileData, ThunkConfigFile)) { - FileData.push_back(0); - + if (LoadFile(FileData, Path)) { JSON::JsonAllocator Pool { .PoolObject = { .init = JSON::PoolInit, @@ -240,101 +306,22 @@ FileManager::FileManager(FEXCore::Context::Context *ctx) }; json_t const *json = json_createWithPool(&FileData.at(0), &Pool.PoolObject); - - json_t const* thunks = json_getProperty( json, "thunks" ); - if (thunks && json_getType(thunks) == JSON_OBJ) { - json_t const* thunk; - for( thunk = json_getChild( thunks ); thunk != 0; thunk = json_getSibling( thunk )) { - char const* GuestThunk = json_getName( thunk ); - jsonType_t propertyType = json_getType( thunk ); - - if (propertyType == JSON_TEXT) { - char const* RootFSLib = json_getValue( thunk ); - auto ThunkPath = ThunkGuestPath / GuestThunk; - if (std::filesystem::exists(ThunkPath)) { - ThunkOverlays.emplace(RootFSLib, ThunkPath); - } - } else if (propertyType == JSON_ARRAY) { - json_t const* child; - for( child = json_getChild( thunk ); child != 0; child = json_getSibling( child ) ) { - if (json_getType( child ) == JSON_TEXT) { - char const* RootFSLib = json_getValue( child ); - auto ThunkPath = ThunkGuestPath / GuestThunk; - if (std::filesystem::exists(ThunkPath)) { - ThunkOverlays.emplace(RootFSLib, ThunkPath); - } - } - } - } - } - } - json_t const* ThunksDB = json_getProperty( json, "ThunksDB" ); if (ThunksDB) { - // If a thunks DB property exists then we pull in data from the thunks database - // Load the initial thunks database - LoadThunkDatabase(true); - LoadThunkDatabase(false); - - // Now load this property - for (json_t const* Item = json_getChild(ThunksDB); Item != nullptr; Item = json_getSibling(Item)) { - const char *LibraryName = json_getName(Item); - int64_t LibraryEnabled = json_getInteger(Item); - if (LibraryEnabled != 0) { - // If the library is enabled then find it in the DB - // Enable the overlay and all the dependencies in one go - auto DBObject = ThunkDB.find(LibraryName); - if (DBObject != ThunkDB.end() && - DBObject->second.Enabled == false) { - - auto ThunkPath = ThunkGuestPath / DBObject->second.LibraryName; - if (std::filesystem::exists(ThunkPath)) { - for (auto Overlay : DBObject->second.Overlays) { - // Direct full path in guest RootFS to our overlay file - ThunkOverlays.emplace(Overlay, ThunkPath); - } - } - DBObject->second.Enabled = true; - // Now walk the dependencies and set them up as well - // Make sure to enable each one as we go to remove circular dependencies - std::function &Depends)> InsertDependencies - = [this, &ThunkGuestPath, &InsertDependencies](std::unordered_set &Depends) -> void { - for (auto &Depend : Depends) { - auto DBDepend = ThunkDB.find(Depend); - if (DBDepend != ThunkDB.end() && - DBDepend->second.Enabled == false) { - - auto ThunkPath = ThunkGuestPath / DBDepend->second.LibraryName; - if (std::filesystem::exists(ThunkPath)) { - for (auto Overlay : DBDepend->second.Overlays) { - // Direct full path in guest RootFS to our overlay file - ThunkOverlays.emplace(Overlay, ThunkPath); - } - } - - // Enabled, now walk this dependencies - DBDepend->second.Enabled = true; - InsertDependencies(DBDepend->second.Depends); - } - } - }; - InsertDependencies(DBObject->second.Depends); - } - } - } - - // Now clear the thunk database since we're loaded - ThunkDB.clear(); + LoadThunksDB(&LoadedThunkDatabase, ThunksDB); } } + } - if (false) { - // Useful for debugging - if (ThunkOverlays.size()) { - LogMan::Msg::IFmt("Thunk Overlays:"); - for (const auto& [Overlay, ThunkPath] : ThunkOverlays) { - LogMan::Msg::IFmt("\t{} -> {}", Overlay, ThunkPath); - } + // Now clear the thunk database since we're loaded + ThunkDB.clear(); + + if (false) { + // Useful for debugging + if (ThunkOverlays.size()) { + LogMan::Msg::IFmt("Thunk Overlays:"); + for (const auto& [Overlay, ThunkPath] : ThunkOverlays) { + LogMan::Msg::IFmt("\t{} -> {}", Overlay, ThunkPath); } } } diff --git a/Source/Tests/LinuxSyscalls/FileManagement.h b/Source/Tests/LinuxSyscalls/FileManagement.h index a2a2bdba7..a302e8ae4 100644 --- a/Source/Tests/LinuxSyscalls/FileManagement.h +++ b/Source/Tests/LinuxSyscalls/FileManagement.h @@ -80,6 +80,7 @@ private: FEX_CONFIG_OPT(ThunkHostLibs, THUNKHOSTLIBS); FEX_CONFIG_OPT(ThunkGuestLibs, THUNKGUESTLIBS); FEX_CONFIG_OPT(ThunkConfig, THUNKCONFIG); + FEX_CONFIG_OPT(AppConfigName, APP_CONFIG_NAME); uint32_t CurrentPID{}; void LoadThunkDatabase(bool Global);