Skip to content
Draft
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
2 changes: 1 addition & 1 deletion Code/client/xmake.lua
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,6 @@ target(name)
"kernel32")
end

add_requires("tiltedcore v0.2.7", {debug = true})
add_requires("tiltedcore")

build_client("SkyrimTogetherClient")
6 changes: 6 additions & 0 deletions Code/immersive_launcher/Launcher.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ LaunchContext* GetLaunchContext()
return g_context;
}

bool LaunchContext::GetLoaded()
{
return isLoaded;
}

// Everything is nothing, life is worth living, just look to the stars
#define DIE_NOW(err) \
{ \
Expand Down Expand Up @@ -126,6 +131,7 @@ bool LoadProgram(LaunchContext& LC)
LC.Version = QueryFileVersion(LC.exePath.c_str());
if (LC.Version.empty())
DIE_NOW(L"Failed to query game version");
LC.SetLoaded();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not after the if (!loader.Load(reinterpret_cast<uint8_t*>(content.data()))) call?

Copy link
Contributor Author

@rfortier rfortier Nov 29, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Slightly subtle. The goal is for the custom loader to load SkyrimSE.exe with spoofing enabled so any DLLs, in particular any preload DLLs like SSE EngineFixes.dll, experience what looks like SkyrimSE.exe is loading rather than experiencing SkyrimTogether.exe as loaded.

In particular, attempts to evaluate the directory of the running executable have to return the GamePath, not the SkyrimTogether.exe path.

So the switch must be thrown before loading the executable.


ExeLoader loader(CurrentTarget.exeLoadSz);
if (!loader.Load(reinterpret_cast<uint8_t*>(content.data())))
Expand Down
6 changes: 6 additions & 0 deletions Code/immersive_launcher/Launcher.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ struct LaunchContext
fs::path gamePath;
TiltedPhoques::String Version;
ExeLoader::TEntryPoint gameMain = nullptr;

void SetLoaded() { isLoaded = true; } // If loaded, need to spoof GetModuleFileName*(nullptr)
bool GetLoaded();

private:
bool isLoaded{false};
};

LaunchContext* GetLaunchContext();
Expand Down
55 changes: 5 additions & 50 deletions Code/immersive_launcher/stubs/FileMapping.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ inline bool IsUsingMO2()
return GetModuleHandleW(L"usvfs_x64.dll");
}

inline bool IsMyModule(HMODULE aHmod)
bool IsMyModule(HMODULE aHmod)
{
return aHmod == nullptr || aHmod == NtInternal::ThePeb()->pImageBase;
}
Expand Down Expand Up @@ -156,56 +156,19 @@ NTSTATUS WINAPI TP_LdrGetDllFullName(HMODULE Module, PUNICODE_STRING DllName)
return RealLdrGetDllFullName(Module, DllName);
}

bool NeedsToFool(void* pRbp, bool* wantsTruth = nullptr)
{
// game code/stub segment within this exe needs to be fooled
if (IsThisExeAddress(static_cast<uint8_t*>(pRbp)))
{
return IsGameMemoryAddress(static_cast<uint8_t*>(pRbp));
}

// this heuristic indicates hooked game code... that is still owned by us...
// not recognized immedeatly, but still looks like game code...
HMODULE hMod = HModFromAddress(pRbp);

// simple debug hook
#if 0
if (hMod == GetModuleHandleW(L"NvCameraAllowlisting64.dll"))
{
__debugbreak();
}
#endif

if (hMod == NtInternal::ThePeb()->pImageBase || hMod == nullptr /*This is a hook, virtual allocd, not owned by anybody, so we assign ownership to the ST directory*/)
{
if (wantsTruth)
*wantsTruth = true;
return false;
}

return !IsLocalModulePath(hMod);
}

DWORD WINAPI TP_GetModuleFileNameW(HMODULE aModule, LPWSTR alpFilename, DWORD aSize)
{
// trampoline space for USVFS
TP_EMPTY_HOOK_PLACEHOLDER;

void* rbp = _ReturnAddress();
// PrintOwnerNa me(rbp);

bool force = false;
if (IsMyModule(aModule) && NeedsToFool(rbp, &force) && launcher::GetLaunchContext())
if (IsMyModule(aModule) && launcher::GetLaunchContext() && launcher::GetLaunchContext()->GetLoaded())
{
auto& aExePath = launcher::GetLaunchContext()->exePath;
StringCchCopyW(alpFilename, aSize, aExePath.c_str());

return static_cast<DWORD>(std::wcslen(alpFilename));
}

if (force)
return MYGetModuleFileNameW(aModule, alpFilename, aSize);

return RealGetModuleFileNameW(aModule, alpFilename, aSize);
}

Expand All @@ -228,15 +191,6 @@ DWORD WINAPI TP_GetModuleFileNameA(HMODULE aModule, char* alpFileName, DWORD aBu
{
TP_EMPTY_HOOK_PLACEHOLDER;

void* rbp = _ReturnAddress();
if (IsMyModule(aModule) && NeedsToFool(rbp) && launcher::GetLaunchContext())
{
auto aExePath = launcher::GetLaunchContext()->exePath.string();
StringCchCopyA(alpFileName, aBufferSize, aExePath.c_str());

return static_cast<DWORD>(std::strlen(alpFileName));
}

ScopedOSHeapItem wideBuffer((aBufferSize * sizeof(wchar_t)) + 1);

wchar_t* pBuffer = static_cast<wchar_t*>(wideBuffer.m_pBlock);
Expand All @@ -246,8 +200,9 @@ DWORD WINAPI TP_GetModuleFileNameA(HMODULE aModule, char* alpFileName, DWORD aBu
// of quitting, so just avoid the bug.
// TODO: Further analysis of what is under MO2 USVFS and what is needed.
DWORD result = 0;
if (aModule != GetModuleHandleW(L"XAudio2_7.dll"))
result = RealGetModuleFileNameW(aModule, pBuffer, aBufferSize * sizeof(wchar_t));
HMODULE tMod = RealGetModuleHandleW(L"XAudio2_7.dll");
if (tMod == nullptr || aModule != tMod)
result = TP_GetModuleFileNameW(aModule, pBuffer, aBufferSize * sizeof(wchar_t)); // To make sure spoofing happens if needed

if (result == 0)
{
Expand Down
2 changes: 1 addition & 1 deletion xmake.lua
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ end
add_requires(
"entt v3.10.0",
"recastnavigation v1.6.0",
"tiltedcore v0.2.7",
"tiltedcore 0.2.8",
"cryptopp 8.9.0",
"spdlog v1.13.0",
"cpp-httplib 0.14.0",
Expand Down
Loading