2023-02-11 11:31:21 -05:00
|
|
|
#include <std_include.hpp>
|
2023-02-02 17:28:48 -05:00
|
|
|
#include "loader/component_loader.hpp"
|
|
|
|
|
|
|
|
#include "game/game.hpp"
|
|
|
|
|
|
|
|
#include <utils/hook.hpp>
|
|
|
|
#include <utils/io.hpp>
|
|
|
|
#include <utils/string.hpp>
|
|
|
|
|
|
|
|
#include "scheduler.hpp"
|
|
|
|
|
|
|
|
namespace dvars
|
|
|
|
{
|
|
|
|
namespace
|
|
|
|
{
|
|
|
|
bool initial_config_read = false;
|
|
|
|
utils::hook::detour dvar_set_variant_hook;
|
|
|
|
|
2023-02-26 16:45:48 -05:00
|
|
|
void dvar_for_each_name_stub(void (*callback)(const char*))
|
|
|
|
{
|
|
|
|
for (int i = 0; i < *game::g_dvarCount; ++i)
|
|
|
|
{
|
|
|
|
const auto offset = game::is_server() ? 136 : 160;
|
|
|
|
const auto* dvar = reinterpret_cast<game::dvar_t*>(&game::s_dvarPool[offset * i]);
|
|
|
|
|
|
|
|
if ((!game::Com_SessionMode_IsMode(game::MODE_COUNT) ||
|
|
|
|
!game::Dvar_IsSessionModeBaseDvar(dvar)) &&
|
|
|
|
(dvar->flags & 0x8000) == 0)
|
|
|
|
{
|
|
|
|
if (dvar->debugName)
|
|
|
|
{
|
|
|
|
callback(dvar->debugName);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void dvar_for_each_name_client_num_stub(int localClientNum, void (*callback)(int, const char*))
|
|
|
|
{
|
|
|
|
for (int i = 0; i < *game::g_dvarCount; ++i)
|
|
|
|
{
|
|
|
|
const auto offset = game::is_server() ? 136 : 160;
|
|
|
|
const auto* dvar = reinterpret_cast<game::dvar_t*>(&game::s_dvarPool[offset * i]);
|
|
|
|
|
|
|
|
if ((!game::Com_SessionMode_IsMode(game::MODE_COUNT) ||
|
|
|
|
!game::Dvar_IsSessionModeBaseDvar(dvar)) &&
|
|
|
|
(dvar->flags & 0x8000) == 0)
|
|
|
|
{
|
|
|
|
if (dvar->debugName)
|
|
|
|
{
|
|
|
|
callback(localClientNum, dvar->debugName);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void read_dvar_name_hashes_data(std::unordered_map<std::uint32_t, std::string>& map)
|
|
|
|
{
|
|
|
|
const auto path = game::get_appdata_path() / "data" / "lookup_tables" / "dvar_lookup_table.csv";
|
|
|
|
std::string data;
|
|
|
|
|
|
|
|
if (!utils::io::read_file(path, &data))
|
|
|
|
{
|
|
|
|
printf("Failed to read Dvar lookup table\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-02-27 04:52:25 -05:00
|
|
|
data.erase(std::remove(data.begin(), data.end(), '\r'), data.end());
|
|
|
|
|
2023-02-26 16:45:48 -05:00
|
|
|
std::istringstream stream(data);
|
|
|
|
std::string line;
|
|
|
|
|
|
|
|
while (std::getline(stream, line, '\n'))
|
|
|
|
{
|
|
|
|
if (utils::string::starts_with(line, "//"))
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
2023-02-02 17:28:48 -05:00
|
|
|
|
2023-02-26 16:45:48 -05:00
|
|
|
const auto separator = line.find(',');
|
|
|
|
|
|
|
|
if (separator == std::string::npos)
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
const auto debug_name = line.substr(separator + 1);
|
|
|
|
|
|
|
|
if (!debug_name.empty())
|
|
|
|
{
|
|
|
|
std::istringstream hash_string(line.substr(0, separator));
|
|
|
|
std::uint32_t hash_value;
|
|
|
|
|
|
|
|
hash_string >> hash_value;
|
|
|
|
|
|
|
|
map.emplace(hash_value, debug_name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void copy_dvar_names_to_pool()
|
|
|
|
{
|
|
|
|
std::unordered_map<std::uint32_t, std::string> dvar_hash_name_map;
|
|
|
|
read_dvar_name_hashes_data(dvar_hash_name_map);
|
|
|
|
|
|
|
|
for (int i = 0; i < *game::g_dvarCount; ++i)
|
|
|
|
{
|
|
|
|
const auto offset = game::is_server() ? 136 : 160;
|
|
|
|
auto* dvar = reinterpret_cast<game::dvar_t*>(&game::s_dvarPool[offset * i]);
|
|
|
|
|
|
|
|
if (!dvar->debugName)
|
|
|
|
{
|
|
|
|
const auto it = dvar_hash_name_map.find(dvar->name);
|
|
|
|
if (it != dvar_hash_name_map.end())
|
|
|
|
{
|
|
|
|
dvar->debugName = game::CopyString(it->second.data());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-02-02 17:28:48 -05:00
|
|
|
|
|
|
|
const std::string get_config_file_path()
|
|
|
|
{
|
|
|
|
return "players/user/config.cfg";
|
|
|
|
}
|
|
|
|
|
|
|
|
void write_archive_dvars()
|
|
|
|
{
|
|
|
|
std::string config_buffer;
|
|
|
|
|
|
|
|
for (int i = 0; i < *game::g_dvarCount; ++i)
|
|
|
|
{
|
|
|
|
const auto* dvar = reinterpret_cast<const game::dvar_t*>(&game::s_dvarPool[160 * i]);
|
|
|
|
|
2023-02-11 14:40:26 -05:00
|
|
|
if (!dvar->debugName)
|
2023-02-02 17:28:48 -05:00
|
|
|
continue;
|
|
|
|
|
|
|
|
auto name = dvar->debugName;
|
|
|
|
auto value = game::Dvar_DisplayableValue(dvar);
|
|
|
|
|
2023-02-18 14:19:09 -05:00
|
|
|
config_buffer.append(utils::string::va("set %s \"%s\"\n", name, value));
|
2023-02-02 17:28:48 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if (config_buffer.length() == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
utils::io::write_file(get_config_file_path(), config_buffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
void dvar_set_variant_stub(game::dvar_t* dvar, game::DvarValue* value, unsigned int source)
|
|
|
|
{
|
|
|
|
dvar_set_variant_hook.invoke(dvar, value, source);
|
|
|
|
|
2023-02-11 14:40:26 -05:00
|
|
|
if (initial_config_read && dvar->debugName)
|
2023-02-02 17:28:48 -05:00
|
|
|
{
|
|
|
|
write_archive_dvars();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void read_archive_dvars()
|
|
|
|
{
|
|
|
|
const std::string path = get_config_file_path();
|
|
|
|
|
|
|
|
if (!utils::io::file_exists(path))
|
2023-02-18 14:19:09 -05:00
|
|
|
{
|
|
|
|
initial_config_read = true;
|
2023-02-02 17:28:48 -05:00
|
|
|
return;
|
2023-02-18 14:19:09 -05:00
|
|
|
}
|
2023-02-02 17:28:48 -05:00
|
|
|
|
|
|
|
std::string filedata;
|
|
|
|
utils::io::read_file(path, &filedata);
|
|
|
|
|
|
|
|
game::Cbuf_ExecuteBuffer(0, game::ControllerIndex_t::CONTROLLER_INDEX_0, filedata.c_str());
|
|
|
|
initial_config_read = true;
|
2023-02-11 14:40:26 -05:00
|
|
|
scheduler::execute(scheduler::pipeline::dvars_loaded);
|
2023-02-02 17:28:48 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-02-26 16:45:48 -05:00
|
|
|
class component final : public generic_component
|
2023-02-11 11:31:21 -05:00
|
|
|
{
|
|
|
|
public:
|
|
|
|
void post_unpack() override
|
2023-02-02 17:28:48 -05:00
|
|
|
{
|
2023-02-26 16:45:48 -05:00
|
|
|
if (!game::is_server())
|
|
|
|
{
|
|
|
|
scheduler::once(read_archive_dvars, scheduler::pipeline::main);
|
|
|
|
dvar_set_variant_hook.create(0x1422C9A90_g, dvar_set_variant_stub);
|
|
|
|
|
|
|
|
// Show all known dvars in console
|
|
|
|
utils::hook::jump(0x1422BD890_g, dvar_for_each_name_stub);
|
|
|
|
utils::hook::jump(0x1422BD7E0_g, dvar_for_each_name_client_num_stub);
|
|
|
|
}
|
|
|
|
|
|
|
|
scheduler::once(copy_dvar_names_to_pool, scheduler::pipeline::main);
|
2023-02-02 17:28:48 -05:00
|
|
|
|
2023-02-26 16:45:48 -05:00
|
|
|
// All dvars are recognized as command
|
|
|
|
utils::hook::nop(game::select(0x14215297A, 0x14050949A), 2);
|
|
|
|
// Show all dvars in dvarlist command
|
|
|
|
utils::hook::nop(game::select(0x142152C87, 0x140509797), 6);
|
|
|
|
// Show all dvars in dvardump command
|
|
|
|
utils::hook::nop(game::select(0x142152659, 0x140509179), 6);
|
2023-02-02 17:28:48 -05:00
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
REGISTER_COMPONENT(dvars::component)
|