mirror of
https://github.com/FEX-Emu/FEX.git
synced 2025-02-02 12:37:14 +00:00
Merge pull request #3929 from Sonicadvance1/portable
FEXInterpreter: Support portable installs
This commit is contained in:
commit
9336e35052
@ -217,6 +217,14 @@ def print_man_environment_tail():
|
||||
],
|
||||
"''", True)
|
||||
|
||||
print_man_env_option(
|
||||
"FEX_PORTABLE",
|
||||
[
|
||||
"Allows FEX to run without installation. Global locations for configuration and binfmt_misc are ignored. These files are instead read from <FEXInterpreterPath>/fex-emu/ by default.",
|
||||
"For further customization, see FEX_APP_CONFIG_LOCATION and FEX_APP_DATA_LOCATION."
|
||||
],
|
||||
"''", True)
|
||||
|
||||
def print_man_header():
|
||||
header ='''.Dd {0}
|
||||
.Dt FEX
|
||||
|
@ -338,10 +338,13 @@ fextl::string RecoverGuestProgramFilename(fextl::string Program, bool ExecFDInte
|
||||
}
|
||||
|
||||
ApplicationNames LoadConfig(fextl::unique_ptr<FEX::ArgLoader::ArgLoader> ArgsLoader, bool LoadProgramConfig, char** const envp,
|
||||
bool ExecFDInterp, int ProgramFDFromEnv) {
|
||||
FEX::Config::InitializeConfigs();
|
||||
bool ExecFDInterp, int ProgramFDFromEnv, const PortableInformation& PortableInfo) {
|
||||
const bool IsPortable = PortableInfo.IsPortable;
|
||||
FEX::Config::InitializeConfigs(PortableInfo);
|
||||
FEXCore::Config::Initialize();
|
||||
FEXCore::Config::AddLayer(CreateGlobalMainLayer());
|
||||
if (!IsPortable) {
|
||||
FEXCore::Config::AddLayer(CreateGlobalMainLayer());
|
||||
}
|
||||
FEXCore::Config::AddLayer(CreateMainLayer());
|
||||
|
||||
auto Args = ArgsLoader->Get();
|
||||
@ -391,7 +394,9 @@ ApplicationNames LoadConfig(fextl::unique_ptr<FEX::ArgLoader::ArgLoader> ArgsLoa
|
||||
}
|
||||
}
|
||||
|
||||
FEXCore::Config::AddLayer(CreateAppLayer(ProgramName, FEXCore::Config::LayerType::LAYER_GLOBAL_APP));
|
||||
if (!IsPortable) {
|
||||
FEXCore::Config::AddLayer(CreateAppLayer(ProgramName, FEXCore::Config::LayerType::LAYER_GLOBAL_APP));
|
||||
}
|
||||
FEXCore::Config::AddLayer(CreateAppLayer(ProgramName, FEXCore::Config::LayerType::LAYER_LOCAL_APP));
|
||||
|
||||
auto SteamID = getenv("SteamAppId");
|
||||
@ -399,7 +404,9 @@ ApplicationNames LoadConfig(fextl::unique_ptr<FEX::ArgLoader::ArgLoader> ArgsLoa
|
||||
// If a SteamID exists then let's search for Steam application configs as well.
|
||||
// We want to key off both the SteamAppId number /and/ the executable since we may not want to thunk all binaries.
|
||||
fextl::string SteamAppName = fextl::fmt::format("Steam_{}_{}", SteamID, ProgramName);
|
||||
FEXCore::Config::AddLayer(CreateAppLayer(SteamAppName, FEXCore::Config::LayerType::LAYER_GLOBAL_STEAM_APP));
|
||||
if (!IsPortable) {
|
||||
FEXCore::Config::AddLayer(CreateAppLayer(SteamAppName, FEXCore::Config::LayerType::LAYER_GLOBAL_STEAM_APP));
|
||||
}
|
||||
FEXCore::Config::AddLayer(CreateAppLayer(SteamAppName, FEXCore::Config::LayerType::LAYER_LOCAL_STEAM_APP));
|
||||
}
|
||||
}
|
||||
@ -473,12 +480,16 @@ const char* GetHomeDirectory() {
|
||||
}
|
||||
#endif
|
||||
|
||||
fextl::string GetDataDirectory() {
|
||||
fextl::string DataDir {};
|
||||
fextl::string GetDataDirectory(const PortableInformation& PortableInfo) {
|
||||
const char* DataOverride = getenv("FEX_APP_DATA_LOCATION");
|
||||
|
||||
if (PortableInfo.IsPortable && !DataOverride) {
|
||||
return fextl::fmt::format("{}fex-emu/", PortableInfo.InterpreterPath);
|
||||
}
|
||||
|
||||
fextl::string DataDir {};
|
||||
const char* HomeDir = GetHomeDirectory();
|
||||
const char* DataXDG = getenv("XDG_DATA_HOME");
|
||||
const char* DataOverride = getenv("FEX_APP_DATA_LOCATION");
|
||||
if (DataOverride) {
|
||||
// Data override will override the complete directory
|
||||
DataDir = DataOverride;
|
||||
@ -489,14 +500,18 @@ fextl::string GetDataDirectory() {
|
||||
return DataDir;
|
||||
}
|
||||
|
||||
fextl::string GetConfigDirectory(bool Global) {
|
||||
fextl::string GetConfigDirectory(bool Global, const PortableInformation& PortableInfo) {
|
||||
const char* ConfigOverride = getenv("FEX_APP_CONFIG_LOCATION");
|
||||
if (PortableInfo.IsPortable && (Global || !ConfigOverride)) {
|
||||
return fextl::fmt::format("{}fex-emu/", PortableInfo.InterpreterPath);
|
||||
}
|
||||
|
||||
fextl::string ConfigDir;
|
||||
if (Global) {
|
||||
ConfigDir = GLOBAL_DATA_DIRECTORY;
|
||||
} else {
|
||||
const char* HomeDir = GetHomeDirectory();
|
||||
const char* ConfigXDG = getenv("XDG_CONFIG_HOME");
|
||||
const char* ConfigOverride = getenv("FEX_APP_CONFIG_LOCATION");
|
||||
if (ConfigOverride) {
|
||||
// Config override completely overrides the config directory
|
||||
ConfigDir = ConfigOverride;
|
||||
@ -515,15 +530,15 @@ fextl::string GetConfigDirectory(bool Global) {
|
||||
return ConfigDir;
|
||||
}
|
||||
|
||||
fextl::string GetConfigFileLocation(bool Global) {
|
||||
return GetConfigDirectory(Global) + "Config.json";
|
||||
fextl::string GetConfigFileLocation(bool Global, const PortableInformation& PortableInfo) {
|
||||
return GetConfigDirectory(Global, PortableInfo) + "Config.json";
|
||||
}
|
||||
|
||||
void InitializeConfigs() {
|
||||
FEXCore::Config::SetDataDirectory(GetDataDirectory());
|
||||
FEXCore::Config::SetConfigDirectory(GetConfigDirectory(false), false);
|
||||
FEXCore::Config::SetConfigDirectory(GetConfigDirectory(true), true);
|
||||
FEXCore::Config::SetConfigFileLocation(GetConfigFileLocation(false), false);
|
||||
FEXCore::Config::SetConfigFileLocation(GetConfigFileLocation(true), true);
|
||||
void InitializeConfigs(const PortableInformation& PortableInfo) {
|
||||
FEXCore::Config::SetDataDirectory(GetDataDirectory(PortableInfo));
|
||||
FEXCore::Config::SetConfigDirectory(GetConfigDirectory(false, PortableInfo), false);
|
||||
FEXCore::Config::SetConfigDirectory(GetConfigDirectory(true, PortableInfo), true);
|
||||
FEXCore::Config::SetConfigFileLocation(GetConfigFileLocation(false, PortableInfo), false);
|
||||
FEXCore::Config::SetConfigFileLocation(GetConfigFileLocation(true, PortableInfo), true);
|
||||
}
|
||||
} // namespace FEX::Config
|
||||
|
@ -28,6 +28,12 @@ struct ApplicationNames {
|
||||
fextl::string ProgramName;
|
||||
};
|
||||
|
||||
struct PortableInformation {
|
||||
bool IsPortable;
|
||||
// Path of folder containing FEXInterpreter (including / at the end)
|
||||
fextl::string InterpreterPath;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Loads the FEX and application configurations for the application that is getting ready to run.
|
||||
*
|
||||
@ -40,15 +46,15 @@ struct ApplicationNames {
|
||||
* @return The application name and path structure
|
||||
*/
|
||||
ApplicationNames LoadConfig(fextl::unique_ptr<FEX::ArgLoader::ArgLoader> ArgLoader, bool LoadProgramConfig, char** const envp,
|
||||
bool ExecFDInterp, int ProgramFDFromEnv);
|
||||
bool ExecFDInterp, int ProgramFDFromEnv, const PortableInformation& PortableInfo);
|
||||
|
||||
const char* GetHomeDirectory();
|
||||
|
||||
fextl::string GetDataDirectory();
|
||||
fextl::string GetConfigDirectory(bool Global);
|
||||
fextl::string GetConfigFileLocation(bool Global);
|
||||
fextl::string GetDataDirectory(const PortableInformation& PortableInfo);
|
||||
fextl::string GetConfigDirectory(bool Global, const PortableInformation& PortableInfo);
|
||||
fextl::string GetConfigFileLocation(bool Global, const PortableInformation& PortableInfo);
|
||||
|
||||
void InitializeConfigs();
|
||||
void InitializeConfigs(const PortableInformation& PortableInfo);
|
||||
|
||||
/**
|
||||
* @brief Loads the global FEX config
|
||||
|
@ -992,7 +992,7 @@ bool DrawUI() {
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
FEX::Config::InitializeConfigs();
|
||||
FEX::Config::InitializeConfigs(FEX::Config::PortableInformation {});
|
||||
|
||||
fextl::string ImGUIConfig = FEXCore::Config::GetConfigDirectory(false) + "FEXConfig_imgui.ini";
|
||||
auto [window, gl_context] = FEX::GUI::SetupIMGui("#FEXConfig", ImGUIConfig);
|
||||
|
@ -107,7 +107,7 @@ TSOEmulationFacts GetTSOEmulationFacts() {
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char** argv, char** envp) {
|
||||
FEX::Config::InitializeConfigs();
|
||||
FEX::Config::InitializeConfigs(FEX::Config::PortableInformation {});
|
||||
FEXCore::Config::Initialize();
|
||||
FEXCore::Config::AddLayer(FEX::Config::CreateGlobalMainLayer());
|
||||
FEXCore::Config::AddLayer(FEX::Config::CreateMainLayer());
|
||||
|
@ -64,7 +64,6 @@ $end_info$
|
||||
namespace {
|
||||
static bool SilentLog;
|
||||
static int OutputFD {-1};
|
||||
static bool ExecutedWithFD {false};
|
||||
|
||||
void MsgHandler(LogMan::DebugLevels Level, const char* Message) {
|
||||
if (SilentLog) {
|
||||
@ -194,14 +193,55 @@ void RootFSRedirect(fextl::string* Filename, const fextl::string& RootFS) {
|
||||
}
|
||||
}
|
||||
|
||||
bool RanAsInterpreter(const char* Program) {
|
||||
FEX::Config::PortableInformation ReadPortabilityInformation() {
|
||||
const FEX::Config::PortableInformation BadResult {false, {}};
|
||||
const char* PortableConfig = getenv("FEX_PORTABLE");
|
||||
if (!PortableConfig) {
|
||||
return BadResult;
|
||||
}
|
||||
|
||||
uint32_t Value {};
|
||||
std::string_view PortableView {PortableConfig};
|
||||
|
||||
if (std::from_chars(PortableView.data(), PortableView.data() + PortableView.size(), Value).ec != std::errc {} || Value == 0) {
|
||||
return BadResult;
|
||||
}
|
||||
|
||||
// Read the FEXInterpreter path from `/proc/self/exe` which is always a symlink to the absolute path of the executable running.
|
||||
// This way we can get the parent path that the application is executing from.
|
||||
char SelfPath[PATH_MAX];
|
||||
auto Result = readlink("/proc/self/exe", SelfPath, PATH_MAX);
|
||||
if (Result == -1) {
|
||||
return BadResult;
|
||||
}
|
||||
|
||||
std::string_view SelfPathView {SelfPath, std::min<size_t>(PATH_MAX, Result)};
|
||||
|
||||
// Extract the absolute path from the FEXInterpreter path
|
||||
return {true, fextl::string {SelfPathView.substr(0, SelfPathView.find_last_of('/') + 1)}};
|
||||
}
|
||||
|
||||
bool RanAsInterpreter(bool ExecutedWithFD) {
|
||||
return ExecutedWithFD || FEXLOADER_AS_INTERPRETER;
|
||||
}
|
||||
|
||||
bool IsInterpreterInstalled() {
|
||||
// The interpreter is installed if both the binfmt_misc handlers are available
|
||||
// Or if we were originally executed with FD. Which means the interpreter is installed
|
||||
/**
|
||||
* @brief Queries if FEX is installed as a binfmt_misc interpreter
|
||||
*
|
||||
* @param ExecutedWithFD If FEXInterpreter was executed using a binfmt_misc FD handle from the kernel
|
||||
* @param Portable Portability information about FEX being run in portable mode
|
||||
*
|
||||
* @return true if the binfmt_misc handlers are installed and being used
|
||||
*/
|
||||
bool QueryInterpreterInstalled(bool ExecutedWithFD, const FEX::Config::PortableInformation& Portable) {
|
||||
if (Portable.IsPortable) {
|
||||
// Don't use binfmt interpreter even if it's installed
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if FEX's binfmt_misc handlers are both installed.
|
||||
// The explicit check can be omitted if FEX was executed from an FD,
|
||||
// since this only happens if the kernel launched FEX through binfmt_misc
|
||||
return ExecutedWithFD || (access("/proc/sys/fs/binfmt_misc/FEX-x86", F_OK) == 0 && access("/proc/sys/fs/binfmt_misc/FEX-x86_64", F_OK) == 0);
|
||||
}
|
||||
|
||||
@ -267,9 +307,12 @@ static int StealFEXFDFromEnv(const char* Env) {
|
||||
int main(int argc, char** argv, char** const envp) {
|
||||
auto SBRKPointer = FEXCore::Allocator::DisableSBRKAllocations();
|
||||
FEXCore::Allocator::GLIBCScopedFault GLIBFaultScope;
|
||||
const bool IsInterpreter = RanAsInterpreter(argv[0]);
|
||||
|
||||
ExecutedWithFD = getauxval(AT_EXECFD) != 0;
|
||||
const bool ExecutedWithFD = getauxval(AT_EXECFD) != 0;
|
||||
const bool IsInterpreter = RanAsInterpreter(ExecutedWithFD);
|
||||
const auto PortableInfo = ReadPortabilityInformation();
|
||||
const bool InterpreterInstalled = QueryInterpreterInstalled(ExecutedWithFD, PortableInfo);
|
||||
|
||||
int FEXFD {StealFEXFDFromEnv("FEX_EXECVEFD")};
|
||||
|
||||
LogMan::Throw::InstallHandler(AssertHandler);
|
||||
@ -280,7 +323,7 @@ int main(int argc, char** argv, char** const envp) {
|
||||
argc, argv);
|
||||
auto Args = ArgsLoader->Get();
|
||||
auto ParsedArgs = ArgsLoader->GetParsedArgs();
|
||||
auto Program = FEX::Config::LoadConfig(std::move(ArgsLoader), true, envp, ExecutedWithFD, FEXFD);
|
||||
auto Program = FEX::Config::LoadConfig(std::move(ArgsLoader), true, envp, ExecutedWithFD, FEXFD, PortableInfo);
|
||||
|
||||
if (Program.ProgramPath.empty() && FEXFD == -1) {
|
||||
// Early exit if we weren't passed an argument
|
||||
@ -290,7 +333,7 @@ int main(int argc, char** argv, char** const envp) {
|
||||
// Reload the meta layer
|
||||
FEXCore::Config::ReloadMetaLayer();
|
||||
FEXCore::Config::Set(FEXCore::Config::CONFIG_IS_INTERPRETER, IsInterpreter ? "1" : "0");
|
||||
FEXCore::Config::Set(FEXCore::Config::CONFIG_INTERPRETER_INSTALLED, IsInterpreterInstalled() ? "1" : "0");
|
||||
FEXCore::Config::Set(FEXCore::Config::CONFIG_INTERPRETER_INSTALLED, InterpreterInstalled ? "1" : "0");
|
||||
#ifdef VIXL_SIMULATOR
|
||||
// If running under the vixl simulator, ensure that indirect runtime calls are enabled.
|
||||
FEXCore::Config::EraseSet(FEXCore::Config::CONFIG_DISABLE_VIXL_INDIRECT_RUNTIME_CALLS, "0");
|
||||
|
@ -351,7 +351,7 @@ void ConfigRuntime::onLoad(const QUrl& Filename) {
|
||||
int main(int Argc, char** Argv) {
|
||||
QApplication App(Argc, Argv);
|
||||
|
||||
FEX::Config::InitializeConfigs();
|
||||
FEX::Config::InitializeConfigs(FEX::Config::PortableInformation {});
|
||||
fextl::string ConfigFilename = Argc > 1 ? Argv[1] : FEXCore::Config::GetConfigFileLocation();
|
||||
ConfigInit(ConfigFilename);
|
||||
|
||||
|
@ -1091,7 +1091,7 @@ bool ExtractEroFS(const fextl::string& Path, const fextl::string& RootFS, const
|
||||
|
||||
int main(int argc, char** argv, char** const envp) {
|
||||
auto ArgsLoader = fextl::make_unique<FEX::ArgLoader::ArgLoader>(FEX::ArgLoader::ArgLoader::LoadType::WITHOUT_FEXLOADER_PARSER, argc, argv);
|
||||
FEX::Config::LoadConfig(std::move(ArgsLoader), false, envp, false, {});
|
||||
FEX::Config::LoadConfig(std::move(ArgsLoader), false, envp, false, false, FEX::Config::PortableInformation {});
|
||||
|
||||
// Reload the meta layer
|
||||
FEXCore::Config::ReloadMetaLayer();
|
||||
|
@ -117,7 +117,7 @@ int main(int argc, char** argv, char** const envp) {
|
||||
}
|
||||
|
||||
auto ArgsLoader = fextl::make_unique<FEX::ArgLoader::ArgLoader>(FEX::ArgLoader::ArgLoader::LoadType::WITHOUT_FEXLOADER_PARSER, argc, argv);
|
||||
FEX::Config::LoadConfig(std::move(ArgsLoader), false, envp, false, {});
|
||||
FEX::Config::LoadConfig(std::move(ArgsLoader), false, envp, false, false, FEX::Config::PortableInformation {});
|
||||
|
||||
// Reload the meta layer
|
||||
FEXCore::Config::ReloadMetaLayer();
|
||||
|
@ -199,7 +199,7 @@ int main(int argc, char** argv, char** const envp) {
|
||||
LogMan::Throw::InstallHandler(AssertHandler);
|
||||
LogMan::Msg::InstallHandler(MsgHandler);
|
||||
|
||||
FEX::Config::InitializeConfigs();
|
||||
FEX::Config::InitializeConfigs(FEX::Config::PortableInformation {});
|
||||
FEXCore::Config::Initialize();
|
||||
auto ArgsLoader = fextl::make_unique<FEX::ArgLoader::ArgLoader>(FEX::ArgLoader::ArgLoader::LoadType::WITH_FEXLOADER_PARSER, argc, argv);
|
||||
auto Args = ArgsLoader->Get();
|
||||
|
@ -456,7 +456,7 @@ extern "C" void SyncThreadContext(CONTEXT* Context) {
|
||||
|
||||
NTSTATUS ProcessInit() {
|
||||
FEX::Windows::InitCRTProcess();
|
||||
FEX::Config::InitializeConfigs();
|
||||
FEX::Config::InitializeConfigs(FEX::Config::PortableInformation {});
|
||||
FEXCore::Config::Initialize();
|
||||
FEXCore::Config::AddLayer(FEX::Config::CreateGlobalMainLayer());
|
||||
FEXCore::Config::AddLayer(FEX::Config::CreateMainLayer());
|
||||
|
@ -424,7 +424,7 @@ public:
|
||||
|
||||
void BTCpuProcessInit() {
|
||||
FEX::Windows::InitCRTProcess();
|
||||
FEX::Config::InitializeConfigs();
|
||||
FEX::Config::InitializeConfigs(FEX::Config::PortableInformation {});
|
||||
FEXCore::Config::Initialize();
|
||||
FEXCore::Config::AddLayer(FEX::Config::CreateGlobalMainLayer());
|
||||
FEXCore::Config::AddLayer(FEX::Config::CreateMainLayer());
|
||||
|
Loading…
x
Reference in New Issue
Block a user