#include #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 #include #include #include #include #include namespace fonts { namespace { std::array 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(a1, font_index); } game::Font_s* ui_get_font_handle2_stub(void* a1, size_t a2) { const auto font_index = *reinterpret_cast(a2 + 208); const auto res = get_font_handle_at_index(font_index); if (res) { return res; } return ui_get_font_handle2_hook.invoke(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(); } 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("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(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)