From 7adfdef62feccdb8c5db41a5d2d750d1bad8ebdc Mon Sep 17 00:00:00 2001 From: VaDiM Date: Tue, 26 May 2026 01:48:56 +0300 Subject: [PATCH] Add support for latest Tuanjie versions (up to v1.9) still partial --- AssetStudio/BuildTarget.cs | 1 + AssetStudio/Classes/AnimationClip.cs | 46 +--- AssetStudio/Classes/AnimatorController.cs | 21 ++ AssetStudio/Classes/Mesh.cs | 185 ++++------------ AssetStudio/Classes/Renderer.cs | 5 +- AssetStudio/Classes/Texture2D.cs | 6 +- AssetStudio/Classes/TextureFormat.cs | 31 +++ AssetStudio/Classes/Tuanjie/TAnimationClip.cs | 45 ++++ AssetStudio/Classes/Tuanjie/TMesh.cs | 200 ++++++++++++++++++ AssetStudioCLI/ParallelExporter.cs | 7 + AssetStudioGUI/ParallelExport.cs | 7 + AssetStudioUtility/Texture2DConverter.cs | 19 ++ 12 files changed, 392 insertions(+), 181 deletions(-) create mode 100644 AssetStudio/Classes/Tuanjie/TAnimationClip.cs create mode 100644 AssetStudio/Classes/Tuanjie/TMesh.cs diff --git a/AssetStudio/BuildTarget.cs b/AssetStudio/BuildTarget.cs index b1e0fc6..94bc643 100644 --- a/AssetStudio/BuildTarget.cs +++ b/AssetStudio/BuildTarget.cs @@ -61,5 +61,6 @@ ArmLinux, ArmLinuxServer, VisionOS, + PlayableAds, } } diff --git a/AssetStudio/Classes/AnimationClip.cs b/AssetStudio/Classes/AnimationClip.cs index bd89e04..73dd851 100644 --- a/AssetStudio/Classes/AnimationClip.cs +++ b/AssetStudio/Classes/AnimationClip.cs @@ -1,4 +1,5 @@ -using System; +using AssetStudio.Classes.Tuanjie.AnimationClip; +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -682,45 +683,6 @@ namespace AssetStudio } } - public class ACLClip //Tuanjie - { - public uint m_FrameCount; - public uint m_BoneCount; - public float m_SampleRate; - public uint m_CurveCount; - public byte[] m_Tracks; - public uint[] m_ACLDecoderMap; - public bool m_UseACLFastSampleMode; - - public ACLClip() { } - - public ACLClip(ObjectReader reader) - { - var version = reader.version; - m_FrameCount = reader.ReadUInt32(); - m_BoneCount = reader.ReadUInt32(); - m_SampleRate = reader.ReadSingle(); - if (version >= (2022, 3, 55)) //2022.3.55t1(1.5.0) and up - { - m_CurveCount = reader.ReadUInt32(); - } - m_Tracks = reader.ReadUInt8Array(); - if (version >= (2022, 3, 61)) //2022.3.61t1(1.6.0) and up - { - reader.AlignStream(); - } - m_ACLDecoderMap = reader.ReadUInt32Array(); - if (version > (2022, 3, 55) || (version == (2022, 3, 55) && version.Build >= 4)) //2022.3.55t4(1.5.3) and up - { - m_UseACLFastSampleMode = reader.ReadBoolean(); - if (version >= (2022, 3, 61)) //2022.3.61t1(1.6.0) and up - { - reader.AlignStream(); - } - } - } - } - public class OffsetPtr { public Clip data; @@ -899,6 +861,10 @@ namespace AssetStudio } m_Mirror = reader.ReadBoolean(); + if (version.IsTuanjie && version >= (2022, 3, 62, 8)) //2022.3.62t8(1.9.0) and up + { + var m_LockToRoot = reader.ReadBoolean(); + } if (version >= (4, 3)) //4.3 and up { m_LoopTime = reader.ReadBoolean(); diff --git a/AssetStudio/Classes/AnimatorController.cs b/AssetStudio/Classes/AnimatorController.cs index e5fd1f8..a032b09 100644 --- a/AssetStudio/Classes/AnimatorController.cs +++ b/AssetStudio/Classes/AnimatorController.cs @@ -128,6 +128,11 @@ namespace AssetStudio } m_ConditionConstantArray = conditionConstantList.ToArray(); + if (version.IsTuanjie && version >= (2022, 3, 62, 2)) //2022.3.62t2(1.8.0) and up + { + var m_PriorityOrder = reader.ReadInt32(); + } + m_DestinationState = reader.ReadUInt32(); if (version >= 5) //5.0 and up { @@ -157,6 +162,11 @@ namespace AssetStudio m_CanTransitionToSelf = reader.ReadBoolean(); } + if (version.IsTuanjie && version >= (2022, 3, 62, 8)) //2022.3.62t8(1.9.0) and up + { + var m_UseInertialization = reader.ReadBoolean(); + } + reader.AlignStream(); } } @@ -400,6 +410,12 @@ namespace AssetStudio { m_CycleOffset = reader.ReadSingle(); } + + if (version.IsTuanjie && version >= (2022, 3, 62, 2)) //2022.3.62t2(1.8.0) and up + { + var m_OverrideDuration = reader.ReadSingle(); + } + m_IKOnFeet = reader.ReadBoolean(); if (version >= 5) //5.0 and up { @@ -412,6 +428,11 @@ namespace AssetStudio m_Mirror = reader.ReadBoolean(); } + if (version.IsTuanjie && version >= (2022, 3, 62, 2)) //2022.3.62t2(1.8.0) and up + { + var m_UseOverrideDuration = reader.ReadBoolean(); + } + reader.AlignStream(); } } diff --git a/AssetStudio/Classes/Mesh.cs b/AssetStudio/Classes/Mesh.cs index ec0e4ad..537b5f0 100644 --- a/AssetStudio/Classes/Mesh.cs +++ b/AssetStudio/Classes/Mesh.cs @@ -1,7 +1,7 @@ -using System; +using AssetStudio.Classes.Tuanjie.Mesh; +using System; using System.Collections; using System.Collections.Generic; -using System.IO; using System.Linq; using System.Runtime.InteropServices; using static AssetStudio.CustomOptions.GameSpecificOptions.CustomMeshOptions; @@ -517,105 +517,7 @@ namespace AssetStudio } } } - - public class VGPackedHierarchyNode - { - public Vector4[] LODBounds = new Vector4[8]; - public Vector3[] AABBCenter = new Vector3[8]; - public uint[] MinLODError_MaxParentLODError = new uint[8]; - public Vector3[] AABBExtent = new Vector3[8]; - public uint[] ChildStartIndex = new uint[8]; - public uint[] PageIndex_PageCount_PageBlockCount = new uint[8]; - - public VGPackedHierarchyNode(ObjectReader reader) - { - for (var i = 0; i < 8; i++) - { - LODBounds[i] = reader.ReadVector4(); - AABBCenter[i] = reader.ReadVector3(); - MinLODError_MaxParentLODError[i] = reader.ReadUInt32(); - AABBExtent[i] = reader.ReadVector3(); - ChildStartIndex[i] = reader.ReadUInt32(); - PageIndex_PageCount_PageBlockCount[i] = reader.ReadUInt32(); - } - } - } - - public class VGPageStreamingInfo - { - public uint offset; - public uint wholeSize; - public uint dataSize; - public uint dependencyOffset; - public uint dependencyCount; - public uint flags; - - public VGPageStreamingInfo(BinaryReader reader) - { - offset = reader.ReadUInt32(); - wholeSize = reader.ReadUInt32(); - dataSize = reader.ReadUInt32(); - dependencyOffset = reader.ReadUInt32(); - dependencyCount = reader.ReadUInt32(); - flags = reader.ReadUInt32(); - } - } - - public class SharedClusterData //Tuanjie - { - public SharedClusterData(ObjectReader reader, byte rev) - { - var m_LightmapUseUV1 = reader.ReadInt32(); - var m_fileScale = reader.ReadSingle(); - if (rev == 1) - { - var NumInputTriangles = reader.ReadUInt32(); - var NumInputVertices = reader.ReadUInt32(); - var NumInputMeshes = reader.ReadUInt16(); - var NumInputTexCoords = reader.ReadUInt16(); - var ResourceFlags = reader.ReadUInt32(); - } - var rootClusterPageSize = reader.ReadInt32(); - reader.Position += rootClusterPageSize; //skip byte[] rootClusterPage - if (rev == 1) - { - var imposterAtlasSize = reader.ReadInt32(); - reader.Position += imposterAtlasSize * 2; //skip ushort[] imposterAtlas - } - var hierarchyNodesSize = reader.ReadInt32(); - for (var i = 0; i < hierarchyNodesSize; i++) - { - var hierarchyNode = new VGPackedHierarchyNode(reader); - } - if (rev == 1) - { - var hierarchyRootOffsetsSize = reader.ReadInt32(); - reader.Position += hierarchyRootOffsetsSize * 4; //skip uint[] hierarchyRootOffsets - } - var pageStreamingInfosSize = reader.ReadInt32(); - for (var i = 0; i < pageStreamingInfosSize; i++) - { - var pageStreamingInfo = new VGPageStreamingInfo(reader); - } - var pageIndicesOfDependenciesSize = reader.ReadInt32(); - reader.Position += pageIndicesOfDependenciesSize * 4; //skip uint[] pageIndicesOfDependencies - if (rev == 2) - { - var inputTrianglesCount = reader.ReadUInt32(); - var inputVerticesCount = reader.ReadUInt32(); - var inputMeshesCount = reader.ReadUInt16(); - var inputTexCoordsCount = reader.ReadUInt16(); - var resourceFlags = reader.ReadUInt32(); - var imposterAtlasSize = reader.ReadInt32(); - reader.Position += imposterAtlasSize * 2; //skip ushort[] imposterAtlas - var hierarchyRootOffsetsSize = reader.ReadInt32(); - reader.Position += hierarchyRootOffsetsSize * 4; //skip uint[] hierarchyRootOffsets - } - var streamableClusterPageSize = reader.ReadInt32(); - reader.Position += streamableClusterPageSize; //skip byte[] streamableClusterPageSize - } - } - + public sealed class Mesh : NamedObject { private bool isLoaded; @@ -677,12 +579,12 @@ namespace AssetStudio if (version <= (2, 5)) //2.0 - 2.5 down { - int m_IndexBufferSize = reader.ReadInt32(); + int m_IndexBufferCount = reader.ReadInt32(); if (m_Use16BitIndices) { var indexBufferList = new List(); - for (var i = 0; i < m_IndexBufferSize / 2; i++) + for (var i = 0; i < m_IndexBufferCount / 2; i++) { indexBufferList.Add(reader.ReadUInt16()); } @@ -694,14 +596,14 @@ namespace AssetStudio } else { - m_IndexBuffer = reader.ReadUInt32Array(m_IndexBufferSize / 4); + m_IndexBuffer = reader.ReadUInt32Array(m_IndexBufferCount / 4); } } - int m_SubMeshesSize = reader.ReadInt32(); - reader.ThrowIfTooLarge(m_SubMeshesSize * 12f); + int m_SubMeshesCount = reader.ReadInt32(); + reader.ThrowIfTooLarge(m_SubMeshesCount * 12f); m_SubMeshes = new List(); - for (var i = 0; i < m_SubMeshesSize; i++) + for (var i = 0; i < m_SubMeshesCount; i++) { m_SubMeshes.Add(new SubMesh(reader)); } @@ -723,14 +625,11 @@ namespace AssetStudio { if (version >= 2019) //2019 and up { - var m_BonesAABBSize = reader.ReadInt32(); - for (var i = 0; i < m_BonesAABBSize; i++) - { - var m_BonesAABB = new MinMaxAABB(reader); - } + var m_BonesAABBCount = reader.ReadInt32(); + reader.Position += m_BonesAABBCount * 24; //skip MinMaxAABB[] m_BonesAABB - var m_VariableBoneCountWeightsSize = reader.ReadInt32(); - reader.Position += m_VariableBoneCountWeightsSize * 4; //skip uint[] m_VariableBoneCountWeights + var m_VariableBoneCountWeightsCount = reader.ReadInt32(); + reader.Position += m_VariableBoneCountWeightsCount * 4; //skip uint[] m_VariableBoneCountWeights } var m_MeshCompression = reader.ReadByte(); @@ -744,33 +643,41 @@ namespace AssetStudio var m_KeepVertices = reader.ReadBoolean(); var m_KeepIndices = reader.ReadBoolean(); } - - var tuanjieParsingMode = reader.assetsFile.assetsManager.Options.GameSpecific.Meshes.TuanjieMeshParsingMode; - var useSharedClusterData = tuanjieParsingMode == MeshParsingMode.Tuanjie; - if (tuanjieParsingMode == MeshParsingMode.Auto) - { - //SharedClusterData becomes optional from v1.6.1, but idk in what cases - useSharedClusterData = version < (2022, 3, 61, 2) || !serializedType.Match(6165448184371784201); - } - - if (version.IsTuanjie && useSharedClusterData) - { - if (version < (2022, 3, 48, 3)) //2022.3.48t3(1.4.0) down - { - _ = new SharedClusterData(reader, rev: 1); - } - else if (version < (2022, 3, 61, 2)) //2022.3.48t3(1.4.0) - 2022.3.61t1(1.6.0) - { - _ = new SharedClusterData(reader, rev: 2); - } - else //2022.3.61t2(1.6.1) and up - { - reader.AlignStream(); - _ = new SharedClusterData(reader, rev: 3); - } - } reader.AlignStream(); + if (version.IsTuanjie) + { + var tuanjieParsingMode = assetsFile.assetsManager.Options.GameSpecific.Meshes.TuanjieMeshParsingMode; + var parseAsTuanjie = tuanjieParsingMode == MeshParsingMode.Tuanjie; + if (tuanjieParsingMode == MeshParsingMode.Auto) + { + //Tuanjie features becomes optional from v1.6.1, but idk in what cases (platform dependent?) + parseAsTuanjie = version < (2022, 3, 61, 2) || !serializedType.Match(6165448184371784201, 16777665667907586384); + } + + if (parseAsTuanjie) + { + if (version >= (2022, 3, 62)) //2022.3.62t1(1.7.0) and up + { + var m_GIFacetMesh = new GIFacetMesh(reader); + var m_GISDFMesh = new GISDFMesh(reader); + } + + if (version < (2022, 3, 48, 3)) //2022.3.48t3(1.4.0) down + { + _ = new SharedClusterData(reader, rev: 1); + } + else if (version < (2022, 3, 61, 2)) //2022.3.48t3(1.4.0) - 2022.3.61t1(1.6.0) + { + _ = new SharedClusterData(reader, rev: 2); + } + else //2022.3.61t2(1.6.1) and up + { + _ = new SharedClusterData(reader, rev: 3); + } + } + } + //Unity fixed it in 2017.3.1p1 and later versions if (version >= (2017, 4) //2017.4 || version == (2017, 3, 1) && version.IsPatch //fixed after 2017.3.1px diff --git a/AssetStudio/Classes/Renderer.cs b/AssetStudio/Classes/Renderer.cs index ab1d665..11fd56a 100644 --- a/AssetStudio/Classes/Renderer.cs +++ b/AssetStudio/Classes/Renderer.cs @@ -69,7 +69,10 @@ namespace AssetStudio if (version.IsTuanjie) //2022.3.2t3(1.0.0) and up { var m_virtualGeometry = reader.ReadByte(); - var m_virtualGeometryShadow = reader.ReadByte(); + if (version < (2022, 3, 62, 2)) //2022.3.62t2(1.8.0) down + { + var m_virtualGeometryShadow = reader.ReadByte(); + } if (version >= (2022, 3, 48, 3)) //2022.3.48t3(1.4.0) and up { reader.AlignStream(); diff --git a/AssetStudio/Classes/Texture2D.cs b/AssetStudio/Classes/Texture2D.cs index d759992..655368a 100644 --- a/AssetStudio/Classes/Texture2D.cs +++ b/AssetStudio/Classes/Texture2D.cs @@ -63,7 +63,9 @@ namespace AssetStudio m_Width = parsedTex2d.m_Width; m_Height = parsedTex2d.m_Height; m_CompleteImageSize = parsedTex2d.m_CompleteImageSize; - m_TextureFormat = parsedTex2d.m_TextureFormat; + m_TextureFormat = version.IsTuanjie && version >= (2022, 3, 62, 8) + ? parsedTex2d.m_TextureFormat.ToTuanjie() //2022.3.62t8(1.9.0) and up + : parsedTex2d.m_TextureFormat; m_MipMap = parsedTex2d.m_MipMap; m_MipCount = parsedTex2d.m_MipCount; m_ImageCount = parsedTex2d.m_ImageCount; @@ -104,6 +106,8 @@ namespace AssetStudio var m_TextureManagerMultiFormatSettingSize = reader.ReadInt32(); reader.Position += m_TextureManagerMultiFormatSettingSize; //skip byte[] m_TextureManagerMultiFormatSetting reader.AlignStream(); + + m_TextureFormat = version >= (2022, 3, 62, 8) ? m_TextureFormat.ToTuanjie() : m_TextureFormat; //2022.3.62t8(1.9.0) and up } if (version < (5, 2)) //5.2 down { diff --git a/AssetStudio/Classes/TextureFormat.cs b/AssetStudio/Classes/TextureFormat.cs index 4db8f12..ed1a9d4 100644 --- a/AssetStudio/Classes/TextureFormat.cs +++ b/AssetStudio/Classes/TextureFormat.cs @@ -311,5 +311,36 @@ /// Four channel (RGBA) texture format, 16-bits signed integer per channel. /// RGBA64_SIGNED, + + //Tuanjie engine + //(adding 1000 for compatibility with Unity formats) + /// + /// Offset for Tuanjie texture formats. + /// + TUANJIE_OFFSET = 1000, + /// + /// PNG (32 bits) compressed RGBA texture format. + /// + PNG_32_RGBA = TUANJIE_OFFSET + 75, + /// + /// PNG (24 bits) compressed RGB texture format. + /// + PNG_24_RGB = TUANJIE_OFFSET + 76, + /// + /// PNG (8 bits) compressed RGBA texture format. + /// + PNG_8_RGBA = TUANJIE_OFFSET + 77, + } + + public static class TextureFormatExtensions + { + public static TextureFormat ToTuanjie(this TextureFormat format) + { + return format > TextureFormat.RGBA64 && format < TextureFormat.TUANJIE_OFFSET + ? TextureFormat.TUANJIE_OFFSET + (int)format + : format; + } + + public static bool IsPng(this TextureFormat format) => format >= TextureFormat.PNG_32_RGBA && format <= TextureFormat.PNG_8_RGBA; } } diff --git a/AssetStudio/Classes/Tuanjie/TAnimationClip.cs b/AssetStudio/Classes/Tuanjie/TAnimationClip.cs new file mode 100644 index 0000000..d11d552 --- /dev/null +++ b/AssetStudio/Classes/Tuanjie/TAnimationClip.cs @@ -0,0 +1,45 @@ +namespace AssetStudio.Classes.Tuanjie.AnimationClip +{ + public sealed class ACLClip + { + public uint m_FrameCount; + public uint m_BoneCount; + public float m_SampleRate; + public uint m_CurveCount; + public byte[] m_Tracks; + public uint[] m_ACLDecoderMap; + public bool m_UseACLFastSampleMode; + + public ACLClip() { } + + public ACLClip(ObjectReader reader) + { + var version = reader.version; + m_FrameCount = reader.ReadUInt32(); + m_BoneCount = reader.ReadUInt32(); + m_SampleRate = reader.ReadSingle(); + if (version >= (2022, 3, 55)) //2022.3.55t1(1.5.0) and up + { + m_CurveCount = reader.ReadUInt32(); + } + if (version >= (2022, 3, 62, 8)) //2022.3.62t8(1.9.0) and up + { + var m_BeginTime = reader.ReadSingle(); + } + m_Tracks = reader.ReadUInt8Array(); + if (version >= (2022, 3, 61)) //2022.3.61t1(1.6.0) and up + { + reader.AlignStream(); + } + m_ACLDecoderMap = reader.ReadUInt32Array(); + if (version > (2022, 3, 55) || (version == (2022, 3, 55) && version.Build >= 4)) //2022.3.55t4(1.5.3) and up + { + m_UseACLFastSampleMode = reader.ReadBoolean(); + if (version >= (2022, 3, 61)) //2022.3.61t1(1.6.0) and up + { + reader.AlignStream(); + } + } + } + } +} diff --git a/AssetStudio/Classes/Tuanjie/TMesh.cs b/AssetStudio/Classes/Tuanjie/TMesh.cs new file mode 100644 index 0000000..339252a --- /dev/null +++ b/AssetStudio/Classes/Tuanjie/TMesh.cs @@ -0,0 +1,200 @@ +using System.Collections.Generic; + +namespace AssetStudio.Classes.Tuanjie.Mesh +{ + public sealed class GIFacetInfo + { + public float[] matrix; //Matrix3x4f + public byte orientation; + + public GIFacetInfo(ObjectReader reader) + { + matrix = reader.ReadSingleArray(12); + orientation = reader.ReadByte(); + reader.AlignStream(); + } + } + + public sealed class GIFacetMesh + { + public MinMaxAABB m_aabb; + public bool m_doubleSide; + public List m_GIFacetInfo; + + public GIFacetMesh(ObjectReader reader) + { + m_aabb = new MinMaxAABB(reader); + m_doubleSide = reader.ReadBoolean(); + reader.AlignStream(); + + var m_GIFacetInfoCount = reader.ReadInt32(); + m_GIFacetInfo = new List(); + for (var i = 0; i < m_GIFacetInfoCount; i++) + { + m_GIFacetInfo.Add(new GIFacetInfo(reader)); + } + } + } + + public sealed class GISDFMeshLOD + { + public uint brickMappingSizeX; + public uint brickMappingSizeY; + public uint brickMappingSizeZ; + public Vector3 volumeToMappingScale; + public Vector3 volumeToMappingBias; + public float SDFDecodeScale; + public float SDFDecodeBias; + public uint[] activeBrickIDs; + public byte[] activeBrickData; + + public GISDFMeshLOD(ObjectReader reader) + { + brickMappingSizeX = reader.ReadUInt32(); + brickMappingSizeY = reader.ReadUInt32(); + brickMappingSizeZ = reader.ReadUInt32(); + volumeToMappingScale = reader.ReadVector3(); + volumeToMappingBias = reader.ReadVector3(); + SDFDecodeScale = reader.ReadSingle(); + SDFDecodeBias = reader.ReadSingle(); + activeBrickIDs = reader.ReadUInt32Array(); + activeBrickData = reader.ReadUInt8Array(); + reader.AlignStream(); + } + } + + public sealed class GISDFMesh + { + public AABB localAABB; + public float sdfGenBias; + public bool is2Sided; + public List LODs; + + public GISDFMesh(ObjectReader reader) + { + localAABB = new AABB(reader); + sdfGenBias = reader.ReadSingle(); + is2Sided = reader.ReadBoolean(); + reader.AlignStream(); + + var LODsCount = reader.ReadInt32(); + LODs = new List(); + for (var i = 0; i < LODsCount; i++) + { + LODs.Add(new GISDFMeshLOD(reader)); + } + } + } + + public sealed class VGPackedHierarchyNode + { + public Vector4[] LODBounds = new Vector4[8]; + public Vector3[] AABBCenter = new Vector3[8]; + public uint[] MinLODError_MaxParentLODError = new uint[8]; + public Vector3[] AABBExtent = new Vector3[8]; + public uint[] ChildStartIndex = new uint[8]; + public uint[] PageIndex_PageCount_PageBlockCount = new uint[8]; + + public VGPackedHierarchyNode(ObjectReader reader) + { + for (var i = 0; i < 8; i++) + { + LODBounds[i] = reader.ReadVector4(); + AABBCenter[i] = reader.ReadVector3(); + MinLODError_MaxParentLODError[i] = reader.ReadUInt32(); + AABBExtent[i] = reader.ReadVector3(); + ChildStartIndex[i] = reader.ReadUInt32(); + PageIndex_PageCount_PageBlockCount[i] = reader.ReadUInt32(); + } + } + } + + public sealed class VGPageStreamingInfo + { + public uint offset; + public uint wholeSize; + public uint dataSize; + public uint dependencyOffset; + public uint dependencyCount; + public uint flags; + + public VGPageStreamingInfo(ObjectReader reader) + { + offset = reader.ReadUInt32(); + wholeSize = reader.ReadUInt32(); + dataSize = reader.ReadUInt32(); + dependencyOffset = reader.ReadUInt32(); + dependencyCount = reader.ReadUInt32(); + flags = reader.ReadUInt32(); + } + } + + public sealed class SharedClusterData + { + public int m_LightmapUseUV1; + public float m_fileScale; + public byte[] rootClusterPage; + public List hierarchyNodes; + public List pageStreamingInfos; + public uint[] pageIndicesOfDependencies; + public byte[] streamableClusterPageSize; + + public SharedClusterData(ObjectReader reader, byte rev) + { + m_LightmapUseUV1 = reader.ReadInt32(); + if (reader.version < (2022, 3, 62)) //2022.3.62t1(1.7.0) down + { + m_fileScale = reader.ReadSingle(); + } + if (rev == 1) + { + var NumInputTriangles = reader.ReadUInt32(); + var NumInputVertices = reader.ReadUInt32(); + var NumInputMeshes = reader.ReadUInt16(); + var NumInputTexCoords = reader.ReadUInt16(); + var ResourceFlags = reader.ReadUInt32(); + } + rootClusterPage = reader.ReadUInt8Array(); + reader.AlignStream(); + if (rev == 1) + { + var imposterAtlasCount = reader.ReadInt32(); + reader.Position += imposterAtlasCount * 2; //skip ushort[] imposterAtlas + reader.AlignStream(); + } + var hierarchyNodesCount = reader.ReadInt32(); + hierarchyNodes = new List(); + for (var i = 0; i < hierarchyNodesCount; i++) + { + hierarchyNodes.Add(new VGPackedHierarchyNode(reader)); + } + if (rev == 1) + { + var hierarchyRootOffsetsCount = reader.ReadInt32(); + reader.Position += hierarchyRootOffsetsCount * 4; //skip uint[] hierarchyRootOffsets + } + var pageStreamingInfosCount = reader.ReadInt32(); + pageStreamingInfos = new List(); + for (var i = 0; i < pageStreamingInfosCount; i++) + { + pageStreamingInfos.Add(new VGPageStreamingInfo(reader)); + } + pageIndicesOfDependencies = reader.ReadUInt32Array(); + if (rev == 2) + { + var inputTrianglesCount = reader.ReadUInt32(); + var inputVerticesCount = reader.ReadUInt32(); + var inputMeshesCount = reader.ReadUInt16(); + var inputTexCoordsCount = reader.ReadUInt16(); + var resourceFlags = reader.ReadUInt32(); + var imposterAtlasCount = reader.ReadInt32(); + reader.Position += imposterAtlasCount * 2; //skip ushort[] imposterAtlas + reader.AlignStream(); + var hierarchyRootOffsetsCount = reader.ReadInt32(); + reader.Position += hierarchyRootOffsetsCount * 4; //skip uint[] hierarchyRootOffsets + } + streamableClusterPageSize = reader.ReadUInt8Array(); + reader.AlignStream(); + } + } +} diff --git a/AssetStudioCLI/ParallelExporter.cs b/AssetStudioCLI/ParallelExporter.cs index 7ed268b..855bf35 100644 --- a/AssetStudioCLI/ParallelExporter.cs +++ b/AssetStudioCLI/ParallelExporter.cs @@ -48,6 +48,13 @@ namespace AssetStudioCLI debugLog += sb.ToString(); } + if (type == ImageFormat.Png && m_Texture2D.m_TextureFormat.IsPng()) + { + File.WriteAllBytes(exportFullPath, m_Texture2D.image_data.GetData()); + debugLog += $"{item.TypeString} \"{item.Text}\" exported to \"{exportFullPath}\""; + return true; + } + if (!converter.TryConvertToBgra32Image(flip: true, out var image)) { Logger.Error($"{debugLog}Export error. Failed to convert texture \"{m_Texture2D.m_Name}\" into image"); diff --git a/AssetStudioGUI/ParallelExport.cs b/AssetStudioGUI/ParallelExport.cs index 8b15455..1899975 100644 --- a/AssetStudioGUI/ParallelExport.cs +++ b/AssetStudioGUI/ParallelExport.cs @@ -47,6 +47,13 @@ namespace AssetStudioGUI debugLog += sb.ToString(); } + if (type == ImageFormat.Png && m_Texture2D.m_TextureFormat.IsPng()) + { + File.WriteAllBytes(exportFullPath, m_Texture2D.image_data.GetData()); + debugLog += $"{item.TypeString} \"{item.Text}\" exported to \"{exportFullPath}\""; + return true; + } + if (!converter.TryConvertToBgra32Image(flip: true, out var image)) { Logger.Warning($"{debugLog}Failed to convert texture \"{m_Texture2D.m_Name}\" into image"); diff --git a/AssetStudioUtility/Texture2DConverter.cs b/AssetStudioUtility/Texture2DConverter.cs index 99e2b68..fe6df56 100644 --- a/AssetStudioUtility/Texture2DConverter.cs +++ b/AssetStudioUtility/Texture2DConverter.cs @@ -322,6 +322,11 @@ namespace AssetStudio case TextureFormat.RGBA64: //test pass flag = DecodeRGBA64(buff, destSpan); break; + case TextureFormat.PNG_8_RGBA: + case TextureFormat.PNG_24_RGB: //test pass + case TextureFormat.PNG_32_RGBA: //test pass + flag = DecodePng(buff, destSpan); + break; } } finally @@ -811,6 +816,20 @@ namespace AssetStudio return true; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private bool DecodePng(ReadOnlySpan buff, Span destSpan) + { + try + { + Image.Load(buff).CopyPixelDataTo(destSpan); + return true; + } + catch (Exception) + { + return false; + } + } + private bool TryUnpackCrunch(ReadOnlySpan buff, out ReadOnlySpan result) { if (m_Texture2D.version >= (2017, 3) //2017.3 and up