1
0
Fork 0

只替换普通字体,保留特殊数字字体

This commit is contained in:
pm chihya 2026-01-01 20:49:08 +08:00
parent 64809af01c
commit 95f72f6241
1 changed files with 64 additions and 32 deletions

View File

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