Allow custom gamesettings to be loaded from disk
This commit is contained in:
parent
586067e6ec
commit
aef16d1e0c
135
src/client/component/gamesettings.cpp
Normal file
135
src/client/component/gamesettings.cpp
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
#include <std_include.hpp>
|
||||||
|
#include "loader/component_loader.hpp"
|
||||||
|
#include "game/game.hpp"
|
||||||
|
|
||||||
|
#include "scheduler.hpp"
|
||||||
|
|
||||||
|
#include <utils/hook.hpp>
|
||||||
|
#include <utils/string.hpp>
|
||||||
|
#include <utils/io.hpp>
|
||||||
|
|
||||||
|
namespace gamesettings
|
||||||
|
{
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
// <name, path>
|
||||||
|
std::unordered_map<std::string, std::string> gamesettings_files;
|
||||||
|
|
||||||
|
const std::string get_gamesettings_name(const std::vector<std::string>& sub_strings)
|
||||||
|
{
|
||||||
|
if (sub_strings.size() > 2)
|
||||||
|
{
|
||||||
|
return sub_strings[sub_strings.size() - 2] + '/' + sub_strings[sub_strings.size() - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string get_gamesettings_path(const std::string& name)
|
||||||
|
{
|
||||||
|
const auto itr = gamesettings_files.find(name);
|
||||||
|
return (itr == gamesettings_files.end()) ? "" : itr->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
void search_gamesettings_folder(const std::string& gamesettings_dir)
|
||||||
|
{
|
||||||
|
if (!utils::io::directory_exists(gamesettings_dir))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto files = utils::io::list_files(gamesettings_dir, true);
|
||||||
|
|
||||||
|
for (const auto& path : files)
|
||||||
|
{
|
||||||
|
if (!std::filesystem::is_directory(path))
|
||||||
|
{
|
||||||
|
auto sub_strings = utils::string::split(path.generic_string(), '/');
|
||||||
|
gamesettings_files.insert_or_assign(get_gamesettings_name(sub_strings), path.generic_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool has_gamesettings_file_on_disk(const char* path)
|
||||||
|
{
|
||||||
|
if (!path)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto sub_strings = utils::string::split(path, '/');
|
||||||
|
auto gamesettings_name = get_gamesettings_name(sub_strings);
|
||||||
|
|
||||||
|
return get_gamesettings_path(gamesettings_name) != "";
|
||||||
|
}
|
||||||
|
|
||||||
|
void cmd_exec_stub(utils::hook::assembler& a)
|
||||||
|
{
|
||||||
|
const auto exec_from_fastfile = a.newLabel();
|
||||||
|
const auto exec_from_disk = a.newLabel();
|
||||||
|
|
||||||
|
a.pushad64();
|
||||||
|
|
||||||
|
a.mov(rcx, r10);
|
||||||
|
a.call_aligned(has_gamesettings_file_on_disk);
|
||||||
|
a.cmp(rax, 1);
|
||||||
|
;
|
||||||
|
a.popad64();
|
||||||
|
|
||||||
|
a.jnz(exec_from_fastfile);
|
||||||
|
|
||||||
|
a.bind(exec_from_disk);
|
||||||
|
a.jmp(game::select(0x1420ED087, 0x1404F855E));
|
||||||
|
|
||||||
|
a.bind(exec_from_fastfile);
|
||||||
|
a.lea(rdx, ptr(rsp, (game::is_server() ? 0x30 : 0x40)));
|
||||||
|
a.jmp(game::select(0x1420ED007, 0x1404F853F));
|
||||||
|
}
|
||||||
|
|
||||||
|
int read_file_stub(const char* qpath, void** buffer)
|
||||||
|
{
|
||||||
|
auto sub_strings = utils::string::split(qpath, '/');
|
||||||
|
auto game_settings_name = get_gamesettings_name(sub_strings);
|
||||||
|
|
||||||
|
std::string gamesettings_data;
|
||||||
|
utils::io::read_file(get_gamesettings_path(game_settings_name), &gamesettings_data);
|
||||||
|
|
||||||
|
if (!gamesettings_data.empty())
|
||||||
|
{
|
||||||
|
++(*game::fs_loadStack);
|
||||||
|
|
||||||
|
auto len = static_cast<int>(gamesettings_data.length());
|
||||||
|
auto buf = game::FS_AllocMem(len + 1);
|
||||||
|
|
||||||
|
*buffer = buf;
|
||||||
|
gamesettings_data.copy(reinterpret_cast<char*>(*buffer), len);
|
||||||
|
buf[len] = '\0';
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
return utils::hook::invoke<int>(game::select(0x1422A48D0, 0x140564F70), qpath, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void search_gamesettings_files_on_disk()
|
||||||
|
{
|
||||||
|
const utils::nt::library host{};
|
||||||
|
|
||||||
|
search_gamesettings_folder((host.get_folder() / "boiii/gamesettings").string());
|
||||||
|
search_gamesettings_folder((game::get_appdata_path() / "data/gamesettings").string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct component final : generic_component
|
||||||
|
{
|
||||||
|
void post_unpack() override
|
||||||
|
{
|
||||||
|
search_gamesettings_files_on_disk();
|
||||||
|
|
||||||
|
utils::hook::call(game::select(0x1420ED0A1, 0x1404F857D), read_file_stub);
|
||||||
|
utils::hook::jump(game::select(0x1420ED002, 0x1404F853A), utils::hook::assemble(cmd_exec_stub));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
REGISTER_COMPONENT(gamesettings::component)
|
@ -35,6 +35,7 @@ namespace game
|
|||||||
WEAK symbol<void(int localClientNum, eModes fromMode, eModes toMode, uint32_t flags)> Com_SwitchMode{
|
WEAK symbol<void(int localClientNum, eModes fromMode, eModes toMode, uint32_t flags)> Com_SwitchMode{
|
||||||
0x14214A4D0
|
0x14214A4D0
|
||||||
};
|
};
|
||||||
|
WEAK symbol<const char*(const char* fullpath)> Com_LoadRawTextFile{0x1420F61B0};
|
||||||
|
|
||||||
WEAK symbol<void(int localClientNum, const char* text)> Cbuf_AddText{0x1420EC010, 0x1404F75B0};
|
WEAK symbol<void(int localClientNum, const char* text)> Cbuf_AddText{0x1420EC010, 0x1404F75B0};
|
||||||
WEAK symbol<void(int localClientNum, ControllerIndex_t controllerIndex, const char* buffer)> Cbuf_ExecuteBuffer{
|
WEAK symbol<void(int localClientNum, ControllerIndex_t controllerIndex, const char* buffer)> Cbuf_ExecuteBuffer{
|
||||||
@ -180,6 +181,9 @@ namespace game
|
|||||||
WEAK symbol<void(const char* text_in)> SV_Cmd_TokenizeString{0x1420EF130, 0x1404FA6C0};
|
WEAK symbol<void(const char* text_in)> SV_Cmd_TokenizeString{0x1420EF130, 0x1404FA6C0};
|
||||||
WEAK symbol<void()> SV_Cmd_EndTokenizedString{0x1420EF0E0, 0x1404FA670};
|
WEAK symbol<void()> SV_Cmd_EndTokenizedString{0x1420EF0E0, 0x1404FA670};
|
||||||
|
|
||||||
|
// FS
|
||||||
|
WEAK symbol<char*(int bytes)> FS_AllocMem{0x1422AC9F0, 0x14056C340};
|
||||||
|
|
||||||
// Utils
|
// Utils
|
||||||
WEAK symbol<const char*(char* str)> I_CleanStr{0x1422E9050, 0x140580E80};
|
WEAK symbol<const char*(char* str)> I_CleanStr{0x1422E9050, 0x140580E80};
|
||||||
|
|
||||||
@ -198,6 +202,8 @@ namespace game
|
|||||||
WEAK symbol<char> s_dvarPool{0x157AC6220, 0x14A3CB620};
|
WEAK symbol<char> s_dvarPool{0x157AC6220, 0x14A3CB620};
|
||||||
WEAK symbol<int> g_dvarCount{0x157AC61CC, 0x14A3CB5FC};
|
WEAK symbol<int> g_dvarCount{0x157AC61CC, 0x14A3CB5FC};
|
||||||
|
|
||||||
|
WEAK symbol<int> fs_loadStack{0x157A65310, 0x14A39C650};
|
||||||
|
|
||||||
// Client and dedi struct size differs :(
|
// Client and dedi struct size differs :(
|
||||||
WEAK symbol<client_s_cl*> svs_clients_cl{0x1576F9318, 0};
|
WEAK symbol<client_s_cl*> svs_clients_cl{0x1576F9318, 0};
|
||||||
WEAK symbol<client_s*> svs_clients{0x0, 0x14A178E98};
|
WEAK symbol<client_s*> svs_clients{0x0, 0x14A178E98};
|
||||||
|
Loading…
Reference in New Issue
Block a user