Jelajahi Sumber

Version 3.0.2

 * BUG FIX - UnityInjector installer package now uses correct folder structure (Translators has been moved into Config folder) and ExIni is no longer distributed
 * BUG FIX - Fixed harmony priority usage, which was incorrectly used in 3.0.1
 * MISC - Plugin should no longer translate text input fields for NGUI
 * MISC - Added config option to 'DisableCertificateValidation' for all hosts under all circumstances in case the plugin locks up. This option is only required by very few games
 * MISC - Experimental hooking support for methods with no body (configured through 'EnableExperimentalHooks' setting)
 * MISC - Restructured README file. New order: 1. Installation, 2. Usage, 3. Configuration, 4. Integration
randoman 6 tahun lalu
induk
melakukan
c6167878f8

+ 9 - 1
CHANGELOG.md

@@ -1,4 +1,12 @@
-### 3.0.1
+### 3.0.2
+ * BUG FIX - UnityInjector installer package now uses correct folder structure (Translators has been moved into Config folder) and ExIni is no longer distributed
+ * BUG FIX - Fixed harmony priority usage, which was incorrectly used in 3.0.1
+ * MISC - Plugin should no longer translate text input fields for NGUI
+ * MISC - Added config option to 'DisableCertificateValidation' for all hosts under all circumstances in case the plugin locks up. This option is only required by very few games
+ * MISC - Experimental hooking support for methods with no body (configured through 'EnableExperimentalHooks' setting)
+ * MISC - Restructured README file. New order: 1. Installation, 2. Usage, 3. Configuration, 4. Integration 
+
+### 3.0.1
  * BUG FIX - Fixed bug that could in certain situation cause IMGUI translation to drain on performance
  * BUG FIX - Never close a service point while a request is ongoing. Previously this could cause the plugin to lockup
  * BUG FIX - Only disable certificate checks if the .NET version is at or below 3.5

+ 108 - 101
README.md

@@ -2,12 +2,12 @@
 
 ## Index
  * [Introduction](#introduction)
+ * [Installation](#installation)
+ * [Key Mapping](#key-mapping)
  * [Translators](#translators)
  * [Text Frameworks](#text-frameworks)
  * [Plugin Frameworks](#plugin-frameworks)
  * [Configuration](#configuration)
- * [Key Mapping](#key-mapping)
- * [Installation](#installation)
  * [Translating Mods](#translating-mods)
  * [Manual Translations](#manual-translations)
  * [Regarding Redistribution](#regarding-redistribution)
@@ -26,6 +26,98 @@ If you intend on redistributing this plugin as part of a translation suite for a
 
 From version 3.0.0 it is possible to implement custom translators. See [this section](#implementing-a-translator) for more info.
 
+## Installation
+The plugin can be installed in following ways:
+
+### BepInEx Plugin
+REQUIRES: [BepInEx plugin manager](https://github.com/BepInEx/BepInEx) (follow its installation instructions first!). 
+
+ 1. Download XUnity.AutoTranslator-BepIn-{VERSION}.zip from [releases](../../releases).
+ 2. Extract directly into the game directory, such that the plugin dlls are placed in BepInEx folder.
+
+The file structure should like like this:
+```
+{GameDirectory}/BepInEx/XUnity.AutoTranslator.Plugin.Core.dll
+{GameDirectory}/BepInEx/XUnity.AutoTranslator.Plugin.BepInEx.dll
+{GameDirectory}/BepInEx/XUnity.AutoTranslator.Plugin.ExtProtocol.dll
+{GameDirectory}/BepInEx/ExIni.dll
+{GameDirectory}/BepInEx/Translators/{Translator}.dll
+{GameDirectory}/BepInEx/Translation/AnyTranslationFile.txt (these files will be auto generated by plugin!)
+```
+
+### IPA Plugin
+REQUIRES: [IPA plugin manager](https://github.com/Eusth/IPA) (follow its installation instructions first!).
+
+ 1. Download XUnity.AutoTranslator-IPA-{VERSION}.zip from [releases](../../releases).
+ 2. Extract directly into the game directory, such that the plugin dlls are placed in Plugins folder.
+
+The file structure should like like this
+```
+{GameDirectory}/Plugins/XUnity.AutoTranslator.Plugin.Core.dll
+{GameDirectory}/Plugins/XUnity.AutoTranslator.Plugin.IPA.dll
+{GameDirectory}/Plugins/XUnity.AutoTranslator.Plugin.ExtProtocol.dll
+{GameDirectory}/Plugins/0Harmony.dll
+{GameDirectory}/Plugins/ExIni.dll
+{GameDirectory}/Plugins/Translators/{Translator}.dll
+{GameDirectory}/Plugins/Translation/AnyTranslationFile.txt (these files will be auto generated by plugin!)
+ ```
+
+### UnityInjector Plugin
+REQUIRES: UnityInjector (follow its installation instructions first!).
+
+ 1. Download XUnity.AutoTranslator-UnityInjector-{VERSION}.zip from [releases](../../releases).
+ 2. Extract directly into the game directory, such that the plugin dlls are placed in UnityInjector folder. **This may not be game root directory!**
+
+The file structure should like like this
+```
+{GameDirectory}/UnityInjector/XUnity.AutoTranslator.Plugin.Core.dll
+{GameDirectory}/UnityInjector/XUnity.AutoTranslator.Plugin.UnityInjector.dll
+{GameDirectory}/UnityInjector/XUnity.AutoTranslator.Plugin.ExtProtocol.dll
+{GameDirectory}/UnityInjector/0Harmony.dll
+{GameDirectory}/UnityInjector/Config/Translators/{Translator}.dll
+{GameDirectory}/UnityInjector/Config/Translation/AnyTranslationFile.txt (these files will be auto generated by plugin!)
+ ```
+ 
+### Standalone Installation (ReiPatcher)
+REQUIRES: Nothing, ReiPatcher is provided by this download.
+
+*NOTE: Only use this installation method if you are not using one of the above plugin managers!*
+
+ 1. Download XUnity.AutoTranslator-ReiPatcher-{VERSION}.zip from [releases](../../releases).
+ 2. Extract directly into the game directory, such that "SetupReiPatcherAndAutoTranslator.exe" is placed alongside other exe files.
+ 3. Execute "SetupReiPatcherAndAutoTranslator.exe". This will setup up ReiPatcher correctly.
+ 4. Execute the shortcut {GameExeName} (Patch and Run).lnk that was created besides existing executables. This will patch and launch the game.
+ 5. From now on you can launch the game from the {GameExeName}.exe instead.
+
+The file structure should like like this
+```
+{GameDirectory}/ReiPatcher/Patches/XUnity.AutoTranslator.Patcher.dll
+{GameDirectory}/ReiPatcher/ExIni.dll
+{GameDirectory}/ReiPatcher/Mono.Cecil.dll
+{GameDirectory}/ReiPatcher/Mono.Cecil.Inject.dll
+{GameDirectory}/ReiPatcher/Mono.Cecil.Mdb.dll
+{GameDirectory}/ReiPatcher/Mono.Cecil.Pdb.dll
+{GameDirectory}/ReiPatcher/Mono.Cecil.Rocks.dll
+{GameDirectory}/ReiPatcher/ReiPatcher.exe
+{GameDirectory}/{GameExeName}_Data/Managed/ReiPatcher.exe
+{GameDirectory}/{GameExeName}_Data/Managed/XUnity.AutoTranslator.Plugin.Core.dll
+{GameDirectory}/{GameExeName}_Data/Managed/XUnity.AutoTranslator.Plugin.ExtProtocol.dll
+{GameDirectory}/{GameExeName}_Data/Managed/0Harmony.dll
+{GameDirectory}/{GameExeName}_Data/Managed/ExIni.dll
+{GameDirectory}/AutoTranslator/Translators/{Translator}.dll
+{GameDirectory}/AutoTranslator/Translation/AnyTranslationFile.txt (these files will be auto generated by plugin!)
+ ```
+ 
+## Key Mapping
+The following key inputs are mapped:
+ * ALT + 0: Toggle XUnity AutoTranslator UI. (That's a zero, not an O)
+ * ALT + T: Alternate between translated and untranslated versions of all texts provided by this plugin.
+ * ALT + D: Dump untranslated texts (if no endpoint is configured)
+ * ALT + R: Reload translation files. Useful if you change the text and texture files on the fly. Not guaranteed to work for all textures.
+ * ALT + U: Manual hooking. The default hooks wont always pick up texts. This will attempt to make lookups manually.
+ * ALT + F: If OverrideFont is configured, will toggle between overridden and default font.
+ * ALT + Q: Reboot the plugin if it was shutdown. This will only work if the plugin was shut down due to consecutive errors towards the translation endpoint. Should only be used if you have reason to believe you have remedied the problem (such as changed VPN endpoint etc.) otherwise it will just shut down again.
+
 ## Translators
 The supported translators are:
  * [GoogleTranslate](https://anonym.to/?https://translate.google.com/), based on the online Google translation service. Does not require authentication.
@@ -52,7 +144,7 @@ The supported translators are:
    * Example Request: GET http://my-custom-translation-service.net/translate?from=ja&to=en&text=こんにちは
    * Example Response (only body): Hello
  
-**Do note that if you use any of the online translators that does not require some form of authentication, that this plugin may break at any time.**
+*NOTE: If you use any of the online translators that does not require some form of authentication, that this plugin may break at any time.*
 
 Since 3.0.0, you can also implement your own translators. To do so, follow the instruction [here](#implementing-a-translator).
 
@@ -143,6 +235,7 @@ TextGetterCompatibilityMode=False ;Indicates whether or not to enable "Text Gett
 GameLogTextPaths=                ;Indicates specific paths for game objects that the game uses as "log components", where it continuously appends or prepends text to. Requires expert knowledge to setup. This is a list seperated by ';'.
 RomajiPostProcessing=ReplaceMacronWithCircumflex;RemoveApostrophes ;Indicates what type of post processing to do on 'translated' romaji texts. This can be important in certain games because the font used does not support various diacritics properly. This is a list seperated by ';'. Possible values: ["RemoveAllDiacritics", "ReplaceMacronWithCircumflex", "RemoveApostrophes"]
 TranslationPostProcessing=ReplaceMacronWithCircumflex ;Indicates what type of post processing to do on translated texts (not romaji). Possible values: ["RemoveAllDiacritics", "ReplaceMacronWithCircumflex", "RemoveApostrophes"]
+EnableExperimentalHooks=False    ;Indicates whether to use experimental hooks to improve the hooking capability of the plugin (currently being tested)
 
 [Texture]
 TextureDirectory=Translation\Texture ;Directory to dump textures to, and root of directories to load images from. Can use placeholder: {GameExeName}
@@ -156,6 +249,7 @@ TextureHashGenerationStrategy=FromImageName ;Indicates how the mod identifies pi
 
 [Http]
 UserAgent=                       ;Override the user agent used by APIs requiring a user agent
+DisableCertificateValidation=False ;Indiciates whether certificate validations for the .NET API should be disabled
 
 [GoogleLegitimate]
 GoogleAPIKey=                    ;OPTIONAL, needed if GoogleTranslateLegitimate is configured
@@ -246,100 +340,11 @@ This type of post processing is also applied to normal translations, but instead
  * `CopyToClipboard`: Copy text to translate to the clipboard to support tools such as Translation Aggregator.
  * `Delay`: Required delay from a text appears until a translation request is queued in seconds. IMGUI not supported.
 
-## Key Mapping
-The following key inputs are mapped:
- * ALT + 0: Toggle XUnity AutoTranslator UI. (That's a zero, not an O)
- * ALT + T: Alternate between translated and untranslated versions of all texts provided by this plugin.
- * ALT + D: Dump untranslated texts (if no endpoint is configured)
- * ALT + R: Reload translation files. Useful if you change the text and texture files on the fly. Not guaranteed to work for all textures.
- * ALT + U: Manual hooking. The default hooks wont always pick up texts. This will attempt to make lookups manually.
- * ALT + F: If OverrideFont is configured, will toggle between overridden and default font.
- * ALT + Q: Reboot the plugin if it was shutdown. This will only work if the plugin was shut down due to consecutive errors towards the translation endpoint. Should only be used if you have reason to believe you have remedied the problem (such as changed VPN endpoint etc.) otherwise it will just shut down again.
-
-## Installation
-The plugin can be installed in following ways:
-
-### BepInEx Plugin
-REQUIRES: [BepInEx plugin manager](https://github.com/BepInEx/BepInEx) (follow its installation instructions first!). 
-
- 1. Download XUnity.AutoTranslator-BepIn-{VERSION}.zip from [releases](../../releases).
- 2. Extract directly into the game directory, such that the plugin dlls are placed in BepInEx folder.
-
-The file structure should like like this:
-```
-{GameDirectory}/BepInEx/XUnity.AutoTranslator.Plugin.Core.dll
-{GameDirectory}/BepInEx/XUnity.AutoTranslator.Plugin.BepInEx.dll
-{GameDirectory}/BepInEx/XUnity.AutoTranslator.Plugin.ExtProtocol.dll
-{GameDirectory}/BepInEx/ExIni.dll
-{GameDirectory}/BepInEx/Translators/{Translator}.dll
-{GameDirectory}/BepInEx/Translation/AnyTranslationFile.txt (these files will be auto generated by plugin!)
-```
-
-### IPA Plugin
-REQUIRES: [IPA plugin manager](https://github.com/Eusth/IPA) (follow its installation instructions first!).
-
- 1. Download XUnity.AutoTranslator-IPA-{VERSION}.zip from [releases](../../releases).
- 2. Extract directly into the game directory, such that the plugin dlls are placed in Plugins folder.
-
-The file structure should like like this
-```
-{GameDirectory}/Plugins/XUnity.AutoTranslator.Plugin.Core.dll
-{GameDirectory}/Plugins/XUnity.AutoTranslator.Plugin.IPA.dll
-{GameDirectory}/Plugins/XUnity.AutoTranslator.Plugin.ExtProtocol.dll
-{GameDirectory}/Plugins/0Harmony.dll
-{GameDirectory}/Plugins/ExIni.dll
-{GameDirectory}/Plugins/Translators/{Translator}.dll
-{GameDirectory}/Plugins/Translation/AnyTranslationFile.txt (these files will be auto generated by plugin!)
- ```
-
-### UnityInjector Plugin
-REQUIRES: UnityInjector (follow its installation instructions first!).
-
- 1. Download XUnity.AutoTranslator-UnityInjector-{VERSION}.zip from [releases](../../releases).
- 2. Extract directly into the game directory, such that the plugin dlls are placed in UnityInjector folder. **This may not be game root directory!**
-
-The file structure should like like this
-```
-{GameDirectory}/UnityInjector/XUnity.AutoTranslator.Plugin.Core.dll
-{GameDirectory}/UnityInjector/XUnity.AutoTranslator.Plugin.UnityInjector.dll
-{GameDirectory}/UnityInjector/XUnity.AutoTranslator.Plugin.ExtProtocol.dll
-{GameDirectory}/UnityInjector/0Harmony.dll
-{GameDirectory}/UnityInjector/ExIni.dll
-{GameDirectory}/UnityInjector/Translators/{Translator}.dll
-{GameDirectory}/UnityInjector/Translation/AnyTranslationFile.txt (these files will be auto generated by plugin!)
- ```
- 
-### Standalone Installation (ReiPatcher)
-REQUIRES: Nothing, ReiPatcher is provided by this download.
-
- 1. Download XUnity.AutoTranslator-ReiPatcher-{VERSION}.zip from [releases](../../releases).
- 2. Extract directly into the game directory, such that "SetupReiPatcherAndAutoTranslator.exe" is placed alongside other exe files.
- 3. Execute "SetupReiPatcherAndAutoTranslator.exe". This will setup up ReiPatcher correctly.
- 4. Execute the shortcut {GameExeName} (Patch and Run).lnk that was created besides existing executables. This will patch and launch the game.
- 5. From now on you can launch the game from the {GameExeName}.exe instead.
-
-The file structure should like like this
-```
-{GameDirectory}/ReiPatcher/Patches/XUnity.AutoTranslator.Patcher.dll
-{GameDirectory}/ReiPatcher/ExIni.dll
-{GameDirectory}/ReiPatcher/Mono.Cecil.dll
-{GameDirectory}/ReiPatcher/Mono.Cecil.Inject.dll
-{GameDirectory}/ReiPatcher/Mono.Cecil.Mdb.dll
-{GameDirectory}/ReiPatcher/Mono.Cecil.Pdb.dll
-{GameDirectory}/ReiPatcher/Mono.Cecil.Rocks.dll
-{GameDirectory}/ReiPatcher/ReiPatcher.exe
-{GameDirectory}/{GameExeName}_Data/Managed/ReiPatcher.exe
-{GameDirectory}/{GameExeName}_Data/Managed/XUnity.AutoTranslator.Plugin.Core.dll
-{GameDirectory}/{GameExeName}_Data/Managed/XUnity.AutoTranslator.Plugin.ExtProtocol.dll
-{GameDirectory}/{GameExeName}_Data/Managed/0Harmony.dll
-{GameDirectory}/{GameExeName}_Data/Managed/ExIni.dll
-{GameDirectory}/AutoTranslator/Translators/{Translator}.dll
-{GameDirectory}/AutoTranslator/Translation/AnyTranslationFile.txt (these files will be auto generated by plugin!)
- ```
-
 ## Translating Mods
 Often other mods UI are implemented through IMGUI. As you can see above, this is disabled by default. By changing the "EnableIMGUI" value to "True", it will start translating IMGUI as well, which likely means that other mods UI will be translated.
 
+This may seem like a nice feature to have enabled by default but **never redistribute the mod with this enabled**. The reason here being that IMGUI has a very spammy nature. This does not mean that IMGUI in general will spam the endpoint, just that it is more likely to cause spam (and therefore cause the plugin to shutdown).
+
 ## Manual Translations
 When you use this plugin, you can always go to the file `Translation\_AutoGeneratedTranslations.{lang}.txt` (OutputFile) to edit any auto generated translations and they will show up the next time you run the game. Or you can press (ALT+R) to reload the translation immediately.
 
@@ -349,7 +354,8 @@ In this context, the `Translation\_AutoGeneratedTranslations.{lang}.txt` (Output
 
 ## Regarding Redistribution
 Redistributing this plugin for various games is absolutely encouraged. However, if you do so, please keep the following in mind:
- * **IMPORTANT: Distribute the _AutoGeneratedTranslations.{lang}.txt file along with the redistribution with as many translations as possible to ensure the online translator is hit as little as possible.**
+ * **Distribute the _AutoGeneratedTranslations.{lang}.txt file along with the redistribution with as many translations as possible to ensure the online translator is hit as little as possible.**
+ * **Do not redistribute the mod with the configuration option `EnableIMGUI=True`.**
  * Ensure you keep the plugin up-to-date, as much as reasonably possible.
  * If you use image loading feature, make sure you read [this section](#texture-translation).
 
@@ -376,15 +382,15 @@ TextureHashGenerationStrategy=FromImageName
 
 `EnableTextureTranslation` enables texture translation. This basically means that textures will be loaded from the `TextureDirectory` and it's subsdirectories. These images will replace the in-game images used by the game.
 
-`EnableTextureDumping` enables texture dumping. This means that the mod will dump any images it has not already dumped to the `TextureDirectory`. When dumping textures, it may also be worth enabling `EnableTextureScanOnSceneLoad` to more quickly find all textures that require translating. **NEVER REDISTRIBUTE THIS MOD WITH THIS ENABLED.**
+`EnableTextureDumping` enables texture dumping. This means that the mod will dump any images it has not already dumped to the `TextureDirectory`. When dumping textures, it may also be worth enabling `EnableTextureScanOnSceneLoad` to more quickly find all textures that require translating. **Never redistribute the mod with this enabled.**
 
 `EnableTextureScanOnSceneLoad` allows the plugin to scan for texture objects on the sceneLoad event. This enables the plugin to find more texture at a tiny performance cost during scene load (which is often during loading screens, etc.). However, because of the way Unity works not all of these are guaranteed to be replacable. If you find an image that is dumped but cannot be translated, please report it. However, please recognize this mod is primarily intended for replacing UI textures, not textures for 3D meshes.
 
 `EnableSpriteRendererHooking` allows the plugin to attempt to hook SpriteRenderer. This is a seperate option because SpriteRenderer can't actually be hooked properly and the implemented workaround could have a theoretical impact on performance in certain situations.
 
-`LoadUnmodifiedTextures` enables whether or not the plugin should load textures that has not been modified. This is only useful for debugging, and likely to cause various visual glitches, especially if `EnableTextureScanOnSceneLoad` is also enabled. **NEVER REDISTRIBUTE THIS MOD WITH THIS ENABLED.**
+`LoadUnmodifiedTextures` enables whether or not the plugin should load textures that has not been modified. This is only useful for debugging, and likely to cause various visual glitches, especially if `EnableTextureScanOnSceneLoad` is also enabled. **Never redistribute the mod with this enabled.**
 
-`EnableTextureToggling` enables whether the ALT+T hotkey will also toggle textures. This is by no means guaranteed to work, especially if `EnableTextureScanOnSceneLoad` is also enabled. **NEVER REDISTRIBUTE THIS MOD WITH THIS ENABLED.**
+`EnableTextureToggling` enables whether the ALT+T hotkey will also toggle textures. This is by no means guaranteed to work, especially if `EnableTextureScanOnSceneLoad` is also enabled. **Never redistribute the mod with this enabled.**
 
 `TextureHashGenerationStrategy` specifies how images are identified. When images are stored, the game will need some way of associating them with the image that it has to replace.
 This is done through a hash-value that is stored in square brackets in each image file name, like this: `file_name [0223B639A2-6E698E9272].png`. This configuration specifies how these hash-values are generated:
@@ -401,8 +407,8 @@ If you redistribute this mod with translated images, it is recommended you delet
 You can also change the file name to whatever you desire, as long as you keep the hash appended to the end of the file name.
 
 If you take anything away from this section, it should be these two points:
- * **NEVER REDISTRIBUTE THIS MOD WITH `EnableTextureDumping=True`, `EnableTextureToggling=True` OR `LoadUnmodifiedTextures=True`**
- * **ONLY REDISTRIBUTE THIS MOD WITH `TextureHashGenerationStrategy=FromImageData` ENABLED IF ABSOLUTELY REQUIRED BY THE GAME.**
+ * **Never redistribute the mod with `EnableTextureDumping=True`, `EnableTextureToggling=True` or `LoadUnmodifiedTextures=True`**
+ * **Only redistribute the mod with `TextureHashGenerationStrategy=FromImageData` enabled if absolutely required by the game.**
 
 ### Technical details about Hash Generation in file names
 There are actually two hashes in the generated file name, separated by a dash (-):
@@ -412,11 +418,12 @@ There are actually two hashes in the generated file name, separated by a dash (-
 If `TextureHashGenerationStrategy=FromImageData` is specified, only a single hash will appear in each file name, as that single hash can be used both to identify the image and to determine whether or not it has been edited.
 
 ## Integrating with Auto Translator
+*NOTE: Everything below this point requires programming knowledge!*
 
 ### Implementing a dedicated translation component
 As a mod author implementing a translation plugin, you are able to, if you cannot find a translation to a string, simply delegate it to this mod, and you can do it without taking any references to this plugin.
 
-Here's how it works, and what is required:
+Here's how it works and what is required:
  * You must implement a Component (MonoBehaviour for instance) that this plugin is able to locate by simply traversing all objects during startup.
  * On this component you must add an event for the text hooks you want to override from XUnity AutoTranslator. This is done on a per text framework basis. The signature of these events must be: Func<object, string, string>. The arguments are, in order: 
     1. The component that represents the text in the UI. (The one that probably has a property called 'text').

+ 3 - 13
src/Translators/LecPowerTranslator15/LecPowerTranslator15Endpoint.cs

@@ -25,23 +25,13 @@ namespace LecPowerTranslator15
          if( string.IsNullOrEmpty( pathToLec ) ) throw new Exception( "The LecPowerTranslator15 requires the path to the installation folder." );
 
          var exePath = Path.Combine( context.PluginDirectory, @"Translators\Lec.ExtProtocol.exe" );
+
          var fileExists = File.Exists( exePath );
-         if( !fileExists )
-         {
-            throw new Exception( $"Could not find any executable at '{exePath}'" );
-         }
+         if( !fileExists ) throw new Exception( $"Could not find any executable at '{exePath}'" );
 
+         ExecutablePath = exePath;
          Arguments = Convert.ToBase64String( Encoding.UTF8.GetBytes( pathToLec ) );
 
-         if( fileExists )
-         {
-            ExecutablePath = exePath;
-         }
-         else
-         {
-            throw new Exception( "Unexpected error occurred." );
-         }
-
          if( context.SourceLanguage != "ja" ) throw new Exception( "Current implementation only supports japanese-to-english." );
          if( context.DestinationLanguage != "en" ) throw new Exception( "Current implementation only supports japanese-to-english." );
       }

+ 1 - 1
src/XUnity.AutoTranslator.Patcher/Patcher.cs

@@ -29,7 +29,7 @@ namespace XUnity.AutoTranslator.Patcher
       {
          get
          {
-            return "3.0.1";
+            return "3.0.2";
          }
       }
 

+ 1 - 1
src/XUnity.AutoTranslator.Plugin.BepIn/XUnity.AutoTranslator.Plugin.BepIn.csproj

@@ -2,7 +2,7 @@
 
    <PropertyGroup>
       <TargetFramework>net35</TargetFramework>
-      <Version>3.0.1</Version>
+      <Version>3.0.2</Version>
    </PropertyGroup>
 
    <ItemGroup>

+ 19 - 10
src/XUnity.AutoTranslator.Plugin.Core/AutoTranslationPlugin.cs

@@ -213,18 +213,26 @@ namespace XUnity.AutoTranslator.Plugin.Core
          {
             XuaLogger.Current.Error( $"Could not find the configured endpoint '{Settings.ServiceEndpoint}'." );
          }
-
-         // TODO: Perhaps some bleeding edge check to see if this is required?
-         var callback = _httpSecurity.GetCertificateValidationCheck();
-         if( callback != null && !Features.SupportsNet4x )
+         
+         if( Settings.DisableCertificateValidation )
          {
-            XuaLogger.Current.Info( $"Disabling certificate checks for endpoints because a .NET 3.x runtime is used." );
+            XuaLogger.Current.Info( $"Disabling certificate checks for endpoints because of configuration." );
 
-            ServicePointManager.ServerCertificateValidationCallback += callback;
+            ServicePointManager.ServerCertificateValidationCallback += (a1, a2, a3, a4) => true;
          }
          else
          {
-            XuaLogger.Current.Info( $"Not disabling certificate checks for endpoints because a .NET 4.x runtime is used." );
+            var callback = _httpSecurity.GetCertificateValidationCheck();
+            if( callback != null && !Features.SupportsNet4x )
+            {
+               XuaLogger.Current.Info( $"Disabling certificate checks for endpoints because a .NET 3.x runtime is used." );
+
+               ServicePointManager.ServerCertificateValidationCallback += callback;
+            }
+            else
+            {
+               XuaLogger.Current.Info( $"Not disabling certificate checks for endpoints because a .NET 4.x runtime is used." );
+            }
          }
 
          // Save again because configuration may be modified by endpoints
@@ -1034,7 +1042,7 @@ namespace XUnity.AutoTranslator.Plugin.Core
       {
          return _reverseTranslations.TryGetValue( value, out key );
       }
-
+      
       internal string Hook_TextChanged_WithResult( object ui, string text )
       {
          if( !ui.IsKnownTextType() ) return null;
@@ -1269,8 +1277,9 @@ namespace XUnity.AutoTranslator.Plugin.Core
                }
             }
 
-            var inputField = component.gameObject.GetFirstComponentInSelfOrAncestor( ClrTypes.InputField )
-               ?? component.gameObject.GetFirstComponentInSelfOrAncestor( ClrTypes.TMP_InputField );
+            var inputField = go.GetFirstComponentInSelfOrAncestor( ClrTypes.InputField )
+               ?? go.GetFirstComponentInSelfOrAncestor( ClrTypes.TMP_InputField )
+               ?? go.GetFirstComponentInSelfOrAncestor( ClrTypes.UIInput );
 
             return inputField == null;
          }

+ 5 - 1
src/XUnity.AutoTranslator.Plugin.Core/Configuration/Settings.cs

@@ -74,6 +74,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Configuration
       public static bool UseStaticTranslations;
       public static string OverrideFont;
       public static string UserAgent;
+      public static bool DisableCertificateValidation;
       public static WhitespaceHandlingStrategy WhitespaceRemovalStrategy;
       public static float? ResizeUILineSpacingScale;
       public static bool ForceUIResizing;
@@ -82,6 +83,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Configuration
       public static bool TextGetterCompatibilityMode;
       public static TextPostProcessing RomajiPostProcessing;
       public static TextPostProcessing TranslationPostProcessing;
+      public static bool EnableExperimentalHooks;
 
       public static string TextureDirectory;
       public static bool EnableTextureTranslation;
@@ -154,6 +156,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Configuration
          WhitespaceRemovalStrategy = PluginEnvironment.Current.Preferences.GetOrDefault( "Behaviour", "WhitespaceRemovalStrategy", WhitespaceHandlingStrategy.TrimPerNewline );
          RomajiPostProcessing = PluginEnvironment.Current.Preferences.GetOrDefault( "Behaviour", "RomajiPostProcessing", TextPostProcessing.ReplaceMacronWithCircumflex | TextPostProcessing.RemoveApostrophes );
          TranslationPostProcessing = PluginEnvironment.Current.Preferences.GetOrDefault( "Behaviour", "TranslationPostProcessing", TextPostProcessing.ReplaceMacronWithCircumflex );
+         EnableExperimentalHooks = PluginEnvironment.Current.Preferences.GetOrDefault( "Behaviour", "EnableExperimentalHooks", false );
 
          TextureDirectory = PluginEnvironment.Current.Preferences.GetOrDefault( "Texture", "TextureDirectory", @"Translation\Texture" );
          EnableTextureTranslation = PluginEnvironment.Current.Preferences.GetOrDefault( "Texture", "EnableTextureTranslation", false );
@@ -163,7 +166,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Configuration
          EnableSpriteRendererHooking = PluginEnvironment.Current.Preferences.GetOrDefault( "Texture", "EnableSpriteRendererHooking", false );
          LoadUnmodifiedTextures = PluginEnvironment.Current.Preferences.GetOrDefault( "Texture", "LoadUnmodifiedTextures", false );
          TextureHashGenerationStrategy = PluginEnvironment.Current.Preferences.GetOrDefault( "Texture", "TextureHashGenerationStrategy", TextureHashGenerationStrategy.FromImageName );
-
+         
          if( MaxCharactersPerTranslation > MaxMaxCharactersPerTranslation )
          {
             PluginEnvironment.Current.Preferences[ "Behaviour" ][ "MaxCharactersPerTranslation" ].Value = MaxMaxCharactersPerTranslation.ToString( CultureInfo.InvariantCulture );
@@ -171,6 +174,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Configuration
          }
 
          UserAgent = PluginEnvironment.Current.Preferences.GetOrDefault( "Http", "UserAgent", string.Empty );
+         DisableCertificateValidation = PluginEnvironment.Current.Preferences.GetOrDefault( "Http", "DisableCertificateValidation", false );
 
          EnablePrintHierarchy = PluginEnvironment.Current.Preferences.GetOrDefault( "Debug", "EnablePrintHierarchy", false );
          EnableConsole = PluginEnvironment.Current.Preferences.GetOrDefault( "Debug", "EnableConsole", false );

+ 3 - 0
src/XUnity.AutoTranslator.Plugin.Core/Constants/ClrTypes.cs

@@ -22,6 +22,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Constants
       public static readonly Type UIFont = FindType( "UIFont" );
       public static readonly Type UIPanel = FindType( "UIPanel" );
       public static readonly Type UIRect = FindType( "UIRect" );
+      public static readonly Type UIInput = FindType( "UIInput" );
 
       // Unity
       public static readonly Type WWW = FindType( "UnityEngine.WWW" );
@@ -31,11 +32,13 @@ namespace XUnity.AutoTranslator.Plugin.Core.Constants
       public static readonly Type ImageConversion = FindType( "UnityEngine.ImageConversion" );
       public static readonly Type Texture = FindType( "UnityEngine.Texture" );
       public static readonly Type SpriteRenderer = FindType( "UnityEngine.SpriteRenderer" );
+      public static readonly Type Sprite = FindType( "UnityEngine.Sprite" );
       public static readonly Type Object = FindType( "UnityEngine.Object" );
       public static readonly Type TextEditor = FindType( "UnityEngine.TextEditor" );
       public static readonly Type CustomYieldInstruction = FindType( "UnityEngine.CustomYieldInstruction" );
       public static readonly Type SceneManager = FindType( "UnityEngine.SceneManagement.SceneManager" );
       public static readonly Type Scene = FindType( "UnityEngine.SceneManagement.Scene" );
+      //public static readonly Type TextMesh = FindType( "UnityEngine.TextMesh" );
       //public static readonly Type GraphicRaycaster = FindType( "UnityEngine.UI.GraphicRaycaster" );
 
       // Something...

+ 1 - 1
src/XUnity.AutoTranslator.Plugin.Core/Constants/PluginData.cs

@@ -23,6 +23,6 @@ namespace XUnity.AutoTranslator.Plugin.Core.Constants
       /// <summary>
       /// Gets the version of the plugin.
       /// </summary>
-      public const string Version = "3.0.1";
+      public const string Version = "3.0.2";
    }
 }

+ 62 - 7
src/XUnity.AutoTranslator.Plugin.Core/Extensions/HarmonyInstanceExtensions.cs

@@ -4,7 +4,11 @@ using System.Linq;
 using System.Reflection;
 using System.Text;
 using Harmony;
+using Harmony.ILCopying;
+using XUnity.AutoTranslator.Plugin.Core.Configuration;
 using XUnity.AutoTranslator.Plugin.Core.Constants;
+using XUnity.AutoTranslator.Plugin.Core.Hooks;
+using XUnity.AutoTranslator.Plugin.Core.Utilities;
 
 namespace XUnity.AutoTranslator.Plugin.Core.Extensions
 {
@@ -30,15 +34,66 @@ namespace XUnity.AutoTranslator.Plugin.Core.Extensions
                var original = (MethodBase)type.GetMethod( "TargetMethod", BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public ).Invoke( null, new object[] { instance } );
                if( original != null )
                {
-                  var prefix = type.GetMethod( "Prefix", BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public );
-                  var postfix = type.GetMethod( "Postfix", BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public );
-                  var transpiler = type.GetMethod( "Transpiler", BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public );
+                  var overrider = type.GetMethod( "Override", BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public );
+                  var callerProperty = type.GetProperty( "Caller", BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public );
 
-                  var harmonyPrefix = prefix != null ? new HarmonyMethod( prefix ) : null;
-                  var harmonyPostfix = postfix != null ? new HarmonyMethod( postfix ) : null;
-                  var harmonyTranspiler = transpiler != null ? new HarmonyMethod( transpiler ) : null;
+                  if( overrider != null && callerProperty != null )
+                  {
+                     if( Settings.EnableExperimentalHooks )
+                     {
+                        long replacementMethodLocation = MemoryHelper.GetMethodStartLocation( overrider );
+                        long originalMethodLocation = MemoryHelper.GetMethodStartLocation( original );
+                        byte[] originalCode = null;
 
-                  PatchMethod.Invoke( instance, new object[] { original, harmonyPrefix, harmonyPostfix, harmonyTranspiler } );
+                        try
+                        {
+                           originalCode = MemoryHelper.GetInstructionsAtLocationRequiredToWriteJump( originalMethodLocation );
+                           var caller = new JumpedMethodCaller( originalMethodLocation, replacementMethodLocation, originalCode );
+                           callerProperty.SetValue( null, caller, null );
+
+                           MemoryHelper.WriteJump( true, originalMethodLocation, replacementMethodLocation );
+                        }
+                        catch
+                        {
+                           if( originalCode != null )
+                           {
+                              MemoryHelper.RestoreInstructionsAtLocation( true, originalMethodLocation, originalCode );
+                           }
+
+                           throw;
+                        }
+                     }
+                  }
+                  else
+                  {
+                     var priority = type.GetCustomAttributes( typeof( HarmonyPriority ), false )
+                        .OfType<HarmonyPriority>()
+                        .FirstOrDefault()
+                        ?.info.prioritiy;
+
+                     var prefix = type.GetMethod( "Prefix", BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public );
+                     var postfix = type.GetMethod( "Postfix", BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public );
+                     var transpiler = type.GetMethod( "Transpiler", BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public );
+
+                     var harmonyPrefix = prefix != null ? new HarmonyMethod( prefix ) : null;
+                     var harmonyPostfix = postfix != null ? new HarmonyMethod( postfix ) : null;
+                     var harmonyTranspiler = transpiler != null ? new HarmonyMethod( transpiler ) : null;
+
+                     if( priority.HasValue )
+                     {
+                        if( harmonyPrefix != null )
+                        {
+                           harmonyPrefix.prioritiy = priority.Value;
+                        }
+
+                        if( harmonyPostfix != null )
+                        {
+                           harmonyPostfix.prioritiy = priority.Value;
+                        }
+                     }
+
+                     PatchMethod.Invoke( instance, new object[] { original, harmonyPrefix, harmonyPostfix, harmonyTranspiler } );
+                  }
                }
                else
                {

+ 31 - 0
src/XUnity.AutoTranslator.Plugin.Core/Hooks/ImageHooks.cs

@@ -22,6 +22,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Hooks
          typeof( RawImage_texture_Hook ),
          typeof( Cursor_SetCursor_Hook ),
          typeof( SpriteRenderer_sprite_Hook ),
+         typeof( Sprite_texture_Hook ),
 
          // fallback hooks on material (Prefix hooks)
          typeof( Material_mainTexture_Hook ),
@@ -44,6 +45,36 @@ namespace XUnity.AutoTranslator.Plugin.Core.Hooks
       };
    }
 
+   [Harmony]
+   internal static class Sprite_texture_Hook
+   {
+      static bool Prepare( HarmonyInstance instance )
+      {
+         return ClrTypes.Sprite != null;
+      }
+
+      static MethodBase TargetMethod( HarmonyInstance instance )
+      {
+         return AccessTools.Property( ClrTypes.Sprite, "texture" )?.GetGetMethod();
+      }
+
+      static object Override( object __instance )
+      {
+         return Caller.Func( () => ActualOverride( __instance ) );
+      }
+
+      static object ActualOverride( object __instance )
+      {
+         var texture = ( (Sprite)__instance ).texture;
+
+         AutoTranslationPlugin.Current.Hook_ImageChanged( texture, true );
+
+         return texture;
+      }
+
+      static JumpedMethodCaller Caller { get; set; }
+   }
+
    [Harmony]
    internal static class SpriteRenderer_sprite_Hook
    {

+ 52 - 0
src/XUnity.AutoTranslator.Plugin.Core/Hooks/JumpedMethodCaller.cs

@@ -0,0 +1,52 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Harmony.ILCopying;
+using XUnity.AutoTranslator.Plugin.Core.Extensions;
+using XUnity.AutoTranslator.Plugin.Core.Utilities;
+
+namespace XUnity.AutoTranslator.Plugin.Core.Hooks
+{
+   internal class JumpedMethodCaller
+   {
+      private readonly long _originalMethodLocation;
+      private readonly long _replacementMethodLocation;
+      private readonly byte[] _originalCode;
+
+      public JumpedMethodCaller( long originalMethodLocation, long replacementMethodLocation, byte[] originalCode )
+      {
+         _originalMethodLocation = originalMethodLocation;
+         _replacementMethodLocation = replacementMethodLocation;
+         _originalCode = originalCode;
+      }
+
+      public object Func( Func<object> call )
+      {
+         try
+         {
+            MemoryHelper.RestoreInstructionsAtLocation( false, _originalMethodLocation, _originalCode );
+
+            return call();
+         }
+         finally
+         {
+            MemoryHelper.WriteJump( false, _originalMethodLocation, _replacementMethodLocation );
+         }
+      }
+
+      public void Action( Action call )
+      {
+         try
+         {
+            MemoryHelper.RestoreInstructionsAtLocation( false, _originalMethodLocation, _originalCode );
+
+            call();
+         }
+         finally
+         {
+            MemoryHelper.WriteJump( false, _originalMethodLocation, _replacementMethodLocation );
+         }
+      }
+   }
+}

+ 32 - 1
src/XUnity.AutoTranslator.Plugin.Core/Debugging/Kernel32.cs → src/XUnity.AutoTranslator.Plugin.Core/Kernel32.cs

@@ -4,7 +4,7 @@ using System.Linq;
 using System.Runtime.InteropServices;
 using System.Text;
 
-namespace XUnity.AutoTranslator.Plugin.Core.Debugging
+namespace XUnity.AutoTranslator.Plugin.Core
 {
    internal static class Kernel32
    {
@@ -55,5 +55,36 @@ namespace XUnity.AutoTranslator.Plugin.Core.Debugging
           int cbMultiByte,
           [Out, MarshalAs( UnmanagedType.LPArray )] char[] lpWideCharStr,
           int cchWideChar );
+
+      [DllImport( "kernel32.dll" )]
+      internal static extern bool VirtualProtect( IntPtr lpAddress, UIntPtr dwSize, Protection flNewProtect, out Protection lpflOldProtect );
+   }
+
+   /// <summary>A bit-field of flags for protections</summary>
+   [Flags]
+   internal enum Protection
+   {
+      /// <summary>No access</summary>
+      PAGE_NOACCESS = 0x01,
+      /// <summary>Read only</summary>
+      PAGE_READONLY = 0x02,
+      /// <summary>Read write</summary>
+      PAGE_READWRITE = 0x04,
+      /// <summary>Write copy</summary>
+      PAGE_WRITECOPY = 0x08,
+      /// <summary>No access</summary>
+      PAGE_EXECUTE = 0x10,
+      /// <summary>Execute read</summary>
+      PAGE_EXECUTE_READ = 0x20,
+      /// <summary>Execute read write</summary>
+      PAGE_EXECUTE_READWRITE = 0x40,
+      /// <summary>Execute write copy</summary>
+      PAGE_EXECUTE_WRITECOPY = 0x80,
+      /// <summary>guard</summary>
+      PAGE_GUARD = 0x100,
+      /// <summary>No cache</summary>
+      PAGE_NOCACHE = 0x200,
+      /// <summary>Write combine</summary>
+      PAGE_WRITECOMBINE = 0x400
    }
 }

+ 215 - 0
src/XUnity.AutoTranslator.Plugin.Core/Utilities/MemoryHelper.cs

@@ -0,0 +1,215 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Reflection.Emit;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Text;
+using Harmony.ILCopying;
+using XUnity.AutoTranslator.Plugin.Core.Constants;
+
+namespace XUnity.AutoTranslator.Plugin.Core.Utilities
+{
+   internal static class MemoryHelper
+   {
+      private static readonly HashSet<PlatformID> WindowsPlatformIDSet = new HashSet<PlatformID> { PlatformID.Win32NT, PlatformID.Win32S, PlatformID.Win32Windows, PlatformID.WinCE };
+      private static readonly bool IsWindows = WindowsPlatformIDSet.Contains( Environment.OSVersion.Platform );
+
+      private static readonly byte[] Prefix = new byte[] { 0xe9 };
+      private static readonly byte[] Instruction_Start_64bit = new byte[] { 0x48, 0xB8 };
+      private static readonly byte[] Instruction_End_64bit = new byte[] { 0xFF, 0xE0 };
+      private const byte Instruction_Start_32bit = 0x68;
+      private const byte Instruction_End_32bit = 0xc3;
+      private const int MemoryRequiredForJumpInstruction_32bit = 6;
+      private const int MemoryRequiredForJumpInstruction_64bit = 12;
+
+      public static long GetMethodStartLocation( MethodBase method )
+      {
+         var handle = GetRuntimeMethodHandle( method );
+         try
+         {
+            RuntimeHelpers.PrepareMethod( handle );
+         }
+         catch( Exception )
+         {
+         }
+
+         return handle.GetFunctionPointer().ToInt64();
+      }
+
+
+      public static string WriteJump( bool unprotect, long memory, long destination )
+      {
+         if( unprotect )
+         {
+            UnprotectMemoryPage( memory );
+         }
+
+         if( IntPtr.Size == sizeof( long ) )
+         {
+            if( CompareBytes( memory, Prefix ) )
+            {
+               var offset = ReadInt( memory + 1 );
+               memory += 5 + offset;
+            }
+
+            memory = WriteBytes( memory, Instruction_Start_64bit );
+            memory = WriteLong( memory, destination );
+            memory = WriteBytes( memory, Instruction_End_64bit );
+         }
+         else
+         {
+            memory = WriteByte( memory, Instruction_Start_32bit );
+            memory = WriteInt( memory, (int)destination );
+            memory = WriteByte( memory, Instruction_End_32bit );
+         }
+         return null;
+      }
+
+      public static byte[] GetInstructionsAtLocationRequiredToWriteJump( long location )
+      {
+         List<byte> array = new List<byte>();
+
+         var flag = IntPtr.Size == sizeof( long );
+         if( flag )
+         {
+            var flag2 = CompareBytes( location, Prefix );
+
+            if( flag2 )
+            {
+               var num = ReadInt( location + 1L );
+               location += 5 + num;
+            }
+            for( var i = 0 ; i < MemoryRequiredForJumpInstruction_64bit ; i++ )
+            {
+               array.Add( ReadByte( location + i ) );
+            }
+         }
+         else
+         {
+            for( var j = 0 ; j < MemoryRequiredForJumpInstruction_32bit ; j++ )
+            {
+               array.Add( ReadByte( location + j ) );
+            }
+         }
+
+         return array.ToArray();
+      }
+
+      public static void RestoreInstructionsAtLocation( bool unprotect, long location, byte[] array )
+      {
+         if( unprotect )
+         {
+            UnprotectMemoryPage( location );
+         }
+
+         var flag = IntPtr.Size == sizeof( long );
+         if( flag )
+         {
+            if( CompareBytes( location, Prefix ) )
+            {
+               var num = ReadInt( location + 1L );
+               location += 5 + num;
+            }
+         }
+
+         foreach( var b in array )
+         {
+            location = WriteByte( location, b );
+         }            
+      }
+
+      public static unsafe bool CompareBytes( long memory, byte[] values )
+      {
+         var p = (byte*)memory;
+         foreach( var value in values )
+         {
+            if( value != *p ) return false;
+            p++;
+         }
+         return true;
+      }
+      public static unsafe byte ReadByte( long memory )
+      {
+         var p = (byte*)memory;
+         return *p;
+      }
+
+      public static unsafe int ReadInt( long memory )
+      {
+         var p = (int*)memory;
+         return *p;
+      }
+
+      public static unsafe long ReadLong( long memory )
+      {
+         var p = (long*)memory;
+         return *p;
+      }
+
+      public static unsafe long WriteByte( long memory, byte value )
+      {
+         var p = (byte*)memory;
+         *p = value;
+         return memory + sizeof( byte );
+      }
+
+      public static unsafe long WriteBytes( long memory, byte[] values )
+      {
+         foreach( var value in values )
+            memory = WriteByte( memory, value );
+         return memory;
+      }
+
+      public static unsafe long WriteInt( long memory, int value )
+      {
+         var p = (int*)memory;
+         *p = value;
+         return memory + sizeof( int );
+      }
+
+      public static unsafe long WriteLong( long memory, long value )
+      {
+         var p = (long*)memory;
+         *p = value;
+         return memory + sizeof( long );
+      }
+
+      private static void UnprotectMemoryPage( long memory )
+      {
+         if( IsWindows )
+         {
+            var success = Kernel32.VirtualProtect( new IntPtr( memory ), new UIntPtr( 1 ), Protection.PAGE_EXECUTE_READWRITE, out var _ignored );
+            if( success == false )
+               throw new System.ComponentModel.Win32Exception();
+         }
+      }
+
+      private static RuntimeMethodHandle GetRuntimeMethodHandle( MethodBase method )
+      {
+         if( method is DynamicMethod )
+         {
+            var noninternalInstance = BindingFlags.NonPublic | BindingFlags.Instance;
+
+            // DynamicMethod actually generates its m_methodHandle on-the-fly and therefore
+            // we should call GetMethodDescriptor to force it to be created
+            //
+            var m_GetMethodDescriptor = typeof( DynamicMethod ).GetMethod( "GetMethodDescriptor", noninternalInstance );
+            if( m_GetMethodDescriptor != null )
+               return (RuntimeMethodHandle)m_GetMethodDescriptor.Invoke( method, new object[ 0 ] );
+
+            // .Net Core
+            var f_m_method = typeof( DynamicMethod ).GetField( "m_method", noninternalInstance );
+            if( f_m_method != null )
+               return (RuntimeMethodHandle)f_m_method.GetValue( method );
+
+            // Mono
+            var f_mhandle = typeof( DynamicMethod ).GetField( "mhandle", noninternalInstance );
+            return (RuntimeMethodHandle)f_mhandle.GetValue( method );
+         }
+
+         return method.MethodHandle;
+      }
+   }
+}

+ 2 - 1
src/XUnity.AutoTranslator.Plugin.Core/XUnity.AutoTranslator.Plugin.Core.csproj

@@ -2,11 +2,12 @@
 
    <PropertyGroup>
       <TargetFramework>net35</TargetFramework>
-      <Version>3.0.1</Version>
+      <Version>3.0.2</Version>
    </PropertyGroup>
 
    <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
      <DocumentationFile>C:\Proj\Secret\XUnity.AutoTranslator\src\XUnity.AutoTranslator.Plugin.Core\bin\Release\net35\XUnity.AutoTranslator.Plugin.Core.xml</DocumentationFile>
+     <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
    </PropertyGroup>
 
    <ItemGroup>

+ 1 - 1
src/XUnity.AutoTranslator.Plugin.IPA/XUnity.AutoTranslator.Plugin.IPA.csproj

@@ -2,7 +2,7 @@
 
    <PropertyGroup>
       <TargetFramework>net35</TargetFramework>
-      <Version>3.0.1</Version>
+      <Version>3.0.2</Version>
    </PropertyGroup>
 
    <ItemGroup>

+ 2 - 2
src/XUnity.AutoTranslator.Plugin.UnityInjector/XUnity.AutoTranslator.Plugin.UnityInjector.csproj

@@ -2,7 +2,7 @@
 
    <PropertyGroup>
       <TargetFramework>net35</TargetFramework>
-      <Version>3.0.1</Version>
+      <Version>3.0.2</Version>
    </PropertyGroup>
 
    <ItemGroup>
@@ -28,7 +28,7 @@
       <ItemGroup>
          <VersionNumber Include="$([System.Text.RegularExpressions.Regex]::Replace(&quot;%(Targets.Version)&quot;, &quot;^(.+?)(\.0+)$&quot;, &quot;$1&quot;))" />
       </ItemGroup>
-      <Exec Command="if $(ConfigurationName) == Release (&#xD;&#xA;   for %%f in (&quot;$(SolutionDir)dist\Translators\*&quot;) do XCOPY /Y /I &quot;%%f&quot; &quot;$(SolutionDir)dist\UnityInjector\UnityInjector\Translators\&quot;&#xD;&#xA;   XCOPY /Y /I &quot;$(TargetDir)ExIni.dll&quot; &quot;$(SolutionDir)dist\UnityInjector\UnityInjector\&quot;&#xD;&#xA;   XCOPY /Y /I &quot;$(TargetDir)0Harmony.dll&quot; &quot;$(SolutionDir)dist\UnityInjector\UnityInjector\&quot;&#xD;&#xA;   XCOPY /Y /I &quot;$(TargetDir)XUnity.AutoTranslator.Plugin.Core.dll&quot; &quot;$(SolutionDir)dist\UnityInjector\UnityInjector\&quot;&#xD;&#xA;   XCOPY /Y /I &quot;$(TargetDir)XUnity.AutoTranslator.Plugin.ExtProtocol.dll&quot; &quot;$(SolutionDir)dist\UnityInjector\UnityInjector\&quot;&#xD;&#xA;   XCOPY /Y /I &quot;$(TargetDir)$(TargetName)$(TargetExt)&quot; &quot;$(SolutionDir)dist\UnityInjector\UnityInjector\&quot;&#xD;&#xA;   COPY /Y &quot;$(SolutionDir)README.md&quot; &quot;$(SolutionDir)dist\UnityInjector\UnityInjector\README (AutoTranslator).md&quot;&#xD;&#xA;   powershell Compress-Archive -Path '$(SolutionDir)dist\UnityInjector\UnityInjector' -DestinationPath '$(SolutionDir)dist\UnityInjector\XUnity.AutoTranslator-UnityInjector-@(VersionNumber).zip' -Force)&#xD;&#xA;)" />
+      <Exec Command="if $(ConfigurationName) == Release (&#xD;&#xA;   for %%f in (&quot;$(SolutionDir)dist\Translators\*&quot;) do XCOPY /Y /I &quot;%%f&quot; &quot;$(SolutionDir)dist\UnityInjector\UnityInjector\Config\Translators\&quot;&#xD;&#xA;   XCOPY /Y /I &quot;$(TargetDir)0Harmony.dll&quot; &quot;$(SolutionDir)dist\UnityInjector\UnityInjector\&quot;&#xD;&#xA;   XCOPY /Y /I &quot;$(TargetDir)XUnity.AutoTranslator.Plugin.Core.dll&quot; &quot;$(SolutionDir)dist\UnityInjector\UnityInjector\&quot;&#xD;&#xA;   XCOPY /Y /I &quot;$(TargetDir)XUnity.AutoTranslator.Plugin.ExtProtocol.dll&quot; &quot;$(SolutionDir)dist\UnityInjector\UnityInjector\&quot;&#xD;&#xA;   XCOPY /Y /I &quot;$(TargetDir)$(TargetName)$(TargetExt)&quot; &quot;$(SolutionDir)dist\UnityInjector\UnityInjector\&quot;&#xD;&#xA;   COPY /Y &quot;$(SolutionDir)README.md&quot; &quot;$(SolutionDir)dist\UnityInjector\UnityInjector\README (AutoTranslator).md&quot;&#xD;&#xA;   powershell Compress-Archive -Path '$(SolutionDir)dist\UnityInjector\UnityInjector' -DestinationPath '$(SolutionDir)dist\UnityInjector\XUnity.AutoTranslator-UnityInjector-@(VersionNumber).zip' -Force)&#xD;&#xA;)" />
    </Target>
 
 </Project>

+ 1 - 1
src/XUnity.AutoTranslator.Setup/XUnity.AutoTranslator.Setup.csproj

@@ -4,7 +4,7 @@
       <OutputType>Exe</OutputType>
       <TargetFramework>net40</TargetFramework>
       <AssemblyName>SetupReiPatcherAndAutoTranslator</AssemblyName>
-      <Version>3.0.1</Version>
+      <Version>3.0.2</Version>
       <ApplicationIcon>icon.ico</ApplicationIcon>
       <Win32Resource />
    </PropertyGroup>