Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

uvfs support for CET with MO2 #877

Merged
merged 1 commit into from
Oct 4, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion src/CET.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,6 @@ CET::CET()
, m_vm(m_paths, m_bindings, m_d3d12)
, m_overlay(m_bindings, m_options, m_persistentState, m_vm)
{
m_vm.Initialize();
}

CET::~CET()
Expand Down
29 changes: 0 additions & 29 deletions src/Utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -286,35 +286,6 @@ std::filesystem::path GetAbsolutePath(std::filesystem::path aFilePath, const std
return aFilePath;
}

std::filesystem::path GetLuaPath(const std::string& acFilePath, const std::filesystem::path& acRootPath, const bool acAllowNonExisting)
{
return GetLuaPath(UTF8ToUTF16(acFilePath), acRootPath, acAllowNonExisting);
}

std::filesystem::path GetLuaPath(std::filesystem::path aFilePath, const std::filesystem::path& acRootPath, const bool acAllowNonExisting)
{
assert(!aFilePath.empty());
assert(!aFilePath.is_absolute());

if (aFilePath.empty() || aFilePath.is_absolute())
return {};

aFilePath.make_preferred();

if (aFilePath.native().starts_with(L"..\\"))
return {};

aFilePath = GetAbsolutePath(aFilePath, acRootPath, acAllowNonExisting, false);
if (aFilePath.empty())
return {};

const auto relativeFilePathToRoot = relative(aFilePath, acRootPath);
if (relativeFilePathToRoot.native().starts_with(L"..\\") || relativeFilePathToRoot.native().find(L"\\..\\") != std::wstring::npos)
return {};

return relative(aFilePath, std::filesystem::current_path());
}

std::vector<char> ReadWholeBinaryFile(const std::filesystem::path& acpPath)
{
if (acpPath.empty() || !exists(acpPath))
Expand Down
5 changes: 2 additions & 3 deletions src/Utils.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#pragma once

#include "scripting/LuaSandbox.h"

#include <overlay/widgets/Widget.h>

void ltrim(std::string& s);
Expand Down Expand Up @@ -53,8 +55,5 @@ GetAbsolutePath(const std::string& acFilePath, const std::filesystem::path& acRo
[[nodiscard]] std::filesystem::path
GetAbsolutePath(std::filesystem::path aFilePath, const std::filesystem::path& acRootPath, const bool acAllowNonExisting, const bool acAllowSymlink = true);

[[nodiscard]] std::filesystem::path GetLuaPath(const std::string& acFilePath, const std::filesystem::path& acRootPath, const bool acAllowNonExisting);
[[nodiscard]] std::filesystem::path GetLuaPath(std::filesystem::path aFilePath, const std::filesystem::path& acRootPath, const bool acAllowNonExisting);

[[nodiscard]] std::vector<char> ReadWholeBinaryFile(const std::filesystem::path& acpPath);
[[nodiscard]] std::string ReadWholeTextFile(const std::filesystem::path& acpPath);
96 changes: 90 additions & 6 deletions src/scripting/LuaSandbox.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,16 @@ void LuaSandbox::Initialize()
// initialize state + environment first
m_globals = {luaState, sol::create};

// log mo2 usage
m_isLaunchedThroughMO2 = GetModuleHandle(TEXT("usvfs_x64.dll")) != nullptr;
if (m_isLaunchedThroughMO2)
{
if (auto existingLogger = spdlog::get("scripting"))
{
existingLogger->warn("CET launched with MO2.");
}
}

// copy whitelisted things from global table
for (const auto* cKey : s_cVMGlobalObjectsWhitelist)
{
Expand Down Expand Up @@ -359,7 +369,7 @@ void LuaSandbox::InitializeIOForSandbox(Sandbox& aSandbox, const sol::state& acp
};
sbEnv["loadstring"] = cLoadString;

const auto cLoadFile = [cSBRootPath, cLoadString, stateView](const std::string& acPath) -> std::tuple<sol::object, sol::object>
const auto cLoadFile = [this, cSBRootPath, cLoadString, stateView](const std::string& acPath) -> std::tuple<sol::object, sol::object>
{
const auto previousCurrentPath = std::filesystem::current_path();
current_path(cSBRootPath);
Expand Down Expand Up @@ -478,7 +488,7 @@ void LuaSandbox::InitializeIOForSandbox(Sandbox& aSandbox, const sol::state& acp
return res;
};

sbEnv["dir"] = [cSBRootPath, stateView](const std::string& acPath) -> sol::table
sbEnv["dir"] = [this, cSBRootPath, stateView](const std::string& acPath) -> sol::table
{
const auto previousCurrentPath = std::filesystem::current_path();
current_path(cSBRootPath);
Expand Down Expand Up @@ -517,7 +527,7 @@ void LuaSandbox::InitializeIOForSandbox(Sandbox& aSandbox, const sol::state& acp
ioSB["output"] = DeepCopySolObject(cIO["output"], acpState);
ioSB["type"] = DeepCopySolObject(cIO["type"], acpState);
ioSB["close"] = DeepCopySolObject(cIO["close"], acpState);
ioSB["lines"] = [cIO, cSBRootPath](const std::string& acPath)
ioSB["lines"] = [this, cIO, cSBRootPath](const std::string& acPath)
{
const auto previousCurrentPath = std::filesystem::current_path();
current_path(cSBRootPath);
Expand All @@ -530,7 +540,7 @@ void LuaSandbox::InitializeIOForSandbox(Sandbox& aSandbox, const sol::state& acp

return result;
};
const auto cOpenWithMode = [cIO, cSBRootPath](const std::string& acPath, const std::string& acMode)
const auto cOpenWithMode = [this, cIO, cSBRootPath](const std::string& acPath, const std::string& acMode)
{
const auto previousCurrentPath = std::filesystem::current_path();
current_path(cSBRootPath);
Expand All @@ -555,7 +565,7 @@ void LuaSandbox::InitializeIOForSandbox(Sandbox& aSandbox, const sol::state& acp
{
const auto cOS = globals["os"].get<sol::table>();
sol::table osSB = DeepCopySolObject(m_globals["os"].get<sol::object>(), acpState);
osSB["rename"] = [cOS, cSBRootPath](const std::string& acOldPath, const std::string& acNewPath) -> std::tuple<sol::object, std::string>
osSB["rename"] = [this, cOS, cSBRootPath](const std::string& acOldPath, const std::string& acNewPath) -> std::tuple<sol::object, std::string>
{
const auto previousCurrentPath = std::filesystem::current_path();
current_path(cSBRootPath);
Expand All @@ -582,7 +592,7 @@ void LuaSandbox::InitializeIOForSandbox(Sandbox& aSandbox, const sol::state& acp

return cResult.valid() ? std::make_tuple(cResult.get<sol::object>(), "") : std::make_tuple(cResult.get<sol::object>(0), cResult.get<std::string>(1));
};
osSB["remove"] = [cOS, cSBRootPath](const std::string& acPath) -> std::tuple<sol::object, std::string>
osSB["remove"] = [this, cOS, cSBRootPath](const std::string& acPath) -> std::tuple<sol::object, std::string>
{
const auto previousCurrentPath = std::filesystem::current_path();
current_path(cSBRootPath);
Expand Down Expand Up @@ -657,3 +667,77 @@ void LuaSandbox::CloseDBForSandbox(const Sandbox& aSandbox) const
end
)");
}

std::filesystem::path LuaSandbox::GetLuaPath(const std::string& acFilePath, const std::filesystem::path& acRootPath, const bool acAllowNonExisting) const
{
return GetLuaPath(UTF8ToUTF16(acFilePath), acRootPath, acAllowNonExisting);
}

std::filesystem::path LuaSandbox::GetLuaPath(std::filesystem::path aFilePath, const std::filesystem::path& acRootPath, const bool acAllowNonExisting) const
{
assert(!aFilePath.empty());
assert(!aFilePath.is_absolute());

if (aFilePath.empty() || aFilePath.is_absolute())
return {};

aFilePath.make_preferred();

if (aFilePath.native().starts_with(L"..\\"))
return {};

aFilePath = GetAbsolutePath(aFilePath, acRootPath, acAllowNonExisting, false);
if (aFilePath.empty())
return {};

const auto relativeFilePathToRoot = relative(aFilePath, acRootPath);
if (relativeFilePathToRoot.native().starts_with(L"..\\") || relativeFilePathToRoot.native().find(L"\\..\\") != std::wstring::npos)
{
auto _Weakly_canonical_path = weakly_canonical(aFilePath);
auto _Weakly_canonical_base = weakly_canonical(acRootPath);

#ifdef CET_DEBUG
if (auto existingLogger = spdlog::get("scripting"))
{
existingLogger->warn("lua sandbox path missmatch for: ");
existingLogger->info("\t{}", acRootPath.string());
existingLogger->info("\tresolved to: {}", _Weakly_canonical_base.string());
existingLogger->info("\t{}", aFilePath.string());
existingLogger->info("\tresolved to: {}", _Weakly_canonical_path.string());
}
#endif // CET_DEBUG

// path outside of sandbox
// make an exception if path outside of sandbox originates from MO2's overwrite directory
if (m_isLaunchedThroughMO2)
{
auto relativeFilePathToGame = aFilePath.lexically_relative(m_pScripting->GameRoot());
auto overwritePath = "overwrite\\bin\\x64\\" + relativeFilePathToGame.string();

const auto relativeBasePathToGame = acRootPath.lexically_relative(m_pScripting->GameRoot());
const auto overwriteBase = "overwrite\\bin\\x64\\" + relativeBasePathToGame.string();

if (_Weakly_canonical_path.string().ends_with(overwritePath) || _Weakly_canonical_base.string().ends_with(overwriteBase))
{
auto resolved = aFilePath.lexically_relative(std::filesystem::current_path());
#ifdef CET_DEBUG
if (auto existingLogger = spdlog::get("scripting"))
{
existingLogger->warn("\tFix filepath for MO2 to: {}", resolved.string());
}
#endif // CET_DEBUG
return resolved;
}
}

#ifdef CET_DEBUG
if (auto existingLogger = spdlog::get("scripting"))
{
existingLogger->error("\tCould not resolve path: {}", aFilePath.string());
}
#endif // CET_DEBUG
return {};
}

return relative(aFilePath, std::filesystem::current_path());
}
7 changes: 7 additions & 0 deletions src/scripting/LuaSandbox.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ struct LuaSandbox

sol::table& GetGlobals();

const bool GetIsLaunchedThroughMO2() const { return m_isLaunchedThroughMO2; }
[[nodiscard]] std::filesystem::path
GetLuaPath(const std::string& acFilePath, const std::filesystem::path& acRootPath, const bool acAllowNonExisting) const;
[[nodiscard]] std::filesystem::path
GetLuaPath(std::filesystem::path aFilePath, const std::filesystem::path& acRootPath, const bool acAllowNonExisting) const;

private:
void InitializeExtraLibsForSandbox(Sandbox& aSandbox, const sol::state& acpState) const;
void InitializeDBForSandbox(Sandbox& aSandbox, const sol::state& acpState);
Expand All @@ -43,4 +49,5 @@ struct LuaSandbox
TiltedPhoques::Map<std::string, sol::object> m_modules{};

bool m_imguiAvailable{false};
bool m_isLaunchedThroughMO2{false};
};
3 changes: 3 additions & 0 deletions src/scripting/LuaVM_Hooks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,9 @@ void LuaVM::HookTDBIDToStringDEBUG(RED4ext::IScriptable*, RED4ext::CStackFrame*

void LuaVM::HookTranslateBytecode(uintptr_t aBinder, uintptr_t aData)
{
// Delay our lua hook until later, to ensure that Mod Organizer's VFS is hooked up.
CET::Get().GetVM().Initialize();

s_vm->m_realTranslateBytecode(aBinder, aData);
s_vm->PostInitializeScripting();
}
Expand Down
2 changes: 1 addition & 1 deletion src/scripting/ScriptContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ ScriptContext::ScriptContext(LuaSandbox& aLuaSandbox, const std::filesystem::pat
const auto previousCurrentPath = std::filesystem::current_path();
current_path(sb.GetRootPath());

const auto path = GetLuaPath(L"init.lua", acPath, false);
const auto path = m_sandbox.GetLuaPath(L"init.lua", acPath, false);

const auto result = sb.ExecuteFile(UTF16ToUTF8(path.native()));

Expand Down
1 change: 1 addition & 0 deletions src/scripting/Scripting.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ struct Scripting

LuaSandbox& GetSandbox();
LockedState GetLockedState() const noexcept;
const std::filesystem::path& GameRoot() const { return m_paths.GameRoot(); }

static size_t Size(RED4ext::CBaseRTTIType* apRttiType);
static sol::object ToLua(LockedState& aState, RED4ext::CStackType& aResult);
Expand Down