forked from chinosk/gkms-local
				
			Compare commits
	
		
			26 Commits
		
	
	
	| Author | SHA1 | Date | 
|---|---|---|
| 
							
							
								
								 | 
						b70376dcef | |
| 
							
							
								
								 | 
						e859589125 | |
| 
							
							
								
								 | 
						d09904643f | |
| 
							
							
								
								 | 
						d83854c755 | |
| 
							
							
								
								 | 
						ade47131f9 | |
| 
							
							
								
								 | 
						0218543935 | |
| 
							
							
								
								 | 
						60c846b2f0 | |
| 
							
							
								 | 
						01591e61c0 | |
| 
							
							
								
								 | 
						56c066bf42 | |
| 
							
							
								
								 | 
						35c2b9f489 | |
| 
							
							
								
								 | 
						c50fdfd678 | |
| 
							
							
								
								 | 
						3c1d1f139a | |
| 
							
							
								
								 | 
						6ac94178fa | |
| 
							
							
								
								 | 
						c27085772f | |
| 
							
							
								
								 | 
						361c48e2c9 | |
| 
							
							
								
								 | 
						e9ba8b58fd | |
| 
							
							
								
								 | 
						e03736bd7d | |
| 
							
							
								
								 | 
						06b552a097 | |
| 
							
							
								
								 | 
						bd9bcae01d | |
| 
							
							
								
								 | 
						8c850ad7db | |
| 
							
							
								
								 | 
						7bf429336b | |
| 
							
							
								
								 | 
						c7e3d4f718 | |
| 
							
							
								
								 | 
						b74713be78 | |
| 
							
							
								
								 | 
						67945c86dd | |
| 
							
							
								
								 | 
						06a96a450e | |
| 
							
							
								 | 
						6e512d9380 | 
							
								
								
									
										11
									
								
								README.md
								
								
								
								
							
							
						
						
									
										11
									
								
								README.md
								
								
								
								
							| 
						 | 
					@ -8,16 +8,7 @@
 | 
				
			||||||
# Usage
 | 
					# Usage
 | 
				
			||||||
 | 
					
 | 
				
			||||||
- 这是一个 XPosed 插件,已 Root 用户可以使用 [LSPosed](https://github.com/LSPosed/LSPosed),未 Root 用户可以使用 [LSPatch](https://github.com/LSPosed/LSPatch)。
 | 
					- 这是一个 XPosed 插件,已 Root 用户可以使用 [LSPosed](https://github.com/LSPosed/LSPosed),未 Root 用户可以使用 [LSPatch](https://github.com/LSPosed/LSPatch)。
 | 
				
			||||||
 | 
					- 安卓 15 及以上的用户,请使用 [JingMatrix/LSPosed](https://github.com/JingMatrix/LSPosed) 或 [JingMatrix/LSPatch](https://github.com/JingMatrix/LSPatch)。因为原版已停止更新。
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# TODO
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
- [x] 卡片信息、TIPS 等部分的文本 hook (`generic`)
 | 
					 | 
				
			||||||
- [ ] 更多类型的文件替换
 | 
					 | 
				
			||||||
- [ ] LSPatch 集成模式无效
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
... and more
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -15,8 +15,9 @@ android {
 | 
				
			||||||
        applicationId "io.github.chinosk.gakumas.localify"
 | 
					        applicationId "io.github.chinosk.gakumas.localify"
 | 
				
			||||||
        minSdk 29
 | 
					        minSdk 29
 | 
				
			||||||
        targetSdk 34
 | 
					        targetSdk 34
 | 
				
			||||||
        versionCode 4
 | 
					        versionCode 12
 | 
				
			||||||
        versionName "v1.6.1"
 | 
					        versionName "v3.1.0"
 | 
				
			||||||
 | 
					        buildConfigField "String", "VERSION_NAME", "\"${versionName}\""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
 | 
					        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
 | 
				
			||||||
        vectorDrawables {
 | 
					        vectorDrawables {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							| 
						 | 
					@ -0,0 +1,3 @@
 | 
				
			||||||
 | 
					<lint>
 | 
				
			||||||
 | 
					    <issue id="ExtraTranslation" severity="ignore" />
 | 
				
			||||||
 | 
					</lint>
 | 
				
			||||||
| 
						 | 
					@ -1 +1 @@
 | 
				
			||||||
Subproject commit 52aefc27eb052eba3fe51a5ff9eb912c7d49dbf0
 | 
					Subproject commit df448152cfb55c528d66832b2470ff1c2277e980
 | 
				
			||||||
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							
										
											Binary file not shown.
										
									
								
							| 
						 | 
					@ -42,6 +42,7 @@ add_library(${CMAKE_PROJECT_NAME} SHARED
 | 
				
			||||||
        GakumasLocalify/Log.cpp
 | 
					        GakumasLocalify/Log.cpp
 | 
				
			||||||
        GakumasLocalify/Misc.cpp
 | 
					        GakumasLocalify/Misc.cpp
 | 
				
			||||||
        GakumasLocalify/Local.cpp
 | 
					        GakumasLocalify/Local.cpp
 | 
				
			||||||
 | 
					        GakumasLocalify/MasterLocal.cpp
 | 
				
			||||||
        GakumasLocalify/camera/baseCamera.cpp
 | 
					        GakumasLocalify/camera/baseCamera.cpp
 | 
				
			||||||
        GakumasLocalify/camera/camera.cpp
 | 
					        GakumasLocalify/camera/camera.cpp
 | 
				
			||||||
        GakumasLocalify/config/Config.cpp
 | 
					        GakumasLocalify/config/Config.cpp
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,29 +1,58 @@
 | 
				
			||||||
#define KEY_W  51
 | 
					#include "../platformDefine.hpp"
 | 
				
			||||||
#define KEY_S  47
 | 
					 | 
				
			||||||
#define KEY_A  29
 | 
					 | 
				
			||||||
#define KEY_D  32
 | 
					 | 
				
			||||||
#define KEY_R  46
 | 
					 | 
				
			||||||
#define KEY_Q  45
 | 
					 | 
				
			||||||
#define KEY_E  33
 | 
					 | 
				
			||||||
#define KEY_F  34
 | 
					 | 
				
			||||||
#define KEY_I  37
 | 
					 | 
				
			||||||
#define KEY_K  39
 | 
					 | 
				
			||||||
#define KEY_J  38
 | 
					 | 
				
			||||||
#define KEY_L  40
 | 
					 | 
				
			||||||
#define KEY_V  50
 | 
					 | 
				
			||||||
#define KEY_UP  19
 | 
					 | 
				
			||||||
#define KEY_DOWN  20
 | 
					 | 
				
			||||||
#define KEY_LEFT  21
 | 
					 | 
				
			||||||
#define KEY_RIGHT  22
 | 
					 | 
				
			||||||
#define KEY_CTRL  113
 | 
					 | 
				
			||||||
#define KEY_SHIFT  59
 | 
					 | 
				
			||||||
#define KEY_ALT  57
 | 
					 | 
				
			||||||
#define KEY_SPACE  62
 | 
					 | 
				
			||||||
#define KEY_ADD  70
 | 
					 | 
				
			||||||
#define KEY_SUB  69
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define WM_KEYDOWN 0
 | 
					#ifndef GKMS_WINDOWS
 | 
				
			||||||
#define WM_KEYUP 1
 | 
						#define KEY_W  51
 | 
				
			||||||
 | 
						#define KEY_S  47
 | 
				
			||||||
 | 
						#define KEY_A  29
 | 
				
			||||||
 | 
						#define KEY_D  32
 | 
				
			||||||
 | 
						#define KEY_R  46
 | 
				
			||||||
 | 
						#define KEY_Q  45
 | 
				
			||||||
 | 
						#define KEY_E  33
 | 
				
			||||||
 | 
						#define KEY_F  34
 | 
				
			||||||
 | 
						#define KEY_I  37
 | 
				
			||||||
 | 
						#define KEY_K  39
 | 
				
			||||||
 | 
						#define KEY_J  38
 | 
				
			||||||
 | 
						#define KEY_L  40
 | 
				
			||||||
 | 
						#define KEY_V  50
 | 
				
			||||||
 | 
						#define KEY_UP  19
 | 
				
			||||||
 | 
						#define KEY_DOWN  20
 | 
				
			||||||
 | 
						#define KEY_LEFT  21
 | 
				
			||||||
 | 
						#define KEY_RIGHT  22
 | 
				
			||||||
 | 
						#define KEY_CTRL  113
 | 
				
			||||||
 | 
						#define KEY_SHIFT  59
 | 
				
			||||||
 | 
						#define KEY_ALT  57
 | 
				
			||||||
 | 
						#define KEY_SPACE  62
 | 
				
			||||||
 | 
						#define KEY_ADD  70
 | 
				
			||||||
 | 
						#define KEY_SUB  69
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						#define WM_KEYDOWN 0
 | 
				
			||||||
 | 
						#define WM_KEYUP 1
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
						#define KEY_W  'W'
 | 
				
			||||||
 | 
						#define KEY_S  'S'
 | 
				
			||||||
 | 
						#define KEY_A  'A'
 | 
				
			||||||
 | 
						#define KEY_D  'D'
 | 
				
			||||||
 | 
						#define KEY_R  'R'
 | 
				
			||||||
 | 
						#define KEY_Q  'Q'
 | 
				
			||||||
 | 
						#define KEY_E  'E'
 | 
				
			||||||
 | 
						#define KEY_F  'F'
 | 
				
			||||||
 | 
						#define KEY_I  'I'
 | 
				
			||||||
 | 
						#define KEY_K  'K'
 | 
				
			||||||
 | 
						#define KEY_J  'J'
 | 
				
			||||||
 | 
						#define KEY_L  'L'
 | 
				
			||||||
 | 
						#define KEY_V  'V'
 | 
				
			||||||
 | 
						#define KEY_UP  38
 | 
				
			||||||
 | 
						#define KEY_DOWN  40
 | 
				
			||||||
 | 
						#define KEY_LEFT  37
 | 
				
			||||||
 | 
						#define KEY_RIGHT  39
 | 
				
			||||||
 | 
						#define KEY_CTRL  17
 | 
				
			||||||
 | 
						#define KEY_SHIFT  16
 | 
				
			||||||
 | 
						#define KEY_ALT  18
 | 
				
			||||||
 | 
						#define KEY_SPACE  32
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						#define KEY_ADD  187
 | 
				
			||||||
 | 
						#define KEY_SUB  189
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define BTN_A 96
 | 
					#define BTN_A 96
 | 
				
			||||||
#define BTN_B 97
 | 
					#define BTN_B 97
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| 
						 | 
					@ -14,14 +14,96 @@ namespace Il2cppUtils {
 | 
				
			||||||
        const char* namespaze;
 | 
					        const char* namespaze;
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    struct Il2CppObject
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        union
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            void* klass;
 | 
				
			||||||
 | 
					            void* vtable;
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					        void* monitor;
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    enum Il2CppTypeEnum
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        IL2CPP_TYPE_END = 0x00,       /* End of List */
 | 
				
			||||||
 | 
					        IL2CPP_TYPE_VOID = 0x01,
 | 
				
			||||||
 | 
					        IL2CPP_TYPE_BOOLEAN = 0x02,
 | 
				
			||||||
 | 
					        IL2CPP_TYPE_CHAR = 0x03,
 | 
				
			||||||
 | 
					        IL2CPP_TYPE_I1 = 0x04,
 | 
				
			||||||
 | 
					        IL2CPP_TYPE_U1 = 0x05,
 | 
				
			||||||
 | 
					        IL2CPP_TYPE_I2 = 0x06,
 | 
				
			||||||
 | 
					        IL2CPP_TYPE_U2 = 0x07,
 | 
				
			||||||
 | 
					        IL2CPP_TYPE_I4 = 0x08,
 | 
				
			||||||
 | 
					        IL2CPP_TYPE_U4 = 0x09,
 | 
				
			||||||
 | 
					        IL2CPP_TYPE_I8 = 0x0a,
 | 
				
			||||||
 | 
					        IL2CPP_TYPE_U8 = 0x0b,
 | 
				
			||||||
 | 
					        IL2CPP_TYPE_R4 = 0x0c,
 | 
				
			||||||
 | 
					        IL2CPP_TYPE_R8 = 0x0d,
 | 
				
			||||||
 | 
					        IL2CPP_TYPE_STRING = 0x0e,
 | 
				
			||||||
 | 
					        IL2CPP_TYPE_PTR = 0x0f,
 | 
				
			||||||
 | 
					        IL2CPP_TYPE_BYREF = 0x10,
 | 
				
			||||||
 | 
					        IL2CPP_TYPE_VALUETYPE = 0x11,
 | 
				
			||||||
 | 
					        IL2CPP_TYPE_CLASS = 0x12,
 | 
				
			||||||
 | 
					        IL2CPP_TYPE_VAR = 0x13,
 | 
				
			||||||
 | 
					        IL2CPP_TYPE_ARRAY = 0x14,
 | 
				
			||||||
 | 
					        IL2CPP_TYPE_GENERICINST = 0x15,
 | 
				
			||||||
 | 
					        IL2CPP_TYPE_TYPEDBYREF = 0x16,
 | 
				
			||||||
 | 
					        IL2CPP_TYPE_I = 0x18,
 | 
				
			||||||
 | 
					        IL2CPP_TYPE_U = 0x19,
 | 
				
			||||||
 | 
					        IL2CPP_TYPE_FNPTR = 0x1b,
 | 
				
			||||||
 | 
					        IL2CPP_TYPE_OBJECT = 0x1c,
 | 
				
			||||||
 | 
					        IL2CPP_TYPE_SZARRAY = 0x1d,
 | 
				
			||||||
 | 
					        IL2CPP_TYPE_MVAR = 0x1e,
 | 
				
			||||||
 | 
					        IL2CPP_TYPE_CMOD_REQD = 0x1f,
 | 
				
			||||||
 | 
					        IL2CPP_TYPE_CMOD_OPT = 0x20,
 | 
				
			||||||
 | 
					        IL2CPP_TYPE_INTERNAL = 0x21,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        IL2CPP_TYPE_MODIFIER = 0x40,
 | 
				
			||||||
 | 
					        IL2CPP_TYPE_SENTINEL = 0x41,
 | 
				
			||||||
 | 
					        IL2CPP_TYPE_PINNED = 0x45,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        IL2CPP_TYPE_ENUM = 0x55
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    typedef struct Il2CppType
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        void* dummy;
 | 
				
			||||||
 | 
					        unsigned int attrs : 16;
 | 
				
			||||||
 | 
					        Il2CppTypeEnum type : 8;
 | 
				
			||||||
 | 
					        unsigned int num_mods : 6;
 | 
				
			||||||
 | 
					        unsigned int byref : 1;
 | 
				
			||||||
 | 
					        unsigned int pinned : 1;
 | 
				
			||||||
 | 
					    } Il2CppType;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    struct Il2CppReflectionType
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        Il2CppObject object;
 | 
				
			||||||
 | 
					        const Il2CppType* type;
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    struct Resolution_t {
 | 
				
			||||||
 | 
					        int width;
 | 
				
			||||||
 | 
					        int height;
 | 
				
			||||||
 | 
					        int herz;
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    struct FieldInfo {
 | 
				
			||||||
 | 
					        const char* name;
 | 
				
			||||||
 | 
					        const Il2CppType* type;
 | 
				
			||||||
 | 
					        uintptr_t parent;
 | 
				
			||||||
 | 
					        int32_t offset;
 | 
				
			||||||
 | 
					        uint32_t token;
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    struct MethodInfo {
 | 
					    struct MethodInfo {
 | 
				
			||||||
        uintptr_t methodPointer;
 | 
					        uintptr_t methodPointer;
 | 
				
			||||||
        uintptr_t invoker_method;
 | 
					        uintptr_t invoker_method;
 | 
				
			||||||
        const char* name;
 | 
					        const char* name;
 | 
				
			||||||
        uintptr_t klass;
 | 
					        uintptr_t klass;
 | 
				
			||||||
        //const Il2CppType* return_type;
 | 
					        const Il2CppType* return_type;
 | 
				
			||||||
        //const ParameterInfo* parameters;
 | 
					        //const ParameterInfo* parameters;
 | 
				
			||||||
        const void* return_type;
 | 
					        // const void* return_type;
 | 
				
			||||||
        const void* parameters;
 | 
					        const void* parameters;
 | 
				
			||||||
        uintptr_t methodDefinition;
 | 
					        uintptr_t methodDefinition;
 | 
				
			||||||
        uintptr_t genericContainer;
 | 
					        uintptr_t genericContainer;
 | 
				
			||||||
| 
						 | 
					@ -36,13 +118,7 @@ namespace Il2cppUtils {
 | 
				
			||||||
        uint8_t is_marshaled_from_native : 1;
 | 
					        uint8_t is_marshaled_from_native : 1;
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    struct Resolution_t {
 | 
					    static UnityResolve::Class* GetClass(const std::string& assemblyName, const std::string& nameSpaceName,
 | 
				
			||||||
        int width;
 | 
					 | 
				
			||||||
        int height;
 | 
					 | 
				
			||||||
        int herz;
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    UnityResolve::Class* GetClass(const std::string& assemblyName, const std::string& nameSpaceName,
 | 
					 | 
				
			||||||
                   const std::string& className) {
 | 
					                   const std::string& className) {
 | 
				
			||||||
        const auto assembly = UnityResolve::Get(assemblyName);
 | 
					        const auto assembly = UnityResolve::Get(assemblyName);
 | 
				
			||||||
        if (!assembly) {
 | 
					        if (!assembly) {
 | 
				
			||||||
| 
						 | 
					@ -81,7 +157,7 @@ namespace Il2cppUtils {
 | 
				
			||||||
        return ret;
 | 
					        return ret;
 | 
				
			||||||
    }*/
 | 
					    }*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    UnityResolve::Method* GetMethod(const std::string& assemblyName, const std::string& nameSpaceName,
 | 
					    static UnityResolve::Method* GetMethod(const std::string& assemblyName, const std::string& nameSpaceName,
 | 
				
			||||||
                           const std::string& className, const std::string& methodName, const std::vector<std::string>& args = {}) {
 | 
					                           const std::string& className, const std::string& methodName, const std::vector<std::string>& args = {}) {
 | 
				
			||||||
        const auto assembly = UnityResolve::Get(assemblyName);
 | 
					        const auto assembly = UnityResolve::Get(assemblyName);
 | 
				
			||||||
        if (!assembly) {
 | 
					        if (!assembly) {
 | 
				
			||||||
| 
						 | 
					@ -108,7 +184,7 @@ namespace Il2cppUtils {
 | 
				
			||||||
        return method;
 | 
					        return method;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void* GetMethodPointer(const std::string& assemblyName, const std::string& nameSpaceName,
 | 
					    static void* GetMethodPointer(const std::string& assemblyName, const std::string& nameSpaceName,
 | 
				
			||||||
                           const std::string& className, const std::string& methodName, const std::vector<std::string>& args = {}) {
 | 
					                           const std::string& className, const std::string& methodName, const std::vector<std::string>& args = {}) {
 | 
				
			||||||
        auto method = GetMethod(assemblyName, nameSpaceName, className, methodName, args);
 | 
					        auto method = GetMethod(assemblyName, nameSpaceName, className, methodName, args);
 | 
				
			||||||
        if (method) {
 | 
					        if (method) {
 | 
				
			||||||
| 
						 | 
					@ -117,20 +193,28 @@ namespace Il2cppUtils {
 | 
				
			||||||
        return nullptr;
 | 
					        return nullptr;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void* il2cpp_resolve_icall(const char* s) {
 | 
					    static void* il2cpp_resolve_icall(const char* s) {
 | 
				
			||||||
        return UnityResolve::Invoke<void*>("il2cpp_resolve_icall", s);
 | 
					        return UnityResolve::Invoke<void*>("il2cpp_resolve_icall", s);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Il2CppClassHead* get_class_from_instance(const void* instance) {
 | 
					    static Il2CppClassHead* get_class_from_instance(const void* instance) {
 | 
				
			||||||
        return static_cast<Il2CppClassHead*>(*static_cast<void* const*>(std::assume_aligned<alignof(void*)>(instance)));
 | 
					        return static_cast<Il2CppClassHead*>(*static_cast<void* const*>(std::assume_aligned<alignof(void*)>(instance)));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    MethodInfo* il2cpp_class_get_method_from_name(void* klass, const char* name, int argsCount) {
 | 
					    static MethodInfo* il2cpp_class_get_method_from_name(void* klass, const char* name, int argsCount) {
 | 
				
			||||||
        return UnityResolve::Invoke<MethodInfo*>("il2cpp_class_get_method_from_name", klass, name, argsCount);
 | 
					        return UnityResolve::Invoke<MethodInfo*>("il2cpp_class_get_method_from_name", klass, name, argsCount);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void* find_nested_class(void* klass, std::predicate<void*> auto&& predicate)
 | 
					    static uintptr_t il2cpp_class_get_method_pointer_from_name(void* klass, const char* name, int argsCount) {
 | 
				
			||||||
    {
 | 
					        auto findKlass = il2cpp_class_get_method_from_name(klass, name, argsCount);
 | 
				
			||||||
 | 
					        if (findKlass) {
 | 
				
			||||||
 | 
					            return findKlass->methodPointer;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        Log::ErrorFmt("method: %s not found", name);
 | 
				
			||||||
 | 
					        return 0;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static void* find_nested_class(void* klass, std::predicate<void*> auto&& predicate) {
 | 
				
			||||||
        void* iter{};
 | 
					        void* iter{};
 | 
				
			||||||
        while (const auto curNestedClass = UnityResolve::Invoke<void*>("il2cpp_class_get_nested_types", klass, &iter))
 | 
					        while (const auto curNestedClass = UnityResolve::Invoke<void*>("il2cpp_class_get_nested_types", klass, &iter))
 | 
				
			||||||
        {
 | 
					        {
 | 
				
			||||||
| 
						 | 
					@ -143,22 +227,267 @@ namespace Il2cppUtils {
 | 
				
			||||||
        return nullptr;
 | 
					        return nullptr;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void* find_nested_class_from_name(void* klass, const char* name)
 | 
					    static void* find_nested_class_from_name(void* klass, const char* name) {
 | 
				
			||||||
    {
 | 
					 | 
				
			||||||
        return find_nested_class(klass, [name = std::string_view(name)](void* nestedClass) {
 | 
					        return find_nested_class(klass, [name = std::string_view(name)](void* nestedClass) {
 | 
				
			||||||
            return static_cast<Il2CppClassHead*>(nestedClass)->name == name;
 | 
					            return static_cast<Il2CppClassHead*>(nestedClass)->name == name;
 | 
				
			||||||
        });
 | 
					        });
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    template <typename RType>
 | 
					    template <typename RType>
 | 
				
			||||||
    auto ClassGetFieldValue(void* obj, UnityResolve::Field* field) -> RType {
 | 
					    static auto ClassGetFieldValue(void* obj, UnityResolve::Field* field) -> RType {
 | 
				
			||||||
        return *reinterpret_cast<RType*>(reinterpret_cast<uintptr_t>(obj) + field->offset);
 | 
					        return *reinterpret_cast<RType*>(reinterpret_cast<uintptr_t>(obj) + field->offset);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    template <typename RType>
 | 
					    template <typename RType>
 | 
				
			||||||
    auto ClassSetFieldValue(void* obj, UnityResolve::Field* field, RType value) -> void {
 | 
					    static auto ClassGetFieldValue(void* obj, FieldInfo* field) -> RType {
 | 
				
			||||||
 | 
					        return *reinterpret_cast<RType*>(reinterpret_cast<uintptr_t>(obj) + field->offset);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    template <typename T>
 | 
				
			||||||
 | 
					    static auto ClassSetFieldValue(void* obj, UnityResolve::Field* field, T value) -> void {
 | 
				
			||||||
 | 
					        const auto fieldPtr = static_cast<std::byte*>(obj) + field->offset;
 | 
				
			||||||
 | 
					        std::memcpy(fieldPtr, std::addressof(value), sizeof(T));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    template <typename RType>
 | 
				
			||||||
 | 
					    static auto ClassSetFieldValue(void* obj, FieldInfo* field, RType value) -> void {
 | 
				
			||||||
        *reinterpret_cast<RType*>(reinterpret_cast<uintptr_t>(obj) + field->offset) = value;
 | 
					        *reinterpret_cast<RType*>(reinterpret_cast<uintptr_t>(obj) + field->offset) = value;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static void* get_system_class_from_reflection_type_str(const char* typeStr, const char* assemblyName = "mscorlib") {
 | 
				
			||||||
 | 
					        using Il2CppString = UnityResolve::UnityType::String;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        static auto assemblyLoad = reinterpret_cast<void* (*)(Il2CppString*)>(
 | 
				
			||||||
 | 
					                GetMethodPointer("mscorlib.dll", "System.Reflection",
 | 
				
			||||||
 | 
					                                 "Assembly", "Load", {"*"})
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					        static auto assemblyGetType = reinterpret_cast<Il2CppReflectionType * (*)(void*, Il2CppString*)>(
 | 
				
			||||||
 | 
					                GetMethodPointer("mscorlib.dll", "System.Reflection",
 | 
				
			||||||
 | 
					                                 "Assembly", "GetType", {"*"})
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        static auto reflectionAssembly = assemblyLoad(Il2CppString::New(assemblyName));
 | 
				
			||||||
 | 
					        auto reflectionType = assemblyGetType(reflectionAssembly, Il2CppString::New(typeStr));
 | 
				
			||||||
 | 
					        return UnityResolve::Invoke<void*>("il2cpp_class_from_system_type", reflectionType);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static std::unordered_map<std::string, std::unordered_map<int, std::string>> enumToValueMapCache{};
 | 
				
			||||||
 | 
					    static std::unordered_map<int, std::string> EnumToValueMap(Il2CppClassHead* enumClass, bool useCache) {
 | 
				
			||||||
 | 
					        std::unordered_map<int, std::string> ret{};
 | 
				
			||||||
 | 
					        auto isEnum = UnityResolve::Invoke<bool>("il2cpp_class_is_enum", enumClass);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (isEnum) {
 | 
				
			||||||
 | 
					            Il2cppUtils::FieldInfo* field = nullptr;
 | 
				
			||||||
 | 
					            void* iter = nullptr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            std::string cacheName = std::string(enumClass->namespaze) + "::" + enumClass->name;
 | 
				
			||||||
 | 
					            if (useCache) {
 | 
				
			||||||
 | 
					                if (auto it = enumToValueMapCache.find(cacheName); it != enumToValueMapCache.end()) {
 | 
				
			||||||
 | 
					                    return it->second;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            while ((field = UnityResolve::Invoke<Il2cppUtils::FieldInfo*>("il2cpp_class_get_fields", enumClass, &iter))) {
 | 
				
			||||||
 | 
					                // Log::DebugFmt("field: %s, off: %d", field->name, field->offset);
 | 
				
			||||||
 | 
					                if (field->offset > 0) continue;  // 非 static
 | 
				
			||||||
 | 
					                if (strcmp(field->name, "value__") == 0) {
 | 
				
			||||||
 | 
					                    continue;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                int value;
 | 
				
			||||||
 | 
					                UnityResolve::Invoke<void>("il2cpp_field_static_get_value", field, &value);
 | 
				
			||||||
 | 
					                // Log::DebugFmt("returnClass: %s - %s: 0x%x", enumClass->name, field->name, value);
 | 
				
			||||||
 | 
					                std::string itemName = std::string(enumClass->name) + "_" + field->name;
 | 
				
			||||||
 | 
					                ret.emplace(value, std::move(itemName));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (useCache) {
 | 
				
			||||||
 | 
					                enumToValueMapCache.emplace(std::move(cacheName), ret);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return ret;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    template <typename T = void*>
 | 
				
			||||||
 | 
					    static void iterate_IEnumerable(const void* obj, std::invocable<T> auto&& receiver)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        const auto klass = get_class_from_instance(obj);
 | 
				
			||||||
 | 
					        const auto getEnumeratorMethod = reinterpret_cast<void* (*)(const void*)>(il2cpp_class_get_method_from_name(klass, "GetEnumerator", 0)->methodPointer);
 | 
				
			||||||
 | 
					        const auto enumerator = getEnumeratorMethod(obj);
 | 
				
			||||||
 | 
					        const auto enumeratorClass = get_class_from_instance(enumerator);
 | 
				
			||||||
 | 
					        const auto getCurrentMethod = reinterpret_cast<T(*)(void*)>(il2cpp_class_get_method_from_name(enumeratorClass, "get_Current", 0)->methodPointer);
 | 
				
			||||||
 | 
					        const auto moveNextMethod = reinterpret_cast<bool(*)(void*)>(il2cpp_class_get_method_from_name(enumeratorClass, "MoveNext", 0)->methodPointer);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        while (moveNextMethod(enumerator))
 | 
				
			||||||
 | 
					        {
 | 
				
			||||||
 | 
					            static_cast<decltype(receiver)>(receiver)(getCurrentMethod(enumerator));
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    namespace Tools {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        template <typename T = void*>
 | 
				
			||||||
 | 
					        class CSListEditor {
 | 
				
			||||||
 | 
					        public:
 | 
				
			||||||
 | 
					            CSListEditor(void* list) {
 | 
				
			||||||
 | 
					                list_klass = get_class_from_instance(list);
 | 
				
			||||||
 | 
					                lst = list;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                lst_get_Count_method = il2cpp_class_get_method_from_name(list_klass, "get_Count", 0);
 | 
				
			||||||
 | 
					                lst_get_Item_method = il2cpp_class_get_method_from_name(list_klass, "get_Item", 1);
 | 
				
			||||||
 | 
					                lst_set_Item_method = il2cpp_class_get_method_from_name(list_klass, "set_Item", 2);
 | 
				
			||||||
 | 
					                lst_Add_method = il2cpp_class_get_method_from_name(list_klass, "Add", 1);
 | 
				
			||||||
 | 
					                lst_Contains_method = il2cpp_class_get_method_from_name(list_klass, "Contains", 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                lst_get_Count = reinterpret_cast<lst_get_Count_t>(lst_get_Count_method->methodPointer);
 | 
				
			||||||
 | 
					                lst_get_Item = reinterpret_cast<lst_get_Item_t>(lst_get_Item_method->methodPointer);
 | 
				
			||||||
 | 
					                lst_set_Item = reinterpret_cast<lst_set_Item_t>(lst_set_Item_method->methodPointer);
 | 
				
			||||||
 | 
					                lst_Add = reinterpret_cast<lst_Add_t>(lst_Add_method->methodPointer);
 | 
				
			||||||
 | 
					                lst_Contains = reinterpret_cast<lst_Contains_t>(lst_Contains_method->methodPointer);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            void Add(T value) {
 | 
				
			||||||
 | 
					                lst_Add(lst, value, lst_Add_method);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            bool Contains(T value) {
 | 
				
			||||||
 | 
					                return lst_Contains(lst, value, lst_Contains_method);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            T get_Item(int index) {
 | 
				
			||||||
 | 
					                return lst_get_Item(lst, index, lst_get_Item_method);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            void set_Item(int index, T value) {
 | 
				
			||||||
 | 
					                return lst_set_Item(lst, index, value, lst_set_Item_method);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            int get_Count() {
 | 
				
			||||||
 | 
					                return lst_get_Count(lst, lst_get_Count_method);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            T operator[] (int key) {
 | 
				
			||||||
 | 
					                return get_Item(key);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            class Iterator {
 | 
				
			||||||
 | 
					            public:
 | 
				
			||||||
 | 
					                Iterator(CSListEditor<T>* editor, int index) : editor(editor), index(index) {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                T operator*() const {
 | 
				
			||||||
 | 
					                    return editor->get_Item(index);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                Iterator& operator++() {
 | 
				
			||||||
 | 
					                    ++index;
 | 
				
			||||||
 | 
					                    return *this;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                bool operator!=(const Iterator& other) const {
 | 
				
			||||||
 | 
					                    return index != other.index;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            private:
 | 
				
			||||||
 | 
					                CSListEditor<T>* editor;
 | 
				
			||||||
 | 
					                int index;
 | 
				
			||||||
 | 
					            };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            Iterator begin() {
 | 
				
			||||||
 | 
					                return Iterator(this, 0);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            Iterator end() {
 | 
				
			||||||
 | 
					                return Iterator(this, get_Count());
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            void* lst;
 | 
				
			||||||
 | 
					            void* list_klass;
 | 
				
			||||||
 | 
					        private:
 | 
				
			||||||
 | 
					            typedef T(*lst_get_Item_t)(void*, int, void* mtd);
 | 
				
			||||||
 | 
					            typedef void(*lst_Add_t)(void*, T, void* mtd);
 | 
				
			||||||
 | 
					            typedef void(*lst_set_Item_t)(void*, int, T, void* mtd);
 | 
				
			||||||
 | 
					            typedef int(*lst_get_Count_t)(void*, void* mtd);
 | 
				
			||||||
 | 
					            typedef bool(*lst_Contains_t)(void*, T, void* mtd);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            MethodInfo* lst_get_Item_method;
 | 
				
			||||||
 | 
					            MethodInfo* lst_Add_method;
 | 
				
			||||||
 | 
					            MethodInfo* lst_get_Count_method;
 | 
				
			||||||
 | 
					            MethodInfo* lst_set_Item_method;
 | 
				
			||||||
 | 
					            MethodInfo* lst_Contains_method;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            lst_get_Item_t lst_get_Item;
 | 
				
			||||||
 | 
					            lst_set_Item_t lst_set_Item;
 | 
				
			||||||
 | 
					            lst_Add_t lst_Add;
 | 
				
			||||||
 | 
					            lst_get_Count_t lst_get_Count;
 | 
				
			||||||
 | 
					            lst_Contains_t lst_Contains;
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        template <typename KT = void*, typename VT = void*>
 | 
				
			||||||
 | 
					        class CSDictEditor {
 | 
				
			||||||
 | 
					        public:
 | 
				
			||||||
 | 
					            // @param dict: Dictionary instance.
 | 
				
			||||||
 | 
					            // @param dictTypeStr: Reflection type. eg: "System.Collections.Generic.Dictionary`2[System.Int32, System.Int32]"
 | 
				
			||||||
 | 
					            CSDictEditor(void* dict, const char* dictTypeStr) {
 | 
				
			||||||
 | 
					                dic_klass = Il2cppUtils::get_system_class_from_reflection_type_str(dictTypeStr);
 | 
				
			||||||
 | 
									initDict(dict);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            CSDictEditor(void* dict) {
 | 
				
			||||||
 | 
									dic_klass = get_class_from_instance(dict);
 | 
				
			||||||
 | 
									initDict(dict);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            CSDictEditor(void* dict, void* dicClass) {
 | 
				
			||||||
 | 
					                dic_klass = dicClass;
 | 
				
			||||||
 | 
									initDict(dict);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            void Add(KT key, VT value) {
 | 
				
			||||||
 | 
					                dic_Add(dict, key, value, Add_method);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            bool ContainsKey(KT key) {
 | 
				
			||||||
 | 
					                return dic_containsKey(dict, key, ContainsKey_method);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            VT get_Item(KT key) {
 | 
				
			||||||
 | 
					                return dic_get_Item(dict, key, get_Item_method);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            VT operator[] (KT key) {
 | 
				
			||||||
 | 
					                return get_Item(key);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            void* dict;
 | 
				
			||||||
 | 
					            void* dic_klass;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        private:
 | 
				
			||||||
 | 
					            void initDict(void* dict) {
 | 
				
			||||||
 | 
									// dic_klass = dicClass;
 | 
				
			||||||
 | 
					                this->dict = dict;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                get_Item_method = il2cpp_class_get_method_from_name(dic_klass, "get_Item", 1);
 | 
				
			||||||
 | 
					                Add_method = il2cpp_class_get_method_from_name(dic_klass, "Add", 2);
 | 
				
			||||||
 | 
					                ContainsKey_method = il2cpp_class_get_method_from_name(dic_klass, "ContainsKey", 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                dic_get_Item = (dic_get_Item_t)get_Item_method->methodPointer;
 | 
				
			||||||
 | 
					                dic_Add = (dic_Add_t)Add_method->methodPointer;
 | 
				
			||||||
 | 
					                dic_containsKey = (dic_containsKey_t)ContainsKey_method->methodPointer;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            typedef VT(*dic_get_Item_t)(void*, KT, void* mtd);
 | 
				
			||||||
 | 
					            typedef VT(*dic_Add_t)(void*, KT, VT, void* mtd);
 | 
				
			||||||
 | 
					            typedef VT(*dic_containsKey_t)(void*, KT, void* mtd);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            CSDictEditor();
 | 
				
			||||||
 | 
					            MethodInfo* get_Item_method;
 | 
				
			||||||
 | 
					            MethodInfo* Add_method;
 | 
				
			||||||
 | 
					            MethodInfo* ContainsKey_method;
 | 
				
			||||||
 | 
					            dic_get_Item_t dic_get_Item;
 | 
				
			||||||
 | 
					            dic_Add_t dic_Add;
 | 
				
			||||||
 | 
					            dic_containsKey_t dic_containsKey;
 | 
				
			||||||
 | 
					        };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -17,6 +17,8 @@
 | 
				
			||||||
#include "BaseDefine.h"
 | 
					#include "BaseDefine.h"
 | 
				
			||||||
#include "string_parser/StringParser.hpp"
 | 
					#include "string_parser/StringParser.hpp"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// #include "cpprest/details/http_helpers.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace GakumasLocal::Local {
 | 
					namespace GakumasLocal::Local {
 | 
				
			||||||
    std::unordered_map<std::string, std::string> i18nData{};
 | 
					    std::unordered_map<std::string, std::string> i18nData{};
 | 
				
			||||||
| 
						 | 
					@ -90,7 +92,7 @@ namespace GakumasLocal::Local {
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            std::ifstream file(filePath);
 | 
					            std::ifstream file(filePath);
 | 
				
			||||||
            if (!file.is_open()) {
 | 
					            if (!file.is_open()) {
 | 
				
			||||||
                Log::ErrorFmt("Load %s failed.\n", filePath.c_str());
 | 
					                Log::ErrorFmt("Load %s failed.\n", filePath.string().c_str());
 | 
				
			||||||
                return;
 | 
					                return;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            std::string fileContent((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
 | 
					            std::string fileContent((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
 | 
				
			||||||
| 
						 | 
					@ -112,7 +114,7 @@ namespace GakumasLocal::Local {
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        catch (std::exception& e) {
 | 
					        catch (std::exception& e) {
 | 
				
			||||||
            Log::ErrorFmt("Load %s failed: %s\n", filePath.c_str(), e.what());
 | 
					            Log::ErrorFmt("Load %s failed: %s\n", filePath.string().c_str(), e.what());
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -249,7 +251,7 @@ namespace GakumasLocal::Local {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    bool GetSplitTagsTranslation(const std::string& origText, std::string* newText, std::vector<std::string>& unTransResultRet) {
 | 
					    bool GetSplitTagsTranslation(const std::string& origText, std::string* newText, std::vector<std::string>& unTransResultRet) {
 | 
				
			||||||
        if (!origText.contains(L'<')) return false;
 | 
					        if (!origText.contains('<')) return false;
 | 
				
			||||||
        const auto splitResult = SplitByTags(origText);
 | 
					        const auto splitResult = SplitByTags(origText);
 | 
				
			||||||
        if (splitResult.empty()) return false;
 | 
					        if (splitResult.empty()) return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -289,10 +291,18 @@ namespace GakumasLocal::Local {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        std::u16string currentWaitingReplaceText;
 | 
					        std::u16string currentWaitingReplaceText;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef GKMS_WINDOWS
 | 
				
			||||||
 | 
					#define checkCurrentWaitingReplaceTextAndClear() \
 | 
				
			||||||
 | 
					    if (!currentWaitingReplaceText.empty()) { \
 | 
				
			||||||
 | 
					        auto trimmed = trim(Misc::ToUTF8(currentWaitingReplaceText)); \
 | 
				
			||||||
 | 
					        waitingReplaceTexts.push_back(trimmed); \
 | 
				
			||||||
 | 
					        currentWaitingReplaceText.clear(); }
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
#define checkCurrentWaitingReplaceTextAndClear() \
 | 
					#define checkCurrentWaitingReplaceTextAndClear() \
 | 
				
			||||||
    if (!currentWaitingReplaceText.empty()) { \
 | 
					    if (!currentWaitingReplaceText.empty()) { \
 | 
				
			||||||
        waitingReplaceTexts.push_back(Misc::ToUTF8(currentWaitingReplaceText)); \
 | 
					        waitingReplaceTexts.push_back(Misc::ToUTF8(currentWaitingReplaceText)); \
 | 
				
			||||||
        currentWaitingReplaceText.clear(); }
 | 
					        currentWaitingReplaceText.clear(); }
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        for (char16_t currChar : origText) {
 | 
					        for (char16_t currChar : origText) {
 | 
				
			||||||
            if (currChar == u'<') {
 | 
					            if (currChar == u'<') {
 | 
				
			||||||
| 
						 | 
					@ -447,7 +457,7 @@ namespace GakumasLocal::Local {
 | 
				
			||||||
            const auto targetFilePath = basePath / "local-files" / "resource" / name;
 | 
					            const auto targetFilePath = basePath / "local-files" / "resource" / name;
 | 
				
			||||||
            // Log::DebugFmt("GetResourceText: %s", targetFilePath.c_str());
 | 
					            // Log::DebugFmt("GetResourceText: %s", targetFilePath.c_str());
 | 
				
			||||||
            if (exists(targetFilePath)) {
 | 
					            if (exists(targetFilePath)) {
 | 
				
			||||||
                auto readStr = readFileToString(targetFilePath);
 | 
					                auto readStr = readFileToString(targetFilePath.string());
 | 
				
			||||||
                *ret = readStr;
 | 
					                *ret = readStr;
 | 
				
			||||||
                return true;
 | 
					                return true;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
| 
						 | 
					@ -521,6 +531,17 @@ namespace GakumasLocal::Local {
 | 
				
			||||||
            return false;
 | 
					            return false;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 匹配升级卡名
 | 
				
			||||||
 | 
					        if (auto plusPos = origText.find_last_not_of('+'); plusPos != std::string::npos) {
 | 
				
			||||||
 | 
					            const auto noPlusText = origText.substr(0, plusPos + 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (const auto iter = genericText.find(noPlusText); iter != genericText.end()) {
 | 
				
			||||||
 | 
					                size_t plusCount = origText.length() - (plusPos + 1);
 | 
				
			||||||
 | 
					                *newStr = iter->second + std::string(plusCount, '+');
 | 
				
			||||||
 | 
					                return true;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // fmt 文本
 | 
					        // fmt 文本
 | 
				
			||||||
        auto fmtText = StringParser::ParseItems::parse(origText, false);
 | 
					        auto fmtText = StringParser::ParseItems::parse(origText, false);
 | 
				
			||||||
        if (fmtText.isValid) {
 | 
					        if (fmtText.isValid) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3,8 +3,11 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <string>
 | 
					#include <string>
 | 
				
			||||||
#include <filesystem>
 | 
					#include <filesystem>
 | 
				
			||||||
 | 
					#include <unordered_set>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace GakumasLocal::Local {
 | 
					namespace GakumasLocal::Local {
 | 
				
			||||||
 | 
					    extern std::unordered_set<std::string> translatedText;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    std::filesystem::path GetBasePath();
 | 
					    std::filesystem::path GetBasePath();
 | 
				
			||||||
    void LoadData();
 | 
					    void LoadData();
 | 
				
			||||||
    bool GetI18n(const std::string& key, std::string* ret);
 | 
					    bool GetI18n(const std::string& key, std::string* ret);
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,14 +1,19 @@
 | 
				
			||||||
#include "Log.h"
 | 
					#include "Log.h"
 | 
				
			||||||
#include <android/log.h>
 | 
					#include "Misc.hpp"
 | 
				
			||||||
#include <Misc.hpp>
 | 
					 | 
				
			||||||
#include <sstream>
 | 
					#include <sstream>
 | 
				
			||||||
#include <string>
 | 
					#include <string>
 | 
				
			||||||
#include <thread>
 | 
					#include <thread>
 | 
				
			||||||
#include <queue>
 | 
					#include <queue>
 | 
				
			||||||
 | 
					#include <cstdarg>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef GKMS_WINDOWS
 | 
				
			||||||
 | 
					    #include <android/log.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    extern JavaVM* g_javaVM;
 | 
				
			||||||
 | 
					    extern jclass g_gakumasHookMainClass;
 | 
				
			||||||
 | 
					    extern jmethodID showToastMethodId;
 | 
				
			||||||
 | 
					#endif // GKMS_WINDOWS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
extern JavaVM* g_javaVM;
 | 
					 | 
				
			||||||
extern jclass g_gakumasHookMainClass;
 | 
					 | 
				
			||||||
extern jmethodID showToastMethodId;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define GetParamStringResult(name)\
 | 
					#define GetParamStringResult(name)\
 | 
				
			||||||
    va_list args;\
 | 
					    va_list args;\
 | 
				
			||||||
| 
						 | 
					@ -75,6 +80,7 @@ namespace GakumasLocal::Log {
 | 
				
			||||||
        __android_log_write(prio, "GakumasLog", result.c_str());
 | 
					        __android_log_write(prio, "GakumasLog", result.c_str());
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /*
 | 
				
			||||||
    void ShowToastJNI(const char* text) {
 | 
					    void ShowToastJNI(const char* text) {
 | 
				
			||||||
        DebugFmt("Toast: %s", text);
 | 
					        DebugFmt("Toast: %s", text);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -99,15 +105,19 @@ namespace GakumasLocal::Log {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            g_javaVM->DetachCurrentThread();
 | 
					            g_javaVM->DetachCurrentThread();
 | 
				
			||||||
        }).detach();
 | 
					        }).detach();
 | 
				
			||||||
    }
 | 
					    }*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void ShowToast(const std::string& text) {
 | 
					    void ShowToast(const std::string& text) {
 | 
				
			||||||
 | 
					#ifndef GKMS_WINDOWS
 | 
				
			||||||
        showingToasts.push(text);
 | 
					        showingToasts.push(text);
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
							InfoFmt("Toast: %s", text.c_str());
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void ShowToast(const char* text) {
 | 
					    void ShowToast(const char* text) {
 | 
				
			||||||
        DebugFmt("Toast: %s", text);
 | 
					        // DebugFmt("Toast: %s", text);
 | 
				
			||||||
        return ShowToast(std::string(text));
 | 
					        return ShowToast(std::string(text));
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -125,6 +135,7 @@ namespace GakumasLocal::Log {
 | 
				
			||||||
        return ret;
 | 
					        return ret;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef GKMS_WINDOWS
 | 
				
			||||||
    void ToastLoop(JNIEnv *env, jclass clazz) {
 | 
					    void ToastLoop(JNIEnv *env, jclass clazz) {
 | 
				
			||||||
        const auto toastString = GetQueuedToast();
 | 
					        const auto toastString = GetQueuedToast();
 | 
				
			||||||
        if (toastString.empty()) return;
 | 
					        if (toastString.empty()) return;
 | 
				
			||||||
| 
						 | 
					@ -140,4 +151,6 @@ namespace GakumasLocal::Log {
 | 
				
			||||||
            _showToastMethodId = env->GetStaticMethodID(clazz, "showToast", "(Ljava/lang/String;)V");
 | 
					            _showToastMethodId = env->GetStaticMethodID(clazz, "showToast", "(Ljava/lang/String;)V");
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,8 +1,14 @@
 | 
				
			||||||
#ifndef GAKUMAS_LOCALIFY_LOG_H
 | 
					#ifndef GAKUMAS_LOCALIFY_LOG_H
 | 
				
			||||||
#define GAKUMAS_LOCALIFY_LOG_H
 | 
					#define GAKUMAS_LOCALIFY_LOG_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "../platformDefine.hpp"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <string>
 | 
					#include <string>
 | 
				
			||||||
#include <jni.h>
 | 
					
 | 
				
			||||||
 | 
					#ifndef GKMS_WINDOWS
 | 
				
			||||||
 | 
					    #include <jni.h>
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace GakumasLocal::Log {
 | 
					namespace GakumasLocal::Log {
 | 
				
			||||||
    std::string StringFormat(const char* fmt, ...);
 | 
					    std::string StringFormat(const char* fmt, ...);
 | 
				
			||||||
| 
						 | 
					@ -18,7 +24,9 @@ namespace GakumasLocal::Log {
 | 
				
			||||||
    void ShowToast(const char* text);
 | 
					    void ShowToast(const char* text);
 | 
				
			||||||
    void ShowToastFmt(const char* fmt, ...);
 | 
					    void ShowToastFmt(const char* fmt, ...);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef GKMS_WINDOWS
 | 
				
			||||||
    void ToastLoop(JNIEnv *env, jclass clazz);
 | 
					    void ToastLoop(JNIEnv *env, jclass clazz);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif //GAKUMAS_LOCALIFY_LOG_H
 | 
					#endif //GAKUMAS_LOCALIFY_LOG_H
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,809 @@
 | 
				
			||||||
 | 
					#include "MasterLocal.h"
 | 
				
			||||||
 | 
					#include "Local.h"
 | 
				
			||||||
 | 
					#include "Il2cppUtils.hpp"
 | 
				
			||||||
 | 
					#include "config/Config.hpp"
 | 
				
			||||||
 | 
					#include <filesystem>
 | 
				
			||||||
 | 
					#include <fstream>
 | 
				
			||||||
 | 
					#include <sstream>
 | 
				
			||||||
 | 
					#include <unordered_map>
 | 
				
			||||||
 | 
					#include <unordered_set>
 | 
				
			||||||
 | 
					#include <vector>
 | 
				
			||||||
 | 
					#include <regex>
 | 
				
			||||||
 | 
					#include <nlohmann/json.hpp>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace GakumasLocal::MasterLocal {
 | 
				
			||||||
 | 
					    using Il2cppString = UnityResolve::UnityType::String;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static std::unordered_map<std::string, Il2cppUtils::MethodInfo*> fieldSetCache;
 | 
				
			||||||
 | 
					    static std::unordered_map<std::string, Il2cppUtils::MethodInfo*> fieldGetCache;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    enum class JsonValueType {
 | 
				
			||||||
 | 
					        JVT_String,
 | 
				
			||||||
 | 
					        JVT_Int,
 | 
				
			||||||
 | 
					        JVT_Object,
 | 
				
			||||||
 | 
					        JVT_ArrayObject,
 | 
				
			||||||
 | 
					        JVT_ArrayString,
 | 
				
			||||||
 | 
					        JVT_Unsupported,
 | 
				
			||||||
 | 
					        JVT_NeedMore_EmptyArray
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    struct ItemRule {
 | 
				
			||||||
 | 
					        std::vector<std::string> mainPrimaryKey;
 | 
				
			||||||
 | 
					        std::map<std::string, std::vector<std::string>> subPrimaryKey;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        std::vector<std::string> mainLocalKey;
 | 
				
			||||||
 | 
					        std::map<std::string, std::vector<std::string>> subLocalKey;
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    struct TableLocalData {
 | 
				
			||||||
 | 
					        ItemRule itemRule;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        std::unordered_map<std::string, JsonValueType> mainKeyType;
 | 
				
			||||||
 | 
					        std::unordered_map<std::string, std::unordered_map<std::string, JsonValueType>> subKeyType;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        std::unordered_map<std::string, std::string> transData;
 | 
				
			||||||
 | 
					        std::unordered_map<std::string, std::vector<std::string>> transStrListData;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [[nodiscard]] JsonValueType GetMainKeyType(const std::string& mainKey) const {
 | 
				
			||||||
 | 
					            if (auto it = mainKeyType.find(mainKey); it != mainKeyType.end()) {
 | 
				
			||||||
 | 
					                return it->second;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return JsonValueType::JVT_Unsupported;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        [[nodiscard]] JsonValueType GetSubKeyType(const std::string& parentKey, const std::string& subKey) const {
 | 
				
			||||||
 | 
					            if (auto it = subKeyType.find(parentKey); it != subKeyType.end()) {
 | 
				
			||||||
 | 
					                if (auto subIt = it->second.find(subKey); subIt != it->second.end()) {
 | 
				
			||||||
 | 
					                    return subIt->second;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return JsonValueType::JVT_Unsupported;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    static std::unordered_map<std::string, TableLocalData> masterLocalData;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class FieldController {
 | 
				
			||||||
 | 
					        void* self;
 | 
				
			||||||
 | 
					        std::string self_klass_name;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        static std::string capitalizeFirstLetter(const std::string& input) {
 | 
				
			||||||
 | 
					            if (input.empty()) return input;
 | 
				
			||||||
 | 
					            std::string result = input;
 | 
				
			||||||
 | 
					            result[0] = static_cast<char>(std::toupper(result[0]));
 | 
				
			||||||
 | 
					            return result;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Il2cppUtils::MethodInfo* GetGetSetMethodFromCache(const std::string& fieldName, int argsCount,
 | 
				
			||||||
 | 
					                                                          std::unordered_map<std::string, Il2cppUtils::MethodInfo*>& fromCache, const std::string& prefix = "set_") {
 | 
				
			||||||
 | 
					            const std::string methodName = prefix + capitalizeFirstLetter(fieldName);
 | 
				
			||||||
 | 
					            const std::string searchName = self_klass_name + "." + methodName;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (auto it = fromCache.find(searchName); it != fromCache.end()) {
 | 
				
			||||||
 | 
					                return it->second;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            auto set_mtd = Il2cppUtils::il2cpp_class_get_method_from_name(
 | 
				
			||||||
 | 
					                    self_klass,
 | 
				
			||||||
 | 
					                    methodName.c_str(),
 | 
				
			||||||
 | 
					                    argsCount
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					            fromCache.emplace(searchName, set_mtd);
 | 
				
			||||||
 | 
					            return set_mtd;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public:
 | 
				
			||||||
 | 
					        Il2cppUtils::Il2CppClassHead* self_klass;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        explicit FieldController(void* from) {
 | 
				
			||||||
 | 
					            if (!from) {
 | 
				
			||||||
 | 
					                self = nullptr;
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            self = from;
 | 
				
			||||||
 | 
					            self_klass = Il2cppUtils::get_class_from_instance(self);
 | 
				
			||||||
 | 
					            if (self_klass) {
 | 
				
			||||||
 | 
					                self_klass_name = self_klass->name;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        template<typename T>
 | 
				
			||||||
 | 
					        T ReadField(const std::string& fieldName) {
 | 
				
			||||||
 | 
					            if (!self) return T();
 | 
				
			||||||
 | 
					            auto get_mtd = GetGetSetMethodFromCache(fieldName, 0, fieldGetCache, "get_");
 | 
				
			||||||
 | 
					            if (get_mtd) {
 | 
				
			||||||
 | 
					                return reinterpret_cast<T (*)(void*, void*)>(get_mtd->methodPointer)(self, get_mtd);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            auto field = UnityResolve::Invoke<Il2cppUtils::FieldInfo*>(
 | 
				
			||||||
 | 
					                    "il2cpp_class_get_field_from_name",
 | 
				
			||||||
 | 
					                    self_klass,
 | 
				
			||||||
 | 
					                    (fieldName + '_').c_str()
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					            if (!field) {
 | 
				
			||||||
 | 
					                return T();
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return Il2cppUtils::ClassGetFieldValue<T>(self, field);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        template<typename T>
 | 
				
			||||||
 | 
					        void SetField(const std::string& fieldName, T value) {
 | 
				
			||||||
 | 
					            if (!self) return;
 | 
				
			||||||
 | 
					            auto set_mtd = GetGetSetMethodFromCache(fieldName, 1, fieldSetCache, "set_");
 | 
				
			||||||
 | 
					            if (set_mtd) {
 | 
				
			||||||
 | 
					                reinterpret_cast<void (*)(void*, T, void*)>(
 | 
				
			||||||
 | 
					                        set_mtd->methodPointer
 | 
				
			||||||
 | 
					                )(self, value, set_mtd);
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            auto field = UnityResolve::Invoke<Il2cppUtils::FieldInfo*>(
 | 
				
			||||||
 | 
					                    "il2cpp_class_get_field_from_name",
 | 
				
			||||||
 | 
					                    self_klass,
 | 
				
			||||||
 | 
					                    (fieldName + '_').c_str()
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					            if (!field) return;
 | 
				
			||||||
 | 
					            Il2cppUtils::ClassSetFieldValue(self, field, value);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        int ReadIntField(const std::string& fieldName) {
 | 
				
			||||||
 | 
					            return ReadField<int>(fieldName);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        Il2cppString* ReadStringField(const std::string& fieldName) {
 | 
				
			||||||
 | 
					            if (!self) return nullptr;
 | 
				
			||||||
 | 
					            auto get_mtd = GetGetSetMethodFromCache(fieldName, 0, fieldGetCache, "get_");
 | 
				
			||||||
 | 
					            if (!get_mtd) {
 | 
				
			||||||
 | 
					                return ReadField<Il2cppString*>(fieldName);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            auto returnClass = UnityResolve::Invoke<Il2cppUtils::Il2CppClassHead*>(
 | 
				
			||||||
 | 
					                    "il2cpp_class_from_type",
 | 
				
			||||||
 | 
					                    UnityResolve::Invoke<void*>("il2cpp_method_get_return_type", get_mtd)
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					            if (!returnClass) {
 | 
				
			||||||
 | 
					                return reinterpret_cast<Il2cppString* (*)(void*, void*)>(
 | 
				
			||||||
 | 
					                        get_mtd->methodPointer
 | 
				
			||||||
 | 
					                )(self, get_mtd);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            auto isEnum = UnityResolve::Invoke<bool>("il2cpp_class_is_enum", returnClass);
 | 
				
			||||||
 | 
					            if (!isEnum) {
 | 
				
			||||||
 | 
					                return reinterpret_cast<Il2cppString* (*)(void*, void*)>(
 | 
				
			||||||
 | 
					                        get_mtd->methodPointer
 | 
				
			||||||
 | 
					                )(self, get_mtd);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            auto enumMap = Il2cppUtils::EnumToValueMap(returnClass, true);
 | 
				
			||||||
 | 
					            auto enumValue = reinterpret_cast<int (*)(void*, void*)>(
 | 
				
			||||||
 | 
					                    get_mtd->methodPointer
 | 
				
			||||||
 | 
					            )(self, get_mtd);
 | 
				
			||||||
 | 
					            if (auto it = enumMap.find(enumValue); it != enumMap.end()) {
 | 
				
			||||||
 | 
					                return Il2cppString::New(it->second);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return nullptr;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        void SetStringField(const std::string& fieldName, const std::string& value) {
 | 
				
			||||||
 | 
					            if (!self) return;
 | 
				
			||||||
 | 
					            auto newString = Il2cppString::New(value);
 | 
				
			||||||
 | 
					            SetField(fieldName, newString);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        void SetStringListField(const std::string& fieldName, const std::vector<std::string>& data) {
 | 
				
			||||||
 | 
					            if (!self) return;
 | 
				
			||||||
 | 
					            static auto List_String_klass = Il2cppUtils::get_system_class_from_reflection_type_str(
 | 
				
			||||||
 | 
					                    "System.Collections.Generic.List`1[System.String]"
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					            static auto List_String_ctor_mtd = Il2cppUtils::il2cpp_class_get_method_from_name(
 | 
				
			||||||
 | 
					                    List_String_klass, ".ctor", 0
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					            static auto List_String_ctor = reinterpret_cast<void (*)(void*, void*)>(
 | 
				
			||||||
 | 
					                    List_String_ctor_mtd->methodPointer
 | 
				
			||||||
 | 
					            );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            auto newList = UnityResolve::Invoke<void*>("il2cpp_object_new", List_String_klass);
 | 
				
			||||||
 | 
					            List_String_ctor(newList, List_String_ctor_mtd);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            Il2cppUtils::Tools::CSListEditor<Il2cppString*> newListEditor(newList);
 | 
				
			||||||
 | 
					            for (auto& s : data) {
 | 
				
			||||||
 | 
					                newListEditor.Add(Il2cppString::New(s));
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            SetField(fieldName, newList);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        void* ReadObjectField(const std::string& fieldName) {
 | 
				
			||||||
 | 
					            if (!self) return nullptr;
 | 
				
			||||||
 | 
					            return ReadField<void*>(fieldName);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        void* ReadObjectListField(const std::string& fieldName) {
 | 
				
			||||||
 | 
					            if (!self) return nullptr;
 | 
				
			||||||
 | 
					            return ReadField<void*>(fieldName);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        static FieldController CreateSubFieldController(void* subObj) {
 | 
				
			||||||
 | 
					            return FieldController(subObj);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        FieldController CreateSubFieldController(const std::string& subObjName) {
 | 
				
			||||||
 | 
					            auto field = ReadObjectField(subObjName);
 | 
				
			||||||
 | 
					            return FieldController(field);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    JsonValueType checkJsonValueType(const nlohmann::json& j) {
 | 
				
			||||||
 | 
					        if (j.is_string())  return JsonValueType::JVT_String;
 | 
				
			||||||
 | 
					        if (j.is_number_integer()) return JsonValueType::JVT_Int;
 | 
				
			||||||
 | 
					        if (j.is_object())  return JsonValueType::JVT_Object;
 | 
				
			||||||
 | 
					        if (j.is_array()) {
 | 
				
			||||||
 | 
					            if (!j.empty()) {
 | 
				
			||||||
 | 
					                if (j.begin()->is_object()) {
 | 
				
			||||||
 | 
					                    return JsonValueType::JVT_ArrayObject;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                else if (j.begin()->is_string()) {
 | 
				
			||||||
 | 
					                    return JsonValueType::JVT_ArrayString;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            else {
 | 
				
			||||||
 | 
					                return JsonValueType::JVT_NeedMore_EmptyArray;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return JsonValueType::JVT_Unsupported;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::string ReadFileToString(const std::filesystem::path& path) {
 | 
				
			||||||
 | 
					        std::ifstream ifs(path, std::ios::binary);
 | 
				
			||||||
 | 
					        if (!ifs) return {};
 | 
				
			||||||
 | 
					        std::stringstream buffer;
 | 
				
			||||||
 | 
					        buffer << ifs.rdbuf();
 | 
				
			||||||
 | 
					        return buffer.str();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    namespace Load {
 | 
				
			||||||
 | 
					        std::vector<std::string> ArrayStrJsonToVec(nlohmann::json& data) {
 | 
				
			||||||
 | 
					            return data;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        bool BuildObjectItemLocalRule(nlohmann::json& transData, ItemRule& itemRule) {
 | 
				
			||||||
 | 
					            // transData: data[]
 | 
				
			||||||
 | 
					            bool hasSuccess = false;
 | 
				
			||||||
 | 
					            for (auto& data : transData) {
 | 
				
			||||||
 | 
					                // data: {"id": "xxx", "produceDescriptions": [{"k", "v"}], "descriptions": {"k2", "v2"}}
 | 
				
			||||||
 | 
					                if (!data.is_object()) continue;
 | 
				
			||||||
 | 
					                for (auto& [key, value] : data.items()) {
 | 
				
			||||||
 | 
					                    // key: "id", value: "xxx"
 | 
				
			||||||
 | 
					                    // key: "produceDescriptions", value: [{"k", "v"}]
 | 
				
			||||||
 | 
					                    const auto valueType = checkJsonValueType(value);
 | 
				
			||||||
 | 
					                    switch (valueType) {
 | 
				
			||||||
 | 
					                        case JsonValueType::JVT_String:
 | 
				
			||||||
 | 
					                            // case JsonValueType::JVT_Int:
 | 
				
			||||||
 | 
					                        case JsonValueType::JVT_ArrayString: {
 | 
				
			||||||
 | 
					                            if (std::find(itemRule.mainPrimaryKey.begin(), itemRule.mainPrimaryKey.end(), key) != itemRule.mainPrimaryKey.end()) {
 | 
				
			||||||
 | 
					                                continue;
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                            if (auto it = std::find(itemRule.mainLocalKey.begin(), itemRule.mainLocalKey.end(), key); it == itemRule.mainLocalKey.end()) {
 | 
				
			||||||
 | 
					                                itemRule.mainLocalKey.emplace_back(key);
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                            hasSuccess = true;
 | 
				
			||||||
 | 
					                        } break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        case JsonValueType::JVT_Object: {
 | 
				
			||||||
 | 
					                            ItemRule currRule{ .mainPrimaryKey = itemRule.subPrimaryKey[key] };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                            auto vJson = nlohmann::json::array();
 | 
				
			||||||
 | 
					                            vJson.push_back(value);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                            if (BuildObjectItemLocalRule(vJson, currRule)) {
 | 
				
			||||||
 | 
					                                itemRule.subLocalKey.emplace(key, currRule.mainLocalKey);
 | 
				
			||||||
 | 
					                                hasSuccess = true;
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        } break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        case JsonValueType::JVT_ArrayObject: {
 | 
				
			||||||
 | 
					                            for (auto& obj : value) {
 | 
				
			||||||
 | 
					                                // obj: {"k", "v"}
 | 
				
			||||||
 | 
					                                ItemRule currRule{ .mainPrimaryKey = itemRule.subPrimaryKey[key] };
 | 
				
			||||||
 | 
					                                if (BuildObjectItemLocalRule(value, currRule)) {
 | 
				
			||||||
 | 
					                                    itemRule.subLocalKey.emplace(key, currRule.mainLocalKey);
 | 
				
			||||||
 | 
					                                    hasSuccess = true;
 | 
				
			||||||
 | 
					                                    break;
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        } break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        case JsonValueType::JVT_Unsupported:
 | 
				
			||||||
 | 
					                        default:
 | 
				
			||||||
 | 
					                            break;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                if (hasSuccess) break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return hasSuccess;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        bool GetItemRule(nlohmann::json& fullData, ItemRule& itemRule) {
 | 
				
			||||||
 | 
					            auto& primaryKeys = fullData["rules"]["primaryKeys"];
 | 
				
			||||||
 | 
					            auto& transData = fullData["data"];
 | 
				
			||||||
 | 
					            if (!primaryKeys.is_array()) return false;
 | 
				
			||||||
 | 
					            if (!transData.is_array()) return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // 首先构造 mainPrimaryKey 规则
 | 
				
			||||||
 | 
					            for (auto& pkItem : primaryKeys) {
 | 
				
			||||||
 | 
					                if (!pkItem.is_string()) {
 | 
				
			||||||
 | 
					                    return false;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                std::string pk = pkItem;
 | 
				
			||||||
 | 
					                auto dotCount = std::ranges::count(pk, '.');
 | 
				
			||||||
 | 
					                if (dotCount == 0) {
 | 
				
			||||||
 | 
					                    itemRule.mainPrimaryKey.emplace_back(pk);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                else if (dotCount == 1) {
 | 
				
			||||||
 | 
					                    auto [parentKey, subKey] = Misc::StringFormat::split_once(pk, ".");
 | 
				
			||||||
 | 
					                    if (itemRule.subPrimaryKey.contains(parentKey)) {
 | 
				
			||||||
 | 
					                        itemRule.subPrimaryKey[parentKey].emplace_back(subKey);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    else {
 | 
				
			||||||
 | 
					                        itemRule.subPrimaryKey.emplace(parentKey, std::vector<std::string>{subKey});
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                else {
 | 
				
			||||||
 | 
					                    Log::ErrorFmt("Unsupported depth: %d", dotCount);
 | 
				
			||||||
 | 
					                    continue;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return BuildObjectItemLocalRule(transData, itemRule);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        std::string BuildBaseMainUniqueKey(nlohmann::json& data, TableLocalData& tableLocalData) {
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					                std::string mainBaseUniqueKey;
 | 
				
			||||||
 | 
					                for (auto& mainPrimaryKey : tableLocalData.itemRule.mainPrimaryKey) {
 | 
				
			||||||
 | 
					                    if (!data.contains(mainPrimaryKey)) {
 | 
				
			||||||
 | 
					                        return "";
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    auto& value = data[mainPrimaryKey];
 | 
				
			||||||
 | 
					                    if (value.is_number_integer()) {
 | 
				
			||||||
 | 
					                        mainBaseUniqueKey.append(std::to_string(value.get<int>()));
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    else {
 | 
				
			||||||
 | 
					                        mainBaseUniqueKey.append(value);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    mainBaseUniqueKey.push_back('|');
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                return mainBaseUniqueKey;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            catch (std::exception& e) {
 | 
				
			||||||
 | 
					                Log::ErrorFmt("LoadData - BuildBaseMainUniqueKey failed: %s", e.what());
 | 
				
			||||||
 | 
					                throw e;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        void BuildBaseObjectSubUniqueKey(nlohmann::json& value, JsonValueType valueType, std::string& currLocalKey) {
 | 
				
			||||||
 | 
					            switch (valueType) {
 | 
				
			||||||
 | 
					                case JsonValueType::JVT_String:
 | 
				
			||||||
 | 
					                    currLocalKey.append(value.get<std::string>());  // p_card-00-acc-0_002|0|produceDescriptions|ProduceDescriptionType_Exam|
 | 
				
			||||||
 | 
					                    currLocalKey.push_back('|');
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					                case JsonValueType::JVT_Int:
 | 
				
			||||||
 | 
					                    currLocalKey.append(std::to_string(value.get<int>()));
 | 
				
			||||||
 | 
					                    currLocalKey.push_back('|');
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					                default:
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        bool BuildUniqueKeyValue(nlohmann::json& data, TableLocalData& tableLocalData) {
 | 
				
			||||||
 | 
					            // 首先处理 main 部分
 | 
				
			||||||
 | 
					            const std::string mainBaseUniqueKey = BuildBaseMainUniqueKey(data, tableLocalData);  // p_card-00-acc-0_002|0|
 | 
				
			||||||
 | 
					            if (mainBaseUniqueKey.empty()) return false;
 | 
				
			||||||
 | 
					            for (auto& mainLocalKey : tableLocalData.itemRule.mainLocalKey) {
 | 
				
			||||||
 | 
					                if (!data.contains(mainLocalKey)) continue;
 | 
				
			||||||
 | 
					                auto& currLocalValue = data[mainLocalKey];
 | 
				
			||||||
 | 
					                auto currUniqueKey = mainBaseUniqueKey + mainLocalKey;  // p_card-00-acc-0_002|0|name
 | 
				
			||||||
 | 
					                if (tableLocalData.GetMainKeyType(mainLocalKey) == JsonValueType::JVT_ArrayString) {
 | 
				
			||||||
 | 
					                    tableLocalData.transStrListData.emplace(currUniqueKey, ArrayStrJsonToVec(currLocalValue));
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                else {
 | 
				
			||||||
 | 
					                    tableLocalData.transData.emplace(currUniqueKey, currLocalValue);
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            // 然后处理 sub 部分
 | 
				
			||||||
 | 
					            /*
 | 
				
			||||||
 | 
					            for (const auto& [subPrimaryParentKey, subPrimarySubKeys] : tableLocalData.itemRule.subPrimaryKey) {
 | 
				
			||||||
 | 
					                if (!data.contains(subPrimaryParentKey)) continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                const std::string subBaseUniqueKey = mainBaseUniqueKey + subPrimaryParentKey + '|';  // p_card-00-acc-0_002|0|produceDescriptions|
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                auto subValueType = checkJsonValueType(data[subPrimaryParentKey]);
 | 
				
			||||||
 | 
					                std::string currLocalKey = subBaseUniqueKey;  // p_card-00-acc-0_002|0|produceDescriptions|
 | 
				
			||||||
 | 
					                switch (subValueType) {
 | 
				
			||||||
 | 
					                    case JsonValueType::JVT_Object: {
 | 
				
			||||||
 | 
					                        for (auto& subPrimarySubKey : subPrimarySubKeys) {
 | 
				
			||||||
 | 
					                            if (!data[subPrimaryParentKey].contains(subPrimarySubKey)) continue;
 | 
				
			||||||
 | 
					                            auto& value = data[subPrimaryParentKey][subPrimarySubKey];
 | 
				
			||||||
 | 
					                            auto valueType = tableLocalData.GetSubKeyType(subPrimaryParentKey, subPrimarySubKey);
 | 
				
			||||||
 | 
					                            BuildBaseObjectSubUniqueKey(value, valueType, currLocalKey);  // p_card-00-acc-0_002|0|produceDescriptions|ProduceDescriptionType_Exam|
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    } break;
 | 
				
			||||||
 | 
					                    case JsonValueType::JVT_ArrayObject: {
 | 
				
			||||||
 | 
					                        int currIndex = 0;
 | 
				
			||||||
 | 
					                        for (auto& obj : data[subPrimaryParentKey]) {
 | 
				
			||||||
 | 
					                            for (auto& subPrimarySubKey : subPrimarySubKeys) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                            currIndex++;
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    } break;
 | 
				
			||||||
 | 
					                    default:
 | 
				
			||||||
 | 
					                        break;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            for (const auto& [subLocalParentKey, subLocalSubKeys] : tableLocalData.itemRule.subLocalKey) {
 | 
				
			||||||
 | 
					                if (!data.contains(subLocalParentKey)) continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                const std::string subBaseUniqueKey = mainBaseUniqueKey + subLocalParentKey + '|';  // p_card-00-acc-0_002|0|produceDescriptions|
 | 
				
			||||||
 | 
					                auto subValueType = checkJsonValueType(data[subLocalParentKey]);
 | 
				
			||||||
 | 
					                if (subValueType != JsonValueType::JVT_NeedMore_EmptyArray) {
 | 
				
			||||||
 | 
					                    tableLocalData.mainKeyType.emplace(subLocalParentKey, subValueType);  // 在这里插入 subParent 的类型
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                switch (subValueType) {
 | 
				
			||||||
 | 
					                    case JsonValueType::JVT_Object: {
 | 
				
			||||||
 | 
					                        for (auto& localSubKey : subLocalSubKeys) {
 | 
				
			||||||
 | 
					                            const std::string currLocalUniqueKey = subBaseUniqueKey + localSubKey;  // p_card-00-acc-0_002|0|produceDescriptions|text
 | 
				
			||||||
 | 
					                            if (tableLocalData.GetSubKeyType(subLocalParentKey, localSubKey) == JsonValueType::JVT_ArrayString) {
 | 
				
			||||||
 | 
					                                tableLocalData.transStrListData.emplace(currLocalUniqueKey, ArrayStrJsonToVec(data[subLocalParentKey][localSubKey]));
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                            else {
 | 
				
			||||||
 | 
					                                tableLocalData.transData.emplace(currLocalUniqueKey, data[subLocalParentKey][localSubKey]);
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    } break;
 | 
				
			||||||
 | 
					                    case JsonValueType::JVT_ArrayObject: {
 | 
				
			||||||
 | 
					                        int currIndex = 0;
 | 
				
			||||||
 | 
					                        for (auto& obj : data[subLocalParentKey]) {
 | 
				
			||||||
 | 
					                            for (auto& localSubKey : subLocalSubKeys) {
 | 
				
			||||||
 | 
					                                std::string currLocalUniqueKey = subBaseUniqueKey;  // p_card-00-acc-0_002|0|produceDescriptions|
 | 
				
			||||||
 | 
					                                currLocalUniqueKey.push_back('[');
 | 
				
			||||||
 | 
					                                currLocalUniqueKey.append(std::to_string(currIndex));
 | 
				
			||||||
 | 
					                                currLocalUniqueKey.append("]|");
 | 
				
			||||||
 | 
					                                currLocalUniqueKey.append(localSubKey);  // p_card-00-acc-0_002|0|produceDescriptions|[0]|text
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                                if (tableLocalData.GetSubKeyType(subLocalParentKey, localSubKey) == JsonValueType::JVT_ArrayString) {
 | 
				
			||||||
 | 
					                                    // if (obj[localSubKey].is_array()) {
 | 
				
			||||||
 | 
					                                    tableLocalData.transStrListData.emplace(currLocalUniqueKey, ArrayStrJsonToVec(obj[localSubKey]));
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                                else if (obj[localSubKey].is_string()) {
 | 
				
			||||||
 | 
					                                    tableLocalData.transData.emplace(currLocalUniqueKey, obj[localSubKey]);
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                            currIndex++;
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    } break;
 | 
				
			||||||
 | 
					                    default:
 | 
				
			||||||
 | 
					                        break;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return true;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define MainKeyTypeProcess() if (!data.contains(mainPrimaryKey)) { Log::ErrorFmt("mainPrimaryKey: %s not found", mainPrimaryKey.c_str()); isFailed = true; break; } \
 | 
				
			||||||
 | 
					    auto currType = checkJsonValueType(data[mainPrimaryKey]); \
 | 
				
			||||||
 | 
					    if (currType == JsonValueType::JVT_NeedMore_EmptyArray) goto NextLoop; \
 | 
				
			||||||
 | 
					    tableLocalData.mainKeyType[mainPrimaryKey] = currType
 | 
				
			||||||
 | 
					#define SubKeyTypeProcess() if (!data.contains(subKeyParent)) { Log::ErrorFmt("subKeyParent: %s not found", subKeyParent.c_str()); isFailed = true; break; } \
 | 
				
			||||||
 | 
					                for (auto& subKey : subKeys) { \
 | 
				
			||||||
 | 
					                    auto& subKeyValue = data[subKeyParent]; \
 | 
				
			||||||
 | 
					                    if (subKeyValue.is_object()) { \
 | 
				
			||||||
 | 
					                        if (!subKeyValue.contains(subKey)) { \
 | 
				
			||||||
 | 
					                            Log::ErrorFmt("subKey: %s not in subKeyParent: %s", subKey.c_str(), subKeyParent.c_str()); isFailed = true; break; \
 | 
				
			||||||
 | 
					                        }                                                                                                                                    \
 | 
				
			||||||
 | 
					                        auto currType = checkJsonValueType(subKeyValue[subKey]);                                                                             \
 | 
				
			||||||
 | 
					                        if (currType == JsonValueType::JVT_NeedMore_EmptyArray) goto NextLoop; \
 | 
				
			||||||
 | 
					                        tableLocalData.subKeyType[subKeyParent].emplace(subKey, currType); \
 | 
				
			||||||
 | 
					                    } \
 | 
				
			||||||
 | 
					                    else if (subKeyValue.is_array()) {                                                                                                       \
 | 
				
			||||||
 | 
					                        if (subKeyValue.empty()) goto NextLoop;                                                                                              \
 | 
				
			||||||
 | 
					                        for (auto& i : subKeyValue) { \
 | 
				
			||||||
 | 
					                            if (!i.is_object()) continue; \
 | 
				
			||||||
 | 
					                            if (!i.contains(subKey)) continue;  \
 | 
				
			||||||
 | 
					                            auto currType = checkJsonValueType(i[subKey]); \
 | 
				
			||||||
 | 
					                            if (currType == JsonValueType::JVT_NeedMore_EmptyArray) goto NextLoop; \
 | 
				
			||||||
 | 
					                            tableLocalData.subKeyType[subKeyParent].emplace(subKey, currType); \
 | 
				
			||||||
 | 
					                            break; \
 | 
				
			||||||
 | 
					                        } \
 | 
				
			||||||
 | 
					                    }                                                                                                                                        \
 | 
				
			||||||
 | 
					                    else {                                                                                                                                   \
 | 
				
			||||||
 | 
					                        goto NextLoop;\
 | 
				
			||||||
 | 
					                    } \
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        bool GetTableLocalData(nlohmann::json& fullData, TableLocalData& tableLocalData) {
 | 
				
			||||||
 | 
					            bool isFailed = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // 首先 Build mainKeyType 和 subKeyType
 | 
				
			||||||
 | 
					            for (auto& data : fullData["data"]) {
 | 
				
			||||||
 | 
					                if (!data.is_object()) continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                for (auto& mainPrimaryKey : tableLocalData.itemRule.mainPrimaryKey) {
 | 
				
			||||||
 | 
					                    MainKeyTypeProcess();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                for (auto& mainPrimaryKey : tableLocalData.itemRule.mainLocalKey) {
 | 
				
			||||||
 | 
					                    MainKeyTypeProcess();
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                for (const auto& [subKeyParent, subKeys] : tableLocalData.itemRule.subPrimaryKey) {
 | 
				
			||||||
 | 
					                    SubKeyTypeProcess()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    if (isFailed) break;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                for (const auto& [subKeyParent, subKeys] : tableLocalData.itemRule.subLocalKey) {
 | 
				
			||||||
 | 
					                    SubKeyTypeProcess()
 | 
				
			||||||
 | 
					                    if (isFailed) break;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                if (!isFailed) break;
 | 
				
			||||||
 | 
					            NextLoop:
 | 
				
			||||||
 | 
					                ;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (isFailed) return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            bool hasSuccess = false;
 | 
				
			||||||
 | 
					            // 然后构造 transData
 | 
				
			||||||
 | 
					            for (auto& data : fullData["data"]) {
 | 
				
			||||||
 | 
					                if (!data.is_object()) continue;
 | 
				
			||||||
 | 
					                if (BuildUniqueKeyValue(data, tableLocalData)) {
 | 
				
			||||||
 | 
					                    hasSuccess = true;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (!hasSuccess) {
 | 
				
			||||||
 | 
					                Log::ErrorFmt("BuildUniqueKeyValue failed.");
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return hasSuccess;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        void LoadData() {
 | 
				
			||||||
 | 
					            masterLocalData.clear();
 | 
				
			||||||
 | 
					            static auto masterDir = Local::GetBasePath() / "local-files" / "masterTrans";
 | 
				
			||||||
 | 
					            if (!std::filesystem::is_directory(masterDir)) {
 | 
				
			||||||
 | 
					                Log::ErrorFmt("LoadData: not found: %s", masterDir.string().c_str());
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            bool isFirstIteration = true;
 | 
				
			||||||
 | 
					            for (auto& p : std::filesystem::directory_iterator(masterDir)) {
 | 
				
			||||||
 | 
					                if (isFirstIteration) {
 | 
				
			||||||
 | 
					                    auto totalFileCount = std::distance(
 | 
				
			||||||
 | 
					                            std::filesystem::directory_iterator(masterDir),
 | 
				
			||||||
 | 
					                            std::filesystem::directory_iterator{}
 | 
				
			||||||
 | 
					                    );
 | 
				
			||||||
 | 
					                    UnityResolveProgress::classProgress.total = totalFileCount <= 0 ? 1 : totalFileCount;
 | 
				
			||||||
 | 
					                    isFirstIteration = false;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					                UnityResolveProgress::classProgress.current++;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                if (!p.is_regular_file()) continue;
 | 
				
			||||||
 | 
					                const auto& path = p.path();
 | 
				
			||||||
 | 
					                if (path.extension() != ".json") continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                std::string tableName = path.stem().string();
 | 
				
			||||||
 | 
					                auto fileContent = ReadFileToString(path);
 | 
				
			||||||
 | 
					                if (fileContent.empty()) continue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                try {
 | 
				
			||||||
 | 
					                    auto j = nlohmann::json::parse(fileContent);
 | 
				
			||||||
 | 
					                    if (!j.contains("rules") || !j["rules"].contains("primaryKeys")) {
 | 
				
			||||||
 | 
					                        continue;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    ItemRule currRule;
 | 
				
			||||||
 | 
					                    if (!GetItemRule(j, currRule)) {
 | 
				
			||||||
 | 
					                        Log::ErrorFmt("GetItemRule failed: %s", path.string().c_str());
 | 
				
			||||||
 | 
					                        continue;
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    /*
 | 
				
			||||||
 | 
					                    if (tableName == "ProduceStepEventDetail") {
 | 
				
			||||||
 | 
					                        for (auto& i : currRule.mainLocalKey) {
 | 
				
			||||||
 | 
					                            Log::DebugFmt("currRule.mainLocalKey: %s", i.c_str());
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                        for (auto& i : currRule.mainPrimaryKey) {
 | 
				
			||||||
 | 
					                            Log::DebugFmt("currRule.mainPrimaryKey: %s", i.c_str());
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                        for (auto& i : currRule.subLocalKey) {
 | 
				
			||||||
 | 
					                            for (auto& m : i.second) {
 | 
				
			||||||
 | 
					                                Log::DebugFmt("currRule.subLocalKey: %s - %s", i.first.c_str(), m.c_str());
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                        for (auto& i : currRule.subPrimaryKey) {
 | 
				
			||||||
 | 
					                            for (auto& m : i.second) {
 | 
				
			||||||
 | 
					                                Log::DebugFmt("currRule.subPrimaryKey: %s - %s", i.first.c_str(), m.c_str());
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    TableLocalData tableLocalData{ .itemRule = currRule };
 | 
				
			||||||
 | 
					                    if (GetTableLocalData(j, tableLocalData)) {
 | 
				
			||||||
 | 
					                        for (auto& i : tableLocalData.transData) {
 | 
				
			||||||
 | 
					                            // Log::DebugFmt("%s: %s -> %s", tableName.c_str(), i.first.c_str(), i.second.c_str());
 | 
				
			||||||
 | 
					                            Local::translatedText.emplace(i.second);
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                        for (auto& i : tableLocalData.transStrListData) {
 | 
				
			||||||
 | 
					                            for (auto& str : i.second) {
 | 
				
			||||||
 | 
					                                // Log::DebugFmt("%s[]: %s -> %s", tableName.c_str(), i.first.c_str(), str.c_str());
 | 
				
			||||||
 | 
					                                Local::translatedText.emplace(str);
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        /*
 | 
				
			||||||
 | 
					                        if (tableName == "ProduceStepEventDetail") {
 | 
				
			||||||
 | 
					                            for (auto& i : tableLocalData.mainKeyType) {
 | 
				
			||||||
 | 
					                                Log::DebugFmt("mainKeyType: %s -> %d", i.first.c_str(), i.second);
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                            for (auto& i : tableLocalData.subKeyType) {
 | 
				
			||||||
 | 
					                                for (auto& m : i.second) {
 | 
				
			||||||
 | 
					                                    Log::DebugFmt("subKeyType: %s - %s -> %d", i.first.c_str(), m.first.c_str(), m.second);
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }*/
 | 
				
			||||||
 | 
					                        // JVT_ArrayString in HelpCategory, ProduceStory, Tutorial
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        masterLocalData.emplace(tableName, std::move(tableLocalData));
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    else {
 | 
				
			||||||
 | 
					                        Log::ErrorFmt("GetTableLocalData failed: %s", path.string().c_str());
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                } catch (std::exception& e) {
 | 
				
			||||||
 | 
					                    Log::ErrorFmt("MasterLocal::LoadData: parse error in '%s': %s",
 | 
				
			||||||
 | 
					                                  path.string().c_str(), e.what());
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void LoadData() {
 | 
				
			||||||
 | 
					        return Load::LoadData();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::string GetTransString(const std::string& key, const TableLocalData& localData) {
 | 
				
			||||||
 | 
					        if (auto it = localData.transData.find(key); it != localData.transData.end()) {
 | 
				
			||||||
 | 
					            return it->second;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return {};
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::vector<std::string> GetTransArrayString(const std::string& key, const TableLocalData& localData) {
 | 
				
			||||||
 | 
					        if (auto it = localData.transStrListData.find(key); it != localData.transStrListData.end()) {
 | 
				
			||||||
 | 
					            return it->second;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        return {};
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void LocalizeMasterItem(FieldController& fc, const std::string& tableName) {
 | 
				
			||||||
 | 
					        auto it = masterLocalData.find(tableName);
 | 
				
			||||||
 | 
					        if (it == masterLocalData.end()) return;
 | 
				
			||||||
 | 
					        const auto& localData = it->second;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 首先拼 BasePrimaryKey
 | 
				
			||||||
 | 
					        std::string baseDataKey;  // p_card-00-acc-0_002|0|
 | 
				
			||||||
 | 
					        for (auto& mainPk : localData.itemRule.mainPrimaryKey) {
 | 
				
			||||||
 | 
					            auto mainPkType = localData.GetMainKeyType(mainPk);
 | 
				
			||||||
 | 
					            switch (mainPkType) {
 | 
				
			||||||
 | 
					                case JsonValueType::JVT_Int: {
 | 
				
			||||||
 | 
					                    auto readValue = std::to_string(fc.ReadIntField(mainPk));
 | 
				
			||||||
 | 
					                    baseDataKey.append(readValue);
 | 
				
			||||||
 | 
					                    baseDataKey.push_back('|');
 | 
				
			||||||
 | 
					                } break;
 | 
				
			||||||
 | 
					                case JsonValueType::JVT_String: {
 | 
				
			||||||
 | 
					                    auto readValue = fc.ReadStringField(mainPk);
 | 
				
			||||||
 | 
					                    if (!readValue) return;
 | 
				
			||||||
 | 
					                    baseDataKey.append(readValue->ToString());
 | 
				
			||||||
 | 
					                    baseDataKey.push_back('|');
 | 
				
			||||||
 | 
					                } break;
 | 
				
			||||||
 | 
					                default:
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 然后本地化 mainLocal
 | 
				
			||||||
 | 
					        for (auto& mainLocal : localData.itemRule.mainLocalKey) {
 | 
				
			||||||
 | 
					            std::string currSearchKey = baseDataKey;
 | 
				
			||||||
 | 
					            currSearchKey.append(mainLocal);  // p_card-00-acc-0_002|0|name
 | 
				
			||||||
 | 
					            auto localVType = localData.GetMainKeyType(mainLocal);
 | 
				
			||||||
 | 
					            switch (localVType) {
 | 
				
			||||||
 | 
					                case JsonValueType::JVT_String: {
 | 
				
			||||||
 | 
					                    auto localValue = GetTransString(currSearchKey, localData);
 | 
				
			||||||
 | 
					                    if (!localValue.empty()) {
 | 
				
			||||||
 | 
					                        fc.SetStringField(mainLocal, localValue);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                } break;
 | 
				
			||||||
 | 
					                case JsonValueType::JVT_ArrayString: {
 | 
				
			||||||
 | 
					                    auto localValue = GetTransArrayString(currSearchKey, localData);
 | 
				
			||||||
 | 
					                    if (!localValue.empty()) {
 | 
				
			||||||
 | 
					                        fc.SetStringListField(mainLocal, localValue);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                } break;
 | 
				
			||||||
 | 
					                default:
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 处理 sub
 | 
				
			||||||
 | 
					        for (const auto& [subParentKey, subLocalKeys] : localData.itemRule.subLocalKey) {
 | 
				
			||||||
 | 
					            const auto subBaseSearchKey = baseDataKey + subParentKey + '|';  // p_card-00-acc-0_002|0|produceDescriptions|
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            const auto subParentType = localData.GetMainKeyType(subParentKey);
 | 
				
			||||||
 | 
					            switch (subParentType) {
 | 
				
			||||||
 | 
					                case JsonValueType::JVT_Object: {
 | 
				
			||||||
 | 
					                    auto subParentField = fc.CreateSubFieldController(subParentKey);
 | 
				
			||||||
 | 
					                    for (const auto& subLocalKey : subLocalKeys) {
 | 
				
			||||||
 | 
					                        const auto currSearchKey = subBaseSearchKey + subLocalKey;  // p_card-00-acc-0_002|0|produceDescriptions|text
 | 
				
			||||||
 | 
					                        auto localKeyType = localData.GetSubKeyType(subParentKey, subLocalKey);
 | 
				
			||||||
 | 
					                        if (localKeyType == JsonValueType::JVT_String) {
 | 
				
			||||||
 | 
					                            auto setData = GetTransString(currSearchKey, localData);
 | 
				
			||||||
 | 
					                            if (!setData.empty()) {
 | 
				
			||||||
 | 
					                                subParentField.SetStringField(subLocalKey, setData);
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                        else if (localKeyType == JsonValueType::JVT_ArrayString) {
 | 
				
			||||||
 | 
					                            auto setData = GetTransArrayString(currSearchKey, localData);
 | 
				
			||||||
 | 
					                            if (!setData.empty()) {
 | 
				
			||||||
 | 
					                                subParentField.SetStringListField(subLocalKey, setData);
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                } break;
 | 
				
			||||||
 | 
					                case JsonValueType::JVT_ArrayObject: {
 | 
				
			||||||
 | 
					                    auto subArrField = fc.ReadObjectListField(subParentKey);
 | 
				
			||||||
 | 
					                    if (!subArrField) continue;
 | 
				
			||||||
 | 
					                    Il2cppUtils::Tools::CSListEditor<void*> subListEdit(subArrField);
 | 
				
			||||||
 | 
					                    auto count = subListEdit.get_Count();
 | 
				
			||||||
 | 
					                    for (int idx = 0; idx < count; idx++) {
 | 
				
			||||||
 | 
					                        auto currItem = subListEdit.get_Item(idx);
 | 
				
			||||||
 | 
					                        if (!currItem) continue;
 | 
				
			||||||
 | 
					                        auto currFc = FieldController::CreateSubFieldController(currItem);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        std::string currSearchBaseKey = subBaseSearchKey;  // p_card-00-acc-0_002|0|produceDescriptions|
 | 
				
			||||||
 | 
					                        currSearchBaseKey.push_back('[');
 | 
				
			||||||
 | 
					                        currSearchBaseKey.append(std::to_string(idx));
 | 
				
			||||||
 | 
					                        currSearchBaseKey.append("]|");  // p_card-00-acc-0_002|0|produceDescriptions|[0]|
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        for (const auto& subLocalKey : subLocalKeys) {
 | 
				
			||||||
 | 
					                            std::string currSearchKey = currSearchBaseKey + subLocalKey;  // p_card-00-acc-0_002|0|produceDescriptions|[0]|text
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                            auto localKeyType = localData.GetSubKeyType(subParentKey, subLocalKey);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                            /*
 | 
				
			||||||
 | 
					                            if (tableName == "ProduceStepEventDetail") {
 | 
				
			||||||
 | 
					                                Log::DebugFmt("localKeyType: %d currSearchKey: %s", localKeyType, currSearchKey.c_str());
 | 
				
			||||||
 | 
					                            }*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                            if (localKeyType == JsonValueType::JVT_String) {
 | 
				
			||||||
 | 
					                                auto setData = GetTransString(currSearchKey, localData);
 | 
				
			||||||
 | 
					                                if (!setData.empty()) {
 | 
				
			||||||
 | 
					                                    currFc.SetStringField(subLocalKey, setData);
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                            else if (localKeyType == JsonValueType::JVT_ArrayString) {
 | 
				
			||||||
 | 
					                                auto setData = GetTransArrayString(currSearchKey, localData);
 | 
				
			||||||
 | 
					                                if (!setData.empty()) {
 | 
				
			||||||
 | 
					                                    currFc.SetStringListField(subLocalKey, setData);
 | 
				
			||||||
 | 
					                                }
 | 
				
			||||||
 | 
					                            }
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                } break;
 | 
				
			||||||
 | 
					                default:
 | 
				
			||||||
 | 
					                    break;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void LocalizeMasterItem(void* item, const std::string& tableName) {
 | 
				
			||||||
 | 
					        if (!Config::useMasterTrans) return;
 | 
				
			||||||
 | 
					        // Log::DebugFmt("LocalizeMasterItem: %s", tableName.c_str());
 | 
				
			||||||
 | 
					        FieldController fc(item);
 | 
				
			||||||
 | 
					        LocalizeMasterItem(fc, tableName);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} // namespace GakumasLocal::MasterLocal
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,12 @@
 | 
				
			||||||
 | 
					#ifndef GAKUMAS_LOCALIFY_MASTERLOCAL_H
 | 
				
			||||||
 | 
					#define GAKUMAS_LOCALIFY_MASTERLOCAL_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <string>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace GakumasLocal::MasterLocal {
 | 
				
			||||||
 | 
					    void LoadData();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void LocalizeMasterItem(void* item, const std::string& tableName);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif //GAKUMAS_LOCALIFY_MASTERLOCAL_H
 | 
				
			||||||
| 
						 | 
					@ -2,14 +2,36 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <codecvt>
 | 
					#include <codecvt>
 | 
				
			||||||
#include <locale>
 | 
					#include <locale>
 | 
				
			||||||
#include <jni.h>
 | 
					 | 
				
			||||||
#include "fmt/core.h"
 | 
					#include "fmt/core.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef GKMS_WINDOWS
 | 
				
			||||||
 | 
					    #include <jni.h>
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
extern JavaVM* g_javaVM;
 | 
					    extern JavaVM* g_javaVM;
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					    #include "cpprest/details/http_helpers.h"
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace GakumasLocal::Misc {
 | 
					namespace GakumasLocal::Misc {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef GKMS_WINDOWS
 | 
				
			||||||
 | 
					    std::string ToUTF8(const std::wstring_view& str) {
 | 
				
			||||||
 | 
							return utility::conversions::to_utf8string(str.data());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::u16string ToUTF16(const std::string_view& str) {
 | 
				
			||||||
 | 
					        std::string input(str);
 | 
				
			||||||
 | 
					        std::wstring wstr = utility::conversions::utf8_to_utf16(input);
 | 
				
			||||||
 | 
					        return std::u16string(wstr.begin(), wstr.end());
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::string ToUTF8(const std::u16string_view& str) {
 | 
				
			||||||
 | 
					        std::u16string u16(str);
 | 
				
			||||||
 | 
					        std::wstring wstr(u16.begin(), u16.end());
 | 
				
			||||||
 | 
					        return utility::conversions::utf16_to_utf8(wstr);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
    std::u16string ToUTF16(const std::string_view& str) {
 | 
					    std::u16string ToUTF16(const std::string_view& str) {
 | 
				
			||||||
        std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> utf16conv;
 | 
					        std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> utf16conv;
 | 
				
			||||||
        return utf16conv.from_bytes(str.data(), str.data() + str.size());
 | 
					        return utf16conv.from_bytes(str.data(), str.data() + str.size());
 | 
				
			||||||
| 
						 | 
					@ -19,7 +41,9 @@ namespace GakumasLocal::Misc {
 | 
				
			||||||
        std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> utf16conv;
 | 
					        std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> utf16conv;
 | 
				
			||||||
        return utf16conv.to_bytes(str.data(), str.data() + str.size());
 | 
					        return utf16conv.to_bytes(str.data(), str.data() + str.size());
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef GKMS_WINDOWS
 | 
				
			||||||
    JNIEnv* GetJNIEnv() {
 | 
					    JNIEnv* GetJNIEnv() {
 | 
				
			||||||
        if (!g_javaVM) return nullptr;
 | 
					        if (!g_javaVM) return nullptr;
 | 
				
			||||||
        JNIEnv* env = nullptr;
 | 
					        JNIEnv* env = nullptr;
 | 
				
			||||||
| 
						 | 
					@ -31,6 +55,7 @@ namespace GakumasLocal::Misc {
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        return env;
 | 
					        return env;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    CSEnum::CSEnum(const std::string& name, const int value) {
 | 
					    CSEnum::CSEnum(const std::string& name, const int value) {
 | 
				
			||||||
        this->Add(name, value);
 | 
					        this->Add(name, value);
 | 
				
			||||||
| 
						 | 
					@ -168,6 +193,33 @@ namespace GakumasLocal::Misc {
 | 
				
			||||||
                return fmt;
 | 
					                return fmt;
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        std::vector<std::string> split(const std::string& str, char delimiter) {
 | 
				
			||||||
 | 
					            std::vector<std::string> result;
 | 
				
			||||||
 | 
					            std::string current;
 | 
				
			||||||
 | 
					            for (char c : str) {
 | 
				
			||||||
 | 
					                if (c == delimiter) {
 | 
				
			||||||
 | 
					                    if (!current.empty()) {
 | 
				
			||||||
 | 
					                        result.push_back(current);
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                    current.clear();
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    current += c;
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            if (!current.empty()) {
 | 
				
			||||||
 | 
					                result.push_back(current);
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return result;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        std::pair<std::string, std::string> split_once(const std::string& str, const std::string& delimiter) {
 | 
				
			||||||
 | 
					            size_t pos = str.find(delimiter);
 | 
				
			||||||
 | 
					            if (pos != std::string::npos) {
 | 
				
			||||||
 | 
					                return {str.substr(0, pos), str.substr(pos + delimiter.size())};
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            return {str, ""};
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2,11 +2,16 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <string>
 | 
					#include <string>
 | 
				
			||||||
#include <string_view>
 | 
					#include <string_view>
 | 
				
			||||||
#include <jni.h>
 | 
					 | 
				
			||||||
#include <deque>
 | 
					#include <deque>
 | 
				
			||||||
#include <numeric>
 | 
					#include <numeric>
 | 
				
			||||||
#include <vector>
 | 
					#include <vector>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "../platformDefine.hpp"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef GKMS_WINDOWS
 | 
				
			||||||
 | 
					    #include <jni.h>
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace GakumasLocal {
 | 
					namespace GakumasLocal {
 | 
				
			||||||
    using OpaqueFunctionPointer = void (*)();
 | 
					    using OpaqueFunctionPointer = void (*)();
 | 
				
			||||||
| 
						 | 
					@ -14,7 +19,13 @@ namespace GakumasLocal {
 | 
				
			||||||
    namespace Misc {
 | 
					    namespace Misc {
 | 
				
			||||||
        std::u16string ToUTF16(const std::string_view& str);
 | 
					        std::u16string ToUTF16(const std::string_view& str);
 | 
				
			||||||
        std::string ToUTF8(const std::u16string_view& str);
 | 
					        std::string ToUTF8(const std::u16string_view& str);
 | 
				
			||||||
 | 
					#ifdef GKMS_WINDOWS
 | 
				
			||||||
 | 
					        std::string ToUTF8(const std::wstring_view& str);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef GKMS_WINDOWS
 | 
				
			||||||
        JNIEnv* GetJNIEnv();
 | 
					        JNIEnv* GetJNIEnv();
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        class CSEnum {
 | 
					        class CSEnum {
 | 
				
			||||||
        public:
 | 
					        public:
 | 
				
			||||||
| 
						 | 
					@ -76,6 +87,8 @@ namespace GakumasLocal {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        namespace StringFormat {
 | 
					        namespace StringFormat {
 | 
				
			||||||
            std::string stringFormatString(const std::string& fmt, const std::vector<std::string>& vec);
 | 
					            std::string stringFormatString(const std::string& fmt, const std::vector<std::string>& vec);
 | 
				
			||||||
 | 
					            std::vector<std::string> split(const std::string& str, char delimiter);
 | 
				
			||||||
 | 
					            std::pair<std::string, std::string> split_once(const std::string& str, const std::string& delimiter);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,7 +4,13 @@
 | 
				
			||||||
#include "Misc.hpp"
 | 
					#include "Misc.hpp"
 | 
				
			||||||
#include <string>
 | 
					#include <string>
 | 
				
			||||||
#include <memory>
 | 
					#include <memory>
 | 
				
			||||||
#include <jni.h>
 | 
					
 | 
				
			||||||
 | 
					#include "../platformDefine.hpp"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef GKMS_WINDOWS
 | 
				
			||||||
 | 
					    #include <jni.h>
 | 
				
			||||||
 | 
					#endif // !GKMS_WINDOWS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace GakumasLocal {
 | 
					namespace GakumasLocal {
 | 
				
			||||||
    struct HookInstaller
 | 
					    struct HookInstaller
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,12 @@
 | 
				
			||||||
#include "baseCamera.hpp"
 | 
					#include "baseCamera.hpp"
 | 
				
			||||||
#include <thread>
 | 
					#include <thread>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "../../platformDefine.hpp"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef GKMS_WINDOWS
 | 
				
			||||||
 | 
						#include <corecrt_math_defines.h>
 | 
				
			||||||
 | 
					#endif // GKMS_WINDOWS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace BaseCamera {
 | 
					namespace BaseCamera {
 | 
				
			||||||
	using Vector3_t = UnityResolve::UnityType::Vector3;
 | 
						using Vector3_t = UnityResolve::UnityType::Vector3;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,6 @@
 | 
				
			||||||
#pragma once
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "../deps/UnityResolve/UnityResolve.hpp"
 | 
					#include "../../deps/UnityResolve/UnityResolve.hpp"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
enum LonMoveHState {
 | 
					enum LonMoveHState {
 | 
				
			||||||
	LonMoveLeftAndRight,
 | 
						LonMoveLeftAndRight,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,8 +1,14 @@
 | 
				
			||||||
#include "baseCamera.hpp"
 | 
					#include "baseCamera.hpp"
 | 
				
			||||||
#include "camera.hpp"
 | 
					#include "camera.hpp"
 | 
				
			||||||
#include <thread>
 | 
					#include <thread>
 | 
				
			||||||
#include "Misc.hpp"
 | 
					#include "../Misc.hpp"
 | 
				
			||||||
#include "../BaseDefine.h"
 | 
					#include "../BaseDefine.h"
 | 
				
			||||||
 | 
					#include "../../platformDefine.hpp"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef GKMS_WINDOWS
 | 
				
			||||||
 | 
					    #include <corecrt_math_defines.h>
 | 
				
			||||||
 | 
					#endif // GKMS_WINDOWS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace GKCamera {
 | 
					namespace GKCamera {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,6 @@
 | 
				
			||||||
#pragma once
 | 
					#pragma once
 | 
				
			||||||
#include "baseCamera.hpp"
 | 
					#include "baseCamera.hpp"
 | 
				
			||||||
#include "Joystick/JoystickEvent.h"
 | 
					#include "../../deps/Joystick/JoystickEvent.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace GKCamera {
 | 
					namespace GKCamera {
 | 
				
			||||||
    enum class CameraMode {
 | 
					    enum class CameraMode {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,6 +1,8 @@
 | 
				
			||||||
#include <string>
 | 
					#include <string>
 | 
				
			||||||
#include "nlohmann/json.hpp"
 | 
					#include "nlohmann/json.hpp"
 | 
				
			||||||
#include "../Log.h"
 | 
					#include "../Log.h"
 | 
				
			||||||
 | 
					#include <thread>
 | 
				
			||||||
 | 
					#include <fstream>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace GakumasLocal::Config {
 | 
					namespace GakumasLocal::Config {
 | 
				
			||||||
    bool isConfigInit = false;
 | 
					    bool isConfigInit = false;
 | 
				
			||||||
| 
						 | 
					@ -11,16 +13,20 @@ namespace GakumasLocal::Config {
 | 
				
			||||||
    bool replaceFont = true;
 | 
					    bool replaceFont = true;
 | 
				
			||||||
    bool forceExportResource = true;
 | 
					    bool forceExportResource = true;
 | 
				
			||||||
    bool textTest = false;
 | 
					    bool textTest = false;
 | 
				
			||||||
 | 
					    bool useMasterTrans = true;
 | 
				
			||||||
    int gameOrientation = 0;
 | 
					    int gameOrientation = 0;
 | 
				
			||||||
    bool dumpText = false;
 | 
					    bool dumpText = false;
 | 
				
			||||||
    bool enableFreeCamera = false;
 | 
					    bool enableFreeCamera = false;
 | 
				
			||||||
    int targetFrameRate = 0;
 | 
					    int targetFrameRate = 0;
 | 
				
			||||||
    bool unlockAllLive = false;
 | 
					    bool unlockAllLive = false;
 | 
				
			||||||
 | 
					    bool unlockAllLiveCostume = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    bool enableLiveCustomeDress = false;
 | 
					    bool enableLiveCustomeDress = false;
 | 
				
			||||||
    std::string liveCustomeHeadId = "";
 | 
					    std::string liveCustomeHeadId = "";
 | 
				
			||||||
    std::string liveCustomeCostumeId = "";
 | 
					    std::string liveCustomeCostumeId = "";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bool loginAsIOS = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    bool useCustomeGraphicSettings = false;
 | 
					    bool useCustomeGraphicSettings = false;
 | 
				
			||||||
    float renderScale = 0.77f;
 | 
					    float renderScale = 0.77f;
 | 
				
			||||||
    int qualitySettingsLevel = 3;
 | 
					    int qualitySettingsLevel = 3;
 | 
				
			||||||
| 
						 | 
					@ -48,6 +54,8 @@ namespace GakumasLocal::Config {
 | 
				
			||||||
    float bLimitZx = 1.0f;
 | 
					    float bLimitZx = 1.0f;
 | 
				
			||||||
    float bLimitZy = 1.0f;
 | 
					    float bLimitZy = 1.0f;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bool dmmUnlockSize = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void LoadConfig(const std::string& configStr) {
 | 
					    void LoadConfig(const std::string& configStr) {
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
            const auto config = nlohmann::json::parse(configStr);
 | 
					            const auto config = nlohmann::json::parse(configStr);
 | 
				
			||||||
| 
						 | 
					@ -61,13 +69,16 @@ namespace GakumasLocal::Config {
 | 
				
			||||||
            GetConfigItem(forceExportResource);
 | 
					            GetConfigItem(forceExportResource);
 | 
				
			||||||
            GetConfigItem(gameOrientation);
 | 
					            GetConfigItem(gameOrientation);
 | 
				
			||||||
            GetConfigItem(textTest);
 | 
					            GetConfigItem(textTest);
 | 
				
			||||||
 | 
					            GetConfigItem(useMasterTrans);
 | 
				
			||||||
            GetConfigItem(dumpText);
 | 
					            GetConfigItem(dumpText);
 | 
				
			||||||
            GetConfigItem(targetFrameRate);
 | 
					            GetConfigItem(targetFrameRate);
 | 
				
			||||||
            GetConfigItem(enableFreeCamera);
 | 
					            GetConfigItem(enableFreeCamera);
 | 
				
			||||||
            GetConfigItem(unlockAllLive);
 | 
					            GetConfigItem(unlockAllLive);
 | 
				
			||||||
 | 
					            GetConfigItem(unlockAllLiveCostume);
 | 
				
			||||||
            GetConfigItem(enableLiveCustomeDress);
 | 
					            GetConfigItem(enableLiveCustomeDress);
 | 
				
			||||||
            GetConfigItem(liveCustomeHeadId);
 | 
					            GetConfigItem(liveCustomeHeadId);
 | 
				
			||||||
            GetConfigItem(liveCustomeCostumeId);
 | 
					            GetConfigItem(liveCustomeCostumeId);
 | 
				
			||||||
 | 
					            GetConfigItem(loginAsIOS);
 | 
				
			||||||
            GetConfigItem(useCustomeGraphicSettings);
 | 
					            GetConfigItem(useCustomeGraphicSettings);
 | 
				
			||||||
            GetConfigItem(renderScale);
 | 
					            GetConfigItem(renderScale);
 | 
				
			||||||
            GetConfigItem(qualitySettingsLevel);
 | 
					            GetConfigItem(qualitySettingsLevel);
 | 
				
			||||||
| 
						 | 
					@ -93,11 +104,74 @@ namespace GakumasLocal::Config {
 | 
				
			||||||
            GetConfigItem(bLimitYy);
 | 
					            GetConfigItem(bLimitYy);
 | 
				
			||||||
            GetConfigItem(bLimitZx);
 | 
					            GetConfigItem(bLimitZx);
 | 
				
			||||||
            GetConfigItem(bLimitZy);
 | 
					            GetConfigItem(bLimitZy);
 | 
				
			||||||
 | 
					            GetConfigItem(dmmUnlockSize);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        catch (std::exception& e) {
 | 
					        catch (std::exception& e) {
 | 
				
			||||||
            Log::ErrorFmt("LoadConfig error: %s", e.what());
 | 
					            Log::ErrorFmt("LoadConfig error: %s", e.what());
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        isConfigInit = true;
 | 
					        isConfigInit = true;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void SaveConfig(const std::string& configPath) {
 | 
				
			||||||
 | 
					        try {
 | 
				
			||||||
 | 
					            nlohmann::json config;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            #define SetConfigItem(name) config[#name] = name
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            SetConfigItem(dbgMode);
 | 
				
			||||||
 | 
					            SetConfigItem(enabled);
 | 
				
			||||||
 | 
					            SetConfigItem(lazyInit);
 | 
				
			||||||
 | 
					            SetConfigItem(replaceFont);
 | 
				
			||||||
 | 
					            SetConfigItem(forceExportResource);
 | 
				
			||||||
 | 
					            SetConfigItem(gameOrientation);
 | 
				
			||||||
 | 
					            SetConfigItem(textTest);
 | 
				
			||||||
 | 
					            SetConfigItem(useMasterTrans);
 | 
				
			||||||
 | 
					            SetConfigItem(dumpText);
 | 
				
			||||||
 | 
					            SetConfigItem(targetFrameRate);
 | 
				
			||||||
 | 
					            SetConfigItem(enableFreeCamera);
 | 
				
			||||||
 | 
					            SetConfigItem(unlockAllLive);
 | 
				
			||||||
 | 
					            SetConfigItem(unlockAllLiveCostume);
 | 
				
			||||||
 | 
					            SetConfigItem(enableLiveCustomeDress);
 | 
				
			||||||
 | 
					            SetConfigItem(liveCustomeHeadId);
 | 
				
			||||||
 | 
					            SetConfigItem(liveCustomeCostumeId);
 | 
				
			||||||
 | 
					            SetConfigItem(loginAsIOS);
 | 
				
			||||||
 | 
					            SetConfigItem(useCustomeGraphicSettings);
 | 
				
			||||||
 | 
					            SetConfigItem(renderScale);
 | 
				
			||||||
 | 
					            SetConfigItem(qualitySettingsLevel);
 | 
				
			||||||
 | 
					            SetConfigItem(volumeIndex);
 | 
				
			||||||
 | 
					            SetConfigItem(maxBufferPixel);
 | 
				
			||||||
 | 
					            SetConfigItem(reflectionQualityLevel);
 | 
				
			||||||
 | 
					            SetConfigItem(lodQualityLevel);
 | 
				
			||||||
 | 
					            SetConfigItem(enableBreastParam);
 | 
				
			||||||
 | 
					            SetConfigItem(bDamping);
 | 
				
			||||||
 | 
					            SetConfigItem(bStiffness);
 | 
				
			||||||
 | 
					            SetConfigItem(bSpring);
 | 
				
			||||||
 | 
					            SetConfigItem(bPendulum);
 | 
				
			||||||
 | 
					            SetConfigItem(bPendulumRange);
 | 
				
			||||||
 | 
					            SetConfigItem(bAverage);
 | 
				
			||||||
 | 
					            SetConfigItem(bRootWeight);
 | 
				
			||||||
 | 
					            SetConfigItem(bUseArmCorrection);
 | 
				
			||||||
 | 
					            SetConfigItem(bUseScale);
 | 
				
			||||||
 | 
					            SetConfigItem(bScale);
 | 
				
			||||||
 | 
					            SetConfigItem(bUseLimit);
 | 
				
			||||||
 | 
					            SetConfigItem(bLimitXx);
 | 
				
			||||||
 | 
					            SetConfigItem(bLimitXy);
 | 
				
			||||||
 | 
					            SetConfigItem(bLimitYx);
 | 
				
			||||||
 | 
					            SetConfigItem(bLimitYy);
 | 
				
			||||||
 | 
					            SetConfigItem(bLimitZx);
 | 
				
			||||||
 | 
					            SetConfigItem(bLimitZy);
 | 
				
			||||||
 | 
					            SetConfigItem(dmmUnlockSize);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            std::ofstream out(configPath);
 | 
				
			||||||
 | 
					            if (!out) {
 | 
				
			||||||
 | 
					                Log::ErrorFmt("SaveConfig error: Cannot open file: %s", configPath.c_str());
 | 
				
			||||||
 | 
					                return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            out << config.dump(4);
 | 
				
			||||||
 | 
								Log::Info("SaveConfig success");
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        catch (std::exception& e) {
 | 
				
			||||||
 | 
					            Log::ErrorFmt("SaveConfig error: %s", e.what());
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -10,15 +10,19 @@ namespace GakumasLocal::Config {
 | 
				
			||||||
    extern bool forceExportResource;
 | 
					    extern bool forceExportResource;
 | 
				
			||||||
    extern int gameOrientation;
 | 
					    extern int gameOrientation;
 | 
				
			||||||
    extern bool textTest;
 | 
					    extern bool textTest;
 | 
				
			||||||
 | 
					    extern bool useMasterTrans;
 | 
				
			||||||
    extern bool dumpText;
 | 
					    extern bool dumpText;
 | 
				
			||||||
    extern bool enableFreeCamera;
 | 
					    extern bool enableFreeCamera;
 | 
				
			||||||
    extern int targetFrameRate;
 | 
					    extern int targetFrameRate;
 | 
				
			||||||
    extern bool unlockAllLive;
 | 
					    extern bool unlockAllLive;
 | 
				
			||||||
 | 
					    extern bool unlockAllLiveCostume;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    extern bool enableLiveCustomeDress;
 | 
					    extern bool enableLiveCustomeDress;
 | 
				
			||||||
    extern std::string liveCustomeHeadId;
 | 
					    extern std::string liveCustomeHeadId;
 | 
				
			||||||
    extern std::string liveCustomeCostumeId;
 | 
					    extern std::string liveCustomeCostumeId;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    extern bool loginAsIOS;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    extern bool useCustomeGraphicSettings;
 | 
					    extern bool useCustomeGraphicSettings;
 | 
				
			||||||
    extern float renderScale;
 | 
					    extern float renderScale;
 | 
				
			||||||
    extern int qualitySettingsLevel;
 | 
					    extern int qualitySettingsLevel;
 | 
				
			||||||
| 
						 | 
					@ -47,5 +51,8 @@ namespace GakumasLocal::Config {
 | 
				
			||||||
    extern float bLimitZx;
 | 
					    extern float bLimitZx;
 | 
				
			||||||
    extern float bLimitZy;
 | 
					    extern float bLimitZy;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    extern bool dmmUnlockSize;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    void LoadConfig(const std::string& configStr);
 | 
					    void LoadConfig(const std::string& configStr);
 | 
				
			||||||
 | 
					    void SaveConfig(const std::string& configPath);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -309,7 +309,7 @@ public:
 | 
				
			||||||
			pDomain = Invoke<void*>("il2cpp_domain_get");
 | 
								pDomain = Invoke<void*>("il2cpp_domain_get");
 | 
				
			||||||
			Invoke<void*>("il2cpp_thread_attach", pDomain);
 | 
								Invoke<void*>("il2cpp_thread_attach", pDomain);
 | 
				
			||||||
			ForeachAssembly();
 | 
								ForeachAssembly();
 | 
				
			||||||
            if (!lazyInit) UnityResolveProgress::startInit = false;
 | 
					            // if (!lazyInit) UnityResolveProgress::startInit = false;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		else {
 | 
							else {
 | 
				
			||||||
			pDomain = Invoke<void*>("mono_get_root_domain");
 | 
								pDomain = Invoke<void*>("mono_get_root_domain");
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,21 @@
 | 
				
			||||||
 | 
					#include "shadowhook.h"
 | 
				
			||||||
 | 
					#include <android/log.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define ADD_HOOK(name, addr)                                                                       \
 | 
				
			||||||
 | 
						name##_Addr = reinterpret_cast<name##_Type>(addr);                                             \
 | 
				
			||||||
 | 
						if (addr) {                                                                                    \
 | 
				
			||||||
 | 
					    	auto stub = hookInstaller->InstallHook(reinterpret_cast<void*>(addr),                      \
 | 
				
			||||||
 | 
					                                               reinterpret_cast<void*>(name##_Hook),               \
 | 
				
			||||||
 | 
					                                               reinterpret_cast<void**>(&name##_Orig));            \
 | 
				
			||||||
 | 
					        if (stub == NULL) {                                                                        \
 | 
				
			||||||
 | 
					            int error_num = shadowhook_get_errno();                                                \
 | 
				
			||||||
 | 
					            const char *error_msg = shadowhook_to_errmsg(error_num);                               \
 | 
				
			||||||
 | 
					            Log::ErrorFmt("ADD_HOOK: %s at %p failed: %s", #name, addr, error_msg);                \
 | 
				
			||||||
 | 
					        }                                                                                          \
 | 
				
			||||||
 | 
					        else {                                                                                     \
 | 
				
			||||||
 | 
					            hookedStubs.emplace(stub);                                                             \
 | 
				
			||||||
 | 
					            GakumasLocal::Log::InfoFmt("ADD_HOOK: %s at %p", #name, addr);                         \
 | 
				
			||||||
 | 
					        }                                                                                          \
 | 
				
			||||||
 | 
					    }                                                                                              \
 | 
				
			||||||
 | 
					    else GakumasLocal::Log::ErrorFmt("Hook failed: %s is NULL", #name, addr);                      \
 | 
				
			||||||
 | 
					    if (Config::lazyInit) UnityResolveProgress::classProgress.current++
 | 
				
			||||||
| 
						 | 
					@ -107,6 +107,7 @@ fun <T> T.onClickStartGame() where T : Activity, T : IHasConfigItems {
 | 
				
			||||||
            getProgramConfigContent(listOf("transRemoteZipUrl", "useAPIAssetsURL",
 | 
					            getProgramConfigContent(listOf("transRemoteZipUrl", "useAPIAssetsURL",
 | 
				
			||||||
                "localAPIAssetsVersion", "p"), programConfig)
 | 
					                "localAPIAssetsVersion", "p"), programConfig)
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					        putExtra("lVerName", version)
 | 
				
			||||||
        flags = Intent.FLAG_ACTIVITY_NEW_TASK
 | 
					        flags = Intent.FLAG_ACTIVITY_NEW_TASK
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -128,7 +129,15 @@ fun <T> T.onClickStartGame() where T : Activity, T : IHasConfigItems {
 | 
				
			||||||
            "io.github.chinosk.gakumas.localify.fileprovider",
 | 
					            "io.github.chinosk.gakumas.localify.fileprovider",
 | 
				
			||||||
            File(targetFile.absolutePath)
 | 
					            File(targetFile.absolutePath)
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        intent.setDataAndType(dirUri, "resource/file")
 | 
					        // intent.setDataAndType(dirUri, "resource/file")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        grantUriPermission(
 | 
				
			||||||
 | 
					            "com.bandainamcoent.idolmaster_gakuen",
 | 
				
			||||||
 | 
					            dirUri,
 | 
				
			||||||
 | 
					            Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        intent.putExtra("resource_file", dirUri)
 | 
				
			||||||
 | 
					        // intent.clipData = ClipData.newRawUri("resource_file", dirUri)
 | 
				
			||||||
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
 | 
					        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -17,12 +17,15 @@ import kotlinx.coroutines.runBlocking
 | 
				
			||||||
interface ConfigListener {
 | 
					interface ConfigListener {
 | 
				
			||||||
    fun onEnabledChanged(value: Boolean)
 | 
					    fun onEnabledChanged(value: Boolean)
 | 
				
			||||||
    fun onForceExportResourceChanged(value: Boolean)
 | 
					    fun onForceExportResourceChanged(value: Boolean)
 | 
				
			||||||
 | 
					    fun onLoginAsIOSChanged(value: Boolean)
 | 
				
			||||||
    fun onTextTestChanged(value: Boolean)
 | 
					    fun onTextTestChanged(value: Boolean)
 | 
				
			||||||
 | 
					    fun onUseMasterTransChanged(value: Boolean)
 | 
				
			||||||
    fun onReplaceFontChanged(value: Boolean)
 | 
					    fun onReplaceFontChanged(value: Boolean)
 | 
				
			||||||
    fun onLazyInitChanged(value: Boolean)
 | 
					    fun onLazyInitChanged(value: Boolean)
 | 
				
			||||||
    fun onEnableFreeCameraChanged(value: Boolean)
 | 
					    fun onEnableFreeCameraChanged(value: Boolean)
 | 
				
			||||||
    fun onTargetFpsChanged(s: CharSequence, start: Int, before: Int, count: Int)
 | 
					    fun onTargetFpsChanged(s: CharSequence, start: Int, before: Int, count: Int)
 | 
				
			||||||
    fun onUnlockAllLiveChanged(value: Boolean)
 | 
					    fun onUnlockAllLiveChanged(value: Boolean)
 | 
				
			||||||
 | 
					    fun onUnlockAllLiveCostumeChanged(value: Boolean)
 | 
				
			||||||
    fun onLiveCustomeDressChanged(value: Boolean)
 | 
					    fun onLiveCustomeDressChanged(value: Boolean)
 | 
				
			||||||
    fun onLiveCustomeHeadIdChanged(s: CharSequence, start: Int, before: Int, count: Int)
 | 
					    fun onLiveCustomeHeadIdChanged(s: CharSequence, start: Int, before: Int, count: Int)
 | 
				
			||||||
    fun onLiveCustomeCostumeIdChanged(s: CharSequence, start: Int, before: Int, count: Int)
 | 
					    fun onLiveCustomeCostumeIdChanged(s: CharSequence, start: Int, before: Int, count: Int)
 | 
				
			||||||
| 
						 | 
					@ -115,6 +118,11 @@ interface ConfigUpdateListener: ConfigListener, IHasConfigItems {
 | 
				
			||||||
        pushKeyEvent(KeyEvent(1145, 30))
 | 
					        pushKeyEvent(KeyEvent(1145, 30))
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    override fun onLoginAsIOSChanged(value: Boolean) {
 | 
				
			||||||
 | 
					        config.loginAsIOS = value
 | 
				
			||||||
 | 
					        saveConfig()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    override fun onReplaceFontChanged(value: Boolean) {
 | 
					    override fun onReplaceFontChanged(value: Boolean) {
 | 
				
			||||||
        config.replaceFont = value
 | 
					        config.replaceFont = value
 | 
				
			||||||
        saveConfig()
 | 
					        saveConfig()
 | 
				
			||||||
| 
						 | 
					@ -131,6 +139,11 @@ interface ConfigUpdateListener: ConfigListener, IHasConfigItems {
 | 
				
			||||||
        saveConfig()
 | 
					        saveConfig()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    override fun onUseMasterTransChanged(value: Boolean) {
 | 
				
			||||||
 | 
					        config.useMasterTrans = value
 | 
				
			||||||
 | 
					        saveConfig()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    override fun onDumpTextChanged(value: Boolean) {
 | 
					    override fun onDumpTextChanged(value: Boolean) {
 | 
				
			||||||
        config.dumpText = value
 | 
					        config.dumpText = value
 | 
				
			||||||
        saveConfig()
 | 
					        saveConfig()
 | 
				
			||||||
| 
						 | 
					@ -146,6 +159,11 @@ interface ConfigUpdateListener: ConfigListener, IHasConfigItems {
 | 
				
			||||||
        saveConfig()
 | 
					        saveConfig()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    override fun onUnlockAllLiveCostumeChanged(value: Boolean) {
 | 
				
			||||||
 | 
					        config.unlockAllLiveCostume = value
 | 
				
			||||||
 | 
					        saveConfig()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    override fun onTargetFpsChanged(s: CharSequence, start: Int, before: Int, count: Int) {
 | 
					    override fun onTargetFpsChanged(s: CharSequence, start: Int, before: Int, count: Int) {
 | 
				
			||||||
        try {
 | 
					        try {
 | 
				
			||||||
            val valueStr = s.toString()
 | 
					            val valueStr = s.toString()
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,6 +8,7 @@ import android.content.Context
 | 
				
			||||||
import android.content.Intent
 | 
					import android.content.Intent
 | 
				
			||||||
import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
 | 
					import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
 | 
				
			||||||
import android.net.Uri
 | 
					import android.net.Uri
 | 
				
			||||||
 | 
					import android.os.Build
 | 
				
			||||||
import android.os.Handler
 | 
					import android.os.Handler
 | 
				
			||||||
import android.os.Looper
 | 
					import android.os.Looper
 | 
				
			||||||
import android.util.Log
 | 
					import android.util.Log
 | 
				
			||||||
| 
						 | 
					@ -247,6 +248,9 @@ class GakumasHookMain : IXposedHookLoadPackage, IXposedHookZygoteInit {
 | 
				
			||||||
        val gkmsData = intent.getStringExtra("gkmsData")
 | 
					        val gkmsData = intent.getStringExtra("gkmsData")
 | 
				
			||||||
        val programData = intent.getStringExtra("localData")
 | 
					        val programData = intent.getStringExtra("localData")
 | 
				
			||||||
        if (gkmsData != null) {
 | 
					        if (gkmsData != null) {
 | 
				
			||||||
 | 
					            val readVersion = intent.getStringExtra("lVerName")
 | 
				
			||||||
 | 
					            checkPluginVersion(activity, readVersion)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            gkmsDataInited = true
 | 
					            gkmsDataInited = true
 | 
				
			||||||
            val initConfig = try {
 | 
					            val initConfig = try {
 | 
				
			||||||
                json.decodeFromString<GakumasConfig>(gkmsData)
 | 
					                json.decodeFromString<GakumasConfig>(gkmsData)
 | 
				
			||||||
| 
						 | 
					@ -282,7 +286,14 @@ class GakumasHookMain : IXposedHookLoadPackage, IXposedHookZygoteInit {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // 使用热更新文件
 | 
					            // 使用热更新文件
 | 
				
			||||||
            if ((programConfig?.useRemoteAssets == true) || (programConfig?.useAPIAssets == true)) {
 | 
					            if ((programConfig?.useRemoteAssets == true) || (programConfig?.useAPIAssets == true)) {
 | 
				
			||||||
                val dataUri = intent.data
 | 
					                // val dataUri = intent.data
 | 
				
			||||||
 | 
					                val dataUri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
 | 
				
			||||||
 | 
					                    intent.getParcelableExtra("resource_file", Uri::class.java)
 | 
				
			||||||
 | 
					                } else {
 | 
				
			||||||
 | 
					                    @Suppress("DEPRECATION")
 | 
				
			||||||
 | 
					                    intent.getParcelableExtra<Uri>("resource_file")
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if (dataUri != null) {
 | 
					                if (dataUri != null) {
 | 
				
			||||||
                    if (!externalFilesChecked) {
 | 
					                    if (!externalFilesChecked) {
 | 
				
			||||||
                        externalFilesChecked = true
 | 
					                        externalFilesChecked = true
 | 
				
			||||||
| 
						 | 
					@ -305,6 +316,40 @@ class GakumasHookMain : IXposedHookLoadPackage, IXposedHookZygoteInit {
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private fun checkPluginVersion(activity: Activity, readVersion: String?) {
 | 
				
			||||||
 | 
					        val buildVersionName = BuildConfig.VERSION_NAME
 | 
				
			||||||
 | 
					        Log.i(TAG, "Checking Plugin Version: Build: $buildVersionName, Request: $readVersion")
 | 
				
			||||||
 | 
					        if (readVersion?.trim() == buildVersionName.trim()) {
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        val builder = AlertDialog.Builder(activity)
 | 
				
			||||||
 | 
					        val infoBuilder = AlertDialog.Builder(activity)
 | 
				
			||||||
 | 
					        builder.setTitle("Warning")
 | 
				
			||||||
 | 
					        builder.setCancelable(false)
 | 
				
			||||||
 | 
					        builder.setMessage(when (getCurrentLanguage(activity)) {
 | 
				
			||||||
 | 
					            "zh" -> "检测到插件版本不一致\n内置版本: $buildVersionName\n请求版本: $readVersion\n\n这可能是使用了 LSPatch 的集成模式,仅更新了插件本体,未重新修补游戏导致的。请使用 $readVersion 版本的插件重新修补或使用本地模式。"
 | 
				
			||||||
 | 
					            else -> "Detected plugin version mismatch\nBuilt-in version: $buildVersionName\nRequested version: $readVersion\n\nThis may be caused by using the LSPatch integration mode, where only the plugin itself was updated without re-patching the game. Please re-patch the game using the $readVersion version of the plugin or use the local mode."
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        builder.setPositiveButton("OK") { dialog, _ ->
 | 
				
			||||||
 | 
					            dialog.dismiss()
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        builder.setNegativeButton("Exit") { dialog, _ ->
 | 
				
			||||||
 | 
					            dialog.dismiss()
 | 
				
			||||||
 | 
					            activity.finishAffinity()
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        val dialog = builder.create()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        infoBuilder.setOnCancelListener {
 | 
				
			||||||
 | 
					            dialog.show()
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        dialog.show()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private fun showGetConfigFailedImpl(activity: Context, title: String, msg: String, infoButton: String, dlButton: String, okButton: String) {
 | 
					    private fun showGetConfigFailedImpl(activity: Context, title: String, msg: String, infoButton: String, dlButton: String, okButton: String) {
 | 
				
			||||||
        if (getConfigError == null) return
 | 
					        if (getConfigError == null) return
 | 
				
			||||||
        val builder = AlertDialog.Builder(activity)
 | 
					        val builder = AlertDialog.Builder(activity)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -645,7 +645,7 @@ class PatchActivity : ComponentActivity() {
 | 
				
			||||||
                    val copyFilesCmd: MutableList<String> = mutableListOf()
 | 
					                    val copyFilesCmd: MutableList<String> = mutableListOf()
 | 
				
			||||||
                    val movedFiles: MutableList<String> = mutableListOf()
 | 
					                    val movedFiles: MutableList<String> = mutableListOf()
 | 
				
			||||||
                    savedFileNames.forEach { file ->
 | 
					                    savedFileNames.forEach { file ->
 | 
				
			||||||
                        val movedFileName = "$installDS/${file}"
 | 
					                        val movedFileName = "\"$installDS/${file}\""
 | 
				
			||||||
                        movedFiles.add(movedFileName)
 | 
					                        movedFiles.add(movedFileName)
 | 
				
			||||||
                        val dlSaveFileName = File(targetDirectory, file)
 | 
					                        val dlSaveFileName = File(targetDirectory, file)
 | 
				
			||||||
                        copyFilesCmd.add("$action ${dlSaveFileName.absolutePath} $movedFileName")
 | 
					                        copyFilesCmd.add("$action ${dlSaveFileName.absolutePath} $movedFileName")
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -84,7 +84,7 @@ object FilesChecker {
 | 
				
			||||||
        for (i in assets.list(localizationFilesDir)!!) {
 | 
					        for (i in assets.list(localizationFilesDir)!!) {
 | 
				
			||||||
            if (i.toString() == "version.txt") {
 | 
					            if (i.toString() == "version.txt") {
 | 
				
			||||||
                val stream = assets.open("$localizationFilesDir/$i")
 | 
					                val stream = assets.open("$localizationFilesDir/$i")
 | 
				
			||||||
                return convertToString(stream)
 | 
					                return convertToString(stream).trim()
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        return "0.0"
 | 
					        return "0.0"
 | 
				
			||||||
| 
						 | 
					@ -96,7 +96,7 @@ object FilesChecker {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        val versionFile = File(pluginFilesDir, "version.txt")
 | 
					        val versionFile = File(pluginFilesDir, "version.txt")
 | 
				
			||||||
        if (!versionFile.exists()) return "0.0"
 | 
					        if (!versionFile.exists()) return "0.0"
 | 
				
			||||||
        return versionFile.readText()
 | 
					        return versionFile.readText().trim()
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fun convertToString(inputStream: InputStream?): String {
 | 
					    fun convertToString(inputStream: InputStream?): String {
 | 
				
			||||||
| 
						 | 
					@ -146,6 +146,7 @@ object FilesChecker {
 | 
				
			||||||
        val genericTransDir = File(localFilesDir, "genericTrans")
 | 
					        val genericTransDir = File(localFilesDir, "genericTrans")
 | 
				
			||||||
        val genericTransFile = File(localFilesDir, "generic.json")
 | 
					        val genericTransFile = File(localFilesDir, "generic.json")
 | 
				
			||||||
        val i18nFile = File(localFilesDir, "localization.json")
 | 
					        val i18nFile = File(localFilesDir, "localization.json")
 | 
				
			||||||
 | 
					        val masterTransDir = File(localFilesDir, "masterTrans")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if (fontFile.exists()) {
 | 
					        if (fontFile.exists()) {
 | 
				
			||||||
            fontFile.delete()
 | 
					            fontFile.delete()
 | 
				
			||||||
| 
						 | 
					@ -156,6 +157,9 @@ object FilesChecker {
 | 
				
			||||||
        if (deleteRecursively(genericTransDir)) {
 | 
					        if (deleteRecursively(genericTransDir)) {
 | 
				
			||||||
            genericTransDir.mkdirs()
 | 
					            genericTransDir.mkdirs()
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					        if (deleteRecursively(masterTransDir)) {
 | 
				
			||||||
 | 
					            masterTransDir.mkdirs()
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
        if (genericTransFile.exists()) {
 | 
					        if (genericTransFile.exists()) {
 | 
				
			||||||
            genericTransFile.writeText("{}")
 | 
					            genericTransFile.writeText("{}")
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,16 +9,20 @@ data class GakumasConfig (
 | 
				
			||||||
    var lazyInit: Boolean = true,
 | 
					    var lazyInit: Boolean = true,
 | 
				
			||||||
    var replaceFont: Boolean = true,
 | 
					    var replaceFont: Boolean = true,
 | 
				
			||||||
    var textTest: Boolean = false,
 | 
					    var textTest: Boolean = false,
 | 
				
			||||||
 | 
					    var useMasterTrans: Boolean = true,
 | 
				
			||||||
    var dumpText: Boolean = false,
 | 
					    var dumpText: Boolean = false,
 | 
				
			||||||
    var gameOrientation: Int = 0,
 | 
					    var gameOrientation: Int = 0,
 | 
				
			||||||
    var forceExportResource: Boolean = false,
 | 
					    var forceExportResource: Boolean = false,
 | 
				
			||||||
    var enableFreeCamera: Boolean = false,
 | 
					    var enableFreeCamera: Boolean = false,
 | 
				
			||||||
    var targetFrameRate: Int = 0,
 | 
					    var targetFrameRate: Int = 0,
 | 
				
			||||||
    var unlockAllLive: Boolean = false,
 | 
					    var unlockAllLive: Boolean = false,
 | 
				
			||||||
 | 
					    var unlockAllLiveCostume: Boolean = false,
 | 
				
			||||||
    var enableLiveCustomeDress: Boolean = false,
 | 
					    var enableLiveCustomeDress: Boolean = false,
 | 
				
			||||||
    var liveCustomeHeadId: String = "",
 | 
					    var liveCustomeHeadId: String = "",
 | 
				
			||||||
    var liveCustomeCostumeId: String = "",
 | 
					    var liveCustomeCostumeId: String = "",
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    var loginAsIOS: Boolean = false,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    var useCustomeGraphicSettings: Boolean = false,
 | 
					    var useCustomeGraphicSettings: Boolean = false,
 | 
				
			||||||
    var renderScale: Float = 0.77f,
 | 
					    var renderScale: Float = 0.77f,
 | 
				
			||||||
    var qualitySettingsLevel: Int = 3,
 | 
					    var qualitySettingsLevel: Int = 3,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -75,6 +75,10 @@ fun AdvanceSettingsPage(modifier: Modifier = Modifier,
 | 
				
			||||||
        item {
 | 
					        item {
 | 
				
			||||||
            GakuGroupBox(modifier, stringResource(R.string.debug_settings)) {
 | 
					            GakuGroupBox(modifier, stringResource(R.string.debug_settings)) {
 | 
				
			||||||
                Column(verticalArrangement = Arrangement.spacedBy(4.dp)) {
 | 
					                Column(verticalArrangement = Arrangement.spacedBy(4.dp)) {
 | 
				
			||||||
 | 
					                    GakuSwitch(modifier, stringResource(R.string.useMasterDBTrans), checked = config.value.useMasterTrans) {
 | 
				
			||||||
 | 
					                            v -> context?.onUseMasterTransChanged(v)
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    GakuSwitch(modifier, stringResource(R.string.text_hook_test_mode), checked = config.value.textTest) {
 | 
					                    GakuSwitch(modifier, stringResource(R.string.text_hook_test_mode), checked = config.value.textTest) {
 | 
				
			||||||
                            v -> context?.onTextTestChanged(v)
 | 
					                            v -> context?.onTextTestChanged(v)
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
| 
						 | 
					@ -86,6 +90,10 @@ fun AdvanceSettingsPage(modifier: Modifier = Modifier,
 | 
				
			||||||
                    GakuSwitch(modifier, stringResource(R.string.force_export_resource), checked = config.value.forceExportResource) {
 | 
					                    GakuSwitch(modifier, stringResource(R.string.force_export_resource), checked = config.value.forceExportResource) {
 | 
				
			||||||
                            v -> context?.onForceExportResourceChanged(v)
 | 
					                            v -> context?.onForceExportResourceChanged(v)
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    GakuSwitch(modifier, stringResource(R.string.login_as_ios), checked = config.value.loginAsIOS) {
 | 
				
			||||||
 | 
					                            v -> context?.onLoginAsIOSChanged(v)
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -353,6 +361,12 @@ fun AdvanceSettingsPage(modifier: Modifier = Modifier,
 | 
				
			||||||
                            checked = config.value.unlockAllLive) {
 | 
					                            checked = config.value.unlockAllLive) {
 | 
				
			||||||
                                v -> context?.onUnlockAllLiveChanged(v)
 | 
					                                v -> context?.onUnlockAllLiveChanged(v)
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
 | 
					                        GakuSwitch(modifier, stringResource(R.string.unlockAllLiveCostume),
 | 
				
			||||||
 | 
					                            checked = config.value.unlockAllLiveCostume) {
 | 
				
			||||||
 | 
					                                v -> context?.onUnlockAllLiveCostumeChanged(v)
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                        /*
 | 
				
			||||||
                        HorizontalDivider(
 | 
					                        HorizontalDivider(
 | 
				
			||||||
                            thickness = 1.dp,
 | 
					                            thickness = 1.dp,
 | 
				
			||||||
                            color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.12f)
 | 
					                            color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.12f)
 | 
				
			||||||
| 
						 | 
					@ -377,7 +391,7 @@ fun AdvanceSettingsPage(modifier: Modifier = Modifier,
 | 
				
			||||||
                            value = config.value.liveCustomeCostumeId,
 | 
					                            value = config.value.liveCustomeCostumeId,
 | 
				
			||||||
                            onValueChange = { c -> context?.onLiveCustomeCostumeIdChanged(c, 0, 0, 0)},
 | 
					                            onValueChange = { c -> context?.onLiveCustomeCostumeIdChanged(c, 0, 0, 0)},
 | 
				
			||||||
                            label = { Text(stringResource(R.string.live_custome_dress_id)) }
 | 
					                            label = { Text(stringResource(R.string.live_custome_dress_id)) }
 | 
				
			||||||
                        )
 | 
					                        )*/
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -314,8 +314,8 @@ fun HomePage(modifier: Modifier = Modifier,
 | 
				
			||||||
                                                fontSize = 14f,
 | 
					                                                fontSize = 14f,
 | 
				
			||||||
                                                value = programConfig.value.useAPIAssetsURL,
 | 
					                                                value = programConfig.value.useAPIAssetsURL,
 | 
				
			||||||
                                                onValueChange = { c -> context?.onPUseAPIAssetsURLChanged(c, 0, 0, 0)},
 | 
					                                                onValueChange = { c -> context?.onPUseAPIAssetsURLChanged(c, 0, 0, 0)},
 | 
				
			||||||
                                                label = { Text(stringResource(R.string.api_addr)) },
 | 
					                                                label = { Text(stringResource(R.string.api_addr)) }
 | 
				
			||||||
                                                keyboardOptions = keyboardOptionsNumber)
 | 
					                                            )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                                            if (downloadAble) {
 | 
					                                            if (downloadAble) {
 | 
				
			||||||
                                                GakuButton(modifier = modifier
 | 
					                                                GakuButton(modifier = modifier
 | 
				
			||||||
| 
						 | 
					@ -411,8 +411,8 @@ fun HomePage(modifier: Modifier = Modifier,
 | 
				
			||||||
                                                fontSize = 14f,
 | 
					                                                fontSize = 14f,
 | 
				
			||||||
                                                value = programConfig.value.transRemoteZipUrl,
 | 
					                                                value = programConfig.value.transRemoteZipUrl,
 | 
				
			||||||
                                                onValueChange = { c -> context?.onPTransRemoteZipUrlChanged(c, 0, 0, 0)},
 | 
					                                                onValueChange = { c -> context?.onPTransRemoteZipUrlChanged(c, 0, 0, 0)},
 | 
				
			||||||
                                                label = { Text(stringResource(id = R.string.resource_url)) },
 | 
					                                                label = { Text(stringResource(id = R.string.resource_url)) }
 | 
				
			||||||
                                                keyboardOptions = keyboardOptionsNumber)
 | 
					                                            )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                                            if (downloadAble) {
 | 
					                                            if (downloadAble) {
 | 
				
			||||||
                                                GakuButton(modifier = modifier
 | 
					                                                GakuButton(modifier = modifier
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,355 @@
 | 
				
			||||||
 | 
					<?xml version="1.0" encoding="utf-8"?>
 | 
				
			||||||
 | 
					<resources>
 | 
				
			||||||
 | 
					    <string name="abc_action_bar_home_description">ホームに戻る</string>
 | 
				
			||||||
 | 
					    <string name="abc_action_bar_up_description">前に戻る</string>
 | 
				
			||||||
 | 
					    <string name="abc_action_menu_overflow_description">その他のオプション</string>
 | 
				
			||||||
 | 
					    <string name="abc_action_mode_done">完了</string>
 | 
				
			||||||
 | 
					    <string name="abc_activity_chooser_view_see_all">すべて表示</string>
 | 
				
			||||||
 | 
					    <string name="abc_activitychooserview_choose_application">アプリの選択</string>
 | 
				
			||||||
 | 
					    <string name="abc_capital_off">OFF</string>
 | 
				
			||||||
 | 
					    <string name="abc_capital_on">ON</string>
 | 
				
			||||||
 | 
					    <string name="abc_menu_alt_shortcut_label">Alt+</string>
 | 
				
			||||||
 | 
					    <string name="abc_menu_ctrl_shortcut_label">Ctrl+</string>
 | 
				
			||||||
 | 
					    <string name="abc_menu_delete_shortcut_label">Delete</string>
 | 
				
			||||||
 | 
					    <string name="abc_menu_enter_shortcut_label">Enter</string>
 | 
				
			||||||
 | 
					    <string name="abc_menu_function_shortcut_label">Function+</string>
 | 
				
			||||||
 | 
					    <string name="abc_menu_meta_shortcut_label">Meta+</string>
 | 
				
			||||||
 | 
					    <string name="abc_menu_shift_shortcut_label">Shift+</string>
 | 
				
			||||||
 | 
					    <string name="abc_menu_space_shortcut_label">Space</string>
 | 
				
			||||||
 | 
					    <string name="abc_menu_sym_shortcut_label">Sym+</string>
 | 
				
			||||||
 | 
					    <string name="abc_prepend_shortcut_label">Menu+</string>
 | 
				
			||||||
 | 
					    <string name="abc_search_hint">検索…</string>
 | 
				
			||||||
 | 
					    <string name="abc_searchview_description_clear">検索キーワードを削除</string>
 | 
				
			||||||
 | 
					    <string name="abc_searchview_description_query">検索キーワード</string>
 | 
				
			||||||
 | 
					    <string name="abc_searchview_description_search">検索</string>
 | 
				
			||||||
 | 
					    <string name="abc_searchview_description_submit">検索キーワードを送信</string>
 | 
				
			||||||
 | 
					    <string name="abc_searchview_description_voice">音声検索</string>
 | 
				
			||||||
 | 
					    <string name="abc_shareactionprovider_share_with">共有</string>
 | 
				
			||||||
 | 
					    <string name="abc_shareactionprovider_share_with_application">%sと共有</string>
 | 
				
			||||||
 | 
					    <string name="abc_toolbar_collapse_description">折りたたむ</string>
 | 
				
			||||||
 | 
					    <string name="about">情報</string>
 | 
				
			||||||
 | 
					    <string name="about_about_p1">このプラグインは完全に無料で提供されます。このプラグインで料金を支払ってしまった場合は、販売者に報告をしてください。</string>
 | 
				
			||||||
 | 
					    <string name="about_about_p2">プラグインの QQ グループ: 975854705</string>
 | 
				
			||||||
 | 
					    <string name="about_about_title">このプラグインについて</string>
 | 
				
			||||||
 | 
					    <string name="about_contributors_asset_file">about_contributors_en.json</string>
 | 
				
			||||||
 | 
					    <string name="about_warn_p1">このプラグインは学習とコミュニケーションのみを目的としています。</string>
 | 
				
			||||||
 | 
					    <string name="about_warn_p2">外部プラグインは関連する TOS に違反するため、自己責任でご使用ください。</string>
 | 
				
			||||||
 | 
					    <string name="about_warn_title">警告</string>
 | 
				
			||||||
 | 
					    <string name="advanced_settings">高度な設定</string>
 | 
				
			||||||
 | 
					    <string name="androidx_startup">androidx.startup</string>
 | 
				
			||||||
 | 
					    <string name="api_addr">API アドレス (GitHub 最新リリース API)</string>
 | 
				
			||||||
 | 
					    <string name="app_name">Gakumas Localify</string>
 | 
				
			||||||
 | 
					    <string name="appbar_scrolling_view_behavior">com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior</string>
 | 
				
			||||||
 | 
					    <string name="average">平均</string>
 | 
				
			||||||
 | 
					    <string name="axisx_x">X 軸.x</string>
 | 
				
			||||||
 | 
					    <string name="axisx_y">X 軸.y</string>
 | 
				
			||||||
 | 
					    <string name="axisy_x">Y 軸.x</string>
 | 
				
			||||||
 | 
					    <string name="axisy_y">Y 軸.y</string>
 | 
				
			||||||
 | 
					    <string name="axisz_x">Z 軸.x</string>
 | 
				
			||||||
 | 
					    <string name="axisz_y">Z 軸.y</string>
 | 
				
			||||||
 | 
					    <string name="basic_settings">基本設定</string>
 | 
				
			||||||
 | 
					    <string name="bottom_sheet_behavior">com.google.android.material.bottomsheet.BottomSheetBehavior</string>
 | 
				
			||||||
 | 
					    <string name="bottomsheet_action_collapse">ボトムシートを閉じる</string>
 | 
				
			||||||
 | 
					    <string name="bottomsheet_action_expand">ボトムシートを開く</string>
 | 
				
			||||||
 | 
					    <string name="bottomsheet_action_expand_halfway">下半分を展開</string>
 | 
				
			||||||
 | 
					    <string name="bottomsheet_drag_handle_clicked">ハンドルをダブルタップしてドラッグ</string>
 | 
				
			||||||
 | 
					    <string name="bottomsheet_drag_handle_content_description">ドラッグハンドル</string>
 | 
				
			||||||
 | 
					    <string name="breast_param">胸のパラメーター</string>
 | 
				
			||||||
 | 
					    <string name="breast_scale">胸の大きさ</string>
 | 
				
			||||||
 | 
					    <string name="call_notification_answer_action">応答</string>
 | 
				
			||||||
 | 
					    <string name="call_notification_answer_video_action">動画</string>
 | 
				
			||||||
 | 
					    <string name="call_notification_decline_action">拒否</string>
 | 
				
			||||||
 | 
					    <string name="call_notification_hang_up_action">通話終了</string>
 | 
				
			||||||
 | 
					    <string name="call_notification_incoming_text">着信</string>
 | 
				
			||||||
 | 
					    <string name="call_notification_ongoing_text">通話中</string>
 | 
				
			||||||
 | 
					    <string name="call_notification_screening_text">着信をスクリーニング中</string>
 | 
				
			||||||
 | 
					    <string name="camera_settings">カメラ設定</string>
 | 
				
			||||||
 | 
					    <string name="cancel">キャンセル</string>
 | 
				
			||||||
 | 
					    <string name="character_counter_content_description">%1$d の %2$d に入力された文字</string>
 | 
				
			||||||
 | 
					    <string name="character_counter_overflowed_content_description">文字制限が %2$d 文字中、 %1$d 文字を超えています</string>
 | 
				
			||||||
 | 
					    <string name="character_counter_pattern">%1$d/%2$d</string>
 | 
				
			||||||
 | 
					    <string name="check_built_in_resource">内蔵アセットの更新を確認</string>
 | 
				
			||||||
 | 
					    <string name="check_resource_from_api">リソースの更新を API から確認</string>
 | 
				
			||||||
 | 
					    <string name="check_update">確認</string>
 | 
				
			||||||
 | 
					    <string name="clear_text_end_icon_content_description">テキストを消去</string>
 | 
				
			||||||
 | 
					    <string name="close_drawer">ナビゲーションメニューを閉じる</string>
 | 
				
			||||||
 | 
					    <string name="close_sheet">シートを閉じる</string>
 | 
				
			||||||
 | 
					    <string name="contributors">貢献者</string>
 | 
				
			||||||
 | 
					    <string name="damping">ダンプ中</string>
 | 
				
			||||||
 | 
					    <string name="debug_settings">デバッグ設定</string>
 | 
				
			||||||
 | 
					    <string name="default_assets_check_api">https://api.github.com/repos/NatsumeLS/Gakumas-Translation-Data-EN/releases/latest</string>
 | 
				
			||||||
 | 
					    <string name="default_error_message">入力が無効です</string>
 | 
				
			||||||
 | 
					    <string name="default_popup_window_title">ポップアップウィンドウ</string>
 | 
				
			||||||
 | 
					    <string name="del_remote_after_update">キャッシュファイルを更新後に削除</string>
 | 
				
			||||||
 | 
					    <string name="delete_plugin_resource">プラグインリソースを削除</string>
 | 
				
			||||||
 | 
					    <string name="download">ダウンロード</string>
 | 
				
			||||||
 | 
					    <string name="downloaded_resource_version">ダウンロードされたバージョン</string>
 | 
				
			||||||
 | 
					    <string name="dropdown_menu">ドロップダウンメニュー</string>
 | 
				
			||||||
 | 
					    <string name="enable_breast_param">胸のパラメーターを有効化</string>
 | 
				
			||||||
 | 
					    <string name="enable_free_camera">フリーカメラを有効化</string>
 | 
				
			||||||
 | 
					    <string name="enable_plugin">プラグイン有効化 (ホットリロードなし)</string>
 | 
				
			||||||
 | 
					    <string name="error_a11y_label">エラー: 無効</string>
 | 
				
			||||||
 | 
					    <string name="error_icon_content_description">エラー</string>
 | 
				
			||||||
 | 
					    <string name="export_text">テキストをエクスポート</string>
 | 
				
			||||||
 | 
					    <string name="exposed_dropdown_menu_content_description">ドロップダウンメニューを表示</string>
 | 
				
			||||||
 | 
					    <string name="fab_transformation_scrim_behavior">com.google.android.material.transformation.FabTransformationScrimBehavior</string>
 | 
				
			||||||
 | 
					    <string name="fab_transformation_sheet_behavior">com.google.android.material.transformation.FabTransformationSheetBehavior</string>
 | 
				
			||||||
 | 
					    <string name="force_export_resource">リソースの更新を強制する</string>
 | 
				
			||||||
 | 
					    <string name="gakumas_localify">Gakumas Localify</string>
 | 
				
			||||||
 | 
					    <string name="game_patch">ゲームパッチ</string>
 | 
				
			||||||
 | 
					    <string name="graphic_settings">グラフィック設定</string>
 | 
				
			||||||
 | 
					    <string name="hide_bottom_view_on_scroll_behavior">com.google.android.material.behavior.HideBottomViewOnScrollBehavior</string>
 | 
				
			||||||
 | 
					    <string name="hign">高</string>
 | 
				
			||||||
 | 
					    <string name="home">ホーム</string>
 | 
				
			||||||
 | 
					    <string name="home_shizuku_warning">一部の機能が使用できません</string>
 | 
				
			||||||
 | 
					    <string name="icon_content_description">ダイアログアイコン</string>
 | 
				
			||||||
 | 
					    <string name="in_progress">実行中</string>
 | 
				
			||||||
 | 
					    <string name="indeterminate">部分的に確認済み</string>
 | 
				
			||||||
 | 
					    <string name="install">インストール</string>
 | 
				
			||||||
 | 
					    <string name="installing">インストール中</string>
 | 
				
			||||||
 | 
					    <string name="invalid_zip_file">無効なファイル</string>
 | 
				
			||||||
 | 
					    <string name="invalid_zip_file_warn">このファイルは有効な ZIP 翻訳リソースパックではありません。</string>
 | 
				
			||||||
 | 
					    <string name="isdirty">IsDirty</string>
 | 
				
			||||||
 | 
					    <string name="item_view_role_description">タブ</string>
 | 
				
			||||||
 | 
					    <string name="lazy_init">高速な初期化 (読み込みを遅延)</string>
 | 
				
			||||||
 | 
					    <string name="liveUseCustomeDress">ライブのキャラクターをカスタム</string>
 | 
				
			||||||
 | 
					    <string name="live_costume_head_id">ライブのカスタムヘッド ID (例: costume_head_hski-cstm-0002)</string>
 | 
				
			||||||
 | 
					    <string name="live_custome_dress_id">ライブ衣装のカスタム ID (例: hski-cstm-0002)</string>
 | 
				
			||||||
 | 
					    <string name="login_as_ios">iOS としてログイン</string>
 | 
				
			||||||
 | 
					    <string name="low">低</string>
 | 
				
			||||||
 | 
					    <string name="m3_exceed_max_badge_text_suffix">%1$s%2$s</string>
 | 
				
			||||||
 | 
					    <string name="m3_ref_typeface_brand_medium">sans-serif-medium</string>
 | 
				
			||||||
 | 
					    <string name="m3_ref_typeface_brand_regular">sans-serif</string>
 | 
				
			||||||
 | 
					    <string name="m3_ref_typeface_plain_medium">sans-serif-medium</string>
 | 
				
			||||||
 | 
					    <string name="m3_ref_typeface_plain_regular">sans-serif</string>
 | 
				
			||||||
 | 
					    <string name="m3_sys_motion_easing_emphasized">path(M 0,0 C 0.05, 0, 0.133333, 0.06, 0.166666, 0.4 C 0.208333, 0.82, 0.25, 1, 1, 1)</string>
 | 
				
			||||||
 | 
					    <string name="m3_sys_motion_easing_emphasized_accelerate">cubic-bezier(0.3, 0, 0.8, 0.2)</string>
 | 
				
			||||||
 | 
					    <string name="m3_sys_motion_easing_emphasized_decelerate">cubic-bezier(0.1, 0.7, 0.1, 1)</string>
 | 
				
			||||||
 | 
					    <string name="m3_sys_motion_easing_emphasized_path_data">M 0,0 C 0.05, 0, 0.133333, 0.06, 0.166666, 0.4 C 0.208333, 0.82, 0.25, 1, 1, 1</string>
 | 
				
			||||||
 | 
					    <string name="m3_sys_motion_easing_legacy">cubic-bezier(0.4, 0, 0.2, 1)</string>
 | 
				
			||||||
 | 
					    <string name="m3_sys_motion_easing_legacy_accelerate">cubic-bezier(0.4, 0, 1, 1)</string>
 | 
				
			||||||
 | 
					    <string name="m3_sys_motion_easing_legacy_decelerate">cubic-bezier(0, 0, 0.2, 1)</string>
 | 
				
			||||||
 | 
					    <string name="m3_sys_motion_easing_linear">cubic-bezier(0, 0, 1, 1)</string>
 | 
				
			||||||
 | 
					    <string name="m3_sys_motion_easing_standard">cubic-bezier(0.2, 0, 0, 1)</string>
 | 
				
			||||||
 | 
					    <string name="m3_sys_motion_easing_standard_accelerate">cubic-bezier(0.3, 0, 1, 1)</string>
 | 
				
			||||||
 | 
					    <string name="m3_sys_motion_easing_standard_decelerate">cubic-bezier(0, 0, 0, 1)</string>
 | 
				
			||||||
 | 
					    <string name="m3c_bottom_sheet_collapse_description">ボトムシートを折りたたみます</string>
 | 
				
			||||||
 | 
					    <string name="m3c_bottom_sheet_dismiss_description">ボトムシートを閉じます</string>
 | 
				
			||||||
 | 
					    <string name="m3c_bottom_sheet_drag_handle_description">ドラッグハンドル</string>
 | 
				
			||||||
 | 
					    <string name="m3c_bottom_sheet_expand_description">ボトムシートを開きます</string>
 | 
				
			||||||
 | 
					    <string name="m3c_bottom_sheet_pane_title">ボトムシート</string>
 | 
				
			||||||
 | 
					    <string name="m3c_date_input_headline">入力された日付</string>
 | 
				
			||||||
 | 
					    <string name="m3c_date_input_headline_description">入力された日付: %1$s</string>
 | 
				
			||||||
 | 
					    <string name="m3c_date_input_invalid_for_pattern">想定パターンと一致しない日付: %1$s</string>
 | 
				
			||||||
 | 
					    <string name="m3c_date_input_invalid_not_allowed">許可されない日付: %1$s</string>
 | 
				
			||||||
 | 
					    <string name="m3c_date_input_invalid_year_range">想定される年の範囲(%1$s~%2$s)から日付が外れています</string>
 | 
				
			||||||
 | 
					    <string name="m3c_date_input_label">日付</string>
 | 
				
			||||||
 | 
					    <string name="m3c_date_input_no_input_description">なし</string>
 | 
				
			||||||
 | 
					    <string name="m3c_date_input_title">日付の選択</string>
 | 
				
			||||||
 | 
					    <string name="m3c_date_picker_headline">選択した日付</string>
 | 
				
			||||||
 | 
					    <string name="m3c_date_picker_headline_description">現在の選択: %1$s</string>
 | 
				
			||||||
 | 
					    <string name="m3c_date_picker_navigate_to_year_description">年に移動 %1$s</string>
 | 
				
			||||||
 | 
					    <string name="m3c_date_picker_no_selection_description">なし</string>
 | 
				
			||||||
 | 
					    <string name="m3c_date_picker_scroll_to_earlier_years">これより前の年を表示するにはスクロールしてください</string>
 | 
				
			||||||
 | 
					    <string name="m3c_date_picker_scroll_to_later_years">これより後の年を表示するにはスクロールしてください</string>
 | 
				
			||||||
 | 
					    <string name="m3c_date_picker_switch_to_calendar_mode">カレンダー入力モードに切り替え</string>
 | 
				
			||||||
 | 
					    <string name="m3c_date_picker_switch_to_day_selection">スワイプして年を選択するか、タップして日付の選択に戻ります</string>
 | 
				
			||||||
 | 
					    <string name="m3c_date_picker_switch_to_input_mode">テキスト入力モードに切り替え</string>
 | 
				
			||||||
 | 
					    <string name="m3c_date_picker_switch_to_next_month">翌月に変更</string>
 | 
				
			||||||
 | 
					    <string name="m3c_date_picker_switch_to_previous_month">前月に変更</string>
 | 
				
			||||||
 | 
					    <string name="m3c_date_picker_switch_to_year_selection">年の選択に切り替え</string>
 | 
				
			||||||
 | 
					    <string name="m3c_date_picker_title">日付の選択</string>
 | 
				
			||||||
 | 
					    <string name="m3c_date_picker_today_description">今日</string>
 | 
				
			||||||
 | 
					    <string name="m3c_date_picker_year_picker_pane_title">年の選択ツールの表示</string>
 | 
				
			||||||
 | 
					    <string name="m3c_date_range_input_invalid_range_input">入力された期間は無効です</string>
 | 
				
			||||||
 | 
					    <string name="m3c_date_range_input_title">日付の入力</string>
 | 
				
			||||||
 | 
					    <string name="m3c_date_range_picker_day_in_range">範囲内</string>
 | 
				
			||||||
 | 
					    <string name="m3c_date_range_picker_end_headline">終了日</string>
 | 
				
			||||||
 | 
					    <string name="m3c_date_range_picker_scroll_to_next_month">翌月を表示するにはスクロールしてください</string>
 | 
				
			||||||
 | 
					    <string name="m3c_date_range_picker_scroll_to_previous_month">前月を表示するにはスクロールしてください</string>
 | 
				
			||||||
 | 
					    <string name="m3c_date_range_picker_start_headline">開始日</string>
 | 
				
			||||||
 | 
					    <string name="m3c_date_range_picker_title">日付の選択</string>
 | 
				
			||||||
 | 
					    <string name="m3c_dialog">ダイアログ</string>
 | 
				
			||||||
 | 
					    <string name="m3c_dropdown_menu_collapsed">閉じています</string>
 | 
				
			||||||
 | 
					    <string name="m3c_dropdown_menu_expanded">開いています</string>
 | 
				
			||||||
 | 
					    <string name="m3c_search_bar_search">検索</string>
 | 
				
			||||||
 | 
					    <string name="m3c_snackbar_dismiss">閉じる</string>
 | 
				
			||||||
 | 
					    <string name="m3c_suggestions_available">検索候補は次のとおりです</string>
 | 
				
			||||||
 | 
					    <string name="m3c_time_picker_am">AM</string>
 | 
				
			||||||
 | 
					    <string name="m3c_time_picker_hour">時間</string>
 | 
				
			||||||
 | 
					    <string name="m3c_time_picker_hour_24h_suffix">%1$d 時間</string>
 | 
				
			||||||
 | 
					    <string name="m3c_time_picker_hour_selection">時刻を選択</string>
 | 
				
			||||||
 | 
					    <string name="m3c_time_picker_hour_suffix">"%1$d 時"</string>
 | 
				
			||||||
 | 
					    <string name="m3c_time_picker_hour_text_field">(時間単位)</string>
 | 
				
			||||||
 | 
					    <string name="m3c_time_picker_minute">分</string>
 | 
				
			||||||
 | 
					    <string name="m3c_time_picker_minute_selection">分を選択</string>
 | 
				
			||||||
 | 
					    <string name="m3c_time_picker_minute_suffix">%1$d 分</string>
 | 
				
			||||||
 | 
					    <string name="m3c_time_picker_minute_text_field">(分単位)</string>
 | 
				
			||||||
 | 
					    <string name="m3c_time_picker_period_toggle_description">午前または午後を選択</string>
 | 
				
			||||||
 | 
					    <string name="m3c_time_picker_pm">PM</string>
 | 
				
			||||||
 | 
					    <string name="m3c_tooltip_long_press_label">ツールチップを表示</string>
 | 
				
			||||||
 | 
					    <string name="m3c_tooltip_pane_description">ツールチップ</string>
 | 
				
			||||||
 | 
					    <string name="material_clock_display_divider">:</string>
 | 
				
			||||||
 | 
					    <string name="material_clock_toggle_content_description">午前または午後を選択</string>
 | 
				
			||||||
 | 
					    <string name="material_hour_24h_suffix">%1$s 時間</string>
 | 
				
			||||||
 | 
					    <string name="material_hour_selection">時刻を選択してください</string>
 | 
				
			||||||
 | 
					    <string name="material_hour_suffix">"%1$s時"</string>
 | 
				
			||||||
 | 
					    <string name="material_minute_selection">分を選択</string>
 | 
				
			||||||
 | 
					    <string name="material_minute_suffix">%1$s分</string>
 | 
				
			||||||
 | 
					    <string name="material_motion_easing_accelerated">cubic-bezier(0.4, 0.0, 1.0, 1.0)</string>
 | 
				
			||||||
 | 
					    <string name="material_motion_easing_decelerated">cubic-bezier(0.0, 0.0, 0.2, 1.0)</string>
 | 
				
			||||||
 | 
					    <string name="material_motion_easing_emphasized">path(M 0,0 C 0.05, 0, 0.133333, 0.06, 0.166666, 0.4 C 0.208333, 0.82, 0.25, 1, 1, 1)</string>
 | 
				
			||||||
 | 
					    <string name="material_motion_easing_linear">cubic-bezier(0.0, 0.0, 1.0, 1.0)</string>
 | 
				
			||||||
 | 
					    <string name="material_motion_easing_standard">cubic-bezier(0.4, 0.0, 0.2, 1.0)</string>
 | 
				
			||||||
 | 
					    <string name="material_slider_range_end">Range end</string>
 | 
				
			||||||
 | 
					    <string name="material_slider_range_start">Range start</string>
 | 
				
			||||||
 | 
					    <string name="material_slider_value">Value</string>
 | 
				
			||||||
 | 
					    <string name="material_timepicker_am">AM</string>
 | 
				
			||||||
 | 
					    <string name="material_timepicker_clock_mode_description">時刻を時計で入力するモードに切り替えます。</string>
 | 
				
			||||||
 | 
					    <string name="material_timepicker_hour">時間</string>
 | 
				
			||||||
 | 
					    <string name="material_timepicker_minute">分</string>
 | 
				
			||||||
 | 
					    <string name="material_timepicker_pm">PM</string>
 | 
				
			||||||
 | 
					    <string name="material_timepicker_select_time">時間を選択</string>
 | 
				
			||||||
 | 
					    <string name="material_timepicker_text_input_mode_description">時刻をテキストで入力するモードに切り替えます。</string>
 | 
				
			||||||
 | 
					    <string name="max_high">ウルトラ</string>
 | 
				
			||||||
 | 
					    <string name="middle">中</string>
 | 
				
			||||||
 | 
					    <string name="mtrl_badge_numberless_content_description">新しい通知</string>
 | 
				
			||||||
 | 
					    <string name="mtrl_checkbox_button_icon_path_checked">M14,18.2 11.4,15.6 10,17 14,21 22,13 20.6,11.6z</string>
 | 
				
			||||||
 | 
					    <string name="mtrl_checkbox_button_icon_path_group_name">icon</string>
 | 
				
			||||||
 | 
					    <string name="mtrl_checkbox_button_icon_path_indeterminate">M13.4,15 11,15 11,17 13.4,17 21,17 21,15z</string>
 | 
				
			||||||
 | 
					    <string name="mtrl_checkbox_button_icon_path_name">icon path</string>
 | 
				
			||||||
 | 
					    <string name="mtrl_checkbox_button_path_checked">M23,7H9C7.9,7,7,7.9,7,9v14c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V9C25,7.9,24.1,7,23,7z</string>
 | 
				
			||||||
 | 
					    <string name="mtrl_checkbox_button_path_group_name">button</string>
 | 
				
			||||||
 | 
					    <string name="mtrl_checkbox_button_path_name">button path</string>
 | 
				
			||||||
 | 
					    <string name="mtrl_checkbox_button_path_unchecked">M23,7H9C7.9,7,7,7.9,7,9v14c0,1.1,0.9,2,2,2h14c1.1,0,2-0.9,2-2V9C25,7.9,24.1,7,23,7z M23,23H9V9h14V23z</string>
 | 
				
			||||||
 | 
					    <string name="mtrl_checkbox_state_description_checked">オン</string>
 | 
				
			||||||
 | 
					    <string name="mtrl_checkbox_state_description_indeterminate">一部オン</string>
 | 
				
			||||||
 | 
					    <string name="mtrl_checkbox_state_description_unchecked">オフ</string>
 | 
				
			||||||
 | 
					    <string name="mtrl_chip_close_icon_content_description">%1$s を削除します</string>
 | 
				
			||||||
 | 
					    <string name="mtrl_exceed_max_badge_number_content_description">%1$d 件以上の新しい通知</string>
 | 
				
			||||||
 | 
					    <string name="mtrl_exceed_max_badge_number_suffix">%1$d%2$s</string>
 | 
				
			||||||
 | 
					    <string name="mtrl_picker_a11y_next_month">翌月に変更</string>
 | 
				
			||||||
 | 
					    <string name="mtrl_picker_a11y_prev_month">前月に変更</string>
 | 
				
			||||||
 | 
					    <string name="mtrl_picker_announce_current_range_selection">開始日の選択: %1$s – 終了日の選択: %2$s</string>
 | 
				
			||||||
 | 
					    <string name="mtrl_picker_announce_current_selection">現在の選択: %1$s</string>
 | 
				
			||||||
 | 
					    <string name="mtrl_picker_announce_current_selection_none">なし</string>
 | 
				
			||||||
 | 
					    <string name="mtrl_picker_cancel">キャンセル</string>
 | 
				
			||||||
 | 
					    <string name="mtrl_picker_confirm">OK</string>
 | 
				
			||||||
 | 
					    <string name="mtrl_picker_date_header_selected">%1$s</string>
 | 
				
			||||||
 | 
					    <string name="mtrl_picker_date_header_title">日付を選択してください</string>
 | 
				
			||||||
 | 
					    <string name="mtrl_picker_date_header_unselected">選択した日付</string>
 | 
				
			||||||
 | 
					    <string name="mtrl_picker_day_of_week_column_header">%1$s</string>
 | 
				
			||||||
 | 
					    <string name="mtrl_picker_end_date_description">終了日 %1$s</string>
 | 
				
			||||||
 | 
					    <string name="mtrl_picker_invalid_format">形式が無効です。</string>
 | 
				
			||||||
 | 
					    <string name="mtrl_picker_invalid_format_example">例: %1$s</string>
 | 
				
			||||||
 | 
					    <string name="mtrl_picker_invalid_format_use">使用: %1$s</string>
 | 
				
			||||||
 | 
					    <string name="mtrl_picker_invalid_range">範囲が無効です。</string>
 | 
				
			||||||
 | 
					    <string name="mtrl_picker_navigate_to_current_year_description">現在の年(%1$d)に移動</string>
 | 
				
			||||||
 | 
					    <string name="mtrl_picker_navigate_to_year_description">%1$d 年に移動</string>
 | 
				
			||||||
 | 
					    <string name="mtrl_picker_out_of_range">範囲外: %1$s</string>
 | 
				
			||||||
 | 
					    <string name="mtrl_picker_range_header_only_end_selected">開始日~%1$s</string>
 | 
				
			||||||
 | 
					    <string name="mtrl_picker_range_header_only_start_selected">%1$s~終了日</string>
 | 
				
			||||||
 | 
					    <string name="mtrl_picker_range_header_selected">%1$s~%2$s</string>
 | 
				
			||||||
 | 
					    <string name="mtrl_picker_range_header_title">期間を選択してください</string>
 | 
				
			||||||
 | 
					    <string name="mtrl_picker_range_header_unselected">開始日~終了日</string>
 | 
				
			||||||
 | 
					    <string name="mtrl_picker_save">保存</string>
 | 
				
			||||||
 | 
					    <string name="mtrl_picker_start_date_description">開始日 %1$s</string>
 | 
				
			||||||
 | 
					    <string name="mtrl_picker_text_input_date_hint">日付</string>
 | 
				
			||||||
 | 
					    <string name="mtrl_picker_text_input_date_range_end_hint">終了日</string>
 | 
				
			||||||
 | 
					    <string name="mtrl_picker_text_input_date_range_start_hint">開始日</string>
 | 
				
			||||||
 | 
					    <string name="mtrl_picker_text_input_day_abbr">d</string>
 | 
				
			||||||
 | 
					    <string name="mtrl_picker_text_input_month_abbr">m</string>
 | 
				
			||||||
 | 
					    <string name="mtrl_picker_text_input_year_abbr">y</string>
 | 
				
			||||||
 | 
					    <string name="mtrl_picker_today_description">今日(%1$s)</string>
 | 
				
			||||||
 | 
					    <string name="mtrl_picker_toggle_to_calendar_input_mode">カレンダー入力モードに切り替え</string>
 | 
				
			||||||
 | 
					    <string name="mtrl_picker_toggle_to_day_selection">タップするとカレンダー表示に切り替わります</string>
 | 
				
			||||||
 | 
					    <string name="mtrl_picker_toggle_to_text_input_mode">テキスト入力モードに切り替え</string>
 | 
				
			||||||
 | 
					    <string name="mtrl_picker_toggle_to_year_selection">タップすると年表示に切り替わります</string>
 | 
				
			||||||
 | 
					    <string name="mtrl_switch_thumb_group_name">circle_group</string>
 | 
				
			||||||
 | 
					    <string name="mtrl_switch_thumb_path_checked">M4,16 A12,12 0 0,1 16,4 H16 A12,12 0 0,1 16,28 H16 A12,12 0 0,1 4,16</string>
 | 
				
			||||||
 | 
					    <string name="mtrl_switch_thumb_path_morphing">M0,16 A11,11 0 0,1 11,5 H21 A11,11 0 0,1 21,27 H11 A11,11 0 0,1 0,16</string>
 | 
				
			||||||
 | 
					    <string name="mtrl_switch_thumb_path_name">circle</string>
 | 
				
			||||||
 | 
					    <string name="mtrl_switch_thumb_path_pressed">M2,16 A14,14 0 0,1 16,2 H16 A14,14 0 0,1 16,30 H16 A14,14 0 0,1 2,16</string>
 | 
				
			||||||
 | 
					    <string name="mtrl_switch_thumb_path_unchecked">M8,16 A8,8 0 0,1 16,8 H16 A8,8 0 0,1 16,24 H16 A8,8 0 0,1 8,16</string>
 | 
				
			||||||
 | 
					    <string name="mtrl_switch_track_decoration_path">M1,16 A15,15 0 0,1 16,1 H36 A15,15 0 0,1 36,31 H16 A15,15 0 0,1 1,16</string>
 | 
				
			||||||
 | 
					    <string name="mtrl_switch_track_path">M0,16 A16,16 0 0,1 16,0 H36 A16,16 0 0,1 36,32 H16 A16,16 0 0,1 0,16</string>
 | 
				
			||||||
 | 
					    <string name="mtrl_timepicker_cancel">キャンセル</string>
 | 
				
			||||||
 | 
					    <string name="mtrl_timepicker_confirm">OK</string>
 | 
				
			||||||
 | 
					    <string name="navigation_menu">ナビゲーションメニュー</string>
 | 
				
			||||||
 | 
					    <string name="not_selected">未選択</string>
 | 
				
			||||||
 | 
					    <string name="off">OFF</string>
 | 
				
			||||||
 | 
					    <string name="ok">OK</string>
 | 
				
			||||||
 | 
					    <string name="on">ON</string>
 | 
				
			||||||
 | 
					    <string name="orientation_landscape">横画面</string>
 | 
				
			||||||
 | 
					    <string name="orientation_lock">画面を固定</string>
 | 
				
			||||||
 | 
					    <string name="orientation_orig">オリジナル</string>
 | 
				
			||||||
 | 
					    <string name="orientation_portrait">縦画面</string>
 | 
				
			||||||
 | 
					    <string name="password_toggle_content_description">パスワードを表示</string>
 | 
				
			||||||
 | 
					    <string name="patch_debuggable">デバッグを可能にする</string>
 | 
				
			||||||
 | 
					    <string name="patch_finished">パッチが完了しました。インストールをしますか?</string>
 | 
				
			||||||
 | 
					    <string name="patch_integrated">統合</string>
 | 
				
			||||||
 | 
					    <string name="patch_integrated_desc">"モジュールを埋め込んだ状態なアプリでパッチを当てます。
 | 
				
			||||||
 | 
					パッチを適用したアプリは LSPatch Manager なしで実行できますが、動的に管理はできません。
 | 
				
			||||||
 | 
					統合パッチが適用されたアプリは、LSPatch Manager がインストールされていないデバイスでも使用が可能です。"</string>
 | 
				
			||||||
 | 
					    <string name="patch_local">ローカル</string>
 | 
				
			||||||
 | 
					    <string name="patch_local_desc">"モジュールを埋め込まずにアプリにパッチを当てます。
 | 
				
			||||||
 | 
					Xposed スコープは再パッチなしで動的に変更が可能です。
 | 
				
			||||||
 | 
					ローカルでのパッチを当てたアプリは、ローカルのデバイスでのみ実行可能です。"</string>
 | 
				
			||||||
 | 
					    <string name="patch_mode">パッチモード</string>
 | 
				
			||||||
 | 
					    <string name="patch_uninstall_confirm">アンインストールをしてもよろしいですか?</string>
 | 
				
			||||||
 | 
					    <string name="patch_uninstall_text">"署名が異なるため、パッチをインストールする前に元となるアプリをアンインストールする必要があります。
 | 
				
			||||||
 | 
					個人データのバックアップを設定済みであることを確認してください。"</string>
 | 
				
			||||||
 | 
					    <string name="path_password_eye">M12,4.5C7,4.5 2.73,7.61 1,12c1.73,4.39 6,7.5 11,7.5s9.27,-3.11 11,-7.5c-1.73,-4.39 -6,-7.5 -11,-7.5zM12,17c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5zM12,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3 3,-1.34 3,-3 -1.34,-3 -3,-3z</string>
 | 
				
			||||||
 | 
					    <string name="path_password_eye_mask_strike_through">M2,4.27 L19.73,22 L22.27,19.46 L4.54,1.73 L4.54,1 L23,1 L23,23 L1,23 L1,4.27 Z</string>
 | 
				
			||||||
 | 
					    <string name="path_password_eye_mask_visible">M2,4.27 L2,4.27 L4.54,1.73 L4.54,1.73 L4.54,1 L23,1 L23,23 L1,23 L1,4.27 Z</string>
 | 
				
			||||||
 | 
					    <string name="path_password_strike_through">M3.27,4.27 L19.74,20.74</string>
 | 
				
			||||||
 | 
					    <string name="pendulum">揺れ</string>
 | 
				
			||||||
 | 
					    <string name="pendulumrange">揺れの範囲</string>
 | 
				
			||||||
 | 
					    <string name="plugin_code">プラグインのコード</string>
 | 
				
			||||||
 | 
					    <string name="project_contribution">プロジェクトの貢献者</string>
 | 
				
			||||||
 | 
					    <string name="range_end">範囲の終了</string>
 | 
				
			||||||
 | 
					    <string name="range_start">範囲の開始</string>
 | 
				
			||||||
 | 
					    <string name="renderscale">RenderScale (0.5/0.59/0.67/0.77/1.0)</string>
 | 
				
			||||||
 | 
					    <string name="replace_font">フォントを置換する</string>
 | 
				
			||||||
 | 
					    <string name="reserve_patched">パッチ済みの APK を予約する</string>
 | 
				
			||||||
 | 
					    <string name="resource_settings">リソース設定</string>
 | 
				
			||||||
 | 
					    <string name="resource_url">リソース URL</string>
 | 
				
			||||||
 | 
					    <string name="rootweight">ルートウエイト</string>
 | 
				
			||||||
 | 
					    <string name="search_menu_title">検索</string>
 | 
				
			||||||
 | 
					    <string name="searchbar_scrolling_view_behavior">com.google.android.material.search.SearchBar$ScrollingViewBehavior</string>
 | 
				
			||||||
 | 
					    <string name="searchview_clear_text_content_description">テキストを消去</string>
 | 
				
			||||||
 | 
					    <string name="searchview_navigation_content_description">戻る</string>
 | 
				
			||||||
 | 
					    <string name="selected">選択済み</string>
 | 
				
			||||||
 | 
					    <string name="setFpsTitle">最大 FPS (0 はオリジナルの設定を使用します)</string>
 | 
				
			||||||
 | 
					    <string name="shizuku_available">Shizuku サービスが有効です</string>
 | 
				
			||||||
 | 
					    <string name="shizuku_unavailable">Shizuku サービスが接続されていません</string>
 | 
				
			||||||
 | 
					    <string name="side_sheet_accessibility_pane_title">サイドシート</string>
 | 
				
			||||||
 | 
					    <string name="side_sheet_behavior">com.google.android.material.sidesheet.SideSheetBehavior</string>
 | 
				
			||||||
 | 
					    <string name="spring">跳ね</string>
 | 
				
			||||||
 | 
					    <string name="start_game">ゲーム開始 / ホットリロードの設定</string>
 | 
				
			||||||
 | 
					    <string name="status_bar_notification_info_overflow">999+</string>
 | 
				
			||||||
 | 
					    <string name="stiffness">剛性</string>
 | 
				
			||||||
 | 
					    <string name="support_file_types">"対応ファイル:
 | 
				
			||||||
 | 
					単一または複数選択: apk
 | 
				
			||||||
 | 
					単一選択: apks、xapk、zip"</string>
 | 
				
			||||||
 | 
					    <string name="switch_role">切り替え</string>
 | 
				
			||||||
 | 
					    <string name="tab">タブ</string>
 | 
				
			||||||
 | 
					    <string name="template_percent">%1$d パーセント。</string>
 | 
				
			||||||
 | 
					    <string name="test_mode_live">テストモード - ライブ</string>
 | 
				
			||||||
 | 
					    <string name="text_hook_test_mode">テキストフックテストモード</string>
 | 
				
			||||||
 | 
					    <string name="tooltip_description">ツールチップ</string>
 | 
				
			||||||
 | 
					    <string name="tooltip_label">ツールチップを表示</string>
 | 
				
			||||||
 | 
					    <string name="translation_repository">翻訳のリポジトリ</string>
 | 
				
			||||||
 | 
					    <string name="translation_resource_update">翻訳リソースを更新</string>
 | 
				
			||||||
 | 
					    <string name="unlockAllLive">すべてのライブを開放</string>
 | 
				
			||||||
 | 
					    <string name="unlockAllLiveCostume">すべてのライブ衣装を開放</string>
 | 
				
			||||||
 | 
					    <string name="useCustomeGraphicSettings">カスタムグラフィック設定を使用する</string>
 | 
				
			||||||
 | 
					    <string name="useMasterDBTrans">MasterDB のローカライズを有効化</string>
 | 
				
			||||||
 | 
					    <string name="use_remote_zip_resource">リモート ZIP リソースを使用する</string>
 | 
				
			||||||
 | 
					    <string name="usearmcorrection">Arm コレクションを使用する</string>
 | 
				
			||||||
 | 
					    <string name="uselimit_0_1">リミットレンジの倍率 (0 は無制限)</string>
 | 
				
			||||||
 | 
					    <string name="uselimitmultiplier">乗数制限を使用する</string>
 | 
				
			||||||
 | 
					    <string name="usescale">胸の大きさを使用する</string>
 | 
				
			||||||
 | 
					    <string name="very_high">最高</string>
 | 
				
			||||||
 | 
					    <string name="warning">警告</string>
 | 
				
			||||||
 | 
					</resources>
 | 
				
			||||||
| 
						 | 
					@ -8,14 +8,17 @@
 | 
				
			||||||
    <string name="start_game">以上述配置启动游戏/重载配置</string>
 | 
					    <string name="start_game">以上述配置启动游戏/重载配置</string>
 | 
				
			||||||
    <string name="setFpsTitle">最大 FPS (0 为保持游戏原设置)</string>
 | 
					    <string name="setFpsTitle">最大 FPS (0 为保持游戏原设置)</string>
 | 
				
			||||||
    <string name="unlockAllLive">解锁所有 Live</string>
 | 
					    <string name="unlockAllLive">解锁所有 Live</string>
 | 
				
			||||||
 | 
					    <string name="unlockAllLiveCostume">解锁所有 Live 服装</string>
 | 
				
			||||||
    <string name="liveUseCustomeDress">Live 使用自定义角色</string>
 | 
					    <string name="liveUseCustomeDress">Live 使用自定义角色</string>
 | 
				
			||||||
    <string name="live_costume_head_id">Live 自定义头部 ID (例: costume_head_hski-cstm-0002)</string>
 | 
					    <string name="live_costume_head_id">Live 自定义头部 ID (例: costume_head_hski-cstm-0002)</string>
 | 
				
			||||||
    <string name="live_custome_dress_id">Live 自定义服装 ID (例: hski-cstm-0002)</string>
 | 
					    <string name="live_custome_dress_id">Live 自定义服装 ID (例: hski-cstm-0002)</string>
 | 
				
			||||||
    <string name="useCustomeGraphicSettings">使用自定义画质设置</string>
 | 
					    <string name="useCustomeGraphicSettings">使用自定义画质设置</string>
 | 
				
			||||||
    <string name="renderscale">RenderScale (0.5/0.59/0.67/0.77/1.0)</string>
 | 
					    <string name="renderscale">RenderScale (0.5/0.59/0.67/0.77/1.0)</string>
 | 
				
			||||||
    <string name="text_hook_test_mode">文本 hook 测试模式</string>
 | 
					    <string name="text_hook_test_mode">文本 hook 测试模式</string>
 | 
				
			||||||
 | 
					    <string name="useMasterDBTrans">使用 MasterDB 本地化</string>
 | 
				
			||||||
    <string name="export_text">导出文本</string>
 | 
					    <string name="export_text">导出文本</string>
 | 
				
			||||||
    <string name="force_export_resource">启动后强制导出资源</string>
 | 
					    <string name="force_export_resource">启动后强制导出资源</string>
 | 
				
			||||||
 | 
					    <string name="login_as_ios">以 iOS 登陆</string>
 | 
				
			||||||
    <string name="max_high">极高</string>
 | 
					    <string name="max_high">极高</string>
 | 
				
			||||||
    <string name="very_high">超高</string>
 | 
					    <string name="very_high">超高</string>
 | 
				
			||||||
    <string name="hign">高</string>
 | 
					    <string name="hign">高</string>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -8,14 +8,17 @@
 | 
				
			||||||
    <string name="start_game">Start Game / Hot Reload Config</string>
 | 
					    <string name="start_game">Start Game / Hot Reload Config</string>
 | 
				
			||||||
    <string name="setFpsTitle">Max FPS (0 is Use Original Settings)</string>
 | 
					    <string name="setFpsTitle">Max FPS (0 is Use Original Settings)</string>
 | 
				
			||||||
    <string name="unlockAllLive">Unlock All Live</string>
 | 
					    <string name="unlockAllLive">Unlock All Live</string>
 | 
				
			||||||
 | 
					    <string name="unlockAllLiveCostume">Unlock All Live Costume</string>
 | 
				
			||||||
    <string name="liveUseCustomeDress">Live Custom Character</string>
 | 
					    <string name="liveUseCustomeDress">Live Custom Character</string>
 | 
				
			||||||
    <string name="live_costume_head_id">Live Custom Head ID (eg. costume_head_hski-cstm-0002)</string>
 | 
					    <string name="live_costume_head_id">Live Custom Head ID (eg. costume_head_hski-cstm-0002)</string>
 | 
				
			||||||
    <string name="live_custome_dress_id">Live Custom Dress ID (eg. hski-cstm-0002)</string>
 | 
					    <string name="live_custome_dress_id">Live Custom Dress ID (eg. hski-cstm-0002)</string>
 | 
				
			||||||
    <string name="useCustomeGraphicSettings">Use Custom Graphics Settings</string>
 | 
					    <string name="useCustomeGraphicSettings">Use Custom Graphics Settings</string>
 | 
				
			||||||
    <string name="renderscale">RenderScale (0.5/0.59/0.67/0.77/1.0)</string>
 | 
					    <string name="renderscale">RenderScale (0.5/0.59/0.67/0.77/1.0)</string>
 | 
				
			||||||
    <string name="text_hook_test_mode">Text Hook Test Mode</string>
 | 
					    <string name="text_hook_test_mode">Text Hook Test Mode</string>
 | 
				
			||||||
 | 
					    <string name="useMasterDBTrans">Enable MasterDB Localization</string>
 | 
				
			||||||
    <string name="export_text">Export Text</string>
 | 
					    <string name="export_text">Export Text</string>
 | 
				
			||||||
    <string name="force_export_resource">Force Update Resource</string>
 | 
					    <string name="force_export_resource">Force Update Resource</string>
 | 
				
			||||||
 | 
					    <string name="login_as_ios">Login as iOS</string>
 | 
				
			||||||
    <string name="max_high">Ultra</string>
 | 
					    <string name="max_high">Ultra</string>
 | 
				
			||||||
    <string name="very_high">Very High</string>
 | 
					    <string name="very_high">Very High</string>
 | 
				
			||||||
    <string name="hign">High</string>
 | 
					    <string name="hign">High</string>
 | 
				
			||||||
| 
						 | 
					@ -45,7 +48,7 @@
 | 
				
			||||||
    <string name="axisx_y">axisX.y</string>
 | 
					    <string name="axisx_y">axisX.y</string>
 | 
				
			||||||
    <string name="axisy_y">axisY.y</string>
 | 
					    <string name="axisy_y">axisY.y</string>
 | 
				
			||||||
    <string name="axisz_y">axisZ.y</string>
 | 
					    <string name="axisz_y">axisZ.y</string>
 | 
				
			||||||
    <string name="basic_settings">Basic Ssettings</string>
 | 
					    <string name="basic_settings">Basic Settings</string>
 | 
				
			||||||
    <string name="graphic_settings">Graphic Settings</string>
 | 
					    <string name="graphic_settings">Graphic Settings</string>
 | 
				
			||||||
    <string name="camera_settings">Camera Settings</string>
 | 
					    <string name="camera_settings">Camera Settings</string>
 | 
				
			||||||
    <string name="test_mode_live">Test Mode - LIVE</string>
 | 
					    <string name="test_mode_live">Test Mode - LIVE</string>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -17,7 +17,7 @@ lifecycle = "2.8.2"
 | 
				
			||||||
material = "1.12.0"
 | 
					material = "1.12.0"
 | 
				
			||||||
navigationCompose = "2.7.7"
 | 
					navigationCompose = "2.7.7"
 | 
				
			||||||
xdl = "2.1.1"
 | 
					xdl = "2.1.1"
 | 
				
			||||||
shadowhook = "1.0.9"
 | 
					shadowhook = "1.0.10"
 | 
				
			||||||
serialization="1.7.1"
 | 
					serialization="1.7.1"
 | 
				
			||||||
zip4j = "2.9.1"
 | 
					zip4j = "2.9.1"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue