From 36699541faed96b7a4cfd5d401443205c27ed6e2 Mon Sep 17 00:00:00 2001 From: VaDiM Date: Sat, 28 Mar 2026 09:29:37 +0300 Subject: [PATCH] Improve support for legacy Unity versions (WIP). Close #103 --- AssetStudio/AssetsManager.cs | 46 +++- AssetStudio/Classes/Animation.cs | 12 +- AssetStudio/Classes/AnimationClip.cs | 50 +++- AssetStudio/Classes/AudioClip.cs | 84 +++--- AssetStudio/Classes/Behaviour.cs | 3 +- AssetStudio/Classes/BuildSettings.cs | 14 +- AssetStudio/Classes/EditorExtension.cs | 9 +- AssetStudio/Classes/GameObject.cs | 15 +- AssetStudio/Classes/Material.cs | 25 +- AssetStudio/Classes/Mesh.cs | 260 +++++++++++++----- AssetStudio/Classes/MonoBehaviour.cs | 3 +- AssetStudio/Classes/MonoScript.cs | 13 +- AssetStudio/Classes/MovieTexture.cs | 12 +- AssetStudio/Classes/NamedObject.cs | 5 +- AssetStudio/Classes/Renderer.cs | 40 ++- AssetStudio/Classes/Shader.cs | 40 ++- AssetStudio/Classes/SkinnedMeshRenderer.cs | 68 ++++- AssetStudio/Classes/Texture2D.cs | 5 +- AssetStudio/Classes/TextureFormat.cs | 1 + AssetStudio/Classes/Transform.cs | 2 +- AssetStudio/Classes/VideoClip.cs | 19 +- ...ons.cs => EndianBinaryReaderExtensions.cs} | 73 ++--- AssetStudio/ObjectReader.cs | 62 +++++ AssetStudio/ResourceReader.cs | 7 +- AssetStudio/SerializedFile.cs | 57 +++- AssetStudio/TypeTreeHelper.cs | 4 +- AssetStudioCLI/Exporter.cs | 25 +- AssetStudioCLI/Studio.cs | 17 +- AssetStudioFBXWrapper/FbxExporterContext.cs | 6 +- AssetStudioGUI/AssetStudioGUIForm.Designer.cs | 2 +- AssetStudioGUI/AssetStudioGUIForm.cs | 91 +++--- AssetStudioGUI/Exporter.cs | 21 +- AssetStudioGUI/ImportOptionsForm.Designer.cs | 121 +++----- AssetStudioGUI/ImportOptionsForm.cs | 90 +++++- AssetStudioGUI/Studio.cs | 3 +- AssetStudioUtility/ModelConverter.cs | 8 +- AssetStudioUtility/ShaderConverter.cs | 14 +- AssetStudioUtility/SpriteConverter.cs | 4 +- AssetStudioUtility/Texture2DConverter.cs | 51 +++- 39 files changed, 959 insertions(+), 423 deletions(-) rename AssetStudio/Extensions/{BinaryReaderExtensions.cs => EndianBinaryReaderExtensions.cs} (66%) diff --git a/AssetStudio/AssetsManager.cs b/AssetStudio/AssetsManager.cs index 046d4aa..e7c7f92 100644 --- a/AssetStudio/AssetsManager.cs +++ b/AssetStudio/AssetsManager.cs @@ -18,6 +18,7 @@ namespace AssetStudio { public bool LoadViaTypeTree = true; public bool MeshLazyLoad = true; + public UnityVersion AutoLegacyVersion = null; public ImportOptions Options = new ImportOptions(); public readonly List> OptionLoaders = new List>(); public readonly List AssetsFileList = new List(); @@ -622,6 +623,18 @@ namespace AssetStudio private void ReadAssets() { + var serializedVer = AssetsFileList[0].header.m_Version; + if (serializedVer <= SerializedFileFormatVersion.Unknown_6 && serializedVer >= SerializedFileFormatVersion.Unknown_5 && AutoLegacyVersion == null && Options.CustomUnityVersion == null) + { + var versions = serializedVer == SerializedFileFormatVersion.Unknown_6 + ? "'2.1.0f1', '2.5.0f1', '2.6.0f1'" + : "'1.2.0f1', '1.6.0f1', '2.0.0f1"; + Logger.Warning( + "\nUnable to detect legacy Unity version automatically\n" + + "If the assets load with errors try to specify one of the following Unity versions\n" + + $"Possible versions: {versions}"); + } + Logger.Info("Read assets..."); var jsonOptions = new JsonSerializerOptions @@ -637,6 +650,9 @@ namespace AssetStudio Progress.Reset(); foreach (var assetsFile in AssetsFileList) { + if (AutoLegacyVersion != null && Options.CustomUnityVersion == null) + assetsFile.version = AutoLegacyVersion; + JsonConverterHelper.AssetsFile = assetsFile; foreach (var objectInfo in assetsFile.m_Objects) { @@ -648,20 +664,21 @@ namespace AssetStudio try { Object obj = null; + var loadViaTypeTree = objectReader.serializedType?.m_Type != null && LoadViaTypeTree; switch (objectReader.type) { case ClassIDType.Animation: obj = new Animation(objectReader); break; - case ClassIDType.AnimationClip: - obj = objectReader.serializedType?.m_Type != null && LoadViaTypeTree + case ClassIDType.AnimationClip when objectReader.version >= (1, 5): + obj = loadViaTypeTree ? new AnimationClip(objectReader, TypeTreeHelper.ReadTypeByteArray(objectReader.serializedType.m_Type, objectReader), jsonOptions, objectInfo) : new AnimationClip(objectReader); break; case ClassIDType.Animator: obj = new Animator(objectReader); break; - case ClassIDType.AnimatorController: + case ClassIDType.AnimatorController when objectReader.version >= (1, 5): obj = new AnimatorController(objectReader); break; case ClassIDType.AnimatorOverrideController: @@ -679,14 +696,14 @@ namespace AssetStudio case ClassIDType.BuildSettings: obj = new BuildSettings(objectReader); break; - case ClassIDType.Font: + case ClassIDType.Font when objectReader.version >= 3: obj = new Font(objectReader); break; case ClassIDType.GameObject: obj = new GameObject(objectReader); break; case ClassIDType.Material: - obj = objectReader.serializedType?.m_Type != null && LoadViaTypeTree + obj = loadViaTypeTree ? new Material(objectReader, TypeTreeHelper.ReadTypeByteArray(objectReader.serializedType.m_Type, objectReader), jsonOptions) : new Material(objectReader); break; @@ -705,24 +722,25 @@ namespace AssetStudio case ClassIDType.MonoScript: obj = new MonoScript(objectReader); break; - case ClassIDType.MovieTexture: + case ClassIDType.MovieTexture when objectReader.version >= 2: obj = new MovieTexture(objectReader); break; case ClassIDType.PlayerSettings: obj = new PlayerSettings(objectReader); break; - case ClassIDType.PreloadData: + case ClassIDType.PreloadData when objectReader.version >= (2, 5): obj = new PreloadData(objectReader); break; case ClassIDType.RectTransform: obj = new RectTransform(objectReader); break; - case ClassIDType.Shader: - if (objectReader.version < 2021) - obj = new Shader(objectReader); + case ClassIDType.Shader when objectReader.version < 2021: + obj = new Shader(objectReader); break; case ClassIDType.SkinnedMeshRenderer: - obj = new SkinnedMeshRenderer(objectReader); + obj = objectReader.version < (2, 1) + ? new SkinnedMeshFilter(objectReader).ToSkinnedMeshRenderer() + : new SkinnedMeshRenderer(objectReader); break; case ClassIDType.Sprite: obj = new Sprite(objectReader); @@ -734,12 +752,12 @@ namespace AssetStudio obj = new TextAsset(objectReader); break; case ClassIDType.Texture2D: - obj = objectReader.serializedType?.m_Type != null && LoadViaTypeTree + obj = loadViaTypeTree ? new Texture2D(objectReader, TypeTreeHelper.ReadTypeByteArray(objectReader.serializedType.m_Type, objectReader), jsonOptions) : new Texture2D(objectReader); break; case ClassIDType.Texture2DArray: - obj = objectReader.serializedType?.m_Type != null && LoadViaTypeTree + obj = loadViaTypeTree ? new Texture2DArray(objectReader, TypeTreeHelper.ReadTypeByteArray(objectReader.serializedType.m_Type, objectReader), jsonOptions) : new Texture2DArray(objectReader); break; @@ -776,6 +794,8 @@ namespace AssetStudio Progress.Report(++i, progressCount); } } + + AutoLegacyVersion = null; } private void ProcessAssets() diff --git a/AssetStudio/Classes/Animation.cs b/AssetStudio/Classes/Animation.cs index 17b0b42..aba5a55 100644 --- a/AssetStudio/Classes/Animation.cs +++ b/AssetStudio/Classes/Animation.cs @@ -2,17 +2,27 @@ namespace AssetStudio { - public sealed class Animation : Behaviour + public sealed class Animation : Component { public List> m_Animations; public Animation(ObjectReader reader) : base(reader) { + if (version >= (1, 5)) //Behaviour + { + var m_Enabled = reader.ReadByte(); + if (reader.version >= (2, 1)) + reader.AlignStream(); + } var m_Animation = new PPtr(reader); int numAnimations = reader.ReadInt32(); m_Animations = new List>(); for (var i = 0; i < numAnimations; i++) { + if (version < (1, 5)) + { + var name = reader.ReadAlignedString(); + } m_Animations.Add(new PPtr(reader)); } } diff --git a/AssetStudio/Classes/AnimationClip.cs b/AssetStudio/Classes/AnimationClip.cs index b1998e6..080ae2f 100644 --- a/AssetStudio/Classes/AnimationClip.cs +++ b/AssetStudio/Classes/AnimationClip.cs @@ -316,7 +316,10 @@ namespace AssetStudio attribute = reader.ReadAlignedString(); path = reader.ReadAlignedString(); classID = (ClassIDType)reader.ReadInt32(); - script = new PPtr(reader); + if (reader.version >= 2) + { + script = new PPtr(reader); + } if (reader.version >= (2022, 2)) //2022.2 and up { flags = reader.ReadInt32(); @@ -528,7 +531,7 @@ namespace AssetStudio public StreamedCurveKey() { } - public StreamedCurveKey(BinaryReader reader) + public StreamedCurveKey(EndianBinaryReader reader) { index = reader.ReadInt32(); coeff = reader.ReadSingleArray(4); @@ -561,7 +564,7 @@ namespace AssetStudio public StreamedFrame() { } - public StreamedFrame(BinaryReader reader) + public StreamedFrame(EndianBinaryReader reader) { time = reader.ReadSingle(); @@ -579,7 +582,7 @@ namespace AssetStudio var frameList = new List(); var buffer = new byte[data.Length * 4]; Buffer.BlockCopy(data, 0, buffer, 0, buffer.Length); - using (var reader = new BinaryReader(new MemoryStream(buffer))) + using (var reader = new EndianBinaryReader(new MemoryStream(buffer), EndianType.LittleEndian)) { while (reader.BaseStream.Position < reader.BaseStream.Length) { @@ -1146,6 +1149,22 @@ namespace AssetStudio public AnimationClip(ObjectReader reader) : base(reader) { + if (version < 2) //2.0 down + { + var numClassIDToTrack = reader.ReadInt32(); + for (var i = 0; i < numClassIDToTrack; ++i) + { + var first = reader.ReadInt32(); + var second = new PPtr(reader); + } + var numChildTracks = reader.ReadInt32(); + for (var i = 0; i < numChildTracks; ++i) + { + var path = reader.ReadAlignedString(); + var classId = reader.ReadInt32(); + var track = new PPtr(reader); + } + } if (version >= 5)//5.0 and up { m_Legacy = reader.ReadBoolean(); @@ -1159,15 +1178,17 @@ namespace AssetStudio { m_Legacy = true; } + if (version >= (2, 6)) //2.6 and up { m_Compressed = reader.ReadBoolean(); + if (version >= (4, 3)) //4.3 and up + { + m_UseHighQualityCurve = reader.ReadBoolean(); + } + reader.AlignStream(); } - if (version >= (4, 3))//4.3 and up - { - m_UseHighQualityCurve = reader.ReadBoolean(); - } - reader.AlignStream(); + int numRCurves = reader.ReadInt32(); m_RotationCurves = new List(); for (var i = 0; i < numRCurves; i++) @@ -1265,11 +1286,14 @@ namespace AssetStudio var m_HasMotionFloatCurves = reader.ReadBoolean(); reader.AlignStream(); } - int numEvents = reader.ReadInt32(); - m_Events = new List(); - for (var i = 0; i < numEvents; i++) + if (version >= (2, 1)) //2.1 and up { - m_Events.Add(new AnimationEvent(reader)); + int numEvents = reader.ReadInt32(); + m_Events = new List(); + for (var i = 0; i < numEvents; i++) + { + m_Events.Add(new AnimationEvent(reader)); + } } if (version >= 2017) //2017 and up { diff --git a/AssetStudio/Classes/AudioClip.cs b/AssetStudio/Classes/AudioClip.cs index df9549c..4844c0d 100644 --- a/AssetStudio/Classes/AudioClip.cs +++ b/AssetStudio/Classes/AudioClip.cs @@ -7,7 +7,7 @@ public bool m_3D; public bool m_UseHardware; - //version 5 + //version 5+ public int m_LoadType; public int m_Channels; public int m_Frequency; @@ -27,36 +27,55 @@ public AudioClip(ObjectReader reader) : base(reader) { - if (version < 5) + if (version < 5) //5.0 down { + if (version == (2, 0)) //2.0 + { + var m_DecompressOnLoad = reader.ReadBoolean(); + } m_Format = reader.ReadInt32(); - if (version >= (2, 6)) //2.6 to 5 + + if (version < (2, 6)) //1.x - 2.6 down + { + m_Length = reader.ReadSingle(); + m_Frequency = reader.ReadInt32(); + m_Size = reader.ReadInt32(); + if (version >= (2, 1)) //2.1 - 2.6 down + { + var m_DecompressOnLoad = reader.ReadBoolean(); + reader.AlignStream(); + } + var typelessDataSize = reader.ReadInt32(); + m_Size = typelessDataSize > 0 + ? typelessDataSize + : reader.byteSize - (reader.Position - reader.byteStart); //todo? + + if ((LegacyAudioType)m_Format != LegacyAudioType.CompressedAudio) + { + m_Channels = (m_Format + 1) / 2; + } + } + else //2.6 to 5 { m_Type = (FMODSoundType)reader.ReadInt32(); m_3D = reader.ReadBoolean(); m_UseHardware = reader.ReadBoolean(); reader.AlignStream(); - } - else - { - m_Length = reader.ReadSingle(); - m_Frequency = reader.ReadInt32(); - m_Channels = m_Format != 0x05 ? m_Format >> 1 : 0; - } - if (version >= (3, 2)) //3.2.0 to 5 - { - int m_Stream = reader.ReadInt32(); - m_Size = reader.ReadInt32(); - var tsize = m_Size % 4 != 0 ? m_Size + 4 - m_Size % 4 : m_Size; - if (reader.byteSize + reader.byteStart - reader.Position != tsize) + if (version >= (3, 2)) //3.2.0 to 5 { - m_Offset = reader.ReadUInt32(); - m_Source = assetsFile.fullName + ".resS"; + int m_Stream = reader.ReadInt32(); + m_Size = reader.ReadInt32(); + var tsize = m_Size % 4 != 0 ? m_Size + 4 - m_Size % 4 : m_Size; + if (reader.byteSize + reader.byteStart - reader.Position != tsize) + { + m_Offset = reader.ReadUInt32(); + m_Source = assetsFile.fullName + ".resS"; + } + } + else + { + m_Size = reader.ReadInt32(); } - } - else - { - m_Size = reader.ReadInt32(); } } else @@ -81,19 +100,20 @@ m_CompressionFormat = (AudioCompressionFormat)reader.ReadInt32(); } - ResourceReader resourceReader; - if (!string.IsNullOrEmpty(m_Source)) - { - resourceReader = new ResourceReader(m_Source, assetsFile, m_Offset, m_Size); - } - else - { - resourceReader = new ResourceReader(reader, reader.BaseStream.Position, m_Size); - } - m_AudioData = resourceReader; + m_AudioData = !string.IsNullOrEmpty(m_Source) + ? new ResourceReader(m_Source, assetsFile, m_Offset, m_Size) + : new ResourceReader(reader, reader.BaseStream.Position, m_Size); } } + public enum LegacyAudioType + { + RawWavU8Mono = 1, + RawWavS16Mono = 2, + RawWavS16Stereo = 4, + CompressedAudio = 5, + } + public enum FMODSoundType { UNKNOWN = 0, diff --git a/AssetStudio/Classes/Behaviour.cs b/AssetStudio/Classes/Behaviour.cs index b619d28..0dd4104 100644 --- a/AssetStudio/Classes/Behaviour.cs +++ b/AssetStudio/Classes/Behaviour.cs @@ -9,7 +9,8 @@ protected Behaviour(ObjectReader reader) : base(reader) { m_Enabled = reader.ReadByte(); - reader.AlignStream(); + if (reader.version >= (2, 1)) + reader.AlignStream(); } } } diff --git a/AssetStudio/Classes/BuildSettings.cs b/AssetStudio/Classes/BuildSettings.cs index 63fcad9..4eade17 100644 --- a/AssetStudio/Classes/BuildSettings.cs +++ b/AssetStudio/Classes/BuildSettings.cs @@ -1,9 +1,21 @@ -namespace AssetStudio +using System; +using System.Text.Json; + +namespace AssetStudio { public sealed class BuildSettings : Object { public string[] levels; public string[] scenes; + public string m_Version; + + public BuildSettings() { } + + public BuildSettings(ObjectReader reader, ReadOnlySpan type, JsonSerializerOptions jsonOptions) : base(reader) + { + var parsedSettings = JsonSerializer.Deserialize(type, jsonOptions); + m_Version = parsedSettings.m_Version; + } public BuildSettings(ObjectReader reader) : base(reader) { diff --git a/AssetStudio/Classes/EditorExtension.cs b/AssetStudio/Classes/EditorExtension.cs index 3b5ea1c..d137e9b 100644 --- a/AssetStudio/Classes/EditorExtension.cs +++ b/AssetStudio/Classes/EditorExtension.cs @@ -9,7 +9,14 @@ if (platform == BuildTarget.NoTarget) { var m_PrefabParentObject = new PPtr(reader); - var m_PrefabInternal = new PPtr(reader); //PPtr + if (version >= (3, 5)) + { + var m_PrefabInternal = new PPtr(reader); //PPtr + } + if (version >= (2018, 3)) + { + var m_PrefabAsset = new PPtr(reader); + } } } } diff --git a/AssetStudio/Classes/GameObject.cs b/AssetStudio/Classes/GameObject.cs index e6d88a2..ad81a73 100644 --- a/AssetStudio/Classes/GameObject.cs +++ b/AssetStudio/Classes/GameObject.cs @@ -31,13 +31,24 @@ namespace AssetStudio m_Components.Add(new PPtr(reader)); } - var m_Layer = reader.ReadInt32(); + if (version < (2, 1)) //2.1 down + { + var m_IsActive = reader.ReadByte(); + var m_Layer = version < (1, 5) ? reader.ReadUInt16() : reader.ReadUInt32(); + var m_Tag = reader.ReadUInt16(); + } + else + { + var m_Layer = reader.ReadInt32(); + } + if (version.IsTuanjie && version >= (2022, 3, 2, 11)) //2022.3.2t11(1.1.3) and up { var m_HasEditorInfo = reader.ReadBoolean(); reader.AlignStream(); } - m_Name = reader.ReadAlignedString(); + + m_Name = version >= (1, 5) ? reader.ReadAlignedString() : $"GameObject {reader.m_PathID}"; } } } diff --git a/AssetStudio/Classes/Material.cs b/AssetStudio/Classes/Material.cs index 72c5a65..ac677ac 100644 --- a/AssetStudio/Classes/Material.cs +++ b/AssetStudio/Classes/Material.cs @@ -14,8 +14,29 @@ namespace AssetStudio public UnityTexEnv(ObjectReader reader) { m_Texture = new PPtr(reader); - m_Scale = reader.ReadVector2(); - m_Offset = reader.ReadVector2(); + if (reader.version < (2, 1)) //2.1 down + { + if (reader.version < (1, 5)) //1.5 down + { + m_Offset = reader.ReadVector3(); + m_Scale = reader.ReadVector3(); + var matRemap0 = reader.ReadInt32(); + var matRemap1 = reader.ReadInt32(); + var matRemap2 = reader.ReadInt32(); + var matRotate = reader.ReadSingle(); + var texGen = reader.ReadInt32(); + } + else + { + m_Scale = reader.ReadVector3(); + m_Offset = reader.ReadVector3(); + } + } + else + { + m_Scale = reader.ReadVector2(); + m_Offset = reader.ReadVector2(); + } } } diff --git a/AssetStudio/Classes/Mesh.cs b/AssetStudio/Classes/Mesh.cs index 8ba6da6..16ff8a7 100644 --- a/AssetStudio/Classes/Mesh.cs +++ b/AssetStudio/Classes/Mesh.cs @@ -7,12 +7,85 @@ using System.Runtime.InteropServices; namespace AssetStudio { + public class MeshData + { + public readonly List IndexBufferList; + public readonly bool IsTriangleStrip; + + public MeshData(ObjectReader reader) + { + IndexBufferList = new List(); + + var facesCount = reader.ReadInt32(); + for (var i = 0; i < facesCount; i++) + { + for (var j = 0; j < 3; j++) //Face data (Vector3) + { + IndexBufferList.Add(reader.ReadUInt16()); + } + } + + var stripsCount = reader.ReadInt32(); + for (var i = 0; i < stripsCount; i++) + { + IndexBufferList.Add(reader.ReadUInt16()); + } + + IsTriangleStrip = stripsCount > 0; + } + } + + public class LOD + { + private readonly List m_MeshData; + private readonly int m_VertexCount; + public readonly List IndexBufferList; + + public LOD(ObjectReader reader) + { + m_MeshData = new List(); + IndexBufferList = new List(); + var meshDataCount = reader.ReadInt32(); + for (var i = 0; i < meshDataCount; i++) + { + var meshData = new MeshData(reader); + m_MeshData.Add(meshData); + IndexBufferList.AddRange(meshData.IndexBufferList); + } + + if (reader.version < (1, 5)) + { + m_VertexCount = reader.ReadInt32(); + var m_NewVertexStart = reader.ReadInt32(); + var m_MeshError = reader.ReadSingle(); + var m_MorphToVertexCount = reader.ReadInt32(); + reader.Position += m_MorphToVertexCount * 2; //skip UInt16[] m_MorphToVertex + } + } + + public List ConvertToSubMeshes() + { + var subMeshList = new List(m_MeshData.Count); + foreach (var meshData in m_MeshData) + { + subMeshList.Add(new SubMesh + { + firstByte = 0, //todo? + indexCount = (uint)meshData.IndexBufferList.Count, + topology = meshData.IsTriangleStrip ? GfxPrimitiveType.TriangleStrip : GfxPrimitiveType.Triangles, + vertexCount = (uint)m_VertexCount, + }); + } + return subMeshList; + } + } + public class MinMaxAABB { public Vector3 m_Min; public Vector3 m_Max; - public MinMaxAABB(BinaryReader reader) + public MinMaxAABB(ObjectReader reader) { m_Min = reader.ReadVector3(); m_Max = reader.ReadVector3(); @@ -415,6 +488,8 @@ namespace AssetStudio public uint vertexCount; public AABB localAABB; + public SubMesh() { } + public SubMesh(ObjectReader reader) { var version = reader.version; @@ -451,7 +526,7 @@ namespace AssetStudio public uint[] ChildStartIndex = new uint[8]; public uint[] PageIndex_PageCount_PageBlockCount = new uint[8]; - public VGPackedHierarchyNode(BinaryReader reader) + public VGPackedHierarchyNode(ObjectReader reader) { for (var i = 0; i < 8; i++) { @@ -572,39 +647,65 @@ namespace AssetStudio public Mesh(ObjectReader reader) : base(reader) { - if (version < (3, 5)) //3.5 down + if (version < 2) //2.0 down { - m_Use16BitIndices = reader.ReadInt32() > 0; - } - - if (version <= (2, 5)) //2.5 and down - { - int m_IndexBufferSize = reader.ReadInt32(); - - if (m_Use16BitIndices) + LOD lod0 = null; + var m_LODDataCount = reader.ReadInt32(); + for (var i = 0; i < m_LODDataCount; i++) { - var indexBufferList = new List(); - for (var i = 0; i < m_IndexBufferSize / 2; i++) + if (i == 0) + lod0 = new LOD(reader); + else + _ = new LOD(reader); + } + if (lod0 == null) + { + Logger.Warning("Can't find lod0 mesh data"); + return; + } + + m_IndexBuffer = lod0.IndexBufferList.ToArray(); + m_SubMeshes = lod0.ConvertToSubMeshes(); + } + else //2.0 and up + { + if (version < (3, 5)) //2.0 - 3.5 down + { + m_Use16BitIndices = reader.ReadInt32() > 0; + } + + if (version <= (2, 5)) //2.0 - 2.5 down + { + int m_IndexBufferSize = reader.ReadInt32(); + + if (m_Use16BitIndices) { - indexBufferList.Add(reader.ReadUInt16()); + var indexBufferList = new List(); + for (var i = 0; i < m_IndexBufferSize / 2; i++) + { + indexBufferList.Add(reader.ReadUInt16()); + } + if (version >= (2, 1)) + { + reader.AlignStream(); + } + m_IndexBuffer = indexBufferList.ToArray(); + } + else + { + m_IndexBuffer = reader.ReadUInt32Array(m_IndexBufferSize / 4); } - - reader.AlignStream(); - m_IndexBuffer = indexBufferList.ToArray(); } - else + + int m_SubMeshesSize = reader.ReadInt32(); + reader.ThrowIfTooLarge(m_SubMeshesSize * 12f); + m_SubMeshes = new List(); + for (var i = 0; i < m_SubMeshesSize; i++) { - m_IndexBuffer = reader.ReadUInt32Array(m_IndexBufferSize / 4); + m_SubMeshes.Add(new SubMesh(reader)); } } - int m_SubMeshesSize = reader.ReadInt32(); - m_SubMeshes = new List(); - for (var i = 0; i < m_SubMeshesSize; i++) - { - m_SubMeshes.Add(new SubMesh(reader)); - } - if (version >= (4, 1)) //4.1 and up { m_Shapes = new BlendShapeData(reader); @@ -693,21 +794,30 @@ namespace AssetStudio m_VertexCount = reader.ReadInt32(); m_Vertices = reader.ReadSingleArray(m_VertexCount * 3); //Vector3 - var numSkin = reader.ReadInt32(); - reader.ThrowIfTooLarge(numSkin * 32f); - m_Skin = new BoneWeights4[numSkin]; - for (var s = 0; s < numSkin; s++) + if (version >= (1, 5)) //1.5 - 3.5 down { - m_Skin[s] = new BoneWeights4(reader); - } + var numSkin = reader.ReadInt32(); + reader.ThrowIfTooLarge(numSkin * 32f); + m_Skin = new BoneWeights4[numSkin]; + for (var s = 0; s < numSkin; s++) + { + m_Skin[s] = new BoneWeights4(reader); + } - m_BindPose = reader.ReadMatrixArray(); + if (version >= (2, 1)) //2.1 - 3.5 down + { + m_BindPose = reader.ReadMatrixArray(); + } + } m_UV0 = reader.ReadSingleArray(reader.ReadInt32() * 2); //Vector2 - m_UV1 = reader.ReadSingleArray(reader.ReadInt32() * 2); //Vector2 + if (version >= (1, 5)) //1.5 - 3.5 down + { + m_UV1 = reader.ReadSingleArray(reader.ReadInt32() * 2); //Vector2 + } - if (version <= (2, 5)) //2.5 and down + if (version < (2, 6)) //2.6 down { int m_TangentSpaceSize = reader.ReadInt32(); reader.ThrowIfTooLarge(m_TangentSpaceSize * 28f); @@ -727,7 +837,6 @@ namespace AssetStudio else //2.6.0 and later { m_Tangents = reader.ReadSingleArray(reader.ReadInt32() * 4); //Vector4 - m_Normals = reader.ReadSingleArray(reader.ReadInt32() * 3); //Vector3 } } @@ -769,12 +878,18 @@ namespace AssetStudio } m_Colors = m_ColorsList.ToArray(); - int m_CollisionTrianglesSize = reader.ReadInt32(); - reader.Position += m_CollisionTrianglesSize * 4; //UInt32 indices - int m_CollisionVertexCount = reader.ReadInt32(); + if (version >= (1, 5)) //1.5 - 3.5 down + { + int m_CollisionTrianglesSize = reader.ReadInt32(); + reader.Position += m_CollisionTrianglesSize * 4; //UInt32 indices + int m_CollisionVertexCount = reader.ReadInt32(); + } } - int m_MeshUsageFlags = reader.ReadInt32(); + if (version >= (2, 5)) //2.5 and up + { + int m_MeshUsageFlags = reader.ReadInt32(); + } if (version >= (2022, 1)) //2022.1 and up { @@ -823,41 +938,48 @@ namespace AssetStudio return; var isStreamedDataSize = false; - if (!string.IsNullOrEmpty(m_StreamData?.path)) + try { - if (m_VertexData.m_VertexCount > 0) + if (!string.IsNullOrEmpty(m_StreamData?.path)) { - m_VertexData.m_DataSize = BigArrayPool.Shared.Rent((int)m_StreamData.size); - var resourceReader = new ResourceReader(m_StreamData.path, assetsFile, m_StreamData.offset, m_StreamData.size); - resourceReader.GetData(m_VertexData.m_DataSize); - isStreamedDataSize = true; + if (m_VertexData.m_VertexCount > 0) + { + m_VertexData.m_DataSize = BigArrayPool.Shared.Rent((int) m_StreamData.size); + var resourceReader = new ResourceReader(m_StreamData.path, assetsFile, m_StreamData.offset, m_StreamData.size); + resourceReader.GetData(m_VertexData.m_DataSize); + isStreamedDataSize = true; + } } - } - if (version >= (3, 5)) //3.5 and up - { - ReadVertexData(); - } - if (version >= (2, 6)) //2.6.0 and later - { - DecompressCompressedMesh(); - } + if (version >= (3, 5)) //3.5 and up + { + ReadVertexData(); + } - if (m_IndexBuffer.Length == 0) - { - var msg = m_HasVirtualGeometryMesh - ? "Unsupported mesh type: Virtual Geometry" - : "Cannot process empty mesh"; - Logger.Warning($"{msg} | PathID: {m_PathID} | Name: \"{m_Name}\""); - } - else - { - GetTriangles(); - } + if (version >= (2, 6)) //2.6.0 and later + { + DecompressCompressedMesh(); + } - isLoaded = true; - if (isStreamedDataSize) - BigArrayPool.Shared.Return(m_VertexData.m_DataSize, clearArray: true); + if (m_IndexBuffer?.Length == 0) + { + var msg = m_HasVirtualGeometryMesh + ? "Unsupported mesh type: Virtual Geometry" + : "Cannot process empty mesh"; + Logger.Warning($"{msg} | PathID: {m_PathID} | Name: \"{m_Name}\""); + } + else + { + GetTriangles(); + } + + isLoaded = true; + } + finally + { + if (isStreamedDataSize) + BigArrayPool.Shared.Return(m_VertexData.m_DataSize, clearArray: true); + } } private void ReadVertexData() diff --git a/AssetStudio/Classes/MonoBehaviour.cs b/AssetStudio/Classes/MonoBehaviour.cs index 2478fe2..29b3e2f 100644 --- a/AssetStudio/Classes/MonoBehaviour.cs +++ b/AssetStudio/Classes/MonoBehaviour.cs @@ -10,7 +10,8 @@ public MonoBehaviour(ObjectReader reader) : base(reader) { m_Script = new PPtr(reader); - m_Name = reader.ReadAlignedString(); + if (version >= (2, 1)) + m_Name = reader.ReadAlignedString(); } } } diff --git a/AssetStudio/Classes/MonoScript.cs b/AssetStudio/Classes/MonoScript.cs index 12cdd17..5f15f9e 100644 --- a/AssetStudio/Classes/MonoScript.cs +++ b/AssetStudio/Classes/MonoScript.cs @@ -26,15 +26,22 @@ { var m_PathName = reader.ReadAlignedString(); } + m_ClassName = reader.ReadAlignedString(); + if (version >= 3) //3.0 and up { m_Namespace = reader.ReadAlignedString(); } - m_AssemblyName = reader.ReadAlignedString(); - if (version < (2018, 2)) //2018.2 down + + if (version >= (1, 5)) //1.5 and up { - var m_IsEditorScript = reader.ReadBoolean(); + m_AssemblyName = reader.ReadAlignedString(); + + if (version < (2018, 2)) //2018.2 down + { + var m_IsEditorScript = reader.ReadBoolean(); + } } } } diff --git a/AssetStudio/Classes/MovieTexture.cs b/AssetStudio/Classes/MovieTexture.cs index b65d889..80ec48b 100644 --- a/AssetStudio/Classes/MovieTexture.cs +++ b/AssetStudio/Classes/MovieTexture.cs @@ -7,13 +7,21 @@ public MovieTexture(ObjectReader reader) : base(reader) { - if (reader.version < (2019, 3)) //2019.3 down + if (reader.version >= (2019, 3)) //2019.3 and up + return; + + var m_Loop = reader.ReadBoolean(); + if (version >= (2, 1)) { - var m_Loop = reader.ReadBoolean(); reader.AlignStream(); m_AudioClip = new PPtr(reader); m_MovieData = reader.ReadUInt8Array(); } + else + { + m_MovieData = reader.ReadUInt8Array(); + m_AudioClip = new PPtr(reader); + } } } } diff --git a/AssetStudio/Classes/NamedObject.cs b/AssetStudio/Classes/NamedObject.cs index 6a56c99..058e125 100644 --- a/AssetStudio/Classes/NamedObject.cs +++ b/AssetStudio/Classes/NamedObject.cs @@ -8,7 +8,10 @@ protected NamedObject(ObjectReader reader) : base(reader) { - m_Name = reader.ReadAlignedString(); + if (version >= (1, 5)) + { + m_Name = reader.ReadAlignedString(); + } } } } diff --git a/AssetStudio/Classes/Renderer.cs b/AssetStudio/Classes/Renderer.cs index d4ad0eb..ab1d665 100644 --- a/AssetStudio/Classes/Renderer.cs +++ b/AssetStudio/Classes/Renderer.cs @@ -20,14 +20,25 @@ namespace AssetStudio public StaticBatchInfo m_StaticBatchInfo; public uint[] m_SubsetIndices; + protected Renderer() { } + protected Renderer(ObjectReader reader) : base(reader) { if (version < 5) //5.0 down { - var m_Enabled = reader.ReadBoolean(); - var m_CastShadows = reader.ReadBoolean(); - var m_ReceiveShadows = reader.ReadBoolean(); - var m_LightmapIndex = reader.ReadByte(); + if (version >= (1, 5)) + { + var m_Enabled = reader.ReadBoolean(); + if (version >= (2, 0)) + { + var m_CastShadows = reader.ReadBoolean(); + var m_ReceiveShadows = reader.ReadBoolean(); + if (version >= (2, 1)) + { + var m_LightmapIndex = reader.ReadByte(); + } + } + } } else //5.0 and up { @@ -59,7 +70,7 @@ namespace AssetStudio { var m_virtualGeometry = reader.ReadByte(); var m_virtualGeometryShadow = reader.ReadByte(); - if (version > (2022, 3, 48) || (version == (2022, 3, 48) && version.Build >= 3)) //2022.3.48t3(1.4.0) and up + if (version >= (2022, 3, 48, 3)) //2022.3.48t3(1.4.0) and up { reader.AlignStream(); var m_ShadingRate = reader.ReadByte(); @@ -125,10 +136,15 @@ namespace AssetStudio { m_Materials.Add(new PPtr(reader)); } + if (reader.version < (2, 1)) //2.1 down + return; if (version < 3) //3.0 down { + //var unk = reader.ReadVector3(); //unity-iphone 1.7 var m_LightmapTilingOffset = reader.ReadVector4(); + if (m_LightmapTilingOffset.W > 0) //if unity-iphone 1.7 + m_LightmapTilingOffset = reader.ReadVector3(); } else //3.0 and up { @@ -164,7 +180,9 @@ namespace AssetStudio if (version >= (4, 3)) //4.3 and up { - if (version == (4, 3)) //4.3 + if (version == (4, 3) //4.3.x + || (version == (5, 6, 0) && version.IsBeta) //5.6.0bx + || (version == (5, 6, 0) && version.Build < 3)) //5.6.0f1 - 5.6.0f2 { var m_SortingLayer = reader.ReadInt16(); } @@ -173,18 +191,18 @@ namespace AssetStudio var m_SortingLayerID = reader.ReadInt32(); } - if (version > (5, 6) || (version == (5, 6) && version.Build >= 3)) //5.6.0f3 and up + if (version > (5, 6, 0) || (version >= (5, 6, 0, 3) && !version.IsBeta)) //5.6.0f3 and up { var m_SortingLayer = reader.ReadInt16(); } var m_SortingOrder = reader.ReadInt16(); reader.AlignStream(); + } - if (version >= (6000, 3)) //6000.3 and up - { - var m_MaskInteraction = reader.ReadInt32(); - } + if (version >= (6000, 3)) //6000.3 and up + { + var m_MaskInteraction = reader.ReadInt32(); } } } diff --git a/AssetStudio/Classes/Shader.cs b/AssetStudio/Classes/Shader.cs index 6157c75..45a4781 100644 --- a/AssetStudio/Classes/Shader.cs +++ b/AssetStudio/Classes/Shader.cs @@ -1,6 +1,4 @@ -using System; -using System.Collections.Generic; -using System.IO; +using System.Collections.Generic; using System.Linq; namespace AssetStudio @@ -9,7 +7,7 @@ namespace AssetStudio { public byte[] bytes; - public Hash128(BinaryReader reader) + public Hash128(ObjectReader reader) { bytes = reader.ReadBytes(16); } @@ -20,7 +18,7 @@ namespace AssetStudio public List m_MatrixParams; public List m_VectorParams; - public StructParameter(BinaryReader reader) + public StructParameter(ObjectReader reader) { var m_NameIndex = reader.ReadInt32(); var m_Index = reader.ReadInt32(); @@ -48,7 +46,7 @@ namespace AssetStudio public uint sampler; public int bindPoint; - public SamplerParameter(BinaryReader reader) + public SamplerParameter(ObjectReader reader) { sampler = reader.ReadUInt32(); bindPoint = reader.ReadInt32(); @@ -71,7 +69,7 @@ namespace AssetStudio public string m_DefaultName; public TextureDimension m_TexDim; - public SerializedTextureProperty(BinaryReader reader) + public SerializedTextureProperty(ObjectReader reader) { m_DefaultName = reader.ReadAlignedString(); m_TexDim = (TextureDimension)reader.ReadInt32(); @@ -98,7 +96,7 @@ namespace AssetStudio public float[] m_DefValue; public SerializedTextureProperty m_DefTexture; - public SerializedProperty(BinaryReader reader) + public SerializedProperty(ObjectReader reader) { m_Name = reader.ReadAlignedString(); m_Description = reader.ReadAlignedString(); @@ -114,7 +112,7 @@ namespace AssetStudio { public List m_Props; - public SerializedProperties(BinaryReader reader) + public SerializedProperties(ObjectReader reader) { int numProps = reader.ReadInt32(); m_Props = new List(); @@ -130,7 +128,7 @@ namespace AssetStudio public float val; public string name; - public SerializedShaderFloatValue(BinaryReader reader) + public SerializedShaderFloatValue(ObjectReader reader) { val = reader.ReadSingle(); name = reader.ReadAlignedString(); @@ -147,7 +145,7 @@ namespace AssetStudio public SerializedShaderFloatValue blendOpAlpha; public SerializedShaderFloatValue colMask; - public SerializedShaderRTBlendState(BinaryReader reader) + public SerializedShaderRTBlendState(ObjectReader reader) { srcBlend = new SerializedShaderFloatValue(reader); destBlend = new SerializedShaderFloatValue(reader); @@ -166,7 +164,7 @@ namespace AssetStudio public SerializedShaderFloatValue zFail; public SerializedShaderFloatValue comp; - public SerializedStencilOp(BinaryReader reader) + public SerializedStencilOp(ObjectReader reader) { pass = new SerializedShaderFloatValue(reader); fail = new SerializedShaderFloatValue(reader); @@ -183,7 +181,7 @@ namespace AssetStudio public SerializedShaderFloatValue w; public string name; - public SerializedShaderVectorValue(BinaryReader reader) + public SerializedShaderVectorValue(ObjectReader reader) { x = new SerializedShaderFloatValue(reader); y = new SerializedShaderFloatValue(reader); @@ -281,7 +279,7 @@ namespace AssetStudio public sbyte source; public sbyte target; - public ShaderBindChannel(BinaryReader reader) + public ShaderBindChannel(ObjectReader reader) { source = reader.ReadSByte(); target = reader.ReadSByte(); @@ -293,7 +291,7 @@ namespace AssetStudio public List m_Channels; public uint m_SourceMap; - public ParserBindChannels(BinaryReader reader) + public ParserBindChannels(ObjectReader reader) { int numChannels = reader.ReadInt32(); m_Channels = new List(); @@ -315,7 +313,7 @@ namespace AssetStudio public sbyte m_Type; public sbyte m_Dim; - public VectorParameter(BinaryReader reader) + public VectorParameter(ObjectReader reader) { m_NameIndex = reader.ReadInt32(); m_Index = reader.ReadInt32(); @@ -334,7 +332,7 @@ namespace AssetStudio public sbyte m_Type; public sbyte m_RowCount; - public MatrixParameter(BinaryReader reader) + public MatrixParameter(ObjectReader reader) { m_NameIndex = reader.ReadInt32(); m_Index = reader.ReadInt32(); @@ -441,7 +439,7 @@ namespace AssetStudio public int m_Index; public int m_OriginalIndex; - public UAVParameter(BinaryReader reader) + public UAVParameter(ObjectReader reader) { m_NameIndex = reader.ReadInt32(); m_Index = reader.ReadInt32(); @@ -738,7 +736,7 @@ namespace AssetStudio { public List> tags; - public SerializedTagMap(BinaryReader reader) + public SerializedTagMap(ObjectReader reader) { int numTags = reader.ReadInt32(); tags = new List>(); @@ -774,7 +772,7 @@ namespace AssetStudio public string from; public string to; - public SerializedShaderDependency(BinaryReader reader) + public SerializedShaderDependency(ObjectReader reader) { from = reader.ReadAlignedString(); to = reader.ReadAlignedString(); @@ -786,7 +784,7 @@ namespace AssetStudio public string customEditorName; public string renderPipelineType; - public SerializedCustomEditorForRenderPipeline(BinaryReader reader) + public SerializedCustomEditorForRenderPipeline(ObjectReader reader) { customEditorName = reader.ReadAlignedString(); renderPipelineType = reader.ReadAlignedString(); diff --git a/AssetStudio/Classes/SkinnedMeshRenderer.cs b/AssetStudio/Classes/SkinnedMeshRenderer.cs index 105e964..bc4426e 100644 --- a/AssetStudio/Classes/SkinnedMeshRenderer.cs +++ b/AssetStudio/Classes/SkinnedMeshRenderer.cs @@ -2,12 +2,15 @@ namespace AssetStudio { + //unity 2.1+ public sealed class SkinnedMeshRenderer : Renderer { public PPtr m_Mesh; public List> m_Bones; public float[] m_BlendShapeWeights; + public SkinnedMeshRenderer() { } + public SkinnedMeshRenderer(ObjectReader reader) : base(reader) { int m_Quality = reader.ReadInt32(); @@ -15,7 +18,7 @@ namespace AssetStudio var m_SkinNormals = reader.ReadBoolean(); //3.1.0 and below reader.AlignStream(); - if (version < (2, 6)) //2.6 down + if (version < (2, 6)) //2.1 - 2.6 down { var m_DisableAnimationWhenOffscreen = new PPtr(reader); } @@ -35,4 +38,67 @@ namespace AssetStudio } } } + + //unity 1.x - 2.1 down + public sealed class SkinnedMeshFilter + { + private PPtr m_Mesh; + private List> m_Bones; + private PPtr m_GameObject; + private List> m_Materials; + private readonly ObjectReader _reader; + + public SkinnedMeshFilter(ObjectReader reader) + { + _reader = reader; + reader.Reset(); + m_GameObject = new PPtr(reader); //Component + if (reader.version == (2, 0)) //Renderer + { + var m_Enabled = reader.ReadBoolean(); + var m_CastShadows = reader.ReadBoolean(); + var m_ReceiveShadows = reader.ReadBoolean(); + var m_MaterialsSize = reader.ReadInt32(); + m_Materials = new List>(); + for (var i = 0; i < m_MaterialsSize; i++) + { + m_Materials.Add(new PPtr(reader)); + } + } + if (reader.version >= (1, 5)) + { + var m_UpdateWhenOffscreen = reader.ReadBoolean(); + var m_Quality = reader.ReadInt32(); + var m_SkinNormals = reader.ReadBoolean(); + var m_Animation = new PPtr(reader); + } + var numBones = reader.ReadInt32(); + m_Bones = new List>(); + for (var b = 0; b < numBones; b++) + { + m_Bones.Add(new PPtr(reader)); + } + m_Mesh = new PPtr(reader); + } + + public SkinnedMeshRenderer ToSkinnedMeshRenderer() + { + return new SkinnedMeshRenderer() + { + reader = _reader, + assetsFile = _reader.assetsFile, + serializedType = _reader.serializedType, + version = _reader.version, + platform = _reader.platform, + type = _reader.type, + classID = _reader.classID, + m_PathID = _reader.m_PathID, + byteSize = _reader.byteSize, + m_GameObject = m_GameObject, + m_Materials = m_Materials, + m_Mesh = m_Mesh, + m_Bones = m_Bones, + }; + } + } } diff --git a/AssetStudio/Classes/Texture2D.cs b/AssetStudio/Classes/Texture2D.cs index cac7ca7..d759992 100644 --- a/AssetStudio/Classes/Texture2D.cs +++ b/AssetStudio/Classes/Texture2D.cs @@ -145,7 +145,10 @@ namespace AssetStudio { var m_StreamingMipmaps = reader.ReadBoolean(); } - reader.AlignStream(); + if (version >= (2, 1)) + { + reader.AlignStream(); + } if (version >= (2018, 2)) //2018.2 and up { var m_StreamingMipmapsPriority = reader.ReadInt32(); diff --git a/AssetStudio/Classes/TextureFormat.cs b/AssetStudio/Classes/TextureFormat.cs index abf6ffe..10eb11e 100644 --- a/AssetStudio/Classes/TextureFormat.cs +++ b/AssetStudio/Classes/TextureFormat.cs @@ -41,6 +41,7 @@ /// DXT1, /// + /// Compressed color with alpha channel texture format. /// DXT3, /// diff --git a/AssetStudio/Classes/Transform.cs b/AssetStudio/Classes/Transform.cs index 076e9af..1828468 100644 --- a/AssetStudio/Classes/Transform.cs +++ b/AssetStudio/Classes/Transform.cs @@ -14,7 +14,7 @@ namespace AssetStudio { m_LocalRotation = reader.ReadQuaternion(); m_LocalPosition = reader.ReadVector3(); - m_LocalScale = reader.ReadVector3(); + m_LocalScale = version >= (1, 5) ? reader.ReadVector3() : new Vector3(1, 1, 1); //? int m_ChildrenCount = reader.ReadInt32(); m_Children = new List>(); diff --git a/AssetStudio/Classes/VideoClip.cs b/AssetStudio/Classes/VideoClip.cs index e036fe3..002d9dd 100644 --- a/AssetStudio/Classes/VideoClip.cs +++ b/AssetStudio/Classes/VideoClip.cs @@ -1,6 +1,4 @@ -using System.IO; - -namespace AssetStudio +namespace AssetStudio { public class StreamedResource { @@ -8,7 +6,7 @@ namespace AssetStudio public long m_Offset; //ulong public long m_Size; //ulong - public StreamedResource(BinaryReader reader) + public StreamedResource(ObjectReader reader) { m_Source = reader.ReadAlignedString(); m_Offset = reader.ReadInt64(); @@ -61,16 +59,9 @@ namespace AssetStudio var m_sRGB = reader.ReadBoolean(); } - ResourceReader resourceReader; - if (!string.IsNullOrEmpty(m_ExternalResources.m_Source)) - { - resourceReader = new ResourceReader(m_ExternalResources.m_Source, assetsFile, m_ExternalResources.m_Offset, m_ExternalResources.m_Size); - } - else - { - resourceReader = new ResourceReader(reader, reader.BaseStream.Position, m_ExternalResources.m_Size); - } - m_VideoData = resourceReader; + m_VideoData = !string.IsNullOrEmpty(m_ExternalResources.m_Source) + ? new ResourceReader(m_ExternalResources.m_Source, assetsFile, m_ExternalResources.m_Offset, m_ExternalResources.m_Size) + : new ResourceReader(reader, reader.BaseStream.Position, m_ExternalResources.m_Size); } } } diff --git a/AssetStudio/Extensions/BinaryReaderExtensions.cs b/AssetStudio/Extensions/EndianBinaryReaderExtensions.cs similarity index 66% rename from AssetStudio/Extensions/BinaryReaderExtensions.cs rename to AssetStudio/Extensions/EndianBinaryReaderExtensions.cs index af79693..7016ae8 100644 --- a/AssetStudio/Extensions/BinaryReaderExtensions.cs +++ b/AssetStudio/Extensions/EndianBinaryReaderExtensions.cs @@ -6,9 +6,9 @@ using System.Text; namespace AssetStudio { - public static class BinaryReaderExtensions + public static class EndianBinaryReaderExtensions { - public static void AlignStream(this BinaryReader reader, int alignment = 4) + public static void AlignStream(this EndianBinaryReader reader, int alignment = 4) { var pos = reader.BaseStream.Position; var mod = pos % alignment; @@ -18,7 +18,7 @@ namespace AssetStudio } } - public static string ReadAlignedString(this BinaryReader reader) + public static string ReadAlignedString(this EndianBinaryReader reader, bool needAlign = true) { var length = reader.ReadInt32(); if (length > reader.BaseStream.Length - reader.BaseStream.Position) @@ -27,13 +27,14 @@ namespace AssetStudio { var stringData = reader.ReadBytes(length); var result = Encoding.UTF8.GetString(stringData); - reader.AlignStream(); + if (needAlign) + reader.AlignStream(); return result; } return ""; } - public static string ReadStringToNull(this BinaryReader reader, int maxLength = 32767, Encoding encoding = null) + public static string ReadStringToNull(this EndianBinaryReader reader, int maxLength = 32767, Encoding encoding = null) { if (encoding?.CodePage == 1200) //Unicode (UTF-16LE) return reader.ReadUnicodeStringToNull(maxLength * 2); @@ -58,7 +59,7 @@ namespace AssetStudio #endif } - private static string ReadUnicodeStringToNull(this BinaryReader reader, int maxLength) + private static string ReadUnicodeStringToNull(this EndianBinaryReader reader, int maxLength) { var bytes = new List(); var count = 0; @@ -75,7 +76,7 @@ namespace AssetStudio return Encoding.Unicode.GetString(bytes.ToArray()); } - public static string ReadBytesAsHexString(this BinaryReader reader, int length) + public static string ReadBytesAsHexString(this EndianBinaryReader reader, int length) { #if NETFRAMEWORK var bytes = reader.ReadBytes(length); @@ -87,74 +88,87 @@ namespace AssetStudio #endif } - public static Quaternion ReadQuaternion(this BinaryReader reader) + public static Quaternion ReadQuaternion(this EndianBinaryReader reader) { return new Quaternion(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle()); } - public static Vector2 ReadVector2(this BinaryReader reader) + public static Vector2 ReadVector2(this EndianBinaryReader reader) { return new Vector2(reader.ReadSingle(), reader.ReadSingle()); } - public static Vector3 ReadVector3(this BinaryReader reader) + public static Vector3 ReadVector3(this EndianBinaryReader reader) { return new Vector3(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle()); } - public static Vector4 ReadVector4(this BinaryReader reader) + public static Vector4 ReadVector4(this EndianBinaryReader reader) { return new Vector4(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle()); } - public static Color ReadColor4(this BinaryReader reader) + public static Color ReadColor4(this EndianBinaryReader reader) { return new Color(reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle()); } - private static T[] ReadArray(BinaryReader reader, int byteLen) where T : struct + private static T[] ReadArray(EndianBinaryReader reader, int byteLen) where T : struct { if (byteLen < 0) throw new ArgumentOutOfRangeException(nameof(byteLen)); if (reader.BaseStream.Position + byteLen > reader.BaseStream.Length) throw new EndOfStreamException(); - + var bytes = reader.ReadBytes(byteLen); + var isBigEndian = reader.Endian == EndianType.BigEndian; + if ((isBigEndian && BitConverter.IsLittleEndian) || (!isBigEndian && !BitConverter.IsLittleEndian)) + { + var elementSize = Marshal.SizeOf(); + if (elementSize > 1) + { + for (var i = 0; i < bytes.Length; i += elementSize) + { + bytes.AsSpan(i, elementSize).Reverse(); + } + } + } + var span = MemoryMarshal.Cast(bytes); return span.ToArray(); } - public static bool[] ReadBooleanArray(this BinaryReader reader) + public static bool[] ReadBooleanArray(this EndianBinaryReader reader) { return ReadArray(reader, reader.ReadInt32()); } - public static byte[] ReadUInt8Array(this BinaryReader reader) + public static byte[] ReadUInt8Array(this EndianBinaryReader reader) { return reader.ReadBytes(reader.ReadInt32()); } - public static ushort[] ReadUInt16Array(this BinaryReader reader) + public static ushort[] ReadUInt16Array(this EndianBinaryReader reader) { return ReadArray(reader, reader.ReadInt32() * 2); } - public static int[] ReadInt32Array(this BinaryReader reader, int length = -1) + public static int[] ReadInt32Array(this EndianBinaryReader reader, int length = -1) { if (length == -1) length = reader.ReadInt32(); return ReadArray(reader, length * 4); } - public static uint[] ReadUInt32Array(this BinaryReader reader, int length = -1) + public static uint[] ReadUInt32Array(this EndianBinaryReader reader, int length = -1) { if (length == -1) length = reader.ReadInt32(); return ReadArray(reader, length * 4); } - public static uint[][] ReadUInt32ArrayArray(this BinaryReader reader) + public static uint[][] ReadUInt32ArrayArray(this EndianBinaryReader reader) { var length = reader.ReadInt32(); var list = new List(); @@ -165,35 +179,24 @@ namespace AssetStudio return list.ToArray(); } - public static float[] ReadSingleArray(this BinaryReader reader, int length = -1) + public static float[] ReadSingleArray(this EndianBinaryReader reader, int length = -1) { if (length == -1) length = reader.ReadInt32(); return ReadArray(reader, length * 4); } - public static string[] ReadStringArray(this BinaryReader reader) - { - var length = reader.ReadInt32(); - var list = new List(); - for (var i = 0; i < length; i++) - { - list.Add(reader.ReadAlignedString()); - } - return list.ToArray(); - } - - public static Vector2[] ReadVector2Array(this BinaryReader reader) + public static Vector2[] ReadVector2Array(this EndianBinaryReader reader) { return ReadArray(reader, reader.ReadInt32() * 8); } - public static Vector4[] ReadVector4Array(this BinaryReader reader) + public static Vector4[] ReadVector4Array(this EndianBinaryReader reader) { return ReadArray(reader, reader.ReadInt32() * 16); } - public static Matrix4x4[] ReadMatrixArray(this BinaryReader reader) + public static Matrix4x4[] ReadMatrixArray(this EndianBinaryReader reader) { return ReadArray(reader, reader.ReadInt32() * 64); } diff --git a/AssetStudio/ObjectReader.cs b/AssetStudio/ObjectReader.cs index 04e5922..3fb38d6 100644 --- a/AssetStudio/ObjectReader.cs +++ b/AssetStudio/ObjectReader.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.IO; namespace AssetStudio @@ -54,5 +55,66 @@ namespace AssetStudio { Position = byteStart; } + + public override short ReadInt16() + { + if (Remaining < 2) + throw new EndOfStreamException(); + + return base.ReadInt16(); + } + + public override ushort ReadUInt16() + { + if (Remaining < 2) + throw new EndOfStreamException(); + + return base.ReadUInt16(); + } + + public override int ReadInt32() + { + if (Remaining < 4) + throw new EndOfStreamException(); + + return base.ReadInt32(); + } + + public override uint ReadUInt32() + { + if (Remaining < 4) + throw new EndOfStreamException(); + + return base.ReadUInt32(); + } + + public override float ReadSingle() + { + if (Remaining < 4) + throw new EndOfStreamException(); + + return base.ReadSingle(); + } + } + + public static class ObjectReaderExtensions + { + public static string ReadAlignedString(this ObjectReader reader) + { + var needAlign = reader.version >= (2, 1); //aligned only since v2.1 + return reader.ReadAlignedString(needAlign); + } + + public static string[] ReadStringArray(this ObjectReader reader) + { + var length = reader.ReadInt32(); + var list = new List(); + var needAlign = reader.version >= (2, 1); + for (var i = 0; i < length; i++) + { + list.Add(reader.ReadAlignedString(needAlign)); + } + return list.ToArray(); + } } } diff --git a/AssetStudio/ResourceReader.cs b/AssetStudio/ResourceReader.cs index 85415e6..283f288 100644 --- a/AssetStudio/ResourceReader.cs +++ b/AssetStudio/ResourceReader.cs @@ -1,4 +1,5 @@ using System.IO; +using System.Text.Json.Serialization; namespace AssetStudio { @@ -16,6 +17,8 @@ namespace AssetStudio set => size = value; } public long Offset { get; set; } + [JsonConverter(typeof(JsonStringEnumConverter))] + public EndianType Endian { get; } public ResourceReader() { } @@ -26,13 +29,15 @@ namespace AssetStudio this.assetsFile = assetsFile; this.Offset = offset; this.size = size; + this.Endian = assetsFile.reader.Endian; } - public ResourceReader(BinaryReader reader, long offset, long size) + public ResourceReader(EndianBinaryReader reader, long offset, long size) { this.reader = reader; this.Offset = offset; this.size = size; + this.Endian = reader.Endian; } private BinaryReader GetReader() diff --git a/AssetStudio/SerializedFile.cs b/AssetStudio/SerializedFile.cs index ea9b6bd..e77d4f6 100644 --- a/AssetStudio/SerializedFile.cs +++ b/AssetStudio/SerializedFile.cs @@ -69,6 +69,21 @@ namespace AssetStudio { reader.Endian = EndianType.LittleEndian; } + switch (header.m_Version) //default unity versions + { + case SerializedFileFormatVersion.Unsupported: + throw new NotSupportedException(); + case SerializedFileFormatVersion.Unknown_2: + case SerializedFileFormatVersion.Unknown_3: + version = new UnityVersion(1, 0); //probably not supported as well + break; + case SerializedFileFormatVersion.Unknown_5: //1.2 - 2.1 down + version = new UnityVersion(1, 6); + break; + case SerializedFileFormatVersion.Unknown_6: //2.1 - 3.0 down + version = new UnityVersion(2, 5); + break; + } if (header.m_Version >= SerializedFileFormatVersion.Unknown_7) { var versionPos = reader.Position; @@ -87,10 +102,6 @@ namespace AssetStudio reader.ReadBytes(version.ToString().Length + 1); } } - else - { - version = new UnityVersion(2, 5, 0); - } if (header.m_Version >= SerializedFileFormatVersion.Unknown_8) { var target = reader.ReadInt32(); @@ -236,6 +247,35 @@ namespace AssetStudio userInformation = reader.ReadStringToNull(); } + //try to set unity version from BuildSettings (unity 2.0 - 3.0 down) + if (header.m_Version <= SerializedFileFormatVersion.Unknown_6) + { + var buildSettingsAsset = m_Objects.FirstOrDefault(x => (ClassIDType)x.classID == ClassIDType.BuildSettings); + if (buildSettingsAsset == null) + return; + + var objReader = new ObjectReader(reader, this, buildSettingsAsset); + if (buildSettingsAsset.serializedType?.m_Type != null) + { + var settings = new BuildSettings(objReader, TypeTreeHelper.ReadTypeByteArray(objReader.serializedType.m_Type, objReader), Object.JsonOptions); + if (UnityVersion.TryParse(settings.m_Version, out var unityVer)) + { + assetsManager.AutoLegacyVersion = unityVer.Major == 1 ? new UnityVersion(2, 1) : unityVer; //detected 1.x versions should mean unity-iphone + Logger.Debug($"Detected legacy version: {unityVer}"); + } + } + else + { + objReader.Position = objReader.byteStart + objReader.byteSize - 8; + var buff = objReader.ReadBytes(8).AsSpan(); + if (UnityVersion.TryParse(buff.ReadStringToNull(), out var unityVer)) + { + assetsManager.AutoLegacyVersion = unityVer.Major == 1 ? new UnityVersion(2, 1) : unityVer; + Logger.Debug($"Detected legacy version: {unityVer}"); + } + } + } + //reader.AlignStream(16); } @@ -318,6 +358,15 @@ namespace AssetStudio m_Type.m_Nodes.Add(typeTreeNode); typeTreeNode.m_Level = level; typeTreeNode.m_Type = reader.ReadStringToNull(); + if (header.m_Version <= SerializedFileFormatVersion.Unknown_6 && typeTreeNode.m_Type == "PPtr") + { + m_TargetPlatform = BuildTarget.NoTarget; + targetPlatformString = m_TargetPlatform.ToString(); + } + if (header.m_Version == SerializedFileFormatVersion.Unknown_5 && (typeTreeNode.m_Type == "TransformComponent" || typeTreeNode.m_Type == "FileTexture")) + { + assetsManager.AutoLegacyVersion = assetsManager.AutoLegacyVersion ?? new UnityVersion(1, 2); + } typeTreeNode.m_Name = reader.ReadStringToNull(); typeTreeNode.m_ByteSize = reader.ReadInt32(); if (header.m_Version == SerializedFileFormatVersion.Unknown_2) diff --git a/AssetStudio/TypeTreeHelper.cs b/AssetStudio/TypeTreeHelper.cs index 1db6a53..c74fc3e 100644 --- a/AssetStudio/TypeTreeHelper.cs +++ b/AssetStudio/TypeTreeHelper.cs @@ -47,7 +47,7 @@ namespace AssetStudio return sb.ToString(); } - private static void ReadStringValue(StringBuilder sb, List m_Nodes, BinaryReader reader, ref int i) + private static void ReadStringValue(StringBuilder sb, List m_Nodes, ObjectReader reader, ref int i) { var m_Node = m_Nodes[i]; var level = m_Node.m_Level; @@ -236,7 +236,7 @@ namespace AssetStudio return obj; } - private static object ReadValue(List m_Nodes, BinaryReader reader, ref int i) + private static object ReadValue(List m_Nodes, ObjectReader reader, ref int i) { var m_Node = m_Nodes[i]; var varTypeStr = m_Node.m_Type; diff --git a/AssetStudioCLI/Exporter.cs b/AssetStudioCLI/Exporter.cs index 9ed3f87..b48f5dc 100644 --- a/AssetStudioCLI/Exporter.cs +++ b/AssetStudioCLI/Exporter.cs @@ -1,5 +1,6 @@ using AssetStudio; using AssetStudioCLI.Options; +using System.Buffers.Binary; using System.Collections.Generic; using System.IO; using System.Linq; @@ -92,14 +93,17 @@ namespace AssetStudioCLI private static bool ExportShader(AssetItem item, string exportPath) { - if (!TryExportFile(exportPath, item, ".shader", out var exportFullPath)) - return false; - var m_Shader = (Shader)item.Asset; - var str = m_Shader.Convert(); - File.WriteAllText(exportFullPath, str); - - Logger.Debug($"{item.TypeString} \"{item.Text}\" exported to \"{exportFullPath}\""); - return true; + if (item.Asset is Shader m_Shader) + { + if (!TryExportFile(exportPath, item, ".shader", out var exportFullPath)) + return false; + var str = m_Shader.Convert(); + File.WriteAllText(exportFullPath, str); + + Logger.Debug($"{item.TypeString} \"{item.Text}\" exported to \"{exportFullPath}\""); + return true; + } + return false; } private static bool ExportTextAsset(AssetItem item, string exportPath) @@ -153,11 +157,10 @@ namespace AssetStudioCLI private static bool ExportFont(AssetItem item, string exportPath) { - var m_Font = (Font)item.Asset; - if (m_Font.m_FontData != null) + if (item.Asset is Font m_Font && m_Font.m_FontData != null) { var extension = ".ttf"; - if (m_Font.m_FontData[0] == 79 && m_Font.m_FontData[1] == 84 && m_Font.m_FontData[2] == 84 && m_Font.m_FontData[3] == 79) + if (BinaryPrimitives.ReadInt32LittleEndian(m_Font.m_FontData) == 0x4F54544F) //"OTTO" { extension = ".otf"; } diff --git a/AssetStudioCLI/Studio.cs b/AssetStudioCLI/Studio.cs index 2c3697a..5935ba4 100644 --- a/AssetStudioCLI/Studio.cs +++ b/AssetStudioCLI/Studio.cs @@ -365,19 +365,7 @@ namespace AssetStudioCLI } var log = $"Finished loading {assetsManager.AssetsFileList.Count} files with {parsedAssetsList.Count} exportable assets"; - var unityVer = assetsManager.AssetsFileList[0].version; - long m_ObjectsCount; - if (unityVer > 2020) - { - m_ObjectsCount = assetsManager.AssetsFileList.Sum(x => x.m_Objects.LongCount(y => - y.classID != (int)ClassIDType.Shader - && CLIOptions.o_exportAssetTypes.Value.Any(k => (int)k == y.classID)) - ); - } - else - { - m_ObjectsCount = assetsManager.AssetsFileList.Sum(x => x.m_Objects.LongCount(y => CLIOptions.o_exportAssetTypes.Value.Any(k => (int)k == y.classID))); - } + var m_ObjectsCount = assetsManager.AssetsFileList.Sum(x => x.m_Objects.LongCount(y => CLIOptions.o_exportAssetTypes.Value.Any(k => (int)k == y.classID))); var objectsCount = assetsManager.AssetsFileList.Sum(x => x.Objects.LongCount(y => CLIOptions.o_exportAssetTypes.Value.Any(k => k == y.type))); if (m_ObjectsCount != objectsCount) { @@ -897,7 +885,8 @@ namespace AssetStudioCLI var gameObjects = new List(); CollectNode(j, gameObjects); - if (gameObjects.All(x => x.m_SkinnedMeshRenderer == null && x.m_MeshFilter == null)) + if ((j.gameObject.m_Transform == null && j.gameObject.m_Animator == null) + || gameObjects.All(x => x.m_SkinnedMeshRenderer == null && x.m_MeshFilter == null)) { Progress.Report(++k, count); continue; diff --git a/AssetStudioFBXWrapper/FbxExporterContext.cs b/AssetStudioFBXWrapper/FbxExporterContext.cs index 8040d0c..251c918 100644 --- a/AssetStudioFBXWrapper/FbxExporterContext.cs +++ b/AssetStudioFBXWrapper/FbxExporterContext.cs @@ -44,9 +44,9 @@ namespace AssetStudio.FbxInterop { IsDisposed = true; - _frameToNode.Clear(); - _createdMaterials.Clear(); - _createdTextures.Clear(); + _frameToNode?.Clear(); + _createdMaterials?.Clear(); + _createdTextures?.Clear(); AsFbxDisposeContext(ref _pContext); } diff --git a/AssetStudioGUI/AssetStudioGUIForm.Designer.cs b/AssetStudioGUI/AssetStudioGUIForm.Designer.cs index 0905af4..ed408e0 100644 --- a/AssetStudioGUI/AssetStudioGUIForm.Designer.cs +++ b/AssetStudioGUI/AssetStudioGUIForm.Designer.cs @@ -406,7 +406,7 @@ this.specifyUnityVersionTextBox.Font = new System.Drawing.Font("Segoe UI", 9F); this.specifyUnityVersionTextBox.Name = "specifyUnityVersionTextBox"; this.specifyUnityVersionTextBox.Size = new System.Drawing.Size(100, 23); - this.specifyUnityVersionTextBox.ToolTipText = "Specify full Unity version, including letters at the end\r\nExample: 2017.4.39f1"; + this.specifyUnityVersionTextBox.ToolTipText = "Specify full Unity version, including letters at the end\r\nExample: 2022.3.2f1"; // // bundleDecompressionToolStripMenuItem // diff --git a/AssetStudioGUI/AssetStudioGUIForm.cs b/AssetStudioGUI/AssetStudioGUIForm.cs index 1d7e9b7..aec3bbb 100644 --- a/AssetStudioGUI/AssetStudioGUIForm.cs +++ b/AssetStudioGUI/AssetStudioGUIForm.cs @@ -342,10 +342,7 @@ namespace AssetStudioGUI } allToolStripMenuItem.Checked = true; var log = $"Finished loading {assetsManager.AssetsFileList.Count} file(s) with {assetListView.Items.Count} exportable assets"; - var unityVer = assetsManager.AssetsFileList[0].version; - var m_ObjectsCount = unityVer > 2020 - ? assetsManager.AssetsFileList.Sum(x => x.m_Objects.LongCount(y => y.classID != (int)ClassIDType.Shader)) - : assetsManager.AssetsFileList.Sum(x => x.m_Objects.Count); + var m_ObjectsCount = assetsManager.AssetsFileList.Sum(x => x.m_Objects.Count); var objectsCount = assetsManager.AssetsFileList.Sum(x => x.Objects.Count); if (m_ObjectsCount != objectsCount) { @@ -1239,7 +1236,12 @@ namespace AssetStudioGUI private void PreviewShader(Shader m_Shader) { - var str = ShaderConverter.Convert(m_Shader); + if (m_Shader == null) + { + StatusStripUpdate("Unsupported shader"); + return; + } + var str = m_Shader.Convert(); PreviewText(str == null ? "Serialized Shader can't be read" : str.Replace("\n", "\r\n")); } @@ -1290,52 +1292,53 @@ namespace AssetStudioGUI private void PreviewFont(Font m_Font) { - if (m_Font.m_FontData != null) + if (m_Font?.m_FontData == null) { - var data = Marshal.AllocCoTaskMem(m_Font.m_FontData.Length); - Marshal.Copy(m_Font.m_FontData, 0, data, m_Font.m_FontData.Length); + StatusStripUpdate("Unsupported font for preview."); + return; + } - uint cFonts = 0; - var re = AddFontMemResourceEx(data, (uint)m_Font.m_FontData.Length, IntPtr.Zero, ref cFonts); - if (re != IntPtr.Zero) + var data = Marshal.AllocCoTaskMem(m_Font.m_FontData.Length); + Marshal.Copy(m_Font.m_FontData, 0, data, m_Font.m_FontData.Length); + + uint cFonts = 0; + var re = AddFontMemResourceEx(data, (uint)m_Font.m_FontData.Length, IntPtr.Zero, ref cFonts); + if (re != IntPtr.Zero) + { + using (var pfc = new PrivateFontCollection()) { - using (var pfc = new PrivateFontCollection()) + pfc.AddMemoryFont(data, m_Font.m_FontData.Length); + Marshal.FreeCoTaskMem(data); + if (pfc.Families.Length > 0) { - 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; - } + 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; } - return; } } - StatusStripUpdate("Unsupported font for preview. Try to export."); } private void PreviewMesh(Mesh m_Mesh) diff --git a/AssetStudioGUI/Exporter.cs b/AssetStudioGUI/Exporter.cs index a0efbd4..751c18c 100644 --- a/AssetStudioGUI/Exporter.cs +++ b/AssetStudioGUI/Exporter.cs @@ -1,4 +1,5 @@ using AssetStudio; +using System.Buffers.Binary; using System.Collections.Generic; using System.IO; using System.Linq; @@ -76,12 +77,15 @@ namespace AssetStudioGUI private static bool ExportShader(AssetItem item, string exportPath) { - if (!TryExportFile(exportPath, item, ".shader", out var exportFullPath)) - return false; - var m_Shader = (Shader)item.Asset; - var str = m_Shader.Convert(); - File.WriteAllText(exportFullPath, str); - return true; + if (item.Asset is Shader m_Shader) + { + if (!TryExportFile(exportPath, item, ".shader", out var exportFullPath)) + return false; + var str = m_Shader.Convert(); + File.WriteAllText(exportFullPath, str); + return true; + } + return false; } private static bool ExportTextAsset(AssetItem item, string exportPath) @@ -127,11 +131,10 @@ namespace AssetStudioGUI private static bool ExportFont(AssetItem item, string exportPath) { - var m_Font = (Font)item.Asset; - if (m_Font.m_FontData != null) + if (item.Asset is Font m_Font && m_Font.m_FontData != null) { var extension = ".ttf"; - if (m_Font.m_FontData[0] == 79 && m_Font.m_FontData[1] == 84 && m_Font.m_FontData[2] == 84 && m_Font.m_FontData[3] == 79) + if (BinaryPrimitives.ReadInt32LittleEndian(m_Font.m_FontData) == 0x4F54544F) //"OTTO" { extension = ".otf"; } diff --git a/AssetStudioGUI/ImportOptionsForm.Designer.cs b/AssetStudioGUI/ImportOptionsForm.Designer.cs index a75e554..95a38d0 100644 --- a/AssetStudioGUI/ImportOptionsForm.Designer.cs +++ b/AssetStudioGUI/ImportOptionsForm.Designer.cs @@ -33,13 +33,10 @@ this.cachePathSelectButton = new System.Windows.Forms.Button(); this.cachePathResetButton = new System.Windows.Forms.Button(); this.unityVersionGroupBox = new System.Windows.Forms.GroupBox(); + this.legacyUnityVersionComboBox = new System.Windows.Forms.ComboBox(); + this.legacyUnityVersionLabel = new System.Windows.Forms.Label(); this.customUnityVersionTextBox = new System.Windows.Forms.TextBox(); this.customUnityVersionLabel = new System.Windows.Forms.Label(); - this.unityVerDetectionModePanel = new System.Windows.Forms.Panel(); - this.unityVerAutoRadioButton = new System.Windows.Forms.RadioButton(); - this.unityVerLegacy2RadioButton = new System.Windows.Forms.RadioButton(); - this.unityVerLegacyRadioButton = new System.Windows.Forms.RadioButton(); - this.unityVerDetectionModeLabel = new System.Windows.Forms.Label(); this.decompressionGroupBox = new System.Windows.Forms.GroupBox(); this.customBlockCompressionComboBox = new System.Windows.Forms.ComboBox(); this.customBlockCompressionLabel = new System.Windows.Forms.Label(); @@ -56,7 +53,6 @@ this.tabPage2 = new System.Windows.Forms.TabPage(); this.cacheGroupBox = new System.Windows.Forms.GroupBox(); this.unityVersionGroupBox.SuspendLayout(); - this.unityVerDetectionModePanel.SuspendLayout(); this.decompressionGroupBox.SuspendLayout(); this.tabControl1.SuspendLayout(); this.tabPage1.SuspendLayout(); @@ -95,25 +91,54 @@ // // unityVersionGroupBox // + this.unityVersionGroupBox.Controls.Add(this.legacyUnityVersionComboBox); + this.unityVersionGroupBox.Controls.Add(this.legacyUnityVersionLabel); this.unityVersionGroupBox.Controls.Add(this.customUnityVersionTextBox); this.unityVersionGroupBox.Controls.Add(this.customUnityVersionLabel); - this.unityVersionGroupBox.Controls.Add(this.unityVerDetectionModePanel); - this.unityVersionGroupBox.Controls.Add(this.unityVerDetectionModeLabel); this.unityVersionGroupBox.Dock = System.Windows.Forms.DockStyle.Top; this.unityVersionGroupBox.Location = new System.Drawing.Point(3, 3); this.unityVersionGroupBox.Name = "unityVersionGroupBox"; - this.unityVersionGroupBox.Size = new System.Drawing.Size(357, 126); + this.unityVersionGroupBox.Size = new System.Drawing.Size(357, 99); this.unityVersionGroupBox.TabIndex = 0; this.unityVersionGroupBox.TabStop = false; this.unityVersionGroupBox.Text = "Unity version"; // + // legacyUnityVersionComboBox + // + this.legacyUnityVersionComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.legacyUnityVersionComboBox.FormattingEnabled = true; + this.legacyUnityVersionComboBox.Items.AddRange(new object[] { + "None", + "1.2 mac", + "1.5 - 1.6 mac", + "2.0 mac", + "2.1 mac / 1.7 iphone", + "2.5", + "2.6"}); + this.legacyUnityVersionComboBox.Location = new System.Drawing.Point(205, 49); + this.legacyUnityVersionComboBox.Name = "legacyUnityVersionComboBox"; + this.legacyUnityVersionComboBox.Size = new System.Drawing.Size(146, 23); + this.legacyUnityVersionComboBox.TabIndex = 3; + this.legacyUnityVersionComboBox.SelectionChangeCommitted += new System.EventHandler(this.legacyUnityVersionComboBox_SelectionChangeCommitted); + // + // legacyUnityVersionLabel + // + this.legacyUnityVersionLabel.AutoSize = true; + this.legacyUnityVersionLabel.Location = new System.Drawing.Point(6, 52); + this.legacyUnityVersionLabel.Name = "legacyUnityVersionLabel"; + this.legacyUnityVersionLabel.Size = new System.Drawing.Size(193, 15); + this.legacyUnityVersionLabel.TabIndex = 2; + this.legacyUnityVersionLabel.Text = "Custom legacy Unity version (WIP):"; + // // customUnityVersionTextBox // this.customUnityVersionTextBox.Location = new System.Drawing.Point(137, 20); this.customUnityVersionTextBox.Name = "customUnityVersionTextBox"; this.customUnityVersionTextBox.Size = new System.Drawing.Size(121, 23); this.customUnityVersionTextBox.TabIndex = 1; - this.toolTip1.SetToolTip(this.customUnityVersionTextBox, "Specify full Unity version, including letters at the end\r\nExample: 2017.4.39f1\r\n"); + this.toolTip1.SetToolTip(this.customUnityVersionTextBox, "Specify full Unity version, including letters at the end\r\nExample: 2022.3.2f1"); + this.customUnityVersionTextBox.Enter += new System.EventHandler(this.customUnityVersionTextBox_Enter); + this.customUnityVersionTextBox.Leave += new System.EventHandler(this.customUnityVersionTextBox_Leave); // // customUnityVersionLabel // @@ -123,70 +148,7 @@ this.customUnityVersionLabel.Size = new System.Drawing.Size(124, 15); this.customUnityVersionLabel.TabIndex = 0; this.customUnityVersionLabel.Text = "Custom Unity version:"; - this.toolTip1.SetToolTip(this.customUnityVersionLabel, "Specify full Unity version, including letters at the end\r\nExample: 2017.4.39f1\r\n"); - // - // unityVerDetectionModePanel - // - this.unityVerDetectionModePanel.Controls.Add(this.unityVerAutoRadioButton); - this.unityVerDetectionModePanel.Controls.Add(this.unityVerLegacy2RadioButton); - this.unityVerDetectionModePanel.Controls.Add(this.unityVerLegacyRadioButton); - this.unityVerDetectionModePanel.Location = new System.Drawing.Point(35, 75); - this.unityVerDetectionModePanel.Name = "unityVerDetectionModePanel"; - this.unityVerDetectionModePanel.Size = new System.Drawing.Size(289, 34); - this.unityVerDetectionModePanel.TabIndex = 3; - // - // unityVerAutoRadioButton - // - this.unityVerAutoRadioButton.AutoSize = true; - this.unityVerAutoRadioButton.Dock = System.Windows.Forms.DockStyle.Left; - this.unityVerAutoRadioButton.Location = new System.Drawing.Point(152, 0); - this.unityVerAutoRadioButton.Name = "unityVerAutoRadioButton"; - this.unityVerAutoRadioButton.Padding = new System.Windows.Forms.Padding(0, 0, 5, 0); - this.unityVerAutoRadioButton.Size = new System.Drawing.Size(103, 34); - this.unityVerAutoRadioButton.TabIndex = 2; - this.unityVerAutoRadioButton.TabStop = true; - this.unityVerAutoRadioButton.Text = "Auto/Custom"; - this.unityVerAutoRadioButton.UseVisualStyleBackColor = true; - // - // unityVerLegacy2RadioButton - // - this.unityVerLegacy2RadioButton.AutoSize = true; - this.unityVerLegacy2RadioButton.Dock = System.Windows.Forms.DockStyle.Left; - this.unityVerLegacy2RadioButton.Enabled = false; - this.unityVerLegacy2RadioButton.Location = new System.Drawing.Point(76, 0); - this.unityVerLegacy2RadioButton.Name = "unityVerLegacy2RadioButton"; - this.unityVerLegacy2RadioButton.Padding = new System.Windows.Forms.Padding(0, 0, 5, 0); - this.unityVerLegacy2RadioButton.Size = new System.Drawing.Size(76, 34); - this.unityVerLegacy2RadioButton.TabIndex = 1; - this.unityVerLegacy2RadioButton.TabStop = true; - this.unityVerLegacy2RadioButton.Text = "Legacy 2"; - this.toolTip1.SetToolTip(this.unityVerLegacy2RadioButton, "WIP"); - this.unityVerLegacy2RadioButton.UseVisualStyleBackColor = true; - // - // unityVerLegacyRadioButton - // - this.unityVerLegacyRadioButton.AutoSize = true; - this.unityVerLegacyRadioButton.Dock = System.Windows.Forms.DockStyle.Left; - this.unityVerLegacyRadioButton.Enabled = false; - this.unityVerLegacyRadioButton.Location = new System.Drawing.Point(0, 0); - this.unityVerLegacyRadioButton.Name = "unityVerLegacyRadioButton"; - this.unityVerLegacyRadioButton.Padding = new System.Windows.Forms.Padding(0, 0, 5, 0); - this.unityVerLegacyRadioButton.Size = new System.Drawing.Size(76, 34); - this.unityVerLegacyRadioButton.TabIndex = 0; - this.unityVerLegacyRadioButton.TabStop = true; - this.unityVerLegacyRadioButton.Text = "Legacy 1"; - this.toolTip1.SetToolTip(this.unityVerLegacyRadioButton, "WIP"); - this.unityVerLegacyRadioButton.UseVisualStyleBackColor = true; - // - // unityVerDetectionModeLabel - // - this.unityVerDetectionModeLabel.AutoSize = true; - this.unityVerDetectionModeLabel.Location = new System.Drawing.Point(6, 57); - this.unityVerDetectionModeLabel.Name = "unityVerDetectionModeLabel"; - this.unityVerDetectionModeLabel.Size = new System.Drawing.Size(92, 15); - this.unityVerDetectionModeLabel.TabIndex = 2; - this.unityVerDetectionModeLabel.Text = "Detection mode"; - this.toolTip1.SetToolTip(this.unityVerDetectionModeLabel, "WIP"); + this.toolTip1.SetToolTip(this.customUnityVersionLabel, "Specify full Unity version, including letters at the end\r\nExample: 2022.3.2f1"); // // decompressionGroupBox // @@ -204,6 +166,7 @@ // // customBlockCompressionComboBox // + this.customBlockCompressionComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.customBlockCompressionComboBox.FormattingEnabled = true; this.customBlockCompressionComboBox.Items.AddRange(new object[] { "Auto", @@ -228,6 +191,7 @@ // // customBlockInfoCompressionComboBox // + this.customBlockInfoCompressionComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; this.customBlockInfoCompressionComboBox.FormattingEnabled = true; this.customBlockInfoCompressionComboBox.Items.AddRange(new object[] { "Auto", @@ -385,8 +349,6 @@ this.Text = "Import options"; this.unityVersionGroupBox.ResumeLayout(false); this.unityVersionGroupBox.PerformLayout(); - this.unityVerDetectionModePanel.ResumeLayout(false); - this.unityVerDetectionModePanel.PerformLayout(); this.decompressionGroupBox.ResumeLayout(false); this.decompressionGroupBox.PerformLayout(); this.tabControl1.ResumeLayout(false); @@ -419,14 +381,11 @@ private System.Windows.Forms.Button saveToDiskButton; private System.Windows.Forms.Button applyButton; private System.Windows.Forms.Label cacheFreeSpaceLabel; - private System.Windows.Forms.Panel unityVerDetectionModePanel; - private System.Windows.Forms.RadioButton unityVerAutoRadioButton; - private System.Windows.Forms.RadioButton unityVerLegacy2RadioButton; - private System.Windows.Forms.RadioButton unityVerLegacyRadioButton; - private System.Windows.Forms.Label unityVerDetectionModeLabel; + private System.Windows.Forms.Label legacyUnityVersionLabel; private System.Windows.Forms.TabControl tabControl1; private System.Windows.Forms.TabPage tabPage1; private System.Windows.Forms.TabPage tabPage2; private System.Windows.Forms.GroupBox cacheGroupBox; + private System.Windows.Forms.ComboBox legacyUnityVersionComboBox; } } \ No newline at end of file diff --git a/AssetStudioGUI/ImportOptionsForm.cs b/AssetStudioGUI/ImportOptionsForm.cs index 82af90c..b9cf893 100644 --- a/AssetStudioGUI/ImportOptionsForm.cs +++ b/AssetStudioGUI/ImportOptionsForm.cs @@ -8,6 +8,7 @@ namespace AssetStudioGUI { public partial class ImportOptionsForm : Form { + private static readonly string SampleVersion = "e.g. 2022.3.2f1"; private readonly AssetsManager _assetsManager; private string _cachePath; @@ -19,18 +20,22 @@ namespace AssetStudioGUI { customUnityVersionTextBox.Text = assetsManager.Options.CustomUnityVersion.FullVersion; } - unityVerAutoRadioButton.Checked = true; + else + { + ResetUnityVersionTextBox(); + } + legacyUnityVersionComboBox.SelectedIndex = SetLegacyUnityVersionComboBoxIndex(); cachePathLabel.AutoSize = IsWine; //to disable AutoEllipsis in Wine _cachePath = _assetsManager.Options.BundleOptions.CustomCachePath; DisplayCachePath(); alwaysDecompressToDiskCheckBox.Checked = assetsManager.Options.BundleOptions.DecompressToDisk; - customBlockInfoCompressionComboBox.SelectedIndex = SetComboBoxIndex(assetsManager.Options.BundleOptions.CustomBlockInfoCompression); - customBlockCompressionComboBox.SelectedIndex = SetComboBoxIndex(assetsManager.Options.BundleOptions.CustomBlockCompression); + customBlockInfoCompressionComboBox.SelectedIndex = SetCompressionComboBoxIndex(assetsManager.Options.BundleOptions.CustomBlockInfoCompression); + customBlockCompressionComboBox.SelectedIndex = SetCompressionComboBoxIndex(assetsManager.Options.BundleOptions.CustomBlockCompression); } private void ApplyOptions() { - if (string.IsNullOrEmpty(customUnityVersionTextBox.Text)) + if (string.IsNullOrEmpty(customUnityVersionTextBox.Text) || customUnityVersionTextBox.Text == SampleVersion) { _assetsManager.Options.CustomUnityVersion = null; } @@ -39,6 +44,7 @@ namespace AssetStudioGUI try { _assetsManager.Options.CustomUnityVersion = new UnityVersion(customUnityVersionTextBox.Text); + legacyUnityVersionComboBox.SelectedIndex = SetLegacyUnityVersionComboBoxIndex(); } catch (Exception ex) { @@ -61,6 +67,79 @@ namespace AssetStudioGUI Properties.Settings.Default.Save(); } + #region General + private void customUnityVersionTextBox_Enter(object sender, EventArgs e) + { + if (customUnityVersionTextBox.Text == SampleVersion) + { + customUnityVersionTextBox.Text = ""; + customUnityVersionTextBox.ForeColor = System.Drawing.SystemColors.WindowText; + } + } + + private void customUnityVersionTextBox_Leave(object sender, EventArgs e) + { + if (string.IsNullOrEmpty(customUnityVersionTextBox.Text)) + { + ResetUnityVersionTextBox(); + } + } + + private void ResetUnityVersionTextBox() + { + customUnityVersionTextBox.ForeColor = System.Drawing.SystemColors.GrayText; + customUnityVersionTextBox.Text = SampleVersion; + } + + private void legacyUnityVersionComboBox_SelectionChangeCommitted(object sender, EventArgs e) + { + var version = GetLegacyUnityVersion(legacyUnityVersionComboBox.SelectedIndex); + if (version != null) + { + customUnityVersionTextBox.ForeColor = System.Drawing.SystemColors.WindowText; + customUnityVersionTextBox.Text = version.ToString(); + } + else + { + ResetUnityVersionTextBox(); + } + } + + private static UnityVersion GetLegacyUnityVersion(int index) + { + switch (index) + { + case 0: return null; + case 1: return new UnityVersion(1, 2); + case 2: return new UnityVersion(1, 6); + case 3: return new UnityVersion(2, 0); + case 4: return new UnityVersion(2, 1); + case 5: return new UnityVersion(2, 5); + case 6: return new UnityVersion(2, 6); + default: throw new NotSupportedException(); + } + } + + private int SetLegacyUnityVersionComboBoxIndex() + { + var version = _assetsManager.Options.CustomUnityVersion; + + switch (version?.Major) + { + case 2 when version.Minor >= 6: return 6; + case 2 when version.Minor == 5: return 5; + case 2 when version.Minor > 0: return 4; + case 2: return 3; + case 1 when version.Minor >= 7: return 4; + case 1 when version.Minor >= 5: return 2; + case 1: return 1; + } + + return 0; + } + #endregion + + #region Asset Bundles private static CompressionType GetCustomCompressionTypes(int index) { switch (index) @@ -74,7 +153,7 @@ namespace AssetStudioGUI } } - private static int SetComboBoxIndex(CompressionType compressionType) + private static int SetCompressionComboBoxIndex(CompressionType compressionType) { switch (compressionType) { @@ -126,6 +205,7 @@ namespace AssetStudioGUI _assetsManager.Options.SaveToFile(savePath); } } + #endregion private void closeButton_Click(object sender, EventArgs e) { diff --git a/AssetStudioGUI/Studio.cs b/AssetStudioGUI/Studio.cs index b878f1c..d9dc7b0 100644 --- a/AssetStudioGUI/Studio.cs +++ b/AssetStudioGUI/Studio.cs @@ -787,7 +787,8 @@ namespace AssetStudioGUI var gameObjects = new List(); CollectNode(j, gameObjects); //跳过一些不需要导出的object - if (gameObjects.All(x => x.m_SkinnedMeshRenderer == null && x.m_MeshFilter == null)) + if ((j.gameObject.m_Transform == null && j.gameObject.m_Animator == null) + || gameObjects.All(x => x.m_SkinnedMeshRenderer == null && x.m_MeshFilter == null)) { Progress.Report(++k, count); continue; diff --git a/AssetStudioUtility/ModelConverter.cs b/AssetStudioUtility/ModelConverter.cs index 95b7281..9065523 100644 --- a/AssetStudioUtility/ModelConverter.cs +++ b/AssetStudioUtility/ModelConverter.cs @@ -37,10 +37,14 @@ namespace AssetStudio CollectAnimationClip(m_GameObject.m_Animator); } } - else + else if (m_GameObject.m_Transform != null) { InitWithGameObject(m_GameObject); } + else + { + throw new NotSupportedException("Unable to export model from this object"); + } if (animationList != null && animationList.Count > 0) { Logger.Debug($"Selected AnimationClip(s):\n\"{string.Join("\"\n\"", animationList.Select(x => x.m_Name))}\""); @@ -656,7 +660,7 @@ namespace AssetStudio return iMat; } iMat = new ImportedMaterial(); - iMat.Name = mat.m_Name; + iMat.Name = mat.m_Name ?? $"material {mat.m_PathID}"; //default values iMat.Diffuse = new Color(0.8f, 0.8f, 0.8f, 1); iMat.Ambient = new Color(0.2f, 0.2f, 0.2f, 1); diff --git a/AssetStudioUtility/ShaderConverter.cs b/AssetStudioUtility/ShaderConverter.cs index 0501e64..d944c58 100644 --- a/AssetStudioUtility/ShaderConverter.cs +++ b/AssetStudioUtility/ShaderConverter.cs @@ -17,7 +17,7 @@ namespace AssetStudio { var decompressedBytes = new byte[shader.decompressedSize]; LZ4Codec.Decode(shader.m_SubProgramBlob, decompressedBytes); - using (var blobReader = new BinaryReader(new MemoryStream(decompressedBytes))) + using (var blobReader = new EndianBinaryReader(new MemoryStream(decompressedBytes), EndianType.LittleEndian)) { var program = new ShaderProgram(blobReader, shader.version); program.Read(blobReader, 0); @@ -46,7 +46,7 @@ namespace AssetStudio var decompressedLength = shader.decompressedLengths[i][j]; var decompressedBytes = new byte[decompressedLength]; LZ4Codec.Decode(shader.compressedBlob, (int)offset, (int)compressedLength, decompressedBytes, 0, (int)decompressedLength); - using (var blobReader = new BinaryReader(new MemoryStream(decompressedBytes))) + using (var blobReader = new EndianBinaryReader(new MemoryStream(decompressedBytes), EndianType.LittleEndian)) { if (j == 0) { @@ -872,7 +872,7 @@ namespace AssetStudio public int Length; public int Segment; - public ShaderSubProgramEntry(BinaryReader reader, UnityVersion version) + public ShaderSubProgramEntry(EndianBinaryReader reader, UnityVersion version) { Offset = reader.ReadInt32(); Length = reader.ReadInt32(); @@ -888,7 +888,7 @@ namespace AssetStudio public ShaderSubProgramEntry[] entries; public ShaderSubProgram[] m_SubPrograms; - public ShaderProgram(BinaryReader reader, UnityVersion version) + public ShaderProgram(EndianBinaryReader reader, UnityVersion version) { var subProgramsCapacity = reader.ReadInt32(); entries = new ShaderSubProgramEntry[subProgramsCapacity]; @@ -899,7 +899,7 @@ namespace AssetStudio m_SubPrograms = new ShaderSubProgram[subProgramsCapacity]; } - public void Read(BinaryReader reader, int segment) + public void Read(EndianBinaryReader reader, int segment) { for (int i = 0; i < entries.Length; i++) { @@ -932,7 +932,7 @@ namespace AssetStudio public string[] m_LocalKeywords; public byte[] m_ProgramCode; - public ShaderSubProgram(BinaryReader reader) + public ShaderSubProgram(EndianBinaryReader reader) { //LoadGpuProgramFromData //201509030 - Unity 5.3 @@ -1043,7 +1043,7 @@ namespace AssetStudio } case ShaderGpuProgramType.MetalVS: case ShaderGpuProgramType.MetalFS: - using (var reader = new BinaryReader(new MemoryStream(m_ProgramCode))) + using (var reader = new EndianBinaryReader(new MemoryStream(m_ProgramCode), EndianType.LittleEndian)) { var fourCC = reader.ReadUInt32(); if (fourCC == 0xf00dcafe) diff --git a/AssetStudioUtility/SpriteConverter.cs b/AssetStudioUtility/SpriteConverter.cs index 2f3115d..bbf33c3 100644 --- a/AssetStudioUtility/SpriteConverter.cs +++ b/AssetStudioUtility/SpriteConverter.cs @@ -260,9 +260,9 @@ namespace AssetStudio var m_VertexData = m_RD.m_VertexData; var m_Channel = m_VertexData.m_Channels[0]; //kShaderChannelVertex var m_Stream = m_VertexData.m_Streams[m_Channel.stream]; - using (var vertexReader = new BinaryReader(new MemoryStream(m_VertexData.m_DataSize))) + using (var vertexReader = new EndianBinaryReader(new MemoryStream(m_VertexData.m_DataSize), EndianType.LittleEndian)) { - using (var indexReader = new BinaryReader(new MemoryStream(m_RD.m_IndexBuffer))) + using (var indexReader = new EndianBinaryReader(new MemoryStream(m_RD.m_IndexBuffer), EndianType.LittleEndian)) { foreach (var subMesh in m_RD.m_SubMeshes) { diff --git a/AssetStudioUtility/Texture2DConverter.cs b/AssetStudioUtility/Texture2DConverter.cs index da9d70e..96a1555 100644 --- a/AssetStudioUtility/Texture2DConverter.cs +++ b/AssetStudioUtility/Texture2DConverter.cs @@ -30,7 +30,7 @@ namespace AssetStudio m_Width = m_Texture2D.m_Width; m_Height = m_Texture2D.m_Height; texDataSize = m_Texture2D.image_data.Size; - if (texDataSize == 0 || m_Width == 0 || m_Height == 0) + if (texDataSize <= 0 || m_Width <= 0 || m_Width > 16384 || m_Height <= 0 || m_Height > 16384) return; m_TextureFormat = m_Texture2D.m_TextureFormat; @@ -83,7 +83,7 @@ namespace AssetStudio if (!DecodeTexture2D(dataSpan)) return false; - if (flip) + if (flip && m_TextureFormat != TextureFormat.DXT3) { var stride = m_Width * 4; Span tmp = new byte[stride]; @@ -116,7 +116,7 @@ namespace AssetStudio image = Image.LoadPixelData(imageBuffSpan, m_Width, m_Height); - if (flip) + if (flip && m_TextureFormat != TextureFormat.DXT3) { image.Mutate(x => x.Flip(FlipMode.Vertical)); } @@ -134,6 +134,7 @@ namespace AssetStudio return false; var flag = false; + var forceSwap = m_Texture2D.image_data.Endian == EndianType.BigEndian; //PowerPC(PPC) builds var buff = BigArrayPool.Shared.Rent(texDataSize); try { @@ -152,7 +153,7 @@ namespace AssetStudio flag = DecodeAlpha8(buff, destSpan); break; case TextureFormat.ARGB4444: //test pass - SwapBytesForXbox(buff); + SwapBytesForXbox(buff, forceSwap); flag = DecodeARGB4444(buff, destSpan); break; case TextureFormat.RGB24: //test pass @@ -165,7 +166,7 @@ namespace AssetStudio flag = DecodeARGB32(buff, destSpan); break; case TextureFormat.RGB565: //test pass - SwapBytesForXbox(buff); + SwapBytesForXbox(buff, forceSwap); flag = DecodeRGB565(buff, destSpan); break; case TextureFormat.R16: //test pass @@ -175,7 +176,8 @@ namespace AssetStudio SwapBytesForXbox(buff); flag = DecodeDXT1(buff, destSpan); break; - case TextureFormat.DXT3: + case TextureFormat.DXT3: //TODO + flag = DecodeDXT3(buff, destSpan); break; case TextureFormat.DXT5: //test pass SwapBytesForXbox(buff); @@ -330,13 +332,13 @@ namespace AssetStudio return flag; } - private void SwapBytesForXbox(Span buff) + private void SwapBytesForXbox(Span buff, bool force = false) { - if (m_Texture2D.platform == BuildTarget.XBOX360) + if (m_Texture2D.platform == BuildTarget.XBOX360 || force) { - for (var i = 0; i < texDataSize / 2; i++) + for (var i = 0; i < texDataSize; i += 2) { - buff.Slice(i * 2, 2).Reverse(); + buff.Slice(i, 2).Reverse(); } } } @@ -439,6 +441,35 @@ namespace AssetStudio return TextureDecoder.DecodeDXT1(buff, m_Width, m_Height, destSpan); } + private bool DecodeDXT3(ReadOnlySpan buff, Span destSpan) + { + if (!TextureDecoder.DecodeDXT5(buff, m_Width, m_Height, destSpan)) + return false; + + //decode dxt3 alpha + var numBlocksX = (m_Width + 3) / 4; + var numBlocksY = (m_Height + 3) / 4; + var offset = 0; + for (var by = 0; by < numBlocksY; by++) + { + for (var bx = 0; bx < numBlocksX; bx++, offset += 16) + { + var alphaPackedVal = BinaryPrimitives.ReadUInt64LittleEndian(buff.Slice(offset)); + var alphaIndex = 0; + for (var y = 0; y < 4; y++) + { + for (var x = 0; x < 4; x++, alphaIndex++) + { + var destOffset = (by * 4 + y) * m_Width * 4 + (bx * 4 + x) * 4; + var alphaValue = ((alphaPackedVal >> (4 * alphaIndex)) & 0x0F) * 0x11; + destSpan[destOffset + 3] = (byte)alphaValue; //A + } + } + } + } + return true; + } + private bool DecodeDXT5(ReadOnlySpan buff, Span destSpan) { return TextureDecoder.DecodeDXT5(buff, m_Width, m_Height, destSpan);