mirror of
https://github.com/aelurum/AssetStudio.git
synced 2025-05-25 05:40:21 -04:00
712 lines
31 KiB
C#
712 lines
31 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.Globalization;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Threading;
|
|
using System.Windows.Forms;
|
|
using AssetStudio;
|
|
using dnlib.DotNet;
|
|
using static AssetStudioGUI.Exporter;
|
|
|
|
namespace AssetStudioGUI
|
|
{
|
|
internal static class Studio
|
|
{
|
|
public static AssetsManager assetsManager = new AssetsManager();
|
|
private static HashSet<string> assetsNameHash = new HashSet<string>();
|
|
public static List<AssetItem> exportableAssets = new List<AssetItem>();
|
|
public static List<AssetItem> visibleAssets = new List<AssetItem>();
|
|
public static Dictionary<string, SortedDictionary<int, TypeTreeItem>> AllTypeMap = new Dictionary<string, SortedDictionary<int, TypeTreeItem>>();
|
|
public static Dictionary<GameObject, GameObjectTreeNode> treeNodeDictionary = new Dictionary<GameObject, GameObjectTreeNode>();
|
|
public static bool ModuleLoaded;
|
|
public static Dictionary<string, ModuleDef> LoadedModuleDic = new Dictionary<string, ModuleDef>();
|
|
|
|
public static void ExtractFile(string[] fileNames)
|
|
{
|
|
ThreadPool.QueueUserWorkItem(state =>
|
|
{
|
|
int extractedCount = 0;
|
|
Progress.Reset();
|
|
for (var i = 0; i < fileNames.Length; i++)
|
|
{
|
|
var fileName = fileNames[i];
|
|
var type = ImportHelper.CheckFileType(fileName, out var reader);
|
|
if (type == FileType.BundleFile)
|
|
extractedCount += ExtractBundleFile(fileName, reader);
|
|
else if (type == FileType.WebFile)
|
|
extractedCount += ExtractWebDataFile(fileName, reader);
|
|
else
|
|
reader.Dispose();
|
|
Progress.Report(i + 1, fileName.Length);
|
|
}
|
|
|
|
Logger.Info($"Finished extracting {extractedCount} files.");
|
|
});
|
|
}
|
|
|
|
private static int ExtractBundleFile(string bundleFileName, EndianBinaryReader reader)
|
|
{
|
|
Logger.Info($"Decompressing {Path.GetFileName(bundleFileName)} ...");
|
|
var bundleFile = new BundleFile(reader, bundleFileName);
|
|
reader.Dispose();
|
|
if (bundleFile.fileList.Count > 0)
|
|
{
|
|
var extractPath = bundleFileName + "_unpacked\\";
|
|
Directory.CreateDirectory(extractPath);
|
|
return ExtractStreamFile(extractPath, bundleFile.fileList);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
private static int ExtractWebDataFile(string webFileName, EndianBinaryReader reader)
|
|
{
|
|
Logger.Info($"Decompressing {Path.GetFileName(webFileName)} ...");
|
|
var webFile = new WebFile(reader);
|
|
reader.Dispose();
|
|
if (webFile.fileList.Count > 0)
|
|
{
|
|
var extractPath = webFileName + "_unpacked\\";
|
|
Directory.CreateDirectory(extractPath);
|
|
return ExtractStreamFile(extractPath, webFile.fileList);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
private static int ExtractStreamFile(string extractPath, List<StreamFile> fileList)
|
|
{
|
|
int extractedCount = 0;
|
|
foreach (var file in fileList)
|
|
{
|
|
var filePath = extractPath + file.fileName;
|
|
if (!Directory.Exists(extractPath))
|
|
{
|
|
Directory.CreateDirectory(extractPath);
|
|
}
|
|
if (!File.Exists(filePath) && file.stream is MemoryStream stream)
|
|
{
|
|
File.WriteAllBytes(filePath, stream.ToArray());
|
|
extractedCount += 1;
|
|
}
|
|
file.stream.Dispose();
|
|
}
|
|
return extractedCount;
|
|
}
|
|
|
|
public static void BuildAssetList(Dictionary<ObjectReader, AssetItem> tempDic, bool displayAll, bool displayOriginalName, out string productName)
|
|
{
|
|
productName = string.Empty;
|
|
Logger.Info("Building asset list...");
|
|
|
|
var progressCount = assetsManager.assetsFileList.Sum(x => x.ObjectReaders.Count);
|
|
int j = 0;
|
|
Progress.Reset();
|
|
foreach (var assetsFile in assetsManager.assetsFileList)
|
|
{
|
|
var tempExportableAssets = new List<AssetItem>();
|
|
AssetBundle ab = null;
|
|
foreach (var objectReader in assetsFile.ObjectReaders.Values)
|
|
{
|
|
var assetItem = new AssetItem(objectReader);
|
|
tempDic.Add(objectReader, assetItem);
|
|
assetItem.UniqueID = " #" + j;
|
|
var exportable = false;
|
|
switch (assetItem.Type)
|
|
{
|
|
case ClassIDType.GameObject:
|
|
{
|
|
var m_GameObject = new GameObject(objectReader);
|
|
assetItem.Text = m_GameObject.m_Name;
|
|
assetsFile.GameObjects.Add(objectReader.m_PathID, m_GameObject);
|
|
break;
|
|
}
|
|
case ClassIDType.Transform:
|
|
{
|
|
var m_Transform = new Transform(objectReader);
|
|
assetsFile.Transforms.Add(objectReader.m_PathID, m_Transform);
|
|
break;
|
|
}
|
|
case ClassIDType.RectTransform:
|
|
{
|
|
var m_Rect = new RectTransform(objectReader);
|
|
assetsFile.Transforms.Add(objectReader.m_PathID, m_Rect);
|
|
break;
|
|
}
|
|
case ClassIDType.Texture2D:
|
|
{
|
|
var m_Texture2D = new Texture2D(objectReader, false);
|
|
if (!string.IsNullOrEmpty(m_Texture2D.path))
|
|
assetItem.FullSize = objectReader.byteSize + m_Texture2D.size;
|
|
assetItem.Text = m_Texture2D.m_Name;
|
|
exportable = true;
|
|
break;
|
|
}
|
|
case ClassIDType.AudioClip:
|
|
{
|
|
var m_AudioClip = new AudioClip(objectReader, false);
|
|
if (!string.IsNullOrEmpty(m_AudioClip.m_Source))
|
|
assetItem.FullSize = objectReader.byteSize + m_AudioClip.m_Size;
|
|
assetItem.Text = m_AudioClip.m_Name;
|
|
exportable = true;
|
|
break;
|
|
}
|
|
case ClassIDType.VideoClip:
|
|
{
|
|
var m_VideoClip = new VideoClip(objectReader, false);
|
|
if (!string.IsNullOrEmpty(m_VideoClip.m_OriginalPath))
|
|
assetItem.FullSize = objectReader.byteSize + (long)m_VideoClip.m_Size;
|
|
assetItem.Text = m_VideoClip.m_Name;
|
|
exportable = true;
|
|
break;
|
|
}
|
|
case ClassIDType.Shader:
|
|
{
|
|
var m_Shader = new Shader(objectReader);
|
|
assetItem.Text = m_Shader.m_ParsedForm?.m_Name ?? m_Shader.m_Name;
|
|
exportable = true;
|
|
break;
|
|
}
|
|
case ClassIDType.Mesh:
|
|
case ClassIDType.TextAsset:
|
|
case ClassIDType.AnimationClip:
|
|
case ClassIDType.Font:
|
|
case ClassIDType.MovieTexture:
|
|
case ClassIDType.Sprite:
|
|
{
|
|
var obj = new NamedObject(objectReader);
|
|
assetItem.Text = obj.m_Name;
|
|
exportable = true;
|
|
break;
|
|
}
|
|
case ClassIDType.Avatar:
|
|
case ClassIDType.AnimatorController:
|
|
case ClassIDType.AnimatorOverrideController:
|
|
case ClassIDType.Material:
|
|
case ClassIDType.MonoScript:
|
|
case ClassIDType.SpriteAtlas:
|
|
{
|
|
var obj = new NamedObject(objectReader);
|
|
assetItem.Text = obj.m_Name;
|
|
break;
|
|
}
|
|
case ClassIDType.Animator:
|
|
{
|
|
exportable = true;
|
|
break;
|
|
}
|
|
case ClassIDType.MonoBehaviour:
|
|
{
|
|
var m_MonoBehaviour = new MonoBehaviour(objectReader);
|
|
if (m_MonoBehaviour.m_Name == "" && m_MonoBehaviour.m_Script.TryGet(out var script))
|
|
{
|
|
var m_Script = new MonoScript(script);
|
|
assetItem.Text = m_Script.m_ClassName;
|
|
}
|
|
else
|
|
{
|
|
assetItem.Text = m_MonoBehaviour.m_Name;
|
|
}
|
|
exportable = true;
|
|
break;
|
|
}
|
|
case ClassIDType.PlayerSettings:
|
|
{
|
|
var plSet = new PlayerSettings(objectReader);
|
|
productName = plSet.productName;
|
|
break;
|
|
}
|
|
case ClassIDType.AssetBundle:
|
|
{
|
|
ab = new AssetBundle(objectReader);
|
|
assetItem.Text = ab.m_Name;
|
|
break;
|
|
}
|
|
}
|
|
if (assetItem.Text == "")
|
|
{
|
|
assetItem.Text = assetItem.TypeString + assetItem.UniqueID;
|
|
}
|
|
assetItem.SubItems.AddRange(new[] { assetItem.TypeString, assetItem.FullSize.ToString() });
|
|
//处理同名文件
|
|
if (!assetsNameHash.Add((assetItem.TypeString + assetItem.Text).ToUpper()))
|
|
{
|
|
assetItem.Text += assetItem.UniqueID;
|
|
}
|
|
//处理非法文件名
|
|
assetItem.Text = FixFileName(assetItem.Text);
|
|
if (displayAll)
|
|
{
|
|
exportable = true;
|
|
}
|
|
if (exportable)
|
|
{
|
|
tempExportableAssets.Add(assetItem);
|
|
}
|
|
|
|
Progress.Report(++j, progressCount);
|
|
}
|
|
if (displayOriginalName && ab != null)
|
|
{
|
|
foreach (var x in tempExportableAssets)
|
|
{
|
|
var replacename = ab.m_Container.Find(y => y.second.asset.m_PathID == x.reader.m_PathID)?.first;
|
|
if (!string.IsNullOrEmpty(replacename))
|
|
{
|
|
var ex = Path.GetExtension(replacename);
|
|
x.Text = !string.IsNullOrEmpty(ex) ? replacename.Replace(ex, "") : replacename;
|
|
if (!assetsNameHash.Add((x.TypeString + x.Text).ToUpper()))
|
|
{
|
|
x.Text = Path.GetDirectoryName(replacename) + "\\" + Path.GetFileNameWithoutExtension(replacename) + x.UniqueID;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
exportableAssets.AddRange(tempExportableAssets);
|
|
tempExportableAssets.Clear();
|
|
}
|
|
|
|
visibleAssets = exportableAssets;
|
|
assetsNameHash.Clear();
|
|
}
|
|
|
|
public static List<GameObjectTreeNode> BuildTreeStructure(Dictionary<ObjectReader, AssetItem> tempDic)
|
|
{
|
|
var treeNodeCollection = new List<GameObjectTreeNode>();
|
|
var gameObjectCount = assetsManager.assetsFileList.Sum(x => x.GameObjects.Count);
|
|
if (gameObjectCount > 0)
|
|
{
|
|
Logger.Info("Building tree structure...");
|
|
int i = 0;
|
|
Progress.Reset();
|
|
foreach (var assetsFile in assetsManager.assetsFileList)
|
|
{
|
|
var fileNode = new GameObjectTreeNode(assetsFile.fileName); //RootNode
|
|
|
|
foreach (var m_GameObject in assetsFile.GameObjects.Values)
|
|
{
|
|
foreach (var m_Component in m_GameObject.m_Components)
|
|
{
|
|
if (m_Component.TryGet(out var asset))
|
|
{
|
|
switch (asset.type)
|
|
{
|
|
case ClassIDType.Transform:
|
|
{
|
|
m_GameObject.m_Transform = m_Component;
|
|
break;
|
|
}
|
|
case ClassIDType.MeshRenderer:
|
|
{
|
|
m_GameObject.m_MeshRenderer = m_Component;
|
|
break;
|
|
}
|
|
case ClassIDType.MeshFilter:
|
|
{
|
|
m_GameObject.m_MeshFilter = m_Component;
|
|
if (m_Component.TryGet(out var objectReader))
|
|
{
|
|
var m_MeshFilter = new MeshFilter(objectReader);
|
|
if (m_MeshFilter.m_Mesh.TryGet(out objectReader))
|
|
{
|
|
var item = tempDic[objectReader];
|
|
item.gameObject = m_GameObject;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case ClassIDType.SkinnedMeshRenderer:
|
|
{
|
|
m_GameObject.m_SkinnedMeshRenderer = m_Component;
|
|
if (m_Component.TryGet(out var objectReader))
|
|
{
|
|
var m_SkinnedMeshRenderer = new SkinnedMeshRenderer(objectReader);
|
|
if (m_SkinnedMeshRenderer.m_Mesh.TryGet(out objectReader))
|
|
{
|
|
var item = tempDic[objectReader];
|
|
item.gameObject = m_GameObject;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case ClassIDType.Animator:
|
|
{
|
|
m_GameObject.m_Animator = m_Component;
|
|
var item = tempDic[asset];
|
|
item.Text = tempDic[m_GameObject.reader].Text;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
var parentNode = fileNode;
|
|
|
|
if (m_GameObject.m_Transform != null && m_GameObject.m_Transform.TryGetTransform(out var m_Transform))
|
|
{
|
|
if (m_Transform.m_Father.TryGetTransform(out var m_Father))
|
|
{
|
|
if (m_Father.m_GameObject.TryGetGameObject(out var parentGameObject))
|
|
{
|
|
if (!treeNodeDictionary.TryGetValue(parentGameObject, out parentNode))
|
|
{
|
|
parentNode = new GameObjectTreeNode(parentGameObject);
|
|
treeNodeDictionary.Add(parentGameObject, parentNode);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!treeNodeDictionary.TryGetValue(m_GameObject, out var currentNode))
|
|
{
|
|
currentNode = new GameObjectTreeNode(m_GameObject);
|
|
treeNodeDictionary.Add(m_GameObject, currentNode);
|
|
}
|
|
parentNode.Nodes.Add(currentNode);
|
|
|
|
Progress.Report(++i, gameObjectCount);
|
|
}
|
|
|
|
if (fileNode.Nodes.Count > 0)
|
|
{
|
|
treeNodeCollection.Add(fileNode);
|
|
}
|
|
}
|
|
}
|
|
|
|
return treeNodeCollection;
|
|
}
|
|
|
|
public static void BuildClassStructure()
|
|
{
|
|
foreach (var assetsFile in assetsManager.assetsFileList)
|
|
{
|
|
if (AllTypeMap.TryGetValue(assetsFile.unityVersion, out var curVer))
|
|
{
|
|
foreach (var type in assetsFile.m_Types.Where(x => x.m_Nodes != null))
|
|
{
|
|
var key = type.classID;
|
|
if (type.m_ScriptTypeIndex >= 0)
|
|
{
|
|
key = -1 - type.m_ScriptTypeIndex;
|
|
}
|
|
curVer[key] = new TypeTreeItem(key, type.m_Nodes);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
var items = new SortedDictionary<int, TypeTreeItem>();
|
|
foreach (var type in assetsFile.m_Types.Where(x => x.m_Nodes != null))
|
|
{
|
|
var key = type.classID;
|
|
if (type.m_ScriptTypeIndex >= 0)
|
|
{
|
|
key = -1 - type.m_ScriptTypeIndex;
|
|
}
|
|
items.Add(key, new TypeTreeItem(key, type.m_Nodes));
|
|
}
|
|
AllTypeMap.Add(assetsFile.unityVersion, items);
|
|
}
|
|
}
|
|
}
|
|
|
|
public static string FixFileName(string str)
|
|
{
|
|
if (str.Length >= 260) return Path.GetRandomFileName();
|
|
return Path.GetInvalidFileNameChars().Aggregate(str, (current, c) => current.Replace(c, '_'));
|
|
}
|
|
|
|
public static void ExportAssets(string savePath, List<AssetItem> toExportAssets, int assetGroupSelectedIndex, bool openAfterExport)
|
|
{
|
|
ThreadPool.QueueUserWorkItem(state =>
|
|
{
|
|
Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");
|
|
|
|
int toExportCount = toExportAssets.Count;
|
|
int exportedCount = 0;
|
|
int i = 0;
|
|
Progress.Reset();
|
|
foreach (var asset in toExportAssets)
|
|
{
|
|
var exportpath = savePath + "\\";
|
|
if (assetGroupSelectedIndex == 1)
|
|
{
|
|
exportpath += Path.GetFileNameWithoutExtension(asset.sourceFile.fullName) + "_export\\";
|
|
}
|
|
else if (assetGroupSelectedIndex == 0)
|
|
{
|
|
exportpath = savePath + "\\" + asset.TypeString + "\\";
|
|
}
|
|
Logger.Info($"Exporting {asset.TypeString}: {asset.Text}");
|
|
try
|
|
{
|
|
switch (asset.Type)
|
|
{
|
|
case ClassIDType.Texture2D:
|
|
if (ExportTexture2D(asset, exportpath))
|
|
{
|
|
exportedCount++;
|
|
}
|
|
break;
|
|
case ClassIDType.AudioClip:
|
|
if (ExportAudioClip(asset, exportpath))
|
|
{
|
|
exportedCount++;
|
|
}
|
|
break;
|
|
case ClassIDType.Shader:
|
|
if (ExportShader(asset, exportpath))
|
|
{
|
|
exportedCount++;
|
|
}
|
|
break;
|
|
case ClassIDType.TextAsset:
|
|
if (ExportTextAsset(asset, exportpath))
|
|
{
|
|
exportedCount++;
|
|
}
|
|
break;
|
|
case ClassIDType.MonoBehaviour:
|
|
if (ExportMonoBehaviour(asset, exportpath))
|
|
{
|
|
exportedCount++;
|
|
}
|
|
break;
|
|
case ClassIDType.Font:
|
|
if (ExportFont(asset, exportpath))
|
|
{
|
|
exportedCount++;
|
|
}
|
|
break;
|
|
case ClassIDType.Mesh:
|
|
if (ExportMesh(asset, exportpath))
|
|
{
|
|
exportedCount++;
|
|
}
|
|
break;
|
|
case ClassIDType.VideoClip:
|
|
if (ExportVideoClip(asset, exportpath))
|
|
{
|
|
exportedCount++;
|
|
}
|
|
break;
|
|
case ClassIDType.MovieTexture:
|
|
if (ExportMovieTexture(asset, exportpath))
|
|
{
|
|
exportedCount++;
|
|
}
|
|
break;
|
|
case ClassIDType.Sprite:
|
|
if (ExportSprite(asset, exportpath))
|
|
{
|
|
exportedCount++;
|
|
}
|
|
break;
|
|
case ClassIDType.Animator:
|
|
if (ExportAnimator(asset, exportpath))
|
|
{
|
|
exportedCount++;
|
|
}
|
|
break;
|
|
case ClassIDType.AnimationClip:
|
|
break;
|
|
default:
|
|
if (ExportRawFile(asset, exportpath))
|
|
{
|
|
exportedCount++;
|
|
}
|
|
break;
|
|
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
MessageBox.Show($"Export {asset.Type}:{asset.Text} error\r\n{ex.Message}\r\n{ex.StackTrace}");
|
|
}
|
|
|
|
Progress.Report(++i, toExportCount);
|
|
}
|
|
|
|
var statusText = exportedCount == 0 ? "Nothing exported." : $"Finished exporting {exportedCount} assets.";
|
|
|
|
if (toExportCount > exportedCount)
|
|
{
|
|
statusText += $" {toExportCount - exportedCount} assets skipped (not extractable or files already exist)";
|
|
}
|
|
|
|
Logger.Info(statusText);
|
|
|
|
if (openAfterExport && exportedCount > 0)
|
|
{
|
|
Process.Start(savePath);
|
|
}
|
|
});
|
|
}
|
|
|
|
public static void ExportSplitObjects(string savePath, TreeNodeCollection nodes)
|
|
{
|
|
ThreadPool.QueueUserWorkItem(state =>
|
|
{
|
|
var count = nodes.Cast<TreeNode>().Sum(x => x.Nodes.Count);
|
|
int k = 0;
|
|
Progress.Reset();
|
|
foreach (GameObjectTreeNode node in nodes)
|
|
{
|
|
//遍历一级子节点
|
|
foreach (GameObjectTreeNode j in node.Nodes)
|
|
{
|
|
//收集所有子节点
|
|
var gameObjects = new List<GameObject>();
|
|
CollectNode(j, gameObjects);
|
|
//跳过一些不需要导出的object
|
|
if (gameObjects.All(x => x.m_SkinnedMeshRenderer == null && x.m_MeshFilter == null))
|
|
continue;
|
|
//处理非法文件名
|
|
var filename = FixFileName(j.Text);
|
|
//每个文件存放在单独的文件夹
|
|
var targetPath = $"{savePath}{filename}\\";
|
|
//重名文件处理
|
|
for (int i = 1; ; i++)
|
|
{
|
|
if (Directory.Exists(targetPath))
|
|
{
|
|
targetPath = $"{savePath}{filename} ({i})\\";
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
Directory.CreateDirectory(targetPath);
|
|
//导出FBX
|
|
Logger.Info($"Exporting {filename}.fbx");
|
|
try
|
|
{
|
|
ExportGameObject(j.gameObject, targetPath);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
MessageBox.Show($"{ex.Message}\r\n{ex.StackTrace}");
|
|
}
|
|
|
|
Progress.Report(++k, count);
|
|
Logger.Info($"Finished exporting {filename}.fbx");
|
|
}
|
|
}
|
|
Logger.Info("Finished");
|
|
});
|
|
}
|
|
|
|
private static void CollectNode(GameObjectTreeNode node, List<GameObject> gameObjects)
|
|
{
|
|
gameObjects.Add(node.gameObject);
|
|
foreach (GameObjectTreeNode i in node.Nodes)
|
|
{
|
|
CollectNode(i, gameObjects);
|
|
}
|
|
}
|
|
|
|
public static void ExportAnimatorWithAnimationClip(AssetItem animator, List<AssetItem> animationList, string exportPath)
|
|
{
|
|
ThreadPool.QueueUserWorkItem(state =>
|
|
{
|
|
Logger.Info($"Exporting {animator.Text}");
|
|
try
|
|
{
|
|
ExportAnimator(animator, exportPath, animationList);
|
|
Logger.Info($"Finished exporting {animator.Text}");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
MessageBox.Show($"{ex.Message}\r\n{ex.StackTrace}");
|
|
Logger.Info("Error in export");
|
|
}
|
|
});
|
|
}
|
|
|
|
public static void ExportObjectsWithAnimationClip(string exportPath, TreeNodeCollection nodes, List<AssetItem> animationList = null)
|
|
{
|
|
ThreadPool.QueueUserWorkItem(state =>
|
|
{
|
|
var gameObjects = new List<GameObject>();
|
|
GetSelectedParentNode(nodes, gameObjects);
|
|
if (gameObjects.Count > 0)
|
|
{
|
|
var count = gameObjects.Count;
|
|
int i = 0;
|
|
Progress.Reset();
|
|
foreach (var gameObject in gameObjects)
|
|
{
|
|
Logger.Info($"Exporting {gameObject.m_Name}");
|
|
try
|
|
{
|
|
ExportGameObject(gameObject, exportPath, animationList);
|
|
Logger.Info($"Finished exporting {gameObject.m_Name}");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
MessageBox.Show($"{ex.Message}\r\n{ex.StackTrace}");
|
|
Logger.Info("Error in export");
|
|
}
|
|
|
|
Progress.Report(++i, count);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Logger.Info("No Object can be exported.");
|
|
}
|
|
});
|
|
}
|
|
|
|
private static void GetSelectedParentNode(TreeNodeCollection nodes, List<GameObject> gameObjects)
|
|
{
|
|
foreach (GameObjectTreeNode i in nodes)
|
|
{
|
|
if (i.Checked)
|
|
{
|
|
gameObjects.Add(i.gameObject);
|
|
}
|
|
else
|
|
{
|
|
GetSelectedParentNode(i.Nodes, gameObjects);
|
|
}
|
|
}
|
|
}
|
|
|
|
public static string GetScriptString(ObjectReader reader)
|
|
{
|
|
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;
|
|
}
|
|
|
|
return ScriptHelper.GetScriptString(reader, LoadedModuleDic);
|
|
}
|
|
}
|
|
}
|