diff --git a/AssetStudio.sln b/AssetStudio.sln index c93f0f4f..bd189c44 100644 --- a/AssetStudio.sln +++ b/AssetStudio.sln @@ -3,11 +3,13 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.27130.2024 MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AssetStudio", "AssetStudio\AssetStudio.csproj", "{24551E2D-E9B6-4CD6-8F2A-D9F4A13E7853}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AssetStudioGUI", "AssetStudioGUI\AssetStudioGUI.csproj", "{24551E2D-E9B6-4CD6-8F2A-D9F4A13E7853}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AssetStudioFBX", "AssetStudioFBX\AssetStudioFBX.vcxproj", "{4F8EF5EF-732B-49CF-9EB3-B23E19AE6267}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AssetStudioUtility", "AssetStudioUtility\AssetStudioUtility.csproj", "{9131C403-7FE8-444D-9AF5-5FE5DF76FF24}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AssetStudioTools", "AssetStudioTools\AssetStudioTools.csproj", "{9131C403-7FE8-444D-9AF5-5FE5DF76FF24}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AssetStudio", "AssetStudio\AssetStudio.csproj", "{AF56B63C-1764-41B7-9E60-8D485422AC3B}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -33,14 +35,22 @@ Global {4F8EF5EF-732B-49CF-9EB3-B23E19AE6267}.Release|x64.Build.0 = Release|x64 {4F8EF5EF-732B-49CF-9EB3-B23E19AE6267}.Release|x86.ActiveCfg = Release|Win32 {4F8EF5EF-732B-49CF-9EB3-B23E19AE6267}.Release|x86.Build.0 = Release|Win32 - {9131C403-7FE8-444D-9AF5-5FE5DF76FF24}.Debug|x64.ActiveCfg = Debug|Any CPU - {9131C403-7FE8-444D-9AF5-5FE5DF76FF24}.Debug|x64.Build.0 = Debug|Any CPU - {9131C403-7FE8-444D-9AF5-5FE5DF76FF24}.Debug|x86.ActiveCfg = Debug|Any CPU - {9131C403-7FE8-444D-9AF5-5FE5DF76FF24}.Debug|x86.Build.0 = Debug|Any CPU - {9131C403-7FE8-444D-9AF5-5FE5DF76FF24}.Release|x64.ActiveCfg = Release|Any CPU - {9131C403-7FE8-444D-9AF5-5FE5DF76FF24}.Release|x64.Build.0 = Release|Any CPU - {9131C403-7FE8-444D-9AF5-5FE5DF76FF24}.Release|x86.ActiveCfg = Release|Any CPU - {9131C403-7FE8-444D-9AF5-5FE5DF76FF24}.Release|x86.Build.0 = Release|Any CPU + {9131C403-7FE8-444D-9AF5-5FE5DF76FF24}.Debug|x64.ActiveCfg = Debug|x64 + {9131C403-7FE8-444D-9AF5-5FE5DF76FF24}.Debug|x64.Build.0 = Debug|x64 + {9131C403-7FE8-444D-9AF5-5FE5DF76FF24}.Debug|x86.ActiveCfg = Debug|x86 + {9131C403-7FE8-444D-9AF5-5FE5DF76FF24}.Debug|x86.Build.0 = Debug|x86 + {9131C403-7FE8-444D-9AF5-5FE5DF76FF24}.Release|x64.ActiveCfg = Release|x64 + {9131C403-7FE8-444D-9AF5-5FE5DF76FF24}.Release|x64.Build.0 = Release|x64 + {9131C403-7FE8-444D-9AF5-5FE5DF76FF24}.Release|x86.ActiveCfg = Release|x86 + {9131C403-7FE8-444D-9AF5-5FE5DF76FF24}.Release|x86.Build.0 = Release|x86 + {AF56B63C-1764-41B7-9E60-8D485422AC3B}.Debug|x64.ActiveCfg = Debug|Any CPU + {AF56B63C-1764-41B7-9E60-8D485422AC3B}.Debug|x64.Build.0 = Debug|Any CPU + {AF56B63C-1764-41B7-9E60-8D485422AC3B}.Debug|x86.ActiveCfg = Debug|Any CPU + {AF56B63C-1764-41B7-9E60-8D485422AC3B}.Debug|x86.Build.0 = Debug|Any CPU + {AF56B63C-1764-41B7-9E60-8D485422AC3B}.Release|x64.ActiveCfg = Release|Any CPU + {AF56B63C-1764-41B7-9E60-8D485422AC3B}.Release|x64.Build.0 = Release|Any CPU + {AF56B63C-1764-41B7-9E60-8D485422AC3B}.Release|x86.ActiveCfg = Release|Any CPU + {AF56B63C-1764-41B7-9E60-8D485422AC3B}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/AssetStudio/7zip/Common/CRC.cs b/AssetStudio/7zip/Common/CRC.cs index 82cc857e..d03fcec1 100644 --- a/AssetStudio/7zip/Common/CRC.cs +++ b/AssetStudio/7zip/Common/CRC.cs @@ -2,7 +2,7 @@ namespace SevenZip { - class CRC + public class CRC { public static readonly uint[] Table; diff --git a/AssetStudio/AssetStudio.csproj b/AssetStudio/AssetStudio.csproj index 59b0e1b9..23e4f76c 100644 --- a/AssetStudio/AssetStudio.csproj +++ b/AssetStudio/AssetStudio.csproj @@ -1,163 +1,72 @@  - + + Debug - x86 - 8.0.30703 - 2.0 - {24551E2D-E9B6-4CD6-8F2A-D9F4A13E7853} - WinExe + AnyCPU + {AF56B63C-1764-41B7-9E60-8D485422AC3B} + Library Properties AssetStudio AssetStudio v4.0 - - 512 - false - publish\ - true - Disk - false - Foreground - 7 - Days - false - false - true - 0 - 1.0.0.%2a - false - true + true - - Resources\as.ico - - + true - bin\x64\Debug\ - DEBUG;TRACE full - x64 - prompt - MinimumRecommendedRules.ruleset - - - bin\x64\Release\ - TRACE - true - pdbonly - x64 - prompt - MinimumRecommendedRules.ruleset - - - OnBuildSuccess - - - true - bin\x86\Debug\ + false + bin\Debug\ DEBUG;TRACE - full - x86 prompt - MinimumRecommendedRules.ruleset + 4 - - bin\x86\Release\ - TRACE - true + pdbonly - x86 + true + bin\Release\ + TRACE prompt - MinimumRecommendedRules.ruleset + 4 - - Libraries\dnlib.dll - False - - - Libraries\OpenTK.dll - False - - - Libraries\OpenTK.GLControl.dll - False - - - Libraries\SharpDX.dll - False - - - Libraries\SharpDX.D3DCompiler.dll - False - Libraries\SharpDX.Mathematics.dll - False + Libraries\System.Half.dll - False - - - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - + + + + + + + + + + + + + + + + + + + + @@ -173,170 +82,67 @@ - - - - - - - - - - - - - - - - - - - - - Code - + + + - - - - - - - - - - - - - - - + + - - - - - - - - - - Form - - - ExportOptions.cs - - - - + + - - Component - - + - + + + + + + - + - + + + - + + + + + + + + + + + + + + + - - - - - - Form - - - AssetStudioForm.cs - - - - ExportOptions.cs - - - ResXFileCodeGenerator - Resources.Designer.cs - Designer - - - True - Resources.resx - True - - - AssetStudioForm.cs - Designer - - - - SettingsSingleFileGenerator - Settings.Designer.cs - - - True - Settings.settings - True - - - - - False - Microsoft .NET Framework 4 Client Profile %28x86 and x64%29 - true - - - False - .NET Framework 3.5 SP1 - false - - - False - Windows Installer 4.5 - true - - - - - - - - {4f8ef5ef-732b-49cf-9eb3-b23e19ae6267} - AssetStudioFBX - - - {9131c403-7fe8-444d-9af5-5fe5df76ff24} - AssetStudioUtility - - - - + + + + + + + + - - xcopy /y "$(ProjectDir)Libraries" "$(TargetDir)" -xcopy /y "$(ProjectDir)Libraries\$(PlatformName)" "$(TargetDir)" - - \ No newline at end of file diff --git a/AssetStudio/AssetsManager.cs b/AssetStudio/AssetsManager.cs new file mode 100644 index 00000000..0fdf0c1e --- /dev/null +++ b/AssetStudio/AssetsManager.cs @@ -0,0 +1,192 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; +using static AssetStudio.ImportHelper; + +namespace AssetStudio +{ + public class AssetsManager + { + public List assetsFileList = new List(); + internal Dictionary assetsFileIndexCache = new Dictionary(); + internal Dictionary resourceFileReaders = new Dictionary(); + + private List importFiles = new List(); + private HashSet importFilesHash = new HashSet(); + private HashSet assetsfileListHash = new HashSet(); + + public void LoadFiles(string[] files) + { + var path = Path.GetDirectoryName(files[0]); + MergeSplitAssets(path); + var toReadFile = ProcessingSplitFiles(files.ToList()); + Load(toReadFile); + } + + public void LoadFolder(string path) + { + MergeSplitAssets(path, true); + var files = Directory.GetFiles(path, "*.*", SearchOption.AllDirectories).ToList(); + var toReadFile = ProcessingSplitFiles(files); + Load(toReadFile); + } + + private void Load(string[] files) + { + foreach (var file in files) + { + importFiles.Add(file); + importFilesHash.Add(Path.GetFileName(file).ToUpper()); + } + Progress.Reset(); + //use a for loop because list size can change + for (var i = 0; i < importFiles.Count; i++) + { + LoadFile(importFiles[i]); + Progress.Report(i + 1, importFiles.Count); + } + importFiles.Clear(); + importFilesHash.Clear(); + assetsfileListHash.Clear(); + } + + private void LoadFile(string fullName) + { + switch (CheckFileType(fullName, out var reader)) + { + case FileType.AssetsFile: + LoadAssetsFile(fullName, reader); + break; + case FileType.BundleFile: + LoadBundleFile(fullName, reader); + break; + case FileType.WebFile: + LoadWebFile(fullName, reader); + break; + } + } + + private void LoadAssetsFile(string fullName, EndianBinaryReader reader) + { + var fileName = Path.GetFileName(fullName); + if (!assetsfileListHash.Contains(fileName.ToUpper())) + { + Logger.Info($"Loading {fileName}"); + var assetsFile = new SerializedFile(this, fullName, reader); + if (assetsFile.valid) + { + assetsFileList.Add(assetsFile); + assetsfileListHash.Add(assetsFile.upperFileName); + + foreach (var sharedFile in assetsFile.m_Externals) + { + var sharedFilePath = Path.GetDirectoryName(fullName) + "\\" + sharedFile.fileName; + var sharedFileName = sharedFile.fileName; + + if (!importFilesHash.Contains(sharedFileName.ToUpper())) + { + if (!File.Exists(sharedFilePath)) + { + var findFiles = Directory.GetFiles(Path.GetDirectoryName(fullName), sharedFileName, SearchOption.AllDirectories); + if (findFiles.Length > 0) + { + sharedFilePath = findFiles[0]; + } + } + + if (File.Exists(sharedFilePath)) + { + importFiles.Add(sharedFilePath); + importFilesHash.Add(sharedFileName.ToUpper()); + } + } + } + } + else + { + reader.Dispose(); + } + } + else + { + reader.Dispose(); + } + } + + private void LoadAssetsFromMemory(string fullName, EndianBinaryReader reader, string originalPath, string unityVersion = null) + { + var fileName = Path.GetFileName(fullName); + if (!assetsfileListHash.Contains(fileName.ToUpper())) + { + Logger.Info($"Loading {fileName}"); + var assetsFile = new SerializedFile(this, fullName, reader); + if (assetsFile.valid) + { + assetsFile.originalPath = originalPath; + if (assetsFile.header.m_Version < 7) + { + assetsFile.SetVersion(unityVersion); + } + assetsFileList.Add(assetsFile); + assetsfileListHash.Add(assetsFile.upperFileName); + } + else + { + resourceFileReaders.Add(assetsFile.upperFileName, assetsFile.reader); + } + } + } + + private void LoadBundleFile(string fullName, EndianBinaryReader reader, string parentPath = null) + { + var fileName = Path.GetFileName(fullName); + Logger.Info("Decompressing " + fileName); + var bundleFile = new BundleFile(reader, fullName); + reader.Dispose(); + foreach (var file in bundleFile.fileList) + { + var dummyPath = Path.GetDirectoryName(fullName) + "\\" + file.fileName; + LoadAssetsFromMemory(dummyPath, new EndianBinaryReader(file.stream), parentPath ?? fullName, bundleFile.versionEngine); + } + } + + private void LoadWebFile(string fullName, EndianBinaryReader reader) + { + var fileName = Path.GetFileName(fullName); + Logger.Info("Loading " + fileName); + var webFile = new WebFile(reader); + reader.Dispose(); + foreach (var file in webFile.fileList) + { + var dummyPath = Path.GetDirectoryName(fullName) + "\\" + file.fileName; + switch (CheckFileType(file.stream, out reader)) + { + case FileType.AssetsFile: + LoadAssetsFromMemory(dummyPath, reader, fullName); + break; + case FileType.BundleFile: + LoadBundleFile(dummyPath, reader, fullName); + break; + case FileType.WebFile: + LoadWebFile(dummyPath, reader); + break; + } + } + } + + public void Clear() + { + foreach (var assetsFile in assetsFileList) + { + assetsFile.reader.Close(); + } + assetsFileList.Clear(); + foreach (var resourceFileReader in resourceFileReaders) + { + resourceFileReader.Value.Close(); + } + resourceFileReaders.Clear(); + assetsFileIndexCache.Clear(); + } + } +} diff --git a/AssetStudio/StudioClasses/BuildTarget.cs b/AssetStudio/BuildTarget.cs similarity index 100% rename from AssetStudio/StudioClasses/BuildTarget.cs rename to AssetStudio/BuildTarget.cs diff --git a/AssetStudio/BuildType.cs b/AssetStudio/BuildType.cs new file mode 100644 index 00000000..acee230b --- /dev/null +++ b/AssetStudio/BuildType.cs @@ -0,0 +1,20 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace AssetStudio +{ + public class BuildType + { + private string buildType; + + public BuildType(string type) + { + buildType = type; + } + + public bool IsAlpha => buildType == "a"; + public bool IsPatch => buildType == "p"; + } +} diff --git a/AssetStudio/StudioClasses/BundleFile.cs b/AssetStudio/BundleFile.cs similarity index 99% rename from AssetStudio/StudioClasses/BundleFile.cs rename to AssetStudio/BundleFile.cs index 10d22779..f22e0cb6 100644 --- a/AssetStudio/StudioClasses/BundleFile.cs +++ b/AssetStudio/BundleFile.cs @@ -102,7 +102,7 @@ private void GetAssetsFiles(EndianBinaryReader reader, int offset) for (int i = 0; i < fileCount; i++) { var file = new StreamFile(); - file.fileName = reader.ReadStringToNull(); + file.fileName = Path.GetFileName(reader.ReadStringToNull()); int fileOffset = reader.ReadInt32(); fileOffset += offset; int fileSize = reader.ReadInt32(); diff --git a/AssetStudio/StudioClasses/ClassIDType.cs b/AssetStudio/ClassIDType.cs similarity index 100% rename from AssetStudio/StudioClasses/ClassIDType.cs rename to AssetStudio/ClassIDType.cs diff --git a/AssetStudio/Classes/AnimationClip.cs b/AssetStudio/Classes/AnimationClip.cs index f316e183..3cc54c2b 100644 --- a/AssetStudio/Classes/AnimationClip.cs +++ b/AssetStudio/Classes/AnimationClip.cs @@ -1,9 +1,6 @@ using System; using System.Collections.Generic; -using System.ComponentModel; using System.IO; -using System.Linq; -using System.Text; using SharpDX; namespace AssetStudio diff --git a/AssetStudio/Classes/AnimatorOverrideController.cs b/AssetStudio/Classes/AnimatorOverrideController.cs index 78406db5..62c30cca 100644 --- a/AssetStudio/Classes/AnimatorOverrideController.cs +++ b/AssetStudio/Classes/AnimatorOverrideController.cs @@ -5,7 +5,7 @@ namespace AssetStudio { - class AnimatorOverrideController : NamedObject + public class AnimatorOverrideController : NamedObject { public PPtr m_Controller; public PPtr[][] m_Clips; diff --git a/AssetStudio/Classes/AudioClip.cs b/AssetStudio/Classes/AudioClip.cs index 3bd37e4a..74d49cc3 100644 --- a/AssetStudio/Classes/AudioClip.cs +++ b/AssetStudio/Classes/AudioClip.cs @@ -49,7 +49,7 @@ public AudioClip(ObjectReader reader, bool readData) : base(reader) if (reader.byteSize + reader.byteStart - reader.Position != tsize) { m_Offset = reader.ReadInt32(); - m_Source = sourceFile.filePath + ".resS"; + m_Source = sourceFile.fullName + ".resS"; } } else @@ -83,7 +83,7 @@ public AudioClip(ObjectReader reader, bool readData) : base(reader) { if (!string.IsNullOrEmpty(m_Source)) { - m_AudioData = ResourcesHelper.GetData(m_Source, sourceFile.filePath, m_Offset, (int)m_Size); + m_AudioData = ResourcesHelper.GetData(m_Source, sourceFile, m_Offset, (int)m_Size); } else { diff --git a/AssetStudio/Classes/GameObject.cs b/AssetStudio/Classes/GameObject.cs index c59113d0..104f56cf 100644 --- a/AssetStudio/Classes/GameObject.cs +++ b/AssetStudio/Classes/GameObject.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using System.Text; -using System.Windows.Forms; namespace AssetStudio { diff --git a/AssetStudio/Classes/Material.cs b/AssetStudio/Classes/Material.cs index aa9a3913..e9fd858a 100644 --- a/AssetStudio/Classes/Material.cs +++ b/AssetStudio/Classes/Material.cs @@ -38,7 +38,7 @@ public Material(ObjectReader reader) : base(reader) { m_Shader = reader.ReadPPtr(); - if (version[0] == 4 && (version[1] >= 2 || (version[1] == 1 && buildType[0] != "a"))) + if (version[0] == 4 && (version[1] >= 2 || (version[1] == 1 && !buildType.IsAlpha))) { m_ShaderKeywords = new string[reader.ReadInt32()]; for (int i = 0; i < m_ShaderKeywords.Length; i++) diff --git a/AssetStudio/Classes/Mesh.cs b/AssetStudio/Classes/Mesh.cs index 961569f6..44690176 100644 --- a/AssetStudio/Classes/Mesh.cs +++ b/AssetStudio/Classes/Mesh.cs @@ -20,10 +20,10 @@ public sealed class Mesh : NamedObject public float[] m_Vertices; public float[] m_Normals; public float[] m_Colors; + public float[] m_UV0; public float[] m_UV1; public float[] m_UV2; public float[] m_UV3; - public float[] m_UV4; public float[] m_Tangents; public uint[] m_BoneNameHashes; public BlendShapeData m_Shapes; @@ -295,7 +295,7 @@ public Mesh(ObjectReader reader) : base(reader) #endregion #region BlendShapeData for 4.1.0 to 4.2.x, excluding 4.1.0 alpha - if (version[0] == 4 && ((version[1] == 1 && buildType[0] != "a") || (version[1] > 1 && version[1] <= 2))) + if (version[0] == 4 && ((version[1] == 1 && !buildType.IsAlpha) || (version[1] > 1 && version[1] <= 2))) { int m_Shapes_size = reader.ReadInt32(); if (m_Shapes_size > 0) @@ -359,7 +359,7 @@ public Mesh(ObjectReader reader) : base(reader) reader.AlignStream(4); //This is a bug fixed in 2017.3.1p1 and later versions if ((version[0] > 2017 || (version[0] == 2017 && version[1] >= 4)) || //2017.4 - ((version[0] == 2017 && version[1] == 3 && version[2] == 1) && buildType[0] == "p") || //fixed after 2017.3.1px + ((version[0] == 2017 && version[1] == 3 && version[2] == 1) && buildType.IsPatch) || //fixed after 2017.3.1px ((version[0] == 2017 && version[1] == 3) && m_MeshCompression == 0))//2017.3.xfx with no compression { var m_IndexFormat = reader.ReadInt32(); @@ -407,12 +407,12 @@ public Mesh(ObjectReader reader) : base(reader) } int m_UV1_size = reader.ReadInt32(); - m_UV1 = new float[m_UV1_size * 2]; - for (int v = 0; v < m_UV1_size * 2; v++) { m_UV1[v] = reader.ReadSingle(); } + m_UV0 = new float[m_UV1_size * 2]; + for (int v = 0; v < m_UV1_size * 2; v++) { m_UV0[v] = reader.ReadSingle(); } int m_UV2_size = reader.ReadInt32(); - m_UV2 = new float[m_UV2_size * 2]; - for (int v = 0; v < m_UV2_size * 2; v++) { m_UV2[v] = reader.ReadSingle(); } + m_UV1 = new float[m_UV2_size * 2]; + for (int v = 0; v < m_UV2_size * 2; v++) { m_UV1[v] = reader.ReadSingle(); } if (version[0] == 2 && version[1] <= 5) { @@ -631,16 +631,16 @@ public Mesh(ObjectReader reader) : base(reader) m_Colors = componentsFloatArray; break; case 4: //kShaderChannelTexCoord0 - m_UV1 = componentsFloatArray; + m_UV0 = componentsFloatArray; break; case 5: //kShaderChannelTexCoord1 - m_UV2 = componentsFloatArray; + m_UV1 = componentsFloatArray; break; case 6: //kShaderChannelTexCoord2 - m_UV3 = componentsFloatArray; + m_UV2 = componentsFloatArray; break; case 7: //kShaderChannelTexCoord3 - m_UV4 = componentsFloatArray; + m_UV3 = componentsFloatArray; break; //kShaderChannelTexCoord4 8 //kShaderChannelTexCoord5 9 @@ -744,15 +744,15 @@ public Mesh(ObjectReader reader) : base(reader) m_Colors = componentsFloatArray; break; case 3: //kShaderChannelTexCoord0 - m_UV1 = componentsFloatArray; + m_UV0 = componentsFloatArray; break; case 4: //kShaderChannelTexCoord1 - m_UV2 = componentsFloatArray; + m_UV1 = componentsFloatArray; break; case 5: //kShaderChannelTangent & kShaderChannelTexCoord2 if (version[0] >= 5) { - m_UV3 = componentsFloatArray; + m_UV2 = componentsFloatArray; } else { @@ -760,7 +760,7 @@ public Mesh(ObjectReader reader) : base(reader) } break; case 6: //kShaderChannelTexCoord3 - m_UV4 = componentsFloatArray; + m_UV3 = componentsFloatArray; break; case 7: //kShaderChannelTangent m_Tangents = componentsFloatArray; @@ -831,8 +831,8 @@ public Mesh(ObjectReader reader) : base(reader) case 0: m_Vertices = componentsArray; break; case 1: m_Normals = componentsArray; break; case 2: m_Colors = componentsArray; break; - case 3: m_UV1 = componentsArray; break; - case 4: m_UV2 = componentsArray; break; + case 3: m_UV0 = componentsArray; break; + case 4: m_UV1 = componentsArray; break; case 5: m_Tangents = componentsArray; break; } @@ -864,18 +864,18 @@ public Mesh(ObjectReader reader) : base(reader) var m_UV_Packed = new PackedFloatVector(reader); if (m_UV_Packed.m_NumItems > 0) { - m_UV1 = m_UV_Packed.UnpackFloats(2, 4, 0, m_VertexCount); + m_UV0 = m_UV_Packed.UnpackFloats(2, 4, 0, m_VertexCount); if (m_UV_Packed.m_NumItems >= m_VertexCount * 4) { - m_UV2 = m_UV_Packed.UnpackFloats(2, 4, m_VertexCount * 2, m_VertexCount); + m_UV1 = m_UV_Packed.UnpackFloats(2, 4, m_VertexCount * 2, m_VertexCount); } if (m_UV_Packed.m_NumItems >= m_VertexCount * 6) { - m_UV3 = m_UV_Packed.UnpackFloats(2, 4, m_VertexCount * 4, m_VertexCount); + m_UV2 = m_UV_Packed.UnpackFloats(2, 4, m_VertexCount * 4, m_VertexCount); } if (m_UV_Packed.m_NumItems >= m_VertexCount * 8) { - m_UV4 = m_UV_Packed.UnpackFloats(2, 4, m_VertexCount * 6, m_VertexCount); + m_UV3 = m_UV_Packed.UnpackFloats(2, 4, m_VertexCount * 6, m_VertexCount); } } #endregion diff --git a/AssetStudio/Classes/Object.cs b/AssetStudio/Classes/Object.cs index b0dbce77..0b9aa674 100644 --- a/AssetStudio/Classes/Object.cs +++ b/AssetStudio/Classes/Object.cs @@ -7,10 +7,10 @@ namespace AssetStudio { public abstract class Object { - protected AssetsFile sourceFile; + protected SerializedFile sourceFile; public ObjectReader reader; public int[] version; - protected string[] buildType; + protected BuildType buildType; public BuildTarget platform; protected Object(ObjectReader reader) diff --git a/AssetStudio/StudioClasses/PPtr.cs b/AssetStudio/Classes/PPtr.cs similarity index 86% rename from AssetStudio/StudioClasses/PPtr.cs rename to AssetStudio/Classes/PPtr.cs index 984c5a80..69536e97 100644 --- a/AssetStudio/StudioClasses/PPtr.cs +++ b/AssetStudio/Classes/PPtr.cs @@ -1,17 +1,14 @@ -using static AssetStudio.Studio; - -namespace AssetStudio +namespace AssetStudio { public class PPtr { public int m_FileID; public long m_PathID; - //custom - public AssetsFile assetsFile; + public SerializedFile assetsFile; public int index = -2; //-2 - Prepare, -1 - Missing - private bool TryGetAssetsFile(out AssetsFile result) + private bool TryGetAssetsFile(out SerializedFile result) { result = null; if (m_FileID == 0) @@ -22,6 +19,10 @@ private bool TryGetAssetsFile(out AssetsFile result) if (m_FileID > 0 && m_FileID - 1 < assetsFile.m_Externals.Count) { + var assetsManager = assetsFile.assetsManager; + var assetsfileList = assetsManager.assetsFileList; + var assetsFileIndexCache = assetsManager.assetsFileIndexCache; + if (index == -2) { var m_External = assetsFile.m_Externals[m_FileID - 1]; diff --git a/AssetStudio/Classes/Texture2D.cs b/AssetStudio/Classes/Texture2D.cs index 22bb1c5d..8db39d58 100644 --- a/AssetStudio/Classes/Texture2D.cs +++ b/AssetStudio/Classes/Texture2D.cs @@ -105,7 +105,7 @@ public Texture2D(ObjectReader reader, bool readData) : base(reader) { if (!string.IsNullOrEmpty(path)) { - image_data = ResourcesHelper.GetData(path, sourceFile.filePath, offset, image_data_size); + image_data = ResourcesHelper.GetData(path, sourceFile, offset, image_data_size); } else { diff --git a/AssetStudio/Classes/VideoClip.cs b/AssetStudio/Classes/VideoClip.cs index 2c24b09d..59d8fb46 100644 --- a/AssetStudio/Classes/VideoClip.cs +++ b/AssetStudio/Classes/VideoClip.cs @@ -51,7 +51,7 @@ public VideoClip(ObjectReader reader, bool readData) : base(reader) { if (!string.IsNullOrEmpty(m_Source)) { - m_VideoData = ResourcesHelper.GetData(m_Source, sourceFile.filePath, (long)m_Offset, (int)m_Size); + m_VideoData = ResourcesHelper.GetData(m_Source, sourceFile, (long)m_Offset, (int)m_Size); } else { diff --git a/AssetStudio/StudioClasses/CommonString.cs b/AssetStudio/CommonString.cs similarity index 97% rename from AssetStudio/StudioClasses/CommonString.cs rename to AssetStudio/CommonString.cs index feefe6df..55bfdee5 100644 --- a/AssetStudio/StudioClasses/CommonString.cs +++ b/AssetStudio/CommonString.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; +using System.Collections.Generic; namespace AssetStudio { diff --git a/AssetStudio/StudioClasses/EndianBinaryReader.cs b/AssetStudio/EndianBinaryReader.cs similarity index 61% rename from AssetStudio/StudioClasses/EndianBinaryReader.cs rename to AssetStudio/EndianBinaryReader.cs index 12f11dae..c7f8ad04 100644 --- a/AssetStudio/StudioClasses/EndianBinaryReader.cs +++ b/AssetStudio/EndianBinaryReader.cs @@ -14,13 +14,11 @@ public enum EndianType public class EndianBinaryReader : BinaryReader { public EndianType endian; - private byte[] a16 = new byte[2]; - private byte[] a32 = new byte[4]; - private byte[] a64 = new byte[8]; - public EndianBinaryReader(Stream stream, EndianType endian = EndianType.BigEndian) - : base(stream) - { this.endian = endian; } + public EndianBinaryReader(Stream stream, EndianType endian = EndianType.BigEndian) : base(stream) + { + this.endian = endian; + } public long Position { @@ -32,9 +30,9 @@ public override short ReadInt16() { if (endian == EndianType.BigEndian) { - a16 = ReadBytes(2); - Array.Reverse(a16); - return BitConverter.ToInt16(a16, 0); + var buff = ReadBytes(2); + Array.Reverse(buff); + return BitConverter.ToInt16(buff, 0); } return base.ReadInt16(); } @@ -43,9 +41,9 @@ public override int ReadInt32() { if (endian == EndianType.BigEndian) { - a32 = ReadBytes(4); - Array.Reverse(a32); - return BitConverter.ToInt32(a32, 0); + var buff = ReadBytes(4); + Array.Reverse(buff); + return BitConverter.ToInt32(buff, 0); } return base.ReadInt32(); } @@ -54,9 +52,9 @@ public override long ReadInt64() { if (endian == EndianType.BigEndian) { - a64 = ReadBytes(8); - Array.Reverse(a64); - return BitConverter.ToInt64(a64, 0); + var buff = ReadBytes(8); + Array.Reverse(buff); + return BitConverter.ToInt64(buff, 0); } return base.ReadInt64(); } @@ -65,9 +63,9 @@ public override ushort ReadUInt16() { if (endian == EndianType.BigEndian) { - a16 = ReadBytes(2); - Array.Reverse(a16); - return BitConverter.ToUInt16(a16, 0); + var buff = ReadBytes(2); + Array.Reverse(buff); + return BitConverter.ToUInt16(buff, 0); } return base.ReadUInt16(); } @@ -76,9 +74,9 @@ public override uint ReadUInt32() { if (endian == EndianType.BigEndian) { - a32 = ReadBytes(4); - Array.Reverse(a32); - return BitConverter.ToUInt32(a32, 0); + var buff = ReadBytes(4); + Array.Reverse(buff); + return BitConverter.ToUInt32(buff, 0); } return base.ReadUInt32(); } @@ -87,9 +85,9 @@ public override ulong ReadUInt64() { if (endian == EndianType.BigEndian) { - a64 = ReadBytes(8); - Array.Reverse(a64); - return BitConverter.ToUInt64(a64, 0); + var buff = ReadBytes(8); + Array.Reverse(buff); + return BitConverter.ToUInt64(buff, 0); } return base.ReadUInt64(); } @@ -98,9 +96,9 @@ public override float ReadSingle() { if (endian == EndianType.BigEndian) { - a32 = ReadBytes(4); - Array.Reverse(a32); - return BitConverter.ToSingle(a32, 0); + var buff = ReadBytes(4); + Array.Reverse(buff); + return BitConverter.ToSingle(buff, 0); } return base.ReadSingle(); } @@ -109,9 +107,9 @@ public override double ReadDouble() { if (endian == EndianType.BigEndian) { - a64 = ReadBytes(8); - Array.Reverse(a64); - return BitConverter.ToUInt64(a64, 0); + var buff = ReadBytes(8); + Array.Reverse(buff); + return BitConverter.ToUInt64(buff, 0); } return base.ReadDouble(); } diff --git a/AssetStudio/StudioClasses/BinaryReaderExtensions.cs b/AssetStudio/Extensions/BinaryReaderExtensions.cs similarity index 99% rename from AssetStudio/StudioClasses/BinaryReaderExtensions.cs rename to AssetStudio/Extensions/BinaryReaderExtensions.cs index 4141e468..6345f045 100644 --- a/AssetStudio/StudioClasses/BinaryReaderExtensions.cs +++ b/AssetStudio/Extensions/BinaryReaderExtensions.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.IO; -using System.Linq; using System.Text; using SharpDX; diff --git a/AssetStudio/StudioClasses/BinaryWriterExtensions.cs b/AssetStudio/Extensions/BinaryWriterExtensions.cs similarity index 95% rename from AssetStudio/StudioClasses/BinaryWriterExtensions.cs rename to AssetStudio/Extensions/BinaryWriterExtensions.cs index 934b0755..81f488fa 100644 --- a/AssetStudio/StudioClasses/BinaryWriterExtensions.cs +++ b/AssetStudio/Extensions/BinaryWriterExtensions.cs @@ -1,7 +1,5 @@ using System; -using System.Collections.Generic; using System.IO; -using System.Linq; using System.Text; namespace AssetStudio diff --git a/AssetStudio/StudioClasses/StreamExtensions.cs b/AssetStudio/Extensions/StreamExtensions.cs similarity index 86% rename from AssetStudio/StudioClasses/StreamExtensions.cs rename to AssetStudio/Extensions/StreamExtensions.cs index 87757cab..3f754036 100644 --- a/AssetStudio/StudioClasses/StreamExtensions.cs +++ b/AssetStudio/Extensions/StreamExtensions.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; +using System.IO; namespace AssetStudio { diff --git a/AssetStudio/StudioClasses/FileIdentifier.cs b/AssetStudio/FileIdentifier.cs similarity index 100% rename from AssetStudio/StudioClasses/FileIdentifier.cs rename to AssetStudio/FileIdentifier.cs diff --git a/AssetStudio/ImportHelper.cs b/AssetStudio/ImportHelper.cs new file mode 100644 index 00000000..440ce716 --- /dev/null +++ b/AssetStudio/ImportHelper.cs @@ -0,0 +1,104 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace AssetStudio +{ + public enum FileType + { + AssetsFile, + BundleFile, + WebFile + } + + public static class ImportHelper + { + public static void MergeSplitAssets(string path, bool allDirectories = false) + { + var splitFiles = Directory.GetFiles(path, "*.split0", allDirectories ? SearchOption.AllDirectories : SearchOption.TopDirectoryOnly); + foreach (var splitFile in splitFiles) + { + var destFile = Path.GetFileNameWithoutExtension(splitFile); + var destPath = Path.GetDirectoryName(splitFile) + "\\"; + var destFull = destPath + destFile; + if (!File.Exists(destFull)) + { + var splitParts = Directory.GetFiles(destPath, destFile + ".split*"); + using (var destStream = File.Create(destFull)) + { + for (int i = 0; i < splitParts.Length; i++) + { + var splitPart = destFull + ".split" + i; + using (var sourceStream = File.OpenRead(splitPart)) + { + sourceStream.CopyTo(destStream); + } + } + } + } + } + } + + public static string[] ProcessingSplitFiles(List 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)) + { + selectFile.Add(file); + } + } + return selectFile.Distinct().ToArray(); + } + + public static FileType CheckFileType(Stream stream, out EndianBinaryReader reader) + { + reader = new EndianBinaryReader(stream); + return CheckFileType(reader); + } + + public static FileType CheckFileType(string fileName, out EndianBinaryReader reader) + { + reader = new EndianBinaryReader(File.OpenRead(fileName)); + return CheckFileType(reader); + } + + private static FileType CheckFileType(EndianBinaryReader reader) + { + var signature = reader.ReadStringToNull(); + reader.Position = 0; + switch (signature) + { + case "UnityWeb": + case "UnityRaw": + case "\xFA\xFA\xFA\xFA\xFA\xFA\xFA\xFA": + case "UnityFS": + return FileType.BundleFile; + case "UnityWebData1.0": + return FileType.WebFile; + default: + { + var magic = reader.ReadBytes(2); + reader.Position = 0; + if (WebFile.gzipMagic.SequenceEqual(magic)) + { + return FileType.WebFile; + } + reader.Position = 0x20; + magic = reader.ReadBytes(6); + reader.Position = 0; + if (WebFile.brotliMagic.SequenceEqual(magic)) + { + return FileType.WebFile; + } + return FileType.AssetsFile; + } + } + } + } +} diff --git a/AssetStudio/StudioClasses/LocalSerializedObjectIdentifier.cs b/AssetStudio/LocalSerializedObjectIdentifier.cs similarity index 100% rename from AssetStudio/StudioClasses/LocalSerializedObjectIdentifier.cs rename to AssetStudio/LocalSerializedObjectIdentifier.cs diff --git a/AssetStudio/Lz4DecoderStream.cs b/AssetStudio/Lz4DecoderStream.cs index 1ff219e7..e767bb70 100644 --- a/AssetStudio/Lz4DecoderStream.cs +++ b/AssetStudio/Lz4DecoderStream.cs @@ -14,7 +14,7 @@ public Lz4DecoderStream(Stream input, long inputLength = long.MaxValue) Reset(input, inputLength); } - public void Reset(Stream input, long inputLength = long.MaxValue) + private void Reset(Stream input, long inputLength = long.MaxValue) { this.inputLength = inputLength; this.input = input; @@ -40,6 +40,7 @@ protected override void Dispose(bool disposing) input.Close(); } input = null; + decodeBuffer = null; } finally { diff --git a/AssetStudio/StudioClasses/ObjectInfo.cs b/AssetStudio/ObjectInfo.cs similarity index 100% rename from AssetStudio/StudioClasses/ObjectInfo.cs rename to AssetStudio/ObjectInfo.cs diff --git a/AssetStudio/StudioClasses/ObjectReader.cs b/AssetStudio/ObjectReader.cs similarity index 87% rename from AssetStudio/StudioClasses/ObjectReader.cs rename to AssetStudio/ObjectReader.cs index e376d644..952ec21d 100644 --- a/AssetStudio/StudioClasses/ObjectReader.cs +++ b/AssetStudio/ObjectReader.cs @@ -8,7 +8,7 @@ namespace AssetStudio { public class ObjectReader : EndianBinaryReader { - public AssetsFile assetsFile; + public SerializedFile assetsFile; public long m_PathID; public uint byteStart; public uint byteSize; @@ -18,11 +18,9 @@ public class ObjectReader : EndianBinaryReader private uint m_Version; public int[] version => assetsFile.version; - public string[] buildType => assetsFile.buildType; + public BuildType buildType => assetsFile.buildType; - public string exportName; //TODO Remove it - - public ObjectReader(EndianBinaryReader reader, AssetsFile assetsFile, ObjectInfo objectInfo) : base(reader.BaseStream, reader.endian) + public ObjectReader(EndianBinaryReader reader, SerializedFile assetsFile, ObjectInfo objectInfo) : base(reader.BaseStream, reader.endian) { this.assetsFile = assetsFile; m_PathID = objectInfo.m_PathID; diff --git a/AssetStudio/Properties/AssemblyInfo.cs b/AssetStudio/Properties/AssemblyInfo.cs index 62522281..cd38ea16 100644 --- a/AssetStudio/Properties/AssemblyInfo.cs +++ b/AssetStudio/Properties/AssemblyInfo.cs @@ -2,35 +2,35 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. +// 有关程序集的一般信息由以下 +// 控制。更改这些特性值可修改 +// 与程序集关联的信息。 [assembly: AssemblyTitle("AssetStudio")] -[assembly: AssemblyDescription("AssetStudio is a tool for exploring, extracting and exporting assets and assetbundles.")] +[assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] [assembly: AssemblyProduct("AssetStudio")] -[assembly: AssemblyCopyright("")] +[assembly: AssemblyCopyright("Copyright © 2018")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. +// 将 ComVisible 设置为 false 会使此程序集中的类型 +//对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型 +//请将此类型的 ComVisible 特性设置为 true。 [assembly: ComVisible(false)] -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("05c04c20-dd89-4895-9f06-33d5cfbfe925")] +// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID +[assembly: Guid("af56b63c-1764-41b7-9e60-8d485422ac3b")] -// Version information for an assembly consists of the following four values: +// 程序集的版本信息由下列四个值组成: // -// Major Version -// Minor Version -// Build Number -// Revision +// 主版本 +// 次版本 +// 生成号 +// 修订号 // -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: +// 可以指定所有值,也可以使用以下所示的 "*" 预置版本号和修订号 +//通过使用 "*",如下所示: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("0.11.0.0")] -[assembly: AssemblyFileVersion("0.11.0.0")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/AssetStudio/StudioClasses/ResourcesHelper.cs b/AssetStudio/ResourcesHelper.cs similarity index 57% rename from AssetStudio/StudioClasses/ResourcesHelper.cs rename to AssetStudio/ResourcesHelper.cs index edb9a390..bdc5e24d 100644 --- a/AssetStudio/StudioClasses/ResourcesHelper.cs +++ b/AssetStudio/ResourcesHelper.cs @@ -3,26 +3,26 @@ using System.IO; using System.Linq; using System.Text; -using System.Windows.Forms; namespace AssetStudio { - public static class ResourcesHelper + internal static class ResourcesHelper { - public static byte[] GetData(string path, string sourceFilePath, long offset, int size) + public static byte[] GetData(string path, SerializedFile assetsFile, long offset, int size) { var resourceFileName = Path.GetFileName(path); - if (Studio.resourceFileReaders.TryGetValue(resourceFileName.ToUpper(), out var reader)) + if (assetsFile.assetsManager.resourceFileReaders.TryGetValue(resourceFileName.ToUpper(), out var reader)) { reader.Position = offset; return reader.ReadBytes(size); } - var resourceFilePath = Path.GetDirectoryName(sourceFilePath) + "\\" + resourceFileName; + var currentDirectory = Path.GetDirectoryName(assetsFile.fullName); + var resourceFilePath = currentDirectory + "\\" + resourceFileName; if (!File.Exists(resourceFilePath)) { - var findFiles = Directory.GetFiles(Path.GetDirectoryName(sourceFilePath), resourceFileName, SearchOption.AllDirectories); + var findFiles = Directory.GetFiles(currentDirectory, resourceFileName, SearchOption.AllDirectories); if (findFiles.Length > 0) { resourceFilePath = findFiles[0]; @@ -36,11 +36,8 @@ public static byte[] GetData(string path, string sourceFilePath, long offset, in return resourceReader.ReadBytes(size); } } - else - { - MessageBox.Show($"can't find the resource file {resourceFileName}"); - return null; - } + + throw new FileNotFoundException($"Can't find the resource file {resourceFileName}"); } } } diff --git a/AssetStudio/StudioClasses/AssetsFile.cs b/AssetStudio/SerializedFile.cs similarity index 88% rename from AssetStudio/StudioClasses/AssetsFile.cs rename to AssetStudio/SerializedFile.cs index af9d264c..f64095c1 100644 --- a/AssetStudio/StudioClasses/AssetsFile.cs +++ b/AssetStudio/SerializedFile.cs @@ -6,41 +6,41 @@ namespace AssetStudio { - public class AssetsFile + public class SerializedFile { + public AssetsManager assetsManager; public EndianBinaryReader reader; - public string filePath; - public string parentPath; + public string fullName; + public string originalPath; public string fileName; public string upperFileName; public int[] version = { 0, 0, 0, 0 }; - public string[] buildType; - public string platformStr; + public BuildType buildType; public bool valid; public Dictionary ObjectReaders = new Dictionary(); public Dictionary GameObjects = new Dictionary(); public Dictionary Transforms = new Dictionary(); - //class SerializedFile public SerializedFileHeader header; private EndianType m_FileEndianess; public string unityVersion = "2.5.0f5"; public BuildTarget m_TargetPlatform = BuildTarget.UnknownPlatform; private bool m_EnableTypeTree = true; public List m_Types; - public Dictionary m_Objects; + private List m_Objects; private List m_ScriptTypes; public List m_Externals; - public AssetsFile(string fullName, EndianBinaryReader reader) + public SerializedFile(AssetsManager assetsManager, string fullName, EndianBinaryReader reader) { + this.assetsManager = assetsManager; this.reader = reader; - filePath = fullName; + this.fullName = fullName; fileName = Path.GetFileName(fullName); upperFileName = fileName.ToUpper(); try { - //SerializedFile::ReadHeader + //ReadHeader header = new SerializedFileHeader(); header.m_MetadataSize = reader.ReadUInt32(); header.m_FileSize = reader.ReadUInt32(); @@ -59,7 +59,7 @@ public AssetsFile(string fullName, EndianBinaryReader reader) m_FileEndianess = (EndianType)reader.ReadByte(); } - //SerializedFile::ReadMetadata + //ReadMetadata if (m_FileEndianess == EndianType.LittleEndian) { reader.endian = EndianType.LittleEndian; @@ -67,6 +67,7 @@ public AssetsFile(string fullName, EndianBinaryReader reader) if (header.m_Version >= 7) { unityVersion = reader.ReadStringToNull(); + SetVersion(unityVersion); } if (header.m_Version >= 8) { @@ -76,13 +77,12 @@ public AssetsFile(string fullName, EndianBinaryReader reader) m_TargetPlatform = BuildTarget.UnknownPlatform; } } - platformStr = m_TargetPlatform.ToString(); if (header.m_Version >= 13) { m_EnableTypeTree = reader.ReadBoolean(); } - //Read types + //ReadTypes int typeCount = reader.ReadInt32(); m_Types = new List(typeCount); for (int i = 0; i < typeCount; i++) @@ -95,9 +95,9 @@ public AssetsFile(string fullName, EndianBinaryReader reader) var bigIDEnabled = reader.ReadInt32(); } - //Read Objects + //ReadObjects int objectCount = reader.ReadInt32(); - m_Objects = new Dictionary(objectCount); + m_Objects = new List(objectCount); for (int i = 0; i < objectCount; i++) { var objectInfo = new ObjectInfo(); @@ -130,23 +130,11 @@ public AssetsFile(string fullName, EndianBinaryReader reader) { var stripped = reader.ReadByte(); } - m_Objects.Add(objectInfo.m_PathID, objectInfo); + m_Objects.Add(objectInfo); //Create Reader var objectReader = new ObjectReader(reader, this, objectInfo); ObjectReaders.Add(objectInfo.m_PathID, objectReader); - - #region read BuildSettings to get version for version 2.x files - if (objectReader.type == ClassIDType.BuildSettings && header.m_Version == 6) - { - var nextAsset = reader.Position; - - var buildSettings = new BuildSettings(objectReader); - unityVersion = buildSettings.m_Version; - - reader.Position = nextAsset; - } - #endregion } if (header.m_Version >= 11) @@ -194,10 +182,6 @@ public AssetsFile(string fullName, EndianBinaryReader reader) //var userInformation = reader.ReadStringToNull(); } - buildType = Regex.Replace(unityVersion, @"\d", "").Split(new[] { "." }, StringSplitOptions.RemoveEmptyEntries); - var versionSplit = Regex.Replace(unityVersion, @"\D", ".").Split(new[] { "." }, StringSplitOptions.RemoveEmptyEntries); - version = versionSplit.Select(int.Parse).ToArray(); - valid = true; } catch @@ -206,6 +190,14 @@ public AssetsFile(string fullName, EndianBinaryReader reader) } } + public void SetVersion(string stringVersion) + { + var buildSplit = Regex.Replace(stringVersion, @"\d", "").Split(new[] { "." }, StringSplitOptions.RemoveEmptyEntries); + buildType = new BuildType(buildSplit[0]); + var versionSplit = Regex.Replace(stringVersion, @"\D", ".").Split(new[] { "." }, StringSplitOptions.RemoveEmptyEntries); + version = versionSplit.Select(int.Parse).ToArray(); + } + private SerializedType ReadSerializedType() { var type = new SerializedType(); diff --git a/AssetStudio/StudioClasses/SerializedFileHeader.cs b/AssetStudio/SerializedFileHeader.cs similarity index 100% rename from AssetStudio/StudioClasses/SerializedFileHeader.cs rename to AssetStudio/SerializedFileHeader.cs diff --git a/AssetStudio/StudioClasses/SerializedType.cs b/AssetStudio/SerializedType.cs similarity index 100% rename from AssetStudio/StudioClasses/SerializedType.cs rename to AssetStudio/SerializedType.cs diff --git a/AssetStudio/StudioClasses/SevenZipHelper.cs b/AssetStudio/SevenZipHelper.cs similarity index 89% rename from AssetStudio/StudioClasses/SevenZipHelper.cs rename to AssetStudio/SevenZipHelper.cs index e3a550e5..c91b0960 100644 --- a/AssetStudio/StudioClasses/SevenZipHelper.cs +++ b/AssetStudio/SevenZipHelper.cs @@ -11,18 +11,18 @@ public static MemoryStream StreamDecompress(MemoryStream inStream) { var decoder = new Decoder(); - inStream.Seek(0, 0); + inStream.Seek(0, SeekOrigin.Begin); var newOutStream = new MemoryStream(); var properties = new byte[5]; if (inStream.Read(properties, 0, 5) != 5) - throw (new Exception("input .lzma is too short")); + throw new Exception("input .lzma is too short"); long outSize = 0; for (var i = 0; i < 8; i++) { var v = inStream.ReadByte(); if (v < 0) - throw (new Exception("Can't Read 1")); + throw new Exception("Can't Read 1"); outSize |= ((long)(byte)v) << (8 * i); } decoder.SetDecoderProperties(properties); diff --git a/AssetStudio/StudioClasses/GameObjectTreeNode.cs b/AssetStudio/StudioClasses/GameObjectTreeNode.cs deleted file mode 100644 index a8bc7a2b..00000000 --- a/AssetStudio/StudioClasses/GameObjectTreeNode.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Windows.Forms; - -namespace AssetStudio -{ - public class GameObjectTreeNode : TreeNode - { - public GameObject gameObject; - - public GameObjectTreeNode(GameObject gameObject) - { - if (gameObject != null) - { - this.gameObject = gameObject; - Text = gameObject.m_Name; - } - } - } -} diff --git a/AssetStudio/StudioClasses/Importer.cs b/AssetStudio/StudioClasses/Importer.cs deleted file mode 100644 index 2a10e835..00000000 --- a/AssetStudio/StudioClasses/Importer.cs +++ /dev/null @@ -1,183 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using System.Text.RegularExpressions; -using static AssetStudio.Studio; - -namespace AssetStudio -{ - static class Importer - { - public static List importFiles = new List(); //files to load - public static HashSet importFilesHash = new HashSet(); //to improve the loading speed - public static HashSet assetsfileListHash = new HashSet(); //to improve the loading speed - - public static void LoadFile(string fullName) - { - switch (CheckFileType(fullName, out var reader)) - { - case FileType.AssetsFile: - LoadAssetsFile(fullName, reader); - break; - case FileType.BundleFile: - LoadBundleFile(fullName, reader); - break; - case FileType.WebFile: - LoadWebFile(fullName, reader); - break; - } - } - - private static void LoadAssetsFile(string fullName, EndianBinaryReader reader, string parentPath = null) - { - var fileName = Path.GetFileName(fullName); - StatusStripUpdate("Loading " + fileName); - if (!assetsfileListHash.Contains(fileName.ToUpper())) - { - var assetsFile = new AssetsFile(fullName, reader); - if (assetsFile.valid) - { - assetsFile.parentPath = parentPath; - assetsfileList.Add(assetsFile); - assetsfileListHash.Add(assetsFile.upperFileName); - - #region for 2.6.x find mainData and get string version - if (assetsFile.header.m_Version == 6 && fileName != "mainData") - { - var mainDataFile = assetsfileList.Find(aFile => aFile.fileName == "mainData"); - if (mainDataFile != null) - { - assetsFile.unityVersion = mainDataFile.unityVersion; - assetsFile.version = mainDataFile.version; - assetsFile.buildType = mainDataFile.buildType; - } - else if (File.Exists(Path.GetDirectoryName(fullName) + "\\mainData")) - { - mainDataFile = new AssetsFile(Path.GetDirectoryName(fullName) + "\\mainData", new EndianBinaryReader(File.OpenRead(Path.GetDirectoryName(fullName) + "\\mainData"))); - assetsFile.unityVersion = mainDataFile.unityVersion; - assetsFile.version = mainDataFile.version; - assetsFile.buildType = mainDataFile.buildType; - } - } - #endregion - - int value = 0; - foreach (var sharedFile in assetsFile.m_Externals) - { - var sharedFilePath = Path.GetDirectoryName(fullName) + "\\" + sharedFile.fileName; - var sharedFileName = sharedFile.fileName; - - if (!importFilesHash.Contains(sharedFileName.ToUpper())) - { - if (!File.Exists(sharedFilePath)) - { - var findFiles = Directory.GetFiles(Path.GetDirectoryName(fullName), sharedFileName, SearchOption.AllDirectories); - if (findFiles.Length > 0) - { - sharedFilePath = findFiles[0]; - } - } - - if (File.Exists(sharedFilePath)) - { - importFiles.Add(sharedFilePath); - importFilesHash.Add(sharedFileName.ToUpper()); - value++; - } - } - } - if (value > 0) - ProgressBarMaximumAdd(value); - } - else - reader.Dispose(); - } - } - - private static void LoadBundleFile(string fullName, EndianBinaryReader reader, string parentPath = null) - { - var fileName = Path.GetFileName(fullName); - StatusStripUpdate("Decompressing " + fileName); - var bundleFile = new BundleFile(reader, fullName); - reader.Dispose(); - foreach (var file in bundleFile.fileList) - { - if (!assetsfileListHash.Contains(file.fileName.ToUpper())) - { - StatusStripUpdate("Loading " + file.fileName); - var assetsFile = new AssetsFile(Path.GetDirectoryName(fullName) + "\\" + file.fileName, new EndianBinaryReader(file.stream)); - if (assetsFile.valid) - { - assetsFile.parentPath = parentPath ?? fullName; - - if (assetsFile.header.m_Version == 6) //2.6.x and earlier don't have a string version before the preload table - { - //make use of the bundle file version - assetsFile.unityVersion = bundleFile.versionEngine; - assetsFile.version = Regex.Matches(bundleFile.versionEngine, @"\d").Cast().Select(m => int.Parse(m.Value)).ToArray(); - assetsFile.buildType = Regex.Replace(bundleFile.versionEngine, @"\d", "").Split(new[] { "." }, StringSplitOptions.RemoveEmptyEntries); - } - - assetsfileList.Add(assetsFile); - assetsfileListHash.Add(assetsFile.upperFileName); - } - else - { - resourceFileReaders.Add(assetsFile.upperFileName, assetsFile.reader); - } - } - } - } - - private static void LoadWebFile(string fullName, EndianBinaryReader reader) - { - var fileName = Path.GetFileName(fullName); - StatusStripUpdate("Loading " + fileName); - var webFile = new WebFile(reader); - reader.Dispose(); - foreach (var file in webFile.fileList) - { - var dummyName = Path.GetDirectoryName(fullName) + "\\" + file.fileName; - switch (CheckFileType(file.stream, out reader)) - { - case FileType.AssetsFile: - LoadAssetsFile(dummyName, reader, fullName); - break; - case FileType.BundleFile: - LoadBundleFile(dummyName, reader, fullName); - break; - case FileType.WebFile: - LoadWebFile(dummyName, reader); - break; - } - resourceFileReaders.Add(file.fileName.ToUpper(), reader); - } - } - - public static void MergeSplitAssets(string dirPath) - { - string[] splitFiles = Directory.GetFiles(dirPath, "*.split0"); - foreach (var splitFile in splitFiles) - { - string destFile = Path.GetFileNameWithoutExtension(splitFile); - string destPath = Path.GetDirectoryName(splitFile) + "\\"; - var destFull = destPath + destFile; - if (!File.Exists(destFull)) - { - string[] splitParts = Directory.GetFiles(destPath, destFile + ".split*"); - using (var destStream = File.Create(destFull)) - { - for (int i = 0; i < splitParts.Length; i++) - { - string splitPart = destFull + ".split" + i; - using (var sourceStream = File.OpenRead(splitPart)) - sourceStream.CopyTo(destStream); - } - } - } - } - } - } -} diff --git a/AssetStudio/StudioClasses/Studio.cs b/AssetStudio/StudioClasses/Studio.cs deleted file mode 100644 index 1fb80dd6..00000000 --- a/AssetStudio/StudioClasses/Studio.cs +++ /dev/null @@ -1,759 +0,0 @@ -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 static AssetStudio.Exporter; - -namespace AssetStudio -{ - internal static class Studio - { - public static List assetsfileList = new List(); //loaded files - public static Dictionary assetsFileIndexCache = new Dictionary(); - public static Dictionary resourceFileReaders = new Dictionary(); //use for read res files - public static List exportableAssets = new List(); //used to hold all assets while the ListView is filtered - private static HashSet assetsNameHash = new HashSet(); //avoid the same name asset - public static List visibleAssets = new List(); //used to build the ListView from all or filtered assets - public static Dictionary> AllTypeMap = new Dictionary>(); - public static List treeNodeCollection = new List(); - public static Dictionary treeNodeDictionary = new Dictionary(); - public static string mainPath; - public static string productName = string.Empty; - - //UI - public static Action SetProgressBarValue; - public static Action SetProgressBarMaximum; - public static Action ProgressBarPerformStep; - public static Action StatusStripUpdate; - public static Action ProgressBarMaximumAdd; - - public enum FileType - { - AssetsFile, - BundleFile, - WebFile - } - - public static FileType CheckFileType(Stream stream, out EndianBinaryReader reader) - { - reader = new EndianBinaryReader(stream); - return CheckFileType(reader); - } - - public static FileType CheckFileType(string fileName, out EndianBinaryReader reader) - { - reader = new EndianBinaryReader(File.OpenRead(fileName)); - return CheckFileType(reader); - } - - private static FileType CheckFileType(EndianBinaryReader reader) - { - var signature = reader.ReadStringToNull(); - reader.Position = 0; - switch (signature) - { - case "UnityWeb": - case "UnityRaw": - case "\xFA\xFA\xFA\xFA\xFA\xFA\xFA\xFA": - case "UnityFS": - return FileType.BundleFile; - case "UnityWebData1.0": - return FileType.WebFile; - default: - { - var magic = reader.ReadBytes(2); - reader.Position = 0; - if (WebFile.gzipMagic.SequenceEqual(magic)) - { - return FileType.WebFile; - } - reader.Position = 0x20; - magic = reader.ReadBytes(6); - reader.Position = 0; - if (WebFile.brotliMagic.SequenceEqual(magic)) - { - return FileType.WebFile; - } - return FileType.AssetsFile; - } - } - } - - public static void ExtractFile(string[] fileNames) - { - ThreadPool.QueueUserWorkItem(state => - { - int extractedCount = 0; - foreach (var fileName in fileNames) - { - var type = 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(); - ProgressBarPerformStep(); - } - StatusStripUpdate($"Finished extracting {extractedCount} files."); - }); - } - - private static int ExtractBundleFile(string bundleFileName, EndianBinaryReader reader) - { - StatusStripUpdate($"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) - { - StatusStripUpdate($"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 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 BuildAssetStructures(bool loadAssets, bool displayAll, bool buildHierarchy, bool buildClassStructures, bool displayOriginalName) - { - var tempDic = new Dictionary(); - // first loop - read asset data & create list - if (loadAssets) - { - SetProgressBarValue(0); - SetProgressBarMaximum(assetsfileList.Sum(x => x.ObjectReaders.Count)); - StatusStripUpdate("Building asset list..."); - - var fileIDfmt = "D" + assetsfileList.Count.ToString().Length; - - for (var i = 0; i < assetsfileList.Count; i++) - { - var assetsFile = assetsfileList[i]; - var tempExportableAssets = new List(); - var fileID = i.ToString(fileIDfmt); - AssetBundle ab = null; - var j = 0; - var assetIDfmt = "D" + assetsFile.m_Objects.Count.ToString().Length; - foreach (var objectReader in assetsFile.ObjectReaders.Values) - { - var assetItem = new AssetItem(objectReader); - tempDic.Add(objectReader, assetItem); - assetItem.UniqueID = fileID + j.ToString(assetIDfmt); - 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); - } - objectReader.exportName = assetItem.Text; - - ProgressBarPerformStep(); - j++; - } - if (displayOriginalName) - { - 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; - x.reader.exportName = x.Text; - } - } - } - exportableAssets.AddRange(tempExportableAssets); - tempExportableAssets.Clear(); - } - - visibleAssets = exportableAssets; - assetsNameHash.Clear(); - } - - // second loop - build tree structure - if (buildHierarchy) - { - var gameObjectCount = assetsfileList.Sum(x => x.GameObjects.Count); - if (gameObjectCount > 0) - { - SetProgressBarValue(0); - SetProgressBarMaximum(gameObjectCount); - StatusStripUpdate("Building tree structure..."); - - foreach (var assetsFile in assetsfileList) - { - var fileNode = new GameObjectTreeNode(null); //RootNode - fileNode.Text = assetsFile.fileName; - - 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 = m_GameObject.reader.exportName; - asset.exportName = m_GameObject.reader.exportName; - 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); - - ProgressBarPerformStep(); - } - - if (fileNode.Nodes.Count > 0) - { - treeNodeCollection.Add(fileNode); - } - } - } - } - tempDic.Clear(); - - // build list of class strucutres - if (buildClassStructures) - { - foreach (var assetsFile in 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(); - 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 string[] ProcessingSplitFiles(List 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)) - { - selectFile.Add(file); - } - } - return selectFile.Distinct().ToArray(); - } - - public static void ExportAssets(string savePath, List toExportAssets, int assetGroupSelectedIndex, bool openAfterExport) - { - ThreadPool.QueueUserWorkItem(state => - { - Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US"); - - int toExport = toExportAssets.Count; - int exportedCount = 0; - - SetProgressBarValue(0); - SetProgressBarMaximum(toExport); - foreach (var asset in toExportAssets) - { - var exportpath = savePath + "\\"; - if (assetGroupSelectedIndex == 1) - { - exportpath += Path.GetFileNameWithoutExtension(asset.sourceFile.filePath) + "_export\\"; - } - else if (assetGroupSelectedIndex == 0) - { - exportpath = savePath + "\\" + asset.TypeString + "\\"; - } - StatusStripUpdate($"Exporting {asset.TypeString}: {asset.Text}"); - var reader = asset.reader; - try - { - switch (asset.Type) - { - case ClassIDType.Texture2D: - if (ExportTexture2D(reader, exportpath, true)) - { - exportedCount++; - } - break; - case ClassIDType.AudioClip: - if (ExportAudioClip(reader, exportpath)) - { - exportedCount++; - } - break; - case ClassIDType.Shader: - if (ExportShader(reader, exportpath)) - { - exportedCount++; - } - break; - case ClassIDType.TextAsset: - if (ExportTextAsset(reader, exportpath)) - { - exportedCount++; - } - break; - case ClassIDType.MonoBehaviour: - if (ExportMonoBehaviour(reader, exportpath)) - { - exportedCount++; - } - break; - case ClassIDType.Font: - if (ExportFont(reader, exportpath)) - { - exportedCount++; - } - break; - case ClassIDType.Mesh: - if (ExportMesh(reader, exportpath)) - { - exportedCount++; - } - break; - case ClassIDType.VideoClip: - if (ExportVideoClip(reader, exportpath)) - { - exportedCount++; - } - break; - case ClassIDType.MovieTexture: - if (ExportMovieTexture(reader, exportpath)) - { - exportedCount++; - } - break; - case ClassIDType.Sprite: - if (ExportSprite(reader, exportpath)) - { - exportedCount++; - } - break; - case ClassIDType.Animator: - if (ExportAnimator(reader, exportpath)) - { - exportedCount++; - } - break; - case ClassIDType.AnimationClip: - break; - default: - if (ExportRawFile(reader, exportpath)) - { - exportedCount++; - } - break; - - } - } - catch (Exception ex) - { - MessageBox.Show($"Export {asset.Type}:{asset.Text} error\r\n{ex.Message}\r\n{ex.StackTrace}"); - } - ProgressBarPerformStep(); - } - - var statusText = exportedCount == 0 ? "Nothing exported." : $"Finished exporting {exportedCount} assets."; - - if (toExport > exportedCount) - { - statusText += $" {toExport - exportedCount} assets skipped (not extractable or files already exist)"; - } - - StatusStripUpdate(statusText); - - if (openAfterExport && exportedCount > 0) - { - Process.Start(savePath); - } - }); - } - - public static void ExportSplitObjects(string savePath, TreeNodeCollection nodes) - { - ThreadPool.QueueUserWorkItem(state => - { - foreach (GameObjectTreeNode node in nodes) - { - //遍历一级子节点 - foreach (GameObjectTreeNode j in node.Nodes) - { - ProgressBarPerformStep(); - //收集所有子节点 - var gameObjects = new List(); - 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 - StatusStripUpdate($"Exporting {filename}.fbx"); - try - { - ExportGameObject(j.gameObject, targetPath); - } - catch (Exception ex) - { - MessageBox.Show($"{ex.Message}\r\n{ex.StackTrace}"); - } - StatusStripUpdate($"Finished exporting {filename}.fbx"); - } - } - StatusStripUpdate("Finished"); - }); - } - - private static void CollectNode(GameObjectTreeNode node, List gameObjects) - { - gameObjects.Add(node.gameObject); - foreach (GameObjectTreeNode i in node.Nodes) - { - CollectNode(i, gameObjects); - } - } - - public static void ExportAnimatorWithAnimationClip(AssetItem animator, List animationList, string exportPath) - { - ThreadPool.QueueUserWorkItem(state => - { - StatusStripUpdate($"Exporting {animator.Text}"); - try - { - ExportAnimator(animator.reader, exportPath, animationList); - StatusStripUpdate($"Finished exporting {animator.Text}"); - } - catch (Exception ex) - { - MessageBox.Show($"{ex.Message}\r\n{ex.StackTrace}"); - StatusStripUpdate("Error in export"); - } - ProgressBarPerformStep(); - }); - } - - public static void ExportObjectsWithAnimationClip(string exportPath, TreeNodeCollection nodes, List animationList = null) - { - ThreadPool.QueueUserWorkItem(state => - { - var gameObjects = new List(); - GetSelectedParentNode(nodes, gameObjects); - if (gameObjects.Count > 0) - { - SetProgressBarValue(0); - SetProgressBarMaximum(gameObjects.Count); - foreach (var gameObject in gameObjects) - { - StatusStripUpdate($"Exporting {gameObject.m_Name}"); - try - { - ExportGameObject(gameObject, exportPath, animationList); - StatusStripUpdate($"Finished exporting {gameObject.m_Name}"); - } - catch (Exception ex) - { - MessageBox.Show($"{ex.Message}\r\n{ex.StackTrace}"); - StatusStripUpdate("Error in export"); - } - - ProgressBarPerformStep(); - } - } - else - { - StatusStripUpdate("No Object can be exported."); - } - }); - } - - private static void GetSelectedParentNode(TreeNodeCollection nodes, List gameObjects) - { - foreach (GameObjectTreeNode i in nodes) - { - if (i.Checked) - { - gameObjects.Add(i.gameObject); - } - else - { - GetSelectedParentNode(i.Nodes, gameObjects); - } - } - } - } -} diff --git a/AssetStudio/StudioClasses/TypeTreeHelper.cs b/AssetStudio/TypeTreeHelper.cs similarity index 94% rename from AssetStudio/StudioClasses/TypeTreeHelper.cs rename to AssetStudio/TypeTreeHelper.cs index 3e9f53f2..f85546b2 100644 --- a/AssetStudio/StudioClasses/TypeTreeHelper.cs +++ b/AssetStudio/TypeTreeHelper.cs @@ -1,6 +1,5 @@ using System; using System.Collections.Generic; -using System.Dynamic; using System.IO; using System.Linq; using System.Text; @@ -154,15 +153,14 @@ private static void ReadStringValue(StringBuilder sb, List members reader.AlignStream(4); } - public static ExpandoObject ReadDynamicType(List members, EndianBinaryReader reader) + public static Dictionary ReadBoxingType(List members, EndianBinaryReader reader) { - var obj = new ExpandoObject(); - var objdic = (IDictionary)obj; + var obj = new Dictionary(); for (int i = 0; i < members.Count; i++) { var member = members[i]; var varNameStr = member.m_Name; - objdic[varNameStr] = ReadValue(members, reader, ref i); + obj[varNameStr] = ReadValue(members, reader, ref i); } return obj; } @@ -274,13 +272,12 @@ private static object ReadValue(List members, EndianBinaryReader r var @class = GetMembers(members, level, i); @class.RemoveAt(0); i += @class.Count; - var obj = new ExpandoObject(); - var objdic = (IDictionary)obj; + var obj = new Dictionary(); for (int j = 0; j < @class.Count; j++) { var classmember = @class[j]; var name = classmember.m_Name; - objdic[name] = ReadValue(@class, reader, ref j); + obj[name] = ReadValue(@class, reader, ref j); } value = obj; break; @@ -308,16 +305,15 @@ private static List GetMembers(List members, int lev return member2; } - public static byte[] WriteDynamicType(ExpandoObject obj, List members) + public static byte[] WriteBoxingType(Dictionary obj, List members) { var stream = new MemoryStream(); var write = new BinaryWriter(stream); - var objdic = (IDictionary)obj; for (int i = 0; i < members.Count; i++) { var member = members[i]; var varNameStr = member.m_Name; - WriteValue(objdic[varNameStr], members, write, ref i); + WriteValue(obj[varNameStr], members, write, ref i); } return stream.ToArray(); } @@ -431,13 +427,12 @@ private static void WriteValue(object value, List members, BinaryW var @class = GetMembers(members, level, i); @class.RemoveAt(0); i += @class.Count; - var obj = (ExpandoObject)value; - var objdic = (IDictionary)obj; + var obj = (Dictionary)value; for (int j = 0; j < @class.Count; j++) { var classmember = @class[j]; var name = classmember.m_Name; - WriteValue(objdic[name], @class, write, ref j); + WriteValue(obj[name], @class, write, ref j); } break; } diff --git a/AssetStudio/StudioClasses/TypeTreeNode.cs b/AssetStudio/TypeTreeNode.cs similarity index 100% rename from AssetStudio/StudioClasses/TypeTreeNode.cs rename to AssetStudio/TypeTreeNode.cs diff --git a/AssetStudioUtility/Imported.cs b/AssetStudio/Utility/IImported.cs similarity index 95% rename from AssetStudioUtility/Imported.cs rename to AssetStudio/Utility/IImported.cs index a00a0123..80bd2f01 100644 --- a/AssetStudioUtility/Imported.cs +++ b/AssetStudio/Utility/IImported.cs @@ -219,7 +219,7 @@ public static ImportedFrame FindChildOrRoot(string name, ImportedFrame root) public static ImportedMesh FindMesh(string frameName, List importedMeshList) { - foreach (ImportedMesh mesh in importedMeshList) + foreach (var mesh in importedMeshList) { if (mesh.Name == frameName) { @@ -232,15 +232,15 @@ public static ImportedMesh FindMesh(string frameName, List importe public static ImportedMesh FindMesh(ImportedFrame frame, List importedMeshList) { - string framePath = frame.Name; - ImportedFrame root = frame; + var framePath = frame.Name; + var root = frame; while (root.Parent != null) { root = root.Parent; framePath = root.Name + "/" + framePath; } - foreach (ImportedMesh mesh in importedMeshList) + foreach (var mesh in importedMeshList) { if (mesh.Name == framePath) { @@ -253,7 +253,7 @@ public static ImportedMesh FindMesh(ImportedFrame frame, List impo public static ImportedMaterial FindMaterial(string name, List importedMats) { - foreach (ImportedMaterial mat in importedMats) + foreach (var mat in importedMats) { if (mat.Name == name) { @@ -271,7 +271,7 @@ public static ImportedTexture FindTexture(string name, List imp return null; } - foreach (ImportedTexture tex in importedTextureList) + foreach (var tex in importedTextureList) { if (tex.Name == name) { diff --git a/AssetStudio/Utility/ILogger.cs b/AssetStudio/Utility/ILogger.cs new file mode 100644 index 00000000..86abc78f --- /dev/null +++ b/AssetStudio/Utility/ILogger.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace AssetStudio +{ + public enum LoggerEvent + { + Verbose, + Debug, + Info, + Warning, + Error, + } + + public interface ILogger + { + void Log(LoggerEvent loggerEvent, string message); + } + + public sealed class DummyLogger : ILogger + { + public void Log(LoggerEvent loggerEvent, string message) { } + } +} diff --git a/AssetStudio/Utility/IProgress.cs b/AssetStudio/Utility/IProgress.cs new file mode 100644 index 00000000..ab203ecc --- /dev/null +++ b/AssetStudio/Utility/IProgress.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace AssetStudio +{ + public interface IProgress + { + void Report(int value); + } + + public sealed class DummyProgress : IProgress + { + public void Report(int value) { } + } +} diff --git a/AssetStudio/Utility/Logger.cs b/AssetStudio/Utility/Logger.cs new file mode 100644 index 00000000..20f067bb --- /dev/null +++ b/AssetStudio/Utility/Logger.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace AssetStudio +{ + public static class Logger + { + public static ILogger Default = new DummyLogger(); + + public static void Verbose(string message) => Default.Log(LoggerEvent.Verbose, message); + public static void Debug(string message) => Default.Log(LoggerEvent.Debug, message); + public static void Info(string message) => Default.Log(LoggerEvent.Info, message); + public static void Warning(string message) => Default.Log(LoggerEvent.Warning, message); + public static void Error(string message) => Default.Log(LoggerEvent.Error, message); + } +} diff --git a/AssetStudio/Utility/Progress.cs b/AssetStudio/Utility/Progress.cs new file mode 100644 index 00000000..a197fe2f --- /dev/null +++ b/AssetStudio/Utility/Progress.cs @@ -0,0 +1,29 @@ +namespace AssetStudio +{ + public static class Progress + { + public static IProgress Default = new DummyProgress(); + private static int preValue; + + public static void Reset() + { + preValue = 0; + Default.Report(0); + } + + public static void Report(int current, int total) + { + var value = (int)(current * 100f / total); + Report(value); + } + + private static void Report(int value) + { + if (value > preValue) + { + preValue = value; + Default.Report(value); + } + } + } +} diff --git a/AssetStudio/StudioClasses/WebFile.cs b/AssetStudio/WebFile.cs similarity index 98% rename from AssetStudio/StudioClasses/WebFile.cs rename to AssetStudio/WebFile.cs index b586548e..f71c47bd 100644 --- a/AssetStudio/StudioClasses/WebFile.cs +++ b/AssetStudio/WebFile.cs @@ -14,15 +14,13 @@ public class WebFile public static byte[] brotliMagic = { 0x62, 0x72, 0x6F, 0x74, 0x6C, 0x69 }; public List fileList = new List(); - - public class WebData + private class WebData { public int dataOffset; public int dataLength; public string path; } - public WebFile(EndianBinaryReader reader) { var magic = reader.ReadBytes(2); diff --git a/AssetStudio/app.config b/AssetStudio/app.config deleted file mode 100644 index 0ca68533..00000000 --- a/AssetStudio/app.config +++ /dev/null @@ -1,69 +0,0 @@ - - - - -
- - - - - - False - - - True - - - True - - - True - - - 0 - - - True - - - True - - - PNG - - - False - - - True - - - 0.25 - - - False - - - True - - - True - - - 10 - - - False - - - 3 - - - 0 - - - 1 - - - - \ No newline at end of file diff --git a/AssetStudioFBX/AssemblyInfo.cpp b/AssetStudioFBX/AssemblyInfo.cpp index cd575341..8ead0f69 100644 --- a/AssetStudioFBX/AssemblyInfo.cpp +++ b/AssetStudioFBX/AssemblyInfo.cpp @@ -9,7 +9,7 @@ using namespace System::Security::Permissions; [assembly:AssemblyConfigurationAttribute(L"")]; [assembly:AssemblyCompanyAttribute(L"")]; [assembly:AssemblyProductAttribute(L"AssetStudioFBX")]; -[assembly:AssemblyCopyrightAttribute(L"Copyright © 2018")]; +[assembly:AssemblyCopyrightAttribute(L"Copyright © Perfare 2018")]; [assembly:AssemblyTrademarkAttribute(L"")]; [assembly:AssemblyCultureAttribute(L"")]; diff --git a/AssetStudioFBX/AssetStudioFBX.vcxproj b/AssetStudioFBX/AssetStudioFBX.vcxproj index 238d3ea6..8dde7599 100644 --- a/AssetStudioFBX/AssetStudioFBX.vcxproj +++ b/AssetStudioFBX/AssetStudioFBX.vcxproj @@ -150,8 +150,8 @@ - - {9131c403-7fe8-444d-9af5-5fe5df76ff24} + + {af56b63c-1764-41b7-9e60-8d485422ac3b} diff --git a/AssetStudioFBX/AssetStudioFBXExporter.cpp b/AssetStudioFBX/AssetStudioFBXExporter.cpp index b265291f..55d752b4 100644 --- a/AssetStudioFBX/AssetStudioFBXExporter.cpp +++ b/AssetStudioFBX/AssetStudioFBXExporter.cpp @@ -487,42 +487,39 @@ namespace AssetStudio pMeshNode->AddMaterial(pMat); bool hasTexture = false; - FbxFileTexture* pTextureDiffuse = ExportTexture(ImportedHelpers::FindTexture((String^)mat->Textures[0], imported->TextureList), pMesh); + FbxFileTexture* pTextureDiffuse = ExportTexture(ImportedHelpers::FindTexture(mat->Textures[0], imported->TextureList), pMesh); if (pTextureDiffuse != NULL) { LinkTexture(mat, 0, pTextureDiffuse, pMat->Diffuse); hasTexture = true; } - FbxFileTexture* pTextureAmbient = ExportTexture(ImportedHelpers::FindTexture((String^)mat->Textures[1], imported->TextureList), pMesh); + FbxFileTexture* pTextureAmbient = ExportTexture(ImportedHelpers::FindTexture(mat->Textures[1], imported->TextureList), pMesh); if (pTextureAmbient != NULL) { LinkTexture(mat, 1, pTextureAmbient, pMat->Ambient); hasTexture = true; } - FbxFileTexture* pTextureEmissive = ExportTexture(ImportedHelpers::FindTexture((String^)mat->Textures[2], imported->TextureList), pMesh); + FbxFileTexture* pTextureEmissive = ExportTexture(ImportedHelpers::FindTexture(mat->Textures[2], imported->TextureList), pMesh); if (pTextureEmissive != NULL) { LinkTexture(mat, 2, pTextureEmissive, pMat->Emissive); hasTexture = true; } - FbxFileTexture* pTextureSpecular = ExportTexture(ImportedHelpers::FindTexture((String^)mat->Textures[3], imported->TextureList), pMesh); + FbxFileTexture* pTextureSpecular = ExportTexture(ImportedHelpers::FindTexture(mat->Textures[3], imported->TextureList), pMesh); if (pTextureSpecular != NULL) { LinkTexture(mat, 3, pTextureSpecular, pMat->Specular); hasTexture = true; } - if (mat->Textures->Length > 4) + FbxFileTexture* pTextureBump = ExportTexture(ImportedHelpers::FindTexture(mat->Textures[4], imported->TextureList), pMesh); + if (pTextureBump != NULL) { - FbxFileTexture* pTextureBump = ExportTexture(ImportedHelpers::FindTexture((String^)mat->Textures[4], imported->TextureList), pMesh); - if (pTextureBump != NULL) - { - LinkTexture(mat, 4, pTextureBump, pMat->Bump); - hasTexture = true; - } + LinkTexture(mat, 4, pTextureBump, pMat->Bump); + hasTexture = true; } if (hasTexture) diff --git a/AssetStudioGUI/AssetStudioGUI.csproj b/AssetStudioGUI/AssetStudioGUI.csproj new file mode 100644 index 00000000..39c15a41 --- /dev/null +++ b/AssetStudioGUI/AssetStudioGUI.csproj @@ -0,0 +1,201 @@ + + + + Debug + x86 + 8.0.30703 + 2.0 + {24551E2D-E9B6-4CD6-8F2A-D9F4A13E7853} + WinExe + Properties + AssetStudioGUI + AssetStudioGUI + v4.0 + + + 512 + false + publish\ + true + Disk + false + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + false + true + + + Resources\as.ico + + + true + bin\x64\Debug\ + DEBUG;TRACE + full + x64 + prompt + MinimumRecommendedRules.ruleset + + + bin\x64\Release\ + TRACE + true + pdbonly + x64 + prompt + MinimumRecommendedRules.ruleset + + + OnBuildSuccess + + + true + bin\x86\Debug\ + DEBUG;TRACE + full + x86 + prompt + MinimumRecommendedRules.ruleset + + + bin\x86\Release\ + TRACE + true + pdbonly + x86 + prompt + MinimumRecommendedRules.ruleset + + + + False + ..\AssetStudio\Libraries\dnlib.dll + False + + + False + ..\AssetStudio\Libraries\OpenTK.dll + False + + + False + ..\AssetStudio\Libraries\OpenTK.GLControl.dll + False + + + + + + + + + + + + + + + + + + + + + Form + + + ExportOptions.cs + + + Component + + + + + Form + + + AssetStudioGUIForm.cs + + + + + ExportOptions.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + True + + + AssetStudioGUIForm.cs + Designer + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + False + Microsoft .NET Framework 4 Client Profile %28x86 and x64%29 + true + + + False + .NET Framework 3.5 SP1 + false + + + False + Windows Installer 4.5 + true + + + + + + + + + + + {9131c403-7fe8-444d-9af5-5fe5df76ff24} + AssetStudioTools + + + {af56b63c-1764-41b7-9e60-8d485422ac3b} + AssetStudio + + + + + xcopy /y "$(SolutionDir)AssetStudio\Libraries" "$(TargetDir)" +xcopy /y "$(SolutionDir)AssetStudio\Libraries\$(PlatformName)" "$(TargetDir)" + + + \ No newline at end of file diff --git a/AssetStudio/AssetStudioForm.Designer.cs b/AssetStudioGUI/AssetStudioGUIForm.Designer.cs similarity index 99% rename from AssetStudio/AssetStudioForm.Designer.cs rename to AssetStudioGUI/AssetStudioGUIForm.Designer.cs index 47b93c65..4d2ccaaa 100644 --- a/AssetStudio/AssetStudioForm.Designer.cs +++ b/AssetStudioGUI/AssetStudioGUIForm.Designer.cs @@ -1,6 +1,6 @@ -namespace AssetStudio +namespace AssetStudioGUI { - partial class AssetStudioForm + partial class AssetStudioGUIForm { /// /// Required designer variable. @@ -29,7 +29,7 @@ protected override void Dispose(bool disposing) private void InitializeComponent() { this.components = new System.ComponentModel.Container(); - System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(AssetStudioForm)); + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(AssetStudioGUIForm)); this.menuStrip1 = new System.Windows.Forms.MenuStrip(); this.fileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.loadFileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); @@ -66,7 +66,7 @@ private void InitializeComponent() this.splitContainer1 = new System.Windows.Forms.SplitContainer(); this.tabControl1 = new System.Windows.Forms.TabControl(); this.tabPage1 = new System.Windows.Forms.TabPage(); - this.sceneTreeView = new AssetStudio.GOHierarchy(); + this.sceneTreeView = new AssetStudioGUI.GOHierarchy(); this.treeSearch = new System.Windows.Forms.TextBox(); this.tabPage2 = new System.Windows.Forms.TabPage(); this.assetListView = new System.Windows.Forms.ListView(); @@ -574,7 +574,7 @@ private void InitializeComponent() // previewPanel // this.previewPanel.BackColor = System.Drawing.SystemColors.ControlDark; - this.previewPanel.BackgroundImage = global::AssetStudio.Properties.Resources.preview; + this.previewPanel.BackgroundImage = global::AssetStudioGUI.Properties.Resources.preview; this.previewPanel.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Center; this.previewPanel.Controls.Add(this.assetInfoLabel); this.previewPanel.Controls.Add(this.FMODpanel); @@ -913,20 +913,20 @@ private void InitializeComponent() this.showOriginalFileToolStripMenuItem.Visible = false; this.showOriginalFileToolStripMenuItem.Click += new System.EventHandler(this.showOriginalFileToolStripMenuItem_Click); // - // AssetStudioForm + // AssetStudioGUIForm // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(1264, 681); this.Controls.Add(this.splitContainer1); this.Controls.Add(this.menuStrip1); - this.Icon = global::AssetStudio.Properties.Resources._as; + this.Icon = global::AssetStudioGUI.Properties.Resources._as; this.KeyPreview = true; this.MainMenuStrip = this.menuStrip1; this.MinimumSize = new System.Drawing.Size(620, 372); - this.Name = "AssetStudioForm"; + this.Name = "AssetStudioGUIForm"; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; - this.Text = "AssetStudio"; + this.Text = "AssetStudioGUI"; this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.AssetStudioForm_KeyDown); this.menuStrip1.ResumeLayout(false); this.menuStrip1.PerformLayout(); diff --git a/AssetStudio/AssetStudioForm.cs b/AssetStudioGUI/AssetStudioGUIForm.cs similarity index 66% rename from AssetStudio/AssetStudioForm.cs rename to AssetStudioGUI/AssetStudioGUIForm.cs index 5d080aab..07d11e6a 100644 --- a/AssetStudio/AssetStudioForm.cs +++ b/AssetStudioGUI/AssetStudioGUIForm.cs @@ -13,12 +13,12 @@ using System.Drawing.Text; using OpenTK; using OpenTK.Graphics.OpenGL; -using static AssetStudio.Studio; -using static AssetStudio.Importer; +using AssetStudio; +using static AssetStudioGUI.Studio; -namespace AssetStudio +namespace AssetStudioGUI { - partial class AssetStudioForm : Form + partial class AssetStudioGUIForm : Form { private AssetItem lastSelectedItem; private AssetItem lastLoadedAsset; @@ -79,57 +79,22 @@ private void loadFile_Click(object sender, EventArgs e) ResetForm(); ThreadPool.QueueUserWorkItem(state => { - mainPath = Path.GetDirectoryName(openFileDialog1.FileNames[0]); - MergeSplitAssets(mainPath); - var readFile = ProcessingSplitFiles(openFileDialog1.FileNames.ToList()); - foreach (var i in readFile) - { - importFiles.Add(i); - importFilesHash.Add(Path.GetFileName(i).ToUpper()); - } - SetProgressBarValue(0); - SetProgressBarMaximum(importFiles.Count); - //use a for loop because list size can change - for (int f = 0; f < importFiles.Count; f++) - { - LoadFile(importFiles[f]); - ProgressBarPerformStep(); - } - importFilesHash.Clear(); - assetsfileListHash.Clear(); - BuildAssetStrucutres(); + assetsManager.LoadFiles(openFileDialog1.FileNames); + BuildAssetStructures(); }); } } private void loadFolder_Click(object sender, EventArgs e) { - var openFolderDialog1 = new OpenFolderDialog(); - if (openFolderDialog1.ShowDialog(this) == DialogResult.OK) + var openFolderDialog = new OpenFolderDialog(); + if (openFolderDialog.ShowDialog(this) == DialogResult.OK) { ResetForm(); ThreadPool.QueueUserWorkItem(state => { - mainPath = openFolderDialog1.Folder; - MergeSplitAssets(mainPath); - var files = Directory.GetFiles(mainPath, "*.*", SearchOption.AllDirectories).ToList(); - var readFile = ProcessingSplitFiles(files); - foreach (var i in readFile) - { - importFiles.Add(i); - importFilesHash.Add(Path.GetFileName(i)); - } - SetProgressBarValue(0); - SetProgressBarMaximum(importFiles.Count); - //use a for loop because list size can change - for (int f = 0; f < importFiles.Count; f++) - { - LoadFile(importFiles[f]); - ProgressBarPerformStep(); - } - importFilesHash.Clear(); - assetsfileListHash.Clear(); - BuildAssetStrucutres(); + assetsManager.LoadFolder(openFolderDialog.Folder); + BuildAssetStructures(); }); } } @@ -146,8 +111,6 @@ private void extractFileToolStripMenuItem_Click(object sender, EventArgs e) if (openBundleDialog.ShowDialog() == DialogResult.OK) { - progressBar1.Value = 0; - progressBar1.Maximum = openBundleDialog.FileNames.Length; ExtractFile(openBundleDialog.FileNames); } } @@ -158,36 +121,49 @@ private void extractFolderToolStripMenuItem_Click(object sender, EventArgs e) if (openFolderDialog1.ShowDialog(this) == DialogResult.OK) { var files = Directory.GetFiles(openFolderDialog1.Folder, "*.*", SearchOption.AllDirectories); - progressBar1.Value = 0; - progressBar1.Maximum = files.Length; ExtractFile(files); } } - private void BuildAssetStrucutres() + private void BuildAssetStructures() { - if (assetsfileList.Count == 0) + if (assetsManager.assetsFileList.Count == 0) { StatusStripUpdate("No file was loaded."); return; } - BuildAssetStructures(!dontLoadAssetsMenuItem.Checked, displayAll.Checked, !dontBuildHierarchyMenuItem.Checked, buildClassStructuresMenuItem.Checked, displayOriginalName.Checked); + var productName = string.Empty; + var tempDic = new Dictionary(); + if (!dontLoadAssetsMenuItem.Checked) + { + BuildAssetList(tempDic, displayAll.Checked, displayOriginalName.Checked, out productName); + } + + List treeNodeCollection = null; + if (!dontBuildHierarchyMenuItem.Checked) + { + treeNodeCollection = BuildTreeStructure(tempDic); + } + tempDic.Clear(); + if (buildClassStructuresMenuItem.Checked) + { + BuildClassStructure(); + } BeginInvoke(new Action(() => { if (!string.IsNullOrEmpty(productName)) { - Text = $"AssetStudio - {productName} - {assetsfileList[0].unityVersion} - {assetsfileList[0].platformStr}"; + Text = $"AssetStudioGUI - {productName} - {assetsManager.assetsFileList[0].unityVersion} - {assetsManager.assetsFileList[0].m_TargetPlatform}"; } else { - Text = $"AssetStudio - no productName - {assetsfileList[0].unityVersion} - {assetsfileList[0].platformStr}"; + Text = $"AssetStudioGUI - no productName - {assetsManager.assetsFileList[0].unityVersion} - {assetsManager.assetsFileList[0].m_TargetPlatform}"; } if (!dontLoadAssetsMenuItem.Checked) { assetListView.VirtualListSize = visibleAssets.Count; - //will only work if ListView is visible resizeAssetListColumns(); } if (!dontBuildHierarchyMenuItem.Checked) @@ -206,7 +182,7 @@ private void BuildAssetStrucutres() classesListView.BeginUpdate(); foreach (var version in AllTypeMap) { - ListViewGroup versionGroup = new ListViewGroup(version.Key); + var versionGroup = new ListViewGroup(version.Key); classesListView.Groups.Add(versionGroup); foreach (var uclass in version.Value) @@ -232,7 +208,7 @@ private void BuildAssetStrucutres() filterTypeToolStripMenuItem.DropDownItems.Add(typeItem); } allToolStripMenuItem.Checked = true; - StatusStripUpdate($"Finished loading {assetsfileList.Count} files with {assetListView.Items.Count} exportable assets."); + StatusStripUpdate($"Finished loading {assetsManager.assetsFileList.Count} files with {assetListView.Items.Count} exportable assets."); treeSearch.Select(); })); } @@ -263,8 +239,14 @@ private void AssetStudioForm_KeyDown(object sender, KeyEventArgs e) buildClassStructuresMenuItem.Checked = debugMenuItem.Visible; dontLoadAssetsMenuItem.Checked = debugMenuItem.Visible; dontBuildHierarchyMenuItem.Checked = debugMenuItem.Visible; - if (tabControl1.TabPages.Contains(tabPage3)) { tabControl1.TabPages.Remove(tabPage3); } - else { tabControl1.TabPages.Add(tabPage3); } + if (tabControl1.TabPages.Contains(tabPage3)) + { + tabControl1.TabPages.Remove(tabPage3); + } + else + { + tabControl1.TabPages.Add(tabPage3); + } } if (glControl1.Visible) @@ -307,22 +289,25 @@ private void dontLoadAssetsMenuItem_CheckedChanged(object sender, EventArgs e) dontBuildHierarchyMenuItem.Checked = true; dontBuildHierarchyMenuItem.Enabled = false; } - else { dontBuildHierarchyMenuItem.Enabled = true; } + else + { + dontBuildHierarchyMenuItem.Enabled = true; + } } private void exportClassStructuresMenuItem_Click(object sender, EventArgs e) { if (AllTypeMap.Count > 0) { - var saveFolderDialog1 = new OpenFolderDialog(); - if (saveFolderDialog1.ShowDialog(this) == DialogResult.OK) + var saveFolderDialog = new OpenFolderDialog(); + if (saveFolderDialog.ShowDialog(this) == DialogResult.OK) { - progressBar1.Value = 0; - progressBar1.Maximum = AllTypeMap.Count; - - var savePath = saveFolderDialog1.Folder; + var count = AllTypeMap.Count; + int i = 0; + Progress.Reset(); foreach (var version in AllTypeMap) { + var savePath = saveFolderDialog.Folder; if (version.Value.Count > 0) { string versionPath = savePath + "\\" + version.Key; @@ -331,18 +316,17 @@ private void exportClassStructuresMenuItem_Click(object sender, EventArgs e) foreach (var uclass in version.Value) { string saveFile = $"{versionPath}\\{uclass.Key} {uclass.Value.Text}.txt"; - using (StreamWriter TXTwriter = new StreamWriter(saveFile)) + using (var writer = new StreamWriter(saveFile)) { - TXTwriter.Write(uclass.Value.ToString()); + writer.Write(uclass.Value.ToString()); } } } - progressBar1.PerformStep(); + Progress.Report(++i, count); } StatusStripUpdate("Finished exporting class structures"); - progressBar1.Value = 0; } } } @@ -384,7 +368,7 @@ private void enablePreview_Check(object sender, EventArgs e) var result = channel.isPlaying(out var playing); if (result == FMOD.RESULT.OK && playing) { - result = channel.stop(); + channel.stop(); FMODreset(); } } @@ -411,8 +395,14 @@ private void enablePreview_Check(object sender, EventArgs e) private void displayAssetInfo_Check(object sender, EventArgs e) { - if (displayInfo.Checked && assetInfoLabel.Text != null) { assetInfoLabel.Visible = true; } - else { assetInfoLabel.Visible = false; } + if (displayInfo.Checked && assetInfoLabel.Text != null) + { + assetInfoLabel.Visible = true; + } + else + { + assetInfoLabel.Visible = false; + } Properties.Settings.Default["displayInfo"] = displayInfo.Checked; Properties.Settings.Default.Save(); @@ -654,444 +644,451 @@ private void classesListView_ItemSelectionChanged(object sender, ListViewItemSel private void PreviewAsset(AssetItem asset) { var reader = asset.reader; - switch (asset.Type) + try { - case ClassIDType.Texture2D: - { - imageTexture?.Dispose(); - var m_Texture2D = new Texture2D(reader, true); - - //Info - asset.InfoText = $"Width: {m_Texture2D.m_Width}\nHeight: {m_Texture2D.m_Height}\nFormat: {m_Texture2D.m_TextureFormat}"; - switch (m_Texture2D.m_FilterMode) - { - case 0: asset.InfoText += "\nFilter Mode: Point "; break; - case 1: asset.InfoText += "\nFilter Mode: Bilinear "; break; - case 2: asset.InfoText += "\nFilter Mode: Trilinear "; break; - } - asset.InfoText += $"\nAnisotropic level: {m_Texture2D.m_Aniso}\nMip map bias: {m_Texture2D.m_MipBias}"; - switch (m_Texture2D.m_WrapMode) + switch (asset.Type) + { + case ClassIDType.Texture2D: { - case 0: asset.InfoText += "\nWrap mode: Repeat"; break; - case 1: asset.InfoText += "\nWrap mode: Clamp"; break; - } + imageTexture?.Dispose(); + var m_Texture2D = new Texture2D(reader, true); - var converter = new Texture2DConverter(m_Texture2D); - imageTexture = converter.ConvertToBitmap(true); - if (imageTexture != null) - { - previewPanel.BackgroundImage = imageTexture; - if (imageTexture.Width > previewPanel.Width || imageTexture.Height > previewPanel.Height) - previewPanel.BackgroundImageLayout = ImageLayout.Zoom; + //Info + asset.InfoText = $"Width: {m_Texture2D.m_Width}\nHeight: {m_Texture2D.m_Height}\nFormat: {m_Texture2D.m_TextureFormat}"; + switch (m_Texture2D.m_FilterMode) + { + case 0: asset.InfoText += "\nFilter Mode: Point "; break; + case 1: asset.InfoText += "\nFilter Mode: Bilinear "; break; + case 2: asset.InfoText += "\nFilter Mode: Trilinear "; break; + } + asset.InfoText += $"\nAnisotropic level: {m_Texture2D.m_Aniso}\nMip map bias: {m_Texture2D.m_MipBias}"; + switch (m_Texture2D.m_WrapMode) + { + case 0: asset.InfoText += "\nWrap mode: Repeat"; break; + case 1: asset.InfoText += "\nWrap mode: Clamp"; break; + } + + var converter = new Texture2DConverter(m_Texture2D); + imageTexture = converter.ConvertToBitmap(true); + if (imageTexture != null) + { + previewPanel.BackgroundImage = imageTexture; + if (imageTexture.Width > previewPanel.Width || imageTexture.Height > previewPanel.Height) + previewPanel.BackgroundImageLayout = ImageLayout.Zoom; + else + previewPanel.BackgroundImageLayout = ImageLayout.Center; + } else - previewPanel.BackgroundImageLayout = ImageLayout.Center; + { + StatusStripUpdate("Unsupported image for preview"); + } + break; } - else + case ClassIDType.AudioClip: { - StatusStripUpdate("Unsupported image for preview"); - } - break; - } - case ClassIDType.AudioClip: - { - var m_AudioClip = new AudioClip(reader, true); + var m_AudioClip = new AudioClip(reader, true); - //Info - asset.InfoText = "Compression format: "; - if (m_AudioClip.version[0] < 5) - { - switch (m_AudioClip.m_Type) + //Info + asset.InfoText = "Compression format: "; + if (m_AudioClip.version[0] < 5) { - case AudioType.ACC: - asset.InfoText += "Acc"; - break; - case AudioType.AIFF: - asset.InfoText += "AIFF"; - break; - case AudioType.IT: - asset.InfoText += "Impulse tracker"; - break; - case AudioType.MOD: - asset.InfoText += "Protracker / Fasttracker MOD"; - break; - case AudioType.MPEG: - asset.InfoText += "MP2/MP3 MPEG"; - break; - case AudioType.OGGVORBIS: - asset.InfoText += "Ogg vorbis"; - break; - case AudioType.S3M: - asset.InfoText += "ScreamTracker 3"; - break; - case AudioType.WAV: - asset.InfoText += "Microsoft WAV"; - break; - case AudioType.XM: - asset.InfoText += "FastTracker 2 XM"; - break; - case AudioType.XMA: - asset.InfoText += "Xbox360 XMA"; - break; - case AudioType.VAG: - asset.InfoText += "PlayStation Portable ADPCM"; - break; - case AudioType.AUDIOQUEUE: - asset.InfoText += "iPhone"; - break; - default: - asset.InfoText += "Unknown"; - break; + switch (m_AudioClip.m_Type) + { + case AudioType.ACC: + asset.InfoText += "Acc"; + break; + case AudioType.AIFF: + asset.InfoText += "AIFF"; + break; + case AudioType.IT: + asset.InfoText += "Impulse tracker"; + break; + case AudioType.MOD: + asset.InfoText += "Protracker / Fasttracker MOD"; + break; + case AudioType.MPEG: + asset.InfoText += "MP2/MP3 MPEG"; + break; + case AudioType.OGGVORBIS: + asset.InfoText += "Ogg vorbis"; + break; + case AudioType.S3M: + asset.InfoText += "ScreamTracker 3"; + break; + case AudioType.WAV: + asset.InfoText += "Microsoft WAV"; + break; + case AudioType.XM: + asset.InfoText += "FastTracker 2 XM"; + break; + case AudioType.XMA: + asset.InfoText += "Xbox360 XMA"; + break; + case AudioType.VAG: + asset.InfoText += "PlayStation Portable ADPCM"; + break; + case AudioType.AUDIOQUEUE: + asset.InfoText += "iPhone"; + break; + default: + asset.InfoText += "Unknown"; + break; + } } - } - else - { - switch (m_AudioClip.m_CompressionFormat) + else { - case AudioCompressionFormat.PCM: - asset.InfoText += "PCM"; - break; - case AudioCompressionFormat.Vorbis: - asset.InfoText += "Vorbis"; - break; - case AudioCompressionFormat.ADPCM: - asset.InfoText += "ADPCM"; - break; - case AudioCompressionFormat.MP3: - asset.InfoText += "MP3"; - break; - case AudioCompressionFormat.VAG: - asset.InfoText += "PlayStation Portable ADPCM"; - break; - case AudioCompressionFormat.HEVAG: - asset.InfoText += "PSVita ADPCM"; - break; - case AudioCompressionFormat.XMA: - asset.InfoText += "Xbox360 XMA"; - break; - case AudioCompressionFormat.AAC: - asset.InfoText += "AAC"; - break; - case AudioCompressionFormat.GCADPCM: - asset.InfoText += "Nintendo 3DS/Wii DSP"; - break; - case AudioCompressionFormat.ATRAC9: - asset.InfoText += "PSVita ATRAC9"; - break; - default: - asset.InfoText += "Unknown"; - break; + switch (m_AudioClip.m_CompressionFormat) + { + case AudioCompressionFormat.PCM: + asset.InfoText += "PCM"; + break; + case AudioCompressionFormat.Vorbis: + asset.InfoText += "Vorbis"; + break; + case AudioCompressionFormat.ADPCM: + asset.InfoText += "ADPCM"; + break; + case AudioCompressionFormat.MP3: + asset.InfoText += "MP3"; + break; + case AudioCompressionFormat.VAG: + asset.InfoText += "PlayStation Portable ADPCM"; + break; + case AudioCompressionFormat.HEVAG: + asset.InfoText += "PSVita ADPCM"; + break; + case AudioCompressionFormat.XMA: + asset.InfoText += "Xbox360 XMA"; + break; + case AudioCompressionFormat.AAC: + asset.InfoText += "AAC"; + break; + case AudioCompressionFormat.GCADPCM: + asset.InfoText += "Nintendo 3DS/Wii DSP"; + break; + case AudioCompressionFormat.ATRAC9: + asset.InfoText += "PSVita ATRAC9"; + break; + default: + asset.InfoText += "Unknown"; + break; + } } - } - if (m_AudioClip.m_AudioData == null) - break; - FMOD.CREATESOUNDEXINFO exinfo = new FMOD.CREATESOUNDEXINFO(); - - exinfo.cbsize = Marshal.SizeOf(exinfo); - exinfo.length = (uint)m_AudioClip.m_Size; + if (m_AudioClip.m_AudioData == null) + break; + FMOD.CREATESOUNDEXINFO exinfo = new FMOD.CREATESOUNDEXINFO(); - var result = system.createSound(m_AudioClip.m_AudioData, FMOD.MODE.OPENMEMORY | loopMode, ref exinfo, out sound); - if (ERRCHECK(result)) { break; } + exinfo.cbsize = Marshal.SizeOf(exinfo); + exinfo.length = (uint)m_AudioClip.m_Size; - result = sound.getSubSound(0, out var subsound); - if (result == FMOD.RESULT.OK) - { - sound = subsound; - } + var result = system.createSound(m_AudioClip.m_AudioData, FMOD.MODE.OPENMEMORY | loopMode, ref exinfo, out sound); + if (ERRCHECK(result)) { break; } - result = sound.getLength(out FMODlenms, FMOD.TIMEUNIT.MS); - if (ERRCHECK(result)) { break; } + result = sound.getSubSound(0, out var subsound); + if (result == FMOD.RESULT.OK) + { + sound = subsound; + } - result = system.playSound(sound, null, true, out channel); - if (ERRCHECK(result)) { break; } + result = sound.getLength(out FMODlenms, FMOD.TIMEUNIT.MS); + if (ERRCHECK(result)) { break; } - FMODpanel.Visible = true; + result = system.playSound(sound, null, true, out channel); + if (ERRCHECK(result)) { break; } - result = channel.getFrequency(out var frequency); - if (ERRCHECK(result)) { break; } + FMODpanel.Visible = true; - FMODinfoLabel.Text = frequency + " Hz"; - FMODtimerLabel.Text = $"0:0.0 / {FMODlenms / 1000 / 60}:{FMODlenms / 1000 % 60}.{FMODlenms / 10 % 100}"; - break; - } - case ClassIDType.Shader: - { - var m_Shader = new Shader(reader); - var str = ShaderConverter.Convert(m_Shader); - textPreviewBox.Text = str == null ? "Serialized Shader can't be read" : str.Replace("\n", "\r\n"); - textPreviewBox.Visible = true; - break; - } - case ClassIDType.TextAsset: - { - TextAsset m_TextAsset = new TextAsset(reader); + result = channel.getFrequency(out var frequency); + if (ERRCHECK(result)) { break; } - string m_Script_Text = Encoding.UTF8.GetString(m_TextAsset.m_Script); - m_Script_Text = Regex.Replace(m_Script_Text, "(? 0) + using (var pfc = new PrivateFontCollection()) { - fontPreviewBox.SelectionStart = 0; - fontPreviewBox.SelectionLength = 80; - fontPreviewBox.SelectionFont = new System.Drawing.Font(pfc.Families[0], 16, FontStyle.Regular); - fontPreviewBox.SelectionStart = 81; - fontPreviewBox.SelectionLength = 56; - fontPreviewBox.SelectionFont = new System.Drawing.Font(pfc.Families[0], 12, FontStyle.Regular); - fontPreviewBox.SelectionStart = 138; - fontPreviewBox.SelectionLength = 56; - fontPreviewBox.SelectionFont = new System.Drawing.Font(pfc.Families[0], 18, FontStyle.Regular); - fontPreviewBox.SelectionStart = 195; - fontPreviewBox.SelectionLength = 56; - fontPreviewBox.SelectionFont = new System.Drawing.Font(pfc.Families[0], 24, FontStyle.Regular); - fontPreviewBox.SelectionStart = 252; - fontPreviewBox.SelectionLength = 56; - fontPreviewBox.SelectionFont = new System.Drawing.Font(pfc.Families[0], 36, FontStyle.Regular); - fontPreviewBox.SelectionStart = 309; - fontPreviewBox.SelectionLength = 56; - fontPreviewBox.SelectionFont = new System.Drawing.Font(pfc.Families[0], 48, FontStyle.Regular); - fontPreviewBox.SelectionStart = 366; - fontPreviewBox.SelectionLength = 56; - fontPreviewBox.SelectionFont = new System.Drawing.Font(pfc.Families[0], 60, FontStyle.Regular); - fontPreviewBox.SelectionStart = 423; - fontPreviewBox.SelectionLength = 55; - fontPreviewBox.SelectionFont = new System.Drawing.Font(pfc.Families[0], 72, FontStyle.Regular); - fontPreviewBox.Visible = true; + pfc.AddMemoryFont(data, m_Font.m_FontData.Length); + Marshal.FreeCoTaskMem(data); + if (pfc.Families.Length > 0) + { + fontPreviewBox.SelectionStart = 0; + fontPreviewBox.SelectionLength = 80; + fontPreviewBox.SelectionFont = new System.Drawing.Font(pfc.Families[0], 16, FontStyle.Regular); + fontPreviewBox.SelectionStart = 81; + fontPreviewBox.SelectionLength = 56; + fontPreviewBox.SelectionFont = new System.Drawing.Font(pfc.Families[0], 12, FontStyle.Regular); + fontPreviewBox.SelectionStart = 138; + fontPreviewBox.SelectionLength = 56; + fontPreviewBox.SelectionFont = new System.Drawing.Font(pfc.Families[0], 18, FontStyle.Regular); + fontPreviewBox.SelectionStart = 195; + fontPreviewBox.SelectionLength = 56; + fontPreviewBox.SelectionFont = new System.Drawing.Font(pfc.Families[0], 24, FontStyle.Regular); + fontPreviewBox.SelectionStart = 252; + fontPreviewBox.SelectionLength = 56; + fontPreviewBox.SelectionFont = new System.Drawing.Font(pfc.Families[0], 36, FontStyle.Regular); + fontPreviewBox.SelectionStart = 309; + fontPreviewBox.SelectionLength = 56; + fontPreviewBox.SelectionFont = new System.Drawing.Font(pfc.Families[0], 48, FontStyle.Regular); + fontPreviewBox.SelectionStart = 366; + fontPreviewBox.SelectionLength = 56; + fontPreviewBox.SelectionFont = new System.Drawing.Font(pfc.Families[0], 60, FontStyle.Regular); + fontPreviewBox.SelectionStart = 423; + fontPreviewBox.SelectionLength = 55; + fontPreviewBox.SelectionFont = new System.Drawing.Font(pfc.Families[0], 72, FontStyle.Regular); + fontPreviewBox.Visible = true; + } } + break; } - break; } + StatusStripUpdate("Unsupported font for preview. Try to export."); + break; } - StatusStripUpdate("Unsupported font for preview. Try to export."); - break; - } - case ClassIDType.Mesh: - { - var m_Mesh = new Mesh(reader); - if (m_Mesh.m_VertexCount > 0) + case ClassIDType.Mesh: { - viewMatrixData = Matrix4.CreateRotationY(-(float)Math.PI / 4) * Matrix4.CreateRotationX(-(float)Math.PI / 6); - #region Vertices - if (m_Mesh.m_Vertices == null || m_Mesh.m_Vertices.Length == 0) - { - StatusStripUpdate("Mesh can't be previewed."); - return; - } - int count = 3; - if (m_Mesh.m_Vertices.Length == m_Mesh.m_VertexCount * 4) - { - count = 4; - } - vertexData = new Vector3[m_Mesh.m_VertexCount]; - // Calculate Bounding - float[] min = new float[3]; - float[] max = new float[3]; - for (int i = 0; i < 3; i++) - { - min[i] = m_Mesh.m_Vertices[i]; - max[i] = m_Mesh.m_Vertices[i]; - } - for (int v = 0; v < m_Mesh.m_VertexCount; v++) + var m_Mesh = new Mesh(reader); + if (m_Mesh.m_VertexCount > 0) { + viewMatrixData = Matrix4.CreateRotationY(-(float)Math.PI / 4) * Matrix4.CreateRotationX(-(float)Math.PI / 6); + #region Vertices + if (m_Mesh.m_Vertices == null || m_Mesh.m_Vertices.Length == 0) + { + StatusStripUpdate("Mesh can't be previewed."); + return; + } + int count = 3; + if (m_Mesh.m_Vertices.Length == m_Mesh.m_VertexCount * 4) + { + count = 4; + } + vertexData = new Vector3[m_Mesh.m_VertexCount]; + // Calculate Bounding + float[] min = new float[3]; + float[] max = new float[3]; for (int i = 0; i < 3; i++) { - min[i] = Math.Min(min[i], m_Mesh.m_Vertices[v * count + i]); - max[i] = Math.Max(max[i], m_Mesh.m_Vertices[v * count + i]); + min[i] = m_Mesh.m_Vertices[i]; + max[i] = m_Mesh.m_Vertices[i]; + } + for (int v = 0; v < m_Mesh.m_VertexCount; v++) + { + for (int i = 0; i < 3; i++) + { + min[i] = Math.Min(min[i], m_Mesh.m_Vertices[v * count + i]); + max[i] = Math.Max(max[i], m_Mesh.m_Vertices[v * count + i]); + } + vertexData[v] = new Vector3( + m_Mesh.m_Vertices[v * count], + m_Mesh.m_Vertices[v * count + 1], + m_Mesh.m_Vertices[v * count + 2]); } - vertexData[v] = new Vector3( - m_Mesh.m_Vertices[v * count], - m_Mesh.m_Vertices[v * count + 1], - m_Mesh.m_Vertices[v * count + 2]); - } - // Calculate modelMatrix - Vector3 dist = Vector3.One, offset = Vector3.Zero; - for (int i = 0; i < 3; i++) - { - dist[i] = max[i] - min[i]; - offset[i] = (max[i] + min[i]) / 2; - } - float d = Math.Max(1e-5f, dist.Length); - modelMatrixData = Matrix4.CreateTranslation(-offset) * Matrix4.CreateScale(2f / d); - #endregion - #region Indicies - indiceData = new int[m_Mesh.m_Indices.Count]; - for (int i = 0; i < m_Mesh.m_Indices.Count; i = i + 3) - { - indiceData[i] = (int)m_Mesh.m_Indices[i]; - indiceData[i + 1] = (int)m_Mesh.m_Indices[i + 1]; - indiceData[i + 2] = (int)m_Mesh.m_Indices[i + 2]; - } - #endregion - #region Normals - if (m_Mesh.m_Normals != null && m_Mesh.m_Normals.Length > 0) - { - if (m_Mesh.m_Normals.Length == m_Mesh.m_VertexCount * 3) - count = 3; - else if (m_Mesh.m_Normals.Length == m_Mesh.m_VertexCount * 4) - count = 4; - normalData = new Vector3[m_Mesh.m_VertexCount]; - for (int n = 0; n < m_Mesh.m_VertexCount; n++) + // Calculate modelMatrix + Vector3 dist = Vector3.One, offset = Vector3.Zero; + for (int i = 0; i < 3; i++) { - normalData[n] = new Vector3( - m_Mesh.m_Normals[n * count], - m_Mesh.m_Normals[n * count + 1], - m_Mesh.m_Normals[n * count + 2]); + dist[i] = max[i] - min[i]; + offset[i] = (max[i] + min[i]) / 2; } - } - else - normalData = null; - // calculate normal by ourself - normal2Data = new Vector3[m_Mesh.m_VertexCount]; - int[] normalCalculatedCount = new int[m_Mesh.m_VertexCount]; - for (int i = 0; i < m_Mesh.m_VertexCount; i++) - { - normal2Data[i] = Vector3.Zero; - normalCalculatedCount[i] = 0; - } - for (int i = 0; i < m_Mesh.m_Indices.Count; i = i + 3) - { - Vector3 dir1 = vertexData[indiceData[i + 1]] - vertexData[indiceData[i]]; - Vector3 dir2 = vertexData[indiceData[i + 2]] - vertexData[indiceData[i]]; - Vector3 normal = Vector3.Cross(dir1, dir2); - normal.Normalize(); - for (int j = 0; j < 3; j++) + float d = Math.Max(1e-5f, dist.Length); + modelMatrixData = Matrix4.CreateTranslation(-offset) * Matrix4.CreateScale(2f / d); + #endregion + #region Indicies + indiceData = new int[m_Mesh.m_Indices.Count]; + for (int i = 0; i < m_Mesh.m_Indices.Count; i = i + 3) { - normal2Data[indiceData[i + j]] += normal; - normalCalculatedCount[indiceData[i + j]]++; + indiceData[i] = (int)m_Mesh.m_Indices[i]; + indiceData[i + 1] = (int)m_Mesh.m_Indices[i + 1]; + indiceData[i + 2] = (int)m_Mesh.m_Indices[i + 2]; + } + #endregion + #region Normals + if (m_Mesh.m_Normals != null && m_Mesh.m_Normals.Length > 0) + { + if (m_Mesh.m_Normals.Length == m_Mesh.m_VertexCount * 3) + count = 3; + else if (m_Mesh.m_Normals.Length == m_Mesh.m_VertexCount * 4) + count = 4; + normalData = new Vector3[m_Mesh.m_VertexCount]; + for (int n = 0; n < m_Mesh.m_VertexCount; n++) + { + normalData[n] = new Vector3( + m_Mesh.m_Normals[n * count], + m_Mesh.m_Normals[n * count + 1], + m_Mesh.m_Normals[n * count + 2]); + } } - } - for (int i = 0; i < m_Mesh.m_VertexCount; i++) - { - if (normalCalculatedCount[i] == 0) - normal2Data[i] = new Vector3(0, 1, 0); else - normal2Data[i] /= normalCalculatedCount[i]; - } - #endregion - #region Colors - if (m_Mesh.m_Colors != null && m_Mesh.m_Colors.Length == m_Mesh.m_VertexCount * 3) - { - colorData = new Vector4[m_Mesh.m_VertexCount]; - for (int c = 0; c < m_Mesh.m_VertexCount; c++) + normalData = null; + // calculate normal by ourself + normal2Data = new Vector3[m_Mesh.m_VertexCount]; + int[] normalCalculatedCount = new int[m_Mesh.m_VertexCount]; + for (int i = 0; i < m_Mesh.m_VertexCount; i++) { - colorData[c] = new Vector4( - m_Mesh.m_Colors[c * 3], - m_Mesh.m_Colors[c * 3 + 1], - m_Mesh.m_Colors[c * 3 + 2], - 1.0f); + normal2Data[i] = Vector3.Zero; + normalCalculatedCount[i] = 0; } - } - else if (m_Mesh.m_Colors != null && m_Mesh.m_Colors.Length == m_Mesh.m_VertexCount * 4) - { - colorData = new Vector4[m_Mesh.m_VertexCount]; - for (int c = 0; c < m_Mesh.m_VertexCount; c++) + for (int i = 0; i < m_Mesh.m_Indices.Count; i = i + 3) { - colorData[c] = new Vector4( - m_Mesh.m_Colors[c * 4], - m_Mesh.m_Colors[c * 4 + 1], - m_Mesh.m_Colors[c * 4 + 2], - m_Mesh.m_Colors[c * 4 + 3]); + Vector3 dir1 = vertexData[indiceData[i + 1]] - vertexData[indiceData[i]]; + Vector3 dir2 = vertexData[indiceData[i + 2]] - vertexData[indiceData[i]]; + Vector3 normal = Vector3.Cross(dir1, dir2); + normal.Normalize(); + for (int j = 0; j < 3; j++) + { + normal2Data[indiceData[i + j]] += normal; + normalCalculatedCount[indiceData[i + j]]++; + } } - } - else - { - colorData = new Vector4[m_Mesh.m_VertexCount]; - for (int c = 0; c < m_Mesh.m_VertexCount; c++) + for (int i = 0; i < m_Mesh.m_VertexCount; i++) + { + if (normalCalculatedCount[i] == 0) + normal2Data[i] = new Vector3(0, 1, 0); + else + normal2Data[i] /= normalCalculatedCount[i]; + } + #endregion + #region Colors + if (m_Mesh.m_Colors != null && m_Mesh.m_Colors.Length == m_Mesh.m_VertexCount * 3) { - colorData[c] = new Vector4(0.5f, 0.5f, 0.5f, 1.0f); + colorData = new Vector4[m_Mesh.m_VertexCount]; + for (int c = 0; c < m_Mesh.m_VertexCount; c++) + { + colorData[c] = new Vector4( + m_Mesh.m_Colors[c * 3], + m_Mesh.m_Colors[c * 3 + 1], + m_Mesh.m_Colors[c * 3 + 2], + 1.0f); + } + } + else if (m_Mesh.m_Colors != null && m_Mesh.m_Colors.Length == m_Mesh.m_VertexCount * 4) + { + colorData = new Vector4[m_Mesh.m_VertexCount]; + for (int c = 0; c < m_Mesh.m_VertexCount; c++) + { + colorData[c] = new Vector4( + m_Mesh.m_Colors[c * 4], + m_Mesh.m_Colors[c * 4 + 1], + m_Mesh.m_Colors[c * 4 + 2], + m_Mesh.m_Colors[c * 4 + 3]); + } } + else + { + colorData = new Vector4[m_Mesh.m_VertexCount]; + for (int c = 0; c < m_Mesh.m_VertexCount; c++) + { + colorData[c] = new Vector4(0.5f, 0.5f, 0.5f, 1.0f); + } + } + #endregion + glControl1.Visible = true; + createVAO(); } - #endregion - glControl1.Visible = true; - createVAO(); + StatusStripUpdate("Using OpenGL Version: " + GL.GetString(StringName.Version) + "\n" + + "'Mouse Left'=Rotate | 'Mouse Right'=Move | 'Mouse Wheel'=Zoom \n" + + "'Ctrl W'=Wireframe | 'Ctrl S'=Shade | 'Ctrl N'=ReNormal "); } - StatusStripUpdate("Using OpenGL Version: " + GL.GetString(StringName.Version) + "\n" - + "'Mouse Left'=Rotate | 'Mouse Right'=Move | 'Mouse Wheel'=Zoom \n" - + "'Ctrl W'=Wireframe | 'Ctrl S'=Shade | 'Ctrl N'=ReNormal "); - } - break; - case ClassIDType.VideoClip: - case ClassIDType.MovieTexture: - { - StatusStripUpdate("Only supported export."); break; - } - case ClassIDType.Sprite: - { - imageTexture?.Dispose(); - imageTexture = SpriteHelper.GetImageFromSprite(new Sprite(reader)); - if (imageTexture != null) + case ClassIDType.VideoClip: + case ClassIDType.MovieTexture: + { + StatusStripUpdate("Only supported export."); + break; + } + case ClassIDType.Sprite: { - asset.InfoText = $"Width: {imageTexture.Width}\nHeight: {imageTexture.Height}\n"; - previewPanel.BackgroundImage = imageTexture; - if (imageTexture.Width > previewPanel.Width || imageTexture.Height > previewPanel.Height) - previewPanel.BackgroundImageLayout = ImageLayout.Zoom; + imageTexture?.Dispose(); + imageTexture = SpriteHelper.GetImageFromSprite(new Sprite(reader)); + if (imageTexture != null) + { + asset.InfoText = $"Width: {imageTexture.Width}\nHeight: {imageTexture.Height}\n"; + previewPanel.BackgroundImage = imageTexture; + if (imageTexture.Width > previewPanel.Width || imageTexture.Height > previewPanel.Height) + previewPanel.BackgroundImageLayout = ImageLayout.Zoom; + else + previewPanel.BackgroundImageLayout = ImageLayout.Center; + } else - previewPanel.BackgroundImageLayout = ImageLayout.Center; + { + StatusStripUpdate("Unsupported sprite for preview."); + } + break; } - else + case ClassIDType.Animator: { - StatusStripUpdate("Unsupported sprite for preview."); + StatusStripUpdate("Can be exported to FBX file."); + break; } - break; - } - case ClassIDType.Animator: - { - StatusStripUpdate("Can be exported to FBX file."); - break; - } - case ClassIDType.AnimationClip: - { - StatusStripUpdate("Can be exported with Animator or objects"); - break; - } - default: - { - var str = reader.Dump(); - if (str != null) + case ClassIDType.AnimationClip: { - textPreviewBox.Text = str; - textPreviewBox.Visible = true; + StatusStripUpdate("Can be exported with Animator or objects"); + break; } - else - StatusStripUpdate("Only supported export the raw file."); - break; - } + default: + { + var str = reader.Dump(); + if (str != null) + { + textPreviewBox.Text = str; + textPreviewBox.Visible = true; + } + else + StatusStripUpdate("Only supported export the raw file."); + break; + } + } + } + catch (Exception e) + { + MessageBox.Show($"Preview {asset.Type}:{asset.Text} error\r\n{e.Message}\r\n{e.StackTrace}"); } } @@ -1410,30 +1407,6 @@ private void SetProgressBarValue(int value) } } - private void SetProgressBarMaximum(int value) - { - if (InvokeRequired) - { - BeginInvoke(new Action(() => { progressBar1.Maximum = value; })); - } - else - { - progressBar1.Maximum = value; - } - } - - private void ProgressBarPerformStep() - { - if (InvokeRequired) - { - BeginInvoke(new Action(() => { progressBar1.PerformStep(); })); - } - else - { - progressBar1.PerformStep(); - } - } - private void StatusStripUpdate(string statusText) { if (InvokeRequired) @@ -1446,19 +1419,7 @@ private void StatusStripUpdate(string statusText) } } - private void ProgressBarMaximumAdd(int value) - { - if (InvokeRequired) - { - BeginInvoke(new Action(() => { progressBar1.Maximum += value; })); - } - else - { - progressBar1.Maximum += value; - } - } - - public AssetStudioForm() + public AssetStudioGUIForm() { Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US"); InitializeComponent(); @@ -1469,12 +1430,9 @@ public AssetStudioForm() openAfterExport.Checked = (bool)Properties.Settings.Default["openAfterExport"]; assetGroupOptions.SelectedIndex = (int)Properties.Settings.Default["assetGroupOption"]; FMODinit(); - //UI - Studio.SetProgressBarValue = SetProgressBarValue; - Studio.SetProgressBarMaximum = SetProgressBarMaximum; - Studio.ProgressBarPerformStep = ProgressBarPerformStep; - Studio.StatusStripUpdate = StatusStripUpdate; - Studio.ProgressBarMaximumAdd = ProgressBarMaximumAdd; + + Logger.Default = new GUILogger(StatusStripUpdate); + Progress.Default = new GUIProgress(SetProgressBarValue); } private void initOpenTK() @@ -1705,32 +1663,15 @@ private void glControl1_MouseUp(object sender, MouseEventArgs e) private void ResetForm() { - Text = "AssetStudio"; - - importFiles.Clear(); - foreach (var assetsFile in assetsfileList) - { - assetsFile.reader.Dispose(); - } - assetsfileList.Clear(); + Text = "AssetStudioGUI"; + assetsManager.Clear(); exportableAssets.Clear(); visibleAssets.Clear(); - foreach (var resourceFileReader in resourceFileReaders) - { - resourceFileReader.Value.Dispose(); - } - resourceFileReaders.Clear(); - assetsFileIndexCache.Clear(); - productName = string.Empty; - sceneTreeView.Nodes.Clear(); - assetListView.VirtualListSize = 0; assetListView.Items.Clear(); - classesListView.Items.Clear(); classesListView.Groups.Clear(); - previewPanel.BackgroundImage = Properties.Resources.preview; previewPanel.BackgroundImageLayout = ImageLayout.Center; assetInfoLabel.Visible = false; @@ -1754,9 +1695,13 @@ private void ResetForm() FMODreset(); - ScriptHelper.moduleLoaded = false; - ScriptHelper.LoadedModuleDic.Clear(); - treeNodeCollection.Clear(); + ModuleLoaded = false; + foreach (var pair in LoadedModuleDic) + { + pair.Value.Dispose(); + } + LoadedModuleDic.Clear(); + treeNodeDictionary.Clear(); } @@ -1804,7 +1749,7 @@ private void exportSelectedAssetsToolStripMenuItem_Click(object sender, EventArg private void showOriginalFileToolStripMenuItem_Click(object sender, EventArgs e) { var selectasset = (AssetItem)assetListView.Items[assetListView.SelectedIndices[0]]; - var args = $"/select, \"{selectasset.sourceFile.parentPath ?? selectasset.sourceFile.filePath}\""; + var args = $"/select, \"{selectasset.sourceFile.originalPath ?? selectasset.sourceFile.fullName}\""; var pfi = new ProcessStartInfo("explorer.exe", args); Process.Start(pfi); } @@ -1832,8 +1777,6 @@ private void exportAnimatorwithAnimationClipMenuItem_Click(object sender, EventA if (saveFolderDialog1.ShowDialog(this) == DialogResult.OK) { var exportPath = saveFolderDialog1.Folder + "\\Animator\\"; - progressBar1.Value = 0; - progressBar1.Maximum = 1; ExportAnimatorWithAnimationClip(animator, animationList, exportPath); } } @@ -1892,8 +1835,6 @@ private void exportAllObjectssplitToolStripMenuItem1_Click(object sender, EventA if (saveFolderDialog1.ShowDialog(this) == DialogResult.OK) { var savePath = saveFolderDialog1.Folder + "\\"; - progressBar1.Value = 0; - progressBar1.Maximum = sceneTreeView.Nodes.Cast().Sum(x => x.Nodes.Count); ; ExportSplitObjects(savePath, sceneTreeView.Nodes); } } diff --git a/AssetStudio/AssetStudioForm.resx b/AssetStudioGUI/AssetStudioGUIForm.resx similarity index 100% rename from AssetStudio/AssetStudioForm.resx rename to AssetStudioGUI/AssetStudioGUIForm.resx diff --git a/AssetStudio/StudioClasses/AssetItem.cs b/AssetStudioGUI/Components/AssetItem.cs similarity index 73% rename from AssetStudio/StudioClasses/AssetItem.cs rename to AssetStudioGUI/Components/AssetItem.cs index 5eef4b13..81f9490f 100644 --- a/AssetStudio/StudioClasses/AssetItem.cs +++ b/AssetStudioGUI/Components/AssetItem.cs @@ -1,13 +1,11 @@ -using System; -using System.Linq; -using System.Text; -using System.Windows.Forms; +using System.Windows.Forms; +using AssetStudio; -namespace AssetStudio +namespace AssetStudioGUI { - public class AssetItem : ListViewItem + internal class AssetItem : ListViewItem { - public AssetsFile sourceFile; + public SerializedFile sourceFile; public ObjectReader reader; public long FullSize; public ClassIDType Type; diff --git a/AssetStudio/GOHierarchy.cs b/AssetStudioGUI/Components/GOHierarchy.cs similarity index 81% rename from AssetStudio/GOHierarchy.cs rename to AssetStudioGUI/Components/GOHierarchy.cs index 664d79f1..cc734d35 100644 --- a/AssetStudio/GOHierarchy.cs +++ b/AssetStudioGUI/Components/GOHierarchy.cs @@ -4,9 +4,9 @@ using System.Text; using System.Windows.Forms; -namespace AssetStudio +namespace AssetStudioGUI { - public class GOHierarchy : TreeView + internal class GOHierarchy : TreeView { protected override void WndProc(ref Message m) { diff --git a/AssetStudioGUI/Components/GameObjectTreeNode.cs b/AssetStudioGUI/Components/GameObjectTreeNode.cs new file mode 100644 index 00000000..f4c6e4e9 --- /dev/null +++ b/AssetStudioGUI/Components/GameObjectTreeNode.cs @@ -0,0 +1,21 @@ +using System.Windows.Forms; +using AssetStudio; + +namespace AssetStudioGUI +{ + internal class GameObjectTreeNode : TreeNode + { + public GameObject gameObject; + + public GameObjectTreeNode(string name) + { + Text = name; + } + + public GameObjectTreeNode(GameObject gameObject) + { + this.gameObject = gameObject; + Text = gameObject.m_Name; + } + } +} diff --git a/AssetStudio/OpenFolderDialog.cs b/AssetStudioGUI/Components/OpenFolderDialog.cs similarity index 99% rename from AssetStudio/OpenFolderDialog.cs rename to AssetStudioGUI/Components/OpenFolderDialog.cs index d363b1ab..d832f012 100644 --- a/AssetStudio/OpenFolderDialog.cs +++ b/AssetStudioGUI/Components/OpenFolderDialog.cs @@ -4,9 +4,9 @@ using System.Runtime.InteropServices; using System.Windows.Forms; -namespace AssetStudio +namespace AssetStudioGUI { - class OpenFolderDialog + internal class OpenFolderDialog { public string InitialFolder { get; set; } public string DefaultFolder { get; set; } diff --git a/AssetStudio/TreeViewExtensions.cs b/AssetStudioGUI/Components/TreeViewExtensions.cs similarity index 91% rename from AssetStudio/TreeViewExtensions.cs rename to AssetStudioGUI/Components/TreeViewExtensions.cs index 4eedecd5..d06bfa76 100644 --- a/AssetStudio/TreeViewExtensions.cs +++ b/AssetStudioGUI/Components/TreeViewExtensions.cs @@ -1,13 +1,10 @@ using System; -using System.Collections.Generic; -using System.Linq; using System.Runtime.InteropServices; -using System.Text; using System.Windows.Forms; -namespace AssetStudio +namespace AssetStudioGUI { - public static class TreeViewExtensions + internal static class TreeViewExtensions { private const int TVIF_STATE = 0x8; private const int TVIS_STATEIMAGEMASK = 0xF000; diff --git a/AssetStudio/StudioClasses/TypeTreeItem.cs b/AssetStudioGUI/Components/TypeTreeItem.cs similarity index 78% rename from AssetStudio/StudioClasses/TypeTreeItem.cs rename to AssetStudioGUI/Components/TypeTreeItem.cs index 3badfdd9..e7ac72e5 100644 --- a/AssetStudio/StudioClasses/TypeTreeItem.cs +++ b/AssetStudioGUI/Components/TypeTreeItem.cs @@ -1,14 +1,13 @@ -using System; -using System.Collections.Generic; -using System.Linq; +using System.Collections.Generic; using System.Text; using System.Windows.Forms; +using AssetStudio; -namespace AssetStudio +namespace AssetStudioGUI { - public class TypeTreeItem : ListViewItem + internal class TypeTreeItem : ListViewItem { - public List m_Nodes; + private List m_Nodes; public TypeTreeItem(int typeID, List m_Nodes) { diff --git a/AssetStudio/ExportOptions.Designer.cs b/AssetStudioGUI/ExportOptions.Designer.cs similarity index 99% rename from AssetStudio/ExportOptions.Designer.cs rename to AssetStudioGUI/ExportOptions.Designer.cs index 692e0e62..fd95457a 100644 --- a/AssetStudio/ExportOptions.Designer.cs +++ b/AssetStudioGUI/ExportOptions.Designer.cs @@ -1,4 +1,4 @@ -namespace AssetStudio +namespace AssetStudioGUI { partial class ExportOptions { diff --git a/AssetStudio/ExportOptions.cs b/AssetStudioGUI/ExportOptions.cs similarity index 99% rename from AssetStudio/ExportOptions.cs rename to AssetStudioGUI/ExportOptions.cs index 2b6ef08d..f23d9aa1 100644 --- a/AssetStudio/ExportOptions.cs +++ b/AssetStudioGUI/ExportOptions.cs @@ -8,7 +8,7 @@ using System.Text; using System.Windows.Forms; -namespace AssetStudio +namespace AssetStudioGUI { public partial class ExportOptions : Form { diff --git a/AssetStudio/ExportOptions.resx b/AssetStudioGUI/ExportOptions.resx similarity index 100% rename from AssetStudio/ExportOptions.resx rename to AssetStudioGUI/ExportOptions.resx diff --git a/AssetStudio/StudioClasses/Exporter.cs b/AssetStudioGUI/Exporter.cs similarity index 71% rename from AssetStudio/StudioClasses/Exporter.cs rename to AssetStudioGUI/Exporter.cs index 1557f4c2..a99455ee 100644 --- a/AssetStudio/StudioClasses/Exporter.cs +++ b/AssetStudioGUI/Exporter.cs @@ -1,25 +1,24 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Drawing.Imaging; using System.IO; using System.Linq; -using System.Runtime.InteropServices; using System.Text; +using AssetStudio; -namespace AssetStudio +namespace AssetStudioGUI { - static class Exporter + internal static class Exporter { - public static bool ExportTexture2D(ObjectReader reader, string exportPathName, bool flip) + public static bool ExportTexture2D(AssetItem item, string exportPathName) { - var m_Texture2D = new Texture2D(reader, true); + var m_Texture2D = new Texture2D(item.reader, true); if (m_Texture2D.image_data == null || m_Texture2D.image_data.Length == 0) return false; var converter = new Texture2DConverter(m_Texture2D); var convertTexture = (bool)Properties.Settings.Default["convertTexture"]; if (convertTexture) { - var bitmap = converter.ConvertToBitmap(flip); + var bitmap = converter.ConvertToBitmap(true); if (bitmap == null) return false; ImageFormat format = null; @@ -36,7 +35,7 @@ public static bool ExportTexture2D(ObjectReader reader, string exportPathName, b format = ImageFormat.Jpeg; break; } - var exportFullName = exportPathName + reader.exportName + "." + ext.ToLower(); + var exportFullName = exportPathName + item.Text + "." + ext.ToLower(); if (ExportFileExists(exportFullName)) return false; bitmap.Save(exportFullName, format); @@ -45,7 +44,7 @@ public static bool ExportTexture2D(ObjectReader reader, string exportPathName, b } else { - var exportFullName = exportPathName + reader.exportName + converter.GetExtensionName(); + var exportFullName = exportPathName + item.Text + converter.GetExtensionName(); if (ExportFileExists(exportFullName)) return false; File.WriteAllBytes(exportFullName, converter.ConvertToContainer()); @@ -53,16 +52,16 @@ public static bool ExportTexture2D(ObjectReader reader, string exportPathName, b } } - public static bool ExportAudioClip(ObjectReader reader, string exportPath) + public static bool ExportAudioClip(AssetItem item, string exportPath) { - var m_AudioClip = new AudioClip(reader, true); + var m_AudioClip = new AudioClip(item.reader, true); if (m_AudioClip.m_AudioData == null) return false; var convertAudio = (bool)Properties.Settings.Default["convertAudio"]; var converter = new AudioClipConverter(m_AudioClip); if (convertAudio && converter.IsFMODSupport) { - var exportFullName = exportPath + reader.exportName + ".wav"; + var exportFullName = exportPath + item.Text + ".wav"; if (ExportFileExists(exportFullName)) return false; var buffer = converter.ConvertToWav(); @@ -72,7 +71,7 @@ public static bool ExportAudioClip(ObjectReader reader, string exportPath) } else { - var exportFullName = exportPath + reader.exportName + converter.GetExtensionName(); + var exportFullName = exportPath + item.Text + converter.GetExtensionName(); if (ExportFileExists(exportFullName)) return false; File.WriteAllBytes(exportFullName, m_AudioClip.m_AudioData); @@ -80,10 +79,10 @@ public static bool ExportAudioClip(ObjectReader reader, string exportPath) return true; } - public static bool ExportShader(ObjectReader reader, string exportPath) + public static bool ExportShader(AssetItem item, string exportPath) { - var m_Shader = new Shader(reader); - var exportFullName = exportPath + reader.exportName + ".shader"; + var m_Shader = new Shader(item.reader); + var exportFullName = exportPath + item.Text + ".shader"; if (ExportFileExists(exportFullName)) return false; var str = ShaderConverter.Convert(m_Shader); @@ -91,22 +90,22 @@ public static bool ExportShader(ObjectReader reader, string exportPath) return true; } - public static bool ExportTextAsset(ObjectReader reader, string exportPath) + public static bool ExportTextAsset(AssetItem item, string exportPath) { - var m_TextAsset = new TextAsset(reader); - var exportFullName = exportPath + reader.exportName + ".txt"; + var m_TextAsset = new TextAsset(item.reader); + var exportFullName = exportPath + item.Text + ".txt"; if (ExportFileExists(exportFullName)) return false; File.WriteAllBytes(exportFullName, m_TextAsset.m_Script); return true; } - public static bool ExportMonoBehaviour(ObjectReader reader, string exportPath) + public static bool ExportMonoBehaviour(AssetItem item, string exportPath) { - var exportFullName = exportPath + reader.exportName + ".txt"; + var exportFullName = exportPath + item.Text + ".txt"; if (ExportFileExists(exportFullName)) return false; - var m_MonoBehaviour = new MonoBehaviour(reader); + var reader = item.reader; string str; if (reader.serializedType?.m_Nodes != null) { @@ -114,15 +113,15 @@ public static bool ExportMonoBehaviour(ObjectReader reader, string exportPath) } else { - str = ScriptHelper.GetScriptString(reader); + str = Studio.GetScriptString(reader); } File.WriteAllText(exportFullName, str); return true; } - public static bool ExportFont(ObjectReader reader, string exportPath) + public static bool ExportFont(AssetItem item, string exportPath) { - var m_Font = new Font(reader); + var m_Font = new Font(item.reader); if (m_Font.m_FontData != null) { var extension = ".ttf"; @@ -130,7 +129,7 @@ public static bool ExportFont(ObjectReader reader, string exportPath) { extension = ".otf"; } - var exportFullName = exportPath + reader.exportName + extension; + var exportFullName = exportPath + item.Text + extension; if (ExportFileExists(exportFullName)) return false; File.WriteAllBytes(exportFullName, m_Font.m_FontData); @@ -139,12 +138,12 @@ public static bool ExportFont(ObjectReader reader, string exportPath) return false; } - public static bool ExportMesh(ObjectReader reader, string exportPath) + public static bool ExportMesh(AssetItem item, string exportPath) { - var m_Mesh = new Mesh(reader); + var m_Mesh = new Mesh(item.reader); if (m_Mesh.m_VertexCount <= 0) return false; - var exportFullName = exportPath + reader.exportName + ".obj"; + var exportFullName = exportPath + item.Text + ".obj"; if (ExportFileExists(exportFullName)) return false; var sb = new StringBuilder(); @@ -166,18 +165,18 @@ public static bool ExportMesh(ObjectReader reader, string exportPath) #endregion #region UV - if (m_Mesh.m_UV1 != null && m_Mesh.m_UV1.Length == m_Mesh.m_VertexCount * 2) + if (m_Mesh.m_UV0 != null && m_Mesh.m_UV0.Length == m_Mesh.m_VertexCount * 2) { for (int v = 0; v < m_Mesh.m_VertexCount; v++) { - sb.AppendFormat("vt {0} {1}\r\n", m_Mesh.m_UV1[v * 2], m_Mesh.m_UV1[v * 2 + 1]); + sb.AppendFormat("vt {0} {1}\r\n", m_Mesh.m_UV0[v * 2], m_Mesh.m_UV0[v * 2 + 1]); } } - else if (m_Mesh.m_UV2 != null && m_Mesh.m_UV2.Length == m_Mesh.m_VertexCount * 2) + else if (m_Mesh.m_UV1 != null && m_Mesh.m_UV1.Length == m_Mesh.m_VertexCount * 2) { for (int v = 0; v < m_Mesh.m_VertexCount; v++) { - sb.AppendFormat("vt {0} {1}\r\n", m_Mesh.m_UV2[v * 2], m_Mesh.m_UV2[v * 2 + 1]); + sb.AppendFormat("vt {0} {1}\r\n", m_Mesh.m_UV1[v * 2], m_Mesh.m_UV1[v * 2 + 1]); } } #endregion @@ -220,12 +219,12 @@ public static bool ExportMesh(ObjectReader reader, string exportPath) return true; } - public static bool ExportVideoClip(ObjectReader reader, string exportPath) + public static bool ExportVideoClip(AssetItem item, string exportPath) { - var m_VideoClip = new VideoClip(reader, true); + var m_VideoClip = new VideoClip(item.reader, true); if (m_VideoClip.m_VideoData != null) { - var exportFullName = exportPath + reader.exportName + Path.GetExtension(m_VideoClip.m_OriginalPath); + var exportFullName = exportPath + item.Text + Path.GetExtension(m_VideoClip.m_OriginalPath); if (ExportFileExists(exportFullName)) return false; File.WriteAllBytes(exportFullName, m_VideoClip.m_VideoData); @@ -234,17 +233,17 @@ public static bool ExportVideoClip(ObjectReader reader, string exportPath) return false; } - public static bool ExportMovieTexture(ObjectReader reader, string exportPath) + public static bool ExportMovieTexture(AssetItem item, string exportPath) { - var m_MovieTexture = new MovieTexture(reader); - var exportFullName = exportPath + reader.exportName + ".ogv"; + var m_MovieTexture = new MovieTexture(item.reader); + var exportFullName = exportPath + item.Text + ".ogv"; if (ExportFileExists(exportFullName)) return false; File.WriteAllBytes(exportFullName, m_MovieTexture.m_MovieData); return true; } - public static bool ExportSprite(ObjectReader reader, string exportPath) + public static bool ExportSprite(AssetItem item, string exportPath) { ImageFormat format = null; var type = (string)Properties.Settings.Default["convertType"]; @@ -260,10 +259,10 @@ public static bool ExportSprite(ObjectReader reader, string exportPath) format = ImageFormat.Jpeg; break; } - var exportFullName = exportPath + reader.exportName + "." + type.ToLower(); + var exportFullName = exportPath + item.Text + "." + type.ToLower(); if (ExportFileExists(exportFullName)) return false; - var bitmap = SpriteHelper.GetImageFromSprite(new Sprite(reader)); + var bitmap = SpriteHelper.GetImageFromSprite(new Sprite(item.reader)); if (bitmap != null) { bitmap.Save(exportFullName, format); @@ -273,12 +272,12 @@ public static bool ExportSprite(ObjectReader reader, string exportPath) return false; } - public static bool ExportRawFile(ObjectReader reader, string exportPath) + public static bool ExportRawFile(AssetItem item, string exportPath) { - var exportFullName = exportPath + reader.exportName + ".dat"; + var exportFullName = exportPath + item.Text + ".dat"; if (ExportFileExists(exportFullName)) return false; - File.WriteAllBytes(exportFullName, reader.GetRawData()); + File.WriteAllBytes(exportFullName, item.reader.GetRawData()); return true; } @@ -292,22 +291,22 @@ private static bool ExportFileExists(string filename) return false; } - public static bool ExportAnimator(ObjectReader animator, string exportPath, List animationList = null) + public static bool ExportAnimator(AssetItem item, string exportPath, List animationList = null) { - var m_Animator = new Animator(animator); - var convert = animationList != null ? new ModelConverter(m_Animator, animationList) : new ModelConverter(m_Animator); - exportPath = exportPath + Studio.FixFileName(animator.exportName) + ".fbx"; - return ModelConverter(convert, exportPath); + var m_Animator = new Animator(item.reader); + var convert = animationList != null ? new ModelConverter(m_Animator, animationList.Select(x => x.reader).ToArray()) : new ModelConverter(m_Animator); + exportPath = exportPath + item.Text + ".fbx"; + return ExportFbx(convert, exportPath); } public static bool ExportGameObject(GameObject gameObject, string exportPath, List animationList = null) { - var convert = animationList != null ? new ModelConverter(gameObject, animationList) : new ModelConverter(gameObject); + var convert = animationList != null ? new ModelConverter(gameObject, animationList.Select(x => x.reader).ToArray()) : new ModelConverter(gameObject); exportPath = exportPath + Studio.FixFileName(gameObject.m_Name) + ".fbx"; - return ModelConverter(convert, exportPath); + return ExportFbx(convert, exportPath); } - private static bool ModelConverter(ModelConverter convert, string exportPath) + private static bool ExportFbx(IImported convert, string exportPath) { var eulerFilter = (bool)Properties.Settings.Default["eulerFilter"]; var filterPrecision = (float)(decimal)Properties.Settings.Default["filterPrecision"]; @@ -319,7 +318,7 @@ private static bool ModelConverter(ModelConverter convert, string exportPath) var flatInbetween = (bool)Properties.Settings.Default["flatInbetween"]; var fbxVersion = (int)Properties.Settings.Default["fbxVersion"]; var fbxFormat = (int)Properties.Settings.Default["fbxFormat"]; - Fbx.Exporter.Export(exportPath, convert, eulerFilter, filterPrecision, allFrames, allBones, skins, boneSize, scaleFactor, flatInbetween, fbxVersion, fbxFormat == 1); + ModelExporter.ExportFbx(exportPath, convert, eulerFilter, filterPrecision, allFrames, allBones, skins, boneSize, scaleFactor, flatInbetween, fbxVersion, fbxFormat == 1); return true; } } diff --git a/AssetStudioGUI/GUILogger.cs b/AssetStudioGUI/GUILogger.cs new file mode 100644 index 00000000..2e264a58 --- /dev/null +++ b/AssetStudioGUI/GUILogger.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using AssetStudio; + +namespace AssetStudioGUI +{ + class GUILogger : ILogger + { + private Action action; + + public GUILogger(Action action) + { + this.action = action; + } + + public void Log(LoggerEvent loggerEvent, string message) + { + action(message); + } + } +} diff --git a/AssetStudioGUI/GUIProgress.cs b/AssetStudioGUI/GUIProgress.cs new file mode 100644 index 00000000..7efe200c --- /dev/null +++ b/AssetStudioGUI/GUIProgress.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using AssetStudio; + +namespace AssetStudioGUI +{ + class GUIProgress : IProgress + { + private Action action; + + public GUIProgress(Action action) + { + this.action = action; + } + + public void Report(int value) + { + action(value); + } + } +} diff --git a/AssetStudio/Program.cs b/AssetStudioGUI/Program.cs similarity index 83% rename from AssetStudio/Program.cs rename to AssetStudioGUI/Program.cs index c76c4eb4..d8ed1049 100644 --- a/AssetStudio/Program.cs +++ b/AssetStudioGUI/Program.cs @@ -3,7 +3,7 @@ using System.Linq; using System.Windows.Forms; -namespace AssetStudio +namespace AssetStudioGUI { static class Program { @@ -15,7 +15,7 @@ static void Main() { Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); - Application.Run(new AssetStudioForm()); + Application.Run(new AssetStudioGUIForm()); } } } diff --git a/AssetStudioGUI/Properties/AssemblyInfo.cs b/AssetStudioGUI/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..0fb74499 --- /dev/null +++ b/AssetStudioGUI/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("AssetStudioGUI")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("AssetStudioGUI")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("05c04c20-dd89-4895-9f06-33d5cfbfe925")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/AssetStudio/Properties/Resources.Designer.cs b/AssetStudioGUI/Properties/Resources.Designer.cs similarity index 95% rename from AssetStudio/Properties/Resources.Designer.cs rename to AssetStudioGUI/Properties/Resources.Designer.cs index 2ad9dfb2..9fd284be 100644 --- a/AssetStudio/Properties/Resources.Designer.cs +++ b/AssetStudioGUI/Properties/Resources.Designer.cs @@ -8,7 +8,7 @@ // //------------------------------------------------------------------------------ -namespace AssetStudio.Properties { +namespace AssetStudioGUI.Properties { using System; @@ -39,16 +39,15 @@ internal Resources() { internal static global::System.Resources.ResourceManager ResourceManager { get { if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("AssetStudio.Properties.Resources", typeof(Resources).Assembly); + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("AssetStudioGUI.Properties.Resources", typeof(Resources).Assembly); resourceMan = temp; } return resourceMan; } } - + /// - /// 使用此强类型资源类,为所有资源查找 - /// 重写当前线程的 CurrentUICulture 属性。 + /// 重写当前线程的 CurrentUICulture 属性 /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] internal static global::System.Globalization.CultureInfo Culture { diff --git a/AssetStudio/Properties/Resources.resx b/AssetStudioGUI/Properties/Resources.resx similarity index 100% rename from AssetStudio/Properties/Resources.resx rename to AssetStudioGUI/Properties/Resources.resx diff --git a/AssetStudio/Properties/Settings.Designer.cs b/AssetStudioGUI/Properties/Settings.Designer.cs similarity index 99% rename from AssetStudio/Properties/Settings.Designer.cs rename to AssetStudioGUI/Properties/Settings.Designer.cs index 7563169e..3481db64 100644 --- a/AssetStudio/Properties/Settings.Designer.cs +++ b/AssetStudioGUI/Properties/Settings.Designer.cs @@ -8,11 +8,11 @@ // //------------------------------------------------------------------------------ -namespace AssetStudio.Properties { +namespace AssetStudioGUI.Properties { [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.8.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.9.0.0")] internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); diff --git a/AssetStudio/Properties/Settings.settings b/AssetStudioGUI/Properties/Settings.settings similarity index 97% rename from AssetStudio/Properties/Settings.settings rename to AssetStudioGUI/Properties/Settings.settings index e5d5fa9f..02aa9152 100644 --- a/AssetStudio/Properties/Settings.settings +++ b/AssetStudioGUI/Properties/Settings.settings @@ -1,5 +1,5 @@  - + diff --git a/AssetStudio/Resources/as.ico b/AssetStudioGUI/Resources/as.ico similarity index 100% rename from AssetStudio/Resources/as.ico rename to AssetStudioGUI/Resources/as.ico diff --git a/AssetStudio/Resources/preview.png b/AssetStudioGUI/Resources/preview.png similarity index 100% rename from AssetStudio/Resources/preview.png rename to AssetStudioGUI/Resources/preview.png diff --git a/AssetStudioGUI/Studio.cs b/AssetStudioGUI/Studio.cs new file mode 100644 index 00000000..f04903f7 --- /dev/null +++ b/AssetStudioGUI/Studio.cs @@ -0,0 +1,711 @@ +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 assetsNameHash = new HashSet(); + public static List exportableAssets = new List(); + public static List visibleAssets = new List(); + public static Dictionary> AllTypeMap = new Dictionary>(); + public static Dictionary treeNodeDictionary = new Dictionary(); + public static bool ModuleLoaded; + public static Dictionary LoadedModuleDic = new Dictionary(); + + 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 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 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(); + 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 BuildTreeStructure(Dictionary tempDic) + { + var treeNodeCollection = new List(); + 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(); + 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 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().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(); + 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 gameObjects) + { + gameObjects.Add(node.gameObject); + foreach (GameObjectTreeNode i in node.Nodes) + { + CollectNode(i, gameObjects); + } + } + + public static void ExportAnimatorWithAnimationClip(AssetItem animator, List 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 animationList = null) + { + ThreadPool.QueueUserWorkItem(state => + { + var gameObjects = new List(); + 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 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); + } + } +} diff --git a/AssetStudioGUI/app.config b/AssetStudioGUI/app.config new file mode 100644 index 00000000..099c4be7 --- /dev/null +++ b/AssetStudioGUI/app.config @@ -0,0 +1,69 @@ + + + + +
+ + + + + + False + + + True + + + True + + + True + + + 0 + + + True + + + True + + + PNG + + + False + + + True + + + 0.25 + + + False + + + True + + + True + + + 10 + + + False + + + 3 + + + 0 + + + 1 + + + + \ No newline at end of file diff --git a/AssetStudioTools/AssetStudioTools.csproj b/AssetStudioTools/AssetStudioTools.csproj new file mode 100644 index 00000000..cd99becc --- /dev/null +++ b/AssetStudioTools/AssetStudioTools.csproj @@ -0,0 +1,101 @@ + + + + + Debug + AnyCPU + {9131C403-7FE8-444D-9AF5-5FE5DF76FF24} + Library + Properties + AssetStudio + AssetStudioTools + v4.0 + 512 + + + true + bin\x64\Debug\ + DEBUG;TRACE + full + x64 + prompt + MinimumRecommendedRules.ruleset + + + bin\x64\Release\ + TRACE + true + pdbonly + x64 + prompt + MinimumRecommendedRules.ruleset + + + true + bin\x86\Debug\ + DEBUG;TRACE + full + x86 + prompt + MinimumRecommendedRules.ruleset + + + bin\x86\Release\ + TRACE + true + pdbonly + x86 + prompt + MinimumRecommendedRules.ruleset + + + + ..\AssetStudio\Libraries\dnlib.dll + + + False + ..\AssetStudio\Libraries\SharpDX.dll + + + ..\AssetStudio\Libraries\SharpDX.D3DCompiler.dll + + + ..\AssetStudio\Libraries\SharpDX.Mathematics.dll + + + + + + ..\AssetStudio\Libraries\System.Half.dll + + + + + + + + + + + + + + + + + + + + + + + {4f8ef5ef-732b-49cf-9eb3-b23e19ae6267} + AssetStudioFBX + + + {af56b63c-1764-41b7-9e60-8d485422ac3b} + AssetStudio + + + + \ No newline at end of file diff --git a/AssetStudio/StudioClasses/AudioClipConverter.cs b/AssetStudioTools/AudioClipConverter.cs similarity index 91% rename from AssetStudio/StudioClasses/AudioClipConverter.cs rename to AssetStudioTools/AudioClipConverter.cs index f50a20b6..ec6452e4 100644 --- a/AssetStudio/StudioClasses/AudioClipConverter.cs +++ b/AssetStudioTools/AudioClipConverter.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; using System.Runtime.InteropServices; using System.Text; @@ -32,13 +30,13 @@ public byte[] ConvertToWav() result = sound.getSubSound(0, out var subsound); if (result != FMOD.RESULT.OK) return null; - result = subsound.getFormat(out var type, out var format, out int NumChannels, out int BitsPerSample); + result = subsound.getFormat(out var type, out var format, out int channels, out int bits); if (result != FMOD.RESULT.OK) return null; result = subsound.getDefaults(out var frequency, out int priority); if (result != FMOD.RESULT.OK) return null; - var SampleRate = (int)frequency; + var sampleRate = (int)frequency; result = subsound.getLength(out var length, FMOD.TIMEUNIT.PCMBYTES); if (result != FMOD.RESULT.OK) return null; @@ -52,11 +50,11 @@ public byte[] ConvertToWav() Encoding.UTF8.GetBytes("WAVEfmt ").CopyTo(buffer, 8); BitConverter.GetBytes(16).CopyTo(buffer, 16); BitConverter.GetBytes((short)1).CopyTo(buffer, 20); - BitConverter.GetBytes((short)NumChannels).CopyTo(buffer, 22); - BitConverter.GetBytes(SampleRate).CopyTo(buffer, 24); - BitConverter.GetBytes(SampleRate * NumChannels * BitsPerSample / 8).CopyTo(buffer, 28); - BitConverter.GetBytes((short)(NumChannels * BitsPerSample / 8)).CopyTo(buffer, 32); - BitConverter.GetBytes((short)BitsPerSample).CopyTo(buffer, 34); + BitConverter.GetBytes((short)channels).CopyTo(buffer, 22); + BitConverter.GetBytes(sampleRate).CopyTo(buffer, 24); + BitConverter.GetBytes(sampleRate * channels * bits / 8).CopyTo(buffer, 28); + BitConverter.GetBytes((short)(channels * bits / 8)).CopyTo(buffer, 32); + BitConverter.GetBytes((short)bits).CopyTo(buffer, 34); Encoding.UTF8.GetBytes("data").CopyTo(buffer, 36); BitConverter.GetBytes(len1).CopyTo(buffer, 40); Marshal.Copy(ptr1, buffer, 44, (int)len1); diff --git a/AssetStudio/FMOD Studio API/fmod.cs b/AssetStudioTools/FMOD Studio API/fmod.cs similarity index 100% rename from AssetStudio/FMOD Studio API/fmod.cs rename to AssetStudioTools/FMOD Studio API/fmod.cs diff --git a/AssetStudio/FMOD Studio API/fmod_dsp.cs b/AssetStudioTools/FMOD Studio API/fmod_dsp.cs similarity index 100% rename from AssetStudio/FMOD Studio API/fmod_dsp.cs rename to AssetStudioTools/FMOD Studio API/fmod_dsp.cs diff --git a/AssetStudio/FMOD Studio API/fmod_errors.cs b/AssetStudioTools/FMOD Studio API/fmod_errors.cs similarity index 100% rename from AssetStudio/FMOD Studio API/fmod_errors.cs rename to AssetStudioTools/FMOD Studio API/fmod_errors.cs diff --git a/AssetStudio/StudioClasses/ModelConverter.cs b/AssetStudioTools/ModelConverter.cs similarity index 94% rename from AssetStudio/StudioClasses/ModelConverter.cs rename to AssetStudioTools/ModelConverter.cs index 2ac9c28b..68893b6f 100644 --- a/AssetStudio/StudioClasses/ModelConverter.cs +++ b/AssetStudioTools/ModelConverter.cs @@ -8,7 +8,7 @@ namespace AssetStudio { - class ModelConverter : IImported + public class ModelConverter : IImported { public List FrameList { get; protected set; } = new List(); public List MeshList { get; protected set; } = new List(); @@ -21,6 +21,7 @@ class ModelConverter : IImported private Dictionary morphChannelInfo = new Dictionary(); private HashSet animationClipHashSet = new HashSet(); private Dictionary bonePathHash = new Dictionary(); + private Dictionary textureNameDictionary = new Dictionary(); public ModelConverter(GameObject m_GameObject) { @@ -35,7 +36,7 @@ public ModelConverter(GameObject m_GameObject) ConvertAnimations(); } - public ModelConverter(GameObject m_GameObject, List animationList) + public ModelConverter(GameObject m_GameObject, ObjectReader[] animationList) { if (m_GameObject.m_Animator != null && m_GameObject.m_Animator.TryGet(out var m_Animator)) { @@ -44,9 +45,9 @@ public ModelConverter(GameObject m_GameObject, List animationList) } else InitWithGameObject(m_GameObject); - foreach (var assetPreloadData in animationList) + foreach (var animationClip in animationList) { - animationClipHashSet.Add(assetPreloadData.reader); + animationClipHashSet.Add(animationClip); } ConvertAnimations(); } @@ -58,12 +59,12 @@ public ModelConverter(Animator m_Animator) ConvertAnimations(); } - public ModelConverter(Animator m_Animator, List animationList) + public ModelConverter(Animator m_Animator, ObjectReader[] animationList) { InitWithAnimator(m_Animator); - foreach (var assetPreloadData in animationList) + foreach (var animationClip in animationList) { - animationClipHashSet.Add(assetPreloadData.reader); + animationClipHashSet.Add(animationClip); } ConvertAnimations(); } @@ -338,13 +339,13 @@ private void ConvertMeshRenderer(Renderer meshR) } } //UV - if (mesh.m_UV1 != null && mesh.m_UV1.Length == mesh.m_VertexCount * 2) + if (mesh.m_UV0 != null && mesh.m_UV0.Length == mesh.m_VertexCount * 2) { - iVertex.UV = new[] { mesh.m_UV1[j * 2], -mesh.m_UV1[j * 2 + 1] }; + iVertex.UV = new[] { mesh.m_UV0[j * 2], -mesh.m_UV0[j * 2 + 1] }; } - else if (mesh.m_UV2 != null && mesh.m_UV2.Length == mesh.m_VertexCount * 2) + else if (mesh.m_UV1 != null && mesh.m_UV1.Length == mesh.m_VertexCount * 2) { - iVertex.UV = new[] { mesh.m_UV2[j * 2], -mesh.m_UV2[j * 2 + 1] }; + iVertex.UV = new[] { mesh.m_UV1[j * 2], -mesh.m_UV1[j * 2 + 1] }; } //Tangent if (mesh.m_Tangents != null && mesh.m_Tangents.Length == mesh.m_VertexCount * 4) @@ -534,7 +535,7 @@ private void ConvertMeshRenderer(Renderer meshR) } } - //TODO + //TODO combine mesh if (combine) { meshR.m_GameObject.TryGetGameObject(out var m_GameObject); @@ -673,25 +674,55 @@ private ImportedMaterial ConvertMaterial(Material mat) iMat.TexScales = new Vector2[5]; foreach (var texEnv in mat.m_TexEnvs) { - Texture2D tex2D = null; - if (texEnv.m_Texture.TryGet(out var TexturePD) && TexturePD.type == ClassIDType.Texture2D)//TODO other Texture + Texture2D m_Texture2D = null; + if (texEnv.m_Texture.TryGet(out var m_Texture) && m_Texture.type == ClassIDType.Texture2D) //TODO other Texture { - tex2D = new Texture2D(TexturePD, true); + m_Texture2D = new Texture2D(m_Texture, true); } - if (tex2D == null) + if (m_Texture2D == null) { continue; } - int dest = texEnv.name == "_MainTex" ? 0 : texEnv.name == "_BumpMap" ? 4 : texEnv.name.Contains("Spec") ? 2 : texEnv.name.Contains("Norm") ? 3 : -1; + int dest = -1; + if (texEnv.name == "_MainTex") + dest = 0; + else if (texEnv.name == "_BumpMap") + dest = 4; + else if (texEnv.name.Contains("Spec")) + dest = 2; + else if (texEnv.name.Contains("Norm")) + dest = 3; if (dest < 0 || iMat.Textures[dest] != null) { continue; } - iMat.Textures[dest] = TexturePD.exportName + ".png"; + + if (textureNameDictionary.TryGetValue(m_Texture, out var textureName)) + { + iMat.Textures[dest] = textureName; + } + else if (ImportedHelpers.FindTexture(m_Texture2D.m_Name + ".png", TextureList) != null) //已有相同名字的图片 + { + for (int i = 1; ; i++) + { + var name = m_Texture2D.m_Name + $" ({i}).png"; + if (ImportedHelpers.FindTexture(name, TextureList) == null) + { + iMat.Textures[dest] = name; + textureNameDictionary.Add(m_Texture, name); + break; + } + } + } + else + { + iMat.Textures[dest] = m_Texture2D.m_Name + ".png"; + textureNameDictionary.Add(m_Texture, iMat.Textures[dest]); + } iMat.TexOffsets[dest] = new Vector2(texEnv.m_Offset[0], texEnv.m_Offset[1]); iMat.TexScales[dest] = new Vector2(texEnv.m_Scale[0], texEnv.m_Scale[1]); - ConvertTexture2D(tex2D, iMat.Textures[dest]); + ConvertTexture2D(m_Texture2D, iMat.Textures[dest]); } MaterialList.Add(iMat); diff --git a/AssetStudioTools/ModelExporter.cs b/AssetStudioTools/ModelExporter.cs new file mode 100644 index 00000000..6c81a067 --- /dev/null +++ b/AssetStudioTools/ModelExporter.cs @@ -0,0 +1,10 @@ +namespace AssetStudio +{ + public static class ModelExporter + { + public static void ExportFbx(string path, IImported imported, bool eulerFilter, float filterPrecision, bool allFrames, bool allBones, bool skins, float boneSize, float scaleFactor, bool flatInbetween, int versionIndex, bool isAscii) + { + Fbx.Exporter.Export(path, imported, eulerFilter, filterPrecision, allFrames, allBones, skins, boneSize, scaleFactor, flatInbetween, versionIndex, isAscii); + } + } +} diff --git a/AssetStudioUtility/Properties/AssemblyInfo.cs b/AssetStudioTools/Properties/AssemblyInfo.cs similarity index 88% rename from AssetStudioUtility/Properties/AssemblyInfo.cs rename to AssetStudioTools/Properties/AssemblyInfo.cs index 762515f6..550d6493 100644 --- a/AssetStudioUtility/Properties/AssemblyInfo.cs +++ b/AssetStudioTools/Properties/AssemblyInfo.cs @@ -5,12 +5,12 @@ // 有关程序集的一般信息由以下 // 控制。更改这些特性值可修改 // 与程序集关联的信息。 -[assembly: AssemblyTitle("AssetStudioUtility")] +[assembly: AssemblyTitle("AssetStudioTools")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("AssetStudioUtility")] -[assembly: AssemblyCopyright("Copyright © 2018")] +[assembly: AssemblyProduct("AssetStudioTools")] +[assembly: AssemblyCopyright("Copyright © Perfare 2018")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] diff --git a/AssetStudio/StudioClasses/ScriptHelper.cs b/AssetStudioTools/ScriptHelper.cs similarity index 88% rename from AssetStudio/StudioClasses/ScriptHelper.cs rename to AssetStudioTools/ScriptHelper.cs index a1958a1d..022e78db 100644 --- a/AssetStudio/StudioClasses/ScriptHelper.cs +++ b/AssetStudioTools/ScriptHelper.cs @@ -1,9 +1,7 @@ using System; using System.Collections.Generic; -using System.IO; using System.Linq; using System.Text; -using System.Windows.Forms; using dnlib.DotNet; namespace AssetStudio @@ -11,45 +9,14 @@ namespace AssetStudio //TODO unfinished public static class ScriptHelper { - public static bool moduleLoaded; - public static Dictionary LoadedModuleDic = new Dictionary(); - - public static string GetScriptString(ObjectReader reader) + public static string GetScriptString(ObjectReader reader, Dictionary moduleDic) { - 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(reader); var sb = CreateMonoBehaviourHeader(m_MonoBehaviour); if (m_MonoBehaviour.m_Script.TryGet(out var script)) { var m_Script = new MonoScript(script); - if (!LoadedModuleDic.TryGetValue(m_Script.m_AssemblyName, out var module)) + if (!moduleDic.TryGetValue(m_Script.m_AssemblyName, out var module)) { return sb.ToString(); } @@ -153,7 +120,6 @@ private static void DumpType(TypeSig typeSig, StringBuilder sb, ObjectReader rea } 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++) { @@ -173,7 +139,6 @@ private static void DumpType(TypeSig typeSig, StringBuilder sb, ObjectReader rea } 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++) { diff --git a/AssetStudio/StudioClasses/ShaderConverter.cs b/AssetStudioTools/ShaderConverter.cs similarity index 99% rename from AssetStudio/StudioClasses/ShaderConverter.cs rename to AssetStudioTools/ShaderConverter.cs index 578a1a9b..e96de538 100644 --- a/AssetStudio/StudioClasses/ShaderConverter.cs +++ b/AssetStudioTools/ShaderConverter.cs @@ -50,7 +50,7 @@ public static string Convert(Shader shader) public class ShaderProgram { - public ShaderSubProgram[] m_SubPrograms; + private ShaderSubProgram[] m_SubPrograms; public ShaderProgram(BinaryReader reader) { diff --git a/AssetStudio/StudioClasses/SpriteHelper.cs b/AssetStudioTools/SpriteHelper.cs similarity index 97% rename from AssetStudio/StudioClasses/SpriteHelper.cs rename to AssetStudioTools/SpriteHelper.cs index 45b9b647..a936f7cb 100644 --- a/AssetStudio/StudioClasses/SpriteHelper.cs +++ b/AssetStudioTools/SpriteHelper.cs @@ -1,14 +1,10 @@ -using System; -using System.Collections.Generic; -using System.Drawing; +using System.Drawing; using System.Drawing.Drawing2D; using System.Drawing.Imaging; -using System.Linq; -using System.Text; namespace AssetStudio { - static class SpriteHelper + public static class SpriteHelper { public static Bitmap GetImageFromSprite(Sprite m_Sprite) { diff --git a/AssetStudio/StudioClasses/Texture2DConverter.cs b/AssetStudioTools/Texture2DConverter.cs similarity index 99% rename from AssetStudio/StudioClasses/Texture2DConverter.cs rename to AssetStudioTools/Texture2DConverter.cs index cd6d5276..f94c90cd 100644 --- a/AssetStudio/StudioClasses/Texture2DConverter.cs +++ b/AssetStudioTools/Texture2DConverter.cs @@ -1,11 +1,9 @@ using System; -using System.Collections.Generic; using System.Drawing; using System.Drawing.Imaging; using System.IO; using System.Linq; using System.Runtime.InteropServices; -using System.Text; namespace AssetStudio { @@ -16,7 +14,7 @@ public class Texture2DConverter private int m_Height; private TextureFormat m_TextureFormat; private int image_data_size; - public byte[] image_data; + private byte[] image_data; private int[] version; //DDS Start @@ -967,9 +965,9 @@ private void DecompressCRN() IntPtr uncompressedData; int uncompressedSize; bool result; - if (version[0] > 2017 || (version[0] == 2017 && version[1] >= 3) + if (version[0] > 2017 || (version[0] == 2017 && version[1] >= 3) //2017.3 and up || m_TextureFormat == TextureFormat.ETC_RGB4Crunched - || m_TextureFormat == TextureFormat.ETC2_RGBA8Crunched) //2017.3 and up + || m_TextureFormat == TextureFormat.ETC2_RGBA8Crunched) { result = DecompressUnityCRN(image_data, image_data_size, out uncompressedData, out uncompressedSize); } diff --git a/AssetStudioUtility/AssetStudioUtility.csproj b/AssetStudioUtility/AssetStudioUtility.csproj deleted file mode 100644 index d86f559f..00000000 --- a/AssetStudioUtility/AssetStudioUtility.csproj +++ /dev/null @@ -1,49 +0,0 @@ - - - - - Debug - AnyCPU - {9131C403-7FE8-444D-9AF5-5FE5DF76FF24} - Library - Properties - AssetStudioUtility - AssetStudioUtility - v4.0 - 512 - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - ..\AssetStudio\Libraries\SharpDX.Mathematics.dll - - - - - - - - - - - - - - - \ No newline at end of file