diff --git a/src/GakumasLocalify/Hook.cpp b/src/GakumasLocalify/Hook.cpp index 94aa5d1..9af7d62 100644 --- a/src/GakumasLocalify/Hook.cpp +++ b/src/GakumasLocalify/Hook.cpp @@ -431,35 +431,45 @@ namespace GakumasLocal::HookMain { return GetBundleHandleByAssetName(utility::conversions::to_string_t(assetName)); } - uint32_t ReplaceFontHandle; + // 多字体支持:字体映射缓存 + struct FontCacheEntry { + uint32_t handle; + std::string assetPath; + }; + std::unordered_map g_fontCache; + uint32_t ReplaceFontHandle; // 默认字体句柄(向后兼容) - void* GetReplaceFont() { + // 字体映射配置:原字体名 -> 替换字体资源路径 + // 注意:这里匹配的是 TMP_FontAsset 的 name,通常是 "XXX Atlas" 或 "XXX SDF Atlas" + std::unordered_map g_fontMapping = { + {"ibmplexsansjp", "assets/fonts/gkamszhfontmix.otf"}, // IBMPlexSansJP Atlas -> 替换为中文字体 + }; + + // 加载指定路径的字体 + void* GetReplaceFont(const std::string& fontPath) { static auto FontClass = Il2cppUtils::GetClass("UnityEngine.TextRenderingModule.dll", "UnityEngine", "Font"); static auto Font_Type = UnityResolve::Invoke("il2cpp_type_get_object", UnityResolve::Invoke("il2cpp_class_get_type", FontClass->address)); using Il2CppString = UnityResolve::UnityType::String; - const auto fontPath = "assets/fonts/gkamszhfontmix.otf"; + // 检查缓存 + auto cacheIt = g_fontCache.find(fontPath); void* replaceFont{}; + + if (cacheIt != g_fontCache.end()) { + replaceFont = UnityResolve::Invoke("il2cpp_gchandle_get_target", cacheIt->second.handle); + if (IsNativeObjectAlive(replaceFont)) { + return replaceFont; + } + // 缓存失效,释放句柄 + UnityResolve::Invoke("il2cpp_gchandle_free", cacheIt->second.handle); + g_fontCache.erase(cacheIt); + } + const auto& bundleHandle = GetBundleHandleByAssetName(fontPath); if (bundleHandle) { - if (ReplaceFontHandle) - { - replaceFont = UnityResolve::Invoke("il2cpp_gchandle_get_target", ReplaceFontHandle); - // 加载场景时会被 Resources.UnloadUnusedAssets 干掉,且不受 DontDestroyOnLoad 影响,暂且判断是否存活,并在必要的时候重新加载 - // TODO: 考虑挂载到 GameObject 上 - // AssetBundle 不会被干掉 - if (IsNativeObjectAlive(replaceFont)) - { - return replaceFont; - } - else - { - UnityResolve::Invoke("il2cpp_gchandle_free", std::exchange(ReplaceFontHandle, 0)); - } - } const auto extraAssetBundle = UnityResolve::Invoke("il2cpp_gchandle_get_target", bundleHandle); static auto AssetBundle_LoadAsset = reinterpret_cast( @@ -469,19 +479,30 @@ namespace GakumasLocal::HookMain { replaceFont = AssetBundle_LoadAsset(extraAssetBundle, Il2cppString::New(fontPath), Font_Type); if (replaceFont) { - ReplaceFontHandle = UnityResolve::Invoke("il2cpp_gchandle_new", replaceFont, false); + auto handle = UnityResolve::Invoke("il2cpp_gchandle_new", replaceFont, false); + g_fontCache[fontPath] = {handle, fontPath}; + + // 向后兼容:如果是默认字体,也更新全局句柄 + if (fontPath == "assets/fonts/gkamszhfontmix.otf") { + ReplaceFontHandle = handle; + } } else { - Log::Error("Cannot load asset font\n"); + Log::ErrorFmt("Cannot load asset font: %s\n", fontPath.c_str()); } } else { - Log::Error("Cannot find asset font\n"); + Log::ErrorFmt("Cannot find asset font: %s\n", fontPath.c_str()); } return replaceFont; } + + // 默认字体加载(向后兼容) + void* GetReplaceFont() { + return GetReplaceFont("assets/fonts/gkamszhfontmix.otf"); + } #else void* fontCache = nullptr; void* GetReplaceFont() { @@ -519,6 +540,8 @@ namespace GakumasLocal::HookMain { "TMPro", "TMP_Text", "get_font"); static auto set_font = Il2cppUtils::GetMethod("Unity.TextMeshPro.dll", "TMPro", "TMP_Text", "set_font"); + static auto get_name = Il2cppUtils::GetMethod("UnityEngine.CoreModule.dll", + "UnityEngine", "Object", "get_name"); // static auto set_fontMaterial = Il2cppUtils::GetMethod("Unity.TextMeshPro.dll", // "TMPro", "TMP_Text", "set_fontMaterial"); // static auto ForceMeshUpdate = Il2cppUtils::GetMethod("Unity.TextMeshPro.dll", @@ -532,21 +555,30 @@ namespace GakumasLocal::HookMain { static auto UpdateFontAssetData = Il2cppUtils::GetMethod("Unity.TextMeshPro.dll", "TMPro", "TMP_FontAsset", "UpdateFontAssetData"); + auto fontAsset = get_font->Invoke(TMP_Textself); + if (!fontAsset) return; + + // 跳过 CampusAlphanumeric 系列字体,保持游戏原版数字字体 + auto fontAssetName = get_name->Invoke(fontAsset); + if (fontAssetName) { + std::string fontName = fontAssetName->ToString(); + std::transform(fontName.begin(), fontName.end(), fontName.begin(), ::tolower); + if (fontName.find("campusalphanumeric") != std::string::npos) { + return; + } + } + + // 替换为中文字体 auto newFont = GetReplaceFont(); if (!newFont) return; - auto fontAsset = get_font->Invoke(TMP_Textself); - if (fontAsset) { - set_sourceFontFile->Invoke(fontAsset, newFont); - if (!updatedFontPtrs.contains(fontAsset)) { - updatedFontPtrs.emplace(fontAsset); - UpdateFontAssetData->Invoke(fontAsset); - } - if (updatedFontPtrs.size() > 200) updatedFontPtrs.clear(); - } - else { - Log::Error("UpdateFont: fontAsset is null."); + set_sourceFontFile->Invoke(fontAsset, newFont); + if (!updatedFontPtrs.contains(fontAsset)) { + updatedFontPtrs.emplace(fontAsset); + UpdateFontAssetData->Invoke(fontAsset); } + if (updatedFontPtrs.size() > 200) updatedFontPtrs.clear(); + set_font->Invoke(TMP_Textself, fontAsset); // auto fontMaterial = get_material->Invoke(fontAsset);