From 1064077b9c91dad543313ed236d1a7d9b7290c6a Mon Sep 17 00:00:00 2001 From: Andreas Maerten <24669514+Yimura@users.noreply.github.com> Date: Wed, 21 Feb 2024 21:26:29 +0100 Subject: [PATCH] feat(renderer): added dynamic font loading based on lang requirements (#2738) Related YimMenu/Translations#117 Closes #2723 Changes made: - Renderer class was changed to a static instance - Some throw code was removed because of change of instantiation of the renderer - Fonts are dynamically loaded based on the language requirements - renderer files have been moved to make the root directory less messy - Added a font mgr to improve the ease of adding more language support --- src/file_manager.cpp | 78 +++---- src/gui.cpp | 24 +- src/hooks/gui/swap_chain_present.cpp | 6 +- src/hooks/gui/swap_chain_resizebuffers.cpp | 6 +- src/hooks/gui/wndproc.cpp | 4 +- src/main.cpp | 10 +- src/renderer.cpp | 219 ------------------ src/renderer/alphabet_types.hpp | 15 ++ src/renderer/font_mgr.cpp | 141 +++++++++++ src/renderer/font_mgr.hpp | 39 ++++ src/renderer/renderer.cpp | 133 +++++++++++ src/{ => renderer}/renderer.hpp | 15 +- src/services/hotkey/hotkey_service.cpp | 6 +- .../translation_service/local_index.hpp | 6 +- .../translation_service/remote_index.hpp | 2 +- .../translation_service/translation_entry.hpp | 7 +- .../translation_service.cpp | 5 + src/views/settings/view_gui_settings.cpp | 6 +- 18 files changed, 426 insertions(+), 296 deletions(-) delete mode 100644 src/renderer.cpp create mode 100644 src/renderer/alphabet_types.hpp create mode 100644 src/renderer/font_mgr.cpp create mode 100644 src/renderer/font_mgr.hpp create mode 100644 src/renderer/renderer.cpp rename src/{ => renderer}/renderer.hpp (85%) diff --git a/src/file_manager.cpp b/src/file_manager.cpp index 3f30d098..438d2ad6 100644 --- a/src/file_manager.cpp +++ b/src/file_manager.cpp @@ -2,58 +2,58 @@ namespace big { - bool file_manager::init(const std::filesystem::path& base_dir) - { - m_base_dir = base_dir; - file_manager::ensure_folder_exists(m_base_dir); + bool file_manager::init(const std::filesystem::path& base_dir) + { + m_base_dir = base_dir; + file_manager::ensure_folder_exists(m_base_dir); - return true; - } + return true; + } + + const std::filesystem::path& file_manager::get_base_dir() + { + return m_base_dir; + } - const std::filesystem::path& file_manager::get_base_dir() - { - return m_base_dir; - } - file file_manager::get_project_file(std::filesystem::path file_path) - { - if (file_path.is_absolute()) - throw std::invalid_argument("Project files are relative to the BaseDir, don't use absolute paths!"); - if (file_path.string().contains("..")) + { + if (file_path.is_absolute()) + throw std::invalid_argument("Project files are relative to the BaseDir, don't use absolute paths!"); + if (file_path.string().contains("..")) throw std::invalid_argument("Relative path traversal is not allowed, refrain from using \"..\" in file paths."); - return file_manager::ensure_file_can_be_created(m_base_dir / file_path); - } + return file_manager::ensure_file_can_be_created(m_base_dir / file_path); + } folder file_manager::get_project_folder(std::filesystem::path folder_path) - { - if (folder_path.is_absolute()) - throw std::invalid_argument("Project folders are relative to the BaseDir, don't use absolute paths!"); - if (folder_path.string().contains("..")) + { + if (folder_path.is_absolute()) + throw std::invalid_argument("Project folders are relative to the BaseDir, don't use absolute paths!"); + if (folder_path.string().contains("..")) throw std::invalid_argument("Relative path traversal is not allowed, refrain from using \"..\" in folder paths."); - return file_manager::ensure_folder_exists(m_base_dir / folder_path); - } + return file_manager::ensure_folder_exists(m_base_dir / folder_path); + } std::filesystem::path file_manager::ensure_file_can_be_created(const std::filesystem::path file_path) - { - file_manager::ensure_folder_exists(file_path.parent_path()); + { + file_manager::ensure_folder_exists(file_path.parent_path()); + + return file_path; + } - return file_path; - } - std::filesystem::path file_manager::ensure_folder_exists(const std::filesystem::path folder_path) - { - bool create_path = !exists(folder_path); + { + bool create_path = !exists(folder_path); - if (!create_path && !is_directory(folder_path)) - { - remove(folder_path); - create_path = true; - } - if (create_path) - create_directories(folder_path); + if (!create_path && !is_directory(folder_path)) + { + remove(folder_path); + create_path = true; + } + if (create_path) + create_directories(folder_path); - return folder_path; - } + return folder_path; + } } diff --git a/src/gui.cpp b/src/gui.cpp index 975434f4..7849be49 100644 --- a/src/gui.cpp +++ b/src/gui.cpp @@ -2,7 +2,7 @@ #include "common.hpp" #include "natives.hpp" -#include "renderer.hpp" +#include "renderer/renderer.hpp" #include "script.hpp" #include "views/view.hpp" @@ -14,35 +14,35 @@ namespace big m_is_open(false), m_override_mouse(false) { - g_renderer->add_dx_callback(view::gta_data, -1); - g_renderer->add_dx_callback(view::notifications, -2); - g_renderer->add_dx_callback(view::overlay, -3); - g_renderer->add_dx_callback(view::cmd_executor, -4); - g_renderer->add_dx_callback( + g_renderer.add_dx_callback(view::gta_data, -1); + g_renderer.add_dx_callback(view::notifications, -2); + g_renderer.add_dx_callback(view::overlay, -3); + g_renderer.add_dx_callback(view::cmd_executor, -4); + g_renderer.add_dx_callback( [this] { dx_on_tick(); }, -5); - g_renderer->add_wndproc_callback([this](HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { + g_renderer.add_wndproc_callback([this](HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { wndproc(hwnd, msg, wparam, lparam); }); - g_renderer->add_wndproc_callback([](HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { + g_renderer.add_wndproc_callback([](HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { if (g.cmd_executor.enabled && msg == WM_KEYUP && wparam == VK_ESCAPE) { g.cmd_executor.enabled = false; } }); - g_renderer->add_dx_callback(view::vehicle_control, 3); - g_renderer->add_dx_callback(esp::draw, 2); // TODO: move to ESP service - g_renderer->add_dx_callback(view::context_menu, 1); + g_renderer.add_dx_callback(view::vehicle_control, 3); + g_renderer.add_dx_callback(esp::draw, 2); // TODO: move to ESP service + g_renderer.add_dx_callback(view::context_menu, 1); dx_init(); g_gui = this; - g_renderer->rescale(g.window.gui_scale); + g_renderer.rescale(g.window.gui_scale); } gui::~gui() diff --git a/src/hooks/gui/swap_chain_present.cpp b/src/hooks/gui/swap_chain_present.cpp index d15657f3..ae74ad12 100644 --- a/src/hooks/gui/swap_chain_present.cpp +++ b/src/hooks/gui/swap_chain_present.cpp @@ -1,5 +1,5 @@ #include "hooking/hooking.hpp" -#include "renderer.hpp" +#include "renderer/renderer.hpp" #include "script.hpp" namespace big @@ -8,9 +8,9 @@ namespace big { if (g_running && ((flags & (UINT)DXGI_PRESENT_TEST) != (UINT)DXGI_PRESENT_TEST)) { - g_renderer->on_present(); + g_renderer.on_present(); } return g_hooking->m_swapchain_hook.get_original(swapchain_present_index)(this_, sync_interval, flags); } -} \ No newline at end of file +} diff --git a/src/hooks/gui/swap_chain_resizebuffers.cpp b/src/hooks/gui/swap_chain_resizebuffers.cpp index 8f3870b9..a68596aa 100644 --- a/src/hooks/gui/swap_chain_resizebuffers.cpp +++ b/src/hooks/gui/swap_chain_resizebuffers.cpp @@ -1,5 +1,5 @@ #include "hooking/hooking.hpp" -#include "renderer.hpp" +#include "renderer/renderer.hpp" #include "script.hpp" namespace big @@ -8,13 +8,13 @@ namespace big { if (g_running) { - g_renderer->pre_reset(); + g_renderer.pre_reset(); const auto result = g_hooking->m_swapchain_hook.get_original(swapchain_resizebuffers_index)(this_, buffer_count, width, height, new_format, swapchain_flags); if (SUCCEEDED(result)) { - g_renderer->post_reset(); + g_renderer.post_reset(); } return result; diff --git a/src/hooks/gui/wndproc.cpp b/src/hooks/gui/wndproc.cpp index 5aa471d7..60e6418d 100644 --- a/src/hooks/gui/wndproc.cpp +++ b/src/hooks/gui/wndproc.cpp @@ -1,5 +1,5 @@ #include "hooking/hooking.hpp" -#include "renderer.hpp" +#include "renderer/renderer.hpp" #include "script.hpp" namespace big @@ -8,7 +8,7 @@ namespace big { if (g_running) { - g_renderer->wndproc(hwnd, msg, wparam, lparam); + g_renderer.wndproc(hwnd, msg, wparam, lparam); } return CallWindowProcW(g_hooking->m_og_wndproc, hwnd, msg, wparam, lparam); diff --git a/src/main.cpp b/src/main.cpp index 894e35f2..c9633de6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -10,7 +10,7 @@ #include "native_hooks/native_hooks.hpp" #include "pointers.hpp" #include "rage/gameSkeleton.hpp" -#include "renderer.hpp" +#include "renderer/renderer.hpp" #include "script_mgr.hpp" #include "services/api/api_service.hpp" #include "services/context_menu/context_menu_service.hpp" @@ -159,10 +159,10 @@ BOOL APIENTRY DllMain(HMODULE hmod, DWORD reason, PVOID) LOG(INFO) << "Yim's Menu Initializing"; LOGF(INFO, "Git Info\n\tBranch:\t{}\n\tHash:\t{}\n\tDate:\t{}", version::GIT_BRANCH, version::GIT_SHA1, version::GIT_DATE); - // more tech debt, YAY! + // more tech debt, YAY! if (is_proton()) { - LOG(INFO) << "Running on proton!"; + LOG(INFO) << "Running on proton!"; } else { @@ -195,7 +195,7 @@ BOOL APIENTRY DllMain(HMODULE hmod, DWORD reason, PVOID) auto byte_patch_manager_instance = std::make_unique(); LOG(INFO) << "Byte Patch Manager initialized."; - auto renderer_instance = std::make_unique(); + g_renderer.init(); LOG(INFO) << "Renderer initialized."; auto gui_instance = std::make_unique(); @@ -327,7 +327,7 @@ BOOL APIENTRY DllMain(HMODULE hmod, DWORD reason, PVOID) fiber_pool_instance.reset(); LOG(INFO) << "Fiber pool uninitialized."; - renderer_instance.reset(); + g_renderer.destroy(); LOG(INFO) << "Renderer uninitialized."; byte_patch_manager_instance.reset(); diff --git a/src/renderer.cpp b/src/renderer.cpp deleted file mode 100644 index 3ac5f29c..00000000 --- a/src/renderer.cpp +++ /dev/null @@ -1,219 +0,0 @@ -#include "renderer.hpp" - -#include "common.hpp" -#include "file_manager.hpp" -#include "fonts/fonts.hpp" -#include "gui.hpp" -#include "pointers.hpp" - -#include -#include -#include -#include - -IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); - -namespace big -{ - renderer::renderer() : - m_dxgi_swapchain(*g_pointers->m_gta.m_swapchain) - { - if (m_dxgi_swapchain->GetDevice(__uuidof(ID3D11Device), reinterpret_cast(&m_d3d_device)) < 0) - { - throw std::runtime_error("Failed to get D3D device."); - } - m_d3d_device->GetImmediateContext(&m_d3d_device_context); - - auto file_path = g_file_manager.get_project_file("./imgui.ini").get_path(); - - ImGuiContext* ctx = ImGui::CreateContext(); - - static std::string path = file_path.make_preferred().string(); - ctx->IO.IniFilename = path.c_str(); - - ImGui_ImplDX11_Init(m_d3d_device, m_d3d_device_context); - ImGui_ImplWin32_Init(g_pointers->m_hwnd); - - const auto fonts_folder = std::filesystem::path(std::getenv("SYSTEMROOT")) / "Fonts"; - - file font_file(fonts_folder / "msyh.ttc"); - if (!font_file.exists()) - font_file = file(fonts_folder / "msyh.ttf"); - - if (*g_pointers->m_gta.m_language == 8) - { - font_file = file(fonts_folder / "malgun.ttf"); - if (!font_file.exists()) - { - LOG(WARNING) << "Korean language detected, but failed to find Korean font, you may not be able to read the menu!"; - } - } - - if (!font_file.exists()) - { - LOG(WARNING) << "Failed to find msyh font, falling back to Arial!"; - font_file = file(fonts_folder / "arial.ttf"); - } - - auto font_file_stream = std::ifstream(font_file.get_path(), std::ios::binary | std::ios::ate); - const auto font_data_size = static_cast(font_file_stream.tellg()); - const auto font_data = std::make_unique(font_data_size); - - font_file_stream.seekg(0); - font_file_stream.read(reinterpret_cast(font_data.get()), font_data_size); - font_file_stream.close(); - - auto& io = ImGui::GetIO(); - - { - ImFontConfig fnt_cfg{}; - fnt_cfg.FontDataOwnedByAtlas = false; - strcpy(fnt_cfg.Name, "Fnt20px"); - - io.Fonts->AddFontFromMemoryTTF(const_cast(font_storopia), - sizeof(font_storopia), - 20.f, - &fnt_cfg, - io.Fonts->GetGlyphRangesDefault()); - fnt_cfg.MergeMode = true; - if (*g_pointers->m_gta.m_language == 8) - io.Fonts->AddFontFromMemoryTTF(font_data.get(), font_data_size, 20.f, &fnt_cfg, ImGui::GetIO().Fonts->GetGlyphRangesKorean()); - else - io.Fonts->AddFontFromMemoryTTF(font_data.get(), font_data_size, 20.f, &fnt_cfg, ImGui::GetIO().Fonts->GetGlyphRangesChineseSimplifiedCommon()); - io.Fonts->AddFontFromMemoryTTF(font_data.get(), font_data_size, 20.f, &fnt_cfg, ImGui::GetIO().Fonts->GetGlyphRangesCyrillic()); - io.Fonts->Build(); - } - - { - ImFontConfig fnt_cfg{}; - fnt_cfg.FontDataOwnedByAtlas = false; - strcpy(fnt_cfg.Name, "Fnt28px"); - - g.window.font_title = io.Fonts->AddFontFromMemoryTTF(const_cast(font_storopia), sizeof(font_storopia), 28.f, &fnt_cfg); - fnt_cfg.MergeMode = true; - if (*g_pointers->m_gta.m_language == 8) - io.Fonts->AddFontFromMemoryTTF(font_data.get(), font_data_size, 28.f, &fnt_cfg, ImGui::GetIO().Fonts->GetGlyphRangesKorean()); - else - io.Fonts->AddFontFromMemoryTTF(font_data.get(), font_data_size, 28.f, &fnt_cfg, ImGui::GetIO().Fonts->GetGlyphRangesChineseSimplifiedCommon()); - io.Fonts->AddFontFromMemoryTTF(font_data.get(), font_data_size, 28.f, &fnt_cfg, ImGui::GetIO().Fonts->GetGlyphRangesCyrillic()); - io.Fonts->Build(); - } - - { - ImFontConfig fnt_cfg{}; - fnt_cfg.FontDataOwnedByAtlas = false; - strcpy(fnt_cfg.Name, "Fnt24px"); - - g.window.font_sub_title = io.Fonts->AddFontFromMemoryTTF(const_cast(font_storopia), sizeof(font_storopia), 24.f, &fnt_cfg); - fnt_cfg.MergeMode = true; - if (*g_pointers->m_gta.m_language == 8) - io.Fonts->AddFontFromMemoryTTF(font_data.get(), font_data_size, 24.f, &fnt_cfg, ImGui::GetIO().Fonts->GetGlyphRangesKorean()); - else - io.Fonts->AddFontFromMemoryTTF(font_data.get(), font_data_size, 24.f, &fnt_cfg, ImGui::GetIO().Fonts->GetGlyphRangesChineseSimplifiedCommon()); - io.Fonts->AddFontFromMemoryTTF(font_data.get(), font_data_size, 24.f, &fnt_cfg, ImGui::GetIO().Fonts->GetGlyphRangesCyrillic()); - io.Fonts->Build(); - } - - { - ImFontConfig fnt_cfg{}; - fnt_cfg.FontDataOwnedByAtlas = false; - strcpy(fnt_cfg.Name, "Fnt18px"); - - g.window.font_small = io.Fonts->AddFontFromMemoryTTF(const_cast(font_storopia), sizeof(font_storopia), 18.f, &fnt_cfg); - fnt_cfg.MergeMode = true; - if (*g_pointers->m_gta.m_language == 8) - io.Fonts->AddFontFromMemoryTTF(font_data.get(), font_data_size, 18.f, &fnt_cfg, ImGui::GetIO().Fonts->GetGlyphRangesKorean()); - else - io.Fonts->AddFontFromMemoryTTF(font_data.get(), font_data_size, 18.f, &fnt_cfg, ImGui::GetIO().Fonts->GetGlyphRangesChineseSimplifiedCommon()); - io.Fonts->AddFontFromMemoryTTF(font_data.get(), font_data_size, 18.f, &fnt_cfg, ImGui::GetIO().Fonts->GetGlyphRangesCyrillic()); - io.Fonts->Build(); - } - - { - ImFontConfig font_icons_cfg{}; - font_icons_cfg.FontDataOwnedByAtlas = false; - std::strcpy(font_icons_cfg.Name, "Icons"); - g.window.font_icon = io.Fonts->AddFontFromMemoryTTF(const_cast(font_icons), sizeof(font_icons), 24.f, &font_icons_cfg); - } - - g_renderer = this; - } - - renderer::~renderer() - { - g_renderer = nullptr; - - ImGui_ImplWin32_Shutdown(); - ImGui_ImplDX11_Shutdown(); - ImGui::DestroyContext(); - } - - bool renderer::add_dx_callback(dx_callback callback, uint32_t priority) - { - if (!m_dx_callbacks.insert({priority, callback}).second) - { - LOG(WARNING) << "Duplicate priority given on DX Callback!"; - - return false; - } - return true; - } - - void renderer::add_wndproc_callback(wndproc_callback callback) - { - m_wndproc_callbacks.emplace_back(callback); - } - - void renderer::on_present() - { - new_frame(); - for (const auto& cb : m_dx_callbacks | std::views::values) - cb(); - end_frame(); - } - - void renderer::rescale(float rel_size) - { - pre_reset(); - g_gui->restore_default_style(); - - if (rel_size != 1.0f) - ImGui::GetStyle().ScaleAllSizes(rel_size); - - ImGui::GetStyle().MouseCursorScale = 1.0f; - ImGui::GetIO().FontGlobalScale = rel_size; - post_reset(); - } - - void renderer::pre_reset() - { - ImGui_ImplDX11_InvalidateDeviceObjects(); - } - - void renderer::post_reset() - { - ImGui_ImplDX11_CreateDeviceObjects(); - } - - void renderer::wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) - { - for (const auto& cb : m_wndproc_callbacks) - cb(hwnd, msg, wparam, lparam); - - - ImGui_ImplWin32_WndProcHandler(hwnd, msg, wparam, lparam); - } - - void renderer::new_frame() - { - ImGui_ImplDX11_NewFrame(); - ImGui_ImplWin32_NewFrame(); - ImGui::NewFrame(); - } - - void renderer::end_frame() - { - ImGui::Render(); - ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData()); - } -} diff --git a/src/renderer/alphabet_types.hpp b/src/renderer/alphabet_types.hpp new file mode 100644 index 00000000..a51de19d --- /dev/null +++ b/src/renderer/alphabet_types.hpp @@ -0,0 +1,15 @@ +#pragma once + +namespace big +{ + enum class eAlphabetType + { + LATIN, // default - always built + CHINESE, + CYRILLIC, + KOREAN, + JAPANESE, + LAST_ALPHABET_TYPE + }; + NLOHMANN_JSON_SERIALIZE_ENUM(eAlphabetType, {{eAlphabetType::LATIN, "latin"}, {eAlphabetType::CHINESE, "chinese"}, {eAlphabetType::CYRILLIC, "cyrillic"}, {eAlphabetType::KOREAN, "korean"}, {eAlphabetType::JAPANESE, "japanese"}}) +} diff --git a/src/renderer/font_mgr.cpp b/src/renderer/font_mgr.cpp new file mode 100644 index 00000000..efaff6eb --- /dev/null +++ b/src/renderer/font_mgr.cpp @@ -0,0 +1,141 @@ +#include "font_mgr.hpp" + +#include "fonts/fonts.hpp" +#include "renderer.hpp" +#include "thread_pool.hpp" + +namespace big +{ + font_mgr::font_mgr(std::vector> extra_font_sizes) : + m_extra_font_sizes(extra_font_sizes), + m_require_extra(eAlphabetType::LATIN), + m_fonts({ + {eAlphabetType::CHINESE, {"msyh.ttc", "msyh.ttf", "arial.ttf"}}, + {eAlphabetType::CYRILLIC, {"msyh.ttc", "msyh.ttf", "arial.ttf"}}, + {eAlphabetType::JAPANESE, {"msyh.ttc", "msyh.ttf", "arial.ttf"}}, + {eAlphabetType::KOREAN, {"malgun.ttf", "arial.ttf"}}, + }) + { + } + + bool font_mgr::can_use() + { + return m_update_lock.try_lock(); + } + + void font_mgr::release_use() + { + m_update_lock.unlock(); + } + + void font_mgr::rebuild() + { + m_update_lock.lock(); + + g_renderer.pre_reset(); + + const auto extra_font_file = get_available_font_file_for_alphabet_type(); + if (m_require_extra != eAlphabetType::LATIN && !extra_font_file.exists()) + { + LOG(WARNING) << "Could not find an appropriate font for the current language!"; + } + const auto extra_glyph_range = get_imgui_alphabet_type(); + + auto& io = ImGui::GetIO(); + io.Fonts->Clear(); + + // default font size + { + ImFontConfig fnt_cfg{}; + fnt_cfg.FontDataOwnedByAtlas = false; + strcpy(fnt_cfg.Name, "Fnt20px"); + + io.Fonts->AddFontFromMemoryTTF(const_cast(font_storopia), + sizeof(font_storopia), + 20.f, + &fnt_cfg, + io.Fonts->GetGlyphRangesDefault()); + if (m_require_extra != eAlphabetType::LATIN && extra_font_file.exists()) + { + fnt_cfg.MergeMode = true; + io.Fonts->AddFontFromFileTTF(extra_font_file.get_path().string().c_str(), 20.f, &fnt_cfg, extra_glyph_range); + } + io.Fonts->Build(); + } + + // any other font sizes we need to support + for (auto [size, font_ptr] : m_extra_font_sizes) + { + ImFontConfig fnt_cfg{}; + fnt_cfg.FontDataOwnedByAtlas = false; + strcpy(fnt_cfg.Name, std::format("Fnt{}px", (int)size).c_str()); + + *font_ptr = io.Fonts->AddFontFromMemoryTTF(const_cast(font_storopia), + sizeof(font_storopia), + size, + &fnt_cfg, + io.Fonts->GetGlyphRangesDefault()); + if (m_require_extra != eAlphabetType::LATIN && extra_font_file.exists()) + { + fnt_cfg.MergeMode = true; + io.Fonts->AddFontFromFileTTF(extra_font_file.get_path().string().c_str(), size, &fnt_cfg, extra_glyph_range); + } + io.Fonts->Build(); + } + + // icons blueh + { + ImFontConfig font_icons_cfg{}; + font_icons_cfg.FontDataOwnedByAtlas = false; + std::strcpy(font_icons_cfg.Name, "Icons"); + g.window.font_icon = ImGui::GetIO().Fonts->AddFontFromMemoryTTF(const_cast(font_icons), sizeof(font_icons), 24.f, &font_icons_cfg); + } + + g_renderer.post_reset(); + + m_update_lock.unlock(); + } + + void font_mgr::update_required_alphabet_type(eAlphabetType type) + { + m_require_extra = type; + + g_thread_pool->push([this] + { + rebuild(); + }); + } + + file font_mgr::get_available_font_file_for_alphabet_type() + { + static const auto fonts_folder = std::filesystem::path(std::getenv("SYSTEMROOT")) / "Fonts"; + + const auto& fonts = m_fonts.find(m_require_extra); + if (fonts == m_fonts.end()) + return {}; + for (const auto& font : fonts->second) + { + auto font_file = file(fonts_folder / font); + if (font_file.exists()) + { + return font_file; + } + } + return {}; + } + + const ImWchar* font_mgr::get_imgui_alphabet_type() + { + auto& io = ImGui::GetIO(); + switch (m_require_extra) + { + case eAlphabetType::CHINESE: return io.Fonts->GetGlyphRangesChineseFull(); + case eAlphabetType::CYRILLIC: return io.Fonts->GetGlyphRangesCyrillic(); + case eAlphabetType::JAPANESE: return io.Fonts->GetGlyphRangesJapanese(); + case eAlphabetType::KOREAN: return io.Fonts->GetGlyphRangesKorean(); + + default: + case eAlphabetType::LATIN: return io.Fonts->GetGlyphRangesDefault(); + } + } +} diff --git a/src/renderer/font_mgr.hpp b/src/renderer/font_mgr.hpp new file mode 100644 index 00000000..af1a9425 --- /dev/null +++ b/src/renderer/font_mgr.hpp @@ -0,0 +1,39 @@ +#pragma once +#include "alphabet_types.hpp" + +namespace big +{ + class font_mgr final + { + private: + const std::vector> m_extra_font_sizes; + // each alphabet type should have a couple of fallback fonts + const std::unordered_map> m_fonts; + // extra alphabet types that may be required to be added to the atlas + eAlphabetType m_require_extra; + // prevent crashes when doing thread unsafe actions + std::mutex m_update_lock; + + public: + font_mgr(std::vector> extra_font_sizes = {{28.f, &g.window.font_title}, + {24.f, &g.window.font_sub_title}, + {18.f, &g.window.font_small}}); + virtual ~font_mgr() = default; + + bool can_use(); + void release_use(); + + /** + * @brief Updates the required underlying glyph ranges to be loaded into the font atlas. Fonts will be updated on the next frame. + * + * @param type + */ + void update_required_alphabet_type(eAlphabetType type); + + private: + void rebuild(); + + file get_available_font_file_for_alphabet_type(); + const ImWchar* get_imgui_alphabet_type(); + }; +} diff --git a/src/renderer/renderer.cpp b/src/renderer/renderer.cpp new file mode 100644 index 00000000..2568deb2 --- /dev/null +++ b/src/renderer/renderer.cpp @@ -0,0 +1,133 @@ +#include "renderer.hpp" + +#include "file_manager.hpp" +#include "fonts/fonts.hpp" +#include "gui.hpp" +#include "pointers.hpp" + +#include +#include +#include +#include + +IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); + +namespace big +{ + renderer::renderer() : + m_font_mgr() + { + } + + bool renderer::init() + { + if (!g_pointers->m_gta.m_swapchain || !*g_pointers->m_gta.m_swapchain) + { + LOG(FATAL) << "Invalid swapchain ptr"; + + return false; + } + m_dxgi_swapchain = *g_pointers->m_gta.m_swapchain; + + if (m_dxgi_swapchain->GetDevice(__uuidof(ID3D11Device), reinterpret_cast(&m_d3d_device)) < 0) + { + LOG(FATAL) << "Failed to get D3D device."; + + return false; + } + m_d3d_device->GetImmediateContext(&m_d3d_device_context); + + auto file_path = g_file_manager.get_project_file("./imgui.ini").get_path(); + + ImGuiContext* ctx = ImGui::CreateContext(); + + static std::string path = file_path.make_preferred().string(); + ctx->IO.IniFilename = path.c_str(); + + ImGui_ImplDX11_Init(m_d3d_device, m_d3d_device_context); + ImGui_ImplWin32_Init(g_pointers->m_hwnd); + + return true; + } + + void renderer::destroy() + { + ImGui_ImplWin32_Shutdown(); + ImGui_ImplDX11_Shutdown(); + ImGui::DestroyContext(); + } + + bool renderer::add_dx_callback(dx_callback callback, uint32_t priority) + { + if (!m_dx_callbacks.insert({priority, callback}).second) + { + LOG(WARNING) << "Duplicate priority given on DX Callback!"; + + return false; + } + return true; + } + + void renderer::add_wndproc_callback(wndproc_callback callback) + { + m_wndproc_callbacks.emplace_back(callback); + } + + void renderer::on_present() + { + if (m_font_mgr.can_use()) + { + new_frame(); + for (const auto& cb : m_dx_callbacks | std::views::values) + cb(); + end_frame(); + + m_font_mgr.release_use(); + } + } + + void renderer::rescale(float rel_size) + { + pre_reset(); + g_gui->restore_default_style(); + + if (rel_size != 1.0f) + ImGui::GetStyle().ScaleAllSizes(rel_size); + + ImGui::GetStyle().MouseCursorScale = 1.0f; + ImGui::GetIO().FontGlobalScale = rel_size; + post_reset(); + } + + void renderer::pre_reset() + { + ImGui_ImplDX11_InvalidateDeviceObjects(); + } + + void renderer::post_reset() + { + ImGui_ImplDX11_CreateDeviceObjects(); + } + + void renderer::wndproc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) + { + for (const auto& cb : m_wndproc_callbacks) + cb(hwnd, msg, wparam, lparam); + + + ImGui_ImplWin32_WndProcHandler(hwnd, msg, wparam, lparam); + } + + void renderer::new_frame() + { + ImGui_ImplDX11_NewFrame(); + ImGui_ImplWin32_NewFrame(); + ImGui::NewFrame(); + } + + void renderer::end_frame() + { + ImGui::Render(); + ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData()); + } +} diff --git a/src/renderer.hpp b/src/renderer/renderer.hpp similarity index 85% rename from src/renderer.hpp rename to src/renderer/renderer.hpp index 1b985db3..7286975c 100644 --- a/src/renderer.hpp +++ b/src/renderer/renderer.hpp @@ -1,5 +1,6 @@ #pragma once #include "common.hpp" +#include "font_mgr.hpp" namespace big { @@ -10,7 +11,15 @@ namespace big { public: explicit renderer(); - ~renderer(); + ~renderer() = default; + + font_mgr& get_font_mgr() + { + return m_font_mgr; + } + + bool init(); + void destroy(); /** * @brief Add a callback function to draw your ImGui content in @@ -48,7 +57,9 @@ namespace big std::map m_dx_callbacks; std::vector m_wndproc_callbacks; + + font_mgr m_font_mgr; }; - inline renderer* g_renderer{}; + inline auto g_renderer = renderer(); } diff --git a/src/services/hotkey/hotkey_service.cpp b/src/services/hotkey/hotkey_service.cpp index 124b428b..97ced35f 100644 --- a/src/services/hotkey/hotkey_service.cpp +++ b/src/services/hotkey/hotkey_service.cpp @@ -4,7 +4,7 @@ #include "gui.hpp" #include "network/ChatData.hpp" #include "pointers.hpp" -#include "renderer.hpp" +#include "renderer/renderer.hpp" #include "util/teleport.hpp" namespace big @@ -37,7 +37,7 @@ namespace big register_hotkey("waypoint", g.settings.hotkeys.teleport_waypoint, RAGE_JOAAT("waypointtp")); register_hotkey("highlighttp", g.settings.hotkeys.teleport_selected, RAGE_JOAAT("highlighttp")); - g_renderer->add_wndproc_callback([this](HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { + g_renderer.add_wndproc_callback([this](HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) { wndproc(static_cast(msg), wparam); }); @@ -115,4 +115,4 @@ namespace big } } } -} \ No newline at end of file +} diff --git a/src/services/translation_service/local_index.hpp b/src/services/translation_service/local_index.hpp index af823191..89ecd53d 100644 --- a/src/services/translation_service/local_index.hpp +++ b/src/services/translation_service/local_index.hpp @@ -1,4 +1,5 @@ #pragma once +#include "renderer/alphabet_types.hpp" #include "translation_entry.hpp" namespace big @@ -7,12 +8,13 @@ namespace big { public: int version = -1; + eAlphabetType alphabet_type = eAlphabetType::LATIN; std::string selected_language; std::string fallback_default_language; std::map fallback_languages; bool default_language_set = false; - NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(local_index, version, selected_language, fallback_default_language, fallback_languages, default_language_set) + NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(local_index, version, alphabet_type, selected_language, fallback_default_language, fallback_languages, default_language_set) }; -} \ No newline at end of file +} diff --git a/src/services/translation_service/remote_index.hpp b/src/services/translation_service/remote_index.hpp index 5c793e44..f0424924 100644 --- a/src/services/translation_service/remote_index.hpp +++ b/src/services/translation_service/remote_index.hpp @@ -12,4 +12,4 @@ namespace big NLOHMANN_DEFINE_TYPE_INTRUSIVE(remote_index, version, default_lang, translations) }; -} \ No newline at end of file +} diff --git a/src/services/translation_service/translation_entry.hpp b/src/services/translation_service/translation_entry.hpp index 053bb79c..d781648b 100644 --- a/src/services/translation_service/translation_entry.hpp +++ b/src/services/translation_service/translation_entry.hpp @@ -1,13 +1,16 @@ #pragma once +#include "renderer/alphabet_types.hpp" namespace big { class translation_entry { public: + // default to latin as most languages make use of it + eAlphabetType alphabet_type = eAlphabetType::LATIN; std::string name; std::string file; - NLOHMANN_DEFINE_TYPE_INTRUSIVE(translation_entry, name, file) + NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(translation_entry, alphabet_type, name, file) }; -} \ No newline at end of file +} diff --git a/src/services/translation_service/translation_service.cpp b/src/services/translation_service/translation_service.cpp index b769a0f7..d7f0f6e5 100644 --- a/src/services/translation_service/translation_service.cpp +++ b/src/services/translation_service/translation_service.cpp @@ -4,6 +4,7 @@ #include "file_manager.hpp" #include "http_client/http_client.hpp" #include "pointers.hpp" +#include "renderer/renderer.hpp" #include "thread_pool.hpp" namespace big @@ -140,6 +141,10 @@ namespace big } } + // local index is saved below so this is prime location to update a value and be sure to have it persisted! + m_local_index.alphabet_type = m_remote_index.translations[m_local_index.selected_language].alphabet_type; + g_renderer.get_font_mgr().update_required_alphabet_type(m_local_index.alphabet_type); + save_local_index(); } diff --git a/src/views/settings/view_gui_settings.cpp b/src/views/settings/view_gui_settings.cpp index 78335ef6..8adb9368 100644 --- a/src/views/settings/view_gui_settings.cpp +++ b/src/views/settings/view_gui_settings.cpp @@ -1,5 +1,5 @@ #include "gui.hpp" -#include "renderer.hpp" +#include "renderer/renderer.hpp" #include "views/view.hpp" namespace big @@ -8,7 +8,7 @@ namespace big { components::sub_title("SETTINGS_UI_SCALE"_T); if (ImGui::SliderFloat("##gui-scale", &g.window.gui_scale, 0.75f, 1.5f, "%.2f")) - g_renderer->rescale(g.window.gui_scale); + g_renderer.rescale(g.window.gui_scale); components::sub_title("SETTINGS_UI_COLOR"_T); static ImVec4 col_gui = ImGui::ColorConvertU32ToFloat4(g.window.background_color); @@ -86,4 +86,4 @@ namespace big } } -} \ No newline at end of file +}