mirror of
https://github.com/GrahamKracker/UnityExplorer.git
synced 2025-07-04 04:22:53 +08:00
Compare commits
33 Commits
Author | SHA1 | Date | |
---|---|---|---|
7a5570a070 | |||
d40537775f | |||
9b6f3fd3ea | |||
bacac929e9 | |||
4e3d3a2e5c | |||
7dbc8fd66e | |||
892cefcc91 | |||
a986b92963 | |||
8837119781 | |||
7eda249ddb | |||
710b4ba74a | |||
4bee55fb25 | |||
c71748d22a | |||
621035c732 | |||
2285a495be | |||
efdf2446bd | |||
6ad45ac8ae | |||
2d3c83cfa3 | |||
3213717ff6 | |||
a6a1a4d046 | |||
078c2e2b51 | |||
49bce650b4 | |||
bd9e80f2b4 | |||
d1eb5671bf | |||
f1ca484712 | |||
3b71b40843 | |||
1292affe6d | |||
6614762fe8 | |||
d81d6d034b | |||
5dfe3bbf0c | |||
dc81451ce5 | |||
d7ab0a23c6 | |||
1a01c740e2 |
@ -45,12 +45,18 @@
|
|||||||
|
|
||||||
The standalone release can be used with any injector or loader of your choice, but it requires you to load the dependencies manually.
|
The standalone release can be used with any injector or loader of your choice, but it requires you to load the dependencies manually.
|
||||||
|
|
||||||
1. Ensure the required libs are loaded - UniverseLib, HarmonyX and MonoMod
|
1. Ensure the required libs are loaded - UniverseLib, HarmonyX and MonoMod. Take them from the [`UnityExplorer.Editor`](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.Editor.zip) release if you need them.
|
||||||
2. For IL2CPP, load Il2CppAssemblyUnhollower and start an [Il2CppAssemblyUnhollower runtime](https://github.com/knah/Il2CppAssemblyUnhollower#required-external-setup)
|
2. For IL2CPP, load Il2CppAssemblyUnhollower and start an [Il2CppAssemblyUnhollower runtime](https://github.com/knah/Il2CppAssemblyUnhollower#required-external-setup)
|
||||||
2. Load the UnityExplorer DLL
|
2. Load the UnityExplorer DLL
|
||||||
3. Create an instance of Unity Explorer with `UnityExplorer.ExplorerStandalone.CreateInstance();`
|
3. Create an instance of Unity Explorer with `UnityExplorer.ExplorerStandalone.CreateInstance();`
|
||||||
4. Optionally subscribe to the `ExplorerStandalone.OnLog` event to handle logging if you wish
|
4. Optionally subscribe to the `ExplorerStandalone.OnLog` event to handle logging if you wish
|
||||||
|
|
||||||
|
## Unity Editor
|
||||||
|
|
||||||
|
1. Download the [`UnityExplorer.Editor`](https://github.com/sinai-dev/UnityExplorer/releases/latest/download/UnityExplorer.Editor.zip) release.
|
||||||
|
2. Install the package, either by using the Package Manager and importing the `package.json` file, or by manually dragging the folder into your `Assets` folder.
|
||||||
|
3. Drag the `Runtime/UnityExplorer` prefab into your scene, or create a GameObject and add the `Explorer Editor Behaviour` script to it.
|
||||||
|
|
||||||
# Common issues and solutions
|
# Common issues and solutions
|
||||||
|
|
||||||
Although UnityExplorer should work out of the box for most Unity games, in some cases you may need to tweak the settings for it to work properly.
|
Although UnityExplorer should work out of the box for most Unity games, in some cases you may need to tweak the settings for it to work properly.
|
||||||
|
BIN
img/social.png
BIN
img/social.png
Binary file not shown.
Before Width: | Height: | Size: 232 KiB |
BIN
lib/unhollowed/UnityEngine.AudioModule.dll
Normal file
BIN
lib/unhollowed/UnityEngine.AudioModule.dll
Normal file
Binary file not shown.
@ -11,8 +11,9 @@ namespace UnityExplorer.CacheObject
|
|||||||
public class CacheConstructor : CacheMember
|
public class CacheConstructor : CacheMember
|
||||||
{
|
{
|
||||||
public ConstructorInfo CtorInfo { get; }
|
public ConstructorInfo CtorInfo { get; }
|
||||||
|
readonly Type typeForStructConstructor;
|
||||||
|
|
||||||
public override Type DeclaringType => CtorInfo.DeclaringType;
|
public override Type DeclaringType => typeForStructConstructor ?? CtorInfo.DeclaringType;
|
||||||
public override bool IsStatic => true;
|
public override bool IsStatic => true;
|
||||||
public override bool ShouldAutoEvaluate => false;
|
public override bool ShouldAutoEvaluate => false;
|
||||||
public override bool CanWrite => false;
|
public override bool CanWrite => false;
|
||||||
@ -22,13 +23,38 @@ namespace UnityExplorer.CacheObject
|
|||||||
this.CtorInfo = ci;
|
this.CtorInfo = ci;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CacheConstructor(Type typeForStructConstructor)
|
||||||
|
{
|
||||||
|
this.typeForStructConstructor = typeForStructConstructor;
|
||||||
|
}
|
||||||
|
|
||||||
public override void SetInspectorOwner(ReflectionInspector inspector, MemberInfo member)
|
public override void SetInspectorOwner(ReflectionInspector inspector, MemberInfo member)
|
||||||
|
{
|
||||||
|
Type ctorReturnType;
|
||||||
|
// if is parameterless struct ctor
|
||||||
|
if (typeForStructConstructor != null)
|
||||||
|
{
|
||||||
|
ctorReturnType = typeForStructConstructor;
|
||||||
|
this.Owner = inspector;
|
||||||
|
|
||||||
|
// eg. Vector3.Vector3()
|
||||||
|
this.NameLabelText = SignatureHighlighter.Parse(typeForStructConstructor, false);
|
||||||
|
NameLabelText += $".{NameLabelText}()";
|
||||||
|
|
||||||
|
this.NameForFiltering = SignatureHighlighter.RemoveHighlighting(NameLabelText);
|
||||||
|
this.NameLabelTextRaw = NameForFiltering;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
base.SetInspectorOwner(inspector, member);
|
base.SetInspectorOwner(inspector, member);
|
||||||
|
|
||||||
Arguments = CtorInfo.GetParameters();
|
Arguments = CtorInfo.GetParameters();
|
||||||
if (CtorInfo.DeclaringType.IsGenericTypeDefinition)
|
ctorReturnType = CtorInfo.DeclaringType;
|
||||||
GenericArguments = CtorInfo.DeclaringType.GetGenericArguments();
|
}
|
||||||
|
|
||||||
|
if (ctorReturnType.IsGenericTypeDefinition)
|
||||||
|
GenericArguments = ctorReturnType.GetGenericArguments();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override object TryEvaluate()
|
protected override object TryEvaluate()
|
||||||
@ -46,13 +72,11 @@ namespace UnityExplorer.CacheObject
|
|||||||
else
|
else
|
||||||
ret = Activator.CreateInstance(returnType, ArgumentUtility.EmptyArgs);
|
ret = Activator.CreateInstance(returnType, ArgumentUtility.EmptyArgs);
|
||||||
|
|
||||||
HadException = false;
|
|
||||||
LastException = null;
|
LastException = null;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
HadException = true;
|
|
||||||
LastException = ex;
|
LastException = ex;
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -32,13 +32,11 @@ namespace UnityExplorer.CacheObject
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var ret = FieldInfo.GetValue(DeclaringInstance);
|
var ret = FieldInfo.GetValue(DeclaringInstance);
|
||||||
HadException = false;
|
|
||||||
LastException = null;
|
LastException = null;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
HadException = true;
|
|
||||||
LastException = ex;
|
LastException = ex;
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -169,174 +169,5 @@ namespace UnityExplorer.CacheObject
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Cache Member Util
|
|
||||||
|
|
||||||
public static List<CacheMember> GetCacheMembers(object inspectorTarget, Type type, ReflectionInspector inspector)
|
|
||||||
{
|
|
||||||
//var list = new List<CacheMember>();
|
|
||||||
HashSet<string> cachedSigs = new();
|
|
||||||
List<CacheMember> props = new();
|
|
||||||
List<CacheMember> fields = new();
|
|
||||||
List<CacheMember> ctors = new();
|
|
||||||
List<CacheMember> methods = new();
|
|
||||||
|
|
||||||
var types = ReflectionUtility.GetAllBaseTypes(type);
|
|
||||||
|
|
||||||
var flags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static;
|
|
||||||
if (!inspector.StaticOnly)
|
|
||||||
flags |= BindingFlags.Instance;
|
|
||||||
|
|
||||||
// Get non-static constructors of the main type.
|
|
||||||
// There's no reason to get the static cctor, it will be invoked when we inspect the class.
|
|
||||||
// Also no point getting ctors on inherited types.
|
|
||||||
foreach (var ctor in type.GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
|
|
||||||
TryCacheMember(ctor, ctors, cachedSigs, type, inspector);
|
|
||||||
|
|
||||||
foreach (var declaringType in types)
|
|
||||||
{
|
|
||||||
var target = inspectorTarget;
|
|
||||||
if (!inspector.StaticOnly)
|
|
||||||
target = target.TryCast(declaringType);
|
|
||||||
|
|
||||||
foreach (var prop in declaringType.GetProperties(flags))
|
|
||||||
if (prop.DeclaringType == declaringType)
|
|
||||||
TryCacheMember(prop, props, cachedSigs, declaringType, inspector);
|
|
||||||
|
|
||||||
foreach (var field in declaringType.GetFields(flags))
|
|
||||||
if (field.DeclaringType == declaringType)
|
|
||||||
TryCacheMember(field, fields, cachedSigs, declaringType, inspector);
|
|
||||||
|
|
||||||
foreach (var method in declaringType.GetMethods(flags))
|
|
||||||
if (method.DeclaringType == declaringType)
|
|
||||||
TryCacheMember(method, methods, cachedSigs, declaringType, inspector);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
var sorted = new List<CacheMember>();
|
|
||||||
sorted.AddRange(props.OrderBy(it => Array.IndexOf(types, it.DeclaringType))
|
|
||||||
.ThenBy(it => it.NameForFiltering));
|
|
||||||
sorted.AddRange(fields.OrderBy(it => Array.IndexOf(types, it.DeclaringType))
|
|
||||||
.ThenBy(it => it.NameForFiltering));
|
|
||||||
sorted.AddRange(ctors.OrderBy(it => Array.IndexOf(types, it.DeclaringType))
|
|
||||||
.ThenBy(it => it.NameForFiltering));
|
|
||||||
sorted.AddRange(methods.OrderBy(it => Array.IndexOf(types, it.DeclaringType))
|
|
||||||
.ThenBy(it => it.NameForFiltering));
|
|
||||||
return sorted;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void TryCacheMember(MemberInfo member, IList list, HashSet<string> cachedSigs,
|
|
||||||
Type declaringType, ReflectionInspector inspector, bool ignorePropertyMethodInfos = true)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (UERuntimeHelper.IsBlacklisted(member))
|
|
||||||
return;
|
|
||||||
|
|
||||||
var sig = GetSig(member);
|
|
||||||
|
|
||||||
// ExplorerCore.Log($"Trying to cache member {sig}... ({member.MemberType})");
|
|
||||||
|
|
||||||
CacheMember cached;
|
|
||||||
Type returnType;
|
|
||||||
switch (member.MemberType)
|
|
||||||
{
|
|
||||||
case MemberTypes.Constructor:
|
|
||||||
{
|
|
||||||
var ci = member as ConstructorInfo;
|
|
||||||
sig += GetArgumentString(ci.GetParameters());
|
|
||||||
if (cachedSigs.Contains(sig))
|
|
||||||
return;
|
|
||||||
cached = new CacheConstructor(ci);
|
|
||||||
returnType = ci.DeclaringType;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MemberTypes.Method:
|
|
||||||
{
|
|
||||||
var mi = member as MethodInfo;
|
|
||||||
if (ignorePropertyMethodInfos
|
|
||||||
&& (mi.Name.StartsWith("get_") || mi.Name.StartsWith("set_")))
|
|
||||||
return;
|
|
||||||
|
|
||||||
sig += GetArgumentString(mi.GetParameters());
|
|
||||||
if (cachedSigs.Contains(sig))
|
|
||||||
return;
|
|
||||||
|
|
||||||
cached = new CacheMethod(mi);
|
|
||||||
returnType = mi.ReturnType;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case MemberTypes.Property:
|
|
||||||
{
|
|
||||||
var pi = member as PropertyInfo;
|
|
||||||
|
|
||||||
if (!pi.CanRead && pi.CanWrite)
|
|
||||||
{
|
|
||||||
// write-only property, cache the set method instead.
|
|
||||||
var setMethod = pi.GetSetMethod(true);
|
|
||||||
if (setMethod != null)
|
|
||||||
TryCacheMember(setMethod, list, cachedSigs, declaringType, inspector, false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
sig += GetArgumentString(pi.GetIndexParameters());
|
|
||||||
if (cachedSigs.Contains(sig))
|
|
||||||
return;
|
|
||||||
|
|
||||||
cached = new CacheProperty(pi);
|
|
||||||
returnType = pi.PropertyType;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case MemberTypes.Field:
|
|
||||||
{
|
|
||||||
var fi = member as FieldInfo;
|
|
||||||
cached = new CacheField(fi);
|
|
||||||
returnType = fi.FieldType;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default: return;
|
|
||||||
}
|
|
||||||
|
|
||||||
cachedSigs.Add(sig);
|
|
||||||
|
|
||||||
cached.SetFallbackType(returnType);
|
|
||||||
cached.SetInspectorOwner(inspector, member);
|
|
||||||
|
|
||||||
list.Add(cached);
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
ExplorerCore.LogWarning($"Exception caching member {member.DeclaringType.FullName}.{member.Name}!");
|
|
||||||
ExplorerCore.Log(e.ToString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal static string GetSig(MemberInfo member)
|
|
||||||
=> $"{member.DeclaringType.Name}.{member.Name}";
|
|
||||||
|
|
||||||
internal static string GetArgumentString(ParameterInfo[] args)
|
|
||||||
{
|
|
||||||
var sb = new StringBuilder();
|
|
||||||
sb.Append(' ');
|
|
||||||
sb.Append('(');
|
|
||||||
foreach (var param in args)
|
|
||||||
{
|
|
||||||
sb.Append(param.ParameterType.Name);
|
|
||||||
sb.Append(' ');
|
|
||||||
sb.Append(param.Name);
|
|
||||||
sb.Append(',');
|
|
||||||
sb.Append(' ');
|
|
||||||
}
|
|
||||||
sb.Append(')');
|
|
||||||
return sb.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
170
src/CacheObject/CacheMemberFactory.cs
Normal file
170
src/CacheObject/CacheMemberFactory.cs
Normal file
@ -0,0 +1,170 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
using UnityExplorer.Inspectors;
|
||||||
|
using UnityExplorer.Runtime;
|
||||||
|
using UniverseLib;
|
||||||
|
using HarmonyLib;
|
||||||
|
using HarmonyLib.Tools;
|
||||||
|
|
||||||
|
namespace UnityExplorer.CacheObject
|
||||||
|
{
|
||||||
|
public static class CacheMemberFactory
|
||||||
|
{
|
||||||
|
public static List<CacheMember> GetCacheMembers(Type type, ReflectionInspector inspector)
|
||||||
|
{
|
||||||
|
//var list = new List<CacheMember>();
|
||||||
|
HashSet<string> cachedSigs = new();
|
||||||
|
List<CacheMember> props = new();
|
||||||
|
List<CacheMember> fields = new();
|
||||||
|
List<CacheMember> ctors = new();
|
||||||
|
List<CacheMember> methods = new();
|
||||||
|
|
||||||
|
var types = ReflectionUtility.GetAllBaseTypes(type);
|
||||||
|
|
||||||
|
var flags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static;
|
||||||
|
if (!inspector.StaticOnly)
|
||||||
|
flags |= BindingFlags.Instance;
|
||||||
|
|
||||||
|
if (!type.IsAbstract)
|
||||||
|
{
|
||||||
|
// Get non-static constructors of the main type.
|
||||||
|
// There's no reason to get the static cctor, it will be invoked when we inspect the class.
|
||||||
|
// Also no point getting ctors on inherited types.
|
||||||
|
foreach (var ctor in type.GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance))
|
||||||
|
TryCacheMember(ctor, ctors, cachedSigs, type, inspector);
|
||||||
|
|
||||||
|
// structs always have a parameterless constructor
|
||||||
|
if (type.IsValueType)
|
||||||
|
{
|
||||||
|
CacheConstructor cached = new(type);
|
||||||
|
cached.SetFallbackType(type);
|
||||||
|
cached.SetInspectorOwner(inspector, null);
|
||||||
|
ctors.Add(cached);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var declaringType in types)
|
||||||
|
{
|
||||||
|
foreach (var prop in declaringType.GetProperties(flags))
|
||||||
|
if (prop.DeclaringType == declaringType)
|
||||||
|
TryCacheMember(prop, props, cachedSigs, declaringType, inspector);
|
||||||
|
|
||||||
|
foreach (var field in declaringType.GetFields(flags))
|
||||||
|
if (field.DeclaringType == declaringType)
|
||||||
|
TryCacheMember(field, fields, cachedSigs, declaringType, inspector);
|
||||||
|
|
||||||
|
foreach (var method in declaringType.GetMethods(flags))
|
||||||
|
if (method.DeclaringType == declaringType)
|
||||||
|
TryCacheMember(method, methods, cachedSigs, declaringType, inspector);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
var sorted = new List<CacheMember>();
|
||||||
|
sorted.AddRange(props.OrderBy(it => Array.IndexOf(types, it.DeclaringType))
|
||||||
|
.ThenBy(it => it.NameForFiltering));
|
||||||
|
sorted.AddRange(fields.OrderBy(it => Array.IndexOf(types, it.DeclaringType))
|
||||||
|
.ThenBy(it => it.NameForFiltering));
|
||||||
|
sorted.AddRange(ctors.OrderBy(it => Array.IndexOf(types, it.DeclaringType))
|
||||||
|
.ThenBy(it => it.NameForFiltering));
|
||||||
|
sorted.AddRange(methods.OrderBy(it => Array.IndexOf(types, it.DeclaringType))
|
||||||
|
.ThenBy(it => it.NameForFiltering));
|
||||||
|
return sorted;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void TryCacheMember<T>(MemberInfo member, List<T> list, HashSet<string> cachedSigs,
|
||||||
|
Type declaringType, ReflectionInspector inspector, bool ignorePropertyMethodInfos = true)
|
||||||
|
where T : CacheMember
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (UERuntimeHelper.IsBlacklisted(member))
|
||||||
|
return;
|
||||||
|
|
||||||
|
string sig = member switch
|
||||||
|
{
|
||||||
|
// method or constructor
|
||||||
|
MethodBase mb => mb.FullDescription(),
|
||||||
|
// property or field
|
||||||
|
PropertyInfo or FieldInfo => $"{member.DeclaringType.FullDescription()}.{member.Name}",
|
||||||
|
_ => throw new NotImplementedException(),
|
||||||
|
};
|
||||||
|
|
||||||
|
if (cachedSigs.Contains(sig))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// ExplorerCore.Log($"Trying to cache member {sig}... ({member.MemberType})");
|
||||||
|
|
||||||
|
CacheMember cached;
|
||||||
|
Type returnType;
|
||||||
|
|
||||||
|
switch (member.MemberType)
|
||||||
|
{
|
||||||
|
case MemberTypes.Constructor:
|
||||||
|
{
|
||||||
|
var ci = member as ConstructorInfo;
|
||||||
|
cached = new CacheConstructor(ci);
|
||||||
|
returnType = ci.DeclaringType;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MemberTypes.Method:
|
||||||
|
{
|
||||||
|
var mi = member as MethodInfo;
|
||||||
|
if (ignorePropertyMethodInfos
|
||||||
|
&& (mi.Name.StartsWith("get_") || mi.Name.StartsWith("set_")))
|
||||||
|
return;
|
||||||
|
|
||||||
|
cached = new CacheMethod(mi);
|
||||||
|
returnType = mi.ReturnType;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case MemberTypes.Property:
|
||||||
|
{
|
||||||
|
var pi = member as PropertyInfo;
|
||||||
|
|
||||||
|
if (!pi.CanRead && pi.CanWrite)
|
||||||
|
{
|
||||||
|
// write-only property, cache the set method instead.
|
||||||
|
var setMethod = pi.GetSetMethod(true);
|
||||||
|
if (setMethod != null)
|
||||||
|
TryCacheMember(setMethod, list, cachedSigs, declaringType, inspector, false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
cached = new CacheProperty(pi);
|
||||||
|
returnType = pi.PropertyType;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case MemberTypes.Field:
|
||||||
|
{
|
||||||
|
var fi = member as FieldInfo;
|
||||||
|
cached = new CacheField(fi);
|
||||||
|
returnType = fi.FieldType;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
cachedSigs.Add(sig);
|
||||||
|
|
||||||
|
cached.SetFallbackType(returnType);
|
||||||
|
cached.SetInspectorOwner(inspector, member);
|
||||||
|
|
||||||
|
list.Add((T)cached);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
ExplorerCore.LogWarning($"Exception caching member {member.DeclaringType.FullName}.{member.Name}!");
|
||||||
|
ExplorerCore.Log(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -45,13 +45,11 @@ namespace UnityExplorer.CacheObject
|
|||||||
ret = methodInfo.Invoke(DeclaringInstance, Evaluator.TryParseArguments());
|
ret = methodInfo.Invoke(DeclaringInstance, Evaluator.TryParseArguments());
|
||||||
else
|
else
|
||||||
ret = methodInfo.Invoke(DeclaringInstance, ArgumentUtility.EmptyArgs);
|
ret = methodInfo.Invoke(DeclaringInstance, ArgumentUtility.EmptyArgs);
|
||||||
HadException = false;
|
|
||||||
LastException = null;
|
LastException = null;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
HadException = true;
|
|
||||||
LastException = ex;
|
LastException = ex;
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -57,7 +57,6 @@ namespace UnityExplorer.CacheObject
|
|||||||
public abstract bool ShouldAutoEvaluate { get; }
|
public abstract bool ShouldAutoEvaluate { get; }
|
||||||
public abstract bool HasArguments { get; }
|
public abstract bool HasArguments { get; }
|
||||||
public abstract bool CanWrite { get; }
|
public abstract bool CanWrite { get; }
|
||||||
public bool HadException { get; protected set; }
|
|
||||||
public Exception LastException { get; protected set; }
|
public Exception LastException { get; protected set; }
|
||||||
|
|
||||||
public virtual void SetFallbackType(Type fallbackType)
|
public virtual void SetFallbackType(Type fallbackType)
|
||||||
@ -106,8 +105,8 @@ namespace UnityExplorer.CacheObject
|
|||||||
if (CellView != null)
|
if (CellView != null)
|
||||||
SetDataToCell(CellView);
|
SetDataToCell(CellView);
|
||||||
|
|
||||||
// If the owner's parent CacheObject is set, we are setting the value of an inspected struct.
|
// If the owner's ParentCacheObject is set, we are setting the value of an inspected struct.
|
||||||
// Set the inspector target as the value back to that parent cacheobject.
|
// Set the inspector target as the value back to that parent.
|
||||||
if (Owner.ParentCacheObject != null)
|
if (Owner.ParentCacheObject != null)
|
||||||
Owner.ParentCacheObject.SetUserValue(Owner.Target);
|
Owner.ParentCacheObject.SetUserValue(Owner.Target);
|
||||||
}
|
}
|
||||||
@ -137,7 +136,7 @@ namespace UnityExplorer.CacheObject
|
|||||||
{
|
{
|
||||||
var prevState = State;
|
var prevState = State;
|
||||||
|
|
||||||
if (HadException)
|
if (LastException != null)
|
||||||
{
|
{
|
||||||
LastValueWasNull = true;
|
LastValueWasNull = true;
|
||||||
LastValueType = FallbackType;
|
LastValueType = FallbackType;
|
||||||
@ -158,7 +157,7 @@ namespace UnityExplorer.CacheObject
|
|||||||
{
|
{
|
||||||
// If we changed states (always needs IValue change)
|
// If we changed states (always needs IValue change)
|
||||||
// or if the value is null, and the fallback type isnt string (we always want to edit strings).
|
// or if the value is null, and the fallback type isnt string (we always want to edit strings).
|
||||||
if (State != prevState || (State != ValueState.String && Value.IsNullOrDestroyed()))
|
if (State != prevState || (State != ValueState.String && State != ValueState.Exception && Value.IsNullOrDestroyed()))
|
||||||
{
|
{
|
||||||
// need to return IValue
|
// need to return IValue
|
||||||
ReleaseIValue();
|
ReleaseIValue();
|
||||||
@ -206,7 +205,7 @@ namespace UnityExplorer.CacheObject
|
|||||||
return $"<i>{NOT_YET_EVAL} ({SignatureHighlighter.Parse(FallbackType, true)})</i>";
|
return $"<i>{NOT_YET_EVAL} ({SignatureHighlighter.Parse(FallbackType, true)})</i>";
|
||||||
|
|
||||||
case ValueState.Exception:
|
case ValueState.Exception:
|
||||||
return $"<i><color=red>{LastException.ReflectionExToString()}</color></i>";
|
return $"<i><color=#eb4034>{LastException.ReflectionExToString()}</color></i>";
|
||||||
|
|
||||||
// bool and number dont want the label for the value at all
|
// bool and number dont want the label for the value at all
|
||||||
case ValueState.Boolean:
|
case ValueState.Boolean:
|
||||||
@ -291,36 +290,36 @@ namespace UnityExplorer.CacheObject
|
|||||||
switch (State)
|
switch (State)
|
||||||
{
|
{
|
||||||
case ValueState.Exception:
|
case ValueState.Exception:
|
||||||
SetValueState(cell, ValueStateArgs.Default);
|
SetValueState(cell, new(true, subContentButtonActive: true));
|
||||||
break;
|
break;
|
||||||
case ValueState.Boolean:
|
case ValueState.Boolean:
|
||||||
SetValueState(cell, new ValueStateArgs(false, toggleActive: true, applyActive: CanWrite));
|
SetValueState(cell, new(false, toggleActive: true, applyActive: CanWrite));
|
||||||
break;
|
break;
|
||||||
case ValueState.Number:
|
case ValueState.Number:
|
||||||
SetValueState(cell, new ValueStateArgs(false, typeLabelActive: true, inputActive: true, applyActive: CanWrite));
|
SetValueState(cell, new(false, typeLabelActive: true, inputActive: true, applyActive: CanWrite));
|
||||||
break;
|
break;
|
||||||
case ValueState.String:
|
case ValueState.String:
|
||||||
if (LastValueWasNull)
|
if (LastValueWasNull)
|
||||||
SetValueState(cell, new ValueStateArgs(true, subContentButtonActive: true));
|
SetValueState(cell, new(true, subContentButtonActive: true));
|
||||||
else
|
else
|
||||||
SetValueState(cell, new ValueStateArgs(true, false, SignatureHighlighter.StringOrange, subContentButtonActive: true));
|
SetValueState(cell, new(true, false, SignatureHighlighter.StringOrange, subContentButtonActive: true));
|
||||||
break;
|
break;
|
||||||
case ValueState.Enum:
|
case ValueState.Enum:
|
||||||
SetValueState(cell, new ValueStateArgs(true, subContentButtonActive: CanWrite));
|
SetValueState(cell, new(true, subContentButtonActive: CanWrite));
|
||||||
break;
|
break;
|
||||||
case ValueState.Color:
|
case ValueState.Color:
|
||||||
case ValueState.ValueStruct:
|
case ValueState.ValueStruct:
|
||||||
if (ParseUtility.CanParse(LastValueType))
|
if (ParseUtility.CanParse(LastValueType))
|
||||||
SetValueState(cell, new ValueStateArgs(false, false, null, true, false, true, CanWrite, true, true));
|
SetValueState(cell, new(false, false, null, true, false, true, CanWrite, true, true));
|
||||||
else
|
else
|
||||||
SetValueState(cell, new ValueStateArgs(true, inspectActive: true, subContentButtonActive: true));
|
SetValueState(cell, new(true, inspectActive: true, subContentButtonActive: true));
|
||||||
break;
|
break;
|
||||||
case ValueState.Collection:
|
case ValueState.Collection:
|
||||||
case ValueState.Dictionary:
|
case ValueState.Dictionary:
|
||||||
SetValueState(cell, new ValueStateArgs(true, inspectActive: !LastValueWasNull, subContentButtonActive: !LastValueWasNull));
|
SetValueState(cell, new(true, inspectActive: !LastValueWasNull, subContentButtonActive: !LastValueWasNull));
|
||||||
break;
|
break;
|
||||||
case ValueState.Unsupported:
|
case ValueState.Unsupported:
|
||||||
SetValueState(cell, new ValueStateArgs(true, inspectActive: !LastValueWasNull));
|
SetValueState(cell, new (true, inspectActive: !LastValueWasNull));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -368,8 +367,10 @@ namespace UnityExplorer.CacheObject
|
|||||||
if (cell.InspectButton != null)
|
if (cell.InspectButton != null)
|
||||||
cell.InspectButton.Component.gameObject.SetActive(args.inspectActive && !LastValueWasNull);
|
cell.InspectButton.Component.gameObject.SetActive(args.inspectActive && !LastValueWasNull);
|
||||||
|
|
||||||
// allow IValue for null strings though
|
// set subcontent button if needed, and for null strings and exceptions
|
||||||
cell.SubContentButton.Component.gameObject.SetActive(args.subContentButtonActive && (!LastValueWasNull || State == ValueState.String));
|
cell.SubContentButton.Component.gameObject.SetActive(
|
||||||
|
args.subContentButtonActive
|
||||||
|
&& (!LastValueWasNull || State == ValueState.String || State == ValueState.Exception));
|
||||||
}
|
}
|
||||||
|
|
||||||
// CacheObjectCell Apply
|
// CacheObjectCell Apply
|
||||||
@ -401,7 +402,7 @@ namespace UnityExplorer.CacheObject
|
|||||||
{
|
{
|
||||||
if (this.IValue == null)
|
if (this.IValue == null)
|
||||||
{
|
{
|
||||||
var ivalueType = InteractiveValue.GetIValueTypeForState(State);
|
Type ivalueType = InteractiveValue.GetIValueTypeForState(State);
|
||||||
|
|
||||||
if (ivalueType == null)
|
if (ivalueType == null)
|
||||||
return;
|
return;
|
||||||
@ -455,6 +456,7 @@ namespace UnityExplorer.CacheObject
|
|||||||
{
|
{
|
||||||
inactiveIValueHolder = new GameObject("Temp_IValue_Holder");
|
inactiveIValueHolder = new GameObject("Temp_IValue_Holder");
|
||||||
GameObject.DontDestroyOnLoad(inactiveIValueHolder);
|
GameObject.DontDestroyOnLoad(inactiveIValueHolder);
|
||||||
|
inactiveIValueHolder.hideFlags = HideFlags.HideAndDontSave;
|
||||||
inactiveIValueHolder.transform.parent = UniversalUI.PoolHolder.transform;
|
inactiveIValueHolder.transform.parent = UniversalUI.PoolHolder.transform;
|
||||||
inactiveIValueHolder.SetActive(false);
|
inactiveIValueHolder.SetActive(false);
|
||||||
}
|
}
|
||||||
@ -467,9 +469,20 @@ namespace UnityExplorer.CacheObject
|
|||||||
|
|
||||||
public struct ValueStateArgs
|
public struct ValueStateArgs
|
||||||
{
|
{
|
||||||
public ValueStateArgs(bool valueActive = true, bool valueRichText = true, Color? valueColor = null,
|
public static ValueStateArgs Default { get; } = new(true);
|
||||||
bool typeLabelActive = false, bool toggleActive = false, bool inputActive = false, bool applyActive = false,
|
|
||||||
bool inspectActive = false, bool subContentButtonActive = false)
|
public Color valueColor;
|
||||||
|
public bool valueActive, valueRichText, typeLabelActive, toggleActive, inputActive, applyActive, inspectActive, subContentButtonActive;
|
||||||
|
|
||||||
|
public ValueStateArgs(bool valueActive = true,
|
||||||
|
bool valueRichText = true,
|
||||||
|
Color? valueColor = null,
|
||||||
|
bool typeLabelActive = false,
|
||||||
|
bool toggleActive = false,
|
||||||
|
bool inputActive = false,
|
||||||
|
bool applyActive = false,
|
||||||
|
bool inspectActive = false,
|
||||||
|
bool subContentButtonActive = false)
|
||||||
{
|
{
|
||||||
this.valueActive = valueActive;
|
this.valueActive = valueActive;
|
||||||
this.valueRichText = valueRichText;
|
this.valueRichText = valueRichText;
|
||||||
@ -481,14 +494,6 @@ namespace UnityExplorer.CacheObject
|
|||||||
this.inspectActive = inspectActive;
|
this.inspectActive = inspectActive;
|
||||||
this.subContentButtonActive = subContentButtonActive;
|
this.subContentButtonActive = subContentButtonActive;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ValueStateArgs Default => _default;
|
|
||||||
private static ValueStateArgs _default = new ValueStateArgs(true);
|
|
||||||
|
|
||||||
public bool valueActive, valueRichText, typeLabelActive, toggleActive,
|
|
||||||
inputActive, applyActive, inspectActive, subContentButtonActive;
|
|
||||||
|
|
||||||
public Color valueColor;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,13 +40,11 @@ namespace UnityExplorer.CacheObject
|
|||||||
ret = PropertyInfo.GetValue(DeclaringInstance, this.Evaluator.TryParseArguments());
|
ret = PropertyInfo.GetValue(DeclaringInstance, this.Evaluator.TryParseArguments());
|
||||||
else
|
else
|
||||||
ret = PropertyInfo.GetValue(DeclaringInstance, null);
|
ret = PropertyInfo.GetValue(DeclaringInstance, null);
|
||||||
HadException = false;
|
|
||||||
LastException = null;
|
LastException = null;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
HadException = true;
|
|
||||||
LastException = ex;
|
LastException = ex;
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -29,8 +29,11 @@ namespace UnityExplorer.CacheObject.IValues
|
|||||||
private EnumCompleter enumCompleter;
|
private EnumCompleter enumCompleter;
|
||||||
|
|
||||||
private GameObject toggleHolder;
|
private GameObject toggleHolder;
|
||||||
private readonly List<Toggle> flagToggles = new List<Toggle>();
|
private readonly List<Toggle> flagToggles = new();
|
||||||
private readonly List<Text> flagTexts = new List<Text>();
|
private readonly List<Text> flagTexts = new();
|
||||||
|
|
||||||
|
public CachedEnumValue ValueAtIndex(int idx) => (CachedEnumValue)CurrentValues[idx];
|
||||||
|
public CachedEnumValue ValueAtKey(object key) => (CachedEnumValue)CurrentValues[key];
|
||||||
|
|
||||||
// Setting value from owner
|
// Setting value from owner
|
||||||
public override void SetValue(object value)
|
public override void SetValue(object value)
|
||||||
@ -70,13 +73,8 @@ namespace UnityExplorer.CacheObject.IValues
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var split = value.ToString().Split(',');
|
|
||||||
var set = new HashSet<string>();
|
|
||||||
foreach (var s in split)
|
|
||||||
set.Add(s.Trim());
|
|
||||||
|
|
||||||
for (int i = 0; i < CurrentValues.Count; i++)
|
for (int i = 0; i < CurrentValues.Count; i++)
|
||||||
flagToggles[i].isOn = set.Contains(ValueAtIdx(i).Name);
|
flagToggles[i].isOn = (value as Enum).HasFlag(ValueAtIndex(i).ActualValue as Enum);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
@ -116,7 +114,7 @@ namespace UnityExplorer.CacheObject.IValues
|
|||||||
for (int i = 0; i < CurrentValues.Count; i++)
|
for (int i = 0; i < CurrentValues.Count; i++)
|
||||||
{
|
{
|
||||||
if (flagToggles[i].isOn)
|
if (flagToggles[i].isOn)
|
||||||
values.Add(ValueAtIdx(i).Name);
|
values.Add(ValueAtIndex(i).Name);
|
||||||
}
|
}
|
||||||
|
|
||||||
CurrentOwner.SetUserValue(Enum.Parse(EnumType, string.Join(", ", values.ToArray())));
|
CurrentOwner.SetUserValue(Enum.Parse(EnumType, string.Join(", ", values.ToArray())));
|
||||||
@ -166,9 +164,6 @@ namespace UnityExplorer.CacheObject.IValues
|
|||||||
return UIRoot;
|
return UIRoot;
|
||||||
}
|
}
|
||||||
|
|
||||||
public CachedEnumValue ValueAtIdx(int idx) => (CachedEnumValue)CurrentValues[idx];
|
|
||||||
public CachedEnumValue ValueAtKey(object key) => (CachedEnumValue)CurrentValues[key];
|
|
||||||
|
|
||||||
private void SetupTogglesForEnumType()
|
private void SetupTogglesForEnumType()
|
||||||
{
|
{
|
||||||
toggleHolder.SetActive(true);
|
toggleHolder.SetActive(true);
|
||||||
@ -191,7 +186,7 @@ namespace UnityExplorer.CacheObject.IValues
|
|||||||
AddToggleRow();
|
AddToggleRow();
|
||||||
|
|
||||||
flagToggles[i].isOn = false;
|
flagToggles[i].isOn = false;
|
||||||
flagTexts[i].text = ValueAtIdx(i).Name;
|
flagTexts[i].text = ValueAtIndex(i).Name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,8 +31,9 @@ namespace UnityExplorer.CacheObject.IValues
|
|||||||
{
|
{
|
||||||
base.OnBorrowed(owner);
|
base.OnBorrowed(owner);
|
||||||
|
|
||||||
inputField.Component.readOnly = !owner.CanWrite;
|
bool canWrite = owner.CanWrite && owner.State != ValueState.Exception;
|
||||||
ApplyButton.Component.gameObject.SetActive(owner.CanWrite);
|
inputField.Component.readOnly = !canWrite;
|
||||||
|
ApplyButton.Component.gameObject.SetActive(canWrite);
|
||||||
|
|
||||||
SaveFilePath.Text = Path.Combine(ConfigManager.Default_Output_Path.Value, "untitled.txt");
|
SaveFilePath.Text = Path.Combine(ConfigManager.Default_Output_Path.Value, "untitled.txt");
|
||||||
}
|
}
|
||||||
@ -47,6 +48,9 @@ namespace UnityExplorer.CacheObject.IValues
|
|||||||
|
|
||||||
public override void SetValue(object value)
|
public override void SetValue(object value)
|
||||||
{
|
{
|
||||||
|
if (CurrentOwner.State == ValueState.Exception)
|
||||||
|
value = CurrentOwner.LastException.ToString();
|
||||||
|
|
||||||
RealValue = value as string;
|
RealValue = value as string;
|
||||||
SaveFileRow.SetActive(IsStringTooLong(RealValue));
|
SaveFileRow.SetActive(IsStringTooLong(RealValue));
|
||||||
|
|
||||||
|
@ -16,22 +16,16 @@ namespace UnityExplorer.CacheObject.IValues
|
|||||||
{
|
{
|
||||||
public static Type GetIValueTypeForState(ValueState state)
|
public static Type GetIValueTypeForState(ValueState state)
|
||||||
{
|
{
|
||||||
switch (state)
|
return state switch
|
||||||
{
|
{
|
||||||
case ValueState.String:
|
ValueState.Exception or ValueState.String => typeof(InteractiveString),
|
||||||
return typeof(InteractiveString);
|
ValueState.Enum => typeof(InteractiveEnum),
|
||||||
case ValueState.Enum:
|
ValueState.Collection => typeof(InteractiveList),
|
||||||
return typeof(InteractiveEnum);
|
ValueState.Dictionary => typeof(InteractiveDictionary),
|
||||||
case ValueState.Collection:
|
ValueState.ValueStruct => typeof(InteractiveValueStruct),
|
||||||
return typeof(InteractiveList);
|
ValueState.Color => typeof(InteractiveColor),
|
||||||
case ValueState.Dictionary:
|
_ => null,
|
||||||
return typeof(InteractiveDictionary);
|
};
|
||||||
case ValueState.ValueStruct:
|
|
||||||
return typeof(InteractiveValueStruct);
|
|
||||||
case ValueState.Color:
|
|
||||||
return typeof(InteractiveColor);
|
|
||||||
default: return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public GameObject UIRoot { get; set; }
|
public GameObject UIRoot { get; set; }
|
||||||
@ -39,28 +33,28 @@ namespace UnityExplorer.CacheObject.IValues
|
|||||||
|
|
||||||
public virtual bool CanWrite => this.CurrentOwner.CanWrite;
|
public virtual bool CanWrite => this.CurrentOwner.CanWrite;
|
||||||
|
|
||||||
public CacheObjectBase CurrentOwner => m_owner;
|
public CacheObjectBase CurrentOwner => owner;
|
||||||
private CacheObjectBase m_owner;
|
private CacheObjectBase owner;
|
||||||
|
|
||||||
public bool PendingValueWanted;
|
public bool PendingValueWanted;
|
||||||
|
|
||||||
public virtual void OnBorrowed(CacheObjectBase owner)
|
public virtual void OnBorrowed(CacheObjectBase owner)
|
||||||
{
|
{
|
||||||
if (this.m_owner != null)
|
if (this.owner != null)
|
||||||
{
|
{
|
||||||
ExplorerCore.LogWarning("Setting an IValue's owner but there is already one set. Maybe it wasn't cleaned up?");
|
ExplorerCore.LogWarning("Setting an IValue's owner but there is already one set. Maybe it wasn't cleaned up?");
|
||||||
ReleaseFromOwner();
|
ReleaseFromOwner();
|
||||||
}
|
}
|
||||||
|
|
||||||
this.m_owner = owner;
|
this.owner = owner;
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual void ReleaseFromOwner()
|
public virtual void ReleaseFromOwner()
|
||||||
{
|
{
|
||||||
if (this.m_owner == null)
|
if (this.owner == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this.m_owner = null;
|
this.owner = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract void SetValue(object value);
|
public abstract void SetValue(object value);
|
||||||
|
@ -9,29 +9,26 @@ using UnhollowerRuntimeLib;
|
|||||||
|
|
||||||
namespace UnityExplorer
|
namespace UnityExplorer
|
||||||
{
|
{
|
||||||
// Handles all Behaviour update calls for UnityExplorer (Update, FixedUpdate, OnPostRender).
|
|
||||||
// Basically just a wrapper which calls the corresponding methods in ExplorerCore.
|
|
||||||
|
|
||||||
public class ExplorerBehaviour : MonoBehaviour
|
public class ExplorerBehaviour : MonoBehaviour
|
||||||
{
|
{
|
||||||
internal static ExplorerBehaviour Instance { get; private set; }
|
internal static ExplorerBehaviour Instance { get; private set; }
|
||||||
|
|
||||||
|
#if CPP
|
||||||
|
public ExplorerBehaviour(IntPtr ptr) : base(ptr) { }
|
||||||
|
#endif
|
||||||
|
|
||||||
internal static void Setup()
|
internal static void Setup()
|
||||||
{
|
{
|
||||||
#if CPP
|
#if CPP
|
||||||
ClassInjector.RegisterTypeInIl2Cpp<ExplorerBehaviour>();
|
ClassInjector.RegisterTypeInIl2Cpp<ExplorerBehaviour>();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
var obj = new GameObject("ExplorerBehaviour");
|
GameObject obj = new("ExplorerBehaviour");
|
||||||
GameObject.DontDestroyOnLoad(obj);
|
DontDestroyOnLoad(obj);
|
||||||
obj.hideFlags |= HideFlags.HideAndDontSave;
|
obj.hideFlags = HideFlags.HideAndDontSave;
|
||||||
Instance = obj.AddComponent<ExplorerBehaviour>();
|
Instance = obj.AddComponent<ExplorerBehaviour>();
|
||||||
}
|
}
|
||||||
|
|
||||||
#if CPP
|
|
||||||
public ExplorerBehaviour(IntPtr ptr) : base(ptr) { }
|
|
||||||
#endif
|
|
||||||
|
|
||||||
internal void Update()
|
internal void Update()
|
||||||
{
|
{
|
||||||
ExplorerCore.Update();
|
ExplorerCore.Update();
|
||||||
|
@ -1,24 +1,21 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.UI;
|
using UnityExplorer.CacheObject;
|
||||||
using UnityExplorer.Config;
|
using UnityExplorer.Config;
|
||||||
using UnityExplorer.UI;
|
|
||||||
using UnityExplorer.ObjectExplorer;
|
using UnityExplorer.ObjectExplorer;
|
||||||
using UnityExplorer.UI.Panels;
|
|
||||||
using UnityExplorer.Runtime;
|
using UnityExplorer.Runtime;
|
||||||
|
using UnityExplorer.UI;
|
||||||
|
using UnityExplorer.UI.Panels;
|
||||||
|
using UniverseLib;
|
||||||
using UniverseLib.Input;
|
using UniverseLib.Input;
|
||||||
using UniverseLib.UI;
|
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
namespace UnityExplorer
|
namespace UnityExplorer
|
||||||
{
|
{
|
||||||
public static class ExplorerCore
|
public static class ExplorerCore
|
||||||
{
|
{
|
||||||
public const string NAME = "UnityExplorer";
|
public const string NAME = "UnityExplorer";
|
||||||
public const string VERSION = "4.5.11";
|
public const string VERSION = "4.6.3";
|
||||||
public const string AUTHOR = "Sinai";
|
public const string AUTHOR = "Sinai";
|
||||||
public const string GUID = "com.sinai.unityexplorer";
|
public const string GUID = "com.sinai.unityexplorer";
|
||||||
|
|
||||||
@ -32,23 +29,20 @@ namespace UnityExplorer
|
|||||||
public static void Init(IExplorerLoader loader)
|
public static void Init(IExplorerLoader loader)
|
||||||
{
|
{
|
||||||
if (Loader != null)
|
if (Loader != null)
|
||||||
{
|
throw new Exception("UnityExplorer is already loaded.");
|
||||||
LogWarning("UnityExplorer is already loaded!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Loader = loader;
|
Loader = loader;
|
||||||
|
|
||||||
Log($"{NAME} {VERSION} initializing...");
|
Log($"{NAME} {VERSION} initializing...");
|
||||||
|
|
||||||
if (!Directory.Exists(Loader.ExplorerFolder))
|
|
||||||
Directory.CreateDirectory(Loader.ExplorerFolder);
|
Directory.CreateDirectory(Loader.ExplorerFolder);
|
||||||
|
|
||||||
ConfigManager.Init(Loader.ConfigHandler);
|
ConfigManager.Init(Loader.ConfigHandler);
|
||||||
|
|
||||||
UERuntimeHelper.Init();
|
UERuntimeHelper.Init();
|
||||||
ExplorerBehaviour.Setup();
|
ExplorerBehaviour.Setup();
|
||||||
UnityCrashPrevention.Init();
|
UnityCrashPrevention.Init();
|
||||||
|
|
||||||
UniverseLib.Universe.Init(ConfigManager.Startup_Delay_Time.Value, LateInit, Log, new()
|
Universe.Init(ConfigManager.Startup_Delay_Time.Value, LateInit, Log, new()
|
||||||
{
|
{
|
||||||
Disable_EventSystem_Override = ConfigManager.Disable_EventSystem_Override.Value,
|
Disable_EventSystem_Override = ConfigManager.Disable_EventSystem_Override.Value,
|
||||||
Force_Unlock_Mouse = ConfigManager.Force_Unlock_Mouse.Value,
|
Force_Unlock_Mouse = ConfigManager.Force_Unlock_Mouse.Value,
|
||||||
@ -59,7 +53,7 @@ namespace UnityExplorer
|
|||||||
// Do a delayed setup so that objects aren't destroyed instantly.
|
// Do a delayed setup so that objects aren't destroyed instantly.
|
||||||
// This can happen for a multitude of reasons.
|
// This can happen for a multitude of reasons.
|
||||||
// Default delay is 1 second which is usually enough.
|
// Default delay is 1 second which is usually enough.
|
||||||
private static void LateInit()
|
static void LateInit()
|
||||||
{
|
{
|
||||||
Log($"Setting up late core features...");
|
Log($"Setting up late core features...");
|
||||||
|
|
||||||
@ -69,21 +63,18 @@ namespace UnityExplorer
|
|||||||
|
|
||||||
UIManager.InitUI();
|
UIManager.InitUI();
|
||||||
|
|
||||||
Log($"{NAME} {VERSION} initialized for {UniverseLib.Universe.Context}.");
|
Log($"{NAME} {VERSION} ({Universe.Context}) initialized.");
|
||||||
|
|
||||||
//InspectorManager.Inspect(typeof(Tests.TestClass));
|
//InspectorManager.Inspect(typeof(Tests.TestClass));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
internal static void Update()
|
||||||
/// Should be called once per frame.
|
|
||||||
/// </summary>
|
|
||||||
public static void Update()
|
|
||||||
{
|
{
|
||||||
UIManager.Update();
|
|
||||||
|
|
||||||
// check master toggle
|
// check master toggle
|
||||||
if (InputManager.GetKeyDown(ConfigManager.Master_Toggle.Value))
|
if (InputManager.GetKeyDown(ConfigManager.Master_Toggle.Value))
|
||||||
UIManager.ShowMenu = !UIManager.ShowMenu;
|
UIManager.ShowMenu = !UIManager.ShowMenu;
|
||||||
|
|
||||||
|
UIManager.Update();
|
||||||
}
|
}
|
||||||
|
|
||||||
#region LOGGING
|
#region LOGGING
|
||||||
|
@ -2,46 +2,25 @@
|
|||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Reflection.Emit;
|
using System.Reflection.Emit;
|
||||||
using System.Text;
|
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityEngine.UI;
|
using UnityEngine.UI;
|
||||||
using UnityExplorer.Config;
|
|
||||||
using UnityExplorer.Runtime;
|
|
||||||
using UnityExplorer.CacheObject;
|
using UnityExplorer.CacheObject;
|
||||||
using UnityExplorer.CacheObject.Views;
|
using UnityExplorer.CacheObject.Views;
|
||||||
using UnityExplorer.UI.Panels;
|
using UnityExplorer.UI.Panels;
|
||||||
using UnityExplorer.UI.Widgets;
|
using UnityExplorer.UI.Widgets;
|
||||||
using UnityExplorer.UI;
|
|
||||||
using UniverseLib.UI.Widgets;
|
|
||||||
using UniverseLib.UI;
|
|
||||||
using UniverseLib;
|
using UniverseLib;
|
||||||
using UniverseLib.Runtime;
|
using UniverseLib.UI;
|
||||||
using UniverseLib.UI.Models;
|
using UniverseLib.UI.Models;
|
||||||
|
using UniverseLib.UI.ObjectPool;
|
||||||
using UniverseLib.UI.Widgets.ScrollView;
|
using UniverseLib.UI.Widgets.ScrollView;
|
||||||
using UniverseLib.Utility;
|
using UniverseLib.Utility;
|
||||||
|
|
||||||
namespace UnityExplorer.Inspectors
|
namespace UnityExplorer.Inspectors
|
||||||
{
|
{
|
||||||
public class ReflectionInspector : InspectorBase, ICellPoolDataSource<CacheMemberCell>, ICacheObjectController
|
[Flags]
|
||||||
{
|
public enum MemberFilter
|
||||||
public CacheObjectBase ParentCacheObject { get; set; }
|
|
||||||
public Type TargetType { get; private set; }
|
|
||||||
public bool StaticOnly { get; internal set; }
|
|
||||||
public bool CanWrite => true;
|
|
||||||
|
|
||||||
private List<CacheMember> members = new();
|
|
||||||
private readonly List<CacheMember> filteredMembers = new();
|
|
||||||
|
|
||||||
public bool AutoUpdateWanted => autoUpdateToggle.isOn;
|
|
||||||
|
|
||||||
private BindingFlags FlagsFilter;
|
|
||||||
private string NameFilter;
|
|
||||||
|
|
||||||
private MemberFlags MemberFilter = MemberFlags.All;
|
|
||||||
private enum MemberFlags
|
|
||||||
{
|
{
|
||||||
None = 0,
|
None = 0,
|
||||||
Property = 1,
|
Property = 1,
|
||||||
@ -51,25 +30,59 @@ namespace UnityExplorer.Inspectors
|
|||||||
All = Property | Field | Method | Constructor,
|
All = Property | Field | Method | Constructor,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class ReflectionInspector : InspectorBase, ICellPoolDataSource<CacheMemberCell>, ICacheObjectController
|
||||||
|
{
|
||||||
|
public CacheObjectBase ParentCacheObject { get; set; }
|
||||||
|
public Type TargetType { get; private set; }
|
||||||
|
public bool StaticOnly { get; internal set; }
|
||||||
|
public bool CanWrite => true;
|
||||||
|
|
||||||
|
public bool AutoUpdateWanted => autoUpdateToggle.isOn;
|
||||||
|
|
||||||
|
private List<CacheMember> members = new();
|
||||||
|
private readonly List<CacheMember> filteredMembers = new();
|
||||||
|
|
||||||
|
private BindingFlags scopeFlagsFilter;
|
||||||
|
private string nameFilter;
|
||||||
|
|
||||||
|
private MemberFilter MemberFilter = MemberFilter.All;
|
||||||
|
|
||||||
|
// Updating
|
||||||
|
|
||||||
|
private bool refreshWanted;
|
||||||
|
private string lastNameFilter;
|
||||||
|
private BindingFlags lastFlagsFilter;
|
||||||
|
private MemberFilter lastMemberFilter = MemberFilter.All;
|
||||||
|
private float timeOfLastAutoUpdate;
|
||||||
|
|
||||||
// UI
|
// UI
|
||||||
|
|
||||||
|
internal GameObject mainContentHolder;
|
||||||
|
private static int LeftGroupWidth { get; set; }
|
||||||
|
private static int RightGroupWidth { get; set; }
|
||||||
|
|
||||||
public ScrollPool<CacheMemberCell> MemberScrollPool { get; private set; }
|
public ScrollPool<CacheMemberCell> MemberScrollPool { get; private set; }
|
||||||
|
public int ItemCount => filteredMembers.Count;
|
||||||
|
|
||||||
|
public UnityObjectWidget UnityWidget;
|
||||||
|
|
||||||
public InputFieldRef HiddenNameText;
|
public InputFieldRef HiddenNameText;
|
||||||
public Text NameText;
|
public Text NameText;
|
||||||
public Text AssemblyText;
|
public Text AssemblyText;
|
||||||
private Toggle autoUpdateToggle;
|
private Toggle autoUpdateToggle;
|
||||||
|
|
||||||
private string currentBaseTabText;
|
internal string currentBaseTabText;
|
||||||
|
|
||||||
private readonly Color disabledButtonColor = new(0.24f, 0.24f, 0.24f);
|
|
||||||
private readonly Color enabledButtonColor = new(0.2f, 0.27f, 0.2f);
|
|
||||||
|
|
||||||
private readonly Dictionary<BindingFlags, ButtonRef> scopeFilterButtons = new();
|
private readonly Dictionary<BindingFlags, ButtonRef> scopeFilterButtons = new();
|
||||||
private readonly List<Toggle> memberTypeToggles = new();
|
private readonly List<Toggle> memberTypeToggles = new();
|
||||||
private InputFieldRef filterInputField;
|
private InputFieldRef filterInputField;
|
||||||
|
|
||||||
// Setup / return
|
// const
|
||||||
|
|
||||||
|
private readonly Color disabledButtonColor = new(0.24f, 0.24f, 0.24f);
|
||||||
|
private readonly Color enabledButtonColor = new(0.2f, 0.27f, 0.2f);
|
||||||
|
|
||||||
|
// Setup
|
||||||
|
|
||||||
public override void OnBorrowedFromPool(object target)
|
public override void OnBorrowedFromPool(object target)
|
||||||
{
|
{
|
||||||
@ -105,10 +118,12 @@ namespace UnityExplorer.Inspectors
|
|||||||
|
|
||||||
autoUpdateToggle.isOn = false;
|
autoUpdateToggle.isOn = false;
|
||||||
|
|
||||||
UnityObjectRef = null;
|
if (UnityWidget != null)
|
||||||
ComponentRef = null;
|
{
|
||||||
TextureRef = null;
|
UnityWidget.OnReturnToPool();
|
||||||
CleanupTextureViewer();
|
Pool.Return(UnityWidget.GetType(), UnityWidget);
|
||||||
|
this.UnityWidget = null;
|
||||||
|
}
|
||||||
|
|
||||||
base.OnReturnToPool();
|
base.OnReturnToPool();
|
||||||
}
|
}
|
||||||
@ -143,18 +158,20 @@ namespace UnityExplorer.Inspectors
|
|||||||
asmText = Path.GetFileName(TargetType.Assembly.Location);
|
asmText = Path.GetFileName(TargetType.Assembly.Location);
|
||||||
AssemblyText.text = $"<color=grey>Assembly:</color> {asmText}";
|
AssemblyText.text = $"<color=grey>Assembly:</color> {asmText}";
|
||||||
|
|
||||||
// unity helpers
|
// Unity object helper widget
|
||||||
SetUnityTargets();
|
|
||||||
|
if (!StaticOnly)
|
||||||
|
this.UnityWidget = UnityObjectWidget.GetUnityWidget(target, TargetType, this);
|
||||||
|
|
||||||
// Get cache members
|
// Get cache members
|
||||||
|
|
||||||
this.members = CacheMember.GetCacheMembers(Target, TargetType, this);
|
this.members = CacheMemberFactory.GetCacheMembers(TargetType, this);
|
||||||
|
|
||||||
// reset filters
|
// reset filters
|
||||||
|
|
||||||
this.filterInputField.Text = "";
|
this.filterInputField.Text = string.Empty;
|
||||||
|
|
||||||
SetFilter("", StaticOnly ? BindingFlags.Static : BindingFlags.Default);
|
SetFilter(string.Empty, StaticOnly ? BindingFlags.Static : BindingFlags.Default);
|
||||||
scopeFilterButtons[BindingFlags.Default].Component.gameObject.SetActive(!StaticOnly);
|
scopeFilterButtons[BindingFlags.Default].Component.gameObject.SetActive(!StaticOnly);
|
||||||
scopeFilterButtons[BindingFlags.Instance].Component.gameObject.SetActive(!StaticOnly);
|
scopeFilterButtons[BindingFlags.Instance].Component.gameObject.SetActive(!StaticOnly);
|
||||||
|
|
||||||
@ -166,12 +183,6 @@ namespace UnityExplorer.Inspectors
|
|||||||
|
|
||||||
// Updating
|
// Updating
|
||||||
|
|
||||||
private bool refreshWanted;
|
|
||||||
private string lastNameFilter;
|
|
||||||
private BindingFlags lastFlagsFilter;
|
|
||||||
private MemberFlags lastMemberFilter = MemberFlags.All;
|
|
||||||
private float timeOfLastAutoUpdate;
|
|
||||||
|
|
||||||
public override void Update()
|
public override void Update()
|
||||||
{
|
{
|
||||||
if (!this.IsActive)
|
if (!this.IsActive)
|
||||||
@ -184,10 +195,10 @@ namespace UnityExplorer.Inspectors
|
|||||||
}
|
}
|
||||||
|
|
||||||
// check filter changes or force-refresh
|
// check filter changes or force-refresh
|
||||||
if (refreshWanted || NameFilter != lastNameFilter || FlagsFilter != lastFlagsFilter || lastMemberFilter != MemberFilter)
|
if (refreshWanted || nameFilter != lastNameFilter || scopeFlagsFilter != lastFlagsFilter || lastMemberFilter != MemberFilter)
|
||||||
{
|
{
|
||||||
lastNameFilter = NameFilter;
|
lastNameFilter = nameFilter;
|
||||||
lastFlagsFilter = FlagsFilter;
|
lastFlagsFilter = scopeFlagsFilter;
|
||||||
lastMemberFilter = MemberFilter;
|
lastMemberFilter = MemberFilter;
|
||||||
|
|
||||||
FilterMembers();
|
FilterMembers();
|
||||||
@ -200,11 +211,8 @@ namespace UnityExplorer.Inspectors
|
|||||||
{
|
{
|
||||||
timeOfLastAutoUpdate = Time.realtimeSinceStartup;
|
timeOfLastAutoUpdate = Time.realtimeSinceStartup;
|
||||||
|
|
||||||
if (this.UnityObjectRef)
|
if (this.UnityWidget != null)
|
||||||
{
|
UnityWidget.Update();
|
||||||
nameInput.Text = UnityObjectRef.name;
|
|
||||||
this.Tab.TabText.text = $"{currentBaseTabText} \"{UnityObjectRef.name}\"";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (AutoUpdateWanted)
|
if (AutoUpdateWanted)
|
||||||
UpdateDisplayedMembers();
|
UpdateDisplayedMembers();
|
||||||
@ -218,26 +226,26 @@ namespace UnityExplorer.Inspectors
|
|||||||
|
|
||||||
// Filtering
|
// Filtering
|
||||||
|
|
||||||
public void SetFilter(string filter) => SetFilter(filter, FlagsFilter);
|
public void SetFilter(string name) => SetFilter(name, scopeFlagsFilter);
|
||||||
|
|
||||||
public void SetFilter(BindingFlags flagsFilter) => SetFilter(NameFilter, flagsFilter);
|
public void SetFilter(BindingFlags flags) => SetFilter(nameFilter, flags);
|
||||||
|
|
||||||
public void SetFilter(string nameFilter, BindingFlags flagsFilter)
|
public void SetFilter(string name, BindingFlags flags)
|
||||||
{
|
{
|
||||||
this.NameFilter = nameFilter;
|
this.nameFilter = name;
|
||||||
|
|
||||||
if (flagsFilter != FlagsFilter)
|
if (flags != scopeFlagsFilter)
|
||||||
{
|
{
|
||||||
var btn = scopeFilterButtons[FlagsFilter].Component;
|
var btn = scopeFilterButtons[scopeFlagsFilter].Component;
|
||||||
RuntimeHelper.SetColorBlock(btn, disabledButtonColor, disabledButtonColor * 1.3f);
|
RuntimeHelper.SetColorBlock(btn, disabledButtonColor, disabledButtonColor * 1.3f);
|
||||||
|
|
||||||
this.FlagsFilter = flagsFilter;
|
this.scopeFlagsFilter = flags;
|
||||||
btn = scopeFilterButtons[FlagsFilter].Component;
|
btn = scopeFilterButtons[scopeFlagsFilter].Component;
|
||||||
RuntimeHelper.SetColorBlock(btn, enabledButtonColor, enabledButtonColor * 1.3f);
|
RuntimeHelper.SetColorBlock(btn, enabledButtonColor, enabledButtonColor * 1.3f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnMemberTypeToggled(MemberFlags flag, bool val)
|
private void OnMemberTypeToggled(MemberFilter flag, bool val)
|
||||||
{
|
{
|
||||||
if (!val)
|
if (!val)
|
||||||
MemberFilter &= ~flag;
|
MemberFilter &= ~flag;
|
||||||
@ -253,19 +261,20 @@ namespace UnityExplorer.Inspectors
|
|||||||
{
|
{
|
||||||
var member = members[i];
|
var member = members[i];
|
||||||
|
|
||||||
if (FlagsFilter != BindingFlags.Default)
|
if (scopeFlagsFilter != BindingFlags.Default)
|
||||||
{
|
{
|
||||||
if (FlagsFilter == BindingFlags.Instance && member.IsStatic
|
if (scopeFlagsFilter == BindingFlags.Instance && member.IsStatic
|
||||||
|| FlagsFilter == BindingFlags.Static && !member.IsStatic)
|
|| scopeFlagsFilter == BindingFlags.Static && !member.IsStatic)
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((member is CacheMethod && !MemberFilter.HasFlag(MemberFlags.Method))
|
if ((member is CacheMethod && !MemberFilter.HasFlag(MemberFilter.Method))
|
||||||
|| (member is CacheField && !MemberFilter.HasFlag(MemberFlags.Field))
|
|| (member is CacheField && !MemberFilter.HasFlag(MemberFilter.Field))
|
||||||
|| (member is CacheProperty && !MemberFilter.HasFlag(MemberFlags.Property)))
|
|| (member is CacheProperty && !MemberFilter.HasFlag(MemberFilter.Property))
|
||||||
|
|| (member is CacheConstructor && !MemberFilter.HasFlag(MemberFilter.Constructor)))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(NameFilter) && !member.NameForFiltering.ContainsIgnoreCase(NameFilter))
|
if (!string.IsNullOrEmpty(nameFilter) && !member.NameForFiltering.ContainsIgnoreCase(nameFilter))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
filteredMembers.Add(member);
|
filteredMembers.Add(member);
|
||||||
@ -294,8 +303,6 @@ namespace UnityExplorer.Inspectors
|
|||||||
|
|
||||||
// Member cells
|
// Member cells
|
||||||
|
|
||||||
public int ItemCount => filteredMembers.Count;
|
|
||||||
|
|
||||||
public void OnCellBorrowed(CacheMemberCell cell) { } // not needed
|
public void OnCellBorrowed(CacheMemberCell cell) { } // not needed
|
||||||
|
|
||||||
public void SetCell(CacheMemberCell cell, int index)
|
public void SetCell(CacheMemberCell cell, int index)
|
||||||
@ -305,9 +312,6 @@ namespace UnityExplorer.Inspectors
|
|||||||
|
|
||||||
// Cell layout (fake table alignment)
|
// Cell layout (fake table alignment)
|
||||||
|
|
||||||
private static int LeftGroupWidth { get; set; }
|
|
||||||
private static int RightGroupWidth { get; set; }
|
|
||||||
|
|
||||||
internal void SetLayouts()
|
internal void SetLayouts()
|
||||||
{
|
{
|
||||||
CalculateLayouts();
|
CalculateLayouts();
|
||||||
@ -338,8 +342,6 @@ namespace UnityExplorer.Inspectors
|
|||||||
|
|
||||||
// UI Construction
|
// UI Construction
|
||||||
|
|
||||||
private GameObject mainContentHolder;
|
|
||||||
|
|
||||||
public override GameObject CreateContent(GameObject parent)
|
public override GameObject CreateContent(GameObject parent)
|
||||||
{
|
{
|
||||||
UIRoot = UIFactory.CreateVerticalGroup(parent, "ReflectionInspector", true, true, true, true, 5,
|
UIRoot = UIFactory.CreateVerticalGroup(parent, "ReflectionInspector", true, true, true, true, 5,
|
||||||
@ -380,8 +382,6 @@ namespace UnityExplorer.Inspectors
|
|||||||
AssemblyText = UIFactory.CreateLabel(UIRoot, "AssemblyLabel", "not set", TextAnchor.MiddleLeft);
|
AssemblyText = UIFactory.CreateLabel(UIRoot, "AssemblyLabel", "not set", TextAnchor.MiddleLeft);
|
||||||
UIFactory.SetLayoutElement(AssemblyText.gameObject, minHeight: 25, flexibleWidth: 9999);
|
UIFactory.SetLayoutElement(AssemblyText.gameObject, minHeight: 25, flexibleWidth: 9999);
|
||||||
|
|
||||||
ConstructUnityObjectRow();
|
|
||||||
|
|
||||||
mainContentHolder = UIFactory.CreateVerticalGroup(UIRoot, "MemberHolder", false, false, true, true, 5, new Vector4(2, 2, 2, 2),
|
mainContentHolder = UIFactory.CreateVerticalGroup(UIRoot, "MemberHolder", false, false, true, true, 5, new Vector4(2, 2, 2, 2),
|
||||||
new Color(0.12f, 0.12f, 0.12f));
|
new Color(0.12f, 0.12f, 0.12f));
|
||||||
UIFactory.SetLayoutElement(mainContentHolder, flexibleWidth: 9999, flexibleHeight: 9999);
|
UIFactory.SetLayoutElement(mainContentHolder, flexibleWidth: 9999, flexibleHeight: 9999);
|
||||||
@ -494,12 +494,12 @@ namespace UnityExplorer.Inspectors
|
|||||||
|
|
||||||
toggle.graphic.TryCast<Image>().color = color.ToColor() * 0.65f;
|
toggle.graphic.TryCast<Image>().color = color.ToColor() * 0.65f;
|
||||||
|
|
||||||
MemberFlags flag = type switch
|
MemberFilter flag = type switch
|
||||||
{
|
{
|
||||||
MemberTypes.Method => MemberFlags.Method,
|
MemberTypes.Method => MemberFilter.Method,
|
||||||
MemberTypes.Property => MemberFlags.Property,
|
MemberTypes.Property => MemberFilter.Property,
|
||||||
MemberTypes.Field => MemberFlags.Field,
|
MemberTypes.Field => MemberFilter.Field,
|
||||||
MemberTypes.Constructor => MemberFlags.Constructor,
|
MemberTypes.Constructor => MemberFilter.Constructor,
|
||||||
_ => throw new NotImplementedException()
|
_ => throw new NotImplementedException()
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -507,237 +507,5 @@ namespace UnityExplorer.Inspectors
|
|||||||
|
|
||||||
memberTypeToggles.Add(toggle);
|
memberTypeToggles.Add(toggle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Todo should probably put this in a separate class or maybe as a widget
|
|
||||||
|
|
||||||
#region UNITY OBJECT SPECIFIC
|
|
||||||
|
|
||||||
// Unity object helpers
|
|
||||||
|
|
||||||
private UnityEngine.Object UnityObjectRef;
|
|
||||||
private Component ComponentRef;
|
|
||||||
private Texture2D TextureRef;
|
|
||||||
private bool TextureViewerWanted;
|
|
||||||
private GameObject unityObjectRow;
|
|
||||||
private ButtonRef gameObjectButton;
|
|
||||||
private InputFieldRef nameInput;
|
|
||||||
private InputFieldRef instanceIdInput;
|
|
||||||
private ButtonRef textureButton;
|
|
||||||
private GameObject textureViewer;
|
|
||||||
|
|
||||||
private void SetUnityTargets()
|
|
||||||
{
|
|
||||||
if (StaticOnly || !typeof(UnityEngine.Object).IsAssignableFrom(TargetType))
|
|
||||||
{
|
|
||||||
unityObjectRow.SetActive(false);
|
|
||||||
textureViewer.SetActive(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
UnityObjectRef = (UnityEngine.Object)Target.TryCast(typeof(UnityEngine.Object));
|
|
||||||
unityObjectRow.SetActive(true);
|
|
||||||
|
|
||||||
nameInput.Text = UnityObjectRef.name;
|
|
||||||
instanceIdInput.Text = UnityObjectRef.GetInstanceID().ToString();
|
|
||||||
|
|
||||||
if (typeof(Component).IsAssignableFrom(TargetType))
|
|
||||||
{
|
|
||||||
ComponentRef = (Component)Target.TryCast(typeof(Component));
|
|
||||||
gameObjectButton.Component.gameObject.SetActive(true);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
gameObjectButton.Component.gameObject.SetActive(false);
|
|
||||||
|
|
||||||
if (typeof(Texture2D).IsAssignableFrom(TargetType))
|
|
||||||
{
|
|
||||||
TextureRef = (Texture2D)Target.TryCast(typeof(Texture2D));
|
|
||||||
textureButton.Component.gameObject.SetActive(true);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
textureButton.Component.gameObject.SetActive(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnGameObjectButtonClicked()
|
|
||||||
{
|
|
||||||
if (!ComponentRef)
|
|
||||||
{
|
|
||||||
ExplorerCore.LogWarning("Component reference is null or destroyed!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
InspectorManager.Inspect(ComponentRef.gameObject);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ToggleTextureViewer()
|
|
||||||
{
|
|
||||||
if (TextureViewerWanted)
|
|
||||||
{
|
|
||||||
// disable
|
|
||||||
TextureViewerWanted = false;
|
|
||||||
textureViewer.SetActive(false);
|
|
||||||
mainContentHolder.SetActive(true);
|
|
||||||
textureButton.ButtonText.text = "View Texture";
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (!textureImage.sprite)
|
|
||||||
{
|
|
||||||
// First show, need to create sprite for displaying texture
|
|
||||||
SetTextureViewer();
|
|
||||||
}
|
|
||||||
|
|
||||||
// enable
|
|
||||||
TextureViewerWanted = true;
|
|
||||||
textureViewer.SetActive(true);
|
|
||||||
mainContentHolder.gameObject.SetActive(false);
|
|
||||||
textureButton.ButtonText.text = "Hide Texture";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// UI construction
|
|
||||||
|
|
||||||
private void ConstructUnityObjectRow()
|
|
||||||
{
|
|
||||||
unityObjectRow = UIFactory.CreateUIObject("UnityObjectRow", UIRoot);
|
|
||||||
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(unityObjectRow, false, false, true, true, 5);
|
|
||||||
UIFactory.SetLayoutElement(unityObjectRow, minHeight: 25, flexibleHeight: 0, flexibleWidth: 9999);
|
|
||||||
|
|
||||||
textureButton = UIFactory.CreateButton(unityObjectRow, "TextureButton", "View Texture", new Color(0.2f, 0.2f, 0.2f));
|
|
||||||
UIFactory.SetLayoutElement(textureButton.Component.gameObject, minHeight: 25, minWidth: 150);
|
|
||||||
textureButton.OnClick += ToggleTextureViewer;
|
|
||||||
|
|
||||||
var nameLabel = UIFactory.CreateLabel(unityObjectRow, "NameLabel", "Name:", TextAnchor.MiddleLeft, Color.grey);
|
|
||||||
UIFactory.SetLayoutElement(nameLabel.gameObject, minHeight: 25, minWidth: 45, flexibleWidth: 0);
|
|
||||||
|
|
||||||
nameInput = UIFactory.CreateInputField(unityObjectRow, "NameInput", "untitled");
|
|
||||||
UIFactory.SetLayoutElement(nameInput.UIRoot, minHeight: 25, minWidth: 100, flexibleWidth: 1000);
|
|
||||||
nameInput.Component.readOnly = true;
|
|
||||||
|
|
||||||
gameObjectButton = UIFactory.CreateButton(unityObjectRow, "GameObjectButton", "Inspect GameObject", new Color(0.2f, 0.2f, 0.2f));
|
|
||||||
UIFactory.SetLayoutElement(gameObjectButton.Component.gameObject, minHeight: 25, minWidth: 160);
|
|
||||||
gameObjectButton.OnClick += OnGameObjectButtonClicked;
|
|
||||||
|
|
||||||
var instanceLabel = UIFactory.CreateLabel(unityObjectRow, "InstanceLabel", "Instance ID:", TextAnchor.MiddleRight, Color.grey);
|
|
||||||
UIFactory.SetLayoutElement(instanceLabel.gameObject, minHeight: 25, minWidth: 100, flexibleWidth: 0);
|
|
||||||
|
|
||||||
instanceIdInput = UIFactory.CreateInputField(unityObjectRow, "InstanceIDInput", "ERROR");
|
|
||||||
UIFactory.SetLayoutElement(instanceIdInput.UIRoot, minHeight: 25, minWidth: 100, flexibleWidth: 0);
|
|
||||||
instanceIdInput.Component.readOnly = true;
|
|
||||||
|
|
||||||
unityObjectRow.SetActive(false);
|
|
||||||
|
|
||||||
ConstructTextureHelper();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Texture viewer helper
|
|
||||||
|
|
||||||
private InputFieldRef textureSavePathInput;
|
|
||||||
private Image textureImage;
|
|
||||||
private LayoutElement textureImageLayout;
|
|
||||||
|
|
||||||
private void CleanupTextureViewer()
|
|
||||||
{
|
|
||||||
if (textureImage.sprite)
|
|
||||||
GameObject.Destroy(textureImage.sprite);
|
|
||||||
|
|
||||||
if (TextureViewerWanted)
|
|
||||||
ToggleTextureViewer();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ConstructTextureHelper()
|
|
||||||
{
|
|
||||||
textureViewer = UIFactory.CreateVerticalGroup(UIRoot, "TextureViewer", false, false, true, true, 2, new Vector4(5, 5, 5, 5),
|
|
||||||
new Color(0.1f, 0.1f, 0.1f));
|
|
||||||
UIFactory.SetLayoutElement(textureViewer, flexibleWidth: 9999, flexibleHeight: 9999);
|
|
||||||
|
|
||||||
// Save helper
|
|
||||||
|
|
||||||
var saveRowObj = UIFactory.CreateHorizontalGroup(textureViewer, "SaveRow", false, false, true, true, 2, new Vector4(2, 2, 2, 2),
|
|
||||||
new Color(0.1f, 0.1f, 0.1f));
|
|
||||||
|
|
||||||
var saveBtn = UIFactory.CreateButton(saveRowObj, "SaveButton", "Save .PNG", new Color(0.2f, 0.25f, 0.2f));
|
|
||||||
UIFactory.SetLayoutElement(saveBtn.Component.gameObject, minHeight: 25, minWidth: 100, flexibleWidth: 0);
|
|
||||||
saveBtn.OnClick += OnSaveTextureClicked;
|
|
||||||
|
|
||||||
textureSavePathInput = UIFactory.CreateInputField(saveRowObj, "SaveInput", "...");
|
|
||||||
UIFactory.SetLayoutElement(textureSavePathInput.UIRoot, minHeight: 25, minWidth: 100, flexibleWidth: 9999);
|
|
||||||
|
|
||||||
// Actual texture viewer
|
|
||||||
|
|
||||||
var imageViewport = UIFactory.CreateVerticalGroup(textureViewer, "Viewport", false, false, true, true);
|
|
||||||
imageViewport.GetComponent<Image>().color = Color.white;
|
|
||||||
imageViewport.AddComponent<Mask>().showMaskGraphic = false;
|
|
||||||
|
|
||||||
var imageObj = UIFactory.CreateUIObject("Image", imageViewport);
|
|
||||||
var fitter = imageObj.AddComponent<ContentSizeFitter>();
|
|
||||||
fitter.verticalFit = ContentSizeFitter.FitMode.PreferredSize;
|
|
||||||
textureImage = imageObj.AddComponent<Image>();
|
|
||||||
textureImageLayout = UIFactory.SetLayoutElement(imageObj, flexibleWidth: 1, flexibleHeight: 1);
|
|
||||||
|
|
||||||
textureViewer.SetActive(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SetTextureViewer()
|
|
||||||
{
|
|
||||||
if (!this.TextureRef)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var name = TextureRef.name;
|
|
||||||
if (string.IsNullOrEmpty(name))
|
|
||||||
name = "untitled";
|
|
||||||
|
|
||||||
textureSavePathInput.Text = Path.Combine(ConfigManager.Default_Output_Path.Value, $"{name}.png");
|
|
||||||
|
|
||||||
var sprite = TextureHelper.CreateSprite(TextureRef);
|
|
||||||
textureImage.sprite = sprite;
|
|
||||||
|
|
||||||
textureImageLayout.preferredHeight = sprite.rect.height;
|
|
||||||
// not really working, its always stretched horizontally for some reason.
|
|
||||||
textureImageLayout.preferredWidth = sprite.rect.width;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnSaveTextureClicked()
|
|
||||||
{
|
|
||||||
if (!TextureRef)
|
|
||||||
{
|
|
||||||
ExplorerCore.LogWarning("Ref Texture is null, maybe it was destroyed?");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(textureSavePathInput.Text))
|
|
||||||
{
|
|
||||||
ExplorerCore.LogWarning("Save path cannot be empty!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var path = textureSavePathInput.Text;
|
|
||||||
if (!path.EndsWith(".png", StringComparison.InvariantCultureIgnoreCase))
|
|
||||||
{
|
|
||||||
ExplorerCore.LogWarning("Desired save path must end with '.png'!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
path = IOUtility.EnsureValidFilePath(path);
|
|
||||||
|
|
||||||
if (File.Exists(path))
|
|
||||||
File.Delete(path);
|
|
||||||
|
|
||||||
var tex = TextureRef;
|
|
||||||
|
|
||||||
if (!TextureHelper.IsReadable(tex))
|
|
||||||
tex = TextureHelper.ForceReadTexture(tex);
|
|
||||||
|
|
||||||
byte[] data = TextureHelper.EncodeToPNG(tex);
|
|
||||||
File.WriteAllBytes(path, data);
|
|
||||||
|
|
||||||
if (tex != TextureRef)
|
|
||||||
{
|
|
||||||
// cleanup temp texture if we had to force-read it.
|
|
||||||
GameObject.Destroy(tex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
33
src/Loader/Standalone/Editor/ExplorerEditorBehaviour.cs
Normal file
33
src/Loader/Standalone/Editor/ExplorerEditorBehaviour.cs
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
#if STANDALONE
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace UnityExplorer.Loader.Standalone
|
||||||
|
{
|
||||||
|
public class ExplorerEditorBehaviour : MonoBehaviour
|
||||||
|
{
|
||||||
|
internal void Awake()
|
||||||
|
{
|
||||||
|
ExplorerEditorLoader.Initialize();
|
||||||
|
DontDestroyOnLoad(this);
|
||||||
|
this.gameObject.hideFlags = HideFlags.HideAndDontSave;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void OnDestroy()
|
||||||
|
{
|
||||||
|
OnApplicationQuit();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void OnApplicationQuit()
|
||||||
|
{
|
||||||
|
if (UI.UIManager.UIRoot)
|
||||||
|
Destroy(UI.UIManager.UIRoot.transform.root.gameObject);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
41
src/Loader/Standalone/Editor/ExplorerEditorLoader.cs
Normal file
41
src/Loader/Standalone/Editor/ExplorerEditorLoader.cs
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
#if STANDALONE
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using UnityEngine;
|
||||||
|
|
||||||
|
namespace UnityExplorer.Loader.Standalone
|
||||||
|
{
|
||||||
|
public class ExplorerEditorLoader : ExplorerStandalone
|
||||||
|
{
|
||||||
|
public static void Initialize()
|
||||||
|
{
|
||||||
|
Instance = new ExplorerEditorLoader();
|
||||||
|
OnLog += LogHandler;
|
||||||
|
Instance.configHandler = new StandaloneConfigHandler();
|
||||||
|
|
||||||
|
ExplorerCore.Init(Instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void LogHandler(string message, LogType logType)
|
||||||
|
{
|
||||||
|
switch (logType)
|
||||||
|
{
|
||||||
|
case LogType.Assert: Debug.LogError(message); break;
|
||||||
|
case LogType.Error: Debug.LogError(message); break;
|
||||||
|
case LogType.Exception: Debug.LogError(message); break;
|
||||||
|
case LogType.Log: Debug.Log(message); break;
|
||||||
|
case LogType.Warning: Debug.LogWarning(message); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void CheckExplorerFolder()
|
||||||
|
{
|
||||||
|
if (explorerFolder == null)
|
||||||
|
explorerFolder = Path.Combine(Application.dataPath, "UnityExplorer~");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
@ -5,9 +5,9 @@ using System.IO;
|
|||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
using UnityExplorer.Config;
|
using UnityExplorer.Config;
|
||||||
using UnityExplorer.Loader.STANDALONE;
|
|
||||||
using UnityEngine.EventSystems;
|
using UnityEngine.EventSystems;
|
||||||
using UniverseLib.Input;
|
using UniverseLib.Input;
|
||||||
|
using UnityExplorer.Loader.Standalone;
|
||||||
#if CPP
|
#if CPP
|
||||||
using UnhollowerRuntimeLib;
|
using UnhollowerRuntimeLib;
|
||||||
#endif
|
#endif
|
||||||
@ -16,6 +16,33 @@ namespace UnityExplorer
|
|||||||
{
|
{
|
||||||
public class ExplorerStandalone : IExplorerLoader
|
public class ExplorerStandalone : IExplorerLoader
|
||||||
{
|
{
|
||||||
|
public static ExplorerStandalone Instance { get; protected set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invoked whenever Explorer logs something. Subscribe to this to handle logging.
|
||||||
|
/// </summary>
|
||||||
|
public static event Action<string, LogType> OnLog;
|
||||||
|
|
||||||
|
public string UnhollowedModulesFolder => unhollowedPath;
|
||||||
|
private string unhollowedPath;
|
||||||
|
|
||||||
|
public ConfigHandler ConfigHandler => configHandler;
|
||||||
|
internal StandaloneConfigHandler configHandler;
|
||||||
|
|
||||||
|
public string ExplorerFolder
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
CheckExplorerFolder();
|
||||||
|
return explorerFolder;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
protected static string explorerFolder;
|
||||||
|
|
||||||
|
Action<object> IExplorerLoader.OnLogMessage => (object log) => { OnLog?.Invoke(log?.ToString() ?? "", LogType.Log); };
|
||||||
|
Action<object> IExplorerLoader.OnLogWarning => (object log) => { OnLog?.Invoke(log?.ToString() ?? "", LogType.Warning); };
|
||||||
|
Action<object> IExplorerLoader.OnLogError => (object log) => { OnLog?.Invoke(log?.ToString() ?? "", LogType.Error); };
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Call this to initialize UnityExplorer without adding a log listener or Unhollowed modules path.
|
/// Call this to initialize UnityExplorer without adding a log listener or Unhollowed modules path.
|
||||||
/// The default Unhollowed path "UnityExplorer\Modules\" will be used.
|
/// The default Unhollowed path "UnityExplorer\Modules\" will be used.
|
||||||
@ -50,60 +77,33 @@ namespace UnityExplorer
|
|||||||
OnLog += logListener;
|
OnLog += logListener;
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(unhollowedModulesPath) || !Directory.Exists(unhollowedModulesPath))
|
if (string.IsNullOrEmpty(unhollowedModulesPath) || !Directory.Exists(unhollowedModulesPath))
|
||||||
instance._unhollowedPath = Path.Combine(instance.ExplorerFolder, "Modules");
|
instance.unhollowedPath = Path.Combine(instance.ExplorerFolder, "Modules");
|
||||||
else
|
else
|
||||||
instance._unhollowedPath = unhollowedModulesPath;
|
instance.unhollowedPath = unhollowedModulesPath;
|
||||||
|
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ExplorerStandalone Instance { get; private set; }
|
internal void Init()
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Invoked whenever Explorer logs something. Subscribe to this to handle logging.
|
|
||||||
/// </summary>
|
|
||||||
public static event Action<string, LogType> OnLog;
|
|
||||||
|
|
||||||
public string UnhollowedModulesFolder => _unhollowedPath;
|
|
||||||
private string _unhollowedPath;
|
|
||||||
|
|
||||||
public ConfigHandler ConfigHandler => _configHandler;
|
|
||||||
private StandaloneConfigHandler _configHandler;
|
|
||||||
|
|
||||||
public string ExplorerFolder
|
|
||||||
{
|
|
||||||
get
|
|
||||||
{
|
|
||||||
CheckExplorerFolder();
|
|
||||||
return s_explorerFolder;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private static string s_explorerFolder;
|
|
||||||
|
|
||||||
Action<object> IExplorerLoader.OnLogMessage => (object log) => { OnLog?.Invoke(log?.ToString() ?? "", LogType.Log); };
|
|
||||||
Action<object> IExplorerLoader.OnLogWarning => (object log) => { OnLog?.Invoke(log?.ToString() ?? "", LogType.Warning); };
|
|
||||||
Action<object> IExplorerLoader.OnLogError => (object log) => { OnLog?.Invoke(log?.ToString() ?? "", LogType.Error); };
|
|
||||||
|
|
||||||
private void Init()
|
|
||||||
{
|
{
|
||||||
Instance = this;
|
Instance = this;
|
||||||
_configHandler = new StandaloneConfigHandler();
|
configHandler = new StandaloneConfigHandler();
|
||||||
|
|
||||||
ExplorerCore.Init(this);
|
ExplorerCore.Init(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CheckExplorerFolder()
|
protected virtual void CheckExplorerFolder()
|
||||||
{
|
{
|
||||||
if (s_explorerFolder == null)
|
if (explorerFolder == null)
|
||||||
{
|
{
|
||||||
s_explorerFolder =
|
explorerFolder =
|
||||||
Path.Combine(
|
Path.Combine(
|
||||||
Path.GetDirectoryName(
|
Path.GetDirectoryName(
|
||||||
Uri.UnescapeDataString(new Uri(Assembly.GetExecutingAssembly().CodeBase).AbsolutePath)),
|
Uri.UnescapeDataString(new Uri(Assembly.GetExecutingAssembly().CodeBase).AbsolutePath)),
|
||||||
"UnityExplorer");
|
"UnityExplorer");
|
||||||
|
|
||||||
if (!Directory.Exists(s_explorerFolder))
|
if (!Directory.Exists(explorerFolder))
|
||||||
Directory.CreateDirectory(s_explorerFolder);
|
Directory.CreateDirectory(explorerFolder);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@ using UnityEngine;
|
|||||||
using Tomlet;
|
using Tomlet;
|
||||||
using Tomlet.Models;
|
using Tomlet.Models;
|
||||||
|
|
||||||
namespace UnityExplorer.Loader.STANDALONE
|
namespace UnityExplorer.Loader.Standalone
|
||||||
{
|
{
|
||||||
public class StandaloneConfigHandler : ConfigHandler
|
public class StandaloneConfigHandler : ConfigHandler
|
||||||
{
|
{
|
||||||
@ -73,6 +73,10 @@ namespace UnityExplorer.Loader.STANDALONE
|
|||||||
return bool.Parse(value);
|
return bool.Parse(value);
|
||||||
else if (elementType == typeof(int))
|
else if (elementType == typeof(int))
|
||||||
return int.Parse(value);
|
return int.Parse(value);
|
||||||
|
else if (elementType == typeof(float))
|
||||||
|
return float.Parse(value);
|
||||||
|
else if (elementType.IsEnum)
|
||||||
|
return Enum.Parse(elementType, value);
|
||||||
else
|
else
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,7 @@ namespace UnityExplorer.ObjectExplorer
|
|||||||
private static Scene? selectedScene;
|
private static Scene? selectedScene;
|
||||||
|
|
||||||
/// <summary>The GameObjects in the currently inspected scene.</summary>
|
/// <summary>The GameObjects in the currently inspected scene.</summary>
|
||||||
public static GameObject[] CurrentRootObjects { get; private set; } = new GameObject[0];
|
public static IEnumerable<GameObject> CurrentRootObjects { get; private set; } = new GameObject[0];
|
||||||
|
|
||||||
/// <summary>All currently loaded Scenes.</summary>
|
/// <summary>All currently loaded Scenes.</summary>
|
||||||
public static List<Scene> LoadedScenes { get; private set; } = new();
|
public static List<Scene> LoadedScenes { get; private set; } = new();
|
||||||
@ -129,7 +129,7 @@ namespace UnityExplorer.ObjectExplorer
|
|||||||
if (go.transform.parent == null && !go.scene.IsValid())
|
if (go.transform.parent == null && !go.scene.IsValid())
|
||||||
objects.Add(go);
|
objects.Add(go);
|
||||||
}
|
}
|
||||||
CurrentRootObjects = objects.ToArray();
|
CurrentRootObjects = objects;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -24,8 +24,12 @@ namespace UnityExplorer.Tests
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#region MONO
|
||||||
|
|
||||||
public static object LiterallyAnything = null;
|
public static object LiterallyAnything = null;
|
||||||
|
|
||||||
|
public static string Exception => throw new Exception("This is a test.");
|
||||||
|
|
||||||
// Test enumerables
|
// Test enumerables
|
||||||
public static int[,,] MultiDimensionalArray = new int[45, 45, 45];
|
public static int[,,] MultiDimensionalArray = new int[45, 45, 45];
|
||||||
public static List<object> ListOfInts;
|
public static List<object> ListOfInts;
|
||||||
@ -145,6 +149,8 @@ namespace UnityExplorer.Tests
|
|||||||
ExplorerCore.Log("Finished TestClass Init_Mono");
|
ExplorerCore.Log("Finished TestClass Init_Mono");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
#if CPP
|
#if CPP
|
||||||
public static Il2CppSystem.Collections.Generic.Dictionary<string, string> IL2CPP_Dict;
|
public static Il2CppSystem.Collections.Generic.Dictionary<string, string> IL2CPP_Dict;
|
||||||
public static Il2CppSystem.Collections.Generic.HashSet<string> IL2CPP_HashSet;
|
public static Il2CppSystem.Collections.Generic.HashSet<string> IL2CPP_HashSet;
|
||||||
@ -257,6 +263,7 @@ namespace UnityExplorer.Tests
|
|||||||
|
|
||||||
ExplorerCore.Log($"Finished Init_Il2Cpp");
|
ExplorerCore.Log($"Finished Init_Il2Cpp");
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,10 @@ namespace UnityExplorer.UI
|
|||||||
public static int Width => ActiveDisplay.renderingWidth;
|
public static int Width => ActiveDisplay.renderingWidth;
|
||||||
public static int Height => ActiveDisplay.renderingHeight;
|
public static int Height => ActiveDisplay.renderingHeight;
|
||||||
|
|
||||||
public static Vector3 MousePosition => Display.RelativeMouseAt(InputManager.MousePosition);
|
public static Vector3 MousePosition => Application.isEditor
|
||||||
|
? InputManager.MousePosition
|
||||||
|
: Display.RelativeMouseAt(InputManager.MousePosition);
|
||||||
|
|
||||||
public static bool MouseInTargetDisplay => MousePosition.z == ActiveDisplayIndex;
|
public static bool MouseInTargetDisplay => MousePosition.z == ActiveDisplayIndex;
|
||||||
|
|
||||||
private static Camera canvasCamera;
|
private static Camera canvasCamera;
|
||||||
|
@ -30,13 +30,13 @@ namespace UnityExplorer.UI.Widgets.AutoComplete
|
|||||||
public override int MinWidth => -1;
|
public override int MinWidth => -1;
|
||||||
public override int MinHeight => -1;
|
public override int MinHeight => -1;
|
||||||
|
|
||||||
public override bool CanDragAndResize => false;
|
public override bool CanDragAndResize => true;
|
||||||
public override bool ShouldSaveActiveState => false;
|
public override bool ShouldSaveActiveState => false;
|
||||||
public override bool NavButtonWanted => false;
|
public override bool NavButtonWanted => false;
|
||||||
|
|
||||||
public static ISuggestionProvider CurrentHandler { get; private set; }
|
public static ISuggestionProvider CurrentHandler { get; private set; }
|
||||||
|
|
||||||
public static ButtonListHandler<Suggestion, ButtonCell> dataHandler;
|
public static ButtonListHandler<Suggestion, ButtonCell> buttonListDataHandler;
|
||||||
public static ScrollPool<ButtonCell> scrollPool;
|
public static ScrollPool<ButtonCell> scrollPool;
|
||||||
private static GameObject navigationTipRow;
|
private static GameObject navigationTipRow;
|
||||||
|
|
||||||
@ -82,7 +82,7 @@ namespace UnityExplorer.UI.Widgets.AutoComplete
|
|||||||
{
|
{
|
||||||
base.UIRoot.SetActive(true);
|
base.UIRoot.SetActive(true);
|
||||||
base.UIRoot.transform.SetAsLastSibling();
|
base.UIRoot.transform.SetAsLastSibling();
|
||||||
dataHandler.RefreshData();
|
buttonListDataHandler.RefreshData();
|
||||||
scrollPool.Refresh(true, true);
|
scrollPool.Refresh(true, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -294,20 +294,40 @@ namespace UnityExplorer.UI.Widgets.AutoComplete
|
|||||||
|
|
||||||
// UI Construction
|
// UI Construction
|
||||||
|
|
||||||
|
const float MIN_X = 0.42f;
|
||||||
|
const float MAX_Y = 0.6f;
|
||||||
|
|
||||||
protected internal override void DoSetDefaultPosAndAnchors()
|
protected internal override void DoSetDefaultPosAndAnchors()
|
||||||
{
|
{
|
||||||
Rect.pivot = new Vector2(0f, 1f);
|
Rect.pivot = new Vector2(0f, 1f);
|
||||||
Rect.anchorMin = new Vector2(0.42f, 0.4f);
|
Rect.anchorMin = new Vector2(MIN_X, 0.4f);
|
||||||
Rect.anchorMax = new Vector2(0.68f, 0.6f);
|
Rect.anchorMax = new Vector2(0.68f, MAX_Y);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnFinishResize(RectTransform panel)
|
||||||
|
{
|
||||||
|
float xDiff = panel.anchorMin.x - MIN_X;
|
||||||
|
float yDiff = panel.anchorMax.y - MAX_Y;
|
||||||
|
|
||||||
|
if (xDiff != 0 || yDiff != 0)
|
||||||
|
{
|
||||||
|
panel.anchorMin = new(MIN_X, panel.anchorMin.y - yDiff);
|
||||||
|
panel.anchorMax = new(panel.anchorMax.x - xDiff, MAX_Y);
|
||||||
|
}
|
||||||
|
|
||||||
|
base.OnFinishResize(panel);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void ConstructPanelContent()
|
public override void ConstructPanelContent()
|
||||||
{
|
{
|
||||||
dataHandler = new ButtonListHandler<Suggestion, ButtonCell>(scrollPool, GetEntries, SetCell, ShouldDisplay, OnCellClicked);
|
// hide the titlebar
|
||||||
|
this.TitleBar.gameObject.SetActive(false);
|
||||||
|
|
||||||
|
buttonListDataHandler = new ButtonListHandler<Suggestion, ButtonCell>(scrollPool, GetEntries, SetCell, ShouldDisplay, OnCellClicked);
|
||||||
|
|
||||||
scrollPool = UIFactory.CreateScrollPool<ButtonCell>(this.uiContent, "AutoCompleter", out GameObject scrollObj,
|
scrollPool = UIFactory.CreateScrollPool<ButtonCell>(this.uiContent, "AutoCompleter", out GameObject scrollObj,
|
||||||
out GameObject scrollContent);
|
out GameObject scrollContent);
|
||||||
scrollPool.Initialize(dataHandler);
|
scrollPool.Initialize(buttonListDataHandler);
|
||||||
UIFactory.SetLayoutElement(scrollObj, flexibleHeight: 9999);
|
UIFactory.SetLayoutElement(scrollObj, flexibleHeight: 9999);
|
||||||
UIFactory.SetLayoutGroup<VerticalLayoutGroup>(scrollContent, true, false, true, false);
|
UIFactory.SetLayoutGroup<VerticalLayoutGroup>(scrollContent, true, false, true, false);
|
||||||
|
|
@ -471,6 +471,9 @@ namespace UnityExplorer.UI.Panels
|
|||||||
{
|
{
|
||||||
var text = UIFactory.CreateLabel(UIManager.UIRoot, "ResizeCursor", "↔", TextAnchor.MiddleCenter, Color.white, true, 35);
|
var text = UIFactory.CreateLabel(UIManager.UIRoot, "ResizeCursor", "↔", TextAnchor.MiddleCenter, Color.white, true, 35);
|
||||||
resizeCursorObj = text.gameObject;
|
resizeCursorObj = text.gameObject;
|
||||||
|
var outline = text.gameObject.AddComponent<Outline>();
|
||||||
|
outline.effectColor = Color.black;
|
||||||
|
outline.effectDistance = new(1, 1);
|
||||||
|
|
||||||
RectTransform rect = resizeCursorObj.GetComponent<RectTransform>();
|
RectTransform rect = resizeCursorObj.GetComponent<RectTransform>();
|
||||||
rect.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, 64);
|
rect.SetSizeWithCurrentAnchors(RectTransform.Axis.Horizontal, 64);
|
||||||
|
@ -88,7 +88,7 @@ namespace UnityExplorer.UI
|
|||||||
|
|
||||||
DisplayManager.Init();
|
DisplayManager.Init();
|
||||||
|
|
||||||
var display = DisplayManager.ActiveDisplay;
|
Display display = DisplayManager.ActiveDisplay;
|
||||||
lastScreenWidth = display.renderingWidth;
|
lastScreenWidth = display.renderingWidth;
|
||||||
lastScreenHeight = display.renderingHeight;
|
lastScreenHeight = display.renderingHeight;
|
||||||
|
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
using HarmonyLib;
|
using HarmonyLib;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using UniverseLib;
|
using UniverseLib;
|
||||||
using UniverseLib.UI;
|
using UniverseLib.UI;
|
||||||
using UniverseLib.UI.Models;
|
using UniverseLib.UI.Models;
|
||||||
@ -67,7 +68,17 @@ namespace UnityExplorer.UI.Widgets.AutoComplete
|
|||||||
{
|
{
|
||||||
allowedTypes = new();
|
allowedTypes = new();
|
||||||
foreach (var entry in ReflectionUtility.AllTypes)
|
foreach (var entry in ReflectionUtility.AllTypes)
|
||||||
allowedTypes.Add(entry.Value);
|
{
|
||||||
|
// skip <PrivateImplementationDetails> and <AnonymousClass> classes
|
||||||
|
var type = entry.Value;
|
||||||
|
if (type.FullName.Contains("PrivateImplementationDetails")
|
||||||
|
|| type.FullName.Contains("DisplayClass")
|
||||||
|
|| type.FullName.Contains('<'))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
allowedTypes.Add(type);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ namespace UnityExplorer.UI.Widgets
|
|||||||
{
|
{
|
||||||
public TransformTree Tree { get; }
|
public TransformTree Tree { get; }
|
||||||
public Transform Value { get; private set; }
|
public Transform Value { get; private set; }
|
||||||
public int InstanceID { get; private set; }
|
public int InstanceID { get; }
|
||||||
public CachedTransform Parent { get; internal set; }
|
public CachedTransform Parent { get; internal set; }
|
||||||
|
|
||||||
public int Depth { get; internal set; }
|
public int Depth { get; internal set; }
|
||||||
@ -23,10 +23,11 @@ namespace UnityExplorer.UI.Widgets
|
|||||||
|
|
||||||
public CachedTransform(TransformTree tree, Transform transform, int depth, CachedTransform parent = null)
|
public CachedTransform(TransformTree tree, Transform transform, int depth, CachedTransform parent = null)
|
||||||
{
|
{
|
||||||
|
InstanceID = transform.GetInstanceID();
|
||||||
|
|
||||||
Tree = tree;
|
Tree = tree;
|
||||||
Value = transform;
|
Value = transform;
|
||||||
Parent = parent;
|
Parent = parent;
|
||||||
InstanceID = transform.GetInstanceID();
|
|
||||||
SiblingIndex = transform.GetSiblingIndex();
|
SiblingIndex = transform.GetSiblingIndex();
|
||||||
Update(transform, depth);
|
Update(transform, depth);
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,6 @@ namespace UnityExplorer.UI.Widgets
|
|||||||
public Action<GameObject> OnGameObjectClicked;
|
public Action<GameObject> OnGameObjectClicked;
|
||||||
|
|
||||||
public CachedTransform cachedTransform;
|
public CachedTransform cachedTransform;
|
||||||
public int cellIndex;
|
|
||||||
|
|
||||||
public GameObject UIRoot { get; set; }
|
public GameObject UIRoot { get; set; }
|
||||||
public RectTransform Rect { get; set; }
|
public RectTransform Rect { get; set; }
|
||||||
@ -53,7 +52,7 @@ namespace UnityExplorer.UI.Widgets
|
|||||||
UIRoot.SetActive(false);
|
UIRoot.SetActive(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ConfigureCell(CachedTransform cached, int cellIndex)
|
public void ConfigureCell(CachedTransform cached)
|
||||||
{
|
{
|
||||||
if (cached == null)
|
if (cached == null)
|
||||||
{
|
{
|
||||||
@ -64,7 +63,6 @@ namespace UnityExplorer.UI.Widgets
|
|||||||
if (!Enabled)
|
if (!Enabled)
|
||||||
Enable();
|
Enable();
|
||||||
|
|
||||||
this.cellIndex = cellIndex;
|
|
||||||
cachedTransform = cached;
|
cachedTransform = cached;
|
||||||
|
|
||||||
spacer.minWidth = cached.Depth * 15;
|
spacer.minWidth = cached.Depth * 15;
|
||||||
@ -153,7 +151,7 @@ namespace UnityExplorer.UI.Widgets
|
|||||||
Rect.sizeDelta = new Vector2(25, 25);
|
Rect.sizeDelta = new Vector2(25, 25);
|
||||||
UIFactory.SetLayoutElement(UIRoot, minWidth: 100, flexibleWidth: 9999, minHeight: 25, flexibleHeight: 0);
|
UIFactory.SetLayoutElement(UIRoot, minWidth: 100, flexibleWidth: 9999, minHeight: 25, flexibleHeight: 0);
|
||||||
|
|
||||||
var spacerObj = UIFactory.CreateUIObject("Spacer", UIRoot, new Vector2(0, 0));
|
GameObject spacerObj = UIFactory.CreateUIObject("Spacer", UIRoot, new Vector2(0, 0));
|
||||||
UIFactory.SetLayoutElement(spacerObj, minWidth: 0, flexibleWidth: 0, minHeight: 0, flexibleHeight: 0);
|
UIFactory.SetLayoutElement(spacerObj, minWidth: 0, flexibleWidth: 0, minHeight: 0, flexibleHeight: 0);
|
||||||
this.spacer = spacerObj.GetComponent<LayoutElement>();
|
this.spacer = spacerObj.GetComponent<LayoutElement>();
|
||||||
|
|
||||||
@ -164,15 +162,20 @@ namespace UnityExplorer.UI.Widgets
|
|||||||
|
|
||||||
// Enabled toggle
|
// Enabled toggle
|
||||||
|
|
||||||
var toggleObj = UIFactory.CreateToggle(UIRoot, "BehaviourToggle", out EnabledToggle, out var behavText, default, 17, 17);
|
GameObject toggleObj = UIFactory.CreateToggle(UIRoot, "BehaviourToggle", out EnabledToggle, out Text behavText, default, 17, 17);
|
||||||
UIFactory.SetLayoutElement(toggleObj, minHeight: 17, flexibleHeight: 0, minWidth: 17);
|
UIFactory.SetLayoutElement(toggleObj, minHeight: 17, flexibleHeight: 0, minWidth: 17);
|
||||||
EnabledToggle.onValueChanged.AddListener(OnEnableClicked);
|
EnabledToggle.onValueChanged.AddListener(OnEnableClicked);
|
||||||
|
|
||||||
// Name button
|
// Name button
|
||||||
|
|
||||||
NameButton = UIFactory.CreateButton(this.UIRoot, "NameButton", "Name", null);
|
GameObject nameBtnHolder = UIFactory.CreateHorizontalGroup(this.UIRoot, "NameButtonHolder",
|
||||||
|
false, false, true, true, childAlignment: TextAnchor.MiddleLeft);
|
||||||
|
UIFactory.SetLayoutElement(nameBtnHolder, flexibleWidth: 9999, minHeight: 25, flexibleHeight: 0);
|
||||||
|
nameBtnHolder.AddComponent<Mask>().showMaskGraphic = false;
|
||||||
|
|
||||||
|
NameButton = UIFactory.CreateButton(nameBtnHolder, "NameButton", "Name", null);
|
||||||
UIFactory.SetLayoutElement(NameButton.Component.gameObject, flexibleWidth: 9999, minHeight: 25, flexibleHeight: 0);
|
UIFactory.SetLayoutElement(NameButton.Component.gameObject, flexibleWidth: 9999, minHeight: 25, flexibleHeight: 0);
|
||||||
var nameLabel = NameButton.Component.GetComponentInChildren<Text>();
|
Text nameLabel = NameButton.Component.GetComponentInChildren<Text>();
|
||||||
nameLabel.horizontalOverflow = HorizontalWrapMode.Overflow;
|
nameLabel.horizontalOverflow = HorizontalWrapMode.Overflow;
|
||||||
nameLabel.alignment = TextAnchor.MiddleLeft;
|
nameLabel.alignment = TextAnchor.MiddleLeft;
|
||||||
|
|
||||||
@ -181,7 +184,7 @@ namespace UnityExplorer.UI.Widgets
|
|||||||
SiblingIndex = UIFactory.CreateInputField(this.UIRoot, "SiblingIndexInput", string.Empty);
|
SiblingIndex = UIFactory.CreateInputField(this.UIRoot, "SiblingIndexInput", string.Empty);
|
||||||
SiblingIndex.Component.textComponent.fontSize = 11;
|
SiblingIndex.Component.textComponent.fontSize = 11;
|
||||||
SiblingIndex.Component.textComponent.alignment = TextAnchor.MiddleRight;
|
SiblingIndex.Component.textComponent.alignment = TextAnchor.MiddleRight;
|
||||||
var siblingImage = SiblingIndex.GameObject.GetComponent<Image>();
|
Image siblingImage = SiblingIndex.GameObject.GetComponent<Image>();
|
||||||
siblingImage.color = new(0f, 0f, 0f, 0.25f);
|
siblingImage.color = new(0f, 0f, 0f, 0.25f);
|
||||||
UIFactory.SetLayoutElement(SiblingIndex.GameObject, 35, 20, 0, 0);
|
UIFactory.SetLayoutElement(SiblingIndex.GameObject, 35, 20, 0, 0);
|
||||||
SiblingIndex.Component.GetOnEndEdit().AddListener(OnSiblingIndexEndEdit);
|
SiblingIndex.Component.GetOnEndEdit().AddListener(OnSiblingIndexEndEdit);
|
||||||
|
@ -22,8 +22,8 @@ namespace UnityExplorer.UI.Widgets
|
|||||||
// - Remove(object)
|
// - Remove(object)
|
||||||
// - set_Item[object]
|
// - set_Item[object]
|
||||||
// These two methods have extremely bad performance due to using IndexOfKey(), which iterates the whole dictionary.
|
// These two methods have extremely bad performance due to using IndexOfKey(), which iterates the whole dictionary.
|
||||||
// Currently we do not use either of these methods, so everything should be constant time hash lookups.
|
// Currently we do not use either of these methods, so everything should be constant time lookups.
|
||||||
// We DO make use of get_Item[object], get_Item[index], Add, Insert and RemoveAt, which OrderedDictionary perfectly meets our needs for.
|
// We DO make use of get_Item[object], get_Item[index], Add, Insert, Contains and RemoveAt, which OrderedDictionary meets our needs for.
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Key: UnityEngine.Transform instance ID<br/>
|
/// Key: UnityEngine.Transform instance ID<br/>
|
||||||
/// Value: CachedTransform
|
/// Value: CachedTransform
|
||||||
@ -182,27 +182,27 @@ namespace UnityExplorer.UI.Widgets
|
|||||||
traversedThisFrame.Reset();
|
traversedThisFrame.Reset();
|
||||||
traversedThisFrame.Start();
|
traversedThisFrame.Start();
|
||||||
|
|
||||||
IEnumerable<GameObject> rootObjects = GetRootEntriesMethod.Invoke();
|
refreshCoroutine = RuntimeHelper.StartCoroutine(RefreshCoroutine(andRefreshUI, jumpToTop, oneShot));
|
||||||
|
|
||||||
refreshCoroutine = RuntimeHelper.StartCoroutine(RefreshCoroutine(rootObjects, andRefreshUI, jumpToTop, oneShot));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Coroutine for batched updates, max 2000 gameobjects per frame so FPS doesn't get tanked when there is like 100k gameobjects.
|
IEnumerator RefreshCoroutine(bool andRefreshUI, bool jumpToTop, bool oneShot)
|
||||||
// if "oneShot", then this will NOT be batched (if we need an immediate full update).
|
|
||||||
IEnumerator RefreshCoroutine(IEnumerable<GameObject> rootObjects, bool andRefreshUI, bool jumpToTop, bool oneShot)
|
|
||||||
{
|
{
|
||||||
|
// Instead of doing string.IsNullOrEmpty(CurrentFilter) many times, let's just do it once per update.
|
||||||
|
bool filtering = Filtering;
|
||||||
|
|
||||||
|
IEnumerable<GameObject> rootObjects = GetRootEntriesMethod();
|
||||||
foreach (var gameObj in rootObjects)
|
foreach (var gameObj in rootObjects)
|
||||||
{
|
{
|
||||||
if (gameObj)
|
if (!gameObj)
|
||||||
{
|
continue;
|
||||||
var enumerator = Traverse(gameObj.transform, null, 0, oneShot);
|
|
||||||
|
IEnumerator enumerator = Traverse(gameObj.transform, null, 0, oneShot, filtering);
|
||||||
while (enumerator.MoveNext())
|
while (enumerator.MoveNext())
|
||||||
{
|
{
|
||||||
if (!oneShot)
|
if (!oneShot)
|
||||||
yield return enumerator.Current;
|
yield return enumerator.Current;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// Prune displayed transforms that we didnt visit in that traverse
|
// Prune displayed transforms that we didnt visit in that traverse
|
||||||
for (int i = cachedTransforms.Count - 1; i >= 0; i--)
|
for (int i = cachedTransforms.Count - 1; i >= 0; i--)
|
||||||
@ -224,7 +224,7 @@ namespace UnityExplorer.UI.Widgets
|
|||||||
|
|
||||||
// Recursive method to check a Transform and its children (if expanded).
|
// Recursive method to check a Transform and its children (if expanded).
|
||||||
// Parent and depth can be null/default.
|
// Parent and depth can be null/default.
|
||||||
private IEnumerator Traverse(Transform transform, CachedTransform parent, int depth, bool oneShot)
|
private IEnumerator Traverse(Transform transform, CachedTransform parent, int depth, bool oneShot, bool filtering)
|
||||||
{
|
{
|
||||||
// Let's only tank 2ms of each frame (60->53fps)
|
// Let's only tank 2ms of each frame (60->53fps)
|
||||||
if (traversedThisFrame.ElapsedMilliseconds > 2)
|
if (traversedThisFrame.ElapsedMilliseconds > 2)
|
||||||
@ -236,20 +236,19 @@ namespace UnityExplorer.UI.Widgets
|
|||||||
|
|
||||||
int instanceID = transform.GetInstanceID();
|
int instanceID = transform.GetInstanceID();
|
||||||
|
|
||||||
|
// Unlikely, but since this method is async it could theoretically happen in extremely rare circumstances
|
||||||
if (visited.Contains(instanceID))
|
if (visited.Contains(instanceID))
|
||||||
yield break;
|
yield break;
|
||||||
|
|
||||||
if (Filtering)
|
if (filtering)
|
||||||
{
|
{
|
||||||
if (!FilterHierarchy(transform))
|
if (!FilterHierarchy(transform))
|
||||||
yield break;
|
yield break;
|
||||||
|
|
||||||
visited.Add(instanceID);
|
|
||||||
|
|
||||||
if (!autoExpandedIDs.Contains(instanceID))
|
if (!autoExpandedIDs.Contains(instanceID))
|
||||||
autoExpandedIDs.Add(instanceID);
|
autoExpandedIDs.Add(instanceID);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
visited.Add(instanceID);
|
visited.Add(instanceID);
|
||||||
|
|
||||||
CachedTransform cached;
|
CachedTransform cached;
|
||||||
@ -286,9 +285,11 @@ namespace UnityExplorer.UI.Widgets
|
|||||||
|
|
||||||
if (IsTransformExpanded(instanceID) && cached.Value.childCount > 0)
|
if (IsTransformExpanded(instanceID) && cached.Value.childCount > 0)
|
||||||
{
|
{
|
||||||
|
ExplorerCore.Log($"Traversing expanded transform {cached.Value.name} ({cached.InstanceID})");
|
||||||
|
|
||||||
for (int i = 0; i < transform.childCount; i++)
|
for (int i = 0; i < transform.childCount; i++)
|
||||||
{
|
{
|
||||||
var enumerator = Traverse(transform.GetChild(i), cached, depth + 1, oneShot);
|
var enumerator = Traverse(transform.GetChild(i), cached, depth + 1, oneShot, filtering);
|
||||||
while (enumerator.MoveNext())
|
while (enumerator.MoveNext())
|
||||||
{
|
{
|
||||||
if (!oneShot)
|
if (!oneShot)
|
||||||
@ -317,7 +318,7 @@ namespace UnityExplorer.UI.Widgets
|
|||||||
{
|
{
|
||||||
if (index < cachedTransforms.Count)
|
if (index < cachedTransforms.Count)
|
||||||
{
|
{
|
||||||
cell.ConfigureCell((CachedTransform)cachedTransforms[index], index);
|
cell.ConfigureCell((CachedTransform)cachedTransforms[index]);
|
||||||
if (Filtering)
|
if (Filtering)
|
||||||
{
|
{
|
||||||
if (cell.cachedTransform.Name.ContainsIgnoreCase(currentFilter))
|
if (cell.cachedTransform.Name.ContainsIgnoreCase(currentFilter))
|
||||||
@ -345,6 +346,8 @@ namespace UnityExplorer.UI.Widgets
|
|||||||
|
|
||||||
public void OnCellExpandToggled(CachedTransform cache)
|
public void OnCellExpandToggled(CachedTransform cache)
|
||||||
{
|
{
|
||||||
|
ExplorerCore.Log($"OnCellExpandToggled: {cache.Value.name} ({cache.InstanceID})");
|
||||||
|
|
||||||
var instanceID = cache.InstanceID;
|
var instanceID = cache.InstanceID;
|
||||||
if (expandedInstanceIDs.Contains(instanceID))
|
if (expandedInstanceIDs.Contains(instanceID))
|
||||||
expandedInstanceIDs.Remove(instanceID);
|
expandedInstanceIDs.Remove(instanceID);
|
||||||
|
409
src/UI/Widgets/UnityObjects/AudioClipWidget.cs
Normal file
409
src/UI/Widgets/UnityObjects/AudioClipWidget.cs
Normal file
@ -0,0 +1,409 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.UI;
|
||||||
|
using UnityExplorer.Config;
|
||||||
|
using UnityExplorer.Inspectors;
|
||||||
|
using UniverseLib;
|
||||||
|
using UniverseLib.UI;
|
||||||
|
using UniverseLib.UI.Models;
|
||||||
|
using UniverseLib.UI.ObjectPool;
|
||||||
|
using UniverseLib.Utility;
|
||||||
|
|
||||||
|
namespace UnityExplorer.UI.Widgets
|
||||||
|
{
|
||||||
|
public class AudioClipWidget : UnityObjectWidget
|
||||||
|
{
|
||||||
|
static GameObject AudioPlayerObject;
|
||||||
|
static AudioSource Source;
|
||||||
|
static AudioClipWidget CurrentlyPlaying;
|
||||||
|
static Coroutine CurrentlyPlayingCoroutine;
|
||||||
|
static readonly string zeroLengthString = GetLengthString(0f);
|
||||||
|
|
||||||
|
public AudioClip RefAudioClip;
|
||||||
|
private string fullLengthText;
|
||||||
|
|
||||||
|
private ButtonRef toggleButton;
|
||||||
|
private bool audioPlayerWanted;
|
||||||
|
|
||||||
|
private GameObject audioPlayerRoot;
|
||||||
|
private ButtonRef playStopButton;
|
||||||
|
private Text progressLabel;
|
||||||
|
private GameObject saveObjectRow;
|
||||||
|
private InputFieldRef savePathInput;
|
||||||
|
private GameObject cantSaveRow;
|
||||||
|
|
||||||
|
public override void OnBorrowed(object target, Type targetType, ReflectionInspector inspector)
|
||||||
|
{
|
||||||
|
base.OnBorrowed(target, targetType, inspector);
|
||||||
|
|
||||||
|
this.audioPlayerRoot.transform.SetParent(inspector.UIRoot.transform);
|
||||||
|
this.audioPlayerRoot.transform.SetSiblingIndex(inspector.UIRoot.transform.childCount - 2);
|
||||||
|
|
||||||
|
RefAudioClip = target.TryCast<AudioClip>();
|
||||||
|
this.fullLengthText = GetLengthString(RefAudioClip.length);
|
||||||
|
|
||||||
|
if (RefAudioClip.loadType == AudioClipLoadType.DecompressOnLoad)
|
||||||
|
{
|
||||||
|
cantSaveRow.SetActive(false);
|
||||||
|
saveObjectRow.SetActive(true);
|
||||||
|
SetDefaultSavePath();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
cantSaveRow.SetActive(true);
|
||||||
|
saveObjectRow.SetActive(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
ResetProgressLabel();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnReturnToPool()
|
||||||
|
{
|
||||||
|
RefAudioClip = null;
|
||||||
|
|
||||||
|
if (audioPlayerWanted)
|
||||||
|
ToggleAudioWidget();
|
||||||
|
|
||||||
|
if (CurrentlyPlaying == this)
|
||||||
|
StopClip();
|
||||||
|
|
||||||
|
this.audioPlayerRoot.transform.SetParent(Pool<AudioClipWidget>.Instance.InactiveHolder.transform);
|
||||||
|
|
||||||
|
base.OnReturnToPool();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ToggleAudioWidget()
|
||||||
|
{
|
||||||
|
if (audioPlayerWanted)
|
||||||
|
{
|
||||||
|
audioPlayerWanted = false;
|
||||||
|
|
||||||
|
toggleButton.ButtonText.text = "Show Player";
|
||||||
|
audioPlayerRoot.SetActive(false);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
audioPlayerWanted = true;
|
||||||
|
|
||||||
|
toggleButton.ButtonText.text = "Hide Player";
|
||||||
|
audioPlayerRoot.SetActive(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetDefaultSavePath()
|
||||||
|
{
|
||||||
|
string name = RefAudioClip.name;
|
||||||
|
if (string.IsNullOrEmpty(name))
|
||||||
|
name = "untitled";
|
||||||
|
savePathInput.Text = Path.Combine(ConfigManager.Default_Output_Path.Value, $"{name}.wav");
|
||||||
|
}
|
||||||
|
|
||||||
|
static string GetLengthString(float seconds)
|
||||||
|
{
|
||||||
|
TimeSpan ts = TimeSpan.FromSeconds(seconds);
|
||||||
|
|
||||||
|
StringBuilder sb = new();
|
||||||
|
|
||||||
|
if (ts.Hours > 0)
|
||||||
|
sb.Append($"{ts.Hours}:");
|
||||||
|
|
||||||
|
sb.Append($"{ts.Minutes:00}:");
|
||||||
|
sb.Append($"{ts.Seconds:00}:");
|
||||||
|
sb.Append($"{ts.Milliseconds:000}");
|
||||||
|
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ResetProgressLabel()
|
||||||
|
{
|
||||||
|
this.progressLabel.text = $"{zeroLengthString} / {fullLengthText}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnPlayStopClicked()
|
||||||
|
{
|
||||||
|
SetupAudioPlayer();
|
||||||
|
|
||||||
|
if (CurrentlyPlaying == this)
|
||||||
|
{
|
||||||
|
// we are playing a clip. stop it.
|
||||||
|
StopClip();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// If something else is playing a clip, stop that.
|
||||||
|
if (CurrentlyPlaying != null)
|
||||||
|
CurrentlyPlaying.StopClip();
|
||||||
|
|
||||||
|
// we want to start playing a clip.
|
||||||
|
CurrentlyPlayingCoroutine = RuntimeHelper.StartCoroutine(PlayClipCoroutine());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void SetupAudioPlayer()
|
||||||
|
{
|
||||||
|
if (AudioPlayerObject)
|
||||||
|
return;
|
||||||
|
|
||||||
|
AudioPlayerObject = new GameObject("UnityExplorer.AudioPlayer");
|
||||||
|
UnityEngine.Object.DontDestroyOnLoad(AudioPlayerObject);
|
||||||
|
AudioPlayerObject.hideFlags = HideFlags.HideAndDontSave;
|
||||||
|
AudioPlayerObject.transform.position = new(int.MinValue, int.MinValue); // move it as far away as possible
|
||||||
|
#if CPP
|
||||||
|
Source = AudioPlayerObject.AddComponent(UnhollowerRuntimeLib.Il2CppType.Of<AudioSource>()).TryCast<AudioSource>();
|
||||||
|
#else
|
||||||
|
Source = AudioPlayerObject.AddComponent<AudioSource>();
|
||||||
|
#endif
|
||||||
|
AudioPlayerObject.AddComponent<AudioListener>();
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerator PlayClipCoroutine()
|
||||||
|
{
|
||||||
|
playStopButton.ButtonText.text = "Stop Clip";
|
||||||
|
CurrentlyPlaying = this;
|
||||||
|
Source.clip = this.RefAudioClip;
|
||||||
|
Source.Play();
|
||||||
|
|
||||||
|
while (Source.isPlaying)
|
||||||
|
{
|
||||||
|
progressLabel.text = $"{GetLengthString(Source.time)} / {fullLengthText}";
|
||||||
|
yield return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
CurrentlyPlayingCoroutine = null;
|
||||||
|
StopClip();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void StopClip()
|
||||||
|
{
|
||||||
|
if (CurrentlyPlayingCoroutine != null)
|
||||||
|
RuntimeHelper.StopCoroutine(CurrentlyPlayingCoroutine);
|
||||||
|
|
||||||
|
Source.Stop();
|
||||||
|
CurrentlyPlaying = null;
|
||||||
|
CurrentlyPlayingCoroutine = null;
|
||||||
|
playStopButton.ButtonText.text = "Play Clip";
|
||||||
|
|
||||||
|
ResetProgressLabel();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnSaveClipClicked()
|
||||||
|
{
|
||||||
|
if (!RefAudioClip)
|
||||||
|
{
|
||||||
|
ExplorerCore.LogWarning("AudioClip is null, maybe it was destroyed?");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(savePathInput.Text))
|
||||||
|
{
|
||||||
|
ExplorerCore.LogWarning("Save path cannot be empty!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
string path = savePathInput.Text;
|
||||||
|
if (!path.EndsWith(".wav", StringComparison.InvariantCultureIgnoreCase))
|
||||||
|
path += ".wav";
|
||||||
|
|
||||||
|
path = IOUtility.EnsureValidFilePath(path);
|
||||||
|
|
||||||
|
if (File.Exists(path))
|
||||||
|
File.Delete(path);
|
||||||
|
|
||||||
|
SavWav.Save(RefAudioClip, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override GameObject CreateContent(GameObject uiRoot)
|
||||||
|
{
|
||||||
|
GameObject ret = base.CreateContent(uiRoot);
|
||||||
|
|
||||||
|
// Toggle Button
|
||||||
|
|
||||||
|
toggleButton = UIFactory.CreateButton(UIRoot, "AudioWidgetToggleButton", "Show Player", new Color(0.2f, 0.3f, 0.2f));
|
||||||
|
toggleButton.Transform.SetSiblingIndex(0);
|
||||||
|
UIFactory.SetLayoutElement(toggleButton.Component.gameObject, minHeight: 25, minWidth: 170);
|
||||||
|
toggleButton.OnClick += ToggleAudioWidget;
|
||||||
|
|
||||||
|
// Actual widget
|
||||||
|
|
||||||
|
audioPlayerRoot = UIFactory.CreateVerticalGroup(uiRoot, "AudioWidget", false, false, true, true, spacing: 5);
|
||||||
|
UIFactory.SetLayoutElement(audioPlayerRoot, flexibleWidth: 9999, flexibleHeight: 50);
|
||||||
|
audioPlayerRoot.SetActive(false);
|
||||||
|
|
||||||
|
// Player
|
||||||
|
|
||||||
|
GameObject playerRow = UIFactory.CreateHorizontalGroup(audioPlayerRoot, "PlayerWidget", false, false, true, true,
|
||||||
|
spacing: 5, padding: new() { x = 3f, w = 3f, y = 3f, z = 3f });
|
||||||
|
|
||||||
|
playStopButton = UIFactory.CreateButton(playerRow, "PlayerButton", "Play", normalColor: new(0.2f, 0.4f, 0.2f));
|
||||||
|
playStopButton.OnClick += OnPlayStopClicked;
|
||||||
|
UIFactory.SetLayoutElement(playStopButton.GameObject, minWidth: 60, minHeight: 25);
|
||||||
|
|
||||||
|
progressLabel = UIFactory.CreateLabel(playerRow, "ProgressLabel", "0 / 0");
|
||||||
|
UIFactory.SetLayoutElement(progressLabel.gameObject, flexibleWidth: 9999, minHeight: 25);
|
||||||
|
|
||||||
|
ResetProgressLabel();
|
||||||
|
|
||||||
|
// Save helper
|
||||||
|
|
||||||
|
saveObjectRow = UIFactory.CreateHorizontalGroup(audioPlayerRoot, "SaveRow", false, false, true, true, 2, new Vector4(2, 2, 2, 2),
|
||||||
|
new Color(0.1f, 0.1f, 0.1f));
|
||||||
|
|
||||||
|
ButtonRef saveBtn = UIFactory.CreateButton(saveObjectRow, "SaveButton", "Save .WAV", new Color(0.2f, 0.25f, 0.2f));
|
||||||
|
UIFactory.SetLayoutElement(saveBtn.Component.gameObject, minHeight: 25, minWidth: 100, flexibleWidth: 0);
|
||||||
|
saveBtn.OnClick += OnSaveClipClicked;
|
||||||
|
|
||||||
|
savePathInput = UIFactory.CreateInputField(saveObjectRow, "SaveInput", "...");
|
||||||
|
UIFactory.SetLayoutElement(savePathInput.UIRoot, minHeight: 25, minWidth: 100, flexibleWidth: 9999);
|
||||||
|
|
||||||
|
// cant save label
|
||||||
|
cantSaveRow = UIFactory.CreateHorizontalGroup(audioPlayerRoot, "CantSaveRow", true, true, true, true);
|
||||||
|
UIFactory.SetLayoutElement(cantSaveRow, minHeight: 25, flexibleWidth: 9999);
|
||||||
|
UIFactory.CreateLabel(
|
||||||
|
cantSaveRow,
|
||||||
|
"CantSaveLabel",
|
||||||
|
"Cannot save this AudioClip as the data is compressed or streamed. Try a tool such as AssetRipper to unpack it.",
|
||||||
|
color: Color.grey);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#region SavWav
|
||||||
|
|
||||||
|
// Copyright (c) 2012 Calvin Rien
|
||||||
|
// http://the.darktable.com
|
||||||
|
//
|
||||||
|
// This software is provided 'as-is', without any express or implied warranty. In
|
||||||
|
// no event will the authors be held liable for any damages arising from the use
|
||||||
|
// of this software.
|
||||||
|
//
|
||||||
|
// Permission is granted to anyone to use this software for any purpose,
|
||||||
|
// including commercial applications, and to alter it and redistribute it freely,
|
||||||
|
// subject to the following restrictions:
|
||||||
|
//
|
||||||
|
// 1. The origin of this software must not be misrepresented; you must not claim
|
||||||
|
// that you wrote the original software. If you use this software in a product,
|
||||||
|
// an acknowledgment in the product documentation would be appreciated but is not
|
||||||
|
// required.
|
||||||
|
//
|
||||||
|
// 2. Altered source versions must be plainly marked as such, and must not be
|
||||||
|
// misrepresented as being the original software.
|
||||||
|
//
|
||||||
|
// 3. This notice may not be removed or altered from any source distribution.
|
||||||
|
//
|
||||||
|
// =============================================================================
|
||||||
|
//
|
||||||
|
// derived from Gregorio Zanon's script
|
||||||
|
// http://forum.unity3d.com/threads/119295-Writing-AudioListener.GetOutputData-to-wav-problem?p=806734&viewfull=1#post806734
|
||||||
|
|
||||||
|
public static class SavWav
|
||||||
|
{
|
||||||
|
public const int HEADER_SIZE = 44;
|
||||||
|
public const float RESCALE_FACTOR = 32767; // to convert float to Int16
|
||||||
|
|
||||||
|
public static void Save(AudioClip clip, string filepath)
|
||||||
|
{
|
||||||
|
using FileStream fileStream = CreateEmpty(filepath);
|
||||||
|
|
||||||
|
ConvertAndWrite(fileStream, clip);
|
||||||
|
WriteHeader(fileStream, clip);
|
||||||
|
}
|
||||||
|
|
||||||
|
static FileStream CreateEmpty(string filepath)
|
||||||
|
{
|
||||||
|
FileStream fileStream = new(filepath, FileMode.Create);
|
||||||
|
byte emptyByte = default;
|
||||||
|
|
||||||
|
for (int i = 0; i < HEADER_SIZE; i++) //preparing the header
|
||||||
|
fileStream.WriteByte(emptyByte);
|
||||||
|
|
||||||
|
return fileStream;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ConvertAndWrite(FileStream fileStream, AudioClip clip)
|
||||||
|
{
|
||||||
|
#if CPP
|
||||||
|
UnhollowerBaseLib.Il2CppStructArray<float> samples = new float[clip.samples * clip.channels];
|
||||||
|
AudioClip.GetData(clip, samples, clip.samples, 0);
|
||||||
|
#else
|
||||||
|
float[] samples = new float[clip.samples * clip.channels];
|
||||||
|
clip.GetData(samples, 0);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int len = samples.Length;
|
||||||
|
|
||||||
|
// converting in 2 float[] steps to Int16[], then Int16[] to Byte[]
|
||||||
|
short[] intData = new short[len];
|
||||||
|
|
||||||
|
// bytesData array is twice the size of dataSource array because a float converted in Int16 is 2 bytes.
|
||||||
|
byte[] bytesData = new byte[len * 2];
|
||||||
|
|
||||||
|
for (int i = 0; i < len; i++)
|
||||||
|
{
|
||||||
|
intData[i] = (short)(samples[i] * RESCALE_FACTOR);
|
||||||
|
byte[] byteArr = BitConverter.GetBytes(intData[i]);
|
||||||
|
byteArr.CopyTo(bytesData, i * 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
fileStream.Write(bytesData, 0, bytesData.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void WriteHeader(FileStream stream, AudioClip clip)
|
||||||
|
{
|
||||||
|
int hz = clip.frequency;
|
||||||
|
int channels = clip.channels;
|
||||||
|
int samples = clip.samples;
|
||||||
|
|
||||||
|
stream.Seek(0, SeekOrigin.Begin);
|
||||||
|
|
||||||
|
byte[] riff = Encoding.UTF8.GetBytes("RIFF");
|
||||||
|
stream.Write(riff, 0, 4);
|
||||||
|
|
||||||
|
byte[] chunkSize = BitConverter.GetBytes(stream.Length - 8);
|
||||||
|
stream.Write(chunkSize, 0, 4);
|
||||||
|
|
||||||
|
byte[] wave = Encoding.ASCII.GetBytes("WAVE");
|
||||||
|
stream.Write(wave, 0, 4);
|
||||||
|
|
||||||
|
byte[] fmt = Encoding.ASCII.GetBytes("fmt ");
|
||||||
|
stream.Write(fmt, 0, 4);
|
||||||
|
|
||||||
|
byte[] subChunk1 = BitConverter.GetBytes(16);
|
||||||
|
stream.Write(subChunk1, 0, 4);
|
||||||
|
|
||||||
|
byte[] audioFormat = BitConverter.GetBytes(1);
|
||||||
|
stream.Write(audioFormat, 0, 2);
|
||||||
|
|
||||||
|
byte[] numChannels = BitConverter.GetBytes(channels);
|
||||||
|
stream.Write(numChannels, 0, 2);
|
||||||
|
|
||||||
|
byte[] sampleRate = BitConverter.GetBytes(hz);
|
||||||
|
stream.Write(sampleRate, 0, 4);
|
||||||
|
|
||||||
|
byte[] byteRate = BitConverter.GetBytes(hz * channels * 2); // sampleRate * bytesPerSample*number of channels, here 44100*2*2
|
||||||
|
stream.Write(byteRate, 0, 4);
|
||||||
|
|
||||||
|
ushort blockAlign = (ushort)(channels * 2);
|
||||||
|
stream.Write(BitConverter.GetBytes(blockAlign), 0, 2);
|
||||||
|
|
||||||
|
ushort bps = 16;
|
||||||
|
byte[] bitsPerSample = BitConverter.GetBytes(bps);
|
||||||
|
stream.Write(bitsPerSample, 0, 2);
|
||||||
|
|
||||||
|
byte[] datastring = Encoding.UTF8.GetBytes("data");
|
||||||
|
stream.Write(datastring, 0, 4);
|
||||||
|
|
||||||
|
byte[] subChunk2 = BitConverter.GetBytes(samples * channels * 2);
|
||||||
|
stream.Write(subChunk2, 0, 4);
|
||||||
|
|
||||||
|
stream.Seek(0, SeekOrigin.Begin);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
}
|
244
src/UI/Widgets/UnityObjects/Texture2DWidget.cs
Normal file
244
src/UI/Widgets/UnityObjects/Texture2DWidget.cs
Normal file
@ -0,0 +1,244 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.UI;
|
||||||
|
using UnityExplorer.Config;
|
||||||
|
using UnityExplorer.Inspectors;
|
||||||
|
using UnityExplorer.UI.Panels;
|
||||||
|
using UniverseLib;
|
||||||
|
using UniverseLib.Runtime;
|
||||||
|
using UniverseLib.UI;
|
||||||
|
using UniverseLib.UI.Models;
|
||||||
|
using UniverseLib.UI.ObjectPool;
|
||||||
|
using UniverseLib.Utility;
|
||||||
|
|
||||||
|
namespace UnityExplorer.UI.Widgets
|
||||||
|
{
|
||||||
|
public class Texture2DWidget : UnityObjectWidget
|
||||||
|
{
|
||||||
|
private Texture2D TextureRef;
|
||||||
|
private float realWidth;
|
||||||
|
private float realHeight;
|
||||||
|
|
||||||
|
private bool textureViewerWanted;
|
||||||
|
private ButtonRef toggleButton;
|
||||||
|
|
||||||
|
private GameObject textureViewerRoot;
|
||||||
|
private InputFieldRef savePathInput;
|
||||||
|
private Image image;
|
||||||
|
private LayoutElement imageLayout;
|
||||||
|
|
||||||
|
public override void OnBorrowed(object target, Type targetType, ReflectionInspector inspector)
|
||||||
|
{
|
||||||
|
base.OnBorrowed(target, targetType, inspector);
|
||||||
|
|
||||||
|
TextureRef = target.TryCast<Texture2D>();
|
||||||
|
|
||||||
|
realWidth = TextureRef.width;
|
||||||
|
realHeight = TextureRef.height;
|
||||||
|
|
||||||
|
if (this.textureViewerRoot)
|
||||||
|
this.textureViewerRoot.transform.SetParent(inspector.UIRoot.transform);
|
||||||
|
|
||||||
|
InspectorPanel.Instance.Dragger.OnFinishResize += OnInspectorFinishResize;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void OnReturnToPool()
|
||||||
|
{
|
||||||
|
InspectorPanel.Instance.Dragger.OnFinishResize -= OnInspectorFinishResize;
|
||||||
|
|
||||||
|
TextureRef = null;
|
||||||
|
|
||||||
|
if (image.sprite)
|
||||||
|
GameObject.Destroy(image.sprite);
|
||||||
|
|
||||||
|
if (textureViewerWanted)
|
||||||
|
ToggleTextureViewer();
|
||||||
|
|
||||||
|
if (this.textureViewerRoot)
|
||||||
|
this.textureViewerRoot.transform.SetParent(Pool<Texture2DWidget>.Instance.InactiveHolder.transform);
|
||||||
|
|
||||||
|
base.OnReturnToPool();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ToggleTextureViewer()
|
||||||
|
{
|
||||||
|
if (textureViewerWanted)
|
||||||
|
{
|
||||||
|
// disable
|
||||||
|
textureViewerWanted = false;
|
||||||
|
textureViewerRoot.SetActive(false);
|
||||||
|
toggleButton.ButtonText.text = "View Texture";
|
||||||
|
|
||||||
|
ParentInspector.mainContentHolder.SetActive(true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// enable
|
||||||
|
if (!image.sprite)
|
||||||
|
SetupTextureViewer();
|
||||||
|
|
||||||
|
SetImageSize();
|
||||||
|
|
||||||
|
textureViewerWanted = true;
|
||||||
|
textureViewerRoot.SetActive(true);
|
||||||
|
toggleButton.ButtonText.text = "Hide Texture";
|
||||||
|
|
||||||
|
ParentInspector.mainContentHolder.gameObject.SetActive(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetupTextureViewer()
|
||||||
|
{
|
||||||
|
if (!this.TextureRef)
|
||||||
|
return;
|
||||||
|
|
||||||
|
string name = TextureRef.name;
|
||||||
|
if (string.IsNullOrEmpty(name))
|
||||||
|
name = "untitled";
|
||||||
|
savePathInput.Text = Path.Combine(ConfigManager.Default_Output_Path.Value, $"{name}.png");
|
||||||
|
|
||||||
|
Sprite sprite = TextureHelper.CreateSprite(TextureRef);
|
||||||
|
image.sprite = sprite;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnInspectorFinishResize(RectTransform _)
|
||||||
|
{
|
||||||
|
SetImageSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetImageSize()
|
||||||
|
{
|
||||||
|
if (!imageLayout)
|
||||||
|
return;
|
||||||
|
|
||||||
|
RuntimeHelper.StartCoroutine(SetImageSizeCoro());
|
||||||
|
}
|
||||||
|
|
||||||
|
IEnumerator SetImageSizeCoro()
|
||||||
|
{
|
||||||
|
// let unity rebuild layout etc
|
||||||
|
yield return null;
|
||||||
|
|
||||||
|
RectTransform imageRect = InspectorPanel.Instance.Rect;
|
||||||
|
|
||||||
|
float rectWidth = imageRect.rect.width - 25;
|
||||||
|
float rectHeight = imageRect.rect.height - 196;
|
||||||
|
|
||||||
|
// If our image is smaller than the viewport, just use 100% scaling
|
||||||
|
if (realWidth < rectWidth && realHeight < rectHeight)
|
||||||
|
{
|
||||||
|
imageLayout.minWidth = realWidth;
|
||||||
|
imageLayout.minHeight = realHeight;
|
||||||
|
}
|
||||||
|
else // we will need to scale down the image to fit
|
||||||
|
{
|
||||||
|
// get the ratio of our viewport dimensions to width and height
|
||||||
|
float viewWidthRatio = (float)((decimal)rectWidth / (decimal)realWidth);
|
||||||
|
float viewHeightRatio = (float)((decimal)rectHeight / (decimal)realHeight);
|
||||||
|
|
||||||
|
// if width needs to be scaled more than height
|
||||||
|
if (viewWidthRatio < viewHeightRatio)
|
||||||
|
{
|
||||||
|
imageLayout.minWidth = realWidth * viewWidthRatio;
|
||||||
|
imageLayout.minHeight = realHeight * viewWidthRatio;
|
||||||
|
}
|
||||||
|
else // if height needs to be scaled more than width
|
||||||
|
{
|
||||||
|
imageLayout.minWidth = realWidth * viewHeightRatio;
|
||||||
|
imageLayout.minHeight = realHeight * viewHeightRatio;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnSaveTextureClicked()
|
||||||
|
{
|
||||||
|
if (!TextureRef)
|
||||||
|
{
|
||||||
|
ExplorerCore.LogWarning("Texture is null, maybe it was destroyed?");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(savePathInput.Text))
|
||||||
|
{
|
||||||
|
ExplorerCore.LogWarning("Save path cannot be empty!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
string path = savePathInput.Text;
|
||||||
|
if (!path.EndsWith(".png", StringComparison.InvariantCultureIgnoreCase))
|
||||||
|
path += ".png";
|
||||||
|
|
||||||
|
path = IOUtility.EnsureValidFilePath(path);
|
||||||
|
|
||||||
|
if (File.Exists(path))
|
||||||
|
File.Delete(path);
|
||||||
|
|
||||||
|
Texture2D tex = TextureRef;
|
||||||
|
if (!TextureHelper.IsReadable(tex))
|
||||||
|
tex = TextureHelper.ForceReadTexture(tex);
|
||||||
|
|
||||||
|
byte[] data = TextureHelper.EncodeToPNG(tex);
|
||||||
|
File.WriteAllBytes(path, data);
|
||||||
|
|
||||||
|
if (tex != TextureRef)
|
||||||
|
{
|
||||||
|
// cleanup temp texture if we had to force-read it.
|
||||||
|
GameObject.Destroy(tex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override GameObject CreateContent(GameObject uiRoot)
|
||||||
|
{
|
||||||
|
GameObject ret = base.CreateContent(uiRoot);
|
||||||
|
|
||||||
|
// Button
|
||||||
|
|
||||||
|
toggleButton = UIFactory.CreateButton(UIRoot, "TextureButton", "View Texture", new Color(0.2f, 0.3f, 0.2f));
|
||||||
|
toggleButton.Transform.SetSiblingIndex(0);
|
||||||
|
UIFactory.SetLayoutElement(toggleButton.Component.gameObject, minHeight: 25, minWidth: 150);
|
||||||
|
toggleButton.OnClick += ToggleTextureViewer;
|
||||||
|
|
||||||
|
// Texture viewer
|
||||||
|
|
||||||
|
textureViewerRoot = UIFactory.CreateVerticalGroup(uiRoot, "TextureViewer", false, false, true, true, 2, new Vector4(5, 5, 5, 5),
|
||||||
|
new Color(0.1f, 0.1f, 0.1f), childAlignment: TextAnchor.UpperLeft);
|
||||||
|
UIFactory.SetLayoutElement(textureViewerRoot, flexibleWidth: 9999, flexibleHeight: 9999);
|
||||||
|
|
||||||
|
// Save helper
|
||||||
|
|
||||||
|
GameObject saveRowObj = UIFactory.CreateHorizontalGroup(textureViewerRoot, "SaveRow", false, false, true, true, 2, new Vector4(2, 2, 2, 2),
|
||||||
|
new Color(0.1f, 0.1f, 0.1f));
|
||||||
|
|
||||||
|
ButtonRef saveBtn = UIFactory.CreateButton(saveRowObj, "SaveButton", "Save .PNG", new Color(0.2f, 0.25f, 0.2f));
|
||||||
|
UIFactory.SetLayoutElement(saveBtn.Component.gameObject, minHeight: 25, minWidth: 100, flexibleWidth: 0);
|
||||||
|
saveBtn.OnClick += OnSaveTextureClicked;
|
||||||
|
|
||||||
|
savePathInput = UIFactory.CreateInputField(saveRowObj, "SaveInput", "...");
|
||||||
|
UIFactory.SetLayoutElement(savePathInput.UIRoot, minHeight: 25, minWidth: 100, flexibleWidth: 9999);
|
||||||
|
|
||||||
|
// Actual texture viewer
|
||||||
|
|
||||||
|
GameObject imageViewport = UIFactory.CreateVerticalGroup(textureViewerRoot, "ImageViewport", false, false, true, true,
|
||||||
|
bgColor: new(1,1,1,0), childAlignment: TextAnchor.MiddleCenter);
|
||||||
|
UIFactory.SetLayoutElement(imageViewport, flexibleWidth: 9999, flexibleHeight: 9999);
|
||||||
|
|
||||||
|
GameObject imageHolder = UIFactory.CreateUIObject("ImageHolder", imageViewport);
|
||||||
|
imageLayout = UIFactory.SetLayoutElement(imageHolder, 1, 1, 0, 0);
|
||||||
|
|
||||||
|
var actualImageObj = UIFactory.CreateUIObject("ActualImage", imageHolder);
|
||||||
|
var actualRect = actualImageObj.GetComponent<RectTransform>();
|
||||||
|
actualRect.anchorMin = new(0, 0);
|
||||||
|
actualRect.anchorMax = new(1, 1);
|
||||||
|
image = actualImageObj.AddComponent<Image>();
|
||||||
|
|
||||||
|
textureViewerRoot.SetActive(false);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
131
src/UI/Widgets/UnityObjects/UnityObjectWidget.cs
Normal file
131
src/UI/Widgets/UnityObjects/UnityObjectWidget.cs
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
using System;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.UI;
|
||||||
|
using UnityExplorer.Inspectors;
|
||||||
|
using UniverseLib;
|
||||||
|
using UniverseLib.UI;
|
||||||
|
using UniverseLib.UI.Models;
|
||||||
|
using UniverseLib.UI.ObjectPool;
|
||||||
|
|
||||||
|
namespace UnityExplorer.UI.Widgets
|
||||||
|
{
|
||||||
|
public class UnityObjectWidget : IPooledObject
|
||||||
|
{
|
||||||
|
public UnityEngine.Object UnityObjectRef;
|
||||||
|
public Component ComponentRef;
|
||||||
|
public ReflectionInspector ParentInspector;
|
||||||
|
|
||||||
|
protected ButtonRef gameObjectButton;
|
||||||
|
protected InputFieldRef nameInput;
|
||||||
|
protected InputFieldRef instanceIdInput;
|
||||||
|
|
||||||
|
// IPooledObject
|
||||||
|
public GameObject UIRoot { get; set; }
|
||||||
|
public float DefaultHeight => -1;
|
||||||
|
|
||||||
|
public static UnityObjectWidget GetUnityWidget(object target, Type targetType, ReflectionInspector inspector)
|
||||||
|
{
|
||||||
|
if (!typeof(UnityEngine.Object).IsAssignableFrom(targetType))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
UnityObjectWidget widget = target switch
|
||||||
|
{
|
||||||
|
Texture2D => Pool<Texture2DWidget>.Borrow(),
|
||||||
|
AudioClip => Pool<AudioClipWidget>.Borrow(),
|
||||||
|
_ => Pool<UnityObjectWidget>.Borrow()
|
||||||
|
};
|
||||||
|
|
||||||
|
widget.OnBorrowed(target, targetType, inspector);
|
||||||
|
|
||||||
|
return widget;
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void OnBorrowed(object target, Type targetType, ReflectionInspector inspector)
|
||||||
|
{
|
||||||
|
this.ParentInspector = inspector ?? throw new ArgumentNullException(nameof(inspector));
|
||||||
|
|
||||||
|
if (!this.UIRoot)
|
||||||
|
CreateContent(inspector.UIRoot);
|
||||||
|
else
|
||||||
|
this.UIRoot.transform.SetParent(inspector.UIRoot.transform);
|
||||||
|
|
||||||
|
this.UIRoot.transform.SetSiblingIndex(inspector.UIRoot.transform.childCount - 2);
|
||||||
|
|
||||||
|
UnityObjectRef = target.TryCast<UnityEngine.Object>();
|
||||||
|
UIRoot.SetActive(true);
|
||||||
|
|
||||||
|
nameInput.Text = UnityObjectRef.name;
|
||||||
|
instanceIdInput.Text = UnityObjectRef.GetInstanceID().ToString();
|
||||||
|
|
||||||
|
if (typeof(Component).IsAssignableFrom(targetType))
|
||||||
|
{
|
||||||
|
ComponentRef = (Component)target.TryCast(typeof(Component));
|
||||||
|
gameObjectButton.Component.gameObject.SetActive(true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
gameObjectButton.Component.gameObject.SetActive(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual void OnReturnToPool()
|
||||||
|
{
|
||||||
|
UnityObjectRef = null;
|
||||||
|
ComponentRef = null;
|
||||||
|
ParentInspector = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update
|
||||||
|
|
||||||
|
public virtual void Update()
|
||||||
|
{
|
||||||
|
if (this.UnityObjectRef)
|
||||||
|
{
|
||||||
|
nameInput.Text = UnityObjectRef.name;
|
||||||
|
ParentInspector.Tab.TabText.text = $"{ParentInspector.currentBaseTabText} \"{UnityObjectRef.name}\"";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UI Listeners
|
||||||
|
|
||||||
|
private void OnGameObjectButtonClicked()
|
||||||
|
{
|
||||||
|
if (!ComponentRef)
|
||||||
|
{
|
||||||
|
ExplorerCore.LogWarning("Component reference is null or destroyed!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
InspectorManager.Inspect(ComponentRef.gameObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
// UI construction
|
||||||
|
|
||||||
|
public virtual GameObject CreateContent(GameObject uiRoot)
|
||||||
|
{
|
||||||
|
UIRoot = UIFactory.CreateUIObject("UnityObjectRow", uiRoot);
|
||||||
|
UIFactory.SetLayoutGroup<HorizontalLayoutGroup>(UIRoot, false, false, true, true, 5);
|
||||||
|
UIFactory.SetLayoutElement(UIRoot, minHeight: 25, flexibleHeight: 0, flexibleWidth: 9999);
|
||||||
|
|
||||||
|
var nameLabel = UIFactory.CreateLabel(UIRoot, "NameLabel", "Name:", TextAnchor.MiddleLeft, Color.grey);
|
||||||
|
UIFactory.SetLayoutElement(nameLabel.gameObject, minHeight: 25, minWidth: 45, flexibleWidth: 0);
|
||||||
|
|
||||||
|
nameInput = UIFactory.CreateInputField(UIRoot, "NameInput", "untitled");
|
||||||
|
UIFactory.SetLayoutElement(nameInput.UIRoot, minHeight: 25, minWidth: 100, flexibleWidth: 1000);
|
||||||
|
nameInput.Component.readOnly = true;
|
||||||
|
|
||||||
|
gameObjectButton = UIFactory.CreateButton(UIRoot, "GameObjectButton", "Inspect GameObject", new Color(0.2f, 0.2f, 0.2f));
|
||||||
|
UIFactory.SetLayoutElement(gameObjectButton.Component.gameObject, minHeight: 25, minWidth: 160);
|
||||||
|
gameObjectButton.OnClick += OnGameObjectButtonClicked;
|
||||||
|
|
||||||
|
var instanceLabel = UIFactory.CreateLabel(UIRoot, "InstanceLabel", "Instance ID:", TextAnchor.MiddleRight, Color.grey);
|
||||||
|
UIFactory.SetLayoutElement(instanceLabel.gameObject, minHeight: 25, minWidth: 100, flexibleWidth: 0);
|
||||||
|
|
||||||
|
instanceIdInput = UIFactory.CreateInputField(UIRoot, "InstanceIDInput", "ERROR");
|
||||||
|
UIFactory.SetLayoutElement(instanceIdInput.UIRoot, minHeight: 25, minWidth: 100, flexibleWidth: 0);
|
||||||
|
instanceIdInput.Component.readOnly = true;
|
||||||
|
|
||||||
|
UIRoot.SetActive(false);
|
||||||
|
|
||||||
|
return UIRoot;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -175,13 +175,13 @@
|
|||||||
<Private>False</Private>
|
<Private>False</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="UniverseLib.Mono">
|
<Reference Include="UniverseLib.Mono">
|
||||||
<HintPath>packages\UniverseLib.1.2.16\lib\net35\UniverseLib.Mono.dll</HintPath>
|
<HintPath>packages\UniverseLib.1.2.18\lib\net35\UniverseLib.Mono.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<!-- Il2Cpp refs -->
|
<!-- Il2Cpp refs -->
|
||||||
<ItemGroup Condition="'$(IsCpp)'=='true'">
|
<ItemGroup Condition="'$(IsCpp)'=='true'">
|
||||||
<Reference Include="UniverseLib.IL2CPP">
|
<Reference Include="UniverseLib.IL2CPP">
|
||||||
<HintPath>packages\UniverseLib.1.2.16\lib\net472\UniverseLib.IL2CPP.dll</HintPath>
|
<HintPath>packages\UniverseLib.1.2.18\lib\net472\UniverseLib.IL2CPP.dll</HintPath>
|
||||||
</Reference>
|
</Reference>
|
||||||
<Reference Include="UnhollowerBaseLib, Version=0.4.22.0, Culture=neutral, processorArchitecture=MSIL">
|
<Reference Include="UnhollowerBaseLib, Version=0.4.22.0, Culture=neutral, processorArchitecture=MSIL">
|
||||||
<HintPath>packages\Il2CppAssemblyUnhollower.BaseLib.0.4.22\lib\net472\UnhollowerBaseLib.dll</HintPath>
|
<HintPath>packages\Il2CppAssemblyUnhollower.BaseLib.0.4.22\lib\net472\UnhollowerBaseLib.dll</HintPath>
|
||||||
@ -199,6 +199,10 @@
|
|||||||
<HintPath>..\lib\unhollowed\UnityEngine.dll</HintPath>
|
<HintPath>..\lib\unhollowed\UnityEngine.dll</HintPath>
|
||||||
<Private>False</Private>
|
<Private>False</Private>
|
||||||
</Reference>
|
</Reference>
|
||||||
|
<Reference Include="UnityEngine.AudioModule">
|
||||||
|
<HintPath>..\lib\unhollowed\UnityEngine.AudioModule.dll</HintPath>
|
||||||
|
<Private>False</Private>
|
||||||
|
</Reference>
|
||||||
<Reference Include="UnityEngine.CoreModule">
|
<Reference Include="UnityEngine.CoreModule">
|
||||||
<HintPath>..\lib\unhollowed\UnityEngine.CoreModule.dll</HintPath>
|
<HintPath>..\lib\unhollowed\UnityEngine.CoreModule.dll</HintPath>
|
||||||
<Private>False</Private>
|
<Private>False</Private>
|
||||||
@ -226,6 +230,7 @@
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="CacheObject\CacheConstructor.cs" />
|
<Compile Include="CacheObject\CacheConstructor.cs" />
|
||||||
|
<Compile Include="CacheObject\CacheMemberFactory.cs" />
|
||||||
<Compile Include="Hooks\HookCell.cs" />
|
<Compile Include="Hooks\HookCell.cs" />
|
||||||
<Compile Include="Hooks\HookInstance.cs" />
|
<Compile Include="Hooks\HookInstance.cs" />
|
||||||
<Compile Include="Hooks\HookManager.cs" />
|
<Compile Include="Hooks\HookManager.cs" />
|
||||||
@ -260,6 +265,8 @@
|
|||||||
<Compile Include="CacheObject\Views\CacheListEntryCell.cs" />
|
<Compile Include="CacheObject\Views\CacheListEntryCell.cs" />
|
||||||
<Compile Include="CacheObject\Views\CacheMemberCell.cs" />
|
<Compile Include="CacheObject\Views\CacheMemberCell.cs" />
|
||||||
<Compile Include="CacheObject\Views\CacheObjectCell.cs" />
|
<Compile Include="CacheObject\Views\CacheObjectCell.cs" />
|
||||||
|
<Compile Include="Loader\Standalone\Editor\ExplorerEditorBehaviour.cs" />
|
||||||
|
<Compile Include="Loader\Standalone\Editor\ExplorerEditorLoader.cs" />
|
||||||
<Compile Include="Runtime\UnityCrashPrevention.cs" />
|
<Compile Include="Runtime\UnityCrashPrevention.cs" />
|
||||||
<Compile Include="UI\DisplayManager.cs" />
|
<Compile Include="UI\DisplayManager.cs" />
|
||||||
<Compile Include="UI\Notification.cs" />
|
<Compile Include="UI\Notification.cs" />
|
||||||
@ -313,7 +320,7 @@
|
|||||||
<Compile Include="UI\Panels\ObjectExplorerPanel.cs" />
|
<Compile Include="UI\Panels\ObjectExplorerPanel.cs" />
|
||||||
<Compile Include="UI\UIManager.cs" />
|
<Compile Include="UI\UIManager.cs" />
|
||||||
<Compile Include="UI\Panels\PanelDragger.cs" />
|
<Compile Include="UI\Panels\PanelDragger.cs" />
|
||||||
<Compile Include="UI\Widgets\AutoComplete\AutoCompleteModal.cs" />
|
<Compile Include="UI\Panels\AutoCompleteModal.cs" />
|
||||||
<Compile Include="UI\Widgets\AutoComplete\TypeCompleter.cs" />
|
<Compile Include="UI\Widgets\AutoComplete\TypeCompleter.cs" />
|
||||||
<Compile Include="ObjectExplorer\ObjectSearch.cs" />
|
<Compile Include="ObjectExplorer\ObjectSearch.cs" />
|
||||||
<Compile Include="ObjectExplorer\SceneExplorer.cs" />
|
<Compile Include="ObjectExplorer\SceneExplorer.cs" />
|
||||||
@ -323,6 +330,9 @@
|
|||||||
<Compile Include="UI\Widgets\TransformTree\CachedTransform.cs" />
|
<Compile Include="UI\Widgets\TransformTree\CachedTransform.cs" />
|
||||||
<Compile Include="UI\Widgets\TransformTree\TransformCell.cs" />
|
<Compile Include="UI\Widgets\TransformTree\TransformCell.cs" />
|
||||||
<Compile Include="UI\Widgets\TransformTree\TransformTree.cs" />
|
<Compile Include="UI\Widgets\TransformTree\TransformTree.cs" />
|
||||||
|
<Compile Include="UI\Widgets\UnityObjects\AudioClipWidget.cs" />
|
||||||
|
<Compile Include="UI\Widgets\UnityObjects\Texture2DWidget.cs" />
|
||||||
|
<Compile Include="UI\Widgets\UnityObjects\UnityObjectWidget.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="ILRepack.targets" />
|
<None Include="ILRepack.targets" />
|
||||||
|
@ -6,6 +6,6 @@
|
|||||||
<package id="ILRepack.Lib.MSBuild.Task" version="2.0.18.2" targetFramework="net35" />
|
<package id="ILRepack.Lib.MSBuild.Task" version="2.0.18.2" targetFramework="net35" />
|
||||||
<package id="Mono.Cecil" version="0.10.4" targetFramework="net35" />
|
<package id="Mono.Cecil" version="0.10.4" targetFramework="net35" />
|
||||||
<package id="Samboy063.Tomlet" version="3.1.3" targetFramework="net472" />
|
<package id="Samboy063.Tomlet" version="3.1.3" targetFramework="net472" />
|
||||||
<package id="UniverseLib" version="1.2.16" targetFramework="net35" />
|
<package id="UniverseLib" version="1.2.18" targetFramework="net35" />
|
||||||
<package id="UniverseLib.Analyzers" version="1.0.3" targetFramework="net35" developmentDependency="true" />
|
<package id="UniverseLib.Analyzers" version="1.0.3" targetFramework="net35" developmentDependency="true" />
|
||||||
</packages>
|
</packages>
|
Reference in New Issue
Block a user