This commit is contained in:
quaK 2022-05-20 21:59:24 +03:00
parent a7537934f0
commit 1bdcdba1a2
26 changed files with 2244 additions and 18 deletions

View File

@ -308,12 +308,7 @@ targetname "iw7-mod"
pchheader "std_include.hpp"
pchsource "src/client/std_include.cpp"
if _OPTIONS["no-inject-host-as-lib"] then
linkoptions {"/IGNORE:4254", "/DYNAMICBASE:NO", "/SAFESEH:NO", "/LARGEADDRESSAWARE", "/LAST:.main", "/PDBCompress"}
else
defines {"INJECT_HOST_AS_LIB"}
linkoptions {"/IGNORE:4254", "/SAFESEH:NO", "/LARGEADDRESSAWARE", "/PDBCompress"}
end
linkoptions {"/IGNORE:4254", "/DYNAMICBASE:NO", "/SAFESEH:NO", "/LARGEADDRESSAWARE", "/LAST:.main", "/PDBCompress"}
files {"./src/client/**.rc", "./src/client/**.hpp", "./src/client/**.cpp", "./src/client/resources/**.*"}

View File

@ -1,6 +1,6 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
//#include "scheduler.hpp"
#include "scheduler.hpp"
#include "game/game.hpp"
#include <utils/hook.hpp>
@ -147,7 +147,7 @@ namespace arxan
void post_load() override
{
hide_being_debugged();
//scheduler::loop(hide_being_debugged, scheduler::pipeline::async);
scheduler::loop(hide_being_debugged, scheduler::pipeline::async);
const utils::nt::library ntdll("ntdll.dll");
nt_close_hook.create(ntdll.get_proc<void*>("NtClose"), nt_close_stub);

View File

@ -0,0 +1,56 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "localized_strings.hpp"
#include "scheduler.hpp"
#include "version.hpp"
#include "game/game.hpp"
//#include "dvars.hpp"
#include <utils/hook.hpp>
#include <utils/string.hpp>
// fonts/default.otf, fonts/defaultBold.otf, fonts/fira_mono_regular.ttf, fonts/fira_mono_bold.ttf
namespace branding
{
namespace
{
utils::hook::detour ui_get_formatted_build_number_hook;
float color[4] = { 0.666f, 0.666f, 0.666f, 0.666f };
const char* ui_get_formatted_build_number_stub()
{
const auto* const build_num = ui_get_formatted_build_number_hook.invoke<const char*>();
return utils::string::va("%s (%s)", VERSION, build_num);
}
}
class component final : public component_interface
{
public:
void post_unpack() override
{
localized_strings::override("LUA_MENU_MULTIPLAYER_CAPS", "IW7-MOD: MULTIPLAYER");
localized_strings::override("LUA_MENU_ALIENS_CAPS", "IW7-MOD: ZOMBIES");
//dvars::override::set_string("version", utils::string::va("H1-Mod %s", VERSION));
//ui_get_formatted_build_number_hook.create(0x1DF300_b, ui_get_formatted_build_number_stub); can't find
scheduler::loop([]()
{
const auto font = game::R_RegisterFont("fonts/fira_mono_bold.ttf", 20);
if (font)
{
game::R_AddCmdDrawText("IW7-Mod: " VERSION, 0x7FFFFFFF, font, 10.f,
5.f + static_cast<float>(font->pixelHeight), 1.f, 1.f, 0.0f, color, 0);
}
}, scheduler::pipeline::renderer);
}
};
}
REGISTER_COMPONENT(branding::component)

View File

@ -0,0 +1,315 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "command.hpp"
#include "console.hpp"
#include "game_console.hpp"
//#include "fastfiles.hpp"
#include "scheduler.hpp"
#include "game/game.hpp"
#include "game/dvars.hpp"
#include <utils/hook.hpp>
#include <utils/string.hpp>
#include <utils/memory.hpp>
#include <utils/io.hpp>
namespace command
{
namespace
{
utils::hook::detour client_command_hook;
utils::hook::detour parse_commandline_hook;
std::unordered_map<std::string, std::function<void(params&)>> handlers;
std::unordered_map<std::string, std::function<void(int, params_sv&)>> handlers_sv;
void main_handler()
{
params params = {};
const auto command = utils::string::to_lower(params[0]);
if (handlers.find(command) != handlers.end())
{
handlers[command](params);
}
}
void client_command(const int client_num)
{
params_sv params = {};
const auto command = utils::string::to_lower(params[0]);
if (handlers_sv.find(command) != handlers_sv.end())
{
handlers_sv[command](client_num, params);
}
client_command_hook.invoke<void>(client_num);
}
// Shamelessly stolen from Quake3
// https://github.com/id-Software/Quake-III-Arena/blob/dbe4ddb10315479fc00086f08e25d968b4b43c49/code/qcommon/common.c#L364
void parse_command_line()
{
static auto parsed = false;
if (parsed)
{
return;
}
static std::string comand_line_buffer = GetCommandLineA();
auto* command_line = comand_line_buffer.data();
auto& com_num_console_lines = *reinterpret_cast<int*>(0x6006DB0_b);
auto* com_console_lines = reinterpret_cast<char**>(0x6006DC0_b);
auto inq = false;
com_console_lines[0] = command_line;
com_num_console_lines = 0;
while (*command_line)
{
if (*command_line == '"')
{
inq = !inq;
}
// look for a + separating character
// if commandLine came from a file, we might have real line seperators
if ((*command_line == '+' && !inq) || *command_line == '\n' || *command_line == '\r')
{
if (com_num_console_lines == 0x20) // MAX_CONSOLE_LINES
{
break;
}
com_console_lines[com_num_console_lines] = command_line + 1;
com_num_console_lines++;
*command_line = '\0';
}
command_line++;
}
parsed = true;
}
game::dvar_t* dvar_command_stub()
{
const params args;
if (args.size() <= 0)
{
return 0;
}
const auto dvar = game::Dvar_FindVar(args[0]);
if (dvar)
{
if (args.size() == 1)
{
const auto current = game::Dvar_ValueToString(dvar, dvar->current);
const auto reset = game::Dvar_ValueToString(dvar, dvar->reset);
console::info("\"%s\" is: \"%s\" default: \"%s\" hash: 0x%08lX type: %i\n",
args[0], current, reset, dvar->hash, dvar->type);
const auto dvar_info = dvars::dvar_get_description(dvar);
if (!dvar_info.empty())
console::info("%s\n", dvar_info.data());
console::info(" %s\n", dvars::dvar_get_domain(dvar->type, dvar->domain).data());
}
else
{
char command[0x1000] = { 0 };
game::Dvar_GetCombinedString(command, 1);
game::Dvar_SetCommand(args[0], command);
}
return dvar;
}
return 0;
}
void client_println(int client_num, const std::string& text)
{
game::SV_GameSendServerCommand(client_num, game::SV_CMD_RELIABLE,
utils::string::va("f \"%s\"", text.data()));
}
bool check_cheats(int client_num)
{
if (!game::Dvar_FindVar("sv_cheats")->current.enabled)
{
client_println(client_num, "Cheats are not enabled on this server");
return false;
}
return true;
}
}
params::params()
: nesting_(game::cmd_args->nesting)
{
}
int params::size() const
{
return game::cmd_args->argc[this->nesting_];
}
const char* params::get(const int index) const
{
if (index >= this->size())
{
return "";
}
return game::cmd_args->argv[this->nesting_][index];
}
std::string params::join(const int index) const
{
std::string result = {};
for (auto i = index; i < this->size(); i++)
{
if (i > index) result.append(" ");
result.append(this->get(i));
}
return result;
}
std::vector<std::string> params::get_all() const
{
std::vector<std::string> params_;
for (auto i = 0; i < this->size(); i++)
{
params_.push_back(this->get(i));
}
return params_;
}
params_sv::params_sv()
: nesting_(game::sv_cmd_args->nesting)
{
}
int params_sv::size() const
{
return game::sv_cmd_args->argc[this->nesting_];
}
const char* params_sv::get(const int index) const
{
if (index >= this->size())
{
return "";
}
return game::sv_cmd_args->argv[this->nesting_][index];
}
std::string params_sv::join(const int index) const
{
std::string result = {};
for (auto i = index; i < this->size(); i++)
{
if (i > index) result.append(" ");
result.append(this->get(i));
}
return result;
}
std::vector<std::string> params_sv::get_all() const
{
std::vector<std::string> params_;
for (auto i = 0; i < this->size(); i++)
{
params_.push_back(this->get(i));
}
return params_;
}
void add_raw(const char* name, void (*callback)())
{
game::Cmd_AddCommandInternal(name, callback, utils::memory::get_allocator()->allocate<game::cmd_function_s>());
}
void add_test(const char* name, void (*callback)())
{
static game::cmd_function_s cmd_test;
return game::Cmd_AddCommandInternal(name, callback, &cmd_test);
}
void add(const char* name, const std::function<void(const params&)>& callback)
{
const auto command = utils::string::to_lower(name);
if (handlers.find(command) == handlers.end())
add_raw(name, main_handler);
handlers[command] = callback;
}
void add(const char* name, const std::function<void()>& callback)
{
add(name, [callback](const params&)
{
callback();
});
}
void add_sv(const char* name, std::function<void(int, const params_sv&)> callback)
{
// doing this so the sv command would show up in the console
add_raw(name, nullptr);
const auto command = utils::string::to_lower(name);
if (handlers_sv.find(command) == handlers_sv.end())
handlers_sv[command] = std::move(callback);
}
void execute(std::string command, const bool sync)
{
command += "\n";
if (sync)
{
game::Cmd_ExecuteSingleCommand(0, 0, command.data());
}
else
{
game::Cbuf_AddText(0, command.data());
}
}
class component final : public component_interface
{
public:
void post_unpack() override
{
utils::hook::jump(0xBB1DC0_b, dvar_command_stub, true);
client_command_hook.create(0xB105D0_b, &client_command);
add_commands();
}
private:
static void add_commands()
{
add("quit", game::Com_Quit_f);
add("crash", []()
{
*reinterpret_cast<int*>(1) = 0;
});
}
};
}
REGISTER_COMPONENT(command::component)

View File

@ -0,0 +1,50 @@
#pragma once
namespace command
{
class params
{
public:
params();
int size() const;
const char* get(int index) const;
std::string join(int index) const;
std::vector<std::string> get_all() const;
const char* operator[](const int index) const
{
return this->get(index); //
}
private:
int nesting_;
};
class params_sv
{
public:
params_sv();
int size() const;
const char* get(int index) const;
std::string join(int index) const;
std::vector<std::string> get_all() const;
const char* operator[](const int index) const
{
return this->get(index); //
}
private:
int nesting_;
};
void add_raw(const char* name, void (*callback)());
void add(const char* name, const std::function<void(const params&)>& callback);
void add(const char* name, const std::function<void()>& callback);
void add_sv(const char* name, std::function<void(int, const params_sv&)> callback);
void execute(std::string command, bool sync = false);
}

View File

@ -583,7 +583,7 @@ namespace demonware
void post_unpack() override
{
/*utils::hook::jump(0x1285040_b, bd_logger_stub, true);
utils::hook::jump(0x1285040_b, bd_logger_stub, true);
utils::hook::set<uint8_t>(0xB5BB96F_b, 0x0); // CURLOPT_SSL_VERIFYPEER
utils::hook::set<uint8_t>(0xB7C6CB1_b, 0xAF); // CURLOPT_SSL_VERIFYHOST
@ -628,7 +628,7 @@ namespace demonware
// auth
const char* auth = "http://%s:%d/auth/";
std::memset(reinterpret_cast<void*>(0x15E3600_b), 0, strlen(auth) + 1);
std::memcpy(reinterpret_cast<void*>(0x15E3600_b), auth, strlen(auth));*/
std::memcpy(reinterpret_cast<void*>(0x15E3600_b), auth, strlen(auth));
// utils::hook::set<uint8_t>(0x19F8C0_b, 0xC3); // SV_SendMatchData, not sure
//utils::hook::nop(0x19BB67_b, 5); // LiveStorage_SendMatchDataComplete
@ -663,4 +663,4 @@ namespace demonware
};
}
REGISTER_COMPONENT(demonware::component)
//REGISTER_COMPONENT(demonware::component)

View File

@ -0,0 +1,153 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "dvars.hpp"
#include "game/game.hpp"
#include "game/dvars.hpp"
#include <utils/hook.hpp>
namespace dvars
{
struct dvar_base
{
unsigned int flags{};
};
struct dvar_bool : dvar_base
{
bool value{};
};
struct dvar_float : dvar_base
{
float value{};
float min{};
float max{};
};
struct dvar_vector2 : dvar_base
{
float x{};
float y{};
float min{};
float max{};
};
struct dvar_vector3 : dvar_base
{
float x{};
float y{};
float z{};
float min{};
float max{};
};
struct dvar_int : dvar_base
{
int value{};
int min{};
int max{};
};
struct dvar_string : dvar_base
{
std::string value{};
};
namespace
{
template <typename T>
T* find_dvar(std::unordered_map<std::string, T>& map, const std::string& name)
{
auto i = map.find(name);
if (i != map.end())
{
return &i->second;
}
return nullptr;
}
bool find_dvar(std::unordered_set<std::string>& set, const std::string& name)
{
return set.find(name) != set.end();
}
}
namespace disable
{
}
namespace override
{
}
std::unordered_map<std::string, std::function<void()>> dvar_on_register_function_map;
void on_register(const std::string& name, const std::function<void()>& callback)
{
dvar_on_register_function_map[name] = callback;
}
utils::hook::detour dvar_register_new_hook;
game::dvar_t* dvar_register_new_internal(const char* name, int hash, game::dvar_type type, unsigned int flags,
game::dvar_value* value, game::dvar_limits* domain, bool level, const char* description)
{
auto* dvar = dvar_register_new_hook.invoke<game::dvar_t*>(name, hash, type, flags, value, domain, level, description);
if (dvar)
{
if (name)
{
dvars::dvar_set_name(dvar, name);
}
if (description)
{
dvars::dvar_set_description(dvar, description);
}
}
if (dvar && name && dvar_on_register_function_map.find(name) != dvar_on_register_function_map.end())
{
dvar_on_register_function_map[name]();
dvar_on_register_function_map.erase(name);
}
return dvar;
}
utils::hook::detour dvar_reregister_hook;
void dvar_reregister(game::dvar_t* dvar, const char* name, game::dvar_type type, unsigned int flags,
game::dvar_value* value, game::dvar_limits* domain, bool level, const char* description)
{
dvar_reregister_hook.invoke<game::dvar_t*>(dvar, name, type, flags, value, domain, level, description);
if (dvar)
{
if (name)
{
dvars::dvar_set_name(dvar, name);
}
if (description)
{
dvars::dvar_set_description(dvar, description);
}
}
}
class component final : public component_interface
{
public:
void post_unpack() override
{
// the post_unpack happens too late for some dvars...
dvar_register_new_hook.create(0xCEBA60_b, dvar_register_new_internal);
dvar_reregister_hook.create(0xCEC210_b, dvar_reregister);
}
};
}
REGISTER_COMPONENT(dvars::component)

View File

@ -0,0 +1,30 @@
#pragma once
namespace dvars
{
namespace disable
{
void set_bool(const std::string& name);
void set_float(const std::string& name);
void set_int(const std::string& name);
void set_string(const std::string& name);
}
namespace override
{
void register_bool(const std::string& name, bool value, const unsigned int flags);
void register_float(const std::string& name, float value, float min, float max, const unsigned int flags);
void register_int(const std::string& name, int value, int min, int max, const unsigned int flags);
void register_string(const std::string& name, const std::string& value, const unsigned int flags);
void register_vec2(const std::string& name, float x, float y, float min, float max, const unsigned int flags);
void register_vec3(const std::string& name, float x, float y, float z, float min, float max, const unsigned int flags);
void set_bool(const std::string& name, bool boolean);
void set_float(const std::string& name, float fl);
void set_int(const std::string& name, int integer);
void set_string(const std::string& name, const std::string& string);
void set_from_string(const std::string& name, const std::string& value);
}
void on_register(const std::string& name, const std::function<void()>& callback);
}

View File

@ -0,0 +1,791 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "game_console.hpp"
#include "command.hpp"
#include "console.hpp"
#include "scheduler.hpp"
#include "game/game.hpp"
#include "game/dvars.hpp"
#include <utils/string.hpp>
#include <utils/hook.hpp>
#include <utils/concurrency.hpp>
#include "version.hpp"
#define console_font game::R_RegisterFont("fonts/fira_mono_regular.ttf", 18)
#define material_white game::Material_RegisterHandle("white")
namespace game_console
{
namespace
{
struct console_globals
{
float x{};
float y{};
float left_x{};
float font_height{};
bool may_auto_complete{};
char auto_complete_choice[64]{};
int info_line_count{};
};
using output_queue = std::deque<std::string>;
struct ingame_console
{
char buffer[256]{};
int cursor{};
int font_height{};
int visible_line_count{};
int visible_pixel_width{};
float screen_min[2]{}; //left & top
float screen_max[2]{}; //right & bottom
console_globals globals{};
bool output_visible{};
int display_line_offset{};
int line_count{};
utils::concurrency::container<output_queue, std::recursive_mutex> output{};
};
ingame_console con{};
std::int32_t history_index = -1;
std::deque<std::string> history{};
std::string fixed_input{};
std::vector<std::string> matches{};
float color_white[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
float color_title[4] = { 0.9f, 0.0f, 0.9f, 1.0f };
void clear()
{
strncpy_s(con.buffer, "", sizeof(con.buffer));
con.cursor = 0;
fixed_input = "";
matches.clear();
}
void print_internal(const std::string& data)
{
con.output.access([&](output_queue& output)
{
if (con.visible_line_count > 0
&& con.display_line_offset == (output.size() - con.visible_line_count))
{
con.display_line_offset++;
}
output.push_back(data);
if (output.size() > 512)
{
output.pop_front();
}
});
}
void toggle_console()
{
clear();
con.output_visible = false;
*game::keyCatchers ^= 1;
}
void toggle_console_output()
{
con.output_visible = con.output_visible == 0;
}
void check_resize()
{
con.screen_min[0] = 6.0f;
con.screen_min[1] = 6.0f;
con.screen_max[0] = game::ScrPlace_GetViewPlacement()->realViewportSize[0] - 6.0f;
con.screen_max[1] = game::ScrPlace_GetViewPlacement()->realViewportSize[1] - 6.0f;
if (console_font)
{
con.font_height = console_font->pixelHeight;
con.visible_line_count = static_cast<int>((con.screen_max[1] - con.screen_min[1] - (con.font_height * 2)
) -
24.0f) / con.font_height;
con.visible_pixel_width = static_cast<int>(((con.screen_max[0] - con.screen_min[0]) - 10.0f) - 18.0f);
}
else
{
con.font_height = 0;
con.visible_line_count = 0;
con.visible_pixel_width = 0;
}
}
void draw_box(const float x, const float y, const float w, const float h, float* color)
{
game::vec4_t dark_color;
dark_color[0] = color[0] * 0.5f;
dark_color[1] = color[1] * 0.5f;
dark_color[2] = color[2] * 0.5f;
dark_color[3] = color[3];
game::R_AddCmdDrawStretchPic(x, y, w, h, 0.0f, 0.0f, 0.0f, 0.0f, color, material_white, 0);
game::R_AddCmdDrawStretchPic(x, y, 2.0f, h, 0.0f, 0.0f, 0.0f, 0.0f, dark_color, material_white, 0);
game::R_AddCmdDrawStretchPic((x + w) - 2.0f, y, 2.0f, h, 0.0f, 0.0f, 0.0f, 0.0f, dark_color,
material_white, 0);
game::R_AddCmdDrawStretchPic(x, y, w, 2.0f, 0.0f, 0.0f, 0.0f, 0.0f, dark_color, material_white, 0);
game::R_AddCmdDrawStretchPic(x, (y + h) - 2.0f, w, 2.0f, 0.0f, 0.0f, 0.0f, 0.0f, dark_color,
material_white, 0);
}
void draw_input_box(const int lines, float* color)
{
draw_box(
con.globals.x - 6.0f,
con.globals.y - 6.0f,
(con.screen_max[0] - con.screen_min[0]) - ((con.globals.x - 6.0f) - con.screen_min[0]),
(lines * con.globals.font_height) + 12.0f,
color);
}
void draw_input_text_and_over(const char* str, float* color)
{
game::R_AddCmdDrawText(str, 0x7FFFFFFF, console_font, con.globals.x,
con.globals.y + con.globals.font_height, 1.0f,
1.0f, 0.0f, color, 0);
con.globals.x = game::R_TextWidth(str, 0, console_font) + con.globals.x + 6.0f;
}
float draw_hint_box(const int lines, float* color, [[maybe_unused]] float offset_x = 0.0f,
[[maybe_unused]] float offset_y = 0.0f)
{
const auto _h = lines * con.globals.font_height + 12.0f;
const auto _y = con.globals.y - 3.0f + con.globals.font_height + 12.0f + offset_y;
const auto _w = (con.screen_max[0] - con.screen_min[0]) - ((con.globals.x - 6.0f) - con.screen_min[0]);
draw_box(con.globals.x - 6.0f, _y, _w, _h, color);
return _h;
}
void draw_hint_text(const int line, const char* text, float* color, const float offset_x = 0.0f, const float offset_y = 0.0f)
{
const auto _y = con.globals.font_height + con.globals.y + (con.globals.font_height * (line + 1)) + 15.0f + offset_y;
game::R_AddCmdDrawText(text, 0x7FFFFFFF, console_font, con.globals.x + offset_x, _y, 1.0f, 1.0f, 0.0f, color, 0);
}
void find_matches(std::string input, std::vector<std::string>& suggestions, const bool exact)
{
input = utils::string::to_lower(input);
for (int i = 0; i < *game::dvarCount; i++)
{
if (game::dvarPool[i])
{
std::string name = dvars::dvar_get_name(game::dvarPool[i]);
if (!name.empty())
{
std::string lower_name = utils::string::to_lower(name);
if (utils::string::match_compare(input, lower_name, exact))
{
suggestions.push_back(name);
}
if (exact && suggestions.size() > 1)
{
return;
}
}
}
}
if (suggestions.size() == 0 && game::Dvar_FindVar(input.data()))
{
suggestions.push_back(input);
}
game::cmd_function_s* cmd = (*game::cmd_functions);
while (cmd)
{
if (cmd->name)
{
std::string name = utils::string::to_lower(cmd->name);
if (utils::string::match_compare(input, name, exact))
{
suggestions.push_back(cmd->name);
}
if (exact && suggestions.size() > 1)
{
return;
}
}
cmd = cmd->next;
}
}
void draw_input()
{
con.globals.font_height = static_cast<float>(console_font->pixelHeight);
con.globals.x = con.screen_min[0] + 6.0f;
con.globals.y = con.screen_min[1] + 6.0f;
con.globals.left_x = con.screen_min[0] + 6.0f;
draw_input_box(1, dvars::con_inputBoxColor->current.vector);
draw_input_text_and_over("IW7-Mod: " VERSION ">", color_title);
con.globals.left_x = con.globals.x;
con.globals.auto_complete_choice[0] = 0;
game::R_AddCmdDrawTextWithCursor(con.buffer, 0x7FFFFFFF, console_font, 18, con.globals.x,
con.globals.y + con.globals.font_height, 1.0f, 1.0f, 0, color_white, 0,
con.cursor, '|');
// check if using a prefixed '/' or not
const auto input = con.buffer[1] && (con.buffer[0] == '/' || con.buffer[0] == '\\')
? std::string(con.buffer).substr(1)
: std::string(con.buffer);
if (!input.length())
{
return;
}
if (input != fixed_input)
{
matches.clear();
if (input.find(" ") != std::string::npos)
{
find_matches(input.substr(0, input.find(" ")), matches, true);
}
else
{
find_matches(input, matches, false);
}
fixed_input = input;
}
con.globals.may_auto_complete = false;
if (matches.size() > 24)
{
draw_hint_box(1, dvars::con_inputHintBoxColor->current.vector);
draw_hint_text(0, utils::string::va("%i matches (too many to show here)", matches.size()),
dvars::con_inputDvarMatchColor->current.vector);
}
else if (matches.size() == 1)
{
auto* const dvar = game::Dvar_FindVar(matches[0].data());
const auto line_count = dvar ? 3 : 1;
const auto height = draw_hint_box(line_count, dvars::con_inputHintBoxColor->current.vector);
draw_hint_text(0, matches[0].data(), dvar
? dvars::con_inputDvarMatchColor->current.vector
: dvars::con_inputCmdMatchColor->current.vector);
if (dvar)
{
const auto offset = (con.screen_max[0] - con.globals.x) / 4.f;
draw_hint_text(0, game::Dvar_ValueToString(dvar, dvar->current),
dvars::con_inputDvarValueColor->current.vector, offset);
draw_hint_text(1, " default", dvars::con_inputDvarInactiveValueColor->current.vector);
draw_hint_text(1, game::Dvar_ValueToString(dvar, dvar->reset),
dvars::con_inputDvarInactiveValueColor->current.vector, offset);
draw_hint_text(2, dvars::dvar_get_description(dvar).data(),
color_white, 0);
const auto offset_y = height + 3.f;
const auto line_count_ = dvar->type == game::dvar_type::enumeration
? dvar->domain.enumeration.stringCount + 1
: 1;
draw_hint_box(line_count_, dvars::con_inputHintBoxColor->current.vector, 0, offset_y);
draw_hint_text(0, dvars::dvar_get_domain(dvar->type, dvar->domain).data(),
dvars::con_inputCmdMatchColor->current.vector, 0, offset_y);
}
strncpy_s(con.globals.auto_complete_choice, matches[0].data(), 64);
con.globals.may_auto_complete = true;
}
else if (matches.size() > 1)
{
draw_hint_box(static_cast<int>(matches.size()), dvars::con_inputHintBoxColor->current.vector);
const auto offset = (con.screen_max[0] - con.globals.x) / 4.f;
for (size_t i = 0; i < matches.size(); i++)
{
auto* const dvar = game::Dvar_FindVar(matches[i].data());
draw_hint_text(static_cast<int>(i), matches[i].data(),
dvar
? dvars::con_inputDvarMatchColor->current.vector
: dvars::con_inputCmdMatchColor->current.vector);
if (dvar)
{
draw_hint_text(static_cast<int>(i), game::Dvar_ValueToString(dvar, dvar->current),
dvars::con_inputDvarValueColor->current.vector, offset);
draw_hint_text(static_cast<int>(i), dvars::dvar_get_description(dvar).data(),
dvars::con_inputDvarValueColor->current.vector, offset * 1.5f);
}
}
strncpy_s(con.globals.auto_complete_choice, matches[0].data(), 64);
con.globals.may_auto_complete = true;
}
}
void draw_output_scrollbar(const float x, float y, const float width, const float height, output_queue& output)
{
const auto _x = (x + width) - 10.0f;
draw_box(_x, y, 10.0f, height, dvars::con_outputBarColor->current.vector);
auto _height = height;
if (output.size() > con.visible_line_count)
{
const auto percentage = static_cast<float>(con.visible_line_count) / output.size();
_height *= percentage;
const auto remainingSpace = height - _height;
const auto percentageAbove = static_cast<float>(con.display_line_offset) / (output.size() - con.
visible_line_count);
y = y + (remainingSpace * percentageAbove);
}
draw_box(_x, y, 10.0f, _height, dvars::con_outputSliderColor->current.vector);
}
void draw_output_text(const float x, float y, output_queue& output)
{
const auto offset = output.size() >= con.visible_line_count
? 0.0f
: (con.font_height * (con.visible_line_count - output.size()));
for (auto i = 0; i < con.visible_line_count; i++)
{
y = console_font->pixelHeight + y;
const auto index = i + con.display_line_offset;
if (index >= output.size())
{
break;
}
game::R_AddCmdDrawText(output.at(index).data(), 0x7FFF, console_font, x, y + offset, 1.0f, 1.0f,
0.0f, color_white, 0);
}
}
void draw_output_window()
{
con.output.access([](output_queue& output)
{
draw_box(con.screen_min[0], con.screen_min[1] + 32.0f, con.screen_max[0] - con.screen_min[0],
(con.screen_max[1] - con.screen_min[1]) - 32.0f, dvars::con_outputWindowColor->current.vector);
const auto x = con.screen_min[0] + 6.0f;
const auto y = (con.screen_min[1] + 32.0f) + 6.0f;
const auto width = (con.screen_max[0] - con.screen_min[0]) - 12.0f;
const auto height = ((con.screen_max[1] - con.screen_min[1]) - 32.0f) - 12.0f;
game::R_AddCmdDrawText("H1-Mod 1.15", 0x7FFFFFFF, console_font, x,
((height - 16.0f) + y) + console_font->pixelHeight, 1.0f, 1.0f, 0.0f, color_title, 0);
draw_output_scrollbar(x, y, width, height, output);
draw_output_text(x, y, output);
});
}
void draw_console()
{
check_resize();
if (*game::keyCatchers & 1)
{
if (!(*game::keyCatchers & 1))
{
con.output_visible = false;
}
if (con.output_visible)
{
draw_output_window();
}
draw_input();
}
}
}
void print_internal(const char* fmt, ...)
{
char va_buffer[0x200] = { 0 };
va_list ap;
va_start(ap, fmt);
vsprintf_s(va_buffer, fmt, ap);
va_end(ap);
const auto formatted = std::string(va_buffer);
const auto lines = utils::string::split(formatted, '\n');
for (const auto& line : lines)
{
print_internal(line);
}
}
void print(const int type, const std::string& data)
{
try
{
if (game::environment::is_dedi())
{
return;
}
}
catch (std::exception&)
{
return;
}
const auto lines = utils::string::split(data, '\n');
for (const auto& line : lines)
{
print_internal(type == console::con_type_info ? line : "^"s.append(std::to_string(type)).append(line));
}
}
bool console_char_event(const int local_client_num, const int key)
{
if (key == game::keyNum_t::K_GRAVE ||
key == game::keyNum_t::K_TILDE ||
key == '|' ||
key == '\\')
{
return false;
}
if (key > 127)
{
return true;
}
if (*game::keyCatchers & 1)
{
if (key == game::keyNum_t::K_TAB) // tab (auto complete)
{
if (con.globals.may_auto_complete)
{
const auto first_char = con.buffer[0];
clear();
if (first_char == '\\' || first_char == '/')
{
con.buffer[0] = first_char;
con.buffer[1] = '\0';
}
strncat_s(con.buffer, con.globals.auto_complete_choice, 64);
con.cursor = static_cast<int>(std::string(con.buffer).length());
if (con.cursor != 254)
{
con.buffer[con.cursor++] = ' ';
con.buffer[con.cursor] = '\0';
}
}
}
if (key == 'v' - 'a' + 1) // paste
{
const auto clipboard = utils::string::get_clipboard_data();
if (clipboard.empty())
{
return false;
}
for (size_t i = 0; i < clipboard.length(); i++)
{
console_char_event(local_client_num, clipboard[i]);
}
return false;
}
if (key == 'c' - 'a' + 1) // clear
{
clear();
con.line_count = 0;
con.display_line_offset = 0;
con.output.access([](output_queue& output)
{
output.clear();
});
history_index = -1;
history.clear();
return false;
}
if (key == 'h' - 'a' + 1) // backspace
{
if (con.cursor > 0)
{
memmove(con.buffer + con.cursor - 1, con.buffer + con.cursor,
strlen(con.buffer) + 1 - con.cursor);
con.cursor--;
}
return false;
}
if (key < 32)
{
return false;
}
if (con.cursor == 256 - 1)
{
return false;
}
memmove(con.buffer + con.cursor + 1, con.buffer + con.cursor, strlen(con.buffer) + 1 - con.cursor);
con.buffer[con.cursor] = static_cast<char>(key);
con.cursor++;
if (con.cursor == strlen(con.buffer) + 1)
{
con.buffer[con.cursor] = 0;
}
}
return true;
}
bool console_key_event(const int local_client_num, const int key, const int down)
{
if (key == game::keyNum_t::K_GRAVE || key == game::keyNum_t::K_TILDE)
{
if (!down)
{
return false;
}
const auto shift_down = game::playerKeys[local_client_num].keys[game::keyNum_t::K_SHIFT].down;
if (shift_down)
{
if (!(*game::keyCatchers & 1))
{
toggle_console();
}
toggle_console_output();
return false;
}
toggle_console();
return false;
}
if (*game::keyCatchers & 1)
{
if (down)
{
if (key == game::keyNum_t::K_UPARROW)
{
if (++history_index >= history.size())
{
history_index = static_cast<int>(history.size()) - 1;
}
clear();
if (history_index != -1)
{
strncpy_s(con.buffer, history.at(history_index).c_str(), 0x100);
con.cursor = static_cast<int>(strlen(con.buffer));
}
}
else if (key == game::keyNum_t::K_DOWNARROW)
{
if (--history_index < -1)
{
history_index = -1;
}
clear();
if (history_index != -1)
{
strncpy_s(con.buffer, history.at(history_index).c_str(), 0x100);
con.cursor = static_cast<int>(strlen(con.buffer));
}
}
if (key == game::keyNum_t::K_RIGHTARROW)
{
if (con.cursor < strlen(con.buffer))
{
con.cursor++;
}
return false;
}
if (key == game::keyNum_t::K_LEFTARROW)
{
if (con.cursor > 0)
{
con.cursor--;
}
return false;
}
//scroll through output
if (key == game::keyNum_t::K_MWHEELUP || key == game::keyNum_t::K_PGUP)
{
con.output.access([](output_queue& output)
{
if (output.size() > con.visible_line_count && con.display_line_offset > 0)
{
con.display_line_offset--;
}
});
}
else if (key == game::keyNum_t::K_MWHEELDOWN || key == game::keyNum_t::K_PGDN)
{
con.output.access([](output_queue& output)
{
if (output.size() > con.visible_line_count
&& con.display_line_offset < (output.size() - con.visible_line_count))
{
con.display_line_offset++;
}
});
}
if (key == game::keyNum_t::K_ENTER)
{
game::Cbuf_AddText(0, utils::string::va("%s \n", fixed_input.data()));
if (history_index != -1)
{
const auto itr = history.begin() + history_index;
if (*itr == con.buffer)
{
history.erase(history.begin() + history_index);
}
}
history.push_front(con.buffer);
console::info("]%s\n", con.buffer);
if (history.size() > 10)
{
history.erase(history.begin() + 10);
}
history_index = -1;
clear();
}
}
}
return true;
}
class component final : public component_interface
{
public:
void post_unpack() override
{
if (game::environment::is_dedi())
{
return;
}
scheduler::loop(draw_console, scheduler::pipeline::renderer);
// initialize our structs
con.cursor = 0;
con.visible_line_count = 0;
con.output_visible = false;
con.display_line_offset = 0;
con.line_count = 0;
strncpy_s(con.buffer, "", 256);
con.globals.x = 0.0f;
con.globals.y = 0.0f;
con.globals.left_x = 0.0f;
con.globals.font_height = 0.0f;
con.globals.may_auto_complete = false;
con.globals.info_line_count = 0;
strncpy_s(con.globals.auto_complete_choice, "", 64);
// add clear command
command::add("clear", [&]()
{
clear();
con.line_count = 0;
con.display_line_offset = 0;
con.output.access([](output_queue& output)
{
output.clear();
});
history_index = -1;
history.clear();
});
// add our dvars
dvars::con_inputBoxColor = game::Dvar_RegisterVec4("con_inputBoxColor", 0.2f, 0.2f, 0.2f, 0.9f, 0.0f, 1.0f,
game::DVAR_FLAG_SAVED,
"color of console input box");
dvars::con_inputHintBoxColor = game::Dvar_RegisterVec4("con_inputHintBoxColor", 0.3f, 0.3f, 0.3f, 1.0f,
0.0f, 1.0f,
game::DVAR_FLAG_SAVED, "color of console input hint box");
dvars::con_outputBarColor = game::Dvar_RegisterVec4("con_outputBarColor", 0.5f, 0.5f, 0.5f, 0.6f, 0.0f,
1.0f, game::DVAR_FLAG_SAVED,
"color of console output bar");
dvars::con_outputSliderColor = game::Dvar_RegisterVec4("con_outputSliderColor", 0.3f, 0.7f, 0.3f, 1.0f,
0.0f, 1.0f,
game::DVAR_FLAG_SAVED, "color of console output slider");
dvars::con_outputWindowColor = game::Dvar_RegisterVec4("con_outputWindowColor", 0.25f, 0.25f, 0.25f, 0.85f,
0.0f,
1.0f, game::DVAR_FLAG_SAVED, "color of console output window");
dvars::con_inputDvarMatchColor = game::Dvar_RegisterVec4("con_inputDvarMatchColor", 1.0f, 1.0f, 0.8f, 1.0f,
0.0f,
1.0f, game::DVAR_FLAG_SAVED, "color of console matched dvar");
dvars::con_inputDvarValueColor = game::Dvar_RegisterVec4("con_inputDvarValueColor", 1.0f, 1.0f, 0.8f, 1.0f,
0.0f,
1.0f, game::DVAR_FLAG_SAVED, "color of console matched dvar value");
dvars::con_inputDvarInactiveValueColor = game::Dvar_RegisterVec4(
"con_inputDvarInactiveValueColor", 0.8f, 0.8f,
0.8f, 1.0f, 0.0f, 1.0f, game::DVAR_FLAG_SAVED,
"color of console inactive dvar value");
dvars::con_inputCmdMatchColor = game::Dvar_RegisterVec4("con_inputCmdMatchColor", 0.80f, 0.80f, 1.0f, 1.0f,
0.0f,
1.0f, game::DVAR_FLAG_SAVED, "color of console matched command");
}
};
}
REGISTER_COMPONENT(game_console::component)

View File

@ -0,0 +1,7 @@
#pragma once
namespace game_console
{
bool console_char_event(int local_client_num, int key);
bool console_key_event(int local_client_num, int key, int down);
}

View File

@ -0,0 +1,54 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "game/game.hpp"
#include "game_console.hpp"
#include <utils/hook.hpp>
namespace input
{
namespace
{
utils::hook::detour cl_char_event_hook;
utils::hook::detour cl_key_event_hook;
void cl_char_event_stub(const int local_client_num, const int key)
{
if (!game_console::console_char_event(local_client_num, key))
{
return;
}
cl_char_event_hook.invoke<void>(local_client_num, key);
}
void cl_key_event_stub(const int local_client_num, const int key, const int down)
{
if (!game_console::console_key_event(local_client_num, key, down))
{
return;
}
cl_key_event_hook.invoke<void>(local_client_num, key, down);
}
}
class component final : public component_interface
{
public:
void post_unpack() override
{
if (game::environment::is_dedi())
{
return;
}
cl_char_event_hook.create(0x9A7350_b, cl_char_event_stub);
cl_key_event_hook.create(0x9A7980_b, cl_key_event_stub);
}
};
}
REGISTER_COMPONENT(input::component)

View File

@ -0,0 +1,52 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "localized_strings.hpp"
#include <utils/hook.hpp>
#include <utils/string.hpp>
#include <utils/concurrency.hpp>
#include "game/game.hpp"
namespace localized_strings
{
namespace
{
utils::hook::detour seh_string_ed_get_string_hook;
using localized_map = std::unordered_map<std::string, std::string>;
utils::concurrency::container<localized_map> localized_overrides;
const char* seh_string_ed_get_string(const char* reference)
{
return localized_overrides.access<const char*>([&](const localized_map& map)
{
const auto entry = map.find(reference);
if (entry != map.end())
{
return utils::string::va("%s", entry->second.data());
}
return seh_string_ed_get_string_hook.invoke<const char*>(reference);
});
}
}
void override(const std::string& key, const std::string& value)
{
localized_overrides.access([&](localized_map& map)
{
map[key] = value;
});
}
class component final : public component_interface
{
public:
void post_unpack() override
{
// Change some localized strings
seh_string_ed_get_string_hook.create(0xCBBB10_b, &seh_string_ed_get_string);
}
};
}
REGISTER_COMPONENT(localized_strings::component)

View File

@ -0,0 +1,6 @@
#pragma once
namespace localized_strings
{
void override(const std::string& key, const std::string& value);
}

View File

@ -0,0 +1,57 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "game/game.hpp"
#include "console.hpp"
#include <utils/hook.hpp>
namespace logger
{
namespace
{
void nullsub_6_stub(const char* msg, ...)
{
char buffer[2048];
{
va_list ap;
va_start(ap, msg);
vsnprintf_s(buffer, sizeof(buffer), _TRUNCATE, msg, ap);
va_end(ap);
console::info("%s", buffer);
}
}
void nullsub_6()
{
utils::hook::call(0xC6E57A_b, nullsub_6_stub);
}
void R_WarnOncePerFrame_print_stub(char* buffer, size_t buffer_length, char* msg, va_list va)
{
vsnprintf(buffer, buffer_length, msg, va);
console::warn(buffer);
}
}
class component final : public component_interface
{
public:
void post_unpack() override
{
//nullsub_6();
if (!game::environment::is_dedi())
{
//utils::hook::call(0xE4B121_b, R_WarnOncePerFrame_print_stub);
}
}
};
}
REGISTER_COMPONENT(logger::component)

View File

@ -0,0 +1,29 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "game/game.hpp"
//#include "dvars.hpp"
#include "console.hpp"
#include <utils/hook.hpp>
#include <utils/string.hpp>
namespace patches
{
namespace
{
}
class component final : public component_interface
{
public:
void post_unpack() override
{
}
};
}
REGISTER_COMPONENT(patches::component)

View File

@ -0,0 +1,204 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "scheduler.hpp"
#include "game/game.hpp"
#include <utils/hook.hpp>
#include <utils/concurrency.hpp>
#include <utils/string.hpp>
#include <utils/thread.hpp>
namespace scheduler
{
namespace
{
struct task
{
std::function<bool()> handler{};
std::chrono::milliseconds interval{};
std::chrono::high_resolution_clock::time_point last_call{};
};
using task_list = std::vector<task>;
class task_pipeline
{
public:
void add(task&& task)
{
new_callbacks_.access([&task](task_list& tasks)
{
tasks.emplace_back(std::move(task));
});
}
void execute()
{
callbacks_.access([&](task_list& tasks)
{
this->merge_callbacks();
for (auto i = tasks.begin(); i != tasks.end();)
{
const auto now = std::chrono::high_resolution_clock::now();
const auto diff = now - i->last_call;
if (diff < i->interval)
{
++i;
continue;
}
i->last_call = now;
const auto res = i->handler();
if (res == cond_end)
{
i = tasks.erase(i);
}
else
{
++i;
}
}
});
}
private:
utils::concurrency::container<task_list> new_callbacks_;
utils::concurrency::container<task_list, std::recursive_mutex> callbacks_;
void merge_callbacks()
{
callbacks_.access([&](task_list& tasks)
{
new_callbacks_.access([&](task_list& new_tasks)
{
tasks.insert(tasks.end(), std::move_iterator<task_list::iterator>(new_tasks.begin()),
std::move_iterator<task_list::iterator>(new_tasks.end()));
new_tasks = {};
});
});
}
};
volatile bool kill = false;
std::thread thread;
task_pipeline pipelines[pipeline::count];
utils::hook::detour r_end_frame_hook;
utils::hook::detour g_run_frame_hook;
utils::hook::detour main_frame_hook;
void execute(const pipeline type)
{
assert(type >= 0 && type < pipeline::count);
pipelines[type].execute();
}
void r_end_frame_stub()
{
execute(pipeline::renderer);
r_end_frame_hook.invoke<void>();
}
void server_frame_stub()
{
g_run_frame_hook.invoke<void>();
execute(pipeline::server);
}
void* main_frame_stub()
{
const auto _0 = gsl::finally([]()
{
execute(pipeline::main);
});
return main_frame_hook.invoke<void*>();
}
}
void schedule(const std::function<bool()>& callback, const pipeline type,
const std::chrono::milliseconds delay)
{
assert(type >= 0 && type < pipeline::count);
task task;
task.handler = callback;
task.interval = delay;
task.last_call = std::chrono::high_resolution_clock::now();
pipelines[type].add(std::move(task));
}
void loop(const std::function<void()>& callback, const pipeline type,
const std::chrono::milliseconds delay)
{
schedule([callback]()
{
callback();
return cond_continue;
}, type, delay);
}
void once(const std::function<void()>& callback, const pipeline type,
const std::chrono::milliseconds delay)
{
schedule([callback]()
{
callback();
return cond_end;
}, type, delay);
}
void on_game_initialized(const std::function<void()>& callback, const pipeline type,
const std::chrono::milliseconds delay)
{
schedule([=]()
{
const auto dw_init = game::Live_SyncOnlineDataFlags(0) == 0;
if (dw_init && game::Sys_IsDatabaseReady2())
{
once(callback, type, delay);
return cond_end;
}
return cond_continue;
}, pipeline::main);
}
class component final : public component_interface
{
public:
void post_start() override
{
thread = utils::thread::create_named_thread("Async Scheduler", []()
{
while (!kill)
{
execute(pipeline::async);
std::this_thread::sleep_for(10ms);
}
});
}
void post_unpack() override
{
r_end_frame_hook.create(0xE267B0_b, scheduler::r_end_frame_stub);
g_run_frame_hook.create(0xB8E2D0_b, scheduler::server_frame_stub);
main_frame_hook.create(0xB15E20_b, scheduler::main_frame_stub);
}
void pre_destroy() override
{
kill = true;
if (thread.joinable())
{
thread.join();
}
}
};
}
REGISTER_COMPONENT(scheduler::component)

View File

@ -0,0 +1,36 @@
#pragma once
namespace scheduler
{
enum pipeline
{
// Asynchronuous pipeline, disconnected from the game
async = 0,
// The game's rendering pipeline
renderer,
// The game's server thread
server,
// The game's main thread
main,
// LUI context
lui,
count,
};
static const bool cond_continue = false;
static const bool cond_end = true;
void schedule(const std::function<bool()>& callback, pipeline type = pipeline::async,
std::chrono::milliseconds delay = 0ms);
void loop(const std::function<void()>& callback, pipeline type = pipeline::async,
std::chrono::milliseconds delay = 0ms);
void once(const std::function<void()>& callback, pipeline type = pipeline::async,
std::chrono::milliseconds delay = 0ms);
void on_game_initialized(const std::function<void()>& callback, pipeline type = pipeline::async,
std::chrono::milliseconds delay = 0ms);
}

View File

@ -5,11 +5,24 @@
#include "game.hpp"
#include "dvars.hpp"
#include <component/console.hpp>
#include <utils/hook.hpp>
namespace dvars
{
game::dvar_t* con_inputBoxColor = nullptr;
game::dvar_t* con_inputHintBoxColor = nullptr;
game::dvar_t* con_outputBarColor = nullptr;
game::dvar_t* con_outputSliderColor = nullptr;
game::dvar_t* con_outputWindowColor = nullptr;
game::dvar_t* con_inputDvarMatchColor = nullptr;
game::dvar_t* con_inputDvarValueColor = nullptr;
game::dvar_t* con_inputDvarInactiveValueColor = nullptr;
game::dvar_t* con_inputCmdMatchColor = nullptr;
game::dvar_t* g_playerEjection = nullptr;
game::dvar_t* g_playerCollision = nullptr;
game::dvar_t* player_sustainAmmo = nullptr;
game::dvar_t* g_enableElevators = nullptr;
std::string dvar_get_vector_domain(const int components, const game::dvar_limits& domain)
{
if (domain.vector.min == -FLT_MAX)
@ -118,4 +131,39 @@ namespace dvars
}
}
std::unordered_map<const game::dvar_t*, std::string> dvar_names;
std::string dvar_get_name(const game::dvar_t* dvar)
{
auto offset = dvar_names.find(dvar);
if (offset != dvar_names.end())
{
return offset->second;
}
return "name not found";
}
void dvar_set_name(const game::dvar_t* dvar, const std::string& name)
{
dvar_names[dvar] = name;
}
std::unordered_map<const game::dvar_t*, std::string> dvar_descriptions;
std::string dvar_get_description(const game::dvar_t* dvar)
{
auto offset = dvar_descriptions.find(dvar);
if (offset != dvar_descriptions.end())
{
return offset->second;
}
return "";
}
void dvar_set_description(const game::dvar_t* dvar, const std::string& description)
{
dvar_descriptions[dvar] = description;
}
}

View File

@ -5,6 +5,20 @@
namespace dvars
{
extern game::dvar_t* con_inputBoxColor;
extern game::dvar_t* con_inputHintBoxColor;
extern game::dvar_t* con_outputBarColor;
extern game::dvar_t* con_outputSliderColor;
extern game::dvar_t* con_outputWindowColor;
extern game::dvar_t* con_inputDvarMatchColor;
extern game::dvar_t* con_inputDvarValueColor;
extern game::dvar_t* con_inputDvarInactiveValueColor;
extern game::dvar_t* con_inputCmdMatchColor;
std::string dvar_get_vector_domain(const int components, const game::dvar_limits& domain);
std::string dvar_get_domain(const game::dvar_type type, const game::dvar_limits& domain);
std::string dvar_get_name(const game::dvar_t* dvar);
std::string dvar_get_description(const game::dvar_t* dvar);
void dvar_set_name(const game::dvar_t* dvar, const std::string& name);
void dvar_set_description(const game::dvar_t* dvar, const std::string& description);
}

View File

@ -15,22 +15,22 @@ namespace game
int Cmd_Argc()
{
return 0; //return cmd_args->argc[cmd_args->nesting];
return cmd_args->argc[cmd_args->nesting];
}
const char* Cmd_Argv(const int index)
{
return 0; //return cmd_args->argv[cmd_args->nesting][index];
return cmd_args->argv[cmd_args->nesting][index];
}
int SV_Cmd_Argc()
{
return 0; //return sv_cmd_args->argc[sv_cmd_args->nesting];
return sv_cmd_args->argc[sv_cmd_args->nesting];
}
const char* SV_Cmd_Argv(const int index)
{
return 0; //return sv_cmd_args->argv[sv_cmd_args->nesting][index];
return sv_cmd_args->argv[sv_cmd_args->nesting][index];
}
bool VirtualLobby_Loaded()
@ -38,6 +38,11 @@ namespace game
return 0; //return *mp::virtualLobby_loaded == 1;
}
bool Sys_IsDatabaseReady2()
{
return game::databaseCompletedEvent2;
}
namespace environment
{
bool is_dedi()

View File

@ -49,6 +49,8 @@ namespace game
const char* SV_Cmd_Argv(int index);
bool VirtualLobby_Loaded();
bool Sys_IsDatabaseReady2();
}
uintptr_t operator"" _b(const uintptr_t ptr);

View File

@ -10,6 +10,15 @@ namespace game
typedef vec_t vec3_t[3];
typedef vec_t vec4_t[4];
struct CmdArgs
{
int nesting;
int localClientNum[8];
int controllerIndex[8];
int argc[8];
const char** argv[8];
};
struct cmd_function_s
{
cmd_function_s* next;
@ -105,11 +114,229 @@ namespace game
};
static_assert(sizeof(dvar_t) == 96);
enum svscmd_type
{
SV_CMD_CAN_IGNORE = 0x0,
SV_CMD_RELIABLE = 0x1,
};
enum keyNum_t
{
K_NONE = 0x0,
K_FIRSTGAMEPADBUTTON_RANGE_1 = 0x1,
K_BUTTON_A = 0x1,
K_BUTTON_B = 0x2,
K_BUTTON_X = 0x3,
K_BUTTON_Y = 0x4,
K_BUTTON_LSHLDR = 0x5,
K_BUTTON_RSHLDR = 0x6,
K_LASTGAMEPADBUTTON_RANGE_1 = 0x6,
K_BS = 0x8,
K_TAB = 0x9,
K_ENTER = 0xD,
K_FIRSTGAMEPADBUTTON_RANGE_2 = 0xE,
K_BUTTON_START = 0xE,
K_BUTTON_BACK = 0xF,
K_BUTTON_LSTICK = 0x10,
K_BUTTON_RSTICK = 0x11,
K_BUTTON_LTRIG = 0x12,
K_BUTTON_RTRIG = 0x13,
K_DPAD_UP = 0x14,
K_FIRSTDPAD = 0x14,
K_DPAD_DOWN = 0x15,
K_DPAD_LEFT = 0x16,
K_DPAD_RIGHT = 0x17,
K_BUTTON_LSTICK_ALTIMAGE2 = 0x10,
K_BUTTON_RSTICK_ALTIMAGE2 = 0x11,
K_BUTTON_LSTICK_ALTIMAGE = 0xBC,
K_BUTTON_RSTICK_ALTIMAGE = 0xBD,
K_LASTDPAD = 0x17,
K_LASTGAMEPADBUTTON_RANGE_2 = 0x17,
K_ESCAPE = 0x1B,
K_FIRSTGAMEPADBUTTON_RANGE_3 = 0x1C,
K_APAD_UP = 0x1C,
K_FIRSTAPAD = 0x1C,
K_APAD_DOWN = 0x1D,
K_APAD_LEFT = 0x1E,
K_APAD_RIGHT = 0x1F,
K_LASTAPAD = 0x1F,
K_LASTGAMEPADBUTTON_RANGE_3 = 0x1F,
K_SPACE = 0x20,
K_GRAVE = 0x60,
K_TILDE = 0x7E,
K_BACKSPACE = 0x7F,
K_ASCII_FIRST = 0x80,
K_ASCII_181 = 0x80,
K_ASCII_191 = 0x81,
K_ASCII_223 = 0x82,
K_ASCII_224 = 0x83,
K_ASCII_225 = 0x84,
K_ASCII_228 = 0x85,
K_ASCII_229 = 0x86,
K_ASCII_230 = 0x87,
K_ASCII_231 = 0x88,
K_ASCII_232 = 0x89,
K_ASCII_233 = 0x8A,
K_ASCII_236 = 0x8B,
K_ASCII_241 = 0x8C,
K_ASCII_242 = 0x8D,
K_ASCII_243 = 0x8E,
K_ASCII_246 = 0x8F,
K_ASCII_248 = 0x90,
K_ASCII_249 = 0x91,
K_ASCII_250 = 0x92,
K_ASCII_252 = 0x93,
K_END_ASCII_CHARS = 0x94,
K_COMMAND = 0x96,
K_CAPSLOCK = 0x97,
K_POWER = 0x98,
K_PAUSE = 0x99,
K_UPARROW = 0x9A,
K_DOWNARROW = 0x9B,
K_LEFTARROW = 0x9C,
K_RIGHTARROW = 0x9D,
K_ALT = 0x9E,
K_CTRL = 0x9F,
K_SHIFT = 0xA0,
K_INS = 0xA1,
K_DEL = 0xA2,
K_PGDN = 0xA3,
K_PGUP = 0xA4,
K_HOME = 0xA5,
K_END = 0xA6,
K_F1 = 0xA7,
K_F2 = 0xA8,
K_F3 = 0xA9,
K_F4 = 0xAA,
K_F5 = 0xAB,
K_F6 = 0xAC,
K_F7 = 0xAD,
K_F8 = 0xAE,
K_F9 = 0xAF,
K_F10 = 0xB0,
K_F11 = 0xB1,
K_F12 = 0xB2,
K_F13 = 0xB3,
K_F14 = 0xB4,
K_F15 = 0xB5,
K_KP_HOME = 0xB6,
K_KP_UPARROW = 0xB7,
K_KP_PGUP = 0xB8,
K_KP_LEFTARROW = 0xB9,
K_KP_5 = 0xBA,
K_KP_RIGHTARROW = 0xBB,
K_KP_END = 0xBC,
K_KP_DOWNARROW = 0xBD,
K_KP_PGDN = 0xBE,
K_KP_ENTER = 0xBF,
K_KP_INS = 0xC0,
K_KP_DEL = 0xC1,
K_KP_SLASH = 0xC2,
K_KP_MINUS = 0xC3,
K_KP_PLUS = 0xC4,
K_KP_NUMLOCK = 0xC5,
K_KP_STAR = 0xC6,
K_KP_EQUALS = 0xC7,
K_MOUSE1 = 0xC8,
K_MOUSE2 = 0xC9,
K_MOUSE3 = 0xCA,
K_MOUSE4 = 0xCB,
K_MOUSE5 = 0xCC,
K_MWHEELDOWN = 0xCD,
K_MWHEELUP = 0xCE,
K_AUX1 = 0xCF,
K_AUX2 = 0xD0,
K_AUX3 = 0xD1,
K_AUX4 = 0xD2,
K_AUX5 = 0xD3,
K_AUX6 = 0xD4,
K_AUX7 = 0xD5,
K_AUX8 = 0xD6,
K_AUX9 = 0xD7,
K_AUX10 = 0xD8,
K_AUX11 = 0xD9,
K_AUX12 = 0xDA,
K_AUX13 = 0xDB,
K_AUX14 = 0xDC,
K_AUX15 = 0xDD,
K_AUX16 = 0xDE,
K_LAST_KEY = 0xDF
};
struct KeyState
{
int down;
int repeats;
int binding;
};
struct PlayerKeyState // probs wrong
{
int overstrikeMode;
int anyKeyDown;
KeyState keys[256];
};
struct ScreenPlacement
{
vec2_t scaleVirtualToReal;
vec2_t scaleVirtualToFull;
vec2_t scaleRealToVirtual;
vec2_t realViewportPosition;
vec2_t realViewportSize;
vec2_t virtualViewableMin;
vec2_t virtualViewableMax;
vec2_t realViewableMin;
vec2_t realViewableMax;
vec2_t virtualAdjustableMin;
vec2_t virtualAdjustableMax;
vec2_t realAdjustableMin;
vec2_t realAdjustableMax;
vec2_t subScreenLeft;
};
namespace assets
{
enum XAssetType : std::int32_t
{
ASSET_TYPE_PHYSPRESET = 0x0,
ASSET_TYPE_PHYSPRESET = 0,
ASSET_TYPE_MATERIAL = 10,
ASSET_TYPE_FX = 41,
ASSET_TYPE_RAWFILE = 48,
ASSET_TYPE_STRINGTABLE = 50,
ASSET_TYPE_SCRIPTABLE = 60,
ASSET_TYPE_FONT = 66,
};
struct Material
{
const char* name;
char __pad0[0x110];
};
static_assert(sizeof(Material) == 0x118);
struct Glyph
{
unsigned short letter;
char x0;
char y0;
char dx;
char pixelWidth;
char pixelHeight;
float s0;
float t0;
float s1;
float t1;
};
struct Font_s
{
const char* fontName;
int pixelHeight;
int glyphCount;
Material* material;
Material* glowMaterial;
Glyph* glyphs;
};
struct RawFile
@ -130,6 +357,20 @@ namespace game
char* bytecode;
};
struct StringTableCell
{
const char* string;
int hash;
};
struct StringTable
{
const char* name;
int columnCount;
int rowCount;
StringTableCell* values;
};
struct LuaFile
{
const char* name;

View File

@ -8,5 +8,75 @@ namespace game
* Functions
**************************************************************/
WEAK symbol<void()> Com_Quit_f{ 0xBADC90 };
WEAK symbol<void(int localClientNum, const char* text)> Cbuf_AddText{ 0xB7C290 };
WEAK symbol<void(int localClientNum, int controllerIndex, const char* text)> Cmd_ExecuteSingleCommand{ 0xB7D040 };
WEAK symbol<void(const char* cmdName, void(), cmd_function_s* allocedCmd)> Cmd_AddCommandInternal{ 0xB7C8F0 };
WEAK symbol<void(const char* cmdName)> Cmd_RemoveCommand{ 0xB7D630 };
WEAK symbol<void(const char* text_in)> Cmd_TokenizeString{ 0xB7D850 };
WEAK symbol<void()> Cmd_EndTokenizeString{ 0xB7CC90 };
WEAK symbol<dvar_t* (const char* name, bool value,
unsigned int flags, const char* description)> Dvar_RegisterBool{ 0xCEB380 };
WEAK symbol<dvar_t* (const char* name, int value, int min, int max,
unsigned int flags, const char* description)> Dvar_RegisterInt{ 0xCEB920 };
WEAK symbol<dvar_t* (const char* name, float value, float min,
float max, unsigned int flags, const char* description)> Dvar_RegisterFloat{ 0xCEB890 };
WEAK symbol<dvar_t* (const char* name, const char* value,
unsigned int flags, const char* description)> Dvar_RegisterString{ 0xCEBD50 };
WEAK symbol<dvar_t* (const char* name, float x, float y,
float min, float max, unsigned int flags, const char* description)> Dvar_RegisterVec2{ 0xCEBF50 };
WEAK symbol<dvar_t* (const char* name, float x, float y, float z,
float min, float max, unsigned int flags, const char* description)> Dvar_RegisterVec3{ 0xCEBFE0 };
WEAK symbol<dvar_t* (const char* name, float x, float y, float z,
float w, float min, float max, unsigned int flags, const char* description)> Dvar_RegisterVec4{ 0xCEC110 };
WEAK symbol<void(const char* name, const char* string)> Dvar_SetCommand{ 0xCECB30 };
WEAK symbol<dvar_t* (const char* name)> Dvar_FindVar{ 0xCEA460 };
WEAK symbol<void(char* buffer, int index)> Dvar_GetCombinedString{ 0xBB1F30 };
WEAK symbol<const char* (dvar_t* dvar, dvar_value value)> Dvar_ValueToString{ 0xCEED00 };
WEAK symbol<int(const char* name)> Dvar_GenerateChecksum{ 0xCEA520 };
#define Dvar_GenerateHash(name) \
Dvar_GenerateChecksum(name);
WEAK symbol<unsigned int(int controllerIndex)> Live_SyncOnlineDataFlags{ 0xDC5CE0 };
WEAK symbol<Material* (const char* material)> Material_RegisterHandle{ 0xE11CE0 };
WEAK symbol<Font_s* (const char* font, int size)> R_RegisterFont{ 0xDFC670 };
WEAK symbol<int(const char* text, int maxChars, Font_s* font)> R_TextWidth{ 0xDFC770 };
WEAK symbol<int(void* font)> R_GetFontHeight{ 0x12727B0 };
WEAK symbol<void* (int a1)> R_DrawSomething{ 0xDFBD00 };
WEAK symbol<void()> R_SyncRenderThread{ 0xE27EE0 };
WEAK symbol<void(float x, float y, float width, float height, float s0, float t0, float s1, float t1,
float* color, Material* material, int unk)> R_AddCmdDrawStretchPic{ 0xE24DC0 };
WEAK symbol<void* (const char* text, int maxChars, void* font, int fontHeight, float x,
float y, float xScale, float yScale, float rotation, float* color,
int cursor_pos, char cursor_char, void* style_unk, int a14, int a15, int a16, int a17)> IW7_AddBaseDrawTextCmd{ 0xE23D90 };
#define R_AddCmdDrawText(TXT, MC, F, X, Y, XS, YS, R, C, S) \
IW7_AddBaseDrawTextCmd(TXT, MC, F, game::R_GetFontHeight(F), X, Y, XS, YS, R, C,-1, 0, game::R_DrawSomething(S), 0, 0, 0, 0)
#define R_AddCmdDrawTextWithCursor(TXT, MC, F, UNK, X, Y, XS, YS, R, C, S, CP, CC) \
IW7_AddBaseDrawTextCmd(TXT, MC, F, game::R_GetFontHeight(F), X, Y, XS, YS, R, C, CP, CC, game::R_DrawSomething(S), 0, 0, 0, 0)
WEAK symbol<ScreenPlacement* ()> ScrPlace_GetViewPlacement{ 0x9E4090 };
WEAK symbol<void(int clientNum, svscmd_type type, const char* text)> SV_GameSendServerCommand{ 0xC54780 };
/***************************************************************
* Variables
**************************************************************/
WEAK symbol<bool> databaseCompletedEvent2{ 0x5685979 };
WEAK symbol<CmdArgs> sv_cmd_args{ 0x5D65C20 };
WEAK symbol<CmdArgs> cmd_args{ 0x5D65B70 };
WEAK symbol<cmd_function_s*> cmd_functions{ 0x5D65CC8 };
WEAK symbol<int> keyCatchers{ 0x2246C34 };
WEAK symbol<PlayerKeyState> playerKeys{ 0x523BA0C };
WEAK symbol<int> dvarCount{ 0x7595E54 };
WEAK symbol<dvar_t*> dvarPool{ 0x7595E60 };
}

View File

@ -3,6 +3,7 @@
#include "loader/component_loader.hpp"
#include "game/game.hpp"
#include <utils/hook.hpp>
#include <utils/string.hpp>
#include <utils/flags.hpp>
#include <utils/io.hpp>
@ -64,6 +65,7 @@ FARPROC load_binary(uint64_t* base_address)
void remove_crash_file()
{
utils::io::remove_file("__iw7-mod");
utils::io::remove_file("__iw7_ship");
}
void enable_dpi_awareness()
@ -152,6 +154,12 @@ int main()
throw std::runtime_error("Unable to load binary into memory");
}
if (base_address != 0x140000000)
{
throw std::runtime_error(utils::string::va(
"Base address was (%p) and not (%p)\nThis should not be possible!",
base_address, 0x140000000));
}
game::base_address = base_address;
if (!component_loader::post_load()) return 0;

View File

@ -3,7 +3,7 @@
#pragma comment(linker, "/stack:0x1000000")
#ifdef INJECT_HOST_AS_LIB
//#pragma comment(linker, "/base:0x160000000")
#pragma comment(linker, "/base:0x160000000")
#else
#pragma comment(linker, "/base:0x140000000")
#pragma comment(linker, "/merge:.data=.cld")

View File

@ -2,6 +2,9 @@
#define BINARY_PAYLOAD_SIZE 0x14000000
// Decide whether to load the game as lib or to inject it
#define INJECT_HOST_AS_LIB
#pragma warning(push)
#pragma warning(disable: 4100)
#pragma warning(disable: 4127)