AssetStudio/AssetStudio/StudioClasses/AssetsFile.cs

332 lines
13 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
namespace AssetStudio
{
public class AssetsFile
{
public EndianBinaryReader reader;
public string filePath;
public string parentPath;
public string fileName;
public string upperFileName;
public int[] version = { 0, 0, 0, 0 };
public string[] buildType;
public string platformStr;
public bool valid;
public Dictionary<long, ObjectReader> ObjectReaders = new Dictionary<long, ObjectReader>();
public Dictionary<long, GameObject> GameObjects = new Dictionary<long, GameObject>();
public Dictionary<long, Transform> Transforms = new Dictionary<long, Transform>();
//class SerializedFile
public SerializedFileHeader header;
private EndianType m_FileEndianess;
public string unityVersion = "2.5.0f5";
public BuildTarget m_TargetPlatform = BuildTarget.UnknownPlatform;
private bool m_EnableTypeTree = true;
public List<SerializedType> m_Types;
public Dictionary<long, ObjectInfo> m_Objects;
private List<LocalSerializedObjectIdentifier> m_ScriptTypes;
public List<FileIdentifier> m_Externals;
public AssetsFile(string fullName, EndianBinaryReader reader)
{
this.reader = reader;
filePath = fullName;
fileName = Path.GetFileName(fullName);
upperFileName = fileName.ToUpper();
try
{
//SerializedFile::ReadHeader
header = new SerializedFileHeader();
header.m_MetadataSize = reader.ReadUInt32();
header.m_FileSize = reader.ReadUInt32();
header.m_Version = reader.ReadUInt32();
header.m_DataOffset = reader.ReadUInt32();
if (header.m_Version >= 9)
{
header.m_Endianess = reader.ReadByte();
header.m_Reserved = reader.ReadBytes(3);
m_FileEndianess = (EndianType)header.m_Endianess;
}
else
{
reader.Position = header.m_FileSize - header.m_MetadataSize;
m_FileEndianess = (EndianType)reader.ReadByte();
}
//SerializedFile::ReadMetadata
if (m_FileEndianess == EndianType.LittleEndian)
{
reader.endian = EndianType.LittleEndian;
}
if (header.m_Version >= 7)
{
unityVersion = reader.ReadStringToNull();
}
if (header.m_Version >= 8)
{
m_TargetPlatform = (BuildTarget)reader.ReadInt32();
if (!Enum.IsDefined(typeof(BuildTarget), m_TargetPlatform))
{
m_TargetPlatform = BuildTarget.UnknownPlatform;
}
}
platformStr = m_TargetPlatform.ToString();
if (header.m_Version >= 13)
{
m_EnableTypeTree = reader.ReadBoolean();
}
//Read types
int typeCount = reader.ReadInt32();
m_Types = new List<SerializedType>(typeCount);
for (int i = 0; i < typeCount; i++)
{
m_Types.Add(ReadSerializedType());
}
if (header.m_Version >= 7 && header.m_Version < 14)
{
var bigIDEnabled = reader.ReadInt32();
}
//Read Objects
int objectCount = reader.ReadInt32();
m_Objects = new Dictionary<long, ObjectInfo>(objectCount);
for (int i = 0; i < objectCount; i++)
{
var objectInfo = new ObjectInfo();
if (header.m_Version < 14)
{
objectInfo.m_PathID = reader.ReadInt32();
}
else
{
reader.AlignStream(4);
objectInfo.m_PathID = reader.ReadInt64();
}
objectInfo.byteStart = reader.ReadUInt32();
objectInfo.byteStart += header.m_DataOffset;
objectInfo.byteSize = reader.ReadUInt32();
objectInfo.typeID = reader.ReadInt32();
if (header.m_Version < 16)
{
objectInfo.classID = reader.ReadUInt16();
objectInfo.serializedType = m_Types.Find(x => x.classID == objectInfo.typeID);
objectInfo.isDestroyed = reader.ReadUInt16();
}
else
{
var type = m_Types[objectInfo.typeID];
objectInfo.serializedType = type;
objectInfo.classID = type.classID;
}
if (header.m_Version == 15 || header.m_Version == 16)
{
var stripped = reader.ReadByte();
}
m_Objects.Add(objectInfo.m_PathID, objectInfo);
//Create Reader
var objectReader = new ObjectReader(reader, this, objectInfo);
ObjectReaders.Add(objectInfo.m_PathID, objectReader);
#region read BuildSettings to get version for version 2.x files
if (objectReader.type == ClassIDType.BuildSettings && header.m_Version == 6)
{
var nextAsset = reader.Position;
var buildSettings = new BuildSettings(objectReader);
unityVersion = buildSettings.m_Version;
reader.Position = nextAsset;
}
#endregion
}
if (header.m_Version >= 11)
{
int scriptCount = reader.ReadInt32();
m_ScriptTypes = new List<LocalSerializedObjectIdentifier>(scriptCount);
for (int i = 0; i < scriptCount; i++)
{
var m_ScriptType = new LocalSerializedObjectIdentifier();
m_ScriptType.localSerializedFileIndex = reader.ReadInt32();
if (header.m_Version < 14)
{
m_ScriptType.localIdentifierInFile = reader.ReadInt32();
}
else
{
reader.AlignStream(4);
m_ScriptType.localIdentifierInFile = reader.ReadInt64();
}
m_ScriptTypes.Add(m_ScriptType);
}
}
int externalsCount = reader.ReadInt32();
m_Externals = new List<FileIdentifier>(externalsCount);
for (int i = 0; i < externalsCount; i++)
{
var m_External = new FileIdentifier();
if (header.m_Version >= 6)
{
var tempEmpty = reader.ReadStringToNull();
}
if (header.m_Version >= 5)
{
m_External.guid = new Guid(reader.ReadBytes(16));
m_External.type = reader.ReadInt32();
}
m_External.pathName = reader.ReadStringToNull();
m_External.fileName = Path.GetFileName(m_External.pathName);
m_Externals.Add(m_External);
}
if (header.m_Version >= 5)
{
//var userInformation = reader.ReadStringToNull();
}
buildType = Regex.Replace(unityVersion, @"\d", "").Split(new[] { "." }, StringSplitOptions.RemoveEmptyEntries);
var versionSplit = Regex.Replace(unityVersion, @"\D", ".").Split(new[] { "." }, StringSplitOptions.RemoveEmptyEntries);
version = versionSplit.Select(int.Parse).ToArray();
valid = true;
}
catch
{
// ignored
}
}
private SerializedType ReadSerializedType()
{
var type = new SerializedType();
type.classID = reader.ReadInt32();
if (header.m_Version >= 16)
{
type.m_IsStrippedType = reader.ReadBoolean();
}
if (header.m_Version >= 17)
{
type.m_ScriptTypeIndex = reader.ReadInt16();
}
if (header.m_Version >= 13)
{
if ((header.m_Version < 16 && type.classID < 0) || (header.m_Version >= 16 && type.classID == 114))
{
type.m_ScriptID = reader.ReadBytes(16); //Hash128
}
type.m_OldTypeHash = reader.ReadBytes(16); //Hash128
}
if (m_EnableTypeTree)
{
var typeTree = new List<TypeTreeNode>();
if (header.m_Version >= 12 || header.m_Version == 10)
{
ReadTypeTree5(typeTree);
}
else
{
ReadTypeTree(typeTree);
}
type.m_Nodes = typeTree;
}
return type;
}
private void ReadTypeTree(List<TypeTreeNode> typeTree, int depth = 0)
{
var typeTreeNode = new TypeTreeNode();
typeTree.Add(typeTreeNode);
typeTreeNode.m_Level = depth;
typeTreeNode.m_Type = reader.ReadStringToNull();
typeTreeNode.m_Name = reader.ReadStringToNull();
typeTreeNode.m_ByteSize = reader.ReadInt32();
if (header.m_Version == 2)
{
var variableCount = reader.ReadInt32();
}
if (header.m_Version != 3)
{
typeTreeNode.m_Index = reader.ReadInt32();
}
typeTreeNode.m_IsArray = reader.ReadInt32();
typeTreeNode.m_Version = reader.ReadInt32();
if (header.m_Version != 3)
{
typeTreeNode.m_MetaFlag = reader.ReadInt32();
}
int childrenCount = reader.ReadInt32();
for (int i = 0; i < childrenCount; i++)
{
ReadTypeTree(typeTree, depth + 1);
}
}
private void ReadTypeTree5(List<TypeTreeNode> typeTree)
{
int numberOfNodes = reader.ReadInt32();
int stringBufferSize = reader.ReadInt32();
reader.Position += numberOfNodes * 24;
using (var stringBufferReader = new BinaryReader(new MemoryStream(reader.ReadBytes(stringBufferSize))))
{
reader.Position -= numberOfNodes * 24 + stringBufferSize;
for (int i = 0; i < numberOfNodes; i++)
{
var typeTreeNode = new TypeTreeNode();
typeTree.Add(typeTreeNode);
typeTreeNode.m_Version = reader.ReadUInt16();
typeTreeNode.m_Level = reader.ReadByte();
typeTreeNode.m_IsArray = reader.ReadBoolean() ? 1 : 0;
var m_TypeStrOffset = reader.ReadUInt16();
var temp = reader.ReadUInt16();
if (temp == 0)
{
stringBufferReader.BaseStream.Position = m_TypeStrOffset;
typeTreeNode.m_Type = stringBufferReader.ReadStringToNull();
}
else
{
typeTreeNode.m_Type = CommonString.StringBuffer.ContainsKey(m_TypeStrOffset) ? CommonString.StringBuffer[m_TypeStrOffset] : m_TypeStrOffset.ToString();
}
var m_NameStrOffset = reader.ReadUInt16();
temp = reader.ReadUInt16();
if (temp == 0)
{
stringBufferReader.BaseStream.Position = m_NameStrOffset;
typeTreeNode.m_Name = stringBufferReader.ReadStringToNull();
}
else
{
typeTreeNode.m_Name = CommonString.StringBuffer.ContainsKey(m_NameStrOffset) ? CommonString.StringBuffer[m_NameStrOffset] : m_NameStrOffset.ToString();
}
typeTreeNode.m_ByteSize = reader.ReadInt32();
typeTreeNode.m_Index = reader.ReadInt32();
typeTreeNode.m_MetaFlag = reader.ReadInt32();
}
reader.Position += stringBufferSize;
}
}
}
}