diff --git a/AssetStudio/AssetStudio-x86.csproj b/AssetStudio/AssetStudio-x86.csproj
index 0498ab3..e9dc167 100644
--- a/AssetStudio/AssetStudio-x86.csproj
+++ b/AssetStudio/AssetStudio-x86.csproj
@@ -55,6 +55,10 @@
MinimumRecommendedRules.ruleset
+
+ Library\dnlib.dll
+ False
+
False
Library\OpenTK.dll
@@ -158,6 +162,7 @@
+
diff --git a/AssetStudio/AssetStudio.csproj b/AssetStudio/AssetStudio.csproj
index dac62fc..839e589 100644
--- a/AssetStudio/AssetStudio.csproj
+++ b/AssetStudio/AssetStudio.csproj
@@ -55,6 +55,10 @@
OnBuildSuccess
+
+ Library\dnlib.dll
+ False
+
False
Library\OpenTK.dll
@@ -146,6 +150,7 @@
+
Code
diff --git a/AssetStudio/AssetStudioForm.cs b/AssetStudio/AssetStudioForm.cs
index 49f405b..9eecd70 100644
--- a/AssetStudio/AssetStudioForm.cs
+++ b/AssetStudio/AssetStudioForm.cs
@@ -778,8 +778,15 @@ namespace AssetStudio
}
case ClassIDReference.MonoBehaviour:
{
- MonoBehaviour m_MonoBehaviour = new MonoBehaviour(asset, true);
- textPreviewBox.Text = m_MonoBehaviour.serializedText;
+ var m_MonoBehaviour = new MonoBehaviour(asset);
+ if (asset.Type1 != asset.Type2 && asset.sourceFile.ClassStructures.ContainsKey(asset.Type1))
+ {
+ textPreviewBox.Text = asset.GetClassString();
+ }
+ else
+ {
+ textPreviewBox.Text = GetScriptString(asset);
+ }
textPreviewBox.Visible = true;
break;
@@ -1685,6 +1692,9 @@ namespace AssetStudio
}
FMODreset();
+
+ moduleLoaded = false;
+ LoadedModuleDic.Clear();
}
private void assetListView_MouseClick(object sender, MouseEventArgs e)
diff --git a/AssetStudio/Classes/MonoBehaviour.cs b/AssetStudio/Classes/MonoBehaviour.cs
index 7b89ec7..ab3b3e1 100644
--- a/AssetStudio/Classes/MonoBehaviour.cs
+++ b/AssetStudio/Classes/MonoBehaviour.cs
@@ -7,38 +7,21 @@ namespace AssetStudio
{
class MonoBehaviour
{
- public string serializedText;
+ public PPtr m_GameObject;
+ public byte m_Enabled;
+ public PPtr m_Script;
+ public string m_Name;
- public MonoBehaviour(AssetPreloadData preloadData, bool readSwitch)
+ public MonoBehaviour(AssetPreloadData preloadData)
{
var sourceFile = preloadData.sourceFile;
var reader = preloadData.InitReader();
- var m_GameObject = sourceFile.ReadPPtr();
- var m_Enabled = reader.ReadByte();
+ m_GameObject = sourceFile.ReadPPtr();
+ m_Enabled = reader.ReadByte();
reader.AlignStream(4);
- var m_Script = sourceFile.ReadPPtr();
- var m_Name = reader.ReadAlignedString();
- if (readSwitch)
- {
- if ((serializedText = preloadData.GetClassString()) == null)
- {
- var str = "PPtr m_GameObject\r\n";
- str += "\tint m_FileID = " + m_GameObject.m_FileID + "\r\n";
- str += "\tint64 m_PathID = " + m_GameObject.m_PathID + "\r\n";
- str += "UInt8 m_Enabled = " + m_Enabled + "\r\n";
- str += "PPtr m_Script\r\n";
- str += "\tint m_FileID = " + m_Script.m_FileID + "\r\n";
- str += "\tint64 m_PathID = " + m_Script.m_PathID + "\r\n";
- str += "string m_Name = \"" + m_Name + "\"\r\n";
- serializedText = str;
- }
- }
- else
- {
- preloadData.extension = ".txt";
- preloadData.Text = m_Name;
- }
+ m_Script = sourceFile.ReadPPtr();
+ m_Name = reader.ReadAlignedString();
}
}
}
diff --git a/AssetStudio/Classes/MonoScript.cs b/AssetStudio/Classes/MonoScript.cs
new file mode 100644
index 0000000..7dac486
--- /dev/null
+++ b/AssetStudio/Classes/MonoScript.cs
@@ -0,0 +1,50 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace AssetStudio
+{
+ class MonoScript
+ {
+ public string m_Name;
+ public string m_ClassName;
+ public string m_Namespace = string.Empty;
+ public string m_AssemblyName;
+
+ public MonoScript(AssetPreloadData preloadData)
+ {
+ var sourceFile = preloadData.sourceFile;
+ var reader = preloadData.InitReader();
+ var version = sourceFile.version;
+
+ m_Name = reader.ReadAlignedString();
+ if (version[0] > 3 || (version[0] == 3 && version[1] >= 4))
+ {
+ var m_ExecutionOrder = reader.ReadAlignedString();
+ }
+ if (version[0] < 5)
+ {
+ var m_PropertiesHash = reader.ReadUInt32();
+ }
+ else
+ {
+ var m_PropertiesHash = reader.ReadBytes(16);
+ }
+ if (version[0] < 3)
+ {
+ var m_PathName = reader.ReadAlignedString();
+ }
+ m_ClassName = reader.ReadAlignedString();
+ if (version[0] >= 3)
+ {
+ m_Namespace = reader.ReadAlignedString();
+ }
+ m_AssemblyName = reader.ReadAlignedString();
+ if (version[0] < 2018 || (version[0] == 2018 && version[1] < 2))
+ {
+ var m_IsEditorScript = reader.ReadBoolean();
+ }
+ }
+ }
+}
diff --git a/AssetStudio/Library/dnlib.dll b/AssetStudio/Library/dnlib.dll
new file mode 100644
index 0000000..718b4bf
Binary files /dev/null and b/AssetStudio/Library/dnlib.dll differ
diff --git a/AssetStudio/OpenFolderDialog.cs b/AssetStudio/OpenFolderDialog.cs
index 370d753..d363b1a 100644
--- a/AssetStudio/OpenFolderDialog.cs
+++ b/AssetStudio/OpenFolderDialog.cs
@@ -8,32 +8,19 @@ namespace AssetStudio
{
class OpenFolderDialog
{
- ///
- /// Gets/sets folder in which dialog will be open.
- ///
public string InitialFolder { get; set; }
-
- ///
- /// Gets/sets directory in which dialog will be open if there is no recent directory available.
- ///
public string DefaultFolder { get; set; }
-
- ///
- /// Gets selected folder.
- ///
public string Folder { get; private set; }
+ public string Title { get; set; }
-
- internal DialogResult ShowDialog(IWin32Window owner)
+ internal DialogResult ShowDialog(IWin32Window owner = null)
{
if (Environment.OSVersion.Version.Major >= 6)
{
return ShowVistaDialog(owner);
}
- else
- {
- return ShowLegacyDialog(owner);
- }
+
+ return ShowLegacyDialog(owner);
}
private DialogResult ShowVistaDialog(IWin32Window owner)
@@ -42,24 +29,26 @@ namespace AssetStudio
frm.GetOptions(out var options);
options |= NativeMethods.FOS_PICKFOLDERS | NativeMethods.FOS_FORCEFILESYSTEM | NativeMethods.FOS_NOVALIDATE | NativeMethods.FOS_NOTESTFILECREATE | NativeMethods.FOS_DONTADDTORECENT;
frm.SetOptions(options);
- if (this.InitialFolder != null)
+ if (Title != null)
+ frm.SetTitle(Title);
+ if (InitialFolder != null)
{
var riid = new Guid("43826D1E-E718-42EE-BC55-A1E261C37BFE"); //IShellItem
- if (NativeMethods.SHCreateItemFromParsingName(this.InitialFolder, IntPtr.Zero, ref riid, out var directoryShellItem) == NativeMethods.S_OK)
+ if (NativeMethods.SHCreateItemFromParsingName(InitialFolder, IntPtr.Zero, ref riid, out var directoryShellItem) == NativeMethods.S_OK)
{
frm.SetFolder(directoryShellItem);
}
}
- if (this.DefaultFolder != null)
+ if (DefaultFolder != null)
{
var riid = new Guid("43826D1E-E718-42EE-BC55-A1E261C37BFE"); //IShellItem
- if (NativeMethods.SHCreateItemFromParsingName(this.DefaultFolder, IntPtr.Zero, ref riid, out var directoryShellItem) == NativeMethods.S_OK)
+ if (NativeMethods.SHCreateItemFromParsingName(DefaultFolder, IntPtr.Zero, ref riid, out var directoryShellItem) == NativeMethods.S_OK)
{
frm.SetDefaultFolder(directoryShellItem);
}
}
- if (frm.Show(owner.Handle) == NativeMethods.S_OK)
+ if ((owner == null ? frm.Show() : frm.Show(owner.Handle)) == NativeMethods.S_OK)
{
if (frm.GetResult(out var shellItem) == NativeMethods.S_OK)
{
@@ -69,7 +58,7 @@ namespace AssetStudio
{
try
{
- this.Folder = Marshal.PtrToStringAuto(pszString);
+ Folder = Marshal.PtrToStringAuto(pszString);
return DialogResult.OK;
}
finally
@@ -87,16 +76,17 @@ namespace AssetStudio
{
using (var frm = new FolderBrowserDialog())
{
- if (this.InitialFolder != null) { frm.SelectedPath = this.InitialFolder; }
- if (frm.ShowDialog(owner) == DialogResult.OK)
+ if (InitialFolder != null)
{
- this.Folder = Path.GetDirectoryName(frm.SelectedPath);
+ frm.SelectedPath = InitialFolder;
+ }
+ if ((owner == null ? frm.ShowDialog() : frm.ShowDialog(owner)) == DialogResult.OK)
+ {
+ Folder = Path.GetDirectoryName(frm.SelectedPath);
return DialogResult.OK;
}
- else
- {
- return DialogResult.Cancel;
- }
+
+ return DialogResult.Cancel;
}
}
}
diff --git a/AssetStudio/StudioClasses/Exporter.cs b/AssetStudio/StudioClasses/Exporter.cs
index 96f2a7c..1ef21dd 100644
--- a/AssetStudio/StudioClasses/Exporter.cs
+++ b/AssetStudio/StudioClasses/Exporter.cs
@@ -145,11 +145,20 @@ namespace AssetStudio
public static bool ExportMonoBehaviour(AssetPreloadData asset, string exportPath)
{
- var m_MonoBehaviour = new MonoBehaviour(asset, true);
- var exportFullName = exportPath + asset.Text + asset.extension;
+ var exportFullName = exportPath + asset.Text + ".txt";
if (ExportFileExists(exportFullName))
return false;
- File.WriteAllText(exportFullName, m_MonoBehaviour.serializedText);
+ var m_MonoBehaviour = new MonoBehaviour(asset);
+ string str;
+ if (asset.Type1 != asset.Type2 && asset.sourceFile.ClassStructures.ContainsKey(asset.Type1))
+ {
+ str = asset.GetClassString();
+ }
+ else
+ {
+ str = Studio.GetScriptString(asset);
+ }
+ File.WriteAllText(exportFullName, str);
return true;
}
diff --git a/AssetStudio/StudioClasses/Studio.cs b/AssetStudio/StudioClasses/Studio.cs
index 8e2bcbf..239b4b3 100644
--- a/AssetStudio/StudioClasses/Studio.cs
+++ b/AssetStudio/StudioClasses/Studio.cs
@@ -4,8 +4,10 @@ using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
+using System.Text;
using System.Threading;
using System.Windows.Forms;
+using dnlib.DotNet;
using static AssetStudio.Exporter;
namespace AssetStudio
@@ -21,6 +23,8 @@ namespace AssetStudio
public static Dictionary> AllClassStructures = new Dictionary>();
public static string mainPath;
public static string productName = "";
+ public static bool moduleLoaded;
+ public static Dictionary LoadedModuleDic = new Dictionary();
//UI
public static Action SetProgressBarValue;
@@ -218,9 +222,17 @@ namespace AssetStudio
}
case ClassIDReference.MonoBehaviour:
{
- var m_MonoBehaviour = new MonoBehaviour(asset, false);
- if (asset.Type1 != asset.Type2 && assetsFile.ClassStructures.ContainsKey(asset.Type1))
- exportable = true;
+ var m_MonoBehaviour = new MonoBehaviour(asset);
+ if (m_MonoBehaviour.m_Name == "" && assetsfileList.TryGetPD(m_MonoBehaviour.m_Script, out var script))
+ {
+ var m_Script = new MonoScript(script);
+ asset.Text = m_Script.m_ClassName;
+ }
+ else
+ {
+ asset.Text = m_MonoBehaviour.m_Name;
+ }
+ exportable = true;
break;
}
case ClassIDReference.Font:
@@ -722,5 +734,257 @@ namespace AssetStudio
}
}
}
+
+ public static string GetScriptString(AssetPreloadData assetPreloadData)
+ {
+ if (!moduleLoaded)
+ {
+ var openFolderDialog = new OpenFolderDialog();
+ openFolderDialog.Title = "Select Assembly Folder";
+ if (openFolderDialog.ShowDialog() == DialogResult.OK)
+ {
+ var files = Directory.GetFiles(openFolderDialog.Folder, "*.dll");
+ var moduleContext = new ModuleContext();
+ var asmResolver = new AssemblyResolver(moduleContext, true);
+ var resolver = new Resolver(asmResolver);
+ moduleContext.AssemblyResolver = asmResolver;
+ moduleContext.Resolver = resolver;
+ try
+ {
+ foreach (var file in files)
+ {
+ var module = ModuleDefMD.Load(file, moduleContext);
+ LoadedModuleDic.Add(Path.GetFileName(file), module);
+ }
+ }
+ catch
+ {
+ // ignored
+ }
+ }
+
+ moduleLoaded = true;
+ }
+ var m_MonoBehaviour = new MonoBehaviour(assetPreloadData);
+ var sb = new StringBuilder();
+ sb.AppendLine("PPtr m_GameObject");
+ sb.AppendLine($"\tint m_FileID = {m_MonoBehaviour.m_GameObject.m_FileID}");
+ sb.AppendLine($"\tint64 m_PathID = {m_MonoBehaviour.m_GameObject.m_PathID}");
+ sb.AppendLine($"UInt8 m_Enabled = {m_MonoBehaviour.m_Enabled}");
+ sb.AppendLine("PPtr m_Script");
+ sb.AppendLine($"\tint m_FileID = {m_MonoBehaviour.m_Script.m_FileID}");
+ sb.AppendLine($"\tint64 m_PathID = {m_MonoBehaviour.m_Script.m_PathID}");
+ sb.AppendLine($"string m_Name = \"{m_MonoBehaviour.m_Name}\"");
+ if (assetsfileList.TryGetPD(m_MonoBehaviour.m_Script, out var script))
+ {
+ var m_Script = new MonoScript(script);
+ if (!LoadedModuleDic.TryGetValue(m_Script.m_AssemblyName, out var module))
+ {
+ /*using (var openFileDialog = new OpenFileDialog())
+ {
+ openFileDialog.Title = $"Select {m_Script.m_AssemblyName}";
+ openFileDialog.FileName = m_Script.m_AssemblyName;
+ openFileDialog.Filter = $"{m_Script.m_AssemblyName}|{m_Script.m_AssemblyName}";
+ if (openFileDialog.ShowDialog() == DialogResult.OK)
+ {
+ var moduleContext = new ModuleContext();
+ var asmResolver = new AssemblyResolver(moduleContext, true);
+ var resolver = new Resolver(asmResolver);
+ moduleContext.AssemblyResolver = asmResolver;
+ moduleContext.Resolver = resolver;
+ module = ModuleDefMD.Load(openFileDialog.FileName, moduleContext);
+ LoadedModule.Add(m_Script.m_AssemblyName, module);
+ }
+ else
+ {
+ return sb.ToString();
+ }
+ }*/
+ return sb.ToString();
+ }
+ var typeDef = module.Assembly.Find(m_Script.m_Namespace != "" ? $"{m_Script.m_Namespace}.{m_Script.m_ClassName}" : m_Script.m_ClassName, false);
+ if (typeDef != null)
+ {
+ try
+ {
+ DumpType(typeDef.ToTypeSig(), sb, assetPreloadData.sourceFile, null, -1, true);
+ }
+ catch
+ {
+ sb = new StringBuilder();
+ sb.AppendLine("PPtr m_GameObject");
+ sb.AppendLine($"\tint m_FileID = {m_MonoBehaviour.m_GameObject.m_FileID}");
+ sb.AppendLine($"\tint64 m_PathID = {m_MonoBehaviour.m_GameObject.m_PathID}");
+ sb.AppendLine($"UInt8 m_Enabled = {m_MonoBehaviour.m_Enabled}");
+ sb.AppendLine("PPtr m_Script");
+ sb.AppendLine($"\tint m_FileID = {m_MonoBehaviour.m_Script.m_FileID}");
+ sb.AppendLine($"\tint64 m_PathID = {m_MonoBehaviour.m_Script.m_PathID}");
+ sb.AppendLine($"string m_Name = \"{m_MonoBehaviour.m_Name}\"");
+ }
+ }
+ }
+ return sb.ToString();
+ }
+
+ private static void DumpType(TypeSig typeSig, StringBuilder sb, AssetsFile assetsFile, string name, int indent, bool isRoot = false)
+ {
+ var typeDef = typeSig.ToTypeDefOrRef().ResolveTypeDefThrow();
+ var reader = assetsFile.reader;
+ if (typeDef.IsPrimitive)
+ {
+ object value = null;
+ switch (typeDef.Name)
+ {
+ case "Boolean":
+ value = reader.ReadBoolean();
+ break;
+ case "Byte":
+ value = reader.ReadByte();
+ break;
+ case "SByte":
+ value = reader.ReadSByte();
+ break;
+ case "Int16":
+ value = reader.ReadInt16();
+ break;
+ case "UInt16":
+ value = reader.ReadUInt16();
+ break;
+ case "Int32":
+ value = reader.ReadInt32();
+ break;
+ case "UInt32":
+ value = reader.ReadUInt32();
+ break;
+ case "Int64":
+ value = reader.ReadInt64();
+ break;
+ case "UInt64":
+ value = reader.ReadUInt64();
+ break;
+ case "Single":
+ value = reader.ReadSingle();
+ break;
+ case "Double":
+ value = reader.ReadDouble();
+ break;
+ }
+ reader.AlignStream(4);
+ sb.AppendLine($"{new string('\t', indent)}{typeDef.Name} {name} = {value}");
+ return;
+ }
+ if (typeDef.FullName == "System.String")
+ {
+ sb.AppendLine($"{new string('\t', indent)}{typeDef.Name} {name} = \"{reader.ReadAlignedString()}\"");
+ return;
+ }
+ if (typeDef.IsEnum)
+ {
+ sb.AppendLine($"{new string('\t', indent)}{typeDef.Name} {name} = {reader.ReadUInt32()}");
+ return;
+ }
+ if (typeSig is ArraySigBase)
+ {
+ var size = reader.ReadInt32();
+ sb.AppendLine($"{new string('\t', indent)}{typeSig.TypeName} {name}");
+ sb.AppendLine($"{new string('\t', indent + 1)}Array Array");
+ sb.AppendLine($"{new string('\t', indent + 1)}int size = {size}");
+ for (int i = 0; i < size; i++)
+ {
+ sb.AppendLine($"{new string('\t', indent + 2)}[{i}]");
+ DumpType(typeDef.ToTypeSig(), sb, assetsFile, "data", indent + 2);
+ }
+ return;
+ }
+ if (!isRoot && typeSig is GenericInstSig genericInstSig)
+ {
+ var size = reader.ReadInt32();
+ sb.AppendLine($"{new string('\t', indent)}{typeSig.TypeName} {name}");
+ sb.AppendLine($"{new string('\t', indent + 1)}Array Array");
+ sb.AppendLine($"{new string('\t', indent + 1)}int size = {size}");
+ if (genericInstSig.GenericArguments.Count == 1) //vector
+ {
+ for (int i = 0; i < size; i++)
+ {
+ sb.AppendLine($"{new string('\t', indent + 2)}[{i}]");
+ DumpType(genericInstSig.GenericArguments[0], sb, assetsFile, "data", indent + 2);
+ }
+ }
+ else if (genericInstSig.GenericArguments.Count == 2) //map
+ {
+ for (int i = 0; i < size; i++)
+ {
+ sb.AppendLine($"{new string('\t', indent + 2)}[{i}]");
+ DumpType(genericInstSig.GenericArguments[0], sb, assetsFile, "first", indent + 2);
+ DumpType(genericInstSig.GenericArguments[1], sb, assetsFile, "second", indent + 2);
+ }
+ }
+ return;
+ }
+ if (indent != -1 && typeDef.FullName == "UnityEngine.Object")
+ {
+ var pptr = assetsFile.ReadPPtr();
+ sb.AppendLine($"{new string('\t', indent)}PPtr<{typeDef.Name}> {name} = {{fileID: {pptr.m_FileID}, pathID: {pptr.m_PathID}}}");
+ return;
+ }
+ if (indent != -1 && typeDef.BaseType != null && typeDef.BaseType.FullName != "System.Object")
+ {
+ var flag = false;
+ var type = typeDef;
+ while (true)
+ {
+ if (type.BaseType.FullName == "UnityEngine.Object")
+ {
+ flag = true;
+ break;
+ }
+ type = type.BaseType.ResolveTypeDefThrow();
+ if (type.BaseType == null)
+ {
+ break;
+ }
+ }
+ if (flag)
+ {
+ var pptr = assetsFile.ReadPPtr();
+ sb.AppendLine($"{new string('\t', indent)}PPtr<{typeDef.Name}> {name} = {{fileID: {pptr.m_FileID}, pathID: {pptr.m_PathID}}}");
+ return;
+ }
+ }
+ if (typeDef.IsClass || typeDef.IsValueType)
+ {
+ if (name != null && indent != -1)
+ {
+ sb.AppendLine($"{new string('\t', indent)}{typeDef.Name} {name}");
+ }
+ if (indent == -1 && typeDef.BaseType.FullName != "UnityEngine.Object")
+ {
+ DumpType(typeDef.BaseType.ToTypeSig(), sb, assetsFile, null, indent, true);
+ }
+ if (indent != -1 && typeDef.BaseType.FullName != "System.Object")
+ {
+ DumpType(typeDef.BaseType.ToTypeSig(), sb, assetsFile, null, indent, true);
+ }
+ /*if (typeDef.FullName == "UnityEngine.AnimationCurve") //TODO
+ {
+ var AnimationCurve = new AnimationCurve(reader, reader.ReadSingle, assetsFile.version);
+ }*/
+ foreach (var fieldDef in typeDef.Fields)
+ {
+ var access = fieldDef.Access & FieldAttributes.FieldAccessMask;
+ if (access != FieldAttributes.Public)
+ {
+ if (fieldDef.CustomAttributes.Any(x => x.TypeFullName.Contains("SerializeField")))
+ {
+ DumpType(fieldDef.FieldType, sb, assetsFile, fieldDef.Name, indent + 1);
+ }
+ }
+ else if ((fieldDef.Attributes & FieldAttributes.Static) == 0 && (fieldDef.Attributes & FieldAttributes.InitOnly) == 0)
+ {
+ DumpType(fieldDef.FieldType, sb, assetsFile, fieldDef.Name, indent + 1);
+ }
+ }
+ }
+ }
}
}