This commit is contained in:
Federico Cecchetto
2022-04-08 16:18:00 +02:00
parent 652ea25c99
commit d0228b92b7
21 changed files with 831 additions and 38 deletions

View File

@ -8,6 +8,7 @@
#include <utils/hook.hpp>
#include <utils/string.hpp>
#include <utils/io.hpp>
namespace filesystem
{
@ -70,6 +71,40 @@ namespace filesystem
return this->name_;
}
std::unordered_set<std::string>& get_search_paths()
{
static std::unordered_set<std::string> search_paths{};
return search_paths;
}
std::string read_file(const std::string& path)
{
for (const auto& search_path : get_search_paths())
{
const auto path_ = search_path + "/" + path;
if (utils::io::file_exists(path_))
{
return utils::io::read_file(path_);
}
}
return {};
}
bool read_file(const std::string& path, std::string* data)
{
for (const auto& search_path : get_search_paths())
{
const auto path_ = search_path + "/" + path;
if (utils::io::read_file(path_, data))
{
return true;
}
}
return false;
}
class component final : public component_interface
{
public:
@ -87,6 +122,10 @@ namespace filesystem
utils::hook::call(SELECT_VALUE(0x1403B8D31, 0x1404EE3D0), register_custom_path_stub);
utils::hook::call(SELECT_VALUE(0x1403B8D51, 0x1404EE3F0), register_custom_path_stub);
utils::hook::call(SELECT_VALUE(0x1403B8D90, 0x1404EE42F), register_custom_path_stub);
get_search_paths().insert(".");
get_search_paths().insert("h1-mod");
get_search_paths().insert("data");
}
};
}

View File

@ -16,4 +16,8 @@ namespace filesystem
std::string name_;
std::string buffer_;
};
std::unordered_set<std::string>& get_search_paths();
std::string read_file(const std::string& path);
bool read_file(const std::string& path, std::string* data);
}

View File

@ -3,6 +3,7 @@
#include "fonts.hpp"
#include "console.hpp"
#include "filesystem.hpp"
#include "game/game.hpp"
#include "game/dvars.hpp"
@ -36,6 +37,13 @@ namespace fonts
return font;
}
void free_font(game::TTF* font)
{
utils::memory::get_allocator()->free(font->buffer);
utils::memory::get_allocator()->free(font->name);
utils::memory::get_allocator()->free(font);
}
game::TTF* load_font(const std::string& name)
{
return font_data.access<game::TTF*>([&](font_data_t& data_) -> game::TTF*
@ -51,9 +59,7 @@ namespace fonts
data = i->second;
}
if (data.empty()
&& !utils::io::read_file(utils::string::va("h1-mod/%s", name.data()), &data)
&& !utils::io::read_file(utils::string::va("data/%s", name.data()), &data))
if (data.empty() && !filesystem::read_file(name, &data))
{
return nullptr;
}
@ -98,6 +104,20 @@ namespace fonts
});
}
void clear()
{
font_data.access([&](font_data_t& data_)
{
for (auto& font : data_.fonts)
{
free_font(font.second);
}
data_.fonts.clear();
utils::hook::set<int>(SELECT_VALUE(0x14F09DBB8, 0x14FD61EE8), 0); // reset registered font count
});
}
class component final : public component_interface
{
public:

View File

@ -3,4 +3,5 @@
namespace fonts
{
void add(const std::string& name, const std::string& data);
void clear();
}

View File

@ -571,7 +571,7 @@ namespace game_console
{
if (key == game::keyNum_t::K_F10)
{
if (!game::Com_InFrontEnd())
if (!game::Com_InFrontend())
{
return false;
}

View File

@ -15,13 +15,21 @@ namespace input
utils::hook::detour cl_char_event_hook;
utils::hook::detour cl_key_event_hook;
bool lui_running()
{
return *game::hks::lua_state != nullptr;
}
void cl_char_event_stub(const int local_client_num, const int key)
{
ui_scripting::notify("keypress",
if (lui_running())
{
{"keynum", key},
{"key", game::Key_KeynumToString(key, 0, 1)},
});
ui_scripting::notify("keypress",
{
{"keynum", key},
{"key", game::Key_KeynumToString(key, 0, 1)},
});
}
if (!game_console::console_char_event(local_client_num, key))
{
@ -33,11 +41,14 @@ namespace input
void cl_key_event_stub(const int local_client_num, const int key, const int down)
{
ui_scripting::notify(down ? "keydown" : "keyup",
if (lui_running())
{
{"keynum", key},
{"key", game::Key_KeynumToString(key, 0, 1)},
});
ui_scripting::notify(down ? "keydown" : "keyup",
{
{"keynum", key},
{"key", game::Key_KeynumToString(key, 0, 1)},
});
}
if (!game_console::console_key_event(local_client_num, key, down))
{

View File

@ -3,6 +3,7 @@
#include "materials.hpp"
#include "console.hpp"
#include "filesystem.hpp"
#include "game/game.hpp"
#include "game/dvars.hpp"
@ -20,6 +21,7 @@ namespace materials
{
utils::hook::detour db_material_streaming_fail_hook;
utils::hook::detour material_register_handle_hook;
utils::hook::detour db_get_material_index_hook;
struct material_data_t
{
@ -68,6 +70,16 @@ namespace materials
return material;
}
void free_material(game::Material* material)
{
material->textureTable->u.image->textures.___u0.map->Release();
material->textureTable->u.image->textures.shaderView->Release();
utils::memory::get_allocator()->free(material->textureTable->u.image);
utils::memory::get_allocator()->free(material->textureTable);
utils::memory::get_allocator()->free(material->name);
utils::memory::get_allocator()->free(material);
}
game::Material* load_material(const std::string& name)
{
return material_data.access<game::Material*>([&](material_data_t& data_) -> game::Material*
@ -83,9 +95,7 @@ namespace materials
data = i->second;
}
if (data.empty()
&& !utils::io::read_file(utils::string::va("h1-mod/materials/%s.png", name.data()), &data)
&& !utils::io::read_file(utils::string::va("data/materials/%s.png", name.data()), &data))
if (data.empty() && !filesystem::read_file(utils::string::va("materials/%s.png", name.data()), &data))
{
data_.materials[name] = nullptr;
return nullptr;
@ -136,6 +146,16 @@ namespace materials
return db_material_streaming_fail_hook.invoke<int>(material);
}
unsigned int db_get_material_index_stub(game::Material* material)
{
if (material->constantTable == &constant_table)
{
return 0;
}
return db_get_material_index_hook.invoke<unsigned int>(material);
}
}
void add(const std::string& name, const std::string& data)
@ -146,6 +166,24 @@ namespace materials
});
}
void clear()
{
material_data.access([&](material_data_t& data_)
{
for (auto& material : data_.materials)
{
if (material.second == nullptr)
{
continue;
}
free_material(material.second);
}
data_.materials.clear();
});
}
class component final : public component_interface
{
public:
@ -158,6 +196,7 @@ namespace materials
material_register_handle_hook.create(game::Material_RegisterHandle, material_register_handle_stub);
db_material_streaming_fail_hook.create(SELECT_VALUE(0x1401D3180, 0x1402C6260), db_material_streaming_fail_stub);
db_get_material_index_hook.create(SELECT_VALUE(0x1401CAD00, 0x1402BBB20), db_get_material_index_stub);
}
};
}

View File

@ -3,4 +3,5 @@
namespace materials
{
void add(const std::string& name, const std::string& data);
void clear();
}

View File

@ -0,0 +1,119 @@
#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "game/game.hpp"
#include "game/dvars.hpp"
#include "command.hpp"
#include "console.hpp"
#include "scheduler.hpp"
#include "filesystem.hpp"
#include "materials.hpp"
#include "fonts.hpp"
#include "mods.hpp"
#include <utils/hook.hpp>
#include <utils/io.hpp>
namespace mods
{
std::string mod_path{};
namespace
{
utils::hook::detour db_release_xassets_hook;
bool release_assets = false;
void db_release_xassets_stub()
{
if (release_assets)
{
materials::clear();
fonts::clear();
}
db_release_xassets_hook.invoke<void>();
}
void restart()
{
scheduler::once([]()
{
release_assets = true;
game::Com_Shutdown("");
release_assets = false;
}, scheduler::pipeline::main);
}
}
class component final : public component_interface
{
public:
void post_unpack() override
{
if (!game::environment::is_sp())
{
return;
}
if (!utils::io::directory_exists("mods"))
{
utils::io::create_directory("mods");
}
db_release_xassets_hook.create(SELECT_VALUE(0x1401CD560, 0x1402BF160), db_release_xassets_stub);
command::add("loadmod", [](const command::params& params)
{
if (params.size() < 2)
{
console::info("Usage: loadmod mods/<modname>");
return;
}
if (!game::Com_InFrontend())
{
console::info("Cannot load mod while in-game!\n");
game::CG_GameMessage(0, "^1Cannot unload mod while in-game!");
return;
}
const auto path = params.get(1);
if (!utils::io::directory_exists(path))
{
console::info("Mod %s not found!\n", path);
return;
}
console::info("Loading mod %s\n", path);
filesystem::get_search_paths().erase(mod_path);
filesystem::get_search_paths().insert(path);
mod_path = path;
restart();
});
command::add("unloadmod", [](const command::params& params)
{
if (mod_path.empty())
{
console::info("No mod loaded\n");
return;
}
if (!game::Com_InFrontend())
{
console::info("Cannot unload mod while in-game!\n");
game::CG_GameMessage(0, "^1Cannot unload mod while in-game!");
return;
}
console::info("Unloading mod %s\n", mod_path.data());
filesystem::get_search_paths().erase(mod_path);
mod_path.clear();
restart();
});
}
};
}
REGISTER_COMPONENT(mods::component)

View File

@ -0,0 +1,6 @@
#pragma once
namespace mods
{
extern std::string mod_path;
}