fix many issues with this repo
All checks were successful
check-formatting / check-formatting (push) Successful in 4m8s

This commit is contained in:
6arelyFuture 2025-04-11 10:41:28 +02:00
parent 89a735326d
commit eb318d32c5
Signed by: Future
GPG Key ID: F2000F181A4F7C85
44 changed files with 1107 additions and 243 deletions

View File

@ -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

2
deps/GSL vendored

@ -1 +1 @@
Subproject commit e64c97fc2cfc11992098bb38eda932de275e3f4d Subproject commit 3325bbd33d24d1f8f5a0f69e782c92ad5a39a68e

2
deps/libtomcrypt vendored

@ -1 +1 @@
Subproject commit f7e6519fae1e11ff5ff9d36c84101a673002133b Subproject commit a6b9aff7aab857fe1b491710a5c5b9e2be49cb08

2
deps/libtommath vendored

@ -1 +1 @@
Subproject commit 5809141a3a6ec1bf3443c927c02b955e19224016 Subproject commit e823b0c34cea291bdb94d672731e1c1f08525557

2
deps/minhook vendored

@ -1 +1 @@
Subproject commit 1cc46107ee522d7a5c73656c519ca16addf2c23a Subproject commit c3fcafdc10146beb5919319d0683e44e3c30d537

2
deps/rapidjson vendored

@ -1 +1 @@
Subproject commit ab1842a2dae061284c0a62dca1cc6d5e7e37e346 Subproject commit 24b5e7a8b27f42fa16b96fc70aade9106cf7102f

2
deps/zlib vendored

@ -1 +1 @@
Subproject commit 0f51fb4933fc9ce18199cb2554dacea8033e7fd3 Subproject commit 5a82f71ed1dfc0bec044d9702463dbdf84ea3b71

View File

@ -1,4 +0,0 @@
@echo off
echo Updating submodules...
call git submodule update --init --recursive
call tools\premake5 %* vs2022

8
scripts/check-format.sh Executable file
View File

@ -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

14
scripts/format.sh Normal file
View File

@ -0,0 +1,14 @@
#!/bin/bash
# Navigate to the repository root
cd "$(dirname "$0")/.." || exit 2
# Set the clang-format binary (defaults to 'clang-format')
CLANG_FORMAT_BIN="${CLANG_FORMAT_BIN:-clang-format}"
# Find and format all .hpp and .cpp files in the src directory
find ./src \( -iname '*.hpp' -o -iname '*.cpp' \) -print0 |
xargs -0 "$CLANG_FORMAT_BIN" -i || {
echo "Error: clang-format failed." >&2
exit 1
}

View File

@ -3,6 +3,9 @@
#include <utils/cryptography.hpp> #include <utils/cryptography.hpp>
#include <utils/hook.hpp> #include <utils/hook.hpp>
#include <utils/io.hpp>
#include <utils/nt.hpp>
#include <utils/properties.hpp>
#include <utils/smbios.hpp> #include <utils/smbios.hpp>
#include <utils/string.hpp> #include <utils/string.hpp>
@ -62,15 +65,94 @@ std::string get_key_entropy() {
return entropy; return entropy;
} }
utils::cryptography::ecc::key& get_key() { bool load_key(utils::cryptography::ecc::key& key) {
static auto key = std::string data{};
utils::cryptography::ecc::generate_key(512, get_key_entropy());
auto key_path = (utils::properties::get_appdata_path() / "iw4xsp-private.key")
.generic_string();
if (!utils::io::read_file(key_path, &data)) {
return false;
}
key.deserialize(data);
if (!key.is_valid()) {
game::Com_PrintError(game::CON_CHANNEL_ERROR, "Loaded key is invalid!\n");
return false;
}
return true;
}
utils::cryptography::ecc::key generate_key() {
auto key = utils::cryptography::ecc::generate_key(512, get_key_entropy());
if (!key.is_valid()) {
throw std::runtime_error("Failed to generate cryptographic key!");
}
auto key_path = (utils::properties::get_appdata_path() / "iw4xsp-private.key")
.generic_string();
if (!utils::io::write_file(key_path, key.serialize())) {
game::Com_PrintError(game::CON_CHANNEL_ERROR,
"Failed to write cryptographic key!\n");
}
game::Com_PrintError(game::CON_CHANNEL_ERROR,
"Generated cryptographic key: %llX\n", key.get_hash());
return key; return key;
} }
utils::cryptography::ecc::key load_or_generate_key() {
utils::cryptography::ecc::key key{};
if (load_key(key)) {
game::Com_PrintError(game::CON_CHANNEL_ERROR,
"Loaded cryptographic key: %llX\n", key.get_hash());
return key;
}
return generate_key();
}
utils::cryptography::ecc::key get_key_internal() {
auto key = load_or_generate_key();
auto key_path = (utils::properties::get_appdata_path() / "iw4xsp-public.key")
.generic_string();
if (!utils::io::write_file(key_path, key.get_public_key())) {
game::Com_PrintError(game::CON_CHANNEL_ERROR,
"Failed to write public key!\n");
}
return key;
}
const utils::cryptography::ecc::key& get_key() {
static auto key = get_key_internal();
return key;
}
bool is_second_instance() {
static const auto is_first = []() -> bool {
static utils::nt::handle mutex =
CreateMutexA(nullptr, FALSE, "iw4xsp_mutex");
return mutex && GetLastError() != ERROR_ALREADY_EXISTS;
}();
return !is_first;
}
} // namespace } // namespace
std::uint64_t get_guid() { return get_key().get_hash(); } std::uint64_t get_guid() {
static const auto guid = []() -> std::uint64_t {
if (is_second_instance()) {
return 0x110000100000000 |
(::utils::cryptography::random::get_integer() & ~0x80000000);
}
return get_key().get_hash();
}();
return guid;
}
class component final : public component_interface { class component final : public component_interface {
public: public:

View File

@ -22,7 +22,7 @@ game::define_s* define_from_string(const char* string) {
string, static_cast<int>(std::strlen(string)), "*extern"); string, static_cast<int>(std::strlen(string)), "*extern");
// create a new source // create a new source
std::memset(&src, 0, sizeof(game::source_s)); std::memset(&src, 0, sizeof(game::source_s));
strncpy_s(src.filename, "*extern", _TRUNCATE); game::I_strncpyz(src.filename, "*extern", sizeof(src.filename));
src.scriptstack = script; src.scriptstack = script;
src.definehash = static_cast<game::define_s**>( src.definehash = static_cast<game::define_s**>(
@ -160,7 +160,7 @@ game::source_s* load_source_file(const char* filename) {
static_cast<game::source_s*>(game::GetMemory(sizeof(game::source_s))); static_cast<game::source_s*>(game::GetMemory(sizeof(game::source_s)));
std::memset(source, 0, sizeof(game::source_s)); std::memset(source, 0, sizeof(game::source_s));
strncpy_s(source->filename, filename, _TRUNCATE); game::I_strncpyz(source->filename, filename, sizeof(source->filename));
source->scriptstack = script; source->scriptstack = script;
source->tokens = nullptr; source->tokens = nullptr;
source->defines = nullptr; source->defines = nullptr;

View File

@ -130,10 +130,10 @@ void set_script_punctuations(game::script_s* script) {
} }
game::script_s* load_script_file(const char* filename) { game::script_s* load_script_file(const char* filename) {
int fp; int fp{};
char pathname[game::MAX_QPATH]; char pathname[game::MAX_QPATH];
sprintf_s(pathname, "%s", filename); game::Com_sprintf(pathname, sizeof(pathname), "%s", filename);
const auto length = game::FS_FOpenFileRead(pathname, &fp); const auto length = game::FS_FOpenFileRead(pathname, &fp);
if (!fp) { if (!fp) {
return nullptr; return nullptr;
@ -141,7 +141,7 @@ game::script_s* load_script_file(const char* filename) {
auto* buffer = game::GetClearedMemory(sizeof(game::script_s) + length + 1); auto* buffer = game::GetClearedMemory(sizeof(game::script_s) + length + 1);
auto* script = static_cast<game::script_s*>(buffer); auto* script = static_cast<game::script_s*>(buffer);
strncpy_s(script->filename, filename, _TRUNCATE); game::I_strncpyz(script->filename, filename, sizeof(script->filename));
script->buffer = static_cast<char*>(buffer) + sizeof(game::script_s); script->buffer = static_cast<char*>(buffer) + sizeof(game::script_s);
script->buffer[length] = '\0'; script->buffer[length] = '\0';
@ -172,7 +172,7 @@ game::script_s* load_script_memory(const char* ptr, int length,
auto* buffer = game::GetClearedMemory(sizeof(game::script_s) + length + 1); auto* buffer = game::GetClearedMemory(sizeof(game::script_s) + length + 1);
auto* script = static_cast<game::script_s*>(buffer); auto* script = static_cast<game::script_s*>(buffer);
strncpy_s(script->filename, name, _TRUNCATE); game::I_strncpyz(script->filename, name, sizeof(script->filename));
script->buffer = static_cast<char*>(buffer) + sizeof(game::script_s); script->buffer = static_cast<char*>(buffer) + sizeof(game::script_s);
script->buffer[length] = '\0'; script->buffer[length] = '\0';
script->length = length; script->length = length;

View File

@ -2,6 +2,7 @@
#include "loader/component_loader.hpp" #include "loader/component_loader.hpp"
#include "game/dvars.hpp" #include "game/dvars.hpp"
#include "branding.hpp"
#include "scheduler.hpp" #include "scheduler.hpp"
#include <utils/hook.hpp> #include <utils/hook.hpp>
@ -17,10 +18,6 @@ constexpr auto* BUILD_TYPE = "IW4x_DEV SP";
constexpr auto* BUILD_TYPE = "IW4x SP"; constexpr auto* BUILD_TYPE = "IW4x SP";
#endif #endif
constexpr const char* get_build_number() {
return SHORTVERSION " latest " __DATE__ " " __TIME__;
}
const char* get_version_string() { const char* get_version_string() {
const auto* result = utils::string::va( const auto* result = utils::string::va(
"{0} {1} build {2} {3}", BUILD_TYPE, "(Alpha)", get_build_number(), "{0} {1} build {2} {3}", BUILD_TYPE, "(Alpha)", get_build_number(),
@ -77,6 +74,7 @@ void cg_draw_full_screen_debug_overlays_stub(int local_client_num) {
utils::hook::invoke<void>(0x44BD00, local_client_num); utils::hook::invoke<void>(0x44BD00, local_client_num);
} }
volatile bool left_side = true;
game::Font_s** small_font; game::Font_s** small_font;
void branding_loop() { void branding_loop() {
float color[4] = {1.0f, 1.0f, 1.0f, 0.25f}; float color[4] = {1.0f, 1.0f, 1.0f, 0.25f};
@ -88,15 +86,22 @@ void branding_loop() {
auto* const scr_place = auto* const scr_place =
game::ScrPlace_GetActivePlacement(game::LOCAL_CLIENT_0); game::ScrPlace_GetActivePlacement(game::LOCAL_CLIENT_0);
const auto x = scr_place->realViewportSize[0] - const auto x =
static_cast<float>(game::R_TextWidth(text, 0, *small_font)) - left_side
10.0f; ? 10.0f
: scr_place->realViewportSize[0] -
static_cast<float>(game::R_TextWidth(text, 0, *small_font)) -
10.0f;
game::R_AddCmdDrawText(text, std::numeric_limits<int>::max(), *small_font, x, game::R_AddCmdDrawText(text, std::numeric_limits<int>::max(), *small_font, x,
30.0f, 1.0f, 1.0f, 0.0f, color, 3); 30.0f, 1.0f, 1.0f, 0.0f, color, 3);
} }
} // namespace } // namespace
const char* get_build_number() {
return SHORTVERSION " latest " __DATE__ " " __TIME__;
}
class component final : public component_interface { class component final : public component_interface {
public: public:
void post_load() override { void post_load() override {
@ -122,6 +127,8 @@ public:
small_font = reinterpret_cast<game::Font_s**>(0x192A0DC); small_font = reinterpret_cast<game::Font_s**>(0x192A0DC);
scheduler::loop(branding_loop, scheduler::pipeline::renderer); scheduler::loop(branding_loop, scheduler::pipeline::renderer);
scheduler::loop([] { left_side = !left_side; },
scheduler::pipeline::renderer, 60s);
} }
static void register_branding_dvars() { static void register_branding_dvars() {

View File

@ -0,0 +1,5 @@
#pragma once
namespace branding {
const char* get_build_number();
}

View File

@ -2,7 +2,6 @@
#include "loader/component_loader.hpp" #include "loader/component_loader.hpp"
#include "game/dvars.hpp" #include "game/dvars.hpp"
#include "game/engine/scoped_critical_section.hpp" #include "game/engine/scoped_critical_section.hpp"
#include "game/engine/large_local.hpp"
#include <utils/hook.hpp> #include <utils/hook.hpp>
@ -14,14 +13,15 @@ namespace {
void com_assert_f() { assert("a" && false); } void com_assert_f() { assert("a" && false); }
void com_bug_f(const command::params& params) { void com_bug_f(const command::params& params) {
char new_file_name[0x105]{}; char new_file_name[MAX_PATH]{};
char to_ospath[MAX_PATH]{}; char to_ospath[MAX_PATH]{};
char from_ospath[MAX_PATH]{}; char from_ospath[MAX_PATH]{};
const char* bug; const char* bug{};
if (!*game::logfile) { if (!*game::logfile) {
game::Com_PrintError(game::CON_CHANNEL_ERROR, game::Com_PrintError(game::CON_CHANNEL_ERROR,
"CopyFile failed: logfile wasn't opened\n"); "CopyFile failed: logfile wasn't opened\n");
return;
} }
if (params.size() == 2) { if (params.size() == 2) {
@ -31,10 +31,11 @@ void com_bug_f(const command::params& params) {
bug = dvars::bug_name->current.string; bug = dvars::bug_name->current.string;
} }
sprintf_s(new_file_name, "%s_%s.log", bug, game::Live_GetLocalClientName(0)); game::Com_sprintf(new_file_name, sizeof(new_file_name), "%s_%s.log", bug,
game::Live_GetLocalClientName(game::CONTROLLER_INDEX_0));
game::engine::scoped_critical_section _(game::CRITSECT_CONSOLE, game::engine::scoped_critical_section lock(game::CRITSECT_CONSOLE,
game::SCOPED_CRITSECT_NORMAL); game::SCOPED_CRITSECT_NORMAL);
if (*game::logfile) { if (*game::logfile) {
game::FS_FCloseFile(*game::logfile); game::FS_FCloseFile(*game::logfile);
@ -48,6 +49,8 @@ void com_bug_f(const command::params& params) {
const auto result = CopyFileA(from_ospath, to_ospath, 0); const auto result = CopyFileA(from_ospath, to_ospath, 0);
game::Com_OpenLogFile(); game::Com_OpenLogFile();
lock.leave_crit_sect();
if (!result) { if (!result) {
game::Com_PrintError(game::CON_CHANNEL_ERROR, "CopyFile failed(%d) %s %s\n", game::Com_PrintError(game::CON_CHANNEL_ERROR, "CopyFile failed(%d) %s %s\n",
GetLastError(), "console.log", new_file_name); GetLastError(), "console.log", new_file_name);
@ -55,7 +58,7 @@ void com_bug_f(const command::params& params) {
} }
void com_bug_name_inc_f() { void com_bug_name_inc_f() {
char buf[260]{}; char buf[MAX_PATH]{};
if (std::strlen(dvars::bug_name->current.string) < 4) { if (std::strlen(dvars::bug_name->current.string) < 4) {
game::Dvar_SetString(dvars::bug_name, "bug0"); game::Dvar_SetString(dvars::bug_name, "bug0");
@ -68,15 +71,15 @@ void com_bug_name_inc_f() {
} }
const auto n = std::strtol(dvars::bug_name->current.string + 3, nullptr, 10); const auto n = std::strtol(dvars::bug_name->current.string + 3, nullptr, 10);
sprintf_s(buf, "bug%d", n + 1); game::Com_sprintf(buf, sizeof(buf), "bug%d", n + 1);
game::Dvar_SetString(dvars::bug_name, buf); game::Dvar_SetString(dvars::bug_name, buf);
} }
void g_print_fast_file_errors(const char* fastfile) { void g_print_fast_file_errors(const char* fastfile) {
assert(fastfile); assert(fastfile);
game::engine::large_local rawfile_buf_large_local(0x18000); const auto rawfile_buf_large = std::make_unique<char[]>(0x18000);
auto* rawfile_buf = static_cast<char*>(rawfile_buf_large_local.get_buf()); auto* rawfile_buf = rawfile_buf_large.get();
auto* text = game::DB_ReadRawFile(fastfile, rawfile_buf, 0x18000); auto* text = game::DB_ReadRawFile(fastfile, rawfile_buf, 0x18000);
@ -87,6 +90,10 @@ void g_print_fast_file_errors(const char* fastfile) {
"There were errors when building fast file '%s'\n", "There were errors when building fast file '%s'\n",
fastfile); fastfile);
game::Com_PrintError(game::CON_CHANNEL_ERROR, "%s", text); game::Com_PrintError(game::CON_CHANNEL_ERROR, "%s", text);
} else {
game::Com_Printf(game::CON_CHANNEL_DONT_FILTER,
"There were no errors when building fast file '%s'\n",
fastfile);
} }
} }

View File

@ -22,7 +22,11 @@ public:
dvar::override::register_bool("intro", false, game::DVAR_ARCHIVE); dvar::override::register_bool("intro", false, game::DVAR_ARCHIVE);
dvar::override::register_float("cg_fov", 65.0f, 65.0f, 160.0f, dvar::override::register_float("cg_fov", 65.0f, 65.0f, 160.0f,
game::DVAR_ARCHIVE | game::DVAR_SAVED); game::DVAR_ARCHIVE | game::DVAR_SAVED);
dvar::override::register_float("cg_fovScale", 1.0f, 0.2f, 2.0f,
game::DVAR_ARCHIVE | game::DVAR_SAVED);
dvar::override::register_string("fs_basegame", BASEGAME, game::DVAR_INIT); dvar::override::register_string("fs_basegame", BASEGAME, game::DVAR_INIT);
dvar::override::register_bool("com_filter_output", true,
game::DVAR_ARCHIVE);
#ifdef _DEBUG #ifdef _DEBUG
dvar::override::register_bool("sv_cheats", true, game::DVAR_NONE); dvar::override::register_bool("sv_cheats", true, game::DVAR_NONE);

View File

@ -11,27 +11,27 @@ bool is_using_mods() {
} }
void db_build_os_path_from_source(const char* zone_name, game::FF_DIR source, void db_build_os_path_from_source(const char* zone_name, game::FF_DIR source,
unsigned int size, char* filename) { const int size, char* filename) {
char user_map[game::MAX_QPATH]{}; char user_map[game::MAX_QPATH]{};
switch (source) { switch (source) {
case game::FFD_DEFAULT: case game::FFD_DEFAULT:
(void)sprintf_s(filename, size, "%s\\%s%s.ff", (void)game::Com_sprintf(filename, size, "%s\\%s%s.ff",
std::filesystem::current_path().string().c_str(), std::filesystem::current_path().string().c_str(),
game::Sys_GetMapZoneDir(zone_name), zone_name); game::Sys_GetMapZoneDir(zone_name), zone_name);
break; break;
case game::FFD_MOD_DIR: case game::FFD_MOD_DIR:
assert(is_using_mods()); assert(is_using_mods());
(void)sprintf_s(filename, size, "%s\\%s\\%s.ff", (void)game::Com_sprintf(filename, size, "%s\\%s\\%s.ff",
std::filesystem::current_path().string().c_str(), std::filesystem::current_path().string().c_str(),
(*dvars::fs_gameDirVar)->current.string, zone_name); (*dvars::fs_gameDirVar)->current.string, zone_name);
break; break;
case game::FFD_USER_MAP: case game::FFD_USER_MAP:
game::I_strncpyz(user_map, zone_name, sizeof(user_map)); game::I_strncpyz(user_map, zone_name, sizeof(user_map));
(void)sprintf_s(filename, size, "%s\\%s\\%s\\%s.ff", (void)game::Com_sprintf(filename, size, "%s\\%s\\%s\\%s.ff",
std::filesystem::current_path().string().c_str(), std::filesystem::current_path().string().c_str(),
"usermaps", user_map, zone_name); "usermaps", user_map, zone_name);
break; break;
default: default:
assert(false && "inconceivable"); assert(false && "inconceivable");

View File

@ -1,11 +1,13 @@
#include <std_include.hpp> #include <std_include.hpp>
#include "loader/component_loader.hpp" #include "loader/component_loader.hpp"
#include "game/game.hpp"
#include "game/dvars.hpp"
#include <utils/hook.hpp> #include <utils/hook.hpp>
#include <utils/string.hpp> #include <utils/string.hpp>
#include "game_module.hpp"
#include "filesystem.hpp" #include "filesystem.hpp"
#include "game_module.hpp"
namespace filesystem { namespace filesystem {
namespace { namespace {
@ -15,6 +17,56 @@ const char* sys_default_install_path_stub() {
} }
} // namespace } // namespace
bool file_wrapper_rotate(const char* ospath) {
constexpr auto MAX_BACKUPS = 20;
char renamed_path[game::MAX_OSPATH]{};
struct _stat64i32 stat_buf;
auto oldest_index = -1;
auto current_index = 0;
time_t oldest_time = 0;
// Check if the original file exists
if (_stat64i32(ospath, &stat_buf)) {
return true; // Return true if the file does not exist (no file to rotate)
}
for (; current_index < MAX_BACKUPS; ++current_index) {
(void)sprintf_s(renamed_path, "%s.%03i", ospath, current_index);
if (_stat64i32(renamed_path, &stat_buf)) {
break; // Stop if an available slot is found
}
if (oldest_index == -1 || stat_buf.st_mtime < oldest_time) {
oldest_time = stat_buf.st_mtime;
oldest_index = current_index;
}
}
if (current_index == MAX_BACKUPS) {
(void)game::Com_sprintf(renamed_path, sizeof(renamed_path), "%s.%03i",
ospath, oldest_index);
(void)std::remove(renamed_path); // Remove the oldest backup file
} else {
(void)game::Com_sprintf(renamed_path, sizeof(renamed_path), "%s.%03i",
ospath, current_index);
}
// Rename the original file to the selected backup slot
return std::rename(ospath, renamed_path) == 0;
}
bool file_rotate(const char* filename) {
char ospath[game::MAX_OSPATH]{};
const auto* basepath = (*dvars::fs_homepath)->current.string;
game::FS_BuildOSPath(basepath, reinterpret_cast<char*>(0x1956038), filename,
ospath);
return file_wrapper_rotate(ospath);
}
std::vector<std::string> vectored_file_list(const std::string& path, std::vector<std::string> vectored_file_list(const std::string& path,
const std::string& extension) { const std::string& extension) {
std::vector<std::string> file_list; std::vector<std::string> file_list;

View File

@ -1,6 +1,9 @@
#pragma once #pragma once
namespace filesystem { namespace filesystem {
bool file_wrapper_rotate(const char* ospath);
bool file_rotate(const char* filename);
std::vector<std::string> vectored_file_list(const std::string& path, std::vector<std::string> vectored_file_list(const std::string& path,
const std::string& extension); const std::string& extension);
std::string get_binary_directory(); std::string get_binary_directory();

View File

@ -0,0 +1,40 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "game/dvars.hpp"
#include <utils/hook.hpp>
namespace fov {
namespace {
void dvar_set_from_string_by_name_from_source(const char* dvar_name,
const char* string,
game::DvarSetSource source) {
if ((!std::strcmp(dvar_name, "cg_fov")) ||
(!std::strcmp(dvar_name, "cg_fovScale"))) {
game::Com_Printf(game::CON_CHANNEL_DONT_FILTER,
"Not allowing the client to override dvar '%s'\n",
dvar_name);
return;
}
game::Dvar_SetFromStringByNameFromSource(dvar_name, string, source);
}
} // namespace
class component final : public component_interface {
public:
void post_load() override {
utils::hook(0x5D7818, dvar_set_from_string_by_name_from_source,
HOOK_CALL)
.install() // hook*
->quick(); // GScr_SetSavedDvar
utils::hook(0x5CC8D9, dvar_set_from_string_by_name_from_source,
HOOK_CALL)
.install() // hook*
->quick(); // GScr_SetDvar_Internal
}
};
} // namespace fov
REGISTER_COMPONENT(fov::component)

View File

@ -0,0 +1,76 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "game/game.hpp"
#include "game/dvars.hpp"
#include "command.hpp"
#include <utils/hook.hpp>
namespace free_move {
namespace {
void cg_noclip_f() {
if (!game::cgArray[game::LOCAL_CLIENT_0].nextSnap) {
return;
}
if (!(*dvars::sv_cheats)->current.enabled) {
game::Com_Printf(game::CON_CHANNEL_SYSTEM, "%s is cheat protected.\n",
"cg_noclip");
return;
}
if ((*dvars::cl_freemove)->current.integer == 1) {
if ((*dvars::cg_paused)->current.integer == 2) {
game::Dvar_SetInt(*dvars::cg_paused, 1);
}
}
game::Dvar_SetInt(*dvars::cl_freemove,
(*dvars::cl_freemove)->current.integer == 1 ? 0 : 1);
game::Com_Printf(
game::CON_CHANNEL_DONT_FILTER, "%s\n",
game::SEH_LocalizeTextMessage((*dvars::cl_freemove)->current.integer == 1
? "GAME_NOCLIPON"
: "GAME_NOCLIPOFF",
"noclip print", game::LOCMSG_SAFE));
}
void cg_ufo_f() {
if (!game::cgArray[game::LOCAL_CLIENT_0].nextSnap) {
return;
}
if (!(*dvars::sv_cheats)->current.enabled) {
game::Com_Printf(game::CON_CHANNEL_SYSTEM, "%s is cheat protected.\n",
"cg_ufo");
return;
}
if ((*dvars::cl_freemove)->current.integer == 2) {
if ((*dvars::cg_paused)->current.integer == 2) {
game::Dvar_SetInt(*dvars::cg_paused, 1);
}
}
game::Dvar_SetInt(*dvars::cl_freemove,
(*dvars::cl_freemove)->current.integer == 2 ? 0 : 2);
game::Com_Printf(
game::CON_CHANNEL_DONT_FILTER, "%s\n",
game::SEH_LocalizeTextMessage((*dvars::cl_freemove)->current.integer == 2
? "GAME_UFOON"
: "GAME_UFOOFF",
"ufo print", game::LOCMSG_SAFE));
}
} // namespace
class component final : public component_interface {
public:
void post_load() override {
command::add("cg_noclip", cg_noclip_f);
command::add("cg_ufo", cg_ufo_f);
}
};
} // namespace free_move
REGISTER_COMPONENT(free_move::component)

View File

@ -28,7 +28,7 @@ void g_scr_log_print() {
break; break;
} }
strncat_s(string, psz_token, _TRUNCATE); game::I_strncat(string, sizeof(string), psz_token);
} }
log_printf("%s", string); log_printf("%s", string);
@ -83,12 +83,14 @@ void log_printf(const char* fmt, ...) {
} }
va_start(ap, fmt); va_start(ap, fmt);
vsnprintf_s(string2, _TRUNCATE, fmt, ap); vsnprintf(string2, sizeof(string2), fmt, ap);
va_end(ap); va_end(ap);
string2[sizeof(string2) - 1] = '\0';
const auto time = game::level->time / 1000; const auto time = game::level->time / 1000;
const auto len = sprintf_s(string, "%3i:%i%i %s", time / 60, time % 60 / 10, const auto len =
time % 60 % 10, string2); game::Com_sprintf(string, sizeof(string), "%3i:%i%i %s", time / 60,
time % 60 / 10, time % 60 % 10, string2);
game::FS_Write(string, len, log_file); game::FS_Write(string, len, log_file);
} }

View File

@ -364,7 +364,7 @@ void add_source_buffer_internal(const char* ext_filename, const char* code_pos,
auto* buf = static_cast<char*>(hunk::alloc_debug_mem( auto* buf = static_cast<char*>(hunk::alloc_debug_mem(
static_cast<int>(new_len))); // Scr_AddSourceBufferInternal static_cast<int>(new_len))); // Scr_AddSourceBufferInternal
strncpy_s(buf, new_len, ext_filename, _TRUNCATE); game::I_strncpyz(buf, ext_filename, static_cast<int>(new_len));
auto* source_buf2 = source_buf ? buf + str_len : nullptr; auto* source_buf2 = source_buf ? buf + str_len : nullptr;
auto* source = source_buf; auto* source = source_buf;
auto* dest = source_buf2; auto* dest = source_buf2;
@ -525,8 +525,9 @@ unsigned int load_script_internal_stub(const char* filename,
game::GetNewVariable(game::scrCompilePub->loadedscripts, name); game::GetNewVariable(game::scrCompilePub->loadedscripts, name);
game::SL_RemoveRefToString(name); game::SL_RemoveRefToString(name);
sprintf_s(ext_filename, "%s.gsc", game::Com_sprintf(
game::SL_ConvertToString(static_cast<unsigned short>(name))); ext_filename, sizeof(ext_filename), "%s.gsc",
game::SL_ConvertToString(static_cast<unsigned short>(name)));
const auto* old_source_buf = parser_pub_.sourceBuf; const auto* old_source_buf = parser_pub_.sourceBuf;
const auto* source_buffer = add_source_buffer( const auto* source_buffer = add_source_buffer(
@ -837,8 +838,9 @@ void compile_error(unsigned int source_pos, const char* msg, ...) {
va_list argptr; va_list argptr;
va_start(argptr, msg); va_start(argptr, msg);
vsnprintf_s(text, _TRUNCATE, msg, argptr); (void)vsnprintf(text, sizeof(text), msg, argptr);
va_end(argptr); va_end(argptr);
text[sizeof(text) - 1] = '\0';
if (game::scrVarPub->evaluate) { if (game::scrVarPub->evaluate) {
if (!game::scrVarPub->error_message) { if (!game::scrVarPub->error_message) {
@ -892,8 +894,9 @@ void compile_error2(const char* code_pos, const char* msg, ...) {
"******* script compile error *******\n"); "******* script compile error *******\n");
va_start(argptr, msg); va_start(argptr, msg);
vsnprintf_s(text, _TRUNCATE, msg, argptr); (void)vsnprintf(text, sizeof(text), msg, argptr);
va_end(argptr); va_end(argptr);
text[sizeof(text) - 1] = '\0';
game::Com_PrintError(game::CON_CHANNEL_PARSERSCRIPT, "%s: ", text); game::Com_PrintError(game::CON_CHANNEL_PARSERSCRIPT, "%s: ", text);

View File

@ -59,6 +59,11 @@ game::BuiltinMethod built_in_get_method_stub(const char** p_name, int* type) {
// If no method was found let's call BuiltIn_GetMethod // If no method was found let's call BuiltIn_GetMethod
return utils::hook::invoke<game::BuiltinMethod>(0x5DB850, p_name, type); return utils::hook::invoke<game::BuiltinMethod>(0x5DB850, p_name, type);
} }
int hud_elem_set_enum_string_stub(char* string, const char* format,
const char* string_value, const char* name) {
return snprintf(string, 0x800, format, string_value, name);
}
} // namespace } // namespace
void add_function(const char* name, game::BuiltinFunction func, bool type) { void add_function(const char* name, game::BuiltinFunction func, bool type) {
@ -89,6 +94,11 @@ public:
.install() .install()
->quick(); // Scr_GetMethod ->quick(); // Scr_GetMethod
// Patch buffer overflows
utils::hook(0x5BD3B9, hud_elem_set_enum_string_stub, HOOK_CALL)
.install()
->quick();
add_functions(); add_functions();
#ifdef GSC_DEBUG_FUNCTIONS #ifdef GSC_DEBUG_FUNCTIONS
add_debug_functions(); add_debug_functions();

View File

@ -19,8 +19,8 @@ void load_scripts_from_folder(const char* dir) {
game::Com_Printf(game::CON_CHANNEL_SERVER, game::Com_Printf(game::CON_CHANNEL_SERVER,
"Scanning directory '%s' for custom GSC scripts...\n", dir); "Scanning directory '%s' for custom GSC scripts...\n", dir);
strncpy_s(search_path, dir, _TRUNCATE); game::I_strncpyz(search_path, dir, sizeof(search_path));
strncat_s(search_path, "/", _TRUNCATE); game::I_strncat(search_path, sizeof(search_path), "/");
auto num_files = 0; auto num_files = 0;
const auto** files = const auto** files =
@ -31,7 +31,8 @@ void load_scripts_from_folder(const char* dir) {
game::Com_Printf(game::CON_CHANNEL_SERVER, "Loading script %s...\n", game::Com_Printf(game::CON_CHANNEL_SERVER, "Loading script %s...\n",
script_file); script_file);
const auto len = sprintf_s(path, "%s/%s", dir, script_file); const auto len =
game::Com_sprintf(path, sizeof(path), "%s/%s", dir, script_file);
if (len == -1) { if (len == -1) {
continue; continue;
} }

View File

@ -0,0 +1,64 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "branding.hpp"
#include "filesystem.hpp"
#include <utils/hook.hpp>
#include <utils/string.hpp>
namespace log_file {
namespace {
void com_open_log_file_stub() {
time_t aclock;
char time[32]{};
char log_file[game::MAX_OSPATH];
if (!game::Sys_IsMainThread() || *game::opening_qconsole) {
return;
}
*game::opening_qconsole = true;
tm new_time{};
_time64(&aclock);
_localtime64_s(&new_time, &aclock);
for (auto i = 0; i < 16; ++i) {
const auto* file_name =
(i == 0) ? "console.log"
: utils::string::va("{0}.{1:03}", "console.log", i);
game::I_strncpyz(log_file, file_name, sizeof(log_file));
if (!filesystem::file_rotate(log_file)) {
continue;
}
*game::logfile = game::FS_FOpenTextFileWrite(log_file);
if (*game::logfile) {
game::Com_Printf(game::CON_CHANNEL_SYSTEM, "\'%s\'\n",
game::Com_GetCommandLine());
(void)asctime_s(time, sizeof(time), &new_time);
game::Com_Printf(game::CON_CHANNEL_SYSTEM,
"Build %s. Logfile opened on %s\n",
branding::get_build_number(), time);
break; // Stop attempting further backups
}
}
*game::opening_qconsole = false;
*game::com_consoleLogOpenFailed = *game::logfile == 0;
}
} // namespace
class component final : public component_interface {
public:
void post_load() override {
utils::hook(0x603103, com_open_log_file_stub, HOOK_CALL)
.install() // hook*
->quick();
}
};
} // namespace log_file
REGISTER_COMPONENT(log_file::component)

View File

@ -46,8 +46,8 @@ private:
// Disable MP packet handler // Disable MP packet handler
utils::hook::set<std::uint8_t>(0x65E717, 0xEB); utils::hook::set<std::uint8_t>(0x65E717, 0xEB);
// Disable LSP packet handler // Disable LSP packet handler (suppress print message)
utils::hook::set<std::uint8_t>(0x65E3A4, 0xEB); utils::hook::nop(0x65E3C4, 5);
// Avoid spam // Avoid spam
utils::hook(0x65E786, game::Com_DPrintf, HOOK_CALL).install()->quick(); utils::hook(0x65E786, game::Com_DPrintf, HOOK_CALL).install()->quick();

View File

@ -31,6 +31,78 @@ const char* live_get_local_client_name_stub() {
} }
bool g_exit_after_tool_complete_stub() { return false; } bool g_exit_after_tool_complete_stub() { return false; }
void ui_replace_directive_stub(const int local_client_num,
const char* src_string, char* dst_string,
const int dst_buffer_size) {
if (!src_string) {
return;
}
if (!dst_string) {
return;
}
if (dst_buffer_size <= 0) {
return;
}
constexpr std::size_t MAX_HUDELEM_TEXT_LEN = 0x100;
if (std::strlen(src_string) > MAX_HUDELEM_TEXT_LEN) {
return;
}
game::UI_ReplaceDirective(local_client_num, src_string, dst_string,
dst_buffer_size);
}
int g_parse_weapon_accuracy_graph_internal(char* string, const char* format,
const char* dir_name,
const char* graph_name) {
return snprintf(string, 0x64, format, dir_name, graph_name);
}
int scr_load_anim_tree_internal(char* string, const char* format,
const char* file_name) {
return snprintf(string, 0x64, format, file_name);
}
// clang-format off
void __declspec(naked) string_table_get_column_value_for_row() {
static const char* fmt =
"Looking up row %i, column %i of table \'%s\' results in an empty string "
"(first column is %s, second column is %s for that row)\n";
static const DWORD Com_PrintError_t = 0x4C6980;
using namespace game;
// EAX is safe to reuse because it's nuked by the game's code after this stub exits
__asm {
// game's code
mov eax, dword ptr [ecx + eax * 0x8];
push edx;
push eax;
mov eax, [esp + 0xC]; // get table pointer
mov eax, [eax]; // get table->name
push eax;
// game's code
push edi; // column
push ebx; // row
// game's format string
push fmt;
push CON_CHANNEL_UI; // channel
call Com_PrintError_t;
add esp, 0x1C;
push 0x4BC84E;
ret;
}
}
// clang-format on
} // namespace } // namespace
class component final : public component_interface { class component final : public component_interface {
@ -58,6 +130,32 @@ public:
.install() // hook* .install() // hook*
->quick(); ->quick();
utils::hook(0x563C0C, ui_replace_directive_stub, HOOK_CALL)
.install() // hook*
->quick();
utils::hook(0x56454F, ui_replace_directive_stub, HOOK_CALL)
.install() // hook*
->quick();
utils::hook(0x568A7E, ui_replace_directive_stub, HOOK_CALL)
.install() // hook*
->quick();
utils::hook(0x6283AE, ui_replace_directive_stub, HOOK_CALL)
.install() // hook*
->quick();
utils::hook(0x5DF16F, g_parse_weapon_accuracy_graph_internal, HOOK_CALL)
.install() // hook*
->quick();
utils::hook(0x609EAF, scr_load_anim_tree_internal, HOOK_CALL)
.install() // hook*
->quick();
// Fix crash in StringTable_GetColumnValueForRow
utils::hook(0x4BC838, string_table_get_column_value_for_row, HOOK_JUMP)
.install() // hook*
->quick();
patch_sp(); patch_sp();
} }

View File

@ -11,7 +11,7 @@ namespace {
void __declspec(naked) pm_step_slide_move_stub() { void __declspec(naked) pm_step_slide_move_stub() {
__asm { __asm {
push eax; push eax;
mov eax, dvars::pm_bounce; mov eax, dvars::pm_bounces;
cmp byte ptr [eax + 0x10], 1; cmp byte ptr [eax + 0x10], 1;
pop eax; pop eax;
@ -30,29 +30,67 @@ void __declspec(naked) pm_step_slide_move_stub() {
} }
} }
void pm_project_velocity_stub(const float* vel_in, const float* normal, void __declspec(naked) pm_project_velocity_stub() {
float* vel_out) { __asm {
const auto length_squared_2d = vel_in[0] * vel_in[0] + vel_in[1] * vel_in[1]; push eax;
mov eax, dvars::pm_bouncesAllAngles;
cmp byte ptr [eax + 0x10], 1;
pop eax;
if (std::fabsf(normal[2]) < 0.001f || length_squared_2d == 0.0f) { je force_bounce;
vel_out[0] = vel_in[0];
vel_out[1] = vel_in[1]; test ah, 0x5;
vel_out[2] = vel_in[2]; jnp force_bounce;
return; push 0x4761AF;
ret;
force_bounce:
push 0x4761CF;
ret;
} }
}
auto new_z = vel_in[0] * normal[0] + vel_in[1] * normal[1]; void __declspec(naked) pm_project_velocity2_stub() {
new_z = -new_z / normal[2]; __asm {
push eax;
mov eax, dvars::pm_bouncesAllAngles;
cmp byte ptr [eax + 0x10], 1;
pop eax;
const auto length_scale = je force_bounce;
std::sqrtf((vel_in[2] * vel_in[2] + length_squared_2d) /
(new_z * new_z + length_squared_2d));
if (dvars::pm_bouncingAllAngles->current.enabled || fld1;
(length_scale < 1.f || new_z < 0.f || vel_in[2] > 0.f)) { jmp go_back;
vel_out[0] = vel_in[0] * length_scale;
vel_out[1] = vel_in[1] * length_scale; force_bounce:
vel_out[2] = new_z * length_scale; fldpi;
go_back:
fld [esp + 0x14 + 0x8];
push 0x4761A4;
ret;
}
}
void __declspec(naked) pm_step_slide_move2_stub() {
static float flt = 0.005f;
__asm {
push eax;
mov eax, dvars::pm_bouncesAllAngles;
cmp byte ptr [eax + 0x10], 1;
pop eax;
je force_bounce;
fld ds:0x6D3AE4;
jmp go_back;
force_bounce:
fld flt;
go_back:
push 0x4E9061;
ret;
} }
} }
@ -116,6 +154,129 @@ void pm_crash_land_stub(const float* v, float scale, const float* result) {
} }
} }
void __declspec(naked) pm_accelerate_stub() {
__asm {
// Game's code
sub esp, 0x20;
push eax;
mov eax, dvars::pm_csStrafe;
cmp byte ptr [eax + 0x10], 1;
pop eax;
je attempt_to_force_movement;
// Game's code
test byte ptr [esi + 0xC], 8;
jnz movement;
go_back:
push 0x64D87D;
ret;
attempt_to_force_movement:
test byte ptr [esi + 0xC], 8;
je movement;
jmp go_back;
movement:
push 0x64D91A;
ret;
}
}
void __declspec(naked) cm_is_edge_walkable_stub() {
__asm {
push eax;
mov eax, dvars::pm_terrainEdgeBounces;
cmp byte ptr [eax + 0x10], 1;
pop eax;
je bounce;
// Game's code
lea eax, [eax + ecx * 2];
add eax, ecx;
push 0x5FAED5;
ret;
bounce:
xor eax, eax;
ret;
}
}
void __declspec(naked) cm_is_edge_walkable2_stub() {
__asm {
push eax;
mov eax, dvars::pm_terrainEdgeBounces;
cmp byte ptr [eax + 0x10], 1;
pop eax;
je bounce;
jmp go_back;
bounce:
mov cl, 0;
go_back:
pop ebp;
mov byte ptr [edx + 0x2A], cl;
pop ebx;
add esp, 0x78;
ret;
}
}
void __declspec(naked) pm_step_slide_move3_stub() {
__asm {
push eax;
mov eax, dvars::pm_doubleBounces;
cmp byte ptr [eax + 0x10], 1;
pop eax;
je force_double_bounce;
test ah, 0x5;
jp go_back;
force_double_bounce:
push 0x4E904A;
ret;
go_back:
push 0x4E90C8;
ret;
}
}
void __declspec(naked) pm_step_slide_move4_stub() {
__asm {
push eax;
mov eax, dvars::pm_doubleBounces;
cmp byte ptr [eax + 0x10], 1;
pop eax;
// Code hook skipped
add esp, 0xC;
je force_double_bounce;
push 0x4E8FB7;
ret;
force_double_bounce:
push 0x4E8FD0;
ret;
}
}
void jump_clear_state_stub(game::playerState_s* ps) {
if (!dvars::pm_doubleBounces->current.enabled) {
game::Jump_ClearState(ps);
}
}
void __declspec(naked) jump_check_stub() { void __declspec(naked) jump_check_stub() {
__asm { __asm {
push eax; push eax;
@ -184,9 +345,16 @@ public:
utils::hook(0x4E9054, pm_step_slide_move_stub, HOOK_JUMP) utils::hook(0x4E9054, pm_step_slide_move_stub, HOOK_JUMP)
.install() .install()
->quick(); // PM_StepSlideMove ->quick(); // PM_StepSlideMove
utils::hook(0x4E90BE, pm_project_velocity_stub, HOOK_CALL)
utils::hook(0x4761AA, pm_project_velocity_stub, HOOK_JUMP)
.install() .install()
->quick(); // PM_StepSlideMove ->quick(); // PM_StepSlideMove
utils::hook(0x47619E, pm_project_velocity2_stub, HOOK_JUMP)
.install()
->quick(); // PM_StepSlideMove
utils::hook(0x4E905B, pm_step_slide_move2_stub, HOOK_JUMP)
.install()
->quick();
utils::hook(0x4FA809, weapon_rocket_launcher_fire_stub, HOOK_CALL) utils::hook(0x4FA809, weapon_rocket_launcher_fire_stub, HOOK_CALL)
.install() .install()
@ -209,10 +377,33 @@ public:
.install() .install()
->quick(); // PM_CorrectAllSolid ->quick(); // PM_CorrectAllSolid
// Double bounces
utils::hook(0x4E9045, pm_step_slide_move3_stub, HOOK_JUMP)
.install()
->quick();
utils::hook(0x4E8FAE, pm_step_slide_move4_stub, HOOK_JUMP)
.install()
->quick();
utils::hook(0x651D80, jump_clear_state_stub, HOOK_CALL)
.install()
->quick(); // PM_GroundTrace
// Bunnny hop
utils::hook(0x4D25E8, jump_check_stub, HOOK_JUMP).install()->quick();
// Enable / Disable jump landing punishment within PM_CrashLand
utils::hook(0x64E571, pm_crash_land_stub, HOOK_CALL) utils::hook(0x64E571, pm_crash_land_stub, HOOK_CALL)
.install() .install()
->quick(); // Vec3Scale ->quick(); // Vec3Scale
utils::hook(0x4D25E8, jump_check_stub, HOOK_JUMP).install()->quick();
utils::hook(0x64D870, pm_accelerate_stub, HOOK_JUMP).install()->quick();
// Implement terrain edge bounces
utils::hook(0x5FAED0, cm_is_edge_walkable_stub, HOOK_JUMP)
.install()
->quick();
utils::hook(0x5FBB14, cm_is_edge_walkable2_stub, HOOK_JUMP)
.install()
->quick();
utils::hook(0x6530C3, pm_move_single_stub, HOOK_JUMP).install()->quick(); utils::hook(0x6530C3, pm_move_single_stub, HOOK_JUMP).install()->quick();
@ -222,12 +413,16 @@ public:
static void register_dvars() { static void register_dvars() {
// clang-format off // clang-format off
dvars::pm_bounce = game::Dvar_RegisterBool( dvars::pm_bounces = game::Dvar_RegisterBool(
"pm_bounce", false, game::DVAR_NONE, "CoD4 Bounces"); "pm_bounces", false, game::DVAR_NONE, "CoD4 Bounces");
dvars::pm_bouncingAllAngles = game::Dvar_RegisterBool( dvars::pm_doubleBounces = game::Dvar_RegisterBool(
"pm_bouncingAllAngles", false, game::DVAR_NONE, "Enable bouncing from all angles"); "pm_doubleBounces", false, game::DVAR_NONE, "CoD4 Double Bounces");
dvars::pm_bouncesAllAngles = game::Dvar_RegisterBool(
"pm_bouncesAllAngles", false, game::DVAR_NONE, "Enable bouncing from all angles");
dvars::pm_rocketJump = game::Dvar_RegisterBool( dvars::pm_rocketJump = game::Dvar_RegisterBool(
"pm_rocketJump", true, game::DVAR_NONE, "CoD4 rocket jumps"); "pm_rocketJump", true, game::DVAR_NONE, "CoD4 rocket jumps");
dvars::pm_csStrafe = game::Dvar_RegisterBool(
"pm_csStrafe", false, game::DVAR_NONE, "Imitate CS Strafe movement");
dvars::pm_rocketJumpScale = game::Dvar_RegisterFloat( dvars::pm_rocketJumpScale = game::Dvar_RegisterFloat(
"pm_rocketJumpScale", 64.0f, 0.0f, 1024.0f, game::DVAR_NONE, ""); "pm_rocketJumpScale", 64.0f, 0.0f, 1024.0f, game::DVAR_NONE, "");
dvars::pm_playerCollision = game::Dvar_RegisterBool( dvars::pm_playerCollision = game::Dvar_RegisterBool(
@ -239,6 +434,8 @@ public:
dvars::pm_bunnyHop = game::Dvar_RegisterBool("pm_bunnyHop", dvars::pm_bunnyHop = game::Dvar_RegisterBool("pm_bunnyHop",
false, game::DVAR_NONE, "Constantly jump when holding space"); false, game::DVAR_NONE, "Constantly jump when holding space");
dvars::pm_snapVector = game::Dvar_RegisterBool("pm_snapVector", false, game::DVAR_NONE, "Snap velocity"); dvars::pm_snapVector = game::Dvar_RegisterBool("pm_snapVector", false, game::DVAR_NONE, "Snap velocity");
dvars::pm_terrainEdgeBounces = game::Dvar_RegisterBool(
"pm_terrainEdgeBounces", false, game::DVAR_NONE, "Bounces on terrain edges");
// clang-format on // clang-format on
} }
}; };

View File

@ -13,18 +13,34 @@
#include "scheduler.hpp" #include "scheduler.hpp"
namespace { namespace {
enum class ownership_state {
success,
unowned,
nosteam,
error,
};
ownership_state state_;
utils::binary_resource runner_file(RUNNER, "iw4sp-runner.exe"); utils::binary_resource runner_file(RUNNER, "iw4sp-runner.exe");
bool is_disabled() { return true; }
} // namespace } // namespace
class steam_proxy final : public component_interface { class steam_proxy final : public component_interface {
public: public:
void post_load() override { void post_load() override {
if (is_disabled()) {
return;
}
this->load_client(); this->load_client();
this->clean_up_on_error(); this->clean_up_on_error();
try { try {
this->start_mod("iw4x-sp singleplayer", 10180); state_ = this->start_mod("iw4x-sp singleplayer", 10180);
} catch (const std::exception& ex) { } catch (const std::exception& ex) {
state_ = ownership_state::error;
printf("Steam: %s\n", ex.what()); printf("Steam: %s\n", ex.what());
} }
} }
@ -96,20 +112,27 @@ private:
14, this->steam_pipe_); // GetIClientUtils 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 { __try {
this->start_mod_unsafe(title, app_id); return this->start_mod_unsafe(title, app_id);
} __except (EXCEPTION_EXECUTE_HANDLER) { } __except (EXCEPTION_EXECUTE_HANDLER) {
this->do_cleanup(); 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_) if (!this->client_utils_ || !this->client_user_)
return; return ownership_state::nosteam;
if (!this->client_user_.invoke<bool>("BIsSubscribedApp", app_id)) { if (!this->client_user_.invoke<bool>("BIsSubscribedApp", app_id)) {
#ifdef _DEBUG
app_id = 480; // Spacewar app_id = 480; // Spacewar
#else
return ownership_state::unowned;
#endif
} }
this->client_utils_.invoke<void>("SetAppIDForCurrentPipe", app_id, false); this->client_utils_.invoke<void>("SetAppIDForCurrentPipe", app_id, false);
@ -125,13 +148,15 @@ private:
game_id.raw.type = 1; // k_EGameIDTypeGameMod game_id.raw.type = 1; // k_EGameIDTypeGameMod
game_id.raw.app_id = app_id & 0xFFFFFF; game_id.raw.app_id = app_id & 0xFFFFFF;
const auto* mod_id = "IW4"; const auto* mod_id = "IW4x-SP";
game_id.raw.mod_id = game_id.raw.mod_id =
*reinterpret_cast<const unsigned int*>(mod_id) | 0x80000000; *reinterpret_cast<const unsigned int*>(mod_id) | 0x80000000;
this->client_user_.invoke<bool>("SpawnProcess", path.data(), cmd_line, this->client_user_.invoke<bool>("SpawnProcess", path.c_str(), cmd_line,
our_directory, game_id.bits, title.data(), our_directory, game_id.bits, title.c_str(),
app_id, 0, 0, 0); app_id, 0, 0, 0);
return ownership_state::success;
} }
void do_cleanup() { void do_cleanup() {

View File

@ -3,15 +3,18 @@
namespace dvars { namespace dvars {
const game::dvar_t* r_noBorder = nullptr; const game::dvar_t* r_noBorder = nullptr;
const game::dvar_t* pm_bounce = nullptr; const game::dvar_t* pm_bounces = nullptr;
const game::dvar_t* pm_bouncingAllAngles = nullptr; const game::dvar_t* pm_doubleBounces = nullptr;
const game::dvar_t* pm_bouncesAllAngles = nullptr;
const game::dvar_t* pm_rocketJump = nullptr; const game::dvar_t* pm_rocketJump = nullptr;
const game::dvar_t* pm_csStrafe = nullptr;
const game::dvar_t* pm_rocketJumpScale = nullptr; const game::dvar_t* pm_rocketJumpScale = nullptr;
const game::dvar_t* pm_playerCollision = nullptr; const game::dvar_t* pm_playerCollision = nullptr;
const game::dvar_t* pm_elevators = nullptr; const game::dvar_t* pm_elevators = nullptr;
const game::dvar_t* pm_disableLandingSlowdown = nullptr; const game::dvar_t* pm_disableLandingSlowdown = nullptr;
const game::dvar_t* pm_bunnyHop = nullptr; const game::dvar_t* pm_bunnyHop = nullptr;
const game::dvar_t* pm_snapVector = nullptr; const game::dvar_t* pm_snapVector = nullptr;
const game::dvar_t* pm_terrainEdgeBounces = nullptr;
const game::dvar_t* cg_drawVersion = nullptr; const game::dvar_t* cg_drawVersion = nullptr;
const game::dvar_t* cg_drawVersionX = nullptr; const game::dvar_t* cg_drawVersionX = nullptr;
@ -22,11 +25,21 @@ const game::dvar_t* bug_name = nullptr;
const game::dvar_t* g_log = nullptr; const game::dvar_t* g_log = nullptr;
// Game dvars // Game dvars
const game::dvar_t** cg_paused =
reinterpret_cast<const game::dvar_t**>(0x86D7C0);
const game::dvar_t** cl_freemove =
reinterpret_cast<const game::dvar_t**>(0x89776C);
const game::dvar_t** cl_freemoveScale =
reinterpret_cast<const game::dvar_t**>(0x897750);
const game::dvar_t** g_specialops = const game::dvar_t** g_specialops =
reinterpret_cast<const game::dvar_t**>(0x1B2E1E8); reinterpret_cast<const game::dvar_t**>(0x1B2E1E8);
const game::dvar_t** sv_mapname = const game::dvar_t** sv_mapname =
reinterpret_cast<const game::dvar_t**>(0x1B2E1E4); reinterpret_cast<const game::dvar_t**>(0x1B2E1E4);
const game::dvar_t** sv_cheats =
reinterpret_cast<const game::dvar_t**>(0x1B2E1EC);
const game::dvar_t** version = const game::dvar_t** version =
reinterpret_cast<const game::dvar_t**>(0x145D690); reinterpret_cast<const game::dvar_t**>(0x145D690);
@ -36,6 +49,8 @@ const game::dvar_t** com_developer =
const game::dvar_t** com_developer_script = const game::dvar_t** com_developer_script =
reinterpret_cast<const game::dvar_t**>(0x145EC58); reinterpret_cast<const game::dvar_t**>(0x145EC58);
const game::dvar_t** fs_homepath =
reinterpret_cast<const game::dvar_t**>(0x195624C);
const game::dvar_t** fs_gameDirVar = const game::dvar_t** fs_gameDirVar =
reinterpret_cast<const game::dvar_t**>(0x1956138); reinterpret_cast<const game::dvar_t**>(0x1956138);
} // namespace dvars } // namespace dvars

View File

@ -3,15 +3,18 @@
namespace dvars { namespace dvars {
extern const game::dvar_t* r_noBorder; extern const game::dvar_t* r_noBorder;
extern const game::dvar_t* pm_bounce; extern const game::dvar_t* pm_bounces;
extern const game::dvar_t* pm_bouncingAllAngles; extern const game::dvar_t* pm_doubleBounces;
extern const game::dvar_t* pm_bouncesAllAngles;
extern const game::dvar_t* pm_rocketJump; extern const game::dvar_t* pm_rocketJump;
extern const game::dvar_t* pm_csStrafe;
extern const game::dvar_t* pm_rocketJumpScale; extern const game::dvar_t* pm_rocketJumpScale;
extern const game::dvar_t* pm_playerCollision; extern const game::dvar_t* pm_playerCollision;
extern const game::dvar_t* pm_elevators; extern const game::dvar_t* pm_elevators;
extern const game::dvar_t* pm_disableLandingSlowdown; extern const game::dvar_t* pm_disableLandingSlowdown;
extern const game::dvar_t* pm_bunnyHop; extern const game::dvar_t* pm_bunnyHop;
extern const game::dvar_t* pm_snapVector; extern const game::dvar_t* pm_snapVector;
extern const game::dvar_t* pm_terrainEdgeBounces;
extern const game::dvar_t* cg_drawVersion; extern const game::dvar_t* cg_drawVersion;
extern const game::dvar_t* cg_drawVersionX; extern const game::dvar_t* cg_drawVersionX;
@ -22,14 +25,21 @@ extern const game::dvar_t* bug_name;
extern const game::dvar_t* g_log; extern const game::dvar_t* g_log;
// Game dvars // Game dvars
extern const game::dvar_t** cg_paused;
extern const game::dvar_t** cl_freemove;
extern const game::dvar_t** cl_freemoveScale;
extern const game::dvar_t** g_specialops; extern const game::dvar_t** g_specialops;
extern const game::dvar_t** sv_mapname; extern const game::dvar_t** sv_mapname;
extern const game::dvar_t** sv_cheats;
extern const game::dvar_t** version; extern const game::dvar_t** version;
extern const game::dvar_t** com_developer; extern const game::dvar_t** com_developer;
extern const game::dvar_t** com_developer_script; extern const game::dvar_t** com_developer_script;
extern const game::dvar_t** fs_homepath;
extern const game::dvar_t** fs_gameDirVar; extern const game::dvar_t** fs_gameDirVar;
} // namespace dvars } // namespace dvars

View File

@ -1,87 +0,0 @@
#include <std_include.hpp>
#include "large_local.hpp"
namespace game::engine {
namespace {
constexpr auto PAGE_SIZE = 4096;
int can_use_server_large_local() { return Sys_IsServerThread(); }
void large_local_end(int start_pos) {
assert(Sys_IsMainThread());
assert(g_largeLocalBuf);
*g_largeLocalPos = start_pos;
assert(((*g_maxLargeLocalPos + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1)) <=
(*g_minLargeLocalRightPos & ~(PAGE_SIZE - 1)));
}
void large_local_end_right(int start_pos) {
assert(can_use_server_large_local());
assert(g_largeLocalBuf);
*g_largeLocalRightPos = start_pos;
assert(((*g_maxLargeLocalPos + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1)) <=
(*g_minLargeLocalRightPos & ~(PAGE_SIZE - 1)));
}
void* large_local_get_buf(int start_pos, int size) {
assert(Sys_IsMainThread() || can_use_server_large_local());
assert(g_largeLocalBuf);
assert(!(size & 127));
if (Sys_IsMainThread()) {
return &g_largeLocalBuf[start_pos];
}
const auto start_index = start_pos - size;
assert(start_index >= 0);
return &g_largeLocalBuf[start_index];
}
} // namespace
large_local::large_local(int size_param) {
assert(size_param);
assert(Sys_IsMainThread() || can_use_server_large_local());
size_param = ((size_param + (128 - 1)) & ~(128 - 1));
if (Sys_IsMainThread()) {
this->start_pos_ = LargeLocalBegin(size_param);
} else {
this->start_pos_ = LargeLocalBeginRight(size_param);
}
this->size_ = size_param;
}
large_local::~large_local() {
if (this->size_) {
this->pop_buf();
}
}
void large_local::pop_buf() {
assert(this->size_);
assert(Sys_IsMainThread() || can_use_server_large_local());
if (Sys_IsMainThread()) {
large_local_end(this->start_pos_);
} else {
large_local_end_right(this->start_pos_);
}
this->size_ = 0;
}
void* large_local::get_buf() const {
assert(this->size_);
assert(Sys_IsMainThread() || can_use_server_large_local());
return large_local_get_buf(this->start_pos_, this->size_);
}
} // namespace game::engine

View File

@ -1,22 +0,0 @@
#pragma once
namespace game::engine {
class large_local {
public:
explicit large_local(int size_param);
~large_local();
large_local(large_local&&) = delete;
large_local(const large_local&) = delete;
large_local& operator=(large_local&&) = delete;
large_local& operator=(const large_local&) = delete;
[[nodiscard]] void* get_buf() const;
private:
void pop_buf();
int start_pos_;
int size_;
};
} // namespace game::engine

View File

@ -3,7 +3,7 @@
namespace game { namespace game {
int FS_FOpenFileReadForThread(const char* filename, int* file, int FS_FOpenFileReadForThread(const char* filename, int* file,
FsThread thread) { FsThread thread) {
const static DWORD FS_FOpenFileReadForThread_t = 0x630380; static const DWORD FS_FOpenFileReadForThread_t = 0x630380;
int result{}; int result{};
__asm { __asm {
@ -23,7 +23,7 @@ int FS_FOpenFileReadForThread(const char* filename, int* file,
} }
void IN_KeyDown(kbutton_t* b) { void IN_KeyDown(kbutton_t* b) {
const static DWORD IN_KeyDown_t = 0x57A350; static const DWORD IN_KeyDown_t = 0x57A350;
__asm { __asm {
pushad; pushad;
@ -36,7 +36,7 @@ void IN_KeyDown(kbutton_t* b) {
} }
void IN_KeyUp(kbutton_t* b) { void IN_KeyUp(kbutton_t* b) {
const static DWORD IN_KeyUp_t = 0x57A3F0; static const DWORD IN_KeyUp_t = 0x57A3F0;
__asm { __asm {
pushad; pushad;
@ -94,7 +94,7 @@ HANDLE Sys_OpenFileReliable(const char* filename) {
} }
int PC_Int_Parse(int handle, int* i) { int PC_Int_Parse(int handle, int* i) {
const static DWORD PC_Int_Parse_t = 0x62DF10; static const DWORD PC_Int_Parse_t = 0x62DF10;
int result{}; int result{};
__asm { __asm {
@ -112,7 +112,7 @@ int PC_Int_Parse(int handle, int* i) {
} }
int PC_Float_Parse(int handle, float* f) { int PC_Float_Parse(int handle, float* f) {
const static DWORD PC_Float_Parse_t = 0x62DE40; static const DWORD PC_Float_Parse_t = 0x62DE40;
int result{}; int result{};
__asm { __asm {
@ -130,7 +130,7 @@ int PC_Float_Parse(int handle, float* f) {
} }
void Menu_FreeItemMemory(itemDef_s* item) { void Menu_FreeItemMemory(itemDef_s* item) {
const static DWORD Menu_FreeItemMemory_t = 0x62B7E0; static const DWORD Menu_FreeItemMemory_t = 0x62B7E0;
__asm { __asm {
pushad; pushad;
@ -141,4 +141,6 @@ void Menu_FreeItemMemory(itemDef_s* item) {
popad; popad;
} }
} }
char* Com_GetCommandLine() { return reinterpret_cast<char*>(0x145D800); }
} // namespace game } // namespace game

View File

@ -47,19 +47,7 @@ int PC_Float_Parse(int handle, float* f);
void Menu_FreeItemMemory(itemDef_s* item); void Menu_FreeItemMemory(itemDef_s* item);
// Global definitions char* Com_GetCommandLine();
constexpr auto CMD_MAX_NESTING = 8;
constexpr auto MAX_POSSIBLE_LOCAL_CLIENTS = 1;
constexpr std::size_t MAX_LOCAL_CLIENTS = 1;
constexpr auto MAX_QPATH = 64;
constexpr auto MAX_OSPATH = 256;
constexpr auto MAX_OPCODE_LOOKUP_SIZE = 0x1000000;
constexpr auto MAX_SOURCEPOS_LOOKUP_SIZE = 0x800000;
constexpr auto MAX_SOURCEBUF_LOOKUP_SIZE = 0x40000;
} // namespace game } // namespace game
#include "symbols.hpp" #include "symbols.hpp"

View File

@ -4,6 +4,20 @@
#pragma warning(disable : 4324) #pragma warning(disable : 4324)
namespace game { namespace game {
// Global definitions
constexpr auto CMD_MAX_NESTING = 8;
constexpr auto MAX_POSSIBLE_LOCAL_CLIENTS = 1;
constexpr std::size_t MAX_LOCAL_CLIENTS = 1;
constexpr auto MAX_QPATH = 64;
constexpr auto MAX_OSPATH = 256;
constexpr auto MAX_OPCODE_LOOKUP_SIZE = 0x1000000;
constexpr auto MAX_SOURCEPOS_LOOKUP_SIZE = 0x800000;
constexpr auto MAX_SOURCEBUF_LOOKUP_SIZE = 0x40000;
typedef float vec_t; typedef float vec_t;
typedef vec_t vec2_t[2]; typedef vec_t vec2_t[2];
typedef vec_t vec3_t[3]; typedef vec_t vec3_t[3];
@ -15,6 +29,13 @@ enum LocalClientNum_t {
LOCAL_CLIENT_COUNT = 1, LOCAL_CLIENT_COUNT = 1,
}; };
enum ControllerIndex_t {
INVALID_CONTROLLER_PORT = -1,
CONTROLLER_INDEX_0 = 0x0,
CONTROLLER_INDEX_FIRST = 0x0,
CONTROLLER_INDEX_COUNT = 0x1,
};
struct scr_entref_t { struct scr_entref_t {
unsigned __int16 entnum; unsigned __int16 entnum;
unsigned __int16 classnum; unsigned __int16 classnum;
@ -362,6 +383,11 @@ enum {
VAR_INTEGER = 0x6, VAR_INTEGER = 0x6,
}; };
enum msgLocErrType_t {
LOCMSG_SAFE = 0x0,
LOCMSG_NOERR = 0x1,
};
enum { enum {
CON_CHANNEL_DONT_FILTER = 0x0, CON_CHANNEL_DONT_FILTER = 0x0,
CON_CHANNEL_ERROR = 0x1, CON_CHANNEL_ERROR = 0x1,
@ -528,8 +554,8 @@ struct indent_s {
}; };
struct source_s { struct source_s {
char filename[64]; char filename[MAX_QPATH];
char includepath[64]; char includepath[MAX_QPATH];
punctuation_s* punctuations; punctuation_s* punctuations;
script_s* scriptstack; script_s* scriptstack;
token_s* tokens; token_s* tokens;
@ -1738,6 +1764,109 @@ struct localization_t {
struct Sys_File { struct Sys_File {
HANDLE handle; HANDLE handle;
}; };
enum ShockViewTypes {
SHELLSHOCK_VIEWTYPE_BLURRED = 0x0,
SHELLSHOCK_VIEWTYPE_FLASHED = 0x1,
SHELLSHOCK_VIEWTYPE_NONE = 0x2,
};
struct shellshock_parms_t {
struct {
int blurredFadeTime;
int blurredEffectTime;
int flashWhiteFadeTime;
int flashShotFadeTime;
ShockViewTypes type;
} screenBlend;
struct {
int fadeTime;
float kickRate;
float kickRadius;
} view;
struct {
bool affect;
char loop[64];
char loopSilent[64];
char end[64];
char endAbort[64];
int fadeInTime;
int fadeOutTime;
float drylevel;
float wetlevel;
char roomtype[16];
float channelvolume[64];
int modEndDelay;
int loopFadeTime;
int loopEndDelay;
} sound;
struct {
bool affect;
int fadeTime;
float mouseSensitivity;
float maxPitchSpeed;
float maxYawSpeed;
} lookControl;
struct {
bool affect;
} movement;
};
struct cgs_t {
int viewX;
int viewY;
int viewWidth;
int viewHeight;
float viewAspect;
bool started;
shellshock_parms_t holdBreathParams;
};
static_assert(sizeof(cgs_t) == 0x280);
struct cpose_t {
unsigned char __pad0[0x64];
};
static_assert(sizeof(cpose_t) == 0x64);
enum CubemapShot {
CUBEMAPSHOT_NONE = 0x0,
CUBEMAPSHOT_RIGHT = 0x1,
CUBEMAPSHOT_LEFT = 0x2,
CUBEMAPSHOT_BACK = 0x3,
CUBEMAPSHOT_FRONT = 0x4,
CUBEMAPSHOT_UP = 0x5,
CUBEMAPSHOT_DOWN = 0x6,
CUBEMAPSHOT_COUNT = 0x7,
};
struct snapshot_s {
int snapFlags;
int serverTime;
int numEntities;
unsigned __int16 entityNums[2048];
int numFxEntities;
unsigned __int16 fxEntityNums[768];
};
struct cg_s {
int clientNum;
int localClientNum;
CubemapShot cubemapShot;
int cubemapSize;
int serverCommandSequence;
int serverLatestCommandSequence;
int loaded;
snapshot_s* snap;
snapshot_s* nextSnap;
playerState_s* ps;
playerState_s* nextPs;
}; // Incomplete
static_assert(offsetof(cg_s, snap) == 0x1C);
static_assert(offsetof(cg_s, nextSnap) == 0x20);
static_assert(offsetof(cg_s, nextPs) == 0x28);
} // namespace game } // namespace game
#pragma warning(pop) #pragma warning(pop)

View File

@ -18,12 +18,17 @@ WEAK symbol<void()> Com_ServerPacketEvent{0x47FD30};
WEAK symbol<void(const char* filename)> Com_BeginParseSession{0x4A5C90}; WEAK symbol<void(const char* filename)> Com_BeginParseSession{0x4A5C90};
WEAK symbol<void()> Com_EndParseSession{0x4D12C0}; WEAK symbol<void()> Com_EndParseSession{0x4D12C0};
WEAK symbol<const char*(const char** data_p)> Com_Parse{0x486600}; WEAK symbol<const char*(const char** data_p)> Com_Parse{0x486600};
WEAK symbol<int(char* dest, int size, const char* fmt, ...)> Com_sprintf{
0x4E85A0};
WEAK symbol<const char*(const char* fmt, ...)> va{0x4869F0}; WEAK symbol<const char*(const char* fmt, ...)> va{0x4869F0};
// Con // Con
WEAK symbol<bool(const char* cmd)> Con_IsDvarCommand{0x4B6610}; WEAK symbol<bool(const char* cmd)> Con_IsDvarCommand{0x4B6610};
// SV
WEAK symbol<void(int clientNum, usercmd_s* cmd)> SV_ClientThink{0x4B43D0};
// ScrPlace // ScrPlace
WEAK symbol<ScreenPlacement*(int localClientNum)> ScrPlace_GetActivePlacement{ WEAK symbol<ScreenPlacement*(int localClientNum)> ScrPlace_GetActivePlacement{
0x4D2A60}; 0x4D2A60};
@ -95,6 +100,9 @@ WEAK symbol<void(const char* dvarName, double value)> Dvar_SetFloatByName{
0x497250}; 0x497250};
WEAK symbol<void(const char* dvarName, const char* value)> Dvar_SetStringByName{ WEAK symbol<void(const char* dvarName, const char* value)> Dvar_SetStringByName{
0x440C60}; 0x440C60};
WEAK symbol<const dvar_t*(const char* dvarName, const char* string,
DvarSetSource source)>
Dvar_SetFromStringByNameFromSource{0x4774E0};
WEAK symbol<void(const dvar_t* dvar, int value)> Dvar_SetInt{0x4FA540}; WEAK symbol<void(const dvar_t* dvar, int value)> Dvar_SetInt{0x4FA540};
WEAK symbol<void(const dvar_t* dvar, bool value)> Dvar_SetBool{0x4E57E0}; WEAK symbol<void(const dvar_t* dvar, bool value)> Dvar_SetBool{0x4E57E0};
WEAK symbol<void(const dvar_t* dvar, const char* value)> Dvar_SetString{ WEAK symbol<void(const dvar_t* dvar, const char* value)> Dvar_SetString{
@ -218,6 +226,7 @@ WEAK symbol<void(const char* base, const char* game, const char* qpath,
char* ospath)> char* ospath)>
FS_BuildOSPath{0x4E48F0}; FS_BuildOSPath{0x4E48F0};
WEAK symbol<void(const char* gameName)> FS_Startup{0x47AF20}; WEAK symbol<void(const char* gameName)> FS_Startup{0x47AF20};
WEAK symbol<int(const char* filename)> FS_FOpenTextFileWrite{0x4495F0};
// UI // UI
WEAK symbol<Font_s*(const ScreenPlacement* scrPlace, int fontEnum, float scale)> WEAK symbol<Font_s*(const ScreenPlacement* scrPlace, int fontEnum, float scale)>
@ -229,6 +238,9 @@ WEAK symbol<void(const ScreenPlacement* scrPlace, const char* text,
int maxChars, Font_s* font, float x, float y, int horzAlign, int maxChars, Font_s* font, float x, float y, int horzAlign,
int vertAlign, float scale, const float* color, int style)> int vertAlign, float scale, const float* color, int style)>
UI_DrawText{0x40FC70}; UI_DrawText{0x40FC70};
WEAK symbol<void(int localClientNum, const char* srcString, char* dstString,
int dstBufferSize)>
UI_ReplaceDirective{0x4BBD10};
// PC // PC
WEAK symbol<int(source_s* source)> PC_Directive_define{0x4F8CF0}; WEAK symbol<int(source_s* source)> PC_Directive_define{0x4F8CF0};
@ -251,6 +263,7 @@ WEAK symbol<void(pmove_t* pm, trace_t* results, const float* start,
int contentMask)> int contentMask)>
PM_playerTrace{0x447B90}; PM_playerTrace{0x447B90};
WEAK symbol<bool(const playerState_s* ps)> PM_IsSprinting{0x47CF70}; WEAK symbol<bool(const playerState_s* ps)> PM_IsSprinting{0x47CF70};
WEAK symbol<void(playerState_s* ps)> Jump_ClearState{0x435A40};
// Live // Live
WEAK symbol<const char*(int controllerIndex)> Live_GetLocalClientName{0x492EF0}; WEAK symbol<const char*(int controllerIndex)> Live_GetLocalClientName{0x492EF0};
@ -263,6 +276,7 @@ WEAK symbol<int(const char* s0, const char* s1)> I_stricmp{0x409B80};
WEAK symbol<int(const char* s0, const char* s1, int n)> I_strnicmp{0x491E60}; WEAK symbol<int(const char* s0, const char* s1, int n)> I_strnicmp{0x491E60};
WEAK symbol<void(char* dest, const char* src, int destsize)> I_strncpyz{ WEAK symbol<void(char* dest, const char* src, int destsize)> I_strncpyz{
0x416920}; 0x416920};
WEAK symbol<void(char* dest, int size, const char* src)> I_strncat{0x45CA00};
WEAK symbol<void(field_t* edit)> Field_Clear{0x45C350}; WEAK symbol<void(field_t* edit)> Field_Clear{0x45C350};
@ -272,20 +286,26 @@ WEAK symbol<int(const char* string)> StringTable_HashString{0x498080};
// Vec3 // Vec3
WEAK symbol<void(const float* v, float scale, const float* result)> Vec3Scale{ WEAK symbol<void(const float* v, float scale, const float* result)> Vec3Scale{
0x429220}; 0x429220};
// Renderer
WEAK symbol<void(const char* text, int maxChars, Font_s* font, float x, float y, WEAK symbol<void(const char* text, int maxChars, Font_s* font, float x, float y,
float xScale, float yScale, float rotation, const float* color, float xScale, float yScale, float rotation, const float* color,
int style)> int style)>
R_AddCmdDrawText{0x50E7A0}; R_AddCmdDrawText{0x50E7A0};
// Renderer
WEAK symbol<int(const char* text, int maxChars, Font_s* font)> R_TextWidth{ WEAK symbol<int(const char* text, int maxChars, Font_s* font)> R_TextWidth{
0x508960}; 0x508960};
WEAK symbol<const char*(const char* pszInputBuffer, const char* pszMessageType,
msgLocErrType_t errType)>
SEH_LocalizeTextMessage{0x4CD8D0};
// Variables // Variables
WEAK symbol<CmdArgs> cmd_args{0x144FED0}; WEAK symbol<CmdArgs> cmd_args{0x144FED0};
WEAK symbol<CmdArgs> sv_cmd_args{0x145ABA0}; WEAK symbol<CmdArgs> sv_cmd_args{0x145ABA0};
WEAK symbol<gentity_s> g_entities{0xEAAC38}; WEAK symbol<gentity_s> g_entities{0xEAAC38};
WEAK symbol<gclient_s> g_clients{0x10911E8}; WEAK symbol<gclient_s> g_clients{0x10911E8};
WEAK symbol<cgs_t> cgsArray{0x762008};
WEAK symbol<cg_s> cgArray{0x7622A0};
WEAK symbol<scrVmPub_t> scrVmPub{0x190DDF0}; WEAK symbol<scrVmPub_t> scrVmPub{0x190DDF0};
WEAK symbol<scrVarPub_t> scrVarPub{0x18E7508}; WEAK symbol<scrVarPub_t> scrVarPub{0x18E7508};
@ -306,11 +326,12 @@ WEAK symbol<int> g_minLargeLocalRightPos{0x195AB00};
WEAK symbol<unsigned long> g_dwTlsIndex{0x1BFC750}; WEAK symbol<unsigned long> g_dwTlsIndex{0x1BFC750};
WEAK symbol<int> com_frameTime{0x145EC7C};
WEAK symbol<bool> cin_skippable{0x73264C}; WEAK symbol<bool> cin_skippable{0x73264C};
WEAK symbol<int> com_frameTime{0x145EC7C};
WEAK symbol<int> com_fixedConsolePosition{0x145EC10}; WEAK symbol<int> com_fixedConsolePosition{0x145EC10};
WEAK symbol<int> opening_qconsole{0x145ECD4};
WEAK symbol<int> com_consoleLogOpenFailed{0x145ECB0};
WEAK symbol<field_t> g_consoleField{0x88C700}; WEAK symbol<field_t> g_consoleField{0x88C700};
WEAK symbol<ConDrawInputGlob> conDrawInputGlob{0x86E788}; WEAK symbol<ConDrawInputGlob> conDrawInputGlob{0x86E788};
@ -336,5 +357,5 @@ WEAK symbol<void*> DB_GetXAssetSizeHandlers{0x733408};
WEAK symbol<void*> DB_XAssetPool{0x7337F8}; WEAK symbol<void*> DB_XAssetPool{0x7337F8};
WEAK symbol<unsigned int> g_poolSize{0x733510}; WEAK symbol<unsigned int> g_poolSize{0x733510};
WEAK symbol<localization_t> localization{0x19ff820}; WEAK symbol<localization_t> localization{0x19FF820};
} // namespace game } // namespace game

View File

@ -10,21 +10,33 @@
#define SP_XLABS_HASH \ #define SP_XLABS_HASH \
"05D499D77028859D4BA30C852DA85CCA5F02678B22AEA9E27D7C56973B14A0BC" "05D499D77028859D4BA30C852DA85CCA5F02678B22AEA9E27D7C56973B14A0BC"
#define SP_ALTER_HASH \
"9480909B18CF5A4EC483C989AAFCE929D2172C5F9448F8C23B723683D1A43565"
namespace binary_loader { namespace binary_loader {
namespace {
std::string load_base() { std::string load_base() {
std::string path = "iw4sp.exe";
if (utils::io::file_exists("data/iw4sp.exe")) {
path = "data/iw4sp.exe";
}
std::string data; std::string data;
if (!utils::io::read_file("iw4sp.exe", &data)) { if (!utils::io::read_file(path, &data)) {
throw std::runtime_error("Failed to read game binary (iw4sp.exe)!"); throw std::runtime_error("Failed to read game binary (iw4sp.exe)!");
} }
const auto hash = utils::cryptography::sha256::compute(data, true); const auto hash = utils::cryptography::sha256::compute(data, true);
if ((hash != SP_XLABS_HASH) && (hash != SP_HASH)) { if ((hash != SP_ALTER_HASH) && (hash != SP_HASH)) {
throw std::runtime_error( throw std::runtime_error(
"Your iw4sp.exe is incompatible with this client."); "Your iw4sp.exe is incompatible with this client. Please use the "
"AlterWare launcher to install IW4x-SP");
} }
return data; return data;
} }
} // namespace
std::string load() { return load_base(); } std::string load() { return load_base(); }
} // namespace binary_loader } // namespace binary_loader

View File

@ -91,8 +91,8 @@ public:
PIMAGE_DOS_HEADER get_dos_header() const; PIMAGE_DOS_HEADER get_dos_header() const;
PIMAGE_OPTIONAL_HEADER get_optional_header() const; PIMAGE_OPTIONAL_HEADER get_optional_header() const;
void** get_iat_entry(const std::string& module_name, [[nodiscard]] void** get_iat_entry(const std::string& module_name,
const std::string& proc_name) const; const std::string& proc_name) const;
static void set_dll_directory(const std::string& directory); static void set_dll_directory(const std::string& directory);
static std::string get_dll_directory(); static std::string get_dll_directory();
@ -101,6 +101,49 @@ private:
HMODULE module_; HMODULE module_;
}; };
template <HANDLE InvalidHandle = nullptr> class handle {
public:
handle() = default;
handle(const HANDLE h) : handle_(h) {}
~handle() {
if (*this) {
CloseHandle(this->handle_);
this->handle_ = InvalidHandle;
}
}
handle(const handle&) = delete;
handle& operator=(const handle&) = delete;
handle(handle&& obj) noexcept : handle() { this->operator=(std::move(obj)); }
handle& operator=(handle&& obj) noexcept {
if (this != &obj) {
this->~handle();
this->handle_ = obj.handle_;
obj.handle_ = InvalidHandle;
}
return *this;
}
handle& operator=(HANDLE h) noexcept {
this->~handle();
this->handle_ = h;
return *this;
}
operator bool() const { return this->handle_ != InvalidHandle; }
operator HANDLE() const { return this->handle_; }
private:
HANDLE handle_{InvalidHandle};
};
bool is_wine(); bool is_wine();
__declspec(noreturn) void raise_hard_exception(); __declspec(noreturn) void raise_hard_exception();

View File

@ -0,0 +1,20 @@
#include "properties.hpp"
#include <gsl/gsl>
#include <ShlObj.h>
namespace utils::properties {
std::filesystem::path get_appdata_path() {
PWSTR path;
if (!SUCCEEDED(
SHGetKnownFolderPath(FOLDERID_LocalAppData, 0, nullptr, &path))) {
throw std::runtime_error("Failed to read APPDATA path!");
}
auto _ = gsl::finally([&path] { CoTaskMemFree(path); });
static auto appdata = std::filesystem::path(path) / "alterware";
return appdata;
}
} // namespace utils::properties

View File

@ -0,0 +1,6 @@
#pragma once
#include <filesystem>
namespace utils::properties {
std::filesystem::path get_appdata_path();
}

View File

@ -1,16 +1,16 @@
#define WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN
#include <Windows.h> #include <Windows.h>
#include <cstring>
#include <cstdlib> #include <cstdlib>
#include "debugger.hpp"
int WINAPI WinMain(HINSTANCE, HINSTANCE, PSTR, int) { int WINAPI WinMain(HINSTANCE, HINSTANCE, PSTR, int) {
const auto* const command = "-proc "; const auto* const command = "-proc ";
const char* parent_proc = std::strstr(GetCommandLineA(), command); const char* parent_proc = std::strstr(GetCommandLineA(), command);
if (parent_proc) { if (parent_proc) {
const auto pid = DWORD(atoi(parent_proc + std::strlen(command))); const auto pid = atoi(parent_proc + std::strlen(command));
auto* const process_handle = OpenProcess(SYNCHRONIZE, FALSE, pid); auto* const process_handle =
OpenProcess(SYNCHRONIZE, FALSE, static_cast<DWORD>(pid));
if (process_handle) { if (process_handle) {
WaitForSingleObject(process_handle, INFINITE); WaitForSingleObject(process_handle, INFINITE);
CloseHandle(process_handle); CloseHandle(process_handle);