diff --git a/.gitea/workflows/check-formatting.yml b/.gitea/workflows/check-formatting.yml new file mode 100644 index 0000000..9dcfb29 --- /dev/null +++ b/.gitea/workflows/check-formatting.yml @@ -0,0 +1,33 @@ +name: check-formatting + +on: + push: + branches: + - "*" + pull_request: + branches: + - "*" + types: [opened, synchronize, reopened] + +jobs: + check-formatting: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Install LLVM + uses: KyleMayes/install-llvm-action@v2.0.5 + with: + version: "17.0" + + - name: Install dependencies (x64) + run: | + apt-get update + apt-get install libtinfo5 -y + + - name: Test formatting for all files + working-directory: ${{ github.workspace }} + run: | + export CLANG_FORMAT_BIN="${LLVM_PATH}/bin/clang-format" + ./scripts/check-format.sh diff --git a/scripts/check-format.sh b/scripts/check-format.sh new file mode 100755 index 0000000..349d7b3 --- /dev/null +++ b/scripts/check-format.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +# Go to repository root +cd "$(dirname "$0")/.." || exit 2 + +CLANG_FORMAT_BIN="${CLANG_FORMAT_BIN:-clang-format}" + +find ./src -iname '*.hpp' -o -iname '*.cpp' | xargs $CLANG_FORMAT_BIN -Werror -ferror-limit=1 --dry-run diff --git a/src/client/component/steam_proxy.cpp b/src/client/component/steam_proxy.cpp index c46c03f..36843a8 100644 --- a/src/client/component/steam_proxy.cpp +++ b/src/client/component/steam_proxy.cpp @@ -12,8 +12,19 @@ #include "scheduler.hpp" +#include + namespace { -utils::binary_resource runner_file(RUNNER, "iw4sp-runner.exe"); +enum class ownership_state { + success, + unowned, + nosteam, + error, +}; + +ownership_state state_; + +utils::binary_resource runner_file(RUNNER, xorstr_("iw4sp-runner.exe")); } // namespace class steam_proxy final : public component_interface { @@ -23,22 +34,35 @@ public: this->clean_up_on_error(); try { - this->start_mod("iw4x-sp singleplayer", 10180); + state_ = this->start_mod(xorstr_("iw4x-sp singleplayer"), 10180); } catch (const std::exception& ex) { - printf("Steam: %s\n", ex.what()); + state_ = ownership_state::error; + printf(xorstr_("Steam: %s\n"), ex.what()); } + +#ifdef STEAM_OWNERSHIP_CHECK + if (utils::nt::is_wine()) { + state_ = ownership_state::success; + return; + } +#endif + +#ifdef STEAM_OWNERSHIP_CHECK + evaluate_ownership_state(state_); +#endif } void pre_destroy() override { if (this->steam_client_module_) { if (this->steam_pipe_) { if (this->global_user_) { - this->steam_client_module_.invoke( - "Steam_ReleaseUser", this->steam_pipe_, this->global_user_); + this->steam_client_module_.invoke(xorstr_("Steam_ReleaseUser"), + this->steam_pipe_, + this->global_user_); } - this->steam_client_module_.invoke("Steam_BReleaseSteamPipe", - this->steam_pipe_); + this->steam_client_module_.invoke( + xorstr_("Steam_BReleaseSteamPipe"), this->steam_pipe_); } } } @@ -59,10 +83,10 @@ private: for (auto i = 1; i < 1000; ++i) { const auto* name = - utils::string::va("CLIENTENGINE_INTERFACE_VERSION{0:03}", i); + utils::string::va(xorstr_("CLIENTENGINE_INTERFACE_VERSION{0:03}"), i); auto* const client_engine = this->steam_client_module_.invoke( - "CreateInterface", name, nullptr); + xorstr_("CreateInterface"), name, nullptr); if (client_engine) return client_engine; } @@ -75,10 +99,10 @@ private: if (steam_path.empty()) return; - utils::nt::library::load(steam_path / "tier0_s.dll"); - utils::nt::library::load(steam_path / "vstdlib_s.dll"); + utils::nt::library::load(steam_path / xorstr_("tier0_s.dll")); + utils::nt::library::load(steam_path / xorstr_("vstdlib_s.dll")); this->steam_client_module_ = - utils::nt::library::load(steam_path / "steamclient.dll"); + utils::nt::library::load(steam_path / xorstr_("steamclient.dll")); if (!this->steam_client_module_) return; @@ -86,52 +110,62 @@ private: if (!this->client_engine_) return; - this->steam_pipe_ = - this->steam_client_module_.invoke("Steam_CreateSteamPipe"); + this->steam_pipe_ = this->steam_client_module_.invoke( + xorstr_("Steam_CreateSteamPipe")); this->global_user_ = this->steam_client_module_.invoke( - "Steam_ConnectToGlobalUser", this->steam_pipe_); + xorstr_("Steam_ConnectToGlobalUser"), this->steam_pipe_); this->client_user_ = this->client_engine_.invoke( 8, this->steam_pipe_, this->global_user_); // GetIClientUser this->client_utils_ = this->client_engine_.invoke( 14, this->steam_pipe_); // GetIClientUtils } - void start_mod(const std::string& title, const std::size_t app_id) { + ownership_state start_mod(const std::string& title, + const std::size_t app_id) { __try { - this->start_mod_unsafe(title, app_id); + return this->start_mod_unsafe(title, app_id); } __except (EXCEPTION_EXECUTE_HANDLER) { this->do_cleanup(); + return ownership_state::error; } } - void start_mod_unsafe(const std::string& title, std::size_t app_id) { + ownership_state start_mod_unsafe(const std::string& title, + std::size_t app_id) { if (!this->client_utils_ || !this->client_user_) - return; + return ownership_state::nosteam; - if (!this->client_user_.invoke("BIsSubscribedApp", app_id)) { + if (!this->client_user_.invoke(xorstr_("BIsSubscribedApp"), app_id)) { +#ifdef _DEBUG app_id = 480; // Spacewar +#else + return ownership_state::unowned; +#endif } - this->client_utils_.invoke("SetAppIDForCurrentPipe", app_id, false); + this->client_utils_.invoke(xorstr_("SetAppIDForCurrentPipe"), app_id, + false); char our_directory[MAX_PATH]{}; GetCurrentDirectoryA(sizeof(our_directory), our_directory); const auto path = runner_file.get_extracted_file(); - const auto* cmd_line = - utils::string::va("\"{0}\" -proc {1}", path, GetCurrentProcessId()); + const auto* cmd_line = utils::string::va(xorstr_("\"{0}\" -proc {1}"), path, + GetCurrentProcessId()); game_id game_id; game_id.raw.type = 1; // k_EGameIDTypeGameMod game_id.raw.app_id = app_id & 0xFFFFFF; - const auto* mod_id = "IW4"; + const auto* mod_id = "IW4x-SP"; game_id.raw.mod_id = *reinterpret_cast(mod_id) | 0x80000000; - this->client_user_.invoke("SpawnProcess", path.data(), cmd_line, - our_directory, game_id.bits, title.data(), - app_id, 0, 0, 0); + this->client_user_.invoke(xorstr_("SpawnProcess"), path.c_str(), + cmd_line, our_directory, game_id.bits, + title.c_str(), app_id, 0, 0, 0); + + return ownership_state::success; } void do_cleanup() { @@ -151,9 +185,11 @@ private: if (this->steam_client_module_ && this->steam_pipe_ && this->global_user_ && this->steam_client_module_.invoke( - "Steam_BConnected", this->global_user_, this->steam_pipe_) && + xorstr_("Steam_BConnected"), this->global_user_, + this->steam_pipe_) && this->steam_client_module_.invoke( - "Steam_BLoggedOn", this->global_user_, this->steam_pipe_)) { + xorstr_("Steam_BLoggedOn"), this->global_user_, + this->steam_pipe_)) { return scheduler::cond_continue; } @@ -162,6 +198,24 @@ private: }, scheduler::pipeline::async); } + +#ifdef STEAM_OWNERSHIP_CHECK + void evaluate_ownership_state(const ownership_state state) { + switch (state) { + case ownership_state::nosteam: + throw std::runtime_error( + xorstr_("Steam must be running to play this game!")); + case ownership_state::unowned: + throw std::runtime_error( + xorstr_("You must own the game on Steam to play this mod!")); + case ownership_state::error: + throw std::runtime_error( + xorstr_("Failed to verify ownership of the game!")); + case ownership_state::success: + break; + } + } +#endif }; REGISTER_COMPONENT(steam_proxy) diff --git a/src/common/utils/nt.cpp b/src/common/utils/nt.cpp index c6fb986..4125a6a 100644 --- a/src/common/utils/nt.cpp +++ b/src/common/utils/nt.cpp @@ -1,5 +1,7 @@ #include "nt.hpp" +#include + namespace utils::nt { library library::load(const std::string& name) { return library(LoadLibraryA(name.c_str())); @@ -206,8 +208,8 @@ std::string library::get_dll_directory() { bool is_wine() { static const auto has_wine_export = []() -> bool { - const library ntdll("ntdll.dll"); - return ntdll.get_proc("wine_get_version"); + const library ntdll(xorstr_("ntdll.dll")); + return ntdll.get_proc(xorstr_("wine_get_version")); }(); return has_wine_export;