mirror of
https://github.com/FEX-Emu/FEX.git
synced 2026-10-06 13:00:15 +02:00
Steam: Add FEXCompatTool
This commit is contained in:
5 files changed
+233
-1
No files matched your search
+5
-1
@@ -33,7 +33,7 @@ option(ENABLE_FEXCORE_PROFILER "Enables use of the FEXCore timeline profiling ca
|
||||
set (FEXCORE_PROFILER_BACKEND "gpuvis" CACHE STRING "Set which backend to use for the FEXCore profiler (gpuvis, tracy)")
|
||||
option(ENABLE_GLIBC_ALLOCATOR_HOOK_FAULT "Enables glibc memory allocation hooking with fault for CI testing")
|
||||
option(USE_PDB_DEBUGINFO "Builds debug info in PDB format" FALSE)
|
||||
option(BUILD_STEAM_SUPPORT "Builds support for steampipe depot creation" FALSE)
|
||||
option(BUILD_STEAM_SUPPORT "Builds FEX for integration into Steam" FALSE)
|
||||
|
||||
set (X86_32_TOOLCHAIN_FILE "${CMAKE_CURRENT_SOURCE_DIR}/Data/CMake/toolchain_x86_32.cmake" CACHE FILEPATH "Toolchain file for the (cross-)compiler targeting i686")
|
||||
set (X86_64_TOOLCHAIN_FILE "${CMAKE_CURRENT_SOURCE_DIR}/Data/CMake/toolchain_x86_64.cmake" CACHE FILEPATH "Toolchain file for the (cross-)compiler targeting x86_64")
|
||||
@@ -590,6 +590,10 @@ if (BUILD_THUNKS)
|
||||
add_dependencies(uninstall uninstall_guest-libs-32)
|
||||
endif()
|
||||
|
||||
if (BUILD_STEAM_SUPPORT)
|
||||
add_subdirectory(Source/Steam/)
|
||||
endif()
|
||||
|
||||
set(FEX_VERSION_MAJOR "0")
|
||||
set(FEX_VERSION_MINOR "0")
|
||||
set(FEX_VERSION_PATCH "0")
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
add_executable(FEXCompatTool
|
||||
CompatTool.cpp)
|
||||
|
||||
target_link_libraries(FEXCompatTool
|
||||
PRIVATE
|
||||
FEXCore Common CommonTools JemallocLibs)
|
||||
|
||||
install(TARGETS FEXCompatTool
|
||||
RUNTIME
|
||||
DESTINATION /
|
||||
COMPONENT Runtime
|
||||
)
|
||||
|
||||
# Description json gets installed into root of depot
|
||||
install(FILES emulator.json
|
||||
DESTINATION /
|
||||
COMPONENT Runtime)
|
||||
install(FILES ConfigTemplate.json
|
||||
DESTINATION /
|
||||
COMPONENT Runtime)
|
||||
@@ -0,0 +1,187 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
/*
|
||||
$info$
|
||||
tags: Bin|FEXCompatTool
|
||||
desc: Used for launching games from Steam
|
||||
$end_info$
|
||||
*/
|
||||
|
||||
#include "PortabilityInfo.h"
|
||||
#include "Common/Config.h"
|
||||
#include "FEXCore/Utils/FileLoading.h"
|
||||
#include "FEXCore/Utils/StringUtils.h"
|
||||
#include "FEXHeaderUtils/Filesystem.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <tiny-json.h>
|
||||
|
||||
fextl::string GenerateSteamConfigTemplate(const FEX::Config::PortableInformation& PortableInfo) {
|
||||
const auto ConfigTemplatePath = PortableInfo.InterpreterPath + "ConfigTemplate.json";
|
||||
if (!FHU::Filesystem::Exists(ConfigTemplatePath)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
fextl::string Data;
|
||||
if (!FEXCore::FileLoading::LoadFile(Data, ConfigTemplatePath)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
// Try and find a mount point.
|
||||
fextl::string MountPoint {};
|
||||
const char* RuntimeDir = getenv("XDG_RUNTIME_DIR");
|
||||
if (RuntimeDir) {
|
||||
MountPoint = fextl::fmt::format("{}/fexrootfs/", RuntimeDir);
|
||||
} else {
|
||||
const auto UserDirectory = fextl::fmt::format("/run/user/{}", geteuid());
|
||||
if (FHU::Filesystem::Exists(UserDirectory)) {
|
||||
MountPoint = fextl::fmt::format("{}/fexrootfs/", UserDirectory);
|
||||
} else {
|
||||
const char* CacheDir = getenv("XDG_CACHE_HOME");
|
||||
if (CacheDir) {
|
||||
MountPoint = fextl::fmt::format("{}/fexrootfs/", CacheDir);
|
||||
} else {
|
||||
// We tried really hard to find a mount path.
|
||||
MountPoint = "~/.cache/fexrootfs/";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Update the @FEX_COMPAT_TOOL@ config to point to the root of the depot.
|
||||
FEXCore::StringUtils::ReplaceAllInPlace(Data, "@FEX_COMPAT_TOOL@", PortableInfo.InterpreterPath);
|
||||
|
||||
// TODO: This path is getting phased out.
|
||||
FEXCore::StringUtils::ReplaceAllInPlace(Data, "@FEX_ROOTFS_PATH@", MountPoint);
|
||||
|
||||
// Save the json.
|
||||
const auto ConfigPath = FEX::Config::GetConfigDirectory(false, PortableInfo);
|
||||
const auto ConfigLocation = ConfigPath + "Config.json";
|
||||
if (!FHU::Filesystem::CreateDirectories(ConfigPath)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
auto File = FEXCore::File::File(ConfigLocation.c_str(),
|
||||
FEXCore::File::FileModes::WRITE | FEXCore::File::FileModes::CREATE | FEXCore::File::FileModes::TRUNCATE);
|
||||
|
||||
if (!File.IsValid()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
File.Write(Data.data(), Data.size());
|
||||
return ConfigPath;
|
||||
}
|
||||
|
||||
fextl::string GenerateSteamAppConfig(const FEX::Config::PortableInformation& PortableInfo) {
|
||||
const auto user_config = getenv("FEX_APP_CONFIG");
|
||||
if (user_config) {
|
||||
// If user supplied config then don't use Steam config.
|
||||
return {};
|
||||
}
|
||||
|
||||
// Current supported Steam options.
|
||||
struct SteamOptions {
|
||||
bool TSO = true;
|
||||
bool Multiblock = true;
|
||||
bool Thunks_GL = false;
|
||||
bool Thunks_Vulkan = false;
|
||||
};
|
||||
SteamOptions Options {};
|
||||
|
||||
// Game overrides.
|
||||
const auto steam_fex_tso = getenv("STEAM_FEX_TSOENABLED");
|
||||
if (steam_fex_tso) {
|
||||
Options.TSO = std::strtoull(steam_fex_tso, nullptr, 0) != 0;
|
||||
}
|
||||
|
||||
const auto steam_fex_multiblock = getenv("STEAM_FEX_MULTIBLOCK");
|
||||
if (steam_fex_multiblock) {
|
||||
Options.Multiblock = std::strtoull(steam_fex_multiblock, nullptr, 0) != 0;
|
||||
}
|
||||
|
||||
// UI overrides.
|
||||
const auto steam_fex_compat = getenv("STEAM_COMPAT_FEX_CONFIG");
|
||||
if (steam_fex_compat) {
|
||||
const auto steam_fex_compat_view = std::string_view(steam_fex_compat);
|
||||
if (steam_fex_compat_view.find("TSOEnabled:1") != steam_fex_compat_view.npos) {
|
||||
Options.TSO = true;
|
||||
}
|
||||
if (steam_fex_compat_view.find("Multiblock:1") != steam_fex_compat_view.npos) {
|
||||
Options.Multiblock = true;
|
||||
}
|
||||
if (steam_fex_compat_view.find("ThunksDB_GL:1") != steam_fex_compat_view.npos) {
|
||||
Options.Thunks_GL = true;
|
||||
}
|
||||
if (steam_fex_compat_view.find("ThunksDB_Vulkan:1") != steam_fex_compat_view.npos) {
|
||||
Options.Thunks_Vulkan = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Create the json.
|
||||
char Buffer[4096];
|
||||
char* Dest {};
|
||||
Dest = json_objOpen(Buffer, nullptr);
|
||||
{
|
||||
Dest = json_objOpen(Dest, "Config");
|
||||
Dest = json_str(Dest, "TSOEnabled", Options.TSO ? "1" : "0");
|
||||
Dest = json_str(Dest, "Multiblock", Options.Multiblock ? "1" : "0");
|
||||
Dest = json_objClose(Dest);
|
||||
}
|
||||
|
||||
{
|
||||
Dest = json_objOpen(Dest, "ThunksDB");
|
||||
Dest = json_str(Dest, "GL", Options.Thunks_GL ? "1" : "0");
|
||||
Dest = json_str(Dest, "Vulkan", Options.Thunks_Vulkan ? "1" : "0");
|
||||
Dest = json_objClose(Dest);
|
||||
}
|
||||
|
||||
Dest = json_objClose(Dest);
|
||||
json_end(Dest);
|
||||
|
||||
// Save the json.
|
||||
const auto ConfigPath = FEX::Config::GetConfigDirectory(false, PortableInfo);
|
||||
const auto ConfigLocation = ConfigPath + "app_config.json";
|
||||
if (!FHU::Filesystem::CreateDirectories(ConfigPath)) {
|
||||
return {};
|
||||
}
|
||||
|
||||
auto File = FEXCore::File::File(ConfigLocation.c_str(),
|
||||
FEXCore::File::FileModes::WRITE | FEXCore::File::FileModes::CREATE | FEXCore::File::FileModes::TRUNCATE);
|
||||
|
||||
if (!File.IsValid()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
File.Write(Buffer, strlen(Buffer));
|
||||
return ConfigLocation;
|
||||
}
|
||||
|
||||
int main(int argc, const char** argv) {
|
||||
const auto PortableInfo = FEX::ReadPortabilityInformation();
|
||||
|
||||
const auto TemplateConfigPath = GenerateSteamConfigTemplate(PortableInfo);
|
||||
const auto AppConfigPath = GenerateSteamAppConfig(PortableInfo);
|
||||
|
||||
if (!TemplateConfigPath.empty()) {
|
||||
setenv("FEX_APP_CONFIG_LOCATION", TemplateConfigPath.c_str(), true);
|
||||
}
|
||||
|
||||
if (!AppConfigPath.empty()) {
|
||||
setenv("FEX_APP_CONFIG", AppConfigPath.c_str(), true);
|
||||
}
|
||||
|
||||
const auto FEXInterpreterPath = PortableInfo.InterpreterPath + "usr/bin/FEX";
|
||||
|
||||
// Due to no arguments for this application, just replace argv[0] and execve again.
|
||||
argv[0] = FEXInterpreterPath.c_str();
|
||||
execv(FEXInterpreterPath.c_str(), const_cast<char* const*>(argv));
|
||||
|
||||
// Save errno as it can change after calling `perror`.
|
||||
const auto saved_errno = errno;
|
||||
|
||||
perror(argv[0]);
|
||||
|
||||
if (saved_errno == ENOENT) {
|
||||
return 127;
|
||||
}
|
||||
|
||||
return 126;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"Config": {
|
||||
"X87ReducedPrecision": "1",
|
||||
"RootFS": "@FEX_ROOTFS_PATH@/",
|
||||
"ThunkHostLibs": "@FEX_COMPAT_TOOL@/usr/lib/aarch64-linux-gnu/fex-emu/HostThunks",
|
||||
"ThunkGuestLibs": "@FEX_COMPAT_TOOL@/usr/share/fex-emu/GuestThunks",
|
||||
"ProfileStats": "1"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"emulator_v0": {
|
||||
"argv": "./usr/bin/FEX",
|
||||
"environment": { "FEX_PORTABLE": "1" },
|
||||
"container_argv": "./usr/bin/FEX",
|
||||
"container_environment": { "FEX_ROOTFS": "" },
|
||||
"main_argv": "./FEXCompatTool",
|
||||
"emulated_architectures": ["x86_64-linux-gnu", "i386-linux-gnu"],
|
||||
"required_architectures": ["aarch64-linux-gnu"],
|
||||
"required_libraries": ["libc.so.6", "libstdc++.so.6"]
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user