fix many issues with this repo
All checks were successful
check-formatting / check-formatting (push) Successful in 4m8s
All checks were successful
check-formatting / check-formatting (push) Successful in 4m8s
This commit is contained in:
parent
89a735326d
commit
eb318d32c5
33
.gitea/workflows/check-formatting.yml
Normal file
33
.gitea/workflows/check-formatting.yml
Normal 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
2
deps/GSL
vendored
@ -1 +1 @@
|
||||
Subproject commit e64c97fc2cfc11992098bb38eda932de275e3f4d
|
||||
Subproject commit 3325bbd33d24d1f8f5a0f69e782c92ad5a39a68e
|
2
deps/libtomcrypt
vendored
2
deps/libtomcrypt
vendored
@ -1 +1 @@
|
||||
Subproject commit f7e6519fae1e11ff5ff9d36c84101a673002133b
|
||||
Subproject commit a6b9aff7aab857fe1b491710a5c5b9e2be49cb08
|
2
deps/libtommath
vendored
2
deps/libtommath
vendored
@ -1 +1 @@
|
||||
Subproject commit 5809141a3a6ec1bf3443c927c02b955e19224016
|
||||
Subproject commit e823b0c34cea291bdb94d672731e1c1f08525557
|
2
deps/minhook
vendored
2
deps/minhook
vendored
@ -1 +1 @@
|
||||
Subproject commit 1cc46107ee522d7a5c73656c519ca16addf2c23a
|
||||
Subproject commit c3fcafdc10146beb5919319d0683e44e3c30d537
|
2
deps/rapidjson
vendored
2
deps/rapidjson
vendored
@ -1 +1 @@
|
||||
Subproject commit ab1842a2dae061284c0a62dca1cc6d5e7e37e346
|
||||
Subproject commit 24b5e7a8b27f42fa16b96fc70aade9106cf7102f
|
2
deps/zlib
vendored
2
deps/zlib
vendored
@ -1 +1 @@
|
||||
Subproject commit 0f51fb4933fc9ce18199cb2554dacea8033e7fd3
|
||||
Subproject commit 5a82f71ed1dfc0bec044d9702463dbdf84ea3b71
|
@ -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
8
scripts/check-format.sh
Executable 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
14
scripts/format.sh
Normal 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
|
||||
}
|
@ -3,6 +3,9 @@
|
||||
|
||||
#include <utils/cryptography.hpp>
|
||||
#include <utils/hook.hpp>
|
||||
#include <utils/io.hpp>
|
||||
#include <utils/nt.hpp>
|
||||
#include <utils/properties.hpp>
|
||||
#include <utils/smbios.hpp>
|
||||
#include <utils/string.hpp>
|
||||
|
||||
@ -62,15 +65,94 @@ std::string get_key_entropy() {
|
||||
return entropy;
|
||||
}
|
||||
|
||||
utils::cryptography::ecc::key& get_key() {
|
||||
static auto key =
|
||||
utils::cryptography::ecc::generate_key(512, get_key_entropy());
|
||||
bool load_key(utils::cryptography::ecc::key& key) {
|
||||
std::string data{};
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
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 {
|
||||
public:
|
||||
|
@ -22,7 +22,7 @@ game::define_s* define_from_string(const char* string) {
|
||||
string, static_cast<int>(std::strlen(string)), "*extern");
|
||||
// create a new source
|
||||
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.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)));
|
||||
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->tokens = nullptr;
|
||||
source->defines = nullptr;
|
||||
|
@ -130,10 +130,10 @@ void set_script_punctuations(game::script_s* script) {
|
||||
}
|
||||
|
||||
game::script_s* load_script_file(const char* filename) {
|
||||
int fp;
|
||||
int fp{};
|
||||
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);
|
||||
if (!fp) {
|
||||
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* 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[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* 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[length] = '\0';
|
||||
script->length = length;
|
||||
|
@ -2,6 +2,7 @@
|
||||
#include "loader/component_loader.hpp"
|
||||
#include "game/dvars.hpp"
|
||||
|
||||
#include "branding.hpp"
|
||||
#include "scheduler.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
@ -17,10 +18,6 @@ constexpr auto* BUILD_TYPE = "IW4x_DEV SP";
|
||||
constexpr auto* BUILD_TYPE = "IW4x SP";
|
||||
#endif
|
||||
|
||||
constexpr const char* get_build_number() {
|
||||
return SHORTVERSION " latest " __DATE__ " " __TIME__;
|
||||
}
|
||||
|
||||
const char* get_version_string() {
|
||||
const auto* result = utils::string::va(
|
||||
"{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);
|
||||
}
|
||||
|
||||
volatile bool left_side = true;
|
||||
game::Font_s** small_font;
|
||||
void branding_loop() {
|
||||
float color[4] = {1.0f, 1.0f, 1.0f, 0.25f};
|
||||
@ -88,15 +86,22 @@ void branding_loop() {
|
||||
|
||||
auto* const scr_place =
|
||||
game::ScrPlace_GetActivePlacement(game::LOCAL_CLIENT_0);
|
||||
const auto x = scr_place->realViewportSize[0] -
|
||||
static_cast<float>(game::R_TextWidth(text, 0, *small_font)) -
|
||||
10.0f;
|
||||
const auto x =
|
||||
left_side
|
||||
? 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,
|
||||
30.0f, 1.0f, 1.0f, 0.0f, color, 3);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
const char* get_build_number() {
|
||||
return SHORTVERSION " latest " __DATE__ " " __TIME__;
|
||||
}
|
||||
|
||||
class component final : public component_interface {
|
||||
public:
|
||||
void post_load() override {
|
||||
@ -122,6 +127,8 @@ public:
|
||||
|
||||
small_font = reinterpret_cast<game::Font_s**>(0x192A0DC);
|
||||
scheduler::loop(branding_loop, scheduler::pipeline::renderer);
|
||||
scheduler::loop([] { left_side = !left_side; },
|
||||
scheduler::pipeline::renderer, 60s);
|
||||
}
|
||||
|
||||
static void register_branding_dvars() {
|
||||
|
5
src/client/component/branding.hpp
Normal file
5
src/client/component/branding.hpp
Normal file
@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
namespace branding {
|
||||
const char* get_build_number();
|
||||
}
|
@ -2,7 +2,6 @@
|
||||
#include "loader/component_loader.hpp"
|
||||
#include "game/dvars.hpp"
|
||||
#include "game/engine/scoped_critical_section.hpp"
|
||||
#include "game/engine/large_local.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
|
||||
@ -14,14 +13,15 @@ namespace {
|
||||
void com_assert_f() { assert("a" && false); }
|
||||
|
||||
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 from_ospath[MAX_PATH]{};
|
||||
const char* bug;
|
||||
const char* bug{};
|
||||
|
||||
if (!*game::logfile) {
|
||||
game::Com_PrintError(game::CON_CHANNEL_ERROR,
|
||||
"CopyFile failed: logfile wasn't opened\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (params.size() == 2) {
|
||||
@ -31,10 +31,11 @@ void com_bug_f(const command::params& params) {
|
||||
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::SCOPED_CRITSECT_NORMAL);
|
||||
game::engine::scoped_critical_section lock(game::CRITSECT_CONSOLE,
|
||||
game::SCOPED_CRITSECT_NORMAL);
|
||||
|
||||
if (*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);
|
||||
game::Com_OpenLogFile();
|
||||
|
||||
lock.leave_crit_sect();
|
||||
|
||||
if (!result) {
|
||||
game::Com_PrintError(game::CON_CHANNEL_ERROR, "CopyFile failed(%d) %s %s\n",
|
||||
GetLastError(), "console.log", new_file_name);
|
||||
@ -55,7 +58,7 @@ void com_bug_f(const command::params& params) {
|
||||
}
|
||||
|
||||
void com_bug_name_inc_f() {
|
||||
char buf[260]{};
|
||||
char buf[MAX_PATH]{};
|
||||
|
||||
if (std::strlen(dvars::bug_name->current.string) < 4) {
|
||||
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);
|
||||
sprintf_s(buf, "bug%d", n + 1);
|
||||
game::Com_sprintf(buf, sizeof(buf), "bug%d", n + 1);
|
||||
game::Dvar_SetString(dvars::bug_name, buf);
|
||||
}
|
||||
|
||||
void g_print_fast_file_errors(const char* fastfile) {
|
||||
assert(fastfile);
|
||||
|
||||
game::engine::large_local rawfile_buf_large_local(0x18000);
|
||||
auto* rawfile_buf = static_cast<char*>(rawfile_buf_large_local.get_buf());
|
||||
const auto rawfile_buf_large = std::make_unique<char[]>(0x18000);
|
||||
auto* rawfile_buf = rawfile_buf_large.get();
|
||||
|
||||
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",
|
||||
fastfile);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -22,7 +22,11 @@ public:
|
||||
dvar::override::register_bool("intro", false, game::DVAR_ARCHIVE);
|
||||
dvar::override::register_float("cg_fov", 65.0f, 65.0f, 160.0f,
|
||||
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_bool("com_filter_output", true,
|
||||
game::DVAR_ARCHIVE);
|
||||
|
||||
#ifdef _DEBUG
|
||||
dvar::override::register_bool("sv_cheats", true, game::DVAR_NONE);
|
||||
|
@ -11,27 +11,27 @@ bool is_using_mods() {
|
||||
}
|
||||
|
||||
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]{};
|
||||
switch (source) {
|
||||
case game::FFD_DEFAULT:
|
||||
(void)sprintf_s(filename, size, "%s\\%s%s.ff",
|
||||
std::filesystem::current_path().string().c_str(),
|
||||
game::Sys_GetMapZoneDir(zone_name), zone_name);
|
||||
(void)game::Com_sprintf(filename, size, "%s\\%s%s.ff",
|
||||
std::filesystem::current_path().string().c_str(),
|
||||
game::Sys_GetMapZoneDir(zone_name), zone_name);
|
||||
break;
|
||||
case game::FFD_MOD_DIR:
|
||||
assert(is_using_mods());
|
||||
|
||||
(void)sprintf_s(filename, size, "%s\\%s\\%s.ff",
|
||||
std::filesystem::current_path().string().c_str(),
|
||||
(*dvars::fs_gameDirVar)->current.string, zone_name);
|
||||
(void)game::Com_sprintf(filename, size, "%s\\%s\\%s.ff",
|
||||
std::filesystem::current_path().string().c_str(),
|
||||
(*dvars::fs_gameDirVar)->current.string, zone_name);
|
||||
break;
|
||||
case game::FFD_USER_MAP:
|
||||
game::I_strncpyz(user_map, zone_name, sizeof(user_map));
|
||||
|
||||
(void)sprintf_s(filename, size, "%s\\%s\\%s\\%s.ff",
|
||||
std::filesystem::current_path().string().c_str(),
|
||||
"usermaps", user_map, zone_name);
|
||||
(void)game::Com_sprintf(filename, size, "%s\\%s\\%s\\%s.ff",
|
||||
std::filesystem::current_path().string().c_str(),
|
||||
"usermaps", user_map, zone_name);
|
||||
break;
|
||||
default:
|
||||
assert(false && "inconceivable");
|
||||
|
@ -1,11 +1,13 @@
|
||||
#include <std_include.hpp>
|
||||
#include "loader/component_loader.hpp"
|
||||
#include "game/game.hpp"
|
||||
#include "game/dvars.hpp"
|
||||
|
||||
#include <utils/hook.hpp>
|
||||
#include <utils/string.hpp>
|
||||
|
||||
#include "game_module.hpp"
|
||||
#include "filesystem.hpp"
|
||||
#include "game_module.hpp"
|
||||
|
||||
namespace filesystem {
|
||||
namespace {
|
||||
@ -15,6 +17,56 @@ const char* sys_default_install_path_stub() {
|
||||
}
|
||||
} // 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,
|
||||
const std::string& extension) {
|
||||
std::vector<std::string> file_list;
|
||||
|
@ -1,6 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
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,
|
||||
const std::string& extension);
|
||||
std::string get_binary_directory();
|
||||
|
40
src/client/component/fov.cpp
Normal file
40
src/client/component/fov.cpp
Normal 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)
|
76
src/client/component/free_move.cpp
Normal file
76
src/client/component/free_move.cpp
Normal 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)
|
@ -28,7 +28,7 @@ void g_scr_log_print() {
|
||||
break;
|
||||
}
|
||||
|
||||
strncat_s(string, psz_token, _TRUNCATE);
|
||||
game::I_strncat(string, sizeof(string), psz_token);
|
||||
}
|
||||
|
||||
log_printf("%s", string);
|
||||
@ -83,12 +83,14 @@ void log_printf(const char* fmt, ...) {
|
||||
}
|
||||
|
||||
va_start(ap, fmt);
|
||||
vsnprintf_s(string2, _TRUNCATE, fmt, ap);
|
||||
vsnprintf(string2, sizeof(string2), fmt, ap);
|
||||
va_end(ap);
|
||||
string2[sizeof(string2) - 1] = '\0';
|
||||
|
||||
const auto time = game::level->time / 1000;
|
||||
const auto len = sprintf_s(string, "%3i:%i%i %s", time / 60, time % 60 / 10,
|
||||
time % 60 % 10, string2);
|
||||
const auto len =
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -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(
|
||||
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 = source_buf;
|
||||
auto* dest = source_buf2;
|
||||
@ -525,8 +525,9 @@ unsigned int load_script_internal_stub(const char* filename,
|
||||
game::GetNewVariable(game::scrCompilePub->loadedscripts, name);
|
||||
game::SL_RemoveRefToString(name);
|
||||
|
||||
sprintf_s(ext_filename, "%s.gsc",
|
||||
game::SL_ConvertToString(static_cast<unsigned short>(name)));
|
||||
game::Com_sprintf(
|
||||
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* source_buffer = add_source_buffer(
|
||||
@ -837,8 +838,9 @@ void compile_error(unsigned int source_pos, const char* msg, ...) {
|
||||
va_list argptr;
|
||||
|
||||
va_start(argptr, msg);
|
||||
vsnprintf_s(text, _TRUNCATE, msg, argptr);
|
||||
(void)vsnprintf(text, sizeof(text), msg, argptr);
|
||||
va_end(argptr);
|
||||
text[sizeof(text) - 1] = '\0';
|
||||
|
||||
if (game::scrVarPub->evaluate) {
|
||||
if (!game::scrVarPub->error_message) {
|
||||
@ -892,8 +894,9 @@ void compile_error2(const char* code_pos, const char* msg, ...) {
|
||||
"******* script compile error *******\n");
|
||||
|
||||
va_start(argptr, msg);
|
||||
vsnprintf_s(text, _TRUNCATE, msg, argptr);
|
||||
(void)vsnprintf(text, sizeof(text), msg, argptr);
|
||||
va_end(argptr);
|
||||
text[sizeof(text) - 1] = '\0';
|
||||
|
||||
game::Com_PrintError(game::CON_CHANNEL_PARSERSCRIPT, "%s: ", text);
|
||||
|
||||
|
@ -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
|
||||
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
|
||||
|
||||
void add_function(const char* name, game::BuiltinFunction func, bool type) {
|
||||
@ -89,6 +94,11 @@ public:
|
||||
.install()
|
||||
->quick(); // Scr_GetMethod
|
||||
|
||||
// Patch buffer overflows
|
||||
utils::hook(0x5BD3B9, hud_elem_set_enum_string_stub, HOOK_CALL)
|
||||
.install()
|
||||
->quick();
|
||||
|
||||
add_functions();
|
||||
#ifdef GSC_DEBUG_FUNCTIONS
|
||||
add_debug_functions();
|
||||
|
@ -19,8 +19,8 @@ void load_scripts_from_folder(const char* dir) {
|
||||
game::Com_Printf(game::CON_CHANNEL_SERVER,
|
||||
"Scanning directory '%s' for custom GSC scripts...\n", dir);
|
||||
|
||||
strncpy_s(search_path, dir, _TRUNCATE);
|
||||
strncat_s(search_path, "/", _TRUNCATE);
|
||||
game::I_strncpyz(search_path, dir, sizeof(search_path));
|
||||
game::I_strncat(search_path, sizeof(search_path), "/");
|
||||
|
||||
auto num_files = 0;
|
||||
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",
|
||||
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) {
|
||||
continue;
|
||||
}
|
||||
|
64
src/client/component/log_file.cpp
Normal file
64
src/client/component/log_file.cpp
Normal 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)
|
@ -46,8 +46,8 @@ private:
|
||||
// Disable MP packet handler
|
||||
utils::hook::set<std::uint8_t>(0x65E717, 0xEB);
|
||||
|
||||
// Disable LSP packet handler
|
||||
utils::hook::set<std::uint8_t>(0x65E3A4, 0xEB);
|
||||
// Disable LSP packet handler (suppress print message)
|
||||
utils::hook::nop(0x65E3C4, 5);
|
||||
|
||||
// Avoid spam
|
||||
utils::hook(0x65E786, game::Com_DPrintf, HOOK_CALL).install()->quick();
|
||||
|
@ -31,6 +31,78 @@ const char* live_get_local_client_name_stub() {
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
class component final : public component_interface {
|
||||
@ -58,6 +130,32 @@ public:
|
||||
.install() // hook*
|
||||
->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();
|
||||
}
|
||||
|
||||
|
@ -11,7 +11,7 @@ namespace {
|
||||
void __declspec(naked) pm_step_slide_move_stub() {
|
||||
__asm {
|
||||
push eax;
|
||||
mov eax, dvars::pm_bounce;
|
||||
mov eax, dvars::pm_bounces;
|
||||
cmp byte ptr [eax + 0x10], 1;
|
||||
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,
|
||||
float* vel_out) {
|
||||
const auto length_squared_2d = vel_in[0] * vel_in[0] + vel_in[1] * vel_in[1];
|
||||
void __declspec(naked) pm_project_velocity_stub() {
|
||||
__asm {
|
||||
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) {
|
||||
vel_out[0] = vel_in[0];
|
||||
vel_out[1] = vel_in[1];
|
||||
vel_out[2] = vel_in[2];
|
||||
return;
|
||||
je force_bounce;
|
||||
|
||||
test ah, 0x5;
|
||||
jnp force_bounce;
|
||||
push 0x4761AF;
|
||||
ret;
|
||||
|
||||
force_bounce:
|
||||
push 0x4761CF;
|
||||
ret;
|
||||
}
|
||||
}
|
||||
|
||||
auto new_z = vel_in[0] * normal[0] + vel_in[1] * normal[1];
|
||||
new_z = -new_z / normal[2];
|
||||
void __declspec(naked) pm_project_velocity2_stub() {
|
||||
__asm {
|
||||
push eax;
|
||||
mov eax, dvars::pm_bouncesAllAngles;
|
||||
cmp byte ptr [eax + 0x10], 1;
|
||||
pop eax;
|
||||
|
||||
const auto length_scale =
|
||||
std::sqrtf((vel_in[2] * vel_in[2] + length_squared_2d) /
|
||||
(new_z * new_z + length_squared_2d));
|
||||
je force_bounce;
|
||||
|
||||
if (dvars::pm_bouncingAllAngles->current.enabled ||
|
||||
(length_scale < 1.f || new_z < 0.f || vel_in[2] > 0.f)) {
|
||||
vel_out[0] = vel_in[0] * length_scale;
|
||||
vel_out[1] = vel_in[1] * length_scale;
|
||||
vel_out[2] = new_z * length_scale;
|
||||
fld1;
|
||||
jmp go_back;
|
||||
|
||||
force_bounce:
|
||||
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() {
|
||||
__asm {
|
||||
push eax;
|
||||
@ -184,9 +345,16 @@ public:
|
||||
utils::hook(0x4E9054, pm_step_slide_move_stub, HOOK_JUMP)
|
||||
.install()
|
||||
->quick(); // PM_StepSlideMove
|
||||
utils::hook(0x4E90BE, pm_project_velocity_stub, HOOK_CALL)
|
||||
|
||||
utils::hook(0x4761AA, pm_project_velocity_stub, HOOK_JUMP)
|
||||
.install()
|
||||
->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)
|
||||
.install()
|
||||
@ -209,10 +377,33 @@ public:
|
||||
.install()
|
||||
->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)
|
||||
.install()
|
||||
->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();
|
||||
|
||||
@ -222,12 +413,16 @@ public:
|
||||
|
||||
static void register_dvars() {
|
||||
// clang-format off
|
||||
dvars::pm_bounce = game::Dvar_RegisterBool(
|
||||
"pm_bounce", false, game::DVAR_NONE, "CoD4 Bounces");
|
||||
dvars::pm_bouncingAllAngles = game::Dvar_RegisterBool(
|
||||
"pm_bouncingAllAngles", false, game::DVAR_NONE, "Enable bouncing from all angles");
|
||||
dvars::pm_bounces = game::Dvar_RegisterBool(
|
||||
"pm_bounces", false, game::DVAR_NONE, "CoD4 Bounces");
|
||||
dvars::pm_doubleBounces = game::Dvar_RegisterBool(
|
||||
"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(
|
||||
"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(
|
||||
"pm_rocketJumpScale", 64.0f, 0.0f, 1024.0f, game::DVAR_NONE, "");
|
||||
dvars::pm_playerCollision = game::Dvar_RegisterBool(
|
||||
@ -239,6 +434,8 @@ public:
|
||||
dvars::pm_bunnyHop = game::Dvar_RegisterBool("pm_bunnyHop",
|
||||
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_terrainEdgeBounces = game::Dvar_RegisterBool(
|
||||
"pm_terrainEdgeBounces", false, game::DVAR_NONE, "Bounces on terrain edges");
|
||||
// clang-format on
|
||||
}
|
||||
};
|
||||
|
@ -13,18 +13,34 @@
|
||||
#include "scheduler.hpp"
|
||||
|
||||
namespace {
|
||||
enum class ownership_state {
|
||||
success,
|
||||
unowned,
|
||||
nosteam,
|
||||
error,
|
||||
};
|
||||
|
||||
ownership_state state_;
|
||||
|
||||
utils::binary_resource runner_file(RUNNER, "iw4sp-runner.exe");
|
||||
|
||||
bool is_disabled() { return true; }
|
||||
} // namespace
|
||||
|
||||
class steam_proxy final : public component_interface {
|
||||
public:
|
||||
void post_load() override {
|
||||
if (is_disabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this->load_client();
|
||||
this->clean_up_on_error();
|
||||
|
||||
try {
|
||||
this->start_mod("iw4x-sp singleplayer", 10180);
|
||||
state_ = this->start_mod("iw4x-sp singleplayer", 10180);
|
||||
} catch (const std::exception& ex) {
|
||||
state_ = ownership_state::error;
|
||||
printf("Steam: %s\n", ex.what());
|
||||
}
|
||||
}
|
||||
@ -96,20 +112,27 @@ private:
|
||||
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<bool>("BIsSubscribedApp", app_id)) {
|
||||
#ifdef _DEBUG
|
||||
app_id = 480; // Spacewar
|
||||
#else
|
||||
return ownership_state::unowned;
|
||||
#endif
|
||||
}
|
||||
|
||||
this->client_utils_.invoke<void>("SetAppIDForCurrentPipe", app_id, false);
|
||||
@ -125,13 +148,15 @@ private:
|
||||
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<const unsigned int*>(mod_id) | 0x80000000;
|
||||
|
||||
this->client_user_.invoke<bool>("SpawnProcess", path.data(), cmd_line,
|
||||
our_directory, game_id.bits, title.data(),
|
||||
this->client_user_.invoke<bool>("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() {
|
||||
|
@ -3,15 +3,18 @@
|
||||
namespace dvars {
|
||||
const game::dvar_t* r_noBorder = nullptr;
|
||||
|
||||
const game::dvar_t* pm_bounce = nullptr;
|
||||
const game::dvar_t* pm_bouncingAllAngles = nullptr;
|
||||
const game::dvar_t* pm_bounces = 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_csStrafe = nullptr;
|
||||
const game::dvar_t* pm_rocketJumpScale = nullptr;
|
||||
const game::dvar_t* pm_playerCollision = nullptr;
|
||||
const game::dvar_t* pm_elevators = nullptr;
|
||||
const game::dvar_t* pm_disableLandingSlowdown = nullptr;
|
||||
const game::dvar_t* pm_bunnyHop = 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_drawVersionX = nullptr;
|
||||
@ -22,11 +25,21 @@ const game::dvar_t* bug_name = nullptr;
|
||||
const game::dvar_t* g_log = nullptr;
|
||||
|
||||
// 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 =
|
||||
reinterpret_cast<const game::dvar_t**>(0x1B2E1E8);
|
||||
|
||||
const game::dvar_t** sv_mapname =
|
||||
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 =
|
||||
reinterpret_cast<const game::dvar_t**>(0x145D690);
|
||||
@ -36,6 +49,8 @@ const game::dvar_t** com_developer =
|
||||
const game::dvar_t** com_developer_script =
|
||||
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 =
|
||||
reinterpret_cast<const game::dvar_t**>(0x1956138);
|
||||
} // namespace dvars
|
||||
|
@ -3,15 +3,18 @@
|
||||
namespace dvars {
|
||||
extern const game::dvar_t* r_noBorder;
|
||||
|
||||
extern const game::dvar_t* pm_bounce;
|
||||
extern const game::dvar_t* pm_bouncingAllAngles;
|
||||
extern const game::dvar_t* pm_bounces;
|
||||
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_csStrafe;
|
||||
extern const game::dvar_t* pm_rocketJumpScale;
|
||||
extern const game::dvar_t* pm_playerCollision;
|
||||
extern const game::dvar_t* pm_elevators;
|
||||
extern const game::dvar_t* pm_disableLandingSlowdown;
|
||||
extern const game::dvar_t* pm_bunnyHop;
|
||||
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_drawVersionX;
|
||||
@ -22,14 +25,21 @@ extern const game::dvar_t* bug_name;
|
||||
extern const game::dvar_t* g_log;
|
||||
|
||||
// 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** sv_mapname;
|
||||
extern const game::dvar_t** sv_cheats;
|
||||
|
||||
extern const game::dvar_t** version;
|
||||
|
||||
extern const game::dvar_t** com_developer;
|
||||
extern const game::dvar_t** com_developer_script;
|
||||
|
||||
extern const game::dvar_t** fs_homepath;
|
||||
extern const game::dvar_t** fs_gameDirVar;
|
||||
} // namespace dvars
|
||||
|
@ -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
|
@ -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
|
@ -3,7 +3,7 @@
|
||||
namespace game {
|
||||
int FS_FOpenFileReadForThread(const char* filename, int* file,
|
||||
FsThread thread) {
|
||||
const static DWORD FS_FOpenFileReadForThread_t = 0x630380;
|
||||
static const DWORD FS_FOpenFileReadForThread_t = 0x630380;
|
||||
int result{};
|
||||
|
||||
__asm {
|
||||
@ -23,7 +23,7 @@ int FS_FOpenFileReadForThread(const char* filename, int* file,
|
||||
}
|
||||
|
||||
void IN_KeyDown(kbutton_t* b) {
|
||||
const static DWORD IN_KeyDown_t = 0x57A350;
|
||||
static const DWORD IN_KeyDown_t = 0x57A350;
|
||||
|
||||
__asm {
|
||||
pushad;
|
||||
@ -36,7 +36,7 @@ void IN_KeyDown(kbutton_t* b) {
|
||||
}
|
||||
|
||||
void IN_KeyUp(kbutton_t* b) {
|
||||
const static DWORD IN_KeyUp_t = 0x57A3F0;
|
||||
static const DWORD IN_KeyUp_t = 0x57A3F0;
|
||||
|
||||
__asm {
|
||||
pushad;
|
||||
@ -94,7 +94,7 @@ HANDLE Sys_OpenFileReliable(const char* filename) {
|
||||
}
|
||||
|
||||
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{};
|
||||
|
||||
__asm {
|
||||
@ -112,7 +112,7 @@ int PC_Int_Parse(int handle, int* i) {
|
||||
}
|
||||
|
||||
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{};
|
||||
|
||||
__asm {
|
||||
@ -130,7 +130,7 @@ int PC_Float_Parse(int handle, float* f) {
|
||||
}
|
||||
|
||||
void Menu_FreeItemMemory(itemDef_s* item) {
|
||||
const static DWORD Menu_FreeItemMemory_t = 0x62B7E0;
|
||||
static const DWORD Menu_FreeItemMemory_t = 0x62B7E0;
|
||||
|
||||
__asm {
|
||||
pushad;
|
||||
@ -141,4 +141,6 @@ void Menu_FreeItemMemory(itemDef_s* item) {
|
||||
popad;
|
||||
}
|
||||
}
|
||||
|
||||
char* Com_GetCommandLine() { return reinterpret_cast<char*>(0x145D800); }
|
||||
} // namespace game
|
||||
|
@ -47,19 +47,7 @@ int PC_Float_Parse(int handle, float* f);
|
||||
|
||||
void Menu_FreeItemMemory(itemDef_s* item);
|
||||
|
||||
// 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;
|
||||
char* Com_GetCommandLine();
|
||||
} // namespace game
|
||||
|
||||
#include "symbols.hpp"
|
||||
|
@ -4,6 +4,20 @@
|
||||
#pragma warning(disable : 4324)
|
||||
|
||||
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 vec_t vec2_t[2];
|
||||
typedef vec_t vec3_t[3];
|
||||
@ -15,6 +29,13 @@ enum LocalClientNum_t {
|
||||
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 {
|
||||
unsigned __int16 entnum;
|
||||
unsigned __int16 classnum;
|
||||
@ -362,6 +383,11 @@ enum {
|
||||
VAR_INTEGER = 0x6,
|
||||
};
|
||||
|
||||
enum msgLocErrType_t {
|
||||
LOCMSG_SAFE = 0x0,
|
||||
LOCMSG_NOERR = 0x1,
|
||||
};
|
||||
|
||||
enum {
|
||||
CON_CHANNEL_DONT_FILTER = 0x0,
|
||||
CON_CHANNEL_ERROR = 0x1,
|
||||
@ -528,8 +554,8 @@ struct indent_s {
|
||||
};
|
||||
|
||||
struct source_s {
|
||||
char filename[64];
|
||||
char includepath[64];
|
||||
char filename[MAX_QPATH];
|
||||
char includepath[MAX_QPATH];
|
||||
punctuation_s* punctuations;
|
||||
script_s* scriptstack;
|
||||
token_s* tokens;
|
||||
@ -1738,6 +1764,109 @@ struct localization_t {
|
||||
struct Sys_File {
|
||||
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
|
||||
|
||||
#pragma warning(pop)
|
||||
|
@ -18,12 +18,17 @@ WEAK symbol<void()> Com_ServerPacketEvent{0x47FD30};
|
||||
WEAK symbol<void(const char* filename)> Com_BeginParseSession{0x4A5C90};
|
||||
WEAK symbol<void()> Com_EndParseSession{0x4D12C0};
|
||||
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};
|
||||
|
||||
// Con
|
||||
WEAK symbol<bool(const char* cmd)> Con_IsDvarCommand{0x4B6610};
|
||||
|
||||
// SV
|
||||
WEAK symbol<void(int clientNum, usercmd_s* cmd)> SV_ClientThink{0x4B43D0};
|
||||
|
||||
// ScrPlace
|
||||
WEAK symbol<ScreenPlacement*(int localClientNum)> ScrPlace_GetActivePlacement{
|
||||
0x4D2A60};
|
||||
@ -95,6 +100,9 @@ WEAK symbol<void(const char* dvarName, double value)> Dvar_SetFloatByName{
|
||||
0x497250};
|
||||
WEAK symbol<void(const char* dvarName, const char* value)> Dvar_SetStringByName{
|
||||
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, bool value)> Dvar_SetBool{0x4E57E0};
|
||||
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)>
|
||||
FS_BuildOSPath{0x4E48F0};
|
||||
WEAK symbol<void(const char* gameName)> FS_Startup{0x47AF20};
|
||||
WEAK symbol<int(const char* filename)> FS_FOpenTextFileWrite{0x4495F0};
|
||||
|
||||
// UI
|
||||
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 vertAlign, float scale, const float* color, int style)>
|
||||
UI_DrawText{0x40FC70};
|
||||
WEAK symbol<void(int localClientNum, const char* srcString, char* dstString,
|
||||
int dstBufferSize)>
|
||||
UI_ReplaceDirective{0x4BBD10};
|
||||
|
||||
// PC
|
||||
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)>
|
||||
PM_playerTrace{0x447B90};
|
||||
WEAK symbol<bool(const playerState_s* ps)> PM_IsSprinting{0x47CF70};
|
||||
WEAK symbol<void(playerState_s* ps)> Jump_ClearState{0x435A40};
|
||||
|
||||
// Live
|
||||
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<void(char* dest, const char* src, int destsize)> I_strncpyz{
|
||||
0x416920};
|
||||
WEAK symbol<void(char* dest, int size, const char* src)> I_strncat{0x45CA00};
|
||||
|
||||
WEAK symbol<void(field_t* edit)> Field_Clear{0x45C350};
|
||||
|
||||
@ -272,20 +286,26 @@ WEAK symbol<int(const char* string)> StringTable_HashString{0x498080};
|
||||
// Vec3
|
||||
WEAK symbol<void(const float* v, float scale, const float* result)> Vec3Scale{
|
||||
0x429220};
|
||||
|
||||
// Renderer
|
||||
WEAK symbol<void(const char* text, int maxChars, Font_s* font, float x, float y,
|
||||
float xScale, float yScale, float rotation, const float* color,
|
||||
int style)>
|
||||
R_AddCmdDrawText{0x50E7A0};
|
||||
|
||||
// Renderer
|
||||
WEAK symbol<int(const char* text, int maxChars, Font_s* font)> R_TextWidth{
|
||||
0x508960};
|
||||
|
||||
WEAK symbol<const char*(const char* pszInputBuffer, const char* pszMessageType,
|
||||
msgLocErrType_t errType)>
|
||||
SEH_LocalizeTextMessage{0x4CD8D0};
|
||||
|
||||
// Variables
|
||||
WEAK symbol<CmdArgs> cmd_args{0x144FED0};
|
||||
WEAK symbol<CmdArgs> sv_cmd_args{0x145ABA0};
|
||||
WEAK symbol<gentity_s> g_entities{0xEAAC38};
|
||||
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<scrVarPub_t> scrVarPub{0x18E7508};
|
||||
@ -306,11 +326,12 @@ WEAK symbol<int> g_minLargeLocalRightPos{0x195AB00};
|
||||
|
||||
WEAK symbol<unsigned long> g_dwTlsIndex{0x1BFC750};
|
||||
|
||||
WEAK symbol<int> com_frameTime{0x145EC7C};
|
||||
|
||||
WEAK symbol<bool> cin_skippable{0x73264C};
|
||||
|
||||
WEAK symbol<int> com_frameTime{0x145EC7C};
|
||||
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<ConDrawInputGlob> conDrawInputGlob{0x86E788};
|
||||
@ -336,5 +357,5 @@ WEAK symbol<void*> DB_GetXAssetSizeHandlers{0x733408};
|
||||
WEAK symbol<void*> DB_XAssetPool{0x7337F8};
|
||||
WEAK symbol<unsigned int> g_poolSize{0x733510};
|
||||
|
||||
WEAK symbol<localization_t> localization{0x19ff820};
|
||||
WEAK symbol<localization_t> localization{0x19FF820};
|
||||
} // namespace game
|
||||
|
@ -10,21 +10,33 @@
|
||||
#define SP_XLABS_HASH \
|
||||
"05D499D77028859D4BA30C852DA85CCA5F02678B22AEA9E27D7C56973B14A0BC"
|
||||
|
||||
#define SP_ALTER_HASH \
|
||||
"9480909B18CF5A4EC483C989AAFCE929D2172C5F9448F8C23B723683D1A43565"
|
||||
|
||||
namespace binary_loader {
|
||||
namespace {
|
||||
std::string load_base() {
|
||||
std::string path = "iw4sp.exe";
|
||||
|
||||
if (utils::io::file_exists("data/iw4sp.exe")) {
|
||||
path = "data/iw4sp.exe";
|
||||
}
|
||||
|
||||
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)!");
|
||||
}
|
||||
|
||||
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(
|
||||
"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;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
std::string load() { return load_base(); }
|
||||
} // namespace binary_loader
|
||||
|
@ -91,8 +91,8 @@ public:
|
||||
PIMAGE_DOS_HEADER get_dos_header() const;
|
||||
PIMAGE_OPTIONAL_HEADER get_optional_header() const;
|
||||
|
||||
void** get_iat_entry(const std::string& module_name,
|
||||
const std::string& proc_name) const;
|
||||
[[nodiscard]] void** get_iat_entry(const std::string& module_name,
|
||||
const std::string& proc_name) const;
|
||||
|
||||
static void set_dll_directory(const std::string& directory);
|
||||
static std::string get_dll_directory();
|
||||
@ -101,6 +101,49 @@ private:
|
||||
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();
|
||||
|
||||
__declspec(noreturn) void raise_hard_exception();
|
||||
|
20
src/common/utils/properties.cpp
Normal file
20
src/common/utils/properties.cpp
Normal 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
|
6
src/common/utils/properties.hpp
Normal file
6
src/common/utils/properties.hpp
Normal file
@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
#include <filesystem>
|
||||
|
||||
namespace utils::properties {
|
||||
std::filesystem::path get_appdata_path();
|
||||
}
|
@ -1,16 +1,16 @@
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <Windows.h>
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
|
||||
#include "debugger.hpp"
|
||||
|
||||
int WINAPI WinMain(HINSTANCE, HINSTANCE, PSTR, int) {
|
||||
const auto* const command = "-proc ";
|
||||
const char* parent_proc = std::strstr(GetCommandLineA(), command);
|
||||
|
||||
if (parent_proc) {
|
||||
const auto pid = DWORD(atoi(parent_proc + std::strlen(command)));
|
||||
auto* const process_handle = OpenProcess(SYNCHRONIZE, FALSE, pid);
|
||||
const auto pid = atoi(parent_proc + std::strlen(command));
|
||||
auto* const process_handle =
|
||||
OpenProcess(SYNCHRONIZE, FALSE, static_cast<DWORD>(pid));
|
||||
if (process_handle) {
|
||||
WaitForSingleObject(process_handle, INFINITE);
|
||||
CloseHandle(process_handle);
|
||||
|
Loading…
x
Reference in New Issue
Block a user