Wine compatibility and patches (#219)
Co-authored-by: Skull <86374920+skkuull@users.noreply.github.com>
This commit is contained in:
parent
902fb83c5c
commit
f7c5b6506a
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
6
src/client/component/arxan.hpp
Normal file
6
src/client/component/arxan.hpp
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace arxan
|
||||||
|
{
|
||||||
|
bool is_wine();
|
||||||
|
}
|
@ -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");
|
||||||
|
@ -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,14 +65,26 @@ 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 (value != 0x60202B6A && value != 0xBC0E9FE)
|
|
||||||
|
if (arxan::is_wine())
|
||||||
{
|
{
|
||||||
throw std::runtime_error("Unsupported Call of Duty: Modern Warfare Remastered version (1.15)");
|
if (value != 0xFFB81262 && value != 0xFFB81143)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (value != 0x60202B6A && value != 0xBC0E9FE)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw std::runtime_error("Unsupported Call of Duty: Modern Warfare Remastered version (1.15)");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
enable_dpi_awareness();
|
|
||||||
|
|
||||||
// This requires admin privilege, but I suppose many
|
// leaving these for Windows only for now, need to test to see if we can have for Wine -mikey
|
||||||
// people will start with admin rights if it crashes.
|
if (!arxan::is_wine())
|
||||||
limit_parallel_dll_loading();
|
{
|
||||||
|
enable_dpi_awareness();
|
||||||
|
|
||||||
|
// This requires admin privilege, but I suppose many
|
||||||
|
// people will start with admin rights if it crashes.
|
||||||
|
limit_parallel_dll_loading();
|
||||||
|
}
|
||||||
|
|
||||||
srand(uint32_t(time(nullptr)));
|
srand(uint32_t(time(nullptr)));
|
||||||
remove_crash_file();
|
remove_crash_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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char path[MAX_PATH] = {0};
|
||||||
|
DWORD length = sizeof(path);
|
||||||
|
|
||||||
HKEY reg_key;
|
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,
|
if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, "Software\\WOW6432Node\\Valve\\Steam", 0, KEY_QUERY_VALUE,
|
||||||
®_key) ==
|
®_key) == ERROR_SUCCESS)
|
||||||
ERROR_SUCCESS)
|
|
||||||
{
|
{
|
||||||
char path[MAX_PATH] = {0};
|
|
||||||
DWORD length = sizeof(path);
|
|
||||||
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;
|
||||||
|
Loading…
Reference in New Issue
Block a user