Update
This commit is contained in:
parent
a7537934f0
commit
1bdcdba1a2
@ -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/**.*"}
|
||||
|
||||
|
@ -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);
|
||||
|
56
src/client/component/branding.cpp
Normal file
56
src/client/component/branding.cpp
Normal 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)
|
315
src/client/component/command.cpp
Normal file
315
src/client/component/command.cpp
Normal 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)
|
50
src/client/component/command.hpp
Normal file
50
src/client/component/command.hpp
Normal 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);
|
||||
}
|
@ -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)
|
||||
|
153
src/client/component/dvars.cpp
Normal file
153
src/client/component/dvars.cpp
Normal 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)
|
30
src/client/component/dvars.hpp
Normal file
30
src/client/component/dvars.hpp
Normal 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);
|
||||
}
|
791
src/client/component/game_console.cpp
Normal file
791
src/client/component/game_console.cpp
Normal 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)
|
7
src/client/component/game_console.hpp
Normal file
7
src/client/component/game_console.hpp
Normal 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);
|
||||
}
|
54
src/client/component/input.cpp
Normal file
54
src/client/component/input.cpp
Normal 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)
|
52
src/client/component/localized_strings.cpp
Normal file
52
src/client/component/localized_strings.cpp
Normal 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)
|
6
src/client/component/localized_strings.hpp
Normal file
6
src/client/component/localized_strings.hpp
Normal file
@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
namespace localized_strings
|
||||
{
|
||||
void override(const std::string& key, const std::string& value);
|
||||
}
|
57
src/client/component/logger.cpp
Normal file
57
src/client/component/logger.cpp
Normal 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)
|
29
src/client/component/patches.cpp
Normal file
29
src/client/component/patches.cpp
Normal 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)
|
204
src/client/component/scheduler.cpp
Normal file
204
src/client/component/scheduler.cpp
Normal 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)
|
36
src/client/component/scheduler.hpp
Normal file
36
src/client/component/scheduler.hpp
Normal 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);
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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()
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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 };
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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")
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user