(UnityFS): implemented encrypted entries.

This commit is contained in:
morkt 2017-04-13 08:47:03 +04:00
parent 3203e48d83
commit 12c2690ae3
2 changed files with 151 additions and 10 deletions

View File

@ -105,14 +105,34 @@ namespace GameRes.Formats.Unity
public override Stream OpenEntry (ArcFile arc, Entry entry)
{
var uarc = (UnityBundle)arc;
var input = new BundleStream (uarc.File, uarc.Segments);
return new StreamRegion (input, entry.Offset, entry.Size);
Stream input = new BundleStream (uarc.File, uarc.Segments);
input = new StreamRegion (input, entry.Offset, entry.Size);
var aent = entry as AssetEntry;
if (null == aent || !aent.IsEncrypted)
return input;
using (input)
{
var data = new byte[entry.Size];
input.Read (data, 0, data.Length);
DecryptAsset (data);
return new BinMemoryStream (data);
}
}
internal static byte[] UnpackLzma (byte[] input, int unpacked_size)
{
throw new NotImplementedException();
}
internal void DecryptAsset (byte[] data)
{
uint key = 0xBF8766F5u;
for (int i = 0; i < data.Length; ++i)
{
key = ((0x343FD * key + 0x269EC3) >> 16) & 0x7FFF; // MSVC rand()
data[i] ^= (byte)key;
}
}
}
internal class BundleEntry : Entry
@ -124,6 +144,7 @@ namespace GameRes.Formats.Unity
{
public BundleEntry Bundle;
public UnityObject AssetObject;
public bool IsEncrypted;
}
internal class UnityBundle : ArcFile
@ -209,7 +230,7 @@ namespace GameRes.Formats.Unity
{
var asset = new Asset();
asset.Load (reader);
var object_dir = ParseAsset (bundle, asset, stream);
var object_dir = ParseAsset (stream, bundle, asset);
dir.AddRange (object_dir);
}
}
@ -219,7 +240,7 @@ namespace GameRes.Formats.Unity
return dir;
}
IEnumerable<Entry> ParseAsset (BundleEntry bundle, Asset asset, Stream file)
IEnumerable<Entry> ParseAsset (Stream file, BundleEntry bundle, Asset asset)
{
Dictionary<long, string> id_map = null;
var bundle_types = asset.Tree.TypeTrees.Where (t => t.Value.Type == "AssetBundle").Select (t => t.Key);
@ -241,7 +262,11 @@ namespace GameRes.Formats.Unity
AssetEntry entry = null;
if ("AudioClip" == type)
{
entry = ReadAudioClip (file, obj, asset);
entry = ReadAudioClip (file, obj);
}
else if ("TextAsset" == type)
{
entry = ReadTextAsset (file, obj);
}
if (null == entry)
{
@ -253,9 +278,11 @@ namespace GameRes.Formats.Unity
Size = obj.Size,
};
}
if (null == entry.Bundle)
entry.Bundle = bundle;
string name;
if (!id_map.TryGetValue (obj.PathId, out name))
name = obj.PathId.ToString ("X16");
name = GetObjectName (file, obj);
else
name = ShortenPath (name);
entry.Name = name;
@ -263,12 +290,56 @@ namespace GameRes.Formats.Unity
}
}
AssetEntry ReadAudioClip (Stream input, UnityObject obj, Asset asset)
string GetObjectName (Stream input, UnityObject obj)
{
using (var stream = new StreamRegion (input, obj.Offset, obj.Size, true))
using (var reader = new AssetReader (stream, ""))
TypeTree type;
if (obj.Asset.Tree.TypeTrees.TryGetValue (obj.TypeId, out type) && type.Children.Count > 0)
{
var first_field = type.Children[0];
if ("m_Name" == first_field.Name && "string" == first_field.Type)
{
using (var reader = obj.Open (input))
{
var name = reader.ReadString();
if (!string.IsNullOrEmpty (name))
return name;
}
}
}
return obj.PathId.ToString ("X16");
}
AssetEntry ReadTextAsset (Stream input, UnityObject obj)
{
var type_def = obj.Asset.Tree.TypeTrees[obj.TypeId];
var script = type_def.Children.FirstOrDefault (f => f.Name == "m_Script");
if (null == script)
return null;
using (var reader = obj.Open (input))
{
var name = reader.ReadString();
reader.Align();
uint size = reader.ReadUInt32();
var entry = new AssetEntry {
AssetObject = obj,
Offset = obj.Offset + reader.Position,
Size = size,
IsEncrypted = 0 != (script.Flags & 0x04000000),
};
if (entry.IsEncrypted)
{
uint signature = reader.ReadUInt32();
if (0x0D15F641 == signature)
entry.Type = "image";
}
return entry;
}
}
AssetEntry ReadAudioClip (Stream input, UnityObject obj)
{
using (var reader = obj.Open (input))
{
reader.SetupReaders (asset.Format, asset.IsLittleEndian);
var clip = new AudioClip();
clip.Load (reader);
var bundle_name = Path.GetFileName (clip.m_Source);

View File

@ -198,6 +198,14 @@ namespace GameRes.Formats.Unity
Asset = owner;
}
public AssetReader Open (Stream input)
{
var stream = new StreamRegion (input, Offset, Size, true);
var reader = new AssetReader (stream, "");
reader.SetupReaders (Asset.Format, Asset.IsLittleEndian);
return reader;
}
public void Load (AssetReader reader)
{
PathId = reader.ReadId();
@ -451,4 +459,66 @@ 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();
}
}
}