2024-01-06 16:37:56 +01:00

269 lines
5.8 KiB
C++

#include <std_include.hpp>
#include "loader/component_loader.hpp"
#include "fonts.hpp"
#include "console.hpp"
#include "filesystem.hpp"
#include "command.hpp"
#include "language.hpp"
#include "config.hpp"
#include "fastfiles.hpp"
#include "game/game.hpp"
#include "game/dvars.hpp"
#include <utils/hook.hpp>
#include <utils/memory.hpp>
#include <utils/io.hpp>
#include <utils/string.hpp>
#include <utils/image.hpp>
#include <utils/concurrency.hpp>
namespace fonts
{
namespace
{
std::array<const char*, game::HE_FONT_COUNT> hudelem_fonts
{
"",
"bigfixed",
"smallfixed",
"objective",
"big",
"small",
"hudbig",
"hudsmall",
"buttomprompt",
"subtitle",
"timer",
"nameplate",
"bank",
"bankshadow",
"bankshadowmore",
};
game::Font_s* bank_font = nullptr;
utils::hook::detour ui_get_font_handle_hook;
utils::hook::detour ui_get_font_handle2_hook;
utils::hook::detour ui_asset_cache_hook;
game::Font_s* get_font_handle_at_index(int font_index)
{
switch (font_index)
{
case game::HE_FONT_BANK:
case game::HE_FONT_BANKSHADOW:
case game::HE_FONT_BANKSHADOWMORE:
return bank_font;
}
return nullptr;
}
game::Font_s* ui_get_font_handle_stub(void* a1, int font_index)
{
const auto res = get_font_handle_at_index(font_index);
if (res)
{
return res;
}
return ui_get_font_handle_hook.invoke<game::Font_s*>(a1, font_index);
}
game::Font_s* ui_get_font_handle2_stub(void* a1, size_t a2)
{
const auto font_index = *reinterpret_cast<int*>(a2 + 208);
const auto res = get_font_handle_at_index(font_index);
if (res)
{
return res;
}
return ui_get_font_handle2_hook.invoke<game::Font_s*>(a1, a2);
}
game::Font_s* get_bank_font()
{
if (language::is_asian())
{
return game::R_RegisterFont("fonts/defaultBold.otf", 32);
}
else
{
return game::R_RegisterFont("fonts/bank.ttf", 32);
}
}
void* ui_asset_cache_stub()
{
bank_font = get_bank_font();
return ui_asset_cache_hook.invoke<void*>();
}
int get_font_handle_index(int hudelem_font_index, int current)
{
switch (hudelem_font_index)
{
case game::HE_FONT_BANK:
case game::HE_FONT_BANKSHADOW:
case game::HE_FONT_BANKSHADOWMORE:
return hudelem_font_index;
}
return current;
}
void get_hud_elem_info_stub(utils::hook::assembler& a)
{
a.push(ebx);
a.pushad64();
a.mov(edx, ebx);
a.mov(ecx, dword_ptr(rsi, 4));
a.call_aligned(get_font_handle_index);
a.mov(dword_ptr(rsp, 0x80), eax);
a.popad64();
a.pop(ebx);
a.mov(edx, dword_ptr(rdi, 0x240));
a.lea(r8, qword_ptr(rsp, 0x98));
a.jmp(0x14037B39E);
}
int get_font_style(int font_index)
{
switch (font_index)
{
case game::HE_FONT_BANK:
return 0; // none
case game::HE_FONT_BANKSHADOW:
return 2; // shadowed
case game::HE_FONT_BANKSHADOWMORE:
return 4; // shadowed more
}
return 0;
}
void get_hud_elem_text_style_stub(utils::hook::assembler& a)
{
const auto original = a.newLabel();
const auto loc_14037AF98 = a.newLabel();
a.mov(eax, dword_ptr(rdi, 4));
a.cmp(eax, 12);
a.jl(original);
a.push(edx);
a.pushad64();
a.mov(ecx, eax);
a.call_aligned(get_font_style);
a.mov(dword_ptr(rsp, 0x80), eax);
a.popad64();
a.pop(edx);
a.jmp(0x14037AFA5);
a.bind(original);
a.cmp(eax, 9);
a.jnz(loc_14037AF98);
a.mov(edx, 0x400);
a.jmp(0x14037AFA5);
a.bind(loc_14037AF98);
a.jmp(0x14037AF98);
}
}
void load_font_zones()
{
const auto disabled = config::get<bool>("disable_custom_fonts");
const auto custom_fonts_disabled = disabled.has_value() && disabled.value() && !language::is_custom();
const auto table = game::DB_FindXAssetHeader(game::ASSET_TYPE_STRINGTABLE, "font_zones.csv", 0).stringTable;
if (table == nullptr)
{
return;
}
const auto lang = language::current();
const std::string lang_name = language::languages[lang].name;
for (auto row = 0; row < table->rowCount; row++)
{
if (table->columnCount < 3)
{
continue;
}
const auto row_values = &table->values[(row * table->columnCount)];
const auto lang_value = row_values[0].string;
const auto is_replacement = lang_value != "*"s;
if (lang_value != lang_name && is_replacement)
{
continue;
}
if (custom_fonts_disabled && is_replacement)
{
continue;
}
for (auto col = 1; col < table->columnCount; col++)
{
const auto zone = row_values[col].string;
if (zone != nullptr)
{
fastfiles::try_load_zone(zone, true);
}
}
}
}
class component final : public component_interface
{
public:
void post_unpack() override
{
// add custom fonts to hud elem fonts
ui_asset_cache_hook.create(0x140606090, ui_asset_cache_stub);
ui_get_font_handle_hook.create(0x1406058F0, ui_get_font_handle_stub);
ui_get_font_handle2_hook.create(0x1405F9820, ui_get_font_handle2_stub);
// change hudelem font array
utils::hook::inject(0x1404C17A6, hudelem_fonts.data());
utils::hook::set(0x1404C17B7, static_cast<int>(hudelem_fonts.size()));
// handle custom fonts
utils::hook::jump(0x14037B390, utils::hook::assemble(get_hud_elem_info_stub), true);
// handle custom font styles
utils::hook::jump(0x14037AF89, utils::hook::assemble(get_hud_elem_text_style_stub), true);
command::add("dumpFont", [](const command::params& params)
{
if (params.size() < 2)
{
return;
}
const auto name = params.get(1);
const auto ttf = game::DB_FindXAssetHeader(game::XAssetType::ASSET_TYPE_TTF, name, false).ttfDef;
if (ttf == nullptr)
{
console::error("Font does not exist\n");
return;
}
const auto path = utils::string::va("dumps/%s", ttf->name);
utils::io::write_file(path, std::string(ttf->file, ttf->fileLen), false);
console::info("Dumped to %s", path);
});
}
};
}
REGISTER_COMPONENT(fonts::component)