From 1c85b1d260007ea3ce1e770a624737a8335c8079 Mon Sep 17 00:00:00 2001 From: nesquack Date: Wed, 1 Oct 2025 22:23:34 -0400 Subject: [PATCH 01/21] fix GetAssetsOfType when filtering monobehaviours --- AssetTools.NET/AssetsTools.NET.csproj | 1 + .../Standard/AssetsFileFormat/AssetsFile.cs | 4 ++-- .../Standard/AssetsFileFormat/AssetsFileMetadata.cs | 13 ++++++++++--- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/AssetTools.NET/AssetsTools.NET.csproj b/AssetTools.NET/AssetsTools.NET.csproj index 81a47c6..cc5970b 100644 --- a/AssetTools.NET/AssetsTools.NET.csproj +++ b/AssetTools.NET/AssetsTools.NET.csproj @@ -7,6 +7,7 @@ 8 True False + Disable bin\x64\Debug\ diff --git a/AssetTools.NET/Standard/AssetsFileFormat/AssetsFile.cs b/AssetTools.NET/Standard/AssetsFileFormat/AssetsFile.cs index fd8742f..4174964 100644 --- a/AssetTools.NET/Standard/AssetsFileFormat/AssetsFile.cs +++ b/AssetTools.NET/Standard/AssetsFileFormat/AssetsFile.cs @@ -291,7 +291,7 @@ public static bool IsAssetsFile(AssetsFileReader reader, long offset, long lengt public List GetAssetsOfType(AssetClassID typeId) => Metadata.GetAssetsOfType(typeId); /// /// Get all assets of a specific type ID and script index. The script index of an asset can be - /// found from or . + /// found from or . /// /// The type ID to search for. /// The script index to search for. @@ -299,7 +299,7 @@ public static bool IsAssetsFile(AssetsFileReader reader, long offset, long lengt public List GetAssetsOfType(int typeId, ushort scriptIndex) => Metadata.GetAssetsOfType(typeId, scriptIndex); /// /// Get all assets of a specific type ID and script index. The script index of an asset can be - /// found from or . + /// found from or . /// /// The type ID to search for. /// The script index to search for. diff --git a/AssetTools.NET/Standard/AssetsFileFormat/AssetsFileMetadata.cs b/AssetTools.NET/Standard/AssetsFileFormat/AssetsFileMetadata.cs index 0636a49..d6d6688 100644 --- a/AssetTools.NET/Standard/AssetsFileFormat/AssetsFileMetadata.cs +++ b/AssetTools.NET/Standard/AssetsFileFormat/AssetsFileMetadata.cs @@ -285,14 +285,14 @@ public List GetAssetsOfType(int typeId) /// found from or . /// /// The type ID to search for. - /// The script index to search for. + /// The script index to search for, or for any. /// A list of infos for that type ID and script index. public List GetAssetsOfType(int typeId, ushort scriptIndex) { List infos = new List(); foreach (AssetFileInfo info in AssetInfos) { - if (scriptIndex != 0xffff) + if (scriptIndex != ushort.MaxValue) { if (info.TypeId < 0) { @@ -304,7 +304,14 @@ public List GetAssetsOfType(int typeId, ushort scriptIndex) } else if (info.TypeId == (int)AssetClassID.MonoBehaviour) { - if (FindTypeTreeTypeByID(info.TypeId, info.ScriptTypeIndex).ScriptTypeIndex != scriptIndex) + // we don't have access to the metadata or format version + // in this function. let's double check this is ver >= 16. + if (info.TypeIdOrIndex == info.TypeId) + continue; + + // we've confirmed at this point we're not ver < 16. if we + // were, we should have had TypeId < 0 + if (info.GetScriptIndex(this, 16) != scriptIndex) continue; if (typeId != (int)AssetClassID.MonoBehaviour) From 2f9b565c795d870e501b0e5f1e70ea9dd9fb0be4 Mon Sep 17 00:00:00 2001 From: nesquack Date: Wed, 1 Oct 2025 22:23:53 -0400 Subject: [PATCH 02/21] add flag to check if bundle dirinf is a serialized file --- .../AssetsBundleFileFormat/AssetBundleDirectoryInfo.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/AssetTools.NET/Standard/AssetsBundleFileFormat/AssetBundleDirectoryInfo.cs b/AssetTools.NET/Standard/AssetsBundleFileFormat/AssetBundleDirectoryInfo.cs index 88abeea..973605d 100644 --- a/AssetTools.NET/Standard/AssetsBundleFileFormat/AssetBundleDirectoryInfo.cs +++ b/AssetTools.NET/Standard/AssetsBundleFileFormat/AssetBundleDirectoryInfo.cs @@ -16,7 +16,7 @@ public class AssetBundleDirectoryInfo /// 0x02: Entry is deleted. Unknown usage. /// 0x04: Entry is serialized file. Assets files should enable this, and other files like .resS or .resource(s) should disable this. /// - public uint Flags; + public uint Flags; // todo: this should have real flags /// /// Name of this entry. /// @@ -36,6 +36,10 @@ public class AssetBundleDirectoryInfo /// Is the replacer non-null and does the replacer has a preview? /// public bool IsReplacerPreviewable => Replacer != null && Replacer.HasPreview(); + /// + /// Is the file serialized? + /// + public bool IsSerialized => (Flags & 4) != 0; /// /// Sets the bytes used when the AssetBundleFile is written. From d3bd997092b7455e84d7b92e9c6210eafb242502 Mon Sep 17 00:00:00 2001 From: nesquack Date: Wed, 1 Oct 2025 22:28:46 -0400 Subject: [PATCH 03/21] temporarily disable nullable --- AssetsTools.NET.Cpp2IL/AssetsTools.NET.Cpp2IL.csproj | 1 + AssetsTools.NET.MonoCecil/AssetsTools.NET.MonoCecil.csproj | 1 + AssetsTools.NET.Texture/AssetsTools.NET.Texture.csproj | 5 +++-- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/AssetsTools.NET.Cpp2IL/AssetsTools.NET.Cpp2IL.csproj b/AssetsTools.NET.Cpp2IL/AssetsTools.NET.Cpp2IL.csproj index dd2434c..00e5769 100644 --- a/AssetsTools.NET.Cpp2IL/AssetsTools.NET.Cpp2IL.csproj +++ b/AssetsTools.NET.Cpp2IL/AssetsTools.NET.Cpp2IL.csproj @@ -3,6 +3,7 @@ netstandard2.0 9 + Disable diff --git a/AssetsTools.NET.MonoCecil/AssetsTools.NET.MonoCecil.csproj b/AssetsTools.NET.MonoCecil/AssetsTools.NET.MonoCecil.csproj index 47cc291..a47971f 100644 --- a/AssetsTools.NET.MonoCecil/AssetsTools.NET.MonoCecil.csproj +++ b/AssetsTools.NET.MonoCecil/AssetsTools.NET.MonoCecil.csproj @@ -3,6 +3,7 @@ netstandard2.0;net40 8 + Disable diff --git a/AssetsTools.NET.Texture/AssetsTools.NET.Texture.csproj b/AssetsTools.NET.Texture/AssetsTools.NET.Texture.csproj index 7aa415f..522b4f2 100644 --- a/AssetsTools.NET.Texture/AssetsTools.NET.Texture.csproj +++ b/AssetsTools.NET.Texture/AssetsTools.NET.Texture.csproj @@ -2,8 +2,9 @@ netstandard2.0;net6.0;net7.0;net8.0 - latest - true + latest + true + Disable From 0a2f7d166a087861eefe6965893cb450e60dd4f5 Mon Sep 17 00:00:00 2001 From: nesquack Date: Fri, 17 Oct 2025 23:18:06 -0400 Subject: [PATCH 04/21] update nuspecs --- AssetTools.NET/AssetsTools.NET.nuspec | 9 ++------- AssetsTools.NET.Cpp2IL/AssetsTools.NET.Cpp2IL.nuspec | 4 ++-- .../AssetsTools.NET.MonoCecil.nuspec | 10 ++-------- AssetsTools.NET.Texture/AssetsTools.NET.Texture.nuspec | 4 ++-- 4 files changed, 8 insertions(+), 19 deletions(-) diff --git a/AssetTools.NET/AssetsTools.NET.nuspec b/AssetTools.NET/AssetsTools.NET.nuspec index 84c6970..19a3a5d 100644 --- a/AssetTools.NET/AssetsTools.NET.nuspec +++ b/AssetTools.NET/AssetsTools.NET.nuspec @@ -2,7 +2,7 @@ AssetsTools.NET - 3.0.0 + 3.0.2 nesrak1 nesrak1 false @@ -11,12 +11,10 @@ icon.png An assets/bundle library, inspired by UABE's AssetsTools An assets/bundle library, inspired by UABE's AssetsTools. Read and write assets and bundle files from engine versions 5.x and later. For most cases, you'll need the class data file which you can find in the AssetRipper/Tpk repo. See the wiki for usage and examples. - v3.0.0 - the complete overhaul of assetstools.net. bug fixes, new replacers, and more. + v3.0.2 - bug fixes, template field [] syntax, lz4 fast compress option https://github.com/nesrak1/AssetsTools.NET - - @@ -26,9 +24,6 @@ - - - diff --git a/AssetsTools.NET.Cpp2IL/AssetsTools.NET.Cpp2IL.nuspec b/AssetsTools.NET.Cpp2IL/AssetsTools.NET.Cpp2IL.nuspec index 02cb765..23621d8 100644 --- a/AssetsTools.NET.Cpp2IL/AssetsTools.NET.Cpp2IL.nuspec +++ b/AssetsTools.NET.Cpp2IL/AssetsTools.NET.Cpp2IL.nuspec @@ -2,7 +2,7 @@ AssetsTools.NET.Cpp2IL - 1.0.0 + 3.0.2 nesrak1 nesrak1 false @@ -11,7 +11,7 @@ icon.png Cpp2IL support for AssetsTools.NET Adds MonoBehaviour deserialization for AssetsTools.NET using Cpp2IL. - v1.0.0 - upgraded at. + v3.0.2 - updates with at https://github.com/nesrak1/AssetsTools.NET diff --git a/AssetsTools.NET.MonoCecil/AssetsTools.NET.MonoCecil.nuspec b/AssetsTools.NET.MonoCecil/AssetsTools.NET.MonoCecil.nuspec index 39f5427..c3d34f7 100644 --- a/AssetsTools.NET.MonoCecil/AssetsTools.NET.MonoCecil.nuspec +++ b/AssetsTools.NET.MonoCecil/AssetsTools.NET.MonoCecil.nuspec @@ -2,7 +2,7 @@ AssetsTools.NET.MonoCecil - 1.0.0 + 3.0.2 nesrak1 nesrak1 false @@ -11,14 +11,10 @@ icon.png Mono.Cecil support for AssetsTools.NET Adds MonoBehaviour deserialization for AssetsTools.NET using Mono.Cecil. - v1.0.0 - upgraded at. + v3.0.2 - updates with at, fix for the type name of string lists https://github.com/nesrak1/AssetsTools.NET - - - - @@ -32,8 +28,6 @@ - - diff --git a/AssetsTools.NET.Texture/AssetsTools.NET.Texture.nuspec b/AssetsTools.NET.Texture/AssetsTools.NET.Texture.nuspec index 84b4d33..8228e43 100644 --- a/AssetsTools.NET.Texture/AssetsTools.NET.Texture.nuspec +++ b/AssetsTools.NET.Texture/AssetsTools.NET.Texture.nuspec @@ -2,7 +2,7 @@ AssetsTools.NET.Texture - 1.0.0 + 3.0.2 nesrak1 nesrak1 false @@ -11,7 +11,7 @@ icon.png Texture support for AssetsTools.NET Adds Texture2D decoding for AssetsTools.NET using TextureDecoder. - v1.0.0 - upgraded at. + v3.0.2 - exposed more methods for working with textures https://github.com/nesrak1/AssetsTools.NET From 7de906dff71c45e774378431093bc42b49591fb1 Mon Sep 17 00:00:00 2001 From: nesquack Date: Fri, 17 Oct 2025 23:18:39 -0400 Subject: [PATCH 05/21] performance improvements --- .../AssetsManager.Deserialization.cs | 2 +- .../Standard/AssetsFileFormat/TypeTreeNode.cs | 14 ++------ AssetTools.NET/Standard/IO/LZ4BlockStream.cs | 2 +- AssetTools.NET/Standard/IO/SegmentStream.cs | 32 ++++++++----------- 4 files changed, 18 insertions(+), 32 deletions(-) diff --git a/AssetTools.NET/Extra/AssetsManager/AssetsManager.Deserialization.cs b/AssetTools.NET/Extra/AssetsManager/AssetsManager.Deserialization.cs index 5323d4e..dc0b9e3 100644 --- a/AssetTools.NET/Extra/AssetsManager/AssetsManager.Deserialization.cs +++ b/AssetTools.NET/Extra/AssetsManager/AssetsManager.Deserialization.cs @@ -334,7 +334,7 @@ public AssetTypeValueField GetBaseField(AssetsFileInstance inst, AssetFileInfo i } else { - using MemoryStream assetDataStream = new MemoryStream(); + using MemoryStream assetDataStream = new MemoryStream((int)info.ByteSize); lock (inst.LockReader) { AssetsFileReader reader = inst.file.Reader; diff --git a/AssetTools.NET/Standard/AssetsFileFormat/TypeTreeNode.cs b/AssetTools.NET/Standard/AssetsFileFormat/TypeTreeNode.cs index 82218c5..9d03e6b 100644 --- a/AssetTools.NET/Standard/AssetsFileFormat/TypeTreeNode.cs +++ b/AssetTools.NET/Standard/AssetsFileFormat/TypeTreeNode.cs @@ -1,4 +1,4 @@ -using System.IO; +using System; using System.Text; namespace AssetsTools.NET @@ -135,16 +135,8 @@ private string ReadStringTableString(byte[] stringTable, byte[] commonStringTabl stringTable = commonStringTable; } - using MemoryStream data = new MemoryStream(); - int pos = (int)offset; - byte b; - while ((b = stringTable[pos]) != 0x00) - { - data.WriteByte(b); - pos++; - } - - return Encoding.UTF8.GetString(data.ToArray()); + var endIdx = Array.IndexOf(stringTable, (byte)0, (int)offset); + return Encoding.ASCII.GetString(stringTable, (int)offset, (int)(endIdx - offset)); } } } diff --git a/AssetTools.NET/Standard/IO/LZ4BlockStream.cs b/AssetTools.NET/Standard/IO/LZ4BlockStream.cs index fd52b7e..410a346 100644 --- a/AssetTools.NET/Standard/IO/LZ4BlockStream.cs +++ b/AssetTools.NET/Standard/IO/LZ4BlockStream.cs @@ -131,7 +131,7 @@ public override int Read(byte[] buffer, int offset, int count) BaseStream.Position = BaseOffset + blockPoses[blockIndex]; MemoryStream compressedStream = new MemoryStream(); - BaseStream.CopyToCompat(compressedStream, blockInfos[blockIndex].CompressedSize); + BaseStream.CopyToCompat(compressedStream, blockInfos[blockIndex].CompressedSize, (int)blockSize); compressedStream.Position = 0; byte compressionType = blockInfos[blockIndex].GetCompressionType(); diff --git a/AssetTools.NET/Standard/IO/SegmentStream.cs b/AssetTools.NET/Standard/IO/SegmentStream.cs index 24c5c33..6712521 100644 --- a/AssetTools.NET/Standard/IO/SegmentStream.cs +++ b/AssetTools.NET/Standard/IO/SegmentStream.cs @@ -54,32 +54,26 @@ public override void Flush() public override int Read(byte[] buffer, int offset, int count) { BaseStream.Position = BaseOffset + Position; - count = BaseStream.Read(buffer, offset, (int)Math.Min(count, Length - Position)); + + // fixed count for performance since .Length is expensive on FileStream base + var countFixed = length >= 0 + ? (int)Math.Min(count, length - Position) + : count; // read as much as possible. Read() will handle if we try to read too much. + + count = BaseStream.Read(buffer, offset, countFixed); Position += count; return count; } public override long Seek(long offset, SeekOrigin origin) { - long newPosition; - switch (origin) + var newPosition = origin switch { - case SeekOrigin.Begin: - newPosition = offset; - break; - - case SeekOrigin.Current: - newPosition = Position + offset; - break; - - case SeekOrigin.End: - newPosition = Position + Length + offset; - break; - - default: - throw new ArgumentException(); - } - + SeekOrigin.Begin => offset, + SeekOrigin.Current => Position + offset, + SeekOrigin.End => Position + Length + offset, + _ => throw new ArgumentException(), + }; if (newPosition < 0 || newPosition > Length) throw new ArgumentOutOfRangeException(nameof(offset)); From 46ea66dbd4ca97570dff828afb5520e9fba62911 Mon Sep 17 00:00:00 2001 From: nesquack Date: Mon, 27 Oct 2025 21:42:36 -0400 Subject: [PATCH 06/21] fix unity version parsing for tuanjie --- AssetTools.NET/Extra/AssetHelper.cs | 2 +- AssetTools.NET/Extra/UnityVersion.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/AssetTools.NET/Extra/AssetHelper.cs b/AssetTools.NET/Extra/AssetHelper.cs index e7004c1..4f6576d 100644 --- a/AssetTools.NET/Extra/AssetHelper.cs +++ b/AssetTools.NET/Extra/AssetHelper.cs @@ -66,7 +66,7 @@ public static string GetAssetNameFast(AssetsFile file, ClassDatabaseFile cldb, A if (file.Metadata.TypeTreeEnabled) { - ushort scriptId = file.GetScriptIndex(info); + ushort scriptId = info.GetScriptIndex(file); TypeTreeType ttType = file.Metadata.FindTypeTreeTypeByID(info.TypeId, scriptId); diff --git a/AssetTools.NET/Extra/UnityVersion.cs b/AssetTools.NET/Extra/UnityVersion.cs index c46196b..510d7ef 100644 --- a/AssetTools.NET/Extra/UnityVersion.cs +++ b/AssetTools.NET/Extra/UnityVersion.cs @@ -20,7 +20,7 @@ public UnityVersion(string version) major = int.Parse(versionSplit[0]); minor = int.Parse(versionSplit[1]); - int verTypeIndex = versionSplit[2].IndexOfAny(new[] { 'f', 'p', 'a', 'b', 'c', 'x' }); + int verTypeIndex = versionSplit[2].IndexOfAny(new[] { 'f', 'p', 'a', 'b', 'c', 't', 'x' }); if (verTypeIndex != -1) { type = versionSplit[2][verTypeIndex].ToString(); From d15cc268c83eb4c132d46d15e2960f4fd2b694bd Mon Sep 17 00:00:00 2001 From: nesquack Date: Mon, 27 Oct 2025 21:43:06 -0400 Subject: [PATCH 07/21] fix duplicate registry in non-typetree --- .../Standard/AssetTypeClass/RefTypeManager.cs | 23 ++++++--- .../MonoCecilTempGenerator.cs | 50 +++++++++++-------- 2 files changed, 46 insertions(+), 27 deletions(-) diff --git a/AssetTools.NET/Standard/AssetTypeClass/RefTypeManager.cs b/AssetTools.NET/Standard/AssetTypeClass/RefTypeManager.cs index 859246f..30bd12a 100644 --- a/AssetTools.NET/Standard/AssetTypeClass/RefTypeManager.cs +++ b/AssetTools.NET/Standard/AssetTypeClass/RefTypeManager.cs @@ -46,12 +46,7 @@ public void FromTypeTree(AssetsFileMetadata metadata) AssetTypeTemplateField templateField = new AssetTypeTemplateField(); templateField.FromTypeTree(type); - // if ref type has fields with [SerializeReference] it can contain its own registry, - // but it shouldn't be there, as the registry is only available at the root type - if (templateField.Children.Count > 0 && templateField.Children[templateField.Children.Count - 1].ValueType == AssetValueType.ManagedReferencesRegistry) - { - templateField.Children.RemoveAt(templateField.Children.Count - 1); - } + RemoveRedundantRegistry(templateField); typeTreeLookup[type.TypeReference] = templateField; } @@ -112,6 +107,7 @@ public AssetTypeTemplateField GetTemplateField(AssetTypeReference type) templateField = monoTemplateGenerator.GetTemplateField(templateField, type.AsmName, type.Namespace, type.ClassName, unityVersion); if (templateField != null) { + RemoveRedundantRegistry(templateField); monoTemplateLookup[type] = templateField; return templateField; } @@ -119,5 +115,20 @@ public AssetTypeTemplateField GetTemplateField(AssetTypeReference type) return null; } + + private void RemoveRedundantRegistry(AssetTypeTemplateField templateField) + { + // if ref type has fields with [SerializeReference] it can contain its own registry, + // but it shouldn't be there, as the registry is only available at the root type + if (templateField.Children.Count == 0) + return; + + int lastChildIdx = templateField.Children.Count - 1; + AssetTypeTemplateField lastChild = templateField[lastChildIdx]; + if (lastChild.ValueType == AssetValueType.ManagedReferencesRegistry) + { + templateField.Children.RemoveAt(lastChildIdx); + } + } } } diff --git a/AssetsTools.NET.MonoCecil/MonoCecilTempGenerator.cs b/AssetsTools.NET.MonoCecil/MonoCecilTempGenerator.cs index 47684ca..1282f90 100644 --- a/AssetsTools.NET.MonoCecil/MonoCecilTempGenerator.cs +++ b/AssetsTools.NET.MonoCecil/MonoCecilTempGenerator.cs @@ -9,7 +9,6 @@ namespace AssetsTools.NET.Extra public class MonoCecilTempGenerator : IMonoBehaviourTemplateGenerator { private UnityVersion unityVersion; - private bool anyFieldIsManagedReference; public string managedPath; public Dictionary loadedAssemblies = new Dictionary(); @@ -63,10 +62,18 @@ public List Read(string assemblyPath, string nameSpace, public List Read(AssemblyDefinition assembly, string nameSpace, string typeName, UnityVersion unityVersion) { this.unityVersion = unityVersion; - anyFieldIsManagedReference = false; + bool usingManagedReference = false; List children = new List(); - RecursiveTypeLoad(assembly.MainModule, nameSpace, typeName, children, CommonMonoTemplateHelper.GetSerializationLimit(unityVersion)); + RecursiveTypeLoad( + assembly.MainModule, nameSpace, typeName, children, + CommonMonoTemplateHelper.GetSerializationLimit(unityVersion), ref usingManagedReference); + + if (usingManagedReference) + { + children.Add(CommonMonoTemplateHelper.ManagedReferencesRegistry("references", unityVersion)); + } + return children; } @@ -100,7 +107,9 @@ private AssemblyDefinition GetAssemblyWithDependencies(string path) return asmDef; } - private void RecursiveTypeLoad(ModuleDefinition module, string nameSpace, string typeName, List attf, int availableDepth) + private void RecursiveTypeLoad( + ModuleDefinition module, string nameSpace, string typeName, List attf, + int availableDepth, ref bool usingManagedReference) { // TypeReference needed for TypeForwardedTo in UnityEngine (and others) TypeReference typeRef; @@ -125,10 +134,12 @@ private void RecursiveTypeLoad(ModuleDefinition module, string nameSpace, string type = typeRef.Resolve(); } - RecursiveTypeLoad(type, attf, availableDepth, true); + RecursiveTypeLoad(type, attf, availableDepth, true, ref usingManagedReference); } - private void RecursiveTypeLoad(TypeDefWithSelfRef type, List attf, int availableDepth, bool isRecursiveCall = false) + private void RecursiveTypeLoad( + TypeDefWithSelfRef type, List attf, int availableDepth, + bool isRecursiveCall, ref bool usingManagedReference) { if (!isRecursiveCall) { @@ -143,13 +154,13 @@ private void RecursiveTypeLoad(TypeDefWithSelfRef type, List ReadTypes(TypeDefWithSelfRef type, int availableDepth) + private List ReadTypes(TypeDefWithSelfRef type, int availableDepth, ref bool usingManagedReference) { List acceptableFields = GetAcceptableFields(type, availableDepth); List localChildren = new List(); @@ -202,7 +213,7 @@ private List ReadTypes(TypeDefWithSelfRef type, int avai } else if (isManagedReference = fieldDef.CustomAttributes.Any(a => a.AttributeType.Name == "SerializeReference")) { - anyFieldIsManagedReference = true; + usingManagedReference = true; field.Type = "managedReference"; } else @@ -220,7 +231,7 @@ private List ReadTypes(TypeDefWithSelfRef type, int avai } else if (CommonMonoTemplateHelper.IsSpecialUnityType(fieldTypeDef.typeDef.FullName)) { - field.Children = SpecialUnity(fieldTypeDef, availableDepth); + field.Children = SpecialUnity(fieldTypeDef, availableDepth, ref usingManagedReference); } else if (derivesFromUEObject) { @@ -232,7 +243,7 @@ private List ReadTypes(TypeDefWithSelfRef type, int avai } else if (fieldTypeDef.typeDef.IsSerializable) { - field.Children = Serialized(fieldTypeDef, availableDepth); + field.Children = Serialized(fieldTypeDef, availableDepth, ref usingManagedReference); } field.ValueType = AssetTypeValueField.GetValueTypeByTypeName(field.Type); @@ -253,11 +264,6 @@ private List ReadTypes(TypeDefWithSelfRef type, int avai localChildren.Add(field); } - if (anyFieldIsManagedReference && DerivesFromUEObject(type)) - { - localChildren.Add(CommonMonoTemplateHelper.ManagedReferencesRegistry("references", unityVersion)); - } - return localChildren; } @@ -395,14 +401,16 @@ private bool DerivesFromUEObject(TypeDefWithSelfRef typeDef) return false; } - private List Serialized(TypeDefWithSelfRef type, int availableDepth) + private List Serialized( + TypeDefWithSelfRef type, int availableDepth, ref bool usingManagedReference) { List types = new List(); - RecursiveTypeLoad(type, types, availableDepth); + RecursiveTypeLoad(type, types, availableDepth, false, ref usingManagedReference); return types; } - private List SpecialUnity(TypeDefWithSelfRef type, int availableDepth) + private List SpecialUnity( + TypeDefWithSelfRef type, int availableDepth, ref bool usingManagedReference) { return type.typeDef.Name switch { @@ -418,7 +426,7 @@ private List SpecialUnity(TypeDefWithSelfRef type, int a "Vector2Int" => CommonMonoTemplateHelper.Vector2Int(), "Vector3Int" => CommonMonoTemplateHelper.Vector3Int(), "PropertyName" => CommonMonoTemplateHelper.PropertyName(), - _ => Serialized(type, availableDepth) + _ => Serialized(type, availableDepth, ref usingManagedReference) }; } } From c19a0c879351de859d0e24e52c8e3ab40d0079b8 Mon Sep 17 00:00:00 2001 From: nesquack Date: Sat, 29 Nov 2025 12:43:50 -0500 Subject: [PATCH 08/21] utf8 type tree strings --- AssetTools.NET/Standard/AssetsFileFormat/TypeTreeNode.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AssetTools.NET/Standard/AssetsFileFormat/TypeTreeNode.cs b/AssetTools.NET/Standard/AssetsFileFormat/TypeTreeNode.cs index 9d03e6b..e7fbeb4 100644 --- a/AssetTools.NET/Standard/AssetsFileFormat/TypeTreeNode.cs +++ b/AssetTools.NET/Standard/AssetsFileFormat/TypeTreeNode.cs @@ -136,7 +136,7 @@ private string ReadStringTableString(byte[] stringTable, byte[] commonStringTabl } var endIdx = Array.IndexOf(stringTable, (byte)0, (int)offset); - return Encoding.ASCII.GetString(stringTable, (int)offset, (int)(endIdx - offset)); + return Encoding.UTF8.GetString(stringTable, (int)offset, (int)(endIdx - offset)); } } } From 69d462edb9ef41768c886d4d87dd4709b52918fa Mon Sep 17 00:00:00 2001 From: nesquack Date: Wed, 3 Dec 2025 00:17:41 -0500 Subject: [PATCH 09/21] remember compression type in bundle file instance --- AssetTools.NET/Extra/AssetsManager/BundleFileInstance.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/AssetTools.NET/Extra/AssetsManager/BundleFileInstance.cs b/AssetTools.NET/Extra/AssetsManager/BundleFileInstance.cs index 1708c11..e47dc07 100644 --- a/AssetTools.NET/Extra/AssetsManager/BundleFileInstance.cs +++ b/AssetTools.NET/Extra/AssetsManager/BundleFileInstance.cs @@ -9,6 +9,11 @@ public class BundleFileInstance public string name; public AssetBundleFile file; /// + /// Original compression type. If this bundle is decompressed, you might + /// use this value to compress it back to its original compression type. + /// + public AssetBundleCompressionType originalCompression; + /// /// List of loaded assets files for this bundle. /// /// @@ -24,12 +29,16 @@ public BundleFileInstance(Stream stream, string filePath, bool unpackIfPacked = { path = Path.GetFullPath(filePath); name = Path.GetFileName(path); + file = new AssetBundleFile(); file.Read(new AssetsFileReader(stream)); + + originalCompression = file.GetCompressionType(); if (file.Header != null && file.DataIsCompressed && unpackIfPacked) { file = BundleHelper.UnpackBundle(file); } + loadedAssetsFiles = new List(); } From 2e9444154ac1ddbfcd90345f991f3e58bf1641d2 Mon Sep 17 00:00:00 2001 From: ds5678 <49847914+ds5678@users.noreply.github.com> Date: Thu, 18 Dec 2025 13:30:43 -0800 Subject: [PATCH 10/21] Support .NET Framework 3.5 --- AssetTools.NET/AssetsTools.NET.csproj | 2 +- .../Extra/AssetsManager/AssetsFileInstance.cs | 3 +- .../AssetsManager.Deserialization.cs | 7 +- .../Extra/AssetsManager/AssetsManager.cs | 3 +- .../Standard/ConcurrentDictionary.cs | 233 ++++++++++++++++++ 5 files changed, 239 insertions(+), 9 deletions(-) create mode 100644 AssetTools.NET/Standard/ConcurrentDictionary.cs diff --git a/AssetTools.NET/AssetsTools.NET.csproj b/AssetTools.NET/AssetsTools.NET.csproj index cc5970b..2d35511 100644 --- a/AssetTools.NET/AssetsTools.NET.csproj +++ b/AssetTools.NET/AssetsTools.NET.csproj @@ -1,6 +1,6 @@  - netstandard2.0;net40 + netstandard2.0;net35;net40 Library false AnyCPU diff --git a/AssetTools.NET/Extra/AssetsManager/AssetsFileInstance.cs b/AssetTools.NET/Extra/AssetsManager/AssetsFileInstance.cs index 0d3672b..cff2896 100644 --- a/AssetTools.NET/Extra/AssetsManager/AssetsFileInstance.cs +++ b/AssetTools.NET/Extra/AssetsManager/AssetsFileInstance.cs @@ -1,5 +1,4 @@ -using System.Collections.Concurrent; -using System.IO; +using System.IO; using System.Linq; namespace AssetsTools.NET.Extra diff --git a/AssetTools.NET/Extra/AssetsManager/AssetsManager.Deserialization.cs b/AssetTools.NET/Extra/AssetsManager/AssetsManager.Deserialization.cs index dc0b9e3..9300478 100644 --- a/AssetTools.NET/Extra/AssetsManager/AssetsManager.Deserialization.cs +++ b/AssetTools.NET/Extra/AssetsManager/AssetsManager.Deserialization.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Concurrent; using System.IO; namespace AssetsTools.NET.Extra @@ -62,9 +61,9 @@ public AssetTypeTemplateField GetTemplateBaseField( AssetTypeTemplateField baseField = null; bool hasTypeTree = inst.file.Metadata.TypeTreeEnabled; - bool preferEditor = readFlags.HasFlag(AssetReadFlags.PreferEditor); - bool forceFromCldb = readFlags.HasFlag(AssetReadFlags.ForceFromCldb); - bool skipMonoBehaviourFields = readFlags.HasFlag(AssetReadFlags.SkipMonoBehaviourFields); + bool preferEditor = (readFlags & AssetReadFlags.PreferEditor) != 0; + bool forceFromCldb = (readFlags & AssetReadFlags.ForceFromCldb) != 0; + bool skipMonoBehaviourFields = (readFlags & AssetReadFlags.SkipMonoBehaviourFields) != 0; // if non-monobehaviour type is in cache, return the cached item if (UseTemplateFieldCache && typeId != (int)AssetClassID.MonoBehaviour && templateFieldCache.TryGetValue(typeId, out baseField)) diff --git a/AssetTools.NET/Extra/AssetsManager/AssetsManager.cs b/AssetTools.NET/Extra/AssetsManager/AssetsManager.cs index ccac01b..d42338f 100644 --- a/AssetTools.NET/Extra/AssetsManager/AssetsManager.cs +++ b/AssetTools.NET/Extra/AssetsManager/AssetsManager.cs @@ -1,5 +1,4 @@ -using System.Collections.Concurrent; -using System.Collections.Generic; +using System.Collections.Generic; namespace AssetsTools.NET.Extra { diff --git a/AssetTools.NET/Standard/ConcurrentDictionary.cs b/AssetTools.NET/Standard/ConcurrentDictionary.cs new file mode 100644 index 0000000..f6a3d64 --- /dev/null +++ b/AssetTools.NET/Standard/ConcurrentDictionary.cs @@ -0,0 +1,233 @@ +using System.Collections; +using System.Collections.Generic; + +namespace AssetsTools.NET +{ + internal sealed class ConcurrentDictionary : IDictionary + { +#if NET35 + private readonly Dictionary _dict = new Dictionary(); + private readonly object _lock = new object(); +#else + private readonly System.Collections.Concurrent.ConcurrentDictionary _dict = new System.Collections.Concurrent.ConcurrentDictionary(); +#endif + + public ICollection Keys + { + get + { +#if NET35 + lock (_lock) + { + return new List(_dict.Keys); + } +#else + return _dict.Keys; +#endif + } + } + + public ICollection Values + { + get + { +#if NET35 + lock (_lock) + { + return new List(_dict.Values); + } +#else + return _dict.Values; +#endif + } + } + + public int Count + { + get + { +#if NET35 + lock (_lock) + { + return _dict.Count; + } +#else + return _dict.Count; +#endif + } + } + + public bool IsReadOnly => false; + + public bool ContainsKey(TKey key) + { +#if NET35 + lock (_lock) + { + return _dict.ContainsKey(key); + } +#else + return _dict.ContainsKey(key); +#endif + } + + public IEnumerator> GetEnumerator() + { +#if NET35 + List> snapshot; + lock (_lock) + { + snapshot = new List>(_dict); + } + return snapshot.GetEnumerator(); +#else + return _dict.GetEnumerator(); +#endif + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + public void Add(TKey key, TValue value) + { +#if NET35 + lock (_lock) + { + _dict.Add(key, value); + } +#else + if (!_dict.TryAdd(key, value)) + { + throw new System.ArgumentException("An item with the same key has already been added.", nameof(key)); + } +#endif + } + + public bool Remove(TKey key) + { +#if NET35 + lock (_lock) + { + return _dict.Remove(key); + } +#else + return _dict.TryRemove(key, out _); +#endif + } + + public bool TryRemove(TKey key, out TValue value) + { +#if NET35 + lock (_lock) + { + if (_dict.TryGetValue(key, out value)) + { + _dict.Remove(key); + return true; + } + value = default; + return false; + } +#else + return _dict.TryRemove(key, out value); +#endif + } + + public bool TryGetValue(TKey key, out TValue value) + { +#if NET35 + lock (_lock) + { + return _dict.TryGetValue(key, out value); + } +#else + return _dict.TryGetValue(key, out value); +#endif + } + + public void Add(KeyValuePair item) + { +#if NET35 + Add(item.Key, item.Value); +#else + if (!_dict.TryAdd(item.Key, item.Value)) + { + throw new System.ArgumentException("An item with the same key has already been added.", nameof(item)); + } +#endif + } + + public void Clear() + { +#if NET35 + lock (_lock) + { + _dict.Clear(); + } +#else + _dict.Clear(); +#endif + } + + public bool Contains(KeyValuePair item) + { +#if NET35 + lock (_lock) + { + return ((IDictionary)_dict).Contains(item); + } +#else + return ((IDictionary)_dict).Contains(item); +#endif + } + + public void CopyTo(KeyValuePair[] array, int arrayIndex) + { +#if NET35 + lock (_lock) + { + ((IDictionary)_dict).CopyTo(array, arrayIndex); + } +#else + ((IDictionary)_dict).CopyTo(array, arrayIndex); +#endif + } + + public bool Remove(KeyValuePair item) + { +#if NET35 + lock (_lock) + { + return ((IDictionary)_dict).Remove(item); + } +#else + return ((IDictionary)_dict).Remove(item); +#endif + } + + public TValue this[TKey key] + { + get + { +#if NET35 + lock (_lock) + { + return _dict[key]; + } +#else + return _dict[key]; +#endif + } + set + { +#if NET35 + lock (_lock) + { + _dict[key] = value; + } +#else + _dict[key] = value; +#endif + } + } + } +} From dc72027aa2c68471ac3b2a3f147047d17e7e487f Mon Sep 17 00:00:00 2001 From: ds5678 <49847914+ds5678@users.noreply.github.com> Date: Thu, 18 Dec 2025 13:46:15 -0800 Subject: [PATCH 11/21] Minor implementation change --- AssetTools.NET/Standard/ConcurrentDictionary.cs | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/AssetTools.NET/Standard/ConcurrentDictionary.cs b/AssetTools.NET/Standard/ConcurrentDictionary.cs index f6a3d64..5fc7dae 100644 --- a/AssetTools.NET/Standard/ConcurrentDictionary.cs +++ b/AssetTools.NET/Standard/ConcurrentDictionary.cs @@ -95,10 +95,7 @@ public void Add(TKey key, TValue value) _dict.Add(key, value); } #else - if (!_dict.TryAdd(key, value)) - { - throw new System.ArgumentException("An item with the same key has already been added.", nameof(key)); - } + ((IDictionary)_dict).Add(key, value); #endif } @@ -146,14 +143,7 @@ public bool TryGetValue(TKey key, out TValue value) public void Add(KeyValuePair item) { -#if NET35 Add(item.Key, item.Value); -#else - if (!_dict.TryAdd(item.Key, item.Value)) - { - throw new System.ArgumentException("An item with the same key has already been added.", nameof(item)); - } -#endif } public void Clear() From ca291f1cab91b6460c14d54c77d878ec81afef19 Mon Sep 17 00:00:00 2001 From: nesquack Date: Sat, 20 Dec 2025 12:51:47 -0500 Subject: [PATCH 12/21] only check GetTemplateBaseField flags when we need to --- .../AssetsManager/AssetsManager.Deserialization.cs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/AssetTools.NET/Extra/AssetsManager/AssetsManager.Deserialization.cs b/AssetTools.NET/Extra/AssetsManager/AssetsManager.Deserialization.cs index 9300478..969ea63 100644 --- a/AssetTools.NET/Extra/AssetsManager/AssetsManager.Deserialization.cs +++ b/AssetTools.NET/Extra/AssetsManager/AssetsManager.Deserialization.cs @@ -57,13 +57,7 @@ public AssetTypeTemplateField GetTemplateBaseField( AssetsFileInstance inst, AssetsFileReader reader, long absByteStart, int typeId, ushort scriptIndex, AssetReadFlags readFlags) { - AssetsFile file = inst.file; AssetTypeTemplateField baseField = null; - bool hasTypeTree = inst.file.Metadata.TypeTreeEnabled; - - bool preferEditor = (readFlags & AssetReadFlags.PreferEditor) != 0; - bool forceFromCldb = (readFlags & AssetReadFlags.ForceFromCldb) != 0; - bool skipMonoBehaviourFields = (readFlags & AssetReadFlags.SkipMonoBehaviourFields) != 0; // if non-monobehaviour type is in cache, return the cached item if (UseTemplateFieldCache && typeId != (int)AssetClassID.MonoBehaviour && templateFieldCache.TryGetValue(typeId, out baseField)) @@ -71,6 +65,10 @@ public AssetTypeTemplateField GetTemplateBaseField( return baseField; } + AssetsFile file = inst.file; + bool hasTypeTree = file.Metadata.TypeTreeEnabled; + bool forceFromCldb = Net35Polyfill.HasFlag(readFlags, AssetReadFlags.ForceFromCldb); + // if there's a type tree AND we aren't forcing from a class database // (with the condition that we actually have a class database) then // load from that instead @@ -133,6 +131,8 @@ public AssetTypeTemplateField GetTemplateBaseField( return null; } + bool preferEditor = Net35Polyfill.HasFlag(readFlags, AssetReadFlags.PreferEditor); + baseField = new AssetTypeTemplateField(); baseField.FromClassDatabase(ClassDatabase, cldbType, preferEditor); @@ -154,6 +154,7 @@ public AssetTypeTemplateField GetTemplateBaseField( // can get the monoscript (we could also use the script index // but this is safer) and then passing the script from there to // the temp generator. we then append those fields to the base. + bool skipMonoBehaviourFields = Net35Polyfill.HasFlag(readFlags, AssetReadFlags.SkipMonoBehaviourFields); if (typeId == (int)AssetClassID.MonoBehaviour && MonoTempGenerator != null && !skipMonoBehaviourFields && reader != null) { AssetTypeValueField mbBaseField = baseField.MakeValue(reader, absByteStart); From c0ac6af0049deffeffc14aef35ef48fac18649cc Mon Sep 17 00:00:00 2001 From: nesquack Date: Sat, 20 Dec 2025 12:52:14 -0500 Subject: [PATCH 13/21] don't throw if a UnityVersion string is missing numbers --- AssetTools.NET/Extra/UnityVersion.cs | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/AssetTools.NET/Extra/UnityVersion.cs b/AssetTools.NET/Extra/UnityVersion.cs index 510d7ef..d01bcda 100644 --- a/AssetTools.NET/Extra/UnityVersion.cs +++ b/AssetTools.NET/Extra/UnityVersion.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace AssetsTools.NET.Extra +namespace AssetsTools.NET.Extra { public class UnityVersion { @@ -16,9 +12,20 @@ public UnityVersion() { } public UnityVersion(string version) { + major = 0; + minor = 0; + patch = 0; + type = ""; + typeNum = 0; + string[] versionSplit = version.Split('.'); - major = int.Parse(versionSplit[0]); - minor = int.Parse(versionSplit[1]); + + if (versionSplit.Length >= 1) + major = int.Parse(versionSplit[0]); + if (versionSplit.Length >= 2) + minor = int.Parse(versionSplit[1]); + if (versionSplit.Length <= 2) + return; int verTypeIndex = versionSplit[2].IndexOfAny(new[] { 'f', 'p', 'a', 'b', 'c', 't', 'x' }); if (verTypeIndex != -1) From 42d7ef65dc850f799a2a9a6bfada79d6ec30ba72 Mon Sep 17 00:00:00 2001 From: nesquack Date: Sat, 20 Dec 2025 12:52:53 -0500 Subject: [PATCH 14/21] add AssetTypeValueIterator, letting us skip reading fields we don't care about --- .../AssetTypeClass/AssetTypeTemplateField.cs | 215 +++++++------ .../AssetTypeClass/AssetTypeValueIterator.cs | 300 ++++++++++++++++++ .../Standard/AssetsFileFormat/TypeTreeNode.cs | 2 + 3 files changed, 424 insertions(+), 93 deletions(-) create mode 100644 AssetTools.NET/Standard/AssetTypeClass/AssetTypeValueIterator.cs diff --git a/AssetTools.NET/Standard/AssetTypeClass/AssetTypeTemplateField.cs b/AssetTools.NET/Standard/AssetTypeClass/AssetTypeTemplateField.cs index 19bebf6..9df319e 100644 --- a/AssetTools.NET/Standard/AssetTypeClass/AssetTypeTemplateField.cs +++ b/AssetTools.NET/Standard/AssetTypeClass/AssetTypeTemplateField.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Runtime.CompilerServices; namespace AssetsTools.NET { @@ -255,108 +256,136 @@ public AssetTypeValueField ReadType(AssetsFileReader reader, AssetTypeValueField } else { - if (type == AssetValueType.String) + ReadPrimitiveType(reader, valueField, type, refMan); + } + + } + return valueField; + } + + /// + /// Deserialize a single primtive field and its children. + /// This method only works for strings, numbers, and ManagedReferencesRegistry. + /// + /// The reader to use. + /// The empty base value field to use. + /// The value type of the template field. + /// The ref type manager to use, if reading a MonoBehaviour using a ref type. + /// The deserialized base field. +#if NETSTANDARD2_0_OR_GREATER + [MethodImpl(MethodImplOptions.AggressiveInlining)] +#endif + public void ReadPrimitiveType(AssetsFileReader reader, AssetTypeValueField valueField, AssetValueType type, RefTypeManager refMan) + { + if (type == AssetValueType.String) + { + valueField.Children = new List(0); + int length = reader.ReadInt32(); + valueField.Value = new AssetTypeValue(reader.ReadBytes(length), true); + reader.Align(); + } + else if (type == AssetValueType.ManagedReferencesRegistry) + { + ReadManagedReferencesRegistryType(reader, valueField, refMan); + } + else + { + int childCount = valueField.TemplateField.Children.Count; + if (childCount == 0) + { + valueField.Children = new List(0); + switch (type) { - valueField.Children = new List(0); - int length = reader.ReadInt32(); - valueField.Value = new AssetTypeValue(reader.ReadBytes(length), true); - reader.Align(); + case AssetValueType.Int8: + valueField.Value = new AssetTypeValue(reader.ReadSByte()); + break; + case AssetValueType.UInt8: + valueField.Value = new AssetTypeValue(reader.ReadByte()); + break; + case AssetValueType.Bool: + valueField.Value = new AssetTypeValue(reader.ReadBoolean()); + break; + case AssetValueType.Int16: + valueField.Value = new AssetTypeValue(reader.ReadInt16()); + break; + case AssetValueType.UInt16: + valueField.Value = new AssetTypeValue(reader.ReadUInt16()); + break; + case AssetValueType.Int32: + valueField.Value = new AssetTypeValue(reader.ReadInt32()); + break; + case AssetValueType.UInt32: + valueField.Value = new AssetTypeValue(reader.ReadUInt32()); + break; + case AssetValueType.Int64: + valueField.Value = new AssetTypeValue(reader.ReadInt64()); + break; + case AssetValueType.UInt64: + valueField.Value = new AssetTypeValue(reader.ReadUInt64()); + break; + case AssetValueType.Float: + valueField.Value = new AssetTypeValue(reader.ReadSingle()); + break; + case AssetValueType.Double: + valueField.Value = new AssetTypeValue(reader.ReadDouble()); + break; } - else if (type == AssetValueType.ManagedReferencesRegistry) - { - // todo: error handling like in array - if (refMan == null) - throw new Exception("refMan MUST be set to deserialize objects with ref types!"); - valueField.Children = new List(0); - ManagedReferencesRegistry registry = new ManagedReferencesRegistry(); - valueField.Value = new AssetTypeValue(registry); - int registryChildCount = valueField.TemplateField.Children.Count; - if (registryChildCount != 2) - throw new Exception($"Expected ManagedReferencesRegistry to have two children, found {registryChildCount} instead!"); + if (valueField.TemplateField.IsAligned) + reader.Align(); + } + else if (type != AssetValueType.None) + { + throw new Exception("Cannot read value of field with children!"); + } + } + } + + /// + /// Deserialize a ManagedReferencesRegistry field. + /// + /// The reader to use. + /// The empty base value field to use. + /// The ref type manager to use, if reading a MonoBehaviour using a ref type. + /// The deserialized base field. + public void ReadManagedReferencesRegistryType(AssetsFileReader reader, AssetTypeValueField valueField, RefTypeManager refMan) + { + if (refMan == null) + throw new Exception($"{nameof(refMan)} must be non-null to deserialize objects with ref types."); + + valueField.Children = new List(0); + ManagedReferencesRegistry registry = new ManagedReferencesRegistry(); + valueField.Value = new AssetTypeValue(registry); + int registryChildCount = valueField.TemplateField.Children.Count; + if (registryChildCount != 2) + throw new Exception($"Expected ManagedReferencesRegistry to have two children, found {registryChildCount} instead!"); - registry.version = reader.ReadInt32(); - registry.references = new List(); + registry.version = reader.ReadInt32(); + registry.references = new List(); - if (registry.version == 1) - { - while (true) - { - // rid is consecutive starting at 0 - var refdObject = MakeReferencedObject(reader, registry.version, registry.references.Count, refMan); - if (refdObject.type.Equals(AssetTypeReference.TERMINUS)) - { - break; - } - registry.references.Add(refdObject); - } - } - else - { - int childCount = reader.ReadInt32(); - for (int i = 0; i < childCount; i++) - { - // rid is read from data - var refdObject = MakeReferencedObject(reader, registry.version, -1, refMan); - registry.references.Add(refdObject); - } - } - } - else + if (registry.version == 1) + { + while (true) + { + // rid is consecutive starting at 0 + var refdObject = MakeReferencedObject(reader, registry.version, registry.references.Count, refMan); + if (refdObject.type.Equals(AssetTypeReference.TERMINUS)) { - int childCount = valueField.TemplateField.Children.Count; - if (childCount == 0) - { - valueField.Children = new List(0); - switch (valueField.TemplateField.ValueType) - { - case AssetValueType.Int8: - valueField.Value = new AssetTypeValue(reader.ReadSByte()); - break; - case AssetValueType.UInt8: - valueField.Value = new AssetTypeValue(reader.ReadByte()); - break; - case AssetValueType.Bool: - valueField.Value = new AssetTypeValue(reader.ReadBoolean()); - break; - case AssetValueType.Int16: - valueField.Value = new AssetTypeValue(reader.ReadInt16()); - break; - case AssetValueType.UInt16: - valueField.Value = new AssetTypeValue(reader.ReadUInt16()); - break; - case AssetValueType.Int32: - valueField.Value = new AssetTypeValue(reader.ReadInt32()); - break; - case AssetValueType.UInt32: - valueField.Value = new AssetTypeValue(reader.ReadUInt32()); - break; - case AssetValueType.Int64: - valueField.Value = new AssetTypeValue(reader.ReadInt64()); - break; - case AssetValueType.UInt64: - valueField.Value = new AssetTypeValue(reader.ReadUInt64()); - break; - case AssetValueType.Float: - valueField.Value = new AssetTypeValue(reader.ReadSingle()); - break; - case AssetValueType.Double: - valueField.Value = new AssetTypeValue(reader.ReadDouble()); - break; - } - - if (valueField.TemplateField.IsAligned) - reader.Align(); - } - else if (valueField.TemplateField.ValueType != AssetValueType.None) - { - throw new Exception("Cannot read value of field with children!"); - } + break; } + registry.references.Add(refdObject); + } + } + else + { + int childCount = reader.ReadInt32(); + for (int i = 0; i < childCount; i++) + { + // rid is read from data + var refdObject = MakeReferencedObject(reader, registry.version, -1, refMan); + registry.references.Add(refdObject); } - } - return valueField; } public AssetTypeTemplateField this[string name] diff --git a/AssetTools.NET/Standard/AssetTypeClass/AssetTypeValueIterator.cs b/AssetTools.NET/Standard/AssetTypeClass/AssetTypeValueIterator.cs new file mode 100644 index 0000000..c6d7807 --- /dev/null +++ b/AssetTools.NET/Standard/AssetTypeClass/AssetTypeValueIterator.cs @@ -0,0 +1,300 @@ +using System; +using System.Collections.Generic; + +namespace AssetsTools.NET +{ + public class AssetTypeValueIterator + { + private AssetTypeTemplateField baseTempField; + private AssetsFileReader reader; + private RefTypeManager refMan; + private readonly Stack> tempFieldStack; + + private AssetTypeValueField valueFieldCache; + private readonly long basePosition; + + public AssetTypeTemplateField TempField; + + public AssetTypeValueField ReadValueField() + { + if (valueFieldCache == null) + { + valueFieldCache = new AssetTypeValueField() + { + TemplateField = TempField + }; + + // rewind since we already read the length + if (TempField.IsArray && TempField.ValueType != AssetValueType.ByteArray) + { + reader.Position -= 4; + } + + TempField.ReadType(reader, valueFieldCache, refMan); + + // prevent iterating the children now that we've read it + if (TempField.Children.Count > 0 && TempField.ValueType != AssetValueType.String) + { + IEnumerator topItem = tempFieldStack.Peek(); + do + { + // special case for arrays: we need to align here rather than below + // because we create child iterators for arrays rather than depend + // on the template field. + if (topItem.Current != null + && topItem.Current.IsArray + && topItem.Current.ValueType != AssetValueType.ByteArray + && topItem.Current.IsAligned) + { + reader.Align(); + } + } while (topItem.MoveNext()); + } + } + return valueFieldCache; + } + + public List TempFieldStack + { + get + { + List stack = new List(tempFieldStack.Count); + foreach (IEnumerator stackItem in tempFieldStack) + { + stack.Add(stackItem.Current); + } + + return stack; + } + } + + public int Depth + { + get + { + bool canHaveChildren = TempField.ValueType switch + { + AssetValueType.None => true, + AssetValueType.Bool => false, + AssetValueType.Int8 => false, + AssetValueType.UInt8 => false, + AssetValueType.Int16 => false, + AssetValueType.UInt16 => false, + AssetValueType.Int32 => false, + AssetValueType.UInt32 => false, + AssetValueType.Int64 => false, + AssetValueType.UInt64 => false, + AssetValueType.Float => false, + AssetValueType.Double => false, + AssetValueType.String => false, + AssetValueType.Array => true, + AssetValueType.ByteArray => false, + AssetValueType.ManagedReferencesRegistry => false, + _ => throw new Exception("Invalid value type") + }; + return tempFieldStack.Count - (canHaveChildren ? 1 : 0); + } + } + + public int ReadPosition + { + get + { + return (int)(reader.Position - basePosition); + } + } + + /// + /// Create an AssetTypeValueIterator, which allows walking an asset's field tree without + /// allocating unnecessary values. If is locked behind a lock, you + /// should manually take the lock while using the iterator. The current iterating position + /// is not remembered by this iterator. Instead, the reader's position is relied on. + /// + /// The template base field to use. + /// The reader to use, set the correct position. + /// The ref type manager to use, if reading a MonoBehaviour using a ref type. + public AssetTypeValueIterator(AssetTypeTemplateField templateField, AssetsFileReader reader, RefTypeManager refMan = null) + { + baseTempField = templateField; + this.reader = reader; + this.refMan = refMan; + + basePosition = reader.Position; + + tempFieldStack = new Stack>(); + Reset(); + } + + /// + /// Create an AssetTypeValueIterator, which allows walking an asset's field tree without + /// allocating unnecessary values. If is locked behind a lock, you + /// should manually take the lock while using the iterator. The current iterating position + /// is not remembered by this iterator. Instead, the reader's position is relied on. + /// + /// The template base field to use. + /// The reader to use. + /// The position to start reading from. + /// The ref type manager to use, if reading a MonoBehaviour using a ref type. + public AssetTypeValueIterator(AssetTypeTemplateField templateField, AssetsFileReader reader, long position, RefTypeManager refMan = null) + : this(templateField, ReaderWithSetPos(reader, position), refMan) + { + } + + private static AssetsFileReader ReaderWithSetPos(AssetsFileReader reader, long position) + { + reader.Position = position; + return reader; + } + + public void Reset() + { + tempFieldStack.Clear(); + tempFieldStack.Push(baseTempField.Children.GetEnumerator()); + + TempField = baseTempField; + valueFieldCache = null; + } + + public void Reset(AssetTypeTemplateField templateField, AssetsFileReader reader, RefTypeManager refMan = null) + { + baseTempField = templateField; + this.reader = reader; + this.refMan = refMan; + + tempFieldStack.Clear(); + tempFieldStack.Push(baseTempField.Children.GetEnumerator()); + + TempField = baseTempField; + valueFieldCache = null; + } + + public bool ReadNext() + { + if (tempFieldStack.Count == 0) + { + return false; + } + + // move reader to next field if we didn't read it + if (valueFieldCache == null) + { + if (TempField.IsArray) + { + if (TempField.ValueType == AssetValueType.ByteArray) + { + int byteLen = reader.ReadInt32(); + reader.Position += byteLen; + + if (TempField.IsAligned) + { + reader.Align(); + } + } + } + else + { + if (TempField.ValueType != AssetValueType.None) + { + bool aligned = TempField.IsAligned; + switch (TempField.ValueType) + { + case AssetValueType.Bool: + case AssetValueType.Int8: + case AssetValueType.UInt8: + reader.Position += 1; + break; + case AssetValueType.Int16: + case AssetValueType.UInt16: + reader.Position += 2; + break; + case AssetValueType.Int32: + case AssetValueType.UInt32: + case AssetValueType.Float: + case AssetValueType.Double: + reader.Position += 4; + break; + case AssetValueType.Int64: + case AssetValueType.UInt64: + reader.Position += 8; + break; + case AssetValueType.String: + int stringLen = reader.ReadInt32(); + reader.Position += stringLen; + + if (TempField.Children.Count > 0) + aligned = TempField.Children[0].IsAligned; + else + aligned = true; + + break; + case AssetValueType.ManagedReferencesRegistry: + // this allocates, but I'm skipping writing code to do this the "right" way + AssetTypeValueField ignored = new AssetTypeValueField(); + TempField.ReadManagedReferencesRegistryType(reader, ignored, refMan); + break; + } + + if (aligned) + { + reader.Align(); + } + } + } + } + + IEnumerator topField = tempFieldStack.Peek(); + while (true) + { + // special case for arrays: we need to align here rather than below + // because we create child iterators for arrays rather than depend + // on the template field. + if (topField.Current != null + && topField.Current.IsArray + && topField.Current.ValueType != AssetValueType.ByteArray + && topField.Current.IsAligned) + { + reader.Align(); + } + + if (topField.MoveNext()) + { + break; + } + + tempFieldStack.Pop(); + if (tempFieldStack.Count == 0) + { + return false; + } + + topField = tempFieldStack.Peek(); + } + + AssetTypeTemplateField childField = topField.Current; + if (childField.IsArray && childField.ValueType != AssetValueType.ByteArray) + { + int arrChildCount = reader.ReadInt32(); + tempFieldStack.Push(GetArrayEnumerator(childField.Children[1], arrChildCount)); + } + else if (childField.ValueType == AssetValueType.None) + { + if (childField.Children.Count > 0) + { + tempFieldStack.Push(childField.Children.GetEnumerator()); + } + } + + TempField = childField; + valueFieldCache = null; + return true; + } + + private IEnumerator GetArrayEnumerator(AssetTypeTemplateField templateField, int times) + { + for (int i = 0; i < times; i++) + { + yield return templateField; + } + } + } +} diff --git a/AssetTools.NET/Standard/AssetsFileFormat/TypeTreeNode.cs b/AssetTools.NET/Standard/AssetsFileFormat/TypeTreeNode.cs index e7fbeb4..8aebbc6 100644 --- a/AssetTools.NET/Standard/AssetsFileFormat/TypeTreeNode.cs +++ b/AssetTools.NET/Standard/AssetsFileFormat/TypeTreeNode.cs @@ -86,6 +86,8 @@ public void Write(AssetsFileWriter writer, uint version) } } + // todo: these two GetXXXString methods should be using caching + /// /// Get the type name from the string table (from ). /// From c7895303a05686c4e613dc0a7e3954411a8f6b0c Mon Sep 17 00:00:00 2001 From: Hikaro <35894722+hikarosato@users.noreply.github.com> Date: Sat, 20 Dec 2025 20:22:58 +0200 Subject: [PATCH 15/21] Fixed bug --- AssetTools.NET/Extra/AssetsManager/AssetsManager.Assets.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AssetTools.NET/Extra/AssetsManager/AssetsManager.Assets.cs b/AssetTools.NET/Extra/AssetsManager/AssetsManager.Assets.cs index dc28d86..dfb7faf 100644 --- a/AssetTools.NET/Extra/AssetsManager/AssetsManager.Assets.cs +++ b/AssetTools.NET/Extra/AssetsManager/AssetsManager.Assets.cs @@ -6,7 +6,7 @@ public partial class AssetsManager { public static string GetFileLookupKey(string path) { - return Path.GetFileName(path).ToLower(); + return Path.GetFullPath(path).ToLower(); } private void LoadAssetsFileDependencies(AssetsFileInstance fileInst, string path, BundleFileInstance bunInst) From 1dbc18cbeb90ae759d458f41ce363a67ce711a6a Mon Sep 17 00:00:00 2001 From: Hikaro <35894722+hikarosato@users.noreply.github.com> Date: Sat, 20 Dec 2025 21:24:40 +0200 Subject: [PATCH 16/21] Better solution by nesrak1 --- .../Extra/AssetsManager/AssetsManager.Assets.cs | 2 +- .../Extra/AssetsManager/AssetsManager.Bundle.cs | 11 ++++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/AssetTools.NET/Extra/AssetsManager/AssetsManager.Assets.cs b/AssetTools.NET/Extra/AssetsManager/AssetsManager.Assets.cs index dfb7faf..dc28d86 100644 --- a/AssetTools.NET/Extra/AssetsManager/AssetsManager.Assets.cs +++ b/AssetTools.NET/Extra/AssetsManager/AssetsManager.Assets.cs @@ -6,7 +6,7 @@ public partial class AssetsManager { public static string GetFileLookupKey(string path) { - return Path.GetFullPath(path).ToLower(); + return Path.GetFileName(path).ToLower(); } private void LoadAssetsFileDependencies(AssetsFileInstance fileInst, string path, BundleFileInstance bunInst) diff --git a/AssetTools.NET/Extra/AssetsManager/AssetsManager.Bundle.cs b/AssetTools.NET/Extra/AssetsManager/AssetsManager.Bundle.cs index d0af9b3..addb584 100644 --- a/AssetTools.NET/Extra/AssetsManager/AssetsManager.Bundle.cs +++ b/AssetTools.NET/Extra/AssetsManager/AssetsManager.Bundle.cs @@ -4,6 +4,11 @@ namespace AssetsTools.NET.Extra { public partial class AssetsManager { + public static string GetBundleLookupKey(string path) + { + return Path.GetFullPath(path); + } + /// /// Load a from a stream with a path. /// Use the version of this method to skip the path argument. @@ -17,7 +22,7 @@ public partial class AssetsManager public BundleFileInstance LoadBundleFile(Stream stream, string path, bool unpackIfPacked = true) { BundleFileInstance bunInst; - string lookupKey = GetFileLookupKey(path); + string lookupKey = GetBundleLookupKey(path); if (BundleLookup.TryGetValue(lookupKey, out bunInst)) return bunInst; @@ -68,7 +73,7 @@ public BundleFileInstance LoadBundleFile(string path, bool unpackIfPacked = true /// True if the file was found and closed, and false if it wasn't found. public bool UnloadBundleFile(string path) { - string lookupKey = GetFileLookupKey(path); + string lookupKey = GetBundleLookupKey(path); if (BundleLookup.TryGetValue(lookupKey, out BundleFileInstance bunInst)) { bunInst.file.Close(); @@ -109,7 +114,7 @@ public bool UnloadBundleFile(BundleFileInstance bunInst) if (Bundles.Contains(bunInst)) { - string lookupKey = GetFileLookupKey(bunInst.path); + string lookupKey = GetBundleLookupKey(bunInst.path); lock (BundleLookup) { lock (Bundles) From d7c25c7d3ddd671afd6a6392625722796140f841 Mon Sep 17 00:00:00 2001 From: nesquack Date: Sat, 20 Dec 2025 14:28:46 -0500 Subject: [PATCH 17/21] update nuspec --- AssetTools.NET/AssetsTools.NET.nuspec | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/AssetTools.NET/AssetsTools.NET.nuspec b/AssetTools.NET/AssetsTools.NET.nuspec index 19a3a5d..95eea86 100644 --- a/AssetTools.NET/AssetsTools.NET.nuspec +++ b/AssetTools.NET/AssetsTools.NET.nuspec @@ -2,7 +2,7 @@ AssetsTools.NET - 3.0.2 + 3.0.3 nesrak1 nesrak1 false @@ -11,10 +11,12 @@ icon.png An assets/bundle library, inspired by UABE's AssetsTools An assets/bundle library, inspired by UABE's AssetsTools. Read and write assets and bundle files from engine versions 5.x and later. For most cases, you'll need the class data file which you can find in the AssetRipper/Tpk repo. See the wiki for usage and examples. - v3.0.2 - bug fixes, template field [] syntax, lz4 fast compress option + v3.0.3 - added back .net 3.5 support, fixed typetrees with unicode, added field iterator helper class https://github.com/nesrak1/AssetsTools.NET + + @@ -24,6 +26,9 @@ + + + From afcabbdbe00cc4665a36437614151b4751b57ff4 Mon Sep 17 00:00:00 2001 From: flibber-hk <76987839+flibber-hk@users.noreply.github.com> Date: Fri, 2 Jan 2026 06:56:04 +0000 Subject: [PATCH 18/21] Fix position increment for AssetValueType.Double Not a common bug because unity rarely uses doubles, but it shows up in e.g. PlayableDirector.initialTime --- .../Standard/AssetTypeClass/AssetTypeValueIterator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/AssetTools.NET/Standard/AssetTypeClass/AssetTypeValueIterator.cs b/AssetTools.NET/Standard/AssetTypeClass/AssetTypeValueIterator.cs index c6d7807..f239c83 100644 --- a/AssetTools.NET/Standard/AssetTypeClass/AssetTypeValueIterator.cs +++ b/AssetTools.NET/Standard/AssetTypeClass/AssetTypeValueIterator.cs @@ -210,11 +210,11 @@ public bool ReadNext() case AssetValueType.Int32: case AssetValueType.UInt32: case AssetValueType.Float: - case AssetValueType.Double: reader.Position += 4; break; case AssetValueType.Int64: case AssetValueType.UInt64: + case AssetValueType.Double: reader.Position += 8; break; case AssetValueType.String: From 57357f193fa4532c31b7569d4b0f9b74d04d12e8 Mon Sep 17 00:00:00 2001 From: nesquack Date: Sun, 11 Jan 2026 12:54:15 -0500 Subject: [PATCH 19/21] fix iterator for aligned arrays --- .../Standard/AssetTypeClass/AssetTypeValueIterator.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/AssetTools.NET/Standard/AssetTypeClass/AssetTypeValueIterator.cs b/AssetTools.NET/Standard/AssetTypeClass/AssetTypeValueIterator.cs index f239c83..cfbdd58 100644 --- a/AssetTools.NET/Standard/AssetTypeClass/AssetTypeValueIterator.cs +++ b/AssetTools.NET/Standard/AssetTypeClass/AssetTypeValueIterator.cs @@ -249,8 +249,7 @@ public bool ReadNext() // because we create child iterators for arrays rather than depend // on the template field. if (topField.Current != null - && topField.Current.IsArray - && topField.Current.ValueType != AssetValueType.ByteArray + && (!topField.Current.IsArray || topField.Current.ValueType != AssetValueType.ByteArray) && topField.Current.IsAligned) { reader.Align(); From b3edace27ddd9a8429c813bd681cb98fcd78ad0b Mon Sep 17 00:00:00 2001 From: nesquack Date: Sun, 11 Jan 2026 12:54:55 -0500 Subject: [PATCH 20/21] restore < 0 monobehaviour support (5.4 and earlier) --- .../AssetsManager.Deserialization.cs | 27 +- .../CommonMonoTemplateHelper.cs | 472 ++++++++++++++---- AssetsTools.NET.Cpp2IL/Cpp2IlTempGenerator.cs | 3 +- .../MonoCecilTempGenerator.cs | 3 +- 4 files changed, 391 insertions(+), 114 deletions(-) diff --git a/AssetTools.NET/Extra/AssetsManager/AssetsManager.Deserialization.cs b/AssetTools.NET/Extra/AssetsManager/AssetsManager.Deserialization.cs index 969ea63..c98e2e2 100644 --- a/AssetTools.NET/Extra/AssetsManager/AssetsManager.Deserialization.cs +++ b/AssetTools.NET/Extra/AssetsManager/AssetsManager.Deserialization.cs @@ -60,7 +60,8 @@ public AssetTypeTemplateField GetTemplateBaseField( AssetTypeTemplateField baseField = null; // if non-monobehaviour type is in cache, return the cached item - if (UseTemplateFieldCache && typeId != (int)AssetClassID.MonoBehaviour && templateFieldCache.TryGetValue(typeId, out baseField)) + bool isMonoBehaviourTypeId = typeId == (int)AssetClassID.MonoBehaviour || typeId < 0; + if (UseTemplateFieldCache && !isMonoBehaviourTypeId && templateFieldCache.TryGetValue(typeId, out baseField)) { return baseField; } @@ -74,7 +75,7 @@ public AssetTypeTemplateField GetTemplateBaseField( // load from that instead if (hasTypeTree && (!forceFromCldb || ClassDatabase == null)) { - if (UseMonoTemplateFieldCache && typeId == (int)AssetClassID.MonoBehaviour) + if (UseMonoTemplateFieldCache && isMonoBehaviourTypeId) { if (monoTypeTreeTemplateFieldCache.TryGetValue(inst, out ConcurrentDictionary templates) && templates.TryGetValue(scriptIndex, out baseField)) @@ -89,11 +90,11 @@ public AssetTypeTemplateField GetTemplateBaseField( baseField = new AssetTypeTemplateField(); baseField.FromTypeTree(ttType); - if (UseTemplateFieldCache && typeId != (int)AssetClassID.MonoBehaviour) + if (UseTemplateFieldCache && !isMonoBehaviourTypeId) { templateFieldCache[typeId] = baseField; } - else if (UseMonoTemplateFieldCache && typeId == (uint)AssetClassID.MonoBehaviour) + else if (UseMonoTemplateFieldCache && isMonoBehaviourTypeId) { if (!monoTypeTreeTemplateFieldCache.TryGetValue(inst, out ConcurrentDictionary templates)) { @@ -107,9 +108,9 @@ public AssetTypeTemplateField GetTemplateBaseField( } // if we cached a monobehaviour from a class database, clone a copy - if (UseTemplateFieldCache && UseMonoTemplateFieldCache && typeId == (int)AssetClassID.MonoBehaviour) + if (UseTemplateFieldCache && UseMonoTemplateFieldCache && isMonoBehaviourTypeId) { - if (templateFieldCache.TryGetValue(typeId, out baseField)) + if (templateFieldCache.TryGetValue((int)AssetClassID.MonoBehaviour, out baseField)) { baseField = baseField.Clone(); } @@ -125,7 +126,9 @@ public AssetTypeTemplateField GetTemplateBaseField( return null; } - ClassDatabaseType cldbType = ClassDatabase.FindAssetClassByID(typeId); + int fixedTypeId = isMonoBehaviourTypeId ? (int)AssetClassID.MonoBehaviour : typeId; + + ClassDatabaseType cldbType = ClassDatabase.FindAssetClassByID(fixedTypeId); if (cldbType == null) { return null; @@ -138,13 +141,13 @@ public AssetTypeTemplateField GetTemplateBaseField( if (UseTemplateFieldCache) { - if (typeId == (int)AssetClassID.MonoBehaviour) + if (fixedTypeId == (int)AssetClassID.MonoBehaviour) { - templateFieldCache[typeId] = baseField.Clone(); + templateFieldCache[fixedTypeId] = baseField.Clone(); } else { - templateFieldCache[typeId] = baseField; + templateFieldCache[fixedTypeId] = baseField; } } } @@ -155,7 +158,7 @@ public AssetTypeTemplateField GetTemplateBaseField( // but this is safer) and then passing the script from there to // the temp generator. we then append those fields to the base. bool skipMonoBehaviourFields = Net35Polyfill.HasFlag(readFlags, AssetReadFlags.SkipMonoBehaviourFields); - if (typeId == (int)AssetClassID.MonoBehaviour && MonoTempGenerator != null && !skipMonoBehaviourFields && reader != null) + if (isMonoBehaviourTypeId && MonoTempGenerator != null && !skipMonoBehaviourFields && reader != null) { AssetTypeValueField mbBaseField = baseField.MakeValue(reader, absByteStart); AssetPPtr msPtr = AssetPPtr.FromField(mbBaseField["m_Script"]); @@ -280,7 +283,7 @@ public AssetTypeTemplateField CreateTemplateBaseField(AssetsFileInstance inst, i } else { - if (id != (int)AssetClassID.MonoBehaviour || scriptIndex == 0xffff) + if ((id != (int)AssetClassID.MonoBehaviour && id >= 0) || scriptIndex == 0xffff) { var cldbType = ClassDatabase.FindAssetClassByID(id); templateField.FromClassDatabase(ClassDatabase, cldbType); diff --git a/AssetTools.NET/Extra/MonoDeserializer/CommonMonoTemplateHelper.cs b/AssetTools.NET/Extra/MonoDeserializer/CommonMonoTemplateHelper.cs index 22f4141..f22e3e4 100644 --- a/AssetTools.NET/Extra/MonoDeserializer/CommonMonoTemplateHelper.cs +++ b/AssetTools.NET/Extra/MonoDeserializer/CommonMonoTemplateHelper.cs @@ -88,45 +88,19 @@ public static class CommonMonoTemplateHelper ["System.String"] = "string" }; - private static readonly Dictionary baseToAssetValueType = new Dictionary() - { - ["System.Boolean"] = AssetValueType.UInt8, - ["System.SByte"] = AssetValueType.Int8, - ["System.Byte"] = AssetValueType.UInt8, - ["System.Char"] = AssetValueType.UInt16, - ["System.Int16"] = AssetValueType.Int16, - ["System.UInt16"] = AssetValueType.UInt16, - ["System.Int32"] = AssetValueType.Int32, - ["System.UInt32"] = AssetValueType.UInt32, - ["System.Int64"] = AssetValueType.Int64, - ["System.UInt64"] = AssetValueType.UInt64, - ["System.Double"] = AssetValueType.Double, - ["System.Single"] = AssetValueType.Float, - ["System.String"] = AssetValueType.String - }; - - #endregion - - #region Checks - - public static string ConvertBaseToPrimitive(string name) { if (baseToPrimitive.TryGetValue(name, out string primitiveName)) { return primitiveName; } + return name; } - public static AssetValueType ConvertBaseToAssetValueType(string name) - { - if (baseToAssetValueType.TryGetValue(name, out AssetValueType value)) - { - return value; - } - return AssetValueType.None; - } + #endregion + + #region Checks public static bool IsSpecialUnityType(string fullName) { @@ -145,20 +119,18 @@ public static bool IsPrimitiveType(string fullName) public static bool TypeAligns(AssetValueType valueType) { - if (valueType.Equals(AssetValueType.Bool) || - valueType.Equals(AssetValueType.Int8) || - valueType.Equals(AssetValueType.UInt8) || - valueType.Equals(AssetValueType.Int16) || - valueType.Equals(AssetValueType.UInt16)) - return true; - return false; + return valueType.Equals(AssetValueType.Bool) + || valueType.Equals(AssetValueType.Int8) + || valueType.Equals(AssetValueType.UInt8) + || valueType.Equals(AssetValueType.Int16) + || valueType.Equals(AssetValueType.UInt16); } public static int GetSerializationLimit(UnityVersion unityVersion) { - if (unityVersion.major > 2020 || - (unityVersion.major == 2020 && (unityVersion.minor >= 2 || (unityVersion.minor == 1 && unityVersion.patch >= 4))) || - (unityVersion.major == 2019 && unityVersion.minor == 4 && unityVersion.patch >= 9)) + if (unityVersion.major > 2020 + || (unityVersion.major == 2020 && (unityVersion.minor > 1 || (unityVersion.minor == 1 && unityVersion.patch >= 4))) + || (unityVersion.major == 2019 && unityVersion.minor == 4 && unityVersion.patch >= 9)) { return 10; } @@ -210,13 +182,16 @@ public static List Array(AssetTypeTemplateField field) { Name = "Array", Type = "Array", - ValueType = field.ValueType == AssetValueType.UInt8 ? AssetValueType.ByteArray : AssetValueType.Array, + ValueType = field.ValueType == AssetValueType.UInt8 + ? AssetValueType.ByteArray + : AssetValueType.Array, IsArray = true, IsAligned = true, HasValue = true, Children = new List { - Int("size"), CreateTemplateField("data", field.Type, field.ValueType, field.Children) + Int("size"), + CreateTemplateField("data", field.Type, field.ValueType, field.Children) } }; @@ -232,10 +207,18 @@ public static List ManagedReference(UnityVersion unityVe { if (unityVersion.major > 2021 || (unityVersion.major == 2021 && unityVersion.minor >= 2)) { - return new List { Long("rid") }; + return new List + { + Long("rid") + }; + } + else + { + return new List + { + Int("id") + }; } - - return new List { Int("id") }; } public static AssetTypeTemplateField ManagedReferencesRegistry(string name, UnityVersion unityVersion) => @@ -244,10 +227,20 @@ public static List ManagedReferencesRegistry(UnityVersio { if (unityVersion.major > 2021 || (unityVersion.major == 2021 && unityVersion.minor >= 2)) { - return new List { Int("version"), Vector(ReferencedObject("RefIds", unityVersion)) }; + return new List + { + Int("version"), + Vector(ReferencedObject("RefIds", unityVersion)) + }; + } + else + { + return new List + { + Int("version"), + ReferencedObject("00000000", unityVersion) + }; } - - return new List { Int("version"), ReferencedObject("00000000", unityVersion) }; } public static AssetTypeTemplateField ReferencedObject(string name, UnityVersion unityVersion) => CreateTemplateField(name, "ReferencedObject", ReferencedObject(unityVersion)); @@ -255,16 +248,32 @@ public static List ReferencedObject(UnityVersion unityVe { if (unityVersion.major > 2021 || (unityVersion.major == 2021 && unityVersion.minor >= 2)) { - return new List { Long("rid"), ReferencedManagedType("type"), CreateTemplateField("data", "ReferencedObjectData", AssetValueType.None) }; + return new List + { + Long("rid"), + ReferencedManagedType("type"), + CreateTemplateField("data", "ReferencedObjectData", AssetValueType.None) + }; + } + else + { + return new List + { + ReferencedManagedType("type"), + CreateTemplateField("data", "ReferencedObjectData", AssetValueType.None) + }; } - - return new List { ReferencedManagedType("type"), CreateTemplateField("data", "ReferencedObjectData", AssetValueType.None) }; } public static AssetTypeTemplateField ReferencedManagedType(string name) => CreateTemplateField(name, "ReferencedManagedType", ReferencedManagedType()); public static List ReferencedManagedType() { - return new List { String("class"), String("ns"), String("asm") }; + return new List + { + String("class"), + String("ns"), + String("asm") + }; } #endregion @@ -274,131 +283,362 @@ public static List ReferencedManagedType() public static AssetTypeTemplateField Gradient(string name, UnityVersion unityVersion) => CreateTemplateField(name, "Gradient", Gradient(unityVersion)); public static List Gradient(UnityVersion unityVersion) { - if (unityVersion.major > 2022 || (unityVersion.major == 2022 && unityVersion.minor >= 2)) + if (unityVersion.major > 5 || (unityVersion.major == 5 && unityVersion.minor >= 6)) { - return new List { - RGBAf("key0"), RGBAf("key1"), RGBAf("key2"), RGBAf("key3"), RGBAf("key4"), RGBAf("key5"), RGBAf("key6"), RGBAf("key7"), - UShort("ctime0"), UShort("ctime1"), UShort("ctime2"), UShort("ctime3"), UShort("ctime4"), UShort("ctime5"), UShort("ctime6"), UShort("ctime7"), - UShort("atime0"), UShort("atime1"), UShort("atime2"), UShort("atime3"), UShort("atime4"), UShort("atime5"), UShort("atime6"), UShort("atime7"), - Byte("m_Mode"), SByte("m_ColorSpace"), Byte("m_NumColorKeys"), Byte("m_NumAlphaKeys", true) + List fields = new List + { + RGBAf("key0"), + RGBAf("key1"), + RGBAf("key2"), + RGBAf("key3"), + RGBAf("key4"), + RGBAf("key5"), + RGBAf("key6"), + RGBAf("key7"), + UShort("ctime0"), + UShort("ctime1"), + UShort("ctime2"), + UShort("ctime3"), + UShort("ctime4"), + UShort("ctime5"), + UShort("ctime6"), + UShort("ctime7"), + UShort("atime0"), + UShort("atime1"), + UShort("atime2"), + UShort("atime3"), + UShort("atime4"), + UShort("atime5"), + UShort("atime6"), + UShort("atime7") }; - } - return new List { - RGBAf("key0"), RGBAf("key1"), RGBAf("key2"), RGBAf("key3"), RGBAf("key4"), RGBAf("key5"), RGBAf("key6"), RGBAf("key7"), - UShort("ctime0"), UShort("ctime1"), UShort("ctime2"), UShort("ctime3"), UShort("ctime4"), UShort("ctime5"), UShort("ctime6"), UShort("ctime7"), - UShort("atime0"), UShort("atime1"), UShort("atime2"), UShort("atime3"), UShort("atime4"), UShort("atime5"), UShort("atime6"), UShort("atime7"), - Int("m_Mode"), Byte("m_NumColorKeys"), Byte("m_NumAlphaKeys", true) - }; + if (unityVersion.major > 2022 || (unityVersion.major == 2022 && unityVersion.minor >= 2)) + { + fields.Add(Byte("m_Mode")); + fields.Add(SByte("m_ColorSpace")); + } + else + { + fields.Add(Int("m_Mode")); + } + + fields.Add(Byte("m_NumColorKeys")); + fields.Add(Byte("m_NumAlphaKeys", true)); + + return fields; + } + else + { + return new List() + { + RGBAi("key0"), + RGBAi("key1"), + RGBAi("key2"), + RGBAi("key3"), + RGBAi("key4"), + RGBAi("key5"), + RGBAi("key6"), + RGBAi("key7") + }; + } } public static AssetTypeTemplateField AnimationCurve(string name, UnityVersion unityVersion) => CreateTemplateField(name, "AnimationCurve", AnimationCurve(unityVersion)); public static List AnimationCurve(UnityVersion unityVersion) { - return new List { - Vector(Keyframe("m_Curve", unityVersion)), Int("m_PreInfinity"), Int("m_PostInfinity"), Int("m_RotationOrder") + List fields = new List + { + Vector(Keyframe("m_Curve", unityVersion)), + Int("m_PreInfinity"), + Int("m_PostInfinity") }; + + if (unityVersion.major > 5 || (unityVersion.major == 5 && unityVersion.minor >= 3)) + { + fields.Add(Int("m_RotationOrder")); + } + + return fields; } - //only supports 2019 right now public static AssetTypeTemplateField GUIStyle(string name, UnityVersion unityVersion) => CreateTemplateField(name, "GUIStyle", GUIStyle(unityVersion)); public static List GUIStyle(UnityVersion unityVersion) { - return new List { + List fields = new List + { String("m_Name"), - GUIStyleState("m_Normal", unityVersion), GUIStyleState("m_Hover", unityVersion), GUIStyleState("m_Active", unityVersion), GUIStyleState("m_Focused", unityVersion), - GUIStyleState("m_OnNormal", unityVersion), GUIStyleState("m_OnHover", unityVersion), GUIStyleState("m_OnActive", unityVersion), GUIStyleState("m_OnFocused", unityVersion), - RectOffset("m_Border"), RectOffset("m_Margin"), RectOffset("m_Padding"), RectOffset("m_Overflow"), - PPtr("m_Font", "Font", unityVersion), Int("m_FontSize"), Int("m_FontStyle"), - Int("m_Alignment"), Bool("m_WordWrap"), Bool("m_RichText", true), - Int("m_TextClipping"), Int("m_ImagePosition"), Vector2f("m_ContentOffset"), - Float("m_FixedWidth"), Float("m_FixedHeight"), Bool("m_StretchWidth"), Bool("m_StretchHeight", true) + GUIStyleState("m_Normal", unityVersion), + GUIStyleState("m_Hover", unityVersion), + GUIStyleState("m_Active", unityVersion), + GUIStyleState("m_Focused", unityVersion), + GUIStyleState("m_OnNormal", unityVersion), + GUIStyleState("m_OnHover", unityVersion), + GUIStyleState("m_OnActive", unityVersion), + GUIStyleState("m_OnFocused", unityVersion), + RectOffset("m_Border"), }; + + if (unityVersion.major >= 4) + { + fields.Add(RectOffset("m_Margin")); + fields.Add(RectOffset("m_Padding")); + } + else + { + fields.Add(RectOffset("m_Padding")); + fields.Add(RectOffset("m_Margin")); + } + + fields.Add(RectOffset("m_Overflow")); + fields.Add(PPtr("m_Font", "Font", unityVersion)); + + if (unityVersion.major >= 4) + { + fields.Add(Int("m_FontSize")); + fields.Add(Int("m_FontStyle")); + fields.Add(Int("m_Alignment")); + fields.Add(Bool("m_WordWrap")); + fields.Add(Bool("m_RichText", true)); + } + else + { + fields.Add(Int("m_ImagePosition")); + fields.Add(Int("m_Alignment")); + fields.Add(Bool("m_WordWrap", true)); + } + + fields.Add(Int("m_TextClipping")); + + if (unityVersion.major >= 4) + { + fields.Add(Int("m_ImagePosition")); + } + + fields.Add(Vector2f("m_ContentOffset")); + + if (unityVersion.major < 4) + { + fields.Add(Vector2f("m_ClipOffset")); + } + + fields.Add(Float("m_FixedWidth")); + fields.Add(Float("m_FixedHeight")); + + if (unityVersion.major >= 4) + { + fields.Add(Bool("m_StretchWidth")); + } + else + { + fields.Add(Int("m_FontSize")); + fields.Add(Int("m_FontStyle")); + fields.Add(Bool("m_StretchWidth", true)); + } + + fields.Add(Bool("m_StretchHeight", true)); + + return fields; } public static AssetTypeTemplateField Keyframe(string name, UnityVersion unityVersion) => CreateTemplateField(name, "Keyframe", Keyframe(unityVersion)); public static List Keyframe(UnityVersion unityVersion) { + List fields = new List + { + Float("time"), + Float("value"), + Float("inSlope"), + Float("outSlope") + }; + if (unityVersion.major >= 2018) { - return new List { - Float("time"), Float("value"), Float("inSlope"), Float("outSlope"), - Int("weightedMode"), Float("inWeight"), Float("outWeight") - }; + fields.Add(Int("weightedMode")); + fields.Add(Float("inWeight")); + fields.Add(Float("outWeight")); } - return new List { Float("time"), Float("value"), Float("inSlope"), Float("outSlope") }; + + return fields; } public static AssetTypeTemplateField GUIStyleState(string name, UnityVersion unityVersion) => CreateTemplateField(name, "GUIStyleState", GUIStyleState(unityVersion)); public static List GUIStyleState(UnityVersion unityVersion) { - return new List { PPtr("m_Background", "Texture2D", unityVersion), RGBAf("m_TextColor") }; + return new List + { + PPtr("m_Background", "Texture2D", unityVersion), + RGBAf("m_TextColor") + }; + } + + public static AssetTypeTemplateField SphericalHarmonicsL2(string name, UnityVersion unityVersion) => CreateTemplateField(name, "SphericalHarmonicsL2", SphericalHarmonicsL2(unityVersion)); + public static List SphericalHarmonicsL2(UnityVersion unityVersion) + { + List fields = new List(); + + if (unityVersion.major >= 5) + { + fields.Add(Float("sh[ 0]")); + fields.Add(Float("sh[ 1]")); + fields.Add(Float("sh[ 2]")); + fields.Add(Float("sh[ 3]")); + fields.Add(Float("sh[ 4]")); + fields.Add(Float("sh[ 5]")); + fields.Add(Float("sh[ 6]")); + fields.Add(Float("sh[ 7]")); + fields.Add(Float("sh[ 8]")); + fields.Add(Float("sh[ 9]")); + } + else + { + fields.Add(Float("sh[0]")); + fields.Add(Float("sh[1]")); + fields.Add(Float("sh[2]")); + fields.Add(Float("sh[3]")); + fields.Add(Float("sh[4]")); + fields.Add(Float("sh[5]")); + fields.Add(Float("sh[6]")); + fields.Add(Float("sh[7]")); + fields.Add(Float("sh[8]")); + fields.Add(Float("sh[9]")); + } + + fields.Add(Float("sh[10]")); + fields.Add(Float("sh[11]")); + fields.Add(Float("sh[12]")); + fields.Add(Float("sh[13]")); + fields.Add(Float("sh[14]")); + fields.Add(Float("sh[15]")); + fields.Add(Float("sh[16]")); + fields.Add(Float("sh[17]")); + fields.Add(Float("sh[18]")); + fields.Add(Float("sh[19]")); + fields.Add(Float("sh[20]")); + fields.Add(Float("sh[21]")); + fields.Add(Float("sh[22]")); + fields.Add(Float("sh[23]")); + fields.Add(Float("sh[24]")); + fields.Add(Float("sh[25]")); + fields.Add(Float("sh[26]")); + + return fields; } public static AssetTypeTemplateField RGBAf(string name) => CreateTemplateField(name, "ColorRGBA", RGBAf()); public static List RGBAf() { - return new List { Float("r"), Float("g"), Float("b"), Float("a") }; + return new List + { + Float("r"), + Float("g"), + Float("b"), + Float("a") + }; } public static AssetTypeTemplateField RGBAi(string name) => CreateTemplateField(name, "ColorRGBA", RGBAi()); public static List RGBAi() { - return new List { UInt("rgba") }; + return new List + { + UInt("rgba") + }; } public static AssetTypeTemplateField AABB(string name) => CreateTemplateField(name, "AABB", AABB()); public static List AABB() { - return new List { Vector3f("m_Center"), Vector3f("m_Extent") }; + return new List + { + Vector3f("m_Center"), + Vector3f("m_Extent") + }; } public static AssetTypeTemplateField BoundsInt(string name) => CreateTemplateField(name, "BoundsInt", BoundsInt()); public static List BoundsInt() { - return new List { Vector3Int("m_Position"), Vector3Int("m_Size") }; + return new List + { + Vector3Int("m_Position"), + Vector3Int("m_Size") + }; } public static AssetTypeTemplateField BitField(string name) => CreateTemplateField(name, "BitField", BitField()); public static List BitField() { - return new List { UInt("m_Bits") }; + return new List + { + UInt("m_Bits") + }; } public static AssetTypeTemplateField Rectf(string name) => CreateTemplateField(name, "Rectf", Rectf()); public static List Rectf() { - return new List { Float("x"), Float("y"), Float("width"), Float("height") }; + return new List + { + Float("x"), + Float("y"), + Float("width"), + Float("height") + }; } public static AssetTypeTemplateField RectOffset(string name) => CreateTemplateField(name, "RectOffset", RectOffset()); public static List RectOffset() { - return new List { Int("m_Left"), Int("m_Right"), Int("m_Top"), Int("m_Bottom") }; + return new List + { + Int("m_Left"), + Int("m_Right"), + Int("m_Top"), + Int("m_Bottom") + }; } public static AssetTypeTemplateField Vector2Int(string name) => CreateTemplateField(name, "int2_storage", Vector2Int()); public static List Vector2Int() { - return new List { Int("x"), Int("y") }; + return new List + { + Int("x"), + Int("y") + }; } public static AssetTypeTemplateField Vector3Int(string name) => CreateTemplateField(name, "int3_storage", Vector3Int()); public static List Vector3Int() { - return new List { Int("x"), Int("y"), Int("z") }; + return new List + { + Int("x"), + Int("y"), + Int("z") + }; } public static AssetTypeTemplateField Vector2f(string name) => CreateTemplateField(name, "Vector2f", Vector2f()); public static List Vector2f() { - return new List { Float("x"), Float("y") }; + return new List + { + Float("x"), + Float("y") + }; } public static AssetTypeTemplateField Vector3f(string name) => CreateTemplateField(name, "Vector3f", Vector3f()); public static List Vector3f() { - return new List { Float("x"), Float("y"), Float("z") }; + return new List + { + Float("x"), + Float("y"), + Float("z") + }; } public static AssetTypeTemplateField PPtr(string name, string typeName, UnityVersion unityVersion) => CreateTemplateField(name, $"PPtr<{typeName}>", PPtr(unityVersion)); @@ -406,17 +646,49 @@ public static List PPtr(UnityVersion unityVersion) { if (unityVersion.major >= 5) { - return new List { Int("m_FileID"), Long("m_PathID") }; + return new List + { + Int("m_FileID"), + Long("m_PathID") + }; + } + else + { + return new List + { + Int("m_FileID"), + Int("m_PathID") + }; } - - return new List { Int("m_FileID"), Int("m_PathID") }; } - // yes, there is a double string here - public static AssetTypeTemplateField PropertyName(string name) => CreateTemplateField(name, "string", PropertyName()); - public static List PropertyName() + public static AssetTypeTemplateField PropertyName(string name, UnityVersion unityVersion) => CreateTemplateField(name, "string", PropertyName(unityVersion)); + public static List PropertyName(UnityVersion unityVersion) { - return new List { String("id") }; + if (unityVersion.major > 2020 + || (unityVersion.major == 2020 && + (unityVersion.minor > 2 || (unityVersion.minor == 2 && (unityVersion.type != "a" || unityVersion.typeNum >= 16)))) + || (unityVersion.major == 2020 && + (unityVersion.minor > 1 || (unityVersion.minor == 1 && unityVersion.type != "a" && (unityVersion.type != "b" || unityVersion.typeNum >= 15)))) + || (unityVersion.major == 2019 && + (unityVersion.minor > 4 || (unityVersion.minor == 4 && (unityVersion.type != "a" || unityVersion.patch >= 3)))) + || (unityVersion.major == 2018 && + (unityVersion.minor > 4 || (unityVersion.minor == 4 && (unityVersion.type != "a" || unityVersion.patch >= 25)))) + ) + { + // yes, there is a double string here + return new List + { + String("id") + }; + } + else + { + return new List + { + Int("id") + }; + } } #endregion diff --git a/AssetsTools.NET.Cpp2IL/Cpp2IlTempGenerator.cs b/AssetsTools.NET.Cpp2IL/Cpp2IlTempGenerator.cs index f6a14f4..818caa6 100644 --- a/AssetsTools.NET.Cpp2IL/Cpp2IlTempGenerator.cs +++ b/AssetsTools.NET.Cpp2IL/Cpp2IlTempGenerator.cs @@ -502,7 +502,8 @@ private List SpecialUnity(TypeDefWithSelfRef type, int a "GUIStyle" => CommonMonoTemplateHelper.GUIStyle(_unityVersion), "Vector2Int" => CommonMonoTemplateHelper.Vector2Int(), "Vector3Int" => CommonMonoTemplateHelper.Vector3Int(), - "PropertyName" => CommonMonoTemplateHelper.PropertyName(), + "PropertyName" => CommonMonoTemplateHelper.PropertyName(_unityVersion), + "SphericalHarmonicsL2" => CommonMonoTemplateHelper.SphericalHarmonicsL2(_unityVersion), _ => Serialized(type, availableDepth) }; } diff --git a/AssetsTools.NET.MonoCecil/MonoCecilTempGenerator.cs b/AssetsTools.NET.MonoCecil/MonoCecilTempGenerator.cs index 1282f90..97ee6a7 100644 --- a/AssetsTools.NET.MonoCecil/MonoCecilTempGenerator.cs +++ b/AssetsTools.NET.MonoCecil/MonoCecilTempGenerator.cs @@ -425,7 +425,8 @@ private List SpecialUnity( "GUIStyle" => CommonMonoTemplateHelper.GUIStyle(unityVersion), "Vector2Int" => CommonMonoTemplateHelper.Vector2Int(), "Vector3Int" => CommonMonoTemplateHelper.Vector3Int(), - "PropertyName" => CommonMonoTemplateHelper.PropertyName(), + "PropertyName" => CommonMonoTemplateHelper.PropertyName(unityVersion), + "SphericalHarmonicsL2" => CommonMonoTemplateHelper.SphericalHarmonicsL2(unityVersion), _ => Serialized(type, availableDepth, ref usingManagedReference) }; } From 2256657c569a54cd95a32ab583ff905fb551d1cd Mon Sep 17 00:00:00 2001 From: Shua-github Date: Fri, 30 Jan 2026 09:07:56 +0800 Subject: [PATCH 21/21] add: AssetsTools.NET.Cpp2IL support byte construction --- AssetsTools.NET.Cpp2IL/Cpp2IlTempGenerator.cs | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/AssetsTools.NET.Cpp2IL/Cpp2IlTempGenerator.cs b/AssetsTools.NET.Cpp2IL/Cpp2IlTempGenerator.cs index 818caa6..bc73ce0 100644 --- a/AssetsTools.NET.Cpp2IL/Cpp2IlTempGenerator.cs +++ b/AssetsTools.NET.Cpp2IL/Cpp2IlTempGenerator.cs @@ -3,6 +3,7 @@ using LibCpp2IL.Metadata; using LibCpp2IL.Reflection; using System; +using System.IO; using System.Collections.Generic; using System.Diagnostics; using System.Linq; @@ -14,16 +15,23 @@ namespace AssetsTools.NET.Cpp2IL { public class Cpp2IlTempGenerator : IMonoBehaviourTemplateGenerator { - private readonly string _globalMetadataPath; - private readonly string _assemblyPath; + private readonly byte[] _globalMetadataBytes; + private readonly byte[] _assemblyBytes; private ATUnityVersion _unityVersion; private bool _initialized; private bool _anyFieldIsManagedReference; public Cpp2IlTempGenerator(string globalMetadataPath, string assemblyPath) { - _globalMetadataPath = globalMetadataPath; - _assemblyPath = assemblyPath; + _globalMetadataBytes = File.ReadAllBytes(globalMetadataPath); + _assemblyBytes = File.ReadAllBytes(assemblyPath); + ResetCpp2IL(); + } + + public Cpp2IlTempGenerator(byte[] globalMetadataBytes, byte[] assemblyBytes) + { + _globalMetadataBytes = globalMetadataBytes; + _assemblyBytes = assemblyBytes; ResetCpp2IL(); } @@ -63,7 +71,7 @@ public void SetUnityVersion(int major, int minor, int patch) public void InitializeCpp2IL() { ARUnityVersion arUnityVersion = ARUnityVersion.Parse(_unityVersion.ToString()); - if (!LibCpp2IlMain.LoadFromFile(_assemblyPath, _globalMetadataPath, arUnityVersion)) + if (!LibCpp2IlMain.Initialize(_assemblyBytes, _globalMetadataBytes, arUnityVersion)) { throw new Exception("Cpp2Il initialization failed"); }