diff --git a/src/client/component/fonts.cpp b/src/client/component/fonts.cpp new file mode 100644 index 00000000..ae8f37fe --- /dev/null +++ b/src/client/component/fonts.cpp @@ -0,0 +1,116 @@ +#include +#include "loader/component_loader.hpp" + +#include "fonts.hpp" +#include "console.hpp" + +#include "game/game.hpp" +#include "game/dvars.hpp" + +#include +#include +#include +#include +#include +#include + +namespace fonts +{ + namespace + { + struct font_data_t + { + std::unordered_map fonts; + std::unordered_map raw_fonts; + }; + + utils::concurrency::container font_data; + + game::TTF* create_font(const std::string& name, const std::string& data) + { + const auto font = utils::memory::get_allocator()->allocate(); + font->name = utils::memory::get_allocator()->duplicate_string(name); + font->buffer = utils::memory::get_allocator()->duplicate_string(data); + font->len = static_cast(data.size()); + font->fontFace = 0; + return font; + } + + game::TTF* load_font(const std::string& name) + { + return font_data.access([&](font_data_t& data_) -> game::TTF* + { + if (const auto i = data_.fonts.find(name); i != data_.fonts.end()) + { + return i->second; + } + + std::string data{}; + if (const auto i = data_.raw_fonts.find(name); i != data_.raw_fonts.end()) + { + 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)) + { + return nullptr; + } + + const auto material = create_font(name, data); + data_.fonts[name] = material; + + return material; + }); + } + + game::TTF* try_load_font(const std::string& name) + { + try + { + return load_font(name); + } + catch (const std::exception& e) + { + console::error("Failed to load font %s: %s\n", name.data(), e.what()); + } + + return nullptr; + } + + game::TTF* db_find_xasset_header_stub(game::XAssetType type, const char* name, int create_default) + { + auto result = try_load_font(name); + if (result == nullptr) + { + result = game::DB_FindXAssetHeader(type, name, create_default).ttf; + } + return result; + } + } + + void add(const std::string& name, const std::string& data) + { + font_data.access([&](font_data_t& data_) + { + data_.raw_fonts[name] = data; + }); + } + + class component final : public component_interface + { + public: + void post_unpack() override + { + if (game::environment::is_dedi()) + { + return; + } + + utils::hook::call(SELECT_VALUE(0x1404D41B6, 0x1405D9296), db_find_xasset_header_stub); + } + }; +} + +REGISTER_COMPONENT(fonts::component) diff --git a/src/client/component/fonts.hpp b/src/client/component/fonts.hpp new file mode 100644 index 00000000..15749bcf --- /dev/null +++ b/src/client/component/fonts.hpp @@ -0,0 +1,6 @@ +#pragma once + +namespace fonts +{ + void add(const std::string& name, const std::string& data); +} diff --git a/src/client/game/structs.hpp b/src/client/game/structs.hpp index 40515051..4d6b3405 100644 --- a/src/client/game/structs.hpp +++ b/src/client/game/structs.hpp @@ -1316,6 +1316,14 @@ namespace game const char* buffer; }; + struct TTF + { + const char* name; + int len; + const char* buffer; + int fontFace; + }; + struct GfxImageLoadDef { char levelCount; @@ -1387,6 +1395,7 @@ namespace game StringTable* stringTable; LuaFile* luaFile; GfxImage* image; + TTF* ttf; }; struct XAsset diff --git a/src/client/game/symbols.hpp b/src/client/game/symbols.hpp index f42835dd..1ade88ce 100644 --- a/src/client/game/symbols.hpp +++ b/src/client/game/symbols.hpp @@ -143,6 +143,8 @@ namespace game DB_EnumXAssets_Internal{0x1401C9C10, 0x1402BA830}; WEAK symbol DB_GetXAssetName{0x14019A390, 0x14028BE50}; WEAK symbol DB_GetXAssetTypeSize{0x14019A3B0, 0x14028BE70}; + WEAK symbol DB_FindXAssetHeader{0x1401CA150, 0x1402BAC70}; WEAK symbol LUI_OpenMenu{0x14039D5F0, 0x1404CD210};