Fixes and improvements for asset parsing via typetree

This commit is contained in:
VaDiM 2024-10-18 20:55:34 +03:00
parent 58ee2b8f1e
commit c37e2e65b7
12 changed files with 184 additions and 40 deletions

View File

@ -515,16 +515,17 @@ namespace AssetStudio
var jsonOptions = new JsonSerializerOptions var jsonOptions = new JsonSerializerOptions
{ {
Converters = { new JsonConverterHelper.ByteArrayConverter() }, Converters = { new JsonConverterHelper.ByteArrayConverter(), new JsonConverterHelper.PPtrConverter() },
NumberHandling = JsonNumberHandling.AllowNamedFloatingPointLiterals, NumberHandling = JsonNumberHandling.AllowNamedFloatingPointLiterals,
IncludeFields = true, IncludeFields = true,
}; };
var progressCount = assetsFileList.Sum(x => x.m_Objects.Count); var progressCount = assetsFileList.Sum(x => x.m_Objects.Count);
int i = 0; var i = 0;
Progress.Reset(); Progress.Reset();
foreach (var assetsFile in assetsFileList) foreach (var assetsFile in assetsFileList)
{ {
JsonConverterHelper.PPtrConverter.AssetsFile = assetsFile;
foreach (var objectInfo in assetsFile.m_Objects) foreach (var objectInfo in assetsFile.m_Objects)
{ {
var objectReader = new ObjectReader(assetsFile.reader, assetsFile, objectInfo); var objectReader = new ObjectReader(assetsFile.reader, assetsFile, objectInfo);
@ -542,7 +543,7 @@ namespace AssetStudio
break; break;
case ClassIDType.AnimationClip: case ClassIDType.AnimationClip:
obj = objectReader.serializedType?.m_Type != null && LoadingViaTypeTreeEnabled obj = objectReader.serializedType?.m_Type != null && LoadingViaTypeTreeEnabled
? new AnimationClip(objectReader, TypeTreeHelper.ReadType(objectReader.serializedType.m_Type, objectReader), jsonOptions) ? new AnimationClip(objectReader, TypeTreeHelper.ReadTypeByteArray(objectReader.serializedType.m_Type, objectReader), jsonOptions)
: new AnimationClip(objectReader); : new AnimationClip(objectReader);
break; break;
case ClassIDType.Animator: case ClassIDType.Animator:
@ -620,12 +621,12 @@ namespace AssetStudio
break; break;
case ClassIDType.Texture2D: case ClassIDType.Texture2D:
obj = objectReader.serializedType?.m_Type != null && LoadingViaTypeTreeEnabled obj = objectReader.serializedType?.m_Type != null && LoadingViaTypeTreeEnabled
? new Texture2D(objectReader, TypeTreeHelper.ReadType(objectReader.serializedType.m_Type, objectReader), jsonOptions) ? new Texture2D(objectReader, TypeTreeHelper.ReadTypeByteArray(objectReader.serializedType.m_Type, objectReader), jsonOptions)
: new Texture2D(objectReader); : new Texture2D(objectReader);
break; break;
case ClassIDType.Texture2DArray: case ClassIDType.Texture2DArray:
obj = objectReader.serializedType?.m_Type != null && LoadingViaTypeTreeEnabled obj = objectReader.serializedType?.m_Type != null && LoadingViaTypeTreeEnabled
? new Texture2DArray(objectReader, TypeTreeHelper.ReadType(objectReader.serializedType.m_Type, objectReader), jsonOptions) ? new Texture2DArray(objectReader, TypeTreeHelper.ReadTypeByteArray(objectReader.serializedType.m_Type, objectReader), jsonOptions)
: new Texture2DArray(objectReader); : new Texture2DArray(objectReader);
break; break;
case ClassIDType.Transform: case ClassIDType.Transform:

View File

@ -1,5 +1,4 @@
using System; using System;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
@ -1031,9 +1030,9 @@ namespace AssetStudio
public AnimationClip() { } public AnimationClip() { }
public AnimationClip(ObjectReader reader, IDictionary typeDict, JsonSerializerOptions jsonOptions) : base(reader) public AnimationClip(ObjectReader reader, byte[] type, JsonSerializerOptions jsonOptions) : base(reader)
{ {
var parsedAnimClip = JsonSerializer.Deserialize<AnimationClip>(JsonSerializer.SerializeToUtf8Bytes(typeDict, jsonOptions), jsonOptions); var parsedAnimClip = JsonSerializer.Deserialize<AnimationClip>(type, jsonOptions);
m_AnimationType = parsedAnimClip.m_AnimationType; m_AnimationType = parsedAnimClip.m_AnimationType;
m_Legacy = parsedAnimClip.m_Legacy; m_Legacy = parsedAnimClip.m_Legacy;
m_Compressed = parsedAnimClip.m_Compressed; m_Compressed = parsedAnimClip.m_Compressed;
@ -1052,8 +1051,6 @@ namespace AssetStudio
m_MuscleClip = parsedAnimClip.m_MuscleClip; m_MuscleClip = parsedAnimClip.m_MuscleClip;
m_ClipBindingConstant = parsedAnimClip.m_ClipBindingConstant; m_ClipBindingConstant = parsedAnimClip.m_ClipBindingConstant;
m_Events = parsedAnimClip.m_Events; m_Events = parsedAnimClip.m_Events;
typeDict.Clear();
} }
public AnimationClip(ObjectReader reader) : base(reader) public AnimationClip(ObjectReader reader) : base(reader)

View File

@ -19,6 +19,11 @@ namespace AssetStudio
public PPtr() { } public PPtr() { }
public void SetAssetsFile(SerializedFile assetsFile)
{
_assetsFile = assetsFile;
}
private bool TryGetAssetsFile(out SerializedFile result) private bool TryGetAssetsFile(out SerializedFile result)
{ {
result = null; result = null;

View File

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text.Json.Serialization;
namespace AssetStudio namespace AssetStudio
{ {
@ -45,6 +46,7 @@ namespace AssetStudio
public sealed class SpriteAtlas : NamedObject public sealed class SpriteAtlas : NamedObject
{ {
public PPtr<Sprite>[] m_PackedSprites; public PPtr<Sprite>[] m_PackedSprites;
[JsonConverter(typeof(JsonConverterHelper.RenderDataMapConverter))]
public Dictionary<KeyValuePair<Guid, long>, SpriteAtlasData> m_RenderDataMap; public Dictionary<KeyValuePair<Guid, long>, SpriteAtlasData> m_RenderDataMap;
public bool m_IsVariant; public bool m_IsVariant;

View File

@ -1,6 +1,6 @@
using System; using System;
using System.Collections;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization;
namespace AssetStudio namespace AssetStudio
{ {
@ -15,6 +15,7 @@ namespace AssetStudio
public GLTextureSettings m_TextureSettings; public GLTextureSettings m_TextureSettings;
public int m_ImageCount; public int m_ImageCount;
public byte[] m_PlatformBlob; public byte[] m_PlatformBlob;
[JsonPropertyName("image data")]
public ResourceReader image_data; public ResourceReader image_data;
public StreamingInfo m_StreamData; public StreamingInfo m_StreamData;
@ -53,9 +54,9 @@ namespace AssetStudio
byteSize = (uint)(m_Width * m_Height) * 4; byteSize = (uint)(m_Width * m_Height) * 4;
} }
public Texture2D(ObjectReader reader, IDictionary typeDict, JsonSerializerOptions jsonOptions) : base(reader) public Texture2D(ObjectReader reader, byte[] type, JsonSerializerOptions jsonOptions) : base(reader)
{ {
var parsedTex2d = JsonSerializer.Deserialize<Texture2D>(JsonSerializer.SerializeToUtf8Bytes(typeDict, jsonOptions), jsonOptions); var parsedTex2d = JsonSerializer.Deserialize<Texture2D>(type, jsonOptions);
m_Width = parsedTex2d.m_Width; m_Width = parsedTex2d.m_Width;
m_Height = parsedTex2d.m_Height; m_Height = parsedTex2d.m_Height;
m_CompleteImageSize = parsedTex2d.m_CompleteImageSize; m_CompleteImageSize = parsedTex2d.m_CompleteImageSize;
@ -70,7 +71,6 @@ namespace AssetStudio
image_data = !string.IsNullOrEmpty(m_StreamData?.path) image_data = !string.IsNullOrEmpty(m_StreamData?.path)
? new ResourceReader(m_StreamData.path, assetsFile, m_StreamData.offset, m_StreamData.size) ? new ResourceReader(m_StreamData.path, assetsFile, m_StreamData.offset, m_StreamData.size)
: new ResourceReader(reader, parsedTex2d.image_data.Offset, parsedTex2d.image_data.Size); : new ResourceReader(reader, parsedTex2d.image_data.Offset, parsedTex2d.image_data.Size);
typeDict.Clear();
} }
public Texture2D(ObjectReader reader) : base(reader) public Texture2D(ObjectReader reader) : base(reader)

View File

@ -1,6 +1,6 @@
using System.Collections; using System.Collections.Generic;
using System.Collections.Generic;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization;
namespace AssetStudio namespace AssetStudio
{ {
@ -14,6 +14,7 @@ namespace AssetStudio
public uint m_DataSize; public uint m_DataSize;
public GLTextureSettings m_TextureSettings; public GLTextureSettings m_TextureSettings;
public int m_ColorSpace; public int m_ColorSpace;
[JsonPropertyName("image data")]
public ResourceReader image_data; public ResourceReader image_data;
public StreamingInfo m_StreamData; public StreamingInfo m_StreamData;
public List<Texture2D> TextureList; public List<Texture2D> TextureList;
@ -50,9 +51,9 @@ namespace AssetStudio
TextureList = new List<Texture2D>(); TextureList = new List<Texture2D>();
} }
public Texture2DArray(ObjectReader reader, IDictionary typeDict, JsonSerializerOptions jsonOptions) : base(reader) public Texture2DArray(ObjectReader reader, byte[] type, JsonSerializerOptions jsonOptions) : base(reader)
{ {
var parsedTex2dArray = JsonSerializer.Deserialize<Texture2DArray>(JsonSerializer.SerializeToUtf8Bytes(typeDict, jsonOptions), jsonOptions); var parsedTex2dArray = JsonSerializer.Deserialize<Texture2DArray>(type, jsonOptions);
m_Width = parsedTex2dArray.m_Width; m_Width = parsedTex2dArray.m_Width;
m_Height = parsedTex2dArray.m_Height; m_Height = parsedTex2dArray.m_Height;
m_Depth = parsedTex2dArray.m_Depth; m_Depth = parsedTex2dArray.m_Depth;
@ -65,7 +66,6 @@ namespace AssetStudio
image_data = !string.IsNullOrEmpty(m_StreamData?.path) image_data = !string.IsNullOrEmpty(m_StreamData?.path)
? new ResourceReader(m_StreamData.path, assetsFile, m_StreamData.offset, m_StreamData.size) ? new ResourceReader(m_StreamData.path, assetsFile, m_StreamData.offset, m_StreamData.size)
: new ResourceReader(reader, parsedTex2dArray.image_data.Offset, parsedTex2dArray.image_data.Size); : new ResourceReader(reader, parsedTex2dArray.image_data.Offset, parsedTex2dArray.image_data.Size);
typeDict.Clear();
TextureList = new List<Texture2D>(); TextureList = new List<Texture2D>();
} }

View File

@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace AssetStudio
{
public static partial class JsonConverterHelper
{
public class ByteArrayConverter : JsonConverter<byte[]>
{
public override byte[] Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return reader.TokenType == JsonTokenType.StartArray
? JsonSerializer.Deserialize<List<byte>>(ref reader).ToArray() //JsonArray to ByteArray
: JsonSerializer.Deserialize<byte[]>(ref reader);
}
public override void Write(Utf8JsonWriter writer, byte[] value, JsonSerializerOptions options)
{
writer.WriteBase64StringValue(value);
}
}
}
}

View File

@ -1,28 +1,12 @@
using System; using System;
using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace AssetStudio namespace AssetStudio
{ {
public static class JsonConverterHelper public static partial class JsonConverterHelper
{ {
public class ByteArrayConverter : JsonConverter<byte[]>
{
public override byte[] Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return reader.TokenType == JsonTokenType.StartArray
? JsonSerializer.Deserialize<List<byte>>(ref reader).ToArray() //JsonArray to ByteArray
: JsonSerializer.Deserialize<byte[]>(ref reader);
}
public override void Write(Utf8JsonWriter writer, byte[] value, JsonSerializerOptions options)
{
writer.WriteBase64StringValue(value);
}
}
public class FloatConverter : JsonConverter<float> public class FloatConverter : JsonConverter<float>
{ {
public override float Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) public override float Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)

View File

@ -0,0 +1,53 @@
using System;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
namespace AssetStudio
{
public static partial class JsonConverterHelper
{
public class PPtrConverter : JsonConverterFactory
{
public static SerializedFile AssetsFile;
public override bool CanConvert(Type typeToConvert)
{
if (!typeToConvert.IsGenericType)
return false;
var generic = typeToConvert.GetGenericTypeDefinition();
return generic == typeof(PPtr<>);
}
public override JsonConverter CreateConverter(Type type, JsonSerializerOptions options)
{
var elementType = type.GetGenericArguments()[0];
var converter = (JsonConverter)Activator.CreateInstance(typeof(PPtrConverter<>).MakeGenericType(elementType), AssetsFile);
return converter;
}
}
private class PPtrConverter<T> : JsonConverter<PPtr<T>> where T : Object
{
private readonly SerializedFile _assetsFile;
public PPtrConverter(SerializedFile assetsFile)
{
_assetsFile = assetsFile;
}
public override PPtr<T> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var pptrObj = JsonSerializer.Deserialize<PPtr<T>>(ref reader);
pptrObj.SetAssetsFile(_assetsFile);
return pptrObj;
}
public override void Write(Utf8JsonWriter writer, PPtr<T> value, JsonSerializerOptions options)
{
JsonSerializer.Serialize(value);
}
}
}
}

View File

@ -0,0 +1,58 @@
using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;
namespace AssetStudio
{
public static partial class JsonConverterHelper
{
public class RenderDataMapConverter : JsonConverter<Dictionary<KeyValuePair<Guid, long>, SpriteAtlasData>>
{
public override Dictionary<KeyValuePair<Guid, long>, SpriteAtlasData> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var dataArray = JsonSerializer.Deserialize<KeyValuePair<JsonObject, SpriteAtlasData>[]>(ref reader, options);
var renderDataMap = new Dictionary<KeyValuePair<Guid, long>, SpriteAtlasData>(dataArray.Length);
foreach (var kvp in dataArray)
{
var jsonFirst = kvp.Key["first"];
var first = jsonFirst.Deserialize<GUID>(options).Convert();
var second = (long) kvp.Key["second"];
renderDataMap.Add(new KeyValuePair<Guid, long>(first, second), kvp.Value);
}
return renderDataMap;
}
public override void Write(Utf8JsonWriter writer, Dictionary<KeyValuePair<Guid, long>, SpriteAtlasData> value, JsonSerializerOptions options)
{
var jsonDict = new Dictionary<string, SpriteAtlasData>();
foreach (var kv in value)
{
var strKey = $"{kv.Key.Key}, {kv.Key.Value}";
jsonDict.Add(strKey, kv.Value);
}
var strValue = JsonSerializer.Serialize(jsonDict, options).Replace(" ", " ");
writer.WriteRawValue(strValue);
}
}
private class GUID
{
[JsonPropertyName("data[0]")] public uint data0;
[JsonPropertyName("data[1]")] public uint data1;
[JsonPropertyName("data[2]")] public uint data2;
[JsonPropertyName("data[3]")] public uint data3;
public Guid Convert()
{
var guidBytes = new byte[16];
BitConverter.GetBytes(data0).CopyTo(guidBytes, 0);
BitConverter.GetBytes(data1).CopyTo(guidBytes, 4);
BitConverter.GetBytes(data2).CopyTo(guidBytes, 8);
BitConverter.GetBytes(data3).CopyTo(guidBytes, 12);
return new Guid(guidBytes);
}
}
}
}

View File

@ -3,11 +3,24 @@ using System.Collections.Generic;
using System.Collections.Specialized; using System.Collections.Specialized;
using System.IO; using System.IO;
using System.Text; using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace AssetStudio namespace AssetStudio
{ {
public static class TypeTreeHelper public static class TypeTreeHelper
{ {
private static readonly JsonSerializerOptions JsonOptions;
static TypeTreeHelper()
{
JsonOptions = new JsonSerializerOptions
{
NumberHandling = JsonNumberHandling.AllowNamedFloatingPointLiterals,
ReferenceHandler = ReferenceHandler.IgnoreCycles,
IncludeFields = true,
};
}
public static string ReadTypeString(TypeTree m_Type, ObjectReader reader) public static string ReadTypeString(TypeTree m_Type, ObjectReader reader)
{ {
reader.Reset(); reader.Reset();
@ -163,6 +176,14 @@ namespace AssetStudio
reader.AlignStream(); reader.AlignStream();
} }
public static byte[] ReadTypeByteArray(TypeTree m_Types, ObjectReader reader)
{
var type = ReadType(m_Types, reader);
var bytes = JsonSerializer.SerializeToUtf8Bytes(type, JsonOptions);
type.Clear();
return bytes;
}
public static OrderedDictionary ReadType(TypeTree m_Types, ObjectReader reader) public static OrderedDictionary ReadType(TypeTree m_Types, ObjectReader reader)
{ {
reader.Reset(); reader.Reset();
@ -171,7 +192,7 @@ namespace AssetStudio
for (int i = 1; i < m_Nodes.Count; i++) for (int i = 1; i < m_Nodes.Count; i++)
{ {
var m_Node = m_Nodes[i]; var m_Node = m_Nodes[i];
var varNameStr = m_Node.m_Name.Replace("image data", "image_data"); var varNameStr = m_Node.m_Name;
obj[varNameStr] = ReadValue(m_Nodes, reader, ref i); obj[varNameStr] = ReadValue(m_Nodes, reader, ref i);
} }
var readed = reader.Position - reader.byteStart; var readed = reader.Position - reader.byteStart;

View File

@ -7,7 +7,6 @@ namespace CubismLive2DExtractor
{ {
class CubismMotion3Converter class CubismMotion3Converter
{ {
private SerializedFile assetsFile;
private Dictionary<uint, string> bonePathHash = new Dictionary<uint, string>(); private Dictionary<uint, string> bonePathHash = new Dictionary<uint, string>();
public List<ImportedKeyframedAnimation> AnimationList { get; protected set; } = new List<ImportedKeyframedAnimation>(); public List<ImportedKeyframedAnimation> AnimationList { get; protected set; } = new List<ImportedKeyframedAnimation>();
@ -30,7 +29,6 @@ namespace CubismLive2DExtractor
foreach (var animationClip in animationClips) foreach (var animationClip in animationClips)
{ {
var iAnim = new ImportedKeyframedAnimation(); var iAnim = new ImportedKeyframedAnimation();
assetsFile = animationClip.assetsFile;
AnimationList.Add(iAnim); AnimationList.Add(iAnim);
iAnim.Name = animationClip.m_Name; iAnim.Name = animationClip.m_Name;
iAnim.SampleRate = animationClip.m_SampleRate; iAnim.SampleRate = animationClip.m_SampleRate;
@ -136,7 +134,7 @@ namespace CubismLive2DExtractor
target = "PartOpacity"; target = "PartOpacity";
} }
} }
else if (binding.script.TryGet(out MonoScript script, assetsFile)) else if (binding.script.TryGet(out MonoScript script))
{ {
switch (script.m_ClassName) switch (script.m_ClassName)
{ {