From 4929938df50d7d19d28846679dce0d6a061d52c4 Mon Sep 17 00:00:00 2001 From: Ji O Date: Sat, 25 Mar 2023 03:35:49 +0900 Subject: [PATCH] Show DialogAnnounceEvent on click of story event announcement voice --- README.md | 4 + src/VERSIONINFO.rc | 12 +- src/hook.cpp | 226 +++++++++++++++++++++++++++++++++- src/il2cpp/il2cpp_symbols.cpp | 6 +- src/il2cpp/il2cpp_symbols.hpp | 12 +- 5 files changed, 249 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 1124d72..6a06245 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # umamusume-localify ## Uma Musume: Pretty Derby localify patch + [한국어](README.ko-KR.md) [中国人](README.zh-Hans.md) @@ -25,6 +26,9 @@ ### Known issue - None +# Build +Platform Toolset: clang required + # Resources - `static.json` upgrade tool - https://github.com/AirJerryWhite/i18upgrade diff --git a/src/VERSIONINFO.rc b/src/VERSIONINFO.rc index b90207f..5fc40d4 100644 --- a/src/VERSIONINFO.rc +++ b/src/VERSIONINFO.rc @@ -7,8 +7,8 @@ // VS_VERSION_INFO VERSIONINFO - FILEVERSION 1,17,1,0 - PRODUCTVERSION 1,17,1,0 + FILEVERSION 1,18,0,0 + PRODUCTVERSION 1,18,0,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -25,19 +25,19 @@ BEGIN BEGIN VALUE "LegalCopyright", "Copyright © Ji O Kim\0" VALUE "FileDescription", "우마무스메 현지화 패치\0" - VALUE "FileVersion", "1.17.1.0\0" + VALUE "FileVersion", "1.18.0.0\0" VALUE "InternalName", "umamusume-localify\0" VALUE "ProductName", "Umamusume Localify\0" - VALUE "ProductVersion", "1.17.1\0" + VALUE "ProductVersion", "1.18.0\0" END BLOCK "000004b0" BEGIN VALUE "LegalCopyright", "Copyright © Ji O Kim\0" VALUE "FileDescription", "Localization patch for Umamusume\0" - VALUE "FileVersion", "1.17.1.0\0" + VALUE "FileVersion", "1.18.0.0\0" VALUE "InternalName", "umamusume-localify\0" VALUE "ProductName", "Umamusume Localify\0" - VALUE "ProductVersion", "1.17.1\0" + VALUE "ProductVersion", "1.18.0\0" END END BLOCK "VarFileInfo" diff --git a/src/hook.cpp b/src/hook.cpp index 86d64b2..159f2f9 100644 --- a/src/hook.cpp +++ b/src/hook.cpp @@ -110,6 +110,119 @@ namespace return il2cpp_type_get_object(il2cpp_class_get_type(il2cpp_symbols::get_class(assemblyName, namespaze, klassName))); } + template + Il2CppDelegate* CreateDelegate(Il2CppObject* target, void (*fn)(Il2CppObject*, T...)) + { + auto delegate = reinterpret_cast( + il2cpp_object_new(il2cpp_symbols::get_class("mscorlib.dll", "System", "MulticastDelegate"))); + delegate->delegates = il2cpp_array_new(il2cpp_symbols::get_class("mscorlib.dll", "System", "Delegate"), 1); + il2cpp_array_setref(delegate->delegates, 0, delegate); + delegate->method_ptr = reinterpret_cast(fn); + + auto methodInfo = reinterpret_cast(il2cpp_object_new( + il2cpp_symbols::get_class("mscorlib.dll", "System.Reflection", "MethodInfo"))); + methodInfo->methodPointer = reinterpret_cast(delegate->method_ptr); + methodInfo->klass = il2cpp_symbols::get_class("mscorlib.dll", "System.Reflection", "MethodInfo"); + delegate->method = methodInfo; + delegate->target = target; + return delegate; + } + + template + Il2CppDelegate* CreateUnityAction(Il2CppObject* target, void (*fn)(Il2CppObject*, T...)) + { + auto delegate = reinterpret_cast( + il2cpp_object_new(il2cpp_symbols::get_class("UnityEngine.CoreModule.dll", "UnityEngine.Events", "UnityAction"))); + delegate->delegates = il2cpp_array_new(il2cpp_symbols::get_class("mscorlib.dll", "System", "Delegate"), 1); + il2cpp_array_setref(delegate->delegates, 0, delegate); + delegate->method_ptr = reinterpret_cast(fn); + + auto methodInfo = reinterpret_cast(il2cpp_object_new( + il2cpp_symbols::get_class("mscorlib.dll", "System.Reflection", "MethodInfo"))); + methodInfo->methodPointer = reinterpret_cast(delegate->method_ptr); + methodInfo->klass = il2cpp_symbols::get_class("mscorlib.dll", "System.Reflection", "MethodInfo"); + delegate->method = methodInfo; + delegate->target = target; + return delegate; + } + + Il2CppDelegate* GetButtonCommonOnClickDelegate(Il2CppObject* object) + { + if (!object) + { + return nullptr; + } + if (object->klass != il2cpp_symbols::get_class("umamusume.dll", "Gallop", "ButtonCommon")) + { + return nullptr; + } + auto onClickField = il2cpp_class_get_field_from_name(object->klass, "m_OnClick"); + Il2CppObject* onClick; + il2cpp_field_get_value(object, onClickField, &onClick); + if (onClick) + { + auto callsField = il2cpp_class_get_field_from_name(onClick->klass, "m_Calls"); + Il2CppObject* calls; + il2cpp_field_get_value(onClick, callsField, &calls); + if (calls) + { + auto runtimeCallsField = il2cpp_class_get_field_from_name(calls->klass, + "m_RuntimeCalls"); + Il2CppObject* runtimeCalls; + il2cpp_field_get_value(calls, runtimeCallsField, &runtimeCalls); + + if (runtimeCalls) + { + FieldInfo* itemsField = il2cpp_class_get_field_from_name(runtimeCalls->klass, + "_items"); + Il2CppArraySize* arr; + il2cpp_field_get_value(runtimeCalls, itemsField, &arr); + if (arr) + { + for (int i = 0; i < arr->max_length; i++) + { + auto value = reinterpret_cast(arr->vector[i]); + if (value) + { + auto delegateField = il2cpp_class_get_field_from_name(value->klass, + "Delegate"); + Il2CppDelegate* delegate; + il2cpp_field_get_value(value, delegateField, &delegate); + if (delegate) + { + // Unbox delegate + auto callbackField = il2cpp_class_get_field_from_name( + delegate->target->klass, "callback"); + Il2CppDelegate* callback; + il2cpp_field_get_value(delegate->target, callbackField, &callback); + + return callback; + } + } + } + } + } + } + } + return nullptr; + } + + Il2CppObject* GetSingletonInstance(Il2CppClass* klass) + { + if (!klass || !klass->parent) + { + return nullptr; + } + if (string(klass->parent->name).find("Singleton`1") == string::npos) + { + return nullptr; + } + auto instanceField = il2cpp_class_get_field_from_name(klass, "_instance"); + Il2CppObject* instance; + il2cpp_field_static_get_value(instanceField, &instance); + return instance; + } + Boolean GetBoolean(bool value) { return reinterpret_cast(il2cpp_symbols::get_method_pointer( @@ -2589,6 +2702,110 @@ namespace return reinterpret_cast(CriMana_Player_SetFile_orig)(_this, binder, moviePath, setMode); } + void* PartsEpisodeList_SetupStoryExtraEpisodeList_orig = nullptr; + + void PartsEpisodeList_SetupStoryExtraEpisodeList_hook(Il2CppObject* _this, Il2CppObject* extraSubCategory, Il2CppObject* partDataList, Il2CppObject* partData, Il2CppDelegate* onClick) + { + reinterpret_cast(PartsEpisodeList_SetupStoryExtraEpisodeList_orig)(_this, extraSubCategory, partDataList, partData, onClick); + + int partDataId; + + auto partDataIdField = il2cpp_class_get_field_from_name(partData->klass, "k__BackingField"); + il2cpp_field_get_value(partData, partDataIdField, &partDataId); + + auto voiceButtonField = il2cpp_class_get_field_from_name(_this->klass, "_voiceButton"); + Il2CppObject* voiceButton; + il2cpp_field_get_value(_this, voiceButtonField, &voiceButton); + + auto buttonField = il2cpp_class_get_field_from_name(voiceButton->klass, "_playVoiceButton"); + Il2CppObject* button; + il2cpp_field_get_value(voiceButton, buttonField, &button); + + if (button) + { + auto callback = GetButtonCommonOnClickDelegate(button); + if (callback) + { + auto newFn = *( + [](Il2CppObject* storyIdBox) + { + + int storyId = *reinterpret_cast(il2cpp_object_unbox(storyIdBox)); + + auto masterDataManager = GetSingletonInstance( + il2cpp_symbols::get_class( + "umamusume.dll", "Gallop", + "MasterDataManager")); + auto masterBannerData = reinterpret_cast(il2cpp_class_get_method_from_name( + masterDataManager->klass, + "get_masterBannerData", + 0)->methodPointer)(masterDataManager); + + auto bannerList = reinterpret_cast(il2cpp_class_get_method_from_name( + masterBannerData->klass, + "GetListWithGroupId", + 1)->methodPointer)(masterBannerData, + 7); + + FieldInfo* itemsField = il2cpp_class_get_field_from_name( + bannerList->klass, "_items"); + Il2CppArraySize* arr; + il2cpp_field_get_value(bannerList, itemsField, + &arr); + + int announceId = -1; + + for (int i = 0; i < arr->max_length; i++) + { + auto item = reinterpret_cast(arr->vector[i]); + if (item) + { + auto typeField = il2cpp_class_get_field_from_name( + item->klass, "Type"); + int type; + il2cpp_field_get_value(item, typeField, + &type); + auto conditionValueField = il2cpp_class_get_field_from_name( + item->klass, "ConditionValue"); + int conditionValue; + il2cpp_field_get_value(item, + conditionValueField, + &conditionValue); + if (type == 7 && + conditionValue == storyId) + { + auto transitionField = il2cpp_class_get_field_from_name( + item->klass, "Transition"); + il2cpp_field_get_value(item, + transitionField, + &announceId); + break; + } + } + } + + if (announceId == -1 && storyId < 1005) + { + announceId = storyId - 1002; + } + + auto action = CreateDelegate(storyIdBox, *([](Il2CppObject*) {})); + + reinterpret_cast(il2cpp_symbols::get_method_pointer( + "umamusume.dll", "Gallop", + "DialogAnnounceEvent", "Open", 3))(announceId, action, action); + }); + reinterpret_cast(il2cpp_class_get_method_from_name(button->klass, "SetOnClick", 1)->methodPointer)(button, + CreateUnityAction(il2cpp_value_box(il2cpp_symbols::get_class("mscorlib.dll", "System", "Int32"), &partDataId), newFn)); + } + } + } + void adjust_size() { thread([]() @@ -2628,11 +2845,11 @@ namespace if (g_static_entries_use_text_id_name) { string textIdName = GetTextIdNameById(i); - text_id_static_entries.emplace_back(make_pair(textIdName, str->start_char)); + text_id_static_entries.emplace_back(textIdName, str->start_char); if (local::get_localized_string(textIdName) == nullptr || local::wide_u8(local::get_localized_string(textIdName)->start_char) == local::wide_u8(str->start_char)) { - text_id_not_matched_entries.emplace_back(make_pair(textIdName, str->start_char)); + text_id_not_matched_entries.emplace_back(textIdName, str->start_char); } } else if (g_static_entries_use_hash) @@ -3162,6 +3379,9 @@ namespace auto MoviePlayerForObj_AdjustScreenSize_addr = il2cpp_symbols::get_method_pointer( "Cute.Cri.Assembly.dll", "Cute.Cri", "MoviePlayerForObj", "AdjustScreenSize", 2); + auto PartsEpisodeList_SetupStoryExtraEpisodeList_addr = il2cpp_symbols::get_method_pointer( + "umamusume.dll", "Gallop", "PartsEpisodeList", "SetupStoryExtraEpisodeList", 4); + load_from_file = reinterpret_cast(il2cpp_symbols::get_method_pointer( "UnityEngine.AssetBundleModule.dll", "UnityEngine", "AssetBundle", "LoadFromFile", 1)); @@ -3285,6 +3505,8 @@ namespace } #pragma endregion + ADD_HOOK(PartsEpisodeList_SetupStoryExtraEpisodeList, "Gallop.PartsEpisodeList::SetupStoryExtraEpisodeList at %p\n"); + ADD_HOOK(CriMana_Player_SetFile, "CriWare.CriMana.Player::SetFile at %p\n"); ADD_HOOK(GameSystem_FixedUpdate, "Gallop.GameSystem::FixedUpdate at %p\n"); diff --git a/src/il2cpp/il2cpp_symbols.cpp b/src/il2cpp/il2cpp_symbols.cpp index 9043819..efe160b 100644 --- a/src/il2cpp/il2cpp_symbols.cpp +++ b/src/il2cpp/il2cpp_symbols.cpp @@ -56,6 +56,8 @@ il2cpp_field_get_parent_t il2cpp_field_get_parent; il2cpp_field_get_offset_t il2cpp_field_get_offset; il2cpp_class_get_property_from_name_t il2cpp_class_get_property_from_name; il2cpp_runtime_object_init_t il2cpp_runtime_object_init; +il2cpp_value_box_t il2cpp_value_box; +il2cpp_object_unbox_t il2cpp_object_unbox; char* il2cpp_array_addr_with_size(void* array, int32_t size, uintptr_t idx) { @@ -66,8 +68,6 @@ namespace il2cpp_symbols { #define RESOLVE_IMPORT(name) name = reinterpret_cast(GetProcAddress(game_module, #name)) - void* il2cpp_domain = nullptr; - void init(HMODULE game_module) { RESOLVE_IMPORT(il2cpp_string_new_utf16); @@ -126,6 +126,8 @@ namespace il2cpp_symbols RESOLVE_IMPORT(il2cpp_field_get_offset); RESOLVE_IMPORT(il2cpp_class_get_property_from_name); RESOLVE_IMPORT(il2cpp_runtime_object_init); + RESOLVE_IMPORT(il2cpp_value_box); + RESOLVE_IMPORT(il2cpp_object_unbox); il2cpp_domain = il2cpp_domain_get(); } diff --git a/src/il2cpp/il2cpp_symbols.hpp b/src/il2cpp/il2cpp_symbols.hpp index c0a5709..06b8eaa 100644 --- a/src/il2cpp/il2cpp_symbols.hpp +++ b/src/il2cpp/il2cpp_symbols.hpp @@ -479,6 +479,10 @@ typedef struct Il2CppDelegate bool method_is_virtual; } Il2CppDelegate; +typedef struct MulticastDelegate : Il2CppDelegate { + Il2CppArraySize* delegates; +} MulticastDelegate; + // function types typedef Il2CppString* (*il2cpp_string_new_utf16_t)(const wchar_t* str, unsigned int len); typedef Il2CppString* (*il2cpp_string_new_t)(const char* str); @@ -494,7 +498,7 @@ typedef const Il2CppType* (*il2cpp_method_get_param_t)(const MethodInfo* method, typedef Il2CppObject* (*il2cpp_object_new_t)(Il2CppClass* klass); typedef void (*il2cpp_add_internal_call_t)(const char* name, uintptr_t pointer); typedef void* (*il2cpp_resolve_icall_t)(const char* name); -typedef void* (*il2cpp_array_new_t)(Il2CppClass* klass, uintptr_t count); +typedef Il2CppArraySize* (*il2cpp_array_new_t)(Il2CppClass* klass, uintptr_t count); typedef void* (*il2cpp_thread_attach_t)(void* domain); typedef void (*il2cpp_thread_detach_t)(void* thread); typedef const Il2CppType* (*il2cpp_class_get_type_t)(Il2CppClass* klass); @@ -536,6 +540,8 @@ typedef Il2CppClass* (*il2cpp_field_get_parent_t)(FieldInfo* field); typedef size_t (*il2cpp_field_get_offset_t)(FieldInfo* field); typedef const PropertyInfo* (*il2cpp_class_get_property_from_name_t)(Il2CppClass* klass, const char* name); typedef void (*il2cpp_runtime_object_init_t)(Il2CppObject* obj); +typedef Il2CppObject* (*il2cpp_value_box_t)(Il2CppClass* klass, void* data); +typedef void* (*il2cpp_object_unbox_t)(Il2CppObject* obj); // function defines extern il2cpp_string_new_utf16_t il2cpp_string_new_utf16; @@ -594,6 +600,8 @@ extern il2cpp_field_get_parent_t il2cpp_field_get_parent; extern il2cpp_field_get_offset_t il2cpp_field_get_offset; extern il2cpp_class_get_property_from_name_t il2cpp_class_get_property_from_name; extern il2cpp_runtime_object_init_t il2cpp_runtime_object_init; +extern il2cpp_value_box_t il2cpp_value_box; +extern il2cpp_object_unbox_t il2cpp_object_unbox; char* il2cpp_array_addr_with_size(void* arr, int32_t size, uintptr_t idx); @@ -608,6 +616,8 @@ char* il2cpp_array_addr_with_size(void* arr, int32_t size, uintptr_t idx); namespace il2cpp_symbols { + inline void* il2cpp_domain = nullptr; + void init(HMODULE game_module); uintptr_t get_method_pointer(const char* assemblyName, const char* namespaze, const char* klassName, const char* name, int argsCount);