Compatible with Unity 6

This commit is contained in:
chinosk
2026-06-13 15:26:36 +08:00
parent 0ea432c854
commit 2d05557bdf
3 changed files with 362 additions and 86 deletions
+253 -36
View File
@@ -52,6 +52,7 @@ void UnHookAll() {
namespace GakumasLocal::HookMain {
using Il2cppString = UnityResolve::UnityType::String;
using Il2CppGCHandle = void*;
UnityResolve::UnityType::String* environment_get_stacktrace() {
/*
@@ -71,23 +72,46 @@ namespace GakumasLocal::HookMain {
return toString_mtd->Invoke<Il2cppString*>(klassInstance);
}
DEFINE_HOOK(void, Internal_LogException, (void* ex, void* obj)) {
Internal_LogException_Orig(ex, obj);
// Unity 6: DebugLogHandler.Internal_Log* is resolved as a managed method
// through GetMethod(), so the generated IL2CPP function includes the
// trailing MethodInfo* argument.
DEFINE_HOOK(void, Internal_LogException, (void* ex, void* obj, void* mtd)) {
Internal_LogException_Orig(ex, obj, mtd);
static auto Exception_ToString = Il2cppUtils::GetMethod("mscorlib.dll", "System", "Exception", "ToString");
Log::LogUnityLog(ANDROID_LOG_ERROR, "UnityLog - Internal_LogException:\n%s", Exception_ToString->Invoke<Il2cppString*>(ex)->ToString().c_str());
}
DEFINE_HOOK(void, Internal_Log, (int logType, int logOption, UnityResolve::UnityType::String* content, void* context)) {
Internal_Log_Orig(logType, logOption, content, context);
// 2022.3.21f1
DEFINE_HOOK(void, Internal_Log, (int logType, int logOption, UnityResolve::UnityType::String* content, void* context, void* mtd)) {
Internal_Log_Orig(logType, logOption, content, context, mtd);
Log::LogUnityLog(ANDROID_LOG_VERBOSE, "Internal_Log:\n%s", content->ToString().c_str());
}
bool IsNativeObjectAlive(void* obj) {
static UnityResolve::Method* IsNativeObjectAliveMtd = nullptr;
if (!IsNativeObjectAliveMtd) IsNativeObjectAliveMtd = Il2cppUtils::GetMethod("UnityEngine.CoreModule.dll", "UnityEngine",
"Object", "IsNativeObjectAlive");
return IsNativeObjectAliveMtd->Invoke<bool>(obj);
if (!obj) {
return false;
}
static const auto IsNativeObjectAliveMtd = Il2cppUtils::GetMethod(
"UnityEngine.CoreModule.dll",
"UnityEngine",
"Object",
"IsNativeObjectAlive",
{ "UnityEngine.Object" }
);
if (!IsNativeObjectAliveMtd || !IsNativeObjectAliveMtd->function) {
return false;
}
using IsNativeObjectAliveFn = bool (*)(void* obj, void* method);
const auto isNativeObjectAlive = reinterpret_cast<IsNativeObjectAliveFn>(
IsNativeObjectAliveMtd->function
);
return isNativeObjectAlive(
obj,
IsNativeObjectAliveMtd->address
);
}
UnityResolve::UnityType::Camera* mainCameraCache = nullptr;
@@ -343,7 +367,10 @@ namespace GakumasLocal::HookMain {
}
DEFINE_HOOK(void, CanvasRenderer_SetTexture, (void* self, void* texture)) {
CanvasRenderer_SetTexture_Orig(self, ReplaceTextureOrSpriteByObjectName(texture));
CanvasRenderer_SetTexture_Orig(
self,
ReplaceTextureOrSpriteByObjectName(texture)
);
}
DEFINE_HOOK(void, SpriteRenderer_set_sprite, (void* self, void* sprite)) {
@@ -372,7 +399,6 @@ namespace GakumasLocal::HookMain {
}
}
#ifdef GKMS_WINDOWS
struct TransparentStringHash : std::hash<std::wstring>, std::hash<std::wstring_view>
{
@@ -381,7 +407,7 @@ namespace GakumasLocal::HookMain {
typedef std::unordered_set<std::wstring, TransparentStringHash, std::equal_to<void>> AssetPathsType;
std::map<std::string, AssetPathsType> CustomAssetBundleAssetPaths;
std::unordered_map<std::string, uint64_t> CustomAssetBundleHandleMap{};
std::unordered_map<std::string, Il2CppGCHandle> CustomAssetBundleHandleMap{};
std::list<std::string> g_extra_assetbundle_paths{};
void LoadExtraAssetBundle() {
@@ -416,7 +442,8 @@ namespace GakumasLocal::HookMain {
assetPath.emplace(path->start_char);
});
CustomAssetBundleAssetPaths.emplace(i, assetPath);
CustomAssetBundleHandleMap.emplace(i, UnityResolve::Invoke<uint64_t>("il2cpp_gchandle_new", extraAssetBundle, false));
const auto bundleHandle = UnityResolve::Invoke<Il2CppGCHandle>("il2cpp_gchandle_new", extraAssetBundle, false);
CustomAssetBundleHandleMap.emplace(i, bundleHandle);
}
else
{
@@ -425,7 +452,7 @@ namespace GakumasLocal::HookMain {
}
}
uint64_t GetBundleHandleByAssetName(std::wstring assetName) {
Il2CppGCHandle GetBundleHandleByAssetName(std::wstring assetName) {
for (const auto& i : CustomAssetBundleAssetPaths) {
for (const auto& m : i.second) {
if (std::equal(m.begin(), m.end(), assetName.begin(), assetName.end(),
@@ -436,14 +463,14 @@ namespace GakumasLocal::HookMain {
}
}
}
return NULL;
return nullptr;
}
uint64_t GetBundleHandleByAssetName(std::string assetName) {
Il2CppGCHandle GetBundleHandleByAssetName(std::string assetName) {
return GetBundleHandleByAssetName(utility::conversions::to_string_t(assetName));
}
uint64_t ReplaceFontHandle;
Il2CppGCHandle ReplaceFontHandle = nullptr;
void* GetReplaceFont() {
static auto FontClass = Il2cppUtils::GetClass("UnityEngine.TextRenderingModule.dll", "UnityEngine", "Font");
@@ -469,7 +496,7 @@ namespace GakumasLocal::HookMain {
}
else
{
UnityResolve::Invoke<void>("il2cpp_gchandle_free", std::exchange(ReplaceFontHandle, 0));
UnityResolve::Invoke<void>("il2cpp_gchandle_free", std::exchange(ReplaceFontHandle, nullptr));
}
}
@@ -485,7 +512,7 @@ namespace GakumasLocal::HookMain {
replaceFont = AssetBundle_LoadAsset->Invoke<void*>(extraAssetBundle, Il2cppString::New(fontPath), Font_Type);
if (replaceFont)
{
ReplaceFontHandle = UnityResolve::Invoke<uint64_t>("il2cpp_gchandle_new", replaceFont, false);
ReplaceFontHandle = UnityResolve::Invoke<Il2CppGCHandle>("il2cpp_gchandle_new", replaceFont, false);
}
else
{
@@ -500,19 +527,72 @@ namespace GakumasLocal::HookMain {
}
#else
void* fontCache = nullptr;
bool CreateFontFromPath(void* font, const std::filesystem::path& fontName) {
if (!font) {
return false;
}
const auto fontPath = Il2cppString::New(fontName.string());
if (!fontPath) {
Log::Error("CreateFontFromPath failed: cannot create path string");
return false;
}
static auto CreateFontFromPathIcall = reinterpret_cast<void (*)(void* self, Il2cppString* path)>(
Il2cppUtils::il2cpp_resolve_icall("UnityEngine.Font::Internal_CreateFontFromPath(UnityEngine.Font,System.String)")
);
if (CreateFontFromPathIcall) {
CreateFontFromPathIcall(font, fontPath);
return true;
}
static auto CreateFontFromPathMethod = [] {
auto method = Il2cppUtils::GetMethod(
"UnityEngine.TextRenderingModule.dll",
"UnityEngine",
"Font",
"Internal_CreateFontFromPath",
{ "UnityEngine.Font", "System.String" }
);
if (method) {
return method;
}
return Il2cppUtils::GetMethod(
"UnityEngine.TextRenderingModule.dll",
"UnityEngine",
"Font",
"Internal_CreateFontFromPath"
);
}();
if (!CreateFontFromPathMethod || !CreateFontFromPathMethod->function || !CreateFontFromPathMethod->address) {
Log::Error("CreateFontFromPath failed: method not found");
return false;
}
using CreateFontFromPathManagedFn = void (*)(void* font, Il2cppString* path, void* method);
const auto createFontFromPath = reinterpret_cast<CreateFontFromPathManagedFn>(
CreateFontFromPathMethod->function
);
createFontFromPath(font, fontPath, CreateFontFromPathMethod->address);
return true;
}
void* GetReplaceFont() {
static auto fontName = Local::GetBasePath() / "local-files" / "gkamsZHFontMIX.otf";
if (!std::filesystem::exists(fontName)) {
return nullptr;
}
static auto CreateFontFromPath = reinterpret_cast<void (*)(void* self, Il2cppString* path)>(
Il2cppUtils::il2cpp_resolve_icall("UnityEngine.Font::Internal_CreateFontFromPath(UnityEngine.Font,System.String)")
);
static auto Font_klass = Il2cppUtils::GetClass("UnityEngine.TextRenderingModule.dll",
"UnityEngine", "Font");
static auto Font_ctor = Il2cppUtils::GetMethod("UnityEngine.TextRenderingModule.dll",
"UnityEngine", "Font", ".ctor");
if (!Font_klass || !Font_ctor) {
Log::Error("GetReplaceFont failed: Font class or constructor not found");
return nullptr;
}
if (fontCache) {
if (IsNativeObjectAlive(fontCache)) {
return fontCache;
@@ -520,9 +600,16 @@ namespace GakumasLocal::HookMain {
}
const auto newFont = Font_klass->New<void*>();
if (!newFont) {
Log::Error("GetReplaceFont failed: cannot create Font instance");
return nullptr;
}
Font_ctor->Invoke<void>(newFont);
CreateFontFromPath(newFont, Il2cppString::New(fontName.string()));
if (!CreateFontFromPath(newFont, fontName)) {
return nullptr;
}
fontCache = newFont;
return newFont;
}
@@ -1760,7 +1847,18 @@ namespace GakumasLocal::HookMain {
UnityResolve::Mode::Il2Cpp, Config::lazyInit);
#endif
// Temporarily isolate texture replacement to CanvasRenderer.SetTexture only.
// Texture replacement is currently isolated to CanvasRenderer.SetTexture.
//
// Unity 6 compatibility warning:
// The resolver helpers below may return either a native icall address or
// a managed IL2CPP methodPointer fallback. These two targets do not share
// the same ABI: a managed methodPointer normally has a trailing MethodInfo*
// argument, while the current hook declarations use the icall-style
// signatures. Do not simply uncomment these ADD_HOOK calls on Unity 6.
// Before restoring them, either:
// 1. make each resolver return only a verified icall address; or
// 2. return ABI metadata and install a separate managed hook signature
// that preserves and forwards the trailing MethodInfo* argument.
// ADD_HOOK(AssetBundle_LoadAsset, ResolveAssetBundleLoadAssetHookAddress());
// ADD_HOOK(AssetBundle_LoadAssetAsync, ResolveAssetBundleLoadAssetAsyncHookAddress());
// ADD_HOOK(AssetBundleRequest_GetResult, ResolveAssetBundleRequestResultHookAddress());
@@ -1999,10 +2097,41 @@ namespace GakumasLocal::HookMain {
Il2cppUtils::GetMethodPointer("campus-submodule.Runtime.dll", "Campus.Common",
"CampusQualityManager", "set_TargetFrameRate"));
ADD_HOOK(Internal_LogException, Il2cppUtils::il2cpp_resolve_icall(
"UnityEngine.DebugLogHandler::Internal_LogException(System.Exception,UnityEngine.Object)"));
ADD_HOOK(Internal_Log, Il2cppUtils::il2cpp_resolve_icall(
"UnityEngine.DebugLogHandler::Internal_Log(UnityEngine.LogType,UnityEngine.LogOption,System.String,UnityEngine.Object)"));
// Unity 6 no longer exposes these two methods through the old icall
// names in this player. Resolve their managed IL2CPP methodPointer
// through GetMethod() and use hook signatures with a trailing MethodInfo*.
const auto Internal_LogException_Method = Il2cppUtils::GetMethod(
"UnityEngine.CoreModule.dll",
"UnityEngine",
"DebugLogHandler",
"Internal_LogException",
{ "System.Exception", "UnityEngine.Object" }
);
ADD_HOOK(
Internal_LogException,
Internal_LogException_Method
? Internal_LogException_Method->function
: nullptr
);
const auto Internal_Log_Method = Il2cppUtils::GetMethod(
"UnityEngine.CoreModule.dll",
"UnityEngine",
"DebugLogHandler",
"Internal_Log",
{
"UnityEngine.LogType",
"UnityEngine.LogOption",
"System.String",
"UnityEngine.Object"
}
);
ADD_HOOK(
Internal_Log,
Internal_Log_Method
? Internal_Log_Method->function
: nullptr
);
// 双端
ADD_HOOK(InternalSetOrientationAsync,
@@ -2039,14 +2168,102 @@ namespace GakumasLocal::HookMain {
// "AspectRatioHandler", "WindowProc"));
if (GakumasLocal::Config::dmmUnlockSize) {
std::thread([]() {
std::this_thread::sleep_for(std::chrono::seconds(3));
auto hWnd = FindWindowW(L"UnityWndClass", L"gakumas");
// 添加可调整大小的边框和最大化按钮
LONG style = GetWindowLong(hWnd, GWL_STYLE);
style |= WS_THICKFRAME | WS_MAXIMIZEBOX;
SetWindowLong(hWnd, GWL_STYLE, style);
}).detach();
std::thread([]() {
std::this_thread::sleep_for(std::chrono::seconds(3));
const auto currentProcessId = GetCurrentProcessId();
HWND hWnd = nullptr;
HWND candidate = nullptr;
while ((candidate = FindWindowExW(
nullptr,
candidate,
L"UnityWndClass",
nullptr
)) != nullptr) {
DWORD windowProcessId = 0;
GetWindowThreadProcessId(
candidate,
&windowProcessId
);
if (windowProcessId == currentProcessId) {
hWnd = candidate;
break;
}
}
if (!hWnd) {
Log::Error(
"DMM unlock size failed: Unity window not found."
);
return;
}
SetLastError(ERROR_SUCCESS);
auto style = GetWindowLongPtrW(
hWnd,
GWL_STYLE
);
if (style == 0 &&
GetLastError() != ERROR_SUCCESS) {
Log::ErrorFmt(
"DMM unlock size failed: "
"GetWindowLongPtrW error=%lu",
GetLastError()
);
return;
}
style |= WS_THICKFRAME |
WS_MAXIMIZEBOX;
SetLastError(ERROR_SUCCESS);
const auto previousStyle =
SetWindowLongPtrW(
hWnd,
GWL_STYLE,
style
);
if (previousStyle == 0 &&
GetLastError() != ERROR_SUCCESS) {
Log::ErrorFmt(
"DMM unlock size failed: "
"SetWindowLongPtrW error=%lu",
GetLastError()
);
return;
}
if (!SetWindowPos(
hWnd,
nullptr,
0,
0,
0,
0,
SWP_NOMOVE |
SWP_NOSIZE |
SWP_NOZORDER |
SWP_NOACTIVATE |
SWP_FRAMECHANGED
)) {
Log::ErrorFmt(
"DMM unlock size failed: "
"SetWindowPos error=%lu",
GetLastError()
);
return;
}
Log::Info(
"DMM window size unlocked."
);
}).detach();
}
g_extra_assetbundle_paths.push_back((gakumasLocalPath / "local-files/gakumasassets").string());