mirror of
https://git.chinosk6.cn/chinosk/gkms-local.git
synced 2026-06-14 21:53:19 +07:00
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1af809c11e | |||
| b39dc37801 |
+1
-1
@@ -16,7 +16,7 @@ android {
|
||||
minSdk 29
|
||||
targetSdk 34
|
||||
versionCode 12
|
||||
versionName "v3.3.1"
|
||||
versionName "v3.4.0"
|
||||
buildConfigField "String", "VERSION_NAME", "\"${versionName}\""
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
|
||||
@@ -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, uint32_t> CustomAssetBundleHandleMap{};
|
||||
std::unordered_map<std::string, Il2CppGCHandle> CustomAssetBundleHandleMap{};
|
||||
std::list<std::string> g_extra_assetbundle_paths{};
|
||||
|
||||
void LoadExtraAssetBundle() {
|
||||
@@ -394,9 +420,12 @@ namespace GakumasLocal::HookMain {
|
||||
// CustomAssetBundleAssetPaths.clear();
|
||||
// assert(!ExtraAssetBundleHandle && ExtraAssetBundleAssetPaths.empty());
|
||||
|
||||
static auto AssetBundle_GetAllAssetNames = reinterpret_cast<void* (*)(void*)>(
|
||||
Il2cppUtils::il2cpp_resolve_icall("UnityEngine.AssetBundle::GetAllAssetNames()")
|
||||
);
|
||||
static auto AssetBundle_GetAllAssetNames = Il2cppUtils::GetMethod(
|
||||
"UnityEngine.AssetBundleModule.dll",
|
||||
"UnityEngine",
|
||||
"AssetBundle",
|
||||
"GetAllAssetNames"
|
||||
);
|
||||
|
||||
for (const auto& i : g_extra_assetbundle_paths) {
|
||||
if (CustomAssetBundleHandleMap.contains(i)) continue;
|
||||
@@ -404,7 +433,7 @@ namespace GakumasLocal::HookMain {
|
||||
const auto extraAssetBundle = WinHooks::LoadAssetBundle(i);
|
||||
if (extraAssetBundle)
|
||||
{
|
||||
const auto allAssetPaths = AssetBundle_GetAllAssetNames(extraAssetBundle);
|
||||
const auto allAssetPaths = AssetBundle_GetAllAssetNames->Invoke<void*>(extraAssetBundle);
|
||||
AssetPathsType assetPath{};
|
||||
Il2cppUtils::iterate_IEnumerable<Il2CppString*>(allAssetPaths, [&assetPath](Il2CppString* path)
|
||||
{
|
||||
@@ -413,7 +442,8 @@ namespace GakumasLocal::HookMain {
|
||||
assetPath.emplace(path->start_char);
|
||||
});
|
||||
CustomAssetBundleAssetPaths.emplace(i, assetPath);
|
||||
CustomAssetBundleHandleMap.emplace(i, UnityResolve::Invoke<uint32_t>("il2cpp_gchandle_new", extraAssetBundle, false));
|
||||
const auto bundleHandle = UnityResolve::Invoke<Il2CppGCHandle>("il2cpp_gchandle_new", extraAssetBundle, false);
|
||||
CustomAssetBundleHandleMap.emplace(i, bundleHandle);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -422,7 +452,7 @@ namespace GakumasLocal::HookMain {
|
||||
}
|
||||
}
|
||||
|
||||
uint32_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(),
|
||||
@@ -433,14 +463,14 @@ namespace GakumasLocal::HookMain {
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint32_t GetBundleHandleByAssetName(std::string assetName) {
|
||||
Il2CppGCHandle GetBundleHandleByAssetName(std::string assetName) {
|
||||
return GetBundleHandleByAssetName(utility::conversions::to_string_t(assetName));
|
||||
}
|
||||
|
||||
uint32_t ReplaceFontHandle;
|
||||
Il2CppGCHandle ReplaceFontHandle = nullptr;
|
||||
|
||||
void* GetReplaceFont() {
|
||||
static auto FontClass = Il2cppUtils::GetClass("UnityEngine.TextRenderingModule.dll", "UnityEngine", "Font");
|
||||
@@ -451,7 +481,7 @@ namespace GakumasLocal::HookMain {
|
||||
const auto fontPath = "assets/fonts/gkamszhfontmix.otf";
|
||||
|
||||
void* replaceFont{};
|
||||
const auto& bundleHandle = GetBundleHandleByAssetName(fontPath);
|
||||
const auto bundleHandle = GetBundleHandleByAssetName(fontPath);
|
||||
if (bundleHandle)
|
||||
{
|
||||
if (ReplaceFontHandle)
|
||||
@@ -466,19 +496,23 @@ namespace GakumasLocal::HookMain {
|
||||
}
|
||||
else
|
||||
{
|
||||
UnityResolve::Invoke<void>("il2cpp_gchandle_free", std::exchange(ReplaceFontHandle, 0));
|
||||
UnityResolve::Invoke<void>("il2cpp_gchandle_free", std::exchange(ReplaceFontHandle, nullptr));
|
||||
}
|
||||
}
|
||||
|
||||
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)>(
|
||||
Il2cppUtils::il2cpp_resolve_icall("UnityEngine.AssetBundle::LoadAsset_Internal(System.String,System.Type)")
|
||||
);;
|
||||
static auto AssetBundle_LoadAsset = Il2cppUtils::GetMethod(
|
||||
"UnityEngine.AssetBundleModule.dll",
|
||||
"UnityEngine",
|
||||
"AssetBundle",
|
||||
"LoadAsset_Internal",
|
||||
{ "System.String", "System.Type" }
|
||||
);
|
||||
|
||||
replaceFont = AssetBundle_LoadAsset(extraAssetBundle, Il2cppString::New(fontPath), Font_Type);
|
||||
replaceFont = AssetBundle_LoadAsset->Invoke<void*>(extraAssetBundle, Il2cppString::New(fontPath), Font_Type);
|
||||
if (replaceFont)
|
||||
{
|
||||
ReplaceFontHandle = UnityResolve::Invoke<uint32_t>("il2cpp_gchandle_new", replaceFont, false);
|
||||
ReplaceFontHandle = UnityResolve::Invoke<Il2CppGCHandle>("il2cpp_gchandle_new", replaceFont, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -493,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;
|
||||
@@ -513,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;
|
||||
}
|
||||
@@ -1753,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());
|
||||
@@ -1992,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,
|
||||
@@ -2032,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());
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
namespace GakumasLocal::HookMain
|
||||
{
|
||||
using Il2cppString = UnityResolve::UnityType::String;
|
||||
using Il2CppGCHandle = void*;
|
||||
|
||||
extern void* (*Sprite_get_texture_Orig)(void* self);
|
||||
|
||||
@@ -27,7 +28,7 @@ namespace GakumasLocal::HookMain
|
||||
|
||||
Il2cppUtils::Il2CppClassHead* Texture2DClass = nullptr;
|
||||
Il2cppUtils::Il2CppClassHead* SpriteClass = nullptr;
|
||||
std::unordered_map<std::string, uint32_t> LoadedLocalTextureHandles{};
|
||||
std::unordered_map<std::string, Il2CppGCHandle> LoadedLocalTextureHandles{};
|
||||
std::unordered_set<std::string> AppliedLocalTextureKeys{};
|
||||
|
||||
Il2cppUtils::Il2CppClassHead* GetTexture2DClass() {
|
||||
@@ -73,9 +74,35 @@ namespace GakumasLocal::HookMain
|
||||
Il2cppString* GetObjectName(void* obj) {
|
||||
if (!obj) return nullptr;
|
||||
|
||||
static auto Object_GetName = reinterpret_cast<Il2cppString * (*)(void*)>(
|
||||
Il2cppUtils::il2cpp_resolve_icall("UnityEngine.Object::GetName(UnityEngine.Object)"));
|
||||
return Object_GetName ? Object_GetName(obj) : nullptr;
|
||||
static const auto Object_get_name =
|
||||
Il2cppUtils::GetMethod(
|
||||
"UnityEngine.CoreModule.dll",
|
||||
"UnityEngine",
|
||||
"Object",
|
||||
"get_name"
|
||||
);
|
||||
|
||||
if (!Object_get_name ||
|
||||
!Object_get_name->function) {
|
||||
Log::Error("Cannot find UnityEngine.Object.get_name");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
using ObjectGetNameFn =
|
||||
Il2cppString* (*)(
|
||||
void* self,
|
||||
void* method
|
||||
);
|
||||
|
||||
const auto getName =
|
||||
reinterpret_cast<ObjectGetNameFn>(
|
||||
Object_get_name->function
|
||||
);
|
||||
|
||||
return getName(
|
||||
obj,
|
||||
Object_get_name->address
|
||||
);
|
||||
}
|
||||
|
||||
void SetDontUnloadUnusedAsset(void* obj) {
|
||||
@@ -494,6 +521,57 @@ namespace GakumasLocal::HookMain
|
||||
}
|
||||
}
|
||||
|
||||
bool LoadImageFromByteArray(
|
||||
void* texture,
|
||||
void* byteArray,
|
||||
bool markNonReadable
|
||||
) {
|
||||
if (!texture || !byteArray) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static const auto LoadImageMethod =
|
||||
Il2cppUtils::GetMethod(
|
||||
"UnityEngine.ImageConversionModule.dll",
|
||||
"UnityEngine",
|
||||
"ImageConversion",
|
||||
"LoadImage",
|
||||
{
|
||||
"UnityEngine.Texture2D",
|
||||
"System.Byte[]",
|
||||
"System.Boolean"
|
||||
}
|
||||
);
|
||||
|
||||
if (!LoadImageMethod ||
|
||||
!LoadImageMethod->function) {
|
||||
Log::Error(
|
||||
"Cannot find ImageConversion.LoadImage(Texture2D, Byte[], Boolean)"
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
using LoadImageFn =
|
||||
bool (*)(
|
||||
void* texture,
|
||||
void* byteArray,
|
||||
bool markNonReadable,
|
||||
void* method
|
||||
);
|
||||
|
||||
const auto loadImage =
|
||||
reinterpret_cast<LoadImageFn>(
|
||||
LoadImageMethod->function
|
||||
);
|
||||
|
||||
return loadImage(
|
||||
texture,
|
||||
byteArray,
|
||||
markNonReadable,
|
||||
LoadImageMethod->address
|
||||
);
|
||||
}
|
||||
|
||||
void* LoadLocalTexture2D(const std::filesystem::path& path) {
|
||||
if (!std::filesystem::is_regular_file(path)) return nullptr;
|
||||
|
||||
@@ -516,33 +594,14 @@ namespace GakumasLocal::HookMain
|
||||
const auto ctor = textureClass ? Il2cppUtils::il2cpp_class_get_method_from_name(textureClass, ".ctor", 2) : nullptr;
|
||||
return ctor ? reinterpret_cast<void (*)(void*, int, int)>(ctor->methodPointer) : nullptr;
|
||||
}();
|
||||
static auto ImageConversion_LoadImage = [] {
|
||||
using LoadImageFn = bool (*)(void*, void*, bool);
|
||||
if (const auto icall = Il2cppUtils::il2cpp_resolve_icall(
|
||||
"UnityEngine.ImageConversion::LoadImage(UnityEngine.Texture2D,System.Byte[],System.Boolean)")) {
|
||||
return reinterpret_cast<LoadImageFn>(icall);
|
||||
}
|
||||
|
||||
for (const auto& assemblyName : {"UnityEngine.ImageConversionModule.dll", "UnityEngine.CoreModule.dll"}) {
|
||||
const auto assembly = UnityResolve::Get(assemblyName);
|
||||
const auto imageConversionClass = assembly ? assembly->Get("ImageConversion", "UnityEngine") : nullptr;
|
||||
const auto method = imageConversionClass
|
||||
? Il2cppUtils::il2cpp_class_get_method_from_name(imageConversionClass->address, "LoadImage", 3)
|
||||
: nullptr;
|
||||
if (method) {
|
||||
return reinterpret_cast<LoadImageFn>(method->methodPointer);
|
||||
}
|
||||
}
|
||||
return static_cast<LoadImageFn>(nullptr);
|
||||
}();
|
||||
static auto File_ReadAllBytes = [] {
|
||||
const auto fileClass = Il2cppUtils::GetClass("mscorlib.dll", "System.IO", "File");
|
||||
const auto method = fileClass ? Il2cppUtils::il2cpp_class_get_method_from_name(fileClass->address, "ReadAllBytes", 1) : nullptr;
|
||||
return method ? reinterpret_cast<void* (*)(Il2cppString*)>(method->methodPointer) : nullptr;
|
||||
}();
|
||||
|
||||
if (!Texture2D_ctor || !ImageConversion_LoadImage || !File_ReadAllBytes) {
|
||||
Log::Error("LoadLocalTexture2D failed: Unity Texture2D/ImageConversion/File API not found.");
|
||||
if (!Texture2D_ctor || !File_ReadAllBytes) {
|
||||
Log::Error("LoadLocalTexture2D failed: Unity Texture2D/File API not found.");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -551,19 +610,28 @@ namespace GakumasLocal::HookMain
|
||||
|
||||
const auto texture = UnityResolve::Invoke<void*>("il2cpp_object_new", textureClass);
|
||||
Texture2D_ctor(texture, 2, 2);
|
||||
if (!ImageConversion_LoadImage(texture, fileBytes, false)) {
|
||||
if (!LoadImageFromByteArray(texture, fileBytes, false)) {
|
||||
Log::ErrorFmt("LoadLocalTexture2D failed: %s", path.string().c_str());
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SetDontUnloadUnusedAsset(texture);
|
||||
LoadedLocalTextureHandles.emplace(cacheKey, UnityResolve::Invoke<uint32_t>("il2cpp_gchandle_new", texture, false));
|
||||
const auto textureHandle = UnityResolve::Invoke<Il2CppGCHandle>("il2cpp_gchandle_new", texture, false);
|
||||
if (!textureHandle) {
|
||||
Log::ErrorFmt("Cannot create texture GCHandle: %s", path.string().c_str());
|
||||
return nullptr;
|
||||
}
|
||||
LoadedLocalTextureHandles.emplace(cacheKey, textureHandle);
|
||||
Log::InfoFmt("Texture replaced from local file: %s", path.string().c_str());
|
||||
return texture;
|
||||
}
|
||||
|
||||
void* LoadLocalTexture2DFromCandidates(const std::vector<std::filesystem::path>& candidates) {
|
||||
for (const auto& candidate : candidates) {
|
||||
if (!std::filesystem::is_regular_file(candidate)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (auto texture = LoadLocalTexture2D(candidate)) {
|
||||
return texture;
|
||||
}
|
||||
@@ -578,40 +646,21 @@ namespace GakumasLocal::HookMain
|
||||
+ "|" + std::to_string(reinterpret_cast<std::uintptr_t>(texture2D));
|
||||
if (AppliedLocalTextureKeys.contains(cacheKey)) return true;
|
||||
|
||||
static auto ImageConversion_LoadImage = [] {
|
||||
using LoadImageFn = bool (*)(void*, void*, bool);
|
||||
if (const auto icall = Il2cppUtils::il2cpp_resolve_icall(
|
||||
"UnityEngine.ImageConversion::LoadImage(UnityEngine.Texture2D,System.Byte[],System.Boolean)")) {
|
||||
return reinterpret_cast<LoadImageFn>(icall);
|
||||
}
|
||||
|
||||
for (const auto& assemblyName : {"UnityEngine.ImageConversionModule.dll", "UnityEngine.CoreModule.dll"}) {
|
||||
const auto assembly = UnityResolve::Get(assemblyName);
|
||||
const auto imageConversionClass = assembly ? assembly->Get("ImageConversion", "UnityEngine") : nullptr;
|
||||
const auto method = imageConversionClass
|
||||
? Il2cppUtils::il2cpp_class_get_method_from_name(imageConversionClass->address, "LoadImage", 3)
|
||||
: nullptr;
|
||||
if (method) {
|
||||
return reinterpret_cast<LoadImageFn>(method->methodPointer);
|
||||
}
|
||||
}
|
||||
return static_cast<LoadImageFn>(nullptr);
|
||||
}();
|
||||
static auto File_ReadAllBytes = [] {
|
||||
const auto fileClass = Il2cppUtils::GetClass("mscorlib.dll", "System.IO", "File");
|
||||
const auto method = fileClass ? Il2cppUtils::il2cpp_class_get_method_from_name(fileClass->address, "ReadAllBytes", 1) : nullptr;
|
||||
return method ? reinterpret_cast<void* (*)(Il2cppString*)>(method->methodPointer) : nullptr;
|
||||
}();
|
||||
|
||||
if (!ImageConversion_LoadImage || !File_ReadAllBytes) {
|
||||
Log::Error("ApplyLocalImageToTexture2D failed: Unity ImageConversion/File API not found.");
|
||||
if (!File_ReadAllBytes) {
|
||||
Log::Error("ApplyLocalImageToTexture2D failed: Unity File API not found.");
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto fileBytes = File_ReadAllBytes(Il2cppString::New(path.string()));
|
||||
if (!fileBytes) return false;
|
||||
|
||||
if (!ImageConversion_LoadImage(texture2D, fileBytes, false)) {
|
||||
if (!LoadImageFromByteArray(texture2D, fileBytes, false)) {
|
||||
Log::ErrorFmt("ApplyLocalImageToTexture2D failed: %s", path.string().c_str());
|
||||
return false;
|
||||
}
|
||||
@@ -736,6 +785,16 @@ namespace GakumasLocal::HookMain
|
||||
return texture2D;
|
||||
}
|
||||
|
||||
// Unity 6 compatibility warning:
|
||||
// The resolver functions below currently mix two possible target ABIs:
|
||||
// - il2cpp_resolve_icall(): native icall signature
|
||||
// - GetMethodPointer(): managed IL2CPP methodPointer, normally with a
|
||||
// trailing MethodInfo* argument
|
||||
//
|
||||
// The corresponding AssetBundle / Resources / Sprite hooks in Hook.cpp are
|
||||
// intentionally disabled. They must not be restored until the resolver and
|
||||
// hook declarations distinguish these ABI forms, or until the managed
|
||||
// fallback is removed and a verified icall is required.
|
||||
void* ResolveSpriteGetTextureHookAddress() {
|
||||
if (const auto addr = Il2cppUtils::il2cpp_resolve_icall("UnityEngine.Sprite::get_texture(UnityEngine.Sprite)")) {
|
||||
return addr;
|
||||
|
||||
@@ -598,7 +598,7 @@ namespace GakumasLocal::Local {
|
||||
}
|
||||
|
||||
if (unTransResultRet.empty() || (splitTransStat == SplitTagsTranslationStat::NO_SPLIT)) {
|
||||
DumpGenericText(origText);
|
||||
DumpGenericText(origText, DumpStrStat::SPLITTABLE_ORIG);
|
||||
}
|
||||
else {
|
||||
for (const auto& i : unTransResultRet) {
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
<string name="gakumas_localify">Gakumas Localify</string>
|
||||
<string name="enable_plugin">啟用插件 (不可熱重載)</string>
|
||||
<string name="replace_font">替換字體</string>
|
||||
<string name="replace_texture">替換貼圖</string>
|
||||
<string name="lazy_init">快速初始化(懶人設定)</string>
|
||||
<string name="enable_free_camera">啟用自由視角(可熱重載; 需使用實體鍵盤)</string>
|
||||
<string name="start_game">以上述設定啟動遊戲/重載設定</string>
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
<string name="gakumas_localify">Gakumas Localify</string>
|
||||
<string name="enable_plugin">啟用插件 (不可熱重載)</string>
|
||||
<string name="replace_font">替換字體</string>
|
||||
<string name="replace_texture">替換貼圖</string>
|
||||
<string name="lazy_init">快速初始化(懶人設定)</string>
|
||||
<string name="enable_free_camera">啟用自由視角(可熱重載; 需使用實體鍵盤)</string>
|
||||
<string name="start_game">以上述設定啟動遊戲/重載設定</string>
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
<string name="gakumas_localify">Gakumas Localify</string>
|
||||
<string name="enable_plugin">啟用插件 (不可熱重載)</string>
|
||||
<string name="replace_font">替換字體</string>
|
||||
<string name="replace_texture">替換貼圖</string>
|
||||
<string name="lazy_init">快速初始化(懶人設定)</string>
|
||||
<string name="enable_free_camera">啟用自由視角(可熱重載; 需使用實體鍵盤)</string>
|
||||
<string name="start_game">以上述設定啟動遊戲/重載設定</string>
|
||||
|
||||
Reference in New Issue
Block a user