AssetStudio/UnityStudio/StudioClasses/Studio.cs

409 lines
19 KiB
C#
Raw Normal View History

using System;
using System.Collections.Generic;
using System.IO;
2018-03-01 12:01:25 +00:00
using System.Linq;
using System.Web.Script.Serialization;
namespace UnityStudio
{
internal static class Studio
{
2017-02-11 20:57:24 +00:00
public static List<AssetsFile> assetsfileList = new List<AssetsFile>(); //loaded files
public static Dictionary<string, int> sharedFileIndex = new Dictionary<string, int>(); //to improve the loading speed
2018-02-28 19:42:43 +00:00
public static Dictionary<string, EndianBinaryReader> resourceFileReaders = new Dictionary<string, EndianBinaryReader>(); //use for read res files
2017-02-11 20:57:24 +00:00
public static List<AssetPreloadData> exportableAssets = new List<AssetPreloadData>(); //used to hold all assets while the ListView is filtered
private static HashSet<string> exportableAssetsHash = new HashSet<string>(); //avoid the same name asset
2017-02-11 20:57:24 +00:00
public static List<AssetPreloadData> visibleAssets = new List<AssetPreloadData>(); //used to build the ListView from all or filtered assets
2017-02-11 20:57:24 +00:00
public static string productName = "";
public static string mainPath = "";
public static List<GameObject> fileNodes = new List<GameObject>();
2018-03-01 12:01:25 +00:00
public static Dictionary<string, Dictionary<string, string>> jsonMats;
2017-02-11 20:57:24 +00:00
public static Dictionary<string, SortedDictionary<int, ClassStruct>> AllClassStructures = new Dictionary<string, SortedDictionary<int, ClassStruct>>();
2017-02-11 20:57:24 +00:00
//UI
public static Action<int> SetProgressBarValue;
public static Action<int> SetProgressBarMaximum;
public static Action ProgressBarPerformStep;
public static Action<string> StatusStripUpdate;
public static Action<int> ProgressBarMaximumAdd;
2018-03-04 18:35:53 +00:00
public enum FileType
{
AssetsFile,
BundleFile,
WebFile
}
2018-02-28 19:42:43 +00:00
public static int ExtractBundleFile(string bundleFileName)
{
int extractedCount = 0;
2018-03-04 18:35:53 +00:00
if (CheckFileType(bundleFileName, out var reader) == FileType.BundleFile)
{
StatusStripUpdate($"Decompressing {Path.GetFileName(bundleFileName)} ...");
var extractPath = bundleFileName + "_unpacked\\";
Directory.CreateDirectory(extractPath);
2018-02-28 19:42:43 +00:00
var bundleFile = new BundleFile(reader);
2018-03-04 18:35:53 +00:00
foreach (var memFile in bundleFile.fileList)
{
var filePath = extractPath + memFile.fileName.Replace('/', '\\');
if (!Directory.Exists(Path.GetDirectoryName(filePath)))
{
Directory.CreateDirectory(Path.GetDirectoryName(filePath));
}
if (!File.Exists(filePath))
{
StatusStripUpdate($"Extracting {Path.GetFileName(memFile.fileName)}");
extractedCount += 1;
using (var file = File.Create(filePath))
{
2018-03-04 18:35:53 +00:00
memFile.stream.WriteTo(file);
memFile.stream.Close();
}
}
}
}
2018-02-28 19:42:43 +00:00
reader.Dispose();
return extractedCount;
}
public static void BuildAssetStructures(bool loadAssetsMenuItem, bool displayAll, bool buildHierarchyMenuItem, bool buildClassStructuresMenuItem, bool displayOriginalName)
{
#region first loop - read asset data & create list
if (loadAssetsMenuItem)
{
2017-02-11 20:57:24 +00:00
SetProgressBarValue(0);
SetProgressBarMaximum(assetsfileList.Sum(x => x.preloadTable.Values.Count));
string fileIDfmt = "D" + assetsfileList.Count.ToString().Length;
2017-02-11 20:57:24 +00:00
for (var i = 0; i < assetsfileList.Count; i++)
{
2017-02-11 20:57:24 +00:00
var assetsFile = assetsfileList[i];
StatusStripUpdate("Building asset list from " + Path.GetFileName(assetsFile.filePath));
2017-02-11 20:57:24 +00:00
string fileID = i.ToString(fileIDfmt);
AssetBundle ab = null;
foreach (var asset in assetsFile.preloadTable.Values)
{
asset.uniqueID = fileID + asset.uniqueID;
var exportable = false;
2018-03-27 22:29:28 +00:00
switch (asset.Type)
{
2018-03-27 22:29:28 +00:00
case ClassIDReference.GameObject:
2017-02-16 07:30:11 +00:00
{
GameObject m_GameObject = new GameObject(asset);
assetsFile.GameObjectList.Add(asset.m_PathID, m_GameObject);
//totalTreeNodes++;
break;
}
2018-03-27 22:29:28 +00:00
case ClassIDReference.Transform:
2017-02-16 07:30:11 +00:00
{
Transform m_Transform = new Transform(asset);
assetsFile.TransformList.Add(asset.m_PathID, m_Transform);
break;
}
2018-03-27 22:29:28 +00:00
case ClassIDReference.RectTransform:
2017-02-16 07:30:11 +00:00
{
RectTransform m_Rect = new RectTransform(asset);
assetsFile.TransformList.Add(asset.m_PathID, m_Rect.m_Transform);
break;
}
2018-03-27 22:29:28 +00:00
case ClassIDReference.Texture2D:
2017-02-16 07:30:11 +00:00
{
Texture2D m_Texture2D = new Texture2D(asset, false);
exportable = true;
break;
}
2018-03-27 22:29:28 +00:00
case ClassIDReference.Shader:
2017-02-16 07:30:11 +00:00
{
Shader m_Shader = new Shader(asset, false);
exportable = true;
break;
}
2018-03-27 22:29:28 +00:00
case ClassIDReference.TextAsset:
2017-02-16 07:30:11 +00:00
{
TextAsset m_TextAsset = new TextAsset(asset, false);
exportable = true;
break;
}
2018-03-27 22:29:28 +00:00
case ClassIDReference.AudioClip:
2017-02-16 07:30:11 +00:00
{
AudioClip m_AudioClip = new AudioClip(asset, false);
exportable = true;
2017-02-16 07:30:11 +00:00
break;
}
2018-03-27 22:29:28 +00:00
case ClassIDReference.MonoBehaviour:
2017-02-16 07:30:11 +00:00
{
var m_MonoBehaviour = new MonoBehaviour(asset, false);
if (asset.Type1 != asset.Type2 && assetsFile.ClassStructures.ContainsKey(asset.Type1))
exportable = true;
break;
}
2018-03-27 22:29:28 +00:00
case ClassIDReference.Font:
2017-02-16 07:30:11 +00:00
{
2018-03-01 12:01:25 +00:00
UnityFont m_Font = new UnityFont(asset, false);
2017-02-16 07:30:11 +00:00
exportable = true;
break;
}
2018-03-27 22:29:28 +00:00
case ClassIDReference.PlayerSettings:
2017-02-16 07:30:11 +00:00
{
var plSet = new PlayerSettings(asset);
productName = plSet.productName;
break;
}
2018-03-27 22:29:28 +00:00
case ClassIDReference.Mesh:
{
Mesh m_Mesh = new Mesh(asset, false);
exportable = true;
break;
}
2018-03-27 22:29:28 +00:00
case ClassIDReference.AssetBundle:
{
ab = new AssetBundle(asset);
break;
}
2018-03-27 22:29:28 +00:00
case ClassIDReference.VideoClip:
2017-11-10 14:00:09 +00:00
{
var m_VideoClip = new VideoClip(asset, false);
exportable = true;
break;
}
2018-03-27 22:29:28 +00:00
case ClassIDReference.MovieTexture:
2017-11-13 09:46:26 +00:00
{
var m_MovieTexture = new MovieTexture(asset, false);
exportable = true;
break;
}
2018-03-27 22:29:28 +00:00
case ClassIDReference.Sprite:
2018-01-18 00:08:48 +00:00
{
var m_Sprite = new Sprite(asset, false);
exportable = true;
break;
}
}
if (!exportable && displayAll)
{
asset.extension = ".dat";
exportable = true;
}
if (exportable)
{
2017-11-13 09:04:29 +00:00
if (asset.Text == "")
{
asset.Text = asset.TypeString + " #" + asset.uniqueID;
}
asset.SubItems.AddRange(new[] { asset.TypeString, asset.fullSize.ToString() });
//处理同名文件
if (!exportableAssetsHash.Add((asset.TypeString + asset.Text).ToUpper()))
{
asset.Text += " #" + asset.uniqueID;
}
2017-11-13 09:04:29 +00:00
//处理非法文件名
2017-03-31 08:41:36 +00:00
asset.Text = FixFileName(asset.Text);
assetsFile.exportableAssets.Add(asset);
}
2017-02-11 20:57:24 +00:00
ProgressBarPerformStep();
}
if (displayOriginalName)
{
assetsFile.exportableAssets.ForEach(x =>
{
var replacename = ab?.m_Container.Find(y => y.second.asset.m_PathID == x.m_PathID)?.first;
if (!string.IsNullOrEmpty(replacename))
2017-06-22 00:51:20 +00:00
{
var ex = Path.GetExtension(replacename);
x.Text = !string.IsNullOrEmpty(ex) ? replacename.Replace(ex, "") : replacename;
}
});
}
exportableAssets.AddRange(assetsFile.exportableAssets);
}
visibleAssets = exportableAssets;
exportableAssetsHash.Clear();
}
#endregion
#region second loop - build tree structure
fileNodes = new List<GameObject>();
if (buildHierarchyMenuItem)
{
SetProgressBarMaximum(1);
2017-02-11 20:57:24 +00:00
SetProgressBarValue(1);
SetProgressBarMaximum(assetsfileList.Sum(x => x.GameObjectList.Values.Count) + 1);
foreach (var assetsFile in assetsfileList)
{
2017-02-11 20:57:24 +00:00
StatusStripUpdate("Building tree structure from " + Path.GetFileName(assetsFile.filePath));
GameObject fileNode = new GameObject(null);
fileNode.Text = Path.GetFileName(assetsFile.filePath);
fileNode.m_Name = "RootNode";
foreach (var m_GameObject in assetsFile.GameObjectList.Values)
{
2018-03-01 12:01:25 +00:00
//ParseGameObject
foreach (var m_Component in m_GameObject.m_Components)
{
if (m_Component.m_FileID >= 0 && m_Component.m_FileID < assetsfileList.Count)
{
var sourceFile = assetsfileList[m_Component.m_FileID];
if (sourceFile.preloadTable.TryGetValue(m_Component.m_PathID, out var asset))
{
2018-03-27 22:29:28 +00:00
switch (asset.Type)
2018-03-01 12:01:25 +00:00
{
2018-03-27 22:29:28 +00:00
case ClassIDReference.Transform:
2018-03-01 12:01:25 +00:00
{
m_GameObject.m_Transform = m_Component;
break;
}
2018-03-27 22:29:28 +00:00
case ClassIDReference.MeshRenderer:
2018-03-01 12:01:25 +00:00
{
m_GameObject.m_MeshRenderer = m_Component;
break;
}
2018-03-27 22:29:28 +00:00
case ClassIDReference.MeshFilter:
2018-03-01 12:01:25 +00:00
{
m_GameObject.m_MeshFilter = m_Component;
break;
}
2018-03-27 22:29:28 +00:00
case ClassIDReference.SkinnedMeshRenderer:
2018-03-01 12:01:25 +00:00
{
m_GameObject.m_SkinnedMeshRenderer = m_Component;
break;
}
}
}
}
}
//
var parentNode = fileNode;
2018-01-16 22:20:06 +00:00
if (assetsfileList.TryGetTransform(m_GameObject.m_Transform, out var m_Transform))
{
2018-01-16 22:20:06 +00:00
if (assetsfileList.TryGetTransform(m_Transform.m_Father, out var m_Father))
{
//GameObject Parent;
if (assetsfileList.TryGetGameObject(m_Father.m_GameObject, out parentNode))
{
//parentNode = Parent;
}
}
}
parentNode.Nodes.Add(m_GameObject);
2017-02-11 20:57:24 +00:00
ProgressBarPerformStep();
}
if (fileNode.Nodes.Count == 0)
{
fileNode.Text += " (no children)";
}
fileNodes.Add(fileNode);
}
if (File.Exists(mainPath + "\\materials.json"))
{
2018-01-16 22:20:06 +00:00
string matLine;
using (StreamReader reader = File.OpenText(mainPath + "\\materials.json"))
{ matLine = reader.ReadToEnd(); }
jsonMats = new JavaScriptSerializer().Deserialize<Dictionary<string, Dictionary<string, string>>>(matLine);
//var jsonMats = new JavaScriptSerializer().DeserializeObject(matLine);
}
}
#endregion
#region build list of class strucutres
if (buildClassStructuresMenuItem)
{
//group class structures by versionv
foreach (var assetsFile in assetsfileList)
{
2018-01-16 22:20:06 +00:00
if (AllClassStructures.TryGetValue(assetsFile.m_Version, out var curVer))
{
foreach (var uClass in assetsFile.ClassStructures)
{
curVer[uClass.Key] = uClass.Value;
}
}
else
{
AllClassStructures.Add(assetsFile.m_Version, assetsFile.ClassStructures);
}
}
}
#endregion
}
2018-03-01 12:01:25 +00:00
public static string FixFileName(string str)
{
2018-03-01 12:01:25 +00:00
if (str.Length >= 260) return Path.GetRandomFileName();
return Path.GetInvalidFileNameChars().Aggregate(str, (current, c) => current.Replace(c, '_'));
}
2018-03-04 18:35:53 +00:00
public static FileType CheckFileType(MemoryStream stream, out EndianBinaryReader reader)
{
reader = new EndianBinaryReader(stream);
return CheckFileType(reader);
}
public static FileType CheckFileType(string fileName, out EndianBinaryReader reader)
2018-03-01 12:01:25 +00:00
{
reader = new EndianBinaryReader(File.OpenRead(fileName));
2018-03-04 18:35:53 +00:00
return CheckFileType(reader);
}
public static FileType CheckFileType(EndianBinaryReader reader)
{
2018-03-01 12:01:25 +00:00
var signature = reader.ReadStringToNull();
reader.Position = 0;
switch (signature)
{
2018-03-01 12:01:25 +00:00
case "UnityWeb":
case "UnityRaw":
case "\xFA\xFA\xFA\xFA\xFA\xFA\xFA\xFA":
case "UnityFS":
2018-03-04 18:35:53 +00:00
return FileType.BundleFile;
case "UnityWebData1.0":
return FileType.WebFile;
2018-03-01 12:01:25 +00:00
default:
2018-03-04 18:35:53 +00:00
{
var magic = reader.ReadBytes(2);
reader.Position = 0;
if (WebFile.gzipMagic.SequenceEqual(magic))
{
return FileType.WebFile;
}
2018-03-04 21:07:41 +00:00
reader.Position = 0x20;
magic = reader.ReadBytes(6);
reader.Position = 0;
if (WebFile.brotliMagic.SequenceEqual(magic))
{
return FileType.WebFile;
}
2018-03-04 18:35:53 +00:00
return FileType.AssetsFile;
}
2018-03-01 12:01:25 +00:00
}
}
2018-03-01 12:01:25 +00:00
public static string[] ProcessingSplitFiles(List<string> selectFile)
{
var splitFiles = selectFile.Where(x => x.Contains(".split"))
.Select(x => Path.GetDirectoryName(x) + "\\" + Path.GetFileNameWithoutExtension(x))
.Distinct()
.ToList();
selectFile.RemoveAll(x => x.Contains(".split"));
foreach (var file in splitFiles)
{
if (File.Exists(file))
{
2018-03-01 12:01:25 +00:00
selectFile.Add(file);
}
}
return selectFile.Distinct().ToArray();
}
}
}