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 "game/game.hpp"
|
||||
|
||||
#include "arxan.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
|
||||
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
|
||||
{
|
||||
public:
|
||||
@ -137,9 +155,12 @@ namespace arxan
|
||||
void post_unpack() override
|
||||
{
|
||||
// cba to implement sp, not sure if it's even needed
|
||||
if (game::environment::is_sp()) return;
|
||||
if (game::environment::is_sp())
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
REGISTER_COMPONENT(arxan::component)
|
||||
REGISTER_COMPONENT(arxan::component)
|
||||
|
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 "scheduler.hpp"
|
||||
|
||||
#include "arxan.hpp"
|
||||
|
||||
#include <utils/nt.hpp>
|
||||
#include <utils/flags.hpp>
|
||||
#include <utils/string.hpp>
|
||||
@ -102,7 +104,10 @@ namespace steam_proxy
|
||||
void load_client()
|
||||
{
|
||||
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 / "vstdlib_s64.dll");
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include <std_include.hpp>
|
||||
#include "loader/component_loader.hpp"
|
||||
#include "system_check.hpp"
|
||||
#include "arxan.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));
|
||||
}
|
||||
|
||||
// need to update these values
|
||||
void verify_binary_version()
|
||||
{
|
||||
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 "game/game.hpp"
|
||||
|
||||
#include "component/arxan.hpp"
|
||||
|
||||
#include <utils/string.hpp>
|
||||
#include <utils/flags.hpp>
|
||||
#include <utils/io.hpp>
|
||||
@ -202,11 +204,16 @@ int main()
|
||||
ShowWindow(GetConsoleWindow(), SW_HIDE);
|
||||
|
||||
FARPROC entry_point;
|
||||
enable_dpi_awareness();
|
||||
|
||||
// This requires admin privilege, but I suppose many
|
||||
// people will start with admin rights if it crashes.
|
||||
limit_parallel_dll_loading();
|
||||
// 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();
|
||||
|
||||
// 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)));
|
||||
remove_crash_file();
|
||||
|
@ -1,10 +1,65 @@
|
||||
#include <std_include.hpp>
|
||||
#include "steam.hpp"
|
||||
|
||||
#include "component/arxan.hpp"
|
||||
#include "component/console.hpp"
|
||||
|
||||
#include <utils/nt.hpp>
|
||||
#include <utils/string.hpp>
|
||||
|
||||
#define GENERIC_RETURN_IF_FAIL(condition) \
|
||||
if (condition != S_OK) \
|
||||
{ \
|
||||
return; \
|
||||
}
|
||||
|
||||
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;
|
||||
std::recursive_mutex callbacks::mutex_;
|
||||
std::map<uint64_t, bool> callbacks::calls_;
|
||||
@ -111,12 +166,16 @@ namespace steam
|
||||
bool SteamAPI_Init()
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
@ -149,6 +208,8 @@ namespace steam
|
||||
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()
|
||||
{
|
||||
static std::string install_path{};
|
||||
@ -157,24 +218,74 @@ namespace steam
|
||||
return install_path.data();
|
||||
}
|
||||
|
||||
char path[MAX_PATH] = {0};
|
||||
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,
|
||||
®_key) ==
|
||||
ERROR_SUCCESS)
|
||||
®_key) == ERROR_SUCCESS)
|
||||
{
|
||||
char path[MAX_PATH] = {0};
|
||||
DWORD length = sizeof(path);
|
||||
RegQueryValueExA(reg_key, "InstallPath", nullptr, nullptr, reinterpret_cast<BYTE*>(path),
|
||||
&length);
|
||||
RegCloseKey(reg_key);
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
|
||||
bool SteamGameServer_Init()
|
||||
{
|
||||
return true;
|
||||
@ -244,4 +355,4 @@ namespace steam
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user