From 0cb17833632599a939d0c0028edabd068038f4a4 Mon Sep 17 00:00:00 2001 From: morkt Date: Fri, 14 Apr 2017 09:43:46 +0400 Subject: [PATCH] (Unity): implemented Texture2D images. --- ArcFormats/ArcFormats.csproj | 1 + ArcFormats/Unity/ArcUnityFS.cs | 53 +++++---- ArcFormats/Unity/Asset.cs | 66 +---------- ArcFormats/Unity/AssetReader.cs | 6 + ArcFormats/Unity/Texture2D.cs | 196 ++++++++++++++++++++++++++++++++ 5 files changed, 238 insertions(+), 84 deletions(-) create mode 100644 ArcFormats/Unity/Texture2D.cs diff --git a/ArcFormats/ArcFormats.csproj b/ArcFormats/ArcFormats.csproj index fe1f0c46..f757161c 100644 --- a/ArcFormats/ArcFormats.csproj +++ b/ArcFormats/ArcFormats.csproj @@ -524,6 +524,7 @@ + diff --git a/ArcFormats/Unity/ArcUnityFS.cs b/ArcFormats/Unity/ArcUnityFS.cs index f76d2d0d..33f2778a 100644 --- a/ArcFormats/Unity/ArcUnityFS.cs +++ b/ArcFormats/Unity/ArcUnityFS.cs @@ -119,6 +119,20 @@ namespace GameRes.Formats.Unity } } + public override IImageDecoder OpenImage (ArcFile arc, Entry entry) + { + var aent = entry as AssetEntry; + if (null == aent || aent.AssetObject.Type != "Texture2D") + return base.OpenImage (arc, entry); + var uarc = (UnityBundle)arc; + var obj = aent.AssetObject; + Stream input = new BundleStream (uarc.File, uarc.Segments); + input = new StreamRegion (input, obj.Offset, obj.Size); + var reader = new AssetReader (input, entry.Name); + reader.SetupReaders (obj.Asset); + return new Texture2DDecoder (reader); + } + internal static byte[] UnpackLzma (byte[] input, int unpacked_size) { throw new NotImplementedException(); @@ -258,26 +272,7 @@ namespace GameRes.Formats.Unity id_map = new Dictionary(); foreach (var obj in asset.Objects) { - string type = obj.Type; - AssetEntry entry = null; - if ("AudioClip" == type) - { - entry = ReadAudioClip (file, obj); - } - else if ("TextAsset" == type) - { - entry = ReadTextAsset (file, obj); - } - if (null == entry) - { - entry = new AssetEntry { - Type = type, - Bundle = bundle, - AssetObject = obj, - Offset = obj.Offset, - Size = obj.Size, - }; - } + var entry = ReadAsset (file, obj); if (null == entry.Bundle) entry.Bundle = bundle; string name; @@ -290,6 +285,24 @@ namespace GameRes.Formats.Unity } } + AssetEntry ReadAsset (Stream file, UnityObject obj) + { + string type = obj.Type; + if ("AudioClip" == type) + return ReadAudioClip (file, obj); + else if ("TextAsset" == type) + return ReadTextAsset (file, obj); + else if ("Texture2D" == type) + type = "image"; + + return new AssetEntry { + Type = type, + AssetObject = obj, + Offset = obj.Offset, + Size = obj.Size, + }; + } + Dictionary ReadAssetBundle (Stream input, UnityObject bundle) { using (var reader = bundle.Open (input)) diff --git a/ArcFormats/Unity/Asset.cs b/ArcFormats/Unity/Asset.cs index 268ffd39..8b7d6454 100644 --- a/ArcFormats/Unity/Asset.cs +++ b/ArcFormats/Unity/Asset.cs @@ -64,7 +64,7 @@ namespace GameRes.Formats.Unity m_data_offset = input.ReadUInt32(); if (m_format >= 9) m_is_little_endian = 0 == input.ReadInt32(); - input.SetupReaders (m_format, m_is_little_endian); + input.SetupReaders (this); m_tree.Load (input); bool long_ids = Format >= 14; @@ -171,7 +171,7 @@ namespace GameRes.Formats.Unity { var stream = new StreamRegion (input, Offset, Size, true); var reader = new AssetReader (stream, ""); - reader.SetupReaders (Asset.Format, Asset.IsLittleEndian); + reader.SetupReaders (Asset); return reader; } @@ -428,66 +428,4 @@ namespace GameRes.Formats.Unity m_CompressionFormat = reader.ReadInt32(); } } - - enum TextureFormat : int - { - Alpha8 = 1, - ARGB4444 = 2, - RGB24 = 3, - RGBA32 = 4, - ARGB32 = 5, - R16 = 6, // A 16 bit color texture format that only has a red channel. - RGB565 = 7, - DXT1 = 10, - DXT5 = 12, - RGBA4444 = 13, - BGRA32 = 14, - } - - internal class Texture2D - { - public string m_Name; - public int m_Width; - public int m_Height; - public int m_CompleteImageSize; - public TextureFormat m_TextureFormat; - public int m_MipCount; - public bool m_IsReadable; - public bool m_ReadAllowed; - public int m_ImageCount; - public int m_TextureDimension; - public int m_FilterMode; - public int m_Aniso; - public int m_MipBias; - public int m_WrapMode; - public int m_LightFormat; - public int m_ColorSpace; - // byte[] m_Data - // StreamingInfo m_StreamData - // uint offset - // uint size - // string path - - public void Load (AssetReader reader) - { - m_Name = reader.ReadString(); - reader.Align(); - m_Width = reader.ReadInt32(); - m_Height = reader.ReadInt32(); - m_CompleteImageSize = reader.ReadInt32(); - m_TextureFormat = (TextureFormat)reader.ReadInt32(); - m_MipCount = reader.ReadInt32(); - m_IsReadable = reader.ReadBool(); - m_ReadAllowed = reader.ReadBool(); - reader.Align(); - m_ImageCount = reader.ReadInt32(); - m_TextureDimension = reader.ReadInt32(); - m_FilterMode = reader.ReadInt32(); - m_Aniso = reader.ReadInt32(); - m_MipBias = reader.ReadInt32(); - m_WrapMode = reader.ReadInt32(); - m_LightFormat = reader.ReadInt32(); - m_ColorSpace = reader.ReadInt32(); - } - } } diff --git a/ArcFormats/Unity/AssetReader.cs b/ArcFormats/Unity/AssetReader.cs index 09a75624..3da70b6f 100644 --- a/ArcFormats/Unity/AssetReader.cs +++ b/ArcFormats/Unity/AssetReader.cs @@ -39,6 +39,7 @@ namespace GameRes.Formats.Unity IBinaryStream m_input; int m_format; + public Stream Source { get { return m_input.AsStream; } } public int Format { get { return m_format; } } public long Position { get { return m_input.Position; } @@ -59,6 +60,11 @@ namespace GameRes.Formats.Unity public Func ReadInt64; public Func ReadId; + public void SetupReaders (Asset asset) + { + SetupReaders (asset.Format, asset.IsLittleEndian); + } + /// /// Setup reader endianness accordingly. /// diff --git a/ArcFormats/Unity/Texture2D.cs b/ArcFormats/Unity/Texture2D.cs new file mode 100644 index 00000000..323761c7 --- /dev/null +++ b/ArcFormats/Unity/Texture2D.cs @@ -0,0 +1,196 @@ +//! \file Texture2D.cs +//! \date Fri Apr 14 08:20:08 2017 +//! \brief Unity engine texture deserialzer. +// +// Copyright (C) 2017 by morkt +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +// + +using System; +using System.IO; +using System.Windows.Media; +using GameRes.Formats.DirectDraw; + +namespace GameRes.Formats.Unity +{ + enum TextureFormat : int + { + Alpha8 = 1, + ARGB4444 = 2, + RGB24 = 3, + RGBA32 = 4, + ARGB32 = 5, + R16 = 6, // A 16 bit color texture format that only has a red channel. + RGB565 = 7, + DXT1 = 10, + DXT5 = 12, + RGBA4444 = 13, + BGRA32 = 14, + } + + internal class Texture2D + { + public string m_Name; + public int m_Width; + public int m_Height; + public int m_CompleteImageSize; + public TextureFormat m_TextureFormat; + public int m_MipCount; + public bool m_IsReadable; + public bool m_ReadAllowed; + public int m_ImageCount; + public int m_TextureDimension; + public int m_FilterMode; + public int m_Aniso; + public int m_MipBias; + public int m_WrapMode; + public int m_LightFormat; + public int m_ColorSpace; + public byte[] m_Data; + // StreamingInfo m_StreamData + // uint offset + // uint size + // string path + + public void Load (AssetReader reader) + { + m_Name = reader.ReadString(); + reader.Align(); + m_Width = reader.ReadInt32(); + m_Height = reader.ReadInt32(); + m_CompleteImageSize = reader.ReadInt32(); + m_TextureFormat = (TextureFormat)reader.ReadInt32(); + m_MipCount = reader.ReadInt32(); + m_IsReadable = reader.ReadBool(); + m_ReadAllowed = reader.ReadBool(); + reader.Align(); + m_ImageCount = reader.ReadInt32(); + m_TextureDimension = reader.ReadInt32(); + m_FilterMode = reader.ReadInt32(); + m_Aniso = reader.ReadInt32(); + m_MipBias = reader.ReadInt32(); + m_WrapMode = reader.ReadInt32(); + m_LightFormat = reader.ReadInt32(); + m_ColorSpace = reader.ReadInt32(); + int length = reader.ReadInt32(); + m_Data = reader.ReadBytes (length); + } + } + + internal class Texture2DDecoder : IImageDecoder + { + AssetReader m_reader; + Texture2D m_texture; + ImageData m_image; + + public Stream Source { get { return m_reader.Source; } } + public ImageFormat SourceFormat { get { return null; } } + public PixelFormat Format { get; private set; } + public ImageMetaData Info { get; private set; } + public ImageData Image { + get { + if (null == m_image) + { + m_image = Unpack(); + } + return m_image; + } + } + + public Texture2DDecoder (AssetReader input) + { + m_reader = input; + m_texture = new Texture2D(); + m_texture.Load (m_reader); + Info = new ImageMetaData { + Width = (uint)m_texture.m_Width, + Height = (uint)m_texture.m_Height, + }; + SetFormat (m_texture.m_TextureFormat); + m_reader.Position = 0; + } + + void SetFormat (TextureFormat format) + { + switch (format) + { + case TextureFormat.Alpha8: + Format = PixelFormats.Gray8; + Info.BPP = 8; + break; + + case TextureFormat.R16: + Format = PixelFormats.Gray16; + Info.BPP = 16; + break; + + case TextureFormat.RGB24: + Format = PixelFormats.Rgb24; + Info.BPP = 24; + break; + + case TextureFormat.RGB565: + Format = PixelFormats.Bgr565; + Info.BPP = 16; + break; + + default: + Format = PixelFormats.Bgra32; + Info.BPP = 32; + break; + } + } + + ImageData Unpack () + { + byte[] pixels; + switch (m_texture.m_TextureFormat) + { + case TextureFormat.DXT5: + { + var decoder = new DxtDecoder (m_texture.m_Data, Info); + pixels = decoder.UnpackDXT5(); + break; + } + case TextureFormat.Alpha8: + case TextureFormat.R16: + case TextureFormat.RGB24: + case TextureFormat.BGRA32: + case TextureFormat.RGB565: + pixels = m_texture.m_Data; + break; + + default: + throw new NotImplementedException ("Not supported Unity Texture2D format."); + } + return ImageData.CreateFlipped (Info, Format, null, pixels, (int)Info.Width*((Format.BitsPerPixel+7)/8)); + } + + bool m_disposed = false; + public void Dispose () + { + if (!m_disposed) + { + m_reader.Dispose(); + m_disposed = true; + } + } + } +}