From b41f7211e5fd8e762f8a4748e070f1584ee7b577 Mon Sep 17 00:00:00 2001
From: sinaioutlander <49360850+sinaioutlander@users.noreply.github.com>
Date: Sun, 11 Oct 2020 20:07:23 +1100
Subject: [PATCH] 2.0.3
* Fixed a few issues related to the Texture2D support/export.
---
src/CacheObject/CacheObjectBase.cs | 4 +
src/Explorer.csproj | 2 +
src/ExplorerCore.cs | 2 +-
src/Helpers/Texture2DHelpers.cs | 194 ++++++++++++++++++
src/UI/Inspectors/ReflectionInspector.cs | 1 +
src/UI/InteractiveValue/InteractiveValue.cs | 14 +-
.../Object/InteractiveDictionary.cs | 3 +
.../Object/InteractiveSprite.cs | 37 +++-
.../Object/InteractiveTexture.cs | 30 +++
.../Object/InteractiveTexture2D.cs | 169 +++------------
src/UI/Main/SearchPage.cs | 9 +-
11 files changed, 313 insertions(+), 152 deletions(-)
create mode 100644 src/Helpers/Texture2DHelpers.cs
create mode 100644 src/UI/InteractiveValue/Object/InteractiveTexture.cs
diff --git a/src/CacheObject/CacheObjectBase.cs b/src/CacheObject/CacheObjectBase.cs
index 16e69b2..82d84eb 100644
--- a/src/CacheObject/CacheObjectBase.cs
+++ b/src/CacheObject/CacheObjectBase.cs
@@ -37,6 +37,10 @@ namespace Explorer.CacheObject
{
interactive = new InteractiveTexture2D();
}
+ else if (valueType == typeof(Texture))
+ {
+ interactive = new InteractiveTexture();
+ }
else if (valueType == typeof(Sprite))
{
interactive = new InteractiveSprite();
diff --git a/src/Explorer.csproj b/src/Explorer.csproj
index 3200986..2a3aa9b 100644
--- a/src/Explorer.csproj
+++ b/src/Explorer.csproj
@@ -208,11 +208,13 @@
+
+
diff --git a/src/ExplorerCore.cs b/src/ExplorerCore.cs
index abc9ddb..29a85ac 100644
--- a/src/ExplorerCore.cs
+++ b/src/ExplorerCore.cs
@@ -10,7 +10,7 @@ namespace Explorer
public class ExplorerCore
{
public const string NAME = "Explorer " + VERSION + " (" + PLATFORM + ", " + MODLOADER + ")";
- public const string VERSION = "2.0.2";
+ public const string VERSION = "2.0.3";
public const string AUTHOR = "Sinai";
public const string GUID = "com.sinai.explorer";
diff --git a/src/Helpers/Texture2DHelpers.cs b/src/Helpers/Texture2DHelpers.cs
new file mode 100644
index 0000000..2875b8e
--- /dev/null
+++ b/src/Helpers/Texture2DHelpers.cs
@@ -0,0 +1,194 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using UnityEngine;
+using System.IO;
+using System.Reflection;
+#if CPP
+using Explorer.Unstrip.ImageConversion;
+#endif
+
+namespace Explorer.Helpers
+{
+ public static class Texture2DHelpers
+ {
+#if CPP
+#else
+ private static bool isNewEncodeMethod = false;
+ private static MethodInfo EncodeToPNGMethod => m_encodeToPNGMethod ?? GetEncodeToPNGMethod();
+ private static MethodInfo m_encodeToPNGMethod;
+
+ private static MethodInfo GetEncodeToPNGMethod()
+ {
+ if (ReflectionHelpers.GetTypeByName("UnityEngine.ImageConversion") is Type imageConversion)
+ {
+ isNewEncodeMethod = true;
+ return m_encodeToPNGMethod = imageConversion.GetMethod("EncodeToPNG", ReflectionHelpers.CommonFlags);
+ }
+
+ var method = typeof(Texture2D).GetMethod("EncodeToPNG", ReflectionHelpers.CommonFlags);
+ if (method != null)
+ {
+ return m_encodeToPNGMethod = method;
+ }
+
+ ExplorerCore.Log("ERROR: Cannot get any EncodeToPNG method!");
+ return null;
+ }
+#endif
+
+
+ public static bool IsReadable(this Texture2D tex)
+ {
+ try
+ {
+ // This will cause an exception if it's not readable.
+ // Reason for doing it this way is not all Unity versions
+ // ship with the 'Texture.isReadable' property.
+
+ tex.GetPixel(0, 0);
+ return true;
+ }
+ catch
+ {
+ return false;
+ }
+ }
+
+ public static Texture2D Copy(Texture2D other, Rect rect, bool isDTXnmNormal = false)
+ {
+ Color[] pixels;
+
+ if (!other.IsReadable())
+ {
+ other = ForceReadTexture(other, isDTXnmNormal);
+ }
+
+ pixels = other.GetPixels((int)rect.x, (int)rect.y, (int)rect.width, (int)rect.height);
+
+ var _newTex = new Texture2D((int)rect.width, (int)rect.height);
+ _newTex.SetPixels(pixels);
+
+ return _newTex;
+ }
+
+ public static Texture2D ForceReadTexture(Texture2D tex, bool isDTXnmNormal = false)
+ {
+ try
+ {
+ var origFilter = tex.filterMode;
+ tex.filterMode = FilterMode.Point;
+
+ RenderTexture rt = RenderTexture.GetTemporary(tex.width, tex.height, 0, RenderTextureFormat.ARGB32);
+ rt.filterMode = FilterMode.Point;
+ RenderTexture.active = rt;
+ Graphics.Blit(tex, rt);
+
+ Texture2D _newTex = new Texture2D(tex.width, tex.height, TextureFormat.RGBA32, false);
+ _newTex.ReadPixels(new Rect(0, 0, tex.width, tex.height), 0, 0);
+
+ if (isDTXnmNormal)
+ {
+ _newTex = DTXnmToRGBA(_newTex);
+ }
+
+ _newTex.Apply(false, false);
+
+ RenderTexture.active = null;
+ tex.filterMode = origFilter;
+
+ return _newTex;
+ }
+ catch (Exception e)
+ {
+ ExplorerCore.Log("Exception on ForceReadTexture: " + e.ToString());
+ return default;
+ }
+ }
+
+ public static void SaveTextureAsPNG(Texture2D tex, string dir, string name, bool isDTXnmNormal = false)
+ {
+ if (!Directory.Exists(dir))
+ {
+ Directory.CreateDirectory(dir);
+ }
+
+ byte[] data;
+ var savepath = dir + @"\" + name + ".png";
+
+ // Fix for non-Readable or Compressed textures.
+ tex = ForceReadTexture(tex, isDTXnmNormal);
+
+ if (isDTXnmNormal)
+ {
+ tex = DTXnmToRGBA(tex);
+ tex.Apply(false, false);
+ }
+
+#if CPP
+ data = tex.EncodeToPNG();
+#else
+ var method = EncodeToPNGMethod;
+
+ if (isNewEncodeMethod)
+ {
+ data = (byte[])method.Invoke(null, new object[] { tex });
+ }
+ else
+ {
+ data = (byte[])method.Invoke(tex, new object[0]);
+ }
+#endif
+
+ if (data == null || data.Length < 1)
+ {
+ ExplorerCore.LogWarning("Couldn't get any data for the texture!");
+ }
+ else
+ {
+#if CPP
+ // The IL2CPP method will return invalid byte data.
+ // However, we can just iterate into safe C# byte[] array.
+ byte[] safeData = new byte[data.Length];
+ for (int i = 0; i < data.Length; i++)
+ {
+ safeData[i] = (byte)data[i]; // not sure if cast is needed
+ }
+
+ File.WriteAllBytes(savepath, safeData);
+#else
+ File.WriteAllBytes(savepath, data);
+#endif
+ }
+ }
+
+ // Converts DTXnm-format Normal Map to RGBA-format Normal Map.
+ public static Texture2D DTXnmToRGBA(Texture2D tex)
+ {
+ Color[] colors = tex.GetPixels();
+
+ for (int i = 0; i < colors.Length; i++)
+ {
+ Color c = colors[i];
+
+ c.r = c.a * 2 - 1; // red <- alpha
+ c.g = c.g * 2 - 1; // green is always the same
+
+ Vector2 rg = new Vector2(c.r, c.g); //this is the red-green vector
+ c.b = Mathf.Sqrt(1 - Mathf.Clamp01(Vector2.Dot(rg, rg))); //recalculate the blue channel
+
+ colors[i] = new Color(
+ (c.r * 0.5f) + 0.5f,
+ (c.g * 0.5f) + 0.25f,
+ (c.b * 0.5f) + 0.5f
+ );
+ }
+
+ var newtex = new Texture2D(tex.width, tex.height, TextureFormat.RGBA32, false);
+ newtex.SetPixels(colors);
+
+ return newtex;
+ }
+ }
+}
diff --git a/src/UI/Inspectors/ReflectionInspector.cs b/src/UI/Inspectors/ReflectionInspector.cs
index cbc802c..a804ff2 100644
--- a/src/UI/Inspectors/ReflectionInspector.cs
+++ b/src/UI/Inspectors/ReflectionInspector.cs
@@ -213,6 +213,7 @@ namespace Explorer.UI.Inspectors
try
{
var cached = CacheFactory.GetCacheObject(member, target);
+
if (cached != null)
{
cachedSigs.Add(sig);
diff --git a/src/UI/InteractiveValue/InteractiveValue.cs b/src/UI/InteractiveValue/InteractiveValue.cs
index 5b2ffad..e873119 100644
--- a/src/UI/InteractiveValue/InteractiveValue.cs
+++ b/src/UI/InteractiveValue/InteractiveValue.cs
@@ -198,31 +198,33 @@ namespace Explorer.UI
return m_toStringMethod;
}
- private string GetButtonLabel()
+ public string GetButtonLabel()
{
if (Value == null) return null;
+ var valueType = ReflectionHelpers.GetActualType(Value);
+
string label = (string)ToStringMethod?.Invoke(Value, null) ?? Value.ToString();
- var classColor = ValueType.IsAbstract && ValueType.IsSealed
+ var classColor = valueType.IsAbstract && valueType.IsSealed
? Syntax.Class_Static
: Syntax.Class_Instance;
- string typeLabel = $"{ValueType.FullName}";
+ string typeLabel = $"{valueType.FullName}";
if (Value is UnityEngine.Object)
{
- label = label.Replace($"({ValueType.FullName})", $"({typeLabel})");
+ label = label.Replace($"({valueType.FullName})", $"({typeLabel})");
}
else
{
- if (!label.Contains(ValueType.FullName))
+ if (!label.Contains(valueType.FullName))
{
label += $" ({typeLabel})";
}
else
{
- label = label.Replace(ValueType.FullName, typeLabel);
+ label = label.Replace(valueType.FullName, typeLabel);
}
}
diff --git a/src/UI/InteractiveValue/Object/InteractiveDictionary.cs b/src/UI/InteractiveValue/Object/InteractiveDictionary.cs
index 850a211..8dc1224 100644
--- a/src/UI/InteractiveValue/Object/InteractiveDictionary.cs
+++ b/src/UI/InteractiveValue/Object/InteractiveDictionary.cs
@@ -10,6 +10,9 @@ using Explorer.CacheObject;
namespace Explorer.UI
{
+ // TODO: Re-work class using InteractiveEnumerable or maybe InteractiveCollection for the Keys/Value lists.
+ // Make the keys and values editable.
+
public class InteractiveDictionary : InteractiveValue, IExpandHeight
{
public bool IsExpanded { get; set; }
diff --git a/src/UI/InteractiveValue/Object/InteractiveSprite.cs b/src/UI/InteractiveValue/Object/InteractiveSprite.cs
index cfed55b..e6cc7c8 100644
--- a/src/UI/InteractiveValue/Object/InteractiveSprite.cs
+++ b/src/UI/InteractiveValue/Object/InteractiveSprite.cs
@@ -2,27 +2,50 @@
using System.Collections.Generic;
using System.Linq;
using System.Text;
+using Explorer.Helpers;
using UnityEngine;
namespace Explorer.UI
{
public class InteractiveSprite : InteractiveTexture2D
{
- public override void GetTexture2D()
+ private Sprite refSprite;
+
+ public override void UpdateValue()
{
#if CPP
if (Value != null && Value.Il2CppCast(typeof(Sprite)) is Sprite sprite)
+ {
+ refSprite = sprite;
+ }
#else
if (Value is Sprite sprite)
-#endif
{
- currentTex = sprite.texture;
- texContent = new GUIContent
- {
- image = currentTex
- };
+ refSprite = sprite;
+ }
+#endif
+
+ base.UpdateValue();
+ }
+
+ public override void GetTexture2D()
+ {
+ if (refSprite)
+ {
+ currentTex = refSprite.texture;
}
}
+ public override void GetGUIContent()
+ {
+ // Check if the Sprite.textureRect is just the entire texture
+ if (refSprite.textureRect != new Rect(0, 0, currentTex.width, currentTex.height))
+ {
+ // It's not, do a sub-copy.
+ currentTex = Texture2DHelpers.Copy(refSprite.texture, refSprite.textureRect);
+ }
+
+ base.GetGUIContent();
+ }
}
}
diff --git a/src/UI/InteractiveValue/Object/InteractiveTexture.cs b/src/UI/InteractiveValue/Object/InteractiveTexture.cs
new file mode 100644
index 0000000..9c73d56
--- /dev/null
+++ b/src/UI/InteractiveValue/Object/InteractiveTexture.cs
@@ -0,0 +1,30 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using UnityEngine;
+
+namespace Explorer.UI
+{
+ // This class is possibly unnecessary.
+ // It's just for CacheMembers that have 'Texture' as the value type, but is actually a Texture2D.
+
+ public class InteractiveTexture : InteractiveTexture2D
+ {
+ public override void GetTexture2D()
+ {
+#if CPP
+ if (Value != null && Value.Il2CppCast(typeof(Texture2D)) is Texture2D tex)
+#else
+ if (Value is Texture2D tex)
+#endif
+ {
+ currentTex = tex;
+ texContent = new GUIContent
+ {
+ image = currentTex
+ };
+ }
+ }
+ }
+}
diff --git a/src/UI/InteractiveValue/Object/InteractiveTexture2D.cs b/src/UI/InteractiveValue/Object/InteractiveTexture2D.cs
index ed2cf38..60e83e0 100644
--- a/src/UI/InteractiveValue/Object/InteractiveTexture2D.cs
+++ b/src/UI/InteractiveValue/Object/InteractiveTexture2D.cs
@@ -6,6 +6,7 @@ using Explorer.CacheObject;
using Explorer.Config;
using UnityEngine;
using System.IO;
+using Explorer.Helpers;
#if CPP
using Explorer.Unstrip.ImageConversion;
#endif
@@ -42,35 +43,37 @@ namespace Explorer.UI
if (Value is Texture2D tex)
#endif
{
- currentTex = tex;
- texContent = new GUIContent
- {
- image = currentTex
- };
+ currentTex = tex;
}
}
+ public virtual void GetGUIContent()
+ {
+ texContent = new GUIContent
+ {
+ image = currentTex
+ };
+ }
+
public override void DrawValue(Rect window, float width)
{
GUIUnstrip.BeginVertical();
GUIUnstrip.BeginHorizontal();
- if (currentTex)
+ if (currentTex && !IsExpanded)
{
- if (!IsExpanded)
+ if (GUILayout.Button("v", new GUILayoutOption[] { GUILayout.Width(25) }))
{
- if (GUILayout.Button("v", new GUILayoutOption[] { GUILayout.Width(25) }))
- {
- IsExpanded = true;
- }
+ IsExpanded = true;
+ GetGUIContent();
}
- else
+ }
+ else if (currentTex)
+ {
+ if (GUILayout.Button("^", new GUILayoutOption[] { GUILayout.Width(25) }))
{
- if (GUILayout.Button("^", new GUILayoutOption[] { GUILayout.Width(25) }))
- {
- IsExpanded = false;
- }
+ IsExpanded = false;
}
}
@@ -112,29 +115,22 @@ namespace Explorer.UI
if (GUILayout.Button("Save to PNG", new GUILayoutOption[] { GUILayout.Width(100f) }))
{
- if (currentTex)
+ var name = RemoveInvalidFilenameChars(currentTex.name ?? "");
+ if (string.IsNullOrEmpty(name))
{
- var name = RemoveInvalidFilenameChars(currentTex.name ?? "");
- if (string.IsNullOrEmpty(name))
+ if (OwnerCacheObject is CacheMember cacheMember)
{
- if (OwnerCacheObject is CacheMember cacheMember)
- {
- name = cacheMember.MemInfo.Name;
- }
- else
- {
- name = "UNTITLED";
- }
+ name = cacheMember.MemInfo.Name;
+ }
+ else
+ {
+ name = "UNTITLED";
}
-
- SaveTextureAsPNG(currentTex, saveFolder, name, false);
-
- ExplorerCore.Log($@"Saved to {saveFolder}\{name}.png!");
- }
- else
- {
- ExplorerCore.Log("Cannot save a null texture!");
}
+
+ Texture2DHelpers.SaveTextureAsPNG(currentTex, saveFolder, name, false);
+
+ ExplorerCore.Log($@"Saved to {saveFolder}\{name}.png!");
}
}
@@ -148,107 +144,6 @@ namespace Explorer.UI
return s;
}
- public static void SaveTextureAsPNG(Texture2D tex, string dir, string name, bool isDTXnmNormal = false)
- {
- if (!Directory.Exists(dir))
- {
- Directory.CreateDirectory(dir);
- }
-
- byte[] data;
- var savepath = dir + @"\" + name + ".png";
-
- try
- {
- if (isDTXnmNormal)
- {
- tex = DTXnmToRGBA(tex);
- tex.Apply(false, false);
- }
-
- data = tex.EncodeToPNG();
-
- if (data == null)
- {
- ExplorerCore.Log("Couldn't get data with EncodeToPNG (probably ReadOnly?), trying manually...");
- throw new Exception();
- }
- }
- catch
- {
- var origFilter = tex.filterMode;
- tex.filterMode = FilterMode.Point;
-
- RenderTexture rt = RenderTexture.GetTemporary(tex.width, tex.height);
- rt.filterMode = FilterMode.Point;
- RenderTexture.active = rt;
- Graphics.Blit(tex, rt);
-
- Texture2D _newTex = new Texture2D(tex.width, tex.height, TextureFormat.RGBA32, false);
- _newTex.ReadPixels(new Rect(0, 0, tex.width, tex.height), 0, 0);
-
- if (isDTXnmNormal)
- {
- _newTex = DTXnmToRGBA(_newTex);
- }
-
- _newTex.Apply(false, false);
-
- RenderTexture.active = null;
- tex.filterMode = origFilter;
-
- data = _newTex.EncodeToPNG();
- //data = _newTex.GetRawTextureData();
- }
-
- if (data == null || data.Length < 1)
- {
- ExplorerCore.LogWarning("Couldn't get any data for the texture!");
- }
- else
- {
-#if CPP
- // The IL2CPP method will return invalid byte data.
- // However, we can just iterate into safe C# byte[] array.
- byte[] safeData = new byte[data.Length];
- for (int i = 0; i < data.Length; i++)
- {
- safeData[i] = (byte)data[i]; // not sure if cast is needed
- }
-
- File.WriteAllBytes(savepath, safeData);
-#else
- File.WriteAllBytes(savepath, data);
-#endif
- }
- }
-
- // Converts DTXnm-format Normal Map to RGBA-format Normal Map.
- public static Texture2D DTXnmToRGBA(Texture2D tex)
- {
- Color[] colors = tex.GetPixels();
-
- for (int i = 0; i < colors.Length; i++)
- {
- Color c = colors[i];
-
- c.r = c.a * 2 - 1; // red <- alpha
- c.g = c.g * 2 - 1; // green is always the same
-
- Vector2 rg = new Vector2(c.r, c.g); //this is the red-green vector
- c.b = Mathf.Sqrt(1 - Mathf.Clamp01(Vector2.Dot(rg, rg))); //recalculate the blue channel
-
- colors[i] = new Color(
- (c.r * 0.5f) + 0.5f,
- (c.g * 0.5f) + 0.25f,
- (c.b * 0.5f) + 0.5f
- );
- }
-
- var newtex = new Texture2D(tex.width, tex.height, TextureFormat.RGBA32, false);
- newtex.SetPixels(colors);
-
- return newtex;
- }
+
}
}
diff --git a/src/UI/Main/SearchPage.cs b/src/UI/Main/SearchPage.cs
index 5ca227d..153aa6e 100644
--- a/src/UI/Main/SearchPage.cs
+++ b/src/UI/Main/SearchPage.cs
@@ -161,8 +161,15 @@ namespace Explorer.UI.Main
GUIUnstrip.EndScrollView();
GUILayout.EndVertical();
}
- catch
+ catch (Exception e)
{
+ ExplorerCore.Log("Exception drawing search results!");
+ while (e != null)
+ {
+ ExplorerCore.Log(e);
+ e = e.InnerException;
+ }
+
m_searchResults.Clear();
}
}