Ver Fonte

improved texture replacement implementation

randoman há 6 anos atrás
pai
commit
e1cd815673

+ 64 - 1
README.md

@@ -1,5 +1,19 @@
 # XUnity Auto Translator
 
+## Index
+ * Notice
+ * Text Frameworks
+ * Plugin Frameworks
+ * Configuration
+ * Key Mapping
+ * Installation
+ * Translating Mods
+ * Texture Translation
+ * Integrating with Auto Translator
+
+## Notice
+The latest version (3.0.0) now also supports basic image loading/dumping. These are not automatically translated and the feature is disabled by default. Please see 'Texture Translation' for further instructions.
+
 ## Text Frameworks
 This is an auto translation mod that hooks into the unity game engine and attempts to provide translations for the following text frameworks for Unity:
  * UGUI
@@ -60,6 +74,15 @@ 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.
 
+[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 capapble of replacing to the TextureDirectory. Has significant performance impact
+EnableTextureToggling=False      ;Indicates whether or not toggle 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 replace more texture
+LoadUnmodifiedTextures=False     ;Indicates whether or not unmodified textures should be loaded. Modifications are determined based on the hash in the file name
+TextureHashGenerationStrategy=FromImageName ;Indicates how the mod identifies pictures through hashes. Can be ["FromImageName", "FromImageData", "FromImageNameThenData"]
+
 [Http]
 UserAgent=                       ;Override the user agent used by APIs requiring a user agent
 
@@ -92,7 +115,7 @@ Tag=2.9.0                        ;Tag representing the last version this plugin
 The following key inputs are mapped:
  * ALT + T: Alternate between translated and untranslated versions of all texts provided by this plugin.
  * ALT + D: Dump untranslated texts (if no endpoint is configured)
- * ALT + R: Reload translation files. Useful if you change the text files on the fly.
+ * 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.
 
@@ -172,6 +195,46 @@ The file structure should like like this
 ## Translating Mods
 Often other mods UI are implemented through IMGUI. As you can see above, this is disabled by default. By changing the "EnableIMGUI" value to "True", it will start translating IMGUI as well, which likely means that other mods UI will be translated.
 
+## Texture Translation
+From version 3.0.0+ this mode provides basic capabilities to replace images. It is a feature that is disabled by default. There is no automatic translation of these images though. 
+
+It is controlled by the following configuration:
+
+```ini
+[Texture]
+TextureDirectory=Translation\Texture
+EnableTextureTranslation=False
+EnableTextureDumping=False
+EnableTextureToggling=False
+TextureHashGenerationStrategy=FromImageName
+```
+
+`TextureDirectory=Translation\Texture` specifies the directory where textures are dumped to and loaded from. Loading will happen from all subdirectories of the specified directory as well, so you can move dumped images to whatever folder structure you desire.
+
+`EnableTextureTranslation=True` enables texture translation. This basically means that textures will be loaded from the `TextureDirectory` and it's subsdirectories. These images will replace the in-game images used by the game.
+
+`EnableTextureDumping=True` enables texture dumping. This means that the mod will dump any images it has not already dumped to the `TextureDirectory`. **NEVER REDISTRIBUTE THIS MOD WITH THIS ENABLED.**
+
+`EnableTextureScanOnSceneLoad=True` 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.
+
+`LoadUnmodifiedTextures=False` enabldes whether or not the plugin should load textures that has not been modified. This is only useful for debugging. **NEVER REDISTRIBUTE THIS MOD WITH THIS ENABLED.**
+
+`EnableTextureToggling=False` enables toggle textures with the ALT+T hotkey. **NEVER REDISTRIBUTE THIS MOD WITH THIS ENABLED.**
+
+`TextureHashGenerationStrategy=FromImageName` specifies how images are identified. When images are stored, the game will need some way of associating them with the image that it has to replace.
+This is done through a hash-value that is stored in square brackets in each image file name. 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.
+`FromImageData` means that the hash is generated from the data stored in the image, which is guaranteed to exist for all images.
+`FromImageNameThenData` means that it should use the name, if available, otherwise use the data.
+
+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.
+
+It is therefore recommended to use `TextureHashGenerationStrategy=FromImageName`. Most likely, images without a resource name wont be interesting to translate anyway.
+
+If you redistribute this mod with translated images then you **must delete all images that you do not translate to avoid resource waste**, as they are all loaded into memory on mod startup.
+
+**NEVER REDISTRIBUTE THIS MOD WITH `EnableTextureDumping=True`, `EnableTextureToggling=True`, `LoadUnmodifiedTextures=False` OR `TextureHashGenerationStrategy=FromImageData|FromImageNameThenData` ENABLED.**
+
 ## Integrating with Auto Translator
 
 ### Implementing a dedicated translation component

+ 197 - 62
src/XUnity.AutoTranslator.Plugin.Core/AutoTranslationPlugin.cs

@@ -90,6 +90,7 @@ namespace XUnity.AutoTranslator.Plugin.Core
       private HashSet<string> _immediatelyTranslating = new HashSet<string>();
 
       private Dictionary<string, byte[]> _translatedImages = new Dictionary<string, byte[]>( StringComparer.InvariantCultureIgnoreCase );
+      private HashSet<string> _untranslatedImages = new HashSet<string>();
 
       private object _advEngine;
       private float? _nextAdvUpdate;
@@ -184,6 +185,18 @@ namespace XUnity.AutoTranslator.Plugin.Core
             _overrideFont = _hasOverrideFont;
          }
 
+         if( Settings.EnableTextureScanOnSceneLoad && ( Settings.EnableTextureDumping || Settings.EnableTextureTranslation ) )
+         {
+            try
+            {
+               EnableSceneLoadScan();
+            }
+            catch( Exception e )
+            {
+               Logger.Current.Error( e, "An error occurred while settings up texture scene-load scans." );
+            }
+         }
+
          LoadTranslations();
          LoadStaticTranslations();
 
@@ -264,6 +277,23 @@ namespace XUnity.AutoTranslator.Plugin.Core
          }
       }
 
+      public void EnableSceneLoadScan()
+      {
+         // specified in own method, because of chance that this has changed through Unity lifetime
+         SceneManager.sceneLoaded += SceneManager_SceneLoaded;
+      }
+
+      private void SceneManager_SceneLoaded( Scene scene, LoadSceneMode arg1 )
+      {
+         Logger.Current.Info( "SceneLoading..." );
+         var startTime = Time.realtimeSinceStartup;
+
+         ManualHookForImages();
+
+         var endTime = Time.realtimeSinceStartup;
+         Logger.Current.Info( $"SceneLoaded (took {Math.Round( endTime - startTime, 2 )} seconds)" );
+      }
+
       /// <summary>
       /// Loads the translations found in Translation.{lang}.txt
       /// </summary>
@@ -287,10 +317,11 @@ namespace XUnity.AutoTranslator.Plugin.Core
             if( Settings.EnableTextureTranslation || Settings.EnableTextureDumping )
             {
                _translatedImages.Clear();
+               _untranslatedImages.Clear();
                Directory.CreateDirectory( Path.Combine( Config.Current.DataPath, Settings.TextureDirectory ) );
                foreach( var fullFileName in GetTextureFiles() )
                {
-                  RegisterImageDataAndHash( fullFileName, null, null );
+                  RegisterImageFromFile( fullFileName );
                }
             }
          }
@@ -300,38 +331,103 @@ namespace XUnity.AutoTranslator.Plugin.Core
          }
       }
 
-      private void RegisterImageDataAndHash( string fullFileName, string key, byte[] data )
+      private void RegisterImageFromFile( string fullFileName )
       {
-         if( key == null )
+         var fileName = Path.GetFileNameWithoutExtension( fullFileName );
+         var startHash = fileName.LastIndexOf( "[" );
+         var endHash = fileName.LastIndexOf( "]" );
+
+         if( endHash > -1 && startHash > -1 && endHash > startHash )
          {
-            var fileName = Path.GetFileNameWithoutExtension( fullFileName );
-            var startHash = fileName.LastIndexOf( "[" );
-            var endHash = fileName.LastIndexOf( "]" );
+            var takeFrom = startHash + 1;
+
+            // load based on whether or not the key is image hashed
+            var parts = fileName.Substring( takeFrom, endHash - takeFrom ).Split( '-' );
+            string key;
+            string originalHash;
+            if( parts.Length == 1 )
+            {
+               key = parts[ 0 ];
+               originalHash = parts[ 0 ];
+            }
+            else if( parts.Length == 2 )
+            {
+               key = parts[ 0 ];
+               originalHash = parts[ 1 ];
+            }
+            else
+            {
+               Logger.Current.Warn( $"Image not loaded (unknown key): {fullFileName}." );
+               return;
+            }
 
-            if( endHash > -1 && startHash > -1 && endHash > startHash )
+            var data = File.ReadAllBytes( fullFileName );
+            var currentHash = HashHelper.Compute( data ).Substring( 0, 8 );
+
+            // only load images that someone has modified!
+            if( Settings.LoadUnmodifiedTextures || StringComparer.InvariantCultureIgnoreCase.Compare( originalHash, currentHash ) != 0 )
+            {
+               RegisterTranslatedImage( key, data );
+               Logger.Current.Debug( $"Image loaded: {fullFileName}." );
+            }
+            else
             {
-               var takeFrom = startHash + 1;
-               key = fileName.Substring( takeFrom, endHash - takeFrom );
+               RegisterUntranslatedImage( key );
+               Logger.Current.Warn( $"Image not loaded (unmodified): {fullFileName}." );
             }
          }
+         else
+         {
+            Logger.Current.Warn( $"Image not loaded (no key): {fullFileName}." );
+         }
+      }
 
-         if( data == null )
+      private void RegisterImageFromData( string textureName, string key, byte[] data )
+      {
+         var name = textureName.SanitizeForFileSystem();
+         var root = Path.Combine( Config.Current.DataPath, Settings.TextureDirectory );
+         var originalHash = HashHelper.Compute( data ).Substring( 0, 8 );
+
+         // allow hash and key to be the same; only store one of them then!
+         string fileName;
+         if( key == originalHash )
+         {
+            fileName = name + " [" + key + "].png";
+         }
+         else
          {
-            data = File.ReadAllBytes( fullFileName );
+            fileName = name + " [" + key + "-" + originalHash + "].png";
          }
 
+         var fullName = Path.Combine( root, fileName );
+         File.WriteAllBytes( fullName, data );
+         Logger.Current.Info( "Dumped texture file: " + fileName );
 
-         if( key != null )
+         if( Settings.LoadUnmodifiedTextures )
          {
-            _translatedImages[ key ] = data;
+            RegisterTranslatedImage( key, data );
          }
+         else
+         {
+            RegisterUntranslatedImage( key );
+         }
+      }
+
+      private void RegisterTranslatedImage( string key, byte[] data )
+      {
+         _translatedImages[ key ] = data;
+      }
+
+      private void RegisterUntranslatedImage( string key )
+      {
+         _untranslatedImages.Add( key );
       }
 
       private void LoadTranslationsInFile( string fullFileName )
       {
          if( File.Exists( fullFileName ) )
          {
-            Logger.Current.Debug( $"Loading translations from {fullFileName}." );
+            Logger.Current.Debug( $"Loading texts: {fullFileName}." );
 
             string[] translations = File.ReadAllLines( fullFileName, Encoding.UTF8 );
             foreach( string translation in translations )
@@ -630,14 +726,14 @@ namespace XUnity.AutoTranslator.Plugin.Core
          }
       }
 
-      private bool IsImageTranslated( string hash )
+      private bool IsImageRegistered( string key )
       {
-         return _translatedImages.ContainsKey( hash );
+         return _translatedImages.ContainsKey( key ) || _untranslatedImages.Contains( key );
       }
 
-      private bool TryGetTranslatedImage( string hash, out byte[] data )
+      private bool TryGetTranslatedImage( string key, out byte[] data )
       {
-         return _translatedImages.TryGetValue( hash, out data );
+         return _translatedImages.TryGetValue( key, out data );
       }
 
       private void AddTranslation( string key, string value )
@@ -747,20 +843,20 @@ namespace XUnity.AutoTranslator.Plugin.Core
          }
       }
 
-      public void Hook_ImageChangedOnComponent( object source )
+      public void Hook_ImageChangedOnComponent( object source, Texture2D texture = null, bool isPrefixHooked = false )
       {
          if( !_imageHooksEnabled ) return;
          if( !source.IsKnownImageType() ) return;
 
-         HandleImage( source, null );
+         HandleImage( source, texture, isPrefixHooked );
       }
 
-      public void Hook_ImageChanged( Texture2D texture )
+      public void Hook_ImageChanged( Texture2D texture, bool isPrefixHooked = false )
       {
          if( !_imageHooksEnabled ) return;
          if( texture == null ) return;
 
-         HandleImage( null, texture );
+         HandleImage( null, texture, isPrefixHooked );
       }
 
       private void SetTranslatedText( object ui, string translatedText, TextTranslationInfo info )
@@ -960,13 +1056,8 @@ namespace XUnity.AutoTranslator.Plugin.Core
          return info.IsCurrentlySettingText;
       }
 
-      private void HandleImage( object source, Texture2D texture )
+      private void HandleImage( object source, Texture2D texture, bool isPrefixHooked )
       {
-         // Make work with raw textures somehow?????
-         // work with obscure games? Sprite?
-
-         // Test without texture replacement (old! in old games!)
-
          if( Settings.EnableTextureDumping )
          {
             try
@@ -983,7 +1074,7 @@ namespace XUnity.AutoTranslator.Plugin.Core
          {
             try
             {
-               TranslateTexture( source, texture, false );
+               TranslateTexture( source, texture, isPrefixHooked, false );
             }
             catch( Exception e )
             {
@@ -992,7 +1083,7 @@ namespace XUnity.AutoTranslator.Plugin.Core
          }
       }
 
-      private void TranslateTexture( object source, Texture2D texture, bool forceReload )
+      private void TranslateTexture( object source, Texture2D texture, bool isPrefixHooked, bool forceReload )
       {
          try
          {
@@ -1030,7 +1121,10 @@ namespace XUnity.AutoTranslator.Plugin.Core
                      {
                         try
                         {
-                           source.SetAllDirtyEx();
+                           if( !isPrefixHooked )
+                           {
+                              source.SetAllDirtyEx();
+                           }
                         }
                         finally
                         {
@@ -1067,7 +1161,10 @@ namespace XUnity.AutoTranslator.Plugin.Core
                      {
                         try
                         {
-                           source.SetAllDirtyEx();
+                           if( !isPrefixHooked )
+                           {
+                              source.SetAllDirtyEx();
+                           }
                         }
                         finally
                         {
@@ -1103,7 +1200,10 @@ namespace XUnity.AutoTranslator.Plugin.Core
                      {
                         try
                         {
-                           source.SetAllDirtyEx();
+                           if( !isPrefixHooked )
+                           {
+                              source.SetAllDirtyEx();
+                           }
                         }
                         finally
                         {
@@ -1137,15 +1237,11 @@ namespace XUnity.AutoTranslator.Plugin.Core
                var key = info.GetKey( texture );
                if( string.IsNullOrEmpty( key ) ) return;
 
-               if( !IsImageTranslated( key ) )
+               if( !IsImageRegistered( key ) )
                {
-                  var name = texture.GetTextureName().SanitizeForFileSystem();
-                  var root = Path.Combine( Config.Current.DataPath, Settings.TextureDirectory );
-                  var fullName = Path.Combine( root, name + " [" + key + "].png" );
-
+                  var name = texture.GetTextureName();
                   var originalData = info.GetOrCreateOriginalData( texture );
-                  File.WriteAllBytes( fullName, originalData );
-                  RegisterImageDataAndHash( fullName, key, originalData );
+                  RegisterImageFromData( name, key, originalData );
                }
             }
             finally
@@ -1901,28 +1997,31 @@ namespace XUnity.AutoTranslator.Plugin.Core
       {
          LoadTranslations();
 
-         // FIXME: Translate TEXTURES first??? Problem if texture is not related to a component!
-
          foreach( var kvp in ObjectExtensions.GetAllRegisteredObjects() )
          {
             var ui = kvp.Key;
             try
             {
-               if( ( ui as Component )?.gameObject?.activeSelf ?? false )
+               if( ui is Component component )
                {
-                  var tti = kvp.Value as TextTranslationInfo;
-                  if( tti != null && !string.IsNullOrEmpty( tti.OriginalText ) )
+                  if( component.gameObject?.activeSelf ?? false )
                   {
-                     var key = new TranslationKey( kvp.Key, tti.OriginalText, false );
-                     if( TryGetTranslation( key, out string translatedText ) && !string.IsNullOrEmpty( translatedText ) )
+                     var tti = kvp.Value as TextTranslationInfo;
+                     if( tti != null && !string.IsNullOrEmpty( tti.OriginalText ) )
                      {
-                        SetTranslatedText( kvp.Key, translatedText, tti ); // no need to untemplatize the translated text
+                        var key = new TranslationKey( kvp.Key, tti.OriginalText, false );
+                        if( TryGetTranslation( key, out string translatedText ) && !string.IsNullOrEmpty( translatedText ) )
+                        {
+                           SetTranslatedText( kvp.Key, translatedText, tti ); // no need to untemplatize the translated text
+                        }
                      }
                   }
-
+               }
+               else
+               {
                   if( Settings.EnableTextureTranslation )
                   {
-                     TranslateTexture( ui, null, true );
+                     TranslateTexture( ui, null, false, true );
                   }
                }
             }
@@ -2040,17 +2139,22 @@ namespace XUnity.AutoTranslator.Plugin.Core
                var ui = kvp.Key;
                try
                {
-                  if( ( ui as Component )?.gameObject?.activeSelf ?? false )
+                  if( ui is Component component )
                   {
-                     var tti = kvp.Value as TextTranslationInfo;
-                     if( tti != null && tti.IsTranslated )
+                     if( component.gameObject?.activeSelf ?? false )
                      {
-                        SetText( ui, tti.TranslatedText, true, tti );
+                        var tti = kvp.Value as TextTranslationInfo;
+                        if( tti != null && tti.IsTranslated )
+                        {
+                           SetText( ui, tti.TranslatedText, true, tti );
+                        }
                      }
-
+                  }
+                  else
+                  {
                      if( Settings.EnableTextureTranslation && Settings.EnableTextureToggling )
                      {
-                        TranslateTexture( ui, null, false );
+                        TranslateTexture( ui, null, false, false );
                      }
                   }
                }
@@ -2069,17 +2173,22 @@ namespace XUnity.AutoTranslator.Plugin.Core
                var ui = kvp.Key;
                try
                {
-                  if( ( ui as Component )?.gameObject?.activeSelf ?? false )
+                  if( ui is Component component )
                   {
-                     var tti = kvp.Value as TextTranslationInfo;
-                     if( tti != null && tti.IsTranslated )
+                     if( component.gameObject?.activeSelf ?? false )
                      {
-                        SetText( ui, tti.OriginalText, true, tti );
+                        var tti = kvp.Value as TextTranslationInfo;
+                        if( tti != null && tti.IsTranslated )
+                        {
+                           SetText( ui, tti.OriginalText, true, tti );
+                        }
                      }
-
+                  }
+                  else
+                  {
                      if( Settings.EnableTextureTranslation && Settings.EnableTextureToggling )
                      {
-                        TranslateTexture( ui, null, false );
+                        TranslateTexture( ui, null, false, false );
                      }
                   }
                }
@@ -2142,6 +2251,12 @@ namespace XUnity.AutoTranslator.Plugin.Core
       }
 
       private void ManualHook()
+      {
+         ManualHookForText();
+         ManualHookForImages();
+      }
+
+      private void ManualHookForText()
       {
          foreach( var root in GetAllRoots() )
          {
@@ -2149,6 +2264,26 @@ namespace XUnity.AutoTranslator.Plugin.Core
          }
       }
 
+      private void ManualHookForImages()
+      {
+         if( Settings.EnableTextureTranslation || Settings.EnableTextureDumping )
+         {
+            // scan all textures and update
+            var textures = Resources.FindObjectsOfTypeAll<Texture2D>();
+            foreach( var texture in textures )
+            {
+               Hook_ImageChanged( texture );
+            }
+
+            //// scan all components and set dirty
+            //var components = GameObject.FindObjectsOfType<Component>();
+            //foreach( var component in components )
+            //{
+            //   component.SetAllDirtyEx();
+            //}
+         }
+      }
+
       private IEnumerable<GameObject> GetAllRoots()
       {
          var objects = GameObject.FindObjectsOfType<GameObject>();

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

@@ -83,6 +83,8 @@ namespace XUnity.AutoTranslator.Plugin.Core.Configuration
       public static bool EnableTextureTranslation;
       public static bool EnableTextureDumping;
       public static bool EnableTextureToggling;
+      public static bool EnableTextureScanOnSceneLoad;
+      public static bool LoadUnmodifiedTextures;
       public static TextureHashGenerationStrategy TextureHashGenerationStrategy;
 
       public static bool CopyToClipboard;
@@ -142,7 +144,9 @@ namespace XUnity.AutoTranslator.Plugin.Core.Configuration
          EnableTextureTranslation = Config.Current.Preferences[ "Texture" ][ "EnableTextureTranslation" ].GetOrDefault( false );
          EnableTextureDumping = Config.Current.Preferences[ "Texture" ][ "EnableTextureDumping" ].GetOrDefault( false );
          EnableTextureToggling = Config.Current.Preferences[ "Texture" ][ "EnableTextureToggling" ].GetOrDefault( false );
-         TextureHashGenerationStrategy = Config.Current.Preferences[ "Texture" ][ "TextureHashGenerationStrategy" ].GetOrDefault( TextureHashGenerationStrategy.FromImageNameThenData );
+         EnableTextureScanOnSceneLoad = Config.Current.Preferences[ "Texture" ][ "EnableTextureScanOnSceneLoad" ].GetOrDefault( true );
+         LoadUnmodifiedTextures = Config.Current.Preferences[ "Texture" ][ "LoadUnmodifiedTextures" ].GetOrDefault( false );
+         TextureHashGenerationStrategy = Config.Current.Preferences[ "Texture" ][ "TextureHashGenerationStrategy" ].GetOrDefault( TextureHashGenerationStrategy.FromImageName );
 
          // special handling because of enum parsing
          try

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

@@ -95,6 +95,10 @@ namespace XUnity.AutoTranslator.Plugin.Core.Extensions
          {
             graphic.SetAllDirty();
          }
+         else if( ui is Material material )
+         {
+            material.mainTexture = material.mainTexture;
+         }
          else
          {
             // lets attempt some reflection for several known types

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

@@ -37,7 +37,7 @@ namespace XUnity.AutoTranslator.Plugin.Core.Extensions
 
          // Simplify this??
 
-         return ( Settings.EnableUGUI && ( ui is Image || ui is RawImage ) )
+         return ( Settings.EnableUGUI && ( ui is Material || ui is Image || ui is RawImage ) )
             || ( Settings.EnableNGUI
                && ( ( ClrTypes.UIWidget != null && type != ClrTypes.UILabel && ClrTypes.UIWidget.IsAssignableFrom( type ) )
                || ( ClrTypes.UIAtlas != null && ClrTypes.UIAtlas.IsAssignableFrom( type ) )

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

@@ -29,7 +29,10 @@ namespace XUnity.AutoTranslator.Plugin.Core.Hooks
             {
                UGUIHooks.HooksOverriden = SetupHook( KnownEvents.OnUnableToTranslateUGUI, AutoTranslationPlugin.Current.ExternalHook_TextChanged_WithResult );
                harmony.PatchAll( UGUIHooks.All );
-               harmony.PatchAll( UGUIImageHooks.All );
+               if( Settings.EnableTextureTranslation || Settings.EnableTextureDumping )
+               {
+                  harmony.PatchAll( UGUIImageHooks.All );
+               }
             }
          }
          catch( Exception e )
@@ -56,7 +59,10 @@ namespace XUnity.AutoTranslator.Plugin.Core.Hooks
             {
                NGUIHooks.HooksOverriden = SetupHook( KnownEvents.OnUnableToTranslateNGUI, AutoTranslationPlugin.Current.ExternalHook_TextChanged_WithResult );
                harmony.PatchAll( NGUIHooks.All );
-               harmony.PatchAll( NGUIImageHooks.All );
+               if( Settings.EnableTextureTranslation || Settings.EnableTextureDumping )
+               {
+                  harmony.PatchAll( NGUIImageHooks.All );
+               }
             }
          }
          catch( Exception e )

+ 26 - 1
src/XUnity.AutoTranslator.Plugin.Core/Hooks/UGUIImageHooks.cs

@@ -16,10 +16,35 @@ namespace XUnity.AutoTranslator.Plugin.Core.Hooks
          typeof( Image_sprite_Hook ),
          typeof( Image_overrideSprite_Hook ),
          typeof( RawImage_texture_Hook ),
-         typeof( Cursor_SetCursor_Hook )
+         typeof( Cursor_SetCursor_Hook ),
+
+         //// fallback hooks on material (Prefix hooks)
+         //typeof( Material_mainTexture_Hook ),
       };
    }
 
+   //[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
    {

+ 4 - 4
src/XUnity.AutoTranslator.Plugin.Core/TextureTranslationInfo.cs

@@ -58,14 +58,14 @@ namespace XUnity.AutoTranslator.Plugin.Core
 
                _originalData = result.Data;
                _nonReadable = result.NonReadable;
-               _key = HashHelper.Compute( _originalData ).Substring( 0, 10 );
+               _key = HashHelper.Compute( _originalData ).Substring( 0, 8 );
             }
             else if( Settings.TextureHashGenerationStrategy == TextureHashGenerationStrategy.FromImageName )
             {
                var name = texture.name; // name may be duplicate, WILL be duplicate!
                if( string.IsNullOrEmpty( name ) || name.Contains( "(Clone)" ) ) return;
 
-               _key = HashHelper.Compute( Encoding.UTF8.GetBytes( name ) ).Substring( 0, 10 );
+               _key = HashHelper.Compute( Encoding.UTF8.GetBytes( name ) ).Substring( 0, 8 );
 
                if( Settings.EnableTextureToggling )
                {
@@ -84,11 +84,11 @@ namespace XUnity.AutoTranslator.Plugin.Core
 
                   _originalData = result.Data;
                   _nonReadable = result.NonReadable;
-                  _key = HashHelper.Compute( _originalData ).Substring( 0, 10 );
+                  _key = HashHelper.Compute( _originalData ).Substring( 0, 8 );
                }
                else
                {
-                  _key = HashHelper.Compute( Encoding.UTF8.GetBytes( name ) ).Substring( 0, 10 );
+                  _key = HashHelper.Compute( Encoding.UTF8.GetBytes( name ) ).Substring( 0, 8 );
 
                   if( Settings.EnableTextureToggling )
                   {