(UnityFS): deserialize textures with "StreamingInfo".

This commit is contained in:
morkt 2018-10-03 18:02:14 +04:00
parent d6f00029fd
commit 740a604c50
4 changed files with 125 additions and 7 deletions

View File

@ -99,7 +99,7 @@ namespace GameRes.Formats.Unity
using (var input = new BinMemoryStream (index_data))
index.Parse (input);
var dir = index.LoadObjects();
return new UnityBundle (file, this, dir, index.Segments);
return new UnityBundle (file, this, dir, index.Segments, index.Bundles);
}
public override Stream OpenEntry (ArcFile arc, Entry entry)
@ -126,12 +126,37 @@ namespace GameRes.Formats.Unity
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 bundles = new BundleStream (uarc.File, uarc.Segments);
var input = new StreamRegion (bundles, obj.Offset, obj.Size);
var reader = new AssetReader (input, entry.Name);
reader.SetupReaders (obj.Asset);
var tex = new Texture2D();
Texture2D tex = null;
if (obj.Asset.Tree.TypeTrees.ContainsKey (obj.TypeId))
{
var type = obj.Asset.Tree.TypeTrees[obj.TypeId];
if (type.Children.Any (t => t.Type == "StreamingInfo"))
{
var fields = obj.Deserialize (reader);
tex = new Texture2D();
tex.Import (fields);
var info = fields["m_StreamData"] as StreamingInfo;
if (info != null)
{
var bundle = uarc.Bundles.FirstOrDefault (b => VFS.IsPathEqualsToFileName (info.Path, b.Name));
if (bundle != null)
{
tex.m_DataLength = (int)info.Size;
input = new StreamRegion (bundles, bundle.Offset+info.Offset, info.Size);
reader = new AssetReader (input, entry.Name);
}
}
}
}
if (null == tex)
{
tex = new Texture2D();
tex.Load (reader);
}
return new Texture2DDecoder (tex, reader);
}
@ -166,11 +191,13 @@ namespace GameRes.Formats.Unity
internal class UnityBundle : ArcFile
{
public readonly List<BundleSegment> Segments;
public readonly List<BundleEntry> Bundles;
public UnityBundle (ArcView arc, ArchiveFormat impl, ICollection<Entry> dir, List<BundleSegment> segments)
public UnityBundle (ArcView arc, ArchiveFormat impl, ICollection<Entry> dir, List<BundleSegment> segments, List<BundleEntry> bundles)
: base (arc, impl, dir)
{
Segments = segments;
Bundles = bundles;
}
}
@ -193,6 +220,7 @@ namespace GameRes.Formats.Unity
List<BundleEntry> m_bundles;
public List<BundleSegment> Segments { get { return m_segments; } }
public List<BundleEntry> Bundles { get { return m_bundles; } }
public AssetDeserializer (ArcView file, long data_offset)
{
@ -239,8 +267,10 @@ namespace GameRes.Formats.Unity
{
foreach (BundleEntry bundle in m_bundles)
{
if (bundle.Name.EndsWith (".resource"))
if (bundle.Name.HasAnyOfExtensions (".resource", ".resS"))
continue;
var res_s_name = bundle.Name + ".resS";
var res_s = m_bundles.FirstOrDefault (b => b.Name == res_s_name);
using (var asset_stream = new StreamRegion (stream, bundle.Offset, bundle.Size, true))
using (var reader = new AssetReader (asset_stream, bundle.Name))
{

View File

@ -28,9 +28,11 @@
//
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using GameRes.Utility;
@ -209,6 +211,71 @@ namespace GameRes.Formats.Unity
{
return string.Format ("<{0} {1}>", Type, ClassId);
}
public IDictionary Deserialize (AssetReader input)
{
var type_tree = Asset.Tree.TypeTrees;
if (!type_tree.ContainsKey (TypeId))
return null;
var type_map = new Hashtable();
var type = type_tree[TypeId];
foreach (var node in type.Children)
{
type_map[node.Name] = DeserializeType (input, node);
}
return type_map;
}
object DeserializeType (AssetReader input, TypeTree node)
{
object obj = null;
if (node.IsArray)
{
int size = input.ReadInt32();
var data_field = node.Children.FirstOrDefault (n => n.Name == "data");
if (data_field != null)
{
if ("TypelessData" == node.Type)
obj = input.ReadBytes (size * data_field.Size);
else
obj = DeserializeArray (input, size, data_field);
}
}
else if (node.Size < 0)
{
if (node.Type == "string")
{
obj = input.ReadString();
if (node.Children[0].IsAligned)
input.Align();
}
else if (node.Type == "StreamingInfo")
{
var info = new StreamingInfo();
info.Load (input);
obj = info;
}
else
throw new NotImplementedException ("Unknown class encountered in asset deserialzation.");
}
else if ("int" == node.Type)
obj = input.ReadInt32();
else if ("bool" == node.Type)
obj = input.ReadBool();
else
input.Position += node.Size;
if (node.IsAligned)
input.Align();
return obj;
}
object[] DeserializeArray (AssetReader input, int length, TypeTree elem)
{
var array = new object[length];
for (int i = 0; i < length; ++i)
array[i] = DeserializeType (input, elem);
return array;
}
}
internal class TypeTree
@ -226,6 +293,8 @@ namespace GameRes.Formats.Unity
public IList<TypeTree> Children { get { return m_children; } }
public bool IsAligned { get { return (Flags & 0x4000) != 0; } }
static readonly string Null = "(null)";
static readonly Lazy<byte[]> StringsDat = new Lazy<byte[]> (() => LoadResource ("strings.dat"));

View File

@ -39,6 +39,8 @@ namespace GameRes.Formats.Unity
IBinaryStream m_input;
int m_format;
const int MaxStringLength = 0x100000;
public Stream Source { get { return m_input.AsStream; } }
public int Format { get { return m_format; } }
public long Position {
@ -144,6 +146,8 @@ namespace GameRes.Formats.Unity
int length = ReadInt32();
if (0 == length)
return string.Empty;
if (length < 0 || length > MaxStringLength)
throw new InvalidFormatException();
var bytes = ReadBytes (length);
return Encoding.UTF8.GetString (bytes);
}

View File

@ -24,6 +24,7 @@
//
using System;
using System.Collections;
using System.IO;
using System.Windows.Media;
using GameRes.Formats.DirectDraw;
@ -100,6 +101,20 @@ namespace GameRes.Formats.Unity
{
m_Data = reader.ReadBytes (m_DataLength);
}
public void Import (IDictionary fields)
{
m_Name = fields["m_Name"] as string ?? "";
m_Width = (int)(fields["m_Width"] ?? 0);
m_Height = (int)(fields["m_Height"] ?? 0);
m_CompleteImageSize = (int)(fields["m_CompleteImageSize"] ?? 0);
m_TextureFormat = (TextureFormat)(fields["m_TextureFormat"] ?? 0);
m_MipCount = (int)(fields["m_MipCount"] ?? 0);
m_ImageCount = (int)(fields["m_ImageCount"] ?? 0);
m_TextureDimension = (int)(fields["m_TextureDimension"] ?? 0);
m_IsReadable = (bool)(fields["m_IsReadable"] ?? false);
m_Data = fields["image data"] as byte[] ?? Array.Empty<byte>();
}
}
internal class Texture2DDecoder : IImageDecoder