From b768acbd9cc47f39c7ea7fa27527353d480471d0 Mon Sep 17 00:00:00 2001 From: Razmoth <32140579+Razmoth@users.noreply.github.com> Date: Mon, 13 Nov 2023 23:06:08 +0400 Subject: [PATCH] - [Core] Adjust `LZ4` namespace. - [Core] Added support to `AnimationClip` [Ark] --- AssetStudio.Utility/ShaderConverter.cs | 4 +- AssetStudio/BlbFile.cs | 2 +- AssetStudio/BundleFile.cs | 6 +- AssetStudio/Classes/AnimationClip.cs | 152 +++++++++++++----- AssetStudio/Extensions/ByteArrayExtensions.cs | 17 ++ AssetStudio/LZ4/LZ4Utils.cs | 2 +- AssetStudio/Mhy0File.cs | 4 +- 7 files changed, 136 insertions(+), 51 deletions(-) diff --git a/AssetStudio.Utility/ShaderConverter.cs b/AssetStudio.Utility/ShaderConverter.cs index 1e86b19..1122aea 100644 --- a/AssetStudio.Utility/ShaderConverter.cs +++ b/AssetStudio.Utility/ShaderConverter.cs @@ -15,7 +15,7 @@ namespace AssetStudio if (shader.m_SubProgramBlob != null) //5.3 - 5.4 { var decompressedBytes = new byte[shader.decompressedSize]; - var numWrite = LZ4.LZ4.Decompress(shader.m_SubProgramBlob, decompressedBytes); + var numWrite = LZ4.Decompress(shader.m_SubProgramBlob, decompressedBytes); if (numWrite != shader.decompressedSize) { throw new IOException($"Lz4 decompression error, write {numWrite} bytes but expected {shader.decompressedSize} bytes"); @@ -54,7 +54,7 @@ namespace AssetStudio } else { - var numWrite = LZ4.LZ4.Decompress(shader.compressedBlob.AsSpan().Slice((int)offset, (int)compressedLength), decompressedBytes.AsSpan().Slice(0, (int)decompressedLength)); + var numWrite = LZ4.Decompress(shader.compressedBlob.AsSpan().Slice((int)offset, (int)compressedLength), decompressedBytes.AsSpan().Slice(0, (int)decompressedLength)); if (numWrite != decompressedLength) { throw new IOException($"Lz4 decompression error, write {numWrite} bytes but expected {decompressedLength} bytes"); diff --git a/AssetStudio/BlbFile.cs b/AssetStudio/BlbFile.cs index 3d7bbb8..27e30ea 100644 --- a/AssetStudio/BlbFile.cs +++ b/AssetStudio/BlbFile.cs @@ -130,7 +130,7 @@ namespace AssetStudio var compressedBytesSpan = compressedBytes.AsSpan(0, compressedSize); var uncompressedBytesSpan = uncompressedBytes.AsSpan(0, uncompressedSize); - var numWrite = LZ4.LZ4.Decompress(compressedBytesSpan, uncompressedBytesSpan); + var numWrite = LZ4.Decompress(compressedBytesSpan, uncompressedBytesSpan); if (numWrite != uncompressedSize) { throw new IOException($"Lz4 decompression error, write {numWrite} bytes but expected {uncompressedSize} bytes"); diff --git a/AssetStudio/BundleFile.cs b/AssetStudio/BundleFile.cs index 65e1edd..ed8b1be 100644 --- a/AssetStudio/BundleFile.cs +++ b/AssetStudio/BundleFile.cs @@ -437,7 +437,7 @@ namespace AssetStudio case CompressionType.Lz4HC: //LZ4HC { var uncompressedBytes = new byte[uncompressedSize]; - var numWrite = LZ4.LZ4.Decompress(blocksInfoBytesSpan, uncompressedBytes); + var numWrite = LZ4.Decompress(blocksInfoBytesSpan, uncompressedBytes); if (numWrite != uncompressedSize) { throw new IOException($"Lz4 decompression error, write {numWrite} bytes but expected {uncompressedSize} bytes"); @@ -553,7 +553,7 @@ namespace AssetStudio var uncompressedSize = (int)blockInfo.uncompressedSize; var uncompressedBytes = BigArrayPool.Shared.Rent(uncompressedSize); var uncompressedBytesSpan = uncompressedBytes.AsSpan(0, uncompressedSize); - var numWrite = LZ4.LZ4.Decompress(compressedBytesSpan, uncompressedBytesSpan); + var numWrite = LZ4.Decompress(compressedBytesSpan, uncompressedBytesSpan); if (numWrite != uncompressedSize) { throw new IOException($"Lz4 decompression error, write {numWrite} bytes but expected {uncompressedSize} bytes"); @@ -577,7 +577,7 @@ namespace AssetStudio var uncompressedSize = (int)blockInfo.uncompressedSize; var uncompressedBytes = BigArrayPool.Shared.Rent(uncompressedSize); var uncompressedBytesSpan = uncompressedBytes.AsSpan(0, uncompressedSize); - var numWrite = LZ4.LZ4Inv.Decompress(compressedBytesSpan, uncompressedBytesSpan); + var numWrite = LZ4Inv.Decompress(compressedBytesSpan, uncompressedBytesSpan); if (numWrite != uncompressedSize) { throw new IOException($"Lz4 decompression error, write {numWrite} bytes but expected {uncompressedSize} bytes"); diff --git a/AssetStudio/Classes/AnimationClip.cs b/AssetStudio/Classes/AnimationClip.cs index b31d15b..c724a89 100644 --- a/AssetStudio/Classes/AnimationClip.cs +++ b/AssetStudio/Classes/AnimationClip.cs @@ -795,42 +795,6 @@ namespace AssetStudio public override void Read(ObjectReader reader) { } } - public class ArkACLClip : ACLClip - { - public int m_ACLType; - public byte[] m_ACLArray; - public float m_PositionFactor; - public float m_EulerFactor; - public float m_ScaleFactor; - public float m_FloatFactor; - public float m_nPositionCurves; - public float m_nRotationCurves; - public float m_nEulerCurves; - public float m_nScaleCurves; - - public override bool IsSet => !m_ACLArray.IsNullOrEmpty(); - - public ArkACLClip() - { - m_ACLArray = Array.Empty(); - } - - public override void Read(ObjectReader reader) - { - m_ACLType = reader.ReadInt32(); - m_ACLArray = reader.ReadUInt8Array(); - reader.AlignStream(); - m_PositionFactor = reader.ReadSingle(); - m_EulerFactor = reader.ReadSingle(); - m_ScaleFactor = reader.ReadSingle(); - m_FloatFactor = reader.ReadSingle(); - m_nPositionCurves = reader.ReadSingle(); - m_nRotationCurves = reader.ReadSingle(); - m_nEulerCurves = reader.ReadSingle(); - m_nScaleCurves = reader.ReadSingle(); - } - } - public class MHYACLClip : ACLClip { public uint m_CurveCount; @@ -1101,6 +1065,108 @@ namespace AssetStudio return denseClip; } } + public class ArkDenseClip : DenseClip + { + public int m_ACLType; + public byte[] m_ACLArray; + public float m_PositionFactor; + public float m_EulerFactor; + public float m_ScaleFactor; + public float m_FloatFactor; + public uint m_nPositionCurves; + public uint m_nRotationCurves; + public uint m_nEulerCurves; + public uint m_nScaleCurves; + + public ArkDenseClip(ObjectReader reader) : base(reader) + { + m_ACLType = reader.ReadInt32(); + m_ACLArray = reader.ReadUInt8Array(); + reader.AlignStream(); + m_PositionFactor = reader.ReadSingle(); + m_EulerFactor = reader.ReadSingle(); + m_ScaleFactor = reader.ReadSingle(); + m_FloatFactor = reader.ReadSingle(); + m_nPositionCurves = reader.ReadUInt32(); + m_nRotationCurves = reader.ReadUInt32(); + m_nEulerCurves = reader.ReadUInt32(); + m_nScaleCurves = reader.ReadUInt32(); + + Process(); + } + + private void Process() + { + if (m_ACLType == 0 || !m_SampleArray.IsNullOrEmpty()) + { + return; + } + + var sampleArray = new List(); + + var size = m_ACLType >> 2; + var factor = (float)((1 << m_ACLType) - 1); + var aclSpan = m_ACLArray.ToUInt4Array().AsSpan(); + var buffer = (stackalloc byte[8]); + + for (int i = 0; i < m_FrameCount; i++) + { + var index = i * (int)(m_CurveCount * size); + for (int j = 0; j < m_nPositionCurves; j++) + { + sampleArray.Add(ReadCurve(aclSpan, m_PositionFactor, ref index)); + } + for (int j = 0; j < m_nRotationCurves; j++) + { + sampleArray.Add(ReadCurve(aclSpan, 1.0f, ref index)); + } + for (int j = 0; j < m_nEulerCurves; j++) + { + sampleArray.Add(ReadCurve(aclSpan, m_EulerFactor, ref index)); + } + for (int j = 0; j < m_nScaleCurves; j++) + { + sampleArray.Add(ReadCurve(aclSpan, m_ScaleFactor, ref index)); + } + var m_nFloatCurves = m_CurveCount - (m_nPositionCurves + m_nRotationCurves + m_nEulerCurves + m_nScaleCurves); + for (int j = 0; j < m_nFloatCurves; j++) + { + sampleArray.Add(ReadCurve(aclSpan, m_FloatFactor, ref index)); + } + } + + m_SampleArray = sampleArray.ToArray(); + } + + private float ReadCurve(Span aclSpan, float curveFactor, ref int curveIndex) + { + var buffer = (stackalloc byte[8]); + + var curveSize = m_ACLType >> 2; + var factor = (float)((1 << m_ACLType) - 1); + + aclSpan.Slice(curveIndex, curveSize).CopyTo(buffer); + var temp = buffer.ToArray().ToUInt8Array(0, curveSize); + buffer.Clear(); + temp.CopyTo(buffer); + + float curve; + var value = BitConverter.ToUInt64(buffer); + if (value != 0) + { + curve = ((value / factor) - 0.5f) * 2; + } + else + { + curve = -1.0f; + } + + curve *= curveFactor; + curveIndex += curveSize; + + return curve; + } + } public class ConstantClip { @@ -1180,17 +1246,19 @@ namespace AssetStudio { var version = reader.version; m_StreamedClip = new StreamedClip(reader); - m_DenseClip = new DenseClip(reader); + if (reader.Game.Type.IsArknightsEndfield()) + { + m_DenseClip = new ArkDenseClip(reader); + } + else + { + m_DenseClip = new DenseClip(reader); + } if (reader.Game.Type.IsSRGroup()) { m_ACLClip = new MHYACLClip(); m_ACLClip.Read(reader); } - if (reader.Game.Type.IsArknightsEndfield()) - { - m_ACLClip = new ArkACLClip(); - m_ACLClip.Read(reader); - } if (version[0] > 4 || (version[0] == 4 && version[1] >= 3)) //4.3 and up { m_ConstantClip = new ConstantClip(reader); diff --git a/AssetStudio/Extensions/ByteArrayExtensions.cs b/AssetStudio/Extensions/ByteArrayExtensions.cs index fa2f54b..be6f77f 100644 --- a/AssetStudio/Extensions/ByteArrayExtensions.cs +++ b/AssetStudio/Extensions/ByteArrayExtensions.cs @@ -18,6 +18,23 @@ namespace AssetStudio } return buffer; } + public static byte[] ToUInt8Array(this byte[] source, int offset, int size) + { + var buffer = new byte[size / 2]; + for (var i = 0; i < size; i++) + { + var idx = i / 2; + if (i % 2 == 0) + { + buffer[idx] = (byte)(source[offset + i] << 4); + } + else + { + buffer[idx] |= source[offset + i]; + } + } + return buffer; + } public static int Search(this byte[] src, string value, int offset = 0) => Search(src.AsSpan(), Encoding.UTF8.GetBytes(value), offset); public static int Search(this Span src, byte[] pattern, int offset = 0) { diff --git a/AssetStudio/LZ4/LZ4Utils.cs b/AssetStudio/LZ4/LZ4Utils.cs index 5c30269..0f22898 100644 --- a/AssetStudio/LZ4/LZ4Utils.cs +++ b/AssetStudio/LZ4/LZ4Utils.cs @@ -1,6 +1,6 @@ using System; -namespace AssetStudio.LZ4; +namespace AssetStudio; public static class LZ4 { public static int Decompress(ReadOnlySpan cmp, Span dec) diff --git a/AssetStudio/Mhy0File.cs b/AssetStudio/Mhy0File.cs index 4ea42d8..cf75ec7 100644 --- a/AssetStudio/Mhy0File.cs +++ b/AssetStudio/Mhy0File.cs @@ -54,7 +54,7 @@ namespace AssetStudio Logger.Verbose($"uncompressed blocksInfo size: 0x{m_Header.uncompressedBlocksInfoSize:X8}"); var compressedBlocksInfo = blocksInfoReader.ReadBytes((int)blocksInfoReader.Remaining); var uncompressedBlocksInfo = new byte[(int)m_Header.uncompressedBlocksInfoSize]; - var numWrite = LZ4.LZ4.Decompress(compressedBlocksInfo, uncompressedBlocksInfo); + var numWrite = LZ4.Decompress(compressedBlocksInfo, uncompressedBlocksInfo); if (numWrite != m_Header.uncompressedBlocksInfoSize) { throw new IOException($"Lz4 decompression error, write {numWrite} bytes but expected {m_Header.uncompressedBlocksInfoSize} bytes"); @@ -127,7 +127,7 @@ namespace AssetStudio DescrambleEntry(compressedBytesSpan); Logger.Verbose($"Descrambled block signature {Convert.ToHexString(compressedBytes, 0, 4)}"); - var numWrite = LZ4.LZ4.Decompress(compressedBytesSpan[0xC..compressedSize], uncompressedBytesSpan); + var numWrite = LZ4.Decompress(compressedBytesSpan[0xC..compressedSize], uncompressedBytesSpan); if (numWrite != uncompressedSize) { throw new IOException($"Lz4 decompression error, write {numWrite} bytes but expected {uncompressedSize} bytes");