mirror of
https://github.com/aelurum/AssetStudio.git
synced 2026-11-14 11:18:47 -05:00
Improve support for Unity 6.7+
- Added support for SerializedFile v26 - "Build Pipeline: Archives built with LZ4 compression now store each compressed chunk at a 16 byte aligned offset and cut chunks on file and serialized object boundaries..."
This commit is contained in:
@@ -394,6 +394,10 @@ namespace AssetStudio
|
||||
compressedSize = blocksInfoReader.ReadUInt32(),
|
||||
flags = (StorageBlockFlags)blocksInfoReader.ReadUInt16()
|
||||
};
|
||||
if (m_Header.version >= 9)
|
||||
{
|
||||
var offset = blocksInfoReader.ReadUInt64();
|
||||
}
|
||||
}
|
||||
|
||||
var nodesCount = blocksInfoReader.ReadInt32();
|
||||
@@ -483,6 +487,11 @@ namespace AssetStudio
|
||||
|
||||
var read = reader.Read(sharedCompressedBuff, 0, compressedSize);
|
||||
debugMsg += $"(read: {read.ToString().ColorIf(read != compressedSize, ColorConsole.BrightRed)})";
|
||||
// https://unity.com/releases/editor/beta/6000.7.0b2#notes
|
||||
if (m_Header.version >= 9)
|
||||
{
|
||||
reader.AlignStream(16);
|
||||
}
|
||||
var compressedSpan = new ReadOnlySpan<byte>(sharedCompressedBuff, 0, compressedSize);
|
||||
var uncompressedSpan = new Span<byte>(sharedUncompressedBuff, 0, uncompressedSize);
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ namespace AssetStudio
|
||||
platform = assetsFile.m_TargetPlatform;
|
||||
m_Version = assetsFile.header.m_Version;
|
||||
|
||||
if (assetsFile.ExtractedTypeTreeHashes.Count > 0)
|
||||
if (assetsFile.IsTypeTreeExtracted)
|
||||
{
|
||||
assetsFile.ReadExtractedTypeTree();
|
||||
}
|
||||
|
||||
+157
-12
@@ -1,4 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
@@ -7,6 +8,8 @@ namespace AssetStudio
|
||||
{
|
||||
public class SerializedFile
|
||||
{
|
||||
private static readonly ConcurrentDictionary<string, SubTreeInfo> SharedSubTreeInfoDict = new ConcurrentDictionary<string, SubTreeInfo>();
|
||||
|
||||
public AssetsManager assetsManager;
|
||||
public FileReader reader;
|
||||
public string fullName;
|
||||
@@ -15,7 +18,8 @@ namespace AssetStudio
|
||||
public UnityVersion version = new UnityVersion();
|
||||
public readonly List<Object> Objects;
|
||||
public readonly Dictionary<long, Object> ObjectsDic;
|
||||
public readonly Dictionary<string, List<SerializedType>> ExtractedTypeTreeHashes = new Dictionary<string, List<SerializedType>>();
|
||||
private readonly Dictionary<string, List<SerializedType>> extractedTypeTreeDict = new Dictionary<string, List<SerializedType>>();
|
||||
public bool IsTypeTreeExtracted => extractedTypeTreeDict.Count > 0;
|
||||
|
||||
public SerializedFileHeader header;
|
||||
private byte m_FileEndianess;
|
||||
@@ -69,6 +73,10 @@ namespace AssetStudio
|
||||
{
|
||||
reader.Endian = EndianType.LittleEndian;
|
||||
}
|
||||
if (header.m_Version >= SerializedFileFormatVersion.NotTested)
|
||||
{
|
||||
Logger.Warning($"Unknown SerializedFile version detected [{(int)header.m_Version}]");
|
||||
}
|
||||
switch (header.m_Version) //default unity versions
|
||||
{
|
||||
case SerializedFileFormatVersion.Unsupported:
|
||||
@@ -241,6 +249,31 @@ namespace AssetStudio
|
||||
}
|
||||
}
|
||||
|
||||
if (header.m_Version >= SerializedFileFormatVersion.SharedSubTreeSupport)
|
||||
{
|
||||
var sharedSubtreeCount = reader.ReadUInt32();
|
||||
for (var i = 0; i < sharedSubtreeCount; i++)
|
||||
{
|
||||
var subTreeHash = reader.ReadBytesAsHexString(16);
|
||||
var subTreeBlobSize = reader.ReadInt32();
|
||||
var subTreeInfo = subTreeBlobSize > 0
|
||||
? ReadSubTreeInfo(reader)
|
||||
: null; //extracted subTree
|
||||
SharedSubTreeInfoDict.TryAdd(subTreeHash, subTreeInfo);
|
||||
}
|
||||
|
||||
foreach (var serializedType in m_Types)
|
||||
{
|
||||
if (serializedType.subTreeHashList == null || serializedType.subTreeHashList.Count <= 0)
|
||||
continue;
|
||||
|
||||
if (!ResolveSubTrees(serializedType.m_Type.m_Nodes, serializedType.subTreeHashList))
|
||||
{
|
||||
serializedType.m_Type = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (header.m_Version >= SerializedFileFormatVersion.Unknown_5)
|
||||
{
|
||||
userInformation = reader.ReadStringToNull();
|
||||
@@ -274,8 +307,48 @@ namespace AssetStudio
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//reader.AlignStream(16);
|
||||
private bool ResolveSubTrees(List<TypeTreeNode> m_Nodes, List<string> subTreeHashes)
|
||||
{
|
||||
try
|
||||
{
|
||||
for (var i = 0; i < m_Nodes.Count; i++)
|
||||
{
|
||||
var m_Node = m_Nodes[i];
|
||||
var isSharedSubTreeNode = (m_Node.m_TypeFlags & 0x20) != 0;
|
||||
if (!isSharedSubTreeNode)
|
||||
continue;
|
||||
|
||||
var hashIndex = (int)m_Node.m_RefTypeHash;
|
||||
if (SharedSubTreeInfoDict.TryGetValue(subTreeHashes[hashIndex], out var subTreeInfo))
|
||||
{
|
||||
if (subTreeInfo.SubTreeDependencies.Count > 0)
|
||||
{
|
||||
ResolveSubTrees(subTreeInfo.SubTreeNodes, subTreeInfo.SubTreeDependencies);
|
||||
}
|
||||
m_Node.m_TypeFlags = subTreeInfo.SubTreeNodes[0].m_TypeFlags;
|
||||
for (var k = 1; k < subTreeInfo.SubTreeNodes.Count; k++) //skip Base node
|
||||
{
|
||||
var subTreeNode = new TypeTreeNode(subTreeInfo.SubTreeNodes[k]); //deep copy
|
||||
subTreeNode.m_Level += m_Node.m_Level;
|
||||
subTreeNode.m_Index = m_Node.m_Index + k;
|
||||
m_Nodes.Insert(i + k, subTreeNode);
|
||||
}
|
||||
i += subTreeInfo.SubTreeNodes.Count - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new IOException($"Can't find a shared subtree for the \"{m_Node.m_Type}\" type");
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Warning($"[SubTree] {e.Message}\n{e.StackTrace}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private SerializedType ReadSerializedType(bool isRefType)
|
||||
@@ -322,13 +395,13 @@ namespace AssetStudio
|
||||
else
|
||||
{
|
||||
type.m_Type = null;
|
||||
if (ExtractedTypeTreeHashes.TryGetValue(typeTreeHash, out var types))
|
||||
if (extractedTypeTreeDict.TryGetValue(typeTreeHash, out var types))
|
||||
{
|
||||
types.Add(type);
|
||||
}
|
||||
else
|
||||
{
|
||||
ExtractedTypeTreeHashes[typeTreeHash] = new List<SerializedType> { type };
|
||||
extractedTypeTreeDict[typeTreeHash] = new List<SerializedType> { type };
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -340,6 +413,15 @@ namespace AssetStudio
|
||||
{
|
||||
ReadTypeTree(type.m_Type);
|
||||
}
|
||||
if (header.m_Version >= SerializedFileFormatVersion.SharedSubTreeSupport && !IsTypeTreeExtracted)
|
||||
{
|
||||
type.subTreeHashList = new List<string>();
|
||||
var sharedSubTreeCount = reader.ReadInt32();
|
||||
for (var i = 0; i < sharedSubTreeCount; i++)
|
||||
{
|
||||
type.subTreeHashList.Add(reader.ReadBytesAsHexString(16));
|
||||
}
|
||||
}
|
||||
if (header.m_Version >= SerializedFileFormatVersion.StoresTypeDependencies)
|
||||
{
|
||||
if (isRefType)
|
||||
@@ -354,7 +436,6 @@ namespace AssetStudio
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
@@ -469,26 +550,36 @@ namespace AssetStudio
|
||||
|
||||
public void ReadExtractedTypeTree()
|
||||
{
|
||||
var hashes = ExtractedTypeTreeHashes.Keys.ToArray();
|
||||
var hashes = extractedTypeTreeDict.Keys.ToArray();
|
||||
foreach (var hashStr in hashes)
|
||||
{
|
||||
if (!assetsManager.ResourceFileReaders.TryGetValue(hashStr, out var binaryReader))
|
||||
if (!assetsManager.ResourceFileReaders.TryGetValue(hashStr, out var binaryReader))
|
||||
continue;
|
||||
|
||||
var fileReader = (FileReader)binaryReader;
|
||||
fileReader.Endian = EndianType.LittleEndian;
|
||||
var serializedTypeList = ExtractedTypeTreeHashes[hashStr];
|
||||
var serializedTypeList = extractedTypeTreeDict[hashStr];
|
||||
foreach (var serializedType in serializedTypeList)
|
||||
{
|
||||
fileReader.Position = 0;
|
||||
serializedType.m_Type = new TypeTree();
|
||||
serializedType.m_Type.m_Nodes = new List<TypeTreeNode>();
|
||||
TypeTreeBlobRead(serializedType.m_Type, fileReader);
|
||||
if (header.m_Version >= SerializedFileFormatVersion.SharedSubTreeSupport)
|
||||
{
|
||||
serializedType.subTreeHashList = new List<string>();
|
||||
var sharedSubTreeCount = fileReader.ReadInt32();
|
||||
for (var i = 0; i < sharedSubTreeCount; i++)
|
||||
{
|
||||
serializedType.subTreeHashList.Add(fileReader.ReadBytesAsHexString(16));
|
||||
}
|
||||
}
|
||||
}
|
||||
ExtractedTypeTreeHashes[hashStr] = null;
|
||||
extractedTypeTreeDict[hashStr] = null;
|
||||
}
|
||||
var notFound = ExtractedTypeTreeHashes.Where(x=> x.Value != null).ToArray();
|
||||
if (notFound.Length > 0 && notFound.Length < ExtractedTypeTreeHashes.Count)
|
||||
|
||||
var notFound = extractedTypeTreeDict.Where(x => x.Value != null).ToArray();
|
||||
if (notFound.Length > 0 && notFound.Length < extractedTypeTreeDict.Count)
|
||||
{
|
||||
foreach (var kv in notFound)
|
||||
{
|
||||
@@ -497,7 +588,56 @@ namespace AssetStudio
|
||||
Logger.Warning("Not all typetree hashes were found in the loaded typetree file\n" +
|
||||
"The typetree file is probably outdated or not designed for the loaded asset bundles");
|
||||
}
|
||||
ExtractedTypeTreeHashes.Clear();
|
||||
if (header.m_Version >= SerializedFileFormatVersion.SharedSubTreeSupport && notFound.Length < extractedTypeTreeDict.Count)
|
||||
{
|
||||
ReadExtractedSubTree();
|
||||
}
|
||||
|
||||
extractedTypeTreeDict.Clear();
|
||||
}
|
||||
|
||||
private void ReadExtractedSubTree()
|
||||
{
|
||||
var hashes = SharedSubTreeInfoDict.Keys.ToArray();
|
||||
foreach (var subTreeHash in hashes)
|
||||
{
|
||||
if (!assetsManager.ResourceFileReaders.TryGetValue(subTreeHash, out var binaryReader))
|
||||
continue;
|
||||
|
||||
var fileReader = (FileReader)binaryReader;
|
||||
fileReader.Endian = EndianType.LittleEndian;
|
||||
fileReader.Position = 0;
|
||||
SharedSubTreeInfoDict[subTreeHash] = ReadSubTreeInfo(fileReader);
|
||||
}
|
||||
foreach (var serializedType in m_Types)
|
||||
{
|
||||
if (serializedType.subTreeHashList == null || serializedType.subTreeHashList.Count <= 0)
|
||||
continue;
|
||||
|
||||
if (!ResolveSubTrees(serializedType.m_Type.m_Nodes, serializedType.subTreeHashList))
|
||||
{
|
||||
serializedType.m_Type = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private SubTreeInfo ReadSubTreeInfo(FileReader subTreeReader)
|
||||
{
|
||||
var m_Type = new TypeTree();
|
||||
m_Type.m_Nodes = new List<TypeTreeNode>();
|
||||
TypeTreeBlobRead(m_Type, subTreeReader);
|
||||
var subTreeDependencies = new List<string>();
|
||||
var subTreeDependenciesCount = subTreeReader.ReadUInt32();
|
||||
for (var i = 0; i < subTreeDependenciesCount; i++)
|
||||
{
|
||||
subTreeDependencies.Add(subTreeReader.ReadBytesAsHexString(16));
|
||||
}
|
||||
|
||||
return new SubTreeInfo
|
||||
{
|
||||
SubTreeNodes = m_Type.m_Nodes,
|
||||
SubTreeDependencies = subTreeDependencies,
|
||||
};
|
||||
}
|
||||
|
||||
public void AddObject(Object obj)
|
||||
@@ -505,5 +645,10 @@ namespace AssetStudio
|
||||
Objects.Add(obj);
|
||||
ObjectsDic.Add(obj.m_PathID, obj);
|
||||
}
|
||||
|
||||
public static void Clear()
|
||||
{
|
||||
SharedSubTreeInfoDict.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,16 +78,23 @@
|
||||
/// </summary>
|
||||
LargeFilesSupport = 22,
|
||||
/// <summary>
|
||||
/// 6000.5.0a5 to x
|
||||
/// 6000.5.0a5 to 6000.6
|
||||
/// </summary>
|
||||
ExtractedTypeTreeSupport = 23,
|
||||
/// <summary>
|
||||
/// 6000.7.0a6 to x
|
||||
/// 6000.7.0a6
|
||||
/// </summary>
|
||||
IndependentTypeTreeVersion = 24,
|
||||
/// <summary>
|
||||
/// 6000.7.0a6 to x
|
||||
/// 6000.7.0a6
|
||||
/// </summary>
|
||||
SerializeReferenceRegistryFirst = 25,
|
||||
/// <summary>
|
||||
/// 6000.7.0b2 to x
|
||||
/// </summary>
|
||||
SharedSubTreeSupport = 26,
|
||||
|
||||
// used by AssetStudio
|
||||
NotTested
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace AssetStudio
|
||||
{
|
||||
@@ -14,6 +15,7 @@ namespace AssetStudio
|
||||
public string m_KlassName;
|
||||
public string m_NameSpace;
|
||||
public string m_AsmName;
|
||||
public List<string> subTreeHashList;
|
||||
|
||||
public bool Match(params ulong[] hashes)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace AssetStudio
|
||||
{
|
||||
public class SubTreeInfo
|
||||
{
|
||||
public List<TypeTreeNode> SubTreeNodes;
|
||||
public List<string> SubTreeDependencies;
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace AssetStudio
|
||||
{
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
@@ -11,6 +10,7 @@ namespace AssetStudio
|
||||
public static class TypeTreeHelper
|
||||
{
|
||||
private static readonly JsonSerializerOptions JsonOptions;
|
||||
|
||||
static TypeTreeHelper()
|
||||
{
|
||||
JsonOptions = new JsonSerializerOptions
|
||||
|
||||
@@ -1,9 +1,4 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace AssetStudio
|
||||
namespace AssetStudio
|
||||
{
|
||||
public class TypeTreeNode
|
||||
{
|
||||
@@ -21,6 +16,21 @@ namespace AssetStudio
|
||||
|
||||
public TypeTreeNode() { }
|
||||
|
||||
public TypeTreeNode(TypeTreeNode node)
|
||||
{
|
||||
m_Type = node.m_Type;
|
||||
m_Name = node.m_Name;
|
||||
m_ByteSize = node.m_ByteSize;
|
||||
m_Index = node.m_Index;
|
||||
m_TypeFlags = node.m_TypeFlags;
|
||||
m_Version = node.m_Version;
|
||||
m_MetaFlag = node.m_MetaFlag;
|
||||
m_Level = node.m_Level;
|
||||
m_TypeStrOffset = node.m_TypeStrOffset;
|
||||
m_NameStrOffset = node.m_NameStrOffset;
|
||||
m_RefTypeHash = node.m_RefTypeHash;
|
||||
}
|
||||
|
||||
public TypeTreeNode(string type, string name, int level, bool align)
|
||||
{
|
||||
m_Type = type;
|
||||
|
||||
@@ -1600,6 +1600,7 @@ namespace AssetStudioGUI
|
||||
|
||||
Text = guiTitle;
|
||||
AssetItem.Reset();
|
||||
SerializedFile.Clear();
|
||||
Studio.assetsManager.Clear();
|
||||
Studio.assemblyLoader.Clear();
|
||||
Studio.exportableAssets.Clear();
|
||||
|
||||
Reference in New Issue
Block a user