Browse Source

Version 2.18.0

 * FEATURE - Text Getter Compatibility Mode. Fools the game into thinking that it is not actually translated
 * FEATURE - Textures - Live2D component support
 * FEATURE - Textures - SpriteRenderer component support
 * FEATURE - Hotkey to reboot plugin in certain situations
 * BUG FIX - No longer trim translated text (if configured) when loading translation files
 * MISC - Support legacy OnLevelWasLoaded
 * MISC - Avoid harmless 'log errors' in relation to texture translation
 * MISC - Set max value of MaxCharactersPerTranslation to 500
 * MISC - Documentation update to describe 'Behaviour' configuration section
 * MISC - Removed "FromImageNameThenData" as hash source on textures
 * MISC - Added "FromImageNameAndScene" as hash source on textures
 * MISC - Inline comment handling when using '//' to indicate a comment
 * MISC - Improved default configuration for Utage-related games to improve translation when newlines are involved
randoman 6 years ago
parent
commit
5919ba3167
35 changed files with 1419 additions and 626 deletions
  1. 16 1
      CHANGELOG.md
  2. 62 8
      README.md
  3. 1 1
      src/XUnity.AutoTranslator.Patcher/Patcher.cs
  4. 1 1
      src/XUnity.AutoTranslator.Plugin.BepIn/XUnity.AutoTranslator.Plugin.BepIn.csproj
  5. 193 53
      src/XUnity.AutoTranslator.Plugin.Core/AutoTranslationPlugin.cs
  6. 14 1
      src/XUnity.AutoTranslator.Plugin.Core/Configuration/Settings.cs
  7. 10 4
      src/XUnity.AutoTranslator.Plugin.Core/Constants/ClrTypes.cs
  8. 1 1
      src/XUnity.AutoTranslator.Plugin.Core/Constants/PluginData.cs
  9. 109 0
      src/XUnity.AutoTranslator.Plugin.Core/Debugging/ConsoleEncoding.cs
  10. 5 0
      src/XUnity.AutoTranslator.Plugin.Core/Debugging/DebugConsole.cs
  11. 23 0
      src/XUnity.AutoTranslator.Plugin.Core/Debugging/Kernel32.cs
  12. 8 1
      src/XUnity.AutoTranslator.Plugin.Core/Extensions/ComponentExtensions.cs
  13. 56 6
      src/XUnity.AutoTranslator.Plugin.Core/Extensions/ObjectExtensions.cs
  14. 4 4
      src/XUnity.AutoTranslator.Plugin.Core/Extensions/TextureExtensions.cs
  15. 0 1
      src/XUnity.AutoTranslator.Plugin.Core/Features.cs
  16. 10 8
      src/XUnity.AutoTranslator.Plugin.Core/Hooks/HooksSetup.cs
  17. 10 10
      src/XUnity.AutoTranslator.Plugin.Core/Hooks/IMGUIHooks.cs
  18. 661 0
      src/XUnity.AutoTranslator.Plugin.Core/Hooks/ImageHooks.cs
  19. 2 2
      src/XUnity.AutoTranslator.Plugin.Core/Hooks/NGUIHooks.cs
  20. 0 297
      src/XUnity.AutoTranslator.Plugin.Core/Hooks/NGUIImageHooks.cs
  21. 54 0
      src/XUnity.AutoTranslator.Plugin.Core/Hooks/TextGetterCompatHooks.cs
  22. 9 9
      src/XUnity.AutoTranslator.Plugin.Core/Hooks/TextMeshProHooks.cs
  23. 3 2
      src/XUnity.AutoTranslator.Plugin.Core/Hooks/UGUIHooks.cs
  24. 0 179
      src/XUnity.AutoTranslator.Plugin.Core/Hooks/UGUIImageHooks.cs
  25. 31 0
      src/XUnity.AutoTranslator.Plugin.Core/SceneManagerEx.cs
  26. 43 0
      src/XUnity.AutoTranslator.Plugin.Core/TextGetterCompatMode.cs
  27. 1 1
      src/XUnity.AutoTranslator.Plugin.Core/TextureHashGenerationStrategy.cs
  28. 20 0
      src/XUnity.AutoTranslator.Plugin.Core/TextureReloadContext.cs
  29. 42 18
      src/XUnity.AutoTranslator.Plugin.Core/TextureTranslationInfo.cs
  30. 10 2
      src/XUnity.AutoTranslator.Plugin.Core/Utilities/TextHelper.cs
  31. 16 12
      src/XUnity.AutoTranslator.Plugin.Core/Utilities/TextureHelper.cs
  32. 1 1
      src/XUnity.AutoTranslator.Plugin.Core/XUnity.AutoTranslator.Plugin.Core.csproj
  33. 1 1
      src/XUnity.AutoTranslator.Plugin.IPA/XUnity.AutoTranslator.Plugin.IPA.csproj
  34. 1 1
      src/XUnity.AutoTranslator.Plugin.UnityInjector/XUnity.AutoTranslator.Plugin.UnityInjector.csproj
  35. 1 1
      src/XUnity.AutoTranslator.Setup/XUnity.AutoTranslator.Setup.csproj

+ 16 - 1
CHANGELOG.md

@@ -1,4 +1,19 @@
-### 2.17.0
+### 2.18.0
+ * FEATURE - Text Getter Compatibility Mode. Fools the game into thinking that it is not actually translated
+ * FEATURE - Textures - Live2D component support
+ * FEATURE - Textures - SpriteRenderer component support
+ * FEATURE - Hotkey to reboot plugin in certain situations
+ * BUG FIX - No longer trim translated text (if configured) when loading translation files
+ * MISC - Support legacy OnLevelWasLoaded
+ * MISC - Avoid harmless 'log errors' in relation to texture translation
+ * MISC - Set max value of MaxCharactersPerTranslation to 500
+ * MISC - Documentation update to describe 'Behaviour' configuration section
+ * MISC - Removed "FromImageNameThenData" as hash source on textures
+ * MISC - Added "FromImageNameAndScene" as hash source on textures
+ * MISC - Inline comment handling when using '//' to indicate a comment
+ * MISC - Improved default configuration for Utage-related games to improve translation when newlines are involved
+
+### 2.17.0
  * FEATURE - Support legitimate Bing translate API (requires key)
  * FEATURE - Documented custom endpoint
  * BUG FIX - Fixed bug in older versions of the Unity engine where the plugin would crash on startup due to missing APIs in relation to the SceneManagement namespace

+ 62 - 8
README.md

@@ -68,6 +68,8 @@ The plugin employs the following spam prevention mechanisms:
  12. It employs an internal dictionary of manual translations (~10000 in total) for commonly used phrases (Japanese-to-English only) to prevent sending out translation requests for these.
  13. Some endpoints support batching of translations so far fewer requests are sent. This does not increase the total number of translations per session (2).
  14. All translation results are cached in memory and stored on disk to prevent making the same translation request twice.
+ 15. Due to its spammy nature, any text that comes from an IMGUI component has any numbers found in it templated away (and substituted back in upon translation) to prevent issues in relation to (6).
+ 16. The plugin will keep a single TCP connection alive towards the translation endpoint. This connection will be gracefully closed if it is not used for 50 seconds.
 
 ## Text Frameworks
 The following text frameworks are supported.
@@ -112,7 +114,7 @@ AllowPluginHookOverride=True     ;Allow other text translation plugins to overri
 
 [Behaviour]
 Delay=0                          ;Delay to wait before attempting to translate a text in seconds
-MaxCharactersPerTranslation=200  ;Max characters per text to translate
+MaxCharactersPerTranslation=200  ;Max characters per text to translate. Max 500.
 IgnoreWhitespaceInDialogue=True  ;Whether or not to ignore whitespace, including newlines, in dialogue keys
 IgnoreWhitespaceInNGUI=True      ;Whether or not to ignore whitespace, including newlines, in NGUI
 MinDialogueChars=20              ;The length of the text for it to be considered a dialogue
@@ -128,15 +130,17 @@ WhitespaceRemovalStrategy=TrimPerNewline ;Indicates how whitespace/newline remov
 ResizeUILineSpacingScale=        ;A decimal value that the default line spacing should be scaled by during UI resizing, for example: 0.80. NOTE: Only works for UGUI
 ForceUIResizing=True             ;Indicates whether the UI resize behavior should be applied to all UI components regardless of them being translated.
 IgnoreTextStartingWith=\u180e;   ;Indicates that the plugin should ignore any strings starting with certain characters. This is a list seperated by ';'.
+TextGetterCompatibilityMode=False ;Indicates whether or not to enable "Text Getter Compatibility Mode". Should only be enabled if required by the game. 
 
 [Texture]
 TextureDirectory=Translation\Texture ;Directory to dump textures to, and root of directories to load images from
 EnableTextureTranslation=False   ;Indicates whether the plugin will attempt to replace in-game images with those from the TextureDirectory directory
 EnableTextureDumping=False       ;Indicates whether the plugin will dump texture it is capable of replacing to the TextureDirectory. Has significant performance impact
 EnableTextureToggling=False      ;Indicates whether or not toggling the translation with the ALT+T hotkey will also affect textures. Not guaranteed to work for all textures. Has significant performance impact
-EnableTextureScanOnSceneLoad=True ;Indicates whether or not the plugin should scan for textures on scene load. This enables the plugin to find and (possibly) replace more texture
+EnableTextureScanOnSceneLoad=False ;Indicates whether or not the plugin should scan for textures on scene load. This enables the plugin to find and (possibly) replace more texture
+EnableSpriteRendererHooking=False ;Indicates whether or not the plugin should 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=False     ;Indicates whether or not unmodified textures should be loaded. Modifications are determined based on the hash in the file name. Only enable this for debugging purposes as it is likely to cause oddities
-TextureHashGenerationStrategy=FromImageName ;Indicates how the mod identifies pictures through hashes. Can be ["FromImageName", "FromImageData", "FromImageNameThenData"]
+TextureHashGenerationStrategy=FromImageName ;Indicates how the mod identifies pictures through hashes. Can be ["FromImageName", "FromImageData", "FromImageNameAndScene"]
 
 [Http]
 UserAgent=                       ;Override the user agent used by APIs requiring a user agent
@@ -169,6 +173,52 @@ Enable=True                      ;Used to enable automatic migrations of this co
 Tag=2.9.0                        ;Tag representing the last version this plugin was executed under. Do not edit
 ```
 
+### Behaviour Configuration Explanation
+
+#### Whitespace Handling
+When it comes to automated translations, proper whitespace handling can really make or break the translation. The parameters that control whitespace handling are:
+ * `IgnoreWhitespaceInDialogue`
+ * `IgnoreWhitespaceInNGUI`
+ * `MinDialogueChars`
+ * `ForceSplitTextAfterCharacters`
+ * `TrimAllText`
+ * `WhitespaceRemovalStrategy`
+
+The first thing the plugin does when it discovers a new text is trim any whitespace, if `TrimAllText` is configured. This does not include newlines or "special" whitespace such as japanese whitespace.
+
+After this initial trimming, the plugin determines whether or not it should perform a special whitespace removal operation. How it removes the whitespace is based on the parameter `WhitespaceRemomvalStrategy`. The default value of this parameter is recommended. The other value (AllOccurrences) is only kept for legacy reasons.
+
+It determine whether or not to perform this operation based on the parameters `IgnoreWhitespaceInDialogue`, `IgnoreWhitespaceInNGUI` and `MinDialogueChars`:
+ * `IgnoreWhitespaceInDialogue`: If the text is longer than `MinDialogueChars`, whitespace is removed.
+ * `IgnoreWhitespaceInNGUI`: If the text comes from an NGUI component, whitespace is removed.
+
+After the text has been translated by the configured service, `ForceSplitTextAfterCharacters` is used to determine if the plugin should force the result into multiple lines after a certain number of characters.
+
+The main reason that this type of handling can make or break a translation really comes down to whether or not whitespace is removed from the source text before sending it to the endpoint. Most endpoints (such as GoogleTranslate) consider text on multiple lines seperately, which can often result in terrible translation if an unnecessary newline is included.
+
+#### UI Resizing
+Often when performing a translation on a text component, the resulting text is larger than the original. This often means that there is not enough room in the text component for the result. This section describes ways to remedy that by changing important parameters of the text components.
+
+The important parameters in relation to this are `EnableUIResizing`, `ResizeUILineSpacingScale`, `ForceUIResizing` and `OverrideFont`.
+ * `EnableUIResizing`: Resizes the components when a translation is performed.
+ * `ForceUIResizing`: Resizes all components at all times, period.
+ * `ResizeUILineSpacingScale`: Changes the line spacing of resized components. UGUI only.
+ * `OverrideFont`: Changes the font of all text components regardless of `EnableUIResizing` and `ForceUIResizing`. UGUI only.
+
+Resizing of a UI component does not refer to changing of it's dimensions, but rather how the component handles overflow. The plugin changes the overflow parameters such that text is more likely to be displayed.
+
+#### Reducing Translation Requests
+The following aims at reducing the number of requests send to the translation endpoint:
+ * `EnableBatching`: Batches several translation requests into a single with supported endpoints (Only GoogleTranslate and GoogleTranslateLegitimate at the moment)
+ * `UseStaticTranslations`: Enables usage of internal lookup dictionary of various english-to-japanese terms.
+ * `MaxCharactersPerTranslation`: Specifies the maximum length of a text to translate. Any texts longer than this is ignored by the plugin. Cannot be greater than 500.
+
+#### Other Options
+ * `TextGetterCompatibilityMode`: This mode fools the game into thinking that the text displayed is not translated. This is required if the game uses text displayed to the user to determine what logic to execute. You can easily determine if this is required if you can see the functionality works fine if you toggle the translation off (hotkey: ALT+T).
+ * `IgnoreTextStartingWith`: Disable translation for any texts starting with values in this ';-separated' setting. The [default value](https://www.charbase.com/180e-unicode-mongolian-vowel-separator) is an invisible character that takes up no space.
+ * `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 + T: Alternate between translated and untranslated versions of all texts provided by this plugin.
@@ -176,12 +226,13 @@ The following key inputs are mapped:
  * 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/bbepis/BepInEx) (follow its installation instructions first!). 
+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.
@@ -254,7 +305,7 @@ The file structure should like like this
 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.
 
 ## 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.
+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.
 
 It is also worth noting that this plugin will read all text files (*.txt) in the `Translation` (Directory), so if you want to provide a manual translation, you can simply cut out texts from the `Translation\_AutoGeneratedTranslations.{lang}.txt` (OutputFile) and place them in new text files in order to replace them with a manual translation.
 
@@ -280,6 +331,7 @@ EnableTextureTranslation=False
 EnableTextureDumping=False
 EnableTextureToggling=False
 EnableTextureScanOnSceneLoad=False
+EnableSpriteRendererHooking=False
 LoadUnmodifiedTextures=False
 TextureHashGenerationStrategy=FromImageName
 ```
@@ -292,6 +344,8 @@ TextureHashGenerationStrategy=FromImageName
 
 `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.**
 
 `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.**
@@ -300,9 +354,9 @@ TextureHashGenerationStrategy=FromImageName
 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:
  * `FromImageName` means that the hash is generated from the internal resource name that the game uses for the image, which may not exist for all images or even be unique. However, it is generally fairly reliable. If an image has no resource name, it will not be dumped.
  * `FromImageData` means that the hash is generated from the data stored in the image, which is guaranteed to exist for all images. However, generating the hash comes at a performance cost, that will also be incurred by the end-users.
- * `FromImageNameThenData` means that it should use the name, if available, otherwise use the data.
+ * `FromImageNameAndScene` means that it should use the name and scene to generate a hash. The name is still required for this to work. When using this option, there is a chance the same texture could be dumped with different hashes, which is undesirable, but it could be required for some games, if the name itself is not unique and the `FromImageData` option causes performance issues.
 
-There's an important catch you need to be aware when dealing with these options and that is if ANY of these options exists: `EnableTextureDumping=True`, `EnableTextureToggling=True`, `TextureHashGenerationStrategy=FromImageData|FromImageNameThenData`, then the game will need to read the raw data from all images it finds in game in order to replace the image and this is an expensive operation.
+There's an important catch you need to be aware when dealing with these options and that is if ANY of these options exists: `EnableTextureDumping=True`, `EnableTextureToggling=True`, `TextureHashGenerationStrategy=FromImageData`, then the game will need to read the raw data from all images it finds in game in order to replace the image and this is an expensive operation.
 
 It is therefore recommended to use `TextureHashGenerationStrategy=FromImageName`. Most likely, images without a resource name won't be interesting to translate anyway.
 
@@ -312,7 +366,7 @@ You can also change the file name to whatever you desire, as long as you keep th
 
 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|FromImageNameThenData` ENABLED IF ABSOLUTELY REQUIRED BY THE GAME.**
+ * **ONLY REDISTRIBUTE THIS 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 (-):

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

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

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

@@ -2,7 +2,7 @@
 
    <PropertyGroup>
       <TargetFramework>net35</TargetFramework>
-      <Version>2.17.0</Version>
+      <Version>2.18.0</Version>
    </PropertyGroup>
 
    <ItemGroup>

+ 193 - 53
src/XUnity.AutoTranslator.Plugin.Core/AutoTranslationPlugin.cs

@@ -28,6 +28,7 @@ using XUnity.AutoTranslator.Plugin.Core.Debugging;
 using XUnity.AutoTranslator.Plugin.Core.Batching;
 using Harmony;
 using XUnity.AutoTranslator.Plugin.Core.Parsing;
+using System.Diagnostics;
 
 namespace XUnity.AutoTranslator.Plugin.Core
 {
@@ -92,6 +93,8 @@ namespace XUnity.AutoTranslator.Plugin.Core
       private Dictionary<string, byte[]> _translatedImages = new Dictionary<string, byte[]>( StringComparer.InvariantCultureIgnoreCase );
       private HashSet<string> _untranslatedImages = new HashSet<string>();
 
+      private readonly List<string> _kickedOff = new List<string>();
+
       private Component _advEngine;
       private float? _nextAdvUpdate;
 
@@ -123,6 +126,9 @@ namespace XUnity.AutoTranslator.Plugin.Core
       private bool _overrideFont = false;
       private bool _initialized = false;
       private bool _temporarilyDisabled = false;
+      private string _requireSpriteRendererCheckCausedBy = null;
+      private int _lastSpriteUpdateFrame = -1;
+      private bool _isCalledFromSceneManager = false;
 
       public void Initialize()
       {
@@ -142,6 +148,7 @@ namespace XUnity.AutoTranslator.Plugin.Core
 
             _endpoint = null;
             Settings.IsShutdown = true;
+            Settings.IsShutdownFatal = true;
 
             return;
          }
@@ -150,6 +157,7 @@ namespace XUnity.AutoTranslator.Plugin.Core
 
          HooksSetup.InstallTextHooks();
          HooksSetup.InstallImageHooks();
+         HooksSetup.InstallTextGetterCompatHooks();
 
          try
          {
@@ -166,6 +174,7 @@ namespace XUnity.AutoTranslator.Plugin.Core
 
             _endpoint = null;
             Settings.IsShutdown = true;
+            Settings.IsShutdownFatal = true;
          }
 
          _symbolCheck = TextHelper.GetSymbolCheck( Settings.FromLanguage );
@@ -186,16 +195,13 @@ namespace XUnity.AutoTranslator.Plugin.Core
             _overrideFont = _hasOverrideFont;
          }
 
-         if( Features.SupportsScenes && Settings.EnableTextureScanOnSceneLoad && ( Settings.EnableTextureDumping || Settings.EnableTextureTranslation ) )
+         try
          {
-            try
-            {
-               EnableSceneLoadScan();
-            }
-            catch( Exception e )
-            {
-               Logger.Current.Error( e, "An error occurred while settings up texture scene-load scans." );
-            }
+            EnableSceneLoadScan();
+         }
+         catch( Exception e )
+         {
+            Logger.Current.Error( e, "An error occurred while settings up texture scene-load scans." );
          }
 
          LoadTranslations();
@@ -280,24 +286,52 @@ namespace XUnity.AutoTranslator.Plugin.Core
 
       public void EnableSceneLoadScan()
       {
-         EnableSceneLoadScanInternal();
+         Logger.Current.Info( "Probing whether OnLevelWasLoaded or SceneManager is supported in this version of Unity. Any warnings related to OnLevelWasLoaded coming from Unity can safely be ignored." );
+         if( Features.SupportsScenes )
+         {
+            Logger.Current.Info( "SceneManager is supported in this version of Unity." );
+            EnableSceneLoadScanInternal();
+         }
+         else
+         {
+            Logger.Current.Info( "SceneManager is not supported in this version of Unity. Falling back to OnLevelWasLoaded and Application level API." );
+         }
       }
 
       public void EnableSceneLoadScanInternal()
       {
-         // specified in own method, because of chance that this has changed through Unity lifetime
-         SceneManager.sceneLoaded += ( arg1, arg2 ) => SceneManager_SceneLoaded();
+         // do this in a different class to avoid having an anonymous method with references to the "Scene" class
+         SceneManagerLoader.EnableSceneLoadScanInternal( this );
       }
 
-      private void SceneManager_SceneLoaded()
+      public void OnLevelWasLoadedFromSceneManager( int id )
       {
-         Logger.Current.Info( "SceneLoading..." );
-         var startTime = Time.realtimeSinceStartup;
+         try
+         {
+            _isCalledFromSceneManager = true;
+            OnLevelWasLoaded( id );
+         }
+         finally
+         {
+            _isCalledFromSceneManager = false;
+         }
+      }
 
-         ManualHookForTextures();
+      private void OnLevelWasLoaded( int id )
+      {
+         if( !Features.SupportsScenes || ( Features.SupportsScenes && _isCalledFromSceneManager ) )
+         {
+            if( Settings.EnableTextureScanOnSceneLoad && ( Settings.EnableTextureDumping || Settings.EnableTextureTranslation ) )
+            {
+               Logger.Current.Info( "Performing texture lookup during scene load..." );
+               var startTime = Time.realtimeSinceStartup;
+
+               ManualHookForTextures();
 
-         var endTime = Time.realtimeSinceStartup;
-         Logger.Current.Info( $"SceneLoaded (took {Math.Round( endTime - startTime, 2 )} seconds)" );
+               var endTime = Time.realtimeSinceStartup;
+               Logger.Current.Info( $"Finished texture lookup (took {Math.Round( endTime - startTime, 2 )} seconds)" );
+            }
+         }
       }
 
       /// <summary>
@@ -459,7 +493,7 @@ namespace XUnity.AutoTranslator.Plugin.Core
                   if( kvp.Length == 2 )
                   {
                      string key = TextHelper.Decode( kvp[ 0 ].TrimIfConfigured() );
-                     string value = TextHelper.Decode( kvp[ 1 ].TrimIfConfigured() );
+                     string value = TextHelper.Decode( kvp[ 1 ] );
 
                      if( !string.IsNullOrEmpty( key ) && !string.IsNullOrEmpty( value ) && IsTranslatable( key ) )
                      {
@@ -566,6 +600,7 @@ namespace XUnity.AutoTranslator.Plugin.Core
                _ongoingJobs.Clear();
 
                Settings.IsShutdown = true;
+               Settings.IsShutdownFatal = true;
                Logger.Current.Error( $"SPAM DETECTED: Translations were queued every second for more than {Settings.MaximumConsecutiveSecondsTranslated} consecutive seconds. Shutting down plugin." );
             }
 
@@ -601,6 +636,7 @@ namespace XUnity.AutoTranslator.Plugin.Core
                _ongoingJobs.Clear();
 
                Settings.IsShutdown = true;
+               Settings.IsShutdownFatal = true;
                Logger.Current.Error( $"SPAM DETECTED: Translations were queued every frame for more than {Settings.MaximumConsecutiveFramesTranslated} consecutive frames. Shutting down plugin." );
             }
 
@@ -656,6 +692,7 @@ namespace XUnity.AutoTranslator.Plugin.Core
                _ongoingJobs.Clear();
 
                Settings.IsShutdown = true;
+               Settings.IsShutdownFatal = true;
                Logger.Current.Error( $"SPAM DETECTED: Text that is 'scrolling in' is being translated. Disable that feature. Shutting down plugin." );
             }
          }
@@ -677,6 +714,7 @@ namespace XUnity.AutoTranslator.Plugin.Core
             _ongoingJobs.Clear();
 
             Settings.IsShutdown = true;
+            Settings.IsShutdownFatal = true;
             Logger.Current.Error( $"SPAM DETECTED: More than {Settings.MaxUnstartedJobs} queued for translations due to unknown reasons. Shutting down plugin." );
          }
 
@@ -704,6 +742,7 @@ namespace XUnity.AutoTranslator.Plugin.Core
                _ongoingJobs.Clear();
 
                Settings.IsShutdown = true;
+               Settings.IsShutdownFatal = true;
                Logger.Current.Error( $"SPAM DETECTED: More than {Settings.MaxTranslationsQueuedPerSecond} translations per seconds queued for a {Settings.MaxSecondsAboveTranslationThreshold} second period. Shutting down plugin." );
             }
          }
@@ -769,6 +808,36 @@ namespace XUnity.AutoTranslator.Plugin.Core
          _reverseTranslations[ value ] = lookup;
       }
 
+      private void UpdateSpriteRenderers()
+      {
+         if( Settings.EnableSpriteRendererHooking && ( Settings.EnableTextureTranslation || Settings.EnableTextureDumping ) )
+         {
+            if( _requireSpriteRendererCheckCausedBy != null )
+            {
+               try
+               {
+                  var start = Time.realtimeSinceStartup;
+
+                  var spriteRenderers = GameObject.FindObjectsOfType<SpriteRenderer>();
+                  foreach( var sr in spriteRenderers )
+                  {
+                     // simulate a hook
+                     Hook_ImageChangedOnComponent( sr, null, false, false );
+                  }
+
+                  var end = Time.realtimeSinceStartup;
+                  var delta = Math.Round( end - start, 2 );
+
+                  Logger.Current.Debug( $"Update SpriteRenderers caused by {_requireSpriteRendererCheckCausedBy} component (took " + delta + " seconds)" );
+               }
+               finally
+               {
+                  _requireSpriteRendererCheckCausedBy = null;
+               }
+            }
+         }
+      }
+
       private void QueueNewUntranslatedForClipboard( TranslationKey key )
       {
          if( Settings.CopyToClipboard && Features.SupportsClipboard )
@@ -855,23 +924,33 @@ namespace XUnity.AutoTranslator.Plugin.Core
          return null;
       }
 
-      public void Hook_TextChanged( object ui )
+      public void Hook_TextChanged( object ui, bool onEnable )
       {
          if( _textHooksEnabled && !_temporarilyDisabled )
          {
             TranslateOrQueueWebJob( ui, null, false );
          }
+
+         if( onEnable )
+         {
+            CheckSpriteRenderer( ui );
+         }
       }
 
-      public void Hook_ImageChangedOnComponent( object source, Texture2D texture = null, bool isPrefixHooked = false )
+      public void Hook_ImageChangedOnComponent( object source, Texture2D texture, bool isPrefixHooked, bool onEnable )
       {
          if( !_imageHooksEnabled ) return;
          if( !source.IsKnownImageType() ) return;
 
          HandleImage( source, texture, isPrefixHooked );
+
+         if( onEnable )
+         {
+            CheckSpriteRenderer( source );
+         }
       }
 
-      public void Hook_ImageChanged( Texture2D texture, bool isPrefixHooked = false )
+      public void Hook_ImageChanged( Texture2D texture, bool isPrefixHooked )
       {
          if( !_imageHooksEnabled ) return;
          if( texture == null ) return;
@@ -893,7 +972,7 @@ namespace XUnity.AutoTranslator.Plugin.Core
       {
          if( _hasOverrideFont )
          {
-            var info = ui.GetTextTranslationInfo();
+            var info = ui.GetOrCreateTextTranslationInfo();
             if( _overrideFont )
             {
                info?.ChangeFont( ui );
@@ -906,7 +985,7 @@ namespace XUnity.AutoTranslator.Plugin.Core
 
          if( Settings.ForceUIResizing )
          {
-            var info = ui.GetTextTranslationInfo();
+            var info = ui.GetOrCreateTextTranslationInfo();
             if( info?.IsCurrentlySettingText == false )
             {
                // force UI resizing is highly problematic for NGUI because text should somehow
@@ -919,6 +998,21 @@ namespace XUnity.AutoTranslator.Plugin.Core
          }
       }
 
+      private void CheckSpriteRenderer( object ui )
+      {
+         if( Settings.EnableSpriteRendererHooking )
+         {
+            var currentFrame = Time.frameCount;
+            var lastFrame = currentFrame - 1;
+
+            if( lastFrame != _lastSpriteUpdateFrame && currentFrame != _lastSpriteUpdateFrame )
+            {
+               _requireSpriteRendererCheckCausedBy = ui?.GetType().Name;
+            }
+            _lastSpriteUpdateFrame = currentFrame;
+         }
+      }
+
       /// <summary>
       /// Sets the text of a UI  text, while ensuring this will not fire a text changed event.
       /// </summary>
@@ -1048,7 +1142,7 @@ namespace XUnity.AutoTranslator.Plugin.Core
 
       private string TranslateOrQueueWebJob( object ui, string text, bool ignoreComponentState )
       {
-         var info = ui.GetTextTranslationInfo();
+         var info = ui.GetOrCreateTextTranslationInfo();
 
          if( _ongoingOperations.Contains( ui ) )
          {
@@ -1097,7 +1191,7 @@ namespace XUnity.AutoTranslator.Plugin.Core
          {
             try
             {
-               TranslateTexture( source, texture, isPrefixHooked, false );
+               TranslateTexture( source, texture, isPrefixHooked, null );
             }
             catch( Exception e )
             {
@@ -1106,19 +1200,19 @@ namespace XUnity.AutoTranslator.Plugin.Core
          }
       }
 
-      private void TranslateTexture( object ui, bool forceReload )
+      private void TranslateTexture( object ui, TextureReloadContext context )
       {
          if( ui is Texture2D texture2d )
          {
-            TranslateTexture( null, texture2d, false, forceReload );
+            TranslateTexture( null, texture2d, false, context );
          }
          else
          {
-            TranslateTexture( ui, null, false, forceReload );
+            TranslateTexture( ui, null, false, context );
          }
       }
 
-      private void TranslateTexture( object source, Texture2D texture, bool isPrefixHooked, bool forceReload )
+      private void TranslateTexture( object source, Texture2D texture, bool isPrefixHooked, TextureReloadContext context )
       {
          try
          {
@@ -1127,11 +1221,18 @@ namespace XUnity.AutoTranslator.Plugin.Core
             texture = texture ?? source.GetTexture();
             if( texture == null ) return;
 
-            var tti = texture.GetTextureTranslationInfo();
-            var iti = source.GetImageTranslationInfo();
+            var tti = texture.GetOrCreateTextureTranslationInfo();
+            var iti = source.GetOrCreateImageTranslationInfo();
             var key = tti.GetKey( texture );
             if( string.IsNullOrEmpty( key ) ) return;
 
+            bool hasContext = context != null;
+            bool forceReload = false;
+            if( hasContext )
+            {
+               forceReload = context.RegisterTextureInContextAndDetermineWhetherToReload( texture );
+            }
+
             if( TryGetTranslatedImage( key, out var newData ) )
             {
                if( _isInTranslatedMode )
@@ -1152,7 +1253,7 @@ namespace XUnity.AutoTranslator.Plugin.Core
                   // handle containing component
                   if( iti != null )
                   {
-                     if( !iti.IsTranslated || forceReload )
+                     if( !iti.IsTranslated || hasContext )
                      {
                         try
                         {
@@ -1248,6 +1349,11 @@ namespace XUnity.AutoTranslator.Plugin.Core
                   }
                }
             }
+
+            if( forceReload )
+            {
+               Logger.Current.Info( $"Reloaded texture: {texture.name} ({key})." );
+            }
          }
          finally
          {
@@ -1264,7 +1370,7 @@ namespace XUnity.AutoTranslator.Plugin.Core
             texture = texture ?? source.GetTexture();
             if( texture == null ) return;
 
-            var info = texture.GetTextureTranslationInfo();
+            var info = texture.GetOrCreateTextureTranslationInfo();
             if( info.HasDumpedAlternativeTexture ) return;
 
             try
@@ -1670,7 +1776,6 @@ namespace XUnity.AutoTranslator.Plugin.Core
       public IEnumerator DelayForSeconds( float delay, Action onContinue )
       {
          yield return new WaitForSeconds( delay );
-
          onContinue();
       }
 
@@ -1720,6 +1825,7 @@ namespace XUnity.AutoTranslator.Plugin.Core
 
             if( !Settings.IsShutdown )
             {
+               UpdateSpriteRenderers();
                PeriodicResetFrameCheck();
                IncrementBatchOperations();
                ResetThresholdTimerIfRequired();
@@ -1735,30 +1841,36 @@ namespace XUnity.AutoTranslator.Plugin.Core
 
             if( Input.anyKey )
             {
-               if( Settings.EnablePrintHierarchy && ( Input.GetKey( KeyCode.LeftAlt ) || Input.GetKey( KeyCode.RightAlt ) ) && Input.GetKeyDown( KeyCode.Y ) )
+               var isAltPressed = Input.GetKey( KeyCode.LeftAlt ) || Input.GetKey( KeyCode.RightAlt );
+
+               if( Settings.EnablePrintHierarchy && isAltPressed && Input.GetKeyDown( KeyCode.Y ) )
                {
                   PrintObjects();
                }
-               else if( ( Input.GetKey( KeyCode.LeftAlt ) || Input.GetKey( KeyCode.RightAlt ) ) && Input.GetKeyDown( KeyCode.T ) )
+               else if( isAltPressed && Input.GetKeyDown( KeyCode.T ) )
                {
                   ToggleTranslation();
                }
-               else if( ( Input.GetKey( KeyCode.LeftAlt ) || Input.GetKey( KeyCode.RightAlt ) ) && Input.GetKeyDown( KeyCode.F ) )
+               else if( isAltPressed && Input.GetKeyDown( KeyCode.F ) )
                {
                   ToggleFont();
                }
-               else if( ( Input.GetKey( KeyCode.LeftAlt ) || Input.GetKey( KeyCode.RightAlt ) ) && Input.GetKeyDown( KeyCode.D ) )
+               else if( isAltPressed && Input.GetKeyDown( KeyCode.D ) )
                {
                   DumpUntranslated();
                }
-               else if( ( Input.GetKey( KeyCode.LeftAlt ) || Input.GetKey( KeyCode.RightAlt ) ) && Input.GetKeyDown( KeyCode.R ) )
+               else if( isAltPressed && Input.GetKeyDown( KeyCode.R ) )
                {
                   ReloadTranslations();
                }
-               else if( ( Input.GetKey( KeyCode.LeftAlt ) || Input.GetKey( KeyCode.RightAlt ) ) && Input.GetKeyDown( KeyCode.U ) )
+               else if( isAltPressed && Input.GetKeyDown( KeyCode.U ) )
                {
                   ManualHook();
                }
+               else if( isAltPressed && Input.GetKeyDown( KeyCode.Q ) )
+               {
+                  RebootPlugin();
+               }
             }
          }
          catch( Exception e )
@@ -1767,8 +1879,26 @@ namespace XUnity.AutoTranslator.Plugin.Core
          }
       }
 
-      // create this as a field instead of local var, to prevent new creation on EVERY game loop
-      private readonly List<string> _kickedOff = new List<string>();
+      private void RebootPlugin()
+      {
+         if( Settings.IsShutdown )
+         {
+            if( !Settings.IsShutdownFatal )
+            {
+               _consecutiveErrors = 0;
+               Settings.IsShutdown = false;
+               Logger.Current.Info( "Rebooted Auto Translator." );
+            }
+            else
+            {
+               Logger.Current.Info( "Cannot reboot Auto Translator because the error that caused the shutdown is bad behaviour by the game." );
+            }
+         }
+         else
+         {
+            Logger.Current.Info( "Cannot reboot Auto Translator because it has not been shut down." );
+         }
+      }
 
       private void KickoffTranslations()
       {
@@ -1886,6 +2016,7 @@ namespace XUnity.AutoTranslator.Plugin.Core
             if( Settings.TranslationCount > Settings.MaxTranslationsBeforeShutdown )
             {
                Settings.IsShutdown = true;
+               Settings.IsShutdownFatal = true;
                Logger.Current.Error( $"Maximum translations ({Settings.MaxTranslationsBeforeShutdown}) per session reached. Shutting plugin down." );
 
                _unstartedJobs.Clear();
@@ -1922,6 +2053,7 @@ namespace XUnity.AutoTranslator.Plugin.Core
             if( Settings.TranslationCount > Settings.MaxTranslationsBeforeShutdown )
             {
                Settings.IsShutdown = true;
+               Settings.IsShutdownFatal = true;
                Logger.Current.Error( $"Maximum translations ({Settings.MaxTranslationsBeforeShutdown}) per session reached. Shutting plugin down." );
 
                _unstartedJobs.Clear();
@@ -1995,7 +2127,7 @@ namespace XUnity.AutoTranslator.Plugin.Core
                      var text = component.GetText().TrimIfConfigured();
                      if( text == job.Key.OriginalText )
                      {
-                        var info = component.GetTextTranslationInfo();
+                        var info = component.GetOrCreateTextTranslationInfo();
                         SetTranslatedText( component, job.TranslatedText, info );
                      }
                   }
@@ -2031,7 +2163,7 @@ namespace XUnity.AutoTranslator.Plugin.Core
                            {
                               if( translatedText != null )
                               {
-                                 var info = context.Component.GetTextTranslationInfo();
+                                 var info = context.Component.GetOrCreateTextTranslationInfo();
                                  SetTranslatedText( context.Component, translatedText, info );
                               }
                            }
@@ -2073,6 +2205,7 @@ namespace XUnity.AutoTranslator.Plugin.Core
       {
          LoadTranslations();
 
+         var context = new TextureReloadContext();
          foreach( var kvp in ObjectExtensions.GetAllRegisteredObjects() )
          {
             var ui = kvp.Key;
@@ -2096,7 +2229,7 @@ namespace XUnity.AutoTranslator.Plugin.Core
 
                if( Settings.EnableTextureTranslation )
                {
-                  TranslateTexture( ui, true );
+                  TranslateTexture( ui, context );
                }
             }
             catch( Exception )
@@ -2203,8 +2336,6 @@ namespace XUnity.AutoTranslator.Plugin.Core
 
          Logger.Current.Info( $"Toggling translations of {objects.Count} objects." );
 
-         // FIXME: Translate TEXTURES first??? Problem if texture is not related to a component!
-
          if( _isInTranslatedMode )
          {
             // make sure we use the translated version of all texts
@@ -2227,7 +2358,7 @@ namespace XUnity.AutoTranslator.Plugin.Core
 
                   if( Settings.EnableTextureTranslation && Settings.EnableTextureToggling )
                   {
-                     TranslateTexture( ui, false );
+                     TranslateTexture( ui, null );
                   }
                }
                catch( Exception )
@@ -2259,7 +2390,7 @@ namespace XUnity.AutoTranslator.Plugin.Core
 
                   if( Settings.EnableTextureTranslation && Settings.EnableTextureToggling )
                   {
-                     TranslateTexture( ui, false );
+                     TranslateTexture( ui, null );
                   }
                }
                catch( Exception )
@@ -2342,7 +2473,7 @@ namespace XUnity.AutoTranslator.Plugin.Core
             var textures = Resources.FindObjectsOfTypeAll<Texture2D>();
             foreach( var texture in textures )
             {
-               Hook_ImageChanged( texture );
+               Hook_ImageChanged( texture, false );
             }
 
             //// scan all components and set dirty
@@ -2398,14 +2529,14 @@ namespace XUnity.AutoTranslator.Plugin.Core
             {
                if( component.IsKnownTextType() )
                {
-                  Hook_TextChanged( component );
+                  Hook_TextChanged( component, false );
                }
 
                if( Settings.EnableTextureTranslation || Settings.EnableTextureDumping )
                {
                   if( component.IsKnownImageType() )
                   {
-                     Hook_ImageChangedOnComponent( component );
+                     Hook_ImageChangedOnComponent( component, null, false, false );
                   }
                }
             }
@@ -2431,4 +2562,13 @@ namespace XUnity.AutoTranslator.Plugin.Core
          _temporarilyDisabled = false;
       }
    }
+
+   internal static class SceneManagerLoader
+   {
+      public static void EnableSceneLoadScanInternal( AutoTranslationPlugin plugin )
+      {
+         // specified in own method, because of chance that this has changed through Unity lifetime
+         SceneManager.sceneLoaded += ( arg1, arg2 ) => plugin.OnLevelWasLoadedFromSceneManager( arg1.buildIndex );
+      }
+   }
 }

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

@@ -1,5 +1,6 @@
 using System;
 using System.Collections.Generic;
+using System.Globalization;
 using System.IO;
 using System.Linq;
 using System.Text;
@@ -13,6 +14,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Configuration
    public static class Settings
    {
       // cannot be changed
+      public static readonly int MaxMaxCharactersPerTranslation = 500;
       public static readonly string DefaultLanguage = "en";
       public static readonly string DefaultFromLanguage = "ja";
       public static readonly string EnglishLanguage = "en";
@@ -30,6 +32,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Configuration
 
 
       public static bool IsShutdown = false;
+      public static bool IsShutdownFatal = false;
       public static int TranslationCount = 0;
       public static int MaxAvailableBatchOperations = 40;
 
@@ -81,12 +84,14 @@ namespace XUnity.AutoTranslator.Plugin.Core.Configuration
       public static float? ResizeUILineSpacingScale;
       public static bool ForceUIResizing;
       public static string[] IgnoreTextStartingWith;
+      public static bool TextGetterCompatibilityMode;
 
       public static string TextureDirectory;
       public static bool EnableTextureTranslation;
       public static bool EnableTextureDumping;
       public static bool EnableTextureToggling;
       public static bool EnableTextureScanOnSceneLoad;
+      public static bool EnableSpriteRendererHooking;
       public static bool LoadUnmodifiedTextures;
       //public static bool DeleteUnmodifiedTextures;
       public static TextureHashGenerationStrategy TextureHashGenerationStrategy;
@@ -129,7 +134,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Configuration
 
          Delay = Config.Current.Preferences[ "Behaviour" ][ "Delay" ].GetOrDefault( 0f );
          MaxCharactersPerTranslation = Config.Current.Preferences[ "Behaviour" ][ "MaxCharactersPerTranslation" ].GetOrDefault( 200 );
-         IgnoreWhitespaceInDialogue = Config.Current.Preferences[ "Behaviour" ][ "IgnoreWhitespaceInDialogue" ].GetOrDefault( ClrTypes.AdvEngine == null );
+         IgnoreWhitespaceInDialogue = Config.Current.Preferences[ "Behaviour" ][ "IgnoreWhitespaceInDialogue" ].GetOrDefault( true );
          IgnoreWhitespaceInNGUI = Config.Current.Preferences[ "Behaviour" ][ "IgnoreWhitespaceInNGUI" ].GetOrDefault( true );
          MinDialogueChars = Config.Current.Preferences[ "Behaviour" ][ "MinDialogueChars" ].GetOrDefault( 20 );
          ForceSplitTextAfterCharacters = Config.Current.Preferences[ "Behaviour" ][ "ForceSplitTextAfterCharacters" ].GetOrDefault( 0 );
@@ -144,16 +149,24 @@ namespace XUnity.AutoTranslator.Plugin.Core.Configuration
          ForceUIResizing = Config.Current.Preferences[ "Behaviour" ][ "ForceUIResizing" ].GetOrDefault( false );
          IgnoreTextStartingWith = Config.Current.Preferences[ "Behaviour" ][ "IgnoreTextStartingWith" ].GetOrDefault( "\\u180e;", true )
             ?.Split( new[] { ';' }, StringSplitOptions.RemoveEmptyEntries ).Select( x => x.UnescapeJson() ).ToArray() ?? new string[ 0 ];
+         TextGetterCompatibilityMode = Config.Current.Preferences[ "Behaviour" ][ "TextGetterCompatibilityMode" ].GetOrDefault( false );
 
          TextureDirectory = Config.Current.Preferences[ "Texture" ][ "TextureDirectory" ].GetOrDefault( @"Translation\Texture" );
          EnableTextureTranslation = Config.Current.Preferences[ "Texture" ][ "EnableTextureTranslation" ].GetOrDefault( false );
          EnableTextureDumping = Config.Current.Preferences[ "Texture" ][ "EnableTextureDumping" ].GetOrDefault( false );
          EnableTextureToggling = Config.Current.Preferences[ "Texture" ][ "EnableTextureToggling" ].GetOrDefault( false );
          EnableTextureScanOnSceneLoad = Config.Current.Preferences[ "Texture" ][ "EnableTextureScanOnSceneLoad" ].GetOrDefault( false );
+         EnableSpriteRendererHooking = Config.Current.Preferences[ "Texture" ][ "EnableSpriteRendererHooking" ].GetOrDefault( false );
          LoadUnmodifiedTextures = Config.Current.Preferences[ "Texture" ][ "LoadUnmodifiedTextures" ].GetOrDefault( false );
          //DeleteUnmodifiedTextures = Config.Current.Preferences[ "Texture" ][ "DeleteUnmodifiedTextures" ].GetOrDefault( false );
          TextureHashGenerationStrategy = Config.Current.Preferences[ "Texture" ][ "TextureHashGenerationStrategy" ].GetOrDefault( TextureHashGenerationStrategy.FromImageName );
 
+         if( MaxCharactersPerTranslation > MaxMaxCharactersPerTranslation )
+         {
+            Config.Current.Preferences[ "Behaviour" ][ "MaxCharactersPerTranslation" ].Value = MaxMaxCharactersPerTranslation.ToString( CultureInfo.InvariantCulture );
+            MaxCharactersPerTranslation = MaxMaxCharactersPerTranslation;
+         }
+
          // special handling because of enum parsing
          try
          {

+ 10 - 4
src/XUnity.AutoTranslator.Plugin.Core/Constants/ClrTypes.cs

@@ -29,14 +29,17 @@ namespace XUnity.AutoTranslator.Plugin.Core.Constants
       public static readonly Type Text = FindType( "UnityEngine.UI.Text" );
       public static readonly Type GUI = FindType( "UnityEngine.GUI" );
       public static readonly Type ImageConversion = FindType( "UnityEngine.ImageConversion" );
-
-      // Something...
-      public static readonly Type Typewriter = FindType( "Typewriter" );
+      public static readonly Type Texture = FindType( "UnityEngine.Texture" );
+      public static readonly Type SpriteRenderer = FindType( "UnityEngine.SpriteRenderer" );
+      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.SceneManager" );
+      public static readonly Type SceneManager = FindType( "UnityEngine.SceneManagement.SceneManager" );
       public static readonly Type Scene = FindType( "UnityEngine.SceneManagement.Scene" );
 
+      // Something...
+      public static readonly Type Typewriter = FindType( "Typewriter" );
+
       // Utage
       public static readonly Type UguiNovelText = FindType( "Utage.UguiNovelText" );
       public static readonly Type AdvCommand = FindType( "Utage.AdvCommand" );
@@ -45,6 +48,9 @@ namespace XUnity.AutoTranslator.Plugin.Core.Constants
       public static readonly Type AdvScenarioData = FindType( "Utage.AdvScenarioData" );
       public static readonly Type AdvScenarioLabelData = FindType( "Utage.AdvScenarioLabelData" );
 
+      // Live2D
+      public static readonly Type CubismRenderer = FindType( "Live2D.Cubism.Rendering.CubismRenderer" );
+
       private static Type FindType( string name )
       {
          return AppDomain.CurrentDomain.GetAssemblies()

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

@@ -11,6 +11,6 @@ namespace XUnity.AutoTranslator.Plugin.Core.Constants
 
       public const string Name = "XUnity Auto Translator";
 
-      public const string Version = "2.17.0";
+      public const string Version = "2.18.0";
    }
 }

+ 109 - 0
src/XUnity.AutoTranslator.Plugin.Core/Debugging/ConsoleEncoding.cs

@@ -0,0 +1,109 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace XUnity.AutoTranslator.Plugin.Core.Debugging
+{
+   internal class ConsoleEncoding : Encoding
+   {
+      private byte[] _byteBuffer = new byte[ 256 ];
+      private char[] _charBuffer = new char[ 256 ];
+      private byte[] _zeroByte = new byte[ 0 ];
+      private char[] _zeroChar = new char[ 0 ];
+
+      private readonly uint _codePage;
+      public override int CodePage => (int)_codePage;
+
+      private ConsoleEncoding( uint codePage )
+      {
+         _codePage = codePage;
+      }
+
+      public static ConsoleEncoding GetEncoding( uint codePage )
+      {
+         return new ConsoleEncoding( codePage );
+      }
+
+      public override int GetByteCount( char[] chars, int index, int count )
+      {
+         WriteCharBuffer( chars, index, count );
+         int result = Kernel32.WideCharToMultiByte( _codePage, 0, _charBuffer, count, _zeroByte, 0, IntPtr.Zero, IntPtr.Zero );
+         return result;
+      }
+
+      public override int GetBytes( char[] chars, int charIndex, int charCount, byte[] bytes, int byteIndex )
+      {
+         var byteCount = GetMaxByteCount( charCount );
+
+         WriteCharBuffer( chars, charIndex, charCount );
+
+         ExpandByteBuffer( byteCount );
+         int result = Kernel32.WideCharToMultiByte( _codePage, 0, chars, charCount, _byteBuffer, byteCount, IntPtr.Zero, IntPtr.Zero );
+         ReadByteBuffer( bytes, byteIndex, byteCount );
+
+         return result;
+      }
+
+      public override int GetCharCount( byte[] bytes, int index, int count )
+      {
+         WriteByteBuffer( bytes, index, count );
+         int result = Kernel32.MultiByteToWideChar( _codePage, 0, bytes, count, _zeroChar, 0 );
+         return result;
+      }
+
+      public override int GetChars( byte[] bytes, int byteIndex, int byteCount, char[] chars, int charIndex )
+      {
+         var charCount = GetMaxCharCount( byteCount );
+
+         WriteByteBuffer( bytes, byteIndex, byteCount );
+
+         ExpandCharBuffer( charCount );
+         int result = Kernel32.MultiByteToWideChar( _codePage, 0, bytes, byteCount, _charBuffer, charCount );
+         ReadCharBuffer( chars, charIndex, charCount );
+
+         return result;
+      }
+
+      public override int GetMaxByteCount( int charCount ) => charCount * 2;
+      public override int GetMaxCharCount( int byteCount ) => byteCount;
+
+      private void ExpandByteBuffer( int count )
+      {
+         if( _byteBuffer.Length < count )
+            _byteBuffer = new byte[ count ];
+      }
+
+      private void ExpandCharBuffer( int count )
+      {
+         if( _charBuffer.Length < count )
+            _charBuffer = new char[ count ];
+      }
+
+      private void ReadByteBuffer( byte[] bytes, int index, int count )
+      {
+         for( int i = 0 ; i < count ; i++ )
+            bytes[ index + i ] = _byteBuffer[ i ];
+      }
+
+      private void ReadCharBuffer( char[] chars, int index, int count )
+      {
+         for( int i = 0 ; i < count ; i++ )
+            chars[ index + i ] = _charBuffer[ i ];
+      }
+
+      private void WriteByteBuffer( byte[] bytes, int index, int count )
+      {
+         ExpandByteBuffer( count );
+         for( int i = 0 ; i < count ; i++ )
+            _byteBuffer[ i ] = bytes[ index + i ];
+      }
+
+      private void WriteCharBuffer( char[] chars, int index, int count )
+      {
+         ExpandCharBuffer( count );
+         for( int i = 0 ; i < count ; i++ )
+            _charBuffer[ i ] = chars[ index + i ];
+      }
+   }
+}

+ 5 - 0
src/XUnity.AutoTranslator.Plugin.Core/Debugging/DebugConsole.cs

@@ -24,6 +24,11 @@ namespace XUnity.AutoTranslator.Plugin.Core.Debugging
 
          Console.SetOut( writer );
          Console.SetError( writer );
+
+         uint shiftjisCodePage = 932;
+
+         Kernel32.SetConsoleOutputCP( shiftjisCodePage );
+         Console.OutputEncoding = ConsoleEncoding.GetEncoding( shiftjisCodePage );
       }
    }
 }

+ 23 - 0
src/XUnity.AutoTranslator.Plugin.Core/Debugging/Kernel32.cs

@@ -32,5 +32,28 @@ namespace XUnity.AutoTranslator.Plugin.Core.Debugging
 
       [DllImport( "kernel32.dll", ExactSpelling = true, SetLastError = true )]
       public static extern bool CloseHandle( IntPtr handle );
+
+      [DllImport( "kernel32.dll" )]
+      public static extern IntPtr SetConsoleOutputCP( uint codepage );
+
+      [DllImport( "kernel32.dll", SetLastError = true )]
+      public static extern int WideCharToMultiByte(
+          uint codePage,
+          uint dwFlags,
+          [In, MarshalAs( UnmanagedType.LPArray )] char[] lpWideCharStr,
+          int cchWideChar,
+          [Out, MarshalAs( UnmanagedType.LPArray )] byte[] lpMultiByteStr,
+          int cbMultiByte,
+          IntPtr lpDefaultChar,
+          IntPtr lpUsedDefaultChar );
+
+      [DllImport( "kernel32.dll", SetLastError = true )]
+      public static extern int MultiByteToWideChar(
+          uint codePage,
+          uint dwFlags,
+          [In, MarshalAs( UnmanagedType.LPArray )] byte[] lpMultiByteStr,
+          int cbMultiByte,
+          [Out, MarshalAs( UnmanagedType.LPArray )] char[] lpWideCharStr,
+          int cchWideChar );
    }
 }

+ 8 - 1
src/XUnity.AutoTranslator.Plugin.Core/Extensions/ComponentExtensions.cs

@@ -1,5 +1,6 @@
 using System;
 using System.Collections.Generic;
+using System.Diagnostics;
 using System.Linq;
 using System.Text;
 using Harmony;
@@ -13,6 +14,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Extensions
       private static readonly string TextPropertyName = "text";
       private static readonly string TexturePropertyName = "texture";
       private static readonly string MainTexturePropertyName = "mainTexture";
+      private static readonly string CapitalMainTexturePropertyName = "MainTexture";
       private static readonly string MarkAsChangedMethodName = "MarkAsChanged";
 
       public static string GetText( this object ui )
@@ -78,12 +80,17 @@ namespace XUnity.AutoTranslator.Plugin.Core.Extensions
          {
             return rawImage.mainTexture as Texture2D;
          }
+         else if( ui is SpriteRenderer spriteRenderer )
+         {
+            return spriteRenderer.sprite?.texture;
+         }
          else
          {
             // lets attempt some reflection for several known types
             var type = ui.GetType();
             var texture = type.GetProperty( MainTexturePropertyName )?.GetValue( ui, null )
-               ?? type.GetProperty( TexturePropertyName )?.GetValue( ui, null );
+               ?? type.GetProperty( TexturePropertyName )?.GetValue( ui, null )
+               ?? type.GetProperty( CapitalMainTexturePropertyName )?.GetValue( ui, null );
 
             return texture as Texture2D;
          }

+ 56 - 6
src/XUnity.AutoTranslator.Plugin.Core/Extensions/ObjectExtensions.cs

@@ -35,7 +35,8 @@ namespace XUnity.AutoTranslator.Plugin.Core.Extensions
       {
          var type = ui.GetType();
 
-         return ( ui is Material || ui is Image || ui is RawImage )
+         return ( ui is Material || ui is Image || ui is RawImage || ui is SpriteRenderer )
+            || ( ClrTypes.CubismRenderer != null && ClrTypes.CubismRenderer.IsAssignableFrom( type ) )
             || ( ClrTypes.UIWidget != null && type != ClrTypes.UILabel && ClrTypes.UIWidget.IsAssignableFrom( type ) )
             || ( ClrTypes.UIAtlas != null && ClrTypes.UIAtlas.IsAssignableFrom( type ) )
             || ( ClrTypes.UITexture != null && ClrTypes.UITexture.IsAssignableFrom( type ) )
@@ -93,6 +94,17 @@ namespace XUnity.AutoTranslator.Plugin.Core.Extensions
          return ClrTypes.UILabel != null && ClrTypes.UILabel.IsAssignableFrom( type );
       }
 
+      public static TextTranslationInfo GetOrCreateTextTranslationInfo( this object obj )
+      {
+         if( !Settings.EnableObjectTracking ) return null;
+
+         if( !obj.SupportsStabilization() ) return null;
+
+         var info = obj.GetOrCreate<TextTranslationInfo>();
+
+         return info;
+      }
+
       public static TextTranslationInfo GetTextTranslationInfo( this object obj )
       {
          if( !Settings.EnableObjectTracking ) return null;
@@ -104,17 +116,17 @@ namespace XUnity.AutoTranslator.Plugin.Core.Extensions
          return info;
       }
 
-      public static ImageTranslationInfo GetImageTranslationInfo( this object obj )
+      public static ImageTranslationInfo GetOrCreateImageTranslationInfo( this object obj )
       {
-         return obj.Get<ImageTranslationInfo>();
+         return obj.GetOrCreate<ImageTranslationInfo>();
       }
 
-      public static TextureTranslationInfo GetTextureTranslationInfo( this Texture texture )
+      public static TextureTranslationInfo GetOrCreateTextureTranslationInfo( this Texture texture )
       {
-         return texture.Get<TextureTranslationInfo>();
+         return texture.GetOrCreate<TextureTranslationInfo>();
       }
 
-      public static T Get<T>( this object obj )
+      public static T GetOrCreate<T>( this object obj )
          where T : new()
       {
          if( obj == null ) return default( T );
@@ -159,6 +171,44 @@ namespace XUnity.AutoTranslator.Plugin.Core.Extensions
          }
       }
 
+      public static T Get<T>( this object obj )
+         where T : new()
+      {
+         if( obj == null ) return default( T );
+
+         lock( Sync )
+         {
+            if( DynamicFields.TryGetValue( obj, out object value ) )
+            {
+               if( value is Dictionary<Type, object> existingDictionary )
+               {
+                  if( existingDictionary.TryGetValue( typeof( T ), out value ) )
+                  {
+                     if( value is T )
+                     {
+                        return (T)value;
+                     }
+                     else
+                     {
+                        return default( T );
+                     }
+                  }
+               }
+
+               if( value is T )
+               {
+                  return (T)value;
+               }
+               else
+               {
+                  return default( T );
+               }
+            }
+         }
+
+         return default( T );
+      }
+
       public static void Cull()
       {
          lock( Sync )

+ 4 - 4
src/XUnity.AutoTranslator.Plugin.Core/Extensions/TextureExtensions.cs

@@ -14,10 +14,10 @@ namespace XUnity.AutoTranslator.Plugin.Core.Extensions
       private static readonly MethodInfo LoadImage = AccessTools.Method( ClrTypes.ImageConversion, "LoadImage", new[] { typeof( Texture2D ), typeof( byte[] ), typeof( bool ) } );
       private static readonly MethodInfo EncodeToPNG = AccessTools.Method( ClrTypes.ImageConversion, "EncodeToPNG", new[] { typeof( Texture2D ) } );
 
-      public static bool IsNonReadable( this Texture2D texture )
-      {
-         return texture.GetRawTextureData().Length == 0;
-      }
+      //public static bool IsNonReadable( this Texture2D texture )
+      //{
+      //   return texture.GetRawTextureData().Length == 0;
+      //}
 
       public static void LoadImageEx( this Texture2D texture, byte[] data, bool markNonReadable )
       {

+ 0 - 1
src/XUnity.AutoTranslator.Plugin.Core/Features.cs

@@ -1,5 +1,4 @@
 using System;
-using System.Collections.Generic;
 using System.Linq;
 using System.Text;
 using XUnity.AutoTranslator.Plugin.Core.Constants;

+ 10 - 8
src/XUnity.AutoTranslator.Plugin.Core/Hooks/HooksSetup.cs

@@ -46,32 +46,35 @@ namespace XUnity.AutoTranslator.Plugin.Core.Hooks
          }
       }
 
-      public static void InstallImageHooks()
+      public static void InstallTextGetterCompatHooks()
       {
          try
          {
-            if( Settings.EnableTextureTranslation || Settings.EnableTextureDumping )
+            if( Settings.TextGetterCompatibilityMode )
             {
-               _harmony.PatchAll( UGUIImageHooks.All );
+               _harmony.PatchAll( TextGetterCompatHooks.All );
             }
          }
          catch( Exception e )
          {
-            Logger.Current.Error( e, "An error occurred while setting up image hooks for UnityEngine." );
+            Logger.Current.Error( e, "An error occurred while setting up text getter compat hooks." );
          }
+      }
 
+      public static void InstallImageHooks()
+      {
          try
          {
             if( Settings.EnableTextureTranslation || Settings.EnableTextureDumping )
             {
-               _harmony.PatchAll( NGUIImageHooks.All );
+               _harmony.PatchAll( ImageHooks.All );
             }
          }
          catch( Exception e )
          {
-            Logger.Current.Error( e, "An error occurred while setting up image hooks for NGUI." );
+            Logger.Current.Error( e, "An error occurred while setting up image hooks." );
          }
-
+         
          //var knownTypes = new HashSet<Type> { typeof( Texture ), typeof( Texture2D ), typeof( Sprite ), typeof( Material ) };
          //foreach( var assembly in AppDomain.CurrentDomain.GetAssemblies() )
          //{
@@ -112,7 +115,6 @@ namespace XUnity.AutoTranslator.Plugin.Core.Hooks
          //      Logger.Current.Error( "Failed getting types of assembly: " + assembly.FullName );
          //   }
          //}
-
       }
 
       public static void InstallTextHooks()

+ 10 - 10
src/XUnity.AutoTranslator.Plugin.Core/Hooks/IMGUIHooks.cs

@@ -43,7 +43,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.IMGUI
       {
          if( !IMGUIHooks.HooksOverriden )
          {
-            AutoTranslationPlugin.Current.Hook_TextChanged( content );
+            AutoTranslationPlugin.Current.Hook_TextChanged( content, false );
          }
       }
    }
@@ -65,7 +65,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.IMGUI
       {
          if( !IMGUIHooks.HooksOverriden )
          {
-            AutoTranslationPlugin.Current.Hook_TextChanged( content );
+            AutoTranslationPlugin.Current.Hook_TextChanged( content, false );
          }
       }
 
@@ -88,7 +88,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.IMGUI
       {
          if( !IMGUIHooks.HooksOverriden )
          {
-            AutoTranslationPlugin.Current.Hook_TextChanged( content );
+            AutoTranslationPlugin.Current.Hook_TextChanged( content, false );
          }
       }
    }
@@ -110,7 +110,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.IMGUI
       {
          if( !IMGUIHooks.HooksOverriden )
          {
-            AutoTranslationPlugin.Current.Hook_TextChanged( content );
+            AutoTranslationPlugin.Current.Hook_TextChanged( content, false );
          }
       }
    }
@@ -132,7 +132,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.IMGUI
       {
          if( !IMGUIHooks.HooksOverriden )
          {
-            AutoTranslationPlugin.Current.Hook_TextChanged( content );
+            AutoTranslationPlugin.Current.Hook_TextChanged( content, false );
          }
       }
    }
@@ -154,7 +154,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.IMGUI
       {
          if( !IMGUIHooks.HooksOverriden )
          {
-            AutoTranslationPlugin.Current.Hook_TextChanged( content );
+            AutoTranslationPlugin.Current.Hook_TextChanged( content, false );
          }
       }
    }
@@ -176,7 +176,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.IMGUI
       {
          if( !IMGUIHooks.HooksOverriden )
          {
-            AutoTranslationPlugin.Current.Hook_TextChanged( title );
+            AutoTranslationPlugin.Current.Hook_TextChanged( title, false );
          }
       }
    }
@@ -200,7 +200,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.IMGUI
          {
             foreach( var content in contents )
             {
-               AutoTranslationPlugin.Current.Hook_TextChanged( content );
+               AutoTranslationPlugin.Current.Hook_TextChanged( content, false );
             }
          }
       }
@@ -223,7 +223,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.IMGUI
       {
          if( !IMGUIHooks.HooksOverriden )
          {
-            AutoTranslationPlugin.Current.Hook_TextChanged( content );
+            AutoTranslationPlugin.Current.Hook_TextChanged( content, false );
          }
       }
    }
@@ -245,7 +245,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.IMGUI
       {
          if( !IMGUIHooks.HooksOverriden )
          {
-            AutoTranslationPlugin.Current.Hook_TextChanged( content );
+            AutoTranslationPlugin.Current.Hook_TextChanged( content, false );
          }
       }
    }

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

@@ -0,0 +1,661 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using Harmony;
+using UnityEngine;
+using UnityEngine.UI;
+using XUnity.AutoTranslator.Plugin.Core.Constants;
+using XUnity.AutoTranslator.Plugin.Core.Extensions;
+
+namespace XUnity.AutoTranslator.Plugin.Core.Hooks
+{
+   public static class ImageHooks
+   {
+      public static readonly Type[] All = new[] {
+         typeof( MaskableGraphic_OnEnable_Hook ),
+         typeof( Image_sprite_Hook ),
+         typeof( Image_overrideSprite_Hook ),
+         typeof( Image_material_Hook ),
+         typeof( RawImage_texture_Hook ),
+         typeof( Cursor_SetCursor_Hook ),
+         typeof( SpriteRenderer_sprite_Hook ),
+
+         // fallback hooks on material (Prefix hooks)
+         typeof( Material_mainTexture_Hook ),
+
+         // Live2D
+         typeof( CubismRenderer_MainTexture_Hook ),
+         typeof( CubismRenderer_TryInitialize_Hook ),
+
+         // NGUI
+         typeof( UIAtlas_spriteMaterial_Hook ),
+         typeof( UISprite_OnInit_Hook ),
+         typeof( UISprite_material_Hook ),
+         typeof( UISprite_atlas_Hook ),
+         typeof( UI2DSprite_sprite2D_Hook ),
+         typeof( UI2DSprite_material_Hook ),
+         typeof( UITexture_mainTexture_Hook ),
+         typeof( UITexture_material_Hook ),
+         typeof( UIPanel_clipTexture_Hook ),
+         typeof( UIRect_OnInit_Hook ),
+         //typeof( UIFont_dynamicFont_Hook ),
+         //typeof( UIFont_material_Hook ),
+         //typeof( UILabel_bitmapFont_Hook ),
+         //typeof( UILabel_trueTypeFont_Hook ),
+      };
+   }
+
+   //public static class GenericPrefix_Hook
+   //{
+   //   public static void Prefix( object __instance, object value )
+   //   {
+   //      if( value is Texture2D texture2d )
+   //      {
+   //         AutoTranslationPlugin.Current.Hook_ImageChangedOnComponent( __instance, texture2d, true );
+   //      }
+   //   }
+   //}
+
+   [Harmony]
+   public static class SpriteRenderer_sprite_Hook
+   {
+      static bool Prepare( HarmonyInstance instance )
+      {
+         return ClrTypes.SpriteRenderer != null;
+      }
+
+      static MethodBase TargetMethod( HarmonyInstance instance )
+      {
+         return AccessTools.Property( ClrTypes.SpriteRenderer, "sprite" ).GetSetMethod();
+      }
+
+      public static void Postfix( object __instance )
+      {
+         AutoTranslationPlugin.Current.Hook_ImageChangedOnComponent( __instance, null, false, false );
+      }
+   }
+
+   //[Harmony]
+   //public static class SpriteRenderer_get_sprite_Hook
+   //{
+   //   static bool Prepare( HarmonyInstance instance )
+   //   {
+   //      return ClrTypes.SpriteRenderer != null;
+   //   }
+
+   //   static MethodBase TargetMethod( HarmonyInstance instance )
+   //   {
+   //      return AccessTools.Property( ClrTypes.SpriteRenderer, "sprite" ).GetGetMethod();
+   //   }
+
+   //   public static void Postfix( object __instance )
+   //   {
+   //      Logger.Current.Error( new StackTrace().ToString() );
+   //      AutoTranslationPlugin.Current.Hook_ImageChangedOnComponent( __instance, null, false, false );
+   //   }
+   //}
+
+   //[Harmony]
+   //public static class SpriteRenderer_get_size_Hook
+   //{
+   //   static bool Prepare( HarmonyInstance instance )
+   //   {
+   //      return ClrTypes.SpriteRenderer != null;
+   //   }
+
+   //   static MethodBase TargetMethod( HarmonyInstance instance )
+   //   {
+   //      return AccessTools.Property( ClrTypes.SpriteRenderer, "size" ).GetGetMethod();
+   //   }
+
+   //   public static void Postfix( object __instance )
+   //   {
+   //      Logger.Current.Error( "SpriteRenderer_get_size_Hook" );
+   //      AutoTranslationPlugin.Current.Hook_ImageChangedOnComponent( __instance, null, false, false );
+   //   }
+   //}
+
+   //[Harmony]
+   //public static class SpriteRenderer_set_size_Hook
+   //{
+   //   static bool Prepare( HarmonyInstance instance )
+   //   {
+   //      return ClrTypes.SpriteRenderer != null;
+   //   }
+
+   //   static MethodBase TargetMethod( HarmonyInstance instance )
+   //   {
+   //      return AccessTools.Property( ClrTypes.SpriteRenderer, "size" ).GetSetMethod();
+   //   }
+
+   //   public static void Postfix( object __instance )
+   //   {
+   //      Logger.Current.Error( "SpriteRenderer_set_size_Hook" );
+   //      AutoTranslationPlugin.Current.Hook_ImageChangedOnComponent( __instance, null, false, false );
+   //   }
+   //}
+
+   //[Harmony]
+   //public static class SpriteRenderer_get_color_Hook
+   //{
+   //   static bool Prepare( HarmonyInstance instance )
+   //   {
+   //      return ClrTypes.SpriteRenderer != null;
+   //   }
+
+   //   static MethodBase TargetMethod( HarmonyInstance instance )
+   //   {
+   //      return AccessTools.Property( ClrTypes.SpriteRenderer, "color" ).GetGetMethod();
+   //   }
+
+   //   public static void Postfix( object __instance )
+   //   {
+   //      Logger.Current.Error( "SpriteRenderer_get_color_Hook" );
+   //      AutoTranslationPlugin.Current.Hook_ImageChangedOnComponent( __instance, null, false, false );
+   //   }
+   //}
+
+   //[Harmony]
+   //public static class SpriteRenderer_set_color_Hook
+   //{
+   //   static bool Prepare( HarmonyInstance instance )
+   //   {
+   //      return ClrTypes.SpriteRenderer != null;
+   //   }
+
+   //   static MethodBase TargetMethod( HarmonyInstance instance )
+   //   {
+   //      return AccessTools.Property( ClrTypes.SpriteRenderer, "color" ).GetSetMethod();
+   //   }
+
+   //   public static void Postfix( object __instance )
+   //   {
+   //      Logger.Current.Error( "SpriteRenderer_set_color_Hook" );
+   //      AutoTranslationPlugin.Current.Hook_ImageChangedOnComponent( __instance, null, false, false );
+   //   }
+   //}
+
+   //[Harmony]
+   //public static class SpriteRenderer_GetSpriteBounds_Hook
+   //{
+   //   static bool Prepare( HarmonyInstance instance )
+   //   {
+   //      return ClrTypes.SpriteRenderer != null;
+   //   }
+
+   //   static MethodBase TargetMethod( HarmonyInstance instance )
+   //   {
+   //      return AccessTools.Method( ClrTypes.SpriteRenderer, "GetSpriteBounds" );
+   //   }
+
+   //   public static void Postfix( object __instance )
+   //   {
+   //      Logger.Current.Error( "SpriteRenderer_GetSpriteBounds_Hook" );
+   //      AutoTranslationPlugin.Current.Hook_ImageChangedOnComponent( __instance, null, false, false );
+   //   }
+   //}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+   [Harmony]
+   public static class CubismRenderer_MainTexture_Hook
+   {
+      static bool Prepare( HarmonyInstance instance )
+      {
+         return ClrTypes.CubismRenderer != null;
+      }
+
+      static MethodBase TargetMethod( HarmonyInstance instance )
+      {
+         return AccessTools.Property( ClrTypes.CubismRenderer, "MainTexture" ).GetSetMethod();
+      }
+
+      public static void Prefix( object __instance, Texture2D value )
+      {
+         AutoTranslationPlugin.Current.Hook_ImageChangedOnComponent( __instance, value, true , false);
+      }
+   }
+
+   [Harmony]
+   public static class CubismRenderer_TryInitialize_Hook
+   {
+      static bool Prepare( HarmonyInstance instance )
+      {
+         return ClrTypes.CubismRenderer != null;
+      }
+
+      static MethodBase TargetMethod( HarmonyInstance instance )
+      {
+         return AccessTools.Method( ClrTypes.CubismRenderer, "TryInitialize" );
+      }
+
+      public static void Prefix( object __instance )
+      {
+         AutoTranslationPlugin.Current.Hook_ImageChangedOnComponent( __instance, null, true, true );
+      }
+   }
+
+   [Harmony]
+   public static class Material_mainTexture_Hook
+   {
+      static bool Prepare( HarmonyInstance instance )
+      {
+         return true;
+      }
+
+      static MethodBase TargetMethod( HarmonyInstance instance )
+      {
+         return AccessTools.Property( typeof( Material ), "mainTexture" ).GetSetMethod();
+      }
+
+      public static void Prefix( object __instance, Texture value )
+      {
+         if( value is Texture2D texture2d )
+         {
+            AutoTranslationPlugin.Current.Hook_ImageChangedOnComponent( __instance, texture2d, true, false );
+         }
+      }
+   }
+
+   [Harmony]
+   public static class MaskableGraphic_OnEnable_Hook
+   {
+      static bool Prepare( HarmonyInstance instance )
+      {
+         return true;
+      }
+
+      static MethodBase TargetMethod( HarmonyInstance instance )
+      {
+         return AccessTools.Method( typeof( MaskableGraphic ), "OnEnable" );
+      }
+
+      public static void Postfix( object __instance )
+      {
+         if( __instance is Image || __instance is RawImage )
+         {
+            AutoTranslationPlugin.Current.Hook_ImageChangedOnComponent( __instance, null, false, true );
+         }
+      }
+   }
+
+   [Harmony]
+   public static class Image_sprite_Hook
+   {
+      static bool Prepare( HarmonyInstance instance )
+      {
+         return true;
+      }
+
+      static MethodBase TargetMethod( HarmonyInstance instance )
+      {
+         return AccessTools.Property( typeof( Image ), "sprite" ).GetSetMethod();
+      }
+
+      public static void Postfix( object __instance )
+      {
+         AutoTranslationPlugin.Current.Hook_ImageChangedOnComponent( __instance, null, false, false );
+      }
+   }
+
+   [Harmony]
+   public static class Image_overrideSprite_Hook
+   {
+      static bool Prepare( HarmonyInstance instance )
+      {
+         return true;
+      }
+
+      static MethodBase TargetMethod( HarmonyInstance instance )
+      {
+         return AccessTools.Property( typeof( Image ), "overrideSprite" ).GetSetMethod();
+      }
+
+      public static void Postfix( object __instance )
+      {
+         AutoTranslationPlugin.Current.Hook_ImageChangedOnComponent( __instance, null, false, false );
+      }
+   }
+
+   [Harmony]
+   public static class Image_material_Hook
+   {
+      static bool Prepare( HarmonyInstance instance )
+      {
+         return true;
+      }
+
+      static MethodBase TargetMethod( HarmonyInstance instance )
+      {
+         return AccessTools.Property( typeof( Image ), "material" ).GetSetMethod();
+      }
+
+      public static void Postfix( object __instance )
+      {
+         AutoTranslationPlugin.Current.Hook_ImageChangedOnComponent( __instance, null, false, false );
+      }
+   }
+
+   [Harmony]
+   public static class RawImage_texture_Hook
+   {
+      static bool Prepare( HarmonyInstance instance )
+      {
+         return true;
+      }
+
+      static MethodBase TargetMethod( HarmonyInstance instance )
+      {
+         return AccessTools.Property( typeof( RawImage ), "texture" ).GetSetMethod();
+      }
+
+      public static void Prefix( object __instance, Texture value )
+      {
+         if( value is Texture2D texture2d )
+         {
+            AutoTranslationPlugin.Current.Hook_ImageChangedOnComponent( __instance, texture2d, true, false );
+         }
+      }
+   }
+
+   [Harmony]
+   public static class Cursor_SetCursor_Hook
+   {
+      static bool Prepare( HarmonyInstance instance )
+      {
+         return true;
+      }
+
+      static MethodBase TargetMethod( HarmonyInstance instance )
+      {
+         return AccessTools.Method( typeof( Cursor ), "SetCursor", new[] { typeof( Texture2D ), typeof( Vector2 ), typeof( CursorMode ) } );
+      }
+
+      public static void Prefix( Texture2D texture )
+      {
+         AutoTranslationPlugin.Current.Hook_ImageChanged( texture, true );
+      }
+   }
+
+   [Harmony]
+   public static class UIAtlas_spriteMaterial_Hook
+   {
+      static bool Prepare( HarmonyInstance instance )
+      {
+         return ClrTypes.UIAtlas != null;
+      }
+
+      static MethodBase TargetMethod( HarmonyInstance instance )
+      {
+         return AccessTools.Property( ClrTypes.UIAtlas, "spriteMaterial" ).GetSetMethod();
+      }
+
+      public static void Postfix( object __instance )
+      {
+         AutoTranslationPlugin.Current.Hook_ImageChangedOnComponent( __instance, null, false, false );
+      }
+   }
+
+   [Harmony]
+   public static class UISprite_OnInit_Hook
+   {
+      static bool Prepare( HarmonyInstance instance )
+      {
+         return ClrTypes.UISprite != null;
+      }
+
+      static MethodBase TargetMethod( HarmonyInstance instance )
+      {
+         return AccessTools.Method( Constants.ClrTypes.UISprite, "OnInit" );
+      }
+
+      public static void Postfix( object __instance )
+      {
+         AutoTranslationPlugin.Current.Hook_ImageChangedOnComponent( __instance, null, false, true );
+      }
+   }
+
+   [Harmony]
+   public static class UISprite_material_Hook
+   {
+      static bool Prepare( HarmonyInstance instance )
+      {
+         return ClrTypes.UISprite != null;
+      }
+
+      static MethodBase TargetMethod( HarmonyInstance instance )
+      {
+         return AccessTools.Property( ClrTypes.UISprite, "material" ).GetSetMethod();
+      }
+
+      public static void Postfix( object __instance )
+      {
+         AutoTranslationPlugin.Current.Hook_ImageChangedOnComponent( __instance, null, false, false );
+      }
+   }
+
+   [Harmony]
+   public static class UISprite_atlas_Hook
+   {
+      static bool Prepare( HarmonyInstance instance )
+      {
+         return ClrTypes.UISprite != null;
+      }
+
+      static MethodBase TargetMethod( HarmonyInstance instance )
+      {
+         return AccessTools.Property( ClrTypes.UISprite, "atlas" ).GetSetMethod();
+      }
+
+      public static void Postfix( object __instance )
+      {
+         AutoTranslationPlugin.Current.Hook_ImageChangedOnComponent( __instance, null, false, false );
+      }
+   }
+
+   [Harmony]
+   public static class UITexture_mainTexture_Hook
+   {
+      static bool Prepare( HarmonyInstance instance )
+      {
+         return ClrTypes.UITexture != null;
+      }
+
+      static MethodBase TargetMethod( HarmonyInstance instance )
+      {
+         return AccessTools.Property( ClrTypes.UITexture, "mainTexture" ).GetSetMethod();
+      }
+
+      public static void Postfix( object __instance )
+      {
+         AutoTranslationPlugin.Current.Hook_ImageChangedOnComponent( __instance, null, false, false );
+      }
+   }
+
+   [Harmony]
+   public static class UITexture_material_Hook
+   {
+      static bool Prepare( HarmonyInstance instance )
+      {
+         return ClrTypes.UITexture != null;
+      }
+
+      static MethodBase TargetMethod( HarmonyInstance instance )
+      {
+         return AccessTools.Property( ClrTypes.UITexture, "material" ).GetSetMethod();
+      }
+
+      public static void Postfix( object __instance )
+      {
+         AutoTranslationPlugin.Current.Hook_ImageChangedOnComponent( __instance, null, false, false );
+      }
+   }
+
+   [Harmony]
+   public static class UIRect_OnInit_Hook
+   {
+      static bool Prepare( HarmonyInstance instance )
+      {
+         return ClrTypes.UIRect != null;
+      }
+
+      static MethodBase TargetMethod( HarmonyInstance instance )
+      {
+         return AccessTools.Method( ClrTypes.UIRect, "OnInit" );
+      }
+
+      public static void Postfix( object __instance )
+      {
+         AutoTranslationPlugin.Current.Hook_ImageChangedOnComponent( __instance, null, false, true );
+      }
+   }
+
+   [Harmony]
+   public static class UI2DSprite_sprite2D_Hook
+   {
+      static bool Prepare( HarmonyInstance instance )
+      {
+         return ClrTypes.UI2DSprite != null;
+      }
+
+      static MethodBase TargetMethod( HarmonyInstance instance )
+      {
+         return AccessTools.Property( ClrTypes.UI2DSprite, "sprite2D" ).GetSetMethod();
+      }
+
+      public static void Postfix( object __instance )
+      {
+         AutoTranslationPlugin.Current.Hook_ImageChangedOnComponent( __instance, null, false, false );
+      }
+   }
+
+   [Harmony]
+   public static class UI2DSprite_material_Hook
+   {
+      static bool Prepare( HarmonyInstance instance )
+      {
+         return ClrTypes.UI2DSprite != null;
+      }
+
+      static MethodBase TargetMethod( HarmonyInstance instance )
+      {
+         return AccessTools.Property( ClrTypes.UI2DSprite, "material" ).GetSetMethod();
+      }
+
+      public static void Postfix( object __instance )
+      {
+         AutoTranslationPlugin.Current.Hook_ImageChangedOnComponent( __instance, null, false, false );
+      }
+   }
+
+   [Harmony]
+   public static class UIPanel_clipTexture_Hook
+   {
+      static bool Prepare( HarmonyInstance instance )
+      {
+         return ClrTypes.UIPanel != null;
+      }
+
+      static MethodBase TargetMethod( HarmonyInstance instance )
+      {
+         return AccessTools.Property( ClrTypes.UIPanel, "clipTexture" ).GetSetMethod();
+      }
+
+      public static void Postfix( object __instance )
+      {
+         AutoTranslationPlugin.Current.Hook_ImageChangedOnComponent( __instance, null, false, false );
+      }
+   }
+
+
+   [Harmony]
+   public static class UIFont_material_Hook
+   {
+      static bool Prepare( HarmonyInstance instance )
+      {
+         return ClrTypes.UIFont != null;
+      }
+
+      static MethodBase TargetMethod( HarmonyInstance instance )
+      {
+         return AccessTools.Property( ClrTypes.UIFont, "material" ).GetSetMethod();
+      }
+
+      public static void Postfix( object __instance )
+      {
+         AutoTranslationPlugin.Current.Hook_ImageChangedOnComponent( __instance, null, false, false );
+      }
+   }
+
+   [Harmony]
+   public static class UIFont_dynamicFont_Hook
+   {
+      static bool Prepare( HarmonyInstance instance )
+      {
+         return ClrTypes.UIFont != null;
+      }
+
+      static MethodBase TargetMethod( HarmonyInstance instance )
+      {
+         return AccessTools.Property( ClrTypes.UIFont, "dynamicFont" ).GetSetMethod();
+      }
+
+      public static void Postfix( object __instance )
+      {
+         AutoTranslationPlugin.Current.Hook_ImageChangedOnComponent( __instance, null, false, false );
+      }
+   }
+
+   [Harmony]
+   public static class UILabel_bitmapFont_Hook
+   {
+      static bool Prepare( HarmonyInstance instance )
+      {
+         return ClrTypes.UILabel != null;
+      }
+
+      static MethodBase TargetMethod( HarmonyInstance instance )
+      {
+         return AccessTools.Property( ClrTypes.UILabel, "bitmapFont" ).GetSetMethod();
+      }
+
+      public static void Postfix( object __instance )
+      {
+         AutoTranslationPlugin.Current.Hook_ImageChangedOnComponent( __instance, null, false, false );
+      }
+   }
+
+   [Harmony]
+   public static class UILabel_trueTypeFont_Hook
+   {
+      static bool Prepare( HarmonyInstance instance )
+      {
+         return ClrTypes.UILabel != null;
+      }
+
+      static MethodBase TargetMethod( HarmonyInstance instance )
+      {
+         return AccessTools.Property( ClrTypes.UILabel, "trueTypeFont" ).GetSetMethod();
+      }
+
+      public static void Postfix( object __instance )
+      {
+         AutoTranslationPlugin.Current.Hook_ImageChangedOnComponent( __instance, null, false, false );
+      }
+   }
+}

+ 2 - 2
src/XUnity.AutoTranslator.Plugin.Core/Hooks/NGUIHooks.cs

@@ -37,7 +37,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Hooks.NGUI
       {
          if( !NGUIHooks.HooksOverriden )
          {
-            AutoTranslationPlugin.Current.Hook_TextChanged( __instance );
+            AutoTranslationPlugin.Current.Hook_TextChanged( __instance, false );
          }
          AutoTranslationPlugin.Current.Hook_HandleComponent( __instance );
       }
@@ -60,7 +60,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Hooks.NGUI
       {
          if( !NGUIHooks.HooksOverriden )
          {
-            AutoTranslationPlugin.Current.Hook_TextChanged( __instance );
+            AutoTranslationPlugin.Current.Hook_TextChanged( __instance, true );
          }
          AutoTranslationPlugin.Current.Hook_HandleComponent( __instance );
       }

+ 0 - 297
src/XUnity.AutoTranslator.Plugin.Core/Hooks/NGUIImageHooks.cs

@@ -1,297 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Reflection;
-using System.Text;
-using Harmony;
-using XUnity.AutoTranslator.Plugin.Core.Constants;
-
-namespace XUnity.AutoTranslator.Plugin.Core.Hooks
-{
-   public static class NGUIImageHooks
-   {
-      public static readonly Type[] All = new Type[] {
-         typeof( UIAtlas_spriteMaterial_Hook ),
-         typeof( UISprite_OnInit_Hook ),
-         typeof( UISprite_material_Hook ),
-         typeof( UISprite_atlas_Hook ),
-         typeof( UI2DSprite_sprite2D_Hook ),
-         typeof( UI2DSprite_material_Hook ),
-         typeof( UITexture_mainTexture_Hook ),
-         typeof( UITexture_material_Hook ),
-         typeof( UIPanel_clipTexture_Hook ),
-         typeof( UIRect_OnInit_Hook ),
-         //typeof( UIFont_dynamicFont_Hook ),
-         //typeof( UIFont_material_Hook ),
-         //typeof( UILabel_bitmapFont_Hook ),
-         //typeof( UILabel_trueTypeFont_Hook ),
-      };
-   }
-
-   [Harmony]
-   public static class UIAtlas_spriteMaterial_Hook
-   {
-      static bool Prepare( HarmonyInstance instance )
-      {
-         return ClrTypes.UIAtlas != null;
-      }
-
-      static MethodBase TargetMethod( HarmonyInstance instance )
-      {
-         return AccessTools.Property( ClrTypes.UIAtlas, "spriteMaterial" ).GetSetMethod();
-      }
-
-      public static void Postfix( object __instance )
-      {
-         AutoTranslationPlugin.Current.Hook_ImageChangedOnComponent( __instance, null, false );
-      }
-   }
-   
-   [Harmony]
-   public static class UISprite_OnInit_Hook
-   {
-      static bool Prepare( HarmonyInstance instance )
-      {
-         return ClrTypes.UISprite != null;
-      }
-
-      static MethodBase TargetMethod( HarmonyInstance instance )
-      {
-         return AccessTools.Method( Constants.ClrTypes.UISprite, "OnInit" );
-      }
-
-      public static void Postfix( object __instance )
-      {
-         AutoTranslationPlugin.Current.Hook_ImageChangedOnComponent( __instance );
-      }
-   }
-
-   [Harmony]
-   public static class UISprite_material_Hook
-   {
-      static bool Prepare( HarmonyInstance instance )
-      {
-         return ClrTypes.UISprite != null;
-      }
-
-      static MethodBase TargetMethod( HarmonyInstance instance )
-      {
-         return AccessTools.Property( ClrTypes.UISprite, "material" ).GetSetMethod();
-      }
-
-      public static void Postfix( object __instance )
-      {
-         AutoTranslationPlugin.Current.Hook_ImageChangedOnComponent( __instance, null, false );
-      }
-   }
-
-   [Harmony]
-   public static class UISprite_atlas_Hook
-   {
-      static bool Prepare( HarmonyInstance instance )
-      {
-         return ClrTypes.UISprite != null;
-      }
-
-      static MethodBase TargetMethod( HarmonyInstance instance )
-      {
-         return AccessTools.Property( ClrTypes.UISprite, "atlas" ).GetSetMethod();
-      }
-
-      public static void Postfix( object __instance )
-      {
-         AutoTranslationPlugin.Current.Hook_ImageChangedOnComponent( __instance, null, false );
-      }
-   }
-   
-   [Harmony]
-   public static class UITexture_mainTexture_Hook
-   {
-      static bool Prepare( HarmonyInstance instance )
-      {
-         return ClrTypes.UITexture != null;
-      }
-
-      static MethodBase TargetMethod( HarmonyInstance instance )
-      {
-         return AccessTools.Property( ClrTypes.UITexture, "mainTexture" ).GetSetMethod();
-      }
-
-      public static void Postfix( object __instance )
-      {
-         AutoTranslationPlugin.Current.Hook_ImageChangedOnComponent( __instance, null, false );
-      }
-   }
-
-   [Harmony]
-   public static class UITexture_material_Hook
-   {
-      static bool Prepare( HarmonyInstance instance )
-      {
-         return ClrTypes.UITexture != null;
-      }
-
-      static MethodBase TargetMethod( HarmonyInstance instance )
-      {
-         return AccessTools.Property( ClrTypes.UITexture, "material" ).GetSetMethod();
-      }
-
-      public static void Postfix( object __instance )
-      {
-         AutoTranslationPlugin.Current.Hook_ImageChangedOnComponent( __instance, null, false );
-      }
-   }
-
-   [Harmony]
-   public static class UIRect_OnInit_Hook
-   {
-      static bool Prepare( HarmonyInstance instance )
-      {
-         return ClrTypes.UIRect != null;
-      }
-
-      static MethodBase TargetMethod( HarmonyInstance instance )
-      {
-         return AccessTools.Method( ClrTypes.UIRect, "OnInit" );
-      }
-
-      public static void Postfix( object __instance )
-      {
-         AutoTranslationPlugin.Current.Hook_ImageChangedOnComponent( __instance );
-      }
-   }
-
-   [Harmony]
-   public static class UI2DSprite_sprite2D_Hook
-   {
-      static bool Prepare( HarmonyInstance instance )
-      {
-         return ClrTypes.UI2DSprite != null;
-      }
-
-      static MethodBase TargetMethod( HarmonyInstance instance )
-      {
-         return AccessTools.Property( ClrTypes.UI2DSprite, "sprite2D" ).GetSetMethod();
-      }
-
-      public static void Postfix( object __instance )
-      {
-         AutoTranslationPlugin.Current.Hook_ImageChangedOnComponent( __instance, null, false );
-      }
-   }
-
-   [Harmony]
-   public static class UI2DSprite_material_Hook
-   {
-      static bool Prepare( HarmonyInstance instance )
-      {
-         return ClrTypes.UI2DSprite != null;
-      }
-
-      static MethodBase TargetMethod( HarmonyInstance instance )
-      {
-         return AccessTools.Property( ClrTypes.UI2DSprite, "material" ).GetSetMethod();
-      }
-
-      public static void Postfix( object __instance )
-      {
-         AutoTranslationPlugin.Current.Hook_ImageChangedOnComponent( __instance, null, false );
-      }
-   }
-
-   [Harmony]
-   public static class UIPanel_clipTexture_Hook
-   {
-      static bool Prepare( HarmonyInstance instance )
-      {
-         return ClrTypes.UIPanel != null;
-      }
-
-      static MethodBase TargetMethod( HarmonyInstance instance )
-      {
-         return AccessTools.Property( ClrTypes.UIPanel, "clipTexture" ).GetSetMethod();
-      }
-
-      public static void Postfix( object __instance )
-      {
-         AutoTranslationPlugin.Current.Hook_ImageChangedOnComponent( __instance );
-      }
-   }
-
-
-   [Harmony]
-   public static class UIFont_material_Hook
-   {
-      static bool Prepare( HarmonyInstance instance )
-      {
-         return ClrTypes.UIFont != null;
-      }
-
-      static MethodBase TargetMethod( HarmonyInstance instance )
-      {
-         return AccessTools.Property( ClrTypes.UIFont, "material" ).GetSetMethod();
-      }
-
-      public static void Postfix( object __instance )
-      {
-         AutoTranslationPlugin.Current.Hook_ImageChangedOnComponent( __instance );
-      }
-   }
-
-   [Harmony]
-   public static class UIFont_dynamicFont_Hook
-   {
-      static bool Prepare( HarmonyInstance instance )
-      {
-         return ClrTypes.UIFont != null;
-      }
-
-      static MethodBase TargetMethod( HarmonyInstance instance )
-      {
-         return AccessTools.Property( ClrTypes.UIFont, "dynamicFont" ).GetSetMethod();
-      }
-
-      public static void Postfix( object __instance )
-      {
-         AutoTranslationPlugin.Current.Hook_ImageChangedOnComponent( __instance );
-      }
-   }
-
-   [Harmony]
-   public static class UILabel_bitmapFont_Hook
-   {
-      static bool Prepare( HarmonyInstance instance )
-      {
-         return ClrTypes.UILabel != null;
-      }
-
-      static MethodBase TargetMethod( HarmonyInstance instance )
-      {
-         return AccessTools.Property( ClrTypes.UILabel, "bitmapFont" ).GetSetMethod();
-      }
-
-      public static void Postfix( object __instance )
-      {
-         AutoTranslationPlugin.Current.Hook_ImageChangedOnComponent( __instance );
-      }
-   }
-
-   [Harmony]
-   public static class UILabel_trueTypeFont_Hook
-   {
-      static bool Prepare( HarmonyInstance instance )
-      {
-         return ClrTypes.UILabel != null;
-      }
-
-      static MethodBase TargetMethod( HarmonyInstance instance )
-      {
-         return AccessTools.Property( ClrTypes.UILabel, "trueTypeFont" ).GetSetMethod();
-      }
-
-      public static void Postfix( object __instance )
-      {
-         AutoTranslationPlugin.Current.Hook_ImageChangedOnComponent( __instance );
-      }
-   }
-}

+ 54 - 0
src/XUnity.AutoTranslator.Plugin.Core/Hooks/TextGetterCompatHooks.cs

@@ -0,0 +1,54 @@
+using System;
+using System.Reflection;
+using Harmony;
+using XUnity.AutoTranslator.Plugin.Core.Constants;
+
+namespace XUnity.AutoTranslator.Plugin.Core.Hooks.UGUI
+{
+   public static class TextGetterCompatHooks
+   {
+      public static readonly Type[] All = new[] {
+         typeof( TextPropertyGetterHook1 ),
+         typeof( TextPropertyGetterHook2 ),
+      };
+   }
+
+   [Harmony, HarmonyAfter( Constants.KnownPlugins.DynamicTranslationLoader )]
+   public static class TextPropertyGetterHook1
+   {
+      static bool Prepare( HarmonyInstance instance )
+      {
+         return ClrTypes.Text != null;
+      }
+
+      static MethodBase TargetMethod( HarmonyInstance instance )
+      {
+         var text = AccessTools.Property( ClrTypes.Text, "text" );
+         return text.GetGetMethod();
+      }
+
+      static void Postfix( object __instance, ref string __result )
+      {
+         TextGetterCompatMode.ReplaceTextWithOriginal( __instance, ref __result );
+      }
+   }
+
+   [Harmony, HarmonyAfter( Constants.KnownPlugins.DynamicTranslationLoader )]
+   public static class TextPropertyGetterHook2
+   {
+      static bool Prepare( HarmonyInstance instance )
+      {
+         return ClrTypes.TMP_Text != null;
+      }
+
+      static MethodBase TargetMethod( HarmonyInstance instance )
+      {
+         return AccessTools.Property( ClrTypes.TMP_Text, "text" ).GetGetMethod();
+      }
+
+      static void Postfix( object __instance, ref string __result )
+      {
+         TextGetterCompatMode.ReplaceTextWithOriginal( __instance, ref __result );
+      }
+   }
+}

+ 9 - 9
src/XUnity.AutoTranslator.Plugin.Core/Hooks/TextMeshProHooks.cs

@@ -42,7 +42,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Hooks.TextMeshPro
       {
          if( !TextMeshProHooks.HooksOverriden )
          {
-            AutoTranslationPlugin.Current.Hook_TextChanged( __instance );
+            AutoTranslationPlugin.Current.Hook_TextChanged( __instance, true );
          }
          AutoTranslationPlugin.Current.Hook_HandleComponent( __instance );
       }
@@ -65,7 +65,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Hooks.TextMeshPro
       {
          if( !TextMeshProHooks.HooksOverriden )
          {
-            AutoTranslationPlugin.Current.Hook_TextChanged( __instance );
+            AutoTranslationPlugin.Current.Hook_TextChanged( __instance, true );
          }
          AutoTranslationPlugin.Current.Hook_HandleComponent( __instance );
       }
@@ -88,7 +88,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Hooks.TextMeshPro
       {
          if( !TextMeshProHooks.HooksOverriden )
          {
-            AutoTranslationPlugin.Current.Hook_TextChanged( __instance );
+            AutoTranslationPlugin.Current.Hook_TextChanged( __instance, false );
          }
          AutoTranslationPlugin.Current.Hook_HandleComponent( __instance );
       }
@@ -111,7 +111,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Hooks.TextMeshPro
       {
          if( !TextMeshProHooks.HooksOverriden )
          {
-            AutoTranslationPlugin.Current.Hook_TextChanged( __instance );
+            AutoTranslationPlugin.Current.Hook_TextChanged( __instance, false );
          }
          AutoTranslationPlugin.Current.Hook_HandleComponent( __instance );
       }
@@ -134,7 +134,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Hooks.TextMeshPro
       {
          if( !TextMeshProHooks.HooksOverriden )
          {
-            AutoTranslationPlugin.Current.Hook_TextChanged( __instance );
+            AutoTranslationPlugin.Current.Hook_TextChanged( __instance, false );
          }
          AutoTranslationPlugin.Current.Hook_HandleComponent( __instance );
       }
@@ -157,7 +157,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Hooks.TextMeshPro
       {
          if( !TextMeshProHooks.HooksOverriden )
          {
-            AutoTranslationPlugin.Current.Hook_TextChanged( __instance );
+            AutoTranslationPlugin.Current.Hook_TextChanged( __instance, false );
          }
          AutoTranslationPlugin.Current.Hook_HandleComponent( __instance );
       }
@@ -180,7 +180,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Hooks.TextMeshPro
       {
          if( !TextMeshProHooks.HooksOverriden )
          {
-            AutoTranslationPlugin.Current.Hook_TextChanged( __instance );
+            AutoTranslationPlugin.Current.Hook_TextChanged( __instance, false );
          }
          AutoTranslationPlugin.Current.Hook_HandleComponent( __instance );
       }
@@ -203,7 +203,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Hooks.TextMeshPro
       {
          if( !TextMeshProHooks.HooksOverriden )
          {
-            AutoTranslationPlugin.Current.Hook_TextChanged( __instance );
+            AutoTranslationPlugin.Current.Hook_TextChanged( __instance, false );
          }
          AutoTranslationPlugin.Current.Hook_HandleComponent( __instance );
       }
@@ -226,7 +226,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Hooks.TextMeshPro
       {
          if( !TextMeshProHooks.HooksOverriden )
          {
-            AutoTranslationPlugin.Current.Hook_TextChanged( __instance );
+            AutoTranslationPlugin.Current.Hook_TextChanged( __instance, false );
          }
          AutoTranslationPlugin.Current.Hook_HandleComponent( __instance );
       }

+ 3 - 2
src/XUnity.AutoTranslator.Plugin.Core/Hooks/UGUIHooks.cs

@@ -8,6 +8,7 @@ using XUnity.AutoTranslator.Plugin.Core.Constants;
 
 namespace XUnity.AutoTranslator.Plugin.Core.Hooks.UGUI
 {
+
    public static class UGUIHooks
    {
       public static bool HooksOverriden = false;
@@ -36,7 +37,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Hooks.UGUI
       {
          if( !UGUIHooks.HooksOverriden )
          {
-            AutoTranslationPlugin.Current.Hook_TextChanged( __instance );
+            AutoTranslationPlugin.Current.Hook_TextChanged( __instance, false );
          }
          AutoTranslationPlugin.Current.Hook_HandleComponent( __instance );
       }
@@ -60,7 +61,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Hooks.UGUI
       {
          if( !UGUIHooks.HooksOverriden )
          {
-            AutoTranslationPlugin.Current.Hook_TextChanged( __instance );
+            AutoTranslationPlugin.Current.Hook_TextChanged( __instance, true );
          }
          AutoTranslationPlugin.Current.Hook_HandleComponent( __instance );
       }

+ 0 - 179
src/XUnity.AutoTranslator.Plugin.Core/Hooks/UGUIImageHooks.cs

@@ -1,179 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Reflection;
-using System.Text;
-using Harmony;
-using UnityEngine;
-using UnityEngine.UI;
-
-namespace XUnity.AutoTranslator.Plugin.Core.Hooks
-{
-   public static class UGUIImageHooks
-   {
-      public static readonly Type[] All = new[] {
-         typeof( MaskableGraphic_OnEnable_Hook ),
-         typeof( Image_sprite_Hook ),
-         typeof( Image_overrideSprite_Hook ),
-         typeof( Image_material_Hook ),
-         typeof( RawImage_texture_Hook ),
-         typeof( Cursor_SetCursor_Hook ),
-
-         // fallback hooks on material (Prefix hooks)
-         typeof( Material_mainTexture_Hook ),
-      };
-   }
-
-   public static class GenericPrefix_Hook
-   {
-      public static void Prefix( object __instance, object value )
-      {
-         if( value is Texture2D texture2d )
-         {
-            AutoTranslationPlugin.Current.Hook_ImageChangedOnComponent( __instance, texture2d, true );
-         }
-      }
-   }
-
-   [Harmony]
-   public static class Material_mainTexture_Hook
-   {
-      static bool Prepare( HarmonyInstance instance )
-      {
-         return true;
-      }
-
-      static MethodBase TargetMethod( HarmonyInstance instance )
-      {
-         return AccessTools.Property( typeof( Material ), "mainTexture" ).GetSetMethod();
-      }
-
-      public static void Prefix( object __instance, Texture value )
-      {
-         if( value is Texture2D texture2d )
-         {
-            AutoTranslationPlugin.Current.Hook_ImageChangedOnComponent( __instance, texture2d, true );
-         }
-      }
-   }
-
-   [Harmony]
-   public static class MaskableGraphic_OnEnable_Hook
-   {
-      static bool Prepare( HarmonyInstance instance )
-      {
-         return true;
-      }
-
-      static MethodBase TargetMethod( HarmonyInstance instance )
-      {
-         return AccessTools.Method( typeof( MaskableGraphic ), "OnEnable" );
-      }
-
-      public static void Postfix( object __instance )
-      {
-         if( __instance is Image || __instance is RawImage )
-         {
-            AutoTranslationPlugin.Current.Hook_ImageChangedOnComponent( __instance, null, false );
-         }
-      }
-   }
-
-   [Harmony]
-   public static class Image_sprite_Hook
-   {
-      static bool Prepare( HarmonyInstance instance )
-      {
-         return true;
-      }
-
-      static MethodBase TargetMethod( HarmonyInstance instance )
-      {
-         return AccessTools.Property( typeof( Image ), "sprite" ).GetSetMethod();
-      }
-
-      public static void Postfix( object __instance )
-      {
-         AutoTranslationPlugin.Current.Hook_ImageChangedOnComponent( __instance, null, false );
-      }
-   }
-
-   [Harmony]
-   public static class Image_overrideSprite_Hook
-   {
-      static bool Prepare( HarmonyInstance instance )
-      {
-         return true;
-      }
-
-      static MethodBase TargetMethod( HarmonyInstance instance )
-      {
-         return AccessTools.Property( typeof( Image ), "overrideSprite" ).GetSetMethod();
-      }
-
-      public static void Postfix( object __instance )
-      {
-         AutoTranslationPlugin.Current.Hook_ImageChangedOnComponent( __instance, null, false );
-      }
-   }
-
-   [Harmony]
-   public static class Image_material_Hook
-   {
-      static bool Prepare( HarmonyInstance instance )
-      {
-         return true;
-      }
-
-      static MethodBase TargetMethod( HarmonyInstance instance )
-      {
-         return AccessTools.Property( typeof( Image ), "material" ).GetSetMethod();
-      }
-
-      public static void Postfix( object __instance )
-      {
-         AutoTranslationPlugin.Current.Hook_ImageChangedOnComponent( __instance, null, false );
-      }
-   }
-
-   [Harmony]
-   public static class RawImage_texture_Hook
-   {
-      static bool Prepare( HarmonyInstance instance )
-      {
-         return true;
-      }
-
-      static MethodBase TargetMethod( HarmonyInstance instance )
-      {
-         return AccessTools.Property( typeof( RawImage ), "texture" ).GetSetMethod();
-      }
-
-      public static void Prefix( object __instance, Texture value )
-      {
-         if( value is Texture2D texture2d )
-         {
-            AutoTranslationPlugin.Current.Hook_ImageChangedOnComponent( __instance, texture2d, true );
-         }
-      }
-   }
-
-   [Harmony]
-   public static class Cursor_SetCursor_Hook
-   {
-      static bool Prepare( HarmonyInstance instance )
-      {
-         return true;
-      }
-
-      static MethodBase TargetMethod( HarmonyInstance instance )
-      {
-         return AccessTools.Method( typeof( Cursor ), "SetCursor", new[] { typeof( Texture2D ), typeof( Vector2 ), typeof( CursorMode ) } );
-      }
-
-      public static void Prefix( Texture2D texture )
-      {
-         AutoTranslationPlugin.Current.Hook_ImageChanged( texture, true );
-      }
-   }
-}

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

@@ -0,0 +1,31 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using UnityEngine;
+using UnityEngine.SceneManagement;
+
+namespace XUnity.AutoTranslator.Plugin.Core
+{
+   public static class SceneManagerEx
+   {
+      public static string GetActiveSceneId()
+      {
+         if( Features.SupportsScenes )
+         {
+            return GetActiveSceneIdBySceneManager();
+         }
+         return GetActiveSceneIdByApplication();
+      }
+
+      private static string GetActiveSceneIdBySceneManager()
+      {
+         return SceneManager.GetActiveScene().ToString();
+      }
+
+      private static string GetActiveSceneIdByApplication()
+      {
+         return Application.loadedLevel.ToString();
+      }
+   }
+}

+ 43 - 0
src/XUnity.AutoTranslator.Plugin.Core/TextGetterCompatMode.cs

@@ -0,0 +1,43 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Text;
+using XUnity.AutoTranslator.Plugin.Core.Configuration;
+using XUnity.AutoTranslator.Plugin.Core.Extensions;
+
+namespace XUnity.AutoTranslator.Plugin.Core
+{
+   public static class TextGetterCompatMode
+   {
+      private static readonly Assembly XUnityAutoTranslatorAssembly = typeof( TextGetterCompatMode ).Assembly;
+
+      [MethodImpl( MethodImplOptions.NoInlining )]
+      public static void ReplaceTextWithOriginal( object instance, ref string __result )
+      {
+         if( !Settings.TextGetterCompatibilityMode ) return;
+
+         var tti = instance.GetTextTranslationInfo();
+         if( tti.IsTranslated )
+         {
+            // 0. This method
+            // 1. Postfix
+            // 2. Harmony-related method
+            // 3. Original method
+            var callingMethod = new StackFrame( 3 ).GetMethod();
+
+            var callingAssembly = callingMethod.DeclaringType.Assembly;
+            if( callingAssembly == XUnityAutoTranslatorAssembly ) return;
+
+            var originalAssembly = instance.GetType().Assembly;
+            if( callingAssembly != originalAssembly ) 
+            {
+               // if the assembly is not the same, it may be call from the game or another mod, so replace
+               __result = tti.OriginalText;
+            }
+         }
+      }
+   }
+}

+ 1 - 1
src/XUnity.AutoTranslator.Plugin.Core/TextureHashGenerationStrategy.cs

@@ -4,6 +4,6 @@
    {
       FromImageName,
       FromImageData,
-      FromImageNameThenData
+      FromImageNameAndScene,
    }
 }

+ 20 - 0
src/XUnity.AutoTranslator.Plugin.Core/TextureReloadContext.cs

@@ -0,0 +1,20 @@
+using System.Collections.Generic;
+using UnityEngine;
+
+namespace XUnity.AutoTranslator.Plugin.Core
+{
+   public class TextureReloadContext
+   {
+      private readonly HashSet<Texture2D> _textures;
+
+      public TextureReloadContext()
+      {
+         _textures = new HashSet<Texture2D>();
+      }
+
+      public bool RegisterTextureInContextAndDetermineWhetherToReload( Texture2D texture )
+      {
+         return _textures.Add( texture );
+      }
+   }
+}

+ 42 - 18
src/XUnity.AutoTranslator.Plugin.Core/TextureTranslationInfo.cs

@@ -1,6 +1,8 @@
-using System.Text;
+using System.Collections.Generic;
+using System.Text;
 using UnityEngine;
 using XUnity.AutoTranslator.Plugin.Core.Configuration;
+using XUnity.AutoTranslator.Plugin.Core.Constants;
 using XUnity.AutoTranslator.Plugin.Core.Extensions;
 using XUnity.AutoTranslator.Plugin.Core.Utilities;
 
@@ -8,6 +10,7 @@ namespace XUnity.AutoTranslator.Plugin.Core
 {
    public class TextureTranslationInfo
    {
+      //private static readonly Dictionary<int, string> KnownHashes = new Dictionary<int, string>();
       private static readonly Encoding UTF8 = new UTF8Encoding( false );
 
       private string _key;
@@ -22,7 +25,7 @@ namespace XUnity.AutoTranslator.Plugin.Core
       {
          if( !_nonReadable.HasValue )
          {
-            _nonReadable = texture.IsNonReadable();
+            _nonReadable = false;
          }
 
          return _nonReadable.Value;
@@ -54,6 +57,8 @@ namespace XUnity.AutoTranslator.Plugin.Core
       {
          if( _key == null )
          {
+            var instanceId = texture.GetInstanceID();
+
             if( Settings.TextureHashGenerationStrategy == TextureHashGenerationStrategy.FromImageData )
             {
                var result = TextureHelper.GetData( texture );
@@ -61,6 +66,32 @@ namespace XUnity.AutoTranslator.Plugin.Core
                _originalData = result.Data;
                _nonReadable = result.NonReadable;
                _key = HashHelper.Compute( _originalData );
+
+               //if( KnownHashes.TryGetValue( instanceId, out string hashValue ) )
+               //{
+               //   _key = hashValue;
+
+               //   if( Settings.EnableTextureToggling )
+               //   {
+               //      var result = TextureHelper.GetData( texture );
+
+               //      _originalData = result.Data;
+               //      _nonReadable = result.NonReadable;
+               //   }
+               //}
+               //else
+               //{
+               //   var result = TextureHelper.GetData( texture );
+
+               //   _originalData = result.Data;
+               //   _nonReadable = result.NonReadable;
+               //   _key = HashHelper.Compute( _originalData );
+
+               //   if( !string.IsNullOrEmpty( texture.name ) && result.CalculationTime > 0.6f )
+               //   {
+               //      KnownHashes[ instanceId ] = _key;
+               //   }
+               //}
             }
             else if( Settings.TextureHashGenerationStrategy == TextureHashGenerationStrategy.FromImageName )
             {
@@ -77,28 +108,21 @@ namespace XUnity.AutoTranslator.Plugin.Core
                   _nonReadable = result.NonReadable;
                }
             }
-            else // if( Settings.TextureHashGenerationStrategy == TextureHashGenerationStrategy.FromImageNameThenData )
+            else if( Settings.TextureHashGenerationStrategy == TextureHashGenerationStrategy.FromImageNameAndScene )
             {
-               var name = texture.name;
-               if( string.IsNullOrEmpty( name ) )
+               var name = texture.name; // name may be duplicate, WILL be duplicate!
+               if( string.IsNullOrEmpty( name ) ) return;
+
+               name += "|" + SceneManagerEx.GetActiveSceneId();
+
+               _key = HashHelper.Compute( UTF8.GetBytes( name ) );
+
+               if( Settings.EnableTextureToggling )
                {
                   var result = TextureHelper.GetData( texture );
 
                   _originalData = result.Data;
                   _nonReadable = result.NonReadable;
-                  _key = HashHelper.Compute( _originalData );
-               }
-               else
-               {
-                  _key = HashHelper.Compute( UTF8.GetBytes( name ) );
-
-                  if( Settings.EnableTextureToggling )
-                  {
-                     var result = TextureHelper.GetData( texture );
-
-                     _originalData = result.Data;
-                     _nonReadable = result.NonReadable;
-                  }
                }
             }
          }

+ 10 - 2
src/XUnity.AutoTranslator.Plugin.Core/Utilities/TextHelper.cs

@@ -126,9 +126,16 @@ namespace XUnity.AutoTranslator.Plugin.Core.Utilities
       /// </summary>
       public static string Decode( string text )
       {
+         var commentIndex = text.IndexOf( "//" );
+         if( commentIndex > -1 )
+         {
+            text = text.Substring( 0, commentIndex );
+         }
+
          return text.Replace( "\\r", "\r" )
             .Replace( "\\n", "\n" )
-            .Replace( "%3D", "=" );
+            .Replace( "%3D", "=" )
+            .Replace( "%2F%2F", "//" );
       }
 
       /// <summary>
@@ -140,7 +147,8 @@ namespace XUnity.AutoTranslator.Plugin.Core.Utilities
       {
          return text.Replace( "\r", "\\r" )
             .Replace( "\n", "\\n" )
-            .Replace( "=", "%3D" );
+            .Replace( "=", "%3D" )
+            .Replace( "//", "%2F%2F" );
       }
    }
 }

+ 16 - 12
src/XUnity.AutoTranslator.Plugin.Core/Utilities/TextureHelper.cs

@@ -14,20 +14,20 @@ namespace XUnity.AutoTranslator.Plugin.Core.Utilities
       
       public static TextureDataResult GetData( Texture2D texture, RenderTextureFormat rtf = RenderTextureFormat.Default, RenderTextureReadWrite cs = RenderTextureReadWrite.Default )
       {
+         var start = Time.realtimeSinceStartup;
+
          byte[] data = null;
-         bool nonReadable = texture.IsNonReadable();
+         //bool nonReadable = texture.IsNonReadable();
 
-         if( !nonReadable )
-         {
-            data = texture.EncodeToPNGEx();
-         }
+         //if( !nonReadable )
+         //{
+         //   data = texture.EncodeToPNGEx();
+         //}
 
          if( data == null )
          {
-            // https://support.unity3d.com/hc/en-us/articles/206486626-How-can-I-get-pixels-from-unreadable-textures-
-            nonReadable = true;
-
             var tmp = RenderTexture.GetTemporary( texture.width, texture.height, 0, rtf, cs );
+            GL.Clear( false, true, Transparent );
             Graphics.Blit( texture, tmp );
             var previousRenderTexture = RenderTexture.active;
             RenderTexture.active = tmp;
@@ -37,30 +37,34 @@ namespace XUnity.AutoTranslator.Plugin.Core.Utilities
             data = texture2d.EncodeToPNGEx();
             UnityEngine.Object.DestroyImmediate( texture2d );
 
-            //GL.Clear( false, true, Transparent );
             //Graphics.Blit( tex, tmp );
             //var texture2d = GetTextureFromRenderTexture( tmp );
             //var data = texture2d.EncodeToPNG();
             //UnityEngine.Object.DestroyImmediate( texture2d );
 
-            RenderTexture.active = previousRenderTexture;
+            RenderTexture.active = previousRenderTexture == tmp ? null : previousRenderTexture;
             RenderTexture.ReleaseTemporary( tmp );
          }
 
-         return new TextureDataResult( data, nonReadable );
+         var end = Time.realtimeSinceStartup;
+
+         return new TextureDataResult( data, false, end - start );
       }
    }
 
    public struct TextureDataResult
    {
-      public TextureDataResult( byte[] data, bool nonReadable )
+      public TextureDataResult( byte[] data, bool nonReadable, float calculationTime )
       {
          Data = data;
          NonReadable = nonReadable;
+         CalculationTime = calculationTime;
       }
 
       public byte[] Data { get; }
 
       public bool NonReadable { get; }
+
+      public float CalculationTime { get; set; }
    }
 }

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

@@ -2,7 +2,7 @@
 
    <PropertyGroup>
       <TargetFramework>net35</TargetFramework>
-      <Version>2.17.0</Version>
+      <Version>2.18.0</Version>
    </PropertyGroup>
 
    <ItemGroup>

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

@@ -2,7 +2,7 @@
 
    <PropertyGroup>
       <TargetFramework>net35</TargetFramework>
-      <Version>2.17.0</Version>
+      <Version>2.18.0</Version>
    </PropertyGroup>
 
    <ItemGroup>

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

@@ -2,7 +2,7 @@
 
    <PropertyGroup>
       <TargetFramework>net35</TargetFramework>
-      <Version>2.17.0</Version>
+      <Version>2.18.0</Version>
    </PropertyGroup>
 
    <ItemGroup>

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

@@ -4,7 +4,7 @@
       <OutputType>Exe</OutputType>
       <TargetFramework>net40</TargetFramework>
       <AssemblyName>SetupReiPatcherAndAutoTranslator</AssemblyName>
-      <Version>2.17.0</Version>
+      <Version>2.18.0</Version>
    </PropertyGroup>
 
    <ItemGroup>