Wine compatibility and patches (#219)

Co-authored-by: Skull <86374920+skkuull@users.noreply.github.com>
This commit is contained in:
m 2022-09-02 21:54:39 -05:00 committed by GitHub
parent 902fb83c5c
commit f7c5b6506a
6 changed files with 185 additions and 22 deletions

View File

@ -3,6 +3,8 @@
#include "scheduler.hpp" #include "scheduler.hpp"
#include "game/game.hpp" #include "game/game.hpp"
#include "arxan.hpp"
#include <utils/hook.hpp> #include <utils/hook.hpp>
namespace arxan namespace arxan
@ -108,6 +110,22 @@ namespace arxan
} }
} }
bool is_wine()
{
static bool is_wine = false;
static bool is_wine_set = false;
if (!is_wine_set)
{
const utils::nt::library ntdll("ntdll.dll");
is_wine = ntdll.get_proc<void*>("wine_get_version") != nullptr;
is_wine_set = true;
}
return is_wine;
}
class component final : public component_interface class component final : public component_interface
{ {
public: public:
@ -137,7 +155,10 @@ namespace arxan
void post_unpack() override void post_unpack() override
{ {
// cba to implement sp, not sure if it's even needed // cba to implement sp, not sure if it's even needed
if (game::environment::is_sp()) return; if (game::environment::is_sp())
{
return;
}
} }
}; };
} }

View File

@ -0,0 +1,6 @@
#pragma once
namespace arxan
{
bool is_wine();
}

View File

@ -3,6 +3,8 @@
#include "steam_proxy.hpp" #include "steam_proxy.hpp"
#include "scheduler.hpp" #include "scheduler.hpp"
#include "arxan.hpp"
#include <utils/nt.hpp> #include <utils/nt.hpp>
#include <utils/flags.hpp> #include <utils/flags.hpp>
#include <utils/string.hpp> #include <utils/string.hpp>
@ -102,7 +104,10 @@ namespace steam_proxy
void load_client() void load_client()
{ {
const std::filesystem::path steam_path = steam::SteamAPI_GetSteamInstallPath(); const std::filesystem::path steam_path = steam::SteamAPI_GetSteamInstallPath();
if (steam_path.empty()) return; if (steam_path.empty())
{
return;
}
utils::nt::library::load(steam_path / "tier0_s64.dll"); utils::nt::library::load(steam_path / "tier0_s64.dll");
utils::nt::library::load(steam_path / "vstdlib_s64.dll"); utils::nt::library::load(steam_path / "vstdlib_s64.dll");

View File

@ -1,6 +1,7 @@
#include <std_include.hpp> #include <std_include.hpp>
#include "loader/component_loader.hpp" #include "loader/component_loader.hpp"
#include "system_check.hpp" #include "system_check.hpp"
#include "arxan.hpp"
#include "game/game.hpp" #include "game/game.hpp"
@ -64,15 +65,27 @@ namespace system_check
return verify_hashes(mp_zone_hashes) && (game::environment::is_dedi() || verify_hashes(sp_zone_hashes)); return verify_hashes(mp_zone_hashes) && (game::environment::is_dedi() || verify_hashes(sp_zone_hashes));
} }
// need to update these values
void verify_binary_version() void verify_binary_version()
{ {
const auto value = *reinterpret_cast<DWORD*>(0x1337_b); const auto value = *reinterpret_cast<DWORD*>(0x1337_b);
if (arxan::is_wine())
{
if (value != 0xFFB81262 && value != 0xFFB81143)
{
return;
}
}
else
{
if (value != 0x60202B6A && value != 0xBC0E9FE) if (value != 0x60202B6A && value != 0xBC0E9FE)
{ {
throw std::runtime_error("Unsupported Call of Duty: Modern Warfare Remastered version (1.15)"); return;
} }
} }
throw std::runtime_error("Unsupported Call of Duty: Modern Warfare Remastered version (1.15)");
}
} }
bool is_valid() bool is_valid()

View File

@ -4,6 +4,8 @@
#include "loader/component_loader.hpp" #include "loader/component_loader.hpp"
#include "game/game.hpp" #include "game/game.hpp"
#include "component/arxan.hpp"
#include <utils/string.hpp> #include <utils/string.hpp>
#include <utils/flags.hpp> #include <utils/flags.hpp>
#include <utils/io.hpp> #include <utils/io.hpp>
@ -202,11 +204,16 @@ int main()
ShowWindow(GetConsoleWindow(), SW_HIDE); ShowWindow(GetConsoleWindow(), SW_HIDE);
FARPROC entry_point; FARPROC entry_point;
// leaving these for Windows only for now, need to test to see if we can have for Wine -mikey
if (!arxan::is_wine())
{
enable_dpi_awareness(); enable_dpi_awareness();
// This requires admin privilege, but I suppose many // This requires admin privilege, but I suppose many
// people will start with admin rights if it crashes. // people will start with admin rights if it crashes.
limit_parallel_dll_loading(); limit_parallel_dll_loading();
}
srand(uint32_t(time(nullptr))); srand(uint32_t(time(nullptr)));
remove_crash_file(); remove_crash_file();

View File

@ -1,10 +1,65 @@
#include <std_include.hpp> #include <std_include.hpp>
#include "steam.hpp" #include "steam.hpp"
#include "component/arxan.hpp"
#include "component/console.hpp"
#include <utils/nt.hpp> #include <utils/nt.hpp>
#include <utils/string.hpp>
#define GENERIC_RETURN_IF_FAIL(condition) \
if (condition != S_OK) \
{ \
return; \
}
namespace steam namespace steam
{ {
namespace
{
void open_folder_prompt(char* directory)
{
if (CoInitializeEx(0, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE) != S_OK)
{
MSG_BOX_ERROR("CoInitializeEx failed. This could be because uninitialization failed, try again.");
return;
}
const auto _ = gsl::finally([]()
{
CoUninitialize(); // uninitialize on any return so we can use this again
});
IFileOpenDialog* dialog;
GENERIC_RETURN_IF_FAIL(CoCreateInstance(CLSID_FileOpenDialog, 0, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&dialog)));
FILEOPENDIALOGOPTIONS dw_options;
GENERIC_RETURN_IF_FAIL(dialog->GetOptions(&dw_options));
GENERIC_RETURN_IF_FAIL(dialog->SetOptions(dw_options | FOS_PICKFOLDERS | FOS_FORCEFILESYSTEM | FOS_PATHMUSTEXIST));
GENERIC_RETURN_IF_FAIL(dialog->SetTitle(L"Select a valid Steam install (contains libraries like 'steam_api64.dll')"));
if (dialog->Show(0) != S_OK) // doesn't returns S_OK when operation is cancelled
{
dialog->Release();
return;
}
IShellItem* shell_item_result;
GENERIC_RETURN_IF_FAIL(dialog->GetResult(&shell_item_result));
PWSTR tmp = nullptr;
GENERIC_RETURN_IF_FAIL(shell_item_result->GetDisplayName(SIGDN_FILESYSPATH, &tmp))
shell_item_result->Release();
dialog->Release();
GENERIC_RETURN_IF_FAIL(tmp == nullptr);
std::size_t i;
wcstombs_s(&i, directory, MAX_PATH, tmp, MAX_PATH);
}
}
uint64_t callbacks::call_id_ = 0; uint64_t callbacks::call_id_ = 0;
std::recursive_mutex callbacks::mutex_; std::recursive_mutex callbacks::mutex_;
std::map<uint64_t, bool> callbacks::calls_; std::map<uint64_t, bool> callbacks::calls_;
@ -111,12 +166,16 @@ namespace steam
bool SteamAPI_Init() bool SteamAPI_Init()
{ {
const std::filesystem::path steam_path = steam::SteamAPI_GetSteamInstallPath(); const std::filesystem::path steam_path = steam::SteamAPI_GetSteamInstallPath();
if (steam_path.empty()) return true; if (steam_path.empty())
{
return true;
}
::utils::nt::library::load(steam_path / "tier0_s64.dll"); // not shipped with Steam on linux
::utils::nt::library::load(steam_path / "vstdlib_s64.dll"); // ^
::utils::nt::library::load(steam_path / "gameoverlayrenderer64.dll"); // however, this one is shipped
::utils::nt::library::load(steam_path / "steamclient64.dll"); // and this one too
::utils::nt::library::load(steam_path / "tier0_s64.dll");
::utils::nt::library::load(steam_path / "vstdlib_s64.dll");
::utils::nt::library::load(steam_path / "gameoverlayrenderer64.dll");
::utils::nt::library::load(steam_path / "steamclient64.dll");
return true; return true;
} }
@ -149,6 +208,8 @@ namespace steam
callbacks::unregister_callback(handler); callbacks::unregister_callback(handler);
} }
// all this additional work I did just for the user to manually select the file may not be needed (Proton recognizes Steam registry)
// or even work half the time. because of that, we may need to revert to the old file dialog too :P -mikey
const char* SteamAPI_GetSteamInstallPath() const char* SteamAPI_GetSteamInstallPath()
{ {
static std::string install_path{}; static std::string install_path{};
@ -157,24 +218,74 @@ namespace steam
return install_path.data(); return install_path.data();
} }
HKEY reg_key;
if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, "Software\\WOW6432Node\\Valve\\Steam", 0, KEY_QUERY_VALUE,
&reg_key) ==
ERROR_SUCCESS)
{
char path[MAX_PATH] = {0}; char path[MAX_PATH] = {0};
DWORD length = sizeof(path); DWORD length = sizeof(path);
HKEY reg_key;
// check if Steam contains information in registry for the install path
if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, "Software\\WOW6432Node\\Valve\\Steam", 0, KEY_QUERY_VALUE,
&reg_key) == ERROR_SUCCESS)
{
RegQueryValueExA(reg_key, "InstallPath", nullptr, nullptr, reinterpret_cast<BYTE*>(path), RegQueryValueExA(reg_key, "InstallPath", nullptr, nullptr, reinterpret_cast<BYTE*>(path),
&length); &length);
RegCloseKey(reg_key); RegCloseKey(reg_key);
install_path = path; install_path = path;
} }
else
{
// if we can't find Steam in the registry, let's check if we are on Wine or not.
// if the user is using Steam's Proton, a fork of Wine, then this code won't even be hit as it works above
// however, on [other] Wine [forks], this should be hit. also, the file dialog may not work.. :P
HKEY steam_install_reg;
if (arxan::is_wine())
{
// let's check the registry to see if the user has already manually selected the Steam installation path
if (RegOpenKeyExA(HKEY_CURRENT_USER, "Software\\h1-mod", 0, KEY_QUERY_VALUE, &steam_install_reg)
!= ERROR_SUCCESS)
{
// create a registry key
if (RegCreateKeyExA(HKEY_CURRENT_USER, "Software\\h1-mod", 0, nullptr, 0, KEY_WRITE, nullptr, &steam_install_reg, nullptr)
!= ERROR_SUCCESS)
{
MSG_BOX_ERROR("Could not create registry for Steam install path.");
return "";
}
// create a pointer to our path variable to use in our open_folder function
char* directory_ptr = path;
// open a file explorer prompt to find the Steam directory (user input)
open_folder_prompt(directory_ptr);
while (!strcmp(directory_ptr, "")) // if this while statement goes, this means that the operation was cancelled
{
MSG_BOX_ERROR("You must select a valid Steam directory before you can continue.");
open_folder_prompt(directory_ptr);
}
// if the directory pointer is defined, then we set "steam_install" inside "Software\\h1-mod" to the path
if (RegSetKeyValueA(steam_install_reg, nullptr, "steam_install", REG_SZ, ::utils::string::va("\"%s\"", path), length) != ERROR_SUCCESS)
{
MSG_BOX_ERROR("Failed to set valid Steam install path in registry, please try again.");
return "";
}
}
// query "steam_install" inside "Software\\h1-mod" which will define our path variable
RegQueryValueExA(steam_install_reg, "steam_install", nullptr, nullptr, reinterpret_cast<BYTE*>(path), &length);
install_path = path;
RegCloseKey(steam_install_reg);
}
else
{
MSG_BOX_ERROR("Failed to find a Steam installation.");
}
}
return install_path.data(); return install_path.data();
} }
bool SteamGameServer_Init() bool SteamGameServer_Init()
{ {
return true; return true;