From fc44ba373b44aeb8cde77d6b92fe00770bdec0b7 Mon Sep 17 00:00:00 2001 From: Perfare Date: Thu, 18 Jan 2018 08:08:48 +0800 Subject: [PATCH] add Sprite support --- Unity Studio/Unity Classes/Sprite.cs | 136 ++++++++++++++++++ Unity Studio/Unity Classes/SpriteAtlas.cs | 65 +++++++++ Unity Studio/Unity Classes/Texture2D.cs | 4 +- .../Unity Studio Classes/AssetPreloadData.cs | 2 +- .../Unity Studio Classes/AssetsFile.cs | 2 +- .../Unity Studio Classes/ClassIDReference.cs | 6 + .../Unity Studio Classes/UnityStudio.cs | 84 ++++++++++- Unity Studio/Unity Studio-x86.csproj | 2 + Unity Studio/Unity Studio.csproj | 2 + Unity Studio/UnityStudioForm.cs | 39 ++++- 10 files changed, 330 insertions(+), 12 deletions(-) create mode 100644 Unity Studio/Unity Classes/Sprite.cs create mode 100644 Unity Studio/Unity Classes/SpriteAtlas.cs diff --git a/Unity Studio/Unity Classes/Sprite.cs b/Unity Studio/Unity Classes/Sprite.cs new file mode 100644 index 0000000..9c9ca80 --- /dev/null +++ b/Unity Studio/Unity Classes/Sprite.cs @@ -0,0 +1,136 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Text; + +namespace Unity_Studio +{ + class Sprite + { + public string m_Name; + public PPtr texture; + public PPtr m_SpriteAtlas; + public RectangleF textureRect; + + public Sprite(AssetPreloadData preloadData, bool readSwitch) + { + var sourceFile = preloadData.sourceFile; + var a_Stream = preloadData.sourceFile.a_Stream; + a_Stream.Position = preloadData.Offset; + + m_Name = a_Stream.ReadAlignedString(a_Stream.ReadInt32()); + if (readSwitch) + { + //Rectf m_Rect + var m_Rect = new RectangleF(a_Stream.ReadSingle(), a_Stream.ReadSingle(), a_Stream.ReadSingle(), a_Stream.ReadSingle()); + //Vector2f m_Offset + a_Stream.Position += 8; + if (sourceFile.version[0] > 4 || (sourceFile.version[0] == 4 && sourceFile.version[1] >= 2)) //4.2 and up + { + //Vector4f m_Border + a_Stream.Position += 16; + } + + var m_PixelsToUnits = a_Stream.ReadSingle(); + if (sourceFile.version[0] > 5 || (sourceFile.version[0] == 5 && sourceFile.version[1] >= 5)) //5.5 and up + { + //Vector2f m_Pivot + a_Stream.Position += 8; + } + + var m_Extrude = a_Stream.ReadUInt32(); + if (sourceFile.version[0] > 5 || (sourceFile.version[0] == 5 && sourceFile.version[1] >= 1)) //5.1 and up + { + var m_IsPolygon = a_Stream.ReadBoolean(); + a_Stream.AlignStream(4); + } + + if (sourceFile.version[0] >= 2017) //2017 and up + { + //pair m_RenderDataKey + a_Stream.Position += 24; + //vector m_AtlasTags + var size = a_Stream.ReadInt32(); + for (int i = 0; i < size; i++) + { + var data = a_Stream.ReadAlignedString(a_Stream.ReadInt32()); + } + + //PPtr m_SpriteAtlas + m_SpriteAtlas = sourceFile.ReadPPtr(); + } + + //SpriteRenderData m_RD + // PPtr texture + texture = sourceFile.ReadPPtr(); + // PPtr alphaTexture + if (sourceFile.version[0] >= 5) //5.0 and up + { + var alphaTexture = sourceFile.ReadPPtr(); + } + + if (sourceFile.version[0] > 5 || (sourceFile.version[0] == 5 && sourceFile.version[1] >= 6)) //5.6 and up + { + // vector m_SubMeshes + var size = a_Stream.ReadInt32(); + // SubMesh data + if (sourceFile.version[0] > 2017 || (sourceFile.version[0] == 2017 && sourceFile.version[1] >= 3)) //2017.3 and up + { + a_Stream.Position += 48 * size; + } + else + { + a_Stream.Position += 44 * size; + } + + // vector m_IndexBuffer + size = a_Stream.ReadInt32(); + a_Stream.Position += size; //UInt8 data + a_Stream.AlignStream(4); + // VertexData m_VertexData + var m_CurrentChannels = a_Stream.ReadInt32(); + var m_VertexCount = a_Stream.ReadUInt32(); + // vector m_Channels + size = a_Stream.ReadInt32(); + a_Stream.Position += size * 4; //ChannelInfo data + // TypelessData m_DataSize + size = a_Stream.ReadInt32(); + a_Stream.Position += size; //UInt8 data + a_Stream.AlignStream(4); + } + else + { + // vector vertices + var size = a_Stream.ReadInt32(); + for (int i = 0; i < size; i++) + { + //SpriteVertex data + a_Stream.Position += 12; //Vector3f pos + if (sourceFile.version[0] < 4 || (sourceFile.version[0] == 4 && sourceFile.version[1] <= 1)) //4.1 and down + a_Stream.Position += 8; //Vector2f uv + } + + // vector indices + size = a_Stream.ReadInt32(); + a_Stream.Position += 2 * size; //UInt16 data + a_Stream.AlignStream(4); + } + + // Rectf textureRect + textureRect = new RectangleF(a_Stream.ReadSingle(), a_Stream.ReadSingle(), a_Stream.ReadSingle(), a_Stream.ReadSingle()); + // Vector2f textureRectOffset + // Vector2f atlasRectOffset - 5.6 and up + // unsigned int settingsRaw + // Vector4f uvTransform - 4.2 and up + // float downscaleMultiplier - 2017 and up + //vector m_PhysicsShape - 2017 and up + } + else + { + preloadData.extension = ".png"; + preloadData.Text = m_Name; + } + } + } +} diff --git a/Unity Studio/Unity Classes/SpriteAtlas.cs b/Unity Studio/Unity Classes/SpriteAtlas.cs new file mode 100644 index 0000000..5e0f782 --- /dev/null +++ b/Unity Studio/Unity Classes/SpriteAtlas.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Text; + +namespace Unity_Studio +{ + class SpriteAtlas + { + public List m_PackedSprites = new List(); + public List textures = new List(); + public List textureRects = new List(); + + + public SpriteAtlas(AssetPreloadData preloadData) + { + var sourceFile = preloadData.sourceFile; + var a_Stream = preloadData.sourceFile.a_Stream; + a_Stream.Position = preloadData.Offset; + + var m_Name = a_Stream.ReadAlignedString(a_Stream.ReadInt32()); + //vector m_PackedSprites + var size = a_Stream.ReadInt32(); + for (int i = 0; i < size; i++) + { + //PPtr data + m_PackedSprites.Add(sourceFile.ReadPPtr()); + } + //vector m_PackedSpriteNamesToIndex + size = a_Stream.ReadInt32(); + for (int i = 0; i < size; i++) + { + var data = a_Stream.ReadAlignedString(a_Stream.ReadInt32()); + } + //map m_RenderDataMap + size = a_Stream.ReadInt32(); + for (int i = 0; i < size; i++) + { + //pair first + a_Stream.Position += 24; + //SpriteAtlasData second + // PPtr texture + textures.Add(sourceFile.ReadPPtr()); + // PPtr alphaTexture + var alphaTexture = sourceFile.ReadPPtr(); + // Rectf textureRect + textureRects.Add(new RectangleF(a_Stream.ReadSingle(), a_Stream.ReadSingle(), a_Stream.ReadSingle(), a_Stream.ReadSingle())); + // Vector2f textureRectOffset + a_Stream.Position += 8; + if (sourceFile.version[0] > 2017 || (sourceFile.version[0] == 2017 && sourceFile.version[1] >= 2))//2017.2 and up + { + // Vector2f atlasRectOffset + a_Stream.Position += 8; + } + // Vector4f uvTransform + // float downscaleMultiplier + // unsigned int settingsRaw + a_Stream.Position += 24; + } + //string m_Tag + //bool m_IsVariant + } + } +} diff --git a/Unity Studio/Unity Classes/Texture2D.cs b/Unity Studio/Unity Classes/Texture2D.cs index ff8c436..bfd2018 100644 --- a/Unity Studio/Unity Classes/Texture2D.cs +++ b/Unity Studio/Unity Classes/Texture2D.cs @@ -658,7 +658,7 @@ namespace Unity_Studio } else { - preloadData.InfoText = "Width: " + m_Width + "\nHeight: " + m_Height + "\nFormat: "; + preloadData.InfoText = $"Width: {m_Width}\nHeight: {m_Height}\nFormat: "; string type = m_TextureFormat.ToString(); preloadData.InfoText += type; @@ -737,7 +737,7 @@ namespace Unity_Studio } - preloadData.InfoText += "\nAnisotropic level: " + m_Aniso + "\nMip map bias: " + m_MipBias; + preloadData.InfoText += $"\nAnisotropic level: {m_Aniso}\nMip map bias: {m_MipBias}"; switch (m_WrapMode) { diff --git a/Unity Studio/Unity Studio Classes/AssetPreloadData.cs b/Unity Studio/Unity Studio Classes/AssetPreloadData.cs index b31d365..a9d5688 100644 --- a/Unity Studio/Unity Studio Classes/AssetPreloadData.cs +++ b/Unity Studio/Unity Studio Classes/AssetPreloadData.cs @@ -12,7 +12,7 @@ namespace Unity_Studio public uint Offset; public int Size; public int Type1; - public ushort Type2; + public int Type2; public string TypeString; public int fullSize; diff --git a/Unity Studio/Unity Studio Classes/AssetsFile.cs b/Unity Studio/Unity Studio Classes/AssetsFile.cs index e0a924c..bd36c34 100644 --- a/Unity Studio/Unity Studio Classes/AssetsFile.cs +++ b/Unity Studio/Unity Studio Classes/AssetsFile.cs @@ -321,7 +321,7 @@ namespace Unity_Studio { int index = a_Stream.ReadInt32(); asset.Type1 = classIDs[index][0]; - asset.Type2 = (ushort)classIDs[index][1]; + asset.Type2 = classIDs[index][1]; } else { diff --git a/Unity Studio/Unity Studio Classes/ClassIDReference.cs b/Unity Studio/Unity Studio Classes/ClassIDReference.cs index e95a25e..b4400a1 100644 --- a/Unity Studio/Unity Studio Classes/ClassIDReference.cs +++ b/Unity Studio/Unity Studio Classes/ClassIDReference.cs @@ -266,6 +266,12 @@ namespace Unity_Studio {1112, "SubstanceImporter"}, {1113, "LightmapParameters"}, {1120, "LightmapSnapshot"}, + {367388927, "SubDerived"}, + {334799969, "SiblingDerived"}, + {687078895, "SpriteAtlas"}, + {1091556383, "Derived"}, + {1480428607, "LowerResBlitTexture"}, + {1571458007, "RenderPassAttachment"} }; } } diff --git a/Unity Studio/Unity Studio Classes/UnityStudio.cs b/Unity Studio/Unity Studio Classes/UnityStudio.cs index d8f8378..3b3a820 100644 --- a/Unity Studio/Unity Studio Classes/UnityStudio.cs +++ b/Unity Studio/Unity Studio Classes/UnityStudio.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Drawing; using System.Linq; using System.Text; using System.IO; @@ -22,6 +23,7 @@ namespace Unity_Studio public static List exportableAssets = new List(); //used to hold all assets while the ListView is filtered private static HashSet exportableAssetsHash = new HashSet(); //avoid the same name asset public static List visibleAssets = new List(); //used to build the ListView from all or filtered assets + private static Dictionary spriteCache = new Dictionary(); public static string productName = ""; public static string mainPath = ""; @@ -303,12 +305,18 @@ namespace Unity_Studio exportable = true; break; } - case 21: //Material + case 213: //Sprite + { + var m_Sprite = new Sprite(asset, false); + exportable = true; + break; + } + /*case 21: //Material case 74: //AnimationClip case 90: //Avatar case 91: //AnimatorController case 115: //MonoScript - case 213: //Sprite + case 687078895: //SpriteAtlas { if (asset.Offset + 4 > asset.sourceFile.a_Stream.BaseStream.Length) break; @@ -320,7 +328,7 @@ namespace Unity_Studio asset.Text = Encoding.UTF8.GetString(bytes); } break; - } + }*/ } if (!exportable && displayAll) { @@ -1823,6 +1831,20 @@ namespace Unity_Studio return true; } + public static bool ExportSprite(AssetPreloadData asset, string exportPath) + { + var exportFullName = exportPath + asset.Text + asset.extension; + if (ExportFileExists(exportFullName)) + return false; + var bitmap = GetImageFromSprite(asset); + if (bitmap != null) + { + bitmap.Save(exportFullName, ImageFormat.Png); + return true; + } + return false; + } + public static bool ExportRawFile(AssetPreloadData asset, string exportPath) { var exportFullName = exportPath + asset.Text + asset.extension; @@ -1884,5 +1906,61 @@ namespace Unity_Studio } return selectFile.Distinct().ToArray(); } + + //TODO Tight方式的Sprite需要读取多边形信息进行绘图 + public static Bitmap GetImageFromSprite(AssetPreloadData asset) + { + if (spriteCache.TryGetValue(asset, out var bitmap)) + return (Bitmap)bitmap.Clone(); + var m_Sprite = new Sprite(asset, true); + if (m_Sprite.m_SpriteAtlas != null && assetsfileList.TryGetPD(m_Sprite.m_SpriteAtlas, out var assetPreloadData)) + { + var m_SpriteAtlas = new SpriteAtlas(assetPreloadData); + bool find = false; + int index = 0; + for (; index < m_SpriteAtlas.m_PackedSprites.Count; index++) + { + if (assetsfileList.TryGetPD(m_SpriteAtlas.m_PackedSprites[index], out assetPreloadData) && assetPreloadData == asset) + { + find = true; + break; + } + } + if (find && assetsfileList.TryGetPD(m_SpriteAtlas.textures[index], out assetPreloadData)) + { + return CutImage(asset, assetPreloadData, m_SpriteAtlas.textureRects[index]); + } + } + else + { + if (assetsfileList.TryGetPD(m_Sprite.texture, out assetPreloadData)) + { + return CutImage(asset, assetPreloadData, m_Sprite.textureRect); + } + } + + return null; + } + + private static Bitmap CutImage(AssetPreloadData asset, AssetPreloadData texture2DAsset, RectangleF textureRect) + { + var texture2D = new Texture2D(texture2DAsset, true); + using (var originalImage = texture2D.ConvertToBitmap(false)) + { + if (originalImage != null) + { + var info = texture2DAsset.InfoText; + var start = info.IndexOf("Format"); + info = info.Substring(start, info.Length - start); + asset.InfoText = $"Width: {textureRect.Width}\nHeight: {textureRect.Height}\n" + info; + var spriteImage = originalImage.Clone(textureRect, PixelFormat.Format32bppArgb); + spriteImage.RotateFlip(RotateFlipType.RotateNoneFlipY); + spriteCache.Add(asset, spriteImage); + return (Bitmap)spriteImage.Clone(); + } + } + + return null; + } } } diff --git a/Unity Studio/Unity Studio-x86.csproj b/Unity Studio/Unity Studio-x86.csproj index a2b4ddd..457d216 100644 --- a/Unity Studio/Unity Studio-x86.csproj +++ b/Unity Studio/Unity Studio-x86.csproj @@ -143,6 +143,8 @@ + + diff --git a/Unity Studio/Unity Studio.csproj b/Unity Studio/Unity Studio.csproj index 05cf86e..992fdef 100644 --- a/Unity Studio/Unity Studio.csproj +++ b/Unity Studio/Unity Studio.csproj @@ -143,6 +143,8 @@ + + diff --git a/Unity Studio/UnityStudioForm.cs b/Unity Studio/UnityStudioForm.cs index d67d6b1..bdab4d2 100644 --- a/Unity Studio/UnityStudioForm.cs +++ b/Unity Studio/UnityStudioForm.cs @@ -424,6 +424,7 @@ namespace Unity_Studio switch (lastLoadedAsset.Type2) { case 28: + case 213: { if (enablePreview.Checked && imageTexture != null) { @@ -737,14 +738,16 @@ namespace Unity_Studio if (e.IsSelected) { - assetInfoLabel.Text = lastSelectedItem.InfoText; - if (displayInfo.Checked && assetInfoLabel.Text != null) { assetInfoLabel.Visible = true; } //only display the label if asset has info text - if (enablePreview.Checked) { lastLoadedAsset = lastSelectedItem; PreviewAsset(lastLoadedAsset); } + if (displayInfo.Checked && assetInfoLabel.Text != null)//only display the label if asset has info text + { + assetInfoLabel.Text = lastSelectedItem.InfoText; + assetInfoLabel.Visible = true; + } } } @@ -1049,14 +1052,34 @@ namespace Unity_Studio } break; #endregion - #region VideoClip - case 329: + #region VideoClip and MovieTexture + case 329: //VideoClip case 152: //MovieTexture { StatusStripUpdate("Only supported export."); break; } #endregion + #region Sprite + case 213: //Sprite + { + imageTexture?.Dispose(); + imageTexture = GetImageFromSprite(asset); + 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 + { + StatusStripUpdate("Unsupported sprite for preview"); + } + break; + } + #endregion default: { var str = asset.ViewStruct(); @@ -1545,6 +1568,12 @@ namespace Unity_Studio exportedCount++; } break; + case 213: //Sprite + if (ExportSprite(asset, exportpath)) + { + exportedCount++; + } + break; default: if (ExportRawFile(asset, exportpath)) {